@ixfx/components 0.8.2 → 0.8.4
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.
- package/bundle/index.d.ts +274 -93
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +2135 -637
- package/bundle/index.js.map +1 -1
- package/dist/ac-text.js +2 -2
- package/dist/ac-token.js +2 -2
- package/dist/checkbox.js +1 -1
- package/dist/{colour-picker-Ddsk-7E_.js → colour-picker-CSxwUmtE.js} +2 -2
- package/dist/{colour-picker-Ddsk-7E_.js.map → colour-picker-CSxwUmtE.js.map} +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.js +2 -2
- package/dist/data-display.js +1 -1
- package/dist/data-grid-D-z7bx5-.d.ts.map +1 -1
- package/dist/data-grid.js +7 -4
- package/dist/data-grid.js.map +1 -1
- package/dist/detail-list-_ZJHYEBr.d.ts.map +1 -1
- package/dist/detail-list.js +67 -4
- package/dist/detail-list.js.map +1 -1
- package/dist/editable-label.js +1 -1
- package/dist/{fallbacks-DNI9xt0F.js → fallbacks-BcKGq-2F.js} +17 -1
- package/dist/{fallbacks-DNI9xt0F.js.map → fallbacks-BcKGq-2F.js.map} +1 -1
- package/dist/{grid-list-bmBWq6P-.js → grid-list-BXfY3XXX.js} +486 -13
- package/dist/grid-list-BXfY3XXX.js.map +1 -0
- package/dist/{grid-list-C1T6WlEp.d.ts → grid-list-x550wJ0X.d.ts} +97 -2
- package/dist/grid-list-x550wJ0X.d.ts.map +1 -0
- package/dist/grid-list.d.ts +2 -2
- package/dist/grid-list.js +1 -1
- package/dist/handle-BPOxBOKf.js +219 -0
- package/dist/handle-BPOxBOKf.js.map +1 -0
- package/dist/{icon-DcEEGQa_.js → icon-Dy33gu4Z.js} +2 -2
- package/dist/{icon-DcEEGQa_.js.map → icon-Dy33gu4Z.js.map} +1 -1
- package/dist/icons.js +1 -1
- package/dist/{index-BeuCXtiO.d.ts → index-DV3iaUBW.d.ts} +2 -2
- package/dist/{index-BeuCXtiO.d.ts.map → index-DV3iaUBW.d.ts.map} +1 -1
- package/dist/index.d.ts +130 -48
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +551 -203
- package/dist/index.js.map +1 -1
- package/dist/labelled-radial-input.js +2 -2
- package/dist/{labelled-slider-input-BInB5GKL.js → labelled-slider-input-BCH9968c.js} +3 -3
- package/dist/{labelled-slider-input-BInB5GKL.js.map → labelled-slider-input-BCH9968c.js.map} +1 -1
- package/dist/labelled-slider-input.js +1 -1
- package/dist/list-selection-controller-SA4piSBi.js +589 -0
- package/dist/list-selection-controller-SA4piSBi.js.map +1 -0
- package/dist/{menu-BDrTh6oo.js → menu-HMoxCPY2.js} +4 -4
- package/dist/{menu-BDrTh6oo.js.map → menu-HMoxCPY2.js.map} +1 -1
- package/dist/{menu-item-B6pKg2Qq.js → menu-item-BQDYxk5A.js} +3 -3
- package/dist/{menu-item-B6pKg2Qq.js.map → menu-item-BQDYxk5A.js.map} +1 -1
- package/dist/menu.js +2 -2
- package/dist/miller.js +1 -1
- package/dist/narrowed-text.js +1 -1
- package/dist/notifier.js +1 -1
- package/dist/panel.d.ts +1 -1
- package/dist/panel.js +1 -1
- package/dist/polar-pad.js +1 -1
- package/dist/{radial-input-BfA3GO0H.js → radial-input-DyxfbNzD.js} +2 -2
- package/dist/{radial-input-BfA3GO0H.js.map → radial-input-DyxfbNzD.js.map} +1 -1
- package/dist/radial-input.js +1 -1
- package/dist/range-input.js +1 -1
- package/dist/range.js +1 -1
- package/dist/select-horiz.js +1 -1
- package/dist/{slider-input-Bl1hFE89.js → slider-input-WR8_RwHg.js} +2 -2
- package/dist/{slider-input-Bl1hFE89.js.map → slider-input-WR8_RwHg.js.map} +1 -1
- package/dist/slider-input.js +1 -1
- package/dist/snackbar.js +1 -1
- package/dist/{split-layout-BFSbUZQv.js → split-layout-CA5jbNbG.js} +22 -33
- package/dist/split-layout-CA5jbNbG.js.map +1 -0
- package/dist/split-layout-JK3xWVX2.d.ts.map +1 -1
- package/dist/split-layout.js +1 -1
- package/dist/swipe.js +1 -1
- package/dist/tabs.js +1 -1
- package/dist/{timeline-B2fgDL4U.js → timeline-THyvH8xy.js} +14 -8
- package/dist/timeline-THyvH8xy.js.map +1 -0
- package/dist/timeline.d.ts +1 -1
- package/dist/timeline.js +1 -1
- package/dist/{titlebar-YQ-jVQoS.d.ts → titlebar-Tqu-VvMU.d.ts} +5 -1
- package/dist/titlebar-Tqu-VvMU.d.ts.map +1 -0
- package/dist/titlebar.d.ts +1 -1
- package/dist/titlebar.js +5 -3
- package/dist/titlebar.js.map +1 -1
- package/dist/{tree-4Jv28Nig.js → tree-DTQon838.js} +2 -2
- package/dist/{tree-4Jv28Nig.js.map → tree-DTQon838.js.map} +1 -1
- package/dist/tree.js +1 -1
- package/dist/vertical-list-CFuFDygi.d.ts.map +1 -1
- package/dist/vertical-list.js +67 -3
- package/dist/vertical-list.js.map +1 -1
- package/dist/xy-pad.js +1 -1
- package/docs-user/data-grid.md +1 -1
- package/docs-user/detail-list.md +4 -0
- package/docs-user/dock.md +33 -8
- package/docs-user/grid-list.md +5 -0
- package/docs-user/index.json +2 -2
- package/docs-user/split-layout.md +2 -4
- package/docs-user/twosplit-layout.md +14 -16
- package/docs-user/vertical-list.md +4 -0
- package/package.json +1 -1
- package/dist/grid-list-C1T6WlEp.d.ts.map +0 -1
- package/dist/grid-list-bmBWq6P-.js.map +0 -1
- package/dist/list-selection-controller-DTFI40li.js +0 -264
- package/dist/list-selection-controller-DTFI40li.js.map +0 -1
- package/dist/split-layout-BFSbUZQv.js.map +0 -1
- package/dist/timeline-B2fgDL4U.js.map +0 -1
- package/dist/titlebar-YQ-jVQoS.d.ts.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"grid-list-bmBWq6P-.js","names":["ITEM_SELECTOR","#host","#effectiveIndices","#items","#itemKey","#keysCache","#captionFor","#predicate","#searchKeys","#recomputeEffective","#slotNames","#seq","#syntheticKeys","#adapter","#sourceElement","#source","#emitSelect","#emitItemEvent","#itemWrap","#cancelScrollRaf","#scrollRaf","#emitViewportChange","#currentWindow","#scrollItemIntoView","#onCellActivate","#searchMode","#searchFilterKeys","#searchRegistry","#effectiveSearchModes","#unregisterSearchCommands","#syncAriaMultiselectable","#mutationObserver","#onChildrenChanged","#ensureSearch","#syncSearchCommands","#resizeObserver","#reflowTimer","#items","#syncItems","#noteChildMutation","#entering","#rebuildSource","#viewportRef","#measureViewport","#applyFilters","#zoomAnchor","#captureCentreAnchor","#applyZoomAnchor","#rawIndexOfKey","#windowKeys","#rowHeight","#selectedSnapshot","#onScroll","#renderDataCells","#renderElementCells","#renderDataCell","#onCellClick","#renderCell","#captionFor","customElement"],"sources":["../src/grid-list/grid-list-source.ts","../src/grid-list/grid-list.ts"],"sourcesContent":["import type { GridListDataAdapter } from './types.js';\nimport { html } from 'lit';\nimport { keyForElement } from '../interaction/keyed-list.js';\n\n/**\n * Uniform view over a grid-list's items, in either mode:\n *\n * - element mode ({@link ElementGridListSource}) — light-DOM children projected\n * through named `<slot>`s;\n * - data mode ({@link DataGridListSource}) — a plain array plus a\n * {@link GridListDataAdapter}.\n *\n * Everything else in `GridListElement` addresses items by **effective index**\n * (0…`count`, after `[hidden]` / filter / search) and **key**, and never\n * branches on the mode.\n */\nexport interface GridListSource {\n /** After `[hidden]`, the filter predicate and the live search filter. */\n readonly count: number;\n /** Before the filter predicate / search filter (element mode still drops `[hidden]`). */\n readonly rawCount: number;\n\n /** Effective index → key. Includes synthetic keys for keyless light-DOM items. */\n keyAt(index: number): string;\n /** Effective index of `key`, or `-1`. First match wins on duplicates. */\n indexOfKey(key: string): number;\n /** Every effective key, in order (synthetic keys included). */\n keys(): string[];\n\n /** Caption text for a cell. */\n labelAt(index: number): string;\n /** Raw (unfiltered) index → key — used by search, which sees the whole collection. */\n rawKeyAt(index: number): string;\n /** Raw (unfiltered) index → fzf haystack (falls back to the label). */\n rawSearchTextAt(index: number): string;\n\n /** `false` for `not-checked` / `disabled` items. */\n isSelectable(index: number): boolean;\n\n /** Lit-renderable cell content (element mode: a named `<slot>`; data mode: `adapter.render`). */\n renderCellContent(index: number): unknown;\n\n /** Push the current filter predicate / search-filter key set; recomputes the effective order. */\n setFilters(opts: { predicate?: ((index: number) => boolean) | undefined; searchKeys?: ReadonlySet<string> | undefined }): void;\n /** Re-read the underlying data. */\n refresh(): void;\n\n /** The Element for `key`, if this source has one. */\n resolveKeyToElement(key: string): Element | undefined;\n /** The data value for `key`, if this source has one. */\n resolveKeyToData(key: string): unknown;\n}\n\nconst ITEM_SELECTOR = `:scope > *`;\nconst SYNTHETIC_PREFIX = `gl-auto-`;\n\n/** Whether `key` is an auto-assigned key for a keyless light-DOM item. */\nexport function isSyntheticKey(key: string): boolean {\n return key.startsWith(SYNTHETIC_PREFIX);\n}\n\nfunction warnDuplicateKeys(keys: readonly string[]): void {\n const seen = new Set<string>();\n const dupes = new Set<string>();\n for (const k of keys) {\n if (isSyntheticKey(k))\n continue;\n if (seen.has(k))\n dupes.add(k);\n else\n seen.add(k);\n }\n if (dupes.size > 0) {\n console.warn(\n `ixfx-grid-list: duplicate item key(s) ${[...dupes].map(k => `\"${k}\"`).join(`, `)} — the first occurrence wins for key-based operations.`,\n );\n }\n}\n\nexport class ElementGridListSource implements GridListSource {\n readonly #host: HTMLElement;\n #items: Element[] = [];\n #slotNames = new WeakMap<Element, string>();\n #syntheticKeys = new WeakMap<Element, string>();\n #seq = 0;\n #predicate: ((index: number) => boolean) | undefined;\n #searchKeys: ReadonlySet<string> | undefined;\n #effectiveIndices: number[] = [];\n #keysCache: string[] | undefined;\n\n constructor(host: HTMLElement) {\n this.#host = host;\n this.refresh();\n }\n\n get count(): number {\n return this.#effectiveIndices.length;\n }\n\n get rawCount(): number {\n return this.#items.length;\n }\n\n keyAt(index: number): string {\n const eff = this.#effectiveIndices[index];\n return eff === undefined ? `` : this.#itemKey(this.#items[eff]);\n }\n\n indexOfKey(key: string): number {\n return this.#effectiveIndices.findIndex(i => this.#itemKey(this.#items[i]) === key);\n }\n\n keys(): string[] {\n this.#keysCache ??= this.#effectiveIndices.map(i => this.#itemKey(this.#items[i]));\n return this.#keysCache;\n }\n\n labelAt(index: number): string {\n const eff = this.#effectiveIndices[index];\n return eff === undefined ? `` : this.#captionFor(this.#items[eff]);\n }\n\n rawKeyAt(index: number): string {\n const el = this.#items[index];\n return el === undefined ? `` : this.#itemKey(el);\n }\n\n rawSearchTextAt(index: number): string {\n const el = this.#items[index];\n if (el === undefined)\n return ``;\n return (\n el.getAttribute(`data-search-label`)\n ?? el.getAttribute(`data-label`)\n ?? el.querySelector(`[data-grid-list-caption]`)?.textContent?.trim()\n ?? el.textContent?.trim()\n ?? ``\n );\n }\n\n isSelectable(index: number): boolean {\n const eff = this.#effectiveIndices[index];\n return eff !== undefined && !this.#items[eff].hasAttribute(`not-checked`);\n }\n\n renderCellContent(index: number): unknown {\n const eff = this.#effectiveIndices[index];\n if (eff === undefined)\n return undefined;\n return html`<slot name=${this.slotNameFor(this.#items[eff])}></slot>`;\n }\n\n setFilters(opts: { predicate?: ((index: number) => boolean) | undefined; searchKeys?: ReadonlySet<string> | undefined }): void {\n if (`predicate` in opts)\n this.#predicate = opts.predicate;\n if (`searchKeys` in opts)\n this.#searchKeys = opts.searchKeys;\n this.#recomputeEffective();\n }\n\n refresh(): void {\n this.#items = Array.from(this.#host.querySelectorAll(ITEM_SELECTOR)).filter(\n el => !(el as HTMLElement).hidden,\n );\n for (const el of this.#items) {\n const name = this.slotNameFor(el);\n if (el.getAttribute(`slot`) !== name)\n el.setAttribute(`slot`, name);\n }\n warnDuplicateKeys(this.#items.map(el => this.#itemKey(el)));\n this.#recomputeEffective();\n }\n\n /** Effective-order elements — the array `GridListElement` renders and slices. */\n effectiveElements(): Element[] {\n return this.#effectiveIndices.map(i => this.#items[i]);\n }\n\n /** Raw-order element at `index`, or `undefined`. */\n rawElementAt(index: number): Element | undefined {\n return this.#items[index];\n }\n\n /** Stable per-element `<slot>` name. */\n slotNameFor(el: Element): string {\n let name = this.#slotNames.get(el);\n if (name === undefined) {\n this.#seq += 1;\n name = `gl-item-${this.#seq}`;\n this.#slotNames.set(el, name);\n }\n return name;\n }\n\n resolveKeyToElement(key: string): Element | undefined {\n if (isSyntheticKey(key))\n return this.#items.find(el => this.#syntheticKeys.get(el) === key);\n return this.#items.find(el => keyForElement(el) === key);\n }\n\n resolveKeyToData(_key: string): undefined {\n return undefined;\n }\n\n #recomputeEffective(): void {\n const effective: number[] = [];\n for (let i = 0; i < this.#items.length; i++) {\n if (this.#predicate !== undefined && !this.#predicate(i))\n continue;\n if (this.#searchKeys !== undefined) {\n const key = this.#itemKey(this.#items[i]);\n if (!isSyntheticKey(key) && !this.#searchKeys.has(key))\n continue;\n }\n effective.push(i);\n }\n this.#effectiveIndices = effective;\n this.#keysCache = undefined;\n }\n\n #itemKey(el: Element): string {\n const userKey = keyForElement(el);\n if (userKey !== undefined)\n return userKey;\n let synthetic = this.#syntheticKeys.get(el);\n if (synthetic === undefined) {\n this.#seq += 1;\n synthetic = `${SYNTHETIC_PREFIX}${this.#seq}`;\n this.#syntheticKeys.set(el, synthetic);\n }\n return synthetic;\n }\n\n #captionFor(el: Element): string {\n return (\n el.getAttribute(`data-label`)\n ?? el.querySelector(`[data-grid-list-caption]`)?.textContent?.trim()\n ?? ``\n );\n }\n}\n\nexport class DataGridListSource<T> implements GridListSource {\n #items: readonly T[];\n readonly #adapter: GridListDataAdapter<T>;\n #predicate: ((index: number) => boolean) | undefined;\n #searchKeys: ReadonlySet<string> | undefined;\n #effectiveIndices: number[] = [];\n #keysCache: string[] | undefined;\n\n constructor(items: readonly T[], adapter: GridListDataAdapter<T>) {\n this.#items = items;\n this.#adapter = adapter;\n warnDuplicateKeys(items.map((it, i) => adapter.key(it, i)));\n this.#recomputeEffective();\n }\n\n get count(): number {\n return this.#effectiveIndices.length;\n }\n\n get rawCount(): number {\n return this.#items.length;\n }\n\n keyAt(index: number): string {\n const eff = this.#effectiveIndices[index];\n return eff === undefined ? `` : this.#adapter.key(this.#items[eff], eff);\n }\n\n indexOfKey(key: string): number {\n return this.#effectiveIndices.findIndex(i => this.#adapter.key(this.#items[i], i) === key);\n }\n\n keys(): string[] {\n this.#keysCache ??= this.#effectiveIndices.map(i => this.#adapter.key(this.#items[i], i));\n return this.#keysCache;\n }\n\n labelAt(index: number): string {\n const eff = this.#effectiveIndices[index];\n if (eff === undefined)\n return ``;\n return this.#adapter.label?.(this.#items[eff], eff) ?? this.#adapter.key(this.#items[eff], eff);\n }\n\n rawKeyAt(index: number): string {\n return index < 0 || index >= this.#items.length ? `` : this.#adapter.key(this.#items[index], index);\n }\n\n rawSearchTextAt(index: number): string {\n if (index < 0 || index >= this.#items.length)\n return ``;\n const item = this.#items[index];\n return this.#adapter.searchableText?.(item, index) ?? this.#adapter.label?.(item, index) ?? ``;\n }\n\n isSelectable(index: number): boolean {\n const eff = this.#effectiveIndices[index];\n return eff !== undefined && !this.#adapter.disabled?.(this.#items[eff], eff);\n }\n\n renderCellContent(index: number): unknown {\n const eff = this.#effectiveIndices[index];\n return eff === undefined ? undefined : this.#adapter.render(this.#items[eff], eff);\n }\n\n setFilters(opts: { predicate?: ((index: number) => boolean) | undefined; searchKeys?: ReadonlySet<string> | undefined }): void {\n if (`predicate` in opts)\n this.#predicate = opts.predicate;\n if (`searchKeys` in opts)\n this.#searchKeys = opts.searchKeys;\n this.#recomputeEffective();\n }\n\n refresh(): void {\n this.#recomputeEffective();\n }\n\n /** Raw-order item at `index`, or `undefined`. */\n rawItemAt(index: number): T | undefined {\n return this.#items[index];\n }\n\n /** Effective-order item at `index`, or `undefined`. */\n getItemAt(index: number): T | undefined {\n const eff = this.#effectiveIndices[index];\n return eff === undefined ? undefined : this.#items[eff];\n }\n\n resolveKeyToElement(_key: string): undefined {\n return undefined;\n }\n\n resolveKeyToData(key: string): T | undefined {\n const idx = this.#items.findIndex((item, i) => this.#adapter.key(item, i) === key);\n return idx >= 0 ? this.#items[idx] : undefined;\n }\n\n #recomputeEffective(): void {\n const effective: number[] = [];\n for (let i = 0; i < this.#items.length; i++) {\n if (this.#predicate !== undefined && !this.#predicate(i))\n continue;\n if (this.#searchKeys !== undefined && !this.#searchKeys.has(this.#adapter.key(this.#items[i], i)))\n continue;\n effective.push(i);\n }\n this.#effectiveIndices = effective;\n this.#keysCache = undefined;\n }\n}\n","/* eslint-disable ts/no-unsafe-declaration-merging */\nimport type { CommandRegistry } from '@clinth/ui-commands';\nimport type { PropertyValues } from 'lit';\nimport type { DataListSearchItem, ListSearchMode } from '../incr-search/index.js';\nimport type { ListInteractionMode, ListSelectionMode } from '../interaction/list-selection-types.js';\nimport type { GridListSource } from './grid-list-source.js';\nimport type {\n GridListCaptionAlign,\n GridListCaptionBackdrop,\n GridListCaptionPosition,\n GridListCaptionStyle,\n GridListDataAdapter as GridListDataAdapterType,\n GridListFullElementEventMap,\n GridListItemClickDetail,\n GridListOrientation,\n GridListSelectDetail,\n GridListViewportChangeDetail,\n} from './types.js';\nimport { createCommandRegistry } from '@clinth/ui-commands';\nimport { css, html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { createRef, ref } from 'lit/directives/ref.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport {\n createDataListSearchAdapter,\n createElementListSearchAdapter,\n LIST_SEARCH_MODES,\n ListSearchController,\n listSearchStyles,\n registerListSearchCommands,\n} from '../incr-search/index.js';\nimport { listItemFadeKeyframes, tickledItemStyles, tickledStyles } from '../interaction/index.js';\nimport { keyForElement } from '../interaction/keyed-list.js';\nimport { ListSelectionController } from '../interaction/list-selection-controller.js';\nimport { TickledController } from '../interaction/tickled-controller.js';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { scrollFadeStyles } from '../styles/scroll-fade.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport { DataGridListSource, ElementGridListSource, isSyntheticKey } from './grid-list-source.js';\n\nexport type {\n GridListActivateDetail,\n GridListActivateEvent,\n GridListCaptionAlign,\n GridListCaptionBackdrop,\n GridListCaptionPosition,\n GridListCaptionStyle,\n GridListDataAdapter,\n GridListElementEventMap,\n GridListFullElementEventMap,\n GridListItemClickDetail,\n GridListItemClickEvent,\n GridListItemEventDetail,\n GridListOrientation,\n GridListSelectDetail,\n GridListSelectEvent,\n GridListTickleDetail,\n GridListTickleEvent,\n GridListViewportChangeDetail,\n GridListViewportChangeEvent,\n ListInteractionMode,\n ListSelectionMode,\n} from './types.js';\n\ntype GridWindow = {\n readonly cols: number;\n readonly rows: number;\n /** Spacer height in px (vertical orientations); 0 when CSS controls it. */\n readonly totalHeight: number;\n /** Spacer width in px (`row` orientation); 0 when CSS controls it. */\n readonly totalWidth: number;\n readonly startIndex: number;\n readonly endIndex: number;\n /** Distance between the left edges of adjacent columns (item width + gap). */\n readonly colStride: number;\n /** Distance between the top edges of adjacent rows (row height + gap). */\n readonly rowStride: number;\n /** `true` for `row` orientation — the list scrolls along the x axis. */\n readonly horizontal: boolean;\n};\n\nconst ITEM_SELECTOR = `:scope > *`;\n\nconst MIN_ITEM_SIZE = 16;\n\n/**\n * How long the host keeps the `[reflowing]` attribute after items are inserted\n * mid-list — long enough for the cell move-transition and the enter animation\n * to finish. Kept a touch above the default `--grid-list-item-move-duration`.\n */\nconst REFLOW_WINDOW_MS = 320;\n\n// `itemWidth` is the square's width; `rowHeight` is the full cell height\n// (square, plus the caption band in `labelled` style). `scrollOffset` is\n// `scrollTop` for `grid`/`column` and `scrollLeft` for `row`.\nfunction computeGridWindow(\n count: number,\n itemWidth: number,\n rowHeight: number,\n gap: number,\n viewportWidth: number,\n viewportHeight: number,\n scrollOffset: number,\n orientation: GridListOrientation,\n overscan = 2,\n): GridWindow {\n const w = Math.max(1, itemWidth);\n const h = Math.max(1, rowHeight);\n const g = Math.max(0, gap);\n const colStride = w + g;\n const rowStride = h + g;\n const offset = Math.max(0, scrollOffset);\n\n if (orientation === `row`) {\n const rows = count === 0 ? 0 : 1;\n const totalWidth = count === 0 ? 0 : (count - 1) * colStride + w;\n if (count === 0 || viewportWidth <= 0) {\n return { cols: count, rows, totalHeight: 0, totalWidth, startIndex: 0, endIndex: 0, colStride, rowStride, horizontal: true };\n }\n const firstVisible = Math.max(0, Math.floor(offset / colStride));\n const lastVisible = Math.ceil((offset + viewportWidth) / colStride);\n return {\n cols: count,\n rows,\n totalHeight: 0,\n totalWidth,\n startIndex: Math.max(0, firstVisible - overscan),\n endIndex: Math.min(count, lastVisible + overscan),\n colStride,\n rowStride,\n horizontal: true,\n };\n }\n\n const cols = orientation === `column`\n ? 1\n : Math.max(1, Math.floor((Math.max(0, viewportWidth) + g) / colStride));\n const rows = count === 0 ? 0 : Math.ceil(count / cols);\n const totalHeight = rows === 0 ? 0 : (rows - 1) * rowStride + h;\n\n if (count === 0 || viewportHeight <= 0) {\n return { cols, rows, totalHeight, totalWidth: 0, startIndex: 0, endIndex: 0, colStride, rowStride, horizontal: false };\n }\n\n const firstVisibleRow = Math.max(0, Math.floor(offset / rowStride));\n const lastVisibleRow = Math.ceil((offset + viewportHeight) / rowStride);\n const rowStart = Math.max(0, firstVisibleRow - overscan);\n const rowEnd = Math.min(rows, lastVisibleRow + overscan);\n\n return {\n cols,\n rows,\n totalHeight,\n totalWidth: 0,\n startIndex: rowStart * cols,\n endIndex: Math.min(count, rowEnd * cols),\n colStride,\n rowStride,\n horizontal: false,\n };\n}\n\nfunction columnOf(index: number, cols: number): number {\n return cols <= 1 ? 0 : index % cols;\n}\n\nfunction rowOf(index: number, cols: number): number {\n return cols <= 1 ? index : Math.floor(index / cols);\n}\n\n@customElement(`ixfx-grid-list`)\nexport class GridListElement<T = unknown> extends LitElement {\n static override styles = [\n themeFallbacks,\n tickledStyles,\n tickledItemStyles,\n // Both axes are registered; `render()` applies whichever one matches the\n // current orientation to the viewport (see the `scroll-fade-*` class there).\n scrollFadeStyles(`x`),\n scrollFadeStyles(`y`),\n listItemFadeKeyframes,\n listSearchStyles,\n css`\n :host {\n --grid-list-item-bg: var(--item-bg);\n --grid-list-radius: var(--radius-m);\n --grid-list-gap: var(--space-xs);\n position: relative;\n display: block;\n height: 100%;\n min-height: 2em;\n overflow: hidden;\n user-select: none;\n background: var(--surface-3);\n color: var(--surface-3-text);\n border-radius: var(--radius-s);\n border: 1px solid var(--border);\n }\n :host(:focus-visible) {\n outline: 2px solid var(--accent);\n outline-offset: -2px;\n }\n .viewport {\n position: relative;\n height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n /* Consumer control over the native scrollbar (both orientations): set\n --grid-list-scrollbar-width to \"none\" to hide it or \"thin\" to slim it.\n The container is also exposed as ::part(viewport). */\n scrollbar-width: var(--grid-list-scrollbar-width, auto);\n scrollbar-gutter: var(--grid-list-scrollbar-gutter, auto);\n scrollbar-color: var(--grid-list-scrollbar-color, auto);\n }\n :host([orientation=\"row\"]) .viewport {\n overflow-x: auto;\n overflow-y: hidden;\n }\n .spacer {\n position: relative;\n width: 100%;\n }\n .cell {\n position: absolute;\n inset: 0 auto auto 0;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n contain: layout style;\n border-radius: var(--grid-list-radius);\n }\n\n /* The square that holds the slotted content — the \"image well\". Its\n background is a set of consumer-settable variables so a Finder-style\n look is possible: on selection the caption takes the accent colouring\n while the well takes a tint of the surface colour. Each variable is\n unset by default, so the fallback (today's look) applies until a\n consumer opts in.\n --grid-list-thumb-bg idle well background\n --grid-list-thumb-bg-tickled under the cursor\n --grid-list-thumb-bg-selected when the cell is selected\n --grid-list-thumb-bg-selected-tickled selected + under the cursor\n --grid-list-thumb-selection-wash colour of the box-mode wash (default: --accent)\n --grid-list-thumb-selection-wash-opacity strength of that wash (default: 0.18; set 0 to drop it) */\n .thumb {\n position: relative;\n flex: 1;\n min-height: 0;\n display: flex;\n align-items: stretch;\n box-sizing: border-box;\n overflow: hidden;\n border-radius: 0;\n background: var(--grid-list-thumb-bg, transparent);\n border: 1px solid transparent;\n transition: background var(--transition), border-color var(--transition);\n }\n .thumb ::slotted(*) {\n flex: 1;\n min-width: 0;\n min-height: 0;\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n }\n .thumb ::slotted(*:empty)::after {\n content: \"Empty\";\n color: var(--surface-muted-text);\n font-size: var(--text-s);\n }\n ::slotted([hidden]) {\n display: none !important;\n }\n\n /* Selection / cursor wash — sits over the slotted content so it reads\n on photos and arbitrary DOM alike. Set\n --grid-list-thumb-selection-wash-opacity: 0 to drop it in favour of a\n --grid-list-thumb-bg-selected tint. */\n .thumb::after {\n content: \"\";\n position: absolute;\n inset: 0;\n pointer-events: none;\n border-radius: inherit;\n background: var(--grid-list-thumb-selection-wash, var(--accent));\n opacity: 0;\n transition: opacity var(--transition, 0.1s ease-out);\n }\n .cell.selected .thumb::after {\n opacity: var(--grid-list-thumb-selection-wash-opacity, 0.18);\n }\n :host(:not([caption-style=\"separate\"])) .cell.selected .thumb {\n background: var(--grid-list-thumb-bg-selected, var(--grid-list-thumb-bg, transparent));\n }\n :host(:not([caption-style=\"separate\"])) .cell.selected.tickled .thumb {\n background: var(--grid-list-thumb-bg-selected-tickled, var(--grid-list-thumb-bg-selected, var(--grid-list-thumb-bg, transparent)));\n }\n\n /* ---- Caption (base) ---- */\n .caption {\n box-sizing: border-box;\n flex: none;\n padding: 3px 6px;\n font-size: var(--text-s, 0.8rem);\n line-height: 1.3;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--surface-3-text);\n }\n :host([caption-align=\"left\"]) .caption { text-align: left; }\n :host([caption-align=\"right\"]) .caption { text-align: right; }\n\n /* ---- Box modes (hidden / under / overlaid): the cell is one box with a\n single selection outline ---- */\n :host(:not([caption-style=\"separate\"])) .cell {\n background: var(--grid-list-item-bg);\n border: 1px solid transparent;\n overflow: hidden;\n transition: background var(--transition), border-color var(--transition);\n }\n :host(:not([caption-style=\"separate\"])) .cell.tickled,\n :host(:not([caption-style=\"separate\"]):not([disable-hover-tickle])) .cell:hover {\n background: var(--item-bg-tickled);\n border: var(--item-border-tickled);\n }\n :host(:not([caption-style=\"separate\"])) .cell.selected {\n outline: 2px solid var(--accent);\n outline-offset: -2px;\n }\n :host(:not([caption-style=\"separate\"])) .cell.selected.tickled {\n background: var(--item-bg-selected-tickled);\n outline: 2px dashed var(--surface-3);\n outline-offset: -2px;\n }\n\n /* under: caption is a strip inside the box, below the content */\n :host([caption-style=\"under\"]) .caption {\n border-top: 1px solid var(--border, rgba(128, 128, 128, 0.3));\n background: var(--surface-4, transparent);\n }\n\n /* overlaid: caption floats over the content */\n :host([caption-style=\"overlaid\"]) .caption {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n color: #fff;\n }\n :host([caption-style=\"overlaid\"][caption-position=\"top\"]) .caption {\n top: 0;\n bottom: auto;\n }\n :host([caption-style=\"overlaid\"][caption-position=\"middle\"]) .caption {\n top: 50%;\n bottom: auto;\n transform: translateY(-50%);\n }\n /* overlaid backdrop */\n :host([caption-style=\"overlaid\"][caption-backdrop=\"scrim\"]) .caption {\n background: rgba(0, 0, 0, 0.55);\n }\n :host([caption-style=\"overlaid\"][caption-backdrop=\"gradient\"]) .caption {\n background: linear-gradient(to top, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));\n padding-block: 10px 4px;\n }\n :host([caption-style=\"overlaid\"][caption-backdrop=\"gradient\"][caption-position=\"top\"]) .caption {\n background: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));\n padding-block: 4px 10px;\n }\n :host([caption-style=\"overlaid\"][caption-backdrop=\"gradient\"][caption-position=\"middle\"]) .caption {\n background: rgba(0, 0, 0, 0.5);\n padding-block: 3px;\n }\n :host([caption-style=\"overlaid\"][caption-backdrop=\"blur\"]) .caption {\n background: rgba(20, 20, 20, 0.3);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n }\n :host([caption-style=\"overlaid\"][caption-backdrop=\"none\"]) .caption {\n background: none;\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95), 0 0 6px rgba(0, 0, 0, 0.7);\n }\n\n /* ---- separate mode: square + detached caption, each highlighted ---- */\n :host([caption-style=\"separate\"]) .thumb {\n flex: none;\n border-radius: var(--grid-list-radius);\n background: var(--grid-list-thumb-bg, var(--grid-list-item-bg));\n border: 1px solid transparent;\n }\n :host([caption-style=\"separate\"]) .cell.tickled .thumb,\n :host([caption-style=\"separate\"]:not([disable-hover-tickle])) .cell:hover .thumb {\n background: var(--grid-list-thumb-bg-tickled, var(--item-bg-tickled));\n border: var(--item-border-tickled);\n }\n :host([caption-style=\"separate\"]) .caption {\n display: flex;\n align-items: center;\n align-self: center;\n max-width: 100%;\n margin-top: 4px;\n border-radius: var(--radius-s, 4px);\n }\n :host([caption-style=\"separate\"][caption-align=\"left\"]) .caption { align-self: flex-start; }\n :host([caption-style=\"separate\"][caption-align=\"right\"]) .caption { align-self: flex-end; }\n /* Inset the selection ring (negative offset) so it is never clipped by the\n scroll viewport's overflow when the cell sits against an edge. */\n :host([caption-style=\"separate\"]) .cell.selected .thumb {\n background: var(--grid-list-thumb-bg-selected, var(--grid-list-thumb-bg, var(--grid-list-item-bg)));\n outline: 2px solid var(--accent);\n outline-offset: -2px;\n }\n :host([caption-style=\"separate\"]) .cell.selected.tickled .thumb {\n background: var(--grid-list-thumb-bg-selected-tickled, var(--item-bg-selected-tickled));\n outline: 2px dashed var(--surface-3);\n outline-offset: -2px;\n }\n :host([caption-style=\"separate\"]) .cell.selected .caption {\n background: var(--accent);\n color: var(--accent-text, #000);\n }\n :host([caption-style=\"separate\"]) .cell.tickled .caption {\n background: var(--item-bg-tickled);\n outline: var(--item-border-tickled);\n }\n :host([caption-style=\"separate\"]) .cell.selected.tickled .caption {\n background: var(--item-bg-selected-tickled);\n outline: 2px dashed var(--surface-3);\n outline-offset: -2px;\n }\n\n /* ---- Insertion / removal animation ----\n Active only while the host carries [reflowing] — the ~0.3s window after\n the child list changes mid-list (see #noteChildMutation). Kept out of\n the base rules so it never fires during scroll virtualisation, where\n cells are recycled to wildly different positions every frame. Placed\n last so it wins over the earlier cell transition rule regardless of\n specificity. Survivors slide to their new slot; a freshly inserted cell\n also fades in. (A removed item's element is already gone, so there's\n nothing left to fade — the gap simply closes as the others slide.) */\n :host([reflowing]) .cell {\n transition:\n transform var(--grid-list-item-move-duration, 200ms) cubic-bezier(0.2, 0, 0, 1),\n background var(--transition),\n border-color var(--transition);\n }\n .cell.entering {\n animation: ixfx-list-item-in var(--grid-list-item-move-duration, 200ms) ease both;\n }\n @media (prefers-reduced-motion: reduce) {\n :host([reflowing]) .cell { transition: background var(--transition), border-color var(--transition); }\n .cell.entering { animation: none; }\n }\n `,\n ];\n\n @property({ type: String, attribute: `selection-mode` })\n selectionMode: ListSelectionMode = `single`;\n\n @property({ type: String, attribute: `interaction-mode` })\n interactionMode: ListInteractionMode = `standard`;\n\n /**\n * Disable hover tickling: pointer hover no longer moves the cursor, fires\n * `list-tickle`, or changes where keyboard navigation resumes from. Keyboard\n * navigation, selection, and `focusKey()` are unaffected.\n */\n @property({ type: Boolean, attribute: `disable-hover-tickle`, reflect: true })\n disableHoverTickle = false;\n\n @property({ type: Number, attribute: `item-size` })\n itemSize = 160;\n\n @property({ type: Number })\n gap = 8;\n\n /**\n * Layout / scroll direction.\n * - `grid` (default): items wrap across as many columns as fit, scrolls vertically.\n * - `row`: a single row of items, scrolls horizontally.\n * - `column`: a single column of items, scrolls vertically.\n */\n @property({ type: String, reflect: true })\n orientation: GridListOrientation = `grid`;\n\n /**\n * How the caption relates to the content — see {@link GridListCaptionStyle}.\n * - `hidden`: no caption.\n * - `separate`: detached label below the square, each highlighted on its own.\n * - `under` (default): one box, content on top, caption strip below it inside.\n * - `overlaid`: caption floats over the content.\n */\n @property({ type: String, attribute: `caption-style`, reflect: true })\n captionStyle: GridListCaptionStyle = `under`;\n\n /** Horizontal alignment of the caption text: `left`, `middle` (default), `right`. */\n @property({ type: String, attribute: `caption-align`, reflect: true })\n captionAlign: GridListCaptionAlign = `middle`;\n\n /** `overlaid` only — vertical placement: `top`, `middle`, `bottom` (default). */\n @property({ type: String, attribute: `caption-position`, reflect: true })\n captionPosition: GridListCaptionPosition = `bottom`;\n\n /** `overlaid` only — backdrop behind the caption: `scrim` (default), `gradient`, `blur`, `none`. */\n @property({ type: String, attribute: `caption-backdrop`, reflect: true })\n captionBackdrop: GridListCaptionBackdrop = `scrim`;\n\n /**\n * Height reserved for the caption in `separate` / `under` styles, in pixels.\n * Ignored by `overlaid` and `hidden`.\n */\n @property({ type: Number, attribute: `caption-height` })\n captionHeight = 24;\n\n @property({ type: String, attribute: `item-wrap` })\n itemWrap: `stop` | `wrap` = `stop`;\n\n /**\n * Element-mode filter predicate (also driven by incremental filter-search).\n * Light-DOM items for which it returns `false` are removed from layout and\n * navigation. For data mode use {@link itemFilterPredicate}.\n */\n @property({ attribute: false })\n filterPredicate: ((el: Element) => boolean) | undefined;\n\n /**\n * Data-mode filter predicate. Items for which it returns `false` are removed\n * from layout and navigation without any element being materialised.\n */\n @property({ attribute: false })\n itemFilterPredicate: ((item: T, index: number) => boolean) | undefined;\n\n /**\n * Space-separated subset of `navigate select filter` — limits which\n * incremental-search modes are offered (as commands and in-popover switches).\n * Unset ⇒ all three; empty ⇒ search disabled. See `docs/infra-incremental.md`.\n */\n @property({ type: String, attribute: `search-modes`, reflect: true })\n searchModes: string | undefined;\n\n /**\n * Data items for data-backed mode. When set along with `itemAdapter`, the\n * component operates in data mode instead of element mode.\n */\n @property({ attribute: false })\n items: readonly T[] = [];\n\n /**\n * Adapter for data-backed mode. When set, the component renders items from\n * the `items` array using this adapter instead of projecting light-DOM children.\n */\n @property({ attribute: false })\n itemAdapter: GridListDataAdapterType<T> | undefined;\n\n @state() private _scrollTop = 0;\n @state() private _scrollLeft = 0;\n @state() private _viewportHeight = 0;\n @state() private _viewportWidth = 0;\n\n #viewportRef = createRef<HTMLDivElement>();\n\n #resizeObserver?: ResizeObserver;\n #mutationObserver?: MutationObserver;\n #scrollRaf = 0;\n #items: Element[] = [];\n #itemWrap: `stop` | `wrap` = `stop`;\n\n /** Items added mid-list that are still playing their enter animation. */\n #entering = new Set<Element>();\n /** Timer that ends the `[reflowing]` window opened by a mid-list insertion. */\n #reflowTimer = 0;\n\n /**\n * When cell geometry changes (zoom), the item nearest the viewport centre is\n * captured here in `willUpdate` and the scroll position is corrected in\n * `updated` so that item stays put — rather than the grid anchoring to its\n * top-left corner.\n */\n #zoomAnchor?: { index: number; lineFrac: number };\n\n /**\n * The active view over the items. Element mode projects light-DOM children\n * through named `<slot>`s (owned by {@link ElementGridListSource}); data mode\n * renders from {@link items} via {@link itemAdapter}. Everything else here\n * addresses items by effective index + key and never branches on the mode.\n *\n * Initialised eagerly (element mode) so the public getters — `count`,\n * `selectedKeys`, … — are safe to touch before the first Lit update. The\n * first `willUpdate` rebuilds it once the light DOM / `itemAdapter` is known.\n */\n #sourceElement: ElementGridListSource | undefined = new ElementGridListSource(this);\n #source: GridListSource = this.#sourceElement!;\n\n readonly selection = new ListSelectionController<string>({\n getOrderedItems: () => this.#source.keys(),\n isSelectable: (k) => {\n const i = this.#source.indexOfKey(k);\n return i >= 0 && this.#source.isSelectable(i);\n },\n getModes: () => ({ selectionMode: this.selectionMode, interactionMode: this.interactionMode }),\n getKey: k => k,\n onChange: (selected, previous) => {\n this._syncItemStates();\n this.requestUpdate();\n this.#emitSelect(selected, previous);\n },\n });\n\n readonly tickled = new TickledController<string>(this, {\n // `keys()` is in effective order, so position === effective index — avoid a\n // per-key `indexOfKey` lookup (that would make this O(n²) for large lists).\n getItems: () =>\n this.#source.keys().map((k, i) => ({\n id: k,\n enabled: this.#source.isSelectable(i),\n })),\n onTickle: (k) => {\n if (k !== undefined)\n this.#emitItemEvent(`list-tickle`, k);\n },\n onStateChange: () => this._syncItemStates(),\n itemWrap: this.#itemWrap,\n hoverDisabled: () => this.disableHoverTickle,\n });\n\n /**\n * Incremental search (navigate / select / filter). Element- or data-backed\n * depending on the mode; the mode-agnostic surface (`renderField`, `open`,\n * `close`, `setModes`, …) is the same either way. See `docs/infra-incremental.md`.\n */\n search!: ListSearchController<unknown>;\n\n #searchMode: `element` | `data` | undefined;\n #searchFilterKeys: ReadonlySet<string> | undefined;\n\n /** (Re)build {@link search} for the current mode; a no-op if it already matches. */\n #ensureSearch(): void {\n const mode = this.itemAdapter !== undefined ? `data` : `element`;\n if (this.#searchMode === mode && this.search !== undefined)\n return;\n\n const previous = this.search as ListSearchController<unknown> | undefined;\n if (previous) {\n previous.hostDisconnected();\n this.removeController(previous);\n }\n\n this.search = (mode === `data`\n ? new ListSearchController<DataListSearchItem>(this, {\n adapter: createDataListSearchAdapter(this, {\n rawCount: () => this.#source?.rawCount ?? 0,\n rawKeyAt: i => this.#source?.rawKeyAt(i) ?? ``,\n rawSearchTextAt: i => this.#source?.rawSearchTextAt(i) ?? ``,\n onFilterChange: (keys) => {\n this.#searchFilterKeys = keys;\n this.#source?.setFilters({ searchKeys: keys });\n this.requestUpdate();\n void this.updateComplete.then(() => this.#emitViewportChange());\n },\n onActivate: key => this.#onCellActivate(key),\n getHighlightRoot: () => this.renderRoot as ShadowRoot | Element,\n }),\n })\n : new ListSearchController<Element>(this, {\n adapter: createElementListSearchAdapter(this, {\n itemSelector: ITEM_SELECTOR,\n getLabel: el =>\n el.getAttribute(`data-label`)\n ?? el.querySelector(`[data-grid-list-caption]`)?.textContent?.trim()\n ?? el.getAttribute(`data-search-label`)\n ?? el.textContent?.trim()\n ?? ``,\n highlightKey: `grid-list-search`,\n }),\n })) as ListSearchController<unknown>;\n this.#searchMode = mode;\n }\n\n #searchRegistry: CommandRegistry<unknown> | undefined;\n #unregisterSearchCommands: (() => void) | undefined;\n\n /** Command registry carrying this grid's `list.search.*` commands. */\n get registry(): CommandRegistry<unknown> | undefined {\n return this.#searchRegistry;\n }\n\n /** Effective search modes: this component's capability (all three) ∩ the `search-modes` attribute. */\n #effectiveSearchModes(): ListSearchMode[] {\n const raw = this.searchModes?.trim();\n if (raw === undefined)\n return [...LIST_SEARCH_MODES];\n const requested = new Set(raw.split(/\\s+/));\n return LIST_SEARCH_MODES.filter(m => requested.has(m));\n }\n\n #syncSearchCommands(): void {\n if (!this.#searchRegistry || !this.search)\n return;\n this.search.setModes(this.#effectiveSearchModes());\n this.#unregisterSearchCommands?.();\n this.#unregisterSearchCommands = registerListSearchCommands(this.#searchRegistry, this.search);\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (!this.hasAttribute(`tabindex`)) {\n this.tabIndex = 0;\n }\n if (!this.hasAttribute(`role`)) {\n this.setAttribute(`role`, `listbox`);\n }\n this.#syncAriaMultiselectable();\n // Reflect caption defaults up-front so the `:host([caption-*])` style rules\n // apply even when the consumer never sets the attributes.\n for (const [attr, value] of [\n [`caption-style`, this.captionStyle],\n [`caption-align`, this.captionAlign],\n [`caption-position`, this.captionPosition],\n [`caption-backdrop`, this.captionBackdrop],\n ] as const) {\n if (!this.hasAttribute(attr)) {\n this.setAttribute(attr, value);\n }\n }\n this.addEventListener(`keydown`, this._onKeyDown as EventListener);\n this.#mutationObserver ??= new MutationObserver(records => this.#onChildrenChanged(records));\n this.#mutationObserver.observe(this, { childList: true, attributeFilter: [`hidden`] });\n this.#searchRegistry ??= createCommandRegistry();\n this.#ensureSearch();\n this.#syncSearchCommands();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener(`keydown`, this._onKeyDown as EventListener);\n this.#resizeObserver?.disconnect();\n this.#resizeObserver = undefined;\n this.#mutationObserver?.disconnect();\n this.#cancelScrollRaf();\n this.#unregisterSearchCommands?.();\n this.#unregisterSearchCommands = undefined;\n this.#searchRegistry = undefined;\n if (this.#reflowTimer) {\n clearTimeout(this.#reflowTimer);\n this.#reflowTimer = 0;\n }\n }\n\n #onChildrenChanged(records?: MutationRecord[]): void {\n const previousLength = this.#items.length;\n this.#syncItems();\n // Prune selection / cursor for elements that were actually removed. Read the\n // records directly rather than diffing `keys()` before/after: a pending\n // `requestUpdate()` can let `updated()` re-sync the source before this\n // callback runs, which would hide the removal from a diff.\n const removedKeys: string[] = [];\n for (const record of records ?? []) {\n for (const node of record.removedNodes) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n continue;\n const key = keyForElement(node as Element);\n if (key !== undefined)\n removedKeys.push(key);\n }\n }\n if (removedKeys.length > 0) {\n const present = new Set(this.#source.keys());\n const gone = removedKeys.filter(k => !present.has(k));\n if (gone.length > 0)\n this.selection.removeKeys(gone, k => k);\n const tickledId = this.tickled.tickledId;\n if (tickledId && gone.includes(tickledId))\n this.tickled.focus(undefined);\n }\n this._syncItemStates();\n this.#noteChildMutation(previousLength, records);\n this.requestUpdate();\n void this.updateComplete.then(() => this.#emitViewportChange());\n }\n\n /**\n * When the child list changes mid-list — not a first populate or a wholesale\n * replace — mark any newcomers for an enter animation and open a short\n * `[reflowing]` window so the cells that shift slide to their new position\n * instead of snapping. Removals get the same window (the survivors slide to\n * close the gap). The window is bounded in time so the transform transition\n * never bleeds into scroll virtualisation.\n *\n * Reads the `MutationRecord`s directly rather than diffing `#items`, because a\n * `requestUpdate()` between the DOM change and this callback (e.g. a caller\n * that clears the selection before removing) lets `updated()` re-sync `#items`\n * first, which would hide the change from a naive diff.\n */\n #noteChildMutation(previousLength: number, records?: MutationRecord[]): void {\n if (!this.hasUpdated || !records || records.length === 0)\n return;\n const currentSet = new Set(this.#items);\n const added: Element[] = [];\n let removedCount = 0;\n for (const record of records) {\n for (const node of record.addedNodes) {\n if (node.nodeType === Node.ELEMENT_NODE && currentSet.has(node as Element))\n added.push(node as Element);\n }\n for (const node of record.removedNodes) {\n if (node.nodeType === Node.ELEMENT_NODE)\n removedCount++;\n }\n }\n // Nothing to animate, a first populate, or a wholesale replace (every\n // current item is new) — leave it alone.\n if (added.length === 0 && removedCount === 0)\n return;\n if (previousLength === 0 && removedCount === 0)\n return;\n if (added.length > 0 && added.length === this.#items.length)\n return;\n for (const el of added)\n this.#entering.add(el);\n this.toggleAttribute(`reflowing`, true);\n if (this.#reflowTimer)\n clearTimeout(this.#reflowTimer);\n this.#reflowTimer = window.setTimeout(() => {\n this.#reflowTimer = 0;\n this.#entering.clear();\n this.toggleAttribute(`reflowing`, false);\n this.requestUpdate();\n }, REFLOW_WINDOW_MS);\n }\n\n #emitViewportChange(): void {\n this._dispatch<GridListViewportChangeDetail>(`grid-list-viewport-change`, {\n renderedKeys: this.renderedKeys(),\n visibleKeys: this.visibleKeys(),\n });\n }\n\n protected override firstUpdated(): void {\n if (!this.#source)\n this.#rebuildSource();\n const el = this.#viewportRef.value;\n if (!el)\n return;\n this.#resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const w = entry.contentRect.width;\n const h = entry.contentRect.height;\n // A zero-sized observation (e.g. an inactive dock tab) must never\n // replace the last usable geometry — see #measureViewport / refreshLayout.\n if (w <= 0 || h <= 0)\n return;\n this._viewportWidth = w;\n this._viewportHeight = h;\n }\n });\n this.#resizeObserver.observe(el);\n this.#measureViewport(false);\n this.#syncItems();\n this.requestUpdate();\n }\n\n /** (Re)construct {@link #source} for the current mode and re-apply the filters. */\n #rebuildSource(): void {\n if (this.itemAdapter !== undefined) {\n this.#source = new DataGridListSource<T>(this.items, this.itemAdapter);\n this.#sourceElement = undefined;\n } else {\n this.#sourceElement = new ElementGridListSource(this);\n this.#source = this.#sourceElement;\n }\n this.#applyFilters();\n if (this.#searchFilterKeys)\n this.#source.setFilters({ searchKeys: this.#searchFilterKeys });\n }\n\n /** Push the current filter predicate into whichever source is active. */\n #applyFilters(): void {\n if (this.#source instanceof ElementGridListSource) {\n const fp = this.filterPredicate;\n const src = this.#source;\n src.setFilters({ predicate: fp ? i => fp(src.rawElementAt(i)!) : undefined });\n this.#items = src.effectiveElements();\n } else if (this.#source instanceof DataGridListSource) {\n const ip = this.itemFilterPredicate;\n const src = this.#source as DataGridListSource<T>;\n src.setFilters({ predicate: ip ? i => ip(src.rawItemAt(i) as T, i) : undefined });\n }\n }\n\n protected override willUpdate(changed: PropertyValues<this>): void {\n if (changed.has(`searchModes`)) {\n this.#syncSearchCommands();\n }\n if (changed.has(`filterPredicate`) || changed.has(`itemFilterPredicate`)) {\n this.#applyFilters();\n void this.updateComplete.then(() => this.#emitViewportChange());\n }\n\n const dataModeChanged = changed.has(`items`) || changed.has(`itemAdapter`);\n if (dataModeChanged) {\n this.#zoomAnchor = undefined;\n const prevKeys = this.#source?.keys() ?? [];\n const modeFlipped = (this.itemAdapter !== undefined)\n !== (this.#source instanceof DataGridListSource);\n this.#rebuildSource();\n const nextKeys = this.#source.keys();\n const nextKeySet = new Set(nextKeys);\n const removedKeys = prevKeys.filter(k => !nextKeySet.has(k));\n if (removedKeys.length > 0)\n this.selection.removeKeys(removedKeys, k => k);\n const tickledId = this.tickled.tickledId;\n if (tickledId && !nextKeySet.has(tickledId))\n this.tickled.focus(undefined);\n this._syncItemStates();\n // No requestUpdate() here — we're already in willUpdate, the render that\n // follows picks up the rebuilt source.\n void this.updateComplete.then(() => this.#emitViewportChange());\n if (modeFlipped) {\n this.#ensureSearch();\n this.#syncSearchCommands();\n }\n this.search?.refresh();\n }\n\n if (changed.has(`itemSize`) || changed.has(`gap`)) {\n this.itemSize = Math.max(MIN_ITEM_SIZE, Math.round(this.itemSize));\n this.gap = Math.max(0, Math.round(this.gap));\n }\n if (changed.has(`captionHeight`)) {\n this.captionHeight = Math.max(0, Math.round(this.captionHeight));\n }\n if (changed.has(`itemWrap`)) {\n this.#itemWrap = this.itemWrap;\n }\n\n const geometryChanged = changed.has(`itemSize`) || changed.has(`gap`)\n || changed.has(`captionStyle`) || changed.has(`captionHeight`)\n || changed.has(`orientation`);\n if (geometryChanged && this.hasUpdated && this.#source.count > 0) {\n const prevOrientation = (changed.get(`orientation`) as GridListOrientation | undefined) ?? this.orientation;\n const horizontal = prevOrientation === `row`;\n const prevOffset = horizontal ? this._scrollLeft : this._scrollTop;\n const prevExtent = horizontal ? this._viewportWidth : this._viewportHeight;\n if (prevExtent > 0 && prevOffset > 1) {\n const prevItemSize = (changed.get(`itemSize`) as number | undefined) ?? this.itemSize;\n const prevGap = (changed.get(`gap`) as number | undefined) ?? this.gap;\n const prevCaptionStyle = (changed.get(`captionStyle`) as GridListCaptionStyle | undefined) ?? this.captionStyle;\n const prevCaptionHeight = (changed.get(`captionHeight`) as number | undefined) ?? this.captionHeight;\n this.#captureCentreAnchor(prevOffset, prevExtent, prevItemSize, prevGap, prevCaptionStyle, prevCaptionHeight, prevOrientation);\n }\n }\n }\n\n #syncAriaMultiselectable(): void {\n this.setAttribute(\n `aria-multiselectable`,\n this.selection.canSelectMultiple() ? `true` : `false`,\n );\n }\n\n /** Full cell height: the square, plus a caption band in `separate` / `under`. */\n #rowHeight(): number {\n const captionBand = this.captionStyle === `separate` || this.captionStyle === `under`;\n return this.itemSize + (captionBand ? this.captionHeight : 0);\n }\n\n #captionFor(el: Element): string {\n return (\n el.getAttribute(`data-label`)\n ?? el.querySelector(`[data-grid-list-caption]`)?.textContent?.trim()\n ?? ``\n );\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n super.updated(changed);\n this.#syncItems();\n if (changed.has(`selectionMode`) || changed.has(`interactionMode`)) {\n this.selection.clearAnchor();\n this.#syncAriaMultiselectable();\n }\n if (changed.has(`orientation`)) {\n // Reset the axis that's no longer scrollable so a later switch back\n // doesn't inherit a stale offset.\n const vp = this.#viewportRef.value;\n if (vp) {\n if (this.orientation === `row`) {\n vp.scrollTop = 0;\n this._scrollTop = 0;\n } else {\n vp.scrollLeft = 0;\n this._scrollLeft = 0;\n }\n }\n }\n const vp = this.#viewportRef.value;\n if (vp) {\n if (Math.abs(vp.scrollTop - this._scrollTop) > 0.5) {\n vp.scrollTop = this._scrollTop;\n }\n if (Math.abs(vp.scrollLeft - this._scrollLeft) > 0.5) {\n vp.scrollLeft = this._scrollLeft;\n }\n }\n this.#applyZoomAnchor();\n }\n\n /** Re-anchor the scroll so the item captured in `willUpdate` stays centred. */\n #applyZoomAnchor(): void {\n if (!this.#zoomAnchor)\n return;\n const { index, lineFrac } = this.#zoomAnchor;\n this.#zoomAnchor = undefined;\n\n const vp = this.#viewportRef.value;\n if (!vp)\n return;\n const win = this.#currentWindow();\n\n if (win.horizontal) {\n if (this._viewportWidth <= 0)\n return;\n const targetCenter = (index + lineFrac) * win.colStride;\n const maxScroll = Math.max(0, win.totalWidth - this._viewportWidth);\n const next = Math.min(maxScroll, Math.max(0, targetCenter - this._viewportWidth / 2));\n if (Math.abs(vp.scrollLeft - next) > 0.5) {\n vp.scrollLeft = next;\n this._scrollLeft = next;\n }\n return;\n }\n\n if (this._viewportHeight <= 0)\n return;\n const line = win.cols > 0 ? Math.floor(index / win.cols) : 0;\n const targetCenter = (line + lineFrac) * win.rowStride;\n const maxScroll = Math.max(0, win.totalHeight - this._viewportHeight);\n const next = Math.min(maxScroll, Math.max(0, targetCenter - this._viewportHeight / 2));\n if (Math.abs(vp.scrollTop - next) > 0.5) {\n vp.scrollTop = next;\n this._scrollTop = next;\n }\n }\n\n #captureCentreAnchor(prevOffset: number, prevExtent: number, prevItemSize: number, prevGap: number, prevCaptionStyle: GridListCaptionStyle, prevCaptionHeight: number, prevOrientation: GridListOrientation): void {\n const count = this.#source?.count ?? 0;\n if (count === 0)\n return;\n const prevBand = prevCaptionStyle === `separate` || prevCaptionStyle === `under`;\n const prevRowHeight = prevItemSize + (prevBand ? prevCaptionHeight : 0);\n const horizontal = prevOrientation === `row`;\n const prev = computeGridWindow(\n count,\n prevItemSize,\n prevRowHeight,\n prevGap,\n this._viewportWidth,\n this._viewportHeight,\n prevOffset,\n prevOrientation,\n );\n const mainStride = horizontal ? prev.colStride : prev.rowStride;\n const centerLine = (prevOffset + prevExtent / 2) / mainStride;\n const lineFloor = Math.max(0, Math.floor(centerLine));\n const index = horizontal\n ? Math.min(count - 1, lineFloor)\n : Math.min(\n count - 1,\n lineFloor * prev.cols + Math.max(0, Math.floor((prev.cols - 1) / 2)),\n );\n this.#zoomAnchor = { index, lineFrac: centerLine - lineFloor };\n }\n\n /**\n * Re-read the viewport after an ancestor's visibility/layout changed.\n * Safe while hidden (zero-sized reads are ignored); forces a repaint of the\n * virtual window even when the size is unchanged.\n */\n refreshLayout(): void {\n this.#measureViewport(true);\n }\n\n /** Alias of {@link refreshLayout}. */\n remeasure(): void {\n this.#measureViewport(true);\n }\n\n #measureViewport(forceRender = false): void {\n const vp = this.#viewportRef.value;\n if (!vp)\n return;\n const w = vp.clientWidth;\n const h = vp.clientHeight;\n if (w <= 0 || h <= 0)\n return;\n const horizontal = this.orientation === `row`;\n const changed = w !== this._viewportWidth || h !== this._viewportHeight;\n if (changed) {\n const prevOffset = horizontal ? this._scrollLeft : this._scrollTop;\n const prevExtent = horizontal ? w : h;\n this.#captureCentreAnchor(\n prevOffset,\n prevExtent,\n this.itemSize,\n this.gap,\n this.captionStyle,\n this.captionHeight,\n this.orientation,\n );\n this._viewportWidth = w;\n this._viewportHeight = h;\n }\n if (forceRender && !changed) {\n this.#captureCentreAnchor(\n horizontal ? this._scrollLeft : this._scrollTop,\n horizontal ? w : h,\n this.itemSize,\n this.gap,\n this.captionStyle,\n this.captionHeight,\n this.orientation,\n );\n this.requestUpdate();\n }\n if (changed || forceRender) {\n void this.updateComplete.then(() => this.#emitViewportChange());\n }\n }\n\n /**\n * Element mode only: re-read the light DOM into the source and refresh the\n * `#items` cache + filter. A no-op in data mode (the source is rebuilt through\n * `#rebuildSource` when `items` / `itemAdapter` change).\n */\n #syncItems(): void {\n if (this.#source instanceof ElementGridListSource) {\n this.#source.refresh();\n this.#applyFilters();\n }\n }\n\n get count(): number {\n return this.#source.count;\n }\n\n /**\n * Number of cells currently rendered in the shadow DOM. With virtualization\n * this is roughly the visible rows plus overscan, regardless of how large\n * `count` is — useful for demos/diagnostics.\n */\n get renderedCount(): number {\n return this.renderRoot.querySelectorAll(`.cell`).length;\n }\n\n get selectedItems(): ReadonlySet<Element> {\n const els = new Set<Element>();\n for (const key of this.selection.selectedKeys) {\n const el = this.#source.resolveKeyToElement(key);\n if (el)\n els.add(el);\n }\n return els;\n }\n\n /**\n * The selection as stable `data-key` / `data-value` strings. Auto-assigned\n * keys for keyless light-DOM items are omitted (they are not a stable contract\n * for the consumer).\n */\n get selectedKeys(): ReadonlySet<string> {\n const out = new Set<string>();\n for (const k of this.selection.selectedKeys) {\n if (!isSyntheticKey(k))\n out.add(k);\n }\n return out;\n }\n\n select(item: Element): void {\n const key = keyForElement(item);\n if (key)\n this.selection.replace(key);\n }\n\n selectMany(items: Iterable<Element>): void {\n const keys: string[] = [];\n for (const item of items) {\n const key = keyForElement(item);\n if (key)\n keys.push(key);\n }\n this.selection.selectMany(keys);\n }\n\n deselect(item: Element): void {\n const key = keyForElement(item);\n if (key)\n this.selection.deselect(key);\n }\n\n clearSelection(): void {\n this.selection.clear();\n }\n\n selectAll(): void {\n if (this.selection.canSelectAll()) {\n this.selection.selectAll();\n }\n }\n\n /**\n * Replace the selection with the items carrying these keys. Unknown keys are\n * ignored; survives the host rebuilding the item set. Requires each item to\n * have a `data-key` (or `data-value`).\n */\n selectKeys(keys: Iterable<string>): void {\n this.selection.selectKeys(keys, k => k);\n }\n\n /** Add items by key to the selection (multi-select modes only). */\n addSelectedKeys(keys: Iterable<string>): void {\n this.selection.addKeys(keys, k => k);\n }\n\n /** Remove items by key from the selection. */\n removeSelectedKeys(keys: Iterable<string>): void {\n this.selection.removeKeys(keys, k => k);\n }\n\n /**\n * Data mode only: raw index of `key` in `this.items`, or `-1`. Data-mode keys\n * come straight from the adapter, so this is exact even under a filter/search.\n */\n #rawIndexOfKey(key: string): number {\n const adapter = this.itemAdapter;\n if (!adapter)\n return -1;\n return this.items.findIndex((it, i) => adapter.key(it, i) === key);\n }\n\n /**\n * Data mode: replace the whole item array. Selection and cursor keep any key\n * that is still present; keys that vanish are dropped (one `list-select`).\n * A no-op in element mode.\n */\n setItems(items: readonly T[]): void {\n if (this.itemAdapter)\n this.items = items;\n }\n\n /**\n * Data mode: append items to the end. Scroll position, selection and cursor\n * are untouched; appended items don't play the mid-list `[reflowing]` slide.\n * A no-op in element mode.\n */\n appendItems(items: readonly T[]): void {\n if (this.itemAdapter && items.length > 0)\n this.items = [...this.items, ...items];\n }\n\n /**\n * Data mode: replace the item carrying `key`. A visible cell re-renders in\n * place (the keyed subtree is kept); an offscreen item shows its new content\n * when scrolled into view. Scroll and selection are untouched. Unknown keys\n * are ignored.\n */\n updateItem(key: string, item: T): void {\n if (!this.itemAdapter)\n return;\n const idx = this.#rawIndexOfKey(key);\n if (idx < 0)\n return;\n const next = [...this.items];\n next[idx] = item;\n this.items = next;\n }\n\n /**\n * Data mode: remove the items carrying these keys. Removed keys are dropped\n * from selection and cursor (via the `items` change). A no-op in element mode.\n */\n removeItems(keys: Iterable<string>): void {\n if (!this.itemAdapter)\n return;\n const adapter = this.itemAdapter;\n const keySet = new Set(keys);\n this.items = this.items.filter((it, i) => !keySet.has(adapter.key(it, i)));\n }\n\n /**\n * Data mode: drop every item. Selection and cursor are reset and one\n * `list-select` with empty sets is emitted. A no-op in element mode.\n */\n clearItems(): void {\n if (!this.itemAdapter)\n return;\n this.selection.reset();\n this.tickled.focus(undefined);\n this.items = [];\n this.#emitSelect(new Set(), new Set());\n }\n\n /** Move the cursor to the item with this key and scroll it into view. */\n focusKey(key: string): void {\n const index = this.#source.indexOfKey(key);\n if (index < 0)\n return;\n this.tickled.focus(key);\n this.#scrollItemIntoView(key);\n }\n\n /** The key of the item under the cursor, or `undefined`. */\n get tickledKey(): string | undefined {\n return this.tickled.tickledId;\n }\n\n /** Element mode: the light-DOM element carrying `key`, or `undefined`. */\n getItem(key: string): Element | undefined {\n return this.#source.resolveKeyToElement(key);\n }\n\n /** Element mode: the light-DOM element at effective index `index`. */\n getItemByIndex(index: number): Element | undefined {\n return this.#items[index];\n }\n\n /** Data mode: the data value carrying `key`, or `undefined`. */\n getData(key: string): T | undefined {\n return this.#source.resolveKeyToData(key) as T | undefined;\n }\n\n getKey(item: Element): string | undefined {\n return keyForElement(item);\n }\n\n /**\n * Keys of items in the rendered window (viewport + overscan) — the priority\n * set for prefetching. Use {@link visibleKeys} for the strict viewport\n * intersection. Auto-assigned keyless-item keys are omitted.\n */\n renderedKeys(): string[] {\n return this.#windowKeys(this.#currentWindow());\n }\n\n /**\n * Keys of items actually intersecting the viewport (no overscan).\n * Auto-assigned keyless-item keys are omitted.\n */\n visibleKeys(): string[] {\n return this.#windowKeys(this.#currentWindow(true));\n }\n\n #windowKeys(win: GridWindow): string[] {\n return this.#source\n .keys()\n .slice(win.startIndex, win.endIndex)\n .filter(k => !isSyntheticKey(k));\n }\n\n #currentWindow(noOverscan = false): GridWindow {\n const overscan = noOverscan ? 0 : 2;\n return computeGridWindow(\n this.#source.count,\n this.itemSize,\n this.#rowHeight(),\n this.gap,\n this._viewportWidth,\n this._viewportHeight,\n this.orientation === `row` ? this._scrollLeft : this._scrollTop,\n this.orientation,\n overscan,\n );\n }\n\n /** Adjust the scroll offset so `key` is fully within the viewport. */\n #scrollItemIntoView(key: string): void {\n const index = this.#source.indexOfKey(key);\n const vp = this.#viewportRef.value;\n if (index < 0 || !vp)\n return;\n const win = this.#currentWindow();\n\n if (win.horizontal) {\n const left = index * win.colStride;\n const right = left + this.itemSize;\n const viewLeft = vp.scrollLeft;\n let next = viewLeft;\n if (left < viewLeft)\n next = left;\n else if (right > viewLeft + this._viewportWidth)\n next = right - this._viewportWidth;\n if (next !== viewLeft) {\n vp.scrollLeft = next;\n this._scrollLeft = next;\n }\n return;\n }\n\n const row = win.cols > 0 ? Math.floor(index / win.cols) : 0;\n const top = row * win.rowStride;\n const bottom = top + this.#rowHeight();\n const viewTop = vp.scrollTop;\n let next = viewTop;\n if (top < viewTop)\n next = top;\n else if (bottom > viewTop + this._viewportHeight)\n next = bottom - this._viewportHeight;\n if (next !== viewTop) {\n vp.scrollTop = next;\n this._scrollTop = next;\n }\n }\n\n private _syncItemStates(): void {\n const selected = this.selection.selectedKeys;\n for (const el of this.#items) {\n const key = keyForElement(el);\n el.toggleAttribute(`data-tickled`, key !== undefined && this.tickled.isTickled(key));\n el.toggleAttribute(`data-selected`, key !== undefined && selected.has(key));\n }\n this.setAttribute(`data-input-mode`, this.tickled.inputMode);\n }\n\n #emitSelect(selected: ReadonlySet<string>, previous: ReadonlySet<string>): void {\n const selectedEls = new Set<Element>();\n const previousEls = new Set<Element>();\n for (const key of selected) {\n const el = this.#source.resolveKeyToElement(key);\n if (el)\n selectedEls.add(el);\n }\n for (const key of previous) {\n const el = this.#source.resolveKeyToElement(key);\n if (el)\n previousEls.add(el);\n }\n this._dispatch<GridListSelectDetail>(`list-select`, {\n selected: selectedEls,\n previous: previousEls,\n selectedKeys: this.selectedKeys,\n previousKeys: new Set([...previous].filter(k => !isSyntheticKey(k))),\n });\n }\n\n #emitItemEvent(name: `list-tickle` | `list-item-click` | `list-activate`, key: string): void {\n const index = this.#source.indexOfKey(key);\n const element = this.#source.resolveKeyToElement(key);\n const item = element ?? this.#source.resolveKeyToData(key);\n this._dispatch<GridListItemClickDetail>(name, { item, key, index, element });\n }\n\n private _dispatch<T>(name: string, detail: T): void {\n this.dispatchEvent(\n new CustomEvent<T>(name, {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n /** Selected keys, snapshotted once per render so the cell renderers don't each rebuild the set. */\n #selectedSnapshot: ReadonlySet<string> = new Set();\n\n protected override render(): ReturnType<typeof html> {\n const win = this.#currentWindow();\n this.#selectedSnapshot = this.selection.selectedKeys;\n const spacerStyle = win.horizontal\n ? `width:${win.totalWidth}px;height:100%`\n : `height:${win.totalHeight}px`;\n // Fade the edge that can still be scrolled towards (see ../styles/scroll-fade.ts).\n const scrollFade = win.horizontal ? `scroll-fade-x` : `scroll-fade-y`;\n return html`\n <div\n class=\"viewport ${scrollFade}\"\n part=\"viewport\"\n ${ref(this.#viewportRef)}\n data-input-mode=${this.tickled.inputMode}\n @scroll=${this.#onScroll}\n >\n <div class=\"spacer\" style=${spacerStyle}>\n ${this.#source instanceof DataGridListSource\n ? this.#renderDataCells(win)\n : this.#renderElementCells(win)}\n </div>\n </div>\n ${this.search?.renderField()}\n `;\n }\n\n #renderDataCells(win: GridWindow): ReturnType<typeof html> {\n const slice = Array.from({ length: win.endIndex - win.startIndex }, (_, i) => win.startIndex + i);\n return repeat(\n slice,\n index => this.#source.keyAt(index),\n index => this.#renderDataCell(index, win),\n ) as ReturnType<typeof html>;\n }\n\n #renderDataCell(index: number, win: GridWindow): ReturnType<typeof html> {\n const key = this.#source.keyAt(index);\n const selected = this.#selectedSnapshot.has(key);\n const tickled = this.tickled.isTickled(key);\n const style = this.captionStyle;\n const separate = style === `separate`;\n const caption = style === `hidden` ? `` : this.#source.labelAt(index);\n const showCaption = caption !== ``;\n const cellHeight = win.rowStride - this.gap;\n const x = win.horizontal ? index * win.colStride : columnOf(index, win.cols) * win.colStride;\n const y = win.horizontal\n ? Math.max(0, (this._viewportHeight - cellHeight) / 2)\n : rowOf(index, win.cols) * win.rowStride;\n const captionCss = separate\n ? `height:${Math.max(0, this.captionHeight - 4)}px`\n : style === `under`\n ? `height:${this.captionHeight}px`\n : ``;\n return html`\n <div\n class=\"cell${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}\"\n part=\"item${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}\"\n role=\"option\"\n aria-selected=${selected ? `true` : `false`}\n style=\"width:${this.itemSize}px;height:${cellHeight}px;transform:translate(${x}px,${y}px)\"\n data-index=${index}\n data-key=${key}\n ?data-selected=${selected}\n ?data-tickled=${tickled}\n @click=${(e: MouseEvent) => this.#onCellClick(e, key)}\n @dblclick=${() => this.#onCellActivate(key)}\n @pointerenter=${() => this.tickled.handlePointerEnter(key)}\n >\n <div class=\"content\" part=\"content\">${this.#source.renderCellContent(index)}</div>\n ${showCaption\n ? html`<div class=\"caption\" part=\"caption\" style=${captionCss} title=${caption}>${caption}</div>`\n : ``}\n </div>\n `;\n }\n\n #renderElementCells(win: GridWindow): ReturnType<typeof html> {\n const slice = this.#items.slice(win.startIndex, win.endIndex);\n return repeat(\n slice,\n (_el, i) => this.#source.keyAt(win.startIndex + i),\n (el, i) => this.#renderCell(el, win.startIndex + i, win),\n ) as ReturnType<typeof html>;\n }\n\n #renderCell(el: Element, index: number, win: GridWindow): ReturnType<typeof html> {\n const key = this.#source.keyAt(index);\n const selected = this.#selectedSnapshot.has(key);\n const tickled = this.tickled.isTickled(key);\n const entering = this.#entering.has(el);\n const style = this.captionStyle;\n const separate = style === `separate`;\n const caption = style === `hidden` ? `` : this.#captionFor(el);\n const showCaption = caption !== ``;\n const cellHeight = win.rowStride - this.gap;\n const x = win.horizontal ? index * win.colStride : columnOf(index, win.cols) * win.colStride;\n // `row`: a single row, vertically centred in the viewport.\n const y = win.horizontal\n ? Math.max(0, (this._viewportHeight - cellHeight) / 2)\n : rowOf(index, win.cols) * win.rowStride;\n const thumbStyle = separate ? `height:${this.itemSize}px` : ``;\n const captionCss = separate\n ? `height:${Math.max(0, this.captionHeight - 4)}px`\n : style === `under`\n ? `height:${this.captionHeight}px`\n : ``;\n return html`\n <div\n class=\"cell${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}${entering ? ` entering` : ``}\"\n part=\"item${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}\"\n role=\"option\"\n aria-selected=${selected ? `true` : `false`}\n style=\"width:${this.itemSize}px;height:${cellHeight}px;transform:translate(${x}px,${y}px)\"\n data-index=${index}\n data-key=${key}\n ?data-selected=${selected}\n ?data-tickled=${tickled}\n @click=${(e: MouseEvent) => this.#onCellClick(e, key)}\n @dblclick=${() => this.#onCellActivate(key)}\n @pointerenter=${() => this.tickled.handlePointerEnter(key)}\n >\n <div class=\"thumb\" part=\"thumb\" style=${thumbStyle}>\n ${this.#source.renderCellContent(index)}\n </div>\n ${showCaption\n ? html`<div class=\"caption\" part=\"caption\" style=${captionCss} title=${caption}>${caption}</div>`\n : ``}\n </div>\n `;\n }\n\n #onScroll = (e: Event): void => {\n const el = e.target as HTMLDivElement;\n this.#cancelScrollRaf();\n this.#scrollRaf = requestAnimationFrame(() => {\n this.#scrollRaf = 0;\n this._scrollTop = el.scrollTop;\n this._scrollLeft = el.scrollLeft;\n this.#emitViewportChange();\n });\n };\n\n #cancelScrollRaf(): void {\n if (this.#scrollRaf !== 0) {\n cancelAnimationFrame(this.#scrollRaf);\n this.#scrollRaf = 0;\n }\n }\n\n #onCellClick(e: MouseEvent, key: string): void {\n this.selection.handleClick(key, e);\n this.#emitItemEvent(`list-item-click`, key);\n }\n\n #onCellActivate(key: string): void {\n if (this.interactionMode !== `manual`) {\n this.selection.replace(key);\n }\n this.#emitItemEvent(`list-activate`, key);\n }\n\n private _onKeyDown = (e: KeyboardEvent): void => {\n if ((e.ctrlKey || e.metaKey) && e.key === `f`) {\n return;\n }\n if ((e.ctrlKey || e.metaKey) && e.key === `a`) {\n if (this.selection.canSelectAll()) {\n e.preventDefault();\n this.selection.selectAll();\n }\n return;\n }\n\n const count = this.#source.count;\n if (count === 0)\n return;\n\n const win = this.#currentWindow();\n const move = (delta: number): void => {\n const from = this.tickled.tickledId\n ? this.#source.indexOfKey(this.tickled.tickledId)\n : -1;\n const nextIndex = Math.max(0, Math.min(count - 1, from + delta));\n const nextKey = this.#source.keyAt(nextIndex);\n if (nextKey) {\n this.tickled.handleKeyboardNavigation(nextKey);\n this.#scrollItemIntoView(nextKey);\n }\n };\n\n // In `grid` a vertical arrow jumps a whole row; in `row`/`column` every\n // arrow just steps one item along the single line.\n const perLine = this.orientation === `grid` ? win.cols : 1;\n\n switch (e.key) {\n case `ArrowLeft`:\n move(-1);\n break;\n case `ArrowRight`:\n move(1);\n break;\n case `ArrowUp`:\n move(-perLine);\n break;\n case `ArrowDown`:\n move(perLine);\n break;\n case `Home`:\n this.tickled.handleKeyboardNavigation(`first`);\n if (this.tickled.tickledId) {\n this.#scrollItemIntoView(this.tickled.tickledId);\n }\n break;\n case `End`:\n this.tickled.handleKeyboardNavigation(`last`);\n if (this.tickled.tickledId) {\n this.#scrollItemIntoView(this.tickled.tickledId);\n }\n break;\n case `PageUp`:\n case `PageDown`: {\n const mainExtent = win.horizontal ? this._viewportWidth : this._viewportHeight;\n const mainStride = win.horizontal ? win.colStride : win.rowStride;\n const visibleLines = Math.max(1, Math.floor(mainExtent / mainStride));\n move((e.key === `PageDown` ? 1 : -1) * visibleLines * perLine);\n break;\n }\n case `Enter`: {\n const id = this.tickled.tickledId;\n if (id) {\n e.preventDefault();\n this.#emitItemEvent(`list-item-click`, id);\n this.#onCellActivate(id);\n }\n break;\n }\n case ` `: {\n const id = this.tickled.tickledId;\n if (id) {\n e.preventDefault();\n this.selection.handleSpace(id);\n }\n break;\n }\n case `Escape`:\n this.selection.clear();\n break;\n default:\n return;\n }\n e.preventDefault();\n };\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-grid-list': GridListElement;\n }\n}\n\nexport interface GridListElement {\n addEventListener<K extends keyof GridListFullElementEventMap>(\n type: K,\n listener: (this: GridListElement, ev: GridListFullElementEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
//#region src/interaction/list-selection-controller.ts
|
|
2
|
-
/** Whether the interaction mode allows building a multi-item selection. */
|
|
3
|
-
function isMultiMode(mode) {
|
|
4
|
-
return mode === `checked` || mode === `sticky` || mode === `vscode` || mode === `standard`;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Generic selection state machine shared by list components.
|
|
8
|
-
*
|
|
9
|
-
* Owns the selected set plus the range-selection anchor, the selection
|
|
10
|
-
* primitives (`replace` / `toggle` / `rangeTo` / `clear` / `selectAll` /
|
|
11
|
-
* `selectMany` / `deselect`) and the per-interactionMode gesture routers
|
|
12
|
-
* (`handleClick` / `handleSpace`).
|
|
13
|
-
*
|
|
14
|
-
* Identity is generic: both `ixfx-vertical-list` and `ixfx-detail-list` use
|
|
15
|
-
* `ListSelectionController<Element>`.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* const selection = new ListSelectionController<Element>({
|
|
20
|
-
* getOrderedItems: () => this.getVisibleItems(),
|
|
21
|
-
* isSelectable: el => !el.hasAttribute(`not-checked`),
|
|
22
|
-
* getModes: () => ({ selectionMode: this.selectionMode, interactionMode: this.interactionMode }),
|
|
23
|
-
* onChange: (selected, previous) => {
|
|
24
|
-
* this.syncItemStates();
|
|
25
|
-
* this.dispatchEvent(new CustomEvent(`list-select`, { detail: { selected, previous } }));
|
|
26
|
-
* },
|
|
27
|
-
* });
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
var ListSelectionController = class {
|
|
31
|
-
#config;
|
|
32
|
-
#selected = /* @__PURE__ */ new Set();
|
|
33
|
-
#anchor;
|
|
34
|
-
constructor(config) {
|
|
35
|
-
this.#config = config;
|
|
36
|
-
}
|
|
37
|
-
/** The current selection set (read-only). */
|
|
38
|
-
get selected() {
|
|
39
|
-
return this.#selected;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* The current selection as stable string keys (read-only). Empty unless a
|
|
43
|
-
* `getKey` was supplied in config. Items whose `getKey` returns `undefined`
|
|
44
|
-
* are omitted.
|
|
45
|
-
*/
|
|
46
|
-
get selectedKeys() {
|
|
47
|
-
const getKey = this.#config.getKey;
|
|
48
|
-
const out = /* @__PURE__ */ new Set();
|
|
49
|
-
if (getKey) for (const item of this.#selected) {
|
|
50
|
-
const key = getKey(item);
|
|
51
|
-
if (key !== void 0) out.add(key);
|
|
52
|
-
}
|
|
53
|
-
return out;
|
|
54
|
-
}
|
|
55
|
-
/** Anchor for range (shift) selection — the last plainly-selected item. */
|
|
56
|
-
get anchor() {
|
|
57
|
-
return this.#anchor;
|
|
58
|
-
}
|
|
59
|
-
/** Whether "select all" (Cmd/Ctrl+A) is currently permitted. */
|
|
60
|
-
canSelectAll() {
|
|
61
|
-
return this.canSelectMultiple() && this.#config.getModes().selectionMode !== `none`;
|
|
62
|
-
}
|
|
63
|
-
/** Whether a multi-item selection can be built in the current modes. */
|
|
64
|
-
canSelectMultiple() {
|
|
65
|
-
const { selectionMode, interactionMode } = this.#config.getModes();
|
|
66
|
-
return selectionMode === `multiple` || isMultiMode(interactionMode);
|
|
67
|
-
}
|
|
68
|
-
/** Replace the entire selection with a single item; updates the anchor. */
|
|
69
|
-
replace(item) {
|
|
70
|
-
if (this.#config.getModes().selectionMode === `none`) return;
|
|
71
|
-
if (!this.#config.isSelectable(item)) return;
|
|
72
|
-
this.#anchor = item;
|
|
73
|
-
this.#apply(/* @__PURE__ */ new Set([item]));
|
|
74
|
-
}
|
|
75
|
-
/** Toggle a single item in/out of the selection without replacing it. Anchor unchanged. */
|
|
76
|
-
toggle(item) {
|
|
77
|
-
const { selectionMode, interactionMode } = this.#config.getModes();
|
|
78
|
-
if (!this.#config.isSelectable(item)) return;
|
|
79
|
-
if (selectionMode === `none` && interactionMode !== `checked` && interactionMode !== `sticky`) return;
|
|
80
|
-
const next = new Set(this.#selected);
|
|
81
|
-
if (next.has(item)) next.delete(item);
|
|
82
|
-
else {
|
|
83
|
-
if (!this.canSelectMultiple()) next.clear();
|
|
84
|
-
next.add(item);
|
|
85
|
-
}
|
|
86
|
-
this.#apply(next);
|
|
87
|
-
}
|
|
88
|
-
/** Select the contiguous ordered range from the anchor to `item`. Anchor unchanged. */
|
|
89
|
-
rangeTo(item) {
|
|
90
|
-
if (this.#config.getModes().selectionMode === `none`) return;
|
|
91
|
-
if (this.#anchor === void 0) {
|
|
92
|
-
this.replace(item);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const ordered = this.#config.getOrderedItems();
|
|
96
|
-
const anchorIdx = ordered.indexOf(this.#anchor);
|
|
97
|
-
const targetIdx = ordered.indexOf(item);
|
|
98
|
-
if (anchorIdx === -1 || targetIdx === -1) {
|
|
99
|
-
this.replace(item);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
const start = Math.min(anchorIdx, targetIdx);
|
|
103
|
-
const end = Math.max(anchorIdx, targetIdx);
|
|
104
|
-
this.#apply(new Set(ordered.slice(start, end + 1).filter((t) => this.#config.isSelectable(t))));
|
|
105
|
-
}
|
|
106
|
-
/** Clear the selection. No change event when the selection is already empty. */
|
|
107
|
-
clear() {
|
|
108
|
-
if (this.#selected.size === 0) return;
|
|
109
|
-
this.#apply(/* @__PURE__ */ new Set());
|
|
110
|
-
}
|
|
111
|
-
/** Select every ordered item that passes `isSelectable`. */
|
|
112
|
-
selectAll() {
|
|
113
|
-
const ordered = this.#config.getOrderedItems();
|
|
114
|
-
if (ordered.length === 0) return;
|
|
115
|
-
const next = new Set(ordered.filter((t) => this.#config.isSelectable(t)));
|
|
116
|
-
if (next.size === 0) return;
|
|
117
|
-
this.#apply(next);
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Replace the selection with many items at once. Requires
|
|
121
|
-
* `selectionMode="multiple"`, throws otherwise. Updates the anchor.
|
|
122
|
-
*/
|
|
123
|
-
selectMany(items) {
|
|
124
|
-
const { selectionMode } = this.#config.getModes();
|
|
125
|
-
if (selectionMode !== `multiple`) throw new Error(`selectMany requires selectionMode="multiple" (current: "${selectionMode}")`);
|
|
126
|
-
const next = new Set([...items].filter((t) => this.#config.isSelectable(t)));
|
|
127
|
-
if (next.size === 0) return;
|
|
128
|
-
this.#anchor = [...next].at(-1);
|
|
129
|
-
this.#apply(next);
|
|
130
|
-
}
|
|
131
|
-
/** Remove a single item from the selection. */
|
|
132
|
-
deselect(item) {
|
|
133
|
-
if (!this.#selected.has(item)) return;
|
|
134
|
-
const next = new Set(this.#selected);
|
|
135
|
-
next.delete(item);
|
|
136
|
-
this.#apply(next);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Whether the current modes permit a selection of more than one item.
|
|
140
|
-
* Stricter than `canSelectMultiple()`: `standard` / `implicit` only count
|
|
141
|
-
* as multi when `selectionMode="multiple"`.
|
|
142
|
-
*/
|
|
143
|
-
#effectiveMulti() {
|
|
144
|
-
const { selectionMode, interactionMode } = this.#config.getModes();
|
|
145
|
-
return selectionMode === `multiple` || interactionMode === `checked` || interactionMode === `sticky` || interactionMode === `vscode`;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Replace the selection with the items resolved from `keys`. Keys that
|
|
149
|
-
* `resolve` cannot map, or that resolve to a non-selectable item, are
|
|
150
|
-
* ignored. `selectionMode="none"` is a no-op. When multi-select is not
|
|
151
|
-
* available, only the last resolvable key is kept. `onChange` fires once.
|
|
152
|
-
*/
|
|
153
|
-
selectKeys(keys, resolve) {
|
|
154
|
-
if (this.#config.getModes().selectionMode === `none`) return;
|
|
155
|
-
let next = /* @__PURE__ */ new Set();
|
|
156
|
-
for (const key of new Set(keys)) {
|
|
157
|
-
const item = resolve(key);
|
|
158
|
-
if (item !== void 0 && this.#config.isSelectable(item)) next.add(item);
|
|
159
|
-
}
|
|
160
|
-
if (!this.#effectiveMulti() && next.size > 1) {
|
|
161
|
-
const last = [...next].at(-1);
|
|
162
|
-
next = last === void 0 ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([last]);
|
|
163
|
-
}
|
|
164
|
-
this.#anchor = [...next].at(-1);
|
|
165
|
-
this.#apply(next);
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Add the items resolved from `keys` to the selection (multi-select only;
|
|
169
|
-
* a no-op otherwise). `onChange` fires once if anything changed.
|
|
170
|
-
*/
|
|
171
|
-
addKeys(keys, resolve) {
|
|
172
|
-
if (!this.#effectiveMulti()) return;
|
|
173
|
-
const next = new Set(this.#selected);
|
|
174
|
-
let changed = false;
|
|
175
|
-
for (const key of new Set(keys)) {
|
|
176
|
-
const item = resolve(key);
|
|
177
|
-
if (item !== void 0 && !next.has(item) && this.#config.isSelectable(item)) {
|
|
178
|
-
next.add(item);
|
|
179
|
-
changed = true;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
if (changed) this.#apply(next);
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Remove the items resolved from `keys` from the selection. `onChange`
|
|
186
|
-
* fires once if anything changed.
|
|
187
|
-
*/
|
|
188
|
-
removeKeys(keys, resolve) {
|
|
189
|
-
if (this.#selected.size === 0) return;
|
|
190
|
-
const next = new Set(this.#selected);
|
|
191
|
-
let changed = false;
|
|
192
|
-
for (const key of new Set(keys)) {
|
|
193
|
-
const item = resolve(key);
|
|
194
|
-
if (item !== void 0 && next.delete(item)) changed = true;
|
|
195
|
-
}
|
|
196
|
-
if (changed) this.#apply(next);
|
|
197
|
-
}
|
|
198
|
-
/** Route a click on `item` according to the current interaction mode. */
|
|
199
|
-
handleClick(item, gesture) {
|
|
200
|
-
const { selectionMode, interactionMode: mode } = this.#config.getModes();
|
|
201
|
-
if (mode === `manual`) return;
|
|
202
|
-
if (selectionMode === `none` && mode !== `checked` && mode !== `sticky`) return;
|
|
203
|
-
switch (mode) {
|
|
204
|
-
case `implicit`:
|
|
205
|
-
this.replace(item);
|
|
206
|
-
break;
|
|
207
|
-
case `standard`:
|
|
208
|
-
case `vscode`:
|
|
209
|
-
if (gesture.ctrlKey || gesture.metaKey) {
|
|
210
|
-
if (selectionMode === `multiple` || mode === `vscode`) this.toggle(item);
|
|
211
|
-
} else if (gesture.shiftKey && (selectionMode === `multiple` || mode === `vscode`)) this.rangeTo(item);
|
|
212
|
-
else this.replace(item);
|
|
213
|
-
break;
|
|
214
|
-
case `checked`: break;
|
|
215
|
-
case `sticky`: this.toggle(item);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
/** Route a Space keypress on `item` according to the current interaction mode. */
|
|
219
|
-
handleSpace(item) {
|
|
220
|
-
const mode = this.#config.getModes().interactionMode;
|
|
221
|
-
if (mode === `manual`) return;
|
|
222
|
-
if (mode === `implicit`) this.replace(item);
|
|
223
|
-
else this.toggle(item);
|
|
224
|
-
}
|
|
225
|
-
/** Drop the range-selection anchor (e.g. when the interaction mode changes). */
|
|
226
|
-
clearAnchor() {
|
|
227
|
-
this.#anchor = void 0;
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Re-key the selection across an identity boundary — e.g. when items are
|
|
231
|
-
* wrapped/unwrapped and the selected Element references change. Silent:
|
|
232
|
-
* no `onChange` is fired.
|
|
233
|
-
*/
|
|
234
|
-
remap(fn) {
|
|
235
|
-
if (this.#selected.size === 0) return;
|
|
236
|
-
this.#selected = new Set([...this.#selected].map(fn));
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Hard reset: empty the selection and clear the anchor without firing
|
|
240
|
-
* `onChange` (e.g. when all items are removed).
|
|
241
|
-
*/
|
|
242
|
-
reset() {
|
|
243
|
-
this.#selected = /* @__PURE__ */ new Set();
|
|
244
|
-
this.#anchor = void 0;
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Replace the selection and anchor silently (no `onChange`). Used to
|
|
248
|
-
* re-key the selection when items are rebuilt from data — e.g. matching
|
|
249
|
-
* new elements to previously selected keys.
|
|
250
|
-
*/
|
|
251
|
-
seed(items, anchor) {
|
|
252
|
-
this.#selected = new Set(items);
|
|
253
|
-
this.#anchor = anchor;
|
|
254
|
-
}
|
|
255
|
-
#apply(next) {
|
|
256
|
-
const previous = this.#selected;
|
|
257
|
-
this.#selected = next;
|
|
258
|
-
this.#config.onChange(next, previous);
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
//#endregion
|
|
262
|
-
export { isMultiMode as n, ListSelectionController as t };
|
|
263
|
-
|
|
264
|
-
//# sourceMappingURL=list-selection-controller-DTFI40li.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"list-selection-controller-DTFI40li.js","names":["#config","#selected","#anchor","#apply","#effectiveMulti"],"sources":["../src/interaction/list-selection-controller.ts"],"sourcesContent":["import type { ListInteractionMode, ListSelectionMode } from './list-selection-types.js';\n\n/** Modifier state needed to route a click gesture per interaction mode. */\nexport type ListSelectionGesture = {\n readonly ctrlKey: boolean;\n readonly metaKey: boolean;\n readonly shiftKey: boolean;\n};\n\nexport type ListSelectionModes = {\n readonly selectionMode: ListSelectionMode;\n readonly interactionMode: ListInteractionMode;\n};\n\n/**\n * Configuration supplied by the host component.\n *\n * The controller owns selection *state* only — everything DOM- or\n * component-specific is expressed through these callbacks.\n */\nexport type ListSelectionControllerConfig<T> = {\n /** Visible items in navigation order — used for range selection and select-all. */\n readonly getOrderedItems: () => readonly T[];\n /** Whether an item may join the selection (e.g. reject `not-checked` rows). */\n readonly isSelectable: (item: T) => boolean;\n /** Current modes; read on every gesture so property changes apply immediately. */\n readonly getModes: () => ListSelectionModes;\n /**\n * Fired after every selection change. The host syncs DOM state\n * (`data-selected` / `data-checked`), dispatches `list-select`, and\n * requests an update.\n */\n readonly onChange: (selected: ReadonlySet<T>, previous: ReadonlySet<T>) => void;\n /**\n * Optional: derive a stable string key for an item. Enables `selectedKeys`\n * and `selectKeys()`. When omitted, `selectedKeys` is always empty.\n */\n readonly getKey?: (item: T) => string | undefined;\n};\n\n/** Whether the interaction mode allows building a multi-item selection. */\nexport function isMultiMode(mode: ListInteractionMode): boolean {\n return mode === `checked` || mode === `sticky` || mode === `vscode` || mode === `standard`;\n}\n\n/**\n * Generic selection state machine shared by list components.\n *\n * Owns the selected set plus the range-selection anchor, the selection\n * primitives (`replace` / `toggle` / `rangeTo` / `clear` / `selectAll` /\n * `selectMany` / `deselect`) and the per-interactionMode gesture routers\n * (`handleClick` / `handleSpace`).\n *\n * Identity is generic: both `ixfx-vertical-list` and `ixfx-detail-list` use\n * `ListSelectionController<Element>`.\n *\n * @example\n * ```ts\n * const selection = new ListSelectionController<Element>({\n * getOrderedItems: () => this.getVisibleItems(),\n * isSelectable: el => !el.hasAttribute(`not-checked`),\n * getModes: () => ({ selectionMode: this.selectionMode, interactionMode: this.interactionMode }),\n * onChange: (selected, previous) => {\n * this.syncItemStates();\n * this.dispatchEvent(new CustomEvent(`list-select`, { detail: { selected, previous } }));\n * },\n * });\n * ```\n */\nexport class ListSelectionController<T> {\n readonly #config: ListSelectionControllerConfig<T>;\n #selected: ReadonlySet<T> = new Set();\n #anchor: T | undefined;\n\n constructor(config: ListSelectionControllerConfig<T>) {\n this.#config = config;\n }\n\n /** The current selection set (read-only). */\n get selected(): ReadonlySet<T> {\n return this.#selected;\n }\n\n /**\n * The current selection as stable string keys (read-only). Empty unless a\n * `getKey` was supplied in config. Items whose `getKey` returns `undefined`\n * are omitted.\n */\n get selectedKeys(): ReadonlySet<string> {\n const getKey = this.#config.getKey;\n const out = new Set<string>();\n if (getKey) {\n for (const item of this.#selected) {\n const key = getKey(item);\n if (key !== undefined) {\n out.add(key);\n }\n }\n }\n return out;\n }\n\n /** Anchor for range (shift) selection — the last plainly-selected item. */\n get anchor(): T | undefined {\n return this.#anchor;\n }\n\n /** Whether \"select all\" (Cmd/Ctrl+A) is currently permitted. */\n canSelectAll(): boolean {\n return this.canSelectMultiple() && this.#config.getModes().selectionMode !== `none`;\n }\n\n /** Whether a multi-item selection can be built in the current modes. */\n canSelectMultiple(): boolean {\n const { selectionMode, interactionMode } = this.#config.getModes();\n return selectionMode === `multiple` || isMultiMode(interactionMode);\n }\n\n // --- Selection primitives ---\n\n /** Replace the entire selection with a single item; updates the anchor. */\n replace(item: T): void {\n if (this.#config.getModes().selectionMode === `none`)\n return;\n if (!this.#config.isSelectable(item))\n return;\n this.#anchor = item;\n this.#apply(new Set([item]));\n }\n\n /** Toggle a single item in/out of the selection without replacing it. Anchor unchanged. */\n toggle(item: T): void {\n const { selectionMode, interactionMode } = this.#config.getModes();\n if (!this.#config.isSelectable(item))\n return;\n // checked/vscode/sticky modes imply multiple selection capacity\n if (selectionMode === `none` && interactionMode !== `checked` && interactionMode !== `sticky`)\n return;\n\n const next = new Set(this.#selected);\n if (next.has(item)) {\n next.delete(item);\n } else {\n if (!this.canSelectMultiple())\n next.clear();\n next.add(item);\n }\n this.#apply(next);\n }\n\n /** Select the contiguous ordered range from the anchor to `item`. Anchor unchanged. */\n rangeTo(item: T): void {\n if (this.#config.getModes().selectionMode === `none`)\n return;\n if (this.#anchor === undefined) {\n this.replace(item);\n return;\n }\n const ordered = this.#config.getOrderedItems();\n const anchorIdx = ordered.indexOf(this.#anchor);\n const targetIdx = ordered.indexOf(item);\n if (anchorIdx === -1 || targetIdx === -1) {\n this.replace(item);\n return;\n }\n const start = Math.min(anchorIdx, targetIdx);\n const end = Math.max(anchorIdx, targetIdx);\n this.#apply(new Set(ordered.slice(start, end + 1).filter(t => this.#config.isSelectable(t))));\n }\n\n /** Clear the selection. No change event when the selection is already empty. */\n clear(): void {\n if (this.#selected.size === 0)\n return;\n this.#apply(new Set());\n }\n\n /** Select every ordered item that passes `isSelectable`. */\n selectAll(): void {\n const ordered = this.#config.getOrderedItems();\n if (ordered.length === 0)\n return;\n const next = new Set(ordered.filter(t => this.#config.isSelectable(t)));\n if (next.size === 0)\n return;\n this.#apply(next);\n }\n\n /**\n * Replace the selection with many items at once. Requires\n * `selectionMode=\"multiple\"`, throws otherwise. Updates the anchor.\n */\n selectMany(items: Iterable<T>): void {\n const { selectionMode } = this.#config.getModes();\n if (selectionMode !== `multiple`) {\n throw new Error(`selectMany requires selectionMode=\"multiple\" (current: \"${selectionMode}\")`);\n }\n const next = new Set([...items].filter(t => this.#config.isSelectable(t)));\n if (next.size === 0)\n return;\n this.#anchor = [...next].at(-1);\n this.#apply(next);\n }\n\n /** Remove a single item from the selection. */\n deselect(item: T): void {\n if (!this.#selected.has(item))\n return;\n const next = new Set(this.#selected);\n next.delete(item);\n this.#apply(next);\n }\n\n // --- Key-based selection ---\n\n /**\n * Whether the current modes permit a selection of more than one item.\n * Stricter than `canSelectMultiple()`: `standard` / `implicit` only count\n * as multi when `selectionMode=\"multiple\"`.\n */\n #effectiveMulti(): boolean {\n const { selectionMode, interactionMode } = this.#config.getModes();\n return selectionMode === `multiple`\n || interactionMode === `checked`\n || interactionMode === `sticky`\n || interactionMode === `vscode`;\n }\n\n /**\n * Replace the selection with the items resolved from `keys`. Keys that\n * `resolve` cannot map, or that resolve to a non-selectable item, are\n * ignored. `selectionMode=\"none\"` is a no-op. When multi-select is not\n * available, only the last resolvable key is kept. `onChange` fires once.\n */\n selectKeys(keys: Iterable<string>, resolve: (key: string) => T | undefined): void {\n if (this.#config.getModes().selectionMode === `none`)\n return;\n let next = new Set<T>();\n for (const key of new Set(keys)) {\n const item = resolve(key);\n if (item !== undefined && this.#config.isSelectable(item)) {\n next.add(item);\n }\n }\n if (!this.#effectiveMulti() && next.size > 1) {\n const last = [...next].at(-1);\n next = last === undefined ? new Set() : new Set([last]);\n }\n this.#anchor = [...next].at(-1);\n this.#apply(next);\n }\n\n /**\n * Add the items resolved from `keys` to the selection (multi-select only;\n * a no-op otherwise). `onChange` fires once if anything changed.\n */\n addKeys(keys: Iterable<string>, resolve: (key: string) => T | undefined): void {\n if (!this.#effectiveMulti())\n return;\n const next = new Set(this.#selected);\n let changed = false;\n for (const key of new Set(keys)) {\n const item = resolve(key);\n if (item !== undefined && !next.has(item) && this.#config.isSelectable(item)) {\n next.add(item);\n changed = true;\n }\n }\n if (changed) {\n this.#apply(next);\n }\n }\n\n /**\n * Remove the items resolved from `keys` from the selection. `onChange`\n * fires once if anything changed.\n */\n removeKeys(keys: Iterable<string>, resolve: (key: string) => T | undefined): void {\n if (this.#selected.size === 0)\n return;\n const next = new Set(this.#selected);\n let changed = false;\n for (const key of new Set(keys)) {\n const item = resolve(key);\n if (item !== undefined && next.delete(item)) {\n changed = true;\n }\n }\n if (changed) {\n this.#apply(next);\n }\n }\n\n // --- Gesture routers ---\n\n /** Route a click on `item` according to the current interaction mode. */\n handleClick(item: T, gesture: ListSelectionGesture): void {\n const { selectionMode, interactionMode: mode } = this.#config.getModes();\n if (mode === `manual`)\n return;\n if (selectionMode === `none` && mode !== `checked` && mode !== `sticky`)\n return;\n\n switch (mode) {\n case `implicit`:\n // Single-select only; modifier keys have no effect\n this.replace(item);\n break;\n\n case `standard`:\n case `vscode`:\n if (gesture.ctrlKey || gesture.metaKey) {\n // Ctrl/Cmd+click: toggle item; requires effective multi-select\n if (selectionMode === `multiple` || mode === `vscode`) {\n this.toggle(item);\n }\n } else if (gesture.shiftKey && (selectionMode === `multiple` || mode === `vscode`)) {\n // Shift+click: range-select from anchor\n this.rangeTo(item);\n } else {\n // Plain click: replace selection, update anchor\n this.replace(item);\n }\n break;\n\n case `checked`:\n // Body clicks do nothing — only the checkbox column toggles.\n break;\n\n case `sticky`:\n // Every tap/click toggles — multi-select without modifier keys (touchscreens).\n this.toggle(item);\n break;\n }\n }\n\n /** Route a Space keypress on `item` according to the current interaction mode. */\n handleSpace(item: T): void {\n const mode = this.#config.getModes().interactionMode;\n if (mode === `manual`)\n return;\n if (mode === `implicit`) {\n // Space selects (same as click) in implicit mode\n this.replace(item);\n } else {\n // standard / vscode / checked / sticky: Space toggles\n this.toggle(item);\n }\n }\n\n // --- Maintenance ---\n\n /** Drop the range-selection anchor (e.g. when the interaction mode changes). */\n clearAnchor(): void {\n this.#anchor = undefined;\n }\n\n /**\n * Re-key the selection across an identity boundary — e.g. when items are\n * wrapped/unwrapped and the selected Element references change. Silent:\n * no `onChange` is fired.\n */\n remap(fn: (item: T) => T): void {\n if (this.#selected.size === 0)\n return;\n this.#selected = new Set([...this.#selected].map(fn));\n }\n\n /**\n * Hard reset: empty the selection and clear the anchor without firing\n * `onChange` (e.g. when all items are removed).\n */\n reset(): void {\n this.#selected = new Set();\n this.#anchor = undefined;\n }\n\n /**\n * Replace the selection and anchor silently (no `onChange`). Used to\n * re-key the selection when items are rebuilt from data — e.g. matching\n * new elements to previously selected keys.\n */\n seed(items: Iterable<T>, anchor?: T): void {\n this.#selected = new Set(items);\n this.#anchor = anchor;\n }\n\n #apply(next: ReadonlySet<T>): void {\n const previous = this.#selected;\n this.#selected = next;\n this.#config.onChange(next, previous);\n }\n}\n"],"mappings":"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"}
|