@primer/view-components 0.43.6 → 0.44.0-rc.af8944c0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/app/assets/javascripts/components/primer/alpha/tree_view/tree_view.d.ts +39 -0
  2. package/app/assets/javascripts/components/primer/alpha/tree_view/tree_view_icon_pair_element.d.ts +15 -0
  3. package/app/assets/javascripts/components/primer/alpha/tree_view/tree_view_include_fragment_element.d.ts +9 -0
  4. package/app/assets/javascripts/components/primer/alpha/tree_view/tree_view_roving_tab_index.d.ts +3 -0
  5. package/app/assets/javascripts/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.d.ts +42 -0
  6. package/app/assets/javascripts/components/primer/primer.d.ts +4 -0
  7. package/app/assets/javascripts/components/primer/shared_events.d.ts +15 -0
  8. package/app/assets/javascripts/primer_view_components.js +1 -1
  9. package/app/assets/javascripts/primer_view_components.js.map +1 -1
  10. package/app/assets/styles/primer_view_components.css +1 -1
  11. package/app/assets/styles/primer_view_components.css.map +1 -1
  12. package/app/components/primer/alpha/skeleton_box.css +1 -0
  13. package/app/components/primer/alpha/skeleton_box.css.json +6 -0
  14. package/app/components/primer/alpha/tree_view/tree_view.d.ts +39 -0
  15. package/app/components/primer/alpha/tree_view/tree_view.js +363 -0
  16. package/app/components/primer/alpha/tree_view/tree_view_icon_pair_element.d.ts +15 -0
  17. package/app/components/primer/alpha/tree_view/tree_view_icon_pair_element.js +62 -0
  18. package/app/components/primer/alpha/tree_view/tree_view_include_fragment_element.d.ts +9 -0
  19. package/app/components/primer/alpha/tree_view/tree_view_include_fragment_element.js +28 -0
  20. package/app/components/primer/alpha/tree_view/tree_view_roving_tab_index.d.ts +3 -0
  21. package/app/components/primer/alpha/tree_view/tree_view_roving_tab_index.js +130 -0
  22. package/app/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.d.ts +42 -0
  23. package/app/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.js +418 -0
  24. package/app/components/primer/alpha/tree_view.css +1 -0
  25. package/app/components/primer/alpha/tree_view.css.json +52 -0
  26. package/app/components/primer/primer.d.ts +4 -0
  27. package/app/components/primer/primer.js +4 -0
  28. package/app/components/primer/shared_events.d.ts +15 -0
  29. package/package.json +1 -1
  30. package/static/classes.json +15 -0
