@openproject/primer-view-components 0.70.5 → 0.71.0-rc.5dc134b91

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 (24) hide show
  1. package/app/assets/javascripts/components/primer/alpha/segmented_control.d.ts +2 -2
  2. package/app/assets/javascripts/components/primer/open_project/filterable_tree_view.d.ts +29 -0
  3. package/app/assets/javascripts/components/primer/open_project/tree_view/tree_view.d.ts +11 -1
  4. package/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +5 -1
  5. package/app/assets/javascripts/components/primer/primer.d.ts +1 -0
  6. package/app/assets/javascripts/primer_view_components.js +1 -1
  7. package/app/assets/javascripts/primer_view_components.js.map +1 -1
  8. package/app/assets/styles/primer_view_components.css +1 -1
  9. package/app/assets/styles/primer_view_components.css.map +1 -1
  10. package/app/components/primer/alpha/segmented_control.d.ts +2 -2
  11. package/app/components/primer/alpha/segmented_control.js +12 -0
  12. package/app/components/primer/alpha/stack.css +1 -1
  13. package/app/components/primer/alpha/stack.css.json +5 -1
  14. package/app/components/primer/open_project/filterable_tree_view.d.ts +29 -0
  15. package/app/components/primer/open_project/filterable_tree_view.js +409 -0
  16. package/app/components/primer/open_project/tree_view/tree_view.d.ts +11 -1
  17. package/app/components/primer/open_project/tree_view/tree_view.js +120 -20
  18. package/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +5 -1
  19. package/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.js +27 -4
  20. package/app/components/primer/open_project/tree_view.css +1 -1
  21. package/app/components/primer/open_project/tree_view.css.json +9 -0
  22. package/app/components/primer/primer.d.ts +1 -0
  23. package/app/components/primer/primer.js +1 -0
  24. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
- declare class SegmentedControlElement extends HTMLElement {
1
+ export declare class SegmentedControlElement extends HTMLElement {
2
2
  #private;
3
3
  items: HTMLElement[];
4
4
  connectedCallback(): void;
5
5
  select(event: Event): void;
6
+ get current(): HTMLElement | null;
6
7
  }
7
8
  declare global {
8
9
  interface Window {
9
10
  SegmentedControlElement: typeof SegmentedControlElement;
10
11
  }
11
12
  }
12
- export {};
@@ -27,6 +27,17 @@ let SegmentedControlElement = class SegmentedControlElement extends HTMLElement
27
27
  }
28
28
  button.closest('li.SegmentedControl-item')?.classList.add('SegmentedControl-item--selected');
29
29
  button.setAttribute('aria-current', 'true');
30
+ this.dispatchEvent(new CustomEvent('itemActivated', {
31
+ bubbles: true,
32
+ detail: {
33
+ item: button,
34
+ checked: false,
35
+ value: button.querySelector('.Button-label')?.textContent,
36
+ },
37
+ }));
38
+ }
39
+ get current() {
40
+ return this.querySelector('[aria-current=true]');
30
41
  }
31
42
  };
32
43
  _SegmentedControlElement_instances = new WeakSet();
