Skip to content

Commit e219d44

Browse files
committed
refactor: leverage native iterators and improve type inference
- This PR cleans up several utility functions in the codebase by replacing hand-rolled implementations with native ECMAScript iterator helpers. - Improved type inference in the `findElementFromEventPath` function. - Replaced and removed the `createCounter` function with module-level counters.
1 parent a9ce9e0 commit e219d44

14 files changed

Lines changed: 60 additions & 94 deletions

File tree

src/components/button-group/button-group.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export default class IgcButtonGroupComponent extends EventEmitterMixin<
167167
}
168168

169169
private handleClick(event: MouseEvent) {
170-
const button = findElementFromEventPath<IgcToggleButtonComponent>(
170+
const button = findElementFromEventPath(
171171
IgcToggleButtonComponent.tagName,
172172
event
173173
);

src/components/calendar/days-view/days-view.ts

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,7 @@ import { registerComponent } from '../../common/definitions/register.js';
99
import type { Constructor } from '../../common/mixins/constructor.js';
1010
import { EventEmitterMixin } from '../../common/mixins/event-emitter.js';
1111
import { partMap } from '../../common/part-map.js';
12-
import {
13-
addSafeEventListener,
14-
chunk,
15-
first,
16-
last,
17-
take,
18-
} from '../../common/util.js';
12+
import { addSafeEventListener, chunk, first, last } from '../../common/util.js';
1913
import { IgcCalendarBaseComponent } from '../base.js';
2014
import {
2115
areSameMonth,
@@ -500,26 +494,25 @@ export default class IgcDaysViewComponent extends EventEmitterMixin<
500494
const aria = getDateFormatter().getIntlFormatter(this.locale, {
501495
weekday: 'long',
502496
});
503-
const days = take(
504-
generateMonth(this._activeDate, this._firstDayOfWeek),
505-
DAYS_IN_WEEK
506-
);
507497

508498
const weekNumber = this.showWeekNumbers
509499
? this._renderHeaderWeekNumber()
510500
: nothing;
511501

512-
const headers = days.map(
513-
(day) => html`
514-
<span
515-
role="columnheader"
516-
part="label"
517-
aria-label=${aria.format(day.native)}
518-
>
519-
<span part="label-inner">${label.format(day.native)}</span>
520-
</span>
521-
`
522-
);
502+
const headers = generateMonth(this._activeDate, this._firstDayOfWeek)
503+
.take(DAYS_IN_WEEK)
504+
.map(
505+
(day) => html`
506+
<span
507+
role="columnheader"
508+
part="label"
509+
aria-label=${aria.format(day.native)}
510+
>
511+
<span part="label-inner">${label.format(day.native)}</span>
512+
</span>
513+
`
514+
)
515+
.toArray();
523516

524517
return html`
525518
<div role="row" part="days-row first">${weekNumber}${headers}</div>

src/components/calendar/helpers.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,10 @@ export function convertToDates(
154154
* Returns the value of the selected/activated element (day/month/year) in the calendar view.
155155
*/
156156
export function getViewElement(event: Event): number {
157-
const element = findElementFromEventPath<HTMLElement>('[data-value]', event);
157+
const element = findElementFromEventPath(
158+
'[data-value]',
159+
event
160+
) as HTMLElement;
158161
return element ? asNumber(element.dataset.value, -1) : -1;
159162
}
160163

src/components/carousel/carousel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ export default class IgcCarouselComponent extends EventEmitterMixin<
501501
}
502502

503503
private async _handleIndicatorClick(event: PointerEvent): Promise<void> {
504-
const indicator = findElementFromEventPath<IgcCarouselIndicatorComponent>(
504+
const indicator = findElementFromEventPath(
505505
IgcCarouselIndicatorComponent.tagName,
506506
event
507507
)!;

src/components/combo/combo-item.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { styles } from './themes/combo-item.base.css.js';
1010

1111
/* blazorSuppress */
1212
export default class IgcComboItemComponent extends LitElement {
13-
public static readonly tagName: string = 'igc-combo-item';
13+
public static readonly tagName = 'igc-combo-item';
1414
public static override styles = [styles, shared];
1515

1616
/* blazorSuppress */

src/components/combo/combo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ export default class IgcComboComponent<
821821

822822
protected itemClickHandler(event: PointerEvent) {
823823
this._setTouchedState();
824-
const target = findElementFromEventPath<IgcComboItemComponent>(
824+
const target = findElementFromEventPath(
825825
IgcComboItemComponent.tagName,
826826
event
827827
);

src/components/common/util.ts

Lines changed: 25 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,6 @@ export function numberInRangeInclusive(
2424
return value >= min && value <= max;
2525
}
2626

27-
export function createCounter() {
28-
let i = 0;
29-
return () => {
30-
i++;
31-
return i;
32-
};
33-
}
34-
3527
/**
3628
* Returns whether an element has a Left-to-Right directionality.
3729
*/
@@ -153,19 +145,25 @@ export function isElement(node: unknown): node is Element {
153145
return node instanceof Node && node.nodeType === Node.ELEMENT_NODE;
154146
}
155147

156-
export function getElementsFromEventPath<T extends Element>(event: Event) {
157-
return event.composedPath().filter((item) => isElement(item)) as T[];
158-
}
159-
148+
export function findElementFromEventPath<K extends keyof HTMLElementTagNameMap>(
149+
predicate: K,
150+
event: Event
151+
): HTMLElementTagNameMap[K] | undefined;
160152
export function findElementFromEventPath<T extends Element>(
161153
predicate: string | ((element: Element) => boolean),
162154
event: Event
155+
): T | undefined;
156+
export function findElementFromEventPath(
157+
predicate: string | ((element: Element) => boolean),
158+
event: Event
163159
) {
164160
const func = isString(predicate)
165161
? (e: Element) => e.matches(predicate)
166162
: (e: Element) => predicate(e);
167163

168-
return getElementsFromEventPath(event).find(func) as T | undefined;
164+
return Iterator.from(event.composedPath()).find(
165+
(item) => isElement(item) && func(item)
166+
) as Element | undefined;
169167
}
170168

171169
export function first<T>(arr: T[]) {
@@ -181,44 +179,27 @@ export function modulo(n: number, d: number) {
181179
}
182180

183181
/**
184-
* Creates an array of `n` elements from a given iterator.
185-
*
186-
*/
187-
export function take<T>(iterable: IterableIterator<T>, n: number) {
188-
const result: T[] = [];
189-
let i = 0;
190-
let current = iterable.next();
191-
192-
while (i < n && !current.done) {
193-
result.push(current.value);
194-
current = iterable.next();
195-
i++;
196-
}
197-
198-
return result;
199-
}
200-
201-
/**
202-
* Splits an array into chunks of length `size` and returns a generator
203-
* yielding each chunk.
204-
* The last chunk may contain less than `size` elements.
182+
* Splits an array into chunks of a specified size and returns a generator that yields each chunk.
205183
*
206184
* @example
207185
* ```typescript
208-
* const arr = [0,1,2,3,4,5,6,7,8,9];
209-
*
210-
* Array.from(chunk(arr, 2)) // [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
211-
* Array.from(chunk(arr, 3)) // [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
212-
* Array.from(chunk([], 3)) // []
213-
* Array.from(chunk(arr, -3)) // Error
186+
* [...chunk([1, 2, 3, 4, 5], 2)]; // [[1, 2], [3, 4], [5]]
214187
* ```
188+
*
189+
* @throws If the `size` parameter is not a safe integer greater than or equal to 1.
215190
*/
216-
export function* chunk<T>(arr: T[], size: number) {
217-
if (size < 1) {
191+
export function* chunk<T>(arr: T[], size: number): Generator<T[]> {
192+
if (!Number.isSafeInteger(size) || size < 1) {
218193
throw new Error('size must be an integer >= 1');
219194
}
220-
for (let i = 0; i < arr.length; i += size) {
221-
yield arr.slice(i, i + size);
195+
196+
const iterator = Iterator.from(arr);
197+
const length = arr.length;
198+
let i = 0;
199+
200+
while (i < length) {
201+
yield iterator.take(size).toArray();
202+
i += size;
222203
}
223204
}
224205

src/components/date-range-picker/date-range-picker.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ import {
5151
addSafeEventListener,
5252
asNumber,
5353
clamp,
54-
createCounter,
5554
equal,
5655
findElementFromEventPath,
5756
isEmpty,
@@ -90,6 +89,8 @@ export interface IgcDateRangePickerComponentEventMap {
9089
igcInput: CustomEvent<DateRangeValue | null>;
9190
}
9291

92+
let nextId = 1;
93+
9394
/* blazorIndirectRender */
9495
/* blazorSupportsVisualChildren */
9596
/**
@@ -217,9 +218,7 @@ export default class IgcDateRangePickerComponent extends FormAssociatedRequiredM
217218

218219
// #region Internal state & properties
219220

220-
private static readonly _increment = createCounter();
221-
222-
protected readonly _inputId = `date-range-picker-${IgcDateRangePickerComponent._increment()}`;
221+
protected readonly _inputId = `date-range-picker-${nextId++}`;
223222

224223
private readonly _themes = addThemingController(this, all);
225224

src/components/dropdown/dropdown.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ export default class IgcDropdownComponent extends EventEmitterMixin<
241241
}
242242

243243
private handleListBoxClick(event: MouseEvent) {
244-
const item = findElementFromEventPath<IgcDropdownItemComponent>(
244+
const item = findElementFromEventPath(
245245
IgcDropdownItemComponent.tagName,
246246
event
247247
);

src/components/resize-container/resize-controller.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,8 @@ class ResizeController implements ReactiveController {
200200
const refs = this._options.ref?.map(({ value }) => value) ?? [this._host];
201201

202202
this._activeRef =
203-
findElementFromEventPath<HTMLElement>(
204-
(e) => refs.includes(e as HTMLElement),
205-
event
206-
) ?? null;
203+
findElementFromEventPath((e) => refs.includes(e as HTMLElement), event) ??
204+
null;
207205
}
208206

209207
private _setResizeState(enabled = true): void {

0 commit comments

Comments
 (0)