@ixfx/components 0.11.7 → 0.11.9
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 +165 -2
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +1160 -164
- package/bundle/index.js.map +1 -1
- package/dist/ac-text.d.ts +1 -1
- package/dist/{ac-token--_vCHXWw.d.ts → ac-token-TCLAZeVg.d.ts} +2 -2
- package/dist/{ac-token--_vCHXWw.d.ts.map → ac-token-TCLAZeVg.d.ts.map} +1 -1
- package/dist/ac-token.d.ts +1 -1
- package/dist/colour-picker.d.ts +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/crumbs.js +1 -1
- package/dist/{data-grid-C6EjCe5O.d.ts → data-grid-Oiyt5qbA.d.ts} +4 -4
- package/dist/{data-grid-C6EjCe5O.d.ts.map → data-grid-Oiyt5qbA.d.ts.map} +1 -1
- package/dist/data-grid.d.ts +1 -1
- package/dist/data-grid.js +1 -1
- package/dist/{detail-list-CgkOHYOa.d.ts → detail-list-B0waVNeS.d.ts} +37 -6
- package/dist/detail-list-B0waVNeS.d.ts.map +1 -0
- package/dist/detail-list.d.ts +4 -3
- package/dist/detail-list.js +271 -6
- package/dist/detail-list.js.map +1 -1
- package/dist/{grid-list-3fGhdaFA.js → grid-list-Bwaf61is.js} +659 -158
- package/dist/grid-list-Bwaf61is.js.map +1 -0
- package/dist/{grid-list-D6AfWfwn.d.ts → grid-list-bzKuNTtu.d.ts} +43 -2
- package/dist/grid-list-bzKuNTtu.d.ts.map +1 -0
- package/dist/grid-list.d.ts +3 -2
- package/dist/grid-list.js +2 -1
- package/dist/{grouped-item-lister-DETte0Dh.d.ts → grouped-item-lister-Bhyo-dkH.d.ts} +2 -2
- package/dist/{grouped-item-lister-DETte0Dh.d.ts.map → grouped-item-lister-Bhyo-dkH.d.ts.map} +1 -1
- package/dist/grouped-item-lister.d.ts +1 -1
- package/dist/grouping-CUKgmkMm.d.ts +63 -0
- package/dist/grouping-CUKgmkMm.d.ts.map +1 -0
- package/dist/grouping-RYPnHkrS.js +53 -0
- package/dist/grouping-RYPnHkrS.js.map +1 -0
- package/dist/{icon-CDGprSdE.d.ts → icon-DR_3rSyZ.d.ts} +2 -2
- package/dist/icon-DR_3rSyZ.d.ts.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/incr-search.d.ts +1 -1
- package/dist/{index-tq6f8007.d.ts → index-BOUaDIW-.d.ts} +2 -2
- package/dist/{index-tq6f8007.d.ts.map → index-BOUaDIW-.d.ts.map} +1 -1
- package/dist/{index-CoqtMSd9.d.ts → index-BUB5SICW.d.ts} +2 -2
- package/dist/{index-CoqtMSd9.d.ts.map → index-BUB5SICW.d.ts.map} +1 -1
- package/dist/{index-CMsmZ6Or.d.ts → index-BvxHQUME.d.ts} +2 -2
- package/dist/{index-CMsmZ6Or.d.ts.map → index-BvxHQUME.d.ts.map} +1 -1
- package/dist/{index-UvnoZUAo.d.ts → index-CJ5fshD3.d.ts} +2 -2
- package/dist/{index-UvnoZUAo.d.ts.map → index-CJ5fshD3.d.ts.map} +1 -1
- package/dist/{index-DyTSt9OK.d.ts → index-CW2QL33o.d.ts} +3 -3
- package/dist/{index-DyTSt9OK.d.ts.map → index-CW2QL33o.d.ts.map} +1 -1
- package/dist/{index-CYndvTWH.d.ts → index-garxxqgV.d.ts} +2 -2
- package/dist/{index-CYndvTWH.d.ts.map → index-garxxqgV.d.ts.map} +1 -1
- package/dist/index.d.ts +20 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/{labelled-input-base-DiTme1JO.d.ts → labelled-input-base-Dp_9KP0G.d.ts} +2 -2
- package/dist/{labelled-input-base-DiTme1JO.d.ts.map → labelled-input-base-Dp_9KP0G.d.ts.map} +1 -1
- package/dist/labelled-radial-input.d.ts +2 -2
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-slider-input.d.ts +1 -1
- package/dist/list-group-header.d.ts +26 -0
- package/dist/list-group-header.d.ts.map +1 -0
- package/dist/list-group-header.js +122 -0
- package/dist/list-group-header.js.map +1 -0
- package/dist/{list-selection-controller-SA4piSBi.js → list-selection-controller-BeE70lbB.js} +42 -4
- package/dist/list-selection-controller-BeE70lbB.js.map +1 -0
- package/dist/{menu-CsBUyUuS.js → menu-DaAlsyhf.js} +2 -2
- package/dist/{menu-CsBUyUuS.js.map → menu-DaAlsyhf.js.map} +1 -1
- package/dist/{menu-item-BbgdcwBX.d.ts → menu-item-B0zqw2xp.d.ts} +14 -2
- package/dist/menu-item-B0zqw2xp.d.ts.map +1 -0
- package/dist/{menu-item-CfndBGO8.js → menu-item-CDPFgaSF.js} +32 -1
- package/dist/menu-item-CDPFgaSF.js.map +1 -0
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +2 -2
- package/dist/miller.d.ts +1 -1
- package/dist/panel.d.ts +1 -1
- package/dist/{split-layout-JK3xWVX2.d.ts → split-layout-C5SYbDrq.d.ts} +2 -2
- package/dist/{split-layout-JK3xWVX2.d.ts.map → split-layout-C5SYbDrq.d.ts.map} +1 -1
- package/dist/split-layout.d.ts +1 -1
- package/dist/{tab-list-pqfKaVAE.d.ts → tab-list-v8mKX7Fu.d.ts} +2 -2
- package/dist/{tab-list-pqfKaVAE.d.ts.map → tab-list-v8mKX7Fu.d.ts.map} +1 -1
- package/dist/tabs.d.ts +1 -1
- package/dist/{titlebar-Tqu-VvMU.d.ts → titlebar-B5zSTZnF.d.ts} +2 -2
- package/dist/{titlebar-Tqu-VvMU.d.ts.map → titlebar-B5zSTZnF.d.ts.map} +1 -1
- package/dist/titlebar.d.ts +1 -1
- package/dist/tree.d.ts +1 -1
- package/dist/{vertical-list-DTp-UIUE.d.ts → vertical-list-DheZSEmB.d.ts} +2 -2
- package/dist/{vertical-list-DTp-UIUE.d.ts.map → vertical-list-DheZSEmB.d.ts.map} +1 -1
- package/dist/vertical-list.d.ts +1 -1
- package/dist/vertical-list.js +1 -1
- package/docs-user/README.md +1 -0
- package/docs-user/detail-list.md +28 -0
- package/docs-user/grid-list.md +35 -3
- package/docs-user/index.json +7 -2
- package/docs-user/list-group-header.md +14 -0
- package/docs-user/llms.txt +1 -0
- package/llms.txt +1 -0
- package/package.json +5 -1
- package/dist/detail-list-CgkOHYOa.d.ts.map +0 -1
- package/dist/grid-list-3fGhdaFA.js.map +0 -1
- package/dist/grid-list-D6AfWfwn.d.ts.map +0 -1
- package/dist/icon-CDGprSdE.d.ts.map +0 -1
- package/dist/list-selection-controller-SA4piSBi.js.map +0 -1
- package/dist/menu-item-BbgdcwBX.d.ts.map +0 -1
- package/dist/menu-item-CfndBGO8.js.map +0 -1
package/bundle/index.js
CHANGED
|
@@ -5549,6 +5549,56 @@ __decorate([state()], RadioGroup.prototype, "_icons", void 0);
|
|
|
5549
5549
|
__decorate([query(`slot`)], RadioGroup.prototype, "_slot", void 0);
|
|
5550
5550
|
RadioGroup = __decorate([safeCustomElement(`ixfx-radio-group`)], RadioGroup);
|
|
5551
5551
|
//#endregion
|
|
5552
|
+
//#region src/interaction/grouping.ts
|
|
5553
|
+
/** Label shown for the empty group key. */
|
|
5554
|
+
const EMPTY_GROUP_LABEL = `(none)`;
|
|
5555
|
+
/** Default header label: the key, or `(none)` for the empty key. */
|
|
5556
|
+
function defaultGroupLabel(key) {
|
|
5557
|
+
return key === `` ? EMPTY_GROUP_LABEL : key;
|
|
5558
|
+
}
|
|
5559
|
+
/** Coerce a raw group value to a key, as data-grid does. */
|
|
5560
|
+
function toGroupKey(value) {
|
|
5561
|
+
return String(value ?? ``);
|
|
5562
|
+
}
|
|
5563
|
+
/**
|
|
5564
|
+
* Group key for `item`. A string `groupBy` is resolved through `readNamed`
|
|
5565
|
+
* (property or `data-*` lookup, supplied by the host); a function is called.
|
|
5566
|
+
*/
|
|
5567
|
+
function resolveGroupKey(groupBy, item, index, readNamed) {
|
|
5568
|
+
if (typeof groupBy === `string`) return toGroupKey(readNamed(item, groupBy));
|
|
5569
|
+
return toGroupKey(groupBy(item, index));
|
|
5570
|
+
}
|
|
5571
|
+
/**
|
|
5572
|
+
* Split `items` into groups in first-encounter order. Single pass, O(n).
|
|
5573
|
+
* Item order within a group is preserved.
|
|
5574
|
+
*/
|
|
5575
|
+
function partitionByGroup(items, keyOf, labelOf) {
|
|
5576
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
5577
|
+
for (let i = 0; i < items.length; i++) {
|
|
5578
|
+
const key = keyOf(items[i], i);
|
|
5579
|
+
const existing = byKey.get(key);
|
|
5580
|
+
if (existing) existing.items.push(items[i]);
|
|
5581
|
+
else byKey.set(key, {
|
|
5582
|
+
key,
|
|
5583
|
+
items: [items[i]],
|
|
5584
|
+
firstIndex: i
|
|
5585
|
+
});
|
|
5586
|
+
}
|
|
5587
|
+
return [...byKey.values()].map((g) => ({
|
|
5588
|
+
key: g.key,
|
|
5589
|
+
items: g.items,
|
|
5590
|
+
firstIndex: g.firstIndex,
|
|
5591
|
+
label: labelOf ? labelOf(g.key, g.items) : defaultGroupLabel(g.key)
|
|
5592
|
+
}));
|
|
5593
|
+
}
|
|
5594
|
+
/** Immutable toggle of `key` in `set`. */
|
|
5595
|
+
function toggleInSet(set, key) {
|
|
5596
|
+
const next = new Set(set);
|
|
5597
|
+
if (next.has(key)) next.delete(key);
|
|
5598
|
+
else next.add(key);
|
|
5599
|
+
return next;
|
|
5600
|
+
}
|
|
5601
|
+
//#endregion
|
|
5552
5602
|
//#region src/interaction/list-drag-preview-default.ts
|
|
5553
5603
|
const CARD_DEFAULT = 72;
|
|
5554
5604
|
const CARD_MIN = 48;
|
|
@@ -6670,6 +6720,10 @@ var ListSelectionController = class {
|
|
|
6670
6720
|
if (next.has(item)) next.delete(item);
|
|
6671
6721
|
else {
|
|
6672
6722
|
if (!this.canSelectMultiple()) next.clear();
|
|
6723
|
+
if (this.#clamped()) {
|
|
6724
|
+
const g = this.#groupOf(item);
|
|
6725
|
+
for (const other of [...next]) if (this.#groupOf(other) !== g) next.delete(other);
|
|
6726
|
+
}
|
|
6673
6727
|
next.add(item);
|
|
6674
6728
|
}
|
|
6675
6729
|
this.#apply(next);
|
|
@@ -6688,6 +6742,22 @@ var ListSelectionController = class {
|
|
|
6688
6742
|
this.replace(item);
|
|
6689
6743
|
return;
|
|
6690
6744
|
}
|
|
6745
|
+
if (this.#clamped()) {
|
|
6746
|
+
const group = this.#groupOf(this.#anchor);
|
|
6747
|
+
if (group !== this.#groupOf(item)) {
|
|
6748
|
+
this.replace(item);
|
|
6749
|
+
return;
|
|
6750
|
+
}
|
|
6751
|
+
const inGroup = ordered.filter((t) => this.#groupOf(t) === group);
|
|
6752
|
+
const a = inGroup.indexOf(this.#anchor);
|
|
6753
|
+
const b = inGroup.indexOf(item);
|
|
6754
|
+
if (a === -1 || b === -1) {
|
|
6755
|
+
this.replace(item);
|
|
6756
|
+
return;
|
|
6757
|
+
}
|
|
6758
|
+
this.#apply(new Set(inGroup.slice(Math.min(a, b), Math.max(a, b) + 1).filter((t) => this.#config.isSelectable(t))));
|
|
6759
|
+
return;
|
|
6760
|
+
}
|
|
6691
6761
|
const start = Math.min(anchorIdx, targetIdx);
|
|
6692
6762
|
const end = Math.max(anchorIdx, targetIdx);
|
|
6693
6763
|
this.#apply(new Set(ordered.slice(start, end + 1).filter((t) => this.#config.isSelectable(t))));
|
|
@@ -6699,8 +6769,12 @@ var ListSelectionController = class {
|
|
|
6699
6769
|
}
|
|
6700
6770
|
/** Select every ordered item that passes `isSelectable`. */
|
|
6701
6771
|
selectAll() {
|
|
6702
|
-
|
|
6772
|
+
let ordered = this.#config.getOrderedItems();
|
|
6703
6773
|
if (ordered.length === 0) return;
|
|
6774
|
+
if (this.#clamped()) {
|
|
6775
|
+
const group = (this.#anchor === void 0 ? null : this.#groupOf(this.#anchor)) ?? this.#config.getFallbackGroupKey?.() ?? null;
|
|
6776
|
+
if (group !== null) ordered = ordered.filter((t) => this.#groupOf(t) === group);
|
|
6777
|
+
}
|
|
6704
6778
|
const next = new Set(ordered.filter((t) => this.#config.isSelectable(t)));
|
|
6705
6779
|
if (next.size === 0) return;
|
|
6706
6780
|
this.#apply(next);
|
|
@@ -6712,7 +6786,7 @@ var ListSelectionController = class {
|
|
|
6712
6786
|
selectMany(items) {
|
|
6713
6787
|
const { selectionMode } = this.#config.getModes();
|
|
6714
6788
|
if (selectionMode !== `multiple`) throw new Error(`selectMany requires selectionMode="multiple" (current: "${selectionMode}")`);
|
|
6715
|
-
const next = new Set([...items].filter((t) => this.#config.isSelectable(t)));
|
|
6789
|
+
const next = this.#clampToFirstGroup(new Set([...items].filter((t) => this.#config.isSelectable(t))));
|
|
6716
6790
|
if (next.size === 0) return;
|
|
6717
6791
|
this.#anchor = [...next].at(-1);
|
|
6718
6792
|
this.#apply(next);
|
|
@@ -6737,7 +6811,7 @@ var ListSelectionController = class {
|
|
|
6737
6811
|
*/
|
|
6738
6812
|
applyDragSelection(items) {
|
|
6739
6813
|
if (!this.canDragSelect()) return;
|
|
6740
|
-
const next = new Set([...items].filter((t) => this.#config.isSelectable(t)));
|
|
6814
|
+
const next = this.#clampToFirstGroup(new Set([...items].filter((t) => this.#config.isSelectable(t))));
|
|
6741
6815
|
if (next.size === 0 && this.#selected.size === 0) return;
|
|
6742
6816
|
this.#anchor = [...next].at(-1);
|
|
6743
6817
|
this.#apply(next);
|
|
@@ -6866,6 +6940,20 @@ var ListSelectionController = class {
|
|
|
6866
6940
|
this.#selected = new Set(items);
|
|
6867
6941
|
this.#anchor = anchor;
|
|
6868
6942
|
}
|
|
6943
|
+
/** Whether gestures must stay inside one group. */
|
|
6944
|
+
#clamped() {
|
|
6945
|
+
return this.#config.getGroupKey !== void 0 && !(this.#config.crossGroupSelection?.() ?? false);
|
|
6946
|
+
}
|
|
6947
|
+
#groupOf(item) {
|
|
6948
|
+
return this.#config.getGroupKey?.(item) ?? null;
|
|
6949
|
+
}
|
|
6950
|
+
/** Keep only items in the group of the first item (in display order) when clamped. */
|
|
6951
|
+
#clampToFirstGroup(items) {
|
|
6952
|
+
if (!this.#clamped() || items.size === 0) return new Set(items);
|
|
6953
|
+
const first = this.#config.getOrderedItems().find((t) => items.has(t)) ?? [...items][0];
|
|
6954
|
+
const group = this.#groupOf(first);
|
|
6955
|
+
return new Set([...items].filter((t) => this.#groupOf(t) === group));
|
|
6956
|
+
}
|
|
6869
6957
|
#apply(next) {
|
|
6870
6958
|
const previous = this.#selected;
|
|
6871
6959
|
this.#selected = next;
|
|
@@ -7249,6 +7337,8 @@ let MenuTrigger = class MenuTrigger extends LitElement {
|
|
|
7249
7337
|
this._anchorName = `--mt-${_MenuTrigger._count++}`;
|
|
7250
7338
|
this._clickListener = this._handleClickOutside.bind(this);
|
|
7251
7339
|
this._focusBeforeOpen = null;
|
|
7340
|
+
this._mouseHeld = false;
|
|
7341
|
+
this._openOnRelease = false;
|
|
7252
7342
|
this._handlePopoverToggle = (e) => {
|
|
7253
7343
|
if (e.newState === `closed`) {
|
|
7254
7344
|
this._isOpen = false;
|
|
@@ -7271,6 +7361,11 @@ let MenuTrigger = class MenuTrigger extends LitElement {
|
|
|
7271
7361
|
this._restoreFocus();
|
|
7272
7362
|
}
|
|
7273
7363
|
};
|
|
7364
|
+
this._handleRelease = () => {
|
|
7365
|
+
const shouldOpen = this._openOnRelease;
|
|
7366
|
+
this._removeReleaseListeners();
|
|
7367
|
+
if (shouldOpen) setTimeout(() => this._openMenu(), 0);
|
|
7368
|
+
};
|
|
7274
7369
|
}
|
|
7275
7370
|
static {
|
|
7276
7371
|
this._count = 0;
|
|
@@ -7287,6 +7382,7 @@ let MenuTrigger = class MenuTrigger extends LitElement {
|
|
|
7287
7382
|
this.removeEventListener(`command`, this._handleCommand);
|
|
7288
7383
|
this.removeEventListener(`radio-change`, this._handleRadioChange);
|
|
7289
7384
|
document.removeEventListener(`click`, this._clickListener);
|
|
7385
|
+
this._removeReleaseListeners();
|
|
7290
7386
|
}
|
|
7291
7387
|
firstUpdated() {
|
|
7292
7388
|
this.style.setProperty(`--anchor-name`, this._anchorName);
|
|
@@ -7453,6 +7549,24 @@ let MenuTrigger = class MenuTrigger extends LitElement {
|
|
|
7453
7549
|
composed: true
|
|
7454
7550
|
}));
|
|
7455
7551
|
}
|
|
7552
|
+
_handlePointerDown(e) {
|
|
7553
|
+
if (this.mode !== `context` || e.pointerType !== `mouse`) return;
|
|
7554
|
+
this._mouseHeld = true;
|
|
7555
|
+
document.addEventListener(`pointerup`, this._handleRelease, {
|
|
7556
|
+
capture: true,
|
|
7557
|
+
once: true
|
|
7558
|
+
});
|
|
7559
|
+
document.addEventListener(`pointercancel`, this._handleRelease, {
|
|
7560
|
+
capture: true,
|
|
7561
|
+
once: true
|
|
7562
|
+
});
|
|
7563
|
+
}
|
|
7564
|
+
_removeReleaseListeners() {
|
|
7565
|
+
document.removeEventListener(`pointerup`, this._handleRelease, { capture: true });
|
|
7566
|
+
document.removeEventListener(`pointercancel`, this._handleRelease, { capture: true });
|
|
7567
|
+
this._mouseHeld = false;
|
|
7568
|
+
this._openOnRelease = false;
|
|
7569
|
+
}
|
|
7456
7570
|
_handleContextMenu(e) {
|
|
7457
7571
|
if (this.mode !== `context`) return;
|
|
7458
7572
|
e.preventDefault();
|
|
@@ -7460,6 +7574,10 @@ let MenuTrigger = class MenuTrigger extends LitElement {
|
|
|
7460
7574
|
this._popup.style.left = `${e.clientX}px`;
|
|
7461
7575
|
this._popup.style.position = `fixed`;
|
|
7462
7576
|
this._popup.style.positionAnchor = `none`;
|
|
7577
|
+
if (this._mouseHeld) {
|
|
7578
|
+
this._openOnRelease = true;
|
|
7579
|
+
return;
|
|
7580
|
+
}
|
|
7463
7581
|
this._openMenu();
|
|
7464
7582
|
}
|
|
7465
7583
|
static {
|
|
@@ -7572,6 +7690,7 @@ let MenuTrigger = class MenuTrigger extends LitElement {
|
|
|
7572
7690
|
class="trigger-area"
|
|
7573
7691
|
@click=${this._handleTriggerClick}
|
|
7574
7692
|
@contextmenu=${this._handleContextMenu}
|
|
7693
|
+
@pointerdown=${this._handlePointerDown}
|
|
7575
7694
|
>
|
|
7576
7695
|
${showButton ? html`<button class="trigger-btn" part="base" tabindex=${this.tabStop ? `0` : `-1`}>${this.iconName ? html`<ixfx-icon name=${this.iconName}></ixfx-icon>` : this.label || html`<ixfx-icon name="ellipsis-vertical"></ixfx-icon>`}</button>` : null}
|
|
7577
7696
|
${this.mode === `context` ? html`<slot name="context-target" @slotchange=${this._handleContextTargetSlotChange}></slot>` : null}
|
|
@@ -17173,8 +17292,125 @@ __decorate([state()], DataTableElement.prototype, "_detailIndex", void 0);
|
|
|
17173
17292
|
__decorate([state()], DataTableElement.prototype, "_stacked", void 0);
|
|
17174
17293
|
DataTableElement = __decorate([safeCustomElement(`ixfx-data-table`)], DataTableElement);
|
|
17175
17294
|
//#endregion
|
|
17295
|
+
//#region src/list-group-header/list-group-header.ts
|
|
17296
|
+
let ListGroupHeaderElement = class ListGroupHeaderElement extends LitElement {
|
|
17297
|
+
constructor(..._args) {
|
|
17298
|
+
super(..._args);
|
|
17299
|
+
this.label = ``;
|
|
17300
|
+
this.count = 0;
|
|
17301
|
+
this.collapsed = false;
|
|
17302
|
+
this.orientation = `horizontal`;
|
|
17303
|
+
}
|
|
17304
|
+
static {
|
|
17305
|
+
this.styles = css`
|
|
17306
|
+
:host {
|
|
17307
|
+
--_height: var(--list-group-header-height, 28px);
|
|
17308
|
+
--_border: var(--list-group-header-border, var(--border));
|
|
17309
|
+
|
|
17310
|
+
display: flex;
|
|
17311
|
+
align-items: center;
|
|
17312
|
+
gap: var(--space-s);
|
|
17313
|
+
box-sizing: border-box;
|
|
17314
|
+
min-height: var(--_height);
|
|
17315
|
+
padding: 0 var(--space-s);
|
|
17316
|
+
background: transparent;
|
|
17317
|
+
color: var(--list-group-header-text, var(--surface-3-text));
|
|
17318
|
+
border-bottom: 1px solid var(--_border);
|
|
17319
|
+
font-weight: 600;
|
|
17320
|
+
cursor: pointer;
|
|
17321
|
+
user-select: none;
|
|
17322
|
+
}
|
|
17323
|
+
|
|
17324
|
+
.caret {
|
|
17325
|
+
display: inline-flex;
|
|
17326
|
+
transition: transform 0.15s;
|
|
17327
|
+
}
|
|
17328
|
+
|
|
17329
|
+
:host([collapsed]) .caret {
|
|
17330
|
+
transform: rotate(-90deg);
|
|
17331
|
+
}
|
|
17332
|
+
|
|
17333
|
+
.count {
|
|
17334
|
+
opacity: 0.6;
|
|
17335
|
+
font-weight: 400;
|
|
17336
|
+
font-size: 0.9em;
|
|
17337
|
+
}
|
|
17338
|
+
|
|
17339
|
+
.label {
|
|
17340
|
+
overflow: hidden;
|
|
17341
|
+
text-overflow: ellipsis;
|
|
17342
|
+
white-space: nowrap;
|
|
17343
|
+
min-width: 0;
|
|
17344
|
+
}
|
|
17345
|
+
|
|
17346
|
+
.divider {
|
|
17347
|
+
flex: 1;
|
|
17348
|
+
height: 1px;
|
|
17349
|
+
background: var(--_border);
|
|
17350
|
+
margin-left: var(--space-s);
|
|
17351
|
+
}
|
|
17352
|
+
|
|
17353
|
+
/* Single-row (grid-list \`row\` orientation) layout: a narrow column cell. */
|
|
17354
|
+
:host([orientation="vertical"]) {
|
|
17355
|
+
flex-direction: column;
|
|
17356
|
+
justify-content: flex-start;
|
|
17357
|
+
padding: var(--space-s) 0;
|
|
17358
|
+
border-bottom: 0;
|
|
17359
|
+
border-right: 1px solid var(--_border);
|
|
17360
|
+
min-height: 0;
|
|
17361
|
+
}
|
|
17362
|
+
|
|
17363
|
+
:host([orientation="vertical"]) .divider {
|
|
17364
|
+
width: 1px;
|
|
17365
|
+
height: auto;
|
|
17366
|
+
margin-left: 0;
|
|
17367
|
+
margin-top: var(--space-s);
|
|
17368
|
+
}
|
|
17369
|
+
|
|
17370
|
+
@media (prefers-reduced-motion: reduce) {
|
|
17371
|
+
.caret { transition: none; }
|
|
17372
|
+
}
|
|
17373
|
+
`;
|
|
17374
|
+
}
|
|
17375
|
+
connectedCallback() {
|
|
17376
|
+
super.connectedCallback();
|
|
17377
|
+
this.setAttribute(`role`, `button`);
|
|
17378
|
+
}
|
|
17379
|
+
willUpdate() {
|
|
17380
|
+
this.setAttribute(`aria-expanded`, this.collapsed ? `false` : `true`);
|
|
17381
|
+
this.setAttribute(`aria-label`, `${this.label}, ${this.count} ${this.count === 1 ? `item` : `items`}`);
|
|
17382
|
+
}
|
|
17383
|
+
render() {
|
|
17384
|
+
return html`
|
|
17385
|
+
<span class="caret"><ixfx-icon name="chevron-down" size="12"></ixfx-icon></span>
|
|
17386
|
+
<span class="label" part="label">${this.label}</span>
|
|
17387
|
+
<span class="count" part="count">(${this.count})</span>
|
|
17388
|
+
<span class="divider" part="divider"></span>
|
|
17389
|
+
`;
|
|
17390
|
+
}
|
|
17391
|
+
};
|
|
17392
|
+
__decorate([property({
|
|
17393
|
+
type: String,
|
|
17394
|
+
reflect: true
|
|
17395
|
+
})], ListGroupHeaderElement.prototype, "label", void 0);
|
|
17396
|
+
__decorate([property({
|
|
17397
|
+
type: Number,
|
|
17398
|
+
reflect: true
|
|
17399
|
+
})], ListGroupHeaderElement.prototype, "count", void 0);
|
|
17400
|
+
__decorate([property({
|
|
17401
|
+
type: Boolean,
|
|
17402
|
+
reflect: true
|
|
17403
|
+
})], ListGroupHeaderElement.prototype, "collapsed", void 0);
|
|
17404
|
+
__decorate([property({
|
|
17405
|
+
type: String,
|
|
17406
|
+
reflect: true
|
|
17407
|
+
})], ListGroupHeaderElement.prototype, "orientation", void 0);
|
|
17408
|
+
ListGroupHeaderElement = __decorate([safeCustomElement(`ixfx-list-group-header`)], ListGroupHeaderElement);
|
|
17409
|
+
//#endregion
|
|
17176
17410
|
//#region src/detail-list/detail-list.ts
|
|
17177
17411
|
const ITEM_SELECTOR$3 = `:scope > li`;
|
|
17412
|
+
const GROUP_HEADER_ATTR = `data-ixfx-group-header`;
|
|
17413
|
+
const GROUP_COLLAPSED_ATTR = `data-group-collapsed`;
|
|
17178
17414
|
const SEARCH_HIGHLIGHT_KEY = `detail-list-search`;
|
|
17179
17415
|
const NAV_KEYS = /* @__PURE__ */ new Set([
|
|
17180
17416
|
`ArrowDown`,
|
|
@@ -17232,14 +17468,26 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17232
17468
|
this.keyProperty = ``;
|
|
17233
17469
|
this.itemWrap = `stop`;
|
|
17234
17470
|
this.itemsDraggable = false;
|
|
17471
|
+
this.groupBy = null;
|
|
17472
|
+
this.crossGroupSelection = false;
|
|
17473
|
+
this._collapsedGroups = /* @__PURE__ */ new Set();
|
|
17235
17474
|
this.#itemData = /* @__PURE__ */ new WeakMap();
|
|
17236
17475
|
this.#grid = [];
|
|
17237
17476
|
this.#positions = /* @__PURE__ */ new Map();
|
|
17477
|
+
this.#tops = /* @__PURE__ */ new Map();
|
|
17238
17478
|
this.#geometryScheduled = false;
|
|
17239
17479
|
this.#sortRaf = 0;
|
|
17240
17480
|
this.#viewportScrollRaf = 0;
|
|
17241
17481
|
this.#animatedItems = /* @__PURE__ */ new WeakSet();
|
|
17242
17482
|
this.#builtFromProvider = [];
|
|
17483
|
+
this.#groupIndex = /* @__PURE__ */ new Map();
|
|
17484
|
+
this.#groupList = [];
|
|
17485
|
+
this.#headerEls = /* @__PURE__ */ new Map();
|
|
17486
|
+
this.#seq = /* @__PURE__ */ new WeakMap();
|
|
17487
|
+
this.#seqNext = 0;
|
|
17488
|
+
this.#wasGrouped = false;
|
|
17489
|
+
this.#groupsScheduled = false;
|
|
17490
|
+
this.#groupByChanged = false;
|
|
17243
17491
|
this.#providerBuilt = 0;
|
|
17244
17492
|
this.#data = new DataController(this);
|
|
17245
17493
|
this.selection = new ListSelectionController({
|
|
@@ -17250,6 +17498,12 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17250
17498
|
interactionMode: this.interactionMode
|
|
17251
17499
|
}),
|
|
17252
17500
|
getKey: keyForElement,
|
|
17501
|
+
getGroupKey: (el) => this.#groupIndex.get(el) ?? null,
|
|
17502
|
+
crossGroupSelection: () => this.crossGroupSelection,
|
|
17503
|
+
getFallbackGroupKey: () => {
|
|
17504
|
+
const t = this.tickled.tickledId;
|
|
17505
|
+
return t ? this.#groupIndex.get(t) ?? null : null;
|
|
17506
|
+
},
|
|
17253
17507
|
onChange: (selected, previous) => {
|
|
17254
17508
|
this._syncItemStates();
|
|
17255
17509
|
this._dispatch(`list-select`, {
|
|
@@ -17331,6 +17585,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17331
17585
|
if (this._listEl) this._listEl.scrollLeft += dx;
|
|
17332
17586
|
},
|
|
17333
17587
|
shouldStart: (event) => {
|
|
17588
|
+
if (this.#findGroupHeader(event.composedPath())) return false;
|
|
17334
17589
|
this.#blankItemPressPending = this.itemsDraggable && this.#isBlankItemPress(event);
|
|
17335
17590
|
return !this.itemsDraggable || this.#blankItemPressPending;
|
|
17336
17591
|
}
|
|
@@ -17422,7 +17677,13 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17422
17677
|
};
|
|
17423
17678
|
this._onClick = (e) => {
|
|
17424
17679
|
if (this.#dragSelect.isDragging || this.#nativeDrag.isDragging || this.#postDragSuppressTimer !== 0) return;
|
|
17425
|
-
const
|
|
17680
|
+
const path = e.composedPath();
|
|
17681
|
+
const header = this.#findGroupHeader(path);
|
|
17682
|
+
if (header) {
|
|
17683
|
+
this.toggleGroup(header.dataset.groupKey ?? ``);
|
|
17684
|
+
return;
|
|
17685
|
+
}
|
|
17686
|
+
const item = this._findListItem(path);
|
|
17426
17687
|
if (!item) return;
|
|
17427
17688
|
this.focus();
|
|
17428
17689
|
this._dispatch(`list-item-click`, { item });
|
|
@@ -17441,6 +17702,8 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17441
17702
|
#itemData;
|
|
17442
17703
|
#grid;
|
|
17443
17704
|
#positions;
|
|
17705
|
+
/** Viewport-relative `top` per item at the last geometry pass — lines rows up across columns. */
|
|
17706
|
+
#tops;
|
|
17444
17707
|
#geometryScheduled;
|
|
17445
17708
|
#sortRaf;
|
|
17446
17709
|
#viewportScrollRaf;
|
|
@@ -17452,6 +17715,17 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17452
17715
|
/** Items that have already played (or been skipped for) their enter animation. */
|
|
17453
17716
|
#animatedItems;
|
|
17454
17717
|
#builtFromProvider;
|
|
17718
|
+
/** Element → group key, for the selection controller. Empty while ungrouped. */
|
|
17719
|
+
#groupIndex;
|
|
17720
|
+
#groupList;
|
|
17721
|
+
#headerEls;
|
|
17722
|
+
/** Insertion sequence per item, so regrouping starts from the original order. */
|
|
17723
|
+
#seq;
|
|
17724
|
+
#seqNext;
|
|
17725
|
+
#wasGrouped;
|
|
17726
|
+
#groupsScheduled;
|
|
17727
|
+
#groupByChanged;
|
|
17728
|
+
#groupObserver;
|
|
17455
17729
|
#providerBuilt;
|
|
17456
17730
|
#data;
|
|
17457
17731
|
#nativeDragging;
|
|
@@ -17568,6 +17842,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17568
17842
|
this.#processChildren();
|
|
17569
17843
|
this.#animateChildMutations(records);
|
|
17570
17844
|
this.#scheduleGeometry();
|
|
17845
|
+
this.#scheduleGroups();
|
|
17571
17846
|
});
|
|
17572
17847
|
this.#mutationObserver.observe(this, {
|
|
17573
17848
|
childList: true,
|
|
@@ -17577,6 +17852,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17577
17852
|
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) this.#animatedItems.add(el);
|
|
17578
17853
|
this.#searchRegistry ??= createCommandRegistry();
|
|
17579
17854
|
this.#syncSearchCommands();
|
|
17855
|
+
this.#scheduleGroups();
|
|
17580
17856
|
}
|
|
17581
17857
|
/**
|
|
17582
17858
|
* Collapse-in items appended to the light DOM (however they got there). A bulk
|
|
@@ -17618,6 +17894,8 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17618
17894
|
this.#resizeObserver = void 0;
|
|
17619
17895
|
this.#mutationObserver?.disconnect();
|
|
17620
17896
|
this.#mutationObserver = void 0;
|
|
17897
|
+
this.#groupObserver?.disconnect();
|
|
17898
|
+
this.#groupObserver = void 0;
|
|
17621
17899
|
this.#unregisterSearchCommands?.();
|
|
17622
17900
|
this.#unregisterSearchCommands = void 0;
|
|
17623
17901
|
this.#searchRegistry = void 0;
|
|
@@ -17632,8 +17910,22 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17632
17910
|
this.#recomputeGeometry();
|
|
17633
17911
|
this._syncNativeDraggable();
|
|
17634
17912
|
}
|
|
17913
|
+
willUpdate(changed) {
|
|
17914
|
+
super.willUpdate(changed);
|
|
17915
|
+
if (changed.has(`groupBy`) && this.hasUpdated) {
|
|
17916
|
+
if (this._collapsedGroups.size > 0) this._collapsedGroups = /* @__PURE__ */ new Set();
|
|
17917
|
+
this.#groupByChanged = true;
|
|
17918
|
+
}
|
|
17919
|
+
}
|
|
17635
17920
|
updated(changed) {
|
|
17636
17921
|
super.updated(changed);
|
|
17922
|
+
if (changed.has(`groupBy`) || changed.has(`groupLabel`) || changed.has(`_collapsedGroups`)) {
|
|
17923
|
+
this.#scheduleGroups();
|
|
17924
|
+
if (this.#groupByChanged) {
|
|
17925
|
+
this.#groupByChanged = false;
|
|
17926
|
+
this._dispatch(`list-group-change`, { groupBy: this.groupBy });
|
|
17927
|
+
}
|
|
17928
|
+
}
|
|
17637
17929
|
if (changed.has(`searchModes`)) this.#syncSearchCommands();
|
|
17638
17930
|
if (changed.has(`provider`)) {
|
|
17639
17931
|
this.#builtFromProvider = [];
|
|
@@ -17650,6 +17942,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17650
17942
|
if (changed.has(`filterPredicate`)) {
|
|
17651
17943
|
this.#applyFilterPredicate();
|
|
17652
17944
|
this.#scheduleGeometry();
|
|
17945
|
+
this.#scheduleGroups();
|
|
17653
17946
|
}
|
|
17654
17947
|
if (changed.has(`interactionMode`)) {
|
|
17655
17948
|
this.selection.clearAnchor();
|
|
@@ -17689,6 +17982,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17689
17982
|
this.#ensureIcon(el);
|
|
17690
17983
|
if (this.sort !== `none`) this.#applySort();
|
|
17691
17984
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
17985
|
+
this.#scheduleGroups();
|
|
17692
17986
|
}
|
|
17693
17987
|
/**
|
|
17694
17988
|
* Collapse + fade `item` out, then detach it; the rows after it slide up to
|
|
@@ -17698,6 +17992,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17698
17992
|
this.selection.deselect(item);
|
|
17699
17993
|
return animateListItemOut(item).then(() => {
|
|
17700
17994
|
item.remove();
|
|
17995
|
+
this.#scheduleGroups();
|
|
17701
17996
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
17702
17997
|
this.#recomputeGeometry();
|
|
17703
17998
|
});
|
|
@@ -17708,6 +18003,8 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17708
18003
|
}
|
|
17709
18004
|
clearItems() {
|
|
17710
18005
|
this.replaceChildren();
|
|
18006
|
+
this.#headerEls.clear();
|
|
18007
|
+
this.#scheduleGroups();
|
|
17711
18008
|
this.selection.reset();
|
|
17712
18009
|
this.#builtFromProvider = [];
|
|
17713
18010
|
this.#providerBuilt = 0;
|
|
@@ -17725,6 +18022,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17725
18022
|
}
|
|
17726
18023
|
if (this.sort !== `none`) this.#applySort();
|
|
17727
18024
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
18025
|
+
this.#scheduleGroups();
|
|
17728
18026
|
}
|
|
17729
18027
|
/**
|
|
17730
18028
|
* Get the source object behind an `<li>` built from an `items` array or
|
|
@@ -17844,6 +18142,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17844
18142
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
17845
18143
|
this._syncItemStates();
|
|
17846
18144
|
this.#scheduleGeometry();
|
|
18145
|
+
this.#scheduleGroups();
|
|
17847
18146
|
}
|
|
17848
18147
|
#createItemEl(item, index) {
|
|
17849
18148
|
const li = document.createElement(`li`);
|
|
@@ -17895,6 +18194,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17895
18194
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
17896
18195
|
this._syncItemStates();
|
|
17897
18196
|
this.#scheduleGeometry();
|
|
18197
|
+
this.#scheduleGroups();
|
|
17898
18198
|
} else {
|
|
17899
18199
|
this.#providerBuilt = 0;
|
|
17900
18200
|
this.#rebuildFromObjects(items);
|
|
@@ -17913,6 +18213,16 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17913
18213
|
if (this.sort === `none`) return;
|
|
17914
18214
|
const rows = [...this.querySelectorAll(ITEM_SELECTOR$3)];
|
|
17915
18215
|
if (rows.length < 2) return;
|
|
18216
|
+
if (this.#groupingActive()) {
|
|
18217
|
+
this.#scheduleGroups();
|
|
18218
|
+
return;
|
|
18219
|
+
}
|
|
18220
|
+
for (const el of this.#sortRows(rows)) this.appendChild(el);
|
|
18221
|
+
this.#scheduleGeometry();
|
|
18222
|
+
this.#scheduleGroups();
|
|
18223
|
+
}
|
|
18224
|
+
/** Stable sort of `rows` by the current `sort` / `sortBy` / `sortComparator`. */
|
|
18225
|
+
#sortRows(rows) {
|
|
17916
18226
|
const dir = this.sort === `descending` ? -1 : 1;
|
|
17917
18227
|
const meta = (el) => ({
|
|
17918
18228
|
element: el,
|
|
@@ -17942,9 +18252,147 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17942
18252
|
i
|
|
17943
18253
|
}));
|
|
17944
18254
|
indexed.sort((a, b) => cmp(a.el, b.el) * dir || a.i - b.i);
|
|
17945
|
-
|
|
18255
|
+
return indexed.map(({ el }) => el);
|
|
18256
|
+
}
|
|
18257
|
+
#groupingActive() {
|
|
18258
|
+
return this.groupBy !== null && this.groupBy !== void 0 && this.groupBy !== ``;
|
|
18259
|
+
}
|
|
18260
|
+
/** Coalesce regrouping to one pass per microtask (streaming batches, search keystrokes). */
|
|
18261
|
+
#scheduleGroups() {
|
|
18262
|
+
if (this.#groupsScheduled || !this.#groupingActive() && !this.#wasGrouped) return;
|
|
18263
|
+
this.#groupsScheduled = true;
|
|
18264
|
+
queueMicrotask(() => {
|
|
18265
|
+
this.#groupsScheduled = false;
|
|
18266
|
+
this.#syncGroups();
|
|
18267
|
+
});
|
|
18268
|
+
}
|
|
18269
|
+
#groupValue(el, name) {
|
|
18270
|
+
return this.#propertyValue(this.getItemData(el), name) ?? el.getAttribute(`data-${name}`);
|
|
18271
|
+
}
|
|
18272
|
+
#groupKeyOf(el) {
|
|
18273
|
+
const by = this.groupBy;
|
|
18274
|
+
if (typeof by === `string`) return toGroupKey(this.#groupValue(el, by));
|
|
18275
|
+
return toGroupKey(by(this.getItemData(el), el));
|
|
18276
|
+
}
|
|
18277
|
+
#isItemVisible(el) {
|
|
18278
|
+
return !el.hidden && (!this.filterPredicate || this.filterPredicate(el));
|
|
18279
|
+
}
|
|
18280
|
+
#headerFor(key) {
|
|
18281
|
+
let header = this.#headerEls.get(key);
|
|
18282
|
+
if (!header) {
|
|
18283
|
+
header = document.createElement(`ixfx-list-group-header`);
|
|
18284
|
+
header.setAttribute(GROUP_HEADER_ATTR, ``);
|
|
18285
|
+
header.dataset.groupKey = key;
|
|
18286
|
+
this.#headerEls.set(key, header);
|
|
18287
|
+
}
|
|
18288
|
+
return header;
|
|
18289
|
+
}
|
|
18290
|
+
/**
|
|
18291
|
+
* Bring the light DOM into grouped order: original order → sort → partition
|
|
18292
|
+
* into contiguous groups, each preceded by a header row. Idempotent — it only
|
|
18293
|
+
* touches the DOM when the order actually changes, so it never loops through
|
|
18294
|
+
* the mutation observer. Ungrouping restores the original (sorted) order.
|
|
18295
|
+
*/
|
|
18296
|
+
#syncGroups() {
|
|
18297
|
+
const active = this.#groupingActive();
|
|
18298
|
+
if (!active && !this.#wasGrouped) return;
|
|
18299
|
+
const items = [...this.querySelectorAll(ITEM_SELECTOR$3)];
|
|
18300
|
+
for (const el of items) if (!this.#seq.has(el)) this.#seq.set(el, this.#seqNext++);
|
|
18301
|
+
let ordered = items.toSorted((a, b) => this.#seq.get(a) - this.#seq.get(b));
|
|
18302
|
+
if (this.sort !== `none` && ordered.length > 1) ordered = this.#sortRows(ordered);
|
|
18303
|
+
const nodes = [];
|
|
18304
|
+
const info = [];
|
|
18305
|
+
const index = /* @__PURE__ */ new Map();
|
|
18306
|
+
const liveHeaders = /* @__PURE__ */ new Set();
|
|
18307
|
+
if (active) {
|
|
18308
|
+
const label = this.groupLabel;
|
|
18309
|
+
const parts = partitionByGroup(ordered, (el) => this.#groupKeyOf(el), label ? (key, els) => label(key, els.map((el) => this.getItemData(el) ?? el)) : void 0);
|
|
18310
|
+
for (const part of parts) {
|
|
18311
|
+
const count = part.items.filter((el) => this.#isItemVisible(el)).length;
|
|
18312
|
+
const collapsed = count > 0 && this._collapsedGroups.has(part.key);
|
|
18313
|
+
if (count > 0) {
|
|
18314
|
+
const header = this.#headerFor(part.key);
|
|
18315
|
+
header.label = part.label;
|
|
18316
|
+
header.count = count;
|
|
18317
|
+
header.collapsed = collapsed;
|
|
18318
|
+
liveHeaders.add(part.key);
|
|
18319
|
+
nodes.push(header);
|
|
18320
|
+
info.push({
|
|
18321
|
+
key: part.key,
|
|
18322
|
+
label: part.label,
|
|
18323
|
+
count,
|
|
18324
|
+
collapsed: this._collapsedGroups.has(part.key)
|
|
18325
|
+
});
|
|
18326
|
+
}
|
|
18327
|
+
for (const el of part.items) {
|
|
18328
|
+
index.set(el, part.key);
|
|
18329
|
+
el.toggleAttribute(GROUP_COLLAPSED_ATTR, collapsed);
|
|
18330
|
+
nodes.push(el);
|
|
18331
|
+
}
|
|
18332
|
+
}
|
|
18333
|
+
} else {
|
|
18334
|
+
for (const el of ordered) el.removeAttribute(GROUP_COLLAPSED_ATTR);
|
|
18335
|
+
nodes.push(...ordered);
|
|
18336
|
+
}
|
|
18337
|
+
for (const [key, header] of this.#headerEls) if (!liveHeaders.has(key)) {
|
|
18338
|
+
header.remove();
|
|
18339
|
+
this.#headerEls.delete(key);
|
|
18340
|
+
}
|
|
18341
|
+
this.#groupIndex = index;
|
|
18342
|
+
this.#groupList = info;
|
|
18343
|
+
this.#wasGrouped = active;
|
|
18344
|
+
const current = [...this.children].filter((c) => c.matches(`li`) || c.hasAttribute(GROUP_HEADER_ATTR));
|
|
18345
|
+
if (current.length !== nodes.length || current.some((c, i) => c !== nodes[i])) this.append(...nodes);
|
|
18346
|
+
if (this.tickled.tickledId?.hasAttribute(GROUP_COLLAPSED_ATTR)) this.tickled.focus(void 0);
|
|
18347
|
+
this.#syncGroupObserver(active);
|
|
17946
18348
|
this.#scheduleGeometry();
|
|
17947
18349
|
}
|
|
18350
|
+
/** Filter-search hides rows via `hidden` on the `<li>`s — regroup when that changes. */
|
|
18351
|
+
#syncGroupObserver(active) {
|
|
18352
|
+
if (!active) {
|
|
18353
|
+
this.#groupObserver?.disconnect();
|
|
18354
|
+
this.#groupObserver = void 0;
|
|
18355
|
+
return;
|
|
18356
|
+
}
|
|
18357
|
+
if (this.#groupObserver || !this.isConnected) return;
|
|
18358
|
+
this.#groupObserver = new MutationObserver(() => this.#scheduleGroups());
|
|
18359
|
+
this.#groupObserver.observe(this, {
|
|
18360
|
+
subtree: true,
|
|
18361
|
+
attributes: true,
|
|
18362
|
+
attributeFilter: [`hidden`]
|
|
18363
|
+
});
|
|
18364
|
+
}
|
|
18365
|
+
/** Groups in display order (empty when ungrouped). */
|
|
18366
|
+
get groups() {
|
|
18367
|
+
return this.#groupList;
|
|
18368
|
+
}
|
|
18369
|
+
/** Collapse or expand one group. Fires `list-group-toggle` when the state changes. */
|
|
18370
|
+
setGroupCollapsed(groupKey, collapsed) {
|
|
18371
|
+
if (this._collapsedGroups.has(groupKey) === collapsed) return;
|
|
18372
|
+
const next = new Set(this._collapsedGroups);
|
|
18373
|
+
if (collapsed) next.add(groupKey);
|
|
18374
|
+
else next.delete(groupKey);
|
|
18375
|
+
this._collapsedGroups = next;
|
|
18376
|
+
this._dispatch(`list-group-toggle`, {
|
|
18377
|
+
groupKey,
|
|
18378
|
+
collapsed
|
|
18379
|
+
});
|
|
18380
|
+
}
|
|
18381
|
+
toggleGroup(groupKey) {
|
|
18382
|
+
this.setGroupCollapsed(groupKey, !this._collapsedGroups.has(groupKey));
|
|
18383
|
+
}
|
|
18384
|
+
collapseAllGroups() {
|
|
18385
|
+
for (const g of this.#groupList) this.setGroupCollapsed(g.key, true);
|
|
18386
|
+
}
|
|
18387
|
+
expandAllGroups() {
|
|
18388
|
+
for (const key of [...this._collapsedGroups]) this.setGroupCollapsed(key, false);
|
|
18389
|
+
}
|
|
18390
|
+
#findGroupHeader(path) {
|
|
18391
|
+
for (const target of path) {
|
|
18392
|
+
if (target === this) break;
|
|
18393
|
+
if (target instanceof HTMLElement && target.parentElement === this && target.hasAttribute(GROUP_HEADER_ATTR)) return target;
|
|
18394
|
+
}
|
|
18395
|
+
}
|
|
17948
18396
|
#scheduleGeometry() {
|
|
17949
18397
|
if (this.#geometryScheduled) return;
|
|
17950
18398
|
this.#geometryScheduled = true;
|
|
@@ -17956,11 +18404,13 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17956
18404
|
#recomputeGeometry() {
|
|
17957
18405
|
this.#grid = [];
|
|
17958
18406
|
this.#positions.clear();
|
|
18407
|
+
this.#tops.clear();
|
|
17959
18408
|
if (!this.isConnected) return;
|
|
17960
18409
|
const columns = [];
|
|
17961
18410
|
for (const el of this._getVisibleItems()) {
|
|
17962
18411
|
const rect = el.getBoundingClientRect();
|
|
17963
18412
|
if (rect.width === 0 && rect.height === 0) continue;
|
|
18413
|
+
this.#tops.set(el, rect.top);
|
|
17964
18414
|
let ci = columns.findIndex((c) => Math.abs(c.left - rect.left) <= 1);
|
|
17965
18415
|
if (ci === -1) {
|
|
17966
18416
|
columns.push({
|
|
@@ -18081,6 +18531,30 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
18081
18531
|
const next = this.#findNextCheckable(cur.col, cur.row, 0, -1);
|
|
18082
18532
|
if (next) this.#navigateTo(next);
|
|
18083
18533
|
}
|
|
18534
|
+
/**
|
|
18535
|
+
* Left/Right target. Ungrouped lists keep the row-index rule. Grouped lists
|
|
18536
|
+
* have headers at arbitrary heights, so an item's row index no longer equals
|
|
18537
|
+
* its visual row — pick the item in the neighbouring column whose top is
|
|
18538
|
+
* nearest the cursor's (ties go to the upper one).
|
|
18539
|
+
*/
|
|
18540
|
+
#horizontalTarget(from, dir) {
|
|
18541
|
+
if (this.#groupList.length === 0) return this.#findNextCheckable(from.col, from.row, dir, 0);
|
|
18542
|
+
const column = this.#grid[from.col + dir];
|
|
18543
|
+
const current = this.tickled.tickledId;
|
|
18544
|
+
const top = current ? this.#tops.get(current) : void 0;
|
|
18545
|
+
if (!column || top === void 0) return void 0;
|
|
18546
|
+
let best;
|
|
18547
|
+
let bestDelta = Number.POSITIVE_INFINITY;
|
|
18548
|
+
for (const el of column) {
|
|
18549
|
+
if (!this._isCheckable(el)) continue;
|
|
18550
|
+
const delta = Math.abs((this.#tops.get(el) ?? Number.POSITIVE_INFINITY) - top);
|
|
18551
|
+
if (delta < bestDelta) {
|
|
18552
|
+
best = el;
|
|
18553
|
+
bestDelta = delta;
|
|
18554
|
+
}
|
|
18555
|
+
}
|
|
18556
|
+
return best;
|
|
18557
|
+
}
|
|
18084
18558
|
#navigateRight() {
|
|
18085
18559
|
this.#recomputeGeometry();
|
|
18086
18560
|
const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
|
|
@@ -18088,7 +18562,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
18088
18562
|
this.#navigateTo(this.#findFirstCheckable());
|
|
18089
18563
|
return;
|
|
18090
18564
|
}
|
|
18091
|
-
const next = this.#
|
|
18565
|
+
const next = this.#horizontalTarget(cur, 1);
|
|
18092
18566
|
if (next) this.#navigateTo(next);
|
|
18093
18567
|
else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findFirstCheckable());
|
|
18094
18568
|
}
|
|
@@ -18099,13 +18573,14 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
18099
18573
|
this.#navigateTo(this.#findFirstCheckable());
|
|
18100
18574
|
return;
|
|
18101
18575
|
}
|
|
18102
|
-
const next = this.#
|
|
18576
|
+
const next = this.#horizontalTarget(cur, -1);
|
|
18103
18577
|
if (next) this.#navigateTo(next);
|
|
18104
18578
|
else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findLastCheckable());
|
|
18105
18579
|
}
|
|
18106
18580
|
_getVisibleItems() {
|
|
18107
18581
|
return Array.from(this.querySelectorAll(ITEM_SELECTOR$3)).filter((el) => {
|
|
18108
18582
|
if (el.hidden) return false;
|
|
18583
|
+
if (el.hasAttribute(GROUP_COLLAPSED_ATTR)) return false;
|
|
18109
18584
|
if (this.filterPredicate && !this.filterPredicate(el)) return false;
|
|
18110
18585
|
return true;
|
|
18111
18586
|
});
|
|
@@ -18394,6 +18869,20 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
18394
18869
|
display: none !important;
|
|
18395
18870
|
}
|
|
18396
18871
|
|
|
18872
|
+
/* Rows of a collapsed group leave the flow; their header stays. */
|
|
18873
|
+
::slotted([data-group-collapsed]) {
|
|
18874
|
+
display: none !important;
|
|
18875
|
+
}
|
|
18876
|
+
|
|
18877
|
+
/* Group headers are rows in the column flow, the same size as an item. */
|
|
18878
|
+
::slotted(ixfx-list-group-header) {
|
|
18879
|
+
box-sizing: border-box;
|
|
18880
|
+
flex: none;
|
|
18881
|
+
width: var(--detail-list-column-width, 220px);
|
|
18882
|
+
height: var(--detail-list-item-height, 28px);
|
|
18883
|
+
--list-group-header-height: var(--detail-list-group-header-height, var(--detail-list-item-height, 28px));
|
|
18884
|
+
}
|
|
18885
|
+
|
|
18397
18886
|
::slotted(li) {
|
|
18398
18887
|
box-sizing: border-box;
|
|
18399
18888
|
display: flex;
|
|
@@ -18570,6 +19059,14 @@ __decorate([property({
|
|
|
18570
19059
|
})], DetailListElement.prototype, "itemsDraggable", void 0);
|
|
18571
19060
|
__decorate([property({ attribute: false })], DetailListElement.prototype, "dragPreviewRenderer", void 0);
|
|
18572
19061
|
__decorate([property({ attribute: false })], DetailListElement.prototype, "dragPreviewSource", void 0);
|
|
19062
|
+
__decorate([property({ attribute: `group-by` })], DetailListElement.prototype, "groupBy", void 0);
|
|
19063
|
+
__decorate([property({ attribute: false })], DetailListElement.prototype, "groupLabel", void 0);
|
|
19064
|
+
__decorate([property({
|
|
19065
|
+
type: Boolean,
|
|
19066
|
+
attribute: `cross-group-selection`,
|
|
19067
|
+
reflect: true
|
|
19068
|
+
})], DetailListElement.prototype, "crossGroupSelection", void 0);
|
|
19069
|
+
__decorate([state()], DetailListElement.prototype, "_collapsedGroups", void 0);
|
|
18573
19070
|
__decorate([query(`ul`)], DetailListElement.prototype, "_listEl", void 0);
|
|
18574
19071
|
DetailListElement = __decorate([safeCustomElement(`ixfx-detail-list`)], DetailListElement);
|
|
18575
19072
|
//#endregion
|
|
@@ -24209,7 +24706,374 @@ let FormElement = class FormElement extends LitElement {
|
|
|
24209
24706
|
};
|
|
24210
24707
|
FormElement = __decorate([safeCustomElement(`ixfx-form`)], FormElement);
|
|
24211
24708
|
//#endregion
|
|
24709
|
+
//#region src/grid-list/grid-list-layout.ts
|
|
24710
|
+
/**
|
|
24711
|
+
* `itemWidth` is the square's width; `rowHeight` is the full cell height
|
|
24712
|
+
* (square, plus the caption band in `separate` / `under` styles).
|
|
24713
|
+
* `scrollOffset` is `scrollTop` for `grid`/`column` and `scrollLeft` for `row`.
|
|
24714
|
+
*/
|
|
24715
|
+
function computeGridWindow(count, itemWidth, rowHeight, gap, viewportWidth, viewportHeight, scrollOffset, orientation, overscan = 2) {
|
|
24716
|
+
const w = Math.max(1, itemWidth);
|
|
24717
|
+
const h = Math.max(1, rowHeight);
|
|
24718
|
+
const g = Math.max(0, gap);
|
|
24719
|
+
const colStride = w + g;
|
|
24720
|
+
const rowStride = h + g;
|
|
24721
|
+
const offset = Math.max(0, scrollOffset);
|
|
24722
|
+
if (orientation === `row`) {
|
|
24723
|
+
const rows = count === 0 ? 0 : 1;
|
|
24724
|
+
const totalWidth = count === 0 ? 0 : (count - 1) * colStride + w;
|
|
24725
|
+
if (count === 0 || viewportWidth <= 0) return {
|
|
24726
|
+
cols: count,
|
|
24727
|
+
rows,
|
|
24728
|
+
totalHeight: 0,
|
|
24729
|
+
totalWidth,
|
|
24730
|
+
startIndex: 0,
|
|
24731
|
+
endIndex: 0,
|
|
24732
|
+
colStride,
|
|
24733
|
+
rowStride,
|
|
24734
|
+
horizontal: true
|
|
24735
|
+
};
|
|
24736
|
+
const firstVisible = Math.max(0, Math.floor(offset / colStride));
|
|
24737
|
+
const lastVisible = Math.ceil((offset + viewportWidth) / colStride);
|
|
24738
|
+
return {
|
|
24739
|
+
cols: count,
|
|
24740
|
+
rows,
|
|
24741
|
+
totalHeight: 0,
|
|
24742
|
+
totalWidth,
|
|
24743
|
+
startIndex: Math.max(0, firstVisible - overscan),
|
|
24744
|
+
endIndex: Math.min(count, lastVisible + overscan),
|
|
24745
|
+
colStride,
|
|
24746
|
+
rowStride,
|
|
24747
|
+
horizontal: true
|
|
24748
|
+
};
|
|
24749
|
+
}
|
|
24750
|
+
const cols = orientation === `column` ? 1 : Math.max(1, Math.floor((Math.max(0, viewportWidth) + g) / colStride));
|
|
24751
|
+
const rows = count === 0 ? 0 : Math.ceil(count / cols);
|
|
24752
|
+
const totalHeight = rows === 0 ? 0 : (rows - 1) * rowStride + h;
|
|
24753
|
+
if (count === 0 || viewportHeight <= 0) return {
|
|
24754
|
+
cols,
|
|
24755
|
+
rows,
|
|
24756
|
+
totalHeight,
|
|
24757
|
+
totalWidth: 0,
|
|
24758
|
+
startIndex: 0,
|
|
24759
|
+
endIndex: 0,
|
|
24760
|
+
colStride,
|
|
24761
|
+
rowStride,
|
|
24762
|
+
horizontal: false
|
|
24763
|
+
};
|
|
24764
|
+
const firstVisibleRow = Math.max(0, Math.floor(offset / rowStride));
|
|
24765
|
+
const lastVisibleRow = Math.ceil((offset + viewportHeight) / rowStride);
|
|
24766
|
+
const rowStart = Math.max(0, firstVisibleRow - overscan);
|
|
24767
|
+
const rowEnd = Math.min(rows, lastVisibleRow + overscan);
|
|
24768
|
+
return {
|
|
24769
|
+
cols,
|
|
24770
|
+
rows,
|
|
24771
|
+
totalHeight,
|
|
24772
|
+
totalWidth: 0,
|
|
24773
|
+
startIndex: rowStart * cols,
|
|
24774
|
+
endIndex: Math.min(count, rowEnd * cols),
|
|
24775
|
+
colStride,
|
|
24776
|
+
rowStride,
|
|
24777
|
+
horizontal: false
|
|
24778
|
+
};
|
|
24779
|
+
}
|
|
24780
|
+
function buildSections(p, cols, stride, itemExtent) {
|
|
24781
|
+
const g = Math.max(0, p.gap);
|
|
24782
|
+
const horizontal = p.orientation === `row`;
|
|
24783
|
+
const lineCount = (n) => horizontal ? n : Math.ceil(n / cols);
|
|
24784
|
+
const extentFor = (n) => n === 0 ? 0 : (lineCount(n) - 1) * stride + itemExtent;
|
|
24785
|
+
if (!p.groups) {
|
|
24786
|
+
const end = extentFor(p.count);
|
|
24787
|
+
return {
|
|
24788
|
+
sections: [{
|
|
24789
|
+
group: void 0,
|
|
24790
|
+
first: 0,
|
|
24791
|
+
count: p.count,
|
|
24792
|
+
headerOffset: 0,
|
|
24793
|
+
itemsOffset: 0,
|
|
24794
|
+
end
|
|
24795
|
+
}],
|
|
24796
|
+
total: end,
|
|
24797
|
+
count: p.count
|
|
24798
|
+
};
|
|
24799
|
+
}
|
|
24800
|
+
const headerSize = horizontal ? p.headerWidth : p.headerHeight;
|
|
24801
|
+
const sections = [];
|
|
24802
|
+
let cursor = 0;
|
|
24803
|
+
let first = 0;
|
|
24804
|
+
for (const group of p.groups) {
|
|
24805
|
+
const count = group.collapsed ? 0 : group.count;
|
|
24806
|
+
const itemsOffset = cursor + headerSize + g;
|
|
24807
|
+
const end = count === 0 ? cursor + headerSize : itemsOffset + extentFor(count);
|
|
24808
|
+
sections.push({
|
|
24809
|
+
group,
|
|
24810
|
+
first,
|
|
24811
|
+
count,
|
|
24812
|
+
headerOffset: cursor,
|
|
24813
|
+
itemsOffset,
|
|
24814
|
+
end
|
|
24815
|
+
});
|
|
24816
|
+
first += count;
|
|
24817
|
+
cursor = end + g;
|
|
24818
|
+
}
|
|
24819
|
+
return {
|
|
24820
|
+
sections,
|
|
24821
|
+
total: sections.length === 0 ? 0 : sections[sections.length - 1].end,
|
|
24822
|
+
count: first
|
|
24823
|
+
};
|
|
24824
|
+
}
|
|
24825
|
+
/** Index of the last section whose `headerOffset` is <= `offset` (0 when before all). */
|
|
24826
|
+
function sectionAtOffset(sections, offset) {
|
|
24827
|
+
let lo = 0;
|
|
24828
|
+
let hi = sections.length - 1;
|
|
24829
|
+
while (lo < hi) {
|
|
24830
|
+
const mid = lo + hi + 1 >> 1;
|
|
24831
|
+
if (sections[mid].headerOffset <= offset) lo = mid;
|
|
24832
|
+
else hi = mid - 1;
|
|
24833
|
+
}
|
|
24834
|
+
return lo;
|
|
24835
|
+
}
|
|
24836
|
+
/** Index of the section holding effective item `index` (skips empty sections). */
|
|
24837
|
+
function sectionOfIndex(sections, index) {
|
|
24838
|
+
let lo = 0;
|
|
24839
|
+
let hi = sections.length - 1;
|
|
24840
|
+
while (lo < hi) {
|
|
24841
|
+
const mid = lo + hi + 1 >> 1;
|
|
24842
|
+
if (sections[mid].first <= index) lo = mid;
|
|
24843
|
+
else hi = mid - 1;
|
|
24844
|
+
}
|
|
24845
|
+
let s = lo;
|
|
24846
|
+
while (s < sections.length - 1 && sections[s].count === 0) s++;
|
|
24847
|
+
return s;
|
|
24848
|
+
}
|
|
24849
|
+
function buildLayout(p) {
|
|
24850
|
+
const horizontal = p.orientation === `row`;
|
|
24851
|
+
const w = Math.max(1, p.itemWidth);
|
|
24852
|
+
const h = Math.max(1, p.rowHeight);
|
|
24853
|
+
const g = Math.max(0, p.gap);
|
|
24854
|
+
const colStride = w + g;
|
|
24855
|
+
const rowStride = h + g;
|
|
24856
|
+
const cols = horizontal ? 1 : p.orientation === `column` ? 1 : Math.max(1, Math.floor((Math.max(0, p.viewportWidth) + g) / colStride));
|
|
24857
|
+
const stride = horizontal ? colStride : rowStride;
|
|
24858
|
+
const itemExtent = horizontal ? w : h;
|
|
24859
|
+
const { sections, total, count } = buildSections(p, cols, stride, itemExtent);
|
|
24860
|
+
const grouped = p.groups !== void 0;
|
|
24861
|
+
const headerSize = horizontal ? p.headerWidth : p.headerHeight;
|
|
24862
|
+
const viewportMain = horizontal ? p.viewportWidth : p.viewportHeight;
|
|
24863
|
+
const cellHeight = rowStride - g;
|
|
24864
|
+
const rowY = Math.max(0, (p.viewportHeight - cellHeight) / 2);
|
|
24865
|
+
const positionOf = (index) => {
|
|
24866
|
+
const si = sectionOfIndex(sections, index);
|
|
24867
|
+
const s = sections[si];
|
|
24868
|
+
const local = Math.max(0, index - s.first);
|
|
24869
|
+
if (horizontal) return {
|
|
24870
|
+
x: s.itemsOffset + local * colStride,
|
|
24871
|
+
y: rowY,
|
|
24872
|
+
col: 0,
|
|
24873
|
+
line: local,
|
|
24874
|
+
section: si
|
|
24875
|
+
};
|
|
24876
|
+
const col = cols <= 1 ? 0 : local % cols;
|
|
24877
|
+
const line = cols <= 1 ? local : Math.floor(local / cols);
|
|
24878
|
+
return {
|
|
24879
|
+
x: col * colStride,
|
|
24880
|
+
y: s.itemsOffset + line * rowStride,
|
|
24881
|
+
col,
|
|
24882
|
+
line,
|
|
24883
|
+
section: si
|
|
24884
|
+
};
|
|
24885
|
+
};
|
|
24886
|
+
const window = (offset, overscan = 2) => {
|
|
24887
|
+
const off = Math.max(0, offset);
|
|
24888
|
+
const headers = [];
|
|
24889
|
+
const ranges = [];
|
|
24890
|
+
const lo = off - overscan * stride;
|
|
24891
|
+
const hi = off + viewportMain + overscan * stride;
|
|
24892
|
+
let firstSection = 0;
|
|
24893
|
+
if (sections.length > 1) {
|
|
24894
|
+
let a = 0;
|
|
24895
|
+
let b = sections.length - 1;
|
|
24896
|
+
while (a < b) {
|
|
24897
|
+
const mid = a + b >> 1;
|
|
24898
|
+
if (sections[mid].end >= lo) b = mid;
|
|
24899
|
+
else a = mid + 1;
|
|
24900
|
+
}
|
|
24901
|
+
firstSection = a;
|
|
24902
|
+
}
|
|
24903
|
+
if (viewportMain > 0) for (let si = firstSection; si < sections.length; si++) {
|
|
24904
|
+
const s = sections[si];
|
|
24905
|
+
if (s.headerOffset > hi) break;
|
|
24906
|
+
if (s.group && s.headerOffset + headerSize >= lo) headers.push({
|
|
24907
|
+
key: s.group.key,
|
|
24908
|
+
label: s.group.label,
|
|
24909
|
+
count: s.group.count,
|
|
24910
|
+
collapsed: s.group.collapsed,
|
|
24911
|
+
offset: s.headerOffset,
|
|
24912
|
+
size: headerSize
|
|
24913
|
+
});
|
|
24914
|
+
if (s.count === 0) continue;
|
|
24915
|
+
const rel = off - s.itemsOffset;
|
|
24916
|
+
const viewport = rel < 0 ? viewportMain + rel : viewportMain;
|
|
24917
|
+
if (viewport <= 0) continue;
|
|
24918
|
+
const sw = computeGridWindow(s.count, w, h, g, horizontal ? viewport : p.viewportWidth, horizontal ? p.viewportHeight : viewport, Math.max(0, rel), p.orientation, overscan);
|
|
24919
|
+
if (sw.endIndex > sw.startIndex) ranges.push([s.first + sw.startIndex, s.first + sw.endIndex]);
|
|
24920
|
+
}
|
|
24921
|
+
return {
|
|
24922
|
+
cols: horizontal ? count : cols,
|
|
24923
|
+
rows: 0,
|
|
24924
|
+
totalHeight: horizontal ? 0 : total,
|
|
24925
|
+
totalWidth: horizontal ? total : 0,
|
|
24926
|
+
startIndex: ranges.length > 0 ? ranges[0][0] : 0,
|
|
24927
|
+
endIndex: ranges.length > 0 ? ranges[ranges.length - 1][1] : 0,
|
|
24928
|
+
colStride,
|
|
24929
|
+
rowStride,
|
|
24930
|
+
horizontal,
|
|
24931
|
+
headers,
|
|
24932
|
+
ranges
|
|
24933
|
+
};
|
|
24934
|
+
};
|
|
24935
|
+
const extentOf = (index) => {
|
|
24936
|
+
const { line, section } = positionOf(index);
|
|
24937
|
+
const s = sections[section];
|
|
24938
|
+
const start = s.itemsOffset + line * stride;
|
|
24939
|
+
return [line === 0 && s.group ? s.headerOffset : start, start + itemExtent];
|
|
24940
|
+
};
|
|
24941
|
+
const neighbour = (index, dLine) => {
|
|
24942
|
+
if (count === 0) return index;
|
|
24943
|
+
const si = sectionOfIndex(sections, index);
|
|
24944
|
+
const s = sections[si];
|
|
24945
|
+
const local = index - s.first;
|
|
24946
|
+
const col = local % cols;
|
|
24947
|
+
if (dLine > 0) {
|
|
24948
|
+
const t = local + cols;
|
|
24949
|
+
if (t <= s.count - 1) return s.first + t;
|
|
24950
|
+
const lastLine = Math.floor((s.count - 1) / cols);
|
|
24951
|
+
if (Math.floor(local / cols) < lastLine) return s.first + s.count - 1;
|
|
24952
|
+
for (let n = si + 1; n < sections.length; n++) if (sections[n].count > 0) return sections[n].first + Math.min(col, sections[n].count - 1);
|
|
24953
|
+
return s.first + s.count - 1;
|
|
24954
|
+
}
|
|
24955
|
+
const t = local - cols;
|
|
24956
|
+
if (t >= 0) return s.first + t;
|
|
24957
|
+
for (let n = si - 1; n >= 0; n--) {
|
|
24958
|
+
const prev = sections[n];
|
|
24959
|
+
if (prev.count > 0) {
|
|
24960
|
+
const line = Math.floor((prev.count - 1) / cols);
|
|
24961
|
+
return prev.first + Math.min(line * cols + col, prev.count - 1);
|
|
24962
|
+
}
|
|
24963
|
+
}
|
|
24964
|
+
return s.first;
|
|
24965
|
+
};
|
|
24966
|
+
const hitTest = (x0, x1, y0, y1) => {
|
|
24967
|
+
const out = [];
|
|
24968
|
+
if (count === 0) return out;
|
|
24969
|
+
const main0 = horizontal ? x0 : y0;
|
|
24970
|
+
const main1 = horizontal ? x1 : y1;
|
|
24971
|
+
const start = sectionAtOffset(sections, main0 - stride);
|
|
24972
|
+
for (let si = Math.max(0, start); si < sections.length; si++) {
|
|
24973
|
+
const s = sections[si];
|
|
24974
|
+
if (s.itemsOffset > main1) break;
|
|
24975
|
+
if (s.count === 0 || s.end < main0) continue;
|
|
24976
|
+
if (horizontal) {
|
|
24977
|
+
const a = Math.max(0, Math.floor((x0 - s.itemsOffset) / colStride));
|
|
24978
|
+
const b = Math.min(s.count - 1, Math.floor((x1 - s.itemsOffset) / colStride));
|
|
24979
|
+
for (let i = a; i <= b; i++) out.push(s.first + i);
|
|
24980
|
+
} else {
|
|
24981
|
+
const colLo = Math.max(0, Math.floor(x0 / colStride));
|
|
24982
|
+
const colHi = Math.min(cols - 1, Math.floor(x1 / colStride));
|
|
24983
|
+
const rowLo = Math.max(0, Math.floor((y0 - s.itemsOffset) / rowStride));
|
|
24984
|
+
const rowHi = Math.floor((y1 - s.itemsOffset) / rowStride);
|
|
24985
|
+
for (let row = rowLo; row <= rowHi; row++) for (let col = colLo; col <= colHi; col++) {
|
|
24986
|
+
const local = row * cols + col;
|
|
24987
|
+
if (local >= 0 && local < s.count) out.push(s.first + local);
|
|
24988
|
+
}
|
|
24989
|
+
}
|
|
24990
|
+
}
|
|
24991
|
+
return out;
|
|
24992
|
+
};
|
|
24993
|
+
const anchorAt = (mainOffset) => {
|
|
24994
|
+
if (count === 0) return {
|
|
24995
|
+
index: 0,
|
|
24996
|
+
lineFrac: 0
|
|
24997
|
+
};
|
|
24998
|
+
let si = sectionAtOffset(sections, mainOffset);
|
|
24999
|
+
while (si < sections.length - 1 && sections[si].count === 0) si++;
|
|
25000
|
+
const s = sections[si];
|
|
25001
|
+
if (s.count === 0) return {
|
|
25002
|
+
index: 0,
|
|
25003
|
+
lineFrac: 0
|
|
25004
|
+
};
|
|
25005
|
+
const centreLine = Math.max(0, mainOffset - s.itemsOffset) / stride;
|
|
25006
|
+
const lineFloor = Math.max(0, Math.floor(centreLine));
|
|
25007
|
+
const local = horizontal ? Math.min(s.count - 1, lineFloor) : Math.min(s.count - 1, lineFloor * cols + Math.max(0, Math.floor((cols - 1) / 2)));
|
|
25008
|
+
return {
|
|
25009
|
+
index: s.first + local,
|
|
25010
|
+
lineFrac: centreLine - lineFloor
|
|
25011
|
+
};
|
|
25012
|
+
};
|
|
25013
|
+
const offsetOfAnchor = (index, lineFrac) => {
|
|
25014
|
+
const { line, section } = positionOf(index);
|
|
25015
|
+
return sections[section].itemsOffset + (line + lineFrac) * stride;
|
|
25016
|
+
};
|
|
25017
|
+
const snapTarget = (offset) => {
|
|
25018
|
+
const si = sectionAtOffset(sections, offset);
|
|
25019
|
+
const s = sections[si];
|
|
25020
|
+
let rel = Math.round((offset - s.itemsOffset) / stride);
|
|
25021
|
+
if (grouped) {
|
|
25022
|
+
const lines = s.count === 0 ? 0 : horizontal ? s.count : Math.ceil(s.count / cols);
|
|
25023
|
+
rel = Math.max(0, Math.min(Math.max(0, lines - 1), rel));
|
|
25024
|
+
}
|
|
25025
|
+
const candidates = [s.itemsOffset + rel * stride];
|
|
25026
|
+
if (grouped) {
|
|
25027
|
+
candidates.push(s.headerOffset);
|
|
25028
|
+
if (si + 1 < sections.length) candidates.push(sections[si + 1].headerOffset);
|
|
25029
|
+
}
|
|
25030
|
+
return candidates.reduce((best, c) => Math.abs(c - offset) < Math.abs(best - offset) ? c : best);
|
|
25031
|
+
};
|
|
25032
|
+
return {
|
|
25033
|
+
horizontal,
|
|
25034
|
+
cols,
|
|
25035
|
+
colStride,
|
|
25036
|
+
rowStride,
|
|
25037
|
+
grouped,
|
|
25038
|
+
count,
|
|
25039
|
+
totalExtent: total,
|
|
25040
|
+
window,
|
|
25041
|
+
positionOf,
|
|
25042
|
+
extentOf,
|
|
25043
|
+
neighbour,
|
|
25044
|
+
hitTest,
|
|
25045
|
+
anchorAt,
|
|
25046
|
+
offsetOfAnchor,
|
|
25047
|
+
snapTarget
|
|
25048
|
+
};
|
|
25049
|
+
}
|
|
25050
|
+
//#endregion
|
|
24212
25051
|
//#region src/grid-list/grid-list-source.ts
|
|
25052
|
+
/** Reorder `effective` raw indices into contiguous groups, dropping collapsed ones. */
|
|
25053
|
+
function applyGrouping(effective, cfg, keyOfRaw) {
|
|
25054
|
+
const parts = partitionByGroup(effective, (raw) => cfg.groupKeyOf(raw), cfg.labelOf ? (key, raws) => cfg.labelOf(key, raws.map((r) => cfg.itemAt(r))) : void 0);
|
|
25055
|
+
const order = [];
|
|
25056
|
+
const groups = [];
|
|
25057
|
+
const groupByKey = /* @__PURE__ */ new Map();
|
|
25058
|
+
for (const part of parts) {
|
|
25059
|
+
const collapsed = cfg.collapsed.has(part.key);
|
|
25060
|
+
groups.push({
|
|
25061
|
+
key: part.key,
|
|
25062
|
+
label: part.label,
|
|
25063
|
+
count: part.items.length,
|
|
25064
|
+
collapsed
|
|
25065
|
+
});
|
|
25066
|
+
for (const raw of part.items) {
|
|
25067
|
+
groupByKey.set(keyOfRaw(raw), part.key);
|
|
25068
|
+
if (!collapsed) order.push(raw);
|
|
25069
|
+
}
|
|
25070
|
+
}
|
|
25071
|
+
return {
|
|
25072
|
+
order,
|
|
25073
|
+
groups,
|
|
25074
|
+
groupByKey
|
|
25075
|
+
};
|
|
25076
|
+
}
|
|
24213
25077
|
const ITEM_SELECTOR$2 = `:scope > *`;
|
|
24214
25078
|
const SYNTHETIC_PREFIX = `gl-auto-`;
|
|
24215
25079
|
/** Whether `key` is an auto-assigned key for a keyless light-DOM item. */
|
|
@@ -24236,6 +25100,9 @@ var ElementGridListSource = class {
|
|
|
24236
25100
|
#searchKeys;
|
|
24237
25101
|
#effectiveIndices = [];
|
|
24238
25102
|
#keysCache;
|
|
25103
|
+
#grouping;
|
|
25104
|
+
#groups = [];
|
|
25105
|
+
#groupByKey = /* @__PURE__ */ new Map();
|
|
24239
25106
|
constructor(host) {
|
|
24240
25107
|
this.#host = host;
|
|
24241
25108
|
this.refresh();
|
|
@@ -24288,6 +25155,16 @@ var ElementGridListSource = class {
|
|
|
24288
25155
|
if (`searchKeys` in opts) this.#searchKeys = opts.searchKeys;
|
|
24289
25156
|
this.#recomputeEffective();
|
|
24290
25157
|
}
|
|
25158
|
+
setGrouping(config) {
|
|
25159
|
+
this.#grouping = config;
|
|
25160
|
+
this.#recomputeEffective();
|
|
25161
|
+
}
|
|
25162
|
+
groups() {
|
|
25163
|
+
return this.#groups;
|
|
25164
|
+
}
|
|
25165
|
+
groupKeyOfKey(key) {
|
|
25166
|
+
return this.#groupByKey.get(key) ?? null;
|
|
25167
|
+
}
|
|
24291
25168
|
refresh() {
|
|
24292
25169
|
this.#items = Array.from(this.#host.querySelectorAll(ITEM_SELECTOR$2)).filter((el) => !el.hidden);
|
|
24293
25170
|
for (const el of this.#items) {
|
|
@@ -24330,9 +25207,21 @@ var ElementGridListSource = class {
|
|
|
24330
25207
|
}
|
|
24331
25208
|
effective.push(i);
|
|
24332
25209
|
}
|
|
24333
|
-
this.#
|
|
25210
|
+
this.#applyGroupingTo(effective);
|
|
24334
25211
|
this.#keysCache = void 0;
|
|
24335
25212
|
}
|
|
25213
|
+
#applyGroupingTo(effective) {
|
|
25214
|
+
if (this.#grouping === void 0) {
|
|
25215
|
+
this.#effectiveIndices = effective;
|
|
25216
|
+
this.#groups = [];
|
|
25217
|
+
this.#groupByKey = /* @__PURE__ */ new Map();
|
|
25218
|
+
return;
|
|
25219
|
+
}
|
|
25220
|
+
const grouped = applyGrouping(effective, this.#grouping, (raw) => this.#itemKey(this.#items[raw]));
|
|
25221
|
+
this.#effectiveIndices = grouped.order;
|
|
25222
|
+
this.#groups = grouped.groups;
|
|
25223
|
+
this.#groupByKey = grouped.groupByKey;
|
|
25224
|
+
}
|
|
24336
25225
|
#itemKey(el) {
|
|
24337
25226
|
const userKey = keyForElement(el);
|
|
24338
25227
|
if (userKey !== void 0) return userKey;
|
|
@@ -24355,6 +25244,9 @@ var DataGridListSource = class {
|
|
|
24355
25244
|
#searchKeys;
|
|
24356
25245
|
#effectiveIndices = [];
|
|
24357
25246
|
#keysCache;
|
|
25247
|
+
#grouping;
|
|
25248
|
+
#groups = [];
|
|
25249
|
+
#groupByKey = /* @__PURE__ */ new Map();
|
|
24358
25250
|
constructor(items, adapter) {
|
|
24359
25251
|
this.#items = items;
|
|
24360
25252
|
this.#adapter = adapter;
|
|
@@ -24420,6 +25312,16 @@ var DataGridListSource = class {
|
|
|
24420
25312
|
if (`searchKeys` in opts) this.#searchKeys = opts.searchKeys;
|
|
24421
25313
|
this.#recomputeEffective();
|
|
24422
25314
|
}
|
|
25315
|
+
setGrouping(config) {
|
|
25316
|
+
this.#grouping = config;
|
|
25317
|
+
this.#recomputeEffective();
|
|
25318
|
+
}
|
|
25319
|
+
groups() {
|
|
25320
|
+
return this.#groups;
|
|
25321
|
+
}
|
|
25322
|
+
groupKeyOfKey(key) {
|
|
25323
|
+
return this.#groupByKey.get(key) ?? null;
|
|
25324
|
+
}
|
|
24423
25325
|
refresh() {
|
|
24424
25326
|
this.#recomputeEffective();
|
|
24425
25327
|
}
|
|
@@ -24444,7 +25346,16 @@ var DataGridListSource = class {
|
|
|
24444
25346
|
if (this.#searchKeys !== void 0 && !this.#searchKeys.has(this.#adapter.key(this.#items[i], i))) continue;
|
|
24445
25347
|
effective.push(i);
|
|
24446
25348
|
}
|
|
24447
|
-
this.#
|
|
25349
|
+
if (this.#grouping === void 0) {
|
|
25350
|
+
this.#effectiveIndices = effective;
|
|
25351
|
+
this.#groups = [];
|
|
25352
|
+
this.#groupByKey = /* @__PURE__ */ new Map();
|
|
25353
|
+
} else {
|
|
25354
|
+
const grouped = applyGrouping(effective, this.#grouping, (raw) => this.#adapter.key(this.#items[raw], raw));
|
|
25355
|
+
this.#effectiveIndices = grouped.order;
|
|
25356
|
+
this.#groups = grouped.groups;
|
|
25357
|
+
this.#groupByKey = grouped.groupByKey;
|
|
25358
|
+
}
|
|
24448
25359
|
this.#keysCache = void 0;
|
|
24449
25360
|
}
|
|
24450
25361
|
};
|
|
@@ -24460,76 +25371,11 @@ const SNAP_DEBOUNCE_MS = 120;
|
|
|
24460
25371
|
* to finish. Kept a touch above the default `--grid-list-item-move-duration`.
|
|
24461
25372
|
*/
|
|
24462
25373
|
const REFLOW_WINDOW_MS = 320;
|
|
24463
|
-
function
|
|
24464
|
-
|
|
24465
|
-
const h = Math.max(1, rowHeight);
|
|
24466
|
-
const g = Math.max(0, gap);
|
|
24467
|
-
const colStride = w + g;
|
|
24468
|
-
const rowStride = h + g;
|
|
24469
|
-
const offset = Math.max(0, scrollOffset);
|
|
24470
|
-
if (orientation === `row`) {
|
|
24471
|
-
const rows = count === 0 ? 0 : 1;
|
|
24472
|
-
const totalWidth = count === 0 ? 0 : (count - 1) * colStride + w;
|
|
24473
|
-
if (count === 0 || viewportWidth <= 0) return {
|
|
24474
|
-
cols: count,
|
|
24475
|
-
rows,
|
|
24476
|
-
totalHeight: 0,
|
|
24477
|
-
totalWidth,
|
|
24478
|
-
startIndex: 0,
|
|
24479
|
-
endIndex: 0,
|
|
24480
|
-
colStride,
|
|
24481
|
-
rowStride,
|
|
24482
|
-
horizontal: true
|
|
24483
|
-
};
|
|
24484
|
-
const firstVisible = Math.max(0, Math.floor(offset / colStride));
|
|
24485
|
-
const lastVisible = Math.ceil((offset + viewportWidth) / colStride);
|
|
24486
|
-
return {
|
|
24487
|
-
cols: count,
|
|
24488
|
-
rows,
|
|
24489
|
-
totalHeight: 0,
|
|
24490
|
-
totalWidth,
|
|
24491
|
-
startIndex: Math.max(0, firstVisible - overscan),
|
|
24492
|
-
endIndex: Math.min(count, lastVisible + overscan),
|
|
24493
|
-
colStride,
|
|
24494
|
-
rowStride,
|
|
24495
|
-
horizontal: true
|
|
24496
|
-
};
|
|
24497
|
-
}
|
|
24498
|
-
const cols = orientation === `column` ? 1 : Math.max(1, Math.floor((Math.max(0, viewportWidth) + g) / colStride));
|
|
24499
|
-
const rows = count === 0 ? 0 : Math.ceil(count / cols);
|
|
24500
|
-
const totalHeight = rows === 0 ? 0 : (rows - 1) * rowStride + h;
|
|
24501
|
-
if (count === 0 || viewportHeight <= 0) return {
|
|
24502
|
-
cols,
|
|
24503
|
-
rows,
|
|
24504
|
-
totalHeight,
|
|
24505
|
-
totalWidth: 0,
|
|
24506
|
-
startIndex: 0,
|
|
24507
|
-
endIndex: 0,
|
|
24508
|
-
colStride,
|
|
24509
|
-
rowStride,
|
|
24510
|
-
horizontal: false
|
|
24511
|
-
};
|
|
24512
|
-
const firstVisibleRow = Math.max(0, Math.floor(offset / rowStride));
|
|
24513
|
-
const lastVisibleRow = Math.ceil((offset + viewportHeight) / rowStride);
|
|
24514
|
-
const rowStart = Math.max(0, firstVisibleRow - overscan);
|
|
24515
|
-
const rowEnd = Math.min(rows, lastVisibleRow + overscan);
|
|
24516
|
-
return {
|
|
24517
|
-
cols,
|
|
24518
|
-
rows,
|
|
24519
|
-
totalHeight,
|
|
24520
|
-
totalWidth: 0,
|
|
24521
|
-
startIndex: rowStart * cols,
|
|
24522
|
-
endIndex: Math.min(count, rowEnd * cols),
|
|
24523
|
-
colStride,
|
|
24524
|
-
rowStride,
|
|
24525
|
-
horizontal: false
|
|
24526
|
-
};
|
|
24527
|
-
}
|
|
24528
|
-
function columnOf(index, cols) {
|
|
24529
|
-
return cols <= 1 ? 0 : index % cols;
|
|
25374
|
+
function readProperty(item, name) {
|
|
25375
|
+
return typeof item === `object` && item !== null ? item[name] : void 0;
|
|
24530
25376
|
}
|
|
24531
|
-
function
|
|
24532
|
-
return
|
|
25377
|
+
function readAttribute(el, name) {
|
|
25378
|
+
return el.getAttribute(`data-${name}`);
|
|
24533
25379
|
}
|
|
24534
25380
|
/** Shallow key-array equality, treating `undefined` (never dispatched) as different. */
|
|
24535
25381
|
function sameKeys$1(a, b) {
|
|
@@ -24557,6 +25403,11 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24557
25403
|
this.marqueeFromCellBlank = false;
|
|
24558
25404
|
this.snapToItem = false;
|
|
24559
25405
|
this.items = [];
|
|
25406
|
+
this.groupBy = null;
|
|
25407
|
+
this.crossGroupSelection = false;
|
|
25408
|
+
this.groupHeaderHeight = 28;
|
|
25409
|
+
this.groupHeaderWidth = 120;
|
|
25410
|
+
this._collapsedGroups = /* @__PURE__ */ new Set();
|
|
24560
25411
|
this._scrollTop = 0;
|
|
24561
25412
|
this._scrollLeft = 0;
|
|
24562
25413
|
this._viewportHeight = 0;
|
|
@@ -24581,6 +25432,12 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24581
25432
|
interactionMode: this.interactionMode
|
|
24582
25433
|
}),
|
|
24583
25434
|
getKey: (k) => k,
|
|
25435
|
+
getGroupKey: (k) => this.groupBy === null ? null : this.#source.groupKeyOfKey(k),
|
|
25436
|
+
crossGroupSelection: () => this.crossGroupSelection,
|
|
25437
|
+
getFallbackGroupKey: () => {
|
|
25438
|
+
const k = this.tickled.tickledId;
|
|
25439
|
+
return k !== void 0 && this.groupBy !== null ? this.#source.groupKeyOfKey(k) : null;
|
|
25440
|
+
},
|
|
24584
25441
|
onChange: (selected, previous) => {
|
|
24585
25442
|
this._syncItemStates();
|
|
24586
25443
|
this.requestUpdate();
|
|
@@ -24678,10 +25535,11 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24678
25535
|
},
|
|
24679
25536
|
shouldStart: (event) => {
|
|
24680
25537
|
if (this.marqueeFromCellBlank && this.#isCellBlankPress(event)) return true;
|
|
24681
|
-
for (const node of event.composedPath()) if (node instanceof Element && node.classList.contains(`cell`)) return false;
|
|
25538
|
+
for (const node of event.composedPath()) if (node instanceof Element && (node.classList.contains(`cell`) || node.classList.contains(`group-header`))) return false;
|
|
24682
25539
|
return true;
|
|
24683
25540
|
}
|
|
24684
25541
|
});
|
|
25542
|
+
this.#groupByChanged = false;
|
|
24685
25543
|
this.#selectedSnapshot = /* @__PURE__ */ new Set();
|
|
24686
25544
|
this.#onScroll = (e) => {
|
|
24687
25545
|
const el = e.target;
|
|
@@ -24731,15 +25589,25 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24731
25589
|
const count = this.#source.count;
|
|
24732
25590
|
if (count === 0) return;
|
|
24733
25591
|
const win = this.#currentWindow();
|
|
24734
|
-
const
|
|
24735
|
-
|
|
24736
|
-
|
|
25592
|
+
const layout = this.#layout();
|
|
25593
|
+
const current = () => this.tickled.tickledId ? this.#source.indexOfKey(this.tickled.tickledId) : -1;
|
|
25594
|
+
const goto = (nextIndex) => {
|
|
24737
25595
|
const nextKey = this.#source.keyAt(nextIndex);
|
|
24738
25596
|
if (nextKey) {
|
|
24739
25597
|
this.tickled.handleKeyboardNavigation(nextKey);
|
|
24740
25598
|
this.#scrollItemIntoView(nextKey);
|
|
24741
25599
|
}
|
|
24742
25600
|
};
|
|
25601
|
+
const move = (delta) => goto(Math.max(0, Math.min(count - 1, current() + delta)));
|
|
25602
|
+
const moveLines = (dir, lines = 1) => {
|
|
25603
|
+
let index = current();
|
|
25604
|
+
if (index < 0) {
|
|
25605
|
+
move(dir * lines * win.cols);
|
|
25606
|
+
return;
|
|
25607
|
+
}
|
|
25608
|
+
for (let i = 0; i < lines; i++) index = layout.neighbour(index, dir);
|
|
25609
|
+
goto(index);
|
|
25610
|
+
};
|
|
24743
25611
|
const perLine = this.orientation === `grid` ? win.cols : 1;
|
|
24744
25612
|
switch (e.key) {
|
|
24745
25613
|
case `ArrowLeft`:
|
|
@@ -24749,10 +25617,12 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24749
25617
|
move(1);
|
|
24750
25618
|
break;
|
|
24751
25619
|
case `ArrowUp`:
|
|
24752
|
-
|
|
25620
|
+
if (this.orientation === `grid`) moveLines(-1);
|
|
25621
|
+
else move(-perLine);
|
|
24753
25622
|
break;
|
|
24754
25623
|
case `ArrowDown`:
|
|
24755
|
-
|
|
25624
|
+
if (this.orientation === `grid`) moveLines(1);
|
|
25625
|
+
else move(perLine);
|
|
24756
25626
|
break;
|
|
24757
25627
|
case `Home`:
|
|
24758
25628
|
this.tickled.handleKeyboardNavigation(`first`);
|
|
@@ -24767,7 +25637,8 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24767
25637
|
const mainExtent = win.horizontal ? this._viewportWidth : this._viewportHeight;
|
|
24768
25638
|
const mainStride = win.horizontal ? win.colStride : win.rowStride;
|
|
24769
25639
|
const visibleLines = Math.max(1, Math.floor(mainExtent / mainStride));
|
|
24770
|
-
|
|
25640
|
+
if (this.orientation === `grid`) moveLines(e.key === `PageDown` ? 1 : -1, visibleLines);
|
|
25641
|
+
else move((e.key === `PageDown` ? 1 : -1) * visibleLines * perLine);
|
|
24771
25642
|
break;
|
|
24772
25643
|
}
|
|
24773
25644
|
case `Enter`: {
|
|
@@ -24848,6 +25719,11 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24848
25719
|
position: relative;
|
|
24849
25720
|
width: 100%;
|
|
24850
25721
|
}
|
|
25722
|
+
.group-header {
|
|
25723
|
+
position: absolute;
|
|
25724
|
+
inset: 0 auto auto 0;
|
|
25725
|
+
z-index: 1;
|
|
25726
|
+
}
|
|
24851
25727
|
.cell {
|
|
24852
25728
|
position: absolute;
|
|
24853
25729
|
inset: 0 auto auto 0;
|
|
@@ -25141,6 +26017,9 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25141
26017
|
specificity. Survivors slide to their new slot; a freshly inserted cell
|
|
25142
26018
|
also fades in. (A removed item's element is already gone, so there's
|
|
25143
26019
|
nothing left to fade — the gap simply closes as the others slide.) */
|
|
26020
|
+
:host([reflowing]) .group-header {
|
|
26021
|
+
transition: transform var(--grid-list-item-move-duration, 200ms) cubic-bezier(0.2, 0, 0, 1);
|
|
26022
|
+
}
|
|
25144
26023
|
:host([reflowing]) .cell {
|
|
25145
26024
|
transition:
|
|
25146
26025
|
transform var(--grid-list-item-move-duration, 200ms) cubic-bezier(0.2, 0, 0, 1),
|
|
@@ -25151,6 +26030,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25151
26030
|
animation: ixfx-list-item-in var(--grid-list-item-move-duration, 200ms) ease both;
|
|
25152
26031
|
}
|
|
25153
26032
|
@media (prefers-reduced-motion: reduce) {
|
|
26033
|
+
:host([reflowing]) .group-header { transition: none; }
|
|
25154
26034
|
:host([reflowing]) .cell { transition: background var(--transition), border-color var(--transition); }
|
|
25155
26035
|
.cell.entering { animation: none; }
|
|
25156
26036
|
}
|
|
@@ -25292,31 +26172,13 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25292
26172
|
* so virtualized off-screen items are covered without DOM measurement.
|
|
25293
26173
|
*/
|
|
25294
26174
|
#marqueeHitTest(rect) {
|
|
25295
|
-
|
|
25296
|
-
if (count === 0) return [];
|
|
26175
|
+
if (this.#source.count === 0) return [];
|
|
25297
26176
|
const horizontal = this.orientation === `row`;
|
|
25298
|
-
const
|
|
25299
|
-
const
|
|
25300
|
-
|
|
25301
|
-
|
|
25302
|
-
|
|
25303
|
-
const lo = Math.max(0, Math.floor(x0 / win.colStride));
|
|
25304
|
-
const hi = Math.min(count - 1, Math.floor(x1 / win.colStride));
|
|
25305
|
-
for (let i = lo; i <= hi; i++) out.push(i);
|
|
25306
|
-
} else {
|
|
25307
|
-
const x0 = rect.x + this._scrollLeft;
|
|
25308
|
-
const x1 = x0 + rect.width;
|
|
25309
|
-
const y0 = rect.y + this._scrollTop;
|
|
25310
|
-
const y1 = y0 + rect.height;
|
|
25311
|
-
const colLo = Math.max(0, Math.floor(x0 / win.colStride));
|
|
25312
|
-
const colHi = Math.min(win.cols - 1, Math.floor(x1 / win.colStride));
|
|
25313
|
-
const rowLo = Math.max(0, Math.floor(y0 / win.rowStride));
|
|
25314
|
-
const rowHi = Math.floor(y1 / win.rowStride);
|
|
25315
|
-
for (let row = rowLo; row <= rowHi; row++) for (let col = colLo; col <= colHi; col++) {
|
|
25316
|
-
const index = row * win.cols + col;
|
|
25317
|
-
if (index >= 0 && index < count) out.push(index);
|
|
25318
|
-
}
|
|
25319
|
-
}
|
|
26177
|
+
const x0 = rect.x + this._scrollLeft;
|
|
26178
|
+
const x1 = x0 + rect.width;
|
|
26179
|
+
const y0 = horizontal ? 0 : rect.y + this._scrollTop;
|
|
26180
|
+
const y1 = horizontal ? 0 : y0 + rect.height;
|
|
26181
|
+
const out = this.#layout().hitTest(x0, x1, y0, y1);
|
|
25320
26182
|
const keys = [];
|
|
25321
26183
|
for (const index of out) {
|
|
25322
26184
|
const key = this.#source.keyAt(index);
|
|
@@ -25466,6 +26328,10 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25466
26328
|
if (previousLength === 0 && removedCount === 0) return;
|
|
25467
26329
|
if (added.length > 0 && added.length === this.#items.length) return;
|
|
25468
26330
|
for (const el of added) this.#entering.add(el);
|
|
26331
|
+
this.#openReflowWindow();
|
|
26332
|
+
}
|
|
26333
|
+
/** Open (or extend) the `[reflowing]` window so moved cells and headers slide. */
|
|
26334
|
+
#openReflowWindow() {
|
|
25469
26335
|
this.toggleAttribute(`reflowing`, true);
|
|
25470
26336
|
if (this.#reflowTimer) clearTimeout(this.#reflowTimer);
|
|
25471
26337
|
this.#reflowTimer = window.setTimeout(() => {
|
|
@@ -25522,6 +26388,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25522
26388
|
this.#sourceElement = new ElementGridListSource(this);
|
|
25523
26389
|
this.#source = this.#sourceElement;
|
|
25524
26390
|
}
|
|
26391
|
+
this.#source.setGrouping(this.#groupingConfig());
|
|
25525
26392
|
this.#applyFilters();
|
|
25526
26393
|
if (this.#searchFilterKeys) this.#source.setFilters({ searchKeys: this.#searchFilterKeys });
|
|
25527
26394
|
}
|
|
@@ -25563,6 +26430,17 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25563
26430
|
}
|
|
25564
26431
|
this.search?.refresh();
|
|
25565
26432
|
}
|
|
26433
|
+
const groupsChanged = changed.has(`groupBy`) || changed.has(`groupLabel`) || changed.has(`_collapsedGroups`);
|
|
26434
|
+
if (groupsChanged && this.hasUpdated) {
|
|
26435
|
+
if (changed.has(`groupBy`)) {
|
|
26436
|
+
if (this._collapsedGroups.size > 0) this._collapsedGroups = /* @__PURE__ */ new Set();
|
|
26437
|
+
this.#groupByChanged = true;
|
|
26438
|
+
}
|
|
26439
|
+
this.#applyGroupingToSource();
|
|
26440
|
+
this.#dropUnreachableCursor();
|
|
26441
|
+
this.#openReflowWindow();
|
|
26442
|
+
this.updateComplete.then(() => this.#emitViewportChange());
|
|
26443
|
+
} else if (groupsChanged) this.#applyGroupingToSource();
|
|
25566
26444
|
if (changed.has(`itemSize`) || changed.has(`gap`)) {
|
|
25567
26445
|
this.itemSize = Math.max(MIN_ITEM_SIZE, Math.round(this.itemSize));
|
|
25568
26446
|
this.gap = Math.max(0, Math.round(this.gap));
|
|
@@ -25583,6 +26461,62 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25583
26461
|
}
|
|
25584
26462
|
}
|
|
25585
26463
|
}
|
|
26464
|
+
#groupByChanged;
|
|
26465
|
+
/** Grouping config for the active source, or `undefined` when ungrouped. */
|
|
26466
|
+
#groupingConfig() {
|
|
26467
|
+
const by = this.groupBy;
|
|
26468
|
+
if (by === null || by === void 0 || by === ``) return void 0;
|
|
26469
|
+
const collapsed = this._collapsedGroups;
|
|
26470
|
+
const src = this.#source;
|
|
26471
|
+
if (src instanceof DataGridListSource) return {
|
|
26472
|
+
groupKeyOf: (raw) => resolveGroupKey(by, src.rawItemAt(raw), raw, readProperty),
|
|
26473
|
+
itemAt: (raw) => src.rawItemAt(raw),
|
|
26474
|
+
labelOf: this.groupLabel,
|
|
26475
|
+
collapsed
|
|
26476
|
+
};
|
|
26477
|
+
const elements = src;
|
|
26478
|
+
return {
|
|
26479
|
+
groupKeyOf: (raw) => resolveGroupKey(by, elements.rawElementAt(raw), raw, readAttribute),
|
|
26480
|
+
itemAt: (raw) => elements.rawElementAt(raw),
|
|
26481
|
+
labelOf: this.groupLabel,
|
|
26482
|
+
collapsed
|
|
26483
|
+
};
|
|
26484
|
+
}
|
|
26485
|
+
/** Push the current grouping into the source and refresh the element-mode item cache. */
|
|
26486
|
+
#applyGroupingToSource() {
|
|
26487
|
+
this.#source.setGrouping(this.#groupingConfig());
|
|
26488
|
+
if (this.#source instanceof ElementGridListSource) this.#items = this.#source.effectiveElements();
|
|
26489
|
+
}
|
|
26490
|
+
/** The cursor can't stay on an item that a collapsed group just removed. */
|
|
26491
|
+
#dropUnreachableCursor() {
|
|
26492
|
+
const id = this.tickled.tickledId;
|
|
26493
|
+
if (id !== void 0 && this.#source.indexOfKey(id) < 0) this.tickled.focus(void 0);
|
|
26494
|
+
}
|
|
26495
|
+
/** Groups in display order (empty when ungrouped). */
|
|
26496
|
+
get groups() {
|
|
26497
|
+
return this.groupBy === null ? [] : this.#source.groups();
|
|
26498
|
+
}
|
|
26499
|
+
/** Collapse or expand one group. Fires `list-group-toggle` when the state changes. */
|
|
26500
|
+
setGroupCollapsed(groupKey, collapsed) {
|
|
26501
|
+
if (this._collapsedGroups.has(groupKey) === collapsed) return;
|
|
26502
|
+
const next = new Set(this._collapsedGroups);
|
|
26503
|
+
if (collapsed) next.add(groupKey);
|
|
26504
|
+
else next.delete(groupKey);
|
|
26505
|
+
this._collapsedGroups = next;
|
|
26506
|
+
this._dispatch(`list-group-toggle`, {
|
|
26507
|
+
groupKey,
|
|
26508
|
+
collapsed
|
|
26509
|
+
});
|
|
26510
|
+
}
|
|
26511
|
+
toggleGroup(groupKey) {
|
|
26512
|
+
this.setGroupCollapsed(groupKey, !this._collapsedGroups.has(groupKey));
|
|
26513
|
+
}
|
|
26514
|
+
collapseAllGroups() {
|
|
26515
|
+
for (const g of this.groups) this.setGroupCollapsed(g.key, true);
|
|
26516
|
+
}
|
|
26517
|
+
expandAllGroups() {
|
|
26518
|
+
for (const key of [...this._collapsedGroups]) this.setGroupCollapsed(key, false);
|
|
26519
|
+
}
|
|
25586
26520
|
#syncAriaMultiselectable() {
|
|
25587
26521
|
this.setAttribute(`aria-multiselectable`, this.selection.canSelectMultiple() ? `true` : `false`);
|
|
25588
26522
|
}
|
|
@@ -25622,6 +26556,10 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25622
26556
|
}
|
|
25623
26557
|
this.#applyZoomAnchor();
|
|
25624
26558
|
this.#activateTickledPan();
|
|
26559
|
+
if (this.#groupByChanged) {
|
|
26560
|
+
this.#groupByChanged = false;
|
|
26561
|
+
this._dispatch(`list-group-change`, { groupBy: this.groupBy });
|
|
26562
|
+
}
|
|
25625
26563
|
}
|
|
25626
26564
|
/** Re-anchor the scroll so the item captured in `willUpdate` stays centred. */
|
|
25627
26565
|
#applyZoomAnchor() {
|
|
@@ -25630,41 +26568,33 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25630
26568
|
this.#zoomAnchor = void 0;
|
|
25631
26569
|
const vp = this.#viewportRef.value;
|
|
25632
26570
|
if (!vp) return;
|
|
25633
|
-
const
|
|
25634
|
-
|
|
25635
|
-
|
|
25636
|
-
|
|
25637
|
-
|
|
25638
|
-
|
|
26571
|
+
const layout = this.#layout();
|
|
26572
|
+
const horizontal = layout.horizontal;
|
|
26573
|
+
const extent = horizontal ? this._viewportWidth : this._viewportHeight;
|
|
26574
|
+
if (extent <= 0) return;
|
|
26575
|
+
const targetCenter = layout.offsetOfAnchor(index, lineFrac);
|
|
26576
|
+
const maxScroll = Math.max(0, layout.totalExtent - extent);
|
|
26577
|
+
const next = Math.min(maxScroll, Math.max(0, targetCenter - extent / 2));
|
|
26578
|
+
if (horizontal) {
|
|
25639
26579
|
if (Math.abs(vp.scrollLeft - next) > .5) {
|
|
25640
26580
|
vp.scrollLeft = next;
|
|
25641
26581
|
this._scrollLeft = next;
|
|
25642
26582
|
}
|
|
25643
|
-
|
|
25644
|
-
}
|
|
25645
|
-
if (this._viewportHeight <= 0) return;
|
|
25646
|
-
const targetCenter = ((win.cols > 0 ? Math.floor(index / win.cols) : 0) + lineFrac) * win.rowStride;
|
|
25647
|
-
const maxScroll = Math.max(0, win.totalHeight - this._viewportHeight);
|
|
25648
|
-
const next = Math.min(maxScroll, Math.max(0, targetCenter - this._viewportHeight / 2));
|
|
25649
|
-
if (Math.abs(vp.scrollTop - next) > .5) {
|
|
26583
|
+
} else if (Math.abs(vp.scrollTop - next) > .5) {
|
|
25650
26584
|
vp.scrollTop = next;
|
|
25651
26585
|
this._scrollTop = next;
|
|
25652
26586
|
}
|
|
25653
26587
|
}
|
|
25654
26588
|
#captureCentreAnchor(prevOffset, prevExtent, prevItemSize, prevGap, prevCaptionStyle, prevCaptionHeight, prevOrientation) {
|
|
25655
|
-
|
|
25656
|
-
if (count === 0) return;
|
|
26589
|
+
if ((this.#source?.count ?? 0) === 0) return;
|
|
25657
26590
|
const prevRowHeight = prevItemSize + (prevCaptionStyle === `separate` || prevCaptionStyle === `under` ? prevCaptionHeight : 0);
|
|
25658
|
-
const
|
|
25659
|
-
|
|
25660
|
-
|
|
25661
|
-
|
|
25662
|
-
|
|
25663
|
-
|
|
25664
|
-
this.#zoomAnchor =
|
|
25665
|
-
index,
|
|
25666
|
-
lineFrac: centerLine - lineFloor
|
|
25667
|
-
};
|
|
26591
|
+
const prev = this.#buildLayout({
|
|
26592
|
+
itemWidth: prevItemSize,
|
|
26593
|
+
rowHeight: prevRowHeight,
|
|
26594
|
+
gap: prevGap,
|
|
26595
|
+
orientation: prevOrientation
|
|
26596
|
+
});
|
|
26597
|
+
this.#zoomAnchor = prev.anchorAt(prevOffset + prevExtent / 2);
|
|
25668
26598
|
}
|
|
25669
26599
|
/**
|
|
25670
26600
|
* Re-read the viewport after an ancestor's visibility/layout changed.
|
|
@@ -25878,37 +26808,75 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25878
26808
|
return this.#windowKeys(this.#currentWindow(true));
|
|
25879
26809
|
}
|
|
25880
26810
|
#windowKeys(win) {
|
|
25881
|
-
|
|
26811
|
+
const keys = this.#source.keys();
|
|
26812
|
+
const out = [];
|
|
26813
|
+
for (const [start, end] of win.ranges) for (let i = start; i < end; i++) if (!isSyntheticKey(keys[i])) out.push(keys[i]);
|
|
26814
|
+
return out;
|
|
26815
|
+
}
|
|
26816
|
+
/** Layout params for the current geometry, with optional overrides (used for zoom anchoring). */
|
|
26817
|
+
#layoutParams(over = {}) {
|
|
26818
|
+
return {
|
|
26819
|
+
count: this.#source.count,
|
|
26820
|
+
groups: this.groupBy === null ? void 0 : this.#source.groups(),
|
|
26821
|
+
itemWidth: this.itemSize,
|
|
26822
|
+
rowHeight: this.#rowHeight(),
|
|
26823
|
+
gap: this.gap,
|
|
26824
|
+
viewportWidth: this._viewportWidth,
|
|
26825
|
+
viewportHeight: this._viewportHeight,
|
|
26826
|
+
orientation: this.orientation,
|
|
26827
|
+
headerHeight: this.groupHeaderHeight,
|
|
26828
|
+
headerWidth: this.groupHeaderWidth,
|
|
26829
|
+
...over
|
|
26830
|
+
};
|
|
26831
|
+
}
|
|
26832
|
+
#buildLayout(over = {}) {
|
|
26833
|
+
return buildLayout(this.#layoutParams(over));
|
|
26834
|
+
}
|
|
26835
|
+
#layoutCache;
|
|
26836
|
+
/** The current layout, rebuilt only when its inputs change (never per scroll). */
|
|
26837
|
+
#layout() {
|
|
26838
|
+
const params = this.#layoutParams();
|
|
26839
|
+
const cached = this.#layoutCache;
|
|
26840
|
+
if (cached) {
|
|
26841
|
+
const a = cached.params;
|
|
26842
|
+
let same = a.groups === params.groups;
|
|
26843
|
+
for (const k of Object.keys(params)) {
|
|
26844
|
+
if (!same) break;
|
|
26845
|
+
same = a[k] === params[k];
|
|
26846
|
+
}
|
|
26847
|
+
if (same) return cached.layout;
|
|
26848
|
+
}
|
|
26849
|
+
const layout = buildLayout(params);
|
|
26850
|
+
this.#layoutCache = {
|
|
26851
|
+
params,
|
|
26852
|
+
layout
|
|
26853
|
+
};
|
|
26854
|
+
return layout;
|
|
25882
26855
|
}
|
|
25883
26856
|
#currentWindow(noOverscan = false) {
|
|
25884
|
-
|
|
25885
|
-
return computeGridWindow(this.#source.count, this.itemSize, this.#rowHeight(), this.gap, this._viewportWidth, this._viewportHeight, this.orientation === `row` ? this._scrollLeft : this._scrollTop, this.orientation, overscan);
|
|
26857
|
+
return this.#layout().window(this.orientation === `row` ? this._scrollLeft : this._scrollTop, noOverscan ? 0 : 2);
|
|
25886
26858
|
}
|
|
25887
26859
|
/** Adjust the scroll offset so `key` is fully within the viewport. */
|
|
25888
26860
|
#scrollItemIntoView(key) {
|
|
25889
26861
|
const index = this.#source.indexOfKey(key);
|
|
25890
26862
|
const vp = this.#viewportRef.value;
|
|
25891
26863
|
if (index < 0 || !vp) return;
|
|
25892
|
-
const
|
|
25893
|
-
if (
|
|
25894
|
-
const left = index * win.colStride;
|
|
25895
|
-
const right = left + this.itemSize;
|
|
26864
|
+
const [start, end] = this.#layout().extentOf(index);
|
|
26865
|
+
if (this.orientation === `row`) {
|
|
25896
26866
|
const viewLeft = vp.scrollLeft;
|
|
25897
26867
|
let next = viewLeft;
|
|
25898
|
-
if (
|
|
25899
|
-
else if (
|
|
26868
|
+
if (start < viewLeft) next = start;
|
|
26869
|
+
else if (end > viewLeft + this._viewportWidth) next = end - this._viewportWidth;
|
|
25900
26870
|
if (next !== viewLeft) {
|
|
25901
26871
|
vp.scrollLeft = next;
|
|
25902
26872
|
this._scrollLeft = next;
|
|
25903
26873
|
}
|
|
25904
26874
|
return;
|
|
25905
26875
|
}
|
|
25906
|
-
const top = (win.cols > 0 ? Math.floor(index / win.cols) : 0) * win.rowStride;
|
|
25907
|
-
const bottom = top + this.#rowHeight();
|
|
25908
26876
|
const viewTop = vp.scrollTop;
|
|
25909
26877
|
let next = viewTop;
|
|
25910
|
-
if (
|
|
25911
|
-
else if (
|
|
26878
|
+
if (start < viewTop) next = start;
|
|
26879
|
+
else if (end > viewTop + this._viewportHeight) next = end - this._viewportHeight;
|
|
25912
26880
|
if (next !== viewTop) {
|
|
25913
26881
|
vp.scrollTop = next;
|
|
25914
26882
|
this._scrollTop = next;
|
|
@@ -25982,6 +26950,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25982
26950
|
>
|
|
25983
26951
|
<div class="spacer" style=${spacerStyle}>
|
|
25984
26952
|
${this.#source instanceof DataGridListSource ? this.#renderDataCells(win) : this.#renderElementCells(win)}
|
|
26953
|
+
${this.#renderHeaders(win)}
|
|
25985
26954
|
</div>
|
|
25986
26955
|
${this.#marqueeRect ? html`<div class="marquee" part="marquee" style=${this.#marqueeStyle(this.#marqueeRect)}></div>` : ``}
|
|
25987
26956
|
</div>
|
|
@@ -25989,7 +26958,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25989
26958
|
`;
|
|
25990
26959
|
}
|
|
25991
26960
|
#renderDataCells(win) {
|
|
25992
|
-
const slice = Array.from({ length:
|
|
26961
|
+
const slice = win.ranges.flatMap(([start, end]) => Array.from({ length: end - start }, (_, i) => start + i));
|
|
25993
26962
|
return repeat(slice, (index) => this.#source.keyAt(index), (index) => this.#renderDataCell(index, win));
|
|
25994
26963
|
}
|
|
25995
26964
|
#renderDataCell(index, win) {
|
|
@@ -26004,8 +26973,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
26004
26973
|
const caption = style === `hidden` ? `` : this.#source.labelAt(index);
|
|
26005
26974
|
const showCaption = caption !== ``;
|
|
26006
26975
|
const cellHeight = win.rowStride - this.gap;
|
|
26007
|
-
const x
|
|
26008
|
-
const y = win.horizontal ? Math.max(0, (this._viewportHeight - cellHeight) / 2) : rowOf(index, win.cols) * win.rowStride;
|
|
26976
|
+
const { x, y } = this.#layout().positionOf(index);
|
|
26009
26977
|
const captionCss = separate ? `height:${Math.max(0, this.captionHeight - 4)}px` : style === `under` ? `height:${this.captionHeight}px` : ``;
|
|
26010
26978
|
const rawIcon = this.#source.iconAt(index);
|
|
26011
26979
|
const icon = appearanceAbsent(rawIcon) ? void 0 : rawIcon;
|
|
@@ -26091,8 +27059,25 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
26091
27059
|
current.startLoop?.();
|
|
26092
27060
|
}
|
|
26093
27061
|
#renderElementCells(win) {
|
|
26094
|
-
const slice =
|
|
26095
|
-
return repeat(slice, (
|
|
27062
|
+
const slice = win.ranges.flatMap(([start, end]) => Array.from({ length: end - start }, (_, i) => start + i));
|
|
27063
|
+
return repeat(slice, (index) => this.#source.keyAt(index), (index) => this.#renderCell(this.#items[index], index, win));
|
|
27064
|
+
}
|
|
27065
|
+
#renderHeaders(win) {
|
|
27066
|
+
if (win.headers.length === 0) return nothing;
|
|
27067
|
+
const horizontal = win.horizontal;
|
|
27068
|
+
return repeat(win.headers, (h) => `__group__${h.key}`, (h) => html`
|
|
27069
|
+
<ixfx-list-group-header
|
|
27070
|
+
class="group-header"
|
|
27071
|
+
part="group-header"
|
|
27072
|
+
data-group-key=${h.key}
|
|
27073
|
+
label=${h.label}
|
|
27074
|
+
count=${h.count}
|
|
27075
|
+
orientation=${horizontal ? `vertical` : `horizontal`}
|
|
27076
|
+
?collapsed=${h.collapsed}
|
|
27077
|
+
style=${horizontal ? `width:${h.size}px;height:100%;transform:translate(${h.offset}px,0)` : `width:100%;height:${h.size}px;transform:translate(0,${h.offset}px)`}
|
|
27078
|
+
@click=${() => this.toggleGroup(h.key)}
|
|
27079
|
+
></ixfx-list-group-header>
|
|
27080
|
+
`);
|
|
26096
27081
|
}
|
|
26097
27082
|
#renderCell(el, index, win) {
|
|
26098
27083
|
const key = this.#source.keyAt(index);
|
|
@@ -26107,8 +27092,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
26107
27092
|
const caption = style === `hidden` ? `` : this.#captionFor(el);
|
|
26108
27093
|
const showCaption = caption !== ``;
|
|
26109
27094
|
const cellHeight = win.rowStride - this.gap;
|
|
26110
|
-
const x
|
|
26111
|
-
const y = win.horizontal ? Math.max(0, (this._viewportHeight - cellHeight) / 2) : rowOf(index, win.cols) * win.rowStride;
|
|
27095
|
+
const { x, y } = this.#layout().positionOf(index);
|
|
26112
27096
|
const thumbStyle = separate ? `height:${this.itemSize}px` : ``;
|
|
26113
27097
|
const captionCss = separate ? `height:${Math.max(0, this.captionHeight - 4)}px` : style === `under` ? `height:${this.captionHeight}px` : ``;
|
|
26114
27098
|
return html`
|
|
@@ -26207,16 +27191,12 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
26207
27191
|
#snapToNearestLine() {
|
|
26208
27192
|
const vp = this.#viewportRef.value;
|
|
26209
27193
|
if (!vp) return;
|
|
26210
|
-
const
|
|
26211
|
-
const horizontal =
|
|
26212
|
-
const stride = horizontal ? win.colStride : win.rowStride;
|
|
26213
|
-
if (stride <= 0) return;
|
|
27194
|
+
const layout = this.#layout();
|
|
27195
|
+
const horizontal = layout.horizontal;
|
|
26214
27196
|
const offset = horizontal ? vp.scrollLeft : vp.scrollTop;
|
|
26215
|
-
const extent = horizontal ? win.totalWidth : win.totalHeight;
|
|
26216
27197
|
const viewportExtent = horizontal ? this._viewportWidth : this._viewportHeight;
|
|
26217
|
-
const maxScroll = Math.max(0,
|
|
26218
|
-
const
|
|
26219
|
-
const target = Math.min(maxScroll, Math.max(0, index * stride));
|
|
27198
|
+
const maxScroll = Math.max(0, layout.totalExtent - viewportExtent);
|
|
27199
|
+
const target = Math.min(maxScroll, Math.max(0, layout.snapTarget(offset)));
|
|
26220
27200
|
if (Math.abs(target - offset) < .5) return;
|
|
26221
27201
|
vp.scrollTo(horizontal ? {
|
|
26222
27202
|
left: target,
|
|
@@ -26244,7 +27224,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
26244
27224
|
if (mode === `manual`) return;
|
|
26245
27225
|
if (this.selectionMode === `none` && mode !== `checked` && mode !== `sticky`) return;
|
|
26246
27226
|
if (this.#dragSelect.isDragging || this.#nativeDragging || this.#nativeDrag.isDragging || this.#postDragSuppressTimer !== 0) return;
|
|
26247
|
-
for (const node of e.composedPath()) if (node instanceof Element && node.classList.contains(`cell`)) return;
|
|
27227
|
+
for (const node of e.composedPath()) if (node instanceof Element && (node.classList.contains(`cell`) || node.classList.contains(`group-header`))) return;
|
|
26248
27228
|
this.selection.clear();
|
|
26249
27229
|
}
|
|
26250
27230
|
#startPostDragSuppressTimer() {
|
|
@@ -26345,6 +27325,22 @@ __decorate([property({
|
|
|
26345
27325
|
})], GridListElement.prototype, "snapToItem", void 0);
|
|
26346
27326
|
__decorate([property({ attribute: false })], GridListElement.prototype, "items", void 0);
|
|
26347
27327
|
__decorate([property({ attribute: false })], GridListElement.prototype, "itemAdapter", void 0);
|
|
27328
|
+
__decorate([property({ attribute: `group-by` })], GridListElement.prototype, "groupBy", void 0);
|
|
27329
|
+
__decorate([property({ attribute: false })], GridListElement.prototype, "groupLabel", void 0);
|
|
27330
|
+
__decorate([property({
|
|
27331
|
+
type: Boolean,
|
|
27332
|
+
attribute: `cross-group-selection`,
|
|
27333
|
+
reflect: true
|
|
27334
|
+
})], GridListElement.prototype, "crossGroupSelection", void 0);
|
|
27335
|
+
__decorate([property({
|
|
27336
|
+
type: Number,
|
|
27337
|
+
attribute: `group-header-height`
|
|
27338
|
+
})], GridListElement.prototype, "groupHeaderHeight", void 0);
|
|
27339
|
+
__decorate([property({
|
|
27340
|
+
type: Number,
|
|
27341
|
+
attribute: `group-header-width`
|
|
27342
|
+
})], GridListElement.prototype, "groupHeaderWidth", void 0);
|
|
27343
|
+
__decorate([state()], GridListElement.prototype, "_collapsedGroups", void 0);
|
|
26348
27344
|
__decorate([state()], GridListElement.prototype, "_scrollTop", void 0);
|
|
26349
27345
|
__decorate([state()], GridListElement.prototype, "_scrollLeft", void 0);
|
|
26350
27346
|
__decorate([state()], GridListElement.prototype, "_viewportHeight", void 0);
|
|
@@ -53949,6 +54945,6 @@ function init() {
|
|
|
53949
54945
|
console.log(`Init`);
|
|
53950
54946
|
}
|
|
53951
54947
|
//#endregion
|
|
53952
|
-
export { AcTextElement, AcTokenElement, BottomDrawerElement, ButtonElement, Checkbox, CheckedSelect, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DEFAULT_SIBLING_SPACING, DataController, DataDisplayComponent, DataGridElement, DataTableElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GridListElement, GroupedItemListerElement, HyperbolicTreeListElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IcicleTreeListElement, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, KeyedElementIndex, LIST_ITEM_MOTION_MS, LIST_SEARCH_COMMAND_IDS, LIST_SEARCH_MODES, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListDragSelectController, ListNativeDragController, ListSearchController, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, MulticolListElement, NarrowedTextElement, NotificationManager, NotificationPillElement, PanTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, ShapefillValueElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TickledController, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeDropController, TreeKeyboardNavController, TreeListElement, TreemapListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, animateListItemIn, animateListItemOut, applyAgeModification, applyHighlights, applyIsometry, applySnapshot, applyTreeDrop, attachTimelineInteractions, bottomDrawerCommandId, buildHighlightTargets, buildModel, captionForEntry, clampToDisk, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, composeIsometry, computeCanonicalPositions, computeColour, computeHyperbolicLayout, computeIcicleLayout, computeLeafGeoms, computeTreemapLayout, createAcCompletionProvider, createDataListSearchAdapter, createDefaultListDragPreview, createElementIncrSearch, createElementListSearchAdapter, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createTreeListSearchAdapter, createVerticalListAdapter, decomposeViewIsometry, distribute, dropTargetFor, euclideanRadiusForHyperbolicDistance, extractStack, findHyperbolicIdOf, findHyperbolicKeyPath, findHyperbolicNode, findHyperbolicPath, findHyperbolicPathById, findIcicleNode, findIciclePath, findNode, findPanelNode, findParentOf, findStackOf, findStackPathByName, findTreemapNode, findTreemapPath, geodesicInterpolate, geodesicPath, geodesicSegment, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, hyperbolicBranchId, hyperbolicChildIds, hyperbolicDistanceForEuclideanRadius, hyperbolicIdDepth, hyperbolicRelativeFrame, iconBus, identityIsometry, init, insertBeside, invertIsometry, isDragging, isHyperbolicDescendantId, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, listSearchStyles, mobiusAdd, mobiusFromOrigin, mobiusToOrigin, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, norm, normalise, normaliseValue, normalize, panelGapIndex, parseColourScaleAttr, parseLayout, poincareDistance, pointerOverAnyHost, prefersReducedMotion, preserveRestoreWeights, pruneDraggedDescendants, readBaseColour, registerFontIcon, registerIcon, registerListSearchCommands, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, rotationIsometry, serializeLayout, setActive, setIconFont, setPanelRestoreWeight, startListItemOutAnimation, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, toolbarGapIndex, translationFromOrigin, viewIsometry, walk, wrapRoot };
|
|
54948
|
+
export { AcTextElement, AcTokenElement, BottomDrawerElement, ButtonElement, Checkbox, CheckedSelect, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DEFAULT_SIBLING_SPACING, DataController, DataDisplayComponent, DataGridElement, DataTableElement, DetailListElement, DockHostElement, DockPanelElement, EMPTY_GROUP_LABEL, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GridListElement, GroupedItemListerElement, HyperbolicTreeListElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IcicleTreeListElement, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, KeyedElementIndex, LIST_ITEM_MOTION_MS, LIST_SEARCH_COMMAND_IDS, LIST_SEARCH_MODES, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListDragSelectController, ListGroupHeaderElement, ListNativeDragController, ListSearchController, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, MulticolListElement, NarrowedTextElement, NotificationManager, NotificationPillElement, PanTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, ShapefillValueElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TickledController, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeDropController, TreeKeyboardNavController, TreeListElement, TreemapListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, animateListItemIn, animateListItemOut, applyAgeModification, applyHighlights, applyIsometry, applySnapshot, applyTreeDrop, attachTimelineInteractions, bottomDrawerCommandId, buildHighlightTargets, buildModel, captionForEntry, clampToDisk, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, composeIsometry, computeCanonicalPositions, computeColour, computeHyperbolicLayout, computeIcicleLayout, computeLeafGeoms, computeTreemapLayout, createAcCompletionProvider, createDataListSearchAdapter, createDefaultListDragPreview, createElementIncrSearch, createElementListSearchAdapter, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createTreeListSearchAdapter, createVerticalListAdapter, decomposeViewIsometry, defaultGroupLabel, distribute, dropTargetFor, euclideanRadiusForHyperbolicDistance, extractStack, findHyperbolicIdOf, findHyperbolicKeyPath, findHyperbolicNode, findHyperbolicPath, findHyperbolicPathById, findIcicleNode, findIciclePath, findNode, findPanelNode, findParentOf, findStackOf, findStackPathByName, findTreemapNode, findTreemapPath, geodesicInterpolate, geodesicPath, geodesicSegment, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, hyperbolicBranchId, hyperbolicChildIds, hyperbolicDistanceForEuclideanRadius, hyperbolicIdDepth, hyperbolicRelativeFrame, iconBus, identityIsometry, init, insertBeside, invertIsometry, isDragging, isHyperbolicDescendantId, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, listSearchStyles, mobiusAdd, mobiusFromOrigin, mobiusToOrigin, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, norm, normalise, normaliseValue, normalize, panelGapIndex, parseColourScaleAttr, parseLayout, partitionByGroup, poincareDistance, pointerOverAnyHost, prefersReducedMotion, preserveRestoreWeights, pruneDraggedDescendants, readBaseColour, registerFontIcon, registerIcon, registerListSearchCommands, removePanelFromStack, resolveCssVars, resolveGrid, resolveGroupKey, resolveLeafPath, resolveTracks, rotationIsometry, serializeLayout, setActive, setIconFont, setPanelRestoreWeight, startListItemOutAnimation, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toGroupKey, toSnapshot, toggleInSet, toolbarGapIndex, translationFromOrigin, viewIsometry, walk, wrapRoot };
|
|
53953
54949
|
|
|
53954
54950
|
//# sourceMappingURL=index.js.map
|