openproject-primer_view_components 0.64.1 → 0.65.0

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view.d.ts +29 -0
  4. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_icon_pair_element.d.ts +15 -0
  5. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_include_fragment_element.d.ts +9 -0
  6. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_roving_tab_index.d.ts +3 -0
  7. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +38 -0
  8. data/app/assets/javascripts/components/primer/primer.d.ts +4 -0
  9. data/app/assets/javascripts/components/primer/shared_events.d.ts +15 -0
  10. data/app/assets/javascripts/primer_view_components.js +1 -1
  11. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  12. data/app/assets/styles/primer_view_components.css +1 -1
  13. data/app/assets/styles/primer_view_components.css.map +1 -1
  14. data/app/components/primer/alpha/select_panel.css +1 -1
  15. data/app/components/primer/alpha/select_panel.css.json +2 -2
  16. data/app/components/primer/alpha/select_panel.css.map +1 -1
  17. data/app/components/primer/alpha/select_panel.html.erb +1 -1
  18. data/app/components/primer/alpha/select_panel.pcss +5 -2
  19. data/app/components/primer/beta/spinner.html.erb +1 -1
  20. data/app/components/primer/beta/spinner.rb +2 -0
  21. data/app/components/primer/open_project/file_tree_view/directory_node.html.erb +5 -0
  22. data/app/components/primer/open_project/file_tree_view/directory_node.rb +24 -0
  23. data/app/components/primer/open_project/file_tree_view/file_node.html.erb +2 -0
  24. data/app/components/primer/open_project/file_tree_view/file_node.rb +14 -0
  25. data/app/components/primer/open_project/file_tree_view.rb +15 -0
  26. data/app/components/primer/open_project/skeleton_box.css +1 -0
  27. data/app/components/primer/open_project/skeleton_box.css.json +6 -0
  28. data/app/components/primer/open_project/skeleton_box.css.map +1 -0
  29. data/app/components/primer/open_project/skeleton_box.html.erb +1 -0
  30. data/app/components/primer/open_project/skeleton_box.pcss +30 -0
  31. data/app/components/primer/open_project/skeleton_box.rb +27 -0
  32. data/app/components/primer/open_project/tree_view/icon.html.erb +1 -0
  33. data/app/components/primer/open_project/tree_view/icon.rb +22 -0
  34. data/app/components/primer/open_project/tree_view/icon_pair.html.erb +13 -0
  35. data/app/components/primer/open_project/tree_view/icon_pair.rb +42 -0
  36. data/app/components/primer/open_project/tree_view/leading_action.html.erb +3 -0
  37. data/app/components/primer/open_project/tree_view/leading_action.rb +18 -0
  38. data/app/components/primer/open_project/tree_view/leaf_node.html.erb +18 -0
  39. data/app/components/primer/open_project/tree_view/leaf_node.rb +96 -0
  40. data/app/components/primer/open_project/tree_view/loading_failure_message.html.erb +13 -0
  41. data/app/components/primer/open_project/tree_view/loading_failure_message.rb +31 -0
  42. data/app/components/primer/open_project/tree_view/node.html.erb +32 -0
  43. data/app/components/primer/open_project/tree_view/node.rb +155 -0
  44. data/app/components/primer/open_project/tree_view/skeleton_loader.html.erb +23 -0
  45. data/app/components/primer/open_project/tree_view/skeleton_loader.rb +36 -0
  46. data/app/components/primer/open_project/tree_view/spinner_loader.html.erb +20 -0
  47. data/app/components/primer/open_project/tree_view/spinner_loader.rb +33 -0
  48. data/app/components/primer/open_project/tree_view/sub_tree.html.erb +21 -0
  49. data/app/components/primer/open_project/tree_view/sub_tree.rb +106 -0
  50. data/app/components/primer/open_project/tree_view/sub_tree_container.html.erb +3 -0
  51. data/app/components/primer/open_project/tree_view/sub_tree_container.rb +39 -0
  52. data/app/components/primer/open_project/tree_view/sub_tree_node.html.erb +49 -0
  53. data/app/components/primer/open_project/tree_view/sub_tree_node.rb +172 -0
  54. data/app/components/primer/open_project/tree_view/tree_view.d.ts +29 -0
  55. data/app/components/primer/open_project/tree_view/tree_view.js +238 -0
  56. data/app/components/primer/open_project/tree_view/tree_view.ts +257 -0
  57. data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.d.ts +15 -0
  58. data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.js +62 -0
  59. data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.ts +56 -0
  60. data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.d.ts +9 -0
  61. data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.js +29 -0
  62. data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.ts +29 -0
  63. data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.d.ts +3 -0
  64. data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.js +126 -0
  65. data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.ts +156 -0
  66. data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +38 -0
  67. data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.js +362 -0
  68. data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.ts +402 -0
  69. data/app/components/primer/open_project/tree_view/visual.html.erb +14 -0
  70. data/app/components/primer/open_project/tree_view/visual.rb +27 -0
  71. data/app/components/primer/open_project/tree_view.css +1 -0
  72. data/app/components/primer/open_project/tree_view.css.json +42 -0
  73. data/app/components/primer/open_project/tree_view.css.map +1 -0
  74. data/app/components/primer/open_project/tree_view.html.erb +7 -0
  75. data/app/components/primer/open_project/tree_view.pcss +319 -0
  76. data/app/components/primer/open_project/tree_view.rb +367 -0
  77. data/app/components/primer/primer.d.ts +4 -0
  78. data/app/components/primer/primer.js +4 -0
  79. data/app/components/primer/primer.pcss +2 -0
  80. data/app/components/primer/primer.ts +4 -0
  81. data/app/components/primer/shared_events.d.ts +15 -0
  82. data/app/components/primer/shared_events.ts +19 -0
  83. data/app/lib/primer/forms/acts_as_component.rb +1 -12
  84. data/lib/primer/view_components/version.rb +2 -2
  85. data/previews/primer/open_project/file_tree_view_preview/default.html.erb +16 -0
  86. data/previews/primer/open_project/file_tree_view_preview/playground.html.erb +4 -0
  87. data/previews/primer/open_project/file_tree_view_preview.rb +69 -0
  88. data/previews/primer/open_project/skeleton_box_preview.rb +20 -0
  89. data/previews/primer/open_project/tree_view_preview/default.html.erb +24 -0
  90. data/previews/primer/open_project/tree_view_preview/empty.html.erb +10 -0
  91. data/previews/primer/open_project/tree_view_preview/leaf_node_playground.html.erb +15 -0
  92. data/previews/primer/open_project/tree_view_preview/loading_failure.html.erb +36 -0
  93. data/previews/primer/open_project/tree_view_preview/loading_skeleton.html.erb +12 -0
  94. data/previews/primer/open_project/tree_view_preview/loading_spinner.html.erb +12 -0
  95. data/previews/primer/open_project/tree_view_preview/playground.html.erb +4 -0
  96. data/previews/primer/open_project/tree_view_preview.rb +139 -0
  97. data/static/arguments.json +388 -0
  98. data/static/audited_at.json +17 -0
  99. data/static/classes.json +15 -0
  100. data/static/constants.json +83 -0
  101. data/static/info_arch.json +1367 -0
  102. data/static/previews.json +167 -0
  103. data/static/statuses.json +17 -0
  104. metadata +75 -2
