Skip to content

Commit e8ea83b

Browse files
committed
refactor: Simplify iterNodes implementation
Moved the filter logic from the JS loop into the native TreeWalker implementation. Aside from a potential performance boost, the code is easier to grok. Dropped support for digging into open shadow roots which was a workaround for igc-radios created inside another ShadowDOM boundry. Radios will now try to resolve their siblings based on their rootNode scope instead of the main document root.
1 parent 063b693 commit e8ea83b

File tree

3 files changed

+40
-34
lines changed

3 files changed

+40
-34
lines changed

src/components/common/mixins/combo-box.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,20 @@ export abstract class IgcBaseComboBoxLikeComponent extends LitElement {
115115
}
116116

117117
export function getItems<T extends HTMLElement>(root: Node, tagName: string) {
118-
return iterNodes<T>(root, 'SHOW_ELEMENT', (item) => item.matches(tagName));
118+
return iterNodes<T>(root, {
119+
show: 'SHOW_ELEMENT',
120+
filter: (item) => item.matches(tagName),
121+
});
119122
}
120123

121124
export function getActiveItems<T extends HTMLElement & { disabled: boolean }>(
122125
root: Node,
123126
tagName: string
124127
) {
125-
return iterNodes<T>(
126-
root,
127-
'SHOW_ELEMENT',
128-
(item) => item.matches(tagName) && !item.disabled
129-
);
128+
return iterNodes<T>(root, {
129+
show: 'SHOW_ELEMENT',
130+
filter: (item) => item.matches(tagName) && !item.disabled,
131+
});
130132
}
131133

132134
export function getNextActiveItem<

src/components/common/util.ts

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,36 +110,40 @@ export function isDefined<T = unknown>(value: T) {
110110
return value !== undefined;
111111
}
112112

113-
export function* iterNodes<T = Node>(
113+
export type IterNodesOptions<T = Node> = {
114+
show?: keyof typeof NodeFilter;
115+
filter?: (node: T) => boolean;
116+
};
117+
118+
function createNodeFilter<T extends Node>(predicate: (node: T) => boolean) {
119+
return {
120+
acceptNode: (node: T): number =>
121+
!predicate || predicate(node)
122+
? NodeFilter.FILTER_ACCEPT
123+
: NodeFilter.FILTER_SKIP,
124+
};
125+
}
126+
127+
export function* iterNodes<T extends Node>(
114128
root: Node,
115-
whatToShow?: keyof typeof NodeFilter,
116-
filter?: (node: T) => boolean
129+
options?: IterNodesOptions<T>
117130
): Generator<T> {
118131
if (!isDefined(globalThis.document)) {
119132
return;
120133
}
121134

122-
const iter = globalThis.document.createTreeWalker(
123-
root,
124-
NodeFilter[whatToShow ?? 'SHOW_ALL']
125-
);
126-
127-
let node = iter.nextNode() as T;
135+
const whatToShow = options?.show
136+
? NodeFilter[options.show]
137+
: NodeFilter.SHOW_ALL;
128138

129-
while (node) {
130-
if (filter) {
131-
if (filter(node)) {
132-
yield node;
133-
}
134-
} else {
135-
yield node;
136-
}
139+
const nodeFilter = options?.filter
140+
? createNodeFilter(options.filter)
141+
: undefined;
137142

138-
if (isElement(node) && node.shadowRoot && node.shadowRoot.mode === 'open') {
139-
yield* iterNodes(node.shadowRoot, whatToShow, filter);
140-
}
143+
const treeWalker = document.createTreeWalker(root, whatToShow, nodeFilter);
141144

142-
node = iter.nextNode() as T;
145+
while (treeWalker.nextNode()) {
146+
yield treeWalker.currentNode as T;
143147
}
144148
}
145149

src/components/radio/utils.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { iterNodes } from '../common/util.js';
1+
import { type IterNodesOptions, getRoot, iterNodes } from '../common/util.js';
22
import type IgcRadioComponent from './radio.js';
33

44
type RadioQueryResult = {
@@ -36,13 +36,13 @@ export function getGroup(member: IgcRadioComponent) {
3636
return result;
3737
}
3838

39-
const iterator = iterNodes<IgcRadioComponent>(
40-
globalThis.document.documentElement,
41-
'SHOW_ELEMENT',
42-
(radio) => radio.matches(member.tagName) && radio.name === member.name
43-
);
39+
const options: IterNodesOptions<IgcRadioComponent> = {
40+
show: 'SHOW_ELEMENT',
41+
filter: (radio) =>
42+
radio.matches(member.tagName) && radio.name === member.name,
43+
};
4444

45-
for (const each of iterator) {
45+
for (const each of iterNodes<IgcRadioComponent>(getRoot(member), options)) {
4646
result.radios.push(each);
4747

4848
if (!each.disabled) {

0 commit comments

Comments
 (0)