@@ -41,6 +52,7 @@ __decorate([
41
52
  SegmentedControlElement = __decorate([
42
53
  controller
43
54
  ], SegmentedControlElement);
55
+ export { SegmentedControlElement };
44
56
  if (!window.customElements.get('segmented-control')) {
45
57
  window.SegmentedControlElement = SegmentedControlElement;
46
58
  window.customElements.define('segmented-control', SegmentedControlElement);
@@ -1 +1 @@
1
- .Stack{align-content:flex-start;align-items:stretch;display:flex;flex-flow:column;gap:var(--stack-gap,var(--stack-gap-normal,1rem))}.Stack[data-padding-narrow=none],.Stack[data-padding=none]{padding:0}.Stack[data-padding-narrow=condensed],.Stack[data-padding=condensed]{padding:var(--stack-padding-condensed,8px)}.Stack[data-padding-narrow=normal],.Stack[data-padding=normal]{padding:var(--stack-padding-normal,16px)}.Stack[data-padding-narrow=spacious],.Stack[data-padding=spacious]{padding:var(--stack-padding-spacious,24px)}.Stack[data-direction-narrow=horizontal],.Stack[data-direction=horizontal]{flex-flow:row}.Stack[data-direction-narrow=vertical],.Stack[data-direction=vertical]{flex-flow:column}.Stack[data-gap-narrow=none],.Stack[data-gap=none]{--stack-gap:var(--stack-gap-none,0)}.Stack[data-gap-narrow=condensed],.Stack[data-gap=condensed]{--stack-gap:var(--stack-gap-condensed,0.5rem)}.Stack[data-gap-narrow=normal],.Stack[data-gap=normal]{--stack-gap:var(--stack-gap-normal,1rem)}.Stack[data-gap-narrow=spacious],.Stack[data-gap=spacious]{--stack-gap:var(--stack-gap-spacious,1.5rem)}.Stack[data-align-narrow=start],.Stack[data-align=start]{align-items:flex-start}.Stack[data-align-narrow=center],.Stack[data-align=center]{align-items:center}.Stack[data-align-narrow=end],.Stack[data-align=end]{align-items:flex-end}.Stack[data-align-narrow=baseline],.Stack[data-align=baseline]{align-items:baseline}.Stack[data-justify-narrow=start],.Stack[data-justify=start]{justify-content:flex-start}.Stack[data-justify-narrow=center],.Stack[data-justify=center]{justify-content:center}.Stack[data-justify-narrow=end],.Stack[data-justify=end]{justify-content:flex-end}.Stack[data-justify-narrow=space-between],.Stack[data-justify=space-between]{justify-content:space-between}.Stack[data-justify-narrow=space-evenly],.Stack[data-justify=space-evenly]{justify-content:space-evenly}.Stack[data-wrap-narrow=wrap],.Stack[data-wrap=wrap]{flex-wrap:wrap}.Stack[data-wrap-narrow=nowrap],.Stack[data-wrap=nowrap]{flex-wrap:nowrap}@media (min-width:48rem){.Stack[data-padding-regular=none]{padding:0}.Stack[data-padding-regular=condensed]{padding:var(--stack-padding-condensed,8px)}.Stack[data-padding-regular=normal]{padding:var(--stack-padding-normal,16px)}.Stack[data-padding-regular=spacious]{padding:var(--stack-padding-spacious,24px)}.Stack[data-direction-regular=horizontal]{flex-flow:row}.Stack[data-direction-regular=vertical]{flex-flow:column}.Stack[data-gap-regular=none]{--stack-gap:var(--stack-gap-none,0)}.Stack[data-gap-regular=condensed]{--stack-gap:var(--stack-gap-condensed,0.5rem)}.Stack[data-gap-regular=normal]{--stack-gap:var(--stack-gap-normal,1rem)}.Stack[data-gap-regular=spacious]{--stack-gap:var(--stack-gap-spacious,1.5rem)}.Stack[data-align-regular=start]{align-items:flex-start}.Stack[data-align-regular=center]{align-items:center}.Stack[data-align-regular=end]{align-items:flex-end}.Stack[data-align-regular=baseline]{align-items:baseline}.Stack[data-justify-regular=start]{justify-content:flex-start}.Stack[data-justify-regular=center]{justify-content:center}.Stack[data-justify-regular=end]{justify-content:flex-end}.Stack[data-justify-regular=space-between]{justify-content:space-between}.Stack[data-justify-regular=space-evenly]{justify-content:space-evenly}.Stack[data-wrap-regular=wrap]{flex-wrap:wrap}.Stack[data-wrap-regular=nowrap]{flex-wrap:nowrap}}@media (min-width:87.5rem){.Stack[data-padding-wide=none]{padding:0}.Stack[data-padding-wide=condensed]{padding:var(--stack-padding-condensed,8px)}.Stack[data-padding-wide=normal]{padding:var(--stack-padding-normal,16px)}.Stack[data-padding-wide=spacious]{padding:var(--stack-padding-spacious,24px)}.Stack[data-direction-wide=horizontal]{flex-flow:row}.Stack[data-direction-wide=vertical]{flex-flow:column}.Stack[data-gap-wide=none]{--stack-gap:var(--stack-gap-none,0)}.Stack[data-gap-wide=condensed]{--stack-gap:var(--stack-gap-condensed,0.5rem)}.Stack[data-gap-wide=normal]{--stack-gap:var(--stack-gap-normal,1rem)}.Stack[data-gap-wide=spacious]{--stack-gap:var(--stack-gap-spacious,1.5rem)}.Stack[data-align-wide=start]{align-items:flex-start}.Stack[data-align-wide=center]{align-items:center}.Stack[data-align-wide=end]{align-items:flex-end}.Stack[data-align-wide=baseline]{align-items:baseline}.Stack[data-justify-wide=start]{justify-content:flex-start}.Stack[data-justify-wide=center]{justify-content:center}.Stack[data-justify-wide=end]{justify-content:flex-end}.Stack[data-justify-wide=space-between]{justify-content:space-between}.Stack[data-justify-wide=space-evenly]{justify-content:space-evenly}.Stack[data-wrap-wide=wrap]{flex-wrap:wrap}.Stack[data-wrap-wide=nowrap]{flex-wrap:nowrap}}
1
+ .Stack{align-content:flex-start;align-items:stretch;display:flex;flex-flow:column;gap:var(--stack-gap,var(--stack-gap-normal,1rem))}.Stack[data-padding-narrow=none],.Stack[data-padding=none]{padding:0}.Stack[data-padding-narrow=condensed],.Stack[data-padding=condensed]{padding:var(--stack-padding-condensed,8px)}.Stack[data-padding-narrow=normal],.Stack[data-padding=normal]{padding:var(--stack-padding-normal,16px)}.Stack[data-padding-narrow=spacious],.Stack[data-padding=spacious]{padding:var(--stack-padding-spacious,24px)}.Stack[data-direction-narrow=horizontal],.Stack[data-direction=horizontal]{flex-flow:row}.Stack[data-direction-narrow=vertical],.Stack[data-direction=vertical]{flex-flow:column}.Stack[data-gap-narrow=none],.Stack[data-gap=none]{--stack-gap:var(--stack-gap-none,0)}.Stack[data-gap-narrow=condensed],.Stack[data-gap=condensed]{--stack-gap:var(--stack-gap-condensed,0.5rem)}.Stack[data-gap-narrow=normal],.Stack[data-gap=normal]{--stack-gap:var(--stack-gap-normal,1rem)}.Stack[data-gap-narrow=spacious],.Stack[data-gap=spacious]{--stack-gap:var(--stack-gap-spacious,1.5rem)}.Stack[data-align-narrow=start],.Stack[data-align=start]{align-items:flex-start}.Stack[data-align-narrow=center],.Stack[data-align=center]{align-items:center}.Stack[data-align-narrow=end],.Stack[data-align=end]{align-items:flex-end}.Stack[data-align-narrow=baseline],.Stack[data-align=baseline]{align-items:baseline}.Stack[data-justify-narrow=start],.Stack[data-justify=start]{justify-content:flex-start}.Stack[data-justify-narrow=center],.Stack[data-justify=center]{justify-content:center}.Stack[data-justify-narrow=end],.Stack[data-justify=end]{justify-content:flex-end}.Stack[data-justify-narrow=space-between],.Stack[data-justify=space-between]{justify-content:space-between}.Stack[data-justify-narrow=space-evenly],.Stack[data-justify=space-evenly]{justify-content:space-evenly}.Stack[data-wrap-narrow=wrap],.Stack[data-wrap=wrap]{flex-wrap:wrap}.Stack[data-wrap-narrow=nowrap],.Stack[data-wrap=nowrap]{flex-wrap:nowrap}.Stack[data-wrap-narrow=reverse],.Stack[data-wrap=reverse]{flex-wrap:wrap-reverse}@media (min-width:48rem){.Stack[data-padding-regular=none]{padding:0}.Stack[data-padding-regular=condensed]{padding:var(--stack-padding-condensed,8px)}.Stack[data-padding-regular=normal]{padding:var(--stack-padding-normal,16px)}.Stack[data-padding-regular=spacious]{padding:var(--stack-padding-spacious,24px)}.Stack[data-direction-regular=horizontal]{flex-flow:row}.Stack[data-direction-regular=vertical]{flex-flow:column}.Stack[data-gap-regular=none]{--stack-gap:var(--stack-gap-none,0)}.Stack[data-gap-regular=condensed]{--stack-gap:var(--stack-gap-condensed,0.5rem)}.Stack[data-gap-regular=normal]{--stack-gap:var(--stack-gap-normal,1rem)}.Stack[data-gap-regular=spacious]{--stack-gap:var(--stack-gap-spacious,1.5rem)}.Stack[data-align-regular=start]{align-items:flex-start}.Stack[data-align-regular=center]{align-items:center}.Stack[data-align-regular=end]{align-items:flex-end}.Stack[data-align-regular=baseline]{align-items:baseline}.Stack[data-justify-regular=start]{justify-content:flex-start}.Stack[data-justify-regular=center]{justify-content:center}.Stack[data-justify-regular=end]{justify-content:flex-end}.Stack[data-justify-regular=space-between]{justify-content:space-between}.Stack[data-justify-regular=space-evenly]{justify-content:space-evenly}.Stack[data-wrap-regular=wrap]{flex-wrap:wrap}.Stack[data-wrap-regular=nowrap]{flex-wrap:nowrap}.Stack[data-wrap-regular=reverse]{flex-wrap:wrap-reverse}}@media (min-width:87.5rem){.Stack[data-padding-wide=none]{padding:0}.Stack[data-padding-wide=condensed]{padding:var(--stack-padding-condensed,8px)}.Stack[data-padding-wide=normal]{padding:var(--stack-padding-normal,16px)}.Stack[data-padding-wide=spacious]{padding:var(--stack-padding-spacious,24px)}.Stack[data-direction-wide=horizontal]{flex-flow:row}.Stack[data-direction-wide=vertical]{flex-flow:column}.Stack[data-gap-wide=none]{--stack-gap:var(--stack-gap-none,0)}.Stack[data-gap-wide=condensed]{--stack-gap:var(--stack-gap-condensed,0.5rem)}.Stack[data-gap-wide=normal]{--stack-gap:var(--stack-gap-normal,1rem)}.Stack[data-gap-wide=spacious]{--stack-gap:var(--stack-gap-spacious,1.5rem)}.Stack[data-align-wide=start]{align-items:flex-start}.Stack[data-align-wide=center]{align-items:center}.Stack[data-align-wide=end]{align-items:flex-end}.Stack[data-align-wide=baseline]{align-items:baseline}.Stack[data-justify-wide=start]{justify-content:flex-start}.Stack[data-justify-wide=center]{justify-content:center}.Stack[data-justify-wide=end]{justify-content:flex-end}.Stack[data-justify-wide=space-between]{justify-content:space-between}.Stack[data-justify-wide=space-evenly]{justify-content:space-evenly}.Stack[data-wrap-wide=wrap]{flex-wrap:wrap}.Stack[data-wrap-wide=nowrap]{flex-wrap:nowrap}.Stack[data-wrap-wide=reverse]{flex-wrap:wrap-reverse}}
@@ -44,6 +44,8 @@
44
44
  ".Stack[data-wrap=wrap]",
45
45
  ".Stack[data-wrap-narrow=nowrap]",
46
46
  ".Stack[data-wrap=nowrap]",
47
+ ".Stack[data-wrap-narrow=reverse]",
48
+ ".Stack[data-wrap=reverse]",
47
49
  ".Stack[data-padding-regular=none]",
48
50
  ".Stack[data-padding-regular=condensed]",
49
51
  ".Stack[data-padding-regular=normal]",
@@ -65,6 +67,7 @@
65
67
  ".Stack[data-justify-regular=space-evenly]",
66
68
  ".Stack[data-wrap-regular=wrap]",
67
69
  ".Stack[data-wrap-regular=nowrap]",
70
+ ".Stack[data-wrap-regular=reverse]",
68
71
  ".Stack[data-padding-wide=none]",
69
72
  ".Stack[data-padding-wide=condensed]",
70
73
  ".Stack[data-padding-wide=normal]",
@@ -85,6 +88,7 @@
85
88
  ".Stack[data-justify-wide=space-between]",
86
89
  ".Stack[data-justify-wide=space-evenly]",
87
90
  ".Stack[data-wrap-wide=wrap]",
88
- ".Stack[data-wrap-wide=nowrap]"
91
+ ".Stack[data-wrap-wide=nowrap]",
92
+ ".Stack[data-wrap-wide=reverse]"
89
93
  ]
90
94
  }
@@ -0,0 +1,29 @@
1
+ import { SegmentedControlElement } from '../alpha/segmented_control';
2
+ import { TreeViewElement } from './tree_view/tree_view';
3
+ import { TreeViewSubTreeNodeElement } from './tree_view/tree_view_sub_tree_node_element';
4
+ export type FilterFn = (node: HTMLElement, query: string, filterMode?: string) => Range[] | null;
5
+ export declare class FilterableTreeViewElement extends HTMLElement {
6
+ #private;
7
+ filterInput: HTMLInputElement;
8
+ filterModeControlList: HTMLElement;
9
+ treeViewList: HTMLElement;
10
+ noResultsMessage: HTMLElement;
11
+ includeSubItemsCheckBox: HTMLInputElement;
12
+ connectedCallback(): void;
13
+ disconnectedCallback(): void;
14
+ handleEvent(event: Event): void;
15
+ get filterModeControl(): SegmentedControlElement | null;
16
+ get treeView(): TreeViewElement | null;
17
+ set filterFn(newFn: FilterFn);
18
+ get filterFn(): FilterFn;
19
+ defaultFilterFn(node: HTMLElement, query: string, filterMode?: string): Range[] | null;
20
+ get filterMode(): string | null;
21
+ get queryString(): string;
22
+ eachDescendantDepthFirst(node: HTMLElement, level: number, ancestry: TreeViewSubTreeNodeElement[]): Generator<[NodeListOf<HTMLElement>, TreeViewSubTreeNodeElement[]]>;
23
+ eachShallowestCheckedSubTree(root: TreeViewSubTreeNodeElement): Generator<TreeViewSubTreeNodeElement>;
24
+ }
25
+ declare global {
26
+ interface Window {
27
+ FilterableTreeViewElement: typeof FilterableTreeViewElement;
28
+ }
29
+ }
@@ -0,0 +1,409 @@
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 _FilterableTreeViewElement_instances, _FilterableTreeViewElement_filterFn, _FilterableTreeViewElement_abortController, _FilterableTreeViewElement_stateMap, _FilterableTreeViewElement_handleTreeViewEvent, _FilterableTreeViewElement_handleTreeViewNodeChecked, _FilterableTreeViewElement_restoreNodeState, _FilterableTreeViewElement_handleFilterModeEvent, _FilterableTreeViewElement_handleFilterInputEvent, _FilterableTreeViewElement_handleIncludeSubItemsCheckBoxEvent, _FilterableTreeViewElement_includeSubItems, _FilterableTreeViewElement_includeSubItemsUnder, _FilterableTreeViewElement_restoreAllNodeStates, _FilterableTreeViewElement_applyFilterOptions, _FilterableTreeViewElement_applyHighlights, _FilterableTreeViewElement_applyManualHighlights, _FilterableTreeViewElement_removeHighlights;
19
+ import { controller, target } from '@github/catalyst';
20
+ import { TreeViewElement } from './tree_view/tree_view';
21
+ import { TreeViewSubTreeNodeElement } from './tree_view/tree_view_sub_tree_node_element';
22
+ let FilterableTreeViewElement = class FilterableTreeViewElement extends HTMLElement {
23
+ constructor() {
24
+ super(...arguments);
25
+ _FilterableTreeViewElement_instances.add(this);
26
+ _FilterableTreeViewElement_filterFn.set(this, void 0);
27
+ _FilterableTreeViewElement_abortController.set(this, void 0);
28
+ _FilterableTreeViewElement_stateMap.set(this, new Map());
29
+ }
30
+ connectedCallback() {
31
+ const { signal } = (__classPrivateFieldSet(this, _FilterableTreeViewElement_abortController, new AbortController(), "f"));
32
+ this.addEventListener('treeViewNodeChecked', this, { signal });
33
+ this.addEventListener('itemActivated', this, { signal });
34
+ this.addEventListener('input', this, { signal });
35
+ }
36
+ disconnectedCallback() {
37
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_abortController, "f").abort();
38
+ }
39
+ handleEvent(event) {
40
+ if (event.target === this.filterModeControl) {
41
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_handleFilterModeEvent).call(this, event);
42
+ }
43
+ else if (event.target === this.filterInput) {
44
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_handleFilterInputEvent).call(this, event);
45
+ }
46
+ else if (event.target === this.includeSubItemsCheckBox) {
47
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_handleIncludeSubItemsCheckBoxEvent).call(this, event);
48
+ }
49
+ else if (event.target instanceof TreeViewElement || event.target instanceof TreeViewSubTreeNodeElement) {
50
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_handleTreeViewEvent).call(this, event);
51
+ }
52
+ }
53
+ get filterModeControl() {
54
+ return this.filterModeControlList.closest('segmented-control');
55
+ }
56
+ get treeView() {
57
+ return this.treeViewList.closest('tree-view');
58
+ }
59
+ set filterFn(newFn) {
60
+ __classPrivateFieldSet(this, _FilterableTreeViewElement_filterFn, newFn, "f");
61
+ }
62
+ get filterFn() {
63
+ if (__classPrivateFieldGet(this, _FilterableTreeViewElement_filterFn, "f")) {
64
+ return __classPrivateFieldGet(this, _FilterableTreeViewElement_filterFn, "f");
65
+ }
66
+ else {
67
+ return this.defaultFilterFn;
68
+ }
69
+ }
70
+ defaultFilterFn(node, query, filterMode) {
71
+ const ranges = [];
72
+ if (query.length > 0) {
73
+ const lowercaseQuery = query.toLowerCase();
74
+ const treeWalker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT);
75
+ let currentNode = treeWalker.nextNode();
76
+ while (currentNode) {
77
+ const lowercaseNodeText = currentNode.textContent?.toLocaleLowerCase() || '';
78
+ let startIndex = 0;
79
+ while (startIndex < lowercaseNodeText.length) {
80
+ const index = lowercaseNodeText.indexOf(lowercaseQuery, startIndex);
81
+ if (index === -1)
82
+ break;
83
+ const range = new Range();
84
+ range.setStart(currentNode, index);
85
+ range.setEnd(currentNode, index + lowercaseQuery.length);
86
+ ranges.push(range);
87
+ startIndex = index + lowercaseQuery.length;
88
+ }
89
+ currentNode = treeWalker.nextNode();
90
+ }
91
+ }
92
+ if (ranges.length === 0 && query.length > 0) {
93
+ return null;
94
+ }
95
+ switch (filterMode) {
96
+ case 'selected': {
97
+ // Only match nodes that have been checked
98
+ if (this.treeView?.getNodeCheckedValue(node) !== 'false') {
99
+ return ranges;
100
+ }
101
+ break;
102
+ }
103
+ case 'all': {
104
+ return ranges;
105
+ }
106
+ }
107
+ return null;
108
+ }
109
+ get filterMode() {
110
+ const current = this.filterModeControl?.current;
111
+ if (current) {
112
+ return current.getAttribute('data-name');
113
+ }
114
+ else {
115
+ return null;
116
+ }
117
+ }
118
+ get queryString() {
119
+ return this.filterInput.value;
120
+ }
121
+ // Iterates over the nodes in the given sub-tree in depth-first order, yielding a list of leaf nodes
122
+ // and an array of ancestor nodes. It uses the aria-level information attached to each node to determine
123
+ // the next level of the tree to visit.
124
+ *eachDescendantDepthFirst(node, level, ancestry) {
125
+ for (const subTreeItem of node.querySelectorAll(`[role=treeitem][data-node-type='sub-tree'][aria-level='${level}']`)) {
126
+ const subTree = subTreeItem.closest('tree-view-sub-tree-node');
127
+ yield* this.eachDescendantDepthFirst(subTree, level + 1, [...ancestry, subTree]);
128
+ }
129
+ const leafNodes = node.querySelectorAll(`[role=treeitem][data-node-type='leaf'][aria-level='${level}']`);
130
+ yield [leafNodes, ancestry];
131
+ }
132
+ // Yields only the shallowest (i.e. lowest depth) sub-tree nodes that are checked, i.e. does not
133
+ // visit a sub-tree's children if that sub-tree is checked.
134
+ *eachShallowestCheckedSubTree(root) {
135
+ if (this.treeView?.getNodeCheckedValue(root.node) === 'true') {
136
+ yield root;
137
+ return; // do not descend further
138
+ }
139
+ for (const childSubTree of root.eachDirectDescendantSubTreeNode()) {
140
+ yield* this.eachShallowestCheckedSubTree(childSubTree);
141
+ }
142
+ }
143
+ };
144
+ _FilterableTreeViewElement_filterFn = new WeakMap();
145
+ _FilterableTreeViewElement_abortController = new WeakMap();
146
+ _FilterableTreeViewElement_stateMap = new WeakMap();
147
+ _FilterableTreeViewElement_instances = new WeakSet();
148
+ _FilterableTreeViewElement_handleTreeViewEvent = function _FilterableTreeViewElement_handleTreeViewEvent(origEvent) {
149
+ const event = origEvent;
150
+ // NOTE: This event only fires if someone actually activates the check mark, i.e. does not fire
151
+ // when calling this.treeView.setNodeCheckedValue.
152
+ switch (origEvent.type) {
153
+ case 'treeViewNodeChecked':
154
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_handleTreeViewNodeChecked).call(this, event);
155
+ break;
156
+ }
157
+ };
158
+ _FilterableTreeViewElement_handleTreeViewNodeChecked = function _FilterableTreeViewElement_handleTreeViewNodeChecked(event) {
159
+ if (!this.treeView)
160
+ return;
161
+ if (!this.includeSubItemsCheckBox.checked)
162
+ return;
163
+ // Although multiple nodes may have been checked (eg. if the TreeView is in descendants mode),
164
+ // the one that actually received the click, i.e. the local root the user checked, is the first
165
+ // entry. We only care about sub-tree nodes because checking them affects all leaf nodes, so
166
+ // there's no need to check or uncheck individual leaves.
167
+ const nodeInfo = event.detail[0];
168
+ if (this.treeView.getNodeType(nodeInfo.node) !== 'sub-tree')
169
+ return;
170
+ const subTree = nodeInfo.node.closest('tree-view-sub-tree-node');
171
+ if (nodeInfo.checkedValue === 'false') {
172
+ // If the sub-tree has been unchecked, restore whatever state they were in before. We don't
173
+ // need to explicitly enable the sub-tree because restoring will handle setting the enabled
174
+ // or disabled state per-node.
175
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_restoreNodeState).call(this, subTree);
176
+ }
177
+ else {
178
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_includeSubItemsUnder).call(this, subTree);
179
+ }
180
+ };
181
+ _FilterableTreeViewElement_restoreNodeState = function _FilterableTreeViewElement_restoreNodeState(subTree) {
182
+ if (!this.treeView)
183
+ return;
184
+ if (!__classPrivateFieldGet(this, _FilterableTreeViewElement_stateMap, "f").has(subTree))
185
+ return;
186
+ const descendantStates = __classPrivateFieldGet(this, _FilterableTreeViewElement_stateMap, "f").get(subTree);
187
+ for (const [element, state] of descendantStates.entries()) {
188
+ let node = element;
189
+ if (element instanceof TreeViewSubTreeNodeElement) {
190
+ node = element.node;
191
+ }
192
+ this.treeView.setNodeCheckedValue(node, state.checked ? 'true' : 'false');
193
+ this.treeView.setNodeDisabledValue(node, state.disabled);
194
+ }
195
+ // once node state has been restored, there's no reason to keep it around - it will be saved
196
+ // again if this sub-tree gets checked
197
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_stateMap, "f").delete(subTree);
198
+ };
199
+ _FilterableTreeViewElement_handleFilterModeEvent = function _FilterableTreeViewElement_handleFilterModeEvent(event) {
200
+ if (event.type !== 'itemActivated')
201
+ return;
202
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_applyFilterOptions).call(this);
203
+ };
204
+ _FilterableTreeViewElement_handleFilterInputEvent = function _FilterableTreeViewElement_handleFilterInputEvent(event) {
205
+ if (event.type !== 'input')
206
+ return;
207
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_applyFilterOptions).call(this);
208
+ };
209
+ _FilterableTreeViewElement_handleIncludeSubItemsCheckBoxEvent = function _FilterableTreeViewElement_handleIncludeSubItemsCheckBoxEvent(event) {
210
+ if (!this.treeView)
211
+ return;
212
+ if (event.type !== 'input')
213
+ return;
214
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_applyFilterOptions).call(this);
215
+ if (this.includeSubItemsCheckBox.checked) {
216
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_includeSubItems).call(this);
217
+ }
218
+ else {
219
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_restoreAllNodeStates).call(this);
220
+ }
221
+ };
222
+ _FilterableTreeViewElement_includeSubItems = function _FilterableTreeViewElement_includeSubItems() {
223
+ if (!this.treeView)
224
+ return;
225
+ for (const subTree of this.treeView.rootSubTreeNodes()) {
226
+ for (const checkedSubTree of this.eachShallowestCheckedSubTree(subTree)) {
227
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_includeSubItemsUnder).call(this, checkedSubTree);
228
+ }
229
+ }
230
+ };
231
+ _FilterableTreeViewElement_includeSubItemsUnder = function _FilterableTreeViewElement_includeSubItemsUnder(subTree) {
232
+ if (!this.treeView)
233
+ return;
234
+ const descendantStates = new Map();
235
+ for (const node of subTree.eachDescendantNode()) {
236
+ descendantStates.set(node, {
237
+ checked: this.treeView.getNodeCheckedValue(node) === 'true',
238
+ disabled: this.treeView.getNodeDisabledValue(node),
239
+ });
240
+ this.treeView.setNodeCheckedValue(node, 'true');
241
+ this.treeView.setNodeDisabledValue(node, true);
242
+ }
243
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_stateMap, "f").set(subTree, descendantStates);
244
+ };
245
+ _FilterableTreeViewElement_restoreAllNodeStates = function _FilterableTreeViewElement_restoreAllNodeStates() {
246
+ for (const subTree of __classPrivateFieldGet(this, _FilterableTreeViewElement_stateMap, "f").keys()) {
247
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_restoreNodeState).call(this, subTree);
248
+ }
249
+ };
250
+ _FilterableTreeViewElement_applyFilterOptions = function _FilterableTreeViewElement_applyFilterOptions() {
251
+ if (!this.treeView)
252
+ return;
253
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_removeHighlights).call(this);
254
+ const query = this.queryString;
255
+ const mode = this.filterMode || undefined;
256
+ const generation = window.crypto.randomUUID();
257
+ const filterRangesCache = new Map();
258
+ const expandAncestors = (...ancestors) => {
259
+ for (const ancestor of ancestors) {
260
+ ancestor.expand();
261
+ ancestor.removeAttribute('hidden');
262
+ ancestor.setAttribute('data-generation', generation);
263
+ if (cachedFilterFn(ancestor.node, query, mode)) {
264
+ ancestor.node.removeAttribute('aria-disabled');
265
+ }
266
+ else {
267
+ ancestor.node.setAttribute('aria-disabled', 'true');
268
+ }
269
+ }
270
+ };
271
+ // This function is called in the loop below for both leaf nodes and sub-tree nodes to determine
272
+ // if they match, and subsequently whether or not to hide them. However, it serves a secondary purpose
273
+ // as well in that it remembers the range information returned by the filter function so it can be
274
+ // used to highlight matching ranges later.
275
+ const cachedFilterFn = (node, queryStr, filterMode) => {
276
+ if (!filterRangesCache.has(node)) {
277
+ filterRangesCache.set(node, this.filterFn(node, queryStr, filterMode));
278
+ }
279
+ return filterRangesCache.get(node) !== null;
280
+ };
281
+ /* We iterate depth-first here in order to be able to examine the most deeply nested leaf nodes
282
+ * before their parents. This enables us to easily hide the parent if none of its children match.
283
+ * To handle expanding and collapsing ancestors, the algorithm iterates over the provided ancestor
284
+ * chain, expanding "upwards" to the root.
285
+ *
286
+ * Using this technique does mean it's possible to iterate over the same ancestor multiple times.
287
+ * For example, consider two nodes that share the same ancestor. Node A contains matching children,
288
+ * but node B does not. The algorithm below will visit node A first and expand it and all its
289
+ * ancestors. Next, the algorithm will visit node B and collapse all its ancestors. To avoid this,
290
+ * the algorithm attaches a random "generation ID" to each node visited. If the generation ID
291
+ * matches when visiting a particular node, we know that node has already been visited and should
292
+ * not be hidden or collapsed.
293
+ */
294
+ for (const [leafNodes, ancestors] of this.eachDescendantDepthFirst(this.treeViewList, 1, [])) {
295
+ const parent = ancestors[ancestors.length - 1];
296
+ let atLeastOneLeafMatches = false;
297
+ for (const leafNode of leafNodes) {
298
+ if (cachedFilterFn(leafNode, query, mode)) {
299
+ leafNode.closest('li')?.removeAttribute('hidden');
300
+ atLeastOneLeafMatches = true;
301
+ }
302
+ else {
303
+ leafNode.closest('li')?.setAttribute('hidden', 'hidden');
304
+ }
305
+ }
306
+ if (atLeastOneLeafMatches) {
307
+ expandAncestors(...ancestors);
308
+ }
309
+ else {
310
+ if (parent) {
311
+ if (cachedFilterFn(parent.node, query, mode)) {
312
+ // sub-tree matched, so expand ancestors
313
+ expandAncestors(...ancestors);
314
+ }
315
+ else {
316
+ // this node has already been marked by the current generation and is therefore
317
+ // a shared ancestor - don't collapse or hide it
318
+ if (parent.getAttribute('data-generation') !== generation) {
319
+ parent.collapse();
320
+ parent.setAttribute('hidden', 'hidden');
321
+ }
322
+ }
323
+ }
324
+ }
325
+ }
326
+ // convert range map into a 1-dimensional array with no nulls so it can be given to
327
+ // #applyHighlights (and therefore CSS.highlights.set) more easily
328
+ const allRanges = Array.from(filterRangesCache.values())
329
+ .flat()
330
+ .filter(r => r !== null);
331
+ if (allRanges.length === 0 && query.length > 0) {
332
+ this.treeViewList.setAttribute('hidden', 'hidden');
333
+ this.noResultsMessage.removeAttribute('hidden');
334
+ }
335
+ else {
336
+ this.treeViewList.removeAttribute('hidden');
337
+ this.noResultsMessage.setAttribute('hidden', 'hidden');
338
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_applyHighlights).call(this, allRanges);
339
+ }
340
+ };
341
+ _FilterableTreeViewElement_applyHighlights = function _FilterableTreeViewElement_applyHighlights(ranges) {
342
+ // Attempt to use the new-ish custom highlight API:
343
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API
344
+ if (CSS.highlights) {
345
+ CSS.highlights.set('primer-filterable-tree-view-search-results', new Highlight(...ranges));
346
+ }
347
+ else {
348
+ __classPrivateFieldGet(this, _FilterableTreeViewElement_instances, "m", _FilterableTreeViewElement_applyManualHighlights).call(this, ranges);
349
+ }
350
+ };
351
+ _FilterableTreeViewElement_applyManualHighlights = function _FilterableTreeViewElement_applyManualHighlights(ranges) {
352
+ const textNode = ranges[0].startContainer;
353
+ const parent = textNode.parentNode;
354
+ const originalText = textNode.textContent;
355
+ const fragments = [];
356
+ let lastIndex = 0;
357
+ for (const { startOffset, endOffset } of ranges) {
358
+ // text before the highlight
359
+ if (startOffset > lastIndex) {
360
+ fragments.push(document.createTextNode(originalText.slice(lastIndex, startOffset)));
361
+ }
362
+ // highlighted text
363
+ const mark = document.createElement('mark');
364
+ mark.textContent = originalText.slice(startOffset, endOffset);
365
+ fragments.push(mark);
366
+ lastIndex = endOffset;
367
+ }
368
+ // remaining text after the last highlight
369
+ if (lastIndex < originalText.length) {
370
+ fragments.push(document.createTextNode(originalText.slice(lastIndex)));
371
+ }
372
+ // replace original text node with our text + <mark> elements
373
+ for (const frag of fragments.reverse()) {
374
+ parent.insertBefore(frag, textNode.nextSibling);
375
+ }
376
+ parent.removeChild(textNode);
377
+ };
378
+ _FilterableTreeViewElement_removeHighlights = function _FilterableTreeViewElement_removeHighlights() {
379
+ // quick-and-dirty way of ignoring any existing <mark> elements and restoring
380
+ // the original text
381
+ for (const mark of this.querySelectorAll('mark')) {
382
+ if (!mark.parentElement)
383
+ continue;
384
+ mark.parentElement.replaceChildren(mark.parentElement.textContent);
385
+ }
386
+ };
387
+ __decorate([
388
+ target
389
+ ], FilterableTreeViewElement.prototype, "filterInput", void 0);
390
+ __decorate([
391
+ target
392
+ ], FilterableTreeViewElement.prototype, "filterModeControlList", void 0);
393
+ __decorate([
394
+ target
395
+ ], FilterableTreeViewElement.prototype, "treeViewList", void 0);
396
+ __decorate([
397
+ target
398
+ ], FilterableTreeViewElement.prototype, "noResultsMessage", void 0);
399
+ __decorate([
400
+ target
401
+ ], FilterableTreeViewElement.prototype, "includeSubItemsCheckBox", void 0);
402
+ FilterableTreeViewElement = __decorate([
403
+ controller
404
+ ], FilterableTreeViewElement);
405
+ export { FilterableTreeViewElement };
406
+ if (!window.customElements.get('filterable-tree-view')) {
407
+ window.FilterableTreeViewElement = FilterableTreeViewElement;
408
+ window.customElements.define('filterable-tree-view', FilterableTreeViewElement);
409
+ }
@@ -1,10 +1,15 @@
1
- import { TreeViewSubTreeNodeElement } from './tree_view_sub_tree_node_element';
1
+ import { SelectStrategy, TreeViewSubTreeNodeElement } from './tree_view_sub_tree_node_element';
2
2
  import type { TreeViewNodeType, TreeViewCheckedValue, TreeViewNodeInfo } from '../../shared_events';
