@ixfx/components 0.7.5 → 0.8.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 +256 -17
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +1461 -165
- package/bundle/index.js.map +1 -1
- package/dist/ac-token.js +1 -1
- package/dist/{data-grid-CtJ35v8L.d.ts → data-grid-D-z7bx5-.d.ts} +2 -2
- package/dist/{data-grid-CtJ35v8L.d.ts.map → data-grid-D-z7bx5-.d.ts.map} +1 -1
- package/dist/data-grid.d.ts +1 -1
- package/dist/data-grid.js +1 -1
- package/dist/{detail-list-BS5dRDda.d.ts → detail-list-_ZJHYEBr.d.ts} +3 -3
- package/dist/{detail-list-BS5dRDda.d.ts.map → detail-list-_ZJHYEBr.d.ts.map} +1 -1
- package/dist/detail-list.d.ts +2 -2
- package/dist/detail-list.js +1 -1
- package/dist/grid-list-nRUQOcEL.js +1537 -0
- package/dist/grid-list-nRUQOcEL.js.map +1 -0
- package/dist/{grid-list-D-GbghSH.d.ts → grid-list-u33vmKzb.d.ts} +116 -15
- package/dist/grid-list-u33vmKzb.d.ts.map +1 -0
- package/dist/grid-list.d.ts +1 -1
- package/dist/grid-list.js +1 -993
- package/dist/{incr-search-DpbSJtgj.js → incr-search-C05WjbZh.js} +40 -3
- package/dist/incr-search-C05WjbZh.js.map +1 -0
- package/dist/incr-search.d.ts +2 -2
- package/dist/incr-search.js +2 -2
- package/dist/{index-CBS4z1v-.d.ts → index-h6gxtfHH.d.ts} +34 -2
- package/dist/index-h6gxtfHH.d.ts.map +1 -0
- package/dist/index.d.ts +60 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +342 -15
- package/dist/index.js.map +1 -1
- package/dist/led.d.ts +33 -1
- package/dist/led.d.ts.map +1 -1
- package/dist/led.js +157 -13
- package/dist/led.js.map +1 -1
- package/dist/miller.d.ts +1 -1
- package/dist/miller.js +1 -1
- package/dist/narrowed-text.js +1 -1
- package/dist/panel.d.ts +3 -1
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +31 -1
- package/dist/panel.js.map +1 -1
- package/dist/plots.d.ts +2 -2
- package/dist/plots.js +1 -1
- package/dist/{titlebar-Bj5pfdw_.d.ts → titlebar-YQ-jVQoS.d.ts} +2 -2
- package/dist/{titlebar-Bj5pfdw_.d.ts.map → titlebar-YQ-jVQoS.d.ts.map} +1 -1
- package/dist/titlebar.d.ts +1 -1
- package/dist/titlebar.js +83 -1
- package/dist/titlebar.js.map +1 -1
- package/dist/{tree-DAii2A1g.js → tree-Chptss7I.js} +2 -2
- package/dist/{tree-DAii2A1g.js.map → tree-Chptss7I.js.map} +1 -1
- package/dist/tree.d.ts +1 -1
- package/dist/tree.js +1 -1
- package/dist/{vertical-list-DwtCPy1b.d.ts → vertical-list-CFuFDygi.d.ts} +2 -2
- package/dist/{vertical-list-DwtCPy1b.d.ts.map → vertical-list-CFuFDygi.d.ts.map} +1 -1
- package/dist/vertical-list.d.ts +1 -1
- package/dist/vertical-list.js +1 -1
- package/dist/{xy-axis-5KhVTNoX.d.ts → xy-axis-BfP9hKLj.d.ts} +25 -2
- package/dist/{xy-axis-5KhVTNoX.d.ts.map → xy-axis-BfP9hKLj.d.ts.map} +1 -1
- package/dist/{xy-axis-VrX8dmvS.js → xy-axis-DzM1brBZ.js} +141 -8
- package/dist/xy-axis-DzM1brBZ.js.map +1 -0
- package/docs-user/grid-list.md +99 -14
- package/docs-user/index.json +3 -3
- package/docs-user/led.md +37 -7
- package/docs-user/llms.txt +1 -1
- package/docs-user/panel.md +68 -13
- package/docs-user/titlebar.md +3 -4
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/dist/grid-list-D-GbghSH.d.ts.map +0 -1
- package/dist/grid-list.js.map +0 -1
- package/dist/incr-search-DpbSJtgj.js.map +0 -1
- package/dist/index-CBS4z1v-.d.ts.map +0 -1
- package/dist/xy-axis-VrX8dmvS.js.map +0 -1
package/bundle/index.js
CHANGED
|
@@ -2499,6 +2499,43 @@ var IncrSearchTreeController = class {
|
|
|
2499
2499
|
}
|
|
2500
2500
|
};
|
|
2501
2501
|
//#endregion
|
|
2502
|
+
//#region src/incr-search/data-list-search-adapter.ts
|
|
2503
|
+
/**
|
|
2504
|
+
* A {@link ListSearchAdapter} over a data-backed list. Unlike the element
|
|
2505
|
+
* adapter it never materialises an element per item — it reads keys and text
|
|
2506
|
+
* straight from the source by index. Highlights are painted on whatever
|
|
2507
|
+
* captions are currently rendered in the shadow window; offscreen matches paint
|
|
2508
|
+
* when scrolled into view. See `docs/infra-incremental.md`.
|
|
2509
|
+
*/
|
|
2510
|
+
function createDataListSearchAdapter(host, options) {
|
|
2511
|
+
return {
|
|
2512
|
+
getItems: () => Array.from({ length: options.rawCount() }, (_, i) => ({
|
|
2513
|
+
key: options.rawKeyAt(i),
|
|
2514
|
+
index: i
|
|
2515
|
+
})),
|
|
2516
|
+
getLabel: (item) => options.rawSearchTextAt(item.index),
|
|
2517
|
+
getKey: (item) => item.key,
|
|
2518
|
+
focusItem: (item) => {
|
|
2519
|
+
if (item) host.focusKey(item.key);
|
|
2520
|
+
},
|
|
2521
|
+
activateItem: (item) => {
|
|
2522
|
+
if (options.onActivate) options.onActivate(item.key);
|
|
2523
|
+
else host.focusKey(item.key);
|
|
2524
|
+
},
|
|
2525
|
+
selectItems: (items) => {
|
|
2526
|
+
host.selectKeys(items.map((i) => i.key));
|
|
2527
|
+
},
|
|
2528
|
+
applyFilter: (matched) => {
|
|
2529
|
+
options.onFilterChange?.(matched);
|
|
2530
|
+
},
|
|
2531
|
+
highlightRoot: options.getHighlightRoot,
|
|
2532
|
+
highlightSelector: `.cell .caption`,
|
|
2533
|
+
highlightKeyOf: (el) => el.closest(`.cell`)?.getAttribute(`data-key`) ?? void 0,
|
|
2534
|
+
highlightText: (el) => el.textContent ?? ``,
|
|
2535
|
+
highlightKey: `grid-list-search`
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
//#endregion
|
|
2502
2539
|
//#region src/interaction/keyed-list.ts
|
|
2503
2540
|
/**
|
|
2504
2541
|
* Helpers for key-based selection on the Element-flavoured list components
|
|
@@ -2794,7 +2831,6 @@ var ListSearchController = class {
|
|
|
2794
2831
|
#filterActive = false;
|
|
2795
2832
|
constructor(host, options) {
|
|
2796
2833
|
this.#host = host;
|
|
2797
|
-
host.addController(this);
|
|
2798
2834
|
this.#adapter = options.adapter;
|
|
2799
2835
|
const requested = new Set(options.modes ?? LIST_SEARCH_MODES);
|
|
2800
2836
|
this.#modes = LIST_SEARCH_MODES.filter((m) => requested.has(m));
|
|
@@ -2805,6 +2841,7 @@ var ListSearchController = class {
|
|
|
2805
2841
|
fzf: options.fzf
|
|
2806
2842
|
});
|
|
2807
2843
|
this.#unsub = this.#search.onChange((state) => this.#onState(state));
|
|
2844
|
+
host.addController(this);
|
|
2808
2845
|
}
|
|
2809
2846
|
/** `false` when no modes are available — the host should register no commands and render no field. */
|
|
2810
2847
|
get enabled() {
|
|
@@ -13340,7 +13377,7 @@ __decorate([state()], DataTableElement.prototype, "_stacked", void 0);
|
|
|
13340
13377
|
DataTableElement = __decorate([safeCustomElement(`ixfx-data-table`)], DataTableElement);
|
|
13341
13378
|
//#endregion
|
|
13342
13379
|
//#region src/detail-list/detail-list.ts
|
|
13343
|
-
const ITEM_SELECTOR$
|
|
13380
|
+
const ITEM_SELECTOR$3 = `:scope > li`;
|
|
13344
13381
|
const SEARCH_HIGHLIGHT_KEY = `detail-list-search`;
|
|
13345
13382
|
const LABEL_SPAN_CSS = `overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0`;
|
|
13346
13383
|
let DetailListElement = class DetailListElement extends i {
|
|
@@ -13392,7 +13429,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
13392
13429
|
itemWrap: this.itemWrap
|
|
13393
13430
|
});
|
|
13394
13431
|
this.search = new ListSearchController(this, { adapter: createElementListSearchAdapter(this, {
|
|
13395
|
-
itemSelector: ITEM_SELECTOR$
|
|
13432
|
+
itemSelector: ITEM_SELECTOR$3,
|
|
13396
13433
|
getLabel: (el) => el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
|
|
13397
13434
|
highlightKey: SEARCH_HIGHLIGHT_KEY
|
|
13398
13435
|
}) });
|
|
@@ -13531,7 +13568,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
13531
13568
|
subtree: false,
|
|
13532
13569
|
attributeFilter: [`hidden`]
|
|
13533
13570
|
});
|
|
13534
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13571
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) this.#animatedItems.add(el);
|
|
13535
13572
|
this.#searchRegistry ??= createCommandRegistry();
|
|
13536
13573
|
this.#syncSearchCommands();
|
|
13537
13574
|
}
|
|
@@ -13550,7 +13587,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
13550
13587
|
fresh.push(node);
|
|
13551
13588
|
}
|
|
13552
13589
|
if (fresh.length === 0) return;
|
|
13553
|
-
const total = this.querySelectorAll(ITEM_SELECTOR$
|
|
13590
|
+
const total = this.querySelectorAll(ITEM_SELECTOR$3).length;
|
|
13554
13591
|
if (fresh.length >= total) return;
|
|
13555
13592
|
for (const el of fresh) animateListItemIn(el);
|
|
13556
13593
|
window.setTimeout(() => this.#scheduleGeometry(), 230);
|
|
@@ -13727,7 +13764,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
13727
13764
|
});
|
|
13728
13765
|
}
|
|
13729
13766
|
#itemForKey(key) {
|
|
13730
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13767
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) if (keyForElement(el) === key) return el;
|
|
13731
13768
|
}
|
|
13732
13769
|
*selectedFilter(kind) {
|
|
13733
13770
|
const wanted = (el) => {
|
|
@@ -13737,7 +13774,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
13737
13774
|
case `either`: return el.hasAttribute(`data-selected`) || el.hasAttribute(`data-checked`);
|
|
13738
13775
|
}
|
|
13739
13776
|
};
|
|
13740
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13777
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) {
|
|
13741
13778
|
if (el.hidden) continue;
|
|
13742
13779
|
if (wanted(el)) yield el;
|
|
13743
13780
|
}
|
|
@@ -13750,7 +13787,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
13750
13787
|
if (previousKeys.size > 0 || anchorKey !== null) {
|
|
13751
13788
|
const next = [];
|
|
13752
13789
|
let anchor;
|
|
13753
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13790
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) {
|
|
13754
13791
|
const key = keyOf(el);
|
|
13755
13792
|
if (key !== null && previousKeys.has(key)) next.push(el);
|
|
13756
13793
|
if (key !== null && key === anchorKey) anchor = el;
|
|
@@ -13805,15 +13842,15 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
13805
13842
|
}
|
|
13806
13843
|
#applyFilterPredicate() {
|
|
13807
13844
|
if (!this.filterPredicate) {
|
|
13808
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13845
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) el.removeAttribute(`hidden`);
|
|
13809
13846
|
return;
|
|
13810
13847
|
}
|
|
13811
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
13848
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) if (this.filterPredicate(el)) el.removeAttribute(`hidden`);
|
|
13812
13849
|
else el.setAttribute(`hidden`, ``);
|
|
13813
13850
|
}
|
|
13814
13851
|
#applySort() {
|
|
13815
13852
|
if (this.sort === `none`) return;
|
|
13816
|
-
const rows = [...this.querySelectorAll(ITEM_SELECTOR$
|
|
13853
|
+
const rows = [...this.querySelectorAll(ITEM_SELECTOR$3)];
|
|
13817
13854
|
if (rows.length < 2) return;
|
|
13818
13855
|
const dir = this.sort === `descending` ? -1 : 1;
|
|
13819
13856
|
const meta = (el) => ({
|
|
@@ -14005,7 +14042,7 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
14005
14042
|
else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findLastCheckable());
|
|
14006
14043
|
}
|
|
14007
14044
|
_getVisibleItems() {
|
|
14008
|
-
return Array.from(this.querySelectorAll(ITEM_SELECTOR$
|
|
14045
|
+
return Array.from(this.querySelectorAll(ITEM_SELECTOR$3)).filter((el) => {
|
|
14009
14046
|
if (el.hidden) return false;
|
|
14010
14047
|
if (this.filterPredicate && !this.filterPredicate(el)) return false;
|
|
14011
14048
|
return true;
|
|
@@ -14060,10 +14097,10 @@ let DetailListElement = class DetailListElement extends i {
|
|
|
14060
14097
|
}
|
|
14061
14098
|
/** Scan light-DOM children for unprocessed `data-icon` items. */
|
|
14062
14099
|
#processChildren() {
|
|
14063
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
14100
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) this.#ensureIcon(el);
|
|
14064
14101
|
}
|
|
14065
14102
|
_syncItemStates() {
|
|
14066
|
-
for (const el of this.querySelectorAll(ITEM_SELECTOR$
|
|
14103
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR$3)) {
|
|
14067
14104
|
el.toggleAttribute(`data-tickled`, this.tickled.isTickled(el));
|
|
14068
14105
|
el.toggleAttribute(`data-selected`, this.selection.selected.has(el));
|
|
14069
14106
|
if (this.interactionMode === `checked`) el.toggleAttribute(`data-checked`, !el.hasAttribute(`not-checked`) && this.selection.selected.has(el));
|
|
@@ -16086,7 +16123,7 @@ SplitLayoutElement = __decorate([safeCustomElement(`ixfx-split-layout`)], SplitL
|
|
|
16086
16123
|
//#endregion
|
|
16087
16124
|
//#region src/tabs/tab-list.ts
|
|
16088
16125
|
/** Pointer movement threshold (px) before a press becomes a drag. */
|
|
16089
|
-
const DRAG_THRESHOLD_PX$
|
|
16126
|
+
const DRAG_THRESHOLD_PX$4 = 5;
|
|
16090
16127
|
let TabListElement = class TabListElement extends i {
|
|
16091
16128
|
constructor(..._args) {
|
|
16092
16129
|
super(..._args);
|
|
@@ -16200,7 +16237,7 @@ let TabListElement = class TabListElement extends i {
|
|
|
16200
16237
|
const dy = event.clientY - state.startY;
|
|
16201
16238
|
const distance = Math.hypot(dx, dy);
|
|
16202
16239
|
if (!state.active) {
|
|
16203
|
-
if (distance < DRAG_THRESHOLD_PX$
|
|
16240
|
+
if (distance < DRAG_THRESHOLD_PX$4) return;
|
|
16204
16241
|
this.#beginDrag(event);
|
|
16205
16242
|
}
|
|
16206
16243
|
state.ghost?.move(event.clientX, event.clientY);
|
|
@@ -17050,12 +17087,23 @@ let TitlebarElement = class TitlebarElement extends i {
|
|
|
17050
17087
|
const showCaret = this.collapsible && this.toggleInteraction !== `disabled`;
|
|
17051
17088
|
const implicitToggle = this.collapsible && this.toggleInteraction === `implicit`;
|
|
17052
17089
|
const caretRotation = isVertical ? this.collapsed ? `` : `-90deg` : this.collapsed ? `-90deg` : ``;
|
|
17090
|
+
const treeVariant = this.variant === `tree`;
|
|
17053
17091
|
return b`
|
|
17054
17092
|
<div
|
|
17055
17093
|
class="bar"
|
|
17056
17094
|
role="toolbar"
|
|
17057
17095
|
@click=${implicitToggle ? this._onToggleClick : A}
|
|
17058
17096
|
>
|
|
17097
|
+
${treeVariant && showCaret ? b`
|
|
17098
|
+
<button
|
|
17099
|
+
class="caret-btn"
|
|
17100
|
+
type="button"
|
|
17101
|
+
aria-label=${this.collapsed ? `Expand panel` : `Collapse panel`}
|
|
17102
|
+
@click=${this._onToggleClick}
|
|
17103
|
+
>
|
|
17104
|
+
<ixfx-icon name=${caretIcon} prominence .rotate=${caretRotation.length > 0 ? caretRotation : A}></ixfx-icon>
|
|
17105
|
+
</button>
|
|
17106
|
+
` : A}
|
|
17059
17107
|
<div class="title-text-wrapper">
|
|
17060
17108
|
${this.iconName ? b`<ixfx-icon class="title-icon" name=${this.iconName}></ixfx-icon>` : A}
|
|
17061
17109
|
<span class="title-text">${this.title}</span>
|
|
@@ -17073,7 +17121,7 @@ let TitlebarElement = class TitlebarElement extends i {
|
|
|
17073
17121
|
<ixfx-icon name="close"></ixfx-icon>
|
|
17074
17122
|
</button>
|
|
17075
17123
|
` : A}
|
|
17076
|
-
${showCaret ? b`
|
|
17124
|
+
${!treeVariant && showCaret ? b`
|
|
17077
17125
|
<button
|
|
17078
17126
|
class="caret-btn"
|
|
17079
17127
|
type="button"
|
|
@@ -17296,6 +17344,77 @@ let TitlebarElement = class TitlebarElement extends i {
|
|
|
17296
17344
|
-1px 0 0 var(--surface-5),
|
|
17297
17345
|
1px 0 0 var(--surface-1);
|
|
17298
17346
|
}
|
|
17347
|
+
|
|
17348
|
+
/* === Tree variant: VS Code explorer / Outline style === */
|
|
17349
|
+
:host([variant="tree"]) .bar {
|
|
17350
|
+
background: transparent;
|
|
17351
|
+
padding: 0 var(--space-xs);
|
|
17352
|
+
min-height: unset;
|
|
17353
|
+
gap: var(--space-xs);
|
|
17354
|
+
}
|
|
17355
|
+
|
|
17356
|
+
:host([variant="tree"][orientation="vertical"]) .bar {
|
|
17357
|
+
padding: var(--space-xs) 0;
|
|
17358
|
+
flex-direction: column;
|
|
17359
|
+
min-width: unset;
|
|
17360
|
+
writing-mode: horizontal-tb;
|
|
17361
|
+
}
|
|
17362
|
+
|
|
17363
|
+
:host([variant="tree"]) .title-text-wrapper {
|
|
17364
|
+
display: inline-flex;
|
|
17365
|
+
align-items: center;
|
|
17366
|
+
gap: var(--space-xs);
|
|
17367
|
+
padding: 0;
|
|
17368
|
+
background: transparent;
|
|
17369
|
+
}
|
|
17370
|
+
|
|
17371
|
+
:host([variant="tree"]) .title-icon {
|
|
17372
|
+
background: transparent;
|
|
17373
|
+
flex-shrink: 0;
|
|
17374
|
+
}
|
|
17375
|
+
|
|
17376
|
+
:host([variant="tree"]) .title-text {
|
|
17377
|
+
font-weight: normal;
|
|
17378
|
+
color: var(--surface-3-text);
|
|
17379
|
+
padding: 0;
|
|
17380
|
+
background: transparent;
|
|
17381
|
+
box-shadow: none;
|
|
17382
|
+
flex: unset;
|
|
17383
|
+
width: auto;
|
|
17384
|
+
white-space: nowrap;
|
|
17385
|
+
}
|
|
17386
|
+
|
|
17387
|
+
:host([variant="tree"][orientation="vertical"]) .title-text {
|
|
17388
|
+
writing-mode: vertical-rl;
|
|
17389
|
+
transform: rotate(180deg);
|
|
17390
|
+
padding: 0;
|
|
17391
|
+
}
|
|
17392
|
+
|
|
17393
|
+
:host([variant="tree"]) .caret-btn {
|
|
17394
|
+
background: transparent;
|
|
17395
|
+
opacity: var(--prominence-idle-opacity);
|
|
17396
|
+
padding: 0;
|
|
17397
|
+
}
|
|
17398
|
+
|
|
17399
|
+
:host([variant="tree"]) .caret-btn:hover {
|
|
17400
|
+
background: transparent;
|
|
17401
|
+
opacity: var(--prominence-tickled-opacity);
|
|
17402
|
+
}
|
|
17403
|
+
|
|
17404
|
+
:host([variant="tree"]) .toolbar {
|
|
17405
|
+
margin-left: auto;
|
|
17406
|
+
}
|
|
17407
|
+
|
|
17408
|
+
:host([variant="tree"][orientation="vertical"]) .toolbar {
|
|
17409
|
+
flex-direction: row;
|
|
17410
|
+
margin-left: 0;
|
|
17411
|
+
margin-top: auto;
|
|
17412
|
+
}
|
|
17413
|
+
|
|
17414
|
+
:host([variant="tree"]) ixfx-icon {
|
|
17415
|
+
width: 1em;
|
|
17416
|
+
height: 1em;
|
|
17417
|
+
}
|
|
17299
17418
|
`];
|
|
17300
17419
|
}
|
|
17301
17420
|
};
|
|
@@ -17331,7 +17450,7 @@ TitlebarElement = __decorate([safeCustomElement(`ixfx-titlebar`)], TitlebarEleme
|
|
|
17331
17450
|
//#endregion
|
|
17332
17451
|
//#region src/dock/dock-panel.ts
|
|
17333
17452
|
/** Pointer movement threshold before a titlebar press becomes a dock drag. */
|
|
17334
|
-
const DRAG_THRESHOLD_PX$
|
|
17453
|
+
const DRAG_THRESHOLD_PX$3 = 5;
|
|
17335
17454
|
let DockPanelElement = class DockPanelElement extends i {
|
|
17336
17455
|
constructor(..._args) {
|
|
17337
17456
|
super(..._args);
|
|
@@ -17363,7 +17482,7 @@ let DockPanelElement = class DockPanelElement extends i {
|
|
|
17363
17482
|
this._onPointerMove = (event) => {
|
|
17364
17483
|
if (this._dragPointerId === null || event.pointerId !== this._dragPointerId) return;
|
|
17365
17484
|
if (!this._dragStarted) {
|
|
17366
|
-
if (Math.hypot(event.clientX - this._dragStartX, event.clientY - this._dragStartY) < DRAG_THRESHOLD_PX$
|
|
17485
|
+
if (Math.hypot(event.clientX - this._dragStartX, event.clientY - this._dragStartY) < DRAG_THRESHOLD_PX$3) return;
|
|
17367
17486
|
this._dragStarted = true;
|
|
17368
17487
|
const pointerId = this._dragPointerId;
|
|
17369
17488
|
this._teardownDragListeners();
|
|
@@ -17518,7 +17637,7 @@ DockPanelElement = __decorate([safeCustomElement(`ixfx-dock-panel`)], DockPanelE
|
|
|
17518
17637
|
/** Fraction of the leaf rect (from each edge) that forms the split bands. */
|
|
17519
17638
|
const BAND = .25;
|
|
17520
17639
|
/** Pointer movement threshold before a press becomes a dock drag (matches tab-list). */
|
|
17521
|
-
const DRAG_THRESHOLD_PX$
|
|
17640
|
+
const DRAG_THRESHOLD_PX$2 = 5;
|
|
17522
17641
|
/** Tolerance (px) when matching a child gutter endpoint to a parent gutter line. */
|
|
17523
17642
|
const JUNCTION_TOL_PX = 6;
|
|
17524
17643
|
/** Junction points closer than this merge into one handle (a 4-way cross yields one per side). */
|
|
@@ -17633,7 +17752,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
17633
17752
|
this._onTabDragMove = (event) => {
|
|
17634
17753
|
const pending = this._tabDragPending;
|
|
17635
17754
|
if (pending === null || event.pointerId !== pending.pointerId) return;
|
|
17636
|
-
if (Math.hypot(event.clientX - pending.startX, event.clientY - pending.startY) < DRAG_THRESHOLD_PX$
|
|
17755
|
+
if (Math.hypot(event.clientX - pending.startX, event.clientY - pending.startY) < DRAG_THRESHOLD_PX$2) return;
|
|
17637
17756
|
this._tabDragPending = null;
|
|
17638
17757
|
this._teardownTabDragListeners();
|
|
17639
17758
|
this._suppressNextClick();
|
|
@@ -19333,7 +19452,7 @@ function WheelNudgeMixin(superClass) {
|
|
|
19333
19452
|
}
|
|
19334
19453
|
//#endregion
|
|
19335
19454
|
//#region src/editable-label/editable-number.ts
|
|
19336
|
-
const DRAG_THRESHOLD_PX = 3;
|
|
19455
|
+
const DRAG_THRESHOLD_PX$1 = 3;
|
|
19337
19456
|
let EditableNumber = class EditableNumber extends WheelNudgeMixin(EditableLabel) {
|
|
19338
19457
|
#dragStartY;
|
|
19339
19458
|
#dragStartValue;
|
|
@@ -19450,7 +19569,7 @@ let EditableNumber = class EditableNumber extends WheelNudgeMixin(EditableLabel)
|
|
|
19450
19569
|
if (this._editing) return;
|
|
19451
19570
|
if (this.disabled || this.noDrag) return;
|
|
19452
19571
|
const deltaY = this.#dragStartY - e.clientY;
|
|
19453
|
-
if (!this.#dragging && Math.abs(deltaY) < DRAG_THRESHOLD_PX) return;
|
|
19572
|
+
if (!this.#dragging && Math.abs(deltaY) < DRAG_THRESHOLD_PX$1) return;
|
|
19454
19573
|
this.#dragging = true;
|
|
19455
19574
|
this.#hasDragged = true;
|
|
19456
19575
|
this.toggleAttribute(`dragging`, true);
|
|
@@ -20059,6 +20178,226 @@ let FormElement = class FormElement extends i {
|
|
|
20059
20178
|
};
|
|
20060
20179
|
FormElement = __decorate([safeCustomElement(`ixfx-form`)], FormElement);
|
|
20061
20180
|
//#endregion
|
|
20181
|
+
//#region src/grid-list/grid-list-source.ts
|
|
20182
|
+
const ITEM_SELECTOR$2 = `:scope > *`;
|
|
20183
|
+
const SYNTHETIC_PREFIX = `gl-auto-`;
|
|
20184
|
+
/** Whether `key` is an auto-assigned key for a keyless light-DOM item. */
|
|
20185
|
+
function isSyntheticKey(key) {
|
|
20186
|
+
return key.startsWith(SYNTHETIC_PREFIX);
|
|
20187
|
+
}
|
|
20188
|
+
function warnDuplicateKeys(keys) {
|
|
20189
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20190
|
+
const dupes = /* @__PURE__ */ new Set();
|
|
20191
|
+
for (const k of keys) {
|
|
20192
|
+
if (isSyntheticKey(k)) continue;
|
|
20193
|
+
if (seen.has(k)) dupes.add(k);
|
|
20194
|
+
else seen.add(k);
|
|
20195
|
+
}
|
|
20196
|
+
if (dupes.size > 0) console.warn(`ixfx-grid-list: duplicate item key(s) ${[...dupes].map((k) => `"${k}"`).join(`, `)} — the first occurrence wins for key-based operations.`);
|
|
20197
|
+
}
|
|
20198
|
+
var ElementGridListSource = class {
|
|
20199
|
+
#host;
|
|
20200
|
+
#items = [];
|
|
20201
|
+
#slotNames = /* @__PURE__ */ new WeakMap();
|
|
20202
|
+
#syntheticKeys = /* @__PURE__ */ new WeakMap();
|
|
20203
|
+
#seq = 0;
|
|
20204
|
+
#predicate;
|
|
20205
|
+
#searchKeys;
|
|
20206
|
+
#effectiveIndices = [];
|
|
20207
|
+
#keysCache;
|
|
20208
|
+
constructor(host) {
|
|
20209
|
+
this.#host = host;
|
|
20210
|
+
this.refresh();
|
|
20211
|
+
}
|
|
20212
|
+
get count() {
|
|
20213
|
+
return this.#effectiveIndices.length;
|
|
20214
|
+
}
|
|
20215
|
+
get rawCount() {
|
|
20216
|
+
return this.#items.length;
|
|
20217
|
+
}
|
|
20218
|
+
keyAt(index) {
|
|
20219
|
+
const eff = this.#effectiveIndices[index];
|
|
20220
|
+
return eff === void 0 ? `` : this.#itemKey(this.#items[eff]);
|
|
20221
|
+
}
|
|
20222
|
+
indexOfKey(key) {
|
|
20223
|
+
return this.#effectiveIndices.findIndex((i) => this.#itemKey(this.#items[i]) === key);
|
|
20224
|
+
}
|
|
20225
|
+
keys() {
|
|
20226
|
+
this.#keysCache ??= this.#effectiveIndices.map((i) => this.#itemKey(this.#items[i]));
|
|
20227
|
+
return this.#keysCache;
|
|
20228
|
+
}
|
|
20229
|
+
labelAt(index) {
|
|
20230
|
+
const eff = this.#effectiveIndices[index];
|
|
20231
|
+
return eff === void 0 ? `` : this.#captionFor(this.#items[eff]);
|
|
20232
|
+
}
|
|
20233
|
+
rawKeyAt(index) {
|
|
20234
|
+
const el = this.#items[index];
|
|
20235
|
+
return el === void 0 ? `` : this.#itemKey(el);
|
|
20236
|
+
}
|
|
20237
|
+
rawSearchTextAt(index) {
|
|
20238
|
+
const el = this.#items[index];
|
|
20239
|
+
if (el === void 0) return ``;
|
|
20240
|
+
return el.getAttribute(`data-search-label`) ?? el.getAttribute(`data-label`) ?? el.querySelector(`[data-grid-list-caption]`)?.textContent?.trim() ?? el.textContent?.trim() ?? ``;
|
|
20241
|
+
}
|
|
20242
|
+
isSelectable(index) {
|
|
20243
|
+
const eff = this.#effectiveIndices[index];
|
|
20244
|
+
return eff !== void 0 && !this.#items[eff].hasAttribute(`not-checked`);
|
|
20245
|
+
}
|
|
20246
|
+
renderCellContent(index) {
|
|
20247
|
+
const eff = this.#effectiveIndices[index];
|
|
20248
|
+
if (eff === void 0) return void 0;
|
|
20249
|
+
return b`<slot name=${this.slotNameFor(this.#items[eff])}></slot>`;
|
|
20250
|
+
}
|
|
20251
|
+
setFilters(opts) {
|
|
20252
|
+
if (`predicate` in opts) this.#predicate = opts.predicate;
|
|
20253
|
+
if (`searchKeys` in opts) this.#searchKeys = opts.searchKeys;
|
|
20254
|
+
this.#recomputeEffective();
|
|
20255
|
+
}
|
|
20256
|
+
refresh() {
|
|
20257
|
+
this.#items = Array.from(this.#host.querySelectorAll(ITEM_SELECTOR$2)).filter((el) => !el.hidden);
|
|
20258
|
+
for (const el of this.#items) {
|
|
20259
|
+
const name = this.slotNameFor(el);
|
|
20260
|
+
if (el.getAttribute(`slot`) !== name) el.setAttribute(`slot`, name);
|
|
20261
|
+
}
|
|
20262
|
+
warnDuplicateKeys(this.#items.map((el) => this.#itemKey(el)));
|
|
20263
|
+
this.#recomputeEffective();
|
|
20264
|
+
}
|
|
20265
|
+
/** Effective-order elements — the array `GridListElement` renders and slices. */
|
|
20266
|
+
effectiveElements() {
|
|
20267
|
+
return this.#effectiveIndices.map((i) => this.#items[i]);
|
|
20268
|
+
}
|
|
20269
|
+
/** Raw-order element at `index`, or `undefined`. */
|
|
20270
|
+
rawElementAt(index) {
|
|
20271
|
+
return this.#items[index];
|
|
20272
|
+
}
|
|
20273
|
+
/** Stable per-element `<slot>` name. */
|
|
20274
|
+
slotNameFor(el) {
|
|
20275
|
+
let name = this.#slotNames.get(el);
|
|
20276
|
+
if (name === void 0) {
|
|
20277
|
+
this.#seq += 1;
|
|
20278
|
+
name = `gl-item-${this.#seq}`;
|
|
20279
|
+
this.#slotNames.set(el, name);
|
|
20280
|
+
}
|
|
20281
|
+
return name;
|
|
20282
|
+
}
|
|
20283
|
+
resolveKeyToElement(key) {
|
|
20284
|
+
if (isSyntheticKey(key)) return this.#items.find((el) => this.#syntheticKeys.get(el) === key);
|
|
20285
|
+
return this.#items.find((el) => keyForElement(el) === key);
|
|
20286
|
+
}
|
|
20287
|
+
resolveKeyToData(_key) {}
|
|
20288
|
+
#recomputeEffective() {
|
|
20289
|
+
const effective = [];
|
|
20290
|
+
for (let i = 0; i < this.#items.length; i++) {
|
|
20291
|
+
if (this.#predicate !== void 0 && !this.#predicate(i)) continue;
|
|
20292
|
+
if (this.#searchKeys !== void 0) {
|
|
20293
|
+
const key = this.#itemKey(this.#items[i]);
|
|
20294
|
+
if (!isSyntheticKey(key) && !this.#searchKeys.has(key)) continue;
|
|
20295
|
+
}
|
|
20296
|
+
effective.push(i);
|
|
20297
|
+
}
|
|
20298
|
+
this.#effectiveIndices = effective;
|
|
20299
|
+
this.#keysCache = void 0;
|
|
20300
|
+
}
|
|
20301
|
+
#itemKey(el) {
|
|
20302
|
+
const userKey = keyForElement(el);
|
|
20303
|
+
if (userKey !== void 0) return userKey;
|
|
20304
|
+
let synthetic = this.#syntheticKeys.get(el);
|
|
20305
|
+
if (synthetic === void 0) {
|
|
20306
|
+
this.#seq += 1;
|
|
20307
|
+
synthetic = `${SYNTHETIC_PREFIX}${this.#seq}`;
|
|
20308
|
+
this.#syntheticKeys.set(el, synthetic);
|
|
20309
|
+
}
|
|
20310
|
+
return synthetic;
|
|
20311
|
+
}
|
|
20312
|
+
#captionFor(el) {
|
|
20313
|
+
return el.getAttribute(`data-label`) ?? el.querySelector(`[data-grid-list-caption]`)?.textContent?.trim() ?? ``;
|
|
20314
|
+
}
|
|
20315
|
+
};
|
|
20316
|
+
var DataGridListSource = class {
|
|
20317
|
+
#items;
|
|
20318
|
+
#adapter;
|
|
20319
|
+
#predicate;
|
|
20320
|
+
#searchKeys;
|
|
20321
|
+
#effectiveIndices = [];
|
|
20322
|
+
#keysCache;
|
|
20323
|
+
constructor(items, adapter) {
|
|
20324
|
+
this.#items = items;
|
|
20325
|
+
this.#adapter = adapter;
|
|
20326
|
+
warnDuplicateKeys(items.map((it, i) => adapter.key(it, i)));
|
|
20327
|
+
this.#recomputeEffective();
|
|
20328
|
+
}
|
|
20329
|
+
get count() {
|
|
20330
|
+
return this.#effectiveIndices.length;
|
|
20331
|
+
}
|
|
20332
|
+
get rawCount() {
|
|
20333
|
+
return this.#items.length;
|
|
20334
|
+
}
|
|
20335
|
+
keyAt(index) {
|
|
20336
|
+
const eff = this.#effectiveIndices[index];
|
|
20337
|
+
return eff === void 0 ? `` : this.#adapter.key(this.#items[eff], eff);
|
|
20338
|
+
}
|
|
20339
|
+
indexOfKey(key) {
|
|
20340
|
+
return this.#effectiveIndices.findIndex((i) => this.#adapter.key(this.#items[i], i) === key);
|
|
20341
|
+
}
|
|
20342
|
+
keys() {
|
|
20343
|
+
this.#keysCache ??= this.#effectiveIndices.map((i) => this.#adapter.key(this.#items[i], i));
|
|
20344
|
+
return this.#keysCache;
|
|
20345
|
+
}
|
|
20346
|
+
labelAt(index) {
|
|
20347
|
+
const eff = this.#effectiveIndices[index];
|
|
20348
|
+
if (eff === void 0) return ``;
|
|
20349
|
+
return this.#adapter.label?.(this.#items[eff], eff) ?? this.#adapter.key(this.#items[eff], eff);
|
|
20350
|
+
}
|
|
20351
|
+
rawKeyAt(index) {
|
|
20352
|
+
return index < 0 || index >= this.#items.length ? `` : this.#adapter.key(this.#items[index], index);
|
|
20353
|
+
}
|
|
20354
|
+
rawSearchTextAt(index) {
|
|
20355
|
+
if (index < 0 || index >= this.#items.length) return ``;
|
|
20356
|
+
const item = this.#items[index];
|
|
20357
|
+
return this.#adapter.searchableText?.(item, index) ?? this.#adapter.label?.(item, index) ?? ``;
|
|
20358
|
+
}
|
|
20359
|
+
isSelectable(index) {
|
|
20360
|
+
const eff = this.#effectiveIndices[index];
|
|
20361
|
+
return eff !== void 0 && !this.#adapter.disabled?.(this.#items[eff], eff);
|
|
20362
|
+
}
|
|
20363
|
+
renderCellContent(index) {
|
|
20364
|
+
const eff = this.#effectiveIndices[index];
|
|
20365
|
+
return eff === void 0 ? void 0 : this.#adapter.render(this.#items[eff], eff);
|
|
20366
|
+
}
|
|
20367
|
+
setFilters(opts) {
|
|
20368
|
+
if (`predicate` in opts) this.#predicate = opts.predicate;
|
|
20369
|
+
if (`searchKeys` in opts) this.#searchKeys = opts.searchKeys;
|
|
20370
|
+
this.#recomputeEffective();
|
|
20371
|
+
}
|
|
20372
|
+
refresh() {
|
|
20373
|
+
this.#recomputeEffective();
|
|
20374
|
+
}
|
|
20375
|
+
/** Raw-order item at `index`, or `undefined`. */
|
|
20376
|
+
rawItemAt(index) {
|
|
20377
|
+
return this.#items[index];
|
|
20378
|
+
}
|
|
20379
|
+
/** Effective-order item at `index`, or `undefined`. */
|
|
20380
|
+
getItemAt(index) {
|
|
20381
|
+
const eff = this.#effectiveIndices[index];
|
|
20382
|
+
return eff === void 0 ? void 0 : this.#items[eff];
|
|
20383
|
+
}
|
|
20384
|
+
resolveKeyToElement(_key) {}
|
|
20385
|
+
resolveKeyToData(key) {
|
|
20386
|
+
const idx = this.#items.findIndex((item, i) => this.#adapter.key(item, i) === key);
|
|
20387
|
+
return idx >= 0 ? this.#items[idx] : void 0;
|
|
20388
|
+
}
|
|
20389
|
+
#recomputeEffective() {
|
|
20390
|
+
const effective = [];
|
|
20391
|
+
for (let i = 0; i < this.#items.length; i++) {
|
|
20392
|
+
if (this.#predicate !== void 0 && !this.#predicate(i)) continue;
|
|
20393
|
+
if (this.#searchKeys !== void 0 && !this.#searchKeys.has(this.#adapter.key(this.#items[i], i))) continue;
|
|
20394
|
+
effective.push(i);
|
|
20395
|
+
}
|
|
20396
|
+
this.#effectiveIndices = effective;
|
|
20397
|
+
this.#keysCache = void 0;
|
|
20398
|
+
}
|
|
20399
|
+
};
|
|
20400
|
+
//#endregion
|
|
20062
20401
|
//#region src/grid-list/grid-list.ts
|
|
20063
20402
|
const ITEM_SELECTOR$1 = `:scope > *`;
|
|
20064
20403
|
const MIN_ITEM_SIZE = 16;
|
|
@@ -20153,6 +20492,7 @@ let GridListElement = class GridListElement extends i {
|
|
|
20153
20492
|
this.captionBackdrop = `scrim`;
|
|
20154
20493
|
this.captionHeight = 24;
|
|
20155
20494
|
this.itemWrap = `stop`;
|
|
20495
|
+
this.items = [];
|
|
20156
20496
|
this._scrollTop = 0;
|
|
20157
20497
|
this._scrollLeft = 0;
|
|
20158
20498
|
this._viewportHeight = 0;
|
|
@@ -20163,42 +20503,37 @@ let GridListElement = class GridListElement extends i {
|
|
|
20163
20503
|
this.#itemWrap = `stop`;
|
|
20164
20504
|
this.#entering = /* @__PURE__ */ new Set();
|
|
20165
20505
|
this.#reflowTimer = 0;
|
|
20166
|
-
this.#
|
|
20167
|
-
this.#
|
|
20506
|
+
this.#sourceElement = new ElementGridListSource(this);
|
|
20507
|
+
this.#source = this.#sourceElement;
|
|
20168
20508
|
this.selection = new ListSelectionController({
|
|
20169
|
-
getOrderedItems: () => this.
|
|
20170
|
-
isSelectable: () =>
|
|
20509
|
+
getOrderedItems: () => this.#source.keys(),
|
|
20510
|
+
isSelectable: (k) => {
|
|
20511
|
+
const i = this.#source.indexOfKey(k);
|
|
20512
|
+
return i >= 0 && this.#source.isSelectable(i);
|
|
20513
|
+
},
|
|
20171
20514
|
getModes: () => ({
|
|
20172
20515
|
selectionMode: this.selectionMode,
|
|
20173
20516
|
interactionMode: this.interactionMode
|
|
20174
20517
|
}),
|
|
20175
|
-
getKey:
|
|
20518
|
+
getKey: (k) => k,
|
|
20176
20519
|
onChange: (selected, previous) => {
|
|
20177
20520
|
this._syncItemStates();
|
|
20178
20521
|
this.requestUpdate();
|
|
20179
|
-
this
|
|
20180
|
-
selected,
|
|
20181
|
-
previous,
|
|
20182
|
-
selectedKeys: this.selection.selectedKeys
|
|
20183
|
-
});
|
|
20522
|
+
this.#emitSelect(selected, previous);
|
|
20184
20523
|
}
|
|
20185
20524
|
});
|
|
20186
20525
|
this.tickled = new TickledController(this, {
|
|
20187
|
-
getItems: () => this.#
|
|
20188
|
-
id:
|
|
20189
|
-
enabled:
|
|
20526
|
+
getItems: () => this.#source.keys().map((k, i) => ({
|
|
20527
|
+
id: k,
|
|
20528
|
+
enabled: this.#source.isSelectable(i)
|
|
20190
20529
|
})),
|
|
20191
|
-
onTickle: (
|
|
20192
|
-
if (
|
|
20530
|
+
onTickle: (k) => {
|
|
20531
|
+
if (k !== void 0) this.#emitItemEvent(`list-tickle`, k);
|
|
20193
20532
|
},
|
|
20194
20533
|
onStateChange: () => this._syncItemStates(),
|
|
20195
20534
|
itemWrap: this.#itemWrap
|
|
20196
20535
|
});
|
|
20197
|
-
this
|
|
20198
|
-
itemSelector: ITEM_SELECTOR$1,
|
|
20199
|
-
getLabel: (el) => el.getAttribute(`data-label`) ?? el.querySelector(`[data-grid-list-caption]`)?.textContent?.trim() ?? el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
|
|
20200
|
-
highlightKey: `grid-list-search`
|
|
20201
|
-
}) });
|
|
20536
|
+
this.#selectedSnapshot = /* @__PURE__ */ new Set();
|
|
20202
20537
|
this.#onScroll = (e) => {
|
|
20203
20538
|
const el = e.target;
|
|
20204
20539
|
this.#cancelScrollRaf();
|
|
@@ -20218,16 +20553,16 @@ let GridListElement = class GridListElement extends i {
|
|
|
20218
20553
|
}
|
|
20219
20554
|
return;
|
|
20220
20555
|
}
|
|
20221
|
-
const count = this.#
|
|
20556
|
+
const count = this.#source.count;
|
|
20222
20557
|
if (count === 0) return;
|
|
20223
20558
|
const win = this.#currentWindow();
|
|
20224
20559
|
const move = (delta) => {
|
|
20225
|
-
const from = this.tickled.tickledId ? this.#
|
|
20560
|
+
const from = this.tickled.tickledId ? this.#source.indexOfKey(this.tickled.tickledId) : -1;
|
|
20226
20561
|
const nextIndex = Math.max(0, Math.min(count - 1, from + delta));
|
|
20227
|
-
const
|
|
20228
|
-
if (
|
|
20229
|
-
this.tickled.handleKeyboardNavigation(
|
|
20230
|
-
this.#scrollItemIntoView(
|
|
20562
|
+
const nextKey = this.#source.keyAt(nextIndex);
|
|
20563
|
+
if (nextKey) {
|
|
20564
|
+
this.tickled.handleKeyboardNavigation(nextKey);
|
|
20565
|
+
this.#scrollItemIntoView(nextKey);
|
|
20231
20566
|
}
|
|
20232
20567
|
};
|
|
20233
20568
|
const perLine = this.orientation === `grid` ? win.cols : 1;
|
|
@@ -20264,7 +20599,7 @@ let GridListElement = class GridListElement extends i {
|
|
|
20264
20599
|
const id = this.tickled.tickledId;
|
|
20265
20600
|
if (id) {
|
|
20266
20601
|
e.preventDefault();
|
|
20267
|
-
this
|
|
20602
|
+
this.#emitItemEvent(`list-item-click`, id);
|
|
20268
20603
|
this.#onCellActivate(id);
|
|
20269
20604
|
}
|
|
20270
20605
|
break;
|
|
@@ -20319,6 +20654,12 @@ let GridListElement = class GridListElement extends i {
|
|
|
20319
20654
|
height: 100%;
|
|
20320
20655
|
overflow-y: auto;
|
|
20321
20656
|
overflow-x: hidden;
|
|
20657
|
+
/* Consumer control over the native scrollbar (both orientations): set
|
|
20658
|
+
--grid-list-scrollbar-width to "none" to hide it or "thin" to slim it.
|
|
20659
|
+
The container is also exposed as ::part(viewport). */
|
|
20660
|
+
scrollbar-width: var(--grid-list-scrollbar-width, auto);
|
|
20661
|
+
scrollbar-gutter: var(--grid-list-scrollbar-gutter, auto);
|
|
20662
|
+
scrollbar-color: var(--grid-list-scrollbar-color, auto);
|
|
20322
20663
|
}
|
|
20323
20664
|
:host([orientation="row"]) .viewport {
|
|
20324
20665
|
overflow-x: auto;
|
|
@@ -20564,13 +20905,47 @@ let GridListElement = class GridListElement extends i {
|
|
|
20564
20905
|
*/
|
|
20565
20906
|
#zoomAnchor;
|
|
20566
20907
|
/**
|
|
20567
|
-
*
|
|
20568
|
-
*
|
|
20569
|
-
*
|
|
20570
|
-
*
|
|
20908
|
+
* The active view over the items. Element mode projects light-DOM children
|
|
20909
|
+
* through named `<slot>`s (owned by {@link ElementGridListSource}); data mode
|
|
20910
|
+
* renders from {@link items} via {@link itemAdapter}. Everything else here
|
|
20911
|
+
* addresses items by effective index + key and never branches on the mode.
|
|
20912
|
+
*
|
|
20913
|
+
* Initialised eagerly (element mode) so the public getters — `count`,
|
|
20914
|
+
* `selectedKeys`, … — are safe to touch before the first Lit update. The
|
|
20915
|
+
* first `willUpdate` rebuilds it once the light DOM / `itemAdapter` is known.
|
|
20571
20916
|
*/
|
|
20572
|
-
#
|
|
20573
|
-
#
|
|
20917
|
+
#sourceElement;
|
|
20918
|
+
#source;
|
|
20919
|
+
#searchMode;
|
|
20920
|
+
#searchFilterKeys;
|
|
20921
|
+
/** (Re)build {@link search} for the current mode; a no-op if it already matches. */
|
|
20922
|
+
#ensureSearch() {
|
|
20923
|
+
const mode = this.itemAdapter !== void 0 ? `data` : `element`;
|
|
20924
|
+
if (this.#searchMode === mode && this.search !== void 0) return;
|
|
20925
|
+
const previous = this.search;
|
|
20926
|
+
if (previous) {
|
|
20927
|
+
previous.hostDisconnected();
|
|
20928
|
+
this.removeController(previous);
|
|
20929
|
+
}
|
|
20930
|
+
this.search = mode === `data` ? new ListSearchController(this, { adapter: createDataListSearchAdapter(this, {
|
|
20931
|
+
rawCount: () => this.#source?.rawCount ?? 0,
|
|
20932
|
+
rawKeyAt: (i) => this.#source?.rawKeyAt(i) ?? ``,
|
|
20933
|
+
rawSearchTextAt: (i) => this.#source?.rawSearchTextAt(i) ?? ``,
|
|
20934
|
+
onFilterChange: (keys) => {
|
|
20935
|
+
this.#searchFilterKeys = keys;
|
|
20936
|
+
this.#source?.setFilters({ searchKeys: keys });
|
|
20937
|
+
this.requestUpdate();
|
|
20938
|
+
this.updateComplete.then(() => this.#emitViewportChange());
|
|
20939
|
+
},
|
|
20940
|
+
onActivate: (key) => this.#onCellActivate(key),
|
|
20941
|
+
getHighlightRoot: () => this.renderRoot
|
|
20942
|
+
}) }) : new ListSearchController(this, { adapter: createElementListSearchAdapter(this, {
|
|
20943
|
+
itemSelector: ITEM_SELECTOR$1,
|
|
20944
|
+
getLabel: (el) => el.getAttribute(`data-label`) ?? el.querySelector(`[data-grid-list-caption]`)?.textContent?.trim() ?? el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
|
|
20945
|
+
highlightKey: `grid-list-search`
|
|
20946
|
+
}) });
|
|
20947
|
+
this.#searchMode = mode;
|
|
20948
|
+
}
|
|
20574
20949
|
#searchRegistry;
|
|
20575
20950
|
#unregisterSearchCommands;
|
|
20576
20951
|
/** Command registry carrying this grid's `list.search.*` commands. */
|
|
@@ -20585,7 +20960,7 @@ let GridListElement = class GridListElement extends i {
|
|
|
20585
20960
|
return LIST_SEARCH_MODES.filter((m) => requested.has(m));
|
|
20586
20961
|
}
|
|
20587
20962
|
#syncSearchCommands() {
|
|
20588
|
-
if (!this.#searchRegistry) return;
|
|
20963
|
+
if (!this.#searchRegistry || !this.search) return;
|
|
20589
20964
|
this.search.setModes(this.#effectiveSearchModes());
|
|
20590
20965
|
this.#unregisterSearchCommands?.();
|
|
20591
20966
|
this.#unregisterSearchCommands = registerListSearchCommands(this.#searchRegistry, this.search);
|
|
@@ -20593,6 +20968,8 @@ let GridListElement = class GridListElement extends i {
|
|
|
20593
20968
|
connectedCallback() {
|
|
20594
20969
|
super.connectedCallback();
|
|
20595
20970
|
if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
|
|
20971
|
+
if (!this.hasAttribute(`role`)) this.setAttribute(`role`, `listbox`);
|
|
20972
|
+
this.#syncAriaMultiselectable();
|
|
20596
20973
|
for (const [attr, value] of [
|
|
20597
20974
|
[`caption-style`, this.captionStyle],
|
|
20598
20975
|
[`caption-align`, this.captionAlign],
|
|
@@ -20606,6 +20983,7 @@ let GridListElement = class GridListElement extends i {
|
|
|
20606
20983
|
attributeFilter: [`hidden`]
|
|
20607
20984
|
});
|
|
20608
20985
|
this.#searchRegistry ??= createCommandRegistry();
|
|
20986
|
+
this.#ensureSearch();
|
|
20609
20987
|
this.#syncSearchCommands();
|
|
20610
20988
|
}
|
|
20611
20989
|
disconnectedCallback() {
|
|
@@ -20626,6 +21004,19 @@ let GridListElement = class GridListElement extends i {
|
|
|
20626
21004
|
#onChildrenChanged(records) {
|
|
20627
21005
|
const previousLength = this.#items.length;
|
|
20628
21006
|
this.#syncItems();
|
|
21007
|
+
const removedKeys = [];
|
|
21008
|
+
for (const record of records ?? []) for (const node of record.removedNodes) {
|
|
21009
|
+
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
21010
|
+
const key = keyForElement(node);
|
|
21011
|
+
if (key !== void 0) removedKeys.push(key);
|
|
21012
|
+
}
|
|
21013
|
+
if (removedKeys.length > 0) {
|
|
21014
|
+
const present = new Set(this.#source.keys());
|
|
21015
|
+
const gone = removedKeys.filter((k) => !present.has(k));
|
|
21016
|
+
if (gone.length > 0) this.selection.removeKeys(gone, (k) => k);
|
|
21017
|
+
const tickledId = this.tickled.tickledId;
|
|
21018
|
+
if (tickledId && gone.includes(tickledId)) this.tickled.focus(void 0);
|
|
21019
|
+
}
|
|
20629
21020
|
this._syncItemStates();
|
|
20630
21021
|
this.#noteChildMutation(previousLength, records);
|
|
20631
21022
|
this.requestUpdate();
|
|
@@ -20667,33 +21058,86 @@ let GridListElement = class GridListElement extends i {
|
|
|
20667
21058
|
}, REFLOW_WINDOW_MS);
|
|
20668
21059
|
}
|
|
20669
21060
|
#emitViewportChange() {
|
|
20670
|
-
this._dispatch(`grid-list-viewport-change`, {
|
|
21061
|
+
this._dispatch(`grid-list-viewport-change`, {
|
|
21062
|
+
renderedKeys: this.renderedKeys(),
|
|
21063
|
+
visibleKeys: this.visibleKeys()
|
|
21064
|
+
});
|
|
20671
21065
|
}
|
|
20672
21066
|
firstUpdated() {
|
|
21067
|
+
if (!this.#source) this.#rebuildSource();
|
|
20673
21068
|
const el = this.#viewportRef.value;
|
|
20674
21069
|
if (!el) return;
|
|
20675
|
-
this._viewportWidth = el.clientWidth;
|
|
20676
|
-
this._viewportHeight = el.clientHeight;
|
|
20677
21070
|
this.#resizeObserver = new ResizeObserver((entries) => {
|
|
20678
21071
|
for (const entry of entries) {
|
|
20679
|
-
|
|
20680
|
-
|
|
21072
|
+
const w = entry.contentRect.width;
|
|
21073
|
+
const h = entry.contentRect.height;
|
|
21074
|
+
if (w <= 0 || h <= 0) return;
|
|
21075
|
+
this._viewportWidth = w;
|
|
21076
|
+
this._viewportHeight = h;
|
|
20681
21077
|
}
|
|
20682
21078
|
});
|
|
20683
21079
|
this.#resizeObserver.observe(el);
|
|
21080
|
+
this.#measureViewport(false);
|
|
20684
21081
|
this.#syncItems();
|
|
20685
21082
|
this.requestUpdate();
|
|
20686
21083
|
}
|
|
21084
|
+
/** (Re)construct {@link #source} for the current mode and re-apply the filters. */
|
|
21085
|
+
#rebuildSource() {
|
|
21086
|
+
if (this.itemAdapter !== void 0) {
|
|
21087
|
+
this.#source = new DataGridListSource(this.items, this.itemAdapter);
|
|
21088
|
+
this.#sourceElement = void 0;
|
|
21089
|
+
} else {
|
|
21090
|
+
this.#sourceElement = new ElementGridListSource(this);
|
|
21091
|
+
this.#source = this.#sourceElement;
|
|
21092
|
+
}
|
|
21093
|
+
this.#applyFilters();
|
|
21094
|
+
if (this.#searchFilterKeys) this.#source.setFilters({ searchKeys: this.#searchFilterKeys });
|
|
21095
|
+
}
|
|
21096
|
+
/** Push the current filter predicate into whichever source is active. */
|
|
21097
|
+
#applyFilters() {
|
|
21098
|
+
if (this.#source instanceof ElementGridListSource) {
|
|
21099
|
+
const fp = this.filterPredicate;
|
|
21100
|
+
const src = this.#source;
|
|
21101
|
+
src.setFilters({ predicate: fp ? (i) => fp(src.rawElementAt(i)) : void 0 });
|
|
21102
|
+
this.#items = src.effectiveElements();
|
|
21103
|
+
} else if (this.#source instanceof DataGridListSource) {
|
|
21104
|
+
const ip = this.itemFilterPredicate;
|
|
21105
|
+
const src = this.#source;
|
|
21106
|
+
src.setFilters({ predicate: ip ? (i) => ip(src.rawItemAt(i), i) : void 0 });
|
|
21107
|
+
}
|
|
21108
|
+
}
|
|
20687
21109
|
willUpdate(changed) {
|
|
20688
21110
|
if (changed.has(`searchModes`)) this.#syncSearchCommands();
|
|
20689
|
-
if (changed.has(`filterPredicate`)
|
|
21111
|
+
if (changed.has(`filterPredicate`) || changed.has(`itemFilterPredicate`)) {
|
|
21112
|
+
this.#applyFilters();
|
|
21113
|
+
this.updateComplete.then(() => this.#emitViewportChange());
|
|
21114
|
+
}
|
|
21115
|
+
if (changed.has(`items`) || changed.has(`itemAdapter`)) {
|
|
21116
|
+
this.#zoomAnchor = void 0;
|
|
21117
|
+
const prevKeys = this.#source?.keys() ?? [];
|
|
21118
|
+
const modeFlipped = this.itemAdapter !== void 0 !== this.#source instanceof DataGridListSource;
|
|
21119
|
+
this.#rebuildSource();
|
|
21120
|
+
const nextKeys = this.#source.keys();
|
|
21121
|
+
const nextKeySet = new Set(nextKeys);
|
|
21122
|
+
const removedKeys = prevKeys.filter((k) => !nextKeySet.has(k));
|
|
21123
|
+
if (removedKeys.length > 0) this.selection.removeKeys(removedKeys, (k) => k);
|
|
21124
|
+
const tickledId = this.tickled.tickledId;
|
|
21125
|
+
if (tickledId && !nextKeySet.has(tickledId)) this.tickled.focus(void 0);
|
|
21126
|
+
this._syncItemStates();
|
|
21127
|
+
this.updateComplete.then(() => this.#emitViewportChange());
|
|
21128
|
+
if (modeFlipped) {
|
|
21129
|
+
this.#ensureSearch();
|
|
21130
|
+
this.#syncSearchCommands();
|
|
21131
|
+
}
|
|
21132
|
+
this.search?.refresh();
|
|
21133
|
+
}
|
|
20690
21134
|
if (changed.has(`itemSize`) || changed.has(`gap`)) {
|
|
20691
21135
|
this.itemSize = Math.max(MIN_ITEM_SIZE, Math.round(this.itemSize));
|
|
20692
21136
|
this.gap = Math.max(0, Math.round(this.gap));
|
|
20693
21137
|
}
|
|
20694
21138
|
if (changed.has(`captionHeight`)) this.captionHeight = Math.max(0, Math.round(this.captionHeight));
|
|
20695
21139
|
if (changed.has(`itemWrap`)) this.#itemWrap = this.itemWrap;
|
|
20696
|
-
if ((changed.has(`itemSize`) || changed.has(`gap`) || changed.has(`captionStyle`) || changed.has(`captionHeight`) || changed.has(`orientation`)) && this.hasUpdated && this.#
|
|
21140
|
+
if ((changed.has(`itemSize`) || changed.has(`gap`) || changed.has(`captionStyle`) || changed.has(`captionHeight`) || changed.has(`orientation`)) && this.hasUpdated && this.#source.count > 0) {
|
|
20697
21141
|
const prevOrientation = changed.get(`orientation`) ?? this.orientation;
|
|
20698
21142
|
const horizontal = prevOrientation === `row`;
|
|
20699
21143
|
const prevOffset = horizontal ? this._scrollLeft : this._scrollTop;
|
|
@@ -20703,19 +21147,13 @@ let GridListElement = class GridListElement extends i {
|
|
|
20703
21147
|
const prevGap = changed.get(`gap`) ?? this.gap;
|
|
20704
21148
|
const prevCaptionStyle = changed.get(`captionStyle`) ?? this.captionStyle;
|
|
20705
21149
|
const prevCaptionHeight = changed.get(`captionHeight`) ?? this.captionHeight;
|
|
20706
|
-
|
|
20707
|
-
const prev = computeGridWindow(this.#items.length, prevItemSize, prevRowHeight, prevGap, this._viewportWidth, this._viewportHeight, prevOffset, prevOrientation);
|
|
20708
|
-
const mainStride = horizontal ? prev.colStride : prev.rowStride;
|
|
20709
|
-
const centerLine = (prevOffset + prevExtent / 2) / mainStride;
|
|
20710
|
-
const lineFloor = Math.max(0, Math.floor(centerLine));
|
|
20711
|
-
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)));
|
|
20712
|
-
this.#zoomAnchor = {
|
|
20713
|
-
index,
|
|
20714
|
-
lineFrac: centerLine - lineFloor
|
|
20715
|
-
};
|
|
21150
|
+
this.#captureCentreAnchor(prevOffset, prevExtent, prevItemSize, prevGap, prevCaptionStyle, prevCaptionHeight, prevOrientation);
|
|
20716
21151
|
}
|
|
20717
21152
|
}
|
|
20718
21153
|
}
|
|
21154
|
+
#syncAriaMultiselectable() {
|
|
21155
|
+
this.setAttribute(`aria-multiselectable`, this.selection.canSelectMultiple() ? `true` : `false`);
|
|
21156
|
+
}
|
|
20719
21157
|
/** Full cell height: the square, plus a caption band in `separate` / `under`. */
|
|
20720
21158
|
#rowHeight() {
|
|
20721
21159
|
const captionBand = this.captionStyle === `separate` || this.captionStyle === `under`;
|
|
@@ -20727,7 +21165,10 @@ let GridListElement = class GridListElement extends i {
|
|
|
20727
21165
|
updated(changed) {
|
|
20728
21166
|
super.updated(changed);
|
|
20729
21167
|
this.#syncItems();
|
|
20730
|
-
if (changed.has(`selectionMode`) || changed.has(`interactionMode`))
|
|
21168
|
+
if (changed.has(`selectionMode`) || changed.has(`interactionMode`)) {
|
|
21169
|
+
this.selection.clearAnchor();
|
|
21170
|
+
this.#syncAriaMultiselectable();
|
|
21171
|
+
}
|
|
20731
21172
|
if (changed.has(`orientation`)) {
|
|
20732
21173
|
const vp = this.#viewportRef.value;
|
|
20733
21174
|
if (vp) {
|
|
@@ -20740,6 +21181,11 @@ let GridListElement = class GridListElement extends i {
|
|
|
20740
21181
|
}
|
|
20741
21182
|
}
|
|
20742
21183
|
}
|
|
21184
|
+
const vp = this.#viewportRef.value;
|
|
21185
|
+
if (vp) {
|
|
21186
|
+
if (Math.abs(vp.scrollTop - this._scrollTop) > .5) vp.scrollTop = this._scrollTop;
|
|
21187
|
+
if (Math.abs(vp.scrollLeft - this._scrollLeft) > .5) vp.scrollLeft = this._scrollLeft;
|
|
21188
|
+
}
|
|
20743
21189
|
this.#applyZoomAnchor();
|
|
20744
21190
|
}
|
|
20745
21191
|
/** Re-anchor the scroll so the item captured in `willUpdate` stays centred. */
|
|
@@ -20770,30 +21216,67 @@ let GridListElement = class GridListElement extends i {
|
|
|
20770
21216
|
this._scrollTop = next;
|
|
20771
21217
|
}
|
|
20772
21218
|
}
|
|
20773
|
-
#
|
|
20774
|
-
|
|
20775
|
-
|
|
20776
|
-
|
|
20777
|
-
|
|
20778
|
-
|
|
21219
|
+
#captureCentreAnchor(prevOffset, prevExtent, prevItemSize, prevGap, prevCaptionStyle, prevCaptionHeight, prevOrientation) {
|
|
21220
|
+
const count = this.#source?.count ?? 0;
|
|
21221
|
+
if (count === 0) return;
|
|
21222
|
+
const prevRowHeight = prevItemSize + (prevCaptionStyle === `separate` || prevCaptionStyle === `under` ? prevCaptionHeight : 0);
|
|
21223
|
+
const horizontal = prevOrientation === `row`;
|
|
21224
|
+
const prev = computeGridWindow(count, prevItemSize, prevRowHeight, prevGap, this._viewportWidth, this._viewportHeight, prevOffset, prevOrientation);
|
|
21225
|
+
const mainStride = horizontal ? prev.colStride : prev.rowStride;
|
|
21226
|
+
const centerLine = (prevOffset + prevExtent / 2) / mainStride;
|
|
21227
|
+
const lineFloor = Math.max(0, Math.floor(centerLine));
|
|
21228
|
+
const index = horizontal ? Math.min(count - 1, lineFloor) : Math.min(count - 1, lineFloor * prev.cols + Math.max(0, Math.floor((prev.cols - 1) / 2)));
|
|
21229
|
+
this.#zoomAnchor = {
|
|
21230
|
+
index,
|
|
21231
|
+
lineFrac: centerLine - lineFloor
|
|
21232
|
+
};
|
|
20779
21233
|
}
|
|
20780
|
-
|
|
20781
|
-
|
|
20782
|
-
|
|
20783
|
-
|
|
20784
|
-
|
|
20785
|
-
|
|
21234
|
+
/**
|
|
21235
|
+
* Re-read the viewport after an ancestor's visibility/layout changed.
|
|
21236
|
+
* Safe while hidden (zero-sized reads are ignored); forces a repaint of the
|
|
21237
|
+
* virtual window even when the size is unchanged.
|
|
21238
|
+
*/
|
|
21239
|
+
refreshLayout() {
|
|
21240
|
+
this.#measureViewport(true);
|
|
21241
|
+
}
|
|
21242
|
+
/** Alias of {@link refreshLayout}. */
|
|
21243
|
+
remeasure() {
|
|
21244
|
+
this.#measureViewport(true);
|
|
21245
|
+
}
|
|
21246
|
+
#measureViewport(forceRender = false) {
|
|
21247
|
+
const vp = this.#viewportRef.value;
|
|
21248
|
+
if (!vp) return;
|
|
21249
|
+
const w = vp.clientWidth;
|
|
21250
|
+
const h = vp.clientHeight;
|
|
21251
|
+
if (w <= 0 || h <= 0) return;
|
|
21252
|
+
const horizontal = this.orientation === `row`;
|
|
21253
|
+
const changed = w !== this._viewportWidth || h !== this._viewportHeight;
|
|
21254
|
+
if (changed) {
|
|
21255
|
+
const prevOffset = horizontal ? this._scrollLeft : this._scrollTop;
|
|
21256
|
+
const prevExtent = horizontal ? w : h;
|
|
21257
|
+
this.#captureCentreAnchor(prevOffset, prevExtent, this.itemSize, this.gap, this.captionStyle, this.captionHeight, this.orientation);
|
|
21258
|
+
this._viewportWidth = w;
|
|
21259
|
+
this._viewportHeight = h;
|
|
20786
21260
|
}
|
|
20787
|
-
|
|
21261
|
+
if (forceRender && !changed) {
|
|
21262
|
+
this.#captureCentreAnchor(horizontal ? this._scrollLeft : this._scrollTop, horizontal ? w : h, this.itemSize, this.gap, this.captionStyle, this.captionHeight, this.orientation);
|
|
21263
|
+
this.requestUpdate();
|
|
21264
|
+
}
|
|
21265
|
+
if (changed || forceRender) this.updateComplete.then(() => this.#emitViewportChange());
|
|
20788
21266
|
}
|
|
20789
|
-
|
|
20790
|
-
|
|
20791
|
-
|
|
20792
|
-
|
|
20793
|
-
|
|
21267
|
+
/**
|
|
21268
|
+
* Element mode only: re-read the light DOM into the source and refresh the
|
|
21269
|
+
* `#items` cache + filter. A no-op in data mode (the source is rebuilt through
|
|
21270
|
+
* `#rebuildSource` when `items` / `itemAdapter` change).
|
|
21271
|
+
*/
|
|
21272
|
+
#syncItems() {
|
|
21273
|
+
if (this.#source instanceof ElementGridListSource) {
|
|
21274
|
+
this.#source.refresh();
|
|
21275
|
+
this.#applyFilters();
|
|
21276
|
+
}
|
|
20794
21277
|
}
|
|
20795
21278
|
get count() {
|
|
20796
|
-
return this.#
|
|
21279
|
+
return this.#source.count;
|
|
20797
21280
|
}
|
|
20798
21281
|
/**
|
|
20799
21282
|
* Number of cells currently rendered in the shadow DOM. With virtualization
|
|
@@ -20804,20 +21287,38 @@ let GridListElement = class GridListElement extends i {
|
|
|
20804
21287
|
return this.renderRoot.querySelectorAll(`.cell`).length;
|
|
20805
21288
|
}
|
|
20806
21289
|
get selectedItems() {
|
|
20807
|
-
|
|
21290
|
+
const els = /* @__PURE__ */ new Set();
|
|
21291
|
+
for (const key of this.selection.selectedKeys) {
|
|
21292
|
+
const el = this.#source.resolveKeyToElement(key);
|
|
21293
|
+
if (el) els.add(el);
|
|
21294
|
+
}
|
|
21295
|
+
return els;
|
|
20808
21296
|
}
|
|
20809
|
-
/**
|
|
21297
|
+
/**
|
|
21298
|
+
* The selection as stable `data-key` / `data-value` strings. Auto-assigned
|
|
21299
|
+
* keys for keyless light-DOM items are omitted (they are not a stable contract
|
|
21300
|
+
* for the consumer).
|
|
21301
|
+
*/
|
|
20810
21302
|
get selectedKeys() {
|
|
20811
|
-
|
|
21303
|
+
const out = /* @__PURE__ */ new Set();
|
|
21304
|
+
for (const k of this.selection.selectedKeys) if (!isSyntheticKey(k)) out.add(k);
|
|
21305
|
+
return out;
|
|
20812
21306
|
}
|
|
20813
21307
|
select(item) {
|
|
20814
|
-
|
|
21308
|
+
const key = keyForElement(item);
|
|
21309
|
+
if (key) this.selection.replace(key);
|
|
20815
21310
|
}
|
|
20816
21311
|
selectMany(items) {
|
|
20817
|
-
|
|
21312
|
+
const keys = [];
|
|
21313
|
+
for (const item of items) {
|
|
21314
|
+
const key = keyForElement(item);
|
|
21315
|
+
if (key) keys.push(key);
|
|
21316
|
+
}
|
|
21317
|
+
this.selection.selectMany(keys);
|
|
20818
21318
|
}
|
|
20819
21319
|
deselect(item) {
|
|
20820
|
-
|
|
21320
|
+
const key = keyForElement(item);
|
|
21321
|
+
if (key) this.selection.deselect(key);
|
|
20821
21322
|
}
|
|
20822
21323
|
clearSelection() {
|
|
20823
21324
|
this.selection.clear();
|
|
@@ -20831,44 +21332,126 @@ let GridListElement = class GridListElement extends i {
|
|
|
20831
21332
|
* have a `data-key` (or `data-value`).
|
|
20832
21333
|
*/
|
|
20833
21334
|
selectKeys(keys) {
|
|
20834
|
-
this.selection.selectKeys(keys, (
|
|
21335
|
+
this.selection.selectKeys(keys, (k) => k);
|
|
20835
21336
|
}
|
|
20836
21337
|
/** Add items by key to the selection (multi-select modes only). */
|
|
20837
21338
|
addSelectedKeys(keys) {
|
|
20838
|
-
this.selection.addKeys(keys, (
|
|
21339
|
+
this.selection.addKeys(keys, (k) => k);
|
|
20839
21340
|
}
|
|
20840
21341
|
/** Remove items by key from the selection. */
|
|
20841
21342
|
removeSelectedKeys(keys) {
|
|
20842
|
-
this.selection.removeKeys(keys, (
|
|
21343
|
+
this.selection.removeKeys(keys, (k) => k);
|
|
21344
|
+
}
|
|
21345
|
+
/**
|
|
21346
|
+
* Data mode only: raw index of `key` in `this.items`, or `-1`. Data-mode keys
|
|
21347
|
+
* come straight from the adapter, so this is exact even under a filter/search.
|
|
21348
|
+
*/
|
|
21349
|
+
#rawIndexOfKey(key) {
|
|
21350
|
+
const adapter = this.itemAdapter;
|
|
21351
|
+
if (!adapter) return -1;
|
|
21352
|
+
return this.items.findIndex((it, i) => adapter.key(it, i) === key);
|
|
21353
|
+
}
|
|
21354
|
+
/**
|
|
21355
|
+
* Data mode: replace the whole item array. Selection and cursor keep any key
|
|
21356
|
+
* that is still present; keys that vanish are dropped (one `list-select`).
|
|
21357
|
+
* A no-op in element mode.
|
|
21358
|
+
*/
|
|
21359
|
+
setItems(items) {
|
|
21360
|
+
if (this.itemAdapter) this.items = items;
|
|
21361
|
+
}
|
|
21362
|
+
/**
|
|
21363
|
+
* Data mode: append items to the end. Scroll position, selection and cursor
|
|
21364
|
+
* are untouched; appended items don't play the mid-list `[reflowing]` slide.
|
|
21365
|
+
* A no-op in element mode.
|
|
21366
|
+
*/
|
|
21367
|
+
appendItems(items) {
|
|
21368
|
+
if (this.itemAdapter && items.length > 0) this.items = [...this.items, ...items];
|
|
21369
|
+
}
|
|
21370
|
+
/**
|
|
21371
|
+
* Data mode: replace the item carrying `key`. A visible cell re-renders in
|
|
21372
|
+
* place (the keyed subtree is kept); an offscreen item shows its new content
|
|
21373
|
+
* when scrolled into view. Scroll and selection are untouched. Unknown keys
|
|
21374
|
+
* are ignored.
|
|
21375
|
+
*/
|
|
21376
|
+
updateItem(key, item) {
|
|
21377
|
+
if (!this.itemAdapter) return;
|
|
21378
|
+
const idx = this.#rawIndexOfKey(key);
|
|
21379
|
+
if (idx < 0) return;
|
|
21380
|
+
const next = [...this.items];
|
|
21381
|
+
next[idx] = item;
|
|
21382
|
+
this.items = next;
|
|
21383
|
+
}
|
|
21384
|
+
/**
|
|
21385
|
+
* Data mode: remove the items carrying these keys. Removed keys are dropped
|
|
21386
|
+
* from selection and cursor (via the `items` change). A no-op in element mode.
|
|
21387
|
+
*/
|
|
21388
|
+
removeItems(keys) {
|
|
21389
|
+
if (!this.itemAdapter) return;
|
|
21390
|
+
const adapter = this.itemAdapter;
|
|
21391
|
+
const keySet = new Set(keys);
|
|
21392
|
+
this.items = this.items.filter((it, i) => !keySet.has(adapter.key(it, i)));
|
|
21393
|
+
}
|
|
21394
|
+
/**
|
|
21395
|
+
* Data mode: drop every item. Selection and cursor are reset and one
|
|
21396
|
+
* `list-select` with empty sets is emitted. A no-op in element mode.
|
|
21397
|
+
*/
|
|
21398
|
+
clearItems() {
|
|
21399
|
+
if (!this.itemAdapter) return;
|
|
21400
|
+
this.selection.reset();
|
|
21401
|
+
this.tickled.focus(void 0);
|
|
21402
|
+
this.items = [];
|
|
21403
|
+
this.#emitSelect(/* @__PURE__ */ new Set(), /* @__PURE__ */ new Set());
|
|
20843
21404
|
}
|
|
20844
21405
|
/** Move the cursor to the item with this key and scroll it into view. */
|
|
20845
21406
|
focusKey(key) {
|
|
20846
|
-
|
|
20847
|
-
|
|
20848
|
-
this
|
|
20849
|
-
this.#scrollItemIntoView(el);
|
|
21407
|
+
if (this.#source.indexOfKey(key) < 0) return;
|
|
21408
|
+
this.tickled.focus(key);
|
|
21409
|
+
this.#scrollItemIntoView(key);
|
|
20850
21410
|
}
|
|
20851
21411
|
/** The key of the item under the cursor, or `undefined`. */
|
|
20852
21412
|
get tickledKey() {
|
|
20853
|
-
|
|
20854
|
-
return id ? keyForElement(id) : void 0;
|
|
21413
|
+
return this.tickled.tickledId;
|
|
20855
21414
|
}
|
|
21415
|
+
/** Element mode: the light-DOM element carrying `key`, or `undefined`. */
|
|
20856
21416
|
getItem(key) {
|
|
20857
|
-
return this.#
|
|
21417
|
+
return this.#source.resolveKeyToElement(key);
|
|
20858
21418
|
}
|
|
21419
|
+
/** Element mode: the light-DOM element at effective index `index`. */
|
|
20859
21420
|
getItemByIndex(index) {
|
|
20860
21421
|
return this.#items[index];
|
|
20861
21422
|
}
|
|
21423
|
+
/** Data mode: the data value carrying `key`, or `undefined`. */
|
|
21424
|
+
getData(key) {
|
|
21425
|
+
return this.#source.resolveKeyToData(key);
|
|
21426
|
+
}
|
|
20862
21427
|
getKey(item) {
|
|
20863
21428
|
return keyForElement(item);
|
|
20864
21429
|
}
|
|
21430
|
+
/**
|
|
21431
|
+
* Keys of items in the rendered window (viewport + overscan) — the priority
|
|
21432
|
+
* set for prefetching. Use {@link visibleKeys} for the strict viewport
|
|
21433
|
+
* intersection. Auto-assigned keyless-item keys are omitted.
|
|
21434
|
+
*/
|
|
21435
|
+
renderedKeys() {
|
|
21436
|
+
return this.#windowKeys(this.#currentWindow());
|
|
21437
|
+
}
|
|
21438
|
+
/**
|
|
21439
|
+
* Keys of items actually intersecting the viewport (no overscan).
|
|
21440
|
+
* Auto-assigned keyless-item keys are omitted.
|
|
21441
|
+
*/
|
|
20865
21442
|
visibleKeys() {
|
|
20866
|
-
|
|
20867
|
-
|
|
21443
|
+
return this.#windowKeys(this.#currentWindow(true));
|
|
21444
|
+
}
|
|
21445
|
+
#windowKeys(win) {
|
|
21446
|
+
return this.#source.keys().slice(win.startIndex, win.endIndex).filter((k) => !isSyntheticKey(k));
|
|
20868
21447
|
}
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
21448
|
+
#currentWindow(noOverscan = false) {
|
|
21449
|
+
const overscan = noOverscan ? 0 : 2;
|
|
21450
|
+
return computeGridWindow(this.#source.count, this.itemSize, this.#rowHeight(), this.gap, this._viewportWidth, this._viewportHeight, this.orientation === `row` ? this._scrollLeft : this._scrollTop, this.orientation, overscan);
|
|
21451
|
+
}
|
|
21452
|
+
/** Adjust the scroll offset so `key` is fully within the viewport. */
|
|
21453
|
+
#scrollItemIntoView(key) {
|
|
21454
|
+
const index = this.#source.indexOfKey(key);
|
|
20872
21455
|
const vp = this.#viewportRef.value;
|
|
20873
21456
|
if (index < 0 || !vp) return;
|
|
20874
21457
|
const win = this.#currentWindow();
|
|
@@ -20897,12 +21480,43 @@ let GridListElement = class GridListElement extends i {
|
|
|
20897
21480
|
}
|
|
20898
21481
|
}
|
|
20899
21482
|
_syncItemStates() {
|
|
21483
|
+
const selected = this.selection.selectedKeys;
|
|
20900
21484
|
for (const el of this.#items) {
|
|
20901
|
-
|
|
20902
|
-
el.toggleAttribute(`data-
|
|
21485
|
+
const key = keyForElement(el);
|
|
21486
|
+
el.toggleAttribute(`data-tickled`, key !== void 0 && this.tickled.isTickled(key));
|
|
21487
|
+
el.toggleAttribute(`data-selected`, key !== void 0 && selected.has(key));
|
|
20903
21488
|
}
|
|
20904
21489
|
this.setAttribute(`data-input-mode`, this.tickled.inputMode);
|
|
20905
21490
|
}
|
|
21491
|
+
#emitSelect(selected, previous) {
|
|
21492
|
+
const selectedEls = /* @__PURE__ */ new Set();
|
|
21493
|
+
const previousEls = /* @__PURE__ */ new Set();
|
|
21494
|
+
for (const key of selected) {
|
|
21495
|
+
const el = this.#source.resolveKeyToElement(key);
|
|
21496
|
+
if (el) selectedEls.add(el);
|
|
21497
|
+
}
|
|
21498
|
+
for (const key of previous) {
|
|
21499
|
+
const el = this.#source.resolveKeyToElement(key);
|
|
21500
|
+
if (el) previousEls.add(el);
|
|
21501
|
+
}
|
|
21502
|
+
this._dispatch(`list-select`, {
|
|
21503
|
+
selected: selectedEls,
|
|
21504
|
+
previous: previousEls,
|
|
21505
|
+
selectedKeys: this.selectedKeys,
|
|
21506
|
+
previousKeys: new Set([...previous].filter((k) => !isSyntheticKey(k)))
|
|
21507
|
+
});
|
|
21508
|
+
}
|
|
21509
|
+
#emitItemEvent(name, key) {
|
|
21510
|
+
const index = this.#source.indexOfKey(key);
|
|
21511
|
+
const element = this.#source.resolveKeyToElement(key);
|
|
21512
|
+
const item = element ?? this.#source.resolveKeyToData(key);
|
|
21513
|
+
this._dispatch(name, {
|
|
21514
|
+
item,
|
|
21515
|
+
key,
|
|
21516
|
+
index,
|
|
21517
|
+
element
|
|
21518
|
+
});
|
|
21519
|
+
}
|
|
20906
21520
|
_dispatch(name, detail) {
|
|
20907
21521
|
this.dispatchEvent(new CustomEvent(name, {
|
|
20908
21522
|
detail,
|
|
@@ -20910,9 +21524,11 @@ let GridListElement = class GridListElement extends i {
|
|
|
20910
21524
|
composed: true
|
|
20911
21525
|
}));
|
|
20912
21526
|
}
|
|
21527
|
+
/** Selected keys, snapshotted once per render so the cell renderers don't each rebuild the set. */
|
|
21528
|
+
#selectedSnapshot;
|
|
20913
21529
|
render() {
|
|
20914
21530
|
const win = this.#currentWindow();
|
|
20915
|
-
|
|
21531
|
+
this.#selectedSnapshot = this.selection.selectedKeys;
|
|
20916
21532
|
const spacerStyle = win.horizontal ? `width:${win.totalWidth}px;height:100%` : `height:${win.totalHeight}px`;
|
|
20917
21533
|
const scrollFade = win.horizontal ? `scroll-fade-x` : `scroll-fade-y`;
|
|
20918
21534
|
return b`
|
|
@@ -20924,15 +21540,56 @@ let GridListElement = class GridListElement extends i {
|
|
|
20924
21540
|
@scroll=${this.#onScroll}
|
|
20925
21541
|
>
|
|
20926
21542
|
<div class="spacer" style=${spacerStyle}>
|
|
20927
|
-
${
|
|
21543
|
+
${this.#source instanceof DataGridListSource ? this.#renderDataCells(win) : this.#renderElementCells(win)}
|
|
20928
21544
|
</div>
|
|
20929
21545
|
</div>
|
|
20930
|
-
${this.search
|
|
21546
|
+
${this.search?.renderField()}
|
|
20931
21547
|
`;
|
|
20932
21548
|
}
|
|
21549
|
+
#renderDataCells(win) {
|
|
21550
|
+
const slice = Array.from({ length: win.endIndex - win.startIndex }, (_, i) => win.startIndex + i);
|
|
21551
|
+
return repeat(slice, (index) => this.#source.keyAt(index), (index) => this.#renderDataCell(index, win));
|
|
21552
|
+
}
|
|
21553
|
+
#renderDataCell(index, win) {
|
|
21554
|
+
const key = this.#source.keyAt(index);
|
|
21555
|
+
const selected = this.#selectedSnapshot.has(key);
|
|
21556
|
+
const tickled = this.tickled.isTickled(key);
|
|
21557
|
+
const style = this.captionStyle;
|
|
21558
|
+
const separate = style === `separate`;
|
|
21559
|
+
const caption = style === `hidden` ? `` : this.#source.labelAt(index);
|
|
21560
|
+
const showCaption = caption !== ``;
|
|
21561
|
+
const cellHeight = win.rowStride - this.gap;
|
|
21562
|
+
const x = win.horizontal ? index * win.colStride : columnOf(index, win.cols) * win.colStride;
|
|
21563
|
+
const y = win.horizontal ? Math.max(0, (this._viewportHeight - cellHeight) / 2) : rowOf(index, win.cols) * win.rowStride;
|
|
21564
|
+
const captionCss = separate ? `height:${Math.max(0, this.captionHeight - 4)}px` : style === `under` ? `height:${this.captionHeight}px` : ``;
|
|
21565
|
+
return b`
|
|
21566
|
+
<div
|
|
21567
|
+
class="cell${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}"
|
|
21568
|
+
part="item${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}"
|
|
21569
|
+
role="option"
|
|
21570
|
+
aria-selected=${selected ? `true` : `false`}
|
|
21571
|
+
style="width:${this.itemSize}px;height:${cellHeight}px;transform:translate(${x}px,${y}px)"
|
|
21572
|
+
data-index=${index}
|
|
21573
|
+
data-key=${key}
|
|
21574
|
+
?data-selected=${selected}
|
|
21575
|
+
?data-tickled=${tickled}
|
|
21576
|
+
@click=${(e) => this.#onCellClick(e, key)}
|
|
21577
|
+
@dblclick=${() => this.#onCellActivate(key)}
|
|
21578
|
+
@pointerenter=${() => this.tickled.handlePointerEnter(key)}
|
|
21579
|
+
>
|
|
21580
|
+
<div class="content" part="content">${this.#source.renderCellContent(index)}</div>
|
|
21581
|
+
${showCaption ? b`<div class="caption" part="caption" style=${captionCss} title=${caption}>${caption}</div>` : ``}
|
|
21582
|
+
</div>
|
|
21583
|
+
`;
|
|
21584
|
+
}
|
|
21585
|
+
#renderElementCells(win) {
|
|
21586
|
+
const slice = this.#items.slice(win.startIndex, win.endIndex);
|
|
21587
|
+
return repeat(slice, (_el, i) => this.#source.keyAt(win.startIndex + i), (el, i) => this.#renderCell(el, win.startIndex + i, win));
|
|
21588
|
+
}
|
|
20933
21589
|
#renderCell(el, index, win) {
|
|
20934
|
-
const
|
|
20935
|
-
const
|
|
21590
|
+
const key = this.#source.keyAt(index);
|
|
21591
|
+
const selected = this.#selectedSnapshot.has(key);
|
|
21592
|
+
const tickled = this.tickled.isTickled(key);
|
|
20936
21593
|
const entering = this.#entering.has(el);
|
|
20937
21594
|
const style = this.captionStyle;
|
|
20938
21595
|
const separate = style === `separate`;
|
|
@@ -20946,17 +21603,22 @@ let GridListElement = class GridListElement extends i {
|
|
|
20946
21603
|
return b`
|
|
20947
21604
|
<div
|
|
20948
21605
|
class="cell${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}${entering ? ` entering` : ``}"
|
|
20949
|
-
part="item"
|
|
21606
|
+
part="item${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}"
|
|
21607
|
+
role="option"
|
|
21608
|
+
aria-selected=${selected ? `true` : `false`}
|
|
20950
21609
|
style="width:${this.itemSize}px;height:${cellHeight}px;transform:translate(${x}px,${y}px)"
|
|
20951
21610
|
data-index=${index}
|
|
20952
|
-
|
|
20953
|
-
|
|
20954
|
-
|
|
21611
|
+
data-key=${key}
|
|
21612
|
+
?data-selected=${selected}
|
|
21613
|
+
?data-tickled=${tickled}
|
|
21614
|
+
@click=${(e) => this.#onCellClick(e, key)}
|
|
21615
|
+
@dblclick=${() => this.#onCellActivate(key)}
|
|
21616
|
+
@pointerenter=${() => this.tickled.handlePointerEnter(key)}
|
|
20955
21617
|
>
|
|
20956
21618
|
<div class="thumb" part="thumb" style=${thumbStyle}>
|
|
20957
|
-
|
|
21619
|
+
${this.#source.renderCellContent(index)}
|
|
20958
21620
|
</div>
|
|
20959
|
-
${showCaption ? b`<div class="caption" part="caption" style=${captionCss}>${caption}</div>` : ``}
|
|
21621
|
+
${showCaption ? b`<div class="caption" part="caption" style=${captionCss} title=${caption}>${caption}</div>` : ``}
|
|
20960
21622
|
</div>
|
|
20961
21623
|
`;
|
|
20962
21624
|
}
|
|
@@ -20967,16 +21629,13 @@ let GridListElement = class GridListElement extends i {
|
|
|
20967
21629
|
this.#scrollRaf = 0;
|
|
20968
21630
|
}
|
|
20969
21631
|
}
|
|
20970
|
-
#onCellClick(e,
|
|
20971
|
-
this.selection.handleClick(
|
|
20972
|
-
this
|
|
21632
|
+
#onCellClick(e, key) {
|
|
21633
|
+
this.selection.handleClick(key, e);
|
|
21634
|
+
this.#emitItemEvent(`list-item-click`, key);
|
|
20973
21635
|
}
|
|
20974
|
-
#onCellActivate(
|
|
20975
|
-
if (this.interactionMode !== `manual`) this.selection.replace(
|
|
20976
|
-
this
|
|
20977
|
-
}
|
|
20978
|
-
#currentWindow() {
|
|
20979
|
-
return computeGridWindow(this.#items.length, this.itemSize, this.#rowHeight(), this.gap, this._viewportWidth, this._viewportHeight, this.orientation === `row` ? this._scrollLeft : this._scrollTop, this.orientation);
|
|
21636
|
+
#onCellActivate(key) {
|
|
21637
|
+
if (this.interactionMode !== `manual`) this.selection.replace(key);
|
|
21638
|
+
this.#emitItemEvent(`list-activate`, key);
|
|
20980
21639
|
}
|
|
20981
21640
|
};
|
|
20982
21641
|
__decorate([property({
|
|
@@ -21025,11 +21684,14 @@ __decorate([property({
|
|
|
21025
21684
|
attribute: `item-wrap`
|
|
21026
21685
|
})], GridListElement.prototype, "itemWrap", void 0);
|
|
21027
21686
|
__decorate([property({ attribute: false })], GridListElement.prototype, "filterPredicate", void 0);
|
|
21687
|
+
__decorate([property({ attribute: false })], GridListElement.prototype, "itemFilterPredicate", void 0);
|
|
21028
21688
|
__decorate([property({
|
|
21029
21689
|
type: String,
|
|
21030
21690
|
attribute: `search-modes`,
|
|
21031
21691
|
reflect: true
|
|
21032
21692
|
})], GridListElement.prototype, "searchModes", void 0);
|
|
21693
|
+
__decorate([property({ attribute: false })], GridListElement.prototype, "items", void 0);
|
|
21694
|
+
__decorate([property({ attribute: false })], GridListElement.prototype, "itemAdapter", void 0);
|
|
21033
21695
|
__decorate([state()], GridListElement.prototype, "_scrollTop", void 0);
|
|
21034
21696
|
__decorate([state()], GridListElement.prototype, "_scrollLeft", void 0);
|
|
21035
21697
|
__decorate([state()], GridListElement.prototype, "_viewportHeight", void 0);
|
|
@@ -21049,6 +21711,7 @@ let PanelElement = class PanelElement extends i {
|
|
|
21049
21711
|
this.toggleInteraction = `implicit`;
|
|
21050
21712
|
this.variant = `titlebar`;
|
|
21051
21713
|
this.hideTitlebar = false;
|
|
21714
|
+
this.keepToolbar = false;
|
|
21052
21715
|
}
|
|
21053
21716
|
_onTitlebarToggle(event) {
|
|
21054
21717
|
event.stopPropagation();
|
|
@@ -21068,6 +21731,7 @@ let PanelElement = class PanelElement extends i {
|
|
|
21068
21731
|
this.remove();
|
|
21069
21732
|
}
|
|
21070
21733
|
render() {
|
|
21734
|
+
const showToolbar = this.keepToolbar || !this.collapsed;
|
|
21071
21735
|
return b`
|
|
21072
21736
|
${this.hideTitlebar ? A : b`
|
|
21073
21737
|
<ixfx-titlebar
|
|
@@ -21082,7 +21746,7 @@ let PanelElement = class PanelElement extends i {
|
|
|
21082
21746
|
@toggle=${this._onTitlebarToggle}
|
|
21083
21747
|
@close=${this._onCloseClick}
|
|
21084
21748
|
>
|
|
21085
|
-
|
|
21749
|
+
${showToolbar ? b`<slot name="toolbar" slot="toolbar"></slot>` : A}
|
|
21086
21750
|
</ixfx-titlebar>
|
|
21087
21751
|
`}
|
|
21088
21752
|
|
|
@@ -21182,6 +21846,30 @@ let PanelElement = class PanelElement extends i {
|
|
|
21182
21846
|
:host([variant="midline"][orientation="vertical"]) .content-wrapper {
|
|
21183
21847
|
background: transparent;
|
|
21184
21848
|
}
|
|
21849
|
+
|
|
21850
|
+
/* === Tree variant: VS Code explorer / Outline style === */
|
|
21851
|
+
|
|
21852
|
+
:host([variant="tree"]) {
|
|
21853
|
+
border: none !important;
|
|
21854
|
+
border-radius: 0 !important;
|
|
21855
|
+
background: transparent;
|
|
21856
|
+
}
|
|
21857
|
+
|
|
21858
|
+
:host([variant="tree"]) .content-inner {
|
|
21859
|
+
box-shadow: none;
|
|
21860
|
+
background: transparent;
|
|
21861
|
+
padding: 0;
|
|
21862
|
+
}
|
|
21863
|
+
|
|
21864
|
+
:host([variant="tree"]) .content-wrapper {
|
|
21865
|
+
background: transparent;
|
|
21866
|
+
}
|
|
21867
|
+
|
|
21868
|
+
:host([variant="tree"][orientation="vertical"]) .content-inner {
|
|
21869
|
+
width: max-content;
|
|
21870
|
+
height: 100%;
|
|
21871
|
+
transform: none;
|
|
21872
|
+
}
|
|
21185
21873
|
`];
|
|
21186
21874
|
}
|
|
21187
21875
|
};
|
|
@@ -21217,6 +21905,10 @@ __decorate([property({
|
|
|
21217
21905
|
type: Boolean,
|
|
21218
21906
|
attribute: `hide-titlebar`
|
|
21219
21907
|
})], PanelElement.prototype, "hideTitlebar", void 0);
|
|
21908
|
+
__decorate([property({
|
|
21909
|
+
type: Boolean,
|
|
21910
|
+
attribute: `keep-toolbar`
|
|
21911
|
+
})], PanelElement.prototype, "keepToolbar", void 0);
|
|
21220
21912
|
PanelElement = __decorate([safeCustomElement(`ixfx-panel`)], PanelElement);
|
|
21221
21913
|
//#endregion
|
|
21222
21914
|
//#region src/grouped-item-lister/grouped-item-lister.ts
|
|
@@ -27885,15 +28577,62 @@ let LedElement = class LedElement extends i {
|
|
|
27885
28577
|
this.value = 0;
|
|
27886
28578
|
this.lit = false;
|
|
27887
28579
|
this.color = `oklch(50% 0.15 25)`;
|
|
28580
|
+
this.glow = false;
|
|
28581
|
+
this.mode = `brightness`;
|
|
27888
28582
|
this.blinking = false;
|
|
27889
28583
|
this.rate = 500;
|
|
28584
|
+
this.attackTime = 40;
|
|
28585
|
+
this.releaseTime = 400;
|
|
28586
|
+
this.pulseRetrigger = false;
|
|
28587
|
+
this.#appliedMode = `brightness`;
|
|
28588
|
+
this.#pulseStart = 0;
|
|
28589
|
+
this.#pulsePeak = 0;
|
|
28590
|
+
this.#pulseFrom = 0;
|
|
28591
|
+
this.#pulseLevel = 0;
|
|
28592
|
+
this.#pulseTick = (now) => {
|
|
28593
|
+
const attack = Math.max(0, this.attackTime);
|
|
28594
|
+
const release = Math.max(0, this.releaseTime);
|
|
28595
|
+
const from = this.#pulseFrom;
|
|
28596
|
+
const peak = this.#pulsePeak;
|
|
28597
|
+
const elapsed = now - this.#pulseStart;
|
|
28598
|
+
let level;
|
|
28599
|
+
if (attack > 0 && elapsed < attack) level = from + (peak - from) * (elapsed / attack);
|
|
28600
|
+
else if (elapsed < attack + release) level = release > 0 ? peak * (1 - (elapsed - attack) / release) : 0;
|
|
28601
|
+
else level = 0;
|
|
28602
|
+
level = Math.max(0, Math.min(Math.max(from, peak), level));
|
|
28603
|
+
this.#pulseLevel = level;
|
|
28604
|
+
this.style.setProperty(`--value`, String(level));
|
|
28605
|
+
if (elapsed < attack + release) this.#pulseRaf = requestAnimationFrame(this.#pulseTick);
|
|
28606
|
+
else {
|
|
28607
|
+
this.#pulseRaf = void 0;
|
|
28608
|
+
this.#pulseLevel = 0;
|
|
28609
|
+
}
|
|
28610
|
+
};
|
|
27890
28611
|
}
|
|
27891
28612
|
#blinkInterval;
|
|
28613
|
+
#appliedMode;
|
|
28614
|
+
#pulseRaf;
|
|
28615
|
+
#pulseStart;
|
|
28616
|
+
#pulsePeak;
|
|
28617
|
+
#pulseFrom;
|
|
28618
|
+
#pulseLevel;
|
|
27892
28619
|
willUpdate(changedProperties) {
|
|
27893
|
-
if (changedProperties.has(`
|
|
27894
|
-
|
|
27895
|
-
|
|
28620
|
+
if (changedProperties.has(`blinking`)) {
|
|
28621
|
+
if (this.blinking) this.mode = `blink`;
|
|
28622
|
+
else if (this.mode === `blink`) this.mode = `brightness`;
|
|
28623
|
+
}
|
|
28624
|
+
if (this.mode !== this.#appliedMode) {
|
|
28625
|
+
this.#appliedMode = this.mode;
|
|
28626
|
+
this.#onModeChange();
|
|
28627
|
+
}
|
|
28628
|
+
if (changedProperties.has(`lit`)) {
|
|
28629
|
+
if (this.lit) this.value = 1;
|
|
28630
|
+
else if (changedProperties.get(`lit`) !== void 0) this.value = 0;
|
|
28631
|
+
}
|
|
28632
|
+
if (changedProperties.has(`color`)) this.style.setProperty(`--_color-attr`, this.color);
|
|
28633
|
+
if (this.mode !== `pulse` && (changedProperties.has(`value`) || changedProperties.has(`lit`))) this.style.setProperty(`--value`, String(this.value));
|
|
27896
28634
|
if (changedProperties.has(`rate`)) {
|
|
28635
|
+
this.#updateBlinking();
|
|
27897
28636
|
const transitionMs = this.rate < 300 ? this.rate * .5 : 150;
|
|
27898
28637
|
this.style.setProperty(`--transition-duration`, `${transitionMs}ms`);
|
|
27899
28638
|
}
|
|
@@ -27901,18 +28640,43 @@ let LedElement = class LedElement extends i {
|
|
|
27901
28640
|
disconnectedCallback() {
|
|
27902
28641
|
super.disconnectedCallback();
|
|
27903
28642
|
this.#stopBlinking();
|
|
28643
|
+
this.#stopPulse();
|
|
27904
28644
|
}
|
|
27905
|
-
|
|
27906
|
-
|
|
27907
|
-
|
|
27908
|
-
|
|
27909
|
-
|
|
27910
|
-
|
|
27911
|
-
|
|
27912
|
-
|
|
27913
|
-
|
|
28645
|
+
/**
|
|
28646
|
+
* Pulse-mode trigger: lights the LED to {@link value} using an attack/release
|
|
28647
|
+
* envelope, then decays back to unlit. Calling again restarts the envelope
|
|
28648
|
+
* (from 0 if {@link pulseRetrigger} is set, otherwise from the current level).
|
|
28649
|
+
* No-op unless {@link mode} is `pulse`.
|
|
28650
|
+
*/
|
|
28651
|
+
pulse() {
|
|
28652
|
+
if (this.mode !== `pulse`) return;
|
|
28653
|
+
this.#pulsePeak = this.value;
|
|
28654
|
+
this.#pulseFrom = this.pulseRetrigger ? 0 : this.#pulseLevel;
|
|
28655
|
+
this.#pulseStart = performance.now();
|
|
28656
|
+
if (this.#pulseRaf === void 0) this.#pulseRaf = requestAnimationFrame(this.#pulseTick);
|
|
28657
|
+
}
|
|
28658
|
+
#onModeChange() {
|
|
28659
|
+
this.#stopPulse();
|
|
28660
|
+
this.#pulseLevel = 0;
|
|
28661
|
+
this.blinking = this.mode === `blink`;
|
|
28662
|
+
this.#updateBlinking();
|
|
28663
|
+
this.style.setProperty(`--value`, this.mode === `pulse` ? `0` : String(this.value));
|
|
28664
|
+
}
|
|
28665
|
+
#pulseTick;
|
|
28666
|
+
#stopPulse() {
|
|
28667
|
+
if (this.#pulseRaf !== void 0) {
|
|
28668
|
+
cancelAnimationFrame(this.#pulseRaf);
|
|
28669
|
+
this.#pulseRaf = void 0;
|
|
27914
28670
|
}
|
|
27915
28671
|
}
|
|
28672
|
+
#updateBlinking() {
|
|
28673
|
+
const wasBlinking = this.#blinkInterval !== void 0;
|
|
28674
|
+
this.#stopBlinking();
|
|
28675
|
+
if (this.mode === `blink` && this.rate > 0) this.#blinkInterval = window.setInterval(() => {
|
|
28676
|
+
this.value = this.value === 0 ? 1 : 0;
|
|
28677
|
+
}, this.rate);
|
|
28678
|
+
else if (wasBlinking || this.mode === `blink`) this.value = 0;
|
|
28679
|
+
}
|
|
27916
28680
|
#stopBlinking() {
|
|
27917
28681
|
if (this.#blinkInterval !== void 0) {
|
|
27918
28682
|
clearInterval(this.#blinkInterval);
|
|
@@ -27926,6 +28690,8 @@ let LedElement = class LedElement extends i {
|
|
|
27926
28690
|
this.styles = i$3`
|
|
27927
28691
|
:host {
|
|
27928
28692
|
--_size: var(--size, 1ex);
|
|
28693
|
+
/* Resolved colour: explicit --colour wins, then the \`color\` attribute, then a default. */
|
|
28694
|
+
--_colour: var(--colour, var(--_color-attr, oklch(50% 0.15 25)));
|
|
27929
28695
|
display: inline-block;
|
|
27930
28696
|
min-width: 1ex;
|
|
27931
28697
|
min-height: 1ex;
|
|
@@ -27935,12 +28701,61 @@ let LedElement = class LedElement extends i {
|
|
|
27935
28701
|
}
|
|
27936
28702
|
|
|
27937
28703
|
div {
|
|
28704
|
+
position: relative;
|
|
27938
28705
|
width: 100%;
|
|
27939
28706
|
height: 100%;
|
|
27940
28707
|
border-radius: 50%;
|
|
27941
|
-
background: color-mix(in oklch, var(--
|
|
28708
|
+
background: color-mix(in oklch, var(--_colour), white calc((1 - var(--value, 0)) * 50%));
|
|
27942
28709
|
transition: background var(--transition-duration, 150ms) ease-out;
|
|
27943
28710
|
}
|
|
28711
|
+
|
|
28712
|
+
/* Glow: a more realistic domed LED with a specular highlight and coloured bloom. */
|
|
28713
|
+
:host([glow]) {
|
|
28714
|
+
/* Body darkens toward black as the LED dims, so an unlit LED reads as a dark dome. */
|
|
28715
|
+
--_body: color-mix(in oklch, var(--_colour), black calc((1 - var(--value, 0)) * 60%));
|
|
28716
|
+
overflow: visible;
|
|
28717
|
+
}
|
|
28718
|
+
|
|
28719
|
+
:host([glow]) div {
|
|
28720
|
+
background:
|
|
28721
|
+
radial-gradient(circle at 32% 28%,
|
|
28722
|
+
color-mix(in oklch, var(--_body), white 55%) 0%,
|
|
28723
|
+
var(--_body) 45%,
|
|
28724
|
+
color-mix(in oklch, var(--_body), black 32%) 100%);
|
|
28725
|
+
box-shadow:
|
|
28726
|
+
/* inner rim shading for depth */
|
|
28727
|
+
inset 0 calc(var(--_size) * -0.1) calc(var(--_size) * 0.18)
|
|
28728
|
+
color-mix(in oklch, var(--_body), black 45%),
|
|
28729
|
+
inset 0 calc(var(--_size) * 0.08) calc(var(--_size) * 0.14)
|
|
28730
|
+
color-mix(in oklch, var(--_body), white 25%),
|
|
28731
|
+
/* coloured bloom, scaled by value so it only glows when lit */
|
|
28732
|
+
0 0 calc(var(--_size) * 0.6 * var(--value, 0)) calc(var(--_size) * 0.12 * var(--value, 0))
|
|
28733
|
+
var(--_colour),
|
|
28734
|
+
0 0 calc(var(--_size) * 1.7 * var(--value, 0)) calc(var(--_size) * 0.5 * var(--value, 0))
|
|
28735
|
+
color-mix(in oklch, var(--_colour), transparent 45%);
|
|
28736
|
+
transition:
|
|
28737
|
+
background var(--transition-duration, 150ms) ease-out,
|
|
28738
|
+
box-shadow var(--transition-duration, 150ms) ease-out;
|
|
28739
|
+
}
|
|
28740
|
+
|
|
28741
|
+
/* Specular highlight blob near the top-left. */
|
|
28742
|
+
:host([glow]) div::after {
|
|
28743
|
+
content: "";
|
|
28744
|
+
position: absolute;
|
|
28745
|
+
inset: 0;
|
|
28746
|
+
border-radius: 50%;
|
|
28747
|
+
background: radial-gradient(ellipse 55% 45% at 30% 22%,
|
|
28748
|
+
color-mix(in oklch, white, transparent 10%) 0%,
|
|
28749
|
+
transparent 60%);
|
|
28750
|
+
pointer-events: none;
|
|
28751
|
+
}
|
|
28752
|
+
|
|
28753
|
+
/* Pulse mode animates --value frame-by-frame, so drop the CSS transition
|
|
28754
|
+
that would otherwise smear the envelope. Placed last to win over the
|
|
28755
|
+
glow rule for elements matching both. */
|
|
28756
|
+
:host([mode="pulse"]) div {
|
|
28757
|
+
transition: none;
|
|
28758
|
+
}
|
|
27944
28759
|
`;
|
|
27945
28760
|
}
|
|
27946
28761
|
};
|
|
@@ -27951,6 +28766,15 @@ __decorate([property({
|
|
|
27951
28766
|
reflect: true
|
|
27952
28767
|
})], LedElement.prototype, "lit", void 0);
|
|
27953
28768
|
__decorate([property({ type: String })], LedElement.prototype, "color", void 0);
|
|
28769
|
+
__decorate([property({
|
|
28770
|
+
type: Boolean,
|
|
28771
|
+
attribute: `glow`,
|
|
28772
|
+
reflect: true
|
|
28773
|
+
})], LedElement.prototype, "glow", void 0);
|
|
28774
|
+
__decorate([property({
|
|
28775
|
+
type: String,
|
|
28776
|
+
reflect: true
|
|
28777
|
+
})], LedElement.prototype, "mode", void 0);
|
|
27954
28778
|
__decorate([property({
|
|
27955
28779
|
type: Boolean,
|
|
27956
28780
|
attribute: `blinking`,
|
|
@@ -27960,6 +28784,18 @@ __decorate([property({
|
|
|
27960
28784
|
type: Number,
|
|
27961
28785
|
attribute: `rate`
|
|
27962
28786
|
})], LedElement.prototype, "rate", void 0);
|
|
28787
|
+
__decorate([property({
|
|
28788
|
+
type: Number,
|
|
28789
|
+
attribute: `attack-time`
|
|
28790
|
+
})], LedElement.prototype, "attackTime", void 0);
|
|
28791
|
+
__decorate([property({
|
|
28792
|
+
type: Number,
|
|
28793
|
+
attribute: `release-time`
|
|
28794
|
+
})], LedElement.prototype, "releaseTime", void 0);
|
|
28795
|
+
__decorate([property({
|
|
28796
|
+
type: Boolean,
|
|
28797
|
+
attribute: `pulse-retrigger`
|
|
28798
|
+
})], LedElement.prototype, "pulseRetrigger", void 0);
|
|
27963
28799
|
LedElement = __decorate([safeCustomElement(`ixfx-led`)], LedElement);
|
|
27964
28800
|
//#endregion
|
|
27965
28801
|
//#region src/miller/miller-base.ts
|
|
@@ -30127,6 +30963,66 @@ __decorate([query(`.text`)], PanTextElement.prototype, "_textEl", void 0);
|
|
|
30127
30963
|
PanTextElement = __decorate([safeCustomElement(`ixfx-pan-text`)], PanTextElement);
|
|
30128
30964
|
//#endregion
|
|
30129
30965
|
//#region src/panel/panel-group.ts
|
|
30966
|
+
/** Pointer movement threshold (px) before a press becomes a drag. */
|
|
30967
|
+
const DRAG_THRESHOLD_PX = 5;
|
|
30968
|
+
/**
|
|
30969
|
+
* Pure helper: nearest gap index for a pointer position along one axis.
|
|
30970
|
+
* `edges` are the [start, end] of each sibling (excluding the drag source),
|
|
30971
|
+
* in DOM order. Returns 0..edges.length.
|
|
30972
|
+
*/
|
|
30973
|
+
function panelGapIndex(edges, pos) {
|
|
30974
|
+
let bestGap = 0;
|
|
30975
|
+
let bestDist = Number.POSITIVE_INFINITY;
|
|
30976
|
+
for (let i = 0; i < edges.length; i++) {
|
|
30977
|
+
const [start, end] = edges[i];
|
|
30978
|
+
if (pos < (start + end) / 2) {
|
|
30979
|
+
const d = Math.abs(pos - start);
|
|
30980
|
+
if (d < bestDist) {
|
|
30981
|
+
bestDist = d;
|
|
30982
|
+
bestGap = i;
|
|
30983
|
+
}
|
|
30984
|
+
} else {
|
|
30985
|
+
const d = Math.abs(pos - end);
|
|
30986
|
+
if (d < bestDist) {
|
|
30987
|
+
bestDist = d;
|
|
30988
|
+
bestGap = i + 1;
|
|
30989
|
+
}
|
|
30990
|
+
}
|
|
30991
|
+
}
|
|
30992
|
+
return bestGap;
|
|
30993
|
+
}
|
|
30994
|
+
let panelGhostStylesInjected = false;
|
|
30995
|
+
/**
|
|
30996
|
+
* Global ghost styling so the drag preview matches `ixfx-titlebar` chrome
|
|
30997
|
+
* (font, colours, padding). Injected once; the ghost lives outside shadow DOM
|
|
30998
|
+
* (body/theme container) so component styles cannot reach it.
|
|
30999
|
+
*/
|
|
31000
|
+
function injectPanelGhostStyles() {
|
|
31001
|
+
if (panelGhostStylesInjected) return;
|
|
31002
|
+
panelGhostStylesInjected = true;
|
|
31003
|
+
const style = document.createElement(`style`);
|
|
31004
|
+
style.textContent = `
|
|
31005
|
+
.ixfx-drag-ghost.ixfx-panel-group-ghost {
|
|
31006
|
+
padding: var(--space-s, 8px) var(--space-m, 16px);
|
|
31007
|
+
background: var(--surface-4);
|
|
31008
|
+
color: var(--surface-3-text);
|
|
31009
|
+
border: none;
|
|
31010
|
+
border-radius: var(--radius-s, 4px);
|
|
31011
|
+
font-family: var(--font-family);
|
|
31012
|
+
font-size: var(--text-m);
|
|
31013
|
+
font-weight: var(--font-weight-bold, 600);
|
|
31014
|
+
min-height: 2em;
|
|
31015
|
+
user-select: none;
|
|
31016
|
+
-webkit-user-select: none;
|
|
31017
|
+
}
|
|
31018
|
+
.ixfx-drag-ghost.ixfx-panel-group-ghost ixfx-icon {
|
|
31019
|
+
width: 1.25em;
|
|
31020
|
+
height: 1.25em;
|
|
31021
|
+
flex-shrink: 0;
|
|
31022
|
+
}
|
|
31023
|
+
`;
|
|
31024
|
+
document.head.appendChild(style);
|
|
31025
|
+
}
|
|
30130
31026
|
let PanelGroupElement = class PanelGroupElement extends i {
|
|
30131
31027
|
constructor(..._args) {
|
|
30132
31028
|
super(..._args);
|
|
@@ -30134,22 +31030,105 @@ let PanelGroupElement = class PanelGroupElement extends i {
|
|
|
30134
31030
|
this.variant = `titlebar`;
|
|
30135
31031
|
this.toggleInteraction = `implicit`;
|
|
30136
31032
|
this.collapsible = true;
|
|
30137
|
-
this.
|
|
30138
|
-
this.
|
|
31033
|
+
this.accordion = false;
|
|
31034
|
+
this.allowReorder = false;
|
|
31035
|
+
this._dragState = null;
|
|
31036
|
+
this._suppressNextClick = false;
|
|
31037
|
+
this._onClickCapture = (event) => {
|
|
31038
|
+
if (!this._suppressNextClick) return;
|
|
31039
|
+
this._suppressNextClick = false;
|
|
31040
|
+
event.preventDefault();
|
|
31041
|
+
event.stopPropagation();
|
|
31042
|
+
};
|
|
31043
|
+
this._onKeyDown = (event) => {
|
|
31044
|
+
const state = this._dragState;
|
|
31045
|
+
if (event.key !== `Escape` || !state?.active) return;
|
|
31046
|
+
event.preventDefault();
|
|
31047
|
+
const pointerId = state.pointerId;
|
|
31048
|
+
this._abortDrag();
|
|
31049
|
+
this._endDrag(pointerId);
|
|
31050
|
+
};
|
|
31051
|
+
this._onPointerDown = (event) => {
|
|
31052
|
+
if (!this.allowReorder || event.button !== 0) return;
|
|
31053
|
+
const target = this._findDragTarget(event);
|
|
31054
|
+
if (!target?.parentNode) return;
|
|
31055
|
+
const fromIndex = Array.prototype.indexOf.call(target.parentNode.children, target);
|
|
31056
|
+
if (fromIndex < 0) return;
|
|
31057
|
+
this._dragState = {
|
|
31058
|
+
pointerId: event.pointerId,
|
|
31059
|
+
source: target,
|
|
31060
|
+
fromIndex,
|
|
31061
|
+
startX: event.clientX,
|
|
31062
|
+
startY: event.clientY,
|
|
31063
|
+
active: false,
|
|
31064
|
+
ghost: null,
|
|
31065
|
+
gapIndex: void 0
|
|
31066
|
+
};
|
|
31067
|
+
window.addEventListener(`pointermove`, this._onPointerMove);
|
|
31068
|
+
window.addEventListener(`pointerup`, this._onPointerUp);
|
|
31069
|
+
window.addEventListener(`pointercancel`, this._onPointerCancel);
|
|
31070
|
+
};
|
|
31071
|
+
this._onPointerMove = (event) => {
|
|
31072
|
+
const state = this._dragState;
|
|
31073
|
+
if (!state || event.pointerId !== state.pointerId) return;
|
|
31074
|
+
const distance = Math.hypot(event.clientX - state.startX, event.clientY - state.startY);
|
|
31075
|
+
if (!state.active) {
|
|
31076
|
+
if (distance < DRAG_THRESHOLD_PX) return;
|
|
31077
|
+
this._beginDrag(event);
|
|
31078
|
+
}
|
|
31079
|
+
state.ghost?.move(event.clientX, event.clientY);
|
|
31080
|
+
const gap = this._computeGapIndex(event.clientX, event.clientY);
|
|
31081
|
+
if (gap !== state.gapIndex) {
|
|
31082
|
+
state.gapIndex = gap;
|
|
31083
|
+
this.requestUpdate();
|
|
31084
|
+
}
|
|
31085
|
+
};
|
|
31086
|
+
this._onPointerUp = (event) => {
|
|
31087
|
+
const state = this._dragState;
|
|
31088
|
+
if (!state || event.pointerId !== state.pointerId) return;
|
|
31089
|
+
if (state.active) {
|
|
31090
|
+
const gap = this._computeGapIndex(event.clientX, event.clientY);
|
|
31091
|
+
this._commitDrag(state.source, state.fromIndex, gap);
|
|
31092
|
+
}
|
|
31093
|
+
this._endDrag(event.pointerId);
|
|
31094
|
+
};
|
|
31095
|
+
this._onPointerCancel = (event) => {
|
|
31096
|
+
const state = this._dragState;
|
|
31097
|
+
if (!state || event.pointerId !== state.pointerId) return;
|
|
31098
|
+
this._abortDrag();
|
|
31099
|
+
this._endDrag(event.pointerId);
|
|
31100
|
+
};
|
|
30139
31101
|
}
|
|
30140
31102
|
_onPanelToggle(e) {
|
|
30141
31103
|
const panel = e.target;
|
|
30142
31104
|
if (panel.collapsed) return;
|
|
30143
|
-
if (this.
|
|
31105
|
+
if (!this.accordion) return;
|
|
30144
31106
|
for (const p of this._panels) if (p !== panel) p.collapsed = true;
|
|
30145
31107
|
}
|
|
31108
|
+
connectedCallback() {
|
|
31109
|
+
super.connectedCallback();
|
|
31110
|
+
this.addEventListener(`click`, this._onClickCapture, true);
|
|
31111
|
+
}
|
|
31112
|
+
disconnectedCallback() {
|
|
31113
|
+
this.removeEventListener(`click`, this._onClickCapture, true);
|
|
31114
|
+
if (this._dragState) {
|
|
31115
|
+
this._abortDrag();
|
|
31116
|
+
this._endDrag(this._dragState.pointerId);
|
|
31117
|
+
}
|
|
31118
|
+
super.disconnectedCallback();
|
|
31119
|
+
}
|
|
30146
31120
|
firstUpdated() {
|
|
31121
|
+
if (this.gap !== void 0) this.style.setProperty(`--group-gap`, this.gap);
|
|
30147
31122
|
this.updateComplete.then(() => {
|
|
30148
31123
|
this._propagateAll();
|
|
30149
31124
|
});
|
|
30150
31125
|
}
|
|
30151
31126
|
updated(changed) {
|
|
30152
31127
|
super.updated(changed);
|
|
31128
|
+
if (changed.has(`gap`)) {
|
|
31129
|
+
if (this.gap !== void 0) this.style.setProperty(`--group-gap`, this.gap);
|
|
31130
|
+
else this.style.removeProperty(`--group-gap`);
|
|
31131
|
+
}
|
|
30153
31132
|
if (changed.has(`variant`) || changed.has(`toggleInteraction`) || changed.has(`orientation`) || changed.has(`collapsible`)) this.updateComplete.then(() => {
|
|
30154
31133
|
this._propagateAll();
|
|
30155
31134
|
});
|
|
@@ -30164,19 +31143,167 @@ let PanelGroupElement = class PanelGroupElement extends i {
|
|
|
30164
31143
|
else panel.removeAttribute(`collapsible`);
|
|
30165
31144
|
}
|
|
30166
31145
|
}
|
|
31146
|
+
_onSlotChange() {
|
|
31147
|
+
this.updateComplete.then(() => {
|
|
31148
|
+
this._propagateAll();
|
|
31149
|
+
});
|
|
31150
|
+
}
|
|
31151
|
+
/**
|
|
31152
|
+
* Walk composedPath for the nearest ixfx-panel, requiring the press to have
|
|
31153
|
+
* gone through an ixfx-titlebar (titlebar-anywhere drag). Bails when the
|
|
31154
|
+
* path crosses interactive chrome (buttons/links/inputs/toolbar/caret/close)
|
|
31155
|
+
* so those controls always win over dragging.
|
|
31156
|
+
*/
|
|
31157
|
+
_findDragTarget(event) {
|
|
31158
|
+
if (!this.allowReorder) return null;
|
|
31159
|
+
const path = event.composedPath();
|
|
31160
|
+
let panel = null;
|
|
31161
|
+
let viaTitlebar = false;
|
|
31162
|
+
for (const node of path) {
|
|
31163
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
31164
|
+
const tag = node.tagName;
|
|
31165
|
+
if (tag === `BUTTON` || tag === `A` || tag === `INPUT` || tag === `SELECT` || tag === `TEXTAREA`) return null;
|
|
31166
|
+
if (node.classList.contains(`toolbar`) || node.classList.contains(`close-btn`) || node.classList.contains(`caret-btn`)) return null;
|
|
31167
|
+
if (node.hasAttribute?.(`slot`) && node.getAttribute(`slot`) === `toolbar`) return null;
|
|
31168
|
+
if (tag === `IXFX-TITLEBAR`) viaTitlebar = true;
|
|
31169
|
+
if (!panel && tag === `IXFX-PANEL`) panel = node;
|
|
31170
|
+
}
|
|
31171
|
+
if (!panel || !viaTitlebar) return null;
|
|
31172
|
+
return panel;
|
|
31173
|
+
}
|
|
31174
|
+
_beginDrag(event) {
|
|
31175
|
+
const state = this._dragState;
|
|
31176
|
+
if (!state || state.active) return;
|
|
31177
|
+
try {
|
|
31178
|
+
this.setPointerCapture(event.pointerId);
|
|
31179
|
+
} catch {}
|
|
31180
|
+
state.active = true;
|
|
31181
|
+
this.setAttribute(`dragging`, ``);
|
|
31182
|
+
state.source.setAttribute(`drag-source`, ``);
|
|
31183
|
+
state.ghost = this._createGhost(state.source);
|
|
31184
|
+
state.ghost?.move(event.clientX, event.clientY);
|
|
31185
|
+
}
|
|
31186
|
+
_abortDrag() {
|
|
31187
|
+
const state = this._dragState;
|
|
31188
|
+
if (!state) return;
|
|
31189
|
+
state.source.removeAttribute(`drag-source`);
|
|
31190
|
+
if (state.gapIndex !== void 0) {
|
|
31191
|
+
state.gapIndex = void 0;
|
|
31192
|
+
this.requestUpdate();
|
|
31193
|
+
}
|
|
31194
|
+
}
|
|
31195
|
+
_endDrag(pointerId) {
|
|
31196
|
+
const state = this._dragState;
|
|
31197
|
+
if (!state) return;
|
|
31198
|
+
state.ghost?.destroy();
|
|
31199
|
+
state.ghost = null;
|
|
31200
|
+
this.removeAttribute(`dragging`);
|
|
31201
|
+
this._dragState = null;
|
|
31202
|
+
window.removeEventListener(`pointermove`, this._onPointerMove);
|
|
31203
|
+
window.removeEventListener(`pointerup`, this._onPointerUp);
|
|
31204
|
+
window.removeEventListener(`pointercancel`, this._onPointerCancel);
|
|
31205
|
+
try {
|
|
31206
|
+
this.releasePointerCapture(pointerId);
|
|
31207
|
+
} catch {}
|
|
31208
|
+
this.requestUpdate();
|
|
31209
|
+
}
|
|
31210
|
+
_commitDrag(source, fromIndex, gapIndex) {
|
|
31211
|
+
source.removeAttribute(`drag-source`);
|
|
31212
|
+
const siblings = (this._panels ?? []).filter((el) => el !== source);
|
|
31213
|
+
const target = gapIndex === void 0 ? siblings.length : Math.max(0, Math.min(gapIndex, siblings.length));
|
|
31214
|
+
const parent = source.parentNode;
|
|
31215
|
+
if (!parent) return;
|
|
31216
|
+
const ref = siblings[target] ?? null;
|
|
31217
|
+
parent.insertBefore(source, ref);
|
|
31218
|
+
const toIndex = Array.prototype.indexOf.call(parent.children, source);
|
|
31219
|
+
if (toIndex === fromIndex) return;
|
|
31220
|
+
this._suppressNextClick = true;
|
|
31221
|
+
this.dispatchEvent(new CustomEvent(`reorder`, {
|
|
31222
|
+
bubbles: true,
|
|
31223
|
+
composed: true,
|
|
31224
|
+
detail: {
|
|
31225
|
+
fromIndex,
|
|
31226
|
+
toIndex,
|
|
31227
|
+
panel: source
|
|
31228
|
+
}
|
|
31229
|
+
}));
|
|
31230
|
+
}
|
|
31231
|
+
_createGhost(source) {
|
|
31232
|
+
const content = document.createElement(`span`);
|
|
31233
|
+
content.style.display = `inline-flex`;
|
|
31234
|
+
content.style.alignItems = `center`;
|
|
31235
|
+
content.style.padding = `0 var(--space-s, 8px)`;
|
|
31236
|
+
content.style.minWidth = `0`;
|
|
31237
|
+
if (source.iconName) {
|
|
31238
|
+
const icon = document.createElement(`ixfx-icon`);
|
|
31239
|
+
icon.name = source.iconName;
|
|
31240
|
+
content.appendChild(icon);
|
|
31241
|
+
}
|
|
31242
|
+
const label = document.createElement(`span`);
|
|
31243
|
+
label.textContent = (source.title || (source.textContent ?? ``).trim()).slice(0, 60);
|
|
31244
|
+
label.style.paddingLeft = `1ch`;
|
|
31245
|
+
label.style.overflow = `hidden`;
|
|
31246
|
+
label.style.textOverflow = `ellipsis`;
|
|
31247
|
+
label.style.whiteSpace = `nowrap`;
|
|
31248
|
+
content.appendChild(label);
|
|
31249
|
+
try {
|
|
31250
|
+
injectPanelGhostStyles();
|
|
31251
|
+
return createDragGhost({
|
|
31252
|
+
anchor: source,
|
|
31253
|
+
content,
|
|
31254
|
+
className: `ixfx-panel-group-ghost`
|
|
31255
|
+
});
|
|
31256
|
+
} catch {
|
|
31257
|
+
return null;
|
|
31258
|
+
}
|
|
31259
|
+
}
|
|
31260
|
+
/**
|
|
31261
|
+
* Nearest panel gap to the pointer, ignoring the drag source.
|
|
31262
|
+
* The perpendicular axis is ignored — the pointer always resolves to a gap.
|
|
31263
|
+
*/
|
|
31264
|
+
_computeGapIndex(clientX, clientY) {
|
|
31265
|
+
const state = this._dragState;
|
|
31266
|
+
if (!state) return;
|
|
31267
|
+
const siblings = (this._panels ?? []).filter((el) => el !== state.source);
|
|
31268
|
+
if (siblings.length === 0) return 0;
|
|
31269
|
+
const useX = this.orientation === `horizontal`;
|
|
31270
|
+
const pos = useX ? clientX : clientY;
|
|
31271
|
+
return panelGapIndex(siblings.map((el) => {
|
|
31272
|
+
const rect = el.getBoundingClientRect();
|
|
31273
|
+
return [useX ? rect.left : rect.top, useX ? rect.right : rect.bottom];
|
|
31274
|
+
}), pos);
|
|
31275
|
+
}
|
|
31276
|
+
_computeCaretStyle() {
|
|
31277
|
+
const state = this._dragState;
|
|
31278
|
+
if (!state?.active || state.gapIndex === void 0) return;
|
|
31279
|
+
const siblings = (this._panels ?? []).filter((el) => el !== state.source);
|
|
31280
|
+
if (siblings.length === 0) return;
|
|
31281
|
+
const hostRect = this.getBoundingClientRect();
|
|
31282
|
+
if (!hostRect) return;
|
|
31283
|
+
const gap = Math.max(0, Math.min(state.gapIndex, siblings.length));
|
|
31284
|
+
const rect = gap < siblings.length ? siblings[gap].getBoundingClientRect() : siblings[siblings.length - 1].getBoundingClientRect();
|
|
31285
|
+
if (this.orientation === `horizontal`) return `transform: translate(${(gap < siblings.length ? rect.left : rect.right) - hostRect.left - 1}px, 0); width: 2px; height: ${hostRect.height}px;`;
|
|
31286
|
+
return `transform: translate(0, ${(gap < siblings.length ? rect.top : rect.bottom) - hostRect.top - 1}px); width: ${hostRect.width}px; height: 2px;`;
|
|
31287
|
+
}
|
|
30167
31288
|
render() {
|
|
31289
|
+
const caretStyle = this._computeCaretStyle();
|
|
30168
31290
|
return b`
|
|
30169
31291
|
<slot
|
|
30170
31292
|
@toggle=${this._onPanelToggle}
|
|
31293
|
+
@slotchange=${this._onSlotChange}
|
|
31294
|
+
@pointerdown=${this._onPointerDown}
|
|
31295
|
+
@keydown=${this._onKeyDown}
|
|
30171
31296
|
></slot>
|
|
31297
|
+
${caretStyle ? b`<div class="drop-caret" style=${caretStyle}></div>` : A}
|
|
30172
31298
|
`;
|
|
30173
31299
|
}
|
|
30174
31300
|
static {
|
|
30175
31301
|
this.styles = i$3`
|
|
30176
31302
|
:host {
|
|
31303
|
+
position: relative;
|
|
30177
31304
|
display: flex;
|
|
30178
31305
|
flex-direction: column;
|
|
30179
|
-
gap: var(--group-gap, var(--space-
|
|
31306
|
+
gap: var(--group-gap, var(--space-l));
|
|
30180
31307
|
}
|
|
30181
31308
|
|
|
30182
31309
|
:host([orientation="horizontal"]) {
|
|
@@ -30197,6 +31324,36 @@ let PanelGroupElement = class PanelGroupElement extends i {
|
|
|
30197
31324
|
flex-basis: content;
|
|
30198
31325
|
min-width: 0;
|
|
30199
31326
|
}
|
|
31327
|
+
|
|
31328
|
+
:host([allow-reorder]) ::slotted(ixfx-panel) {
|
|
31329
|
+
cursor: grab;
|
|
31330
|
+
}
|
|
31331
|
+
|
|
31332
|
+
:host([allow-reorder]) ::slotted(ixfx-panel[drag-source]) {
|
|
31333
|
+
cursor: grabbing;
|
|
31334
|
+
opacity: 0.4;
|
|
31335
|
+
}
|
|
31336
|
+
|
|
31337
|
+
/* While dragging, suppress text selection inside the panels. */
|
|
31338
|
+
:host([dragging]),
|
|
31339
|
+
:host([dragging]) ::slotted(*) {
|
|
31340
|
+
user-select: none;
|
|
31341
|
+
-webkit-user-select: none;
|
|
31342
|
+
}
|
|
31343
|
+
|
|
31344
|
+
:host([dragging]) ::slotted(ixfx-panel) {
|
|
31345
|
+
cursor: grabbing;
|
|
31346
|
+
}
|
|
31347
|
+
|
|
31348
|
+
.drop-caret {
|
|
31349
|
+
position: absolute;
|
|
31350
|
+
top: 0;
|
|
31351
|
+
left: 0;
|
|
31352
|
+
background: var(--accent);
|
|
31353
|
+
border-radius: 1px;
|
|
31354
|
+
pointer-events: none;
|
|
31355
|
+
z-index: 1;
|
|
31356
|
+
}
|
|
30200
31357
|
`;
|
|
30201
31358
|
}
|
|
30202
31359
|
};
|
|
@@ -30212,8 +31369,14 @@ __decorate([property({
|
|
|
30212
31369
|
__decorate([property({ type: Boolean })], PanelGroupElement.prototype, "collapsible", void 0);
|
|
30213
31370
|
__decorate([property({
|
|
30214
31371
|
type: Boolean,
|
|
30215
|
-
attribute: `
|
|
30216
|
-
|
|
31372
|
+
attribute: `accordion`,
|
|
31373
|
+
reflect: true
|
|
31374
|
+
})], PanelGroupElement.prototype, "accordion", void 0);
|
|
31375
|
+
__decorate([property({
|
|
31376
|
+
type: Boolean,
|
|
31377
|
+
attribute: `allow-reorder`,
|
|
31378
|
+
reflect: true
|
|
31379
|
+
})], PanelGroupElement.prototype, "allowReorder", void 0);
|
|
30217
31380
|
__decorate([property()], PanelGroupElement.prototype, "gap", void 0);
|
|
30218
31381
|
__decorate([queryAssignedElements({ slot: `` })], PanelGroupElement.prototype, "_panels", void 0);
|
|
30219
31382
|
PanelGroupElement = __decorate([safeCustomElement(`ixfx-panel-group`)], PanelGroupElement);
|
|
@@ -30926,11 +32089,18 @@ let PlotHistogram = class PlotHistogram extends i {
|
|
|
30926
32089
|
this.valueTooltips = `hover`;
|
|
30927
32090
|
this.tooltipDelay = 400;
|
|
30928
32091
|
this.valueUpdateInterval = 500;
|
|
32092
|
+
this.interactionMode = `inspect`;
|
|
30929
32093
|
this.valueFormat = (valueMin, valueMax, frequency) => `${valueMin.toFixed(2)} - ${valueMax.toFixed(2)}: ${frequency}`;
|
|
30930
32094
|
this._width = 100;
|
|
30931
32095
|
this._height = 40;
|
|
30932
32096
|
this._hoveredIndex = null;
|
|
30933
32097
|
this._inlineOverlayText = null;
|
|
32098
|
+
this._coloredRangeMin = null;
|
|
32099
|
+
this._coloredRangeMax = null;
|
|
32100
|
+
this._selectionStart = null;
|
|
32101
|
+
this._selectionEnd = null;
|
|
32102
|
+
this._isDragging = false;
|
|
32103
|
+
this._selectionOverlayText = null;
|
|
30934
32104
|
this.#series = new PlotDataSeries({
|
|
30935
32105
|
capacityLimit: 100,
|
|
30936
32106
|
persistentScaling: true
|
|
@@ -30943,6 +32113,22 @@ let PlotHistogram = class PlotHistogram extends i {
|
|
|
30943
32113
|
this.#tooltipPosition = null;
|
|
30944
32114
|
this.#valueRefreshTimer = null;
|
|
30945
32115
|
this.#onPointerMove = (e) => {
|
|
32116
|
+
if (this.interactionMode === `select` && this._isDragging) {
|
|
32117
|
+
const index = this.#bucketIndexFromPosition(e.clientX, e.clientY);
|
|
32118
|
+
this._selectionEnd = index;
|
|
32119
|
+
const start = Math.min(this._selectionStart, this._selectionEnd);
|
|
32120
|
+
const end = Math.max(this._selectionStart, this._selectionEnd);
|
|
32121
|
+
const bars = this.#computeBars();
|
|
32122
|
+
if (start <= end) {
|
|
32123
|
+
const startBar = bars[start];
|
|
32124
|
+
const endBar = bars[end];
|
|
32125
|
+
this.setColoredRange(startBar.valueMin, endBar.valueMax);
|
|
32126
|
+
this._selectionOverlayText = `Select: ${startBar.valueMin.toFixed(2)} – ${endBar.valueMax.toFixed(2)}`;
|
|
32127
|
+
}
|
|
32128
|
+
this.requestUpdate();
|
|
32129
|
+
return;
|
|
32130
|
+
}
|
|
32131
|
+
if (this.interactionMode === `select`) return;
|
|
30946
32132
|
if (this.valueTooltips === `none`) return;
|
|
30947
32133
|
const index = this.#bucketIndexFromPointer(e.clientX, e.clientY);
|
|
30948
32134
|
const bucketIndex = index >= 0 ? index : null;
|
|
@@ -30950,10 +32136,34 @@ let PlotHistogram = class PlotHistogram extends i {
|
|
|
30950
32136
|
else this.#updateBubbleTooltip(bucketIndex, e.clientX, e.clientY);
|
|
30951
32137
|
};
|
|
30952
32138
|
this.#onPointerLeave = () => {
|
|
32139
|
+
if (this.interactionMode === `select`) {
|
|
32140
|
+
if (this._isDragging) {
|
|
32141
|
+
this._isDragging = false;
|
|
32142
|
+
this._selectionOverlayText = null;
|
|
32143
|
+
this.requestUpdate();
|
|
32144
|
+
}
|
|
32145
|
+
return;
|
|
32146
|
+
}
|
|
30953
32147
|
if (this.valueTooltips === `none`) return;
|
|
30954
32148
|
if (this.valueTooltips === `inline`) this.#setInlineIndex(null);
|
|
30955
32149
|
else this.#updateBubbleTooltip(null, 0, 0);
|
|
30956
32150
|
};
|
|
32151
|
+
this.#onPointerDown = (e) => {
|
|
32152
|
+
if (this.interactionMode !== `select`) return;
|
|
32153
|
+
const index = this.#bucketIndexFromPosition(e.clientX, e.clientY);
|
|
32154
|
+
this._isDragging = true;
|
|
32155
|
+
this._selectionStart = index;
|
|
32156
|
+
this._selectionEnd = index;
|
|
32157
|
+
const bars = this.#computeBars();
|
|
32158
|
+
const startBar = bars[index];
|
|
32159
|
+
const endBar = bars[index];
|
|
32160
|
+
this.setColoredRange(startBar.valueMin, endBar.valueMax);
|
|
32161
|
+
this._selectionOverlayText = `Select: ${startBar.valueMin.toFixed(2)} – ${endBar.valueMax.toFixed(2)}`;
|
|
32162
|
+
this.requestUpdate();
|
|
32163
|
+
};
|
|
32164
|
+
this.#onPointerUp = () => {
|
|
32165
|
+
if (this.interactionMode === `select`) this._isDragging = false;
|
|
32166
|
+
};
|
|
30957
32167
|
}
|
|
30958
32168
|
#series;
|
|
30959
32169
|
#hasProgrammaticData;
|
|
@@ -30977,6 +32187,59 @@ let PlotHistogram = class PlotHistogram extends i {
|
|
|
30977
32187
|
this.#series.clear();
|
|
30978
32188
|
this.requestUpdate();
|
|
30979
32189
|
}
|
|
32190
|
+
setColoredRange(min, max) {
|
|
32191
|
+
this._coloredRangeMin = min;
|
|
32192
|
+
this._coloredRangeMax = max;
|
|
32193
|
+
this.requestUpdate();
|
|
32194
|
+
}
|
|
32195
|
+
clearColoredRange() {
|
|
32196
|
+
this._coloredRangeMin = null;
|
|
32197
|
+
this._coloredRangeMax = null;
|
|
32198
|
+
this.requestUpdate();
|
|
32199
|
+
}
|
|
32200
|
+
setSelection(startBarIndex, endBarIndex) {
|
|
32201
|
+
const bars = this.#computeBars();
|
|
32202
|
+
const start = Math.min(startBarIndex, endBarIndex);
|
|
32203
|
+
const end = Math.max(startBarIndex, endBarIndex);
|
|
32204
|
+
const clampedStart = Math.max(0, Math.min(start, bars.length - 1));
|
|
32205
|
+
const clampedEnd = Math.max(0, Math.min(end, bars.length - 1));
|
|
32206
|
+
this._selectionStart = clampedStart;
|
|
32207
|
+
this._selectionEnd = clampedEnd;
|
|
32208
|
+
if (clampedStart <= clampedEnd) {
|
|
32209
|
+
const startBar = bars[clampedStart];
|
|
32210
|
+
const endBar = bars[clampedEnd];
|
|
32211
|
+
this.setColoredRange(startBar.valueMin, endBar.valueMax);
|
|
32212
|
+
}
|
|
32213
|
+
this.requestUpdate();
|
|
32214
|
+
}
|
|
32215
|
+
clearSelection() {
|
|
32216
|
+
this._selectionStart = null;
|
|
32217
|
+
this._selectionEnd = null;
|
|
32218
|
+
this.requestUpdate();
|
|
32219
|
+
}
|
|
32220
|
+
getSelectedRange() {
|
|
32221
|
+
if (this._selectionStart === null || this._selectionEnd === null) return {
|
|
32222
|
+
barIndices: null,
|
|
32223
|
+
dataValues: null
|
|
32224
|
+
};
|
|
32225
|
+
const bars = this.#computeBars();
|
|
32226
|
+
const start = Math.min(this._selectionStart, this._selectionEnd);
|
|
32227
|
+
const end = Math.max(this._selectionStart, this._selectionEnd);
|
|
32228
|
+
const clampedStart = Math.max(0, Math.min(start, bars.length - 1));
|
|
32229
|
+
const clampedEnd = Math.max(0, Math.min(end, bars.length - 1));
|
|
32230
|
+
const startBar = bars[clampedStart];
|
|
32231
|
+
const endBar = bars[clampedEnd];
|
|
32232
|
+
return {
|
|
32233
|
+
barIndices: {
|
|
32234
|
+
start: clampedStart,
|
|
32235
|
+
end: clampedEnd
|
|
32236
|
+
},
|
|
32237
|
+
dataValues: {
|
|
32238
|
+
min: startBar.valueMin,
|
|
32239
|
+
max: endBar.valueMax
|
|
32240
|
+
}
|
|
32241
|
+
};
|
|
32242
|
+
}
|
|
30980
32243
|
firstUpdated(_changed) {
|
|
30981
32244
|
this._width = this.clientWidth || 100;
|
|
30982
32245
|
this._height = this.clientHeight || 40;
|
|
@@ -31111,6 +32374,10 @@ let PlotHistogram = class PlotHistogram extends i {
|
|
|
31111
32374
|
}
|
|
31112
32375
|
});
|
|
31113
32376
|
}
|
|
32377
|
+
#isBarInColoredRange(bar) {
|
|
32378
|
+
if (this._coloredRangeMin === null || this._coloredRangeMax === null) return true;
|
|
32379
|
+
return bar.valueMin < this._coloredRangeMax && bar.valueMax > this._coloredRangeMin;
|
|
32380
|
+
}
|
|
31114
32381
|
/** Index of the bar rect under the pointer, or -1 if none (matches click hit-testing). */
|
|
31115
32382
|
#bucketIndexFromPointer(clientX, clientY) {
|
|
31116
32383
|
const rect = this.getBoundingClientRect();
|
|
@@ -31118,6 +32385,18 @@ let PlotHistogram = class PlotHistogram extends i {
|
|
|
31118
32385
|
const y = clientY - rect.top;
|
|
31119
32386
|
return this.#computeBars().findIndex((b) => x >= b.x && x <= b.x + b.width && y >= b.y && y <= b.y + b.height);
|
|
31120
32387
|
}
|
|
32388
|
+
/** Gets bucket index from position, clamping to valid range (0 to bucketCount-1). */
|
|
32389
|
+
#bucketIndexFromPosition(clientX, clientY) {
|
|
32390
|
+
const { w, h, isHorizontal, padding, dataMin, dataSpan, bucketCount, availableMain } = this.#computeGeometry();
|
|
32391
|
+
const rect = this.getBoundingClientRect();
|
|
32392
|
+
const x = clientX - rect.left;
|
|
32393
|
+
const y = clientY - rect.top;
|
|
32394
|
+
let relativePos;
|
|
32395
|
+
if (isHorizontal) relativePos = (x - padding) / availableMain;
|
|
32396
|
+
else relativePos = (y - padding) / availableMain;
|
|
32397
|
+
relativePos = Math.max(0, Math.min(1, relativePos));
|
|
32398
|
+
return Math.floor(relativePos * bucketCount);
|
|
32399
|
+
}
|
|
31121
32400
|
#tooltipIndex;
|
|
31122
32401
|
#tooltipVisible;
|
|
31123
32402
|
#tooltipShowTimer;
|
|
@@ -31252,15 +32531,23 @@ let PlotHistogram = class PlotHistogram extends i {
|
|
|
31252
32531
|
}
|
|
31253
32532
|
#onPointerMove;
|
|
31254
32533
|
#onPointerLeave;
|
|
32534
|
+
#onPointerDown;
|
|
32535
|
+
#onPointerUp;
|
|
31255
32536
|
render() {
|
|
31256
32537
|
const { w: w$7, h, padding } = this.#computeGeometry();
|
|
31257
|
-
const barsContent = this.#computeBars().map((b, i) =>
|
|
31258
|
-
|
|
31259
|
-
|
|
31260
|
-
|
|
31261
|
-
|
|
32538
|
+
const barsContent = this.#computeBars().map((b, i) => {
|
|
32539
|
+
const greyStyle = this.#isBarInColoredRange(b) ? `` : `filter: grayscale(1)`;
|
|
32540
|
+
return w`
|
|
32541
|
+
<rect class="bar" x=${b.x} y=${b.y} width=${b.width} height=${b.height}
|
|
32542
|
+
style="--value: ${b.relativeValue}; ${greyStyle}"
|
|
32543
|
+
@click=${() => this.#emitClick(i, b.valueMin, b.valueMax, b.relativeValue)} />
|
|
32544
|
+
`;
|
|
32545
|
+
});
|
|
31262
32546
|
const textColor = `var(--plot-text, var(--surface-2-text, #fff))`;
|
|
31263
|
-
|
|
32547
|
+
let overlayText;
|
|
32548
|
+
if (this.interactionMode === `select` && this._selectionOverlayText !== null) overlayText = this._selectionOverlayText;
|
|
32549
|
+
else if (this.valueTooltips === `inline` && this._inlineOverlayText !== null) overlayText = this._inlineOverlayText;
|
|
32550
|
+
else overlayText = this.title;
|
|
31264
32551
|
const titleContent = overlayText ? w`<text x=${padding + 2} y=${padding + Math.min(10, h * .6)}
|
|
31265
32552
|
class="overlay-text" fill=${textColor}
|
|
31266
32553
|
style="opacity: 0.6">${overlayText}</text>` : w``;
|
|
@@ -31268,7 +32555,9 @@ let PlotHistogram = class PlotHistogram extends i {
|
|
|
31268
32555
|
<svg width=${w$7} height=${h} viewBox="0 0 ${w$7} ${h}"
|
|
31269
32556
|
xmlns="http://www.w3.org/2000/svg"
|
|
31270
32557
|
@pointermove=${this.#onPointerMove}
|
|
31271
|
-
@pointerleave=${this.#onPointerLeave}
|
|
32558
|
+
@pointerleave=${this.#onPointerLeave}
|
|
32559
|
+
@pointerdown=${this.#onPointerDown}
|
|
32560
|
+
@pointerup=${this.#onPointerUp}>
|
|
31272
32561
|
<rect width=${w$7} height=${h} fill="transparent" />
|
|
31273
32562
|
${barsContent}
|
|
31274
32563
|
${titleContent}
|
|
@@ -31330,10 +32619,17 @@ __decorate([property({
|
|
|
31330
32619
|
type: Number,
|
|
31331
32620
|
attribute: `value-update-interval`
|
|
31332
32621
|
})], PlotHistogram.prototype, "valueUpdateInterval", void 0);
|
|
32622
|
+
__decorate([property({ attribute: `interaction-mode` })], PlotHistogram.prototype, "interactionMode", void 0);
|
|
31333
32623
|
__decorate([state()], PlotHistogram.prototype, "_width", void 0);
|
|
31334
32624
|
__decorate([state()], PlotHistogram.prototype, "_height", void 0);
|
|
31335
32625
|
__decorate([state()], PlotHistogram.prototype, "_hoveredIndex", void 0);
|
|
31336
32626
|
__decorate([state()], PlotHistogram.prototype, "_inlineOverlayText", void 0);
|
|
32627
|
+
__decorate([state()], PlotHistogram.prototype, "_coloredRangeMin", void 0);
|
|
32628
|
+
__decorate([state()], PlotHistogram.prototype, "_coloredRangeMax", void 0);
|
|
32629
|
+
__decorate([state()], PlotHistogram.prototype, "_selectionStart", void 0);
|
|
32630
|
+
__decorate([state()], PlotHistogram.prototype, "_selectionEnd", void 0);
|
|
32631
|
+
__decorate([state()], PlotHistogram.prototype, "_isDragging", void 0);
|
|
32632
|
+
__decorate([state()], PlotHistogram.prototype, "_selectionOverlayText", void 0);
|
|
31337
32633
|
PlotHistogram = __decorate([safeCustomElement(`ixfx-plot-histogram`)], PlotHistogram);
|
|
31338
32634
|
//#endregion
|
|
31339
32635
|
//#region src/plots/single-axis.ts
|
|
@@ -39524,6 +40820,6 @@ function init() {
|
|
|
39524
40820
|
console.log(`Init`);
|
|
39525
40821
|
}
|
|
39526
40822
|
//#endregion
|
|
39527
|
-
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, LIST_SEARCH_COMMAND_IDS, LIST_SEARCH_MODES, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListSearchController, 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, createElementListSearchAdapter, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createTreeListSearchAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, listSearchStyles, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, prefersReducedMotion, readBaseColour, registerFontIcon, registerIcon, registerListSearchCommands, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, walk, wrapRoot };
|
|
40823
|
+
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, LIST_SEARCH_COMMAND_IDS, LIST_SEARCH_MODES, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListSearchController, 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, createDataListSearchAdapter, createElementIncrSearch, createElementListSearchAdapter, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createTreeListSearchAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, listSearchStyles, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, panelGapIndex, parseColourScaleAttr, parseLayout, pointerOverAnyHost, prefersReducedMotion, readBaseColour, registerFontIcon, registerIcon, registerListSearchCommands, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, walk, wrapRoot };
|
|
39528
40824
|
|
|
39529
40825
|
//# sourceMappingURL=index.js.map
|