@ixfx/components 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/index.d.ts +727 -256
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +2046 -35
- package/bundle/index.js.map +1 -1
- package/dist/ac-text.d.ts +1 -1
- package/dist/ac-text.js +1 -1
- package/dist/{ac-token-TCLAZeVg.d.ts → ac-token--_vCHXWw.d.ts} +2 -2
- package/dist/{ac-token-TCLAZeVg.d.ts.map → ac-token--_vCHXWw.d.ts.map} +1 -1
- package/dist/ac-token.d.ts +1 -1
- package/dist/ac-token.js +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/button.d.ts.map +1 -1
- package/dist/colour-picker.d.ts +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/crumbs.js +3 -3
- package/dist/{data-grid-Db4QpnII.d.ts → data-grid-BddUjAti.d.ts} +34 -2
- package/dist/data-grid-BddUjAti.d.ts.map +1 -0
- package/dist/data-grid.d.ts +1 -1
- package/dist/data-grid.js +180 -7
- package/dist/data-grid.js.map +1 -1
- package/dist/detail-list-Br0WAnry.d.ts +167 -0
- package/dist/detail-list-Br0WAnry.d.ts.map +1 -0
- package/dist/detail-list.d.ts +3 -0
- package/dist/detail-list.js +1115 -0
- package/dist/detail-list.js.map +1 -0
- package/dist/grid-list-B_orPsot.d.ts +304 -0
- package/dist/grid-list-B_orPsot.d.ts.map +1 -0
- package/dist/grid-list.d.ts +3 -0
- package/dist/grid-list.js +933 -0
- package/dist/grid-list.js.map +1 -0
- package/dist/{grouped-item-lister-BWlC9S4e.d.ts → grouped-item-lister-fdZHVxFM.d.ts} +3 -3
- package/dist/{grouped-item-lister-BWlC9S4e.d.ts.map → grouped-item-lister-fdZHVxFM.d.ts.map} +1 -1
- package/dist/grouped-item-lister.d.ts +1 -1
- package/dist/{icon-DR_3rSyZ.d.ts → icon-CDGprSdE.d.ts} +2 -2
- package/dist/icon-CDGprSdE.d.ts.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/{index-C59-JEPl.d.ts → index-9AhaHIT2.d.ts} +3 -3
- package/dist/{index-C59-JEPl.d.ts.map → index-9AhaHIT2.d.ts.map} +1 -1
- package/dist/{index-DsjpDksO.d.ts → index-CS9yM8L-.d.ts} +2 -2
- package/dist/{index-DsjpDksO.d.ts.map → index-CS9yM8L-.d.ts.map} +1 -1
- package/dist/{index-BUB5SICW.d.ts → index-CoqtMSd9.d.ts} +2 -2
- package/dist/{index-BUB5SICW.d.ts.map → index-CoqtMSd9.d.ts.map} +1 -1
- package/dist/{index-CJ5fshD3.d.ts → index-UvnoZUAo.d.ts} +2 -2
- package/dist/{index-CJ5fshD3.d.ts.map → index-UvnoZUAo.d.ts.map} +1 -1
- package/dist/{index-BOUaDIW-.d.ts → index-tq6f8007.d.ts} +2 -2
- package/dist/{index-BOUaDIW-.d.ts.map → index-tq6f8007.d.ts.map} +1 -1
- package/dist/index.d.ts +185 -240
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +375 -1002
- package/dist/index.js.map +1 -1
- package/dist/interaction-Cr13azbM.js +3 -0
- package/dist/{labelled-input-base-Dp_9KP0G.d.ts → labelled-input-base-DiTme1JO.d.ts} +2 -2
- package/dist/{labelled-input-base-Dp_9KP0G.d.ts.map → labelled-input-base-DiTme1JO.d.ts.map} +1 -1
- package/dist/labelled-radial-input.d.ts +2 -2
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-slider-input.d.ts +1 -1
- package/dist/list-item-animation-DuiwgN3l.js +112 -0
- package/dist/list-item-animation-DuiwgN3l.js.map +1 -0
- package/dist/list-selection-controller-CUo-Hhcm.js +338 -0
- package/dist/list-selection-controller-CUo-Hhcm.js.map +1 -0
- package/dist/{list-selection-types-DSuRNWpx.d.ts → list-selection-types-B5ioL-dG.d.ts} +6 -1
- package/dist/list-selection-types-B5ioL-dG.d.ts.map +1 -0
- package/dist/{menu-DMzjkVnK.js → menu-Bg90Qr0B.js} +3 -3
- package/dist/{menu-DMzjkVnK.js.map → menu-Bg90Qr0B.js.map} +1 -1
- package/dist/{menu-item-D2S4i8u1.js → menu-item-Bx-Fs7jq.js} +3 -3
- package/dist/{menu-item-D2S4i8u1.js.map → menu-item-Bx-Fs7jq.js.map} +1 -1
- package/dist/{menu-item-ZmkXNjek.d.ts → menu-item-CZi9vy5O.d.ts} +2 -2
- package/dist/{menu-item-ZmkXNjek.d.ts.map → menu-item-CZi9vy5O.d.ts.map} +1 -1
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +2 -2
- package/dist/miller.d.ts +1 -1
- package/dist/miller.js +2 -2
- package/dist/narrowed-text.js +2 -2
- package/dist/panel.d.ts +1 -1
- package/dist/plots.js +1 -1
- package/dist/select-horiz.js +2 -2
- package/dist/{split-layout-C5SYbDrq.d.ts → split-layout-JK3xWVX2.d.ts} +2 -2
- package/dist/{split-layout-C5SYbDrq.d.ts.map → split-layout-JK3xWVX2.d.ts.map} +1 -1
- package/dist/split-layout.d.ts +1 -1
- package/dist/{tab-list-DbMIL520.d.ts → tab-list-CGyPt07Z.d.ts} +2 -2
- package/dist/{tab-list-DbMIL520.d.ts.map → tab-list-CGyPt07Z.d.ts.map} +1 -1
- package/dist/tabs.d.ts +1 -1
- package/dist/{tickled-controller-Ds1DMyG7.d.ts → tickled-controller-Bn3YdQsM.d.ts} +12 -1
- package/dist/{tickled-controller-Ds1DMyG7.d.ts.map → tickled-controller-Bn3YdQsM.d.ts.map} +1 -1
- package/dist/{tickled-controller-h9GmJ_bU.js → tickled-controller-C66DgJHl.js} +16 -1
- package/dist/tickled-controller-C66DgJHl.js.map +1 -0
- package/dist/{titlebar-B23MrIbI.d.ts → titlebar-Bj5pfdw_.d.ts} +2 -2
- package/dist/{titlebar-B23MrIbI.d.ts.map → titlebar-Bj5pfdw_.d.ts.map} +1 -1
- package/dist/titlebar.d.ts +1 -1
- package/dist/{tree-Ch4W8qXQ.js → tree-B74zdI60.js} +116 -17
- package/dist/tree-B74zdI60.js.map +1 -0
- package/dist/tree.d.ts +6 -3
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +1 -1
- package/dist/vertical-list-CQSEVeOT.d.ts +140 -0
- package/dist/vertical-list-CQSEVeOT.d.ts.map +1 -0
- package/dist/vertical-list.d.ts +3 -121
- package/dist/vertical-list.js +710 -1
- package/dist/vertical-list.js.map +1 -0
- package/dist/{xy-axis-DWizs0bt.js → xy-axis-VrX8dmvS.js} +2 -2
- package/dist/{xy-axis-DWizs0bt.js.map → xy-axis-VrX8dmvS.js.map} +1 -1
- package/docs-user/README.md +1 -0
- package/docs-user/data-grid.md +51 -2
- package/docs-user/detail-list.md +38 -0
- package/docs-user/grid-list.md +274 -0
- package/docs-user/index.json +7 -2
- package/docs-user/llms.txt +1 -0
- package/docs-user/tree.md +13 -0
- package/docs-user/vertical-list.md +51 -1
- package/llms.txt +1 -0
- package/package.json +17 -2
- package/dist/data-grid-Db4QpnII.d.ts.map +0 -1
- package/dist/icon-DR_3rSyZ.d.ts.map +0 -1
- package/dist/interaction-D5XdKzbR.js +0 -2
- package/dist/list-selection-types-DSuRNWpx.d.ts.map +0 -1
- package/dist/tickled-controller-h9GmJ_bU.js.map +0 -1
- package/dist/tree-Ch4W8qXQ.js.map +0 -1
- package/dist/vertical-list-B4w_ksJk.js +0 -814
- package/dist/vertical-list-B4w_ksJk.js.map +0 -1
- package/dist/vertical-list.d.ts.map +0 -1
package/bundle/index.js
CHANGED
|
@@ -8,8 +8,9 @@ import { createCommandRegistry } from "@clinth/ui-commands";
|
|
|
8
8
|
import { Trees } from "@ixfx/collections";
|
|
9
9
|
import { clamp, clamper, rangeStream, round, scaler } from "@ixfx/numbers";
|
|
10
10
|
import { repeat } from "lit/directives/repeat.js";
|
|
11
|
-
import {
|
|
11
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
12
12
|
import { classMap } from "lit/directives/class-map.js";
|
|
13
|
+
import { Easings } from "@ixfx/modulation";
|
|
13
14
|
import "@clinth/vcanvas";
|
|
14
15
|
import { keyed } from "lit/directives/keyed.js";
|
|
15
16
|
//#region node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/css-tag.js
|
|
@@ -752,6 +753,21 @@ var TickledController = class TickledController {
|
|
|
752
753
|
return this._tickledId;
|
|
753
754
|
}
|
|
754
755
|
/**
|
|
756
|
+
* The item currently under the cursor, or `undefined`. Alias of `tickledId`
|
|
757
|
+
* for parity with other controllers.
|
|
758
|
+
*/
|
|
759
|
+
get current() {
|
|
760
|
+
return this._tickledId;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Move the cursor programmatically (e.g. to reveal a match or re-seat it
|
|
764
|
+
* after the item set changed). Does not change the input mode. Alias of
|
|
765
|
+
* `setTickled`.
|
|
766
|
+
*/
|
|
767
|
+
focus(id) {
|
|
768
|
+
this.setTickled(id);
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
755
771
|
* Check if a specific item is tickled
|
|
756
772
|
*/
|
|
757
773
|
isTickled(id) {
|
|
@@ -3861,6 +3877,188 @@ __decorate([state()], RadioGroup.prototype, "_icons", void 0);
|
|
|
3861
3877
|
__decorate([query(`slot`)], RadioGroup.prototype, "_slot", void 0);
|
|
3862
3878
|
RadioGroup = __decorate([safeCustomElement(`ixfx-radio-group`)], RadioGroup);
|
|
3863
3879
|
//#endregion
|
|
3880
|
+
//#region src/interaction/keyed-list.ts
|
|
3881
|
+
/**
|
|
3882
|
+
* Helpers for key-based selection on the Element-flavoured list components
|
|
3883
|
+
* (`ixfx-vertical-list`, `ixfx-detail-list`, `ixfx-grid-list`).
|
|
3884
|
+
*
|
|
3885
|
+
* A "key" is a stable string the host puts on each item element — `data-key`
|
|
3886
|
+
* (preferred) or `data-value`. It survives the host rebuilding the item set
|
|
3887
|
+
* (re-fetch, re-sort, filter) where raw `Element` references do not.
|
|
3888
|
+
*/
|
|
3889
|
+
/**
|
|
3890
|
+
* The stable key for an item element: `data-key`, then `data-value`, else
|
|
3891
|
+
* `undefined`.
|
|
3892
|
+
*/
|
|
3893
|
+
function keyForElement(el) {
|
|
3894
|
+
return el.getAttribute(`data-key`) ?? el.getAttribute(`data-value`) ?? void 0;
|
|
3895
|
+
}
|
|
3896
|
+
/**
|
|
3897
|
+
* A lazily-built key → element index over a container. Rebuilt on first use
|
|
3898
|
+
* and after any child-list mutation under `root`; `invalidate()` forces a
|
|
3899
|
+
* rebuild on the next `get()`.
|
|
3900
|
+
*
|
|
3901
|
+
* ```ts
|
|
3902
|
+
* #keyIndex = new KeyedElementIndex(this);
|
|
3903
|
+
* // ...
|
|
3904
|
+
* const el = this.#keyIndex.get(`row-42`);
|
|
3905
|
+
* // on disconnect:
|
|
3906
|
+
* this.#keyIndex.dispose();
|
|
3907
|
+
* ```
|
|
3908
|
+
*/
|
|
3909
|
+
var KeyedElementIndex = class {
|
|
3910
|
+
#root;
|
|
3911
|
+
#selector;
|
|
3912
|
+
#map = /* @__PURE__ */ new Map();
|
|
3913
|
+
#dirty = true;
|
|
3914
|
+
#observer;
|
|
3915
|
+
/**
|
|
3916
|
+
* @param root Container whose descendants are indexed.
|
|
3917
|
+
* @param itemSelector Which descendants count as items (default: any `[data-key]`).
|
|
3918
|
+
*/
|
|
3919
|
+
constructor(root, itemSelector = `[data-key]`) {
|
|
3920
|
+
this.#root = root;
|
|
3921
|
+
this.#selector = itemSelector;
|
|
3922
|
+
this.#observer = new MutationObserver(() => {
|
|
3923
|
+
this.#dirty = true;
|
|
3924
|
+
});
|
|
3925
|
+
this.#observer.observe(root, {
|
|
3926
|
+
childList: true,
|
|
3927
|
+
subtree: true
|
|
3928
|
+
});
|
|
3929
|
+
}
|
|
3930
|
+
/** The element with this key, or `undefined`. First match wins on duplicates. */
|
|
3931
|
+
get(key) {
|
|
3932
|
+
if (this.#dirty) this.#rebuild();
|
|
3933
|
+
return this.#map.get(key);
|
|
3934
|
+
}
|
|
3935
|
+
/** Force a rebuild on the next `get()` — e.g. after a `data-key` attribute change. */
|
|
3936
|
+
invalidate() {
|
|
3937
|
+
this.#dirty = true;
|
|
3938
|
+
}
|
|
3939
|
+
/** Stop observing and drop the cache. */
|
|
3940
|
+
dispose() {
|
|
3941
|
+
this.#observer.disconnect();
|
|
3942
|
+
this.#map.clear();
|
|
3943
|
+
}
|
|
3944
|
+
#rebuild() {
|
|
3945
|
+
this.#map.clear();
|
|
3946
|
+
for (const el of this.#root.querySelectorAll(this.#selector)) {
|
|
3947
|
+
const key = keyForElement(el);
|
|
3948
|
+
if (key !== void 0 && !this.#map.has(key)) this.#map.set(key, el);
|
|
3949
|
+
}
|
|
3950
|
+
this.#dirty = false;
|
|
3951
|
+
}
|
|
3952
|
+
};
|
|
3953
|
+
//#endregion
|
|
3954
|
+
//#region src/interaction/list-item-animation.ts
|
|
3955
|
+
/**
|
|
3956
|
+
* Shared insertion / removal animation for the list & tree components.
|
|
3957
|
+
*
|
|
3958
|
+
* Two flavours, because the components split into two layout families:
|
|
3959
|
+
*
|
|
3960
|
+
* - **Flow layouts** (`ixfx-vertical-list`, `ixfx-detail-list`, `ixfx-tree-*`) —
|
|
3961
|
+
* rows sit in normal flow, so a new row can collapse in from zero height and a
|
|
3962
|
+
* leaving row can collapse back to zero, and the rows around it slide to make
|
|
3963
|
+
* or close the gap for free. Use {@link animateListItemIn} /
|
|
3964
|
+
* {@link animateListItemOut} (Web Animations API, exact measured sizes).
|
|
3965
|
+
*
|
|
3966
|
+
* - **Virtualised layouts** (`ixfx-grid-list`, `ixfx-data-grid`) — rows are
|
|
3967
|
+
* absolutely positioned (`transform` / `top`), so collapsing a row's box moves
|
|
3968
|
+
* nothing. Those components instead fade the entering / leaving row with the
|
|
3969
|
+
* {@link listItemFadeKeyframes} keyframes and slide the survivors by
|
|
3970
|
+
* transitioning their position for a short window after the change.
|
|
3971
|
+
*
|
|
3972
|
+
* Everything here is a no-op under `prefers-reduced-motion: reduce`.
|
|
3973
|
+
*/
|
|
3974
|
+
/** Default duration (ms) for both flavours; overridable per call / per component. */
|
|
3975
|
+
const LIST_ITEM_MOTION_MS = 200;
|
|
3976
|
+
function prefersReducedMotion() {
|
|
3977
|
+
return typeof window !== `undefined` && typeof window.matchMedia === `function` && window.matchMedia(`(prefers-reduced-motion: reduce)`).matches;
|
|
3978
|
+
}
|
|
3979
|
+
/**
|
|
3980
|
+
* Opacity keyframes for the virtualised components. Each one adds its own
|
|
3981
|
+
* one-line rule binding `.<row>.ixfx-item-entering` / `.ixfx-item-leaving` to
|
|
3982
|
+
* these, plus a `prefers-reduced-motion` guard on its own selectors.
|
|
3983
|
+
*/
|
|
3984
|
+
const listItemFadeKeyframes = i$3`
|
|
3985
|
+
@keyframes ixfx-list-item-in {
|
|
3986
|
+
from { opacity: 0; }
|
|
3987
|
+
to { opacity: 1; }
|
|
3988
|
+
}
|
|
3989
|
+
@keyframes ixfx-list-item-out {
|
|
3990
|
+
from { opacity: 1; }
|
|
3991
|
+
to { opacity: 0; }
|
|
3992
|
+
}
|
|
3993
|
+
`;
|
|
3994
|
+
function measure(el, axis) {
|
|
3995
|
+
const rect = el.getBoundingClientRect();
|
|
3996
|
+
return axis === `inline` ? rect.width : rect.height;
|
|
3997
|
+
}
|
|
3998
|
+
function resolveDuration(el, explicit) {
|
|
3999
|
+
if (explicit !== void 0) return explicit;
|
|
4000
|
+
const raw = getComputedStyle(el).getPropertyValue(`--ixfx-list-item-move-duration`).trim();
|
|
4001
|
+
if (raw) {
|
|
4002
|
+
const n = Number.parseFloat(raw);
|
|
4003
|
+
if (!Number.isNaN(n)) return raw.endsWith(`ms`) ? n : raw.endsWith(`s`) ? n * 1e3 : n;
|
|
4004
|
+
}
|
|
4005
|
+
return 200;
|
|
4006
|
+
}
|
|
4007
|
+
function collapsedFrame(axis) {
|
|
4008
|
+
const prop = axis === `inline` ? `width` : `height`;
|
|
4009
|
+
const minProp = axis === `inline` ? `minWidth` : `minHeight`;
|
|
4010
|
+
return {
|
|
4011
|
+
[prop]: `0px`,
|
|
4012
|
+
[minProp]: `0px`,
|
|
4013
|
+
opacity: 0,
|
|
4014
|
+
overflow: `hidden`
|
|
4015
|
+
};
|
|
4016
|
+
}
|
|
4017
|
+
function fullFrame(size, axis) {
|
|
4018
|
+
const prop = axis === `inline` ? `width` : `height`;
|
|
4019
|
+
const minProp = axis === `inline` ? `minWidth` : `minHeight`;
|
|
4020
|
+
return {
|
|
4021
|
+
[prop]: `${size}px`,
|
|
4022
|
+
[minProp]: `0px`,
|
|
4023
|
+
opacity: 1,
|
|
4024
|
+
overflow: `hidden`
|
|
4025
|
+
};
|
|
4026
|
+
}
|
|
4027
|
+
/**
|
|
4028
|
+
* Collapse + fade `el` in from nothing to its natural size. Call it just after
|
|
4029
|
+
* the element has been inserted (it must already be laid out at full size so it
|
|
4030
|
+
* can be measured). No-op under reduced motion.
|
|
4031
|
+
*/
|
|
4032
|
+
function animateListItemIn(el, options = {}) {
|
|
4033
|
+
if (prefersReducedMotion()) return void 0;
|
|
4034
|
+
const { axis = `block`, easing = `ease` } = options;
|
|
4035
|
+
const duration = resolveDuration(el, options.duration);
|
|
4036
|
+
const size = measure(el, axis);
|
|
4037
|
+
if (size <= 0) return void 0;
|
|
4038
|
+
return el.animate([collapsedFrame(axis), fullFrame(size, axis)], {
|
|
4039
|
+
duration,
|
|
4040
|
+
easing
|
|
4041
|
+
});
|
|
4042
|
+
}
|
|
4043
|
+
/**
|
|
4044
|
+
* Collapse + fade `el` out. Resolves once the animation finishes (or straight
|
|
4045
|
+
* away under reduced motion); the caller detaches the element after. The final
|
|
4046
|
+
* collapsed state is held (`fill: forwards`) so there's no flash back to full
|
|
4047
|
+
* size in the gap before removal.
|
|
4048
|
+
*/
|
|
4049
|
+
function animateListItemOut(el, options = {}) {
|
|
4050
|
+
if (prefersReducedMotion()) return Promise.resolve();
|
|
4051
|
+
const { axis = `block`, easing = `ease` } = options;
|
|
4052
|
+
const duration = resolveDuration(el, options.duration);
|
|
4053
|
+
const size = measure(el, axis);
|
|
4054
|
+
if (size <= 0) return Promise.resolve();
|
|
4055
|
+
return el.animate([fullFrame(size, axis), collapsedFrame(axis)], {
|
|
4056
|
+
duration,
|
|
4057
|
+
easing,
|
|
4058
|
+
fill: `forwards`
|
|
4059
|
+
}).finished.then(() => void 0, () => void 0);
|
|
4060
|
+
}
|
|
4061
|
+
//#endregion
|
|
3864
4062
|
//#region src/interaction/list-selection-controller.ts
|
|
3865
4063
|
/** Whether the interaction mode allows building a multi-item selection. */
|
|
3866
4064
|
function isMultiMode(mode) {
|
|
@@ -3901,6 +4099,20 @@ var ListSelectionController = class {
|
|
|
3901
4099
|
get selected() {
|
|
3902
4100
|
return this.#selected;
|
|
3903
4101
|
}
|
|
4102
|
+
/**
|
|
4103
|
+
* The current selection as stable string keys (read-only). Empty unless a
|
|
4104
|
+
* `getKey` was supplied in config. Items whose `getKey` returns `undefined`
|
|
4105
|
+
* are omitted.
|
|
4106
|
+
*/
|
|
4107
|
+
get selectedKeys() {
|
|
4108
|
+
const getKey = this.#config.getKey;
|
|
4109
|
+
const out = /* @__PURE__ */ new Set();
|
|
4110
|
+
if (getKey) for (const item of this.#selected) {
|
|
4111
|
+
const key = getKey(item);
|
|
4112
|
+
if (key !== void 0) out.add(key);
|
|
4113
|
+
}
|
|
4114
|
+
return out;
|
|
4115
|
+
}
|
|
3904
4116
|
/** Anchor for range (shift) selection — the last plainly-selected item. */
|
|
3905
4117
|
get anchor() {
|
|
3906
4118
|
return this.#anchor;
|
|
@@ -3984,6 +4196,66 @@ var ListSelectionController = class {
|
|
|
3984
4196
|
next.delete(item);
|
|
3985
4197
|
this.#apply(next);
|
|
3986
4198
|
}
|
|
4199
|
+
/**
|
|
4200
|
+
* Whether the current modes permit a selection of more than one item.
|
|
4201
|
+
* Stricter than `canSelectMultiple()`: `standard` / `implicit` only count
|
|
4202
|
+
* as multi when `selectionMode="multiple"`.
|
|
4203
|
+
*/
|
|
4204
|
+
#effectiveMulti() {
|
|
4205
|
+
const { selectionMode, interactionMode } = this.#config.getModes();
|
|
4206
|
+
return selectionMode === `multiple` || interactionMode === `checked` || interactionMode === `sticky` || interactionMode === `vscode`;
|
|
4207
|
+
}
|
|
4208
|
+
/**
|
|
4209
|
+
* Replace the selection with the items resolved from `keys`. Keys that
|
|
4210
|
+
* `resolve` cannot map, or that resolve to a non-selectable item, are
|
|
4211
|
+
* ignored. `selectionMode="none"` is a no-op. When multi-select is not
|
|
4212
|
+
* available, only the last resolvable key is kept. `onChange` fires once.
|
|
4213
|
+
*/
|
|
4214
|
+
selectKeys(keys, resolve) {
|
|
4215
|
+
if (this.#config.getModes().selectionMode === `none`) return;
|
|
4216
|
+
let next = /* @__PURE__ */ new Set();
|
|
4217
|
+
for (const key of new Set(keys)) {
|
|
4218
|
+
const item = resolve(key);
|
|
4219
|
+
if (item !== void 0 && this.#config.isSelectable(item)) next.add(item);
|
|
4220
|
+
}
|
|
4221
|
+
if (!this.#effectiveMulti() && next.size > 1) {
|
|
4222
|
+
const last = [...next].at(-1);
|
|
4223
|
+
next = last === void 0 ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([last]);
|
|
4224
|
+
}
|
|
4225
|
+
this.#anchor = [...next].at(-1);
|
|
4226
|
+
this.#apply(next);
|
|
4227
|
+
}
|
|
4228
|
+
/**
|
|
4229
|
+
* Add the items resolved from `keys` to the selection (multi-select only;
|
|
4230
|
+
* a no-op otherwise). `onChange` fires once if anything changed.
|
|
4231
|
+
*/
|
|
4232
|
+
addKeys(keys, resolve) {
|
|
4233
|
+
if (!this.#effectiveMulti()) return;
|
|
4234
|
+
const next = new Set(this.#selected);
|
|
4235
|
+
let changed = false;
|
|
4236
|
+
for (const key of new Set(keys)) {
|
|
4237
|
+
const item = resolve(key);
|
|
4238
|
+
if (item !== void 0 && !next.has(item) && this.#config.isSelectable(item)) {
|
|
4239
|
+
next.add(item);
|
|
4240
|
+
changed = true;
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
if (changed) this.#apply(next);
|
|
4244
|
+
}
|
|
4245
|
+
/**
|
|
4246
|
+
* Remove the items resolved from `keys` from the selection. `onChange`
|
|
4247
|
+
* fires once if anything changed.
|
|
4248
|
+
*/
|
|
4249
|
+
removeKeys(keys, resolve) {
|
|
4250
|
+
if (this.#selected.size === 0) return;
|
|
4251
|
+
const next = new Set(this.#selected);
|
|
4252
|
+
let changed = false;
|
|
4253
|
+
for (const key of new Set(keys)) {
|
|
4254
|
+
const item = resolve(key);
|
|
4255
|
+
if (item !== void 0 && next.delete(item)) changed = true;
|
|
4256
|
+
}
|
|
4257
|
+
if (changed) this.#apply(next);
|
|
4258
|
+
}
|
|
3987
4259
|
/** Route a click on `item` according to the current interaction mode. */
|
|
3988
4260
|
handleClick(item, gesture) {
|
|
3989
4261
|
const { selectionMode, interactionMode: mode } = this.#config.getModes();
|
|
@@ -10772,6 +11044,7 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
10772
11044
|
this.groupBy = null;
|
|
10773
11045
|
this.allowGrouping = false;
|
|
10774
11046
|
this.crossGroupSelection = false;
|
|
11047
|
+
this.columnMenu = `auto`;
|
|
10775
11048
|
this._columnFormatters = /* @__PURE__ */ new Map();
|
|
10776
11049
|
this._selectedKeys = /* @__PURE__ */ new Set();
|
|
10777
11050
|
this._scrollTop = 0;
|
|
@@ -10794,10 +11067,16 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
10794
11067
|
this._sortedRows = [];
|
|
10795
11068
|
this._flat = [];
|
|
10796
11069
|
this._flatKeys = [];
|
|
11070
|
+
this.#prevFlatKeys = [];
|
|
11071
|
+
this.#enteringRowKeys = /* @__PURE__ */ new Set();
|
|
11072
|
+
this.#rowReflowTimer = 0;
|
|
11073
|
+
this.#dataGridRenderedOnce = false;
|
|
10797
11074
|
this._visibleColumns = [];
|
|
10798
11075
|
this._colOffsets = [];
|
|
10799
11076
|
this._colWidthSum = 0;
|
|
10800
11077
|
this._raf = 0;
|
|
11078
|
+
this.#appendInProgress = false;
|
|
11079
|
+
this.#resortRaf = 0;
|
|
10801
11080
|
this._drag = null;
|
|
10802
11081
|
this._onFocusOut = (e) => {
|
|
10803
11082
|
if (this._suppressBlurCommit) return;
|
|
@@ -10992,6 +11271,7 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
10992
11271
|
themeFallbacks,
|
|
10993
11272
|
tickledStyles,
|
|
10994
11273
|
tickledItemStyles,
|
|
11274
|
+
listItemFadeKeyframes,
|
|
10995
11275
|
i$3`
|
|
10996
11276
|
:host {
|
|
10997
11277
|
--data-grid-row-height: 28px;
|
|
@@ -11048,6 +11328,22 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
11048
11328
|
.row.item.tickled { background: var(--item-bg-tickled); border: var(--item-border-tickled); color: var(--item-text-tickled); }
|
|
11049
11329
|
.row.item.selected { background: var(--item-bg-selected); border: var(--item-border-selected); color: var(--item-text-selected); }
|
|
11050
11330
|
.row.item.selected.tickled { background: var(--item-bg-selected-tickled); }
|
|
11331
|
+
|
|
11332
|
+
/* Insertion / removal animation. Only while [reflowing] — the ~0.3s after
|
|
11333
|
+
the row set changes — do rows slide (their top transitions) to make or
|
|
11334
|
+
close a gap; a freshly inserted row also fades in. Kept scoped so it
|
|
11335
|
+
never fires while scrolling the virtualised list. */
|
|
11336
|
+
:host([reflowing]) .row.item,
|
|
11337
|
+
:host([reflowing]) .group-row {
|
|
11338
|
+
transition: top var(--data-grid-row-move-duration, 200ms) cubic-bezier(0.2, 0, 0, 1);
|
|
11339
|
+
}
|
|
11340
|
+
.row.item.entering {
|
|
11341
|
+
animation: ixfx-list-item-in var(--data-grid-row-move-duration, 200ms) ease both;
|
|
11342
|
+
}
|
|
11343
|
+
@media (prefers-reduced-motion: reduce) {
|
|
11344
|
+
:host([reflowing]) .row.item, :host([reflowing]) .group-row { transition: none; }
|
|
11345
|
+
.row.item.entering { animation: none; }
|
|
11346
|
+
}
|
|
11051
11347
|
.cell {
|
|
11052
11348
|
flex-shrink: 0; display: flex; align-items: center; padding: 0 var(--space-s);
|
|
11053
11349
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-right: var(--border-subtle);
|
|
@@ -11077,6 +11373,13 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
11077
11373
|
`
|
|
11078
11374
|
];
|
|
11079
11375
|
}
|
|
11376
|
+
/** Row-key snapshot from the previous update, for the insertion / removal animation. */
|
|
11377
|
+
#prevFlatKeys;
|
|
11378
|
+
#enteringRowKeys;
|
|
11379
|
+
#rowReflowTimer;
|
|
11380
|
+
#dataGridRenderedOnce;
|
|
11381
|
+
#appendInProgress;
|
|
11382
|
+
#resortRaf;
|
|
11080
11383
|
connectedCallback() {
|
|
11081
11384
|
super.connectedCallback();
|
|
11082
11385
|
if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
|
|
@@ -11088,6 +11391,10 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
11088
11391
|
this.removeEventListener(`keydown`, this._onKeyDown);
|
|
11089
11392
|
this.removeEventListener(`focusout`, this._onFocusOut);
|
|
11090
11393
|
cancelAnimationFrame(this._raf);
|
|
11394
|
+
if (this.#rowReflowTimer) {
|
|
11395
|
+
clearTimeout(this.#rowReflowTimer);
|
|
11396
|
+
this.#rowReflowTimer = 0;
|
|
11397
|
+
}
|
|
11091
11398
|
}
|
|
11092
11399
|
firstUpdated() {
|
|
11093
11400
|
new ResizeObserver(() => {
|
|
@@ -11114,8 +11421,10 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
11114
11421
|
willUpdate(changed) {
|
|
11115
11422
|
super.willUpdate(changed);
|
|
11116
11423
|
this.setAttribute(`data-input-mode`, this._tickled.inputMode);
|
|
11117
|
-
if (
|
|
11118
|
-
|
|
11424
|
+
if (!this.#appendInProgress) {
|
|
11425
|
+
if (changed.has(`rows`) || changed.has(`sortState`) || changed.has(`columns`) || changed.has(`groupBy`) || changed.has(`groupLabel`)) this._sortedRows = this._applySort([...this.rows]);
|
|
11426
|
+
this._rebuildFlat();
|
|
11427
|
+
}
|
|
11119
11428
|
this._visibleColumns = this.columns.filter((c) => c.visible !== false);
|
|
11120
11429
|
let off = 0;
|
|
11121
11430
|
this._colOffsets = this._visibleColumns.map((c) => {
|
|
@@ -11125,6 +11434,40 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
11125
11434
|
});
|
|
11126
11435
|
this._colWidthSum = off;
|
|
11127
11436
|
if (this.interactionMode === `checked`) this._colWidthSum += 28;
|
|
11437
|
+
this.#noteFlatMutation();
|
|
11438
|
+
}
|
|
11439
|
+
/**
|
|
11440
|
+
* Diff the row-key list against the previous update. Rows that appeared get an
|
|
11441
|
+
* enter fade; any add/remove opens a short `[reflowing]` window so the rows
|
|
11442
|
+
* that shift slide (their `top` transitions) instead of snapping. Bounded in
|
|
11443
|
+
* time so it never bleeds into scroll virtualisation.
|
|
11444
|
+
*/
|
|
11445
|
+
#noteFlatMutation() {
|
|
11446
|
+
const now = this._flatKeys;
|
|
11447
|
+
const prev = this.#prevFlatKeys;
|
|
11448
|
+
this.#prevFlatKeys = now.slice();
|
|
11449
|
+
if (!this.#dataGridRenderedOnce) {
|
|
11450
|
+
this.#dataGridRenderedOnce = true;
|
|
11451
|
+
return;
|
|
11452
|
+
}
|
|
11453
|
+
if (prev.length === 0 || now.length === 0) {
|
|
11454
|
+
this.#enteringRowKeys.clear();
|
|
11455
|
+
return;
|
|
11456
|
+
}
|
|
11457
|
+
const before = new Set(prev);
|
|
11458
|
+
const after = new Set(now);
|
|
11459
|
+
const added = now.filter((k) => !before.has(k));
|
|
11460
|
+
const removed = prev.filter((k) => !after.has(k));
|
|
11461
|
+
if (added.length === now.length || added.length === 0 && removed.length === 0) return;
|
|
11462
|
+
this.#enteringRowKeys = new Set(added);
|
|
11463
|
+
this.toggleAttribute(`reflowing`, true);
|
|
11464
|
+
if (this.#rowReflowTimer) clearTimeout(this.#rowReflowTimer);
|
|
11465
|
+
this.#rowReflowTimer = window.setTimeout(() => {
|
|
11466
|
+
this.#rowReflowTimer = 0;
|
|
11467
|
+
this.#enteringRowKeys.clear();
|
|
11468
|
+
this.toggleAttribute(`reflowing`, false);
|
|
11469
|
+
this.requestUpdate();
|
|
11470
|
+
}, 320);
|
|
11128
11471
|
}
|
|
11129
11472
|
_getGroupKey(row) {
|
|
11130
11473
|
if (!this.groupBy) return null;
|
|
@@ -11372,6 +11715,102 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
11372
11715
|
_getRowForKey(key) {
|
|
11373
11716
|
return this._flat.find((f) => f.key === key)?.row;
|
|
11374
11717
|
}
|
|
11718
|
+
/** The selected row keys (read-only). */
|
|
11719
|
+
get selectedKeys() {
|
|
11720
|
+
return this._selectedKeys;
|
|
11721
|
+
}
|
|
11722
|
+
/** Replace the selection with these row keys (clamped to known rows). */
|
|
11723
|
+
set selectedKeys(keys) {
|
|
11724
|
+
this.selectKeys(keys);
|
|
11725
|
+
}
|
|
11726
|
+
/**
|
|
11727
|
+
* Replace the selection with the given row keys. Unknown keys are dropped;
|
|
11728
|
+
* in a single-select mode only the last surviving key is kept;
|
|
11729
|
+
* `selectionMode="none"` clears. Fires `datagrid-select` with the previous
|
|
11730
|
+
* set when the selection actually changes.
|
|
11731
|
+
*/
|
|
11732
|
+
selectKeys(keys) {
|
|
11733
|
+
const known = new Set(this._flat.filter((f) => f.type === `row`).map((f) => f.key));
|
|
11734
|
+
let next = new Set([...keys].filter((k) => known.has(k)));
|
|
11735
|
+
const multi = this.selectionMode === `multiple` || this.interactionMode === `checked` || this.interactionMode === `sticky` || this.interactionMode === `vscode`;
|
|
11736
|
+
if (this.selectionMode === `none`) next = /* @__PURE__ */ new Set();
|
|
11737
|
+
else if (!multi && next.size > 1) {
|
|
11738
|
+
const last = [...next].at(-1);
|
|
11739
|
+
next = last === void 0 ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([last]);
|
|
11740
|
+
}
|
|
11741
|
+
const prev = new Set(this._selectedKeys);
|
|
11742
|
+
if (prev.size === next.size && [...next].every((k) => prev.has(k))) return;
|
|
11743
|
+
this._selectedKeys = next;
|
|
11744
|
+
this._anchorKey = [...next].at(-1);
|
|
11745
|
+
this.dispatchEvent(new CustomEvent(`datagrid-select`, {
|
|
11746
|
+
detail: {
|
|
11747
|
+
selectedKeys: new Set(next),
|
|
11748
|
+
previous: prev
|
|
11749
|
+
},
|
|
11750
|
+
bubbles: true,
|
|
11751
|
+
composed: true
|
|
11752
|
+
}));
|
|
11753
|
+
}
|
|
11754
|
+
/** The row key under the cursor, or `undefined`. */
|
|
11755
|
+
get tickledKey() {
|
|
11756
|
+
return this._tickled.tickledId;
|
|
11757
|
+
}
|
|
11758
|
+
/** Move the cursor to this row key and scroll it into view. */
|
|
11759
|
+
tickleRow(key) {
|
|
11760
|
+
if (!this._flatKeys.includes(key)) return;
|
|
11761
|
+
this._tickled.focus(key);
|
|
11762
|
+
this._scrollRowIntoView(key);
|
|
11763
|
+
}
|
|
11764
|
+
/**
|
|
11765
|
+
* Append rows without the full O(n log n) rebuild that reassigning `rows`
|
|
11766
|
+
* incurs. `rows` still gets a new array reference (reactivity is preserved),
|
|
11767
|
+
* but the derived arrays are extended over the appended slice only. When a
|
|
11768
|
+
* sort is active the new rows show unsorted for one frame, then a single
|
|
11769
|
+
* coalesced re-sort runs. Scroll position, selection and cursor are kept.
|
|
11770
|
+
*/
|
|
11771
|
+
appendRows(rows) {
|
|
11772
|
+
if (rows.length === 0) return;
|
|
11773
|
+
const startIdx = this.rows.length;
|
|
11774
|
+
this.#appendInProgress = true;
|
|
11775
|
+
this.rows = [...this.rows, ...rows];
|
|
11776
|
+
const appended = this.rows.slice(startIdx);
|
|
11777
|
+
if (this._sortedRows.length === startIdx) this._sortedRows = [...this._sortedRows, ...appended];
|
|
11778
|
+
else {
|
|
11779
|
+
this._sortedRows = this._applySort([...this.rows]);
|
|
11780
|
+
this._rebuildFlat();
|
|
11781
|
+
this.#appendInProgress = false;
|
|
11782
|
+
this.requestUpdate();
|
|
11783
|
+
return;
|
|
11784
|
+
}
|
|
11785
|
+
this.#appendToFlat(appended, startIdx);
|
|
11786
|
+
this.#appendInProgress = false;
|
|
11787
|
+
this.requestUpdate();
|
|
11788
|
+
if (this.sortState != null) {
|
|
11789
|
+
cancelAnimationFrame(this.#resortRaf);
|
|
11790
|
+
this.#resortRaf = requestAnimationFrame(() => {
|
|
11791
|
+
this._sortedRows = this._applySort([...this.rows]);
|
|
11792
|
+
this._rebuildFlat();
|
|
11793
|
+
this.requestUpdate();
|
|
11794
|
+
});
|
|
11795
|
+
}
|
|
11796
|
+
}
|
|
11797
|
+
#appendToFlat(appended, startIdx) {
|
|
11798
|
+
if (this.groupBy) {
|
|
11799
|
+
this._rebuildFlat();
|
|
11800
|
+
return;
|
|
11801
|
+
}
|
|
11802
|
+
for (let j = 0; j < appended.length; j++) {
|
|
11803
|
+
const idx = startIdx + j;
|
|
11804
|
+
const key = this.getRowKey(appended[j], idx);
|
|
11805
|
+
this._flat.push({
|
|
11806
|
+
type: `row`,
|
|
11807
|
+
key,
|
|
11808
|
+
row: appended[j],
|
|
11809
|
+
idx
|
|
11810
|
+
});
|
|
11811
|
+
this._flatKeys.push(key);
|
|
11812
|
+
}
|
|
11813
|
+
}
|
|
11375
11814
|
_replaceSelection(key) {
|
|
11376
11815
|
if (this.selectionMode === `none`) return;
|
|
11377
11816
|
const prev = new Set(this._selectedKeys);
|
|
@@ -11727,6 +12166,7 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
11727
12166
|
return last >= 0 ? this._colOffsets[last] + (this._visibleColumns[last].width ?? 150) : 0;
|
|
11728
12167
|
})()}px"></div>` : A}
|
|
11729
12168
|
</div>
|
|
12169
|
+
${this.columnMenu === `none` ? A : b`
|
|
11730
12170
|
<ixfx-menu-trigger label="⚙" placement="bottom-end" @menu-command=${(e) => {
|
|
11731
12171
|
const cmd = e.detail.command;
|
|
11732
12172
|
const checked = e.detail.checked;
|
|
@@ -11766,7 +12206,7 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
11766
12206
|
</ixfx-menu-radio-group>
|
|
11767
12207
|
` : A}
|
|
11768
12208
|
</ixfx-menu-container>
|
|
11769
|
-
</ixfx-menu-trigger
|
|
12209
|
+
</ixfx-menu-trigger>`}
|
|
11770
12210
|
</div>
|
|
11771
12211
|
<div class="viewport" @scroll=${this._onScroll}>
|
|
11772
12212
|
<div class="spacer" style="height:${this._flat.length * rh}px; width:${this._colWidthSum}px">
|
|
@@ -11783,7 +12223,7 @@ let DataGridElement = class DataGridElement extends i {
|
|
|
11783
12223
|
const selected = this._selectedKeys.has(rowKey);
|
|
11784
12224
|
const tickled = this._tickled.isTickled(rowKey);
|
|
11785
12225
|
return b`
|
|
11786
|
-
<div class="row item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``}" style="top:${absIdx * rh}px; height:${rh}px"
|
|
12226
|
+
<div class="row item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${this.#enteringRowKeys.has(rowKey) ? `entering` : ``}" style="top:${absIdx * rh}px; height:${rh}px"
|
|
11787
12227
|
data-key=${rowKey}
|
|
11788
12228
|
@click=${(e) => {
|
|
11789
12229
|
this._onRowClick(rowKey, null, e);
|
|
@@ -11954,6 +12394,10 @@ __decorate([property({
|
|
|
11954
12394
|
attribute: `cross-group-selection`,
|
|
11955
12395
|
reflect: true
|
|
11956
12396
|
})], DataGridElement.prototype, "crossGroupSelection", void 0);
|
|
12397
|
+
__decorate([property({
|
|
12398
|
+
type: String,
|
|
12399
|
+
attribute: `column-menu`
|
|
12400
|
+
})], DataGridElement.prototype, "columnMenu", void 0);
|
|
11957
12401
|
__decorate([property({ attribute: false })], DataGridElement.prototype, "globalFormatter", void 0);
|
|
11958
12402
|
__decorate([state()], DataGridElement.prototype, "_selectedKeys", void 0);
|
|
11959
12403
|
__decorate([state()], DataGridElement.prototype, "_scrollTop", void 0);
|
|
@@ -11970,8 +12414,273 @@ __decorate([query(`.viewport`)], DataGridElement.prototype, "_viewportEl", void
|
|
|
11970
12414
|
__decorate([query(`.header-scroll`)], DataGridElement.prototype, "_headerScrollEl", void 0);
|
|
11971
12415
|
DataGridElement = __decorate([safeCustomElement(`ixfx-data-grid`)], DataGridElement);
|
|
11972
12416
|
//#endregion
|
|
12417
|
+
//#region src/data-table/data-table.ts
|
|
12418
|
+
let DataTableElement = class DataTableElement extends i {
|
|
12419
|
+
constructor(..._args) {
|
|
12420
|
+
super(..._args);
|
|
12421
|
+
this.rows = [];
|
|
12422
|
+
this.tickleDelay = 400;
|
|
12423
|
+
this.layout = `auto`;
|
|
12424
|
+
this.narrowBreakpoint = 420;
|
|
12425
|
+
this._tickled = new TickledController(this, { getItems: () => this.rows.map((_, i) => ({
|
|
12426
|
+
id: i,
|
|
12427
|
+
enabled: true
|
|
12428
|
+
})) });
|
|
12429
|
+
this._stacked = false;
|
|
12430
|
+
this.#lastWidth = 0;
|
|
12431
|
+
}
|
|
12432
|
+
static {
|
|
12433
|
+
this.styles = [
|
|
12434
|
+
tickledStyles,
|
|
12435
|
+
tickledItemStyles,
|
|
12436
|
+
i$3`
|
|
12437
|
+
:host {
|
|
12438
|
+
display: block;
|
|
12439
|
+
container-type: inline-size;
|
|
12440
|
+
}
|
|
12441
|
+
|
|
12442
|
+
.properties {
|
|
12443
|
+
display: grid;
|
|
12444
|
+
grid-template-columns: var(--data-table-name-width, max-content)
|
|
12445
|
+
var(--data-table-value-width, minmax(0, 1fr));
|
|
12446
|
+
/* No column gap: the tickled highlight reads as one solid block.
|
|
12447
|
+
Separation comes from cell padding, which the highlight covers. */
|
|
12448
|
+
gap: 2px 0;
|
|
12449
|
+
}
|
|
12450
|
+
|
|
12451
|
+
/* Widening: 2 then 3 property-pairs per row on roomy containers.
|
|
12452
|
+
Suppressed when stacked, or when locked to 'columns'. */
|
|
12453
|
+
@container (min-width: 650px) {
|
|
12454
|
+
:host(:not([layout='columns'])) .properties:not(.stacked) {
|
|
12455
|
+
grid-template-columns:
|
|
12456
|
+
var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr))
|
|
12457
|
+
var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr));
|
|
12458
|
+
}
|
|
12459
|
+
}
|
|
12460
|
+
|
|
12461
|
+
@container (min-width: 1000px) {
|
|
12462
|
+
:host(:not([layout='columns'])) .properties:not(.stacked) {
|
|
12463
|
+
grid-template-columns:
|
|
12464
|
+
var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr))
|
|
12465
|
+
var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr))
|
|
12466
|
+
var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr));
|
|
12467
|
+
}
|
|
12468
|
+
}
|
|
12469
|
+
|
|
12470
|
+
/* Stacked (key above value). Threshold is JS-driven via 'narrowBreakpoint'.
|
|
12471
|
+
minmax(0, 1fr) — not 1fr — so a long value's min-content size can't
|
|
12472
|
+
blow the column out past the container; the value then ellipsises. */
|
|
12473
|
+
.properties.stacked {
|
|
12474
|
+
grid-template-columns: minmax(0, 1fr);
|
|
12475
|
+
}
|
|
12476
|
+
|
|
12477
|
+
/* A pair is its own subgrid so the whole row is one element:
|
|
12478
|
+
the tickled highlight can't split, and key/value share a baseline. */
|
|
12479
|
+
.pair {
|
|
12480
|
+
display: grid;
|
|
12481
|
+
grid-column: span 2;
|
|
12482
|
+
grid-template-columns: subgrid;
|
|
12483
|
+
align-items: var(--data-table-cell-align, baseline);
|
|
12484
|
+
cursor: pointer;
|
|
12485
|
+
}
|
|
12486
|
+
|
|
12487
|
+
.pair[data-full] {
|
|
12488
|
+
grid-column: 1 / -1;
|
|
12489
|
+
}
|
|
12490
|
+
|
|
12491
|
+
.pair[data-full] > .value {
|
|
12492
|
+
grid-column: 2 / -1;
|
|
12493
|
+
}
|
|
12494
|
+
|
|
12495
|
+
.properties.stacked .pair {
|
|
12496
|
+
display: block;
|
|
12497
|
+
grid-column: 1 / -1;
|
|
12498
|
+
/* Let the block shrink below its content so .value-text can clip. */
|
|
12499
|
+
min-width: 0;
|
|
12500
|
+
}
|
|
12501
|
+
|
|
12502
|
+
.properties.stacked .pair:not(:last-child) {
|
|
12503
|
+
margin-bottom: var(--space-s);
|
|
12504
|
+
}
|
|
12505
|
+
|
|
12506
|
+
/* Stacked keys get their own line, so a long label must wrap rather than
|
|
12507
|
+
run off the edge (nowrap is only wanted for the max-content column). */
|
|
12508
|
+
.properties.stacked .key {
|
|
12509
|
+
white-space: normal;
|
|
12510
|
+
overflow-wrap: break-word;
|
|
12511
|
+
}
|
|
12512
|
+
|
|
12513
|
+
.key,
|
|
12514
|
+
.value {
|
|
12515
|
+
min-width: 0;
|
|
12516
|
+
/* Row rhythm comes from this padding (tunable), not line-height. */
|
|
12517
|
+
padding: var(--data-table-row-padding, 0.35em) 0;
|
|
12518
|
+
}
|
|
12519
|
+
|
|
12520
|
+
.key {
|
|
12521
|
+
padding-right: var(--space-m);
|
|
12522
|
+
opacity: 0.6;
|
|
12523
|
+
white-space: nowrap;
|
|
12524
|
+
text-align: var(--data-table-name-align, left);
|
|
12525
|
+
font-size: 10px;
|
|
12526
|
+
text-transform: uppercase;
|
|
12527
|
+
letter-spacing: 0.05em;
|
|
12528
|
+
/* Uppercase keys have no descenders, so both edges can be trimmed to
|
|
12529
|
+
the glyphs for tight baseline / centre alignment. */
|
|
12530
|
+
text-box: trim-both cap alphabetic;
|
|
12531
|
+
}
|
|
12532
|
+
|
|
12533
|
+
.value {
|
|
12534
|
+
position: relative;
|
|
12535
|
+
padding-right: var(--space-l);
|
|
12536
|
+
font: 12px ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
12537
|
+
}
|
|
12538
|
+
|
|
12539
|
+
/* The value text is clipped here — never on .value itself — so a long
|
|
12540
|
+
value stays within its cell even while tickled, and the detail
|
|
12541
|
+
popover (a child of .value) can still escape the cell. */
|
|
12542
|
+
.value-text {
|
|
12543
|
+
display: block;
|
|
12544
|
+
overflow: hidden;
|
|
12545
|
+
text-overflow: ellipsis;
|
|
12546
|
+
white-space: nowrap;
|
|
12547
|
+
/* No text-box trim here: the natural line box gives descenders the room
|
|
12548
|
+
overflow:hidden would otherwise clip. Baseline alignment of key and
|
|
12549
|
+
value comes from align-items on .pair, which needs no trimming. */
|
|
12550
|
+
}
|
|
12551
|
+
|
|
12552
|
+
.pair.tickled {
|
|
12553
|
+
background: var(--item-bg-tickled, color-mix(in oklch, var(--surface-2), var(--accent) 10%));
|
|
12554
|
+
}
|
|
12555
|
+
|
|
12556
|
+
/* Lift the row whose detail is open above the rows it slides over. */
|
|
12557
|
+
.pair.show-detail {
|
|
12558
|
+
position: relative;
|
|
12559
|
+
z-index: 1;
|
|
12560
|
+
}
|
|
12561
|
+
|
|
12562
|
+
.detail {
|
|
12563
|
+
position: absolute;
|
|
12564
|
+
z-index: 2;
|
|
12565
|
+
/* Flush with the bottom of the value (no gap) so the pointer can travel
|
|
12566
|
+
from value to panel without crossing a dead zone that would dismiss it.
|
|
12567
|
+
Left-aligned with the value; only as wide as its content, capped at
|
|
12568
|
+
the table width. */
|
|
12569
|
+
top: 100%;
|
|
12570
|
+
left: 0;
|
|
12571
|
+
right: auto;
|
|
12572
|
+
width: max-content;
|
|
12573
|
+
max-width: 100cqi;
|
|
12574
|
+
padding: var(--space-xs);
|
|
12575
|
+
background: var(--surface-2);
|
|
12576
|
+
visibility: hidden;
|
|
12577
|
+
transform: translateY(-4px);
|
|
12578
|
+
pointer-events: none;
|
|
12579
|
+
transition: transform var(--transition);
|
|
12580
|
+
}
|
|
12581
|
+
|
|
12582
|
+
.pair.show-detail .detail {
|
|
12583
|
+
visibility: visible;
|
|
12584
|
+
transform: translateY(0);
|
|
12585
|
+
pointer-events: auto;
|
|
12586
|
+
}
|
|
12587
|
+
`
|
|
12588
|
+
];
|
|
12589
|
+
}
|
|
12590
|
+
#detailTimer;
|
|
12591
|
+
#resizeObserver;
|
|
12592
|
+
#lastWidth;
|
|
12593
|
+
connectedCallback() {
|
|
12594
|
+
super.connectedCallback();
|
|
12595
|
+
this.#resizeObserver = new ResizeObserver((entries) => {
|
|
12596
|
+
const entry = entries[0];
|
|
12597
|
+
if (!entry) return;
|
|
12598
|
+
this.#lastWidth = entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width;
|
|
12599
|
+
this.#updateStacked();
|
|
12600
|
+
});
|
|
12601
|
+
this.#resizeObserver.observe(this);
|
|
12602
|
+
}
|
|
12603
|
+
disconnectedCallback() {
|
|
12604
|
+
super.disconnectedCallback();
|
|
12605
|
+
clearTimeout(this.#detailTimer);
|
|
12606
|
+
this.#resizeObserver?.disconnect();
|
|
12607
|
+
this.#resizeObserver = void 0;
|
|
12608
|
+
}
|
|
12609
|
+
willUpdate(changed) {
|
|
12610
|
+
if (changed.has(`layout`) || changed.has(`narrowBreakpoint`)) this.#updateStacked();
|
|
12611
|
+
}
|
|
12612
|
+
#updateStacked() {
|
|
12613
|
+
const stacked = this.layout === `rows` ? true : this.layout === `columns` ? false : this.#lastWidth > 0 && this.#lastWidth <= this.narrowBreakpoint;
|
|
12614
|
+
if (stacked !== this._stacked) this._stacked = stacked;
|
|
12615
|
+
}
|
|
12616
|
+
#handlePointerEnter(i) {
|
|
12617
|
+
this._tickled.handlePointerEnter(i);
|
|
12618
|
+
clearTimeout(this.#detailTimer);
|
|
12619
|
+
if (this._detailIndex !== void 0) this._detailIndex = void 0;
|
|
12620
|
+
this.#detailTimer = setTimeout(() => {
|
|
12621
|
+
if (this._tickled.isTickled(i)) this._detailIndex = i;
|
|
12622
|
+
}, this.tickleDelay);
|
|
12623
|
+
}
|
|
12624
|
+
#handlePointerLeave(e) {
|
|
12625
|
+
clearTimeout(this.#detailTimer);
|
|
12626
|
+
this._detailIndex = void 0;
|
|
12627
|
+
this._tickled.handlePointerLeave(e);
|
|
12628
|
+
}
|
|
12629
|
+
#select(i) {
|
|
12630
|
+
clearTimeout(this.#detailTimer);
|
|
12631
|
+
this.selectedIndex = this.selectedIndex === i ? void 0 : i;
|
|
12632
|
+
this._tickled.setTickled(i);
|
|
12633
|
+
this._detailIndex = i;
|
|
12634
|
+
this.dispatchEvent(new CustomEvent(`data-table-row-click`, {
|
|
12635
|
+
bubbles: true,
|
|
12636
|
+
composed: true,
|
|
12637
|
+
detail: {
|
|
12638
|
+
row: this.rows[i],
|
|
12639
|
+
index: i,
|
|
12640
|
+
selected: this.selectedIndex === i
|
|
12641
|
+
}
|
|
12642
|
+
}));
|
|
12643
|
+
}
|
|
12644
|
+
render() {
|
|
12645
|
+
return b`<div
|
|
12646
|
+
class="properties ${this._stacked ? `stacked` : ``}"
|
|
12647
|
+
@pointerleave=${(e) => this.#handlePointerLeave(e)}
|
|
12648
|
+
>
|
|
12649
|
+
${this.rows.map((r, i) => b`<div
|
|
12650
|
+
class="pair ${this._tickled.isTickled(i) ? `tickled` : ``} ${this._detailIndex === i ? `show-detail` : ``}"
|
|
12651
|
+
?data-full=${r.fullWidth}
|
|
12652
|
+
@pointerenter=${() => this.#handlePointerEnter(i)}
|
|
12653
|
+
@click=${() => this.#select(i)}
|
|
12654
|
+
><div class="key">${r.label}</div><div class="value"><span class="value-text">${typeof r.value === `boolean` ? r.value ? `Yes` : `No` : r.value}</span>${r.detail === void 0 ? A : b`<div class="detail" part="detail">${r.detail}</div>`}</div></div>`)}</div>`;
|
|
12655
|
+
}
|
|
12656
|
+
};
|
|
12657
|
+
__decorate([property({
|
|
12658
|
+
type: Array,
|
|
12659
|
+
attribute: false
|
|
12660
|
+
})], DataTableElement.prototype, "rows", void 0);
|
|
12661
|
+
__decorate([property({
|
|
12662
|
+
type: Number,
|
|
12663
|
+
attribute: false
|
|
12664
|
+
})], DataTableElement.prototype, "selectedIndex", void 0);
|
|
12665
|
+
__decorate([property({
|
|
12666
|
+
type: Number,
|
|
12667
|
+
attribute: `tickle-delay`
|
|
12668
|
+
})], DataTableElement.prototype, "tickleDelay", void 0);
|
|
12669
|
+
__decorate([property({
|
|
12670
|
+
type: String,
|
|
12671
|
+
reflect: true
|
|
12672
|
+
})], DataTableElement.prototype, "layout", void 0);
|
|
12673
|
+
__decorate([property({
|
|
12674
|
+
type: Number,
|
|
12675
|
+
attribute: `narrow-breakpoint`
|
|
12676
|
+
})], DataTableElement.prototype, "narrowBreakpoint", void 0);
|
|
12677
|
+
__decorate([state()], DataTableElement.prototype, "_tickled", void 0);
|
|
12678
|
+
__decorate([state()], DataTableElement.prototype, "_detailIndex", void 0);
|
|
12679
|
+
__decorate([state()], DataTableElement.prototype, "_stacked", void 0);
|
|
12680
|
+
DataTableElement = __decorate([safeCustomElement(`ixfx-data-table`)], DataTableElement);
|
|
12681
|
+
//#endregion
|
|
11973
12682
|
//#region src/detail-list/detail-list.ts
|
|
11974
|
-
const ITEM_SELECTOR$
|
|
12683
|
+
const ITEM_SELECTOR$2 = `:scope > li`;
|
|
11975
12684
|
const SEARCH_HIGHLIGHT_KEY = `detail-list-search`;
|
|
11976
12685
|
const LABEL_SPAN_CSS = `overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0`;
|
|
11977
12686
|
let DetailListElement = class DetailListElement extends i {
|
|
@@ -11992,6 +12701,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
11992
12701
|
this.#grid = [];
|
|
11993
12702
|
this.#positions = /* @__PURE__ */ new Map();
|
|
11994
12703
|
this.#geometryScheduled = false;
|
|
12704
|
+
this.#animatedItems = /* @__PURE__ */ new WeakSet();
|
|
11995
12705
|
this.#builtFromProvider = [];
|
|
11996
12706
|
this.#providerBuilt = 0;
|
|
11997
12707
|
this.#data = new DataController(this);
|
|
@@ -12002,11 +12712,13 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12002
12712
|
selectionMode: this.selectionMode,
|
|
12003
12713
|
interactionMode: this.interactionMode
|
|
12004
12714
|
}),
|
|
12715
|
+
getKey: keyForElement,
|
|
12005
12716
|
onChange: (selected, previous) => {
|
|
12006
12717
|
this._syncItemStates();
|
|
12007
12718
|
this._dispatch(`list-select`, {
|
|
12008
12719
|
selected,
|
|
12009
|
-
previous
|
|
12720
|
+
previous,
|
|
12721
|
+
selectedKeys: this.selection.selectedKeys
|
|
12010
12722
|
});
|
|
12011
12723
|
}
|
|
12012
12724
|
});
|
|
@@ -12158,6 +12870,8 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12158
12870
|
#geometryScheduled;
|
|
12159
12871
|
#resizeObserver;
|
|
12160
12872
|
#mutationObserver;
|
|
12873
|
+
/** Items that have already played (or been skipped for) their enter animation. */
|
|
12874
|
+
#animatedItems;
|
|
12161
12875
|
#builtFromProvider;
|
|
12162
12876
|
#providerBuilt;
|
|
12163
12877
|
#data;
|
|
@@ -12172,8 +12886,9 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12172
12886
|
this.addEventListener(`wheel`, this._onWheel);
|
|
12173
12887
|
this.#resizeObserver = new ResizeObserver(() => this.#scheduleGeometry());
|
|
12174
12888
|
this.#resizeObserver.observe(this);
|
|
12175
|
-
this.#mutationObserver = new MutationObserver(() => {
|
|
12889
|
+
this.#mutationObserver = new MutationObserver((records) => {
|
|
12176
12890
|
this.#processChildren();
|
|
12891
|
+
this.#animateChildMutations(records);
|
|
12177
12892
|
this.#scheduleGeometry();
|
|
12178
12893
|
});
|
|
12179
12894
|
this.#mutationObserver.observe(this, {
|
|
@@ -12181,6 +12896,27 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12181
12896
|
subtree: false,
|
|
12182
12897
|
attributeFilter: [`hidden`]
|
|
12183
12898
|
});
|
|
12899
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$2)) this.#animatedItems.add(el);
|
|
12900
|
+
}
|
|
12901
|
+
/**
|
|
12902
|
+
* Collapse-in items appended to the light DOM (however they got there). A bulk
|
|
12903
|
+
* populate is left alone. Geometry is re-read once the animations settle so
|
|
12904
|
+
* keyboard navigation lines back up with the final layout.
|
|
12905
|
+
*/
|
|
12906
|
+
#animateChildMutations(records) {
|
|
12907
|
+
if (!this.isConnected) return;
|
|
12908
|
+
const fresh = [];
|
|
12909
|
+
for (const record of records) for (const node of record.addedNodes) {
|
|
12910
|
+
if (!(node instanceof HTMLElement) || node.parentElement !== this || node.localName !== `li`) continue;
|
|
12911
|
+
if (this.#animatedItems.has(node)) continue;
|
|
12912
|
+
this.#animatedItems.add(node);
|
|
12913
|
+
fresh.push(node);
|
|
12914
|
+
}
|
|
12915
|
+
if (fresh.length === 0) return;
|
|
12916
|
+
const total = this.querySelectorAll(ITEM_SELECTOR$2).length;
|
|
12917
|
+
if (fresh.length >= total) return;
|
|
12918
|
+
for (const el of fresh) animateListItemIn(el);
|
|
12919
|
+
window.setTimeout(() => this.#scheduleGeometry(), 230);
|
|
12184
12920
|
}
|
|
12185
12921
|
disconnectedCallback() {
|
|
12186
12922
|
super.disconnectedCallback();
|
|
@@ -12202,7 +12938,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12202
12938
|
if (this.filterPredicate) this.#applyFilterPredicate();
|
|
12203
12939
|
this.#search = createElementIncrSearch({
|
|
12204
12940
|
container: this,
|
|
12205
|
-
itemSelector: ITEM_SELECTOR$
|
|
12941
|
+
itemSelector: ITEM_SELECTOR$2,
|
|
12206
12942
|
getLabel: (el) => el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
|
|
12207
12943
|
getKey: (el) => el.getAttribute(`data-key`) ?? el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
|
|
12208
12944
|
highlightKey: SEARCH_HIGHLIGHT_KEY
|
|
@@ -12261,8 +12997,21 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12261
12997
|
if (this.sort !== `none`) this.#applySort();
|
|
12262
12998
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
12263
12999
|
}
|
|
13000
|
+
/**
|
|
13001
|
+
* Collapse + fade `item` out, then detach it; the rows after it slide up to
|
|
13002
|
+
* close the gap. Resolves once removed. Immediate under reduced motion.
|
|
13003
|
+
*/
|
|
12264
13004
|
removeItem(item) {
|
|
12265
|
-
|
|
13005
|
+
this.selection.deselect(item);
|
|
13006
|
+
return animateListItemOut(item).then(() => {
|
|
13007
|
+
item.remove();
|
|
13008
|
+
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
13009
|
+
this.#recomputeGeometry();
|
|
13010
|
+
});
|
|
13011
|
+
}
|
|
13012
|
+
/** {@link removeItem} for several items at once. */
|
|
13013
|
+
removeItems(items) {
|
|
13014
|
+
return Promise.all([...items].map((item) => this.removeItem(item))).then(() => void 0);
|
|
12266
13015
|
}
|
|
12267
13016
|
clearItems() {
|
|
12268
13017
|
this.replaceChildren();
|
|
@@ -12308,6 +13057,40 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12308
13057
|
selectAll() {
|
|
12309
13058
|
if (this.selection.canSelectAll()) this.selection.selectAll();
|
|
12310
13059
|
}
|
|
13060
|
+
/** The selection as stable `data-key` / `data-value` strings. */
|
|
13061
|
+
get selectedKeys() {
|
|
13062
|
+
return this.selection.selectedKeys;
|
|
13063
|
+
}
|
|
13064
|
+
/** The key of the item under the cursor, or `undefined`. */
|
|
13065
|
+
get tickledKey() {
|
|
13066
|
+
const id = this.tickled.tickledId;
|
|
13067
|
+
return id ? keyForElement(id) : void 0;
|
|
13068
|
+
}
|
|
13069
|
+
/** Replace the selection with the items carrying these keys (unknown keys ignored). */
|
|
13070
|
+
selectKeys(keys) {
|
|
13071
|
+
this.selection.selectKeys(keys, (key) => this.#itemForKey(key));
|
|
13072
|
+
}
|
|
13073
|
+
/** Add items by key to the selection (multi-select modes only). */
|
|
13074
|
+
addSelectedKeys(keys) {
|
|
13075
|
+
this.selection.addKeys(keys, (key) => this.#itemForKey(key));
|
|
13076
|
+
}
|
|
13077
|
+
/** Remove items by key from the selection. */
|
|
13078
|
+
removeSelectedKeys(keys) {
|
|
13079
|
+
this.selection.removeKeys(keys, (key) => this.#itemForKey(key));
|
|
13080
|
+
}
|
|
13081
|
+
/** Move the cursor to the item with this key and scroll it into view. */
|
|
13082
|
+
focusKey(key) {
|
|
13083
|
+
const el = this.#itemForKey(key);
|
|
13084
|
+
if (!el) return;
|
|
13085
|
+
this.tickled.focus(el);
|
|
13086
|
+
el.scrollIntoView({
|
|
13087
|
+
block: `nearest`,
|
|
13088
|
+
inline: `nearest`
|
|
13089
|
+
});
|
|
13090
|
+
}
|
|
13091
|
+
#itemForKey(key) {
|
|
13092
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$2)) if (keyForElement(el) === key) return el;
|
|
13093
|
+
}
|
|
12311
13094
|
*selectedFilter(kind) {
|
|
12312
13095
|
const wanted = (el) => {
|
|
12313
13096
|
switch (kind) {
|
|
@@ -12316,7 +13099,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12316
13099
|
case `either`: return el.hasAttribute(`data-selected`) || el.hasAttribute(`data-checked`);
|
|
12317
13100
|
}
|
|
12318
13101
|
};
|
|
12319
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13102
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$2)) {
|
|
12320
13103
|
if (el.hidden) continue;
|
|
12321
13104
|
if (wanted(el)) yield el;
|
|
12322
13105
|
}
|
|
@@ -12329,7 +13112,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12329
13112
|
if (previousKeys.size > 0 || anchorKey !== null) {
|
|
12330
13113
|
const next = [];
|
|
12331
13114
|
let anchor;
|
|
12332
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13115
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$2)) {
|
|
12333
13116
|
const key = keyOf(el);
|
|
12334
13117
|
if (key !== null && previousKeys.has(key)) next.push(el);
|
|
12335
13118
|
if (key !== null && key === anchorKey) anchor = el;
|
|
@@ -12384,15 +13167,15 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12384
13167
|
}
|
|
12385
13168
|
#applyFilterPredicate() {
|
|
12386
13169
|
if (!this.filterPredicate) {
|
|
12387
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13170
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$2)) el.removeAttribute(`hidden`);
|
|
12388
13171
|
return;
|
|
12389
13172
|
}
|
|
12390
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13173
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$2)) if (this.filterPredicate(el)) el.removeAttribute(`hidden`);
|
|
12391
13174
|
else el.setAttribute(`hidden`, ``);
|
|
12392
13175
|
}
|
|
12393
13176
|
#applySort() {
|
|
12394
13177
|
if (this.sort === `none`) return;
|
|
12395
|
-
const rows = [...this.querySelectorAll(ITEM_SELECTOR$
|
|
13178
|
+
const rows = [...this.querySelectorAll(ITEM_SELECTOR$2)];
|
|
12396
13179
|
if (rows.length < 2) return;
|
|
12397
13180
|
const dir = this.sort === `descending` ? -1 : 1;
|
|
12398
13181
|
const meta = (el) => ({
|
|
@@ -12584,7 +13367,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12584
13367
|
else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findLastCheckable());
|
|
12585
13368
|
}
|
|
12586
13369
|
_getVisibleItems() {
|
|
12587
|
-
return Array.from(this.querySelectorAll(ITEM_SELECTOR$
|
|
13370
|
+
return Array.from(this.querySelectorAll(ITEM_SELECTOR$2)).filter((el) => {
|
|
12588
13371
|
if (el.hidden) return false;
|
|
12589
13372
|
if (this.filterPredicate && !this.filterPredicate(el)) return false;
|
|
12590
13373
|
return true;
|
|
@@ -12639,10 +13422,10 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
12639
13422
|
}
|
|
12640
13423
|
/** Scan light-DOM children for unprocessed `data-icon` items. */
|
|
12641
13424
|
#processChildren() {
|
|
12642
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13425
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$2)) this.#ensureIcon(el);
|
|
12643
13426
|
}
|
|
12644
13427
|
_syncItemStates() {
|
|
12645
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13428
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$2)) {
|
|
12646
13429
|
el.toggleAttribute(`data-tickled`, this.tickled.isTickled(el));
|
|
12647
13430
|
el.toggleAttribute(`data-selected`, this.selection.selected.has(el));
|
|
12648
13431
|
if (this.interactionMode === `checked`) el.toggleAttribute(`data-checked`, !el.hasAttribute(`not-checked`) && this.selection.selected.has(el));
|
|
@@ -18701,6 +19484,924 @@ let FormElement = class FormElement extends i {
|
|
|
18701
19484
|
};
|
|
18702
19485
|
FormElement = __decorate([safeCustomElement(`ixfx-form`)], FormElement);
|
|
18703
19486
|
//#endregion
|
|
19487
|
+
//#region src/grid-list/grid-list.ts
|
|
19488
|
+
const ITEM_SELECTOR$1 = `:scope > *`;
|
|
19489
|
+
const MIN_ITEM_SIZE = 16;
|
|
19490
|
+
/**
|
|
19491
|
+
* How long the host keeps the `[reflowing]` attribute after items are inserted
|
|
19492
|
+
* mid-list — long enough for the cell move-transition and the enter animation
|
|
19493
|
+
* to finish. Kept a touch above the default `--grid-list-item-move-duration`.
|
|
19494
|
+
*/
|
|
19495
|
+
const REFLOW_WINDOW_MS = 320;
|
|
19496
|
+
function computeGridWindow(count, itemWidth, rowHeight, gap, viewportWidth, viewportHeight, scrollOffset, orientation, overscan = 2) {
|
|
19497
|
+
const w = Math.max(1, itemWidth);
|
|
19498
|
+
const h = Math.max(1, rowHeight);
|
|
19499
|
+
const g = Math.max(0, gap);
|
|
19500
|
+
const colStride = w + g;
|
|
19501
|
+
const rowStride = h + g;
|
|
19502
|
+
const offset = Math.max(0, scrollOffset);
|
|
19503
|
+
if (orientation === `row`) {
|
|
19504
|
+
const rows = count === 0 ? 0 : 1;
|
|
19505
|
+
const totalWidth = count === 0 ? 0 : (count - 1) * colStride + w;
|
|
19506
|
+
if (count === 0 || viewportWidth <= 0) return {
|
|
19507
|
+
cols: count,
|
|
19508
|
+
rows,
|
|
19509
|
+
totalHeight: 0,
|
|
19510
|
+
totalWidth,
|
|
19511
|
+
startIndex: 0,
|
|
19512
|
+
endIndex: 0,
|
|
19513
|
+
colStride,
|
|
19514
|
+
rowStride,
|
|
19515
|
+
horizontal: true
|
|
19516
|
+
};
|
|
19517
|
+
const firstVisible = Math.max(0, Math.floor(offset / colStride));
|
|
19518
|
+
const lastVisible = Math.ceil((offset + viewportWidth) / colStride);
|
|
19519
|
+
return {
|
|
19520
|
+
cols: count,
|
|
19521
|
+
rows,
|
|
19522
|
+
totalHeight: 0,
|
|
19523
|
+
totalWidth,
|
|
19524
|
+
startIndex: Math.max(0, firstVisible - overscan),
|
|
19525
|
+
endIndex: Math.min(count, lastVisible + overscan),
|
|
19526
|
+
colStride,
|
|
19527
|
+
rowStride,
|
|
19528
|
+
horizontal: true
|
|
19529
|
+
};
|
|
19530
|
+
}
|
|
19531
|
+
const cols = orientation === `column` ? 1 : Math.max(1, Math.floor((Math.max(0, viewportWidth) + g) / colStride));
|
|
19532
|
+
const rows = count === 0 ? 0 : Math.ceil(count / cols);
|
|
19533
|
+
const totalHeight = rows === 0 ? 0 : (rows - 1) * rowStride + h;
|
|
19534
|
+
if (count === 0 || viewportHeight <= 0) return {
|
|
19535
|
+
cols,
|
|
19536
|
+
rows,
|
|
19537
|
+
totalHeight,
|
|
19538
|
+
totalWidth: 0,
|
|
19539
|
+
startIndex: 0,
|
|
19540
|
+
endIndex: 0,
|
|
19541
|
+
colStride,
|
|
19542
|
+
rowStride,
|
|
19543
|
+
horizontal: false
|
|
19544
|
+
};
|
|
19545
|
+
const firstVisibleRow = Math.max(0, Math.floor(offset / rowStride));
|
|
19546
|
+
const lastVisibleRow = Math.ceil((offset + viewportHeight) / rowStride);
|
|
19547
|
+
const rowStart = Math.max(0, firstVisibleRow - overscan);
|
|
19548
|
+
const rowEnd = Math.min(rows, lastVisibleRow + overscan);
|
|
19549
|
+
return {
|
|
19550
|
+
cols,
|
|
19551
|
+
rows,
|
|
19552
|
+
totalHeight,
|
|
19553
|
+
totalWidth: 0,
|
|
19554
|
+
startIndex: rowStart * cols,
|
|
19555
|
+
endIndex: Math.min(count, rowEnd * cols),
|
|
19556
|
+
colStride,
|
|
19557
|
+
rowStride,
|
|
19558
|
+
horizontal: false
|
|
19559
|
+
};
|
|
19560
|
+
}
|
|
19561
|
+
function columnOf(index, cols) {
|
|
19562
|
+
return cols <= 1 ? 0 : index % cols;
|
|
19563
|
+
}
|
|
19564
|
+
function rowOf(index, cols) {
|
|
19565
|
+
return cols <= 1 ? index : Math.floor(index / cols);
|
|
19566
|
+
}
|
|
19567
|
+
let GridListElement = class GridListElement extends i {
|
|
19568
|
+
constructor(..._args) {
|
|
19569
|
+
super(..._args);
|
|
19570
|
+
this.selectionMode = `single`;
|
|
19571
|
+
this.interactionMode = `standard`;
|
|
19572
|
+
this.itemSize = 160;
|
|
19573
|
+
this.gap = 8;
|
|
19574
|
+
this.orientation = `grid`;
|
|
19575
|
+
this.captionStyle = `under`;
|
|
19576
|
+
this.captionAlign = `middle`;
|
|
19577
|
+
this.captionPosition = `bottom`;
|
|
19578
|
+
this.captionBackdrop = `scrim`;
|
|
19579
|
+
this.captionHeight = 24;
|
|
19580
|
+
this.itemWrap = `stop`;
|
|
19581
|
+
this._scrollTop = 0;
|
|
19582
|
+
this._scrollLeft = 0;
|
|
19583
|
+
this._viewportHeight = 0;
|
|
19584
|
+
this._viewportWidth = 0;
|
|
19585
|
+
this.#viewportRef = createRef();
|
|
19586
|
+
this.#scrollRaf = 0;
|
|
19587
|
+
this.#items = [];
|
|
19588
|
+
this.#itemWrap = `stop`;
|
|
19589
|
+
this.#entering = /* @__PURE__ */ new Set();
|
|
19590
|
+
this.#reflowTimer = 0;
|
|
19591
|
+
this.#slotNames = /* @__PURE__ */ new WeakMap();
|
|
19592
|
+
this.#slotSeq = 0;
|
|
19593
|
+
this.selection = new ListSelectionController({
|
|
19594
|
+
getOrderedItems: () => this._getVisibleItems(),
|
|
19595
|
+
isSelectable: () => true,
|
|
19596
|
+
getModes: () => ({
|
|
19597
|
+
selectionMode: this.selectionMode,
|
|
19598
|
+
interactionMode: this.interactionMode
|
|
19599
|
+
}),
|
|
19600
|
+
getKey: keyForElement,
|
|
19601
|
+
onChange: (selected, previous) => {
|
|
19602
|
+
this._syncItemStates();
|
|
19603
|
+
this.requestUpdate();
|
|
19604
|
+
this._dispatch(`list-select`, {
|
|
19605
|
+
selected,
|
|
19606
|
+
previous,
|
|
19607
|
+
selectedKeys: this.selection.selectedKeys
|
|
19608
|
+
});
|
|
19609
|
+
}
|
|
19610
|
+
});
|
|
19611
|
+
this.tickled = new TickledController(this, {
|
|
19612
|
+
getItems: () => this.#items.map((el) => ({
|
|
19613
|
+
id: el,
|
|
19614
|
+
enabled: !el.hasAttribute(`not-checked`)
|
|
19615
|
+
})),
|
|
19616
|
+
onTickle: (id) => {
|
|
19617
|
+
if (id !== void 0) this._dispatch(`list-tickle`, { item: id });
|
|
19618
|
+
},
|
|
19619
|
+
onStateChange: () => this._syncItemStates(),
|
|
19620
|
+
itemWrap: this.#itemWrap
|
|
19621
|
+
});
|
|
19622
|
+
this.#onScroll = (e) => {
|
|
19623
|
+
const el = e.target;
|
|
19624
|
+
this.#cancelScrollRaf();
|
|
19625
|
+
this.#scrollRaf = requestAnimationFrame(() => {
|
|
19626
|
+
this.#scrollRaf = 0;
|
|
19627
|
+
this._scrollTop = el.scrollTop;
|
|
19628
|
+
this._scrollLeft = el.scrollLeft;
|
|
19629
|
+
this.#emitViewportChange();
|
|
19630
|
+
});
|
|
19631
|
+
};
|
|
19632
|
+
this._onKeyDown = (e) => {
|
|
19633
|
+
if ((e.ctrlKey || e.metaKey) && e.key === `f`) return;
|
|
19634
|
+
if ((e.ctrlKey || e.metaKey) && e.key === `a`) {
|
|
19635
|
+
if (this.selection.canSelectAll()) {
|
|
19636
|
+
e.preventDefault();
|
|
19637
|
+
this.selection.selectAll();
|
|
19638
|
+
}
|
|
19639
|
+
return;
|
|
19640
|
+
}
|
|
19641
|
+
const count = this.#items.length;
|
|
19642
|
+
if (count === 0) return;
|
|
19643
|
+
const win = this.#currentWindow();
|
|
19644
|
+
const move = (delta) => {
|
|
19645
|
+
const from = this.tickled.tickledId ? this.#items.indexOf(this.tickled.tickledId) : -1;
|
|
19646
|
+
const nextIndex = Math.max(0, Math.min(count - 1, from + delta));
|
|
19647
|
+
const next = this.#items[nextIndex];
|
|
19648
|
+
if (next) this.tickled.handleKeyboardNavigation(next);
|
|
19649
|
+
};
|
|
19650
|
+
const perLine = this.orientation === `grid` ? win.cols : 1;
|
|
19651
|
+
switch (e.key) {
|
|
19652
|
+
case `ArrowLeft`:
|
|
19653
|
+
move(-1);
|
|
19654
|
+
break;
|
|
19655
|
+
case `ArrowRight`:
|
|
19656
|
+
move(1);
|
|
19657
|
+
break;
|
|
19658
|
+
case `ArrowUp`:
|
|
19659
|
+
move(-perLine);
|
|
19660
|
+
break;
|
|
19661
|
+
case `ArrowDown`:
|
|
19662
|
+
move(perLine);
|
|
19663
|
+
break;
|
|
19664
|
+
case `Home`:
|
|
19665
|
+
this.tickled.handleKeyboardNavigation(`first`);
|
|
19666
|
+
break;
|
|
19667
|
+
case `End`:
|
|
19668
|
+
this.tickled.handleKeyboardNavigation(`last`);
|
|
19669
|
+
break;
|
|
19670
|
+
case `PageUp`:
|
|
19671
|
+
case `PageDown`: {
|
|
19672
|
+
const mainExtent = win.horizontal ? this._viewportWidth : this._viewportHeight;
|
|
19673
|
+
const mainStride = win.horizontal ? win.colStride : win.rowStride;
|
|
19674
|
+
const visibleLines = Math.max(1, Math.floor(mainExtent / mainStride));
|
|
19675
|
+
move((e.key === `PageDown` ? 1 : -1) * visibleLines * perLine);
|
|
19676
|
+
break;
|
|
19677
|
+
}
|
|
19678
|
+
case `Enter`: {
|
|
19679
|
+
const id = this.tickled.tickledId;
|
|
19680
|
+
if (id) {
|
|
19681
|
+
e.preventDefault();
|
|
19682
|
+
this._dispatch(`list-item-click`, { item: id });
|
|
19683
|
+
this.#onCellActivate(id);
|
|
19684
|
+
}
|
|
19685
|
+
break;
|
|
19686
|
+
}
|
|
19687
|
+
case ` `: {
|
|
19688
|
+
const id = this.tickled.tickledId;
|
|
19689
|
+
if (id) {
|
|
19690
|
+
e.preventDefault();
|
|
19691
|
+
this.selection.handleSpace(id);
|
|
19692
|
+
}
|
|
19693
|
+
break;
|
|
19694
|
+
}
|
|
19695
|
+
case `Escape`:
|
|
19696
|
+
this.selection.clear();
|
|
19697
|
+
break;
|
|
19698
|
+
default: return;
|
|
19699
|
+
}
|
|
19700
|
+
e.preventDefault();
|
|
19701
|
+
};
|
|
19702
|
+
}
|
|
19703
|
+
static {
|
|
19704
|
+
this.styles = [
|
|
19705
|
+
themeFallbacks,
|
|
19706
|
+
tickledStyles,
|
|
19707
|
+
tickledItemStyles,
|
|
19708
|
+
scrollFadeStyles(`x`),
|
|
19709
|
+
scrollFadeStyles(`y`),
|
|
19710
|
+
listItemFadeKeyframes,
|
|
19711
|
+
i$3`
|
|
19712
|
+
:host {
|
|
19713
|
+
--grid-list-item-bg: var(--item-bg);
|
|
19714
|
+
--grid-list-radius: var(--radius-m);
|
|
19715
|
+
--grid-list-gap: var(--space-xs);
|
|
19716
|
+
display: block;
|
|
19717
|
+
height: 100%;
|
|
19718
|
+
min-height: 2em;
|
|
19719
|
+
overflow: hidden;
|
|
19720
|
+
user-select: none;
|
|
19721
|
+
background: var(--surface-3);
|
|
19722
|
+
color: var(--surface-3-text);
|
|
19723
|
+
border-radius: var(--radius-s);
|
|
19724
|
+
border: 1px solid var(--border);
|
|
19725
|
+
}
|
|
19726
|
+
:host(:focus-visible) {
|
|
19727
|
+
outline: 2px solid var(--accent);
|
|
19728
|
+
outline-offset: -2px;
|
|
19729
|
+
}
|
|
19730
|
+
.viewport {
|
|
19731
|
+
position: relative;
|
|
19732
|
+
height: 100%;
|
|
19733
|
+
overflow-y: auto;
|
|
19734
|
+
overflow-x: hidden;
|
|
19735
|
+
}
|
|
19736
|
+
:host([orientation="row"]) .viewport {
|
|
19737
|
+
overflow-x: auto;
|
|
19738
|
+
overflow-y: hidden;
|
|
19739
|
+
}
|
|
19740
|
+
.spacer {
|
|
19741
|
+
position: relative;
|
|
19742
|
+
width: 100%;
|
|
19743
|
+
}
|
|
19744
|
+
.cell {
|
|
19745
|
+
position: absolute;
|
|
19746
|
+
inset: 0 auto auto 0;
|
|
19747
|
+
box-sizing: border-box;
|
|
19748
|
+
display: flex;
|
|
19749
|
+
flex-direction: column;
|
|
19750
|
+
align-items: stretch;
|
|
19751
|
+
contain: layout style;
|
|
19752
|
+
border-radius: var(--grid-list-radius);
|
|
19753
|
+
}
|
|
19754
|
+
|
|
19755
|
+
/* The square that holds the slotted content. */
|
|
19756
|
+
.thumb {
|
|
19757
|
+
position: relative;
|
|
19758
|
+
flex: 1;
|
|
19759
|
+
min-height: 0;
|
|
19760
|
+
display: flex;
|
|
19761
|
+
align-items: stretch;
|
|
19762
|
+
box-sizing: border-box;
|
|
19763
|
+
overflow: hidden;
|
|
19764
|
+
border-radius: 0;
|
|
19765
|
+
background: transparent;
|
|
19766
|
+
border: 1px solid transparent;
|
|
19767
|
+
transition: background var(--transition), border-color var(--transition);
|
|
19768
|
+
}
|
|
19769
|
+
.thumb ::slotted(*) {
|
|
19770
|
+
flex: 1;
|
|
19771
|
+
min-width: 0;
|
|
19772
|
+
min-height: 0;
|
|
19773
|
+
width: 100%;
|
|
19774
|
+
display: flex;
|
|
19775
|
+
align-items: center;
|
|
19776
|
+
justify-content: center;
|
|
19777
|
+
overflow: hidden;
|
|
19778
|
+
}
|
|
19779
|
+
.thumb ::slotted(*:empty)::after {
|
|
19780
|
+
content: "Empty";
|
|
19781
|
+
color: var(--surface-muted-text);
|
|
19782
|
+
font-size: var(--text-s);
|
|
19783
|
+
}
|
|
19784
|
+
::slotted([hidden]) {
|
|
19785
|
+
display: none !important;
|
|
19786
|
+
}
|
|
19787
|
+
|
|
19788
|
+
/* Selection / cursor wash — sits over the slotted content so it reads
|
|
19789
|
+
on photos and arbitrary DOM alike. */
|
|
19790
|
+
.thumb::after {
|
|
19791
|
+
content: "";
|
|
19792
|
+
position: absolute;
|
|
19793
|
+
inset: 0;
|
|
19794
|
+
pointer-events: none;
|
|
19795
|
+
border-radius: inherit;
|
|
19796
|
+
background: var(--accent);
|
|
19797
|
+
opacity: 0;
|
|
19798
|
+
transition: opacity var(--transition, 0.1s ease-out);
|
|
19799
|
+
}
|
|
19800
|
+
.cell.selected .thumb::after {
|
|
19801
|
+
opacity: 0.18;
|
|
19802
|
+
}
|
|
19803
|
+
|
|
19804
|
+
/* ---- Caption (base) ---- */
|
|
19805
|
+
.caption {
|
|
19806
|
+
box-sizing: border-box;
|
|
19807
|
+
flex: none;
|
|
19808
|
+
padding: 3px 6px;
|
|
19809
|
+
font-size: var(--text-s, 0.8rem);
|
|
19810
|
+
line-height: 1.3;
|
|
19811
|
+
text-align: center;
|
|
19812
|
+
white-space: nowrap;
|
|
19813
|
+
overflow: hidden;
|
|
19814
|
+
text-overflow: ellipsis;
|
|
19815
|
+
color: var(--surface-3-text);
|
|
19816
|
+
}
|
|
19817
|
+
:host([caption-align="left"]) .caption { text-align: left; }
|
|
19818
|
+
:host([caption-align="right"]) .caption { text-align: right; }
|
|
19819
|
+
|
|
19820
|
+
/* ---- Box modes (hidden / under / overlaid): the cell is one box with a
|
|
19821
|
+
single selection outline ---- */
|
|
19822
|
+
:host(:not([caption-style="separate"])) .cell {
|
|
19823
|
+
background: var(--grid-list-item-bg);
|
|
19824
|
+
border: 1px solid transparent;
|
|
19825
|
+
overflow: hidden;
|
|
19826
|
+
transition: background var(--transition), border-color var(--transition);
|
|
19827
|
+
}
|
|
19828
|
+
:host(:not([caption-style="separate"])) .cell:hover {
|
|
19829
|
+
background: var(--item-bg-tickled);
|
|
19830
|
+
border-color: var(--item-border-tickled);
|
|
19831
|
+
}
|
|
19832
|
+
:host(:not([caption-style="separate"])) .cell.selected {
|
|
19833
|
+
outline: 2px solid var(--accent);
|
|
19834
|
+
outline-offset: -2px;
|
|
19835
|
+
}
|
|
19836
|
+
:host(:not([caption-style="separate"])) .cell.tickled {
|
|
19837
|
+
box-shadow: inset 0 0 0 2px var(--accent);
|
|
19838
|
+
}
|
|
19839
|
+
|
|
19840
|
+
/* under: caption is a strip inside the box, below the content */
|
|
19841
|
+
:host([caption-style="under"]) .caption {
|
|
19842
|
+
border-top: 1px solid var(--border, rgba(128, 128, 128, 0.3));
|
|
19843
|
+
background: var(--surface-4, transparent);
|
|
19844
|
+
}
|
|
19845
|
+
|
|
19846
|
+
/* overlaid: caption floats over the content */
|
|
19847
|
+
:host([caption-style="overlaid"]) .caption {
|
|
19848
|
+
position: absolute;
|
|
19849
|
+
left: 0;
|
|
19850
|
+
right: 0;
|
|
19851
|
+
bottom: 0;
|
|
19852
|
+
color: #fff;
|
|
19853
|
+
}
|
|
19854
|
+
:host([caption-style="overlaid"][caption-position="top"]) .caption {
|
|
19855
|
+
top: 0;
|
|
19856
|
+
bottom: auto;
|
|
19857
|
+
}
|
|
19858
|
+
:host([caption-style="overlaid"][caption-position="middle"]) .caption {
|
|
19859
|
+
top: 50%;
|
|
19860
|
+
bottom: auto;
|
|
19861
|
+
transform: translateY(-50%);
|
|
19862
|
+
}
|
|
19863
|
+
/* overlaid backdrop */
|
|
19864
|
+
:host([caption-style="overlaid"][caption-backdrop="scrim"]) .caption {
|
|
19865
|
+
background: rgba(0, 0, 0, 0.55);
|
|
19866
|
+
}
|
|
19867
|
+
:host([caption-style="overlaid"][caption-backdrop="gradient"]) .caption {
|
|
19868
|
+
background: linear-gradient(to top, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));
|
|
19869
|
+
padding-block: 10px 4px;
|
|
19870
|
+
}
|
|
19871
|
+
:host([caption-style="overlaid"][caption-backdrop="gradient"][caption-position="top"]) .caption {
|
|
19872
|
+
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));
|
|
19873
|
+
padding-block: 4px 10px;
|
|
19874
|
+
}
|
|
19875
|
+
:host([caption-style="overlaid"][caption-backdrop="gradient"][caption-position="middle"]) .caption {
|
|
19876
|
+
background: rgba(0, 0, 0, 0.5);
|
|
19877
|
+
padding-block: 3px;
|
|
19878
|
+
}
|
|
19879
|
+
:host([caption-style="overlaid"][caption-backdrop="blur"]) .caption {
|
|
19880
|
+
background: rgba(20, 20, 20, 0.3);
|
|
19881
|
+
backdrop-filter: blur(6px);
|
|
19882
|
+
-webkit-backdrop-filter: blur(6px);
|
|
19883
|
+
}
|
|
19884
|
+
:host([caption-style="overlaid"][caption-backdrop="none"]) .caption {
|
|
19885
|
+
background: none;
|
|
19886
|
+
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95), 0 0 6px rgba(0, 0, 0, 0.7);
|
|
19887
|
+
}
|
|
19888
|
+
|
|
19889
|
+
/* ---- separate mode: square + detached caption, each highlighted ---- */
|
|
19890
|
+
:host([caption-style="separate"]) .thumb {
|
|
19891
|
+
flex: none;
|
|
19892
|
+
border-radius: var(--grid-list-radius);
|
|
19893
|
+
background: var(--grid-list-item-bg);
|
|
19894
|
+
border: 1px solid transparent;
|
|
19895
|
+
}
|
|
19896
|
+
:host([caption-style="separate"]) .cell:hover .thumb {
|
|
19897
|
+
background: var(--item-bg-tickled);
|
|
19898
|
+
border-color: var(--item-border-tickled);
|
|
19899
|
+
}
|
|
19900
|
+
:host([caption-style="separate"]) .caption {
|
|
19901
|
+
display: flex;
|
|
19902
|
+
align-items: center;
|
|
19903
|
+
align-self: center;
|
|
19904
|
+
max-width: 100%;
|
|
19905
|
+
margin-top: 4px;
|
|
19906
|
+
border-radius: var(--radius-s, 4px);
|
|
19907
|
+
}
|
|
19908
|
+
:host([caption-style="separate"][caption-align="left"]) .caption { align-self: flex-start; }
|
|
19909
|
+
:host([caption-style="separate"][caption-align="right"]) .caption { align-self: flex-end; }
|
|
19910
|
+
:host([caption-style="separate"]) .cell.selected .thumb,
|
|
19911
|
+
:host([caption-style="separate"]) .cell.tickled .thumb {
|
|
19912
|
+
outline: 2px solid var(--accent);
|
|
19913
|
+
outline-offset: 2px;
|
|
19914
|
+
}
|
|
19915
|
+
:host([caption-style="separate"]) .cell.tickled .thumb {
|
|
19916
|
+
outline-style: dashed;
|
|
19917
|
+
}
|
|
19918
|
+
:host([caption-style="separate"]) .cell.selected .caption {
|
|
19919
|
+
background: var(--accent);
|
|
19920
|
+
color: var(--accent-text, #000);
|
|
19921
|
+
}
|
|
19922
|
+
:host([caption-style="separate"]) .cell.tickled .caption {
|
|
19923
|
+
outline: 1px dashed var(--accent);
|
|
19924
|
+
}
|
|
19925
|
+
|
|
19926
|
+
/* ---- Insertion / removal animation ----
|
|
19927
|
+
Active only while the host carries [reflowing] — the ~0.3s window after
|
|
19928
|
+
the child list changes mid-list (see #noteChildMutation). Kept out of
|
|
19929
|
+
the base rules so it never fires during scroll virtualisation, where
|
|
19930
|
+
cells are recycled to wildly different positions every frame. Placed
|
|
19931
|
+
last so it wins over the earlier cell transition rule regardless of
|
|
19932
|
+
specificity. Survivors slide to their new slot; a freshly inserted cell
|
|
19933
|
+
also fades in. (A removed item's element is already gone, so there's
|
|
19934
|
+
nothing left to fade — the gap simply closes as the others slide.) */
|
|
19935
|
+
:host([reflowing]) .cell {
|
|
19936
|
+
transition:
|
|
19937
|
+
transform var(--grid-list-item-move-duration, 200ms) cubic-bezier(0.2, 0, 0, 1),
|
|
19938
|
+
background var(--transition),
|
|
19939
|
+
border-color var(--transition);
|
|
19940
|
+
}
|
|
19941
|
+
.cell.entering {
|
|
19942
|
+
animation: ixfx-list-item-in var(--grid-list-item-move-duration, 200ms) ease both;
|
|
19943
|
+
}
|
|
19944
|
+
@media (prefers-reduced-motion: reduce) {
|
|
19945
|
+
:host([reflowing]) .cell { transition: background var(--transition), border-color var(--transition); }
|
|
19946
|
+
.cell.entering { animation: none; }
|
|
19947
|
+
}
|
|
19948
|
+
`
|
|
19949
|
+
];
|
|
19950
|
+
}
|
|
19951
|
+
#viewportRef;
|
|
19952
|
+
#resizeObserver;
|
|
19953
|
+
#mutationObserver;
|
|
19954
|
+
#scrollRaf;
|
|
19955
|
+
#items;
|
|
19956
|
+
#itemWrap;
|
|
19957
|
+
/** Items added mid-list that are still playing their enter animation. */
|
|
19958
|
+
#entering;
|
|
19959
|
+
/** Timer that ends the `[reflowing]` window opened by a mid-list insertion. */
|
|
19960
|
+
#reflowTimer;
|
|
19961
|
+
/**
|
|
19962
|
+
* When cell geometry changes (zoom), the item nearest the viewport centre is
|
|
19963
|
+
* captured here in `willUpdate` and the scroll position is corrected in
|
|
19964
|
+
* `updated` so that item stays put — rather than the grid anchoring to its
|
|
19965
|
+
* top-left corner.
|
|
19966
|
+
*/
|
|
19967
|
+
#zoomAnchor;
|
|
19968
|
+
/**
|
|
19969
|
+
* Per-item slot name. Each cell renders its own named `<slot>` and the
|
|
19970
|
+
* matching light-DOM child carries `slot="<name>"`, so a child is projected
|
|
19971
|
+
* into exactly one cell. (A single unnamed slot would collapse every child
|
|
19972
|
+
* into the first cell.)
|
|
19973
|
+
*/
|
|
19974
|
+
#slotNames;
|
|
19975
|
+
#slotSeq;
|
|
19976
|
+
connectedCallback() {
|
|
19977
|
+
super.connectedCallback();
|
|
19978
|
+
if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
|
|
19979
|
+
for (const [attr, value] of [
|
|
19980
|
+
[`caption-style`, this.captionStyle],
|
|
19981
|
+
[`caption-align`, this.captionAlign],
|
|
19982
|
+
[`caption-position`, this.captionPosition],
|
|
19983
|
+
[`caption-backdrop`, this.captionBackdrop]
|
|
19984
|
+
]) if (!this.hasAttribute(attr)) this.setAttribute(attr, value);
|
|
19985
|
+
this.addEventListener(`keydown`, this._onKeyDown);
|
|
19986
|
+
this.#mutationObserver ??= new MutationObserver((records) => this.#onChildrenChanged(records));
|
|
19987
|
+
this.#mutationObserver.observe(this, { childList: true });
|
|
19988
|
+
}
|
|
19989
|
+
disconnectedCallback() {
|
|
19990
|
+
super.disconnectedCallback();
|
|
19991
|
+
this.removeEventListener(`keydown`, this._onKeyDown);
|
|
19992
|
+
this.#resizeObserver?.disconnect();
|
|
19993
|
+
this.#resizeObserver = void 0;
|
|
19994
|
+
this.#mutationObserver?.disconnect();
|
|
19995
|
+
this.#cancelScrollRaf();
|
|
19996
|
+
if (this.#reflowTimer) {
|
|
19997
|
+
clearTimeout(this.#reflowTimer);
|
|
19998
|
+
this.#reflowTimer = 0;
|
|
19999
|
+
}
|
|
20000
|
+
}
|
|
20001
|
+
#onChildrenChanged(records) {
|
|
20002
|
+
const previousLength = this.#items.length;
|
|
20003
|
+
this.#syncItems();
|
|
20004
|
+
this._syncItemStates();
|
|
20005
|
+
this.#noteChildMutation(previousLength, records);
|
|
20006
|
+
this.requestUpdate();
|
|
20007
|
+
this.updateComplete.then(() => this.#emitViewportChange());
|
|
20008
|
+
}
|
|
20009
|
+
/**
|
|
20010
|
+
* When the child list changes mid-list — not a first populate or a wholesale
|
|
20011
|
+
* replace — mark any newcomers for an enter animation and open a short
|
|
20012
|
+
* `[reflowing]` window so the cells that shift slide to their new position
|
|
20013
|
+
* instead of snapping. Removals get the same window (the survivors slide to
|
|
20014
|
+
* close the gap). The window is bounded in time so the transform transition
|
|
20015
|
+
* never bleeds into scroll virtualisation.
|
|
20016
|
+
*
|
|
20017
|
+
* Reads the `MutationRecord`s directly rather than diffing `#items`, because a
|
|
20018
|
+
* `requestUpdate()` between the DOM change and this callback (e.g. a caller
|
|
20019
|
+
* that clears the selection before removing) lets `updated()` re-sync `#items`
|
|
20020
|
+
* first, which would hide the change from a naive diff.
|
|
20021
|
+
*/
|
|
20022
|
+
#noteChildMutation(previousLength, records) {
|
|
20023
|
+
if (!this.hasUpdated || !records || records.length === 0) return;
|
|
20024
|
+
const currentSet = new Set(this.#items);
|
|
20025
|
+
const added = [];
|
|
20026
|
+
let removedCount = 0;
|
|
20027
|
+
for (const record of records) {
|
|
20028
|
+
for (const node of record.addedNodes) if (node.nodeType === Node.ELEMENT_NODE && currentSet.has(node)) added.push(node);
|
|
20029
|
+
for (const node of record.removedNodes) if (node.nodeType === Node.ELEMENT_NODE) removedCount++;
|
|
20030
|
+
}
|
|
20031
|
+
if (added.length === 0 && removedCount === 0) return;
|
|
20032
|
+
if (previousLength === 0 && removedCount === 0) return;
|
|
20033
|
+
if (added.length > 0 && added.length === this.#items.length) return;
|
|
20034
|
+
for (const el of added) this.#entering.add(el);
|
|
20035
|
+
this.toggleAttribute(`reflowing`, true);
|
|
20036
|
+
if (this.#reflowTimer) clearTimeout(this.#reflowTimer);
|
|
20037
|
+
this.#reflowTimer = window.setTimeout(() => {
|
|
20038
|
+
this.#reflowTimer = 0;
|
|
20039
|
+
this.#entering.clear();
|
|
20040
|
+
this.toggleAttribute(`reflowing`, false);
|
|
20041
|
+
this.requestUpdate();
|
|
20042
|
+
}, REFLOW_WINDOW_MS);
|
|
20043
|
+
}
|
|
20044
|
+
#emitViewportChange() {
|
|
20045
|
+
this._dispatch(`grid-list-viewport-change`, { visibleKeys: this.visibleKeys() });
|
|
20046
|
+
}
|
|
20047
|
+
firstUpdated() {
|
|
20048
|
+
const el = this.#viewportRef.value;
|
|
20049
|
+
if (!el) return;
|
|
20050
|
+
this._viewportWidth = el.clientWidth;
|
|
20051
|
+
this._viewportHeight = el.clientHeight;
|
|
20052
|
+
this.#resizeObserver = new ResizeObserver((entries) => {
|
|
20053
|
+
for (const entry of entries) {
|
|
20054
|
+
this._viewportWidth = entry.contentRect.width;
|
|
20055
|
+
this._viewportHeight = entry.contentRect.height;
|
|
20056
|
+
}
|
|
20057
|
+
});
|
|
20058
|
+
this.#resizeObserver.observe(el);
|
|
20059
|
+
this.#syncItems();
|
|
20060
|
+
this.requestUpdate();
|
|
20061
|
+
}
|
|
20062
|
+
willUpdate(changed) {
|
|
20063
|
+
if (changed.has(`itemSize`) || changed.has(`gap`)) {
|
|
20064
|
+
this.itemSize = Math.max(MIN_ITEM_SIZE, Math.round(this.itemSize));
|
|
20065
|
+
this.gap = Math.max(0, Math.round(this.gap));
|
|
20066
|
+
}
|
|
20067
|
+
if (changed.has(`captionHeight`)) this.captionHeight = Math.max(0, Math.round(this.captionHeight));
|
|
20068
|
+
if (changed.has(`itemWrap`)) this.#itemWrap = this.itemWrap;
|
|
20069
|
+
if ((changed.has(`itemSize`) || changed.has(`gap`) || changed.has(`captionStyle`) || changed.has(`captionHeight`) || changed.has(`orientation`)) && this.hasUpdated && this.#items.length > 0) {
|
|
20070
|
+
const prevOrientation = changed.get(`orientation`) ?? this.orientation;
|
|
20071
|
+
const horizontal = prevOrientation === `row`;
|
|
20072
|
+
const prevOffset = horizontal ? this._scrollLeft : this._scrollTop;
|
|
20073
|
+
const prevExtent = horizontal ? this._viewportWidth : this._viewportHeight;
|
|
20074
|
+
if (prevExtent > 0 && prevOffset > 1) {
|
|
20075
|
+
const prevItemSize = changed.get(`itemSize`) ?? this.itemSize;
|
|
20076
|
+
const prevGap = changed.get(`gap`) ?? this.gap;
|
|
20077
|
+
const prevCaptionStyle = changed.get(`captionStyle`) ?? this.captionStyle;
|
|
20078
|
+
const prevCaptionHeight = changed.get(`captionHeight`) ?? this.captionHeight;
|
|
20079
|
+
const prevRowHeight = prevItemSize + (prevCaptionStyle === `separate` || prevCaptionStyle === `under` ? prevCaptionHeight : 0);
|
|
20080
|
+
const prev = computeGridWindow(this.#items.length, prevItemSize, prevRowHeight, prevGap, this._viewportWidth, this._viewportHeight, prevOffset, prevOrientation);
|
|
20081
|
+
const mainStride = horizontal ? prev.colStride : prev.rowStride;
|
|
20082
|
+
const centerLine = (prevOffset + prevExtent / 2) / mainStride;
|
|
20083
|
+
const lineFloor = Math.max(0, Math.floor(centerLine));
|
|
20084
|
+
const index = horizontal ? Math.min(this.#items.length - 1, lineFloor) : Math.min(this.#items.length - 1, lineFloor * prev.cols + Math.max(0, Math.floor((prev.cols - 1) / 2)));
|
|
20085
|
+
this.#zoomAnchor = {
|
|
20086
|
+
index,
|
|
20087
|
+
lineFrac: centerLine - lineFloor
|
|
20088
|
+
};
|
|
20089
|
+
}
|
|
20090
|
+
}
|
|
20091
|
+
}
|
|
20092
|
+
/** Full cell height: the square, plus a caption band in `separate` / `under`. */
|
|
20093
|
+
#rowHeight() {
|
|
20094
|
+
const captionBand = this.captionStyle === `separate` || this.captionStyle === `under`;
|
|
20095
|
+
return this.itemSize + (captionBand ? this.captionHeight : 0);
|
|
20096
|
+
}
|
|
20097
|
+
#captionFor(el) {
|
|
20098
|
+
return el.getAttribute(`data-label`) ?? el.querySelector(`[data-grid-list-caption]`)?.textContent?.trim() ?? ``;
|
|
20099
|
+
}
|
|
20100
|
+
updated(changed) {
|
|
20101
|
+
super.updated(changed);
|
|
20102
|
+
this.#syncItems();
|
|
20103
|
+
if (changed.has(`selectionMode`) || changed.has(`interactionMode`)) this.selection.clearAnchor();
|
|
20104
|
+
if (changed.has(`orientation`)) {
|
|
20105
|
+
const vp = this.#viewportRef.value;
|
|
20106
|
+
if (vp) {
|
|
20107
|
+
if (this.orientation === `row`) {
|
|
20108
|
+
vp.scrollTop = 0;
|
|
20109
|
+
this._scrollTop = 0;
|
|
20110
|
+
} else {
|
|
20111
|
+
vp.scrollLeft = 0;
|
|
20112
|
+
this._scrollLeft = 0;
|
|
20113
|
+
}
|
|
20114
|
+
}
|
|
20115
|
+
}
|
|
20116
|
+
this.#applyZoomAnchor();
|
|
20117
|
+
}
|
|
20118
|
+
/** Re-anchor the scroll so the item captured in `willUpdate` stays centred. */
|
|
20119
|
+
#applyZoomAnchor() {
|
|
20120
|
+
if (!this.#zoomAnchor) return;
|
|
20121
|
+
const { index, lineFrac } = this.#zoomAnchor;
|
|
20122
|
+
this.#zoomAnchor = void 0;
|
|
20123
|
+
const vp = this.#viewportRef.value;
|
|
20124
|
+
if (!vp) return;
|
|
20125
|
+
const win = this.#currentWindow();
|
|
20126
|
+
if (win.horizontal) {
|
|
20127
|
+
if (this._viewportWidth <= 0) return;
|
|
20128
|
+
const targetCenter = (index + lineFrac) * win.colStride;
|
|
20129
|
+
const maxScroll = Math.max(0, win.totalWidth - this._viewportWidth);
|
|
20130
|
+
const next = Math.min(maxScroll, Math.max(0, targetCenter - this._viewportWidth / 2));
|
|
20131
|
+
if (Math.abs(vp.scrollLeft - next) > .5) {
|
|
20132
|
+
vp.scrollLeft = next;
|
|
20133
|
+
this._scrollLeft = next;
|
|
20134
|
+
}
|
|
20135
|
+
return;
|
|
20136
|
+
}
|
|
20137
|
+
if (this._viewportHeight <= 0) return;
|
|
20138
|
+
const targetCenter = ((win.cols > 0 ? Math.floor(index / win.cols) : 0) + lineFrac) * win.rowStride;
|
|
20139
|
+
const maxScroll = Math.max(0, win.totalHeight - this._viewportHeight);
|
|
20140
|
+
const next = Math.min(maxScroll, Math.max(0, targetCenter - this._viewportHeight / 2));
|
|
20141
|
+
if (Math.abs(vp.scrollTop - next) > .5) {
|
|
20142
|
+
vp.scrollTop = next;
|
|
20143
|
+
this._scrollTop = next;
|
|
20144
|
+
}
|
|
20145
|
+
}
|
|
20146
|
+
#syncItems() {
|
|
20147
|
+
this.#items = Array.from(this.querySelectorAll(ITEM_SELECTOR$1));
|
|
20148
|
+
for (const el of this.#items) {
|
|
20149
|
+
const name = this.#slotNameFor(el);
|
|
20150
|
+
if (el.getAttribute(`slot`) !== name) el.setAttribute(`slot`, name);
|
|
20151
|
+
}
|
|
20152
|
+
}
|
|
20153
|
+
#slotNameFor(el) {
|
|
20154
|
+
let name = this.#slotNames.get(el);
|
|
20155
|
+
if (name === void 0) {
|
|
20156
|
+
this.#slotSeq += 1;
|
|
20157
|
+
name = `gl-item-${this.#slotSeq}`;
|
|
20158
|
+
this.#slotNames.set(el, name);
|
|
20159
|
+
}
|
|
20160
|
+
return name;
|
|
20161
|
+
}
|
|
20162
|
+
_getVisibleItems() {
|
|
20163
|
+
return this.#items.filter((el) => {
|
|
20164
|
+
if (el.hidden) return false;
|
|
20165
|
+
return true;
|
|
20166
|
+
});
|
|
20167
|
+
}
|
|
20168
|
+
get count() {
|
|
20169
|
+
return this.#items.length;
|
|
20170
|
+
}
|
|
20171
|
+
/**
|
|
20172
|
+
* Number of cells currently rendered in the shadow DOM. With virtualization
|
|
20173
|
+
* this is roughly the visible rows plus overscan, regardless of how large
|
|
20174
|
+
* `count` is — useful for demos/diagnostics.
|
|
20175
|
+
*/
|
|
20176
|
+
get renderedCount() {
|
|
20177
|
+
return this.renderRoot.querySelectorAll(`.cell`).length;
|
|
20178
|
+
}
|
|
20179
|
+
get selectedItems() {
|
|
20180
|
+
return this.selection.selected;
|
|
20181
|
+
}
|
|
20182
|
+
/** The selection as stable `data-key` / `data-value` strings. */
|
|
20183
|
+
get selectedKeys() {
|
|
20184
|
+
return this.selection.selectedKeys;
|
|
20185
|
+
}
|
|
20186
|
+
select(item) {
|
|
20187
|
+
this.selection.replace(item);
|
|
20188
|
+
}
|
|
20189
|
+
selectMany(items) {
|
|
20190
|
+
this.selection.selectMany(items);
|
|
20191
|
+
}
|
|
20192
|
+
deselect(item) {
|
|
20193
|
+
this.selection.deselect(item);
|
|
20194
|
+
}
|
|
20195
|
+
clearSelection() {
|
|
20196
|
+
this.selection.clear();
|
|
20197
|
+
}
|
|
20198
|
+
selectAll() {
|
|
20199
|
+
if (this.selection.canSelectAll()) this.selection.selectAll();
|
|
20200
|
+
}
|
|
20201
|
+
/**
|
|
20202
|
+
* Replace the selection with the items carrying these keys. Unknown keys are
|
|
20203
|
+
* ignored; survives the host rebuilding the item set. Requires each item to
|
|
20204
|
+
* have a `data-key` (or `data-value`).
|
|
20205
|
+
*/
|
|
20206
|
+
selectKeys(keys) {
|
|
20207
|
+
this.selection.selectKeys(keys, (key) => this.getItem(key));
|
|
20208
|
+
}
|
|
20209
|
+
/** Add items by key to the selection (multi-select modes only). */
|
|
20210
|
+
addSelectedKeys(keys) {
|
|
20211
|
+
this.selection.addKeys(keys, (key) => this.getItem(key));
|
|
20212
|
+
}
|
|
20213
|
+
/** Remove items by key from the selection. */
|
|
20214
|
+
removeSelectedKeys(keys) {
|
|
20215
|
+
this.selection.removeKeys(keys, (key) => this.getItem(key));
|
|
20216
|
+
}
|
|
20217
|
+
/** Move the cursor to the item with this key and scroll it into view. */
|
|
20218
|
+
focusKey(key) {
|
|
20219
|
+
const el = this.getItem(key);
|
|
20220
|
+
if (!el) return;
|
|
20221
|
+
this.tickled.focus(el);
|
|
20222
|
+
this.#scrollItemIntoView(el);
|
|
20223
|
+
}
|
|
20224
|
+
/** The key of the item under the cursor, or `undefined`. */
|
|
20225
|
+
get tickledKey() {
|
|
20226
|
+
const id = this.tickled.tickledId;
|
|
20227
|
+
return id ? keyForElement(id) : void 0;
|
|
20228
|
+
}
|
|
20229
|
+
getItem(key) {
|
|
20230
|
+
return this.#items.find((el) => keyForElement(el) === key);
|
|
20231
|
+
}
|
|
20232
|
+
getItemByIndex(index) {
|
|
20233
|
+
return this.#items[index];
|
|
20234
|
+
}
|
|
20235
|
+
getKey(item) {
|
|
20236
|
+
return keyForElement(item);
|
|
20237
|
+
}
|
|
20238
|
+
visibleKeys() {
|
|
20239
|
+
const win = this.#currentWindow();
|
|
20240
|
+
return this.#items.slice(win.startIndex, win.endIndex).map((el) => keyForElement(el)).filter((k) => k !== void 0);
|
|
20241
|
+
}
|
|
20242
|
+
/** Adjust the scroll offset so `el` is fully within the viewport. */
|
|
20243
|
+
#scrollItemIntoView(el) {
|
|
20244
|
+
const index = this.#items.indexOf(el);
|
|
20245
|
+
const vp = this.#viewportRef.value;
|
|
20246
|
+
if (index < 0 || !vp) return;
|
|
20247
|
+
const win = this.#currentWindow();
|
|
20248
|
+
if (win.horizontal) {
|
|
20249
|
+
const left = index * win.colStride;
|
|
20250
|
+
const right = left + this.itemSize;
|
|
20251
|
+
const viewLeft = vp.scrollLeft;
|
|
20252
|
+
let next = viewLeft;
|
|
20253
|
+
if (left < viewLeft) next = left;
|
|
20254
|
+
else if (right > viewLeft + this._viewportWidth) next = right - this._viewportWidth;
|
|
20255
|
+
if (next !== viewLeft) {
|
|
20256
|
+
vp.scrollLeft = next;
|
|
20257
|
+
this._scrollLeft = next;
|
|
20258
|
+
}
|
|
20259
|
+
return;
|
|
20260
|
+
}
|
|
20261
|
+
const top = (win.cols > 0 ? Math.floor(index / win.cols) : 0) * win.rowStride;
|
|
20262
|
+
const bottom = top + this.#rowHeight();
|
|
20263
|
+
const viewTop = vp.scrollTop;
|
|
20264
|
+
let next = viewTop;
|
|
20265
|
+
if (top < viewTop) next = top;
|
|
20266
|
+
else if (bottom > viewTop + this._viewportHeight) next = bottom - this._viewportHeight;
|
|
20267
|
+
if (next !== viewTop) {
|
|
20268
|
+
vp.scrollTop = next;
|
|
20269
|
+
this._scrollTop = next;
|
|
20270
|
+
}
|
|
20271
|
+
}
|
|
20272
|
+
_syncItemStates() {
|
|
20273
|
+
for (const el of this.#items) {
|
|
20274
|
+
el.toggleAttribute(`data-tickled`, this.tickled.isTickled(el));
|
|
20275
|
+
el.toggleAttribute(`data-selected`, this.selection.selected.has(el));
|
|
20276
|
+
}
|
|
20277
|
+
this.setAttribute(`data-input-mode`, this.tickled.inputMode);
|
|
20278
|
+
}
|
|
20279
|
+
_dispatch(name, detail) {
|
|
20280
|
+
this.dispatchEvent(new CustomEvent(name, {
|
|
20281
|
+
detail,
|
|
20282
|
+
bubbles: true,
|
|
20283
|
+
composed: true
|
|
20284
|
+
}));
|
|
20285
|
+
}
|
|
20286
|
+
render() {
|
|
20287
|
+
const win = this.#currentWindow();
|
|
20288
|
+
const slice = this.#items.slice(win.startIndex, win.endIndex);
|
|
20289
|
+
const spacerStyle = win.horizontal ? `width:${win.totalWidth}px;height:100%` : `height:${win.totalHeight}px`;
|
|
20290
|
+
const scrollFade = win.horizontal ? `scroll-fade-x` : `scroll-fade-y`;
|
|
20291
|
+
return b`
|
|
20292
|
+
<div
|
|
20293
|
+
class="viewport ${scrollFade}"
|
|
20294
|
+
part="viewport"
|
|
20295
|
+
${ref(this.#viewportRef)}
|
|
20296
|
+
data-input-mode=${this.tickled.inputMode}
|
|
20297
|
+
@scroll=${this.#onScroll}
|
|
20298
|
+
>
|
|
20299
|
+
<div class="spacer" style=${spacerStyle}>
|
|
20300
|
+
${repeat(slice, (el) => this.#slotNameFor(el), (el, i) => this.#renderCell(el, win.startIndex + i, win))}
|
|
20301
|
+
</div>
|
|
20302
|
+
</div>
|
|
20303
|
+
`;
|
|
20304
|
+
}
|
|
20305
|
+
#renderCell(el, index, win) {
|
|
20306
|
+
const selected = this.selection.selected.has(el);
|
|
20307
|
+
const tickled = this.tickled.isTickled(el);
|
|
20308
|
+
const entering = this.#entering.has(el);
|
|
20309
|
+
const style = this.captionStyle;
|
|
20310
|
+
const separate = style === `separate`;
|
|
20311
|
+
const caption = style === `hidden` ? `` : this.#captionFor(el);
|
|
20312
|
+
const showCaption = caption !== ``;
|
|
20313
|
+
const cellHeight = win.rowStride - this.gap;
|
|
20314
|
+
const x = win.horizontal ? index * win.colStride : columnOf(index, win.cols) * win.colStride;
|
|
20315
|
+
const y = win.horizontal ? Math.max(0, (this._viewportHeight - cellHeight) / 2) : rowOf(index, win.cols) * win.rowStride;
|
|
20316
|
+
const thumbStyle = separate ? `height:${this.itemSize}px` : ``;
|
|
20317
|
+
const captionCss = separate ? `height:${Math.max(0, this.captionHeight - 4)}px` : style === `under` ? `height:${this.captionHeight}px` : ``;
|
|
20318
|
+
return b`
|
|
20319
|
+
<div
|
|
20320
|
+
class="cell${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}${entering ? ` entering` : ``}"
|
|
20321
|
+
part="item"
|
|
20322
|
+
style="width:${this.itemSize}px;height:${cellHeight}px;transform:translate(${x}px,${y}px)"
|
|
20323
|
+
data-index=${index}
|
|
20324
|
+
@click=${(e) => this.#onCellClick(e, el)}
|
|
20325
|
+
@dblclick=${() => this.#onCellActivate(el)}
|
|
20326
|
+
@pointerenter=${() => this.tickled.handlePointerEnter(el)}
|
|
20327
|
+
>
|
|
20328
|
+
<div class="thumb" part="thumb" style=${thumbStyle}>
|
|
20329
|
+
<slot name=${this.#slotNameFor(el)}></slot>
|
|
20330
|
+
</div>
|
|
20331
|
+
${showCaption ? b`<div class="caption" part="caption" style=${captionCss}>${caption}</div>` : ``}
|
|
20332
|
+
</div>
|
|
20333
|
+
`;
|
|
20334
|
+
}
|
|
20335
|
+
#onScroll;
|
|
20336
|
+
#cancelScrollRaf() {
|
|
20337
|
+
if (this.#scrollRaf !== 0) {
|
|
20338
|
+
cancelAnimationFrame(this.#scrollRaf);
|
|
20339
|
+
this.#scrollRaf = 0;
|
|
20340
|
+
}
|
|
20341
|
+
}
|
|
20342
|
+
#onCellClick(e, item) {
|
|
20343
|
+
this.selection.handleClick(item, e);
|
|
20344
|
+
this._dispatch(`list-item-click`, { item });
|
|
20345
|
+
}
|
|
20346
|
+
#onCellActivate(item) {
|
|
20347
|
+
if (this.interactionMode !== `manual`) this.selection.replace(item);
|
|
20348
|
+
this._dispatch(`list-activate`, { item });
|
|
20349
|
+
}
|
|
20350
|
+
#currentWindow() {
|
|
20351
|
+
return computeGridWindow(this.#items.length, this.itemSize, this.#rowHeight(), this.gap, this._viewportWidth, this._viewportHeight, this.orientation === `row` ? this._scrollLeft : this._scrollTop, this.orientation);
|
|
20352
|
+
}
|
|
20353
|
+
};
|
|
20354
|
+
__decorate([property({
|
|
20355
|
+
type: String,
|
|
20356
|
+
attribute: `selection-mode`
|
|
20357
|
+
})], GridListElement.prototype, "selectionMode", void 0);
|
|
20358
|
+
__decorate([property({
|
|
20359
|
+
type: String,
|
|
20360
|
+
attribute: `interaction-mode`
|
|
20361
|
+
})], GridListElement.prototype, "interactionMode", void 0);
|
|
20362
|
+
__decorate([property({
|
|
20363
|
+
type: Number,
|
|
20364
|
+
attribute: `item-size`
|
|
20365
|
+
})], GridListElement.prototype, "itemSize", void 0);
|
|
20366
|
+
__decorate([property({ type: Number })], GridListElement.prototype, "gap", void 0);
|
|
20367
|
+
__decorate([property({
|
|
20368
|
+
type: String,
|
|
20369
|
+
reflect: true
|
|
20370
|
+
})], GridListElement.prototype, "orientation", void 0);
|
|
20371
|
+
__decorate([property({
|
|
20372
|
+
type: String,
|
|
20373
|
+
attribute: `caption-style`,
|
|
20374
|
+
reflect: true
|
|
20375
|
+
})], GridListElement.prototype, "captionStyle", void 0);
|
|
20376
|
+
__decorate([property({
|
|
20377
|
+
type: String,
|
|
20378
|
+
attribute: `caption-align`,
|
|
20379
|
+
reflect: true
|
|
20380
|
+
})], GridListElement.prototype, "captionAlign", void 0);
|
|
20381
|
+
__decorate([property({
|
|
20382
|
+
type: String,
|
|
20383
|
+
attribute: `caption-position`,
|
|
20384
|
+
reflect: true
|
|
20385
|
+
})], GridListElement.prototype, "captionPosition", void 0);
|
|
20386
|
+
__decorate([property({
|
|
20387
|
+
type: String,
|
|
20388
|
+
attribute: `caption-backdrop`,
|
|
20389
|
+
reflect: true
|
|
20390
|
+
})], GridListElement.prototype, "captionBackdrop", void 0);
|
|
20391
|
+
__decorate([property({
|
|
20392
|
+
type: Number,
|
|
20393
|
+
attribute: `caption-height`
|
|
20394
|
+
})], GridListElement.prototype, "captionHeight", void 0);
|
|
20395
|
+
__decorate([property({
|
|
20396
|
+
type: String,
|
|
20397
|
+
attribute: `item-wrap`
|
|
20398
|
+
})], GridListElement.prototype, "itemWrap", void 0);
|
|
20399
|
+
__decorate([state()], GridListElement.prototype, "_scrollTop", void 0);
|
|
20400
|
+
__decorate([state()], GridListElement.prototype, "_scrollLeft", void 0);
|
|
20401
|
+
__decorate([state()], GridListElement.prototype, "_viewportHeight", void 0);
|
|
20402
|
+
__decorate([state()], GridListElement.prototype, "_viewportWidth", void 0);
|
|
20403
|
+
GridListElement = __decorate([safeCustomElement(`ixfx-grid-list`)], GridListElement);
|
|
20404
|
+
//#endregion
|
|
18704
20405
|
//#region src/panel/panel.ts
|
|
18705
20406
|
let PanelElement = class PanelElement extends i {
|
|
18706
20407
|
constructor(..._args) {
|
|
@@ -19053,6 +20754,8 @@ __decorate([property({
|
|
|
19053
20754
|
GroupedItemListerElement = __decorate([safeCustomElement(`ixfx-grouped-item-lister`)], GroupedItemListerElement);
|
|
19054
20755
|
//#endregion
|
|
19055
20756
|
//#region src/tree/tree-base.ts
|
|
20757
|
+
/** Above this many rows entering / leaving at once, skip the animation entirely. */
|
|
20758
|
+
const MAX_ANIMATED_ROWS = 50;
|
|
19056
20759
|
let TreeBaseElement = class TreeBaseElement extends i {
|
|
19057
20760
|
constructor(..._args) {
|
|
19058
20761
|
super(..._args);
|
|
@@ -19076,6 +20779,12 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19076
20779
|
});
|
|
19077
20780
|
this._focusedDepth = 0;
|
|
19078
20781
|
this.#config = { indent: 20 };
|
|
20782
|
+
this.#renderedKeys = [];
|
|
20783
|
+
this.#lastNodeByKey = /* @__PURE__ */ new Map();
|
|
20784
|
+
this.#enteringKeys = /* @__PURE__ */ new Set();
|
|
20785
|
+
this.#leaving = /* @__PURE__ */ new Map();
|
|
20786
|
+
this.#leavingAnimating = /* @__PURE__ */ new Set();
|
|
20787
|
+
this.#treeAnimReady = false;
|
|
19079
20788
|
this.#onKeyDown = (event) => {
|
|
19080
20789
|
const nodes = this.#getAllNodes();
|
|
19081
20790
|
if (nodes.length === 0) return;
|
|
@@ -19137,7 +20846,7 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19137
20846
|
bubbles: true,
|
|
19138
20847
|
composed: true
|
|
19139
20848
|
}));
|
|
19140
|
-
} else if (current.depth >
|
|
20849
|
+
} else if (current.depth > 0) {
|
|
19141
20850
|
const parentDepth = current.depth - 1;
|
|
19142
20851
|
for (let i = currentIdx - 1; i >= 0; i--) if (nodes[i].depth === parentDepth) {
|
|
19143
20852
|
this.#setFocusedIndex(i);
|
|
@@ -19203,6 +20912,85 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19203
20912
|
#anchor;
|
|
19204
20913
|
#ownedRegistry;
|
|
19205
20914
|
#config;
|
|
20915
|
+
#renderedKeys;
|
|
20916
|
+
#lastNodeByKey;
|
|
20917
|
+
#enteringKeys;
|
|
20918
|
+
#leaving;
|
|
20919
|
+
#leavingAnimating;
|
|
20920
|
+
#treeAnimReady;
|
|
20921
|
+
willUpdate(changedProps) {
|
|
20922
|
+
super.willUpdate(changedProps);
|
|
20923
|
+
this.#diffVisibleNodes();
|
|
20924
|
+
}
|
|
20925
|
+
#diffVisibleNodes() {
|
|
20926
|
+
const nodes = this.root ? this.#getAllNodes() : [];
|
|
20927
|
+
const nowKeys = new Set(nodes.map((n) => n.node.item.key));
|
|
20928
|
+
this.#enteringKeys.clear();
|
|
20929
|
+
if (this.#treeAnimReady && this.#renderedKeys.length > 0) {
|
|
20930
|
+
const prevSet = new Set(this.#renderedKeys);
|
|
20931
|
+
for (const { node } of nodes) if (!prevSet.has(node.item.key)) this.#enteringKeys.add(node.item.key);
|
|
20932
|
+
if (this.#enteringKeys.size > MAX_ANIMATED_ROWS) this.#enteringKeys.clear();
|
|
20933
|
+
for (const key of this.#renderedKeys) if (!nowKeys.has(key) && !this.#leaving.has(key)) {
|
|
20934
|
+
const last = this.#lastNodeByKey.get(key);
|
|
20935
|
+
if (last) this.#leaving.set(key, last);
|
|
20936
|
+
}
|
|
20937
|
+
}
|
|
20938
|
+
for (const key of [...this.#leaving.keys()]) if (nowKeys.has(key)) {
|
|
20939
|
+
this.#leaving.delete(key);
|
|
20940
|
+
this.#leavingAnimating.delete(key);
|
|
20941
|
+
}
|
|
20942
|
+
if (this.#leaving.size > MAX_ANIMATED_ROWS) {
|
|
20943
|
+
this.#leaving.clear();
|
|
20944
|
+
this.#leavingAnimating.clear();
|
|
20945
|
+
}
|
|
20946
|
+
}
|
|
20947
|
+
/** True while `node` is a fading ghost row (removed / collapsed, still animating out). */
|
|
20948
|
+
_isLeaving(node) {
|
|
20949
|
+
return this.#leaving.has(node.item.key);
|
|
20950
|
+
}
|
|
20951
|
+
/** Splice the fading ghost rows back into the visible list near where they were. */
|
|
20952
|
+
#weaveLeaving(nodes) {
|
|
20953
|
+
if (this.#leaving.size === 0) return nodes;
|
|
20954
|
+
const result = [...nodes];
|
|
20955
|
+
const indexOfKey = (key) => result.findIndex((e) => e.node.item.key === key);
|
|
20956
|
+
for (const [key, entry] of this.#leaving) {
|
|
20957
|
+
const prevIdx = this.#renderedKeys.indexOf(key);
|
|
20958
|
+
let insertAt = result.length;
|
|
20959
|
+
for (let j = prevIdx + 1; j < this.#renderedKeys.length; j++) {
|
|
20960
|
+
const at = indexOfKey(this.#renderedKeys[j]);
|
|
20961
|
+
if (at !== -1) {
|
|
20962
|
+
insertAt = at;
|
|
20963
|
+
break;
|
|
20964
|
+
}
|
|
20965
|
+
}
|
|
20966
|
+
result.splice(insertAt, 0, entry);
|
|
20967
|
+
}
|
|
20968
|
+
return result;
|
|
20969
|
+
}
|
|
20970
|
+
#runItemAnimations() {
|
|
20971
|
+
if (this.#enteringKeys.size > 0) {
|
|
20972
|
+
for (const el of this.renderRoot.querySelectorAll(`.item`)) {
|
|
20973
|
+
const key = el.dataset.key;
|
|
20974
|
+
if (key !== void 0 && this.#enteringKeys.has(key)) animateListItemIn(el);
|
|
20975
|
+
}
|
|
20976
|
+
this.#enteringKeys.clear();
|
|
20977
|
+
}
|
|
20978
|
+
if (this.#leaving.size > 0) for (const el of this.renderRoot.querySelectorAll(`.item.leaving`)) {
|
|
20979
|
+
const key = el.dataset.key;
|
|
20980
|
+
if (key === void 0 || this.#leavingAnimating.has(key)) continue;
|
|
20981
|
+
this.#leavingAnimating.add(key);
|
|
20982
|
+
animateListItemOut(el).then(() => {
|
|
20983
|
+
this.#leaving.delete(key);
|
|
20984
|
+
this.#leavingAnimating.delete(key);
|
|
20985
|
+
this.requestUpdate();
|
|
20986
|
+
});
|
|
20987
|
+
}
|
|
20988
|
+
const visible = this.root ? this.#getAllNodes() : [];
|
|
20989
|
+
this.#renderedKeys = visible.map((n) => n.node.item.key);
|
|
20990
|
+
this.#lastNodeByKey.clear();
|
|
20991
|
+
for (const entry of visible) this.#lastNodeByKey.set(entry.node.item.key, entry);
|
|
20992
|
+
this.#treeAnimReady = true;
|
|
20993
|
+
}
|
|
19206
20994
|
updated(changedProps) {
|
|
19207
20995
|
if (changedProps.has(`root`) && this.root) {
|
|
19208
20996
|
this.#abortPending();
|
|
@@ -19237,6 +21025,7 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19237
21025
|
if (changedProps.has(`selectedNode`) && this.selectedNode) {
|
|
19238
21026
|
if (changedProps.get(`selectedNode`) !== this.selectedNode) this.tickled.setTickled(this.selectedNode.item.key);
|
|
19239
21027
|
}
|
|
21028
|
+
this.#runItemAnimations();
|
|
19240
21029
|
}
|
|
19241
21030
|
connectedCallback() {
|
|
19242
21031
|
super.connectedCallback();
|
|
@@ -19401,9 +21190,9 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19401
21190
|
}
|
|
19402
21191
|
}
|
|
19403
21192
|
}
|
|
19404
|
-
renderNodeContent(node,
|
|
21193
|
+
renderNodeContent(node, depth) {
|
|
19405
21194
|
const isBranch = node.item.isLeaf !== true;
|
|
19406
|
-
const isExpanded = this._isExpanded(node);
|
|
21195
|
+
const isExpanded = this._isExpanded(node, depth);
|
|
19407
21196
|
return b`
|
|
19408
21197
|
<span class="tree-caret">
|
|
19409
21198
|
${isBranch ? b`<ixfx-icon class="caret-icon" name="chevron-down" .rotate=${isExpanded ? A : `-90deg`}></ixfx-icon>` : ``}
|
|
@@ -19684,9 +21473,9 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19684
21473
|
_isFocused(node) {
|
|
19685
21474
|
return this.tickled.isTickled(node.item.key);
|
|
19686
21475
|
}
|
|
19687
|
-
_isExpanded(node) {
|
|
21476
|
+
_isExpanded(node, depth = 0) {
|
|
19688
21477
|
if (this._expandedKeys.has(node.item.key)) return true;
|
|
19689
|
-
if (this.filterPredicate && node.children) return node.children.some((child) => this.#matchesFilter(child,
|
|
21478
|
+
if (this.filterPredicate && node.children) return node.children.some((child) => this.#matchesFilter(child, depth + 1));
|
|
19690
21479
|
return false;
|
|
19691
21480
|
}
|
|
19692
21481
|
#matchesFilter(node, depth) {
|
|
@@ -19767,7 +21556,7 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19767
21556
|
}
|
|
19768
21557
|
#getAllNodes() {
|
|
19769
21558
|
if (!this.root) return [];
|
|
19770
|
-
return this.#getVisibleNodes(this.root,
|
|
21559
|
+
return this.#getVisibleNodes(this.root, -1);
|
|
19771
21560
|
}
|
|
19772
21561
|
#getFocusedIndex() {
|
|
19773
21562
|
const nodes = this.#getAllNodes();
|
|
@@ -19821,14 +21610,14 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19821
21610
|
}
|
|
19822
21611
|
#onDblClick;
|
|
19823
21612
|
#renderNodes(nodes) {
|
|
19824
|
-
if (nodes.length === 0 && this.#pendingNode) return b`
|
|
21613
|
+
if (nodes.length === 0 && this.#pendingNode && this.#leaving.size === 0) return b`
|
|
19825
21614
|
<div class="tree-loading">
|
|
19826
21615
|
${this.renderLoadingIndicator()}
|
|
19827
21616
|
</div>
|
|
19828
21617
|
`;
|
|
19829
21618
|
return b`
|
|
19830
21619
|
<ul class="tree-list" role="tree">
|
|
19831
|
-
${nodes.map(({ node, depth }) => this._renderNode(node, depth))}
|
|
21620
|
+
${this.#weaveLeaving(nodes).map(({ node, depth }) => this._renderNode(node, depth))}
|
|
19832
21621
|
</ul>
|
|
19833
21622
|
`;
|
|
19834
21623
|
}
|
|
@@ -19836,15 +21625,17 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19836
21625
|
const selected = this._isSelected(node);
|
|
19837
21626
|
const tickled = this._isFocused(node);
|
|
19838
21627
|
const isBranch = node.item.isLeaf !== true;
|
|
19839
|
-
const isExpanded = this._isExpanded(node);
|
|
21628
|
+
const isExpanded = this._isExpanded(node, depth);
|
|
19840
21629
|
const isPending = this.#pendingNode === node;
|
|
19841
21630
|
const isFiltered = this.filterPredicate !== void 0 && !this.#matchesFilter(node, depth);
|
|
21631
|
+
const isLeaving = this._isLeaving(node);
|
|
19842
21632
|
return b`
|
|
19843
21633
|
<li
|
|
19844
|
-
class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${isFiltered ? `filtered` : ``}"
|
|
21634
|
+
class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${isFiltered ? `filtered` : ``} ${isLeaving ? `leaving` : ``}"
|
|
19845
21635
|
role="treeitem"
|
|
19846
21636
|
aria-selected=${selected}
|
|
19847
21637
|
aria-expanded=${isBranch ? isExpanded : A}
|
|
21638
|
+
aria-hidden=${isLeaving ? `true` : A}
|
|
19848
21639
|
aria-level=${depth + 1}
|
|
19849
21640
|
data-key=${node.item.key}
|
|
19850
21641
|
style=${`--depth: ${depth}`}
|
|
@@ -19965,6 +21756,12 @@ let TreeBaseElement = class TreeBaseElement extends i {
|
|
|
19965
21756
|
display: none;
|
|
19966
21757
|
}
|
|
19967
21758
|
|
|
21759
|
+
/* Fading-out ghost row (see #runItemAnimations) — inert while it collapses. */
|
|
21760
|
+
.item.leaving {
|
|
21761
|
+
pointer-events: none;
|
|
21762
|
+
overflow: hidden;
|
|
21763
|
+
}
|
|
21764
|
+
|
|
19968
21765
|
::highlight(incr-search-highlight) {
|
|
19969
21766
|
background: var(--search-highlight-bg, var(--accent));
|
|
19970
21767
|
color: var(--search-highlight-color, var(--accent-text, #fff));
|
|
@@ -20024,11 +21821,11 @@ let TreeListElement = class TreeListElement extends TreeBaseElement {
|
|
|
20024
21821
|
if (node.item.icon) this.#depthsWithIcons.add(depth);
|
|
20025
21822
|
if (node.children) for (const child of node.children) visit(child, depth + 1);
|
|
20026
21823
|
};
|
|
20027
|
-
if (this.root) visit(this.root,
|
|
21824
|
+
if (this.root) visit(this.root, -1);
|
|
20028
21825
|
}
|
|
20029
21826
|
renderNodeContent(node, depth) {
|
|
20030
21827
|
const isBranch = node.item.isLeaf !== true;
|
|
20031
|
-
const isExpanded = this._isExpanded(node);
|
|
21828
|
+
const isExpanded = this._isExpanded(node, depth);
|
|
20032
21829
|
const icon = node.item.icon;
|
|
20033
21830
|
const tooltip = node.item.tooltip;
|
|
20034
21831
|
const caretAndLabel = b`
|
|
@@ -20074,16 +21871,18 @@ let TreeListElement = class TreeListElement extends TreeBaseElement {
|
|
|
20074
21871
|
const selected = this._isSelected(node);
|
|
20075
21872
|
const tickled = this._isFocused(node);
|
|
20076
21873
|
const isBranch = node.item.isLeaf !== true;
|
|
20077
|
-
const isExpanded = this._isExpanded(node);
|
|
21874
|
+
const isExpanded = this._isExpanded(node, depth);
|
|
20078
21875
|
const isPending = this._pendingNode === node;
|
|
20079
21876
|
const hasIconAtDepth = this.#depthsWithIcons.has(depth);
|
|
21877
|
+
const isLeaving = this._isLeaving(node);
|
|
20080
21878
|
return b`
|
|
20081
21879
|
<li
|
|
20082
|
-
class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${hasIconAtDepth ? `has-icon-at-depth` : ``}"
|
|
21880
|
+
class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${hasIconAtDepth ? `has-icon-at-depth` : ``} ${isLeaving ? `leaving` : ``}"
|
|
20083
21881
|
data-key=${node.item.key}
|
|
20084
21882
|
role="treeitem"
|
|
20085
21883
|
aria-selected=${selected}
|
|
20086
21884
|
aria-expanded=${isBranch ? isExpanded : A}
|
|
21885
|
+
aria-hidden=${isLeaving ? `true` : A}
|
|
20087
21886
|
aria-level=${depth + 1}
|
|
20088
21887
|
style=${`--depth: ${depth}`}
|
|
20089
21888
|
@click=${(e) => this._handleItemClick(node, depth, e)}
|
|
@@ -20356,6 +22155,7 @@ let VerticalListElement = class VerticalListElement extends i {
|
|
|
20356
22155
|
this._searchQuery = ``;
|
|
20357
22156
|
this.#customWrappers = /* @__PURE__ */ new Map();
|
|
20358
22157
|
this.#wrappedElements = /* @__PURE__ */ new Set();
|
|
22158
|
+
this.#animatedItems = /* @__PURE__ */ new WeakSet();
|
|
20359
22159
|
this.selection = new ListSelectionController({
|
|
20360
22160
|
getOrderedItems: () => this._getVisibleItems(),
|
|
20361
22161
|
isSelectable: (el) => this._isCheckable(el),
|
|
@@ -20363,11 +22163,13 @@ let VerticalListElement = class VerticalListElement extends i {
|
|
|
20363
22163
|
selectionMode: this.selectionMode,
|
|
20364
22164
|
interactionMode: this.interactionMode
|
|
20365
22165
|
}),
|
|
22166
|
+
getKey: keyForElement,
|
|
20366
22167
|
onChange: (selected, previous) => {
|
|
20367
22168
|
this._syncItemStates();
|
|
20368
22169
|
this._dispatch(`list-select`, {
|
|
20369
22170
|
selected,
|
|
20370
|
-
previous
|
|
22171
|
+
previous,
|
|
22172
|
+
selectedKeys: this.selection.selectedKeys
|
|
20371
22173
|
});
|
|
20372
22174
|
}
|
|
20373
22175
|
});
|
|
@@ -20480,6 +22282,10 @@ let VerticalListElement = class VerticalListElement extends i {
|
|
|
20480
22282
|
#search;
|
|
20481
22283
|
#customWrappers;
|
|
20482
22284
|
#wrappedElements;
|
|
22285
|
+
/** Watches for items added to the light DOM so they can be animated in. */
|
|
22286
|
+
#itemObserver;
|
|
22287
|
+
/** Items that have already played (or been skipped for) their enter animation. */
|
|
22288
|
+
#animatedItems;
|
|
20483
22289
|
connectedCallback() {
|
|
20484
22290
|
super.connectedCallback();
|
|
20485
22291
|
if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
|
|
@@ -20488,6 +22294,9 @@ let VerticalListElement = class VerticalListElement extends i {
|
|
|
20488
22294
|
this.addEventListener(`keydown`, this._onKeyDown);
|
|
20489
22295
|
this.addEventListener(`click`, this._onClick);
|
|
20490
22296
|
this.addEventListener(`dblclick`, this._onDblClick);
|
|
22297
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR)) this.#animatedItems.add(el);
|
|
22298
|
+
this.#itemObserver ??= new MutationObserver((records) => this.#onItemsMutated(records));
|
|
22299
|
+
this.#itemObserver.observe(this, { childList: true });
|
|
20491
22300
|
}
|
|
20492
22301
|
disconnectedCallback() {
|
|
20493
22302
|
super.disconnectedCallback();
|
|
@@ -20496,9 +22305,31 @@ let VerticalListElement = class VerticalListElement extends i {
|
|
|
20496
22305
|
this.removeEventListener(`keydown`, this._onKeyDown);
|
|
20497
22306
|
this.removeEventListener(`click`, this._onClick);
|
|
20498
22307
|
this.removeEventListener(`dblclick`, this._onDblClick);
|
|
22308
|
+
this.#itemObserver?.disconnect();
|
|
20499
22309
|
this.#search?.dispose();
|
|
20500
22310
|
this.#search = void 0;
|
|
20501
22311
|
}
|
|
22312
|
+
/**
|
|
22313
|
+
* Animate in items appended to the light DOM (however they got there —
|
|
22314
|
+
* {@link addItem}, direct `appendChild`, a framework binding). A bulk populate
|
|
22315
|
+
* (roughly everything at once) is left alone, as are the wrapper `<li>`s the
|
|
22316
|
+
* checked-mode plumbing shuffles around.
|
|
22317
|
+
*/
|
|
22318
|
+
#onItemsMutated(records) {
|
|
22319
|
+
if (!this.isConnected) return;
|
|
22320
|
+
const fresh = [];
|
|
22321
|
+
for (const record of records) for (const node of record.addedNodes) {
|
|
22322
|
+
if (!(node instanceof HTMLElement) || node.parentElement !== this) continue;
|
|
22323
|
+
if (!node.matches(ITEM_SELECTOR)) continue;
|
|
22324
|
+
if (this.#animatedItems.has(node) || this.#customWrappers.has(node) || this.#wrappedElements.has(node)) continue;
|
|
22325
|
+
this.#animatedItems.add(node);
|
|
22326
|
+
fresh.push(node);
|
|
22327
|
+
}
|
|
22328
|
+
if (fresh.length === 0) return;
|
|
22329
|
+
const total = this.querySelectorAll(ITEM_SELECTOR).length;
|
|
22330
|
+
if (fresh.length >= total) return;
|
|
22331
|
+
for (const el of fresh) animateListItemIn(el);
|
|
22332
|
+
}
|
|
20502
22333
|
firstUpdated() {
|
|
20503
22334
|
this.#search = createElementIncrSearch({
|
|
20504
22335
|
container: this,
|
|
@@ -20544,8 +22375,21 @@ let VerticalListElement = class VerticalListElement extends i {
|
|
|
20544
22375
|
this.appendChild(el);
|
|
20545
22376
|
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
20546
22377
|
}
|
|
22378
|
+
/**
|
|
22379
|
+
* Collapse + fade `item` out, then detach it. The rows below slide up to
|
|
22380
|
+
* close the gap. Resolves once the element has actually been removed.
|
|
22381
|
+
* Under `prefers-reduced-motion: reduce` it's removed immediately.
|
|
22382
|
+
*/
|
|
20547
22383
|
removeItem(item) {
|
|
20548
|
-
|
|
22384
|
+
this.selection.deselect(item);
|
|
22385
|
+
return animateListItemOut(item).then(() => {
|
|
22386
|
+
item.remove();
|
|
22387
|
+
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
22388
|
+
});
|
|
22389
|
+
}
|
|
22390
|
+
/** {@link removeItem} for several items at once. */
|
|
22391
|
+
removeItems(items) {
|
|
22392
|
+
return Promise.all([...items].map((item) => this.removeItem(item))).then(() => void 0);
|
|
20549
22393
|
}
|
|
20550
22394
|
clearItems() {
|
|
20551
22395
|
this.#customWrappers.clear();
|
|
@@ -20584,6 +22428,43 @@ let VerticalListElement = class VerticalListElement extends i {
|
|
|
20584
22428
|
selectAll() {
|
|
20585
22429
|
if (this.selection.canSelectAll()) this.selection.selectAll();
|
|
20586
22430
|
}
|
|
22431
|
+
/** The selection as stable `data-key` / `data-value` strings. */
|
|
22432
|
+
get selectedKeys() {
|
|
22433
|
+
return this.selection.selectedKeys;
|
|
22434
|
+
}
|
|
22435
|
+
/** The key of the item under the cursor, or `undefined`. */
|
|
22436
|
+
get tickledKey() {
|
|
22437
|
+
const id = this.tickled.tickledId;
|
|
22438
|
+
return id ? keyForElement(id) : void 0;
|
|
22439
|
+
}
|
|
22440
|
+
/** Replace the selection with the items carrying these keys (unknown keys ignored). */
|
|
22441
|
+
selectKeys(keys) {
|
|
22442
|
+
this.selection.selectKeys(keys, (key) => this.#itemForKey(key));
|
|
22443
|
+
}
|
|
22444
|
+
/** Add items by key to the selection (multi-select modes only). */
|
|
22445
|
+
addSelectedKeys(keys) {
|
|
22446
|
+
this.selection.addKeys(keys, (key) => this.#itemForKey(key));
|
|
22447
|
+
}
|
|
22448
|
+
/** Remove items by key from the selection. */
|
|
22449
|
+
removeSelectedKeys(keys) {
|
|
22450
|
+
this.selection.removeKeys(keys, (key) => this.#itemForKey(key));
|
|
22451
|
+
}
|
|
22452
|
+
/** Move the cursor to the item with this key and scroll it into view. */
|
|
22453
|
+
focusKey(key) {
|
|
22454
|
+
const el = this.#itemForKey(key);
|
|
22455
|
+
if (!el) return;
|
|
22456
|
+
this.tickled.focus(el);
|
|
22457
|
+
el.scrollIntoView({
|
|
22458
|
+
block: `nearest`,
|
|
22459
|
+
inline: `nearest`
|
|
22460
|
+
});
|
|
22461
|
+
}
|
|
22462
|
+
#itemForKey(key) {
|
|
22463
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
|
|
22464
|
+
if (this.#wrappedElements.has(el)) continue;
|
|
22465
|
+
if (keyForElement(el) === key) return el;
|
|
22466
|
+
}
|
|
22467
|
+
}
|
|
20587
22468
|
*selectedFilter(kind) {
|
|
20588
22469
|
const wanted = (el) => {
|
|
20589
22470
|
switch (kind) {
|
|
@@ -27228,6 +29109,136 @@ function dismiss(id) {
|
|
|
27228
29109
|
syncRenderer();
|
|
27229
29110
|
}
|
|
27230
29111
|
//#endregion
|
|
29112
|
+
//#region src/pan-text/pan-text.ts
|
|
29113
|
+
const SPEED = 45;
|
|
29114
|
+
const END_PADDING = 4;
|
|
29115
|
+
const START_DELAY = 300;
|
|
29116
|
+
const PAN_EASING = `cubic-bezier(0.25, 0.46, 0.45, 0.94)`;
|
|
29117
|
+
let PanTextElement = class PanTextElement extends i {
|
|
29118
|
+
constructor(..._args) {
|
|
29119
|
+
super(..._args);
|
|
29120
|
+
this.direction = `end`;
|
|
29121
|
+
this.disabled = false;
|
|
29122
|
+
this._isOverflowing = false;
|
|
29123
|
+
this._isHovering = false;
|
|
29124
|
+
this._overflow = 0;
|
|
29125
|
+
this._resizeObserver = null;
|
|
29126
|
+
this._onMouseEnter = () => {
|
|
29127
|
+
this._isHovering = true;
|
|
29128
|
+
};
|
|
29129
|
+
this._onMouseLeave = () => {
|
|
29130
|
+
this._isHovering = false;
|
|
29131
|
+
};
|
|
29132
|
+
this._measure = () => {
|
|
29133
|
+
if (this._isHovering) return;
|
|
29134
|
+
const el = this._textEl;
|
|
29135
|
+
if (!el) return;
|
|
29136
|
+
this._overflow = Math.max(0, el.scrollWidth - el.clientWidth);
|
|
29137
|
+
const overflowing = this._overflow > 0;
|
|
29138
|
+
this.classList.toggle(`is-overflowing`, overflowing);
|
|
29139
|
+
if (overflowing !== this._isOverflowing) this._isOverflowing = overflowing;
|
|
29140
|
+
};
|
|
29141
|
+
}
|
|
29142
|
+
connectedCallback() {
|
|
29143
|
+
super.connectedCallback();
|
|
29144
|
+
this._resizeObserver = new ResizeObserver(() => this._measure());
|
|
29145
|
+
this.addEventListener(`mouseenter`, this._onMouseEnter);
|
|
29146
|
+
this.addEventListener(`mouseleave`, this._onMouseLeave);
|
|
29147
|
+
}
|
|
29148
|
+
disconnectedCallback() {
|
|
29149
|
+
super.disconnectedCallback();
|
|
29150
|
+
this._resizeObserver?.disconnect();
|
|
29151
|
+
this._resizeObserver = null;
|
|
29152
|
+
this.removeEventListener(`mouseenter`, this._onMouseEnter);
|
|
29153
|
+
this.removeEventListener(`mouseleave`, this._onMouseLeave);
|
|
29154
|
+
}
|
|
29155
|
+
firstUpdated() {
|
|
29156
|
+
this._measure();
|
|
29157
|
+
this._resizeObserver?.observe(this);
|
|
29158
|
+
document.fonts?.ready.then(() => this._measure());
|
|
29159
|
+
}
|
|
29160
|
+
updated() {
|
|
29161
|
+
this._measure();
|
|
29162
|
+
}
|
|
29163
|
+
_textStyle() {
|
|
29164
|
+
if (!this._isOverflowing) return ``;
|
|
29165
|
+
const distance = this._overflow + END_PADDING;
|
|
29166
|
+
const duration = Math.max(1.5, distance / SPEED);
|
|
29167
|
+
const revealEnd = this.direction === `end`;
|
|
29168
|
+
const restX = revealEnd ? 0 : -distance;
|
|
29169
|
+
const hoverX = revealEnd ? -distance : 0;
|
|
29170
|
+
if (this._isHovering) return [
|
|
29171
|
+
`transform: translateX(${hoverX}px)`,
|
|
29172
|
+
`transition: transform ${duration}s ${PAN_EASING} ${START_DELAY}ms`,
|
|
29173
|
+
`overflow: visible`,
|
|
29174
|
+
`text-overflow: clip`
|
|
29175
|
+
].join(`; `);
|
|
29176
|
+
return [
|
|
29177
|
+
`transform: translateX(${restX}px)`,
|
|
29178
|
+
`transition: transform 400ms ${PAN_EASING}`,
|
|
29179
|
+
revealEnd ? `` : `overflow: visible; text-overflow: clip`
|
|
29180
|
+
].filter(Boolean).join(`; `);
|
|
29181
|
+
}
|
|
29182
|
+
render() {
|
|
29183
|
+
const textClasses = {
|
|
29184
|
+
"is-overflowing": this._isOverflowing,
|
|
29185
|
+
"is-hovering": this._isHovering
|
|
29186
|
+
};
|
|
29187
|
+
return b`
|
|
29188
|
+
<span class="text ${classMap(textClasses)}" part="text" style=${this._textStyle()}>
|
|
29189
|
+
<slot @slotchange=${this._measure}></slot>
|
|
29190
|
+
</span>
|
|
29191
|
+
`;
|
|
29192
|
+
}
|
|
29193
|
+
static {
|
|
29194
|
+
this.styles = [themeFallbacks, i$3`
|
|
29195
|
+
/* Renders like plain inline text: no colours, border, padding or margin
|
|
29196
|
+
of its own — it only adds the clipping needed to pan on hover. */
|
|
29197
|
+
:host {
|
|
29198
|
+
display: inline-block;
|
|
29199
|
+
overflow: hidden;
|
|
29200
|
+
white-space: nowrap;
|
|
29201
|
+
max-width: 100%;
|
|
29202
|
+
box-sizing: border-box;
|
|
29203
|
+
font: inherit;
|
|
29204
|
+
color: inherit;
|
|
29205
|
+
}
|
|
29206
|
+
|
|
29207
|
+
:host([disabled]) {
|
|
29208
|
+
opacity: var(--disabled-opacity, 0.5);
|
|
29209
|
+
pointer-events: none;
|
|
29210
|
+
}
|
|
29211
|
+
|
|
29212
|
+
.text {
|
|
29213
|
+
display: block;
|
|
29214
|
+
overflow: hidden;
|
|
29215
|
+
text-overflow: ellipsis;
|
|
29216
|
+
white-space: nowrap;
|
|
29217
|
+
will-change: transform;
|
|
29218
|
+
}
|
|
29219
|
+
|
|
29220
|
+
@media (prefers-reduced-motion: reduce) {
|
|
29221
|
+
.text {
|
|
29222
|
+
transition: none !important;
|
|
29223
|
+
transform: none !important;
|
|
29224
|
+
}
|
|
29225
|
+
}
|
|
29226
|
+
`];
|
|
29227
|
+
}
|
|
29228
|
+
};
|
|
29229
|
+
__decorate([property({
|
|
29230
|
+
type: String,
|
|
29231
|
+
attribute: `direction`
|
|
29232
|
+
})], PanTextElement.prototype, "direction", void 0);
|
|
29233
|
+
__decorate([property({
|
|
29234
|
+
type: Boolean,
|
|
29235
|
+
attribute: `disabled`
|
|
29236
|
+
})], PanTextElement.prototype, "disabled", void 0);
|
|
29237
|
+
__decorate([state()], PanTextElement.prototype, "_isOverflowing", void 0);
|
|
29238
|
+
__decorate([state()], PanTextElement.prototype, "_isHovering", void 0);
|
|
29239
|
+
__decorate([query(`.text`)], PanTextElement.prototype, "_textEl", void 0);
|
|
29240
|
+
PanTextElement = __decorate([safeCustomElement(`ixfx-pan-text`)], PanTextElement);
|
|
29241
|
+
//#endregion
|
|
27231
29242
|
//#region src/panel/panel-group.ts
|
|
27232
29243
|
let PanelGroupElement = class PanelGroupElement extends i {
|
|
27233
29244
|
constructor(..._args) {
|
|
@@ -36631,6 +38642,6 @@ function init() {
|
|
|
36631
38642
|
console.log(`Init`);
|
|
36632
38643
|
}
|
|
36633
38644
|
//#endregion
|
|
36634
|
-
export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, 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, TreeKeyboardNavController, TreeListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, applyAgeModification, applyHighlights, applySnapshot, attachTimelineInteractions, buildHighlightTargets, buildModel, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeColour, computeLeafGeoms, createAcCompletionProvider, createElementIncrSearch, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, readBaseColour, registerFontIcon, registerIcon, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, walk, wrapRoot };
|
|
38645
|
+
export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DataTableElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GridListElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, KeyedElementIndex, LIST_ITEM_MOTION_MS, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, 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, TreeKeyboardNavController, TreeListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, animateListItemIn, animateListItemOut, applyAgeModification, applyHighlights, applySnapshot, attachTimelineInteractions, buildHighlightTargets, buildModel, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeColour, computeLeafGeoms, createAcCompletionProvider, createElementIncrSearch, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, prefersReducedMotion, readBaseColour, registerFontIcon, registerIcon, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, walk, wrapRoot };
|
|
36635
38646
|
|
|
36636
38647
|
//# sourceMappingURL=index.js.map
|