1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
import './toggle-files-button.css';
import select from 'select-dom';
import {CachedValue} from 'webext-storage-cache';
import React from 'dom-chef';
import delegate, {DelegateEvent} from 'delegate-it';
import * as pageDetect from 'github-url-detection';
import {ChevronDownIcon} from '@primer/octicons-react';
import features from '../feature-manager.js';
import observe from '../helpers/selector-observer.js';
import {isHasSelectorSupported} from '../helpers/select-has.js';
const wereFilesHidden = new CachedValue<boolean>('files-hidden');
const toggleButtonClass = 'rgh-toggle-files';
function addButton(filesBox: HTMLElement): void {
select('ul:has(.octicon-history)', filesBox)?.append(
<button
type="button"
className={`btn-octicon ${toggleButtonClass}`}
aria-label="Hide files"
>
<ChevronDownIcon/>
</button>,
);
}
type Targets = {
fileList: HTMLElement;
buttonWrapper: Element;
};
function getTargets(): Targets {
const fileList = select('[aria-labelledby="files"]')!;
const buttonWrapper = fileList.nextElementSibling!;
return {fileList, buttonWrapper};
}
function firstCollapseOnDesktop(targets = getTargets()): void {
targets.fileList.classList.remove('d-md-block');
targets.buttonWrapper.classList.remove('d-md-none');
}
async function toggleList(): Promise<void> {
const targets = getTargets();
const button = targets.buttonWrapper.firstElementChild as HTMLButtonElement;
if (targets.fileList.classList.contains('d-md-block')) {
// On the first click, collapse the list and enable native toggling on desktop
firstCollapseOnDesktop(targets);
if (window.matchMedia('(min-width: 768px)').matches) {
// We just hid the file list, no further action is necessary on desktop
void wereFilesHidden.set(true);
return;
}
// On mobile nothing visually happened because by default the list is already hidden, so it continues to actually hide the list via the native button.
}
// Toggle file list via native button, open or close
button.click();
}
async function updateView(anchor: HTMLHeadingElement): Promise<void> {
const filesBox = anchor.parentElement!;
addButton(filesBox);
if (await wereFilesHidden.get()) {
// This only applies on desktop; Mobile already always starts collapsed and we're not changing that
firstCollapseOnDesktop();
}
}
async function recordToggle({detail}: DelegateEvent<CustomEvent>): Promise<void> {
await wereFilesHidden.set(!detail.open);
}
async function init(signal: AbortSignal): Promise<void> {
observe('.Box h2#files', updateView, {signal});
delegate(`.${toggleButtonClass}`, 'click', toggleList, {signal});
delegate('#files ~ .Details', 'details:toggled', recordToggle, {signal});
}
void features.add(import.meta.url, {
asLongAs: [
isHasSelectorSupported,
],
include: [
pageDetect.isRepoTree,
],
init,
});
/*
Test URLs
https://github.com/refined-github/refined-github
https://github.com/refined-github/sandbox/tree/other-branch
*/
|