@ixfx/components 0.5.20 → 0.6.0
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 +535 -263
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +7971 -6596
- package/bundle/index.js.map +1 -1
- package/dist/ac-text.d.ts +12 -7
- package/dist/ac-text.d.ts.map +1 -1
- package/dist/ac-text.js +3 -4
- package/dist/ac-text.js.map +1 -1
- package/dist/ac-token-DVbmHmky.d.ts +107 -0
- package/dist/ac-token-DVbmHmky.d.ts.map +1 -0
- package/dist/ac-token.d.ts +2 -0
- package/dist/ac-token.js +646 -0
- package/dist/ac-token.js.map +1 -0
- package/dist/{button-pcDaCemT.js → button-Bn0BuLpO.js} +3 -4
- package/dist/{button-pcDaCemT.js.map → button-Bn0BuLpO.js.map} +1 -1
- package/dist/button.d.ts +1 -2
- package/dist/button.d.ts.map +1 -1
- package/dist/button.js +1 -1
- package/dist/checkbox.d.ts +0 -1
- package/dist/checkbox.d.ts.map +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/checkbox.js.map +1 -1
- package/dist/{colour-picker-D43Q96r0.js → colour-picker-0stHB90J.js} +11 -14
- package/dist/{colour-picker-D43Q96r0.js.map → colour-picker-0stHB90J.js.map} +1 -1
- package/dist/colour-picker.d.ts +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.d.ts +4 -2
- package/dist/crumbs.d.ts.map +1 -1
- package/dist/crumbs.js +1152 -1
- package/dist/crumbs.js.map +1 -0
- package/dist/data-controller-DOlmqNKb.js +171 -0
- package/dist/data-controller-DOlmqNKb.js.map +1 -0
- package/dist/data-display.d.ts +1 -2
- package/dist/data-display.d.ts.map +1 -1
- package/dist/data-display.js +24 -22
- package/dist/data-display.js.map +1 -1
- package/dist/data-grid-BeAkORz2.d.ts +215 -0
- package/dist/data-grid-BeAkORz2.d.ts.map +1 -0
- package/dist/data-grid.d.ts +2 -0
- package/dist/data-grid.js +1225 -0
- package/dist/data-grid.js.map +1 -0
- package/dist/data-provider-BAn_-oR1.d.ts +61 -0
- package/dist/data-provider-BAn_-oR1.d.ts.map +1 -0
- package/dist/{decorate-DdjvVHS-.js → decorate-VRK8rslU.js} +2 -2
- package/dist/{decorate-DdjvVHS-.js.map → decorate-VRK8rslU.js.map} +1 -1
- package/dist/{defaults-BkWCPJFL.js → defaults-BCiDhZbF.js} +20 -3
- package/dist/defaults-BCiDhZbF.js.map +1 -0
- package/dist/drag-ghost-DtSDvjri.js +85 -0
- package/dist/drag-ghost-DtSDvjri.js.map +1 -0
- package/dist/editable-label.d.ts +1 -2
- package/dist/editable-label.d.ts.map +1 -1
- package/dist/editable-label.js +1 -1
- package/dist/editable-label.js.map +1 -1
- package/dist/{incr-search-CeERfijK.js → element-search-Y8SRfPGM.js} +7 -135
- package/dist/element-search-Y8SRfPGM.js.map +1 -0
- package/dist/form-CsXJX15p.js +64 -0
- package/dist/form-CsXJX15p.js.map +1 -0
- package/dist/form.d.ts +15 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +2 -0
- package/dist/grouped-item-lister-CNLMgdIr.d.ts +121 -0
- package/dist/grouped-item-lister-CNLMgdIr.d.ts.map +1 -0
- package/dist/grouped-item-lister.d.ts +2 -0
- package/dist/grouped-item-lister.js +177 -0
- package/dist/grouped-item-lister.js.map +1 -0
- package/dist/{hex-editor-B-UI4OZu.js → hex-editor-Bk6rxhfg.js} +6 -8
- package/dist/{hex-editor-B-UI4OZu.js.map → hex-editor-Bk6rxhfg.js.map} +1 -1
- package/dist/{hex-editor-CMIxtNgT.d.ts → hex-editor-Caq8vNQk.d.ts} +2 -3
- package/dist/hex-editor-Caq8vNQk.d.ts.map +1 -0
- package/dist/hex.d.ts +1 -1
- package/dist/hex.js +1 -1
- package/dist/highlight-B-eQrhhb.js.map +1 -1
- package/dist/{icon-Bd5BiU4b.d.ts → icon-BUwP1CZ1.d.ts} +3 -4
- package/dist/icon-BUwP1CZ1.d.ts.map +1 -0
- package/dist/{icon-CK14NUcE.js → icons-Bm2ByQ3s.js} +10 -5
- package/dist/icons-Bm2ByQ3s.js.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +2 -8
- package/dist/incr-search-vCzuao-2.js +137 -0
- package/dist/incr-search-vCzuao-2.js.map +1 -0
- package/dist/incr-search.d.ts +1 -1
- package/dist/incr-search.js +2 -1
- package/dist/{index-DrKRD8_a.d.ts → index-BOUaDIW-.d.ts} +4 -4
- package/dist/index-BOUaDIW-.d.ts.map +1 -0
- package/dist/{index-CkMIKEWp.d.ts → index-BUB5SICW.d.ts} +3 -4
- package/dist/index-BUB5SICW.d.ts.map +1 -0
- package/dist/{index-tX6fKF8m.d.ts → index-BeuCXtiO.d.ts} +3 -3
- package/dist/index-BeuCXtiO.d.ts.map +1 -0
- package/dist/{index-BslNaMLo.d.ts → index-Cu5i8zZY.d.ts} +3 -3
- package/dist/index-Cu5i8zZY.d.ts.map +1 -0
- package/dist/{index-D87kRcEs.d.ts → index-RqMh1i-O.d.ts} +2 -1
- package/dist/index-RqMh1i-O.d.ts.map +1 -0
- package/dist/{index-Cdmrn0SZ.d.ts → index-ovq_GTdl.d.ts} +11 -11
- package/dist/index-ovq_GTdl.d.ts.map +1 -0
- package/dist/index.d.ts +93 -606
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1351 -3714
- package/dist/index.js.map +1 -1
- package/dist/{labelled-input-base-ORb1rGq6.d.ts → labelled-input-base-Dp_9KP0G.d.ts} +2 -3
- package/dist/labelled-input-base-Dp_9KP0G.d.ts.map +1 -0
- package/dist/{labelled-input-base-9K_w3oLj.js → labelled-input-base-hoKGwyKT.js} +2 -2
- package/dist/{labelled-input-base-9K_w3oLj.js.map → labelled-input-base-hoKGwyKT.js.map} +1 -1
- package/dist/labelled-radial-input.d.ts +4 -5
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-radial-input.js +2 -3
- package/dist/labelled-radial-input.js.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-range-input.js +2 -2
- package/dist/labelled-range-input.js.map +1 -1
- package/dist/led.d.ts +1 -2
- package/dist/led.d.ts.map +1 -1
- package/dist/led.js +1 -1
- package/dist/led.js.map +1 -1
- package/dist/menu-DIfU70AA.js +1609 -0
- package/dist/menu-DIfU70AA.js.map +1 -0
- package/dist/{menu-item-AtoP6bD4.d.ts → menu-item-BG5w7fi4.d.ts} +4 -4
- package/dist/menu-item-BG5w7fi4.d.ts.map +1 -0
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +1 -2
- package/dist/miller.d.ts +7 -5
- package/dist/miller.d.ts.map +1 -1
- package/dist/miller.js +23 -25
- package/dist/miller.js.map +1 -1
- package/dist/narrowed-text.d.ts +1 -2
- package/dist/narrowed-text.d.ts.map +1 -1
- package/dist/narrowed-text.js +14 -13
- package/dist/narrowed-text.js.map +1 -1
- package/dist/notification-pill-DyZydXoZ.d.ts +68 -0
- package/dist/notification-pill-DyZydXoZ.d.ts.map +1 -0
- package/dist/notifier.d.ts +2 -0
- package/dist/notifier.js +146 -0
- package/dist/notifier.js.map +1 -0
- package/dist/panel.d.ts +1 -1
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +1 -2
- package/dist/panel.js.map +1 -1
- package/dist/plots.d.ts +1 -1
- package/dist/plots.js +1 -1
- package/dist/polar-pad.d.ts +1 -2
- package/dist/polar-pad.d.ts.map +1 -1
- package/dist/polar-pad.js +2 -2
- package/dist/polar-pad.js.map +1 -1
- package/dist/{radial-input-Dhu5ZGP5.d.ts → radial-input-DXMN55Nz.d.ts} +2 -3
- package/dist/radial-input-DXMN55Nz.d.ts.map +1 -0
- package/dist/{radial-input-C4f-2Bya.js → radial-input-Dk8Wydp5.js} +26 -16
- package/dist/radial-input-Dk8Wydp5.js.map +1 -0
- package/dist/radial-input.d.ts +1 -1
- package/dist/radial-input.js +1 -1
- package/dist/range-input.d.ts +2 -3
- package/dist/range-input.d.ts.map +1 -1
- package/dist/range-input.js +2 -2
- package/dist/range-input.js.map +1 -1
- package/dist/range.d.ts +2 -3
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js +2 -2
- package/dist/range.js.map +1 -1
- package/dist/scroll-fade-CAs1EabA.js +70 -0
- package/dist/scroll-fade-CAs1EabA.js.map +1 -0
- package/dist/select-horiz.d.ts +50 -0
- package/dist/select-horiz.d.ts.map +1 -0
- package/dist/select-horiz.js +263 -0
- package/dist/select-horiz.js.map +1 -0
- package/dist/{slider-input-SW7e2Ac2.js → slider-input-CT4gcF-o.js} +30 -27
- package/dist/{slider-input-SW7e2Ac2.js.map → slider-input-CT4gcF-o.js.map} +1 -1
- package/dist/{slider-input-BEaztT-g.d.ts → slider-input-XppH3vcy.d.ts} +2 -3
- package/dist/slider-input-XppH3vcy.d.ts.map +1 -0
- package/dist/slider-input.d.ts +1 -1
- package/dist/slider-input.js +1 -1
- package/dist/snackbar.d.ts +58 -0
- package/dist/snackbar.d.ts.map +1 -0
- package/dist/snackbar.js +265 -0
- package/dist/snackbar.js.map +1 -0
- package/dist/snap-container.d.ts +1 -2
- package/dist/snap-container.d.ts.map +1 -1
- package/dist/snap-container.js +6 -5
- package/dist/snap-container.js.map +1 -1
- package/dist/split-layout.d.ts +1 -2
- package/dist/split-layout.d.ts.map +1 -1
- package/dist/split-layout.js +5 -3
- package/dist/split-layout.js.map +1 -1
- package/dist/swipe.d.ts +1 -2
- package/dist/swipe.d.ts.map +1 -1
- package/dist/swipe.js +4 -3
- package/dist/swipe.js.map +1 -1
- package/dist/{tab-list-B8v9KntS.d.ts → tab-list-Beuh2nom.d.ts} +4 -4
- package/dist/tab-list-Beuh2nom.d.ts.map +1 -0
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +533 -1
- package/dist/tabs.js.map +1 -0
- package/dist/{tickled-controller-JDOR9B8V.d.ts → tickled-controller-BtevrRLV.d.ts} +1 -2
- package/dist/tickled-controller-BtevrRLV.d.ts.map +1 -0
- package/dist/{tickled-styles-Bgo_Lpw-.js → tickled-controller-DkAU-0qL.js} +2 -88
- package/dist/tickled-controller-DkAU-0qL.js.map +1 -0
- package/dist/tickled-styles-BrAHEj1q.js +89 -0
- package/dist/tickled-styles-BrAHEj1q.js.map +1 -0
- package/dist/{timeline-CdSI003L.js → timeline-B2fgDL4U.js} +3 -3
- package/dist/{timeline-CdSI003L.js.map → timeline-B2fgDL4U.js.map} +1 -1
- package/dist/timeline.d.ts +1 -1
- package/dist/timeline.js +1 -1
- package/dist/{tooltip-BMTSUPBU.d.ts → tooltip-BlvBUQxx.d.ts} +2 -3
- package/dist/tooltip-BlvBUQxx.d.ts.map +1 -0
- package/dist/transitory-label.d.ts +30 -0
- package/dist/transitory-label.d.ts.map +1 -0
- package/dist/transitory-label.js +249 -0
- package/dist/transitory-label.js.map +1 -0
- package/dist/{tree-CqPhQRHo.js → tree-BO6BXI2b.js} +9 -10
- package/dist/{tree-CqPhQRHo.js.map → tree-BO6BXI2b.js.map} +1 -1
- package/dist/{tree-component-CDLO1ITF.d.ts → tree-component-BvV4muxx.d.ts} +8 -124
- package/dist/tree-component-BvV4muxx.d.ts.map +1 -0
- package/dist/tree.d.ts +9 -4
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +1 -1
- package/dist/types-Bc23SI94.d.ts +72 -0
- package/dist/types-Bc23SI94.d.ts.map +1 -0
- package/dist/types-DlVNJez_.d.ts +41 -0
- package/dist/types-DlVNJez_.d.ts.map +1 -0
- package/dist/vertical-list.d.ts +122 -0
- package/dist/vertical-list.d.ts.map +1 -0
- package/dist/vertical-list.js +737 -0
- package/dist/vertical-list.js.map +1 -0
- package/dist/{wheel-nudge-Dhzi5nHc.js → wheel-nudge-BlSCe_hD.js} +2 -2
- package/dist/wheel-nudge-BlSCe_hD.js.map +1 -0
- package/dist/{wheel-nudge-C-ra_1os.d.ts → wheel-nudge-DGrNzWuE.d.ts} +3 -4
- package/dist/wheel-nudge-DGrNzWuE.d.ts.map +1 -0
- package/dist/{xy-axis-CtDDQzGZ.d.ts → xy-axis-5KhVTNoX.d.ts} +8 -6
- package/dist/xy-axis-5KhVTNoX.d.ts.map +1 -0
- package/dist/{xy-axis-BpA2eJq6.js → xy-axis-B6yMjckW.js} +8 -112
- package/dist/xy-axis-B6yMjckW.js.map +1 -0
- package/dist/xy-pad.d.ts +1 -2
- package/dist/xy-pad.d.ts.map +1 -1
- package/dist/xy-pad.js +2 -2
- package/dist/xy-pad.js.map +1 -1
- package/docs-user/README.md +3 -2
- package/docs-user/data-grid.md +248 -0
- package/docs-user/index.json +217 -0
- package/docs-user/llms.txt +57 -0
- package/docs-user/rating.md +102 -0
- package/llms.txt +57 -0
- package/package.json +21 -11
- package/dist/crumb-navigation-Pif-kupA.js +0 -1221
- package/dist/crumb-navigation-Pif-kupA.js.map +0 -1
- package/dist/defaults-BkWCPJFL.js.map +0 -1
- package/dist/hex-editor-CMIxtNgT.d.ts.map +0 -1
- package/dist/icon-Bd5BiU4b.d.ts.map +0 -1
- package/dist/icon-CK14NUcE.js.map +0 -1
- package/dist/icons.js.map +0 -1
- package/dist/incr-search-CeERfijK.js.map +0 -1
- package/dist/index-BslNaMLo.d.ts.map +0 -1
- package/dist/index-Cdmrn0SZ.d.ts.map +0 -1
- package/dist/index-CkMIKEWp.d.ts.map +0 -1
- package/dist/index-D87kRcEs.d.ts.map +0 -1
- package/dist/index-DrKRD8_a.d.ts.map +0 -1
- package/dist/index-tX6fKF8m.d.ts.map +0 -1
- package/dist/interaction-_Y2qO3QI.js +0 -1
- package/dist/labelled-input-base-ORb1rGq6.d.ts.map +0 -1
- package/dist/menu-BiFarLUA.js +0 -826
- package/dist/menu-BiFarLUA.js.map +0 -1
- package/dist/menu-item-AtoP6bD4.d.ts.map +0 -1
- package/dist/menu-item-yHSPV8nS.js +0 -783
- package/dist/menu-item-yHSPV8nS.js.map +0 -1
- package/dist/radial-input-C4f-2Bya.js.map +0 -1
- package/dist/radial-input-Dhu5ZGP5.d.ts.map +0 -1
- package/dist/registry-CkW7q09Q.js +0 -21
- package/dist/registry-CkW7q09Q.js.map +0 -1
- package/dist/selecthorizontal.d.ts +0 -16
- package/dist/selecthorizontal.d.ts.map +0 -1
- package/dist/selecthorizontal.js +0 -43
- package/dist/selecthorizontal.js.map +0 -1
- package/dist/slider-input-BEaztT-g.d.ts.map +0 -1
- package/dist/tab-list-B8v9KntS.d.ts.map +0 -1
- package/dist/tab-list-CR0v53KJ.js +0 -615
- package/dist/tab-list-CR0v53KJ.js.map +0 -1
- package/dist/tickled-controller-JDOR9B8V.d.ts.map +0 -1
- package/dist/tickled-styles-Bgo_Lpw-.js.map +0 -1
- package/dist/tooltip-BMTSUPBU.d.ts.map +0 -1
- package/dist/tree-component-CDLO1ITF.d.ts.map +0 -1
- package/dist/types-DSfbtHs3.d.ts +0 -8
- package/dist/types-DSfbtHs3.d.ts.map +0 -1
- package/dist/wheel-nudge-C-ra_1os.d.ts.map +0 -1
- package/dist/wheel-nudge-Dhzi5nHc.js.map +0 -1
- package/dist/xy-axis-BpA2eJq6.js.map +0 -1
- package/dist/xy-axis-CtDDQzGZ.d.ts.map +0 -1
- package/docs-user/selecthorizontal.md +0 -32
- /package/bundle/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
- /package/dist/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
|
@@ -10,8 +10,9 @@ function defaultSelector(item) {
|
|
|
10
10
|
return String(item);
|
|
11
11
|
}
|
|
12
12
|
function createFuzzySearch(items, options = {}) {
|
|
13
|
+
const selector = options.selector ?? defaultSelector;
|
|
13
14
|
const fzf = new Fzf(items, {
|
|
14
|
-
selector
|
|
15
|
+
selector,
|
|
15
16
|
...options.fzf ?? {}
|
|
16
17
|
});
|
|
17
18
|
const find = (query) => {
|
|
@@ -32,37 +33,6 @@ function createFuzzySearch(items, options = {}) {
|
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
//#endregion
|
|
35
|
-
//#region src/incr-search/ac-adapter.ts
|
|
36
|
-
/**
|
|
37
|
-
* Creates an ixfx-ac-text-compatible `completionProvider` function from a
|
|
38
|
-
* static item list. Uses synchronous fuzzy search wrapped in a Promise so it
|
|
39
|
-
* fits the async provider signature.
|
|
40
|
-
*
|
|
41
|
-
* Each returned item has `positions` populated with matched character indices,
|
|
42
|
-
* which `ixfx-ac-text` uses to render CSS Custom Highlights on the label.
|
|
43
|
-
*
|
|
44
|
-
* Empty query returns all items in original order (no positions).
|
|
45
|
-
* A cancelled signal causes the returned promise to reject with an AbortError.
|
|
46
|
-
*/
|
|
47
|
-
function createAcCompletionProvider(items, options = {}) {
|
|
48
|
-
const fuzzy = createFuzzySearch(items, { selector: options.selector ?? ((item) => item.label) });
|
|
49
|
-
return (query, signal) => new Promise((resolve, reject) => {
|
|
50
|
-
if (signal.aborted) {
|
|
51
|
-
reject(new DOMException(`Aborted`, `AbortError`));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const results = fuzzy.find(query).map((r) => ({
|
|
55
|
-
...r.item,
|
|
56
|
-
positions: r.positions
|
|
57
|
-
}));
|
|
58
|
-
if (signal.aborted) {
|
|
59
|
-
reject(new DOMException(`Aborted`, `AbortError`));
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
resolve(results);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
//#endregion
|
|
66
36
|
//#region src/incr-search/incr-search.ts
|
|
67
37
|
function buildState(results, query) {
|
|
68
38
|
const positionsByItem = /* @__PURE__ */ new Map();
|
|
@@ -131,105 +101,6 @@ function createIncrSearch(options = {}) {
|
|
|
131
101
|
};
|
|
132
102
|
}
|
|
133
103
|
//#endregion
|
|
134
|
-
//#region src/incr-search/controller-tree.ts
|
|
135
|
-
const DEFAULT_HIGHLIGHT_KEY = `incr-search-highlight`;
|
|
136
|
-
/**
|
|
137
|
-
* Incremental-search controller for any component implementing `TreeSearchHost`
|
|
138
|
-
* (i.e. `ixfx-tree-list`, `ixfx-miller-list`, `ixfx-crumb-navigation`).
|
|
139
|
-
*
|
|
140
|
-
* Replaces the previous separate `IncrSearchTreeController` / `IncrSearchMillerController`
|
|
141
|
-
* classes — the unified `TreeFilterPredicate` (with depth) means one implementation works
|
|
142
|
-
* for all tree-like components.
|
|
143
|
-
*
|
|
144
|
-
* @example Using with ixfx-tree-list:
|
|
145
|
-
* ```typescript
|
|
146
|
-
* const ctrl = new IncrSearchTreeController(myTreeList, {
|
|
147
|
-
* selector: item => item.node.item.label,
|
|
148
|
-
* });
|
|
149
|
-
* ctrl.connect();
|
|
150
|
-
* ```
|
|
151
|
-
*
|
|
152
|
-
* @example Using with ixfx-miller-list (different label selector):
|
|
153
|
-
* ```typescript
|
|
154
|
-
* const ctrl = new IncrSearchTreeController(myMillerList, {
|
|
155
|
-
* selector: item => item.node.item.label,
|
|
156
|
-
* labelSelector: '.label',
|
|
157
|
-
* });
|
|
158
|
-
* ctrl.connect();
|
|
159
|
-
* ```
|
|
160
|
-
*/
|
|
161
|
-
var IncrSearchTreeController = class {
|
|
162
|
-
#el;
|
|
163
|
-
#opts;
|
|
164
|
-
#search;
|
|
165
|
-
#unsub;
|
|
166
|
-
#labelMap = /* @__PURE__ */ new Map();
|
|
167
|
-
constructor(el, opts) {
|
|
168
|
-
this.#el = el;
|
|
169
|
-
this.#opts = opts;
|
|
170
|
-
this.#search = createIncrSearch({
|
|
171
|
-
selector: opts.selector,
|
|
172
|
-
fzf: opts.fzf
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
connect() {
|
|
176
|
-
this.disconnect();
|
|
177
|
-
const items = this.#el.getNodes();
|
|
178
|
-
for (const item of items) this.#labelMap.set(this.#opts.selector(item), item);
|
|
179
|
-
this.#search.setItems(items);
|
|
180
|
-
if (this.#opts.input) {
|
|
181
|
-
this.#opts.input.addEventListener(`input`, this.#onInput);
|
|
182
|
-
this.#opts.input.addEventListener(`keydown`, this.#onKeydown);
|
|
183
|
-
}
|
|
184
|
-
this.#unsub = this.#search.onChange((state) => this.#onStateChange(state));
|
|
185
|
-
}
|
|
186
|
-
disconnect() {
|
|
187
|
-
if (this.#unsub) {
|
|
188
|
-
this.#unsub();
|
|
189
|
-
this.#unsub = void 0;
|
|
190
|
-
}
|
|
191
|
-
if (this.#opts.input) {
|
|
192
|
-
this.#opts.input.removeEventListener(`input`, this.#onInput);
|
|
193
|
-
this.#opts.input.removeEventListener(`keydown`, this.#onKeydown);
|
|
194
|
-
}
|
|
195
|
-
this.#el.filterPredicate = void 0;
|
|
196
|
-
clearHighlights({ key: this.#opts.highlightKey ?? DEFAULT_HIGHLIGHT_KEY });
|
|
197
|
-
this.#labelMap.clear();
|
|
198
|
-
}
|
|
199
|
-
async setQuery(query) {
|
|
200
|
-
await this.#search.setQuery(query);
|
|
201
|
-
}
|
|
202
|
-
async clearQuery() {
|
|
203
|
-
await this.#search.clearQuery();
|
|
204
|
-
}
|
|
205
|
-
#onInput = () => {
|
|
206
|
-
const value = this.#opts.input?.value ?? ``;
|
|
207
|
-
this.setQuery(value);
|
|
208
|
-
};
|
|
209
|
-
#onKeydown = (e) => {
|
|
210
|
-
if (e.key === `Escape` && this.#opts.input) {
|
|
211
|
-
this.#opts.input.value = ``;
|
|
212
|
-
this.clearQuery();
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
#onStateChange(state) {
|
|
216
|
-
const matched = new Set(state.results.map((r) => r.item.node.item.key));
|
|
217
|
-
const hasQuery = state.query.trim().length > 0;
|
|
218
|
-
this.#el.filterPredicate = hasQuery && matched.size > 0 ? (node, _depth) => matched.has(node.item.key) : void 0;
|
|
219
|
-
const key = this.#opts.highlightKey ?? DEFAULT_HIGHLIGHT_KEY;
|
|
220
|
-
if (!this.#el.shadowRoot) return;
|
|
221
|
-
if (!state.query.trim()) {
|
|
222
|
-
clearHighlights({ key });
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
const labelSelector = this.#opts.labelSelector ?? `.tree-label`;
|
|
226
|
-
applyHighlights(buildHighlightTargets(this.#el.shadowRoot, labelSelector, state.positionsByItem, (labelEl) => {
|
|
227
|
-
const text = labelEl.textContent ?? ``;
|
|
228
|
-
return this.#labelMap.get(text);
|
|
229
|
-
}, (labelEl) => labelEl.textContent ?? ``), { key });
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
//#endregion
|
|
233
104
|
//#region src/incr-search/element-search.ts
|
|
234
105
|
const defaultGetLabel = (el) => el.textContent?.trim() ?? ``;
|
|
235
106
|
function defaultGetKey(el) {
|
|
@@ -252,10 +123,11 @@ function createElementIncrSearch(opts) {
|
|
|
252
123
|
clearHighlights({ key: highlightKey });
|
|
253
124
|
return;
|
|
254
125
|
}
|
|
255
|
-
|
|
126
|
+
const targets = buildHighlightTargets(container, labelSelector ?? itemSelector, state.positionsByItem, (el) => {
|
|
256
127
|
const key = getKey(el);
|
|
257
128
|
return state.results.find((r) => r.item.key === key)?.item;
|
|
258
|
-
}, (el) => getLabel(el))
|
|
129
|
+
}, (el) => getLabel(el));
|
|
130
|
+
applyHighlights(targets, { key: highlightKey });
|
|
259
131
|
};
|
|
260
132
|
const unsubscribeState = search.onChange((state) => {
|
|
261
133
|
updateVisibility(state);
|
|
@@ -308,6 +180,6 @@ function createElementIncrSearch(opts) {
|
|
|
308
180
|
};
|
|
309
181
|
}
|
|
310
182
|
//#endregion
|
|
311
|
-
export {
|
|
183
|
+
export { createIncrSearch as n, createFuzzySearch as r, createElementIncrSearch as t };
|
|
312
184
|
|
|
313
|
-
//# sourceMappingURL=
|
|
185
|
+
//# sourceMappingURL=element-search-Y8SRfPGM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-search-Y8SRfPGM.js","names":["FzfAny"],"sources":["../src/incr-search/fuzzy.ts","../src/incr-search/incr-search.ts","../src/incr-search/element-search.ts"],"sourcesContent":["import type { FuzzySearch, FuzzySearchOptions, IncrSearchResult } from './types.js';\nimport { Fzf } from 'fzf';\n\nfunction defaultSelector<T>(item: T): string {\n if (typeof item === `string`)\n return item;\n const r = item as Record<string, unknown>;\n if (typeof r.label === `string`)\n return r.label;\n if (typeof r.key === `string`)\n return r.key;\n if (typeof r.value === `string`)\n return r.value;\n return String(item);\n}\n\nexport function createFuzzySearch<T>(\n items: readonly T[],\n options: FuzzySearchOptions<T> = {},\n): FuzzySearch<T> {\n const selector = options.selector ?? defaultSelector<T>;\n // fzf uses a conditional SyncOptionsTuple<T> rest-parameter overload that TypeScript\n // cannot narrow through a spread when T is generic.\n const FzfAny = Fzf as new (list: readonly T[], opts: any) => { find: (q: string) => Array<{ item: T; positions: ReadonlySet<number>; score: number }> };\n const fzf = new FzfAny(items as readonly T[], { selector, ...(options.fzf ?? {}) });\n\n const find = (query: string): ReadonlyArray<IncrSearchResult<T>> => {\n if (!query.trim()) {\n return items.map(item => ({\n item,\n positions: new Set<number>(),\n score: 0,\n }));\n }\n return fzf.find(query).map(r => ({\n item: r.item,\n positions: r.positions,\n score: r.score,\n }));\n };\n\n return { find, items };\n}\n","import type {\n IncrSearch,\n IncrSearchChangeCallback,\n IncrSearchOptions,\n IncrSearchState,\n} from './types.js';\nimport { createFuzzySearch } from './fuzzy.js';\n\nfunction buildState<T>(results: ReturnType<ReturnType<typeof createFuzzySearch<T>>[`find`]>, query: string): IncrSearchState<T> {\n const positionsByItem = new Map<T, ReadonlySet<number>>();\n for (const r of results) {\n positionsByItem.set(r.item, r.positions);\n }\n return Object.freeze({\n query,\n results: Object.freeze([...results]),\n positionsByItem,\n });\n}\n\nexport function createIncrSearch<T>(options: IncrSearchOptions<T> = {}): IncrSearch<T> {\n let items: readonly T[] = options.items ?? [];\n let query = ``;\n let fuzzy = createFuzzySearch(items, { selector: options.selector, fzf: options.fzf });\n let state: IncrSearchState<T> = buildState(fuzzy.find(query), query);\n let disposed = false;\n\n const observers = new Set<IncrSearchChangeCallback<T>>();\n\n if (options.onChange) {\n observers.add(options.onChange);\n }\n\n const notify = (s: IncrSearchState<T>): void => {\n for (const cb of observers) {\n cb(s);\n }\n };\n\n const run = async (q: string, signal?: AbortSignal): Promise<IncrSearchState<T>> => {\n // Yield to the microtask queue for a consistent async interface\n await Promise.resolve();\n if (signal?.aborted || disposed) {\n return state;\n }\n const results = fuzzy.find(q);\n if (signal?.aborted || disposed) {\n return state;\n }\n state = buildState(results, q);\n notify(state);\n return state;\n };\n\n return {\n get state() {\n return state;\n },\n\n async setItems(newItems: readonly T[], signal?: AbortSignal): Promise<void> {\n items = newItems;\n fuzzy = createFuzzySearch(items, { selector: options.selector, fzf: options.fzf });\n await run(query, signal);\n },\n\n async setQuery(q: string, signal?: AbortSignal): Promise<IncrSearchState<T>> {\n query = q;\n return run(q, signal);\n },\n\n async clearQuery(): Promise<void> {\n query = ``;\n await run(``, undefined);\n },\n\n onChange(cb: IncrSearchChangeCallback<T>): () => void {\n observers.add(cb);\n cb(state);\n return () => {\n observers.delete(cb);\n };\n },\n\n dispose(): void {\n disposed = true;\n observers.clear();\n items = [];\n },\n };\n}\n","import type {\n ElementIncrSearchOptions,\n IncrSearchChangeCallback,\n IncrSearchState,\n} from './types.js';\nimport { applyHighlights, buildHighlightTargets, clearHighlights } from './highlight.js';\nimport { createIncrSearch } from './incr-search.js';\n\ntype ElementItem = { readonly key: string; readonly label: string };\n\nconst defaultGetLabel = (el: Element): string => el.textContent?.trim() ?? ``;\n\nfunction defaultGetKey(el: Element): string {\n return el.getAttribute(`data-key`)\n ?? el.getAttribute(`data-value`)\n ?? el.textContent?.trim()\n ?? ``;\n}\n\nexport type ElementIncrSearch = {\n readonly setQuery: (query: string, signal?: AbortSignal) => Promise<void>;\n readonly clearQuery: () => Promise<void>;\n readonly refresh: () => void;\n readonly onChange: (cb: IncrSearchChangeCallback<ElementItem>) => () => void;\n readonly dispose: () => void;\n};\n\nexport function createElementIncrSearch(opts: ElementIncrSearchOptions): ElementIncrSearch {\n const {\n container,\n itemSelector,\n getLabel = defaultGetLabel,\n getKey = defaultGetKey,\n labelSelector,\n highlightKey,\n } = opts;\n\n // Map from key → element reference for highlight lookup\n let elementByKey = new Map<string, Element>();\n\n const search = createIncrSearch<ElementItem>({\n selector: item => item.label,\n fzf: opts.fzf,\n });\n\n const updateVisibility = (state: IncrSearchState<ElementItem>): void => {\n const matchedKeys = new Set(state.results.map(r => r.item.key));\n const hasQuery = state.query.trim().length > 0;\n\n for (const [key, el] of elementByKey) {\n (el as HTMLElement).hidden = hasQuery ? !matchedKeys.has(key) : false;\n }\n };\n\n const updateHighlights = (state: IncrSearchState<ElementItem>): void => {\n if (!state.query.trim()) {\n clearHighlights({ key: highlightKey });\n return;\n }\n\n const sel = labelSelector ?? itemSelector;\n const targets = buildHighlightTargets(\n container,\n sel,\n state.positionsByItem,\n (el) => {\n const key = getKey(el);\n // Find the ElementItem whose key matches this element's key\n const result = state.results.find(r => r.item.key === key);\n return result?.item;\n },\n el => getLabel(el),\n );\n applyHighlights(targets, { key: highlightKey });\n };\n\n const unsubscribeState = search.onChange((state) => {\n updateVisibility(state);\n updateHighlights(state);\n opts.onChange?.(state);\n });\n\n let refreshPending = false;\n\n const refresh = (): void => {\n if (refreshPending)\n return;\n refreshPending = true;\n void Promise.resolve().then(() => {\n refreshPending = false;\n elementByKey = new Map<string, Element>();\n const els = container.querySelectorAll(itemSelector);\n const items: ElementItem[] = [];\n for (const el of els) {\n const key = getKey(el);\n elementByKey.set(key, el);\n items.push({ key, label: getLabel(el) });\n }\n void search.setItems(items);\n });\n };\n\n const observer = new MutationObserver(() => refresh());\n observer.observe(container, { childList: true, subtree: false });\n\n refresh();\n\n return {\n async setQuery(query, signal) {\n await search.setQuery(query, signal);\n },\n\n async clearQuery() {\n await search.clearQuery();\n },\n\n refresh,\n\n onChange(cb) {\n return search.onChange(cb);\n },\n\n dispose() {\n observer.disconnect();\n unsubscribeState();\n search.dispose();\n clearHighlights({ key: highlightKey });\n },\n };\n}\n"],"mappings":";;;AAGA,SAAS,gBAAmB,MAAiB;CAC3C,IAAI,OAAO,SAAS,UAClB,OAAO;CACT,MAAM,IAAI;CACV,IAAI,OAAO,EAAE,UAAU,UACrB,OAAO,EAAE;CACX,IAAI,OAAO,EAAE,QAAQ,UACnB,OAAO,EAAE;CACX,IAAI,OAAO,EAAE,UAAU,UACrB,OAAO,EAAE;CACX,OAAO,OAAO,IAAI;AACpB;AAEA,SAAgB,kBACd,OACA,UAAiC,CAAC,GAClB;CAChB,MAAM,WAAW,QAAQ,YAAY;CAIrC,MAAM,MAAM,IAAIA,IAAO,OAAuB;EAAE;EAAU,GAAI,QAAQ,OAAO,CAAC;CAAG,CAAC;CAElF,MAAM,QAAQ,UAAsD;EAClE,IAAI,CAAC,MAAM,KAAK,GACd,OAAO,MAAM,KAAI,UAAS;GACxB;GACA,2BAAW,IAAI,IAAY;GAC3B,OAAO;EACT,EAAE;EAEJ,OAAO,IAAI,KAAK,KAAK,CAAC,CAAC,KAAI,OAAM;GAC/B,MAAM,EAAE;GACR,WAAW,EAAE;GACb,OAAO,EAAE;EACX,EAAE;CACJ;CAEA,OAAO;EAAE;EAAM;CAAM;AACvB;;;AClCA,SAAS,WAAc,SAAsE,OAAmC;CAC9H,MAAM,kCAAkB,IAAI,IAA4B;CACxD,KAAK,MAAM,KAAK,SACd,gBAAgB,IAAI,EAAE,MAAM,EAAE,SAAS;CAEzC,OAAO,OAAO,OAAO;EACnB;EACA,SAAS,OAAO,OAAO,CAAC,GAAG,OAAO,CAAC;EACnC;CACF,CAAC;AACH;AAEA,SAAgB,iBAAoB,UAAgC,CAAC,GAAkB;CACrF,IAAI,QAAsB,QAAQ,SAAS,CAAC;CAC5C,IAAI,QAAQ;CACZ,IAAI,QAAQ,kBAAkB,OAAO;EAAE,UAAU,QAAQ;EAAU,KAAK,QAAQ;CAAI,CAAC;CACrF,IAAI,QAA4B,WAAW,MAAM,KAAK,KAAK,GAAG,KAAK;CACnE,IAAI,WAAW;CAEf,MAAM,4BAAY,IAAI,IAAiC;CAEvD,IAAI,QAAQ,UACV,UAAU,IAAI,QAAQ,QAAQ;CAGhC,MAAM,UAAU,MAAgC;EAC9C,KAAK,MAAM,MAAM,WACf,GAAG,CAAC;CAER;CAEA,MAAM,MAAM,OAAO,GAAW,WAAsD;EAElF,MAAM,QAAQ,QAAQ;EACtB,IAAI,QAAQ,WAAW,UACrB,OAAO;EAET,MAAM,UAAU,MAAM,KAAK,CAAC;EAC5B,IAAI,QAAQ,WAAW,UACrB,OAAO;EAET,QAAQ,WAAW,SAAS,CAAC;EAC7B,OAAO,KAAK;EACZ,OAAO;CACT;CAEA,OAAO;EACL,IAAI,QAAQ;GACV,OAAO;EACT;EAEA,MAAM,SAAS,UAAwB,QAAqC;GAC1E,QAAQ;GACR,QAAQ,kBAAkB,OAAO;IAAE,UAAU,QAAQ;IAAU,KAAK,QAAQ;GAAI,CAAC;GACjF,MAAM,IAAI,OAAO,MAAM;EACzB;EAEA,MAAM,SAAS,GAAW,QAAmD;GAC3E,QAAQ;GACR,OAAO,IAAI,GAAG,MAAM;EACtB;EAEA,MAAM,aAA4B;GAChC,QAAQ;GACR,MAAM,IAAI,IAAI,KAAA,CAAS;EACzB;EAEA,SAAS,IAA6C;GACpD,UAAU,IAAI,EAAE;GAChB,GAAG,KAAK;GACR,aAAa;IACX,UAAU,OAAO,EAAE;GACrB;EACF;EAEA,UAAgB;GACd,WAAW;GACX,UAAU,MAAM;GAChB,QAAQ,CAAC;EACX;CACF;AACF;;;AC/EA,MAAM,mBAAmB,OAAwB,GAAG,aAAa,KAAK,KAAK;AAE3E,SAAS,cAAc,IAAqB;CAC1C,OAAO,GAAG,aAAa,UAAU,KAC5B,GAAG,aAAa,YAAY,KAC5B,GAAG,aAAa,KAAK,KACrB;AACP;AAUA,SAAgB,wBAAwB,MAAmD;CACzF,MAAM,EACJ,WACA,cACA,WAAW,iBACX,SAAS,eACT,eACA,iBACE;CAGJ,IAAI,+BAAe,IAAI,IAAqB;CAE5C,MAAM,SAAS,iBAA8B;EAC3C,WAAU,SAAQ,KAAK;EACvB,KAAK,KAAK;CACZ,CAAC;CAED,MAAM,oBAAoB,UAA8C;EACtE,MAAM,cAAc,IAAI,IAAI,MAAM,QAAQ,KAAI,MAAK,EAAE,KAAK,GAAG,CAAC;EAC9D,MAAM,WAAW,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS;EAE7C,KAAK,MAAM,CAAC,KAAK,OAAO,cACtB,GAAoB,SAAS,WAAW,CAAC,YAAY,IAAI,GAAG,IAAI;CAEpE;CAEA,MAAM,oBAAoB,UAA8C;EACtE,IAAI,CAAC,MAAM,MAAM,KAAK,GAAG;GACvB,gBAAgB,EAAE,KAAK,aAAa,CAAC;GACrC;EACF;EAGA,MAAM,UAAU,sBACd,WAFU,iBAAiB,cAI3B,MAAM,kBACL,OAAO;GACN,MAAM,MAAM,OAAO,EAAE;GAGrB,OADe,MAAM,QAAQ,MAAK,MAAK,EAAE,KAAK,QAAQ,GAC1C,CAAC,EAAE;EACjB,IACA,OAAM,SAAS,EAAE,CACnB;EACA,gBAAgB,SAAS,EAAE,KAAK,aAAa,CAAC;CAChD;CAEA,MAAM,mBAAmB,OAAO,UAAU,UAAU;EAClD,iBAAiB,KAAK;EACtB,iBAAiB,KAAK;EACtB,KAAK,WAAW,KAAK;CACvB,CAAC;CAED,IAAI,iBAAiB;CAErB,MAAM,gBAAsB;EAC1B,IAAI,gBACF;EACF,iBAAiB;EACjB,QAAa,QAAQ,CAAC,CAAC,WAAW;GAChC,iBAAiB;GACjB,+BAAe,IAAI,IAAqB;GACxC,MAAM,MAAM,UAAU,iBAAiB,YAAY;GACnD,MAAM,QAAuB,CAAC;GAC9B,KAAK,MAAM,MAAM,KAAK;IACpB,MAAM,MAAM,OAAO,EAAE;IACrB,aAAa,IAAI,KAAK,EAAE;IACxB,MAAM,KAAK;KAAE;KAAK,OAAO,SAAS,EAAE;IAAE,CAAC;GACzC;GACA,OAAY,SAAS,KAAK;EAC5B,CAAC;CACH;CAEA,MAAM,WAAW,IAAI,uBAAuB,QAAQ,CAAC;CACrD,SAAS,QAAQ,WAAW;EAAE,WAAW;EAAM,SAAS;CAAM,CAAC;CAE/D,QAAQ;CAER,OAAO;EACL,MAAM,SAAS,OAAO,QAAQ;GAC5B,MAAM,OAAO,SAAS,OAAO,MAAM;EACrC;EAEA,MAAM,aAAa;GACjB,MAAM,OAAO,WAAW;EAC1B;EAEA;EAEA,SAAS,IAAI;GACX,OAAO,OAAO,SAAS,EAAE;EAC3B;EAEA,UAAU;GACR,SAAS,WAAW;GACpB,iBAAiB;GACjB,OAAO,QAAQ;GACf,gBAAgB,EAAE,KAAK,aAAa,CAAC;EACvC;CACF;AACF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
|
|
2
|
+
import { LitElement } from "lit";
|
|
3
|
+
//#region src/form/form-values.ts
|
|
4
|
+
function collectFormValues(root) {
|
|
5
|
+
const values = {};
|
|
6
|
+
root.querySelectorAll(`[name]`).forEach((el) => {
|
|
7
|
+
const name = el.getAttribute(`name`);
|
|
8
|
+
if (!name) return;
|
|
9
|
+
if (el instanceof HTMLInputElement) {
|
|
10
|
+
if (el.type === `radio`) {
|
|
11
|
+
if (el.checked) values[name] = el.value;
|
|
12
|
+
} else if (el.type === `checkbox`) values[name] = el.checked;
|
|
13
|
+
else values[name] = el.value;
|
|
14
|
+
} else if (el instanceof HTMLSelectElement) {
|
|
15
|
+
if (el.multiple) values[name] = Array.from(el.selectedOptions).map((opt) => opt.value);
|
|
16
|
+
else values[name] = el.value;
|
|
17
|
+
} else if (el instanceof HTMLTextAreaElement) values[name] = el.value;
|
|
18
|
+
else if (`value` in el) values[name] = el.value;
|
|
19
|
+
});
|
|
20
|
+
return values;
|
|
21
|
+
}
|
|
22
|
+
function applyFormValues(root, data) {
|
|
23
|
+
root.querySelectorAll(`[name]`).forEach((el) => {
|
|
24
|
+
const name = el.getAttribute(`name`);
|
|
25
|
+
if (!name || !(name in data)) return;
|
|
26
|
+
const value = data[name];
|
|
27
|
+
if (el instanceof HTMLInputElement) {
|
|
28
|
+
if (el.type === `radio`) el.checked = el.value === value;
|
|
29
|
+
else if (el.type === `checkbox`) el.checked = Boolean(value);
|
|
30
|
+
else el.value = String(value ?? ``);
|
|
31
|
+
el.dispatchEvent(new Event(`input`, { bubbles: true }));
|
|
32
|
+
el.dispatchEvent(new Event(`change`, { bubbles: true }));
|
|
33
|
+
} else if (el instanceof HTMLSelectElement) {
|
|
34
|
+
if (el.multiple && Array.isArray(value)) Array.from(el.options).forEach((opt) => {
|
|
35
|
+
opt.selected = value.includes(opt.value);
|
|
36
|
+
});
|
|
37
|
+
else el.value = String(value ?? ``);
|
|
38
|
+
el.dispatchEvent(new Event(`input`, { bubbles: true }));
|
|
39
|
+
el.dispatchEvent(new Event(`change`, { bubbles: true }));
|
|
40
|
+
} else if (el instanceof HTMLTextAreaElement) {
|
|
41
|
+
el.value = String(value ?? ``);
|
|
42
|
+
el.dispatchEvent(new Event(`input`, { bubbles: true }));
|
|
43
|
+
el.dispatchEvent(new Event(`change`, { bubbles: true }));
|
|
44
|
+
} else if (`value` in el) el.value = value;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/form/form.ts
|
|
49
|
+
let FormElement = class FormElement extends LitElement {
|
|
50
|
+
createRenderRoot() {
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
getValues() {
|
|
54
|
+
return collectFormValues(this);
|
|
55
|
+
}
|
|
56
|
+
setValues(data) {
|
|
57
|
+
applyFormValues(this, data);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
FormElement = __decorate([safeCustomElement(`ixfx-form`)], FormElement);
|
|
61
|
+
//#endregion
|
|
62
|
+
export { FormElement as t };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=form-CsXJX15p.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-CsXJX15p.js","names":[],"sources":["../src/form/form-values.ts","../src/form/form.ts"],"sourcesContent":["export function collectFormValues(root: ParentNode): Record<string, unknown> {\n const values: Record<string, unknown> = {};\n const elements = root.querySelectorAll(`[name]`);\n\n elements.forEach((el) => {\n const name = el.getAttribute(`name`);\n if (!name) {\n return;\n }\n\n if (el instanceof HTMLInputElement) {\n if (el.type === `radio`) {\n if (el.checked) {\n values[name] = el.value;\n }\n } else if (el.type === `checkbox`) {\n values[name] = el.checked;\n } else {\n values[name] = el.value;\n }\n } else if (el instanceof HTMLSelectElement) {\n if (el.multiple) {\n values[name] = Array.from(el.selectedOptions).map(opt => opt.value);\n } else {\n values[name] = el.value;\n }\n } else if (el instanceof HTMLTextAreaElement) {\n values[name] = el.value;\n } else if (`value` in el) {\n values[name] = (el as { value: unknown }).value;\n }\n });\n\n return values;\n}\n\nexport function applyFormValues(root: ParentNode, data: Record<string, unknown>): void {\n const elements = root.querySelectorAll(`[name]`);\n\n elements.forEach((el) => {\n const name = el.getAttribute(`name`);\n if (!name || !(name in data)) {\n return;\n }\n\n const value = data[name];\n\n if (el instanceof HTMLInputElement) {\n if (el.type === `radio`) {\n el.checked = el.value === value;\n } else if (el.type === `checkbox`) {\n el.checked = Boolean(value);\n } else {\n el.value = String(value ?? ``);\n }\n el.dispatchEvent(new Event(`input`, { bubbles: true }));\n el.dispatchEvent(new Event(`change`, { bubbles: true }));\n } else if (el instanceof HTMLSelectElement) {\n if (el.multiple && Array.isArray(value)) {\n Array.from(el.options).forEach((opt) => {\n opt.selected = value.includes(opt.value);\n });\n } else {\n el.value = String(value ?? ``);\n }\n el.dispatchEvent(new Event(`input`, { bubbles: true }));\n el.dispatchEvent(new Event(`change`, { bubbles: true }));\n } else if (el instanceof HTMLTextAreaElement) {\n el.value = String(value ?? ``);\n el.dispatchEvent(new Event(`input`, { bubbles: true }));\n el.dispatchEvent(new Event(`change`, { bubbles: true }));\n } else if (`value` in el) {\n (el as { value: unknown }).value = value;\n }\n });\n}","import { LitElement } from 'lit';\nimport { safeCustomElement } from '../util/custom-elements.js';\nimport { applyFormValues, collectFormValues } from './form-values.js';\nimport './form.css';\n\n@safeCustomElement(`ixfx-form`)\nexport class FormElement extends LitElement {\n protected override createRenderRoot() {\n return this;\n }\n\n getValues(): Record<string, unknown> {\n return collectFormValues(this);\n }\n\n setValues(data: Record<string, unknown>): void {\n applyFormValues(this, data);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-form': FormElement;\n }\n}"],"mappings":";;;AAAA,SAAgB,kBAAkB,MAA2C;CAC3E,MAAM,SAAkC,CAAC;CAGzC,KAFsB,iBAAiB,QAEhC,CAAC,CAAC,SAAS,OAAO;EACvB,MAAM,OAAO,GAAG,aAAa,MAAM;EACnC,IAAI,CAAC,MACH;EAGF,IAAI,cAAc,kBAAkB;GAClC,IAAI,GAAG,SAAS,SACV;QAAA,GAAG,SACL,OAAO,QAAQ,GAAG;GAAA,OAEf,IAAI,GAAG,SAAS,YACrB,OAAO,QAAQ,GAAG;QAElB,OAAO,QAAQ,GAAG;EAEtB,OAAO,IAAI,cAAc,mBAAmB;GAC1C,IAAI,GAAG,UACL,OAAO,QAAQ,MAAM,KAAK,GAAG,eAAe,CAAC,CAAC,KAAI,QAAO,IAAI,KAAK;QAElE,OAAO,QAAQ,GAAG;EAEtB,OAAO,IAAI,cAAc,qBACvB,OAAO,QAAQ,GAAG;OACb,IAAI,WAAW,IACpB,OAAO,QAAS,GAA0B;CAE9C,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,gBAAgB,MAAkB,MAAqC;CAGrF,KAFsB,iBAAiB,QAEhC,CAAC,CAAC,SAAS,OAAO;EACvB,MAAM,OAAO,GAAG,aAAa,MAAM;EACnC,IAAI,CAAC,QAAQ,EAAE,QAAQ,OACrB;EAGF,MAAM,QAAQ,KAAK;EAEnB,IAAI,cAAc,kBAAkB;GAClC,IAAI,GAAG,SAAS,SACd,GAAG,UAAU,GAAG,UAAU;QACrB,IAAI,GAAG,SAAS,YACrB,GAAG,UAAU,QAAQ,KAAK;QAE1B,GAAG,QAAQ,OAAO,SAAS,EAAE;GAE/B,GAAG,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;GACtD,GAAG,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;EACzD,OAAO,IAAI,cAAc,mBAAmB;GAC1C,IAAI,GAAG,YAAY,MAAM,QAAQ,KAAK,GACpC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,SAAS,QAAQ;IACtC,IAAI,WAAW,MAAM,SAAS,IAAI,KAAK;GACzC,CAAC;QAED,GAAG,QAAQ,OAAO,SAAS,EAAE;GAE/B,GAAG,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;GACtD,GAAG,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;EACzD,OAAO,IAAI,cAAc,qBAAqB;GAC5C,GAAG,QAAQ,OAAO,SAAS,EAAE;GAC7B,GAAG,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;GACtD,GAAG,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;EACzD,OAAO,IAAI,WAAW,IACpB,GAA2B,QAAQ;CAEvC,CAAC;AACH;;;ACrEO,IAAM,cAAN,MAAM,oBAAoB,WAAW;CAC1C,mBAAsC;EACpC,OAAO;CACT;CAEA,YAAqC;EACnC,OAAO,kBAAkB,IAAI;CAC/B;CAEA,UAAU,MAAqC;EAC7C,gBAAgB,MAAM,IAAI;CAC5B;AACF;AAbC,cAAA,WAAA,CAAA,kBAAkB,WAAW,CAAA,GAAA,WAAA"}
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
//#region src/form/form.d.ts
|
|
3
|
+
declare class FormElement extends LitElement {
|
|
4
|
+
protected createRenderRoot(): this;
|
|
5
|
+
getValues(): Record<string, unknown>;
|
|
6
|
+
setValues(data: Record<string, unknown>): void;
|
|
7
|
+
}
|
|
8
|
+
declare global {
|
|
9
|
+
interface HTMLElementTagNameMap {
|
|
10
|
+
'ixfx-form': FormElement;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { FormElement };
|
|
15
|
+
//# sourceMappingURL=form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.d.ts","names":[],"sources":["../src/form/form.ts"],"mappings":";;cAMa,oBAAoB;YACZ;EAInB,aAAa;EAIb,UAAU,MAAM;;QAKV;YACI;IACR,aAAa"}
|
package/dist/form.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { r as IDataProvider } from "./data-provider-BAn_-oR1.js";
|
|
2
|
+
import { l as ListSelectionMode } from "./types-DlVNJez_.js";
|
|
3
|
+
import "./panel.js";
|
|
4
|
+
import { LitElement, PropertyValues, nothing } from "lit";
|
|
5
|
+
import { CommandId } from "@clinth/ui-commands";
|
|
6
|
+
//#region src/grouped-item-lister/types.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Fired when the selection set changes by any means.
|
|
9
|
+
* Event name: `select`
|
|
10
|
+
*/
|
|
11
|
+
type SubComponentSelectDetail<T> = {
|
|
12
|
+
readonly selected: ReadonlySet<T>;
|
|
13
|
+
readonly previous: ReadonlySet<T>;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Fired when an item is explicitly activated (double-click or Enter key).
|
|
17
|
+
* Event name: `activate`
|
|
18
|
+
*/
|
|
19
|
+
type SubComponentActivateDetail<T> = {
|
|
20
|
+
readonly item: T;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Fired on a raw pointer click before any selection change.
|
|
24
|
+
* Event name: `item-click`
|
|
25
|
+
*/
|
|
26
|
+
type SubComponentItemClickDetail<T> = {
|
|
27
|
+
readonly item: T;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Fired when the pointer hovers over an item or the keyboard cursor moves to it.
|
|
31
|
+
* Event name: `tickle`
|
|
32
|
+
*/
|
|
33
|
+
type SubComponentTickleDetail<T> = {
|
|
34
|
+
readonly item: T;
|
|
35
|
+
};
|
|
36
|
+
type SubComponentSelectEvent<T> = CustomEvent<SubComponentSelectDetail<T>>;
|
|
37
|
+
type SubComponentActivateEvent<T> = CustomEvent<SubComponentActivateDetail<T>>;
|
|
38
|
+
type SubComponentItemClickEvent<T> = CustomEvent<SubComponentItemClickDetail<T>>;
|
|
39
|
+
type SubComponentTickleEvent<T> = CustomEvent<SubComponentTickleDetail<T>>;
|
|
40
|
+
interface IGroupedSubComponentEventMap<T> {
|
|
41
|
+
'select': SubComponentSelectEvent<T>;
|
|
42
|
+
'activate': SubComponentActivateEvent<T>;
|
|
43
|
+
'item-click': SubComponentItemClickEvent<T>;
|
|
44
|
+
'tickle': SubComponentTickleEvent<T>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Contract that sub-components passed to `ixfx-grouped-item-lister` must satisfy.
|
|
48
|
+
*
|
|
49
|
+
* All four events (`select`, `activate`, `item-click`, `tickle`) must bubble and be composed
|
|
50
|
+
* so they propagate through the lister's light DOM to external listeners.
|
|
51
|
+
*/
|
|
52
|
+
interface IGroupedSubComponent<T> extends HTMLElement {
|
|
53
|
+
/** Replace the full item list. Called whenever the lister's items or grouping changes. */
|
|
54
|
+
setItems: (items: readonly T[]) => void;
|
|
55
|
+
/** Clear the current selection without firing a `select` event (avoids re-entrant loops). */
|
|
56
|
+
clearSelection: () => void;
|
|
57
|
+
/** The current selection set. */
|
|
58
|
+
readonly selectedItems: ReadonlySet<T>;
|
|
59
|
+
/** Controls how many items can be selected simultaneously. */
|
|
60
|
+
selectionMode: ListSelectionMode;
|
|
61
|
+
/**
|
|
62
|
+
* Forward a command (e.g. sort-order change) to this sub-component.
|
|
63
|
+
* Uses `CommandId` from `@clinth/ui-commands` for interoperability.
|
|
64
|
+
*/
|
|
65
|
+
dispatch: (commandId: CommandId, args?: Record<string, unknown>) => void;
|
|
66
|
+
addEventListener: (<K extends keyof IGroupedSubComponentEventMap<T>>(type: K, listener: (event: IGroupedSubComponentEventMap<T>[K]) => void, options?: boolean | AddEventListenerOptions) => void) & ((type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => void);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Factory that creates a sub-component for a given group.
|
|
70
|
+
* Called once per group (or once with `undefined` in ungrouped mode).
|
|
71
|
+
*/
|
|
72
|
+
type SubComponentFactory<T> = (groupKey: string | undefined) => HTMLElement & IGroupedSubComponent<T>;
|
|
73
|
+
/**
|
|
74
|
+
* Fired by `ixfx-grouped-item-lister` after any sub-component fires `select`.
|
|
75
|
+
* Carries the full cross-group selection union and the key of the group whose
|
|
76
|
+
* selection just changed (or `undefined` in ungrouped mode).
|
|
77
|
+
*/
|
|
78
|
+
type GroupedSelectDetail<T> = {
|
|
79
|
+
readonly selected: ReadonlySet<T>;
|
|
80
|
+
readonly groupKey: string | undefined;
|
|
81
|
+
};
|
|
82
|
+
type GroupedSelectEvent<T> = CustomEvent<GroupedSelectDetail<T>>;
|
|
83
|
+
interface GroupedItemListerElementEventMap<T> {
|
|
84
|
+
'grouped-select': GroupedSelectEvent<T>;
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/grouped-item-lister/grouped-item-lister.d.ts
|
|
88
|
+
declare class GroupedItemListerElement<T = unknown> extends LitElement {
|
|
89
|
+
#private;
|
|
90
|
+
grouped: boolean;
|
|
91
|
+
crossGroupSelection: boolean;
|
|
92
|
+
get groupBy(): ((item: T) => string) | undefined;
|
|
93
|
+
set groupBy(value: ((item: T) => string) | undefined);
|
|
94
|
+
get factory(): SubComponentFactory<T> | undefined;
|
|
95
|
+
set factory(value: SubComponentFactory<T> | undefined);
|
|
96
|
+
get provider(): IDataProvider<void, T> | undefined;
|
|
97
|
+
set provider(value: IDataProvider<void, T> | undefined);
|
|
98
|
+
/** The union of all selected items across every sub-component. */
|
|
99
|
+
get selectedItems(): ReadonlySet<T>;
|
|
100
|
+
/**
|
|
101
|
+
* Forward a command to all live sub-components.
|
|
102
|
+
* Use this to propagate changes like sort order, filter state, or view mode.
|
|
103
|
+
*/
|
|
104
|
+
dispatch(commandId: CommandId, args?: Record<string, unknown>): void;
|
|
105
|
+
protected createRenderRoot(): this;
|
|
106
|
+
connectedCallback(): void;
|
|
107
|
+
disconnectedCallback(): void;
|
|
108
|
+
protected updated(changed: PropertyValues): void;
|
|
109
|
+
protected render(): import("lit").TemplateResult<1> | typeof nothing;
|
|
110
|
+
}
|
|
111
|
+
declare global {
|
|
112
|
+
interface HTMLElementTagNameMap {
|
|
113
|
+
'ixfx-grouped-item-lister': GroupedItemListerElement;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
interface GroupedItemListerElement<T> {
|
|
117
|
+
addEventListener: (<K extends keyof GroupedItemListerElementEventMap<T>>(type: K, listener: (this: GroupedItemListerElement<T>, event: GroupedItemListerElementEventMap<T>[K]) => void, options?: boolean | AddEventListenerOptions) => void) & ((type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => void);
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { IGroupedSubComponent as a, SubComponentActivateEvent as c, SubComponentItemClickEvent as d, SubComponentSelectDetail as f, SubComponentTickleEvent as h, GroupedSelectEvent as i, SubComponentFactory as l, SubComponentTickleDetail as m, GroupedItemListerElementEventMap as n, IGroupedSubComponentEventMap as o, SubComponentSelectEvent as p, GroupedSelectDetail as r, SubComponentActivateDetail as s, GroupedItemListerElement as t, SubComponentItemClickDetail as u };
|
|
121
|
+
//# sourceMappingURL=grouped-item-lister-CNLMgdIr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grouped-item-lister-CNLMgdIr.d.ts","names":[],"sources":["../src/grouped-item-lister/types.ts","../src/grouped-item-lister/grouped-item-lister.ts"],"mappings":";;;;;;;;;;KAWY,yBAAyB;WAC1B,UAAU,YAAY;WACtB,UAAU,YAAY;;;;;;KAOrB,2BAA2B;WAC5B,MAAM;;;;;;KAOL,4BAA4B;WAC7B,MAAM;;;;;;KAOL,yBAAyB;WAC1B,MAAM;;KAGL,wBAAwB,KAAK,YAAY,yBAAyB;KAClE,0BAA0B,KAAK,YAAY,2BAA2B;KACtE,2BAA2B,KAAK,YAAY,4BAA4B;KACxE,wBAAwB,KAAK,YAAY,yBAAyB;UAE7D,6BAA6B;EAC5C,UAAU,wBAAwB;EAClC,YAAY,0BAA0B;EACtC,cAAc,2BAA2B;EACzC,UAAU,wBAAwB;;;;;;;;UAanB,qBAAqB,WAAW;;EAE/C,WAAW,gBAAgB;;EAG3B;;WAGS,eAAe,YAAY;;EAGpC,eAAe;;;;;EAMf,WAAW,WAAW,WAAW,OAAO;EAExC,oBAAoB,gBAAgB,6BAA6B,IAC/D,MAAM,GACN,WAAW,OAAO,6BAA6B,GAAG,aAClD,oBAAoB,sCAEpB,cACA,UAAU,oCACV,oBAAoB;;;;;;KAQZ,oBAAoB,MAAM,iCAAiC,cAAc,qBAAqB;;;;;;KAW9F,oBAAoB;WACrB,UAAU,YAAY;WACtB;;KAGC,mBAAmB,KAAK,YAAY,oBAAoB;UAEnD,iCAAiC;EAChD,kBAAkB,mBAAmB;;;;cC9E1B,yBAAyB,qBAAqB;;EACb;EAG5C;MAgBI,aAAa,MAAM;MAInB,QAAQ,SAAS,MAAM;MAKvB,WAAW,oBAAoB;MAI/B,QAAQ,OAAO,oBAAoB;MAQnC,YAAY,oBAAoB;MAIhC,SAAS,OAAO,oBAAoB;;MAWpC,iBAAiB,YAAY;;;;;EAcjC,SAAS,WAAW,WAAW,OAAO;YAUnB;EAIV;EAKA;YAKU,QAAQ,SAAS;YAQjB,wBAAM,2BAAA;;QAmJnB;YACI;IACR,4BAA4B;;;UAIf,yBAAyB;EACxC,oBAAoB,gBAAgB,iCAAiC,IACnE,MAAM,GACN,WAAW,MAAM,yBAAyB,IAAI,OAAO,iCAAiC,GAAG,aACzF,oBAAoB,sCAEpB,cACA,UAAU,oCACV,oBAAoB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as IGroupedSubComponent, c as SubComponentActivateEvent, d as SubComponentItemClickEvent, f as SubComponentSelectDetail, h as SubComponentTickleEvent, i as GroupedSelectEvent, l as SubComponentFactory, m as SubComponentTickleDetail, n as GroupedItemListerElementEventMap, o as IGroupedSubComponentEventMap, p as SubComponentSelectEvent, r as GroupedSelectDetail, s as SubComponentActivateDetail, t as GroupedItemListerElement, u as SubComponentItemClickDetail } from "./grouped-item-lister-CNLMgdIr.js";
|
|
2
|
+
export { GroupedItemListerElement, type GroupedItemListerElementEventMap, type GroupedSelectDetail, type GroupedSelectEvent, type IGroupedSubComponent, type IGroupedSubComponentEventMap, type SubComponentActivateDetail, type SubComponentActivateEvent, type SubComponentFactory, type SubComponentItemClickDetail, type SubComponentItemClickEvent, type SubComponentSelectDetail, type SubComponentSelectEvent, type SubComponentTickleDetail, type SubComponentTickleEvent };
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
|
|
2
|
+
import { t as DataController } from "./data-controller-DOlmqNKb.js";
|
|
3
|
+
import "./panel.js";
|
|
4
|
+
import { LitElement, html, nothing } from "lit";
|
|
5
|
+
import { property } from "lit/decorators.js";
|
|
6
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
7
|
+
//#region src/grouped-item-lister/grouped-item-lister.ts
|
|
8
|
+
const UNGROUPED_KEY = `__ungrouped__`;
|
|
9
|
+
let GroupedItemListerElement = class GroupedItemListerElement extends LitElement {
|
|
10
|
+
constructor(..._args) {
|
|
11
|
+
super(..._args);
|
|
12
|
+
this.grouped = false;
|
|
13
|
+
this.crossGroupSelection = false;
|
|
14
|
+
this.#subComponents = /* @__PURE__ */ new Map();
|
|
15
|
+
this.#clearingSelection = false;
|
|
16
|
+
this.#data = new DataController(this);
|
|
17
|
+
this.#onSelect = (event) => {
|
|
18
|
+
if (this.#clearingSelection) return;
|
|
19
|
+
const sourceEl = event.target;
|
|
20
|
+
let sourceKey;
|
|
21
|
+
for (const [key, subEl] of this.#subComponents) if (subEl === sourceEl) {
|
|
22
|
+
sourceKey = key;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
if (sourceKey === void 0) return;
|
|
26
|
+
if (this.grouped && !this.crossGroupSelection) {
|
|
27
|
+
this.#clearingSelection = true;
|
|
28
|
+
for (const [key, subEl] of this.#subComponents) if (key !== sourceKey) subEl.clearSelection();
|
|
29
|
+
this.#clearingSelection = false;
|
|
30
|
+
}
|
|
31
|
+
this.dispatchEvent(new CustomEvent(`grouped-select`, {
|
|
32
|
+
detail: {
|
|
33
|
+
selected: this.selectedItems,
|
|
34
|
+
groupKey: sourceKey === UNGROUPED_KEY ? void 0 : sourceKey
|
|
35
|
+
},
|
|
36
|
+
bubbles: true,
|
|
37
|
+
composed: true
|
|
38
|
+
}));
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
#groupBy;
|
|
42
|
+
#factory;
|
|
43
|
+
#subComponents;
|
|
44
|
+
#renderedGroups;
|
|
45
|
+
#clearingSelection;
|
|
46
|
+
#data;
|
|
47
|
+
get groupBy() {
|
|
48
|
+
return this.#groupBy;
|
|
49
|
+
}
|
|
50
|
+
set groupBy(value) {
|
|
51
|
+
this.#groupBy = value;
|
|
52
|
+
this.requestUpdate();
|
|
53
|
+
}
|
|
54
|
+
get factory() {
|
|
55
|
+
return this.#factory;
|
|
56
|
+
}
|
|
57
|
+
set factory(value) {
|
|
58
|
+
if (value === this.#factory) return;
|
|
59
|
+
this.#clearSubComponents();
|
|
60
|
+
this.#factory = value;
|
|
61
|
+
this.requestUpdate();
|
|
62
|
+
}
|
|
63
|
+
get provider() {
|
|
64
|
+
return this.#data.provider;
|
|
65
|
+
}
|
|
66
|
+
set provider(value) {
|
|
67
|
+
this.#data.provider = value;
|
|
68
|
+
if (value) this.#data.request(void 0);
|
|
69
|
+
}
|
|
70
|
+
/** The union of all selected items across every sub-component. */
|
|
71
|
+
get selectedItems() {
|
|
72
|
+
const all = /* @__PURE__ */ new Set();
|
|
73
|
+
for (const subEl of this.#subComponents.values()) for (const item of subEl.selectedItems) all.add(item);
|
|
74
|
+
return all;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Forward a command to all live sub-components.
|
|
78
|
+
* Use this to propagate changes like sort order, filter state, or view mode.
|
|
79
|
+
*/
|
|
80
|
+
dispatch(commandId, args) {
|
|
81
|
+
for (const subEl of this.#subComponents.values()) subEl.dispatch(commandId, args);
|
|
82
|
+
}
|
|
83
|
+
createRenderRoot() {
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
connectedCallback() {
|
|
87
|
+
super.connectedCallback();
|
|
88
|
+
this.addEventListener(`select`, this.#onSelect);
|
|
89
|
+
}
|
|
90
|
+
disconnectedCallback() {
|
|
91
|
+
super.disconnectedCallback();
|
|
92
|
+
this.removeEventListener(`select`, this.#onSelect);
|
|
93
|
+
}
|
|
94
|
+
updated(changed) {
|
|
95
|
+
super.updated(changed);
|
|
96
|
+
if (changed.has(`grouped`)) this.#clearSubComponents();
|
|
97
|
+
this.#syncSubComponents();
|
|
98
|
+
}
|
|
99
|
+
render() {
|
|
100
|
+
if (!this.#factory) return nothing;
|
|
101
|
+
if (!this.grouped) return html`<div class="ungrouped-host"></div>`;
|
|
102
|
+
if (!this.#groupBy) return nothing;
|
|
103
|
+
this.#renderedGroups = this.#computeGroups();
|
|
104
|
+
return html`${repeat(this.#renderedGroups.keys(), (key) => key, (key) => html`<ixfx-panel data-group-key=${key} .title=${key}></ixfx-panel>`)}`;
|
|
105
|
+
}
|
|
106
|
+
#computeGroups() {
|
|
107
|
+
const groups = /* @__PURE__ */ new Map();
|
|
108
|
+
if (!this.#groupBy) return groups;
|
|
109
|
+
for (const item of this.#data.items) {
|
|
110
|
+
const key = this.#groupBy(item);
|
|
111
|
+
let bucket = groups.get(key);
|
|
112
|
+
if (!bucket) {
|
|
113
|
+
bucket = [];
|
|
114
|
+
groups.set(key, bucket);
|
|
115
|
+
}
|
|
116
|
+
bucket.push(item);
|
|
117
|
+
}
|
|
118
|
+
return groups;
|
|
119
|
+
}
|
|
120
|
+
#clearSubComponents() {
|
|
121
|
+
for (const subEl of this.#subComponents.values()) subEl.remove();
|
|
122
|
+
this.#subComponents.clear();
|
|
123
|
+
}
|
|
124
|
+
#syncSubComponents() {
|
|
125
|
+
if (!this.#factory) return;
|
|
126
|
+
if (!this.grouped) {
|
|
127
|
+
this.#syncUngrouped();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (!this.#groupBy) return;
|
|
131
|
+
this.#syncGrouped();
|
|
132
|
+
}
|
|
133
|
+
#syncUngrouped() {
|
|
134
|
+
const host = this.querySelector(`.ungrouped-host`);
|
|
135
|
+
if (!host) return;
|
|
136
|
+
let subEl = this.#subComponents.get(UNGROUPED_KEY);
|
|
137
|
+
if (!subEl) {
|
|
138
|
+
subEl = this.#factory(void 0);
|
|
139
|
+
host.appendChild(subEl);
|
|
140
|
+
this.#subComponents.set(UNGROUPED_KEY, subEl);
|
|
141
|
+
}
|
|
142
|
+
subEl.setItems(this.#data.items);
|
|
143
|
+
}
|
|
144
|
+
#syncGrouped() {
|
|
145
|
+
const groups = this.#renderedGroups ?? this.#computeGroups();
|
|
146
|
+
for (const key of [...this.#subComponents.keys()]) if (!groups.has(key)) {
|
|
147
|
+
this.#subComponents.get(key)?.remove();
|
|
148
|
+
this.#subComponents.delete(key);
|
|
149
|
+
}
|
|
150
|
+
for (const [key, items] of groups) {
|
|
151
|
+
const panel = [...this.children].find((el) => el instanceof HTMLElement && el.dataset.groupKey === key);
|
|
152
|
+
if (!panel) continue;
|
|
153
|
+
let subEl = this.#subComponents.get(key);
|
|
154
|
+
if (!subEl) {
|
|
155
|
+
subEl = this.#factory(key);
|
|
156
|
+
panel.appendChild(subEl);
|
|
157
|
+
this.#subComponents.set(key, subEl);
|
|
158
|
+
}
|
|
159
|
+
subEl.setItems(items);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
#onSelect;
|
|
163
|
+
};
|
|
164
|
+
__decorate([property({
|
|
165
|
+
type: Boolean,
|
|
166
|
+
reflect: true
|
|
167
|
+
})], GroupedItemListerElement.prototype, "grouped", void 0);
|
|
168
|
+
__decorate([property({
|
|
169
|
+
type: Boolean,
|
|
170
|
+
attribute: `cross-group-selection`,
|
|
171
|
+
reflect: true
|
|
172
|
+
})], GroupedItemListerElement.prototype, "crossGroupSelection", void 0);
|
|
173
|
+
GroupedItemListerElement = __decorate([safeCustomElement(`ixfx-grouped-item-lister`)], GroupedItemListerElement);
|
|
174
|
+
//#endregion
|
|
175
|
+
export { GroupedItemListerElement };
|
|
176
|
+
|
|
177
|
+
//# sourceMappingURL=grouped-item-lister.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grouped-item-lister.js","names":["#clearingSelection","#subComponents","#data","#groupBy","#factory","#clearSubComponents","#onSelect","#syncSubComponents","#renderedGroups","#computeGroups","#syncUngrouped","#syncGrouped","customElement"],"sources":["../src/grouped-item-lister/grouped-item-lister.ts"],"sourcesContent":["/* eslint-disable ts/no-unsafe-declaration-merging */\nimport type { CommandId } from '@clinth/ui-commands';\nimport type { PropertyValues } from 'lit';\nimport type { IDataProvider } from '../common/data-provider.js';\nimport type {\n GroupedItemListerElementEventMap,\n GroupedSelectDetail,\n IGroupedSubComponent,\n SubComponentFactory,\n} from './types.js';\nimport { html, LitElement, nothing } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { DataController } from '../common/data-controller.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport '../panel/panel.js';\n\nexport type {\n GroupedItemListerElementEventMap,\n GroupedSelectDetail,\n GroupedSelectEvent,\n IGroupedSubComponent,\n IGroupedSubComponentEventMap,\n SubComponentActivateDetail,\n SubComponentActivateEvent,\n SubComponentFactory,\n SubComponentItemClickDetail,\n SubComponentItemClickEvent,\n SubComponentSelectDetail,\n SubComponentSelectEvent,\n SubComponentTickleDetail,\n SubComponentTickleEvent,\n} from './types.js';\n\nconst UNGROUPED_KEY = `__ungrouped__`;\n\n@customElement(`ixfx-grouped-item-lister`)\nexport class GroupedItemListerElement<T = unknown> extends LitElement {\n @property({ type: Boolean, reflect: true }) grouped = false;\n\n @property({ type: Boolean, attribute: `cross-group-selection`, reflect: true })\n crossGroupSelection = false;\n\n #groupBy: ((item: T) => string) | undefined;\n #factory: SubComponentFactory<T> | undefined;\n #subComponents = new Map<string, HTMLElement & IGroupedSubComponent<T>>();\n // Computed in render() and reused in updated() to avoid a second groupBy pass\n #renderedGroups: Map<string, T[]> | undefined;\n // Guard against re-entrant select events when we call clearSelection() on siblings\n #clearingSelection = false;\n\n readonly #data = new DataController<void, T>(this);\n\n // ---------------------------------------------------------------------------\n // JS-only properties (not HTML attributes — carry function/type information)\n // ---------------------------------------------------------------------------\n\n get groupBy(): ((item: T) => string) | undefined {\n return this.#groupBy;\n }\n\n set groupBy(value: ((item: T) => string) | undefined) {\n this.#groupBy = value;\n this.requestUpdate();\n }\n\n get factory(): SubComponentFactory<T> | undefined {\n return this.#factory;\n }\n\n set factory(value: SubComponentFactory<T> | undefined) {\n if (value === this.#factory)\n return;\n this.#clearSubComponents();\n this.#factory = value;\n this.requestUpdate();\n }\n\n get provider(): IDataProvider<void, T> | undefined {\n return this.#data.provider;\n }\n\n set provider(value: IDataProvider<void, T> | undefined) {\n this.#data.provider = value;\n if (value)\n this.#data.request(undefined);\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n /** The union of all selected items across every sub-component. */\n get selectedItems(): ReadonlySet<T> {\n const all = new Set<T>();\n for (const subEl of this.#subComponents.values()) {\n for (const item of subEl.selectedItems) {\n all.add(item);\n }\n }\n return all;\n }\n\n /**\n * Forward a command to all live sub-components.\n * Use this to propagate changes like sort order, filter state, or view mode.\n */\n dispatch(commandId: CommandId, args?: Record<string, unknown>): void {\n for (const subEl of this.#subComponents.values()) {\n subEl.dispatch(commandId, args);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Lit lifecycle\n // ---------------------------------------------------------------------------\n\n protected override createRenderRoot() {\n return this;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener(`select`, this.#onSelect as EventListener);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener(`select`, this.#onSelect as EventListener);\n }\n\n protected override updated(changed: PropertyValues): void {\n super.updated(changed);\n if (changed.has(`grouped`)) {\n this.#clearSubComponents();\n }\n this.#syncSubComponents();\n }\n\n protected override render() {\n if (!this.#factory)\n return nothing;\n\n if (!this.grouped) {\n return html`<div class=\"ungrouped-host\"></div>`;\n }\n\n if (!this.#groupBy)\n return nothing;\n\n // Store computed groups so updated() can reuse them without a second pass\n this.#renderedGroups = this.#computeGroups();\n return html`${repeat(\n this.#renderedGroups.keys(),\n key => key,\n key => html`<ixfx-panel data-group-key=${key} .title=${key}></ixfx-panel>`,\n )}`;\n }\n\n // ---------------------------------------------------------------------------\n // Internal — DOM management\n // ---------------------------------------------------------------------------\n\n #computeGroups(): Map<string, T[]> {\n const groups = new Map<string, T[]>();\n if (!this.#groupBy)\n return groups;\n for (const item of this.#data.items) {\n const key = this.#groupBy(item);\n let bucket = groups.get(key);\n if (!bucket) {\n bucket = [];\n groups.set(key, bucket);\n }\n bucket.push(item);\n }\n return groups;\n }\n\n #clearSubComponents(): void {\n for (const subEl of this.#subComponents.values()) {\n subEl.remove();\n }\n this.#subComponents.clear();\n }\n\n #syncSubComponents(): void {\n if (!this.#factory)\n return;\n\n if (!this.grouped) {\n this.#syncUngrouped();\n return;\n }\n\n if (!this.#groupBy)\n return;\n this.#syncGrouped();\n }\n\n #syncUngrouped(): void {\n const host = this.querySelector<HTMLElement>(`.ungrouped-host`);\n if (!host)\n return;\n\n let subEl = this.#subComponents.get(UNGROUPED_KEY);\n if (!subEl) {\n subEl = this.#factory!(undefined);\n host.appendChild(subEl);\n this.#subComponents.set(UNGROUPED_KEY, subEl);\n }\n subEl.setItems(this.#data.items);\n }\n\n #syncGrouped(): void {\n const groups = this.#renderedGroups ?? this.#computeGroups();\n\n // Remove sub-components for groups that no longer exist\n for (const key of [...this.#subComponents.keys()]) {\n if (!groups.has(key)) {\n this.#subComponents.get(key)?.remove();\n this.#subComponents.delete(key);\n }\n }\n\n // Mount or update sub-components per group.\n // Panels are already in the DOM at this point (rendered by Lit's repeat above).\n for (const [key, items] of groups) {\n const panel = [...this.children].find(\n el => el instanceof HTMLElement && (el as HTMLElement).dataset.groupKey === key,\n ) as HTMLElement | undefined;\n if (!panel)\n continue;\n\n let subEl = this.#subComponents.get(key);\n if (!subEl) {\n subEl = this.#factory!(key);\n panel.appendChild(subEl);\n this.#subComponents.set(key, subEl);\n }\n subEl.setItems(items);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal — selection coordination\n // ---------------------------------------------------------------------------\n\n #onSelect = (event: Event): void => {\n if (this.#clearingSelection)\n return;\n\n const sourceEl = event.target as HTMLElement;\n let sourceKey: string | undefined;\n for (const [key, subEl] of this.#subComponents) {\n if (subEl === sourceEl) {\n sourceKey = key;\n break;\n }\n }\n\n if (sourceKey === undefined)\n return;\n\n if (this.grouped && !this.crossGroupSelection) {\n this.#clearingSelection = true;\n for (const [key, subEl] of this.#subComponents) {\n if (key !== sourceKey)\n subEl.clearSelection();\n }\n this.#clearingSelection = false;\n }\n\n this.dispatchEvent(\n new CustomEvent<GroupedSelectDetail<T>>(`grouped-select`, {\n detail: {\n selected: this.selectedItems,\n groupKey: sourceKey === UNGROUPED_KEY ? undefined : sourceKey,\n },\n bubbles: true,\n composed: true,\n }),\n );\n };\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-grouped-item-lister': GroupedItemListerElement;\n }\n}\n\nexport interface GroupedItemListerElement<T> {\n addEventListener: (<K extends keyof GroupedItemListerElementEventMap<T>>(\n type: K,\n listener: (this: GroupedItemListerElement<T>, event: GroupedItemListerElementEventMap<T>[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ) => void) & ((\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ) => void);\n}\n"],"mappings":"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"}
|