@@ -0,0 +1,29 @@
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
+ connectedCallback(): void;
6
+ disconnectedCallback(): void;
7
+ handleEvent(event: Event): void;
8
+ getNodePath(node: Element): string[];
9
+ getNodeType(node: Element): TreeViewNodeType | null;
10
+ markCurrentAtPath(path: string[]): void;
11
+ get currentNode(): HTMLLIElement | null;
12
+ expandAtPath(path: string[]): void;
13
+ collapseAtPath(path: string[]): void;
14
+ toggleAtPath(path: string[]): void;
15
+ checkAtPath(path: string[]): void;
16
+ uncheckAtPath(path: string[]): void;
17
+ toggleCheckedAtPath(path: string[]): void;
18
+ checkedValueAtPath(path: string[]): TreeViewCheckedValue;
19
+ nodeAtPath(path: string[], selector?: string): Element | null;
20
+ subTreeAtPath(path: string[]): TreeViewSubTreeNodeElement | null;
21
+ leafAtPath(path: string[]): HTMLLIElement | null;
22
+ getNodeCheckedValue(node: Element): TreeViewCheckedValue;
23
+ infoFromNode(node: Element, newCheckedValue?: TreeViewCheckedValue): TreeViewNodeInfo | null;
24
+ }
25
+ declare global {
26
+ interface Window {
27
+ TreeViewElement: typeof TreeViewElement;
28
+ }
29
+ }
@@ -0,0 +1,238 @@
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_eventIsActivation, _TreeViewElement_nodeForEvent, _TreeViewElement_handleNodeEvent, _TreeViewElement_eventIsCheckboxToggle, _TreeViewElement_handleCheckboxToggle, _TreeViewElement_handleNodeActivated, _TreeViewElement_handleNodeFocused, _TreeViewElement_handleNodeKeyboardEvent, _TreeViewElement_setNodeCheckedValue;
19
+ import { controller } 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
+ }
34
+ disconnectedCallback() {
35
+ __classPrivateFieldGet(this, _TreeViewElement_abortController, "f").abort();
36
+ }
37
+ handleEvent(event) {
38
+ const node = __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_nodeForEvent).call(this, event);
39
+ if (node) {
40
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleNodeEvent).call(this, node, event);
41
+ }
42
+ }
43
+ getNodePath(node) {
44
+ const rawPath = node.getAttribute('data-path');
45
+ if (rawPath) {
46
+ return JSON.parse(rawPath);
47
+ }
48
+ return [];
49
+ }
50
+ getNodeType(node) {
51
+ return node.getAttribute('data-node-type');
52
+ }
53
+ markCurrentAtPath(path) {
54
+ const pathStr = JSON.stringify(path);
55
+ const nodeToMark = this.querySelector(`[data-path="${CSS.escape(pathStr)}"`);
56
+ if (!nodeToMark)
57
+ return;
58
+ this.currentNode?.setAttribute('aria-current', 'false');
59
+ nodeToMark.setAttribute('aria-current', 'true');
60
+ }
61
+ get currentNode() {
62
+ return this.querySelector('[aria-current=true]');
63
+ }
64
+ expandAtPath(path) {
65
+ const node = this.subTreeAtPath(path);
66
+ if (!node)
67
+ return;
68
+ node.expand();
69
+ }
70
+ collapseAtPath(path) {
71
+ const node = this.subTreeAtPath(path);
72
+ if (!node)
73
+ return;
74
+ node.collapse();
75
+ }
76
+ toggleAtPath(path) {
77
+ const node = this.subTreeAtPath(path);
78
+ if (!node)
79
+ return;
80
+ node.toggle();
81
+ }
82
+ checkAtPath(path) {
83
+ const node = this.nodeAtPath(path);
84
+ if (!node)
85
+ return;
86
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'true');
87
+ }
88
+ uncheckAtPath(path) {
89
+ const node = this.nodeAtPath(path);
90
+ if (!node)
91
+ return;
92
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'false');
93
+ }
94
+ toggleCheckedAtPath(path) {
95
+ const node = this.nodeAtPath(path);
96
+ if (!node)
97
+ return;
98
+ if (this.getNodeType(node) === 'leaf') {
99
+ if (this.getNodeCheckedValue(node) === 'true') {
100
+ this.uncheckAtPath(path);
101
+ }
102
+ else {
103
+ this.checkAtPath(path);
104
+ }
105
+ }
106
+ }
107
+ checkedValueAtPath(path) {
108
+ const node = this.nodeAtPath(path);
109
+ if (!node)
110
+ return 'false';
111
+ return this.getNodeCheckedValue(node);
112
+ }
113
+ nodeAtPath(path, selector) {
114
+ const pathStr = JSON.stringify(path);
115
+ return this.querySelector(`${selector || ''}[data-path="${CSS.escape(pathStr)}"]`);
116
+ }
117
+ subTreeAtPath(path) {
118
+ const node = this.nodeAtPath(path, '[data-node-type=sub-tree]');
119
+ if (!node)
120
+ return null;
121
+ return node.closest('tree-view-sub-tree-node');
122
+ }
123
+ leafAtPath(path) {
124
+ return this.nodeAtPath(path, '[data-node-type=leaf]');
125
+ }
126
+ getNodeCheckedValue(node) {
127
+ return (node.getAttribute('aria-checked') || 'false');
128
+ }
129
+ // PRIVATE API METHOD
130
+ //
131
+ // This would normally be marked private, but it's called by TreeViewSubTreeNodes
132
+ // and thus must be public.
133
+ infoFromNode(node, newCheckedValue) {
134
+ const type = this.getNodeType(node);
135
+ if (!type)
136
+ return null;
137
+ const checkedValue = this.getNodeCheckedValue(node);
138
+ return {
139
+ node,
140
+ type,
141
+ path: this.getNodePath(node),
142
+ checkedValue: newCheckedValue || checkedValue,
143
+ previousCheckedValue: checkedValue,
144
+ };
145
+ }
146
+ };
147
+ _TreeViewElement_abortController = new WeakMap();
148
+ _TreeViewElement_instances = new WeakSet();
149
+ _TreeViewElement_eventIsActivation = function _TreeViewElement_eventIsActivation(event) {
150
+ return event.type === 'click';
151
+ };
152
+ _TreeViewElement_nodeForEvent = function _TreeViewElement_nodeForEvent(event) {
153
+ const target = event.target;
154
+ const node = target.closest('[role=treeitem]');
155
+ if (!node)
156
+ return null;
157
+ if (target.closest('.TreeViewItemToggle'))
158
+ return null;
159
+ if (target.closest('.TreeViewItemLeadingAction'))
160
+ return null;
161
+ return node;
162
+ };
163
+ _TreeViewElement_handleNodeEvent = function _TreeViewElement_handleNodeEvent(node, event) {
164
+ if (__classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_eventIsCheckboxToggle).call(this, event)) {
165
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleCheckboxToggle).call(this, node);
166
+ }
167
+ else if (__classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_eventIsActivation).call(this, event)) {
168
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleNodeActivated).call(this, node);
169
+ }
170
+ else if (event.type === 'focusin') {
171
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleNodeFocused).call(this, node);
172
+ }
173
+ else if (event instanceof KeyboardEvent) {
174
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleNodeKeyboardEvent).call(this, event, node);
175
+ }
176
+ };
177
+ _TreeViewElement_eventIsCheckboxToggle = function _TreeViewElement_eventIsCheckboxToggle(event) {
178
+ return event.type === 'click' && event.target.closest('.TreeViewItemCheckbox') !== null;
179
+ };
180
+ _TreeViewElement_handleCheckboxToggle = function _TreeViewElement_handleCheckboxToggle(node) {
181
+ // only handle checking of leaf nodes
182
+ const type = this.getNodeType(node);
183
+ if (type !== 'leaf')
184
+ return;
185
+ if (this.getNodeCheckedValue(node) === 'true') {
186
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'false');
187
+ }
188
+ else {
189
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'true');
190
+ }
191
+ };
192
+ _TreeViewElement_handleNodeActivated = function _TreeViewElement_handleNodeActivated(node) {
193
+ const path = this.getNodePath(node);
194
+ const activationSuccess = this.dispatchEvent(new CustomEvent('treeViewBeforeNodeActivated', {
195
+ bubbles: true,
196
+ cancelable: true,
197
+ detail: this.infoFromNode(node),
198
+ }));
199
+ if (!activationSuccess)
200
+ return;
201
+ this.toggleAtPath(path);
202
+ this.dispatchEvent(new CustomEvent('treeViewNodeActivated', {
203
+ bubbles: true,
204
+ detail: this.infoFromNode(node),
205
+ }));
206
+ };
207
+ _TreeViewElement_handleNodeFocused = function _TreeViewElement_handleNodeFocused(node) {
208
+ const previousNode = this.querySelector('[aria-selected=true]');
209
+ previousNode?.setAttribute('aria-selected', 'false');
210
+ node.setAttribute('aria-selected', 'true');
211
+ };
212
+ _TreeViewElement_handleNodeKeyboardEvent = function _TreeViewElement_handleNodeKeyboardEvent(event, node) {
213
+ if (!node || this.getNodeType(node) !== 'leaf') {
214
+ return;
215
+ }
216
+ switch (event.key) {
217
+ case ' ':
218
+ event.preventDefault();
219
+ if (this.getNodeCheckedValue(node) === 'true') {
220
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'false');
221
+ }
222
+ else {
223
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'true');
224
+ }
225
+ break;
226
+ }
227
+ };
228
+ _TreeViewElement_setNodeCheckedValue = function _TreeViewElement_setNodeCheckedValue(node, value) {
229
+ node.setAttribute('aria-checked', value.toString());
230
+ };
231
+ TreeViewElement = __decorate([
232
+ controller
233
+ ], TreeViewElement);
234
+ export { TreeViewElement };
235
+ if (!window.customElements.get('tree-view')) {
236
+ window.TreeViewElement = TreeViewElement;
237
+ window.customElements.define('tree-view', TreeViewElement);
238
+ }
@@ -0,0 +1,257 @@
1
+ import {controller} from '@github/catalyst'
2
+ import {TreeViewSubTreeNodeElement} from './tree_view_sub_tree_node_element'
3
+ import {useRovingTabIndex} from './tree_view_roving_tab_index'
4
+ import type {TreeViewNodeType, TreeViewCheckedValue, TreeViewNodeInfo} from '../../shared_events'
5
+
6
+ @controller
7
+ export class TreeViewElement extends HTMLElement {
8
+ #abortController: AbortController
9
+
10
+ connectedCallback() {
11
+ const {signal} = (this.#abortController = new AbortController())
12
+ this.addEventListener('click', this, {signal})
13
+ this.addEventListener('focusin', this, {signal})
14
+ this.addEventListener('keydown', this, {signal})
15
+
16
+ useRovingTabIndex(this)
17
+ }
18
+
19
+ disconnectedCallback() {
20
+ this.#abortController.abort()
21
+ }
22
+
23
+ handleEvent(event: Event) {
24
+ const node = this.#nodeForEvent(event)
25
+
26
+ if (node) {
27
+ this.#handleNodeEvent(node, event)
28
+ }
29
+ }
30
+
31
+ #eventIsActivation(event: Event): boolean {
32
+ return event.type === 'click'
33
+ }
34
+
35
+ #nodeForEvent(event: Event): Element | null {
36
+ const target = event.target as Element
37
+ const node = target.closest('[role=treeitem]')
38
+ if (!node) return null
39
+
40
+ if (target.closest('.TreeViewItemToggle')) return null
41
+ if (target.closest('.TreeViewItemLeadingAction')) return null
42
+
43
+ return node
44
+ }
45
+
46
+ #handleNodeEvent(node: Element, event: Event) {
47
+ if (this.#eventIsCheckboxToggle(event)) {
48
+ this.#handleCheckboxToggle(node)
49
+ } else if (this.#eventIsActivation(event)) {
50
+ this.#handleNodeActivated(node)
51
+ } else if (event.type === 'focusin') {
52
+ this.#handleNodeFocused(node)
53
+ } else if (event instanceof KeyboardEvent) {
54
+ this.#handleNodeKeyboardEvent(event, node)
55
+ }
56
+ }
57
+
58
+ #eventIsCheckboxToggle(event: Event) {
59
+ return event.type === 'click' && (event.target as HTMLElement).closest('.TreeViewItemCheckbox') !== null
60
+ }
61
+
62
+ #handleCheckboxToggle(node: Element) {
63
+ // only handle checking of leaf nodes
64
+ const type = this.getNodeType(node)
65
+ if (type !== 'leaf') return
66
+
67
+ if (this.getNodeCheckedValue(node) === 'true') {
68
+ this.#setNodeCheckedValue(node, 'false')
69
+ } else {
70
+ this.#setNodeCheckedValue(node, 'true')
71
+ }
72
+ }
73
+
74
+ #handleNodeActivated(node: Element) {
75
+ const path = this.getNodePath(node)
76
+
77
+ const activationSuccess = this.dispatchEvent(
78
+ new CustomEvent('treeViewBeforeNodeActivated', {
79
+ bubbles: true,
80
+ cancelable: true,
81
+ detail: this.infoFromNode(node),
82
+ }),
83
+ )
84
+
85
+ if (!activationSuccess) return
86
+
87
+ this.toggleAtPath(path)
88
+
89
+ this.dispatchEvent(
90
+ new CustomEvent('treeViewNodeActivated', {
91
+ bubbles: true,
92
+ detail: this.infoFromNode(node),
93
+ }),
94
+ )
95
+ }
96
+
97
+ #handleNodeFocused(node: Element) {
98
+ const previousNode = this.querySelector('[aria-selected=true]')
99
+ previousNode?.setAttribute('aria-selected', 'false')
100
+ node.setAttribute('aria-selected', 'true')
101
+ }
102
+
103
+ #handleNodeKeyboardEvent(event: KeyboardEvent, node: Element) {
104
+ if (!node || this.getNodeType(node) !== 'leaf') {
105
+ return
106
+ }
107
+
108
+ switch (event.key) {
109
+ case ' ':
110
+ event.preventDefault()
111
+
112
+ if (this.getNodeCheckedValue(node) === 'true') {
113
+ this.#setNodeCheckedValue(node, 'false')
114
+ } else {
115
+ this.#setNodeCheckedValue(node, 'true')
116
+ }
117
+
118
+ break
119
+ }
120
+ }
121
+
122
+ getNodePath(node: Element): string[] {
123
+ const rawPath = node.getAttribute('data-path')
124
+
125
+ if (rawPath) {
126
+ return JSON.parse(rawPath)
127
+ }
128
+
129
+ return []
130
+ }
131
+
132
+ getNodeType(node: Element): TreeViewNodeType | null {
133
+ return node.getAttribute('data-node-type') as TreeViewNodeType | null
134
+ }
135
+
136
+ markCurrentAtPath(path: string[]) {
137
+ const pathStr = JSON.stringify(path)
138
+ const nodeToMark = this.querySelector(`[data-path="${CSS.escape(pathStr)}"`)
139
+ if (!nodeToMark) return
140
+
141
+ this.currentNode?.setAttribute('aria-current', 'false')
142
+ nodeToMark.setAttribute('aria-current', 'true')
143
+ }
144
+
145
+ get currentNode(): HTMLLIElement | null {
146
+ return this.querySelector('[aria-current=true]')
147
+ }
148
+
149
+ expandAtPath(path: string[]) {
150
+ const node = this.subTreeAtPath(path)
151
+ if (!node) return
152
+
153
+ node.expand()
154
+ }
155
+
156
+ collapseAtPath(path: string[]) {
157
+ const node = this.subTreeAtPath(path)
158
+ if (!node) return
159
+
160
+ node.collapse()
161
+ }
162
+
163
+ toggleAtPath(path: string[]) {
164
+ const node = this.subTreeAtPath(path)
165
+ if (!node) return
166
+
167
+ node.toggle()
168
+ }
169
+
170
+ checkAtPath(path: string[]) {
171
+ const node = this.nodeAtPath(path)
172
+ if (!node) return
173
+
174
+ this.#setNodeCheckedValue(node, 'true')
175
+ }
176
+
177
+ uncheckAtPath(path: string[]) {
178
+ const node = this.nodeAtPath(path)
179
+ if (!node) return
180
+
181
+ this.#setNodeCheckedValue(node, 'false')
182
+ }
183
+
184
+ toggleCheckedAtPath(path: string[]) {
185
+ const node = this.nodeAtPath(path)
186
+ if (!node) return
187
+
188
+ if (this.getNodeType(node) === 'leaf') {
189
+ if (this.getNodeCheckedValue(node) === 'true') {
190
+ this.uncheckAtPath(path)
191
+ } else {
192
+ this.checkAtPath(path)
193
+ }
194
+ }
195
+ }
196
+
197
+ checkedValueAtPath(path: string[]): TreeViewCheckedValue {
198
+ const node = this.nodeAtPath(path)
199
+ if (!node) return 'false'
200
+
201
+ return this.getNodeCheckedValue(node)
202
+ }
203
+
204
+ nodeAtPath(path: string[], selector?: string): Element | null {
205
+ const pathStr = JSON.stringify(path)
206
+ return this.querySelector(`${selector || ''}[data-path="${CSS.escape(pathStr)}"]`)
207
+ }
208
+
209
+ subTreeAtPath(path: string[]): TreeViewSubTreeNodeElement | null {
210
+ const node = this.nodeAtPath(path, '[data-node-type=sub-tree]')
211
+ if (!node) return null
212
+
213
+ return node.closest('tree-view-sub-tree-node') as TreeViewSubTreeNodeElement | null
214
+ }
215
+
216
+ leafAtPath(path: string[]): HTMLLIElement | null {
217
+ return this.nodeAtPath(path, '[data-node-type=leaf]') as HTMLLIElement | null
218
+ }
219
+
220
+ #setNodeCheckedValue(node: Element, value: TreeViewCheckedValue) {
221
+ node.setAttribute('aria-checked', value.toString())
222
+ }
223
+
224
+ getNodeCheckedValue(node: Element): TreeViewCheckedValue {
225
+ return (node.getAttribute('aria-checked') || 'false') as TreeViewCheckedValue
226
+ }
227
+
228
+ // PRIVATE API METHOD
229
+ //
230
+ // This would normally be marked private, but it's called by TreeViewSubTreeNodes
231
+ // and thus must be public.
232
+ infoFromNode(node: Element, newCheckedValue?: TreeViewCheckedValue): TreeViewNodeInfo | null {
233
+ const type = this.getNodeType(node)
234
+ if (!type) return null
235
+
236
+ const checkedValue = this.getNodeCheckedValue(node)
237
+
238
+ return {
239
+ node,
240
+ type,
241
+ path: this.getNodePath(node),
242
+ checkedValue: newCheckedValue || checkedValue,
243
+ previousCheckedValue: checkedValue,
244
+ }
245
+ }
246
+ }
247
+
248
+ if (!window.customElements.get('tree-view')) {
249
+ window.TreeViewElement = TreeViewElement
250
+ window.customElements.define('tree-view', TreeViewElement)
251
+ }
252
+
253
+ declare global {
254
+ interface Window {
255
+ TreeViewElement: typeof TreeViewElement
256
+ }
257
+ }
@@ -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,56 @@
1
+ import {controller, target} from '@github/catalyst'
2
+ import {observeMutationsUntilConditionMet} from '../../utils'
3
+
4
+ @controller
5
+ export class TreeViewIconPairElement extends HTMLElement {
6
+ @target expandedIcon: HTMLElement
7
+ @target collapsedIcon: HTMLElement
8
+
9
+ expanded: boolean
10
+
11
+ connectedCallback() {
12
+ observeMutationsUntilConditionMet(
13
+ this,
14
+ () => Boolean(this.collapsedIcon) && Boolean(this.expandedIcon),
15
+ () => {
16
+ this.expanded = this.collapsedIcon.hidden
17
+ },
18
+ )
19
+ }
20
+
21
+ showExpanded() {
22
+ this.expanded = true
23
+ this.#update()
24
+ }
25
+
26
+ showCollapsed() {
27
+ this.expanded = false
28
+ this.#update()
29
+ }
30
+
31
+ toggle() {
32
+ this.expanded = !this.expanded
33
+ this.#update()
34
+ }
35
+
36
+ #update() {
37
+ if (this.expanded) {
38
+ this.expandedIcon.hidden = false
39
+ this.collapsedIcon.hidden = true
40
+ } else {
41
+ this.expandedIcon.hidden = true
42
+ this.collapsedIcon.hidden = false
43
+ }
44
+ }
45
+ }
46
+
47
+ if (!window.customElements.get('tree-view-icon-pair')) {
48
+ window.TreeViewIconPairElement = TreeViewIconPairElement
49
+ window.customElements.define('tree-view-icon-pair', TreeViewIconPairElement)
50
+ }
51
+
52
+ declare global {
53
+ interface Window {
54
+ TreeViewIconPairElement: typeof TreeViewIconPairElement
55
+ }
56
+ }
@@ -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
+ }