@ixfx/components 0.11.6 → 0.11.8
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 +213 -2
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +1766 -165
- package/bundle/index.js.map +1 -1
- package/dist/checkbox.d.ts +3 -0
- package/dist/checkbox.d.ts.map +1 -1
- package/dist/checkbox.js +24 -1
- package/dist/checkbox.js.map +1 -1
- package/dist/checked-select-CglRukbP.d.ts +59 -0
- package/dist/checked-select-CglRukbP.d.ts.map +1 -0
- package/dist/checked-select-Dejl13qd.js +604 -0
- package/dist/checked-select-Dejl13qd.js.map +1 -0
- package/dist/checked-select.d.ts +2 -0
- package/dist/checked-select.js +3 -0
- package/dist/{detail-list-CmdCxddo.d.ts → detail-list-lE_5UWVl.d.ts} +36 -3
- package/dist/detail-list-lE_5UWVl.d.ts.map +1 -0
- package/dist/detail-list.d.ts +3 -2
- package/dist/detail-list.js +293 -6
- package/dist/detail-list.js.map +1 -1
- package/dist/{grid-list-D6AfWfwn.d.ts → grid-list-Bc-RkDoO.d.ts} +42 -1
- package/dist/grid-list-Bc-RkDoO.d.ts.map +1 -0
- 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.d.ts +3 -2
- package/dist/grid-list.js +2 -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/{hex-editor-BiRfwTz1.js → hex-editor-DiYpgIKs.js} +2 -2
- package/dist/{hex-editor-BiRfwTz1.js.map → hex-editor-DiYpgIKs.js.map} +1 -1
- package/dist/hex.js +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -6
- package/dist/index.js.map +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/plots.js +1 -1
- package/dist/vertical-list.js +1 -1
- package/dist/{xy-axis-COKw338Q.js → xy-axis-CyMd_Bn1.js} +2 -2
- package/dist/{xy-axis-COKw338Q.js.map → xy-axis-CyMd_Bn1.js.map} +1 -1
- package/docs-user/README.md +2 -0
- package/docs-user/checked-select.md +63 -0
- package/docs-user/detail-list.md +28 -0
- package/docs-user/grid-list.md +35 -3
- package/docs-user/index.json +12 -2
- package/docs-user/list-group-header.md +14 -0
- package/docs-user/llms.txt +2 -0
- package/llms.txt +2 -0
- package/package.json +9 -1
- package/dist/detail-list-CmdCxddo.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/list-selection-controller-SA4piSBi.js.map +0 -1
package/bundle/index.js
CHANGED
|
@@ -5,10 +5,10 @@ import { styleMap } from "lit/directives/style-map.js";
|
|
|
5
5
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
6
6
|
import { Fzf } from "fzf";
|
|
7
7
|
import { createCommandRegistry } from "@clinth/ui-commands";
|
|
8
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
8
9
|
import { Colour } from "@ixfx/visual";
|
|
9
10
|
import { Trees } from "@ixfx/collections";
|
|
10
11
|
import { clamp, clamper, rangeStream, round, scaler } from "@ixfx/numbers";
|
|
11
|
-
import { repeat } from "lit/directives/repeat.js";
|
|
12
12
|
import { classMap } from "lit/directives/class-map.js";
|
|
13
13
|
import { createRef, ref } from "lit/directives/ref.js";
|
|
14
14
|
import { Easings } from "@ixfx/modulation";
|
|
@@ -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;
|
|
@@ -8238,6 +8326,7 @@ SplitButton = __decorate([safeCustomElement(`ixfx-split-button`)], SplitButton);
|
|
|
8238
8326
|
* Properties
|
|
8239
8327
|
* checked – checkbox state (default: false)
|
|
8240
8328
|
* disabled – disabled state (default: false)
|
|
8329
|
+
* readonly – display-only: no pointer/keyboard interaction, full opacity (default: false)
|
|
8241
8330
|
* label – label text
|
|
8242
8331
|
* name – form field name
|
|
8243
8332
|
* value – form field value
|
|
@@ -8269,12 +8358,14 @@ let Checkbox = class Checkbox extends LitElement {
|
|
|
8269
8358
|
this.checked = false;
|
|
8270
8359
|
this.disabled = false;
|
|
8271
8360
|
this.indeterminate = false;
|
|
8361
|
+
this.readonly = false;
|
|
8272
8362
|
this.label = ``;
|
|
8273
8363
|
this.name = ``;
|
|
8274
8364
|
this.value = ``;
|
|
8275
8365
|
this.#handleChange = (e) => {
|
|
8276
8366
|
const target = e.target;
|
|
8277
8367
|
this.checked = target.checked;
|
|
8368
|
+
this.indeterminate = false;
|
|
8278
8369
|
this.dispatchEvent(new CustomEvent(`change`, {
|
|
8279
8370
|
detail: { checked: this.checked },
|
|
8280
8371
|
bubbles: true,
|
|
@@ -8337,6 +8428,16 @@ let Checkbox = class Checkbox extends LitElement {
|
|
|
8337
8428
|
opacity: var(--box-opacity-checked);
|
|
8338
8429
|
}
|
|
8339
8430
|
|
|
8431
|
+
/* Display-only: inert, keeps full opacity, no hover reveal */
|
|
8432
|
+
:host([readonly]) {
|
|
8433
|
+
pointer-events: none;
|
|
8434
|
+
}
|
|
8435
|
+
:host([readonly]) label,
|
|
8436
|
+
:host([readonly]) label:hover {
|
|
8437
|
+
cursor: default;
|
|
8438
|
+
opacity: var(--box-opacity-checked);
|
|
8439
|
+
}
|
|
8440
|
+
|
|
8340
8441
|
input {
|
|
8341
8442
|
position: absolute;
|
|
8342
8443
|
opacity: 0;
|
|
@@ -8448,6 +8549,10 @@ let Checkbox = class Checkbox extends LitElement {
|
|
|
8448
8549
|
`
|
|
8449
8550
|
];
|
|
8450
8551
|
}
|
|
8552
|
+
/** Focus the inner input, so the host can be focused like a native checkbox. */
|
|
8553
|
+
focus(options) {
|
|
8554
|
+
this.renderRoot.querySelector(`input`)?.focus(options);
|
|
8555
|
+
}
|
|
8451
8556
|
#handleChange;
|
|
8452
8557
|
render() {
|
|
8453
8558
|
return html`
|
|
@@ -8457,6 +8562,8 @@ let Checkbox = class Checkbox extends LitElement {
|
|
|
8457
8562
|
type="checkbox"
|
|
8458
8563
|
.checked=${this.checked}
|
|
8459
8564
|
.disabled=${this.disabled}
|
|
8565
|
+
tabindex=${this.readonly ? `-1` : nothing}
|
|
8566
|
+
aria-hidden=${this.readonly ? `true` : nothing}
|
|
8460
8567
|
.indeterminate=${this.indeterminate}
|
|
8461
8568
|
.name=${this.name}
|
|
8462
8569
|
.value=${this.value}
|
|
@@ -8471,10 +8578,605 @@ let Checkbox = class Checkbox extends LitElement {
|
|
|
8471
8578
|
__decorate([property({ type: Boolean })], Checkbox.prototype, "checked", void 0);
|
|
8472
8579
|
__decorate([property({ type: Boolean })], Checkbox.prototype, "disabled", void 0);
|
|
8473
8580
|
__decorate([property({ type: Boolean })], Checkbox.prototype, "indeterminate", void 0);
|
|
8581
|
+
__decorate([property({
|
|
8582
|
+
type: Boolean,
|
|
8583
|
+
reflect: true
|
|
8584
|
+
})], Checkbox.prototype, "readonly", void 0);
|
|
8474
8585
|
__decorate([property({ type: String })], Checkbox.prototype, "label", void 0);
|
|
8475
8586
|
__decorate([property({ type: String })], Checkbox.prototype, "name", void 0);
|
|
8476
8587
|
__decorate([property({ type: String })], Checkbox.prototype, "value", void 0);
|
|
8477
8588
|
Checkbox = __decorate([safeCustomElement(`ixfx-checkbox`)], Checkbox);
|
|
8589
|
+
//#endregion
|
|
8590
|
+
//#region src/checked-select/logic.ts
|
|
8591
|
+
function byLabel(a, b) {
|
|
8592
|
+
return a.label.localeCompare(b.label, void 0, { sensitivity: `base` });
|
|
8593
|
+
}
|
|
8594
|
+
/** Checked/indeterminate items first, then the rest; each group sorted by label. */
|
|
8595
|
+
function sortForOpen(items) {
|
|
8596
|
+
const on = items.filter((i) => i.checked !== `false`).toSorted(byLabel);
|
|
8597
|
+
const off = items.filter((i) => i.checked === `false`).toSorted(byLabel);
|
|
8598
|
+
return [...on, ...off];
|
|
8599
|
+
}
|
|
8600
|
+
/** User toggle: indeterminate and unchecked become checked, checked becomes unchecked. */
|
|
8601
|
+
function toggleState(state) {
|
|
8602
|
+
return state === `true` ? `false` : `true`;
|
|
8603
|
+
}
|
|
8604
|
+
/** Items shown in the resting state, alphabetically. */
|
|
8605
|
+
function visibleAtRest(items) {
|
|
8606
|
+
return items.filter((i) => i.checked !== `false`).toSorted(byLabel);
|
|
8607
|
+
}
|
|
8608
|
+
const VARIES_LABEL = `(varies)`;
|
|
8609
|
+
/** Resting text entries: checked labels, then a single `(varies)` if any are indeterminate. */
|
|
8610
|
+
function restTextParts(items) {
|
|
8611
|
+
const on = items.filter((i) => i.checked === `true`).toSorted(byLabel).map((i) => i.label);
|
|
8612
|
+
const varies = items.some((i) => i.checked === `indeterminate`) ? [VARIES_LABEL] : [];
|
|
8613
|
+
return [...on, ...varies];
|
|
8614
|
+
}
|
|
8615
|
+
/** Plain-text resting summary, comma separated. */
|
|
8616
|
+
function restText(items) {
|
|
8617
|
+
return restTextParts(items).join(`, `);
|
|
8618
|
+
}
|
|
8619
|
+
/** Returns a new list with the item matching `value` toggled. */
|
|
8620
|
+
function applyToggle(items, value) {
|
|
8621
|
+
return items.map((i) => i.value === value ? {
|
|
8622
|
+
...i,
|
|
8623
|
+
checked: toggleState(i.checked)
|
|
8624
|
+
} : i);
|
|
8625
|
+
}
|
|
8626
|
+
/**
|
|
8627
|
+
* Index of the next label after `current` beginning with `char` (case-insensitive),
|
|
8628
|
+
* wrapping around. Returns -1 if nothing matches.
|
|
8629
|
+
*/
|
|
8630
|
+
function nextMatchIndex(labels, current, char) {
|
|
8631
|
+
const c = char.toLocaleLowerCase();
|
|
8632
|
+
const count = labels.length;
|
|
8633
|
+
for (let step = 1; step <= count; step++) {
|
|
8634
|
+
const index = (current + step + count) % count;
|
|
8635
|
+
if (labels[index].toLocaleLowerCase().startsWith(c)) return index;
|
|
8636
|
+
}
|
|
8637
|
+
return -1;
|
|
8638
|
+
}
|
|
8639
|
+
/** Items in `draft` whose state differs from `original`. */
|
|
8640
|
+
function diffItems(original, draft) {
|
|
8641
|
+
const before = new Map(original.map((i) => [i.value, i.checked]));
|
|
8642
|
+
return draft.filter((i) => before.get(i.value) !== i.checked);
|
|
8643
|
+
}
|
|
8644
|
+
function clampSize(size, min, max) {
|
|
8645
|
+
return {
|
|
8646
|
+
width: Math.min(Math.max(size.width, min.width), Math.max(max.width, min.width)),
|
|
8647
|
+
height: Math.min(Math.max(size.height, min.height), Math.max(max.height, min.height))
|
|
8648
|
+
};
|
|
8649
|
+
}
|
|
8650
|
+
//#endregion
|
|
8651
|
+
//#region src/checked-select/checked-select.ts
|
|
8652
|
+
let instanceCounter = 0;
|
|
8653
|
+
let CheckedSelect = class CheckedSelect extends LitElement {
|
|
8654
|
+
constructor(..._args) {
|
|
8655
|
+
super(..._args);
|
|
8656
|
+
this.items = [];
|
|
8657
|
+
this.multiline = false;
|
|
8658
|
+
this.itemsInert = false;
|
|
8659
|
+
this.itemDisplay = `checkbox`;
|
|
8660
|
+
this.orientation = `horizontal`;
|
|
8661
|
+
this.placeholder = ``;
|
|
8662
|
+
this.disabled = false;
|
|
8663
|
+
this._open = false;
|
|
8664
|
+
this._draft = [];
|
|
8665
|
+
this._order = [];
|
|
8666
|
+
this.#anchorName = `--checked-select-anchor-${instanceCounter++}`;
|
|
8667
|
+
this.#tickled = new TickledController(this, { getItems: () => this._order.map((_, i) => ({ id: i })) });
|
|
8668
|
+
this.#onRestCheckboxChange = (e, value) => {
|
|
8669
|
+
e.stopPropagation();
|
|
8670
|
+
if (this._open) return;
|
|
8671
|
+
const next = applyToggle(this.items, value);
|
|
8672
|
+
this.#emitChange(next, diffItems(this.items, next));
|
|
8673
|
+
this.#focusRestCheckbox(value);
|
|
8674
|
+
};
|
|
8675
|
+
this.#onDocumentPointerDown = (e) => {
|
|
8676
|
+
const path = e.composedPath();
|
|
8677
|
+
const popup = this.#popup;
|
|
8678
|
+
const trigger = this.#trigger;
|
|
8679
|
+
if (popup && path.includes(popup) || trigger && path.includes(trigger)) return;
|
|
8680
|
+
this.cancel();
|
|
8681
|
+
};
|
|
8682
|
+
this.#onTriggerClick = (e) => {
|
|
8683
|
+
if (e.target.closest?.(`ixfx-checkbox`)) return;
|
|
8684
|
+
const scroller = e.composedPath()[0];
|
|
8685
|
+
if (scroller.classList?.contains(`rest-items`) && e instanceof MouseEvent && (e.offsetX > scroller.clientWidth || e.offsetY > scroller.clientHeight)) return;
|
|
8686
|
+
if (this._open) this.commit();
|
|
8687
|
+
else this.open();
|
|
8688
|
+
};
|
|
8689
|
+
this.#onTriggerKeyDown = (e) => {
|
|
8690
|
+
if (this._open || e.target !== e.currentTarget) return;
|
|
8691
|
+
if (e.key === `Enter` || e.key === ` ` || e.key === `ArrowDown`) {
|
|
8692
|
+
e.preventDefault();
|
|
8693
|
+
this.open();
|
|
8694
|
+
}
|
|
8695
|
+
};
|
|
8696
|
+
this.#onListKeyDown = (e) => {
|
|
8697
|
+
const rows = this.#rows();
|
|
8698
|
+
switch (e.key) {
|
|
8699
|
+
case `ArrowDown`:
|
|
8700
|
+
e.preventDefault();
|
|
8701
|
+
this.#tickled.handleKeyboardNavigation(`next`);
|
|
8702
|
+
this.#scrollToCursor();
|
|
8703
|
+
return;
|
|
8704
|
+
case `ArrowUp`:
|
|
8705
|
+
e.preventDefault();
|
|
8706
|
+
this.#tickled.handleKeyboardNavigation(`previous`);
|
|
8707
|
+
this.#scrollToCursor();
|
|
8708
|
+
return;
|
|
8709
|
+
case `Home`:
|
|
8710
|
+
e.preventDefault();
|
|
8711
|
+
this.#tickled.handleKeyboardNavigation(`first`);
|
|
8712
|
+
this.#scrollToCursor();
|
|
8713
|
+
return;
|
|
8714
|
+
case `End`:
|
|
8715
|
+
e.preventDefault();
|
|
8716
|
+
this.#tickled.handleKeyboardNavigation(`last`);
|
|
8717
|
+
this.#scrollToCursor();
|
|
8718
|
+
return;
|
|
8719
|
+
case ` `: {
|
|
8720
|
+
e.preventDefault();
|
|
8721
|
+
const row = rows[this.#tickled.tickledId ?? -1];
|
|
8722
|
+
if (row) this.#toggleRow(row.value);
|
|
8723
|
+
return;
|
|
8724
|
+
}
|
|
8725
|
+
case `Enter`:
|
|
8726
|
+
e.preventDefault();
|
|
8727
|
+
this.commit();
|
|
8728
|
+
return;
|
|
8729
|
+
case `Escape`:
|
|
8730
|
+
e.preventDefault();
|
|
8731
|
+
this.cancel();
|
|
8732
|
+
return;
|
|
8733
|
+
case `Tab`:
|
|
8734
|
+
this.cancel();
|
|
8735
|
+
return;
|
|
8736
|
+
}
|
|
8737
|
+
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
8738
|
+
const next = nextMatchIndex(rows.map((r) => r.label), this.#tickled.tickledId ?? -1, e.key);
|
|
8739
|
+
if (next >= 0) {
|
|
8740
|
+
this.#tickled.handleKeyboardNavigation(next);
|
|
8741
|
+
this.#scrollToCursor();
|
|
8742
|
+
}
|
|
8743
|
+
}
|
|
8744
|
+
};
|
|
8745
|
+
this.#onResizeDown = (e) => {
|
|
8746
|
+
const frame = this.shadowRoot?.querySelector(`.frame`);
|
|
8747
|
+
const zone = e.currentTarget;
|
|
8748
|
+
if (!frame) return;
|
|
8749
|
+
e.preventDefault();
|
|
8750
|
+
const start = frame.getBoundingClientRect();
|
|
8751
|
+
const originX = e.clientX;
|
|
8752
|
+
const originY = e.clientY;
|
|
8753
|
+
const style = getComputedStyle(this);
|
|
8754
|
+
const min = {
|
|
8755
|
+
width: Number.parseFloat(style.getPropertyValue(`--checked-select-min-width`)) || 128,
|
|
8756
|
+
height: Number.parseFloat(style.getPropertyValue(`--checked-select-min-height`)) || 80
|
|
8757
|
+
};
|
|
8758
|
+
const max = {
|
|
8759
|
+
width: window.innerWidth - start.left - 8,
|
|
8760
|
+
height: window.innerHeight - start.top - 8
|
|
8761
|
+
};
|
|
8762
|
+
zone.setPointerCapture?.(e.pointerId);
|
|
8763
|
+
zone.setAttribute(`data-active`, ``);
|
|
8764
|
+
const onMove = (m) => {
|
|
8765
|
+
this._size = clampSize({
|
|
8766
|
+
width: start.width + m.clientX - originX,
|
|
8767
|
+
height: start.height + m.clientY - originY
|
|
8768
|
+
}, min, max);
|
|
8769
|
+
};
|
|
8770
|
+
const onEnd = () => {
|
|
8771
|
+
zone.removeAttribute(`data-active`);
|
|
8772
|
+
zone.removeEventListener(`pointermove`, onMove);
|
|
8773
|
+
zone.removeEventListener(`pointerup`, onEnd);
|
|
8774
|
+
zone.removeEventListener(`pointercancel`, onEnd);
|
|
8775
|
+
this.#listbox?.focus();
|
|
8776
|
+
};
|
|
8777
|
+
zone.addEventListener(`pointermove`, onMove);
|
|
8778
|
+
zone.addEventListener(`pointerup`, onEnd);
|
|
8779
|
+
zone.addEventListener(`pointercancel`, onEnd);
|
|
8780
|
+
};
|
|
8781
|
+
}
|
|
8782
|
+
static {
|
|
8783
|
+
this.styles = [
|
|
8784
|
+
themeFallbacks,
|
|
8785
|
+
handleStyles,
|
|
8786
|
+
tickledStyles,
|
|
8787
|
+
tickledItemStyles,
|
|
8788
|
+
css`
|
|
8789
|
+
:host {
|
|
8790
|
+
--checked-select-gap: var(--space-m);
|
|
8791
|
+
--checked-select-padding: var(--space-s) var(--space-m);
|
|
8792
|
+
--checked-select-caret-width: 2rem;
|
|
8793
|
+
--checked-select-rest-max-height: 8rem;
|
|
8794
|
+
--checked-select-max-height: 16rem;
|
|
8795
|
+
--checked-select-min-width: 8rem;
|
|
8796
|
+
--checked-select-min-height: 5rem;
|
|
8797
|
+
|
|
8798
|
+
display: block;
|
|
8799
|
+
position: relative;
|
|
8800
|
+
}
|
|
8801
|
+
|
|
8802
|
+
:host([disabled]) {
|
|
8803
|
+
opacity: var(--prominence-disabled-opacity, 0.4);
|
|
8804
|
+
pointer-events: none;
|
|
8805
|
+
}
|
|
8806
|
+
|
|
8807
|
+
.rest {
|
|
8808
|
+
position: relative;
|
|
8809
|
+
box-sizing: border-box;
|
|
8810
|
+
cursor: pointer;
|
|
8811
|
+
background: var(--surface-4);
|
|
8812
|
+
color: var(--surface-4-text);
|
|
8813
|
+
border: var(--border);
|
|
8814
|
+
border-radius: var(--radius-m);
|
|
8815
|
+
user-select: none;
|
|
8816
|
+
}
|
|
8817
|
+
|
|
8818
|
+
.rest-items {
|
|
8819
|
+
display: flex;
|
|
8820
|
+
flex-wrap: nowrap;
|
|
8821
|
+
align-items: center;
|
|
8822
|
+
gap: var(--checked-select-gap);
|
|
8823
|
+
padding: var(--checked-select-padding);
|
|
8824
|
+
/* Room to scroll the last item clear of the caret */
|
|
8825
|
+
padding-inline-end: var(--checked-select-caret-width);
|
|
8826
|
+
min-height: 1.5em;
|
|
8827
|
+
box-sizing: border-box;
|
|
8828
|
+
overflow-x: auto;
|
|
8829
|
+
overflow-y: hidden;
|
|
8830
|
+
scrollbar-width: thin;
|
|
8831
|
+
}
|
|
8832
|
+
:host([multiline]) .rest-items {
|
|
8833
|
+
flex-wrap: wrap;
|
|
8834
|
+
overflow-x: hidden;
|
|
8835
|
+
overflow-y: auto;
|
|
8836
|
+
max-height: var(--checked-select-rest-max-height);
|
|
8837
|
+
/* Wrapping never runs under the caret, so keep its column clear (and the scrollbar beside it) */
|
|
8838
|
+
padding-inline-end: var(--space-m);
|
|
8839
|
+
margin-inline-end: var(--checked-select-caret-width);
|
|
8840
|
+
}
|
|
8841
|
+
|
|
8842
|
+
/* Vertical: one item per row, scrolling vertically beside the caret. Must follow the multiline rule. */
|
|
8843
|
+
:host([orientation="vertical"]) .rest-items {
|
|
8844
|
+
flex-direction: column;
|
|
8845
|
+
flex-wrap: nowrap;
|
|
8846
|
+
align-items: flex-start;
|
|
8847
|
+
overflow-x: hidden;
|
|
8848
|
+
overflow-y: auto;
|
|
8849
|
+
max-height: var(--checked-select-rest-max-height);
|
|
8850
|
+
padding-inline-end: var(--space-m);
|
|
8851
|
+
margin-inline-end: var(--checked-select-caret-width);
|
|
8852
|
+
}
|
|
8853
|
+
|
|
8854
|
+
/* Always-visible caret; items scroll underneath it */
|
|
8855
|
+
.caret {
|
|
8856
|
+
position: absolute;
|
|
8857
|
+
inset-block: 0;
|
|
8858
|
+
inset-inline-end: 0;
|
|
8859
|
+
width: var(--checked-select-caret-width);
|
|
8860
|
+
display: grid;
|
|
8861
|
+
place-items: center;
|
|
8862
|
+
background: inherit;
|
|
8863
|
+
border-radius: inherit;
|
|
8864
|
+
pointer-events: auto;
|
|
8865
|
+
}
|
|
8866
|
+
.caret svg {
|
|
8867
|
+
width: 1.1em;
|
|
8868
|
+
height: 1.1em;
|
|
8869
|
+
fill: none;
|
|
8870
|
+
stroke: currentColor;
|
|
8871
|
+
stroke-width: 2;
|
|
8872
|
+
stroke-linecap: round;
|
|
8873
|
+
stroke-linejoin: round;
|
|
8874
|
+
transition: rotate var(--transition, 0.1s ease-out);
|
|
8875
|
+
}
|
|
8876
|
+
.rest[aria-expanded="true"] .caret svg {
|
|
8877
|
+
rotate: 180deg;
|
|
8878
|
+
}
|
|
8879
|
+
/* While the popup is open its draft is authoritative */
|
|
8880
|
+
.rest[aria-expanded="true"] ixfx-checkbox {
|
|
8881
|
+
pointer-events: none;
|
|
8882
|
+
}
|
|
8883
|
+
.rest:hover {
|
|
8884
|
+
background: var(--surface-5);
|
|
8885
|
+
}
|
|
8886
|
+
.rest:focus-visible {
|
|
8887
|
+
outline: 2px solid var(--accent);
|
|
8888
|
+
outline-offset: 2px;
|
|
8889
|
+
}
|
|
8890
|
+
.rest ixfx-checkbox {
|
|
8891
|
+
flex-shrink: 0;
|
|
8892
|
+
}
|
|
8893
|
+
.rest-text {
|
|
8894
|
+
white-space: nowrap;
|
|
8895
|
+
}
|
|
8896
|
+
:host([multiline]) .rest-text {
|
|
8897
|
+
white-space: normal;
|
|
8898
|
+
}
|
|
8899
|
+
.placeholder {
|
|
8900
|
+
opacity: 0.6;
|
|
8901
|
+
}
|
|
8902
|
+
|
|
8903
|
+
.popup {
|
|
8904
|
+
/* Reset popover UA styles */
|
|
8905
|
+
inset: unset;
|
|
8906
|
+
margin: 0;
|
|
8907
|
+
padding: 0;
|
|
8908
|
+
border: none;
|
|
8909
|
+
background: transparent;
|
|
8910
|
+
overflow: visible;
|
|
8911
|
+
|
|
8912
|
+
/* CSS anchor positioning — position-anchor set inline */
|
|
8913
|
+
position: fixed;
|
|
8914
|
+
left: anchor(left);
|
|
8915
|
+
top: anchor(bottom);
|
|
8916
|
+
/* anchor-size() only works on the anchored element itself, so the width lives here */
|
|
8917
|
+
width: anchor-size(width);
|
|
8918
|
+
min-width: var(--checked-select-min-width);
|
|
8919
|
+
position-try-fallbacks: flip-block;
|
|
8920
|
+
margin-block: 4px;
|
|
8921
|
+
z-index: 100;
|
|
8922
|
+
}
|
|
8923
|
+
|
|
8924
|
+
.frame {
|
|
8925
|
+
position: relative;
|
|
8926
|
+
box-sizing: border-box;
|
|
8927
|
+
display: flex;
|
|
8928
|
+
flex-direction: column;
|
|
8929
|
+
width: 100%;
|
|
8930
|
+
max-height: var(--checked-select-max-height);
|
|
8931
|
+
background: var(--surface-3);
|
|
8932
|
+
color: var(--surface-3-text);
|
|
8933
|
+
border: var(--border);
|
|
8934
|
+
border-radius: var(--radius-m);
|
|
8935
|
+
}
|
|
8936
|
+
|
|
8937
|
+
.listbox {
|
|
8938
|
+
flex: 1;
|
|
8939
|
+
min-height: 0;
|
|
8940
|
+
overflow-y: auto;
|
|
8941
|
+
padding: var(--space-s);
|
|
8942
|
+
outline: none;
|
|
8943
|
+
}
|
|
8944
|
+
.listbox:focus-visible {
|
|
8945
|
+
outline: 2px solid var(--accent);
|
|
8946
|
+
outline-offset: -2px;
|
|
8947
|
+
}
|
|
8948
|
+
|
|
8949
|
+
.item {
|
|
8950
|
+
display: flex;
|
|
8951
|
+
width: 100%;
|
|
8952
|
+
box-sizing: border-box;
|
|
8953
|
+
}
|
|
8954
|
+
|
|
8955
|
+
/* Key-repeat outpaces the fade, smearing the cursor across rows. Snap while
|
|
8956
|
+
the keyboard drives the cursor; hover (mouse mode) keeps the transition. */
|
|
8957
|
+
.listbox[data-input-mode="keyboard"] .item {
|
|
8958
|
+
transition: none;
|
|
8959
|
+
}
|
|
8960
|
+
|
|
8961
|
+
.resize-zone {
|
|
8962
|
+
position: absolute;
|
|
8963
|
+
inset-block-end: 0;
|
|
8964
|
+
inset-inline-end: 0;
|
|
8965
|
+
width: 16px;
|
|
8966
|
+
height: 16px;
|
|
8967
|
+
display: grid;
|
|
8968
|
+
place-items: center;
|
|
8969
|
+
cursor: nwse-resize;
|
|
8970
|
+
touch-action: none;
|
|
8971
|
+
}
|
|
8972
|
+
.resize-zone .ixfx-handle {
|
|
8973
|
+
--ixfx-handle-spot-size: 10px;
|
|
8974
|
+
}
|
|
8975
|
+
`
|
|
8976
|
+
];
|
|
8977
|
+
}
|
|
8978
|
+
#anchorName;
|
|
8979
|
+
#tickled;
|
|
8980
|
+
get isOpen() {
|
|
8981
|
+
return this._open;
|
|
8982
|
+
}
|
|
8983
|
+
get #trigger() {
|
|
8984
|
+
return this.shadowRoot?.querySelector(`.rest`) ?? null;
|
|
8985
|
+
}
|
|
8986
|
+
get #popup() {
|
|
8987
|
+
return this.shadowRoot?.querySelector(`.popup`) ?? null;
|
|
8988
|
+
}
|
|
8989
|
+
get #listbox() {
|
|
8990
|
+
return this.shadowRoot?.querySelector(`.listbox`) ?? null;
|
|
8991
|
+
}
|
|
8992
|
+
/** Draft items in their frozen open-time order. */
|
|
8993
|
+
#rows() {
|
|
8994
|
+
const byValue = new Map(this._draft.map((i) => [i.value, i]));
|
|
8995
|
+
return this._order.flatMap((v) => byValue.get(v) ?? []);
|
|
8996
|
+
}
|
|
8997
|
+
disconnectedCallback() {
|
|
8998
|
+
super.disconnectedCallback();
|
|
8999
|
+
document.removeEventListener(`pointerdown`, this.#onDocumentPointerDown, true);
|
|
9000
|
+
}
|
|
9001
|
+
willUpdate(changed) {
|
|
9002
|
+
super.willUpdate(changed);
|
|
9003
|
+
if (changed.has(`disabled`) && this.disabled && this._open) this.cancel();
|
|
9004
|
+
}
|
|
9005
|
+
async open() {
|
|
9006
|
+
if (this._open || this.disabled) return;
|
|
9007
|
+
this._draft = this.items;
|
|
9008
|
+
this._order = sortForOpen(this.items).map((i) => i.value);
|
|
9009
|
+
this._size = void 0;
|
|
9010
|
+
this._open = true;
|
|
9011
|
+
if (this._order.length > 0) this.#tickled.handleKeyboardNavigation(`first`);
|
|
9012
|
+
document.addEventListener(`pointerdown`, this.#onDocumentPointerDown, true);
|
|
9013
|
+
await this.updateComplete;
|
|
9014
|
+
this.#popup?.showPopover?.();
|
|
9015
|
+
this.#listbox?.focus();
|
|
9016
|
+
}
|
|
9017
|
+
commit() {
|
|
9018
|
+
if (!this._open) return;
|
|
9019
|
+
const changed = diffItems(this.items, this._draft);
|
|
9020
|
+
const next = this._draft;
|
|
9021
|
+
this.#close();
|
|
9022
|
+
if (changed.length === 0) return;
|
|
9023
|
+
this.#emitChange(next, changed);
|
|
9024
|
+
}
|
|
9025
|
+
#emitChange(next, changed) {
|
|
9026
|
+
this.items = next;
|
|
9027
|
+
this.dispatchEvent(new CustomEvent(`change`, {
|
|
9028
|
+
detail: {
|
|
9029
|
+
items: next,
|
|
9030
|
+
changed
|
|
9031
|
+
},
|
|
9032
|
+
bubbles: true,
|
|
9033
|
+
composed: true
|
|
9034
|
+
}));
|
|
9035
|
+
}
|
|
9036
|
+
/** A resting checkbox was used directly: toggle and commit immediately. */
|
|
9037
|
+
#onRestCheckboxChange;
|
|
9038
|
+
async #focusRestCheckbox(value) {
|
|
9039
|
+
await this.updateComplete;
|
|
9040
|
+
(this.shadowRoot?.querySelector(`.rest ixfx-checkbox[data-value="${CSS.escape(value)}"]`) ?? this.#trigger)?.focus();
|
|
9041
|
+
}
|
|
9042
|
+
cancel() {
|
|
9043
|
+
if (!this._open) return;
|
|
9044
|
+
this.#close();
|
|
9045
|
+
}
|
|
9046
|
+
#close() {
|
|
9047
|
+
document.removeEventListener(`pointerdown`, this.#onDocumentPointerDown, true);
|
|
9048
|
+
this.#popup?.hidePopover?.();
|
|
9049
|
+
this._open = false;
|
|
9050
|
+
this._draft = [];
|
|
9051
|
+
this.#tickled.clearTickled();
|
|
9052
|
+
this.#trigger?.focus();
|
|
9053
|
+
}
|
|
9054
|
+
#onDocumentPointerDown;
|
|
9055
|
+
#onTriggerClick;
|
|
9056
|
+
#onTriggerKeyDown;
|
|
9057
|
+
#toggleRow(value) {
|
|
9058
|
+
this._draft = applyToggle(this._draft, value);
|
|
9059
|
+
}
|
|
9060
|
+
#onRowClick(value, index) {
|
|
9061
|
+
this.#tickled.setTickled(index);
|
|
9062
|
+
this.#toggleRow(value);
|
|
9063
|
+
}
|
|
9064
|
+
#onListKeyDown;
|
|
9065
|
+
async #scrollToCursor() {
|
|
9066
|
+
await this.updateComplete;
|
|
9067
|
+
const index = this.#tickled.tickledId;
|
|
9068
|
+
this.shadowRoot?.querySelector(`[data-index="${index}"]`)?.scrollIntoView?.({ block: `nearest` });
|
|
9069
|
+
}
|
|
9070
|
+
#onResizeDown;
|
|
9071
|
+
#renderRest() {
|
|
9072
|
+
const shown = visibleAtRest(this.items);
|
|
9073
|
+
if (shown.length === 0) return html`<span class="placeholder">${this.placeholder}</span>`;
|
|
9074
|
+
if (this.itemDisplay === `text`) {
|
|
9075
|
+
if (this.orientation === `vertical`) return restTextParts(this.items).map((part) => html`<span class="rest-text">${part}</span>`);
|
|
9076
|
+
return html`<span class="rest-text">${restText(this.items)}</span>`;
|
|
9077
|
+
}
|
|
9078
|
+
return repeat(shown, (i) => i.value, (i) => html`
|
|
9079
|
+
<ixfx-checkbox
|
|
9080
|
+
?readonly=${this.itemsInert}
|
|
9081
|
+
data-value=${i.value}
|
|
9082
|
+
label=${i.label}
|
|
9083
|
+
.checked=${i.checked === `true`}
|
|
9084
|
+
.indeterminate=${i.checked === `indeterminate`}
|
|
9085
|
+
@change=${(e) => this.#onRestCheckboxChange(e, i.value)}
|
|
9086
|
+
></ixfx-checkbox>`);
|
|
9087
|
+
}
|
|
9088
|
+
#renderRow(row, index) {
|
|
9089
|
+
return html`
|
|
9090
|
+
<div
|
|
9091
|
+
class="item ${this.#tickled.isTickled(index) ? `tickled` : ``}"
|
|
9092
|
+
role="option"
|
|
9093
|
+
id="opt-${index}"
|
|
9094
|
+
data-index=${index}
|
|
9095
|
+
aria-checked=${row.checked === `indeterminate` ? `mixed` : row.checked}
|
|
9096
|
+
@pointerenter=${() => this.#tickled.handlePointerEnter(index)}
|
|
9097
|
+
@click=${() => this.#onRowClick(row.value, index)}
|
|
9098
|
+
>
|
|
9099
|
+
<ixfx-checkbox
|
|
9100
|
+
readonly
|
|
9101
|
+
label=${row.label}
|
|
9102
|
+
.checked=${row.checked === `true`}
|
|
9103
|
+
.indeterminate=${row.checked === `indeterminate`}
|
|
9104
|
+
></ixfx-checkbox>
|
|
9105
|
+
</div>`;
|
|
9106
|
+
}
|
|
9107
|
+
render() {
|
|
9108
|
+
const rows = this._open ? this.#rows() : [];
|
|
9109
|
+
const cursor = this.#tickled.tickledId;
|
|
9110
|
+
const popupSize = this._size ? `width: ${this._size.width}px;` : ``;
|
|
9111
|
+
const frameStyle = this._size ? `height: ${this._size.height}px; max-height: none;` : ``;
|
|
9112
|
+
const summary = visibleAtRest(this.items).map((i) => i.label).join(`, `);
|
|
9113
|
+
return html`
|
|
9114
|
+
<div
|
|
9115
|
+
class="rest"
|
|
9116
|
+
role="combobox"
|
|
9117
|
+
tabindex=${this.disabled ? `-1` : `0`}
|
|
9118
|
+
aria-haspopup="listbox"
|
|
9119
|
+
aria-expanded=${this._open ? `true` : `false`}
|
|
9120
|
+
aria-disabled=${this.disabled ? `true` : nothing}
|
|
9121
|
+
title=${summary || nothing}
|
|
9122
|
+
style="anchor-name: ${this.#anchorName}"
|
|
9123
|
+
@click=${this.#onTriggerClick}
|
|
9124
|
+
@keydown=${this.#onTriggerKeyDown}
|
|
9125
|
+
>
|
|
9126
|
+
<div class="rest-items">${this.#renderRest()}</div>
|
|
9127
|
+
<div class="caret" aria-hidden="true">
|
|
9128
|
+
<svg viewBox="0 0 24 24"><path d="m6 9 6 6 6-6" /></svg>
|
|
9129
|
+
</div>
|
|
9130
|
+
</div>
|
|
9131
|
+
<div class="popup" popover="manual" style="position-anchor: ${this.#anchorName}; ${popupSize}">
|
|
9132
|
+
<div class="frame" style=${frameStyle}>
|
|
9133
|
+
<div
|
|
9134
|
+
class="listbox"
|
|
9135
|
+
role="listbox"
|
|
9136
|
+
aria-multiselectable="true"
|
|
9137
|
+
tabindex="-1"
|
|
9138
|
+
aria-activedescendant=${cursor === void 0 ? nothing : `opt-${cursor}`}
|
|
9139
|
+
data-input-mode=${this.#tickled.inputMode}
|
|
9140
|
+
@pointerleave=${(e) => this.#tickled.handlePointerLeave(e)}
|
|
9141
|
+
@keydown=${this.#onListKeyDown}
|
|
9142
|
+
>${rows.map((r, i) => this.#renderRow(r, i))}</div>
|
|
9143
|
+
<div class="resize-zone" @pointerdown=${this.#onResizeDown}>
|
|
9144
|
+
<span class="ixfx-handle" data-handle="dotted" data-shape="spot" part="handle"></span>
|
|
9145
|
+
</div>
|
|
9146
|
+
</div>
|
|
9147
|
+
</div>
|
|
9148
|
+
`;
|
|
9149
|
+
}
|
|
9150
|
+
};
|
|
9151
|
+
__decorate([property({ type: Array })], CheckedSelect.prototype, "items", void 0);
|
|
9152
|
+
__decorate([property({
|
|
9153
|
+
type: Boolean,
|
|
9154
|
+
reflect: true
|
|
9155
|
+
})], CheckedSelect.prototype, "multiline", void 0);
|
|
9156
|
+
__decorate([property({
|
|
9157
|
+
type: Boolean,
|
|
9158
|
+
attribute: `items-inert`,
|
|
9159
|
+
reflect: true
|
|
9160
|
+
})], CheckedSelect.prototype, "itemsInert", void 0);
|
|
9161
|
+
__decorate([property({
|
|
9162
|
+
type: String,
|
|
9163
|
+
attribute: `item-display`,
|
|
9164
|
+
reflect: true
|
|
9165
|
+
})], CheckedSelect.prototype, "itemDisplay", void 0);
|
|
9166
|
+
__decorate([property({
|
|
9167
|
+
type: String,
|
|
9168
|
+
reflect: true
|
|
9169
|
+
})], CheckedSelect.prototype, "orientation", void 0);
|
|
9170
|
+
__decorate([property({ type: String })], CheckedSelect.prototype, "placeholder", void 0);
|
|
9171
|
+
__decorate([property({
|
|
9172
|
+
type: Boolean,
|
|
9173
|
+
reflect: true
|
|
9174
|
+
})], CheckedSelect.prototype, "disabled", void 0);
|
|
9175
|
+
__decorate([state()], CheckedSelect.prototype, "_open", void 0);
|
|
9176
|
+
__decorate([state()], CheckedSelect.prototype, "_draft", void 0);
|
|
9177
|
+
__decorate([state()], CheckedSelect.prototype, "_order", void 0);
|
|
9178
|
+
__decorate([state()], CheckedSelect.prototype, "_size", void 0);
|
|
9179
|
+
CheckedSelect = __decorate([safeCustomElement(`ixfx-checked-select`)], CheckedSelect);
|
|
8478
9180
|
const RANGE_LIMITS = {
|
|
8479
9181
|
l: [0, 1],
|
|
8480
9182
|
c: [0, .4],
|
|
@@ -16559,9 +17261,134 @@ __decorate([state()], DataTableElement.prototype, "_detailIndex", void 0);
|
|
|
16559
17261
|
__decorate([state()], DataTableElement.prototype, "_stacked", void 0);
|
|
16560
17262
|
DataTableElement = __decorate([safeCustomElement(`ixfx-data-table`)], DataTableElement);
|
|
16561
17263
|
//#endregion
|
|
17264
|
+
//#region src/list-group-header/list-group-header.ts
|
|
17265
|
+
let ListGroupHeaderElement = class ListGroupHeaderElement extends LitElement {
|
|
17266
|
+
constructor(..._args) {
|
|
17267
|
+
super(..._args);
|
|
17268
|
+
this.label = ``;
|
|
17269
|
+
this.count = 0;
|
|
17270
|
+
this.collapsed = false;
|
|
17271
|
+
this.orientation = `horizontal`;
|
|
17272
|
+
}
|
|
17273
|
+
static {
|
|
17274
|
+
this.styles = css`
|
|
17275
|
+
:host {
|
|
17276
|
+
--_height: var(--list-group-header-height, 28px);
|
|
17277
|
+
--_border: var(--list-group-header-border, var(--border));
|
|
17278
|
+
|
|
17279
|
+
display: flex;
|
|
17280
|
+
align-items: center;
|
|
17281
|
+
gap: var(--space-s);
|
|
17282
|
+
box-sizing: border-box;
|
|
17283
|
+
min-height: var(--_height);
|
|
17284
|
+
padding: 0 var(--space-s);
|
|
17285
|
+
background: transparent;
|
|
17286
|
+
color: var(--list-group-header-text, var(--surface-3-text));
|
|
17287
|
+
border-bottom: 1px solid var(--_border);
|
|
17288
|
+
font-weight: 600;
|
|
17289
|
+
cursor: pointer;
|
|
17290
|
+
user-select: none;
|
|
17291
|
+
}
|
|
17292
|
+
|
|
17293
|
+
.caret {
|
|
17294
|
+
display: inline-flex;
|
|
17295
|
+
transition: transform 0.15s;
|
|
17296
|
+
}
|
|
17297
|
+
|
|
17298
|
+
:host([collapsed]) .caret {
|
|
17299
|
+
transform: rotate(-90deg);
|
|
17300
|
+
}
|
|
17301
|
+
|
|
17302
|
+
.count {
|
|
17303
|
+
opacity: 0.6;
|
|
17304
|
+
font-weight: 400;
|
|
17305
|
+
font-size: 0.9em;
|
|
17306
|
+
}
|
|
17307
|
+
|
|
17308
|
+
.label {
|
|
17309
|
+
overflow: hidden;
|
|
17310
|
+
text-overflow: ellipsis;
|
|
17311
|
+
white-space: nowrap;
|
|
17312
|
+
min-width: 0;
|
|
17313
|
+
}
|
|
17314
|
+
|
|
17315
|
+
.divider {
|
|
17316
|
+
flex: 1;
|
|
17317
|
+
height: 1px;
|
|
17318
|
+
background: var(--_border);
|
|
17319
|
+
margin-left: var(--space-s);
|
|
17320
|
+
}
|
|
17321
|
+
|
|
17322
|
+
/* Single-row (grid-list \`row\` orientation) layout: a narrow column cell. */
|
|
17323
|
+
:host([orientation="vertical"]) {
|
|
17324
|
+
flex-direction: column;
|
|
17325
|
+
justify-content: flex-start;
|
|
17326
|
+
padding: var(--space-s) 0;
|
|
17327
|
+
border-bottom: 0;
|
|
17328
|
+
border-right: 1px solid var(--_border);
|
|
17329
|
+
min-height: 0;
|
|
17330
|
+
}
|
|
17331
|
+
|
|
17332
|
+
:host([orientation="vertical"]) .divider {
|
|
17333
|
+
width: 1px;
|
|
17334
|
+
height: auto;
|
|
17335
|
+
margin-left: 0;
|
|
17336
|
+
margin-top: var(--space-s);
|
|
17337
|
+
}
|
|
17338
|
+
|
|
17339
|
+
@media (prefers-reduced-motion: reduce) {
|
|
17340
|
+
.caret { transition: none; }
|
|
17341
|
+
}
|
|
17342
|
+
`;
|
|
17343
|
+
}
|
|
17344
|
+
connectedCallback() {
|
|
17345
|
+
super.connectedCallback();
|
|
17346
|
+
this.setAttribute(`role`, `button`);
|
|
17347
|
+
}
|
|
17348
|
+
willUpdate() {
|
|
17349
|
+
this.setAttribute(`aria-expanded`, this.collapsed ? `false` : `true`);
|
|
17350
|
+
this.setAttribute(`aria-label`, `${this.label}, ${this.count} ${this.count === 1 ? `item` : `items`}`);
|
|
17351
|
+
}
|
|
17352
|
+
render() {
|
|
17353
|
+
return html`
|
|
17354
|
+
<span class="caret"><ixfx-icon name="chevron-down" size="12"></ixfx-icon></span>
|
|
17355
|
+
<span class="label" part="label">${this.label}</span>
|
|
17356
|
+
<span class="count" part="count">(${this.count})</span>
|
|
17357
|
+
<span class="divider" part="divider"></span>
|
|
17358
|
+
`;
|
|
17359
|
+
}
|
|
17360
|
+
};
|
|
17361
|
+
__decorate([property({
|
|
17362
|
+
type: String,
|
|
17363
|
+
reflect: true
|
|
17364
|
+
})], ListGroupHeaderElement.prototype, "label", void 0);
|
|
17365
|
+
__decorate([property({
|
|
17366
|
+
type: Number,
|
|
17367
|
+
reflect: true
|
|
17368
|
+
})], ListGroupHeaderElement.prototype, "count", void 0);
|
|
17369
|
+
__decorate([property({
|
|
17370
|
+
type: Boolean,
|
|
17371
|
+
reflect: true
|
|
17372
|
+
})], ListGroupHeaderElement.prototype, "collapsed", void 0);
|
|
17373
|
+
__decorate([property({
|
|
17374
|
+
type: String,
|
|
17375
|
+
reflect: true
|
|
17376
|
+
})], ListGroupHeaderElement.prototype, "orientation", void 0);
|
|
17377
|
+
ListGroupHeaderElement = __decorate([safeCustomElement(`ixfx-list-group-header`)], ListGroupHeaderElement);
|
|
17378
|
+
//#endregion
|
|
16562
17379
|
//#region src/detail-list/detail-list.ts
|
|
16563
17380
|
const ITEM_SELECTOR$3 = `:scope > li`;
|
|
17381
|
+
const GROUP_HEADER_ATTR = `data-ixfx-group-header`;
|
|
17382
|
+
const GROUP_COLLAPSED_ATTR = `data-group-collapsed`;
|
|
16564
17383
|
const SEARCH_HIGHLIGHT_KEY = `detail-list-search`;
|
|
17384
|
+
const NAV_KEYS = /* @__PURE__ */ new Set([
|
|
17385
|
+
`ArrowDown`,
|
|
17386
|
+
`ArrowUp`,
|
|
17387
|
+
`ArrowLeft`,
|
|
17388
|
+
`ArrowRight`,
|
|
17389
|
+
`Home`,
|
|
17390
|
+
`End`
|
|
17391
|
+
]);
|
|
16565
17392
|
const LABEL_SPAN_CSS = `overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0`;
|
|
16566
17393
|
/**
|
|
16567
17394
|
* Layout for the per-item badge wrapper `#ensureBadge` adds. Applied as inline
|
|
@@ -16610,14 +17437,26 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16610
17437
|
this.keyProperty = ``;
|
|
16611
17438
|
this.itemWrap = `stop`;
|
|
16612
17439
|
this.itemsDraggable = false;
|
|
17440
|
+
this.groupBy = null;
|
|
17441
|
+
this.crossGroupSelection = false;
|
|
17442
|
+
this._collapsedGroups = /* @__PURE__ */ new Set();
|
|
16613
17443
|
this.#itemData = /* @__PURE__ */ new WeakMap();
|
|
16614
17444
|
this.#grid = [];
|
|
16615
17445
|
this.#positions = /* @__PURE__ */ new Map();
|
|
17446
|
+
this.#tops = /* @__PURE__ */ new Map();
|
|
16616
17447
|
this.#geometryScheduled = false;
|
|
16617
17448
|
this.#sortRaf = 0;
|
|
16618
17449
|
this.#viewportScrollRaf = 0;
|
|
16619
17450
|
this.#animatedItems = /* @__PURE__ */ new WeakSet();
|
|
16620
17451
|
this.#builtFromProvider = [];
|
|
17452
|
+
this.#groupIndex = /* @__PURE__ */ new Map();
|
|
17453
|
+
this.#groupList = [];
|
|
17454
|
+
this.#headerEls = /* @__PURE__ */ new Map();
|
|
17455
|
+
this.#seq = /* @__PURE__ */ new WeakMap();
|
|
17456
|
+
this.#seqNext = 0;
|
|
17457
|
+
this.#wasGrouped = false;
|
|
17458
|
+
this.#groupsScheduled = false;
|
|
17459
|
+
this.#groupByChanged = false;
|
|
16621
17460
|
this.#providerBuilt = 0;
|
|
16622
17461
|
this.#data = new DataController(this);
|
|
16623
17462
|
this.selection = new ListSelectionController({
|
|
@@ -16628,6 +17467,12 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16628
17467
|
interactionMode: this.interactionMode
|
|
16629
17468
|
}),
|
|
16630
17469
|
getKey: keyForElement,
|
|
17470
|
+
getGroupKey: (el) => this.#groupIndex.get(el) ?? null,
|
|
17471
|
+
crossGroupSelection: () => this.crossGroupSelection,
|
|
17472
|
+
getFallbackGroupKey: () => {
|
|
17473
|
+
const t = this.tickled.tickledId;
|
|
17474
|
+
return t ? this.#groupIndex.get(t) ?? null : null;
|
|
17475
|
+
},
|
|
16631
17476
|
onChange: (selected, previous) => {
|
|
16632
17477
|
this._syncItemStates();
|
|
16633
17478
|
this._dispatch(`list-select`, {
|
|
@@ -16709,6 +17554,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16709
17554
|
if (this._listEl) this._listEl.scrollLeft += dx;
|
|
16710
17555
|
},
|
|
16711
17556
|
shouldStart: (event) => {
|
|
17557
|
+
if (this.#findGroupHeader(event.composedPath())) return false;
|
|
16712
17558
|
this.#blankItemPressPending = this.itemsDraggable && this.#isBlankItemPress(event);
|
|
16713
17559
|
return !this.itemsDraggable || this.#blankItemPressPending;
|
|
16714
17560
|
}
|
|
@@ -16732,6 +17578,9 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16732
17578
|
this._onPointerLeave = (e) => {
|
|
16733
17579
|
this.tickled.handlePointerLeave(e);
|
|
16734
17580
|
};
|
|
17581
|
+
this._endKeyboardNav = () => {
|
|
17582
|
+
this.removeAttribute(`data-keyboard-nav`);
|
|
17583
|
+
};
|
|
16735
17584
|
this._onKeyDown = (e) => {
|
|
16736
17585
|
if (this.search.isOpen) return;
|
|
16737
17586
|
if ((e.ctrlKey || e.metaKey) && e.key === `a`) {
|
|
@@ -16741,6 +17590,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16741
17590
|
}
|
|
16742
17591
|
return;
|
|
16743
17592
|
}
|
|
17593
|
+
if (NAV_KEYS.has(e.key)) this.setAttribute(`data-keyboard-nav`, ``);
|
|
16744
17594
|
switch (e.key) {
|
|
16745
17595
|
case `ArrowDown`:
|
|
16746
17596
|
e.preventDefault();
|
|
@@ -16796,7 +17646,13 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16796
17646
|
};
|
|
16797
17647
|
this._onClick = (e) => {
|
|
16798
17648
|
if (this.#dragSelect.isDragging || this.#nativeDrag.isDragging || this.#postDragSuppressTimer !== 0) return;
|
|
16799
|
-
const
|
|
17649
|
+
const path = e.composedPath();
|
|
17650
|
+
const header = this.#findGroupHeader(path);
|
|
17651
|
+
if (header) {
|
|
17652
|
+
this.toggleGroup(header.dataset.groupKey ?? ``);
|
|
17653
|
+
return;
|
|
17654
|
+
}
|
|
17655
|
+
const item = this._findListItem(path);
|
|
16800
17656
|
if (!item) return;
|
|
16801
17657
|
this.focus();
|
|
16802
17658
|
this._dispatch(`list-item-click`, { item });
|
|
@@ -16815,6 +17671,8 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16815
17671
|
#itemData;
|
|
16816
17672
|
#grid;
|
|
16817
17673
|
#positions;
|
|
17674
|
+
/** Viewport-relative `top` per item at the last geometry pass — lines rows up across columns. */
|
|
17675
|
+
#tops;
|
|
16818
17676
|
#geometryScheduled;
|
|
16819
17677
|
#sortRaf;
|
|
16820
17678
|
#viewportScrollRaf;
|
|
@@ -16826,6 +17684,17 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16826
17684
|
/** Items that have already played (or been skipped for) their enter animation. */
|
|
16827
17685
|
#animatedItems;
|
|
16828
17686
|
#builtFromProvider;
|
|
17687
|
+
/** Element → group key, for the selection controller. Empty while ungrouped. */
|
|
17688
|
+
#groupIndex;
|
|
17689
|
+
#groupList;
|
|
17690
|
+
#headerEls;
|
|
17691
|
+
/** Insertion sequence per item, so regrouping starts from the original order. */
|
|
17692
|
+
#seq;
|
|
17693
|
+
#seqNext;
|
|
17694
|
+
#wasGrouped;
|
|
17695
|
+
#groupsScheduled;
|
|
17696
|
+
#groupByChanged;
|
|
17697
|
+
#groupObserver;
|
|
16829
17698
|
#providerBuilt;
|
|
16830
17699
|
#data;
|
|
16831
17700
|
#nativeDragging;
|
|
@@ -16929,6 +17798,8 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16929
17798
|
this.addEventListener(`pointerover`, this._onPointerOver);
|
|
16930
17799
|
this.addEventListener(`pointerleave`, this._onPointerLeave);
|
|
16931
17800
|
this.addEventListener(`keydown`, this._onKeyDown);
|
|
17801
|
+
this.addEventListener(`pointermove`, this._endKeyboardNav);
|
|
17802
|
+
this.addEventListener(`pointerdown`, this._endKeyboardNav);
|
|
16932
17803
|
this.addEventListener(`click`, this._onClick);
|
|
16933
17804
|
this.addEventListener(`dblclick`, this._onDblClick);
|
|
16934
17805
|
this.addEventListener(`wheel`, this._onWheel);
|
|
@@ -16940,6 +17811,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16940
17811
|
this.#processChildren();
|
|
16941
17812
|
this.#animateChildMutations(records);
|
|
16942
17813
|
this.#scheduleGeometry();
|
|
17814
|
+
this.#scheduleGroups();
|
|
16943
17815
|
});
|
|
16944
17816
|
this.#mutationObserver.observe(this, {
|
|
16945
17817
|
childList: true,
|
|
@@ -16949,6 +17821,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16949
17821
|
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) this.#animatedItems.add(el);
|
|
16950
17822
|
this.#searchRegistry ??= createCommandRegistry();
|
|
16951
17823
|
this.#syncSearchCommands();
|
|
17824
|
+
this.#scheduleGroups();
|
|
16952
17825
|
}
|
|
16953
17826
|
/**
|
|
16954
17827
|
* Collapse-in items appended to the light DOM (however they got there). A bulk
|
|
@@ -16975,6 +17848,8 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16975
17848
|
this.removeEventListener(`pointerover`, this._onPointerOver);
|
|
16976
17849
|
this.removeEventListener(`pointerleave`, this._onPointerLeave);
|
|
16977
17850
|
this.removeEventListener(`keydown`, this._onKeyDown);
|
|
17851
|
+
this.removeEventListener(`pointermove`, this._endKeyboardNav);
|
|
17852
|
+
this.removeEventListener(`pointerdown`, this._endKeyboardNav);
|
|
16978
17853
|
this.removeEventListener(`click`, this._onClick);
|
|
16979
17854
|
this.removeEventListener(`dblclick`, this._onDblClick);
|
|
16980
17855
|
this.removeEventListener(`wheel`, this._onWheel);
|
|
@@ -16988,6 +17863,8 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
16988
17863
|
this.#resizeObserver = void 0;
|
|
16989
17864
|
this.#mutationObserver?.disconnect();
|
|
16990
17865
|
this.#mutationObserver = void 0;
|
|
17866
|
+
this.#groupObserver?.disconnect();
|
|
17867
|
+
this.#groupObserver = void 0;
|
|
16991
17868
|
this.#unregisterSearchCommands?.();
|
|
16992
17869
|
this.#unregisterSearchCommands = void 0;
|
|
16993
17870
|
this.#searchRegistry = void 0;
|
|
@@ -17002,8 +17879,22 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17002
17879
|
this.#recomputeGeometry();
|
|
17003
17880
|
this._syncNativeDraggable();
|
|
17004
17881
|
}
|
|
17882
|
+
willUpdate(changed) {
|
|
17883
|
+
super.willUpdate(changed);
|
|
17884
|
+
if (changed.has(`groupBy`) && this.hasUpdated) {
|
|
17885
|
+
if (this._collapsedGroups.size > 0) this._collapsedGroups = /* @__PURE__ */ new Set();
|
|
17886
|
+
this.#groupByChanged = true;
|
|
17887
|
+
}
|
|
17888
|
+
}
|
|
17005
17889
|
updated(changed) {
|
|
17006
17890
|
super.updated(changed);
|
|
17891
|
+
if (changed.has(`groupBy`) || changed.has(`groupLabel`) || changed.has(`_collapsedGroups`)) {
|
|
17892
|
+
this.#scheduleGroups();
|
|
17893
|
+
if (this.#groupByChanged) {
|
|
17894
|
+
this.#groupByChanged = false;
|
|
17895
|
+
this._dispatch(`list-group-change`, { groupBy: this.groupBy });
|
|
17896
|
+
}
|
|
17897
|
+
}
|
|
17007
17898
|
if (changed.has(`searchModes`)) this.#syncSearchCommands();
|
|
17008
17899
|
if (changed.has(`provider`)) {
|
|
17009
17900
|
this.#builtFromProvider = [];
|
|
@@ -17020,6 +17911,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17020
17911
|
if (changed.has(`filterPredicate`)) {
|
|
17021
17912
|
this.#applyFilterPredicate();
|
|
17022
17913
|
this.#scheduleGeometry();
|
|
17914
|
+
this.#scheduleGroups();
|
|
17023
17915
|
}
|
|
17024
17916
|
if (changed.has(`interactionMode`)) {
|
|
17025
17917
|
this.selection.clearAnchor();
|
|
@@ -17059,6 +17951,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17059
17951
|
this.#ensureIcon(el);
|
|
17060
17952
|
if (this.sort !== `none`) this.#applySort();
|
|
17061
17953
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
17954
|
+
this.#scheduleGroups();
|
|
17062
17955
|
}
|
|
17063
17956
|
/**
|
|
17064
17957
|
* Collapse + fade `item` out, then detach it; the rows after it slide up to
|
|
@@ -17068,6 +17961,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17068
17961
|
this.selection.deselect(item);
|
|
17069
17962
|
return animateListItemOut(item).then(() => {
|
|
17070
17963
|
item.remove();
|
|
17964
|
+
this.#scheduleGroups();
|
|
17071
17965
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
17072
17966
|
this.#recomputeGeometry();
|
|
17073
17967
|
});
|
|
@@ -17078,6 +17972,8 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17078
17972
|
}
|
|
17079
17973
|
clearItems() {
|
|
17080
17974
|
this.replaceChildren();
|
|
17975
|
+
this.#headerEls.clear();
|
|
17976
|
+
this.#scheduleGroups();
|
|
17081
17977
|
this.selection.reset();
|
|
17082
17978
|
this.#builtFromProvider = [];
|
|
17083
17979
|
this.#providerBuilt = 0;
|
|
@@ -17095,6 +17991,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17095
17991
|
}
|
|
17096
17992
|
if (this.sort !== `none`) this.#applySort();
|
|
17097
17993
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
17994
|
+
this.#scheduleGroups();
|
|
17098
17995
|
}
|
|
17099
17996
|
/**
|
|
17100
17997
|
* Get the source object behind an `<li>` built from an `items` array or
|
|
@@ -17214,6 +18111,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17214
18111
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
17215
18112
|
this._syncItemStates();
|
|
17216
18113
|
this.#scheduleGeometry();
|
|
18114
|
+
this.#scheduleGroups();
|
|
17217
18115
|
}
|
|
17218
18116
|
#createItemEl(item, index) {
|
|
17219
18117
|
const li = document.createElement(`li`);
|
|
@@ -17265,6 +18163,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17265
18163
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
17266
18164
|
this._syncItemStates();
|
|
17267
18165
|
this.#scheduleGeometry();
|
|
18166
|
+
this.#scheduleGroups();
|
|
17268
18167
|
} else {
|
|
17269
18168
|
this.#providerBuilt = 0;
|
|
17270
18169
|
this.#rebuildFromObjects(items);
|
|
@@ -17283,6 +18182,16 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17283
18182
|
if (this.sort === `none`) return;
|
|
17284
18183
|
const rows = [...this.querySelectorAll(ITEM_SELECTOR$3)];
|
|
17285
18184
|
if (rows.length < 2) return;
|
|
18185
|
+
if (this.#groupingActive()) {
|
|
18186
|
+
this.#scheduleGroups();
|
|
18187
|
+
return;
|
|
18188
|
+
}
|
|
18189
|
+
for (const el of this.#sortRows(rows)) this.appendChild(el);
|
|
18190
|
+
this.#scheduleGeometry();
|
|
18191
|
+
this.#scheduleGroups();
|
|
18192
|
+
}
|
|
18193
|
+
/** Stable sort of `rows` by the current `sort` / `sortBy` / `sortComparator`. */
|
|
18194
|
+
#sortRows(rows) {
|
|
17286
18195
|
const dir = this.sort === `descending` ? -1 : 1;
|
|
17287
18196
|
const meta = (el) => ({
|
|
17288
18197
|
element: el,
|
|
@@ -17312,9 +18221,147 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17312
18221
|
i
|
|
17313
18222
|
}));
|
|
17314
18223
|
indexed.sort((a, b) => cmp(a.el, b.el) * dir || a.i - b.i);
|
|
17315
|
-
|
|
18224
|
+
return indexed.map(({ el }) => el);
|
|
18225
|
+
}
|
|
18226
|
+
#groupingActive() {
|
|
18227
|
+
return this.groupBy !== null && this.groupBy !== void 0 && this.groupBy !== ``;
|
|
18228
|
+
}
|
|
18229
|
+
/** Coalesce regrouping to one pass per microtask (streaming batches, search keystrokes). */
|
|
18230
|
+
#scheduleGroups() {
|
|
18231
|
+
if (this.#groupsScheduled || !this.#groupingActive() && !this.#wasGrouped) return;
|
|
18232
|
+
this.#groupsScheduled = true;
|
|
18233
|
+
queueMicrotask(() => {
|
|
18234
|
+
this.#groupsScheduled = false;
|
|
18235
|
+
this.#syncGroups();
|
|
18236
|
+
});
|
|
18237
|
+
}
|
|
18238
|
+
#groupValue(el, name) {
|
|
18239
|
+
return this.#propertyValue(this.getItemData(el), name) ?? el.getAttribute(`data-${name}`);
|
|
18240
|
+
}
|
|
18241
|
+
#groupKeyOf(el) {
|
|
18242
|
+
const by = this.groupBy;
|
|
18243
|
+
if (typeof by === `string`) return toGroupKey(this.#groupValue(el, by));
|
|
18244
|
+
return toGroupKey(by(this.getItemData(el), el));
|
|
18245
|
+
}
|
|
18246
|
+
#isItemVisible(el) {
|
|
18247
|
+
return !el.hidden && (!this.filterPredicate || this.filterPredicate(el));
|
|
18248
|
+
}
|
|
18249
|
+
#headerFor(key) {
|
|
18250
|
+
let header = this.#headerEls.get(key);
|
|
18251
|
+
if (!header) {
|
|
18252
|
+
header = document.createElement(`ixfx-list-group-header`);
|
|
18253
|
+
header.setAttribute(GROUP_HEADER_ATTR, ``);
|
|
18254
|
+
header.dataset.groupKey = key;
|
|
18255
|
+
this.#headerEls.set(key, header);
|
|
18256
|
+
}
|
|
18257
|
+
return header;
|
|
18258
|
+
}
|
|
18259
|
+
/**
|
|
18260
|
+
* Bring the light DOM into grouped order: original order → sort → partition
|
|
18261
|
+
* into contiguous groups, each preceded by a header row. Idempotent — it only
|
|
18262
|
+
* touches the DOM when the order actually changes, so it never loops through
|
|
18263
|
+
* the mutation observer. Ungrouping restores the original (sorted) order.
|
|
18264
|
+
*/
|
|
18265
|
+
#syncGroups() {
|
|
18266
|
+
const active = this.#groupingActive();
|
|
18267
|
+
if (!active && !this.#wasGrouped) return;
|
|
18268
|
+
const items = [...this.querySelectorAll(ITEM_SELECTOR$3)];
|
|
18269
|
+
for (const el of items) if (!this.#seq.has(el)) this.#seq.set(el, this.#seqNext++);
|
|
18270
|
+
let ordered = items.toSorted((a, b) => this.#seq.get(a) - this.#seq.get(b));
|
|
18271
|
+
if (this.sort !== `none` && ordered.length > 1) ordered = this.#sortRows(ordered);
|
|
18272
|
+
const nodes = [];
|
|
18273
|
+
const info = [];
|
|
18274
|
+
const index = /* @__PURE__ */ new Map();
|
|
18275
|
+
const liveHeaders = /* @__PURE__ */ new Set();
|
|
18276
|
+
if (active) {
|
|
18277
|
+
const label = this.groupLabel;
|
|
18278
|
+
const parts = partitionByGroup(ordered, (el) => this.#groupKeyOf(el), label ? (key, els) => label(key, els.map((el) => this.getItemData(el) ?? el)) : void 0);
|
|
18279
|
+
for (const part of parts) {
|
|
18280
|
+
const count = part.items.filter((el) => this.#isItemVisible(el)).length;
|
|
18281
|
+
const collapsed = count > 0 && this._collapsedGroups.has(part.key);
|
|
18282
|
+
if (count > 0) {
|
|
18283
|
+
const header = this.#headerFor(part.key);
|
|
18284
|
+
header.label = part.label;
|
|
18285
|
+
header.count = count;
|
|
18286
|
+
header.collapsed = collapsed;
|
|
18287
|
+
liveHeaders.add(part.key);
|
|
18288
|
+
nodes.push(header);
|
|
18289
|
+
info.push({
|
|
18290
|
+
key: part.key,
|
|
18291
|
+
label: part.label,
|
|
18292
|
+
count,
|
|
18293
|
+
collapsed: this._collapsedGroups.has(part.key)
|
|
18294
|
+
});
|
|
18295
|
+
}
|
|
18296
|
+
for (const el of part.items) {
|
|
18297
|
+
index.set(el, part.key);
|
|
18298
|
+
el.toggleAttribute(GROUP_COLLAPSED_ATTR, collapsed);
|
|
18299
|
+
nodes.push(el);
|
|
18300
|
+
}
|
|
18301
|
+
}
|
|
18302
|
+
} else {
|
|
18303
|
+
for (const el of ordered) el.removeAttribute(GROUP_COLLAPSED_ATTR);
|
|
18304
|
+
nodes.push(...ordered);
|
|
18305
|
+
}
|
|
18306
|
+
for (const [key, header] of this.#headerEls) if (!liveHeaders.has(key)) {
|
|
18307
|
+
header.remove();
|
|
18308
|
+
this.#headerEls.delete(key);
|
|
18309
|
+
}
|
|
18310
|
+
this.#groupIndex = index;
|
|
18311
|
+
this.#groupList = info;
|
|
18312
|
+
this.#wasGrouped = active;
|
|
18313
|
+
const current = [...this.children].filter((c) => c.matches(`li`) || c.hasAttribute(GROUP_HEADER_ATTR));
|
|
18314
|
+
if (current.length !== nodes.length || current.some((c, i) => c !== nodes[i])) this.append(...nodes);
|
|
18315
|
+
if (this.tickled.tickledId?.hasAttribute(GROUP_COLLAPSED_ATTR)) this.tickled.focus(void 0);
|
|
18316
|
+
this.#syncGroupObserver(active);
|
|
17316
18317
|
this.#scheduleGeometry();
|
|
17317
18318
|
}
|
|
18319
|
+
/** Filter-search hides rows via `hidden` on the `<li>`s — regroup when that changes. */
|
|
18320
|
+
#syncGroupObserver(active) {
|
|
18321
|
+
if (!active) {
|
|
18322
|
+
this.#groupObserver?.disconnect();
|
|
18323
|
+
this.#groupObserver = void 0;
|
|
18324
|
+
return;
|
|
18325
|
+
}
|
|
18326
|
+
if (this.#groupObserver || !this.isConnected) return;
|
|
18327
|
+
this.#groupObserver = new MutationObserver(() => this.#scheduleGroups());
|
|
18328
|
+
this.#groupObserver.observe(this, {
|
|
18329
|
+
subtree: true,
|
|
18330
|
+
attributes: true,
|
|
18331
|
+
attributeFilter: [`hidden`]
|
|
18332
|
+
});
|
|
18333
|
+
}
|
|
18334
|
+
/** Groups in display order (empty when ungrouped). */
|
|
18335
|
+
get groups() {
|
|
18336
|
+
return this.#groupList;
|
|
18337
|
+
}
|
|
18338
|
+
/** Collapse or expand one group. Fires `list-group-toggle` when the state changes. */
|
|
18339
|
+
setGroupCollapsed(groupKey, collapsed) {
|
|
18340
|
+
if (this._collapsedGroups.has(groupKey) === collapsed) return;
|
|
18341
|
+
const next = new Set(this._collapsedGroups);
|
|
18342
|
+
if (collapsed) next.add(groupKey);
|
|
18343
|
+
else next.delete(groupKey);
|
|
18344
|
+
this._collapsedGroups = next;
|
|
18345
|
+
this._dispatch(`list-group-toggle`, {
|
|
18346
|
+
groupKey,
|
|
18347
|
+
collapsed
|
|
18348
|
+
});
|
|
18349
|
+
}
|
|
18350
|
+
toggleGroup(groupKey) {
|
|
18351
|
+
this.setGroupCollapsed(groupKey, !this._collapsedGroups.has(groupKey));
|
|
18352
|
+
}
|
|
18353
|
+
collapseAllGroups() {
|
|
18354
|
+
for (const g of this.#groupList) this.setGroupCollapsed(g.key, true);
|
|
18355
|
+
}
|
|
18356
|
+
expandAllGroups() {
|
|
18357
|
+
for (const key of [...this._collapsedGroups]) this.setGroupCollapsed(key, false);
|
|
18358
|
+
}
|
|
18359
|
+
#findGroupHeader(path) {
|
|
18360
|
+
for (const target of path) {
|
|
18361
|
+
if (target === this) break;
|
|
18362
|
+
if (target instanceof HTMLElement && target.parentElement === this && target.hasAttribute(GROUP_HEADER_ATTR)) return target;
|
|
18363
|
+
}
|
|
18364
|
+
}
|
|
17318
18365
|
#scheduleGeometry() {
|
|
17319
18366
|
if (this.#geometryScheduled) return;
|
|
17320
18367
|
this.#geometryScheduled = true;
|
|
@@ -17326,11 +18373,13 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17326
18373
|
#recomputeGeometry() {
|
|
17327
18374
|
this.#grid = [];
|
|
17328
18375
|
this.#positions.clear();
|
|
18376
|
+
this.#tops.clear();
|
|
17329
18377
|
if (!this.isConnected) return;
|
|
17330
18378
|
const columns = [];
|
|
17331
18379
|
for (const el of this._getVisibleItems()) {
|
|
17332
18380
|
const rect = el.getBoundingClientRect();
|
|
17333
18381
|
if (rect.width === 0 && rect.height === 0) continue;
|
|
18382
|
+
this.#tops.set(el, rect.top);
|
|
17334
18383
|
let ci = columns.findIndex((c) => Math.abs(c.left - rect.left) <= 1);
|
|
17335
18384
|
if (ci === -1) {
|
|
17336
18385
|
columns.push({
|
|
@@ -17451,6 +18500,30 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17451
18500
|
const next = this.#findNextCheckable(cur.col, cur.row, 0, -1);
|
|
17452
18501
|
if (next) this.#navigateTo(next);
|
|
17453
18502
|
}
|
|
18503
|
+
/**
|
|
18504
|
+
* Left/Right target. Ungrouped lists keep the row-index rule. Grouped lists
|
|
18505
|
+
* have headers at arbitrary heights, so an item's row index no longer equals
|
|
18506
|
+
* its visual row — pick the item in the neighbouring column whose top is
|
|
18507
|
+
* nearest the cursor's (ties go to the upper one).
|
|
18508
|
+
*/
|
|
18509
|
+
#horizontalTarget(from, dir) {
|
|
18510
|
+
if (this.#groupList.length === 0) return this.#findNextCheckable(from.col, from.row, dir, 0);
|
|
18511
|
+
const column = this.#grid[from.col + dir];
|
|
18512
|
+
const current = this.tickled.tickledId;
|
|
18513
|
+
const top = current ? this.#tops.get(current) : void 0;
|
|
18514
|
+
if (!column || top === void 0) return void 0;
|
|
18515
|
+
let best;
|
|
18516
|
+
let bestDelta = Number.POSITIVE_INFINITY;
|
|
18517
|
+
for (const el of column) {
|
|
18518
|
+
if (!this._isCheckable(el)) continue;
|
|
18519
|
+
const delta = Math.abs((this.#tops.get(el) ?? Number.POSITIVE_INFINITY) - top);
|
|
18520
|
+
if (delta < bestDelta) {
|
|
18521
|
+
best = el;
|
|
18522
|
+
bestDelta = delta;
|
|
18523
|
+
}
|
|
18524
|
+
}
|
|
18525
|
+
return best;
|
|
18526
|
+
}
|
|
17454
18527
|
#navigateRight() {
|
|
17455
18528
|
this.#recomputeGeometry();
|
|
17456
18529
|
const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
|
|
@@ -17458,7 +18531,7 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17458
18531
|
this.#navigateTo(this.#findFirstCheckable());
|
|
17459
18532
|
return;
|
|
17460
18533
|
}
|
|
17461
|
-
const next = this.#
|
|
18534
|
+
const next = this.#horizontalTarget(cur, 1);
|
|
17462
18535
|
if (next) this.#navigateTo(next);
|
|
17463
18536
|
else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findFirstCheckable());
|
|
17464
18537
|
}
|
|
@@ -17469,13 +18542,14 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17469
18542
|
this.#navigateTo(this.#findFirstCheckable());
|
|
17470
18543
|
return;
|
|
17471
18544
|
}
|
|
17472
|
-
const next = this.#
|
|
18545
|
+
const next = this.#horizontalTarget(cur, -1);
|
|
17473
18546
|
if (next) this.#navigateTo(next);
|
|
17474
18547
|
else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findLastCheckable());
|
|
17475
18548
|
}
|
|
17476
18549
|
_getVisibleItems() {
|
|
17477
18550
|
return Array.from(this.querySelectorAll(ITEM_SELECTOR$3)).filter((el) => {
|
|
17478
18551
|
if (el.hidden) return false;
|
|
18552
|
+
if (el.hasAttribute(GROUP_COLLAPSED_ATTR)) return false;
|
|
17479
18553
|
if (this.filterPredicate && !this.filterPredicate(el)) return false;
|
|
17480
18554
|
return true;
|
|
17481
18555
|
});
|
|
@@ -17764,6 +18838,20 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17764
18838
|
display: none !important;
|
|
17765
18839
|
}
|
|
17766
18840
|
|
|
18841
|
+
/* Rows of a collapsed group leave the flow; their header stays. */
|
|
18842
|
+
::slotted([data-group-collapsed]) {
|
|
18843
|
+
display: none !important;
|
|
18844
|
+
}
|
|
18845
|
+
|
|
18846
|
+
/* Group headers are rows in the column flow, the same size as an item. */
|
|
18847
|
+
::slotted(ixfx-list-group-header) {
|
|
18848
|
+
box-sizing: border-box;
|
|
18849
|
+
flex: none;
|
|
18850
|
+
width: var(--detail-list-column-width, 220px);
|
|
18851
|
+
height: var(--detail-list-item-height, 28px);
|
|
18852
|
+
--list-group-header-height: var(--detail-list-group-header-height, var(--detail-list-item-height, 28px));
|
|
18853
|
+
}
|
|
18854
|
+
|
|
17767
18855
|
::slotted(li) {
|
|
17768
18856
|
box-sizing: border-box;
|
|
17769
18857
|
display: flex;
|
|
@@ -17779,6 +18867,12 @@ let DetailListElement = class DetailListElement extends LitElement {
|
|
|
17779
18867
|
outline: 1px solid transparent;
|
|
17780
18868
|
}
|
|
17781
18869
|
|
|
18870
|
+
/* While navigating by keyboard, key-repeat outpaces the 0.1s fade so the
|
|
18871
|
+
cursor smears across rows. Snap instead. */
|
|
18872
|
+
:host([data-keyboard-nav]) ::slotted(li) {
|
|
18873
|
+
transition: none;
|
|
18874
|
+
}
|
|
18875
|
+
|
|
17782
18876
|
/* In checked mode, only the checkbox column is clickable; the row body
|
|
17783
18877
|
gets a default cursor so it doesn't suggest otherwise. */
|
|
17784
18878
|
:host([interaction-mode="checked"]) ::slotted(li) {
|
|
@@ -17934,6 +19028,14 @@ __decorate([property({
|
|
|
17934
19028
|
})], DetailListElement.prototype, "itemsDraggable", void 0);
|
|
17935
19029
|
__decorate([property({ attribute: false })], DetailListElement.prototype, "dragPreviewRenderer", void 0);
|
|
17936
19030
|
__decorate([property({ attribute: false })], DetailListElement.prototype, "dragPreviewSource", void 0);
|
|
19031
|
+
__decorate([property({ attribute: `group-by` })], DetailListElement.prototype, "groupBy", void 0);
|
|
19032
|
+
__decorate([property({ attribute: false })], DetailListElement.prototype, "groupLabel", void 0);
|
|
19033
|
+
__decorate([property({
|
|
19034
|
+
type: Boolean,
|
|
19035
|
+
attribute: `cross-group-selection`,
|
|
19036
|
+
reflect: true
|
|
19037
|
+
})], DetailListElement.prototype, "crossGroupSelection", void 0);
|
|
19038
|
+
__decorate([state()], DetailListElement.prototype, "_collapsedGroups", void 0);
|
|
17937
19039
|
__decorate([query(`ul`)], DetailListElement.prototype, "_listEl", void 0);
|
|
17938
19040
|
DetailListElement = __decorate([safeCustomElement(`ixfx-detail-list`)], DetailListElement);
|
|
17939
19041
|
//#endregion
|
|
@@ -23573,7 +24675,374 @@ let FormElement = class FormElement extends LitElement {
|
|
|
23573
24675
|
};
|
|
23574
24676
|
FormElement = __decorate([safeCustomElement(`ixfx-form`)], FormElement);
|
|
23575
24677
|
//#endregion
|
|
24678
|
+
//#region src/grid-list/grid-list-layout.ts
|
|
24679
|
+
/**
|
|
24680
|
+
* `itemWidth` is the square's width; `rowHeight` is the full cell height
|
|
24681
|
+
* (square, plus the caption band in `separate` / `under` styles).
|
|
24682
|
+
* `scrollOffset` is `scrollTop` for `grid`/`column` and `scrollLeft` for `row`.
|
|
24683
|
+
*/
|
|
24684
|
+
function computeGridWindow(count, itemWidth, rowHeight, gap, viewportWidth, viewportHeight, scrollOffset, orientation, overscan = 2) {
|
|
24685
|
+
const w = Math.max(1, itemWidth);
|
|
24686
|
+
const h = Math.max(1, rowHeight);
|
|
24687
|
+
const g = Math.max(0, gap);
|
|
24688
|
+
const colStride = w + g;
|
|
24689
|
+
const rowStride = h + g;
|
|
24690
|
+
const offset = Math.max(0, scrollOffset);
|
|
24691
|
+
if (orientation === `row`) {
|
|
24692
|
+
const rows = count === 0 ? 0 : 1;
|
|
24693
|
+
const totalWidth = count === 0 ? 0 : (count - 1) * colStride + w;
|
|
24694
|
+
if (count === 0 || viewportWidth <= 0) return {
|
|
24695
|
+
cols: count,
|
|
24696
|
+
rows,
|
|
24697
|
+
totalHeight: 0,
|
|
24698
|
+
totalWidth,
|
|
24699
|
+
startIndex: 0,
|
|
24700
|
+
endIndex: 0,
|
|
24701
|
+
colStride,
|
|
24702
|
+
rowStride,
|
|
24703
|
+
horizontal: true
|
|
24704
|
+
};
|
|
24705
|
+
const firstVisible = Math.max(0, Math.floor(offset / colStride));
|
|
24706
|
+
const lastVisible = Math.ceil((offset + viewportWidth) / colStride);
|
|
24707
|
+
return {
|
|
24708
|
+
cols: count,
|
|
24709
|
+
rows,
|
|
24710
|
+
totalHeight: 0,
|
|
24711
|
+
totalWidth,
|
|
24712
|
+
startIndex: Math.max(0, firstVisible - overscan),
|
|
24713
|
+
endIndex: Math.min(count, lastVisible + overscan),
|
|
24714
|
+
colStride,
|
|
24715
|
+
rowStride,
|
|
24716
|
+
horizontal: true
|
|
24717
|
+
};
|
|
24718
|
+
}
|
|
24719
|
+
const cols = orientation === `column` ? 1 : Math.max(1, Math.floor((Math.max(0, viewportWidth) + g) / colStride));
|
|
24720
|
+
const rows = count === 0 ? 0 : Math.ceil(count / cols);
|
|
24721
|
+
const totalHeight = rows === 0 ? 0 : (rows - 1) * rowStride + h;
|
|
24722
|
+
if (count === 0 || viewportHeight <= 0) return {
|
|
24723
|
+
cols,
|
|
24724
|
+
rows,
|
|
24725
|
+
totalHeight,
|
|
24726
|
+
totalWidth: 0,
|
|
24727
|
+
startIndex: 0,
|
|
24728
|
+
endIndex: 0,
|
|
24729
|
+
colStride,
|
|
24730
|
+
rowStride,
|
|
24731
|
+
horizontal: false
|
|
24732
|
+
};
|
|
24733
|
+
const firstVisibleRow = Math.max(0, Math.floor(offset / rowStride));
|
|
24734
|
+
const lastVisibleRow = Math.ceil((offset + viewportHeight) / rowStride);
|
|
24735
|
+
const rowStart = Math.max(0, firstVisibleRow - overscan);
|
|
24736
|
+
const rowEnd = Math.min(rows, lastVisibleRow + overscan);
|
|
24737
|
+
return {
|
|
24738
|
+
cols,
|
|
24739
|
+
rows,
|
|
24740
|
+
totalHeight,
|
|
24741
|
+
totalWidth: 0,
|
|
24742
|
+
startIndex: rowStart * cols,
|
|
24743
|
+
endIndex: Math.min(count, rowEnd * cols),
|
|
24744
|
+
colStride,
|
|
24745
|
+
rowStride,
|
|
24746
|
+
horizontal: false
|
|
24747
|
+
};
|
|
24748
|
+
}
|
|
24749
|
+
function buildSections(p, cols, stride, itemExtent) {
|
|
24750
|
+
const g = Math.max(0, p.gap);
|
|
24751
|
+
const horizontal = p.orientation === `row`;
|
|
24752
|
+
const lineCount = (n) => horizontal ? n : Math.ceil(n / cols);
|
|
24753
|
+
const extentFor = (n) => n === 0 ? 0 : (lineCount(n) - 1) * stride + itemExtent;
|
|
24754
|
+
if (!p.groups) {
|
|
24755
|
+
const end = extentFor(p.count);
|
|
24756
|
+
return {
|
|
24757
|
+
sections: [{
|
|
24758
|
+
group: void 0,
|
|
24759
|
+
first: 0,
|
|
24760
|
+
count: p.count,
|
|
24761
|
+
headerOffset: 0,
|
|
24762
|
+
itemsOffset: 0,
|
|
24763
|
+
end
|
|
24764
|
+
}],
|
|
24765
|
+
total: end,
|
|
24766
|
+
count: p.count
|
|
24767
|
+
};
|
|
24768
|
+
}
|
|
24769
|
+
const headerSize = horizontal ? p.headerWidth : p.headerHeight;
|
|
24770
|
+
const sections = [];
|
|
24771
|
+
let cursor = 0;
|
|
24772
|
+
let first = 0;
|
|
24773
|
+
for (const group of p.groups) {
|
|
24774
|
+
const count = group.collapsed ? 0 : group.count;
|
|
24775
|
+
const itemsOffset = cursor + headerSize + g;
|
|
24776
|
+
const end = count === 0 ? cursor + headerSize : itemsOffset + extentFor(count);
|
|
24777
|
+
sections.push({
|
|
24778
|
+
group,
|
|
24779
|
+
first,
|
|
24780
|
+
count,
|
|
24781
|
+
headerOffset: cursor,
|
|
24782
|
+
itemsOffset,
|
|
24783
|
+
end
|
|
24784
|
+
});
|
|
24785
|
+
first += count;
|
|
24786
|
+
cursor = end + g;
|
|
24787
|
+
}
|
|
24788
|
+
return {
|
|
24789
|
+
sections,
|
|
24790
|
+
total: sections.length === 0 ? 0 : sections[sections.length - 1].end,
|
|
24791
|
+
count: first
|
|
24792
|
+
};
|
|
24793
|
+
}
|
|
24794
|
+
/** Index of the last section whose `headerOffset` is <= `offset` (0 when before all). */
|
|
24795
|
+
function sectionAtOffset(sections, offset) {
|
|
24796
|
+
let lo = 0;
|
|
24797
|
+
let hi = sections.length - 1;
|
|
24798
|
+
while (lo < hi) {
|
|
24799
|
+
const mid = lo + hi + 1 >> 1;
|
|
24800
|
+
if (sections[mid].headerOffset <= offset) lo = mid;
|
|
24801
|
+
else hi = mid - 1;
|
|
24802
|
+
}
|
|
24803
|
+
return lo;
|
|
24804
|
+
}
|
|
24805
|
+
/** Index of the section holding effective item `index` (skips empty sections). */
|
|
24806
|
+
function sectionOfIndex(sections, index) {
|
|
24807
|
+
let lo = 0;
|
|
24808
|
+
let hi = sections.length - 1;
|
|
24809
|
+
while (lo < hi) {
|
|
24810
|
+
const mid = lo + hi + 1 >> 1;
|
|
24811
|
+
if (sections[mid].first <= index) lo = mid;
|
|
24812
|
+
else hi = mid - 1;
|
|
24813
|
+
}
|
|
24814
|
+
let s = lo;
|
|
24815
|
+
while (s < sections.length - 1 && sections[s].count === 0) s++;
|
|
24816
|
+
return s;
|
|
24817
|
+
}
|
|
24818
|
+
function buildLayout(p) {
|
|
24819
|
+
const horizontal = p.orientation === `row`;
|
|
24820
|
+
const w = Math.max(1, p.itemWidth);
|
|
24821
|
+
const h = Math.max(1, p.rowHeight);
|
|
24822
|
+
const g = Math.max(0, p.gap);
|
|
24823
|
+
const colStride = w + g;
|
|
24824
|
+
const rowStride = h + g;
|
|
24825
|
+
const cols = horizontal ? 1 : p.orientation === `column` ? 1 : Math.max(1, Math.floor((Math.max(0, p.viewportWidth) + g) / colStride));
|
|
24826
|
+
const stride = horizontal ? colStride : rowStride;
|
|
24827
|
+
const itemExtent = horizontal ? w : h;
|
|
24828
|
+
const { sections, total, count } = buildSections(p, cols, stride, itemExtent);
|
|
24829
|
+
const grouped = p.groups !== void 0;
|
|
24830
|
+
const headerSize = horizontal ? p.headerWidth : p.headerHeight;
|
|
24831
|
+
const viewportMain = horizontal ? p.viewportWidth : p.viewportHeight;
|
|
24832
|
+
const cellHeight = rowStride - g;
|
|
24833
|
+
const rowY = Math.max(0, (p.viewportHeight - cellHeight) / 2);
|
|
24834
|
+
const positionOf = (index) => {
|
|
24835
|
+
const si = sectionOfIndex(sections, index);
|
|
24836
|
+
const s = sections[si];
|
|
24837
|
+
const local = Math.max(0, index - s.first);
|
|
24838
|
+
if (horizontal) return {
|
|
24839
|
+
x: s.itemsOffset + local * colStride,
|
|
24840
|
+
y: rowY,
|
|
24841
|
+
col: 0,
|
|
24842
|
+
line: local,
|
|
24843
|
+
section: si
|
|
24844
|
+
};
|
|
24845
|
+
const col = cols <= 1 ? 0 : local % cols;
|
|
24846
|
+
const line = cols <= 1 ? local : Math.floor(local / cols);
|
|
24847
|
+
return {
|
|
24848
|
+
x: col * colStride,
|
|
24849
|
+
y: s.itemsOffset + line * rowStride,
|
|
24850
|
+
col,
|
|
24851
|
+
line,
|
|
24852
|
+
section: si
|
|
24853
|
+
};
|
|
24854
|
+
};
|
|
24855
|
+
const window = (offset, overscan = 2) => {
|
|
24856
|
+
const off = Math.max(0, offset);
|
|
24857
|
+
const headers = [];
|
|
24858
|
+
const ranges = [];
|
|
24859
|
+
const lo = off - overscan * stride;
|
|
24860
|
+
const hi = off + viewportMain + overscan * stride;
|
|
24861
|
+
let firstSection = 0;
|
|
24862
|
+
if (sections.length > 1) {
|
|
24863
|
+
let a = 0;
|
|
24864
|
+
let b = sections.length - 1;
|
|
24865
|
+
while (a < b) {
|
|
24866
|
+
const mid = a + b >> 1;
|
|
24867
|
+
if (sections[mid].end >= lo) b = mid;
|
|
24868
|
+
else a = mid + 1;
|
|
24869
|
+
}
|
|
24870
|
+
firstSection = a;
|
|
24871
|
+
}
|
|
24872
|
+
if (viewportMain > 0) for (let si = firstSection; si < sections.length; si++) {
|
|
24873
|
+
const s = sections[si];
|
|
24874
|
+
if (s.headerOffset > hi) break;
|
|
24875
|
+
if (s.group && s.headerOffset + headerSize >= lo) headers.push({
|
|
24876
|
+
key: s.group.key,
|
|
24877
|
+
label: s.group.label,
|
|
24878
|
+
count: s.group.count,
|
|
24879
|
+
collapsed: s.group.collapsed,
|
|
24880
|
+
offset: s.headerOffset,
|
|
24881
|
+
size: headerSize
|
|
24882
|
+
});
|
|
24883
|
+
if (s.count === 0) continue;
|
|
24884
|
+
const rel = off - s.itemsOffset;
|
|
24885
|
+
const viewport = rel < 0 ? viewportMain + rel : viewportMain;
|
|
24886
|
+
if (viewport <= 0) continue;
|
|
24887
|
+
const sw = computeGridWindow(s.count, w, h, g, horizontal ? viewport : p.viewportWidth, horizontal ? p.viewportHeight : viewport, Math.max(0, rel), p.orientation, overscan);
|
|
24888
|
+
if (sw.endIndex > sw.startIndex) ranges.push([s.first + sw.startIndex, s.first + sw.endIndex]);
|
|
24889
|
+
}
|
|
24890
|
+
return {
|
|
24891
|
+
cols: horizontal ? count : cols,
|
|
24892
|
+
rows: 0,
|
|
24893
|
+
totalHeight: horizontal ? 0 : total,
|
|
24894
|
+
totalWidth: horizontal ? total : 0,
|
|
24895
|
+
startIndex: ranges.length > 0 ? ranges[0][0] : 0,
|
|
24896
|
+
endIndex: ranges.length > 0 ? ranges[ranges.length - 1][1] : 0,
|
|
24897
|
+
colStride,
|
|
24898
|
+
rowStride,
|
|
24899
|
+
horizontal,
|
|
24900
|
+
headers,
|
|
24901
|
+
ranges
|
|
24902
|
+
};
|
|
24903
|
+
};
|
|
24904
|
+
const extentOf = (index) => {
|
|
24905
|
+
const { line, section } = positionOf(index);
|
|
24906
|
+
const s = sections[section];
|
|
24907
|
+
const start = s.itemsOffset + line * stride;
|
|
24908
|
+
return [line === 0 && s.group ? s.headerOffset : start, start + itemExtent];
|
|
24909
|
+
};
|
|
24910
|
+
const neighbour = (index, dLine) => {
|
|
24911
|
+
if (count === 0) return index;
|
|
24912
|
+
const si = sectionOfIndex(sections, index);
|
|
24913
|
+
const s = sections[si];
|
|
24914
|
+
const local = index - s.first;
|
|
24915
|
+
const col = local % cols;
|
|
24916
|
+
if (dLine > 0) {
|
|
24917
|
+
const t = local + cols;
|
|
24918
|
+
if (t <= s.count - 1) return s.first + t;
|
|
24919
|
+
const lastLine = Math.floor((s.count - 1) / cols);
|
|
24920
|
+
if (Math.floor(local / cols) < lastLine) return s.first + s.count - 1;
|
|
24921
|
+
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);
|
|
24922
|
+
return s.first + s.count - 1;
|
|
24923
|
+
}
|
|
24924
|
+
const t = local - cols;
|
|
24925
|
+
if (t >= 0) return s.first + t;
|
|
24926
|
+
for (let n = si - 1; n >= 0; n--) {
|
|
24927
|
+
const prev = sections[n];
|
|
24928
|
+
if (prev.count > 0) {
|
|
24929
|
+
const line = Math.floor((prev.count - 1) / cols);
|
|
24930
|
+
return prev.first + Math.min(line * cols + col, prev.count - 1);
|
|
24931
|
+
}
|
|
24932
|
+
}
|
|
24933
|
+
return s.first;
|
|
24934
|
+
};
|
|
24935
|
+
const hitTest = (x0, x1, y0, y1) => {
|
|
24936
|
+
const out = [];
|
|
24937
|
+
if (count === 0) return out;
|
|
24938
|
+
const main0 = horizontal ? x0 : y0;
|
|
24939
|
+
const main1 = horizontal ? x1 : y1;
|
|
24940
|
+
const start = sectionAtOffset(sections, main0 - stride);
|
|
24941
|
+
for (let si = Math.max(0, start); si < sections.length; si++) {
|
|
24942
|
+
const s = sections[si];
|
|
24943
|
+
if (s.itemsOffset > main1) break;
|
|
24944
|
+
if (s.count === 0 || s.end < main0) continue;
|
|
24945
|
+
if (horizontal) {
|
|
24946
|
+
const a = Math.max(0, Math.floor((x0 - s.itemsOffset) / colStride));
|
|
24947
|
+
const b = Math.min(s.count - 1, Math.floor((x1 - s.itemsOffset) / colStride));
|
|
24948
|
+
for (let i = a; i <= b; i++) out.push(s.first + i);
|
|
24949
|
+
} else {
|
|
24950
|
+
const colLo = Math.max(0, Math.floor(x0 / colStride));
|
|
24951
|
+
const colHi = Math.min(cols - 1, Math.floor(x1 / colStride));
|
|
24952
|
+
const rowLo = Math.max(0, Math.floor((y0 - s.itemsOffset) / rowStride));
|
|
24953
|
+
const rowHi = Math.floor((y1 - s.itemsOffset) / rowStride);
|
|
24954
|
+
for (let row = rowLo; row <= rowHi; row++) for (let col = colLo; col <= colHi; col++) {
|
|
24955
|
+
const local = row * cols + col;
|
|
24956
|
+
if (local >= 0 && local < s.count) out.push(s.first + local);
|
|
24957
|
+
}
|
|
24958
|
+
}
|
|
24959
|
+
}
|
|
24960
|
+
return out;
|
|
24961
|
+
};
|
|
24962
|
+
const anchorAt = (mainOffset) => {
|
|
24963
|
+
if (count === 0) return {
|
|
24964
|
+
index: 0,
|
|
24965
|
+
lineFrac: 0
|
|
24966
|
+
};
|
|
24967
|
+
let si = sectionAtOffset(sections, mainOffset);
|
|
24968
|
+
while (si < sections.length - 1 && sections[si].count === 0) si++;
|
|
24969
|
+
const s = sections[si];
|
|
24970
|
+
if (s.count === 0) return {
|
|
24971
|
+
index: 0,
|
|
24972
|
+
lineFrac: 0
|
|
24973
|
+
};
|
|
24974
|
+
const centreLine = Math.max(0, mainOffset - s.itemsOffset) / stride;
|
|
24975
|
+
const lineFloor = Math.max(0, Math.floor(centreLine));
|
|
24976
|
+
const local = horizontal ? Math.min(s.count - 1, lineFloor) : Math.min(s.count - 1, lineFloor * cols + Math.max(0, Math.floor((cols - 1) / 2)));
|
|
24977
|
+
return {
|
|
24978
|
+
index: s.first + local,
|
|
24979
|
+
lineFrac: centreLine - lineFloor
|
|
24980
|
+
};
|
|
24981
|
+
};
|
|
24982
|
+
const offsetOfAnchor = (index, lineFrac) => {
|
|
24983
|
+
const { line, section } = positionOf(index);
|
|
24984
|
+
return sections[section].itemsOffset + (line + lineFrac) * stride;
|
|
24985
|
+
};
|
|
24986
|
+
const snapTarget = (offset) => {
|
|
24987
|
+
const si = sectionAtOffset(sections, offset);
|
|
24988
|
+
const s = sections[si];
|
|
24989
|
+
let rel = Math.round((offset - s.itemsOffset) / stride);
|
|
24990
|
+
if (grouped) {
|
|
24991
|
+
const lines = s.count === 0 ? 0 : horizontal ? s.count : Math.ceil(s.count / cols);
|
|
24992
|
+
rel = Math.max(0, Math.min(Math.max(0, lines - 1), rel));
|
|
24993
|
+
}
|
|
24994
|
+
const candidates = [s.itemsOffset + rel * stride];
|
|
24995
|
+
if (grouped) {
|
|
24996
|
+
candidates.push(s.headerOffset);
|
|
24997
|
+
if (si + 1 < sections.length) candidates.push(sections[si + 1].headerOffset);
|
|
24998
|
+
}
|
|
24999
|
+
return candidates.reduce((best, c) => Math.abs(c - offset) < Math.abs(best - offset) ? c : best);
|
|
25000
|
+
};
|
|
25001
|
+
return {
|
|
25002
|
+
horizontal,
|
|
25003
|
+
cols,
|
|
25004
|
+
colStride,
|
|
25005
|
+
rowStride,
|
|
25006
|
+
grouped,
|
|
25007
|
+
count,
|
|
25008
|
+
totalExtent: total,
|
|
25009
|
+
window,
|
|
25010
|
+
positionOf,
|
|
25011
|
+
extentOf,
|
|
25012
|
+
neighbour,
|
|
25013
|
+
hitTest,
|
|
25014
|
+
anchorAt,
|
|
25015
|
+
offsetOfAnchor,
|
|
25016
|
+
snapTarget
|
|
25017
|
+
};
|
|
25018
|
+
}
|
|
25019
|
+
//#endregion
|
|
23576
25020
|
//#region src/grid-list/grid-list-source.ts
|
|
25021
|
+
/** Reorder `effective` raw indices into contiguous groups, dropping collapsed ones. */
|
|
25022
|
+
function applyGrouping(effective, cfg, keyOfRaw) {
|
|
25023
|
+
const parts = partitionByGroup(effective, (raw) => cfg.groupKeyOf(raw), cfg.labelOf ? (key, raws) => cfg.labelOf(key, raws.map((r) => cfg.itemAt(r))) : void 0);
|
|
25024
|
+
const order = [];
|
|
25025
|
+
const groups = [];
|
|
25026
|
+
const groupByKey = /* @__PURE__ */ new Map();
|
|
25027
|
+
for (const part of parts) {
|
|
25028
|
+
const collapsed = cfg.collapsed.has(part.key);
|
|
25029
|
+
groups.push({
|
|
25030
|
+
key: part.key,
|
|
25031
|
+
label: part.label,
|
|
25032
|
+
count: part.items.length,
|
|
25033
|
+
collapsed
|
|
25034
|
+
});
|
|
25035
|
+
for (const raw of part.items) {
|
|
25036
|
+
groupByKey.set(keyOfRaw(raw), part.key);
|
|
25037
|
+
if (!collapsed) order.push(raw);
|
|
25038
|
+
}
|
|
25039
|
+
}
|
|
25040
|
+
return {
|
|
25041
|
+
order,
|
|
25042
|
+
groups,
|
|
25043
|
+
groupByKey
|
|
25044
|
+
};
|
|
25045
|
+
}
|
|
23577
25046
|
const ITEM_SELECTOR$2 = `:scope > *`;
|
|
23578
25047
|
const SYNTHETIC_PREFIX = `gl-auto-`;
|
|
23579
25048
|
/** Whether `key` is an auto-assigned key for a keyless light-DOM item. */
|
|
@@ -23600,6 +25069,9 @@ var ElementGridListSource = class {
|
|
|
23600
25069
|
#searchKeys;
|
|
23601
25070
|
#effectiveIndices = [];
|
|
23602
25071
|
#keysCache;
|
|
25072
|
+
#grouping;
|
|
25073
|
+
#groups = [];
|
|
25074
|
+
#groupByKey = /* @__PURE__ */ new Map();
|
|
23603
25075
|
constructor(host) {
|
|
23604
25076
|
this.#host = host;
|
|
23605
25077
|
this.refresh();
|
|
@@ -23652,6 +25124,16 @@ var ElementGridListSource = class {
|
|
|
23652
25124
|
if (`searchKeys` in opts) this.#searchKeys = opts.searchKeys;
|
|
23653
25125
|
this.#recomputeEffective();
|
|
23654
25126
|
}
|
|
25127
|
+
setGrouping(config) {
|
|
25128
|
+
this.#grouping = config;
|
|
25129
|
+
this.#recomputeEffective();
|
|
25130
|
+
}
|
|
25131
|
+
groups() {
|
|
25132
|
+
return this.#groups;
|
|
25133
|
+
}
|
|
25134
|
+
groupKeyOfKey(key) {
|
|
25135
|
+
return this.#groupByKey.get(key) ?? null;
|
|
25136
|
+
}
|
|
23655
25137
|
refresh() {
|
|
23656
25138
|
this.#items = Array.from(this.#host.querySelectorAll(ITEM_SELECTOR$2)).filter((el) => !el.hidden);
|
|
23657
25139
|
for (const el of this.#items) {
|
|
@@ -23694,9 +25176,21 @@ var ElementGridListSource = class {
|
|
|
23694
25176
|
}
|
|
23695
25177
|
effective.push(i);
|
|
23696
25178
|
}
|
|
23697
|
-
this.#
|
|
25179
|
+
this.#applyGroupingTo(effective);
|
|
23698
25180
|
this.#keysCache = void 0;
|
|
23699
25181
|
}
|
|
25182
|
+
#applyGroupingTo(effective) {
|
|
25183
|
+
if (this.#grouping === void 0) {
|
|
25184
|
+
this.#effectiveIndices = effective;
|
|
25185
|
+
this.#groups = [];
|
|
25186
|
+
this.#groupByKey = /* @__PURE__ */ new Map();
|
|
25187
|
+
return;
|
|
25188
|
+
}
|
|
25189
|
+
const grouped = applyGrouping(effective, this.#grouping, (raw) => this.#itemKey(this.#items[raw]));
|
|
25190
|
+
this.#effectiveIndices = grouped.order;
|
|
25191
|
+
this.#groups = grouped.groups;
|
|
25192
|
+
this.#groupByKey = grouped.groupByKey;
|
|
25193
|
+
}
|
|
23700
25194
|
#itemKey(el) {
|
|
23701
25195
|
const userKey = keyForElement(el);
|
|
23702
25196
|
if (userKey !== void 0) return userKey;
|
|
@@ -23719,6 +25213,9 @@ var DataGridListSource = class {
|
|
|
23719
25213
|
#searchKeys;
|
|
23720
25214
|
#effectiveIndices = [];
|
|
23721
25215
|
#keysCache;
|
|
25216
|
+
#grouping;
|
|
25217
|
+
#groups = [];
|
|
25218
|
+
#groupByKey = /* @__PURE__ */ new Map();
|
|
23722
25219
|
constructor(items, adapter) {
|
|
23723
25220
|
this.#items = items;
|
|
23724
25221
|
this.#adapter = adapter;
|
|
@@ -23784,6 +25281,16 @@ var DataGridListSource = class {
|
|
|
23784
25281
|
if (`searchKeys` in opts) this.#searchKeys = opts.searchKeys;
|
|
23785
25282
|
this.#recomputeEffective();
|
|
23786
25283
|
}
|
|
25284
|
+
setGrouping(config) {
|
|
25285
|
+
this.#grouping = config;
|
|
25286
|
+
this.#recomputeEffective();
|
|
25287
|
+
}
|
|
25288
|
+
groups() {
|
|
25289
|
+
return this.#groups;
|
|
25290
|
+
}
|
|
25291
|
+
groupKeyOfKey(key) {
|
|
25292
|
+
return this.#groupByKey.get(key) ?? null;
|
|
25293
|
+
}
|
|
23787
25294
|
refresh() {
|
|
23788
25295
|
this.#recomputeEffective();
|
|
23789
25296
|
}
|
|
@@ -23808,7 +25315,16 @@ var DataGridListSource = class {
|
|
|
23808
25315
|
if (this.#searchKeys !== void 0 && !this.#searchKeys.has(this.#adapter.key(this.#items[i], i))) continue;
|
|
23809
25316
|
effective.push(i);
|
|
23810
25317
|
}
|
|
23811
|
-
this.#
|
|
25318
|
+
if (this.#grouping === void 0) {
|
|
25319
|
+
this.#effectiveIndices = effective;
|
|
25320
|
+
this.#groups = [];
|
|
25321
|
+
this.#groupByKey = /* @__PURE__ */ new Map();
|
|
25322
|
+
} else {
|
|
25323
|
+
const grouped = applyGrouping(effective, this.#grouping, (raw) => this.#adapter.key(this.#items[raw], raw));
|
|
25324
|
+
this.#effectiveIndices = grouped.order;
|
|
25325
|
+
this.#groups = grouped.groups;
|
|
25326
|
+
this.#groupByKey = grouped.groupByKey;
|
|
25327
|
+
}
|
|
23812
25328
|
this.#keysCache = void 0;
|
|
23813
25329
|
}
|
|
23814
25330
|
};
|
|
@@ -23824,76 +25340,11 @@ const SNAP_DEBOUNCE_MS = 120;
|
|
|
23824
25340
|
* to finish. Kept a touch above the default `--grid-list-item-move-duration`.
|
|
23825
25341
|
*/
|
|
23826
25342
|
const REFLOW_WINDOW_MS = 320;
|
|
23827
|
-
function
|
|
23828
|
-
|
|
23829
|
-
const h = Math.max(1, rowHeight);
|
|
23830
|
-
const g = Math.max(0, gap);
|
|
23831
|
-
const colStride = w + g;
|
|
23832
|
-
const rowStride = h + g;
|
|
23833
|
-
const offset = Math.max(0, scrollOffset);
|
|
23834
|
-
if (orientation === `row`) {
|
|
23835
|
-
const rows = count === 0 ? 0 : 1;
|
|
23836
|
-
const totalWidth = count === 0 ? 0 : (count - 1) * colStride + w;
|
|
23837
|
-
if (count === 0 || viewportWidth <= 0) return {
|
|
23838
|
-
cols: count,
|
|
23839
|
-
rows,
|
|
23840
|
-
totalHeight: 0,
|
|
23841
|
-
totalWidth,
|
|
23842
|
-
startIndex: 0,
|
|
23843
|
-
endIndex: 0,
|
|
23844
|
-
colStride,
|
|
23845
|
-
rowStride,
|
|
23846
|
-
horizontal: true
|
|
23847
|
-
};
|
|
23848
|
-
const firstVisible = Math.max(0, Math.floor(offset / colStride));
|
|
23849
|
-
const lastVisible = Math.ceil((offset + viewportWidth) / colStride);
|
|
23850
|
-
return {
|
|
23851
|
-
cols: count,
|
|
23852
|
-
rows,
|
|
23853
|
-
totalHeight: 0,
|
|
23854
|
-
totalWidth,
|
|
23855
|
-
startIndex: Math.max(0, firstVisible - overscan),
|
|
23856
|
-
endIndex: Math.min(count, lastVisible + overscan),
|
|
23857
|
-
colStride,
|
|
23858
|
-
rowStride,
|
|
23859
|
-
horizontal: true
|
|
23860
|
-
};
|
|
23861
|
-
}
|
|
23862
|
-
const cols = orientation === `column` ? 1 : Math.max(1, Math.floor((Math.max(0, viewportWidth) + g) / colStride));
|
|
23863
|
-
const rows = count === 0 ? 0 : Math.ceil(count / cols);
|
|
23864
|
-
const totalHeight = rows === 0 ? 0 : (rows - 1) * rowStride + h;
|
|
23865
|
-
if (count === 0 || viewportHeight <= 0) return {
|
|
23866
|
-
cols,
|
|
23867
|
-
rows,
|
|
23868
|
-
totalHeight,
|
|
23869
|
-
totalWidth: 0,
|
|
23870
|
-
startIndex: 0,
|
|
23871
|
-
endIndex: 0,
|
|
23872
|
-
colStride,
|
|
23873
|
-
rowStride,
|
|
23874
|
-
horizontal: false
|
|
23875
|
-
};
|
|
23876
|
-
const firstVisibleRow = Math.max(0, Math.floor(offset / rowStride));
|
|
23877
|
-
const lastVisibleRow = Math.ceil((offset + viewportHeight) / rowStride);
|
|
23878
|
-
const rowStart = Math.max(0, firstVisibleRow - overscan);
|
|
23879
|
-
const rowEnd = Math.min(rows, lastVisibleRow + overscan);
|
|
23880
|
-
return {
|
|
23881
|
-
cols,
|
|
23882
|
-
rows,
|
|
23883
|
-
totalHeight,
|
|
23884
|
-
totalWidth: 0,
|
|
23885
|
-
startIndex: rowStart * cols,
|
|
23886
|
-
endIndex: Math.min(count, rowEnd * cols),
|
|
23887
|
-
colStride,
|
|
23888
|
-
rowStride,
|
|
23889
|
-
horizontal: false
|
|
23890
|
-
};
|
|
25343
|
+
function readProperty(item, name) {
|
|
25344
|
+
return typeof item === `object` && item !== null ? item[name] : void 0;
|
|
23891
25345
|
}
|
|
23892
|
-
function
|
|
23893
|
-
return
|
|
23894
|
-
}
|
|
23895
|
-
function rowOf(index, cols) {
|
|
23896
|
-
return cols <= 1 ? index : Math.floor(index / cols);
|
|
25346
|
+
function readAttribute(el, name) {
|
|
25347
|
+
return el.getAttribute(`data-${name}`);
|
|
23897
25348
|
}
|
|
23898
25349
|
/** Shallow key-array equality, treating `undefined` (never dispatched) as different. */
|
|
23899
25350
|
function sameKeys$1(a, b) {
|
|
@@ -23921,6 +25372,11 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
23921
25372
|
this.marqueeFromCellBlank = false;
|
|
23922
25373
|
this.snapToItem = false;
|
|
23923
25374
|
this.items = [];
|
|
25375
|
+
this.groupBy = null;
|
|
25376
|
+
this.crossGroupSelection = false;
|
|
25377
|
+
this.groupHeaderHeight = 28;
|
|
25378
|
+
this.groupHeaderWidth = 120;
|
|
25379
|
+
this._collapsedGroups = /* @__PURE__ */ new Set();
|
|
23924
25380
|
this._scrollTop = 0;
|
|
23925
25381
|
this._scrollLeft = 0;
|
|
23926
25382
|
this._viewportHeight = 0;
|
|
@@ -23945,6 +25401,12 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
23945
25401
|
interactionMode: this.interactionMode
|
|
23946
25402
|
}),
|
|
23947
25403
|
getKey: (k) => k,
|
|
25404
|
+
getGroupKey: (k) => this.groupBy === null ? null : this.#source.groupKeyOfKey(k),
|
|
25405
|
+
crossGroupSelection: () => this.crossGroupSelection,
|
|
25406
|
+
getFallbackGroupKey: () => {
|
|
25407
|
+
const k = this.tickled.tickledId;
|
|
25408
|
+
return k !== void 0 && this.groupBy !== null ? this.#source.groupKeyOfKey(k) : null;
|
|
25409
|
+
},
|
|
23948
25410
|
onChange: (selected, previous) => {
|
|
23949
25411
|
this._syncItemStates();
|
|
23950
25412
|
this.requestUpdate();
|
|
@@ -24042,10 +25504,11 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24042
25504
|
},
|
|
24043
25505
|
shouldStart: (event) => {
|
|
24044
25506
|
if (this.marqueeFromCellBlank && this.#isCellBlankPress(event)) return true;
|
|
24045
|
-
for (const node of event.composedPath()) if (node instanceof Element && node.classList.contains(`cell`)) return false;
|
|
25507
|
+
for (const node of event.composedPath()) if (node instanceof Element && (node.classList.contains(`cell`) || node.classList.contains(`group-header`))) return false;
|
|
24046
25508
|
return true;
|
|
24047
25509
|
}
|
|
24048
25510
|
});
|
|
25511
|
+
this.#groupByChanged = false;
|
|
24049
25512
|
this.#selectedSnapshot = /* @__PURE__ */ new Set();
|
|
24050
25513
|
this.#onScroll = (e) => {
|
|
24051
25514
|
const el = e.target;
|
|
@@ -24095,15 +25558,25 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24095
25558
|
const count = this.#source.count;
|
|
24096
25559
|
if (count === 0) return;
|
|
24097
25560
|
const win = this.#currentWindow();
|
|
24098
|
-
const
|
|
24099
|
-
|
|
24100
|
-
|
|
25561
|
+
const layout = this.#layout();
|
|
25562
|
+
const current = () => this.tickled.tickledId ? this.#source.indexOfKey(this.tickled.tickledId) : -1;
|
|
25563
|
+
const goto = (nextIndex) => {
|
|
24101
25564
|
const nextKey = this.#source.keyAt(nextIndex);
|
|
24102
25565
|
if (nextKey) {
|
|
24103
25566
|
this.tickled.handleKeyboardNavigation(nextKey);
|
|
24104
25567
|
this.#scrollItemIntoView(nextKey);
|
|
24105
25568
|
}
|
|
24106
25569
|
};
|
|
25570
|
+
const move = (delta) => goto(Math.max(0, Math.min(count - 1, current() + delta)));
|
|
25571
|
+
const moveLines = (dir, lines = 1) => {
|
|
25572
|
+
let index = current();
|
|
25573
|
+
if (index < 0) {
|
|
25574
|
+
move(dir * lines * win.cols);
|
|
25575
|
+
return;
|
|
25576
|
+
}
|
|
25577
|
+
for (let i = 0; i < lines; i++) index = layout.neighbour(index, dir);
|
|
25578
|
+
goto(index);
|
|
25579
|
+
};
|
|
24107
25580
|
const perLine = this.orientation === `grid` ? win.cols : 1;
|
|
24108
25581
|
switch (e.key) {
|
|
24109
25582
|
case `ArrowLeft`:
|
|
@@ -24113,10 +25586,12 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24113
25586
|
move(1);
|
|
24114
25587
|
break;
|
|
24115
25588
|
case `ArrowUp`:
|
|
24116
|
-
|
|
25589
|
+
if (this.orientation === `grid`) moveLines(-1);
|
|
25590
|
+
else move(-perLine);
|
|
24117
25591
|
break;
|
|
24118
25592
|
case `ArrowDown`:
|
|
24119
|
-
|
|
25593
|
+
if (this.orientation === `grid`) moveLines(1);
|
|
25594
|
+
else move(perLine);
|
|
24120
25595
|
break;
|
|
24121
25596
|
case `Home`:
|
|
24122
25597
|
this.tickled.handleKeyboardNavigation(`first`);
|
|
@@ -24131,7 +25606,8 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24131
25606
|
const mainExtent = win.horizontal ? this._viewportWidth : this._viewportHeight;
|
|
24132
25607
|
const mainStride = win.horizontal ? win.colStride : win.rowStride;
|
|
24133
25608
|
const visibleLines = Math.max(1, Math.floor(mainExtent / mainStride));
|
|
24134
|
-
|
|
25609
|
+
if (this.orientation === `grid`) moveLines(e.key === `PageDown` ? 1 : -1, visibleLines);
|
|
25610
|
+
else move((e.key === `PageDown` ? 1 : -1) * visibleLines * perLine);
|
|
24135
25611
|
break;
|
|
24136
25612
|
}
|
|
24137
25613
|
case `Enter`: {
|
|
@@ -24212,6 +25688,11 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24212
25688
|
position: relative;
|
|
24213
25689
|
width: 100%;
|
|
24214
25690
|
}
|
|
25691
|
+
.group-header {
|
|
25692
|
+
position: absolute;
|
|
25693
|
+
inset: 0 auto auto 0;
|
|
25694
|
+
z-index: 1;
|
|
25695
|
+
}
|
|
24215
25696
|
.cell {
|
|
24216
25697
|
position: absolute;
|
|
24217
25698
|
inset: 0 auto auto 0;
|
|
@@ -24505,6 +25986,9 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24505
25986
|
specificity. Survivors slide to their new slot; a freshly inserted cell
|
|
24506
25987
|
also fades in. (A removed item's element is already gone, so there's
|
|
24507
25988
|
nothing left to fade — the gap simply closes as the others slide.) */
|
|
25989
|
+
:host([reflowing]) .group-header {
|
|
25990
|
+
transition: transform var(--grid-list-item-move-duration, 200ms) cubic-bezier(0.2, 0, 0, 1);
|
|
25991
|
+
}
|
|
24508
25992
|
:host([reflowing]) .cell {
|
|
24509
25993
|
transition:
|
|
24510
25994
|
transform var(--grid-list-item-move-duration, 200ms) cubic-bezier(0.2, 0, 0, 1),
|
|
@@ -24515,6 +25999,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24515
25999
|
animation: ixfx-list-item-in var(--grid-list-item-move-duration, 200ms) ease both;
|
|
24516
26000
|
}
|
|
24517
26001
|
@media (prefers-reduced-motion: reduce) {
|
|
26002
|
+
:host([reflowing]) .group-header { transition: none; }
|
|
24518
26003
|
:host([reflowing]) .cell { transition: background var(--transition), border-color var(--transition); }
|
|
24519
26004
|
.cell.entering { animation: none; }
|
|
24520
26005
|
}
|
|
@@ -24656,31 +26141,13 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24656
26141
|
* so virtualized off-screen items are covered without DOM measurement.
|
|
24657
26142
|
*/
|
|
24658
26143
|
#marqueeHitTest(rect) {
|
|
24659
|
-
|
|
24660
|
-
if (count === 0) return [];
|
|
26144
|
+
if (this.#source.count === 0) return [];
|
|
24661
26145
|
const horizontal = this.orientation === `row`;
|
|
24662
|
-
const
|
|
24663
|
-
const
|
|
24664
|
-
|
|
24665
|
-
|
|
24666
|
-
|
|
24667
|
-
const lo = Math.max(0, Math.floor(x0 / win.colStride));
|
|
24668
|
-
const hi = Math.min(count - 1, Math.floor(x1 / win.colStride));
|
|
24669
|
-
for (let i = lo; i <= hi; i++) out.push(i);
|
|
24670
|
-
} else {
|
|
24671
|
-
const x0 = rect.x + this._scrollLeft;
|
|
24672
|
-
const x1 = x0 + rect.width;
|
|
24673
|
-
const y0 = rect.y + this._scrollTop;
|
|
24674
|
-
const y1 = y0 + rect.height;
|
|
24675
|
-
const colLo = Math.max(0, Math.floor(x0 / win.colStride));
|
|
24676
|
-
const colHi = Math.min(win.cols - 1, Math.floor(x1 / win.colStride));
|
|
24677
|
-
const rowLo = Math.max(0, Math.floor(y0 / win.rowStride));
|
|
24678
|
-
const rowHi = Math.floor(y1 / win.rowStride);
|
|
24679
|
-
for (let row = rowLo; row <= rowHi; row++) for (let col = colLo; col <= colHi; col++) {
|
|
24680
|
-
const index = row * win.cols + col;
|
|
24681
|
-
if (index >= 0 && index < count) out.push(index);
|
|
24682
|
-
}
|
|
24683
|
-
}
|
|
26146
|
+
const x0 = rect.x + this._scrollLeft;
|
|
26147
|
+
const x1 = x0 + rect.width;
|
|
26148
|
+
const y0 = horizontal ? 0 : rect.y + this._scrollTop;
|
|
26149
|
+
const y1 = horizontal ? 0 : y0 + rect.height;
|
|
26150
|
+
const out = this.#layout().hitTest(x0, x1, y0, y1);
|
|
24684
26151
|
const keys = [];
|
|
24685
26152
|
for (const index of out) {
|
|
24686
26153
|
const key = this.#source.keyAt(index);
|
|
@@ -24830,6 +26297,10 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24830
26297
|
if (previousLength === 0 && removedCount === 0) return;
|
|
24831
26298
|
if (added.length > 0 && added.length === this.#items.length) return;
|
|
24832
26299
|
for (const el of added) this.#entering.add(el);
|
|
26300
|
+
this.#openReflowWindow();
|
|
26301
|
+
}
|
|
26302
|
+
/** Open (or extend) the `[reflowing]` window so moved cells and headers slide. */
|
|
26303
|
+
#openReflowWindow() {
|
|
24833
26304
|
this.toggleAttribute(`reflowing`, true);
|
|
24834
26305
|
if (this.#reflowTimer) clearTimeout(this.#reflowTimer);
|
|
24835
26306
|
this.#reflowTimer = window.setTimeout(() => {
|
|
@@ -24886,6 +26357,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24886
26357
|
this.#sourceElement = new ElementGridListSource(this);
|
|
24887
26358
|
this.#source = this.#sourceElement;
|
|
24888
26359
|
}
|
|
26360
|
+
this.#source.setGrouping(this.#groupingConfig());
|
|
24889
26361
|
this.#applyFilters();
|
|
24890
26362
|
if (this.#searchFilterKeys) this.#source.setFilters({ searchKeys: this.#searchFilterKeys });
|
|
24891
26363
|
}
|
|
@@ -24927,6 +26399,17 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24927
26399
|
}
|
|
24928
26400
|
this.search?.refresh();
|
|
24929
26401
|
}
|
|
26402
|
+
const groupsChanged = changed.has(`groupBy`) || changed.has(`groupLabel`) || changed.has(`_collapsedGroups`);
|
|
26403
|
+
if (groupsChanged && this.hasUpdated) {
|
|
26404
|
+
if (changed.has(`groupBy`)) {
|
|
26405
|
+
if (this._collapsedGroups.size > 0) this._collapsedGroups = /* @__PURE__ */ new Set();
|
|
26406
|
+
this.#groupByChanged = true;
|
|
26407
|
+
}
|
|
26408
|
+
this.#applyGroupingToSource();
|
|
26409
|
+
this.#dropUnreachableCursor();
|
|
26410
|
+
this.#openReflowWindow();
|
|
26411
|
+
this.updateComplete.then(() => this.#emitViewportChange());
|
|
26412
|
+
} else if (groupsChanged) this.#applyGroupingToSource();
|
|
24930
26413
|
if (changed.has(`itemSize`) || changed.has(`gap`)) {
|
|
24931
26414
|
this.itemSize = Math.max(MIN_ITEM_SIZE, Math.round(this.itemSize));
|
|
24932
26415
|
this.gap = Math.max(0, Math.round(this.gap));
|
|
@@ -24947,6 +26430,62 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24947
26430
|
}
|
|
24948
26431
|
}
|
|
24949
26432
|
}
|
|
26433
|
+
#groupByChanged;
|
|
26434
|
+
/** Grouping config for the active source, or `undefined` when ungrouped. */
|
|
26435
|
+
#groupingConfig() {
|
|
26436
|
+
const by = this.groupBy;
|
|
26437
|
+
if (by === null || by === void 0 || by === ``) return void 0;
|
|
26438
|
+
const collapsed = this._collapsedGroups;
|
|
26439
|
+
const src = this.#source;
|
|
26440
|
+
if (src instanceof DataGridListSource) return {
|
|
26441
|
+
groupKeyOf: (raw) => resolveGroupKey(by, src.rawItemAt(raw), raw, readProperty),
|
|
26442
|
+
itemAt: (raw) => src.rawItemAt(raw),
|
|
26443
|
+
labelOf: this.groupLabel,
|
|
26444
|
+
collapsed
|
|
26445
|
+
};
|
|
26446
|
+
const elements = src;
|
|
26447
|
+
return {
|
|
26448
|
+
groupKeyOf: (raw) => resolveGroupKey(by, elements.rawElementAt(raw), raw, readAttribute),
|
|
26449
|
+
itemAt: (raw) => elements.rawElementAt(raw),
|
|
26450
|
+
labelOf: this.groupLabel,
|
|
26451
|
+
collapsed
|
|
26452
|
+
};
|
|
26453
|
+
}
|
|
26454
|
+
/** Push the current grouping into the source and refresh the element-mode item cache. */
|
|
26455
|
+
#applyGroupingToSource() {
|
|
26456
|
+
this.#source.setGrouping(this.#groupingConfig());
|
|
26457
|
+
if (this.#source instanceof ElementGridListSource) this.#items = this.#source.effectiveElements();
|
|
26458
|
+
}
|
|
26459
|
+
/** The cursor can't stay on an item that a collapsed group just removed. */
|
|
26460
|
+
#dropUnreachableCursor() {
|
|
26461
|
+
const id = this.tickled.tickledId;
|
|
26462
|
+
if (id !== void 0 && this.#source.indexOfKey(id) < 0) this.tickled.focus(void 0);
|
|
26463
|
+
}
|
|
26464
|
+
/** Groups in display order (empty when ungrouped). */
|
|
26465
|
+
get groups() {
|
|
26466
|
+
return this.groupBy === null ? [] : this.#source.groups();
|
|
26467
|
+
}
|
|
26468
|
+
/** Collapse or expand one group. Fires `list-group-toggle` when the state changes. */
|
|
26469
|
+
setGroupCollapsed(groupKey, collapsed) {
|
|
26470
|
+
if (this._collapsedGroups.has(groupKey) === collapsed) return;
|
|
26471
|
+
const next = new Set(this._collapsedGroups);
|
|
26472
|
+
if (collapsed) next.add(groupKey);
|
|
26473
|
+
else next.delete(groupKey);
|
|
26474
|
+
this._collapsedGroups = next;
|
|
26475
|
+
this._dispatch(`list-group-toggle`, {
|
|
26476
|
+
groupKey,
|
|
26477
|
+
collapsed
|
|
26478
|
+
});
|
|
26479
|
+
}
|
|
26480
|
+
toggleGroup(groupKey) {
|
|
26481
|
+
this.setGroupCollapsed(groupKey, !this._collapsedGroups.has(groupKey));
|
|
26482
|
+
}
|
|
26483
|
+
collapseAllGroups() {
|
|
26484
|
+
for (const g of this.groups) this.setGroupCollapsed(g.key, true);
|
|
26485
|
+
}
|
|
26486
|
+
expandAllGroups() {
|
|
26487
|
+
for (const key of [...this._collapsedGroups]) this.setGroupCollapsed(key, false);
|
|
26488
|
+
}
|
|
24950
26489
|
#syncAriaMultiselectable() {
|
|
24951
26490
|
this.setAttribute(`aria-multiselectable`, this.selection.canSelectMultiple() ? `true` : `false`);
|
|
24952
26491
|
}
|
|
@@ -24986,6 +26525,10 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24986
26525
|
}
|
|
24987
26526
|
this.#applyZoomAnchor();
|
|
24988
26527
|
this.#activateTickledPan();
|
|
26528
|
+
if (this.#groupByChanged) {
|
|
26529
|
+
this.#groupByChanged = false;
|
|
26530
|
+
this._dispatch(`list-group-change`, { groupBy: this.groupBy });
|
|
26531
|
+
}
|
|
24989
26532
|
}
|
|
24990
26533
|
/** Re-anchor the scroll so the item captured in `willUpdate` stays centred. */
|
|
24991
26534
|
#applyZoomAnchor() {
|
|
@@ -24994,41 +26537,33 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
24994
26537
|
this.#zoomAnchor = void 0;
|
|
24995
26538
|
const vp = this.#viewportRef.value;
|
|
24996
26539
|
if (!vp) return;
|
|
24997
|
-
const
|
|
24998
|
-
|
|
24999
|
-
|
|
25000
|
-
|
|
25001
|
-
|
|
25002
|
-
|
|
26540
|
+
const layout = this.#layout();
|
|
26541
|
+
const horizontal = layout.horizontal;
|
|
26542
|
+
const extent = horizontal ? this._viewportWidth : this._viewportHeight;
|
|
26543
|
+
if (extent <= 0) return;
|
|
26544
|
+
const targetCenter = layout.offsetOfAnchor(index, lineFrac);
|
|
26545
|
+
const maxScroll = Math.max(0, layout.totalExtent - extent);
|
|
26546
|
+
const next = Math.min(maxScroll, Math.max(0, targetCenter - extent / 2));
|
|
26547
|
+
if (horizontal) {
|
|
25003
26548
|
if (Math.abs(vp.scrollLeft - next) > .5) {
|
|
25004
26549
|
vp.scrollLeft = next;
|
|
25005
26550
|
this._scrollLeft = next;
|
|
25006
26551
|
}
|
|
25007
|
-
|
|
25008
|
-
}
|
|
25009
|
-
if (this._viewportHeight <= 0) return;
|
|
25010
|
-
const targetCenter = ((win.cols > 0 ? Math.floor(index / win.cols) : 0) + lineFrac) * win.rowStride;
|
|
25011
|
-
const maxScroll = Math.max(0, win.totalHeight - this._viewportHeight);
|
|
25012
|
-
const next = Math.min(maxScroll, Math.max(0, targetCenter - this._viewportHeight / 2));
|
|
25013
|
-
if (Math.abs(vp.scrollTop - next) > .5) {
|
|
26552
|
+
} else if (Math.abs(vp.scrollTop - next) > .5) {
|
|
25014
26553
|
vp.scrollTop = next;
|
|
25015
26554
|
this._scrollTop = next;
|
|
25016
26555
|
}
|
|
25017
26556
|
}
|
|
25018
26557
|
#captureCentreAnchor(prevOffset, prevExtent, prevItemSize, prevGap, prevCaptionStyle, prevCaptionHeight, prevOrientation) {
|
|
25019
|
-
|
|
25020
|
-
if (count === 0) return;
|
|
26558
|
+
if ((this.#source?.count ?? 0) === 0) return;
|
|
25021
26559
|
const prevRowHeight = prevItemSize + (prevCaptionStyle === `separate` || prevCaptionStyle === `under` ? prevCaptionHeight : 0);
|
|
25022
|
-
const
|
|
25023
|
-
|
|
25024
|
-
|
|
25025
|
-
|
|
25026
|
-
|
|
25027
|
-
|
|
25028
|
-
this.#zoomAnchor =
|
|
25029
|
-
index,
|
|
25030
|
-
lineFrac: centerLine - lineFloor
|
|
25031
|
-
};
|
|
26560
|
+
const prev = this.#buildLayout({
|
|
26561
|
+
itemWidth: prevItemSize,
|
|
26562
|
+
rowHeight: prevRowHeight,
|
|
26563
|
+
gap: prevGap,
|
|
26564
|
+
orientation: prevOrientation
|
|
26565
|
+
});
|
|
26566
|
+
this.#zoomAnchor = prev.anchorAt(prevOffset + prevExtent / 2);
|
|
25032
26567
|
}
|
|
25033
26568
|
/**
|
|
25034
26569
|
* Re-read the viewport after an ancestor's visibility/layout changed.
|
|
@@ -25242,37 +26777,75 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25242
26777
|
return this.#windowKeys(this.#currentWindow(true));
|
|
25243
26778
|
}
|
|
25244
26779
|
#windowKeys(win) {
|
|
25245
|
-
|
|
26780
|
+
const keys = this.#source.keys();
|
|
26781
|
+
const out = [];
|
|
26782
|
+
for (const [start, end] of win.ranges) for (let i = start; i < end; i++) if (!isSyntheticKey(keys[i])) out.push(keys[i]);
|
|
26783
|
+
return out;
|
|
26784
|
+
}
|
|
26785
|
+
/** Layout params for the current geometry, with optional overrides (used for zoom anchoring). */
|
|
26786
|
+
#layoutParams(over = {}) {
|
|
26787
|
+
return {
|
|
26788
|
+
count: this.#source.count,
|
|
26789
|
+
groups: this.groupBy === null ? void 0 : this.#source.groups(),
|
|
26790
|
+
itemWidth: this.itemSize,
|
|
26791
|
+
rowHeight: this.#rowHeight(),
|
|
26792
|
+
gap: this.gap,
|
|
26793
|
+
viewportWidth: this._viewportWidth,
|
|
26794
|
+
viewportHeight: this._viewportHeight,
|
|
26795
|
+
orientation: this.orientation,
|
|
26796
|
+
headerHeight: this.groupHeaderHeight,
|
|
26797
|
+
headerWidth: this.groupHeaderWidth,
|
|
26798
|
+
...over
|
|
26799
|
+
};
|
|
26800
|
+
}
|
|
26801
|
+
#buildLayout(over = {}) {
|
|
26802
|
+
return buildLayout(this.#layoutParams(over));
|
|
26803
|
+
}
|
|
26804
|
+
#layoutCache;
|
|
26805
|
+
/** The current layout, rebuilt only when its inputs change (never per scroll). */
|
|
26806
|
+
#layout() {
|
|
26807
|
+
const params = this.#layoutParams();
|
|
26808
|
+
const cached = this.#layoutCache;
|
|
26809
|
+
if (cached) {
|
|
26810
|
+
const a = cached.params;
|
|
26811
|
+
let same = a.groups === params.groups;
|
|
26812
|
+
for (const k of Object.keys(params)) {
|
|
26813
|
+
if (!same) break;
|
|
26814
|
+
same = a[k] === params[k];
|
|
26815
|
+
}
|
|
26816
|
+
if (same) return cached.layout;
|
|
26817
|
+
}
|
|
26818
|
+
const layout = buildLayout(params);
|
|
26819
|
+
this.#layoutCache = {
|
|
26820
|
+
params,
|
|
26821
|
+
layout
|
|
26822
|
+
};
|
|
26823
|
+
return layout;
|
|
25246
26824
|
}
|
|
25247
26825
|
#currentWindow(noOverscan = false) {
|
|
25248
|
-
|
|
25249
|
-
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);
|
|
26826
|
+
return this.#layout().window(this.orientation === `row` ? this._scrollLeft : this._scrollTop, noOverscan ? 0 : 2);
|
|
25250
26827
|
}
|
|
25251
26828
|
/** Adjust the scroll offset so `key` is fully within the viewport. */
|
|
25252
26829
|
#scrollItemIntoView(key) {
|
|
25253
26830
|
const index = this.#source.indexOfKey(key);
|
|
25254
26831
|
const vp = this.#viewportRef.value;
|
|
25255
26832
|
if (index < 0 || !vp) return;
|
|
25256
|
-
const
|
|
25257
|
-
if (
|
|
25258
|
-
const left = index * win.colStride;
|
|
25259
|
-
const right = left + this.itemSize;
|
|
26833
|
+
const [start, end] = this.#layout().extentOf(index);
|
|
26834
|
+
if (this.orientation === `row`) {
|
|
25260
26835
|
const viewLeft = vp.scrollLeft;
|
|
25261
26836
|
let next = viewLeft;
|
|
25262
|
-
if (
|
|
25263
|
-
else if (
|
|
26837
|
+
if (start < viewLeft) next = start;
|
|
26838
|
+
else if (end > viewLeft + this._viewportWidth) next = end - this._viewportWidth;
|
|
25264
26839
|
if (next !== viewLeft) {
|
|
25265
26840
|
vp.scrollLeft = next;
|
|
25266
26841
|
this._scrollLeft = next;
|
|
25267
26842
|
}
|
|
25268
26843
|
return;
|
|
25269
26844
|
}
|
|
25270
|
-
const top = (win.cols > 0 ? Math.floor(index / win.cols) : 0) * win.rowStride;
|
|
25271
|
-
const bottom = top + this.#rowHeight();
|
|
25272
26845
|
const viewTop = vp.scrollTop;
|
|
25273
26846
|
let next = viewTop;
|
|
25274
|
-
if (
|
|
25275
|
-
else if (
|
|
26847
|
+
if (start < viewTop) next = start;
|
|
26848
|
+
else if (end > viewTop + this._viewportHeight) next = end - this._viewportHeight;
|
|
25276
26849
|
if (next !== viewTop) {
|
|
25277
26850
|
vp.scrollTop = next;
|
|
25278
26851
|
this._scrollTop = next;
|
|
@@ -25346,6 +26919,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25346
26919
|
>
|
|
25347
26920
|
<div class="spacer" style=${spacerStyle}>
|
|
25348
26921
|
${this.#source instanceof DataGridListSource ? this.#renderDataCells(win) : this.#renderElementCells(win)}
|
|
26922
|
+
${this.#renderHeaders(win)}
|
|
25349
26923
|
</div>
|
|
25350
26924
|
${this.#marqueeRect ? html`<div class="marquee" part="marquee" style=${this.#marqueeStyle(this.#marqueeRect)}></div>` : ``}
|
|
25351
26925
|
</div>
|
|
@@ -25353,7 +26927,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25353
26927
|
`;
|
|
25354
26928
|
}
|
|
25355
26929
|
#renderDataCells(win) {
|
|
25356
|
-
const slice = Array.from({ length:
|
|
26930
|
+
const slice = win.ranges.flatMap(([start, end]) => Array.from({ length: end - start }, (_, i) => start + i));
|
|
25357
26931
|
return repeat(slice, (index) => this.#source.keyAt(index), (index) => this.#renderDataCell(index, win));
|
|
25358
26932
|
}
|
|
25359
26933
|
#renderDataCell(index, win) {
|
|
@@ -25368,8 +26942,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25368
26942
|
const caption = style === `hidden` ? `` : this.#source.labelAt(index);
|
|
25369
26943
|
const showCaption = caption !== ``;
|
|
25370
26944
|
const cellHeight = win.rowStride - this.gap;
|
|
25371
|
-
const x
|
|
25372
|
-
const y = win.horizontal ? Math.max(0, (this._viewportHeight - cellHeight) / 2) : rowOf(index, win.cols) * win.rowStride;
|
|
26945
|
+
const { x, y } = this.#layout().positionOf(index);
|
|
25373
26946
|
const captionCss = separate ? `height:${Math.max(0, this.captionHeight - 4)}px` : style === `under` ? `height:${this.captionHeight}px` : ``;
|
|
25374
26947
|
const rawIcon = this.#source.iconAt(index);
|
|
25375
26948
|
const icon = appearanceAbsent(rawIcon) ? void 0 : rawIcon;
|
|
@@ -25455,8 +27028,25 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25455
27028
|
current.startLoop?.();
|
|
25456
27029
|
}
|
|
25457
27030
|
#renderElementCells(win) {
|
|
25458
|
-
const slice =
|
|
25459
|
-
return repeat(slice, (
|
|
27031
|
+
const slice = win.ranges.flatMap(([start, end]) => Array.from({ length: end - start }, (_, i) => start + i));
|
|
27032
|
+
return repeat(slice, (index) => this.#source.keyAt(index), (index) => this.#renderCell(this.#items[index], index, win));
|
|
27033
|
+
}
|
|
27034
|
+
#renderHeaders(win) {
|
|
27035
|
+
if (win.headers.length === 0) return nothing;
|
|
27036
|
+
const horizontal = win.horizontal;
|
|
27037
|
+
return repeat(win.headers, (h) => `__group__${h.key}`, (h) => html`
|
|
27038
|
+
<ixfx-list-group-header
|
|
27039
|
+
class="group-header"
|
|
27040
|
+
part="group-header"
|
|
27041
|
+
data-group-key=${h.key}
|
|
27042
|
+
label=${h.label}
|
|
27043
|
+
count=${h.count}
|
|
27044
|
+
orientation=${horizontal ? `vertical` : `horizontal`}
|
|
27045
|
+
?collapsed=${h.collapsed}
|
|
27046
|
+
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)`}
|
|
27047
|
+
@click=${() => this.toggleGroup(h.key)}
|
|
27048
|
+
></ixfx-list-group-header>
|
|
27049
|
+
`);
|
|
25460
27050
|
}
|
|
25461
27051
|
#renderCell(el, index, win) {
|
|
25462
27052
|
const key = this.#source.keyAt(index);
|
|
@@ -25471,8 +27061,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25471
27061
|
const caption = style === `hidden` ? `` : this.#captionFor(el);
|
|
25472
27062
|
const showCaption = caption !== ``;
|
|
25473
27063
|
const cellHeight = win.rowStride - this.gap;
|
|
25474
|
-
const x
|
|
25475
|
-
const y = win.horizontal ? Math.max(0, (this._viewportHeight - cellHeight) / 2) : rowOf(index, win.cols) * win.rowStride;
|
|
27064
|
+
const { x, y } = this.#layout().positionOf(index);
|
|
25476
27065
|
const thumbStyle = separate ? `height:${this.itemSize}px` : ``;
|
|
25477
27066
|
const captionCss = separate ? `height:${Math.max(0, this.captionHeight - 4)}px` : style === `under` ? `height:${this.captionHeight}px` : ``;
|
|
25478
27067
|
return html`
|
|
@@ -25571,16 +27160,12 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25571
27160
|
#snapToNearestLine() {
|
|
25572
27161
|
const vp = this.#viewportRef.value;
|
|
25573
27162
|
if (!vp) return;
|
|
25574
|
-
const
|
|
25575
|
-
const horizontal =
|
|
25576
|
-
const stride = horizontal ? win.colStride : win.rowStride;
|
|
25577
|
-
if (stride <= 0) return;
|
|
27163
|
+
const layout = this.#layout();
|
|
27164
|
+
const horizontal = layout.horizontal;
|
|
25578
27165
|
const offset = horizontal ? vp.scrollLeft : vp.scrollTop;
|
|
25579
|
-
const extent = horizontal ? win.totalWidth : win.totalHeight;
|
|
25580
27166
|
const viewportExtent = horizontal ? this._viewportWidth : this._viewportHeight;
|
|
25581
|
-
const maxScroll = Math.max(0,
|
|
25582
|
-
const
|
|
25583
|
-
const target = Math.min(maxScroll, Math.max(0, index * stride));
|
|
27167
|
+
const maxScroll = Math.max(0, layout.totalExtent - viewportExtent);
|
|
27168
|
+
const target = Math.min(maxScroll, Math.max(0, layout.snapTarget(offset)));
|
|
25584
27169
|
if (Math.abs(target - offset) < .5) return;
|
|
25585
27170
|
vp.scrollTo(horizontal ? {
|
|
25586
27171
|
left: target,
|
|
@@ -25608,7 +27193,7 @@ let GridListElement = class GridListElement extends LitElement {
|
|
|
25608
27193
|
if (mode === `manual`) return;
|
|
25609
27194
|
if (this.selectionMode === `none` && mode !== `checked` && mode !== `sticky`) return;
|
|
25610
27195
|
if (this.#dragSelect.isDragging || this.#nativeDragging || this.#nativeDrag.isDragging || this.#postDragSuppressTimer !== 0) return;
|
|
25611
|
-
for (const node of e.composedPath()) if (node instanceof Element && node.classList.contains(`cell`)) return;
|
|
27196
|
+
for (const node of e.composedPath()) if (node instanceof Element && (node.classList.contains(`cell`) || node.classList.contains(`group-header`))) return;
|
|
25612
27197
|
this.selection.clear();
|
|
25613
27198
|
}
|
|
25614
27199
|
#startPostDragSuppressTimer() {
|
|
@@ -25709,6 +27294,22 @@ __decorate([property({
|
|
|
25709
27294
|
})], GridListElement.prototype, "snapToItem", void 0);
|
|
25710
27295
|
__decorate([property({ attribute: false })], GridListElement.prototype, "items", void 0);
|
|
25711
27296
|
__decorate([property({ attribute: false })], GridListElement.prototype, "itemAdapter", void 0);
|
|
27297
|
+
__decorate([property({ attribute: `group-by` })], GridListElement.prototype, "groupBy", void 0);
|
|
27298
|
+
__decorate([property({ attribute: false })], GridListElement.prototype, "groupLabel", void 0);
|
|
27299
|
+
__decorate([property({
|
|
27300
|
+
type: Boolean,
|
|
27301
|
+
attribute: `cross-group-selection`,
|
|
27302
|
+
reflect: true
|
|
27303
|
+
})], GridListElement.prototype, "crossGroupSelection", void 0);
|
|
27304
|
+
__decorate([property({
|
|
27305
|
+
type: Number,
|
|
27306
|
+
attribute: `group-header-height`
|
|
27307
|
+
})], GridListElement.prototype, "groupHeaderHeight", void 0);
|
|
27308
|
+
__decorate([property({
|
|
27309
|
+
type: Number,
|
|
27310
|
+
attribute: `group-header-width`
|
|
27311
|
+
})], GridListElement.prototype, "groupHeaderWidth", void 0);
|
|
27312
|
+
__decorate([state()], GridListElement.prototype, "_collapsedGroups", void 0);
|
|
25712
27313
|
__decorate([state()], GridListElement.prototype, "_scrollTop", void 0);
|
|
25713
27314
|
__decorate([state()], GridListElement.prototype, "_scrollLeft", void 0);
|
|
25714
27315
|
__decorate([state()], GridListElement.prototype, "_viewportHeight", void 0);
|
|
@@ -53313,6 +54914,6 @@ function init() {
|
|
|
53313
54914
|
console.log(`Init`);
|
|
53314
54915
|
}
|
|
53315
54916
|
//#endregion
|
|
53316
|
-
export { AcTextElement, AcTokenElement, BottomDrawerElement, ButtonElement, Checkbox, 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 };
|
|
54917
|
+
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 };
|
|
53317
54918
|
|
|
53318
54919
|
//# sourceMappingURL=index.js.map
|