3
3
  export declare class TreeViewElement extends HTMLElement {
4
4
  #private;
5
+ formInputContainer: HTMLElement;
6
+ formInputPrototype: HTMLInputElement;
5
7
  connectedCallback(): void;
8
+ rootLeafNodes(): NodeListOf<HTMLElement>;
9
+ rootSubTreeNodes(): NodeListOf<TreeViewSubTreeNodeElement>;
6
10
  disconnectedCallback(): void;
7
11
  handleEvent(event: Event): void;
12
+ getFormInputValueForNode(node: Element): string | null;
8
13
  getNodePath(node: Element): string[];
9
14
  getNodeType(node: Element): TreeViewNodeType | null;
10
15
  markCurrentAtPath(path: string[]): void;
@@ -16,13 +21,18 @@ export declare class TreeViewElement extends HTMLElement {
16
21
  uncheckAtPath(path: string[]): void;
17
22
  toggleCheckedAtPath(path: string[]): void;
18
23
  checkedValueAtPath(path: string[]): TreeViewCheckedValue;
24
+ disabledValueAtPath(path: string[]): boolean;
19
25
  nodeAtPath(path: string[], selector?: string): Element | null;
20
26
  subTreeAtPath(path: string[]): TreeViewSubTreeNodeElement | null;
21
27
  leafAtPath(path: string[]): HTMLLIElement | null;
28
+ setNodeCheckedValue(node: Element, value: TreeViewCheckedValue): void;
22
29
  getNodeCheckedValue(node: Element): TreeViewCheckedValue;
30
+ getNodeDisabledValue(node: Element): boolean;
31
+ setNodeDisabledValue(node: Element, disabled: boolean): void;
23
32
  nodeHasCheckBox(node: Element): boolean;
24
33
  nodeHasNativeAction(node: Element): boolean;
25
34
  expandAncestorsForNode(node: HTMLElement): void;
35
+ changeSelectStrategy(newStrategy: SelectStrategy): void;
26
36
  infoFromNode(node: Element, newCheckedValue?: TreeViewCheckedValue): TreeViewNodeInfo | null;
27
37
  }
28
38
  declare global {