@@ -0,0 +1 @@
1
+ @keyframes shimmer{0%{mask-position:200%}to{mask-position:0}}.SkeletonBox{animation:shimmer;background-color:var(--bgColor-muted);border-radius:var(--borderRadius-small);display:block;height:1rem}@media (prefers-reduced-motion:no-preference){.SkeletonBox{animation:shimmer;animation-duration:1s;animation-iteration-count:infinite;mask-image:linear-gradient(75deg,#000 30%,#000000a6 80%);mask-size:200%}}@media (forced-colors:active){.SkeletonBox{outline:1px solid #0000;outline-offset:-1px}}
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "alpha/skeleton_box",
3
+ "selectors": [
4
+ ".SkeletonBox"
5
+ ]
6
+ }
@@ -0,0 +1,39 @@
1
+ import { TreeViewSubTreeNodeElement } from './tree_view_sub_tree_node_element';
2
+ import type { TreeViewNodeType, TreeViewCheckedValue, TreeViewNodeInfo } from '../../shared_events';
3
+ export declare class TreeViewElement extends HTMLElement {
4
+ #private;
5
+ formInputContainer: HTMLElement;
6
+ formInputPrototype: HTMLInputElement;
7
+ connectedCallback(): void;
8
+ disconnectedCallback(): void;
9
+ handleEvent(event: Event): void;
10
+ getFormInputValueForNode(node: Element): string | null;
11
+ getNodePath(node: Element): string[];
12
+ getNodeType(node: Element): TreeViewNodeType | null;
13
+ markCurrentAtPath(path: string[]): void;
14
+ get currentNode(): HTMLLIElement | null;
15
+ expandAtPath(path: string[]): void;
16
+ collapseAtPath(path: string[]): void;
17
+ toggleAtPath(path: string[]): void;
18
+ checkAtPath(path: string[]): void;
19
+ uncheckAtPath(path: string[]): void;
20
+ toggleCheckedAtPath(path: string[]): void;
21
+ checkedValueAtPath(path: string[]): TreeViewCheckedValue;
22
+ disabledValueAtPath(path: string[]): boolean;
23
+ nodeAtPath(path: string[], selector?: string): Element | null;
24
+ subTreeAtPath(path: string[]): TreeViewSubTreeNodeElement | null;
25
+ leafAtPath(path: string[]): HTMLLIElement | null;
26
+ setNodeCheckedValue(node: Element, value: TreeViewCheckedValue): void;
27
+ getNodeCheckedValue(node: Element): TreeViewCheckedValue;
28
+ getNodeDisabledValue(node: Element): boolean;
29
+ setNodeDisabledValue(node: Element, disabled: boolean): void;
30
+ nodeHasCheckBox(node: Element): boolean;
31
+ nodeHasNativeAction(node: Element): boolean;
32
+ expandAncestorsForNode(node: HTMLElement): void;
33
+ infoFromNode(node: Element, newCheckedValue?: TreeViewCheckedValue): TreeViewNodeInfo | null;
34
+ }
35
+ declare global {
36
+ interface Window {
37
+ TreeViewElement: typeof TreeViewElement;
38
+ }
39
+ }
@@ -0,0 +1,363 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
14
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
15
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
16
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
17
+ };
18
+ var _TreeViewElement_instances, _TreeViewElement_abortController, _TreeViewElement_autoExpandFrom, _TreeViewElement_eventIsActivation, _TreeViewElement_nodeForEvent, _TreeViewElement_handleNodeEvent, _TreeViewElement_eventIsCheckboxToggle, _TreeViewElement_handleCheckboxToggle, _TreeViewElement_handleNodeActivated, _TreeViewElement_handleNodeFocused, _TreeViewElement_handleNodeKeyboardEvent;
19
+ import { controller, target } from '@github/catalyst';
20
+ import { useRovingTabIndex } from './tree_view_roving_tab_index';
21
+ let TreeViewElement = class TreeViewElement extends HTMLElement {
22
+ constructor() {
23
+ super(...arguments);
24
+ _TreeViewElement_instances.add(this);
25
+ _TreeViewElement_abortController.set(this, void 0);
26
+ }
27
+ connectedCallback() {
28
+ const { signal } = (__classPrivateFieldSet(this, _TreeViewElement_abortController, new AbortController(), "f"));
29
+ this.addEventListener('click', this, { signal });
30
+ this.addEventListener('focusin', this, { signal });
31
+ this.addEventListener('keydown', this, { signal });
32
+ useRovingTabIndex(this);
33
+ // catch-all for any straggler nodes that aren't available when connectedCallback runs
34
+ new MutationObserver(mutations => {
35
+ for (const mutation of mutations) {
36
+ for (const addedNode of mutation.addedNodes) {
37
+ if (!(addedNode instanceof HTMLElement))
38
+ continue;
39
+ if (addedNode.querySelector('[aria-expanded=true]')) {
40
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_autoExpandFrom).call(this, addedNode);
41
+ }
42
+ }
43
+ }
44
+ }).observe(this, { childList: true, subtree: true });
45
+ const updateInputsObserver = new MutationObserver(mutations => {
46
+ if (!this.formInputContainer)
47
+ return;
48
+ // There is another MutationObserver in TreeViewSubTreeNodeElement that manages checking/unchecking
49
+ // nodes based on the component's select strategy. These two observers can conflict and cause infinite
50
+ // looping, so we make sure something actually changed before computing inputs again.
51
+ const somethingChanged = mutations.some(m => {
52
+ if (!(m.target instanceof HTMLElement))
53
+ return false;
54
+ return m.target.getAttribute('aria-checked') !== m.oldValue;
55
+ });
56
+ if (!somethingChanged)
57
+ return;
58
+ const newInputs = [];
59
+ for (const node of this.querySelectorAll('[role=treeitem][aria-checked=true]')) {
60
+ const newInput = this.formInputPrototype.cloneNode();
61
+ newInput.removeAttribute('data-target');
62
+ newInput.removeAttribute('form');
63
+ const payload = {
64
+ path: this.getNodePath(node),
65
+ };
66
+ const inputValue = this.getFormInputValueForNode(node);
67
+ if (inputValue)
68
+ payload.value = inputValue;
69
+ newInput.value = JSON.stringify(payload);
70
+ newInputs.push(newInput);
71
+ }
72
+ this.formInputContainer.replaceChildren(...newInputs);
73
+ });
74
+ updateInputsObserver.observe(this, {
75
+ childList: true,
76
+ subtree: true,
77
+ attributeFilter: ['aria-checked'],
78
+ });
79
+ // eslint-disable-next-line github/no-then -- We don't want to wait for this to resolve, just get on with it
80
+ customElements.whenDefined('tree-view-sub-tree-node').then(() => {
81
+ // depends on TreeViewSubTreeNodeElement#eachAncestorSubTreeNode, which may not be defined yet
82
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_autoExpandFrom).call(this, this);
83
+ });
84
+ }
85
+ disconnectedCallback() {
86
+ __classPrivateFieldGet(this, _TreeViewElement_abortController, "f").abort();
87
+ }
88
+ handleEvent(event) {
89
+ const node = __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_nodeForEvent).call(this, event);
90
+ if (node) {
91
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleNodeEvent).call(this, node, event);
92
+ }
93
+ }
94
+ getFormInputValueForNode(node) {
95
+ return node.getAttribute('data-value');
96
+ }
97
+ getNodePath(node) {
98
+ const rawPath = node.getAttribute('data-path');
99
+ if (rawPath) {
100
+ return JSON.parse(rawPath);
101
+ }
102
+ return [];
103
+ }
104
+ getNodeType(node) {
105
+ return node.getAttribute('data-node-type');
106
+ }
107
+ markCurrentAtPath(path) {
108
+ const pathStr = JSON.stringify(path);
109
+ const nodeToMark = this.querySelector(`[data-path="${CSS.escape(pathStr)}"`);
110
+ if (!nodeToMark)
111
+ return;
112
+ this.currentNode?.setAttribute('aria-current', 'false');
113
+ nodeToMark.setAttribute('aria-current', 'true');
114
+ }
115
+ get currentNode() {
116
+ return this.querySelector('[aria-current=true]');
117
+ }
118
+ expandAtPath(path) {
119
+ const node = this.subTreeAtPath(path);
120
+ if (!node)
121
+ return;
122
+ node.expand();
123
+ }
124
+ collapseAtPath(path) {
125
+ const node = this.subTreeAtPath(path);
126
+ if (!node)
127
+ return;
128
+ node.collapse();
129
+ }
130
+ toggleAtPath(path) {
131
+ const node = this.subTreeAtPath(path);
132
+ if (!node)
133
+ return;
134
+ node.toggle();
135
+ }
136
+ checkAtPath(path) {
137
+ const node = this.nodeAtPath(path);
138
+ if (!node)
139
+ return;
140
+ this.setNodeCheckedValue(node, 'true');
141
+ }
142
+ uncheckAtPath(path) {
143
+ const node = this.nodeAtPath(path);
144
+ if (!node)
145
+ return;
146
+ this.setNodeCheckedValue(node, 'false');
147
+ }
148
+ toggleCheckedAtPath(path) {
149
+ const node = this.nodeAtPath(path);
150
+ if (!node)
151
+ return;
152
+ if (this.getNodeType(node) === 'leaf') {
153
+ if (this.getNodeCheckedValue(node) === 'true') {
154
+ this.uncheckAtPath(path);
155
+ }
156
+ else {
157
+ this.checkAtPath(path);
158
+ }
159
+ }
160
+ }
161
+ checkedValueAtPath(path) {
162
+ const node = this.nodeAtPath(path);
163
+ if (!node)
164
+ return 'false';
165
+ return this.getNodeCheckedValue(node);
166
+ }
167
+ disabledValueAtPath(path) {
168
+ const node = this.nodeAtPath(path);
169
+ if (!node)
170
+ return false;
171
+ return this.getNodeDisabledValue(node);
172
+ }
173
+ nodeAtPath(path, selector) {
174
+ const pathStr = JSON.stringify(path);
175
+ return this.querySelector(`${selector || ''}[data-path="${CSS.escape(pathStr)}"]`);
176
+ }
177
+ subTreeAtPath(path) {
178
+ const node = this.nodeAtPath(path, '[data-node-type=sub-tree]');
179
+ if (!node)
180
+ return null;
181
+ return node.closest('tree-view-sub-tree-node');
182
+ }
183
+ leafAtPath(path) {
184
+ return this.nodeAtPath(path, '[data-node-type=leaf]');
185
+ }
186
+ setNodeCheckedValue(node, value) {
187
+ node.setAttribute('aria-checked', value.toString());
188
+ }
189
+ getNodeCheckedValue(node) {
190
+ return (node.getAttribute('aria-checked') || 'false');
191
+ }
192
+ getNodeDisabledValue(node) {
193
+ return node.getAttribute('aria-disabled') === 'true';
194
+ }
195
+ setNodeDisabledValue(node, disabled) {
196
+ if (disabled) {
197
+ node.setAttribute('aria-disabled', 'true');
198
+ }
199
+ else {
200
+ node.removeAttribute('aria-disabled');
201
+ }
202
+ }
203
+ nodeHasCheckBox(node) {
204
+ return node.querySelector('.TreeViewItemCheckbox') !== null;
205
+ }
206
+ nodeHasNativeAction(node) {
207
+ return node instanceof HTMLAnchorElement || node instanceof HTMLButtonElement;
208
+ }
209
+ expandAncestorsForNode(node) {
210
+ const subTreeNode = node.closest('tree-view-sub-tree-node');
211
+ if (!subTreeNode)
212
+ return;
213
+ for (const ancestor of subTreeNode.eachAncestorSubTreeNode()) {
214
+ if (!ancestor.expanded) {
215
+ ancestor.expand();
216
+ }
217
+ }
218
+ }
219
+ // PRIVATE API METHOD
220
+ //
221
+ // This would normally be marked private, but it's called by TreeViewSubTreeNodes
222
+ // and thus must be public.
223
+ infoFromNode(node, newCheckedValue) {
224
+ const type = this.getNodeType(node);
225
+ if (!type)
226
+ return null;
227
+ const checkedValue = this.getNodeCheckedValue(node);
228
+ return {
229
+ node,
230
+ type,
231
+ path: this.getNodePath(node),
232
+ checkedValue: newCheckedValue || checkedValue,
233
+ previousCheckedValue: checkedValue,
234
+ };
235
+ }
236
+ };
237
+ _TreeViewElement_abortController = new WeakMap();
238
+ _TreeViewElement_instances = new WeakSet();
239
+ _TreeViewElement_autoExpandFrom = function _TreeViewElement_autoExpandFrom(root) {
240
+ for (const element of root.querySelectorAll('[aria-expanded=true]')) {
241
+ this.expandAncestorsForNode(element);
242
+ }
243
+ };
244
+ _TreeViewElement_eventIsActivation = function _TreeViewElement_eventIsActivation(event) {
245
+ return event.type === 'click';
246
+ };
247
+ _TreeViewElement_nodeForEvent = function _TreeViewElement_nodeForEvent(event) {
248
+ const eventTarget = event.target;
249
+ const node = eventTarget.closest('[role=treeitem]');
250
+ if (!node)
251
+ return null;
252
+ if (eventTarget.closest('.TreeViewItemToggle'))
253
+ return null;
254
+ if (eventTarget.closest('.TreeViewItemLeadingAction'))
255
+ return null;
256
+ return node;
257
+ };
258
+ _TreeViewElement_handleNodeEvent = function _TreeViewElement_handleNodeEvent(node, event) {
259
+ if (__classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_eventIsCheckboxToggle).call(this, event, node)) {
260
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleCheckboxToggle).call(this, event, node);
261
+ }
262
+ else if (__classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_eventIsActivation).call(this, event)) {
263
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleNodeActivated).call(this, event, node);
264
+ }
265
+ else if (event.type === 'focusin') {
266
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleNodeFocused).call(this, node);
267
+ }
268
+ else if (event instanceof KeyboardEvent) {
269
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleNodeKeyboardEvent).call(this, event, node);
270
+ }
271
+ };
272
+ _TreeViewElement_eventIsCheckboxToggle = function _TreeViewElement_eventIsCheckboxToggle(event, node) {
273
+ return event.type === 'click' && this.nodeHasCheckBox(node);
274
+ };
275
+ _TreeViewElement_handleCheckboxToggle = function _TreeViewElement_handleCheckboxToggle(event, node) {
276
+ if (this.getNodeDisabledValue(node)) {
277
+ event.preventDefault();
278
+ return;
279
+ }
280
+ // only handle checking of leaf nodes, see TreeViewSubTreeNodeElement for the code that
281
+ // handles checking sub tree items.
282
+ const type = this.getNodeType(node);
283
+ if (type !== 'leaf')
284
+ return;
285
+ if (this.getNodeCheckedValue(node) === 'true') {
286
+ this.setNodeCheckedValue(node, 'false');
287
+ }
288
+ else {
289
+ this.setNodeCheckedValue(node, 'true');
290
+ }
291
+ };
292
+ _TreeViewElement_handleNodeActivated = function _TreeViewElement_handleNodeActivated(event, node) {
293
+ if (this.getNodeDisabledValue(node)) {
294
+ event.preventDefault();
295
+ return;
296
+ }
297
+ // do not emit activation events for buttons and anchors, since it is assumed any activation
298
+ // behavior for these element types is user- or browser-defined
299
+ if (!(node instanceof HTMLDivElement))
300
+ return;
301
+ const path = this.getNodePath(node);
302
+ const activationSuccess = this.dispatchEvent(new CustomEvent('treeViewBeforeNodeActivated', {
303
+ bubbles: true,
304
+ cancelable: true,
305
+ detail: this.infoFromNode(node),
306
+ }));
307
+ if (!activationSuccess)
308
+ return;
309
+ // navigate or trigger button, don't toggle
310
+ if (!this.nodeHasNativeAction(node)) {
311
+ this.toggleAtPath(path);
312
+ }
313
+ this.dispatchEvent(new CustomEvent('treeViewNodeActivated', {
314
+ bubbles: true,
315
+ detail: this.infoFromNode(node),
316
+ }));
317
+ };
318
+ _TreeViewElement_handleNodeFocused = function _TreeViewElement_handleNodeFocused(node) {
319
+ const previousNode = this.querySelector('[aria-selected=true]');
320
+ previousNode?.setAttribute('aria-selected', 'false');
321
+ node.setAttribute('aria-selected', 'true');
322
+ };
323
+ _TreeViewElement_handleNodeKeyboardEvent = function _TreeViewElement_handleNodeKeyboardEvent(event, node) {
324
+ if (!node || this.getNodeType(node) !== 'leaf') {
325
+ return;
326
+ }
327
+ switch (event.key) {
328
+ case ' ':
329
+ case 'Enter':
330
+ if (this.getNodeDisabledValue(node)) {
331
+ event.preventDefault();
332
+ break;
333
+ }
334
+ if (this.nodeHasCheckBox(node)) {
335
+ event.preventDefault();
336
+ if (this.getNodeCheckedValue(node) === 'true') {
337
+ this.setNodeCheckedValue(node, 'false');
338
+ }
339
+ else {
340
+ this.setNodeCheckedValue(node, 'true');
341
+ }
342
+ }
343
+ else if (node instanceof HTMLAnchorElement) {
344
+ // simulate click on space
345
+ node.click();
346
+ }
347
+ break;
348
+ }
349
+ };
350
+ __decorate([
351
+ target
352
+ ], TreeViewElement.prototype, "formInputContainer", void 0);
353
+ __decorate([
354
+ target
355
+ ], TreeViewElement.prototype, "formInputPrototype", void 0);
356
+ TreeViewElement = __decorate([
357
+ controller
358
+ ], TreeViewElement);
359
+ export { TreeViewElement };
360
+ if (!window.customElements.get('tree-view')) {
361
+ window.TreeViewElement = TreeViewElement;
362
+ window.customElements.define('tree-view', TreeViewElement);
363
+ }
@@ -0,0 +1,15 @@
1
+ export declare class TreeViewIconPairElement extends HTMLElement {
2
+ #private;
3
+ expandedIcon: HTMLElement;
4
+ collapsedIcon: HTMLElement;
5
+ expanded: boolean;
6
+ connectedCallback(): void;
7
+ showExpanded(): void;
8
+ showCollapsed(): void;
9
+ toggle(): void;
10
+ }
11
+ declare global {
12
+ interface Window {
13
+ TreeViewIconPairElement: typeof TreeViewIconPairElement;
14
+ }
15
+ }
@@ -0,0 +1,62 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _TreeViewIconPairElement_instances, _TreeViewIconPairElement_update;
13
+ import { controller, target } from '@github/catalyst';
14
+ import { observeMutationsUntilConditionMet } from '../../utils';
15
+ let TreeViewIconPairElement = class TreeViewIconPairElement extends HTMLElement {
16
+ constructor() {
17
+ super(...arguments);
18
+ _TreeViewIconPairElement_instances.add(this);
19
+ }
20
+ connectedCallback() {
21
+ observeMutationsUntilConditionMet(this, () => Boolean(this.collapsedIcon) && Boolean(this.expandedIcon), () => {
22
+ this.expanded = this.collapsedIcon.hidden;
23
+ });
24
+ }
25
+ showExpanded() {
26
+ this.expanded = true;
27
+ __classPrivateFieldGet(this, _TreeViewIconPairElement_instances, "m", _TreeViewIconPairElement_update).call(this);
28
+ }
29
+ showCollapsed() {
30
+ this.expanded = false;
31
+ __classPrivateFieldGet(this, _TreeViewIconPairElement_instances, "m", _TreeViewIconPairElement_update).call(this);
32
+ }
33
+ toggle() {
34
+ this.expanded = !this.expanded;
35
+ __classPrivateFieldGet(this, _TreeViewIconPairElement_instances, "m", _TreeViewIconPairElement_update).call(this);
36
+ }
37
+ };
38
+ _TreeViewIconPairElement_instances = new WeakSet();
39
+ _TreeViewIconPairElement_update = function _TreeViewIconPairElement_update() {
40
+ if (this.expanded) {
41
+ this.expandedIcon.hidden = false;
42
+ this.collapsedIcon.hidden = true;
43
+ }
44
+ else {
45
+ this.expandedIcon.hidden = true;
46
+ this.collapsedIcon.hidden = false;
47
+ }
48
+ };
49
+ __decorate([
50
+ target
51
+ ], TreeViewIconPairElement.prototype, "expandedIcon", void 0);
52
+ __decorate([
53
+ target
54
+ ], TreeViewIconPairElement.prototype, "collapsedIcon", void 0);
55
+ TreeViewIconPairElement = __decorate([
56
+ controller
57
+ ], TreeViewIconPairElement);
58
+ export { TreeViewIconPairElement };
59
+ if (!window.customElements.get('tree-view-icon-pair')) {
60
+ window.TreeViewIconPairElement = TreeViewIconPairElement;
61
+ window.customElements.define('tree-view-icon-pair', TreeViewIconPairElement);
62
+ }
@@ -0,0 +1,9 @@
1
+ import { IncludeFragmentElement } from '@github/include-fragment-element';
2
+ export declare class TreeViewIncludeFragmentElement extends IncludeFragmentElement {
3
+ request(): Request;
4
+ }
5
+ declare global {
6
+ interface Window {
7
+ TreeViewIncludeFragmentElement: typeof TreeViewIncludeFragmentElement;
8
+ }
9
+ }
@@ -0,0 +1,28 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { controller } from '@github/catalyst';
8
+ import { IncludeFragmentElement } from '@github/include-fragment-element';
9
+ let TreeViewIncludeFragmentElement = class TreeViewIncludeFragmentElement extends IncludeFragmentElement {
10
+ request() {
11
+ const originalRequest = super.request();
12
+ const url = new URL(originalRequest.url);
13
+ url.searchParams.set('path', this.getAttribute('data-path') || '');
14
+ return new Request(url, {
15
+ method: originalRequest.method,
16
+ headers: originalRequest.headers,
17
+ credentials: originalRequest.credentials,
18
+ });
19
+ }
20
+ };
21
+ TreeViewIncludeFragmentElement = __decorate([
22
+ controller
23
+ ], TreeViewIncludeFragmentElement);
24
+ export { TreeViewIncludeFragmentElement };
25
+ if (!window.customElements.get('tree-view-include-fragment')) {
26
+ window.TreeViewIncludeFragmentElement = TreeViewIncludeFragmentElement;
27
+ window.customElements.define('tree-view-include-fragment', TreeViewIncludeFragmentElement);
28
+ }
@@ -0,0 +1,3 @@
1
+ import { TreeViewElement } from './tree_view';
2
+ export declare function useRovingTabIndex(containerEl: TreeViewElement): void;
3
+ export declare function getElementState(element: HTMLElement): 'open' | 'closed' | 'end';
@@ -0,0 +1,130 @@
1
+ import { FocusKeys, focusZone } from '@primer/behaviors';
2
+ // This code was adapted from the roving tab index implementation in primer/react, see:
3
+ // https://github.com/primer/react/blob/f9785343716435f43e3d82482b057a17bd345c25/packages/react/src/TreeView/useRovingTabIndex.ts
4
+ export function useRovingTabIndex(containerEl) {
5
+ // TODO: Initialize focus to the aria-current item if it exists
6
+ focusZone(containerEl, {
7
+ bindKeys: FocusKeys.ArrowVertical | FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd | FocusKeys.Backspace,
8
+ getNextFocusable: (_direction, from, event) => {
9
+ if (!(from instanceof HTMLElement))
10
+ return;
11
+ // Skip elements within a modal dialog
12
+ // This need to be in a try/catch to avoid errors in
13
+ // non-supported browsers
14
+ try {
15
+ if (from.closest('dialog:modal')) {
16
+ return;
17
+ }
18
+ }
19
+ catch {
20
+ // Don't return
21
+ }
22
+ return getNextFocusableElement(from, event) ?? from;
23
+ },
24
+ focusInStrategy: () => {
25
+ let currentItem = containerEl.querySelector('[aria-current]');
26
+ currentItem = currentItem?.checkVisibility() ? currentItem : null;
27
+ const firstItem = containerEl.querySelector('[role="treeitem"]');
28
+ // Focus the aria-current item if it exists
29
+ if (currentItem instanceof HTMLElement) {
30
+ return currentItem;
31
+ }
32
+ // Otherwise, focus the activeElement if it's a treeitem
33
+ if (document.activeElement instanceof HTMLElement &&
34
+ containerEl.contains(document.activeElement) &&
35
+ document.activeElement.getAttribute('role') === 'treeitem') {
36
+ return document.activeElement;
37
+ }
38
+ // Otherwise, focus the first treeitem
39
+ return firstItem instanceof HTMLElement ? firstItem : undefined;
40
+ },
41
+ });
42
+ }
43
+ // DOM utilities used for focus management
44
+ function getNextFocusableElement(activeElement, event) {
45
+ const elementState = getElementState(activeElement);
46
+ // Reference: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboard-interaction-24
47
+ switch (`${elementState} ${event.key}`) {
48
+ case 'open ArrowRight':
49
+ // Focus first child node
50
+ return getFirstChildElement(activeElement);
51
+ case 'open ArrowLeft':
52
+ // Close node; don't change focus
53
+ return;
54
+ case 'closed ArrowRight':
55
+ // Open node; don't change focus
56
+ return;
57
+ case 'closed ArrowLeft':
58
+ // Focus parent element
59
+ return getParentElement(activeElement);
60
+ case 'end ArrowRight':
61
+ // Do nothing
62
+ return;
63
+ case 'end ArrowLeft':
64
+ // Focus parent element
65
+ return getParentElement(activeElement);
66
+ }
67
+ // ArrowUp and ArrowDown behavior is the same regardless of element state
68
+ switch (event.key) {
69
+ case 'ArrowUp':
70
+ // Focus previous visible element
71
+ return getVisibleElement(activeElement, 'previous');
72
+ case 'ArrowDown':
73
+ // Focus next visible element
74
+ return getVisibleElement(activeElement, 'next');
75
+ case 'Backspace':
76
+ return getParentElement(activeElement);
77
+ }
78
+ }
79
+ export function getElementState(element) {
80
+ if (element.getAttribute('role') !== 'treeitem') {
81
+ throw new Error('Element is not a treeitem');
82
+ }
83
+ switch (element.getAttribute('aria-expanded')) {
84
+ case 'true':
85
+ return 'open';
86
+ case 'false':
87
+ return 'closed';
88
+ default:
89
+ return 'end';
90
+ }
91
+ }
92
+ function getVisibleElement(element, direction) {
93
+ const root = element.closest('[role=tree]');
94
+ if (!root)
95
+ return;
96
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, node => {
97
+ if (!(node instanceof HTMLElement))
98
+ return NodeFilter.FILTER_SKIP;
99
+ return node.getAttribute('role') === 'treeitem' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
100
+ });
101
+ let current = walker.firstChild();
102
+ while (current !== element) {
103
+ current = walker.nextNode();
104
+ }
105
+ let next = direction === 'next' ? walker.nextNode() : walker.previousNode();
106
+ // If next element is nested inside a collapsed subtree, continue iterating
107
+ while (next instanceof HTMLElement && collapsedParent(next, root)) {
108
+ next = direction === 'next' ? walker.nextNode() : walker.previousNode();
109
+ }
110
+ return next instanceof HTMLElement ? next : undefined;
111
+ }
112
+ function collapsedParent(node, root) {
113
+ for (const ancestor of root.querySelectorAll('[role=treeitem][aria-expanded=false]')) {
114
+ if (node === ancestor)
115
+ continue;
116
+ if (ancestor.closest('li')?.contains(node)) {
117
+ return ancestor;
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+ function getFirstChildElement(element) {
123
+ const firstChild = element.querySelector('[role=treeitem]');
124
+ return firstChild instanceof HTMLElement ? firstChild : undefined;
125
+ }
126
+ function getParentElement(element) {
127
+ const group = element.closest('[role=group]');
128
+ const parent = group?.closest('[role=treeitem]');
129
+ return parent instanceof HTMLElement ? parent : undefined;
130
+ }