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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view.d.ts +29 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_icon_pair_element.d.ts +15 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_include_fragment_element.d.ts +9 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_roving_tab_index.d.ts +3 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +38 -0
- data/app/assets/javascripts/components/primer/primer.d.ts +4 -0
- data/app/assets/javascripts/components/primer/shared_events.d.ts +15 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/assets/styles/primer_view_components.css +1 -1
- data/app/assets/styles/primer_view_components.css.map +1 -1
- data/app/components/primer/alpha/select_panel.css +1 -1
- data/app/components/primer/alpha/select_panel.css.json +2 -2
- data/app/components/primer/alpha/select_panel.css.map +1 -1
- data/app/components/primer/alpha/select_panel.html.erb +1 -1
- data/app/components/primer/alpha/select_panel.pcss +5 -2
- data/app/components/primer/beta/spinner.html.erb +1 -1
- data/app/components/primer/beta/spinner.rb +2 -0
- data/app/components/primer/open_project/file_tree_view/directory_node.html.erb +5 -0
- data/app/components/primer/open_project/file_tree_view/directory_node.rb +24 -0
- data/app/components/primer/open_project/file_tree_view/file_node.html.erb +2 -0
- data/app/components/primer/open_project/file_tree_view/file_node.rb +14 -0
- data/app/components/primer/open_project/file_tree_view.rb +15 -0
- data/app/components/primer/open_project/skeleton_box.css +1 -0
- data/app/components/primer/open_project/skeleton_box.css.json +6 -0
- data/app/components/primer/open_project/skeleton_box.css.map +1 -0
- data/app/components/primer/open_project/skeleton_box.html.erb +1 -0
- data/app/components/primer/open_project/skeleton_box.pcss +30 -0
- data/app/components/primer/open_project/skeleton_box.rb +27 -0
- data/app/components/primer/open_project/tree_view/icon.html.erb +1 -0
- data/app/components/primer/open_project/tree_view/icon.rb +22 -0
- data/app/components/primer/open_project/tree_view/icon_pair.html.erb +13 -0
- data/app/components/primer/open_project/tree_view/icon_pair.rb +42 -0
- data/app/components/primer/open_project/tree_view/leading_action.html.erb +3 -0
- data/app/components/primer/open_project/tree_view/leading_action.rb +18 -0
- data/app/components/primer/open_project/tree_view/leaf_node.html.erb +18 -0
- data/app/components/primer/open_project/tree_view/leaf_node.rb +96 -0
- data/app/components/primer/open_project/tree_view/loading_failure_message.html.erb +13 -0
- data/app/components/primer/open_project/tree_view/loading_failure_message.rb +31 -0
- data/app/components/primer/open_project/tree_view/node.html.erb +32 -0
- data/app/components/primer/open_project/tree_view/node.rb +155 -0
- data/app/components/primer/open_project/tree_view/skeleton_loader.html.erb +23 -0
- data/app/components/primer/open_project/tree_view/skeleton_loader.rb +36 -0
- data/app/components/primer/open_project/tree_view/spinner_loader.html.erb +20 -0
- data/app/components/primer/open_project/tree_view/spinner_loader.rb +33 -0
- data/app/components/primer/open_project/tree_view/sub_tree.html.erb +21 -0
- data/app/components/primer/open_project/tree_view/sub_tree.rb +106 -0
- data/app/components/primer/open_project/tree_view/sub_tree_container.html.erb +3 -0
- data/app/components/primer/open_project/tree_view/sub_tree_container.rb +39 -0
- data/app/components/primer/open_project/tree_view/sub_tree_node.html.erb +49 -0
- data/app/components/primer/open_project/tree_view/sub_tree_node.rb +172 -0
- data/app/components/primer/open_project/tree_view/tree_view.d.ts +29 -0
- data/app/components/primer/open_project/tree_view/tree_view.js +238 -0
- data/app/components/primer/open_project/tree_view/tree_view.ts +257 -0
- data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.d.ts +15 -0
- data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.js +62 -0
- data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.ts +56 -0
- data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.d.ts +9 -0
- data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.js +29 -0
- data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.ts +29 -0
- data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.d.ts +3 -0
- data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.js +126 -0
- data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.ts +156 -0
- data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +38 -0
- data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.js +362 -0
- data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.ts +402 -0
- data/app/components/primer/open_project/tree_view/visual.html.erb +14 -0
- data/app/components/primer/open_project/tree_view/visual.rb +27 -0
- data/app/components/primer/open_project/tree_view.css +1 -0
- data/app/components/primer/open_project/tree_view.css.json +42 -0
- data/app/components/primer/open_project/tree_view.css.map +1 -0
- data/app/components/primer/open_project/tree_view.html.erb +7 -0
- data/app/components/primer/open_project/tree_view.pcss +319 -0
- data/app/components/primer/open_project/tree_view.rb +367 -0
- data/app/components/primer/primer.d.ts +4 -0
- data/app/components/primer/primer.js +4 -0
- data/app/components/primer/primer.pcss +2 -0
- data/app/components/primer/primer.ts +4 -0
- data/app/components/primer/shared_events.d.ts +15 -0
- data/app/components/primer/shared_events.ts +19 -0
- data/app/lib/primer/forms/acts_as_component.rb +1 -12
- data/lib/primer/view_components/version.rb +2 -2
- data/previews/primer/open_project/file_tree_view_preview/default.html.erb +16 -0
- data/previews/primer/open_project/file_tree_view_preview/playground.html.erb +4 -0
- data/previews/primer/open_project/file_tree_view_preview.rb +69 -0
- data/previews/primer/open_project/skeleton_box_preview.rb +20 -0
- data/previews/primer/open_project/tree_view_preview/default.html.erb +24 -0
- data/previews/primer/open_project/tree_view_preview/empty.html.erb +10 -0
- data/previews/primer/open_project/tree_view_preview/leaf_node_playground.html.erb +15 -0
- data/previews/primer/open_project/tree_view_preview/loading_failure.html.erb +36 -0
- data/previews/primer/open_project/tree_view_preview/loading_skeleton.html.erb +12 -0
- data/previews/primer/open_project/tree_view_preview/loading_spinner.html.erb +12 -0
- data/previews/primer/open_project/tree_view_preview/playground.html.erb +4 -0
- data/previews/primer/open_project/tree_view_preview.rb +139 -0
- data/static/arguments.json +388 -0
- data/static/audited_at.json +17 -0
- data/static/classes.json +15 -0
- data/static/constants.json +83 -0
- data/static/info_arch.json +1367 -0
- data/static/previews.json +167 -0
- data/static/statuses.json +17 -0
- metadata +75 -2
@@ -0,0 +1,402 @@
|
|
1
|
+
import {controller, target} from '@github/catalyst'
|
2
|
+
import {TreeViewIconPairElement} from './tree_view_icon_pair_element'
|
3
|
+
import {observeMutationsUntilConditionMet} from '../../utils'
|
4
|
+
import {TreeViewIncludeFragmentElement} from './tree_view_include_fragment_element'
|
5
|
+
import {TreeViewElement} from './tree_view'
|
6
|
+
import type {TreeViewNodeInfo} from '../../shared_events'
|
7
|
+
|
8
|
+
type LoadingState = 'loading' | 'error' | 'success'
|
9
|
+
|
10
|
+
@controller
|
11
|
+
export class TreeViewSubTreeNodeElement extends HTMLElement {
|
12
|
+
@target node: HTMLElement
|
13
|
+
@target subTree: HTMLElement
|
14
|
+
@target iconPair: TreeViewIconPairElement
|
15
|
+
@target toggleButton: HTMLElement
|
16
|
+
@target expandedToggleIcon: HTMLElement
|
17
|
+
@target collapsedToggleIcon: HTMLElement
|
18
|
+
@target includeFragment: TreeViewIncludeFragmentElement
|
19
|
+
@target loadingIndicator: HTMLElement
|
20
|
+
@target loadingFailureMessage: HTMLElement
|
21
|
+
@target retryButton: HTMLButtonElement
|
22
|
+
|
23
|
+
expanded: boolean
|
24
|
+
loadingState: LoadingState
|
25
|
+
|
26
|
+
#abortController: AbortController
|
27
|
+
#activeElementIsLoader: boolean = false
|
28
|
+
|
29
|
+
connectedCallback() {
|
30
|
+
this.expanded = this.node.getAttribute('aria-expanded') === 'true'
|
31
|
+
this.loadingState = 'success'
|
32
|
+
|
33
|
+
observeMutationsUntilConditionMet(
|
34
|
+
this,
|
35
|
+
() => Boolean(this.node) && Boolean(this.subTree),
|
36
|
+
() => {
|
37
|
+
this.#update()
|
38
|
+
},
|
39
|
+
)
|
40
|
+
|
41
|
+
const {signal} = (this.#abortController = new AbortController())
|
42
|
+
this.addEventListener('click', this, {signal})
|
43
|
+
this.addEventListener('keydown', this, {signal})
|
44
|
+
|
45
|
+
observeMutationsUntilConditionMet(
|
46
|
+
this,
|
47
|
+
() => Boolean(this.includeFragment),
|
48
|
+
() => {
|
49
|
+
this.includeFragment.addEventListener('loadstart', this, {signal})
|
50
|
+
this.includeFragment.addEventListener('error', this, {signal})
|
51
|
+
this.includeFragment.addEventListener('include-fragment-replace', this, {signal})
|
52
|
+
this.includeFragment.addEventListener(
|
53
|
+
'include-fragment-replaced',
|
54
|
+
(e: Event) => {
|
55
|
+
this.#handleIncludeFragmentEvent(e)
|
56
|
+
},
|
57
|
+
{signal},
|
58
|
+
)
|
59
|
+
},
|
60
|
+
)
|
61
|
+
|
62
|
+
observeMutationsUntilConditionMet(
|
63
|
+
this,
|
64
|
+
() => Boolean(this.retryButton),
|
65
|
+
() => {
|
66
|
+
this.retryButton.addEventListener(
|
67
|
+
'click',
|
68
|
+
event => {
|
69
|
+
this.#handleRetryButtonEvent(event)
|
70
|
+
},
|
71
|
+
{signal},
|
72
|
+
)
|
73
|
+
},
|
74
|
+
)
|
75
|
+
|
76
|
+
const checkedMutationObserver = new MutationObserver(() => {
|
77
|
+
if (this.selectStrategy !== 'descendants') return
|
78
|
+
|
79
|
+
let checkType = 'unknown'
|
80
|
+
|
81
|
+
for (const node of this.eachDirectDescendantNode()) {
|
82
|
+
switch (`${checkType} ${node.getAttribute('aria-checked') || 'false'}`) {
|
83
|
+
case 'unknown mixed':
|
84
|
+
case 'false mixed':
|
85
|
+
case 'true mixed':
|
86
|
+
case 'false true':
|
87
|
+
case 'true false':
|
88
|
+
checkType = 'mixed'
|
89
|
+
break
|
90
|
+
|
91
|
+
case 'unknown false':
|
92
|
+
checkType = 'false'
|
93
|
+
break
|
94
|
+
|
95
|
+
case 'unknown true':
|
96
|
+
checkType = 'true'
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
if (checkType !== 'unknown' && this.node?.getAttribute('aria-checked') !== checkType) {
|
101
|
+
this.node?.setAttribute('aria-checked', checkType)
|
102
|
+
}
|
103
|
+
})
|
104
|
+
|
105
|
+
checkedMutationObserver.observe(this, {
|
106
|
+
childList: true,
|
107
|
+
subtree: true,
|
108
|
+
attributeFilter: ['aria-checked'],
|
109
|
+
})
|
110
|
+
}
|
111
|
+
|
112
|
+
get selectStrategy(): string {
|
113
|
+
return this.node.getAttribute('data-select-strategy') || 'descendants'
|
114
|
+
}
|
115
|
+
|
116
|
+
disconnectedCallback() {
|
117
|
+
this.#abortController.abort()
|
118
|
+
}
|
119
|
+
|
120
|
+
handleEvent(event: Event) {
|
121
|
+
const checkbox = (event.target as Element).closest('.TreeViewItemCheckbox')
|
122
|
+
|
123
|
+
if (checkbox && checkbox === this.#checkboxElement) {
|
124
|
+
this.#handleCheckboxEvent(event)
|
125
|
+
} else if (event.target === this.toggleButton) {
|
126
|
+
this.#handleToggleEvent(event)
|
127
|
+
} else if (event.target === this.includeFragment) {
|
128
|
+
this.#handleIncludeFragmentEvent(event)
|
129
|
+
} else if (event instanceof KeyboardEvent) {
|
130
|
+
this.#handleKeyboardEvent(event)
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
expand() {
|
135
|
+
const alreadyExpanded = this.expanded
|
136
|
+
|
137
|
+
this.expanded = true
|
138
|
+
this.#update()
|
139
|
+
|
140
|
+
if (!alreadyExpanded && this.treeView) {
|
141
|
+
this.treeView.dispatchEvent(
|
142
|
+
new CustomEvent('treeViewNodeExpanded', {
|
143
|
+
bubbles: true,
|
144
|
+
detail: this.treeView?.infoFromNode(this.node),
|
145
|
+
}),
|
146
|
+
)
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
collapse() {
|
151
|
+
const alreadyCollapsed = !this.expanded
|
152
|
+
|
153
|
+
this.expanded = false
|
154
|
+
this.#update()
|
155
|
+
|
156
|
+
if (!alreadyCollapsed && this.treeView) {
|
157
|
+
// Prevent issue where currently focusable node is stuck inside a collapsed
|
158
|
+
// sub-tree and no node in the entire tree can be focused
|
159
|
+
const previousNode = this.subTree.querySelector("[tabindex='0']")
|
160
|
+
previousNode?.setAttribute('tabindex', '-1')
|
161
|
+
this.node.setAttribute('tabindex', '0')
|
162
|
+
|
163
|
+
this.treeView.dispatchEvent(
|
164
|
+
new CustomEvent('treeViewNodeCollapsed', {
|
165
|
+
bubbles: true,
|
166
|
+
detail: this.treeView?.infoFromNode(this.node),
|
167
|
+
}),
|
168
|
+
)
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
toggle() {
|
173
|
+
if (this.expanded) {
|
174
|
+
this.collapse()
|
175
|
+
} else {
|
176
|
+
this.expand()
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
get nodes(): NodeListOf<Element> {
|
181
|
+
return this.querySelectorAll(':scope > [role=treeitem]')
|
182
|
+
}
|
183
|
+
|
184
|
+
*eachDirectDescendantNode(): Generator<Element> {
|
185
|
+
for (const leaf of this.subTree.querySelectorAll(':scope > [role=treeitem]')) {
|
186
|
+
yield leaf
|
187
|
+
}
|
188
|
+
|
189
|
+
for (const subTree of this.subTree.querySelectorAll(':scope > tree-view-sub-tree-node > [role=treeitem]')) {
|
190
|
+
yield subTree
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
*eachDescendantNode(): Generator<Element> {
|
195
|
+
for (const node of this.subTree.querySelectorAll('[role=treeitem]')) {
|
196
|
+
yield node
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
get isEmpty(): boolean {
|
201
|
+
return this.nodes.length === 0
|
202
|
+
}
|
203
|
+
|
204
|
+
get treeView(): TreeViewElement | null {
|
205
|
+
return this.closest('tree-view')
|
206
|
+
}
|
207
|
+
|
208
|
+
#handleToggleEvent(event: Event) {
|
209
|
+
if (event.type === 'click') {
|
210
|
+
this.toggle()
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
#handleIncludeFragmentEvent(event: Event) {
|
215
|
+
switch (event.type) {
|
216
|
+
// the request has started
|
217
|
+
case 'loadstart':
|
218
|
+
this.loadingState = 'loading'
|
219
|
+
this.#update()
|
220
|
+
break
|
221
|
+
|
222
|
+
// the request failed
|
223
|
+
case 'error':
|
224
|
+
this.loadingState = 'error'
|
225
|
+
this.#update()
|
226
|
+
break
|
227
|
+
|
228
|
+
// request succeeded but element has not yet been replaced
|
229
|
+
case 'include-fragment-replace':
|
230
|
+
this.loadingState = 'success'
|
231
|
+
this.#activeElementIsLoader = document.activeElement === this.loadingIndicator.closest('li')
|
232
|
+
this.#update()
|
233
|
+
break
|
234
|
+
|
235
|
+
case 'include-fragment-replaced':
|
236
|
+
if (this.#activeElementIsLoader) {
|
237
|
+
const firstItem = this.querySelector('[role=treeitem] [role=group] > :first-child') as HTMLElement | null
|
238
|
+
if (!firstItem) return
|
239
|
+
|
240
|
+
if (firstItem.tagName.toLowerCase() === 'tree-view-sub-tree-node') {
|
241
|
+
const firstChild = firstItem.querySelector('[role=treeitem]') as HTMLElement | null
|
242
|
+
firstChild?.focus()
|
243
|
+
} else {
|
244
|
+
firstItem?.focus()
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
this.#activeElementIsLoader = false
|
249
|
+
break
|
250
|
+
}
|
251
|
+
}
|
252
|
+
|
253
|
+
#handleRetryButtonEvent(event: Event) {
|
254
|
+
if (event.type === 'click') {
|
255
|
+
this.loadingState = 'loading'
|
256
|
+
this.#update()
|
257
|
+
|
258
|
+
this.includeFragment.refetch()
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
#handleKeyboardEvent(event: KeyboardEvent) {
|
263
|
+
const node = (event.target as HTMLElement).closest('[role=treeitem]')
|
264
|
+
if (!node || this.treeView?.getNodeType(node) !== 'sub-tree') {
|
265
|
+
return
|
266
|
+
}
|
267
|
+
|
268
|
+
switch (event.key) {
|
269
|
+
case 'Enter':
|
270
|
+
// eslint-disable-next-line no-restricted-syntax
|
271
|
+
event.stopPropagation()
|
272
|
+
this.toggle()
|
273
|
+
break
|
274
|
+
|
275
|
+
case 'ArrowRight':
|
276
|
+
// eslint-disable-next-line no-restricted-syntax
|
277
|
+
event.stopPropagation()
|
278
|
+
this.expand()
|
279
|
+
break
|
280
|
+
|
281
|
+
case 'ArrowLeft':
|
282
|
+
// eslint-disable-next-line no-restricted-syntax
|
283
|
+
event.stopPropagation()
|
284
|
+
this.collapse()
|
285
|
+
break
|
286
|
+
|
287
|
+
case ' ':
|
288
|
+
// eslint-disable-next-line no-restricted-syntax
|
289
|
+
event.stopPropagation()
|
290
|
+
event.preventDefault()
|
291
|
+
this.toggleChecked()
|
292
|
+
break
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
#handleCheckboxEvent(event: Event) {
|
297
|
+
if (event.type !== 'click') return
|
298
|
+
|
299
|
+
this.toggleChecked()
|
300
|
+
|
301
|
+
// prevent receiving this event twice
|
302
|
+
// eslint-disable-next-line no-restricted-syntax
|
303
|
+
event.stopPropagation()
|
304
|
+
}
|
305
|
+
|
306
|
+
toggleChecked() {
|
307
|
+
const checkValue = this.node.getAttribute('aria-checked') || 'false'
|
308
|
+
const newCheckValue = checkValue === 'false' ? 'true' : 'false'
|
309
|
+
const nodeInfos: TreeViewNodeInfo[] = []
|
310
|
+
const rootInfo = this.treeView?.infoFromNode(this.node, newCheckValue)
|
311
|
+
if (rootInfo) nodeInfos.push(rootInfo)
|
312
|
+
|
313
|
+
if (this.selectStrategy === 'descendants') {
|
314
|
+
for (const node of this.eachDescendantNode()) {
|
315
|
+
const info = this.treeView?.infoFromNode(node, newCheckValue)
|
316
|
+
if (info) nodeInfos.push(info)
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
320
|
+
const checkSuccess = this.dispatchEvent(
|
321
|
+
new CustomEvent('treeViewBeforeNodeChecked', {
|
322
|
+
bubbles: true,
|
323
|
+
cancelable: true,
|
324
|
+
detail: nodeInfos,
|
325
|
+
}),
|
326
|
+
)
|
327
|
+
|
328
|
+
if (!checkSuccess) return
|
329
|
+
|
330
|
+
for (const nodeInfo of nodeInfos) {
|
331
|
+
nodeInfo.node.setAttribute('aria-checked', newCheckValue)
|
332
|
+
}
|
333
|
+
|
334
|
+
this.dispatchEvent(
|
335
|
+
new CustomEvent('treeViewNodeChecked', {
|
336
|
+
bubbles: true,
|
337
|
+
cancelable: true,
|
338
|
+
detail: nodeInfos,
|
339
|
+
}),
|
340
|
+
)
|
341
|
+
}
|
342
|
+
|
343
|
+
#update() {
|
344
|
+
if (this.expanded) {
|
345
|
+
if (this.subTree) this.subTree.hidden = false
|
346
|
+
this.node.setAttribute('aria-expanded', 'true')
|
347
|
+
|
348
|
+
if (this.iconPair) {
|
349
|
+
this.iconPair.showExpanded()
|
350
|
+
}
|
351
|
+
|
352
|
+
if (this.expandedToggleIcon && this.collapsedToggleIcon) {
|
353
|
+
this.expandedToggleIcon.removeAttribute('hidden')
|
354
|
+
this.collapsedToggleIcon.setAttribute('hidden', 'hidden')
|
355
|
+
}
|
356
|
+
} else {
|
357
|
+
if (this.subTree) this.subTree.hidden = true
|
358
|
+
this.node.setAttribute('aria-expanded', 'false')
|
359
|
+
|
360
|
+
if (this.iconPair) {
|
361
|
+
this.iconPair.showCollapsed()
|
362
|
+
}
|
363
|
+
|
364
|
+
if (this.expandedToggleIcon && this.collapsedToggleIcon) {
|
365
|
+
this.expandedToggleIcon.setAttribute('hidden', 'hidden')
|
366
|
+
this.collapsedToggleIcon.removeAttribute('hidden')
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
switch (this.loadingState) {
|
371
|
+
case 'loading':
|
372
|
+
if (this.loadingFailureMessage) this.loadingFailureMessage.hidden = true
|
373
|
+
if (this.loadingIndicator) this.loadingIndicator.hidden = false
|
374
|
+
break
|
375
|
+
|
376
|
+
case 'error':
|
377
|
+
if (this.loadingIndicator) this.loadingIndicator.hidden = true
|
378
|
+
if (this.loadingFailureMessage) this.loadingFailureMessage.hidden = false
|
379
|
+
break
|
380
|
+
|
381
|
+
// success/init case
|
382
|
+
default:
|
383
|
+
if (this.loadingIndicator) this.loadingIndicator.hidden = true
|
384
|
+
if (this.loadingFailureMessage) this.loadingFailureMessage.hidden = true
|
385
|
+
}
|
386
|
+
}
|
387
|
+
|
388
|
+
get #checkboxElement(): HTMLElement | null {
|
389
|
+
return this.querySelector('.TreeViewItemCheckbox')
|
390
|
+
}
|
391
|
+
}
|
392
|
+
|
393
|
+
if (!window.customElements.get('tree-view-sub-tree-node')) {
|
394
|
+
window.TreeViewSubTreeNodeElement = TreeViewSubTreeNodeElement
|
395
|
+
window.customElements.define('tree-view-sub-tree-node', TreeViewSubTreeNodeElement)
|
396
|
+
}
|
397
|
+
|
398
|
+
declare global {
|
399
|
+
interface Window {
|
400
|
+
TreeViewSubTreeNodeElement: typeof TreeViewSubTreeNodeElement
|
401
|
+
}
|
402
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%= render(
|
2
|
+
Primer::BaseComponent.new(
|
3
|
+
tag: :div,
|
4
|
+
classes: "sr-only TreeViewVisuallyHidden",
|
5
|
+
aria: { hidden: true },
|
6
|
+
id: @id,
|
7
|
+
test_selector: "tree-view-visual-label"
|
8
|
+
)
|
9
|
+
) do %>
|
10
|
+
<%= @label %>
|
11
|
+
<% end %>
|
12
|
+
<div class="TreeViewItemVisual" aria-hidden="true">
|
13
|
+
<%= render(@visual) %>
|
14
|
+
</div>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
class TreeView
|
6
|
+
# A `TreeView` visual, either leading or trailing.
|
7
|
+
#
|
8
|
+
# This component is part of the <%= link_to_component(Primer::OpenProject::TreeView) %> component and should
|
9
|
+
# not be used directly.
|
10
|
+
class Visual < Primer::Component
|
11
|
+
# @param id [String] This visual's HTML ID.
|
12
|
+
# @param visual [ViewComponent::Base] A renderable component like an instance of <%= link_to_component(Primer::Beta::Octicon) %> to render as the visual.
|
13
|
+
# @param label [String] Text describing this visual that will be visible only to screen readers.
|
14
|
+
def initialize(id:, visual:, label: nil)
|
15
|
+
@id = id
|
16
|
+
@visual = visual
|
17
|
+
@label = label
|
18
|
+
end
|
19
|
+
|
20
|
+
def render_in(_view_context, &block)
|
21
|
+
block&.call(@visual)
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
.TreeViewRootUlStyles{list-style:none;margin:0;padding:0}.TreeViewRootUlStyles .TreeViewItem{outline:none}:is(.TreeViewRootUlStyles .TreeViewItem):focus-visible>div{box-shadow:var(--boxShadow-thick) var(--fgColor-accent)}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItem):focus-visible>div{outline:2px solid HighlightText;outline-offset:-2}}[data-has-leading-action]:is(.TreeViewRootUlStyles .TreeViewItem){--has-leading-action:1}.TreeViewRootUlStyles .TreeViewItemContainer{--level:1;--toggle-width:1rem;--min-item-height:2rem;border-radius:var(--borderRadius-medium);color:var(--fgColor-default);cursor:pointer;display:grid;font-size:var(--text-body-size-medium);grid-template-areas:"spacer leadingAction toggle content";grid-template-columns:var(--spacer-width) var(--leading-action-width) var(--toggle-width) 1fr;position:relative;width:100%;--leading-action-width:calc(var(--has-leading-action, 0)*1.5rem);--spacer-width:calc((var(--level) - 1)*(var(--toggle-width)/2))}:is(.TreeViewRootUlStyles .TreeViewItemContainer):hover{background-color:var(--control-transparent-bgColor-hover)}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItemContainer):hover{outline:2px solid #0000;outline-offset:-2px}}@media (pointer:coarse){.TreeViewRootUlStyles .TreeViewItemContainer{--toggle-width:1.5rem;--min-item-height:2.75rem}}:is(.TreeViewRootUlStyles .TreeViewItemContainer):has(.TreeViewFailureMessage):hover{background-color:initial;cursor:default}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItemContainer):has(.TreeViewFailureMessage):hover{outline:none}}.TreeViewRootUlStyles:where([data-omit-spacer=true]) .TreeViewItemContainer{grid-template-columns:0 0 0 1fr}.TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer{background-color:var(--control-transparent-bgColor-selected)}:is(.TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer):after{background-color:var(--fgColor-accent);border-radius:var(--borderRadius-medium);content:"";height:1.5rem;left:calc(var(--base-size-8)*-1);position:absolute;top:calc(50% - var(--base-size-12));width:.25rem}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer):after{background-color:HighlightText}}[aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox{background:var(--control-checked-bgColor-rest);border-color:var(--control-checked-borderColor-rest);transition:background-color,border-color 80ms cubic-bezier(.32,0,.67,0) 0s}:is([aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox):before{animation:checkmarkIn 80ms cubic-bezier(.65,0,.35,1) 80ms forwards;transition:visibility 0s linear 0s;visibility:visible}[aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox{background:var(--control-checked-bgColor-rest);border-color:var(--control-checked-borderColor-rest);transition:background-color,border-color 80ms cubic-bezier(.32,0,.67,0) 0s}:is([aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox):before{animation:checkmarkIn 80ms cubic-bezier(.65,0,.35,1) 80ms forwards;clip-path:none;mask-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMCIgaGVpZ2h0PSIyIiBmaWxsPSJub25lIiB2aWV3Qm94PSIwIDAgMTAgMiI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMCAxYTEgMSAwIDAgMSAxLTFoOGExIDEgMCAxIDEgMCAySDFhMSAxIDAgMCAxLTEtMSIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+");visibility:visible}.TreeViewRootUlStyles .TreeViewItemToggle{align-items:flex-start;color:var(--fgColor-muted);display:flex;grid-area:toggle;height:100%;justify-content:center;padding-top:calc(var(--min-item-height)/2 - var(--base-size-12)/2)}.TreeViewRootUlStyles .TreeViewItemToggleHover:hover{background-color:var(--control-transparent-bgColor-hover)}.TreeViewRootUlStyles .TreeViewItemToggleEnd{border-bottom-left-radius:var(--borderRadius-medium);border-top-left-radius:var(--borderRadius-medium)}.TreeViewRootUlStyles .TreeViewItemContent{display:flex;gap:var(--stack-gap-condensed);grid-area:content;height:100%;line-height:var(--custom-line-height,var(--text-body-lineHeight-medium,1.4285));padding:0 var(--base-size-8);padding-bottom:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2);padding-top:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2)}:is(.TreeViewRootUlStyles .TreeViewItemContent) .TreeViewItemCheckbox{background-color:initial;border:none;border-radius:var(--borderRadius-medium);color:var(--control-fgColor-rest);position:relative;text-align:left;touch-action:manipulation;transition:background 33.333ms linear;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.TreeViewRootUlStyles .TreeViewItemContentText{flex:1 1 auto;width:0}.TreeViewRootUlStyles:where([data-truncate-text=true]) .TreeViewItemContentText{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.TreeViewRootUlStyles:where([data-truncate-text=false]) .TreeViewItemContentText{word-break:break-word}.TreeViewRootUlStyles .TreeViewItemVisual{align-items:center;color:var(--fgColor-muted);display:flex;height:var(--custom-line-height,1.3rem)}.TreeViewRootUlStyles .TreeViewItemLeadingAction{color:var(--fgColor-muted);display:flex;grid-area:leadingAction}:is(.TreeViewRootUlStyles .TreeViewItemLeadingAction)>button{flex-shrink:1}.TreeViewRootUlStyles .TreeViewItemLevelLine{border-color:var(--borderColor-muted);border-right:var(--borderWidth-thin) solid;height:100%;width:100%}@media (hover:hover){.TreeViewRootUlStyles .TreeViewItemLevelLine{border-color:#0000}.TreeViewRootUlStyles:focus-within .TreeViewItemLevelLine,.TreeViewRootUlStyles:hover .TreeViewItemLevelLine{border-color:var(--borderColor-muted)}}.TreeViewRootUlStyles .TreeViewVisuallyHidden{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border-width:0;white-space:nowrap}.TreeViewSkeletonItemContainerStyle{align-items:center;column-gap:.5rem;display:flex;height:2rem}@media (pointer:coarse){.TreeViewSkeletonItemContainerStyle{height:2.75rem}}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+1){--tree-item-loading-width:67%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+2){--tree-item-loading-width:47%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+3){--tree-item-loading-width:73%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+4){--tree-item-loading-width:64%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+5){--tree-item-loading-width:50%}.TreeItemSkeletonTextStyles{width:var(--tree-item-loading-width,67%)}.TreeViewFailureMessage{align-items:center;display:grid;gap:.5rem;grid-template-columns:auto 1fr;width:100%}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
{
|
2
|
+
"name": "open_project/tree_view",
|
3
|
+
"selectors": [
|
4
|
+
".TreeViewRootUlStyles",
|
5
|
+
".TreeViewRootUlStyles .TreeViewItem",
|
6
|
+
":is(.TreeViewRootUlStyles .TreeViewItem):focus-visible>div",
|
7
|
+
"[data-has-leading-action]:is(.TreeViewRootUlStyles .TreeViewItem)",
|
8
|
+
".TreeViewRootUlStyles .TreeViewItemContainer",
|
9
|
+
":is(.TreeViewRootUlStyles .TreeViewItemContainer):hover",
|
10
|
+
":is(.TreeViewRootUlStyles .TreeViewItemContainer):has(.TreeViewFailureMessage):hover",
|
11
|
+
".TreeViewRootUlStyles:where([data-omit-spacer=true]) .TreeViewItemContainer",
|
12
|
+
".TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer",
|
13
|
+
":is(.TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer):after",
|
14
|
+
"[aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox",
|
15
|
+
":is([aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox):before",
|
16
|
+
"[aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox",
|
17
|
+
":is([aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox):before",
|
18
|
+
".TreeViewRootUlStyles .TreeViewItemToggle",
|
19
|
+
".TreeViewRootUlStyles .TreeViewItemToggleHover:hover",
|
20
|
+
".TreeViewRootUlStyles .TreeViewItemToggleEnd",
|
21
|
+
".TreeViewRootUlStyles .TreeViewItemContent",
|
22
|
+
":is(.TreeViewRootUlStyles .TreeViewItemContent) .TreeViewItemCheckbox",
|
23
|
+
".TreeViewRootUlStyles .TreeViewItemContentText",
|
24
|
+
".TreeViewRootUlStyles:where([data-truncate-text=true]) .TreeViewItemContentText",
|
25
|
+
".TreeViewRootUlStyles:where([data-truncate-text=false]) .TreeViewItemContentText",
|
26
|
+
".TreeViewRootUlStyles .TreeViewItemVisual",
|
27
|
+
".TreeViewRootUlStyles .TreeViewItemLeadingAction",
|
28
|
+
":is(.TreeViewRootUlStyles .TreeViewItemLeadingAction)>button",
|
29
|
+
".TreeViewRootUlStyles .TreeViewItemLevelLine",
|
30
|
+
".TreeViewRootUlStyles:focus-within .TreeViewItemLevelLine",
|
31
|
+
".TreeViewRootUlStyles:hover .TreeViewItemLevelLine",
|
32
|
+
".TreeViewRootUlStyles .TreeViewVisuallyHidden",
|
33
|
+
".TreeViewSkeletonItemContainerStyle",
|
34
|
+
".TreeViewSkeletonItemContainerStyle:nth-of-type(5n+1)",
|
35
|
+
".TreeViewSkeletonItemContainerStyle:nth-of-type(5n+2)",
|
36
|
+
".TreeViewSkeletonItemContainerStyle:nth-of-type(5n+3)",
|
37
|
+
".TreeViewSkeletonItemContainerStyle:nth-of-type(5n+4)",
|
38
|
+
".TreeViewSkeletonItemContainerStyle:nth-of-type(5n+5)",
|
39
|
+
".TreeItemSkeletonTextStyles",
|
40
|
+
".TreeViewFailureMessage"
|
41
|
+
]
|
42
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["tree_view.pcss"],"names":[],"mappings":"AAEA,sBAGE,eAAgB,CADhB,QAAS,CADT,SAgRF,CA/PE,oCACE,YAeF,CAbE,2DACE,uDAOF,CALE,8BAHF,2DAII,+BAAgC,CAEhC,iBAEJ,CADE,CAGF,kEACE,sBACF,CAGF,6CACE,SAAU,CACV,mBAAoB,CACpB,sBAAuB,CAQvB,wCAAyC,CAFzC,4BAA6B,CAC7B,cAAe,CAJf,YAAa,CAEb,sCAAuC,CAKvC,yDAA0D,CAD1D,6FAA8F,CAP9F,iBAAkB,CAElB,UAAW,CAQX,gEAAmE,CACnE,+DAwBF,CAtBE,wDACE,yDAMF,CAJE,8BAHF,wDAII,uBAA8B,CAC9B,mBAEJ,CADE,CAGF,wBA3BF,6CA4BI,qBAAsB,CACtB,yBAWJ,CAVE,CAEA,qFAEE,wBAA6B,CAD7B,cAMF,CAHE,8BAJF,qFAKI,YAEJ,CADE,CAIJ,4EACE,+BACF,CAEA,8EACE,4DAwBF,CApBE,yFAaE,sCAAuC,CACvC,wCAAyC,CARzC,UAAW,CADX,aAAc,CAFd,gCAAmC,CAFnC,iBAAkB,CAClB,mCAAoC,CAEpC,YAeF,CAHE,8BAhBF,yFAiBI,8BAEJ,CADE,CAQA,yGACE,8CAA+C,CAC/C,oDAAqD,CACrD,0EAQF,CALE,qHAGE,kEAAwE,CADxE,kCAAmC,CADnC,kBAGF,CAMF,0GACE,8CAA+C,CAC/C,oDAAqD,CACrD,0EASF,CANE,sHAGE,kEAAwE,CACxE,cAAe,CAFf,wUAAia,CADja,kBAIF,CAKN,0CAWE,sBAAuB,CAHvB,0BAA2B,CAP3B,YAAa,CAQb,gBAAiB,CAPjB,WAAY,CAQZ,sBAAuB,CAHvB,kEAKF,CAEA,qDACE,yDACF,CAEA,6CAEE,oDAAqD,CADrD,iDAEF,CAEA,2CACE,YAAa,CAWb,8BAA+B,CAD/B,iBAAkB,CATlB,WAAY,CAQZ,+EAAkF,CAPlF,4BAA6B,CAM7B,mFAAsF,CAFtF,gFAmBF,CAZE,sEAKE,wBAA6B,CAC7B,WAAY,CACZ,wCAAyC,CALzC,iCAAkC,CADlC,iBAAkB,CAElB,eAAgB,CAMhB,yBAA0B,CAD1B,qCAAsC,CAJtC,wBAAiB,CAAjB,gBAAiB,CAMjB,uCACF,CAGF,+CACE,aAAc,CACd,OACF,CAEA,gFACE,eAAgB,CAChB,sBAAuB,CACvB,kBACF,CAEA,iFAEE,qBACF,CAEA,0CAOE,kBAAmB,CADnB,0BAA2B,CAL3B,YAAa,CAIb,uCAGF,CAEA,iDAEE,0BAA2B,CAD3B,YAAa,CAEb,uBAKF,CAHE,6DACE,aACF,CAGF,6CAQE,qCAAsC,CACtC,0CAA2C,CAP3C,WAAY,CADZ,UASF,CAQA,qBACE,6CACE,kBACF,CAEA,6GAEE,qCACF,CACF,CAEA,8CAGE,UAAW,CAGX,WAAY,CACZ,eAAgB,CAHhB,SAAU,CAHV,iBAAkB,CAClB,SAAU,CAMV,kBAAsB,CAEtB,cAAe,CADf,kBAEF,CAGF,oCAEE,kBAAmB,CACnB,gBAAkB,CAFlB,YAAa,CAGb,WAyBF,CAvBE,wBANF,oCAOI,cAsBJ,CArBE,CAEA,sDACE,6BACF,CAEA,sDACE,6BACF,CAEA,sDACE,6BACF,CAEA,sDACE,6BACF,CAEA,sDACE,6BACF,CAGF,4BACE,wCACF,CAEA,wBAKE,kBAAmB,CAJnB,YAAa,CAEb,SAAW,CADX,8BAA+B,CAE/B,UAEF","file":"tree_view.css","sourcesContent":["/* stylelint-disable selector-max-type -- Copied from primer/react */\n\n.TreeViewRootUlStyles {\n padding: 0;\n margin: 0;\n list-style: none;\n\n /*\n * WARNING: This is a performance optimization.\n *\n * We define styles for the tree items at the root level of the tree\n * to avoid recomputing the styles for each item when the tree updates.\n * We're sacrificing maintainability for performance because TreeView\n * needs to be performant enough to handle large trees (thousands of items).\n *\n * This is intended to be a temporary solution until we can improve the\n * performance of our styling patterns.\n *\n * Do NOT copy this pattern without understanding the tradeoffs.\n */\n & .TreeViewItem {\n outline: none;\n\n &:focus-visible > div {\n box-shadow: var(--boxShadow-thick) var(--fgColor-accent);\n\n @media (forced-colors: active) {\n outline: 2px solid HighlightText;\n /* stylelint-disable-next-line declaration-property-value-no-unknown -- Copied from primer/react */\n outline-offset: -2;\n }\n }\n\n &[data-has-leading-action] {\n --has-leading-action: 1;\n }\n }\n\n & .TreeViewItemContainer {\n --level: 1;\n --toggle-width: 1rem;\n --min-item-height: 2rem;\n\n position: relative;\n display: grid;\n width: 100%;\n font-size: var(--text-body-size-medium);\n color: var(--fgColor-default);\n cursor: pointer;\n border-radius: var(--borderRadius-medium);\n grid-template-columns: var(--spacer-width) var(--leading-action-width) var(--toggle-width) 1fr;\n grid-template-areas: 'spacer leadingAction toggle content';\n\n --leading-action-width: calc(var(--has-leading-action, 0) * 1.5rem);\n --spacer-width: calc(calc(var(--level) - 1) * (var(--toggle-width) / 2));\n\n &:hover {\n background-color: var(--control-transparent-bgColor-hover);\n\n @media (forced-colors: active) {\n outline: 2px solid transparent;\n outline-offset: -2px;\n }\n }\n\n @media (pointer: coarse) {\n --toggle-width: 1.5rem;\n --min-item-height: 2.75rem;\n }\n\n &:has(.TreeViewFailureMessage):hover {\n cursor: default;\n background-color: transparent;\n\n @media (forced-colors: active) {\n outline: none;\n }\n }\n }\n\n &:where([data-omit-spacer='true']) .TreeViewItemContainer {\n grid-template-columns: 0 0 0 1fr;\n }\n\n & .TreeViewItem[aria-current='true'] > .TreeViewItemContainer {\n background-color: var(--control-transparent-bgColor-selected);\n\n /* Current item indicator */\n /* stylelint-disable-next-line selector-max-specificity -- Copied from primer/react */\n &::after {\n position: absolute;\n top: calc(50% - var(--base-size-12));\n left: calc(-1 * var(--base-size-8));\n width: 0.25rem;\n height: 1.5rem;\n content: '';\n\n /*\n * Use fgColor accent for consistency across all themes. Using the \"correct\" variable,\n * --bgColor-accent-emphasis, causes vrt failures for dark high contrast mode\n */\n /* stylelint-disable-next-line primer/colors */\n background-color: var(--fgColor-accent);\n border-radius: var(--borderRadius-medium);\n\n @media (forced-colors: active) {\n background-color: HighlightText;\n }\n }\n }\n\n /* stylelint-disable-next-line no-duplicate-selectors -- Copied from primer/react */\n & .TreeViewItem {\n &[aria-checked='true'] {\n /* stylelint-disable-next-line selector-max-compound-selectors, selector-max-specificity -- Copied from primer/react */\n & > .TreeViewItemContainer .FormControl-checkbox {\n background: var(--control-checked-bgColor-rest);\n border-color: var(--control-checked-borderColor-rest);\n transition: background-color, border-color 80ms cubic-bezier(0.32, 0, 0.67, 0) 0ms; /* unchecked -> checked */\n\n /* stylelint-disable-next-line max-nesting-depth, selector-max-compound-selectors, selector-max-specificity -- Copied from primer/react */\n &::before {\n visibility: visible;\n transition: visibility 0s linear 0s;\n animation: checkmarkIn 80ms cubic-bezier(0.65, 0, 0.35, 1) forwards 80ms;\n }\n }\n }\n\n &[aria-checked='mixed'] {\n /* stylelint-disable-next-line selector-max-compound-selectors, selector-max-specificity -- Copied from primer/react */\n & > .TreeViewItemContainer .FormControl-checkbox {\n background: var(--control-checked-bgColor-rest);\n border-color: var(--control-checked-borderColor-rest);\n transition: background-color, border-color 80ms cubic-bezier(0.32, 0, 0.67, 0) 0ms; /* unchecked -> checked */\n\n /* stylelint-disable-next-line max-nesting-depth, selector-max-compound-selectors, selector-max-specificity -- Copied from primer/react */\n &::before {\n visibility: visible;\n mask-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMiIgdmlld0JveD0iMCAwIDEwIDIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMCAxQzAgMC40NDc3MTUgMC40NDc3MTUgMCAxIDBIOUM5LjU1MjI5IDAgMTAgMC40NDc3MTUgMTAgMUMxMCAxLjU1MjI4IDkuNTUyMjkgMiA5IDJIMUMwLjQ0NzcxNSAyIDAgMS41NTIyOCAwIDFaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K');\n animation: checkmarkIn 80ms cubic-bezier(0.65, 0, 0.35, 1) forwards 80ms;\n clip-path: none;\n }\n }\n }\n }\n\n & .TreeViewItemToggle {\n display: flex;\n height: 100%;\n\n /* The toggle should appear vertically centered for single-line items, but remain at the top for items that wrap\n across more lines. */\n /* stylelint-disable-next-line primer/spacing */\n padding-top: calc(var(--min-item-height) / 2 - var(--base-size-12) / 2);\n color: var(--fgColor-muted);\n grid-area: toggle;\n justify-content: center;\n align-items: flex-start;\n }\n\n & .TreeViewItemToggleHover:hover {\n background-color: var(--control-transparent-bgColor-hover);\n }\n\n & .TreeViewItemToggleEnd {\n border-top-left-radius: var(--borderRadius-medium);\n border-bottom-left-radius: var(--borderRadius-medium);\n }\n\n & .TreeViewItemContent {\n display: flex;\n height: 100%;\n padding: 0 var(--base-size-8);\n\n /* The dynamic top and bottom padding to maintain the minimum item height for single line items */\n /* stylelint-disable-next-line primer/spacing */\n padding-top: calc((var(--min-item-height) - var(--custom-line-height, 1.3rem)) / 2);\n /* stylelint-disable-next-line primer/spacing */\n padding-bottom: calc((var(--min-item-height) - var(--custom-line-height, 1.3rem)) / 2);\n line-height: var(--custom-line-height, var(--text-body-lineHeight-medium, 1.4285));\n grid-area: content;\n gap: var(--stack-gap-condensed);\n\n & .TreeViewItemCheckbox {\n position: relative;\n color: var(--control-fgColor-rest);\n text-align: left;\n user-select: none;\n background-color: transparent;\n border: none;\n border-radius: var(--borderRadius-medium);\n transition: background 33.333ms linear;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n & .TreeViewItemContentText {\n flex: 1 1 auto;\n width: 0;\n }\n\n &:where([data-truncate-text='true']) .TreeViewItemContentText {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &:where([data-truncate-text='false']) .TreeViewItemContentText {\n /* stylelint-disable-next-line declaration-property-value-keyword-no-deprecated -- Copied from primer/react */\n word-break: break-word;\n }\n\n & .TreeViewItemVisual {\n display: flex;\n\n /* The visual icons should appear vertically centered for single-line items, but remain at the top for items that wrap\n across more lines. */\n height: var(--custom-line-height, 1.3rem);\n color: var(--fgColor-muted);\n align-items: center;\n }\n\n & .TreeViewItemLeadingAction {\n display: flex;\n color: var(--fgColor-muted);\n grid-area: leadingAction;\n\n & > button {\n flex-shrink: 1;\n }\n }\n\n & .TreeViewItemLevelLine {\n width: 100%;\n height: 100%;\n\n /*\n * On devices without hover, the nesting indicator lines\n * appear at all times.\n */\n border-color: var(--borderColor-muted);\n border-right: var(--borderWidth-thin) solid;\n }\n\n /*\n * On devices with :hover support, the nesting indicator lines\n * fade in when the user mouses over the entire component,\n * or when there's focus inside the component. This makes\n * sure the component remains simple when not in use.\n */\n @media (hover: hover) {\n .TreeViewItemLevelLine {\n border-color: transparent;\n }\n\n &:hover .TreeViewItemLevelLine,\n &:focus-within .TreeViewItemLevelLine {\n border-color: var(--borderColor-muted);\n }\n }\n\n & .TreeViewVisuallyHidden {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n /* stylelint-disable-next-line primer/spacing */\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n }\n}\n\n.TreeViewSkeletonItemContainerStyle {\n display: flex;\n align-items: center;\n column-gap: 0.5rem;\n height: 2rem;\n\n @media (pointer: coarse) {\n height: 2.75rem;\n }\n\n &:nth-of-type(5n + 1) {\n --tree-item-loading-width: 67%;\n }\n\n &:nth-of-type(5n + 2) {\n --tree-item-loading-width: 47%;\n }\n\n &:nth-of-type(5n + 3) {\n --tree-item-loading-width: 73%;\n }\n\n &:nth-of-type(5n + 4) {\n --tree-item-loading-width: 64%;\n }\n\n &:nth-of-type(5n + 5) {\n --tree-item-loading-width: 50%;\n }\n}\n\n.TreeItemSkeletonTextStyles {\n width: var(--tree-item-loading-width, 67%);\n}\n\n.TreeViewFailureMessage {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 0.5rem;\n width: 100%;\n align-items: center;\n}\n"]}
|