@ixfx/components 0.6.2 → 0.7.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 +1490 -615
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +20477 -15855
- package/bundle/index.js.map +1 -1
- package/bundle/style.css +1 -1
- package/dist/ac-text.js +3 -2
- package/dist/ac-text.js.map +1 -1
- package/dist/ac-token.js +3 -2
- package/dist/ac-token.js.map +1 -1
- package/dist/{button-Bn0BuLpO.js → button-Dat2yHAU.js} +4 -3
- package/dist/button-Dat2yHAU.js.map +1 -0
- package/dist/button.js +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/{colour-picker-0stHB90J.js → colour-picker-BSDBL-QL.js} +4 -3
- package/dist/{colour-picker-0stHB90J.js.map → colour-picker-BSDBL-QL.js.map} +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.js +4 -2
- package/dist/crumbs.js.map +1 -1
- package/dist/data-display.js +1 -1
- package/dist/data-grid.js +4 -2
- package/dist/data-grid.js.map +1 -1
- package/dist/{defaults-BCiDhZbF.js → defaults-BkWCPJFL.js} +3 -20
- package/dist/defaults-BkWCPJFL.js.map +1 -0
- package/dist/editable-label-rzXkjf1H.d.ts +75 -0
- package/dist/editable-label-rzXkjf1H.d.ts.map +1 -0
- package/dist/editable-label.d.ts +2 -75
- package/dist/editable-label.js +2 -1
- package/dist/editable-label.js.map +1 -1
- package/dist/{fallbacks-DEE1nCTt.js → fallbacks-DNI9xt0F.js} +2 -2
- package/dist/{fallbacks-DEE1nCTt.js.map → fallbacks-DNI9xt0F.js.map} +1 -1
- package/dist/{grouped-item-lister-_pMDwIo1.d.ts → grouped-item-lister-DnWB-eFT.d.ts} +2 -2
- package/dist/{grouped-item-lister-_pMDwIo1.d.ts.map → grouped-item-lister-DnWB-eFT.d.ts.map} +1 -1
- package/dist/grouped-item-lister.d.ts +1 -1
- package/dist/{icons-Bm2ByQ3s.js → icon-CvnPanhA.js} +3 -6
- package/dist/icon-CvnPanhA.js.map +1 -0
- package/dist/icons.js +8 -2
- package/dist/icons.js.map +1 -0
- package/dist/index-CG2vIgPv.d.ts +101 -0
- package/dist/index-CG2vIgPv.d.ts.map +1 -0
- package/dist/index.d.ts +655 -65
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3682 -924
- package/dist/index.js.map +1 -1
- package/dist/interaction-D5XdKzbR.js +2 -0
- package/dist/labelled-radial-input.js +2 -1
- package/dist/labelled-radial-input.js.map +1 -1
- package/dist/labelled-slider-input-DzigjMwP.js +808 -0
- package/dist/labelled-slider-input-DzigjMwP.js.map +1 -0
- package/dist/labelled-slider-input.d.ts +2 -0
- package/dist/labelled-slider-input.js +2 -0
- package/dist/{types-DlVNJez_.d.ts → list-selection-types-DSuRNWpx.d.ts} +10 -2
- package/dist/list-selection-types-DSuRNWpx.d.ts.map +1 -0
- package/dist/menu-BjRvlgPy.js +825 -0
- package/dist/menu-BjRvlgPy.js.map +1 -0
- package/dist/menu-item-B-zYrTTi.js +797 -0
- package/dist/menu-item-B-zYrTTi.js.map +1 -0
- package/dist/menu.js +2 -1
- package/dist/miller.d.ts +1 -1
- package/dist/miller.js +3 -2
- package/dist/miller.js.map +1 -1
- package/dist/narrowed-text.js +4 -2
- package/dist/narrowed-text.js.map +1 -1
- package/dist/notifier.js +3 -2
- package/dist/notifier.js.map +1 -1
- package/dist/panel.d.ts +13 -13
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +37 -253
- package/dist/panel.js.map +1 -1
- package/dist/plots.js +1 -1
- package/dist/polar-pad.js +1 -1
- package/dist/{radial-input-Dk8Wydp5.js → radial-input-Dp5qz0ly.js} +2 -2
- package/dist/{radial-input-Dk8Wydp5.js.map → radial-input-Dp5qz0ly.js.map} +1 -1
- package/dist/radial-input.js +1 -1
- package/dist/range-input.js +1 -1
- package/dist/range.js +1 -1
- package/dist/registry-CkW7q09Q.js +21 -0
- package/dist/registry-CkW7q09Q.js.map +1 -0
- package/dist/select-horiz.js +3 -2
- package/dist/select-horiz.js.map +1 -1
- package/dist/{slider-input-XppH3vcy.d.ts → slider-input-BfRUW2ZB.d.ts} +16 -3
- package/dist/slider-input-BfRUW2ZB.d.ts.map +1 -0
- package/dist/{slider-input-CT4gcF-o.js → slider-input-De4eoT11.js} +382 -67
- package/dist/slider-input-De4eoT11.js.map +1 -0
- package/dist/slider-input.d.ts +1 -1
- package/dist/slider-input.js +1 -1
- package/dist/snackbar.js +1 -1
- package/dist/split-layout-BFSbUZQv.js +969 -0
- package/dist/split-layout-BFSbUZQv.js.map +1 -0
- package/dist/split-layout-JK3xWVX2.d.ts +179 -0
- package/dist/split-layout-JK3xWVX2.d.ts.map +1 -0
- package/dist/split-layout.d.ts +2 -60
- package/dist/split-layout.js +1 -617
- package/dist/style.css +1 -1
- package/dist/swipe.js +1 -1
- package/dist/{tab-list-D_1cYhB_.d.ts → tab-list-MPwq_1Jj.d.ts} +2 -1
- package/dist/tab-list-MPwq_1Jj.d.ts.map +1 -0
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +96 -14
- package/dist/tabs.js.map +1 -1
- package/dist/{tickled-controller-BtevrRLV.d.ts → tickled-controller-CRzdIEjJ.d.ts} +7 -1
- package/dist/tickled-controller-CRzdIEjJ.d.ts.map +1 -0
- package/dist/{tickled-controller-DkAU-0qL.js → tickled-controller-h9GmJ_bU.js} +8 -2
- package/dist/tickled-controller-h9GmJ_bU.js.map +1 -0
- package/dist/titlebar-DaCfpliv.d.ts +50 -0
- package/dist/titlebar-DaCfpliv.d.ts.map +1 -0
- package/dist/titlebar.d.ts +2 -0
- package/dist/titlebar.js +324 -0
- package/dist/titlebar.js.map +1 -0
- package/dist/{tree-BO6BXI2b.js → tree-Ch4W8qXQ.js} +4 -3
- package/dist/{tree-BO6BXI2b.js.map → tree-Ch4W8qXQ.js.map} +1 -1
- package/dist/tree.d.ts +1 -1
- package/dist/tree.js +1 -1
- package/dist/vertical-list-ClnqTkF3.js +814 -0
- package/dist/vertical-list-ClnqTkF3.js.map +1 -0
- package/dist/vertical-list.d.ts +8 -9
- package/dist/vertical-list.d.ts.map +1 -1
- package/dist/vertical-list.js +1 -736
- package/dist/{xy-axis-B6yMjckW.js → xy-axis-CxHC8z5E.js} +109 -3
- package/dist/xy-axis-CxHC8z5E.js.map +1 -0
- package/dist/xy-pad.js +1 -1
- package/docs-user/README.md +5 -0
- package/docs-user/detail-list.md +398 -0
- package/docs-user/dock.md +310 -0
- package/docs-user/index.json +27 -2
- package/docs-user/labelled-slider-input.md +198 -0
- package/docs-user/llms.txt +6 -1
- package/docs-user/panel.md +20 -1
- package/docs-user/slider-input.md +55 -15
- package/docs-user/split-layout.md +136 -170
- package/docs-user/tabs.md +34 -0
- package/docs-user/titlebar.md +102 -0
- package/docs-user/toolbar.md +95 -22
- package/docs-user/twosplit-layout.md +254 -0
- package/docs-user/user-catalog.md +8 -1
- package/llms.txt +6 -1
- package/package.json +1 -2
- package/dist/button-Bn0BuLpO.js.map +0 -1
- package/dist/defaults-BCiDhZbF.js.map +0 -1
- package/dist/editable-label.d.ts.map +0 -1
- package/dist/icons-Bm2ByQ3s.js.map +0 -1
- package/dist/menu-DIfU70AA.js +0 -1609
- package/dist/menu-DIfU70AA.js.map +0 -1
- package/dist/slider-input-CT4gcF-o.js.map +0 -1
- package/dist/slider-input-XppH3vcy.d.ts.map +0 -1
- package/dist/split-layout.d.ts.map +0 -1
- package/dist/split-layout.js.map +0 -1
- package/dist/tab-list-D_1cYhB_.d.ts.map +0 -1
- package/dist/tickled-controller-BtevrRLV.d.ts.map +0 -1
- package/dist/tickled-controller-DkAU-0qL.js.map +0 -1
- package/dist/types-DlVNJez_.d.ts.map +0 -1
- package/dist/vertical-list.js.map +0 -1
- package/dist/xy-axis-B6yMjckW.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,62 +1,66 @@
|
|
|
1
1
|
import { n as buildHighlightTargets, r as clearHighlights, t as applyHighlights } from "./highlight-B-eQrhhb.js";
|
|
2
|
-
import { t as
|
|
2
|
+
import { t as TickledController } from "./tickled-controller-h9GmJ_bU.js";
|
|
3
|
+
import { t as themeFallbacks } from "./fallbacks-DNI9xt0F.js";
|
|
3
4
|
import { t as popupSurfaceStyles } from "./popup-D80sj2QC.js";
|
|
4
5
|
import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
|
|
5
|
-
import {
|
|
6
|
-
import { t as IxfxIconElement } from "./
|
|
6
|
+
import { a as svgToDataUri, i as registerIcon, n as hasIcon, r as iconBus, t as getIcon } from "./registry-CkW7q09Q.js";
|
|
7
|
+
import { t as IxfxIconElement } from "./icon-CvnPanhA.js";
|
|
7
8
|
import { AcTextElement } from "./ac-text.js";
|
|
8
9
|
import { t as DataController } from "./data-controller-DOlmqNKb.js";
|
|
9
10
|
import { n as createIncrSearch, r as createFuzzySearch, t as createElementIncrSearch } from "./element-search-Y8SRfPGM.js";
|
|
10
11
|
import { n as createAcCompletionProvider, t as IncrSearchTreeController } from "./incr-search-vCzuao-2.js";
|
|
11
12
|
import { AcTokenElement } from "./ac-token.js";
|
|
13
|
+
import { i as ICON_CLOSE, n as ICON_CHECK, r as ICON_CHEVRON_DOWN, t as ICON_CARET_RIGHT } from "./defaults-BkWCPJFL.js";
|
|
14
|
+
import "./icons.js";
|
|
12
15
|
import { t as prominenceStyles } from "./prominence-DlFShic3.js";
|
|
13
|
-
import { n as buttonStyles, t as ButtonElement } from "./button-
|
|
14
|
-
import { t as
|
|
16
|
+
import { n as buttonStyles, t as ButtonElement } from "./button-Dat2yHAU.js";
|
|
17
|
+
import { n as ListSelectionController, t as VerticalListElement } from "./vertical-list-ClnqTkF3.js";
|
|
18
|
+
import "./menu-item-B-zYrTTi.js";
|
|
15
19
|
import { Checkbox } from "./checkbox.js";
|
|
16
|
-
import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-
|
|
20
|
+
import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-BSDBL-QL.js";
|
|
17
21
|
import { LabelledRangeInput } from "./labelled-range-input.js";
|
|
18
22
|
import { t as collectDataResult } from "./data-provider-35TyWclX.js";
|
|
19
|
-
import { i as TreeDataModel, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-
|
|
23
|
+
import { i as TreeDataModel, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-Ch4W8qXQ.js";
|
|
20
24
|
import { t as scrollFadeStyles } from "./scroll-fade-CAs1EabA.js";
|
|
21
25
|
import { NarrowedTextElement } from "./narrowed-text.js";
|
|
22
26
|
import { CrumbNavigationElement } from "./crumbs.js";
|
|
23
27
|
import { DataDisplayComponent } from "./data-display.js";
|
|
28
|
+
import { t as createDragGhost } from "./drag-ghost-DtSDvjri.js";
|
|
29
|
+
import { t as menu_exports } from "./menu-BjRvlgPy.js";
|
|
24
30
|
import { DataGridElement } from "./data-grid.js";
|
|
31
|
+
import { a as distribute, c as toSnapshot, i as buildModel, l as walk, n as SplitPanelElement, o as normalise, r as applySnapshot, s as resolveTracks, t as SplitLayoutElement } from "./split-layout-BFSbUZQv.js";
|
|
32
|
+
import { TabListElement } from "./tabs.js";
|
|
33
|
+
import { TitlebarElement } from "./titlebar.js";
|
|
25
34
|
import { EditableLabel } from "./editable-label.js";
|
|
26
|
-
import { t as
|
|
27
|
-
import { t as WheelNudgeMixin } from "./wheel-nudge-BlSCe_hD.js";
|
|
35
|
+
import { n as EditableNumber, t as LabelledSliderInput } from "./labelled-slider-input-DzigjMwP.js";
|
|
28
36
|
import { t as FormElement } from "./form-CsXJX15p.js";
|
|
29
37
|
import { PanelElement } from "./panel.js";
|
|
30
38
|
import { GroupedItemListerElement } from "./grouped-item-lister.js";
|
|
31
|
-
import { VerticalListElement } from "./vertical-list.js";
|
|
32
39
|
import { t as IxfxHexEditorElement } from "./hex-editor-Bk6rxhfg.js";
|
|
33
|
-
import { t as RadialInputElement } from "./radial-input-
|
|
40
|
+
import { t as RadialInputElement } from "./radial-input-Dp5qz0ly.js";
|
|
34
41
|
import { LabelledRadialInput } from "./labelled-radial-input.js";
|
|
42
|
+
import { t as SliderInputElement } from "./slider-input-De4eoT11.js";
|
|
35
43
|
import { LedElement } from "./led.js";
|
|
36
44
|
import { MillerBaseElement, MillerListElement, MillerTreeController } from "./miller.js";
|
|
37
45
|
import { NotificationPillElement } from "./notifier.js";
|
|
38
|
-
import { a as
|
|
46
|
+
import { a as TooltipElement, c as computeColour, i as PlotMultiAxis, l as parseColourScaleAttr, n as PlotSingleAxis, o as PlotDataSeries, r as PlotHistogram, s as applyAgeModification, t as PlotXyAxis, u as readBaseColour } from "./xy-axis-CxHC8z5E.js";
|
|
39
47
|
import { PolarPad } from "./polar-pad.js";
|
|
40
48
|
import { t as tooltipValueStyles } from "./tooltip-CJt3WLmN.js";
|
|
41
49
|
import { RangeInput } from "./range-input.js";
|
|
42
50
|
import { RangeElement } from "./range.js";
|
|
43
51
|
import { SelectHorizElement } from "./select-horiz.js";
|
|
44
|
-
import { t as SliderInputElement } from "./slider-input-CT4gcF-o.js";
|
|
45
52
|
import { SnackbarElement } from "./snackbar.js";
|
|
46
53
|
import { SnapContainer } from "./snap-container.js";
|
|
47
|
-
import { SplitLayoutElement } from "./split-layout.js";
|
|
48
54
|
import { SwipeElement } from "./swipe.js";
|
|
49
|
-
import { TabListElement } from "./tabs.js";
|
|
50
55
|
import { a as resolveCssVars, i as attachTimelineInteractions, n as TimelineTrackTabElement, o as resolveGrid, r as timelineStyles, s as TimelineController, t as IxfxTimelineElement } from "./timeline-B2fgDL4U.js";
|
|
51
56
|
import { TransitoryLabelElement } from "./transitory-label.js";
|
|
52
57
|
import { XyPad } from "./xy-pad.js";
|
|
53
|
-
import { LitElement, css, html, nothing } from "lit";
|
|
58
|
+
import { LitElement, css, html, nothing, render } from "lit";
|
|
54
59
|
import { customElement, eventOptions, property, query, queryAssignedElements, state } from "lit/decorators.js";
|
|
55
60
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
56
61
|
import { Trees } from "@ixfx/collections";
|
|
57
|
-
import { clamper, scaler } from "@ixfx/numbers";
|
|
62
|
+
import { clamp, clamper, scaler } from "@ixfx/numbers";
|
|
58
63
|
import { repeat } from "lit/directives/repeat.js";
|
|
59
|
-
import { classMap } from "lit/directives/class-map.js";
|
|
60
64
|
//#region src/button/radio-group.ts
|
|
61
65
|
let RadioGroup = class RadioGroup extends LitElement {
|
|
62
66
|
constructor(..._args) {
|
|
@@ -1503,606 +1507,3053 @@ __decorate([state()], CrumbPathElement.prototype, "_editStartIndex", void 0);
|
|
|
1503
1507
|
__decorate([state()], CrumbPathElement.prototype, "_openCumPath", void 0);
|
|
1504
1508
|
CrumbPathElement = __decorate([safeCustomElement(`ixfx-crumb-path`)], CrumbPathElement);
|
|
1505
1509
|
//#endregion
|
|
1506
|
-
//#region src/
|
|
1507
|
-
const
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
this.
|
|
1519
|
-
this.
|
|
1520
|
-
this.
|
|
1521
|
-
this
|
|
1522
|
-
this
|
|
1523
|
-
this
|
|
1524
|
-
this.#
|
|
1525
|
-
this.#
|
|
1526
|
-
this.#
|
|
1527
|
-
this.#
|
|
1528
|
-
this
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
this.
|
|
1532
|
-
|
|
1533
|
-
this.
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
this.
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1510
|
+
//#region src/detail-list/detail-list.ts
|
|
1511
|
+
const ITEM_SELECTOR = `:scope > li`;
|
|
1512
|
+
const SEARCH_HIGHLIGHT_KEY = `detail-list-search`;
|
|
1513
|
+
const LABEL_SPAN_CSS = `overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0`;
|
|
1514
|
+
let DetailListElement = class DetailListElement extends LitElement {
|
|
1515
|
+
constructor(..._args2) {
|
|
1516
|
+
super(..._args2);
|
|
1517
|
+
this.selectionMode = `single`;
|
|
1518
|
+
this.interactionMode = `standard`;
|
|
1519
|
+
this.sort = `none`;
|
|
1520
|
+
this.sortBy = ``;
|
|
1521
|
+
this.items = [];
|
|
1522
|
+
this.displayProperty = ``;
|
|
1523
|
+
this.iconProperty = ``;
|
|
1524
|
+
this.keyProperty = ``;
|
|
1525
|
+
this.itemWrap = `stop`;
|
|
1526
|
+
this._searchVisible = false;
|
|
1527
|
+
this._searchQuery = ``;
|
|
1528
|
+
this.#itemData = /* @__PURE__ */ new WeakMap();
|
|
1529
|
+
this.#grid = [];
|
|
1530
|
+
this.#positions = /* @__PURE__ */ new Map();
|
|
1531
|
+
this.#geometryScheduled = false;
|
|
1532
|
+
this.#builtFromProvider = [];
|
|
1533
|
+
this.#providerBuilt = 0;
|
|
1534
|
+
this.#data = new DataController(this);
|
|
1535
|
+
this.selection = new ListSelectionController({
|
|
1536
|
+
getOrderedItems: () => this._getVisibleItems(),
|
|
1537
|
+
isSelectable: (el) => this._isCheckable(el),
|
|
1538
|
+
getModes: () => ({
|
|
1539
|
+
selectionMode: this.selectionMode,
|
|
1540
|
+
interactionMode: this.interactionMode
|
|
1541
|
+
}),
|
|
1542
|
+
onChange: (selected, previous) => {
|
|
1543
|
+
this._syncItemStates();
|
|
1544
|
+
this._dispatch(`list-select`, {
|
|
1545
|
+
selected,
|
|
1546
|
+
previous
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
});
|
|
1550
|
+
this.tickled = new TickledController(this, {
|
|
1551
|
+
getItems: () => this._getVisibleItems().map((el) => ({
|
|
1552
|
+
id: el,
|
|
1553
|
+
enabled: !el.hasAttribute(`not-checked`)
|
|
1554
|
+
})),
|
|
1555
|
+
onTickle: (id) => {
|
|
1556
|
+
if (id !== void 0) this._dispatch(`list-tickle`, { item: id });
|
|
1557
|
+
},
|
|
1558
|
+
onStateChange: () => this._syncItemStates(),
|
|
1559
|
+
itemWrap: this.itemWrap
|
|
1560
|
+
});
|
|
1561
|
+
this._onPointerOver = (e) => {
|
|
1562
|
+
const item = this._findListItem(e.composedPath());
|
|
1563
|
+
if (item) this.tickled.handlePointerEnter(item);
|
|
1550
1564
|
};
|
|
1551
|
-
this.
|
|
1552
|
-
|
|
1553
|
-
this.renderRoot?.querySelector(`.number`)?.focus();
|
|
1565
|
+
this._onPointerLeave = (e) => {
|
|
1566
|
+
this.tickled.handlePointerLeave(e);
|
|
1554
1567
|
};
|
|
1555
|
-
this.
|
|
1556
|
-
|
|
1557
|
-
if (e.inputType !== `insertText` || !e.data) return;
|
|
1558
|
-
if (!this._isAllowedInput(e.data)) {
|
|
1568
|
+
this._onKeyDown = (e) => {
|
|
1569
|
+
if ((e.ctrlKey || e.metaKey) && e.key === `f`) {
|
|
1559
1570
|
e.preventDefault();
|
|
1571
|
+
this._openSearch();
|
|
1560
1572
|
return;
|
|
1561
1573
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
this.#lastInputValue = newValue;
|
|
1569
|
-
this.dispatchEvent(new CustomEvent(`input`, {
|
|
1570
|
-
detail: { value: newValue },
|
|
1571
|
-
bubbles: true,
|
|
1572
|
-
composed: true
|
|
1573
|
-
}));
|
|
1574
|
+
if ((e.ctrlKey || e.metaKey) && e.key === `a`) {
|
|
1575
|
+
if (this.selection.canSelectAll()) {
|
|
1576
|
+
e.preventDefault();
|
|
1577
|
+
this.selection.selectAll();
|
|
1578
|
+
}
|
|
1579
|
+
return;
|
|
1574
1580
|
}
|
|
1575
|
-
|
|
1576
|
-
this._handleKeydown = (e) => {
|
|
1577
|
-
if (!this._editing) return;
|
|
1578
|
-
if (e.key === `Enter`) {
|
|
1581
|
+
if (this._searchVisible && e.key === `Escape`) {
|
|
1579
1582
|
e.preventDefault();
|
|
1580
|
-
this.
|
|
1581
|
-
|
|
1582
|
-
} else if (e.key === `Escape`) {
|
|
1583
|
-
e.preventDefault();
|
|
1584
|
-
this.dispatchEvent(new CustomEvent(`cancelled`, {
|
|
1585
|
-
detail: { value: this._prevValue },
|
|
1586
|
-
bubbles: true,
|
|
1587
|
-
composed: true
|
|
1588
|
-
}));
|
|
1589
|
-
this._cancelEdit();
|
|
1590
|
-
this.dispatchEvent(new CustomEvent(`change`, {
|
|
1591
|
-
detail: { value: this._prevValue },
|
|
1592
|
-
bubbles: true,
|
|
1593
|
-
composed: true
|
|
1594
|
-
}));
|
|
1595
|
-
if (this.mode === `editor`) this._contentEl?.blur();
|
|
1583
|
+
this._closeSearch();
|
|
1584
|
+
return;
|
|
1596
1585
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1586
|
+
switch (e.key) {
|
|
1587
|
+
case `ArrowDown`:
|
|
1588
|
+
e.preventDefault();
|
|
1589
|
+
this.#navigateDown();
|
|
1590
|
+
if (e.shiftKey && this.interactionMode === `vscode`) {
|
|
1591
|
+
const cur = this.tickled.tickledId;
|
|
1592
|
+
if (cur) this.selection.rangeTo(cur);
|
|
1593
|
+
}
|
|
1594
|
+
break;
|
|
1595
|
+
case `ArrowUp`:
|
|
1596
|
+
e.preventDefault();
|
|
1597
|
+
this.#navigateUp();
|
|
1598
|
+
if (e.shiftKey && this.interactionMode === `vscode`) {
|
|
1599
|
+
const cur = this.tickled.tickledId;
|
|
1600
|
+
if (cur) this.selection.rangeTo(cur);
|
|
1601
|
+
}
|
|
1602
|
+
break;
|
|
1603
|
+
case `ArrowRight`:
|
|
1604
|
+
e.preventDefault();
|
|
1605
|
+
this.#navigateRight();
|
|
1606
|
+
break;
|
|
1607
|
+
case `ArrowLeft`:
|
|
1608
|
+
e.preventDefault();
|
|
1609
|
+
this.#navigateLeft();
|
|
1610
|
+
break;
|
|
1611
|
+
case `Home`:
|
|
1612
|
+
e.preventDefault();
|
|
1613
|
+
this.#navigateTo(this.#findFirstCheckable());
|
|
1614
|
+
break;
|
|
1615
|
+
case `End`:
|
|
1616
|
+
e.preventDefault();
|
|
1617
|
+
this.#navigateTo(this.#findLastCheckable());
|
|
1618
|
+
break;
|
|
1619
|
+
case `Enter`: {
|
|
1620
|
+
const id = this.tickled.tickledId;
|
|
1621
|
+
if (id) {
|
|
1622
|
+
e.preventDefault();
|
|
1623
|
+
this._dispatch(`list-item-click`, { item: id });
|
|
1624
|
+
this._activateItem(id);
|
|
1625
|
+
}
|
|
1626
|
+
break;
|
|
1627
|
+
}
|
|
1628
|
+
case ` `: {
|
|
1629
|
+
const id = this.tickled.tickledId;
|
|
1630
|
+
if (id) {
|
|
1631
|
+
e.preventDefault();
|
|
1632
|
+
this.selection.handleSpace(id);
|
|
1633
|
+
}
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
case `Escape`: this.selection.clear();
|
|
1604
1637
|
}
|
|
1605
1638
|
};
|
|
1606
|
-
this.
|
|
1607
|
-
|
|
1608
|
-
if (
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
this.
|
|
1612
|
-
this.#dragStartY = e.clientY;
|
|
1613
|
-
this.#dragStartValue = this._parseValue();
|
|
1614
|
-
this.#dragCurrentValue = this.#dragStartValue;
|
|
1615
|
-
this.#dragging = false;
|
|
1616
|
-
this.#hasDragged = false;
|
|
1617
|
-
this.setPointerCapture(e.pointerId);
|
|
1639
|
+
this._onClick = (e) => {
|
|
1640
|
+
const item = this._findListItem(e.composedPath());
|
|
1641
|
+
if (!item) return;
|
|
1642
|
+
this.focus();
|
|
1643
|
+
this._dispatch(`list-item-click`, { item });
|
|
1644
|
+
this.selection.handleClick(item, e);
|
|
1618
1645
|
};
|
|
1619
|
-
this.
|
|
1620
|
-
|
|
1621
|
-
if (this.
|
|
1622
|
-
if (this.disabled || this.noDrag) return;
|
|
1623
|
-
const deltaY = this.#dragStartY - e.clientY;
|
|
1624
|
-
if (!this.#dragging && Math.abs(deltaY) < DRAG_THRESHOLD_PX) return;
|
|
1625
|
-
this.#dragging = true;
|
|
1626
|
-
this.#hasDragged = true;
|
|
1627
|
-
this.toggleAttribute(`dragging`, true);
|
|
1628
|
-
this._onUserInteraction();
|
|
1629
|
-
const raw = this.#dragStartValue + deltaY * this.dragSensitivity * (this.step || 1);
|
|
1630
|
-
const snapped = this._clamp(this._snap(raw));
|
|
1631
|
-
this.#dragCurrentValue = this.step !== void 0 ? snapped : Number.parseFloat(snapped.toFixed(this.precision));
|
|
1632
|
-
const dragValueStr = this.step !== void 0 ? this._formatNumber(this.#dragCurrentValue) : this.#dragCurrentValue.toFixed(this.precision);
|
|
1633
|
-
if (dragValueStr !== this.#lastInputValue) {
|
|
1634
|
-
this.#lastInputValue = dragValueStr;
|
|
1635
|
-
this.dispatchEvent(new CustomEvent(`input`, {
|
|
1636
|
-
detail: { value: dragValueStr },
|
|
1637
|
-
bubbles: true,
|
|
1638
|
-
composed: true
|
|
1639
|
-
}));
|
|
1640
|
-
}
|
|
1641
|
-
this._requestDisplayUpdate();
|
|
1642
|
-
this._updateValueCss(this._normalizedValue(this.#dragCurrentValue));
|
|
1646
|
+
this._onDblClick = (e) => {
|
|
1647
|
+
const item = this._findListItem(e.composedPath());
|
|
1648
|
+
if (item) this._activateItem(item);
|
|
1643
1649
|
};
|
|
1644
|
-
this.
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
if (this.disabled || this.noDrag) return;
|
|
1648
|
-
this.#dragging = false;
|
|
1649
|
-
this.toggleAttribute(`dragging`, false);
|
|
1650
|
-
const finalStr = this.step !== void 0 ? this._formatNumber(this.#dragCurrentValue) : this.#dragCurrentValue.toFixed(this.precision);
|
|
1651
|
-
const prev = this.value;
|
|
1652
|
-
this.value = finalStr;
|
|
1653
|
-
if (finalStr !== prev) this.dispatchEvent(new CustomEvent(`change`, {
|
|
1654
|
-
detail: { value: finalStr },
|
|
1655
|
-
bubbles: true,
|
|
1656
|
-
composed: true
|
|
1657
|
-
}));
|
|
1650
|
+
this._onWheel = (e) => {
|
|
1651
|
+
e.preventDefault();
|
|
1652
|
+
this._listEl.scrollLeft += e.deltaY;
|
|
1658
1653
|
};
|
|
1659
|
-
this.
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1654
|
+
this._onSearchInput = (e) => {
|
|
1655
|
+
const input = e.target;
|
|
1656
|
+
this._searchQuery = input.value;
|
|
1657
|
+
this.#search?.setQuery(this._searchQuery).then(() => this.#recomputeGeometry());
|
|
1658
|
+
};
|
|
1659
|
+
this._onSearchKeydown = (e) => {
|
|
1660
|
+
if (e.key === `Escape`) {
|
|
1661
|
+
e.stopPropagation();
|
|
1662
|
+
this._closeSearch();
|
|
1663
|
+
} else if (e.key === `ArrowDown`) {
|
|
1664
|
+
e.preventDefault();
|
|
1665
|
+
this.focus();
|
|
1666
|
+
this.#navigateDown();
|
|
1667
|
+
} else if (e.key === `ArrowUp`) {
|
|
1668
|
+
e.preventDefault();
|
|
1669
|
+
this.focus();
|
|
1670
|
+
this.#navigateUp();
|
|
1671
|
+
} else if (e.key === `ArrowRight`) e.preventDefault();
|
|
1672
|
+
else if (e.key === `ArrowLeft`) e.preventDefault();
|
|
1673
|
+
else if (e.key === `Enter`) {
|
|
1674
|
+
e.preventDefault();
|
|
1675
|
+
e.stopPropagation();
|
|
1676
|
+
const id = this.tickled.tickledId ?? this.#findFirstCheckable();
|
|
1677
|
+
if (id) {
|
|
1678
|
+
this._dispatch(`list-item-click`, { item: id });
|
|
1679
|
+
this._activateItem(id);
|
|
1680
|
+
this._closeSearch();
|
|
1681
|
+
}
|
|
1682
|
+
} else if (e.key === ` `) {
|
|
1683
|
+
const id = this.tickled.tickledId;
|
|
1684
|
+
if (id) {
|
|
1685
|
+
e.preventDefault();
|
|
1686
|
+
this.selection.handleSpace(id);
|
|
1687
|
+
}
|
|
1663
1688
|
}
|
|
1664
1689
|
};
|
|
1665
|
-
this.mode = `editor`;
|
|
1666
|
-
this.entryMode = `number`;
|
|
1667
|
-
this.value = `0`;
|
|
1668
|
-
}
|
|
1669
|
-
render() {
|
|
1670
|
-
return html`
|
|
1671
|
-
${this.fillStyle ? html`<div class="track"></div>` : ``}
|
|
1672
|
-
${this.fillStyle ? html`<div class="value"><div class="bar"></div></div>` : ``}
|
|
1673
|
-
<div
|
|
1674
|
-
class="number"
|
|
1675
|
-
part="content"
|
|
1676
|
-
tabindex="${this.disabled ? -1 : 0}"
|
|
1677
|
-
contenteditable="${this._editing ? `plaintext-only` : `false`}"
|
|
1678
|
-
data-placeholder="${this.placeholder}"
|
|
1679
|
-
@click=${this._handleClick}
|
|
1680
|
-
@focus=${this._handleFocus}
|
|
1681
|
-
@blur=${this._handleBlur}
|
|
1682
|
-
@keydown=${this._handleKeydown}
|
|
1683
|
-
@beforeinput=${this._handleBeforeInput}
|
|
1684
|
-
@input=${this._stopNativeInput}
|
|
1685
|
-
></div>
|
|
1686
|
-
`;
|
|
1687
1690
|
}
|
|
1691
|
+
#search;
|
|
1692
|
+
#itemData;
|
|
1693
|
+
#grid;
|
|
1694
|
+
#positions;
|
|
1695
|
+
#geometryScheduled;
|
|
1696
|
+
#resizeObserver;
|
|
1697
|
+
#mutationObserver;
|
|
1698
|
+
#builtFromProvider;
|
|
1699
|
+
#providerBuilt;
|
|
1700
|
+
#data;
|
|
1688
1701
|
connectedCallback() {
|
|
1689
1702
|
super.connectedCallback();
|
|
1690
|
-
this.
|
|
1691
|
-
this.addEventListener(`
|
|
1692
|
-
this.addEventListener(`
|
|
1703
|
+
if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
|
|
1704
|
+
this.addEventListener(`pointerover`, this._onPointerOver);
|
|
1705
|
+
this.addEventListener(`pointerleave`, this._onPointerLeave);
|
|
1693
1706
|
this.addEventListener(`keydown`, this._onKeyDown);
|
|
1694
|
-
this.addEventListener(`click`, this.
|
|
1695
|
-
this.addEventListener(`
|
|
1696
|
-
this.addEventListener(`
|
|
1707
|
+
this.addEventListener(`click`, this._onClick);
|
|
1708
|
+
this.addEventListener(`dblclick`, this._onDblClick);
|
|
1709
|
+
this.addEventListener(`wheel`, this._onWheel);
|
|
1710
|
+
this.#resizeObserver = new ResizeObserver(() => this.#scheduleGeometry());
|
|
1711
|
+
this.#resizeObserver.observe(this);
|
|
1712
|
+
this.#mutationObserver = new MutationObserver(() => {
|
|
1713
|
+
this.#processChildren();
|
|
1714
|
+
this.#scheduleGeometry();
|
|
1715
|
+
});
|
|
1716
|
+
this.#mutationObserver.observe(this, {
|
|
1717
|
+
childList: true,
|
|
1718
|
+
subtree: false,
|
|
1719
|
+
attributeFilter: [`hidden`]
|
|
1720
|
+
});
|
|
1697
1721
|
}
|
|
1698
1722
|
disconnectedCallback() {
|
|
1699
1723
|
super.disconnectedCallback();
|
|
1700
|
-
this.removeEventListener(`
|
|
1701
|
-
this.removeEventListener(`
|
|
1702
|
-
this.removeEventListener(`pointerup`, this._onPointerUp);
|
|
1724
|
+
this.removeEventListener(`pointerover`, this._onPointerOver);
|
|
1725
|
+
this.removeEventListener(`pointerleave`, this._onPointerLeave);
|
|
1703
1726
|
this.removeEventListener(`keydown`, this._onKeyDown);
|
|
1704
|
-
this.removeEventListener(`click`, this.
|
|
1705
|
-
this.removeEventListener(`
|
|
1706
|
-
this.removeEventListener(`
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
this
|
|
1716
|
-
this.
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
+
this.removeEventListener(`click`, this._onClick);
|
|
1728
|
+
this.removeEventListener(`dblclick`, this._onDblClick);
|
|
1729
|
+
this.removeEventListener(`wheel`, this._onWheel);
|
|
1730
|
+
this.#resizeObserver?.disconnect();
|
|
1731
|
+
this.#resizeObserver = void 0;
|
|
1732
|
+
this.#mutationObserver?.disconnect();
|
|
1733
|
+
this.#mutationObserver = void 0;
|
|
1734
|
+
this.#search?.dispose();
|
|
1735
|
+
this.#search = void 0;
|
|
1736
|
+
}
|
|
1737
|
+
firstUpdated() {
|
|
1738
|
+
this.#processChildren();
|
|
1739
|
+
if (this.filterPredicate) this.#applyFilterPredicate();
|
|
1740
|
+
this.#search = createElementIncrSearch({
|
|
1741
|
+
container: this,
|
|
1742
|
+
itemSelector: ITEM_SELECTOR,
|
|
1743
|
+
getLabel: (el) => el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
|
|
1744
|
+
getKey: (el) => el.getAttribute(`data-key`) ?? el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
|
|
1745
|
+
highlightKey: SEARCH_HIGHLIGHT_KEY
|
|
1746
|
+
});
|
|
1747
|
+
this.#recomputeGeometry();
|
|
1748
|
+
}
|
|
1749
|
+
updated(changed) {
|
|
1750
|
+
super.updated(changed);
|
|
1751
|
+
if (changed.has(`provider`)) {
|
|
1752
|
+
this.#builtFromProvider = [];
|
|
1753
|
+
this.#providerBuilt = 0;
|
|
1754
|
+
this.#data.provider = this.provider;
|
|
1755
|
+
queueMicrotask(() => {
|
|
1756
|
+
if (this.provider) this.#data.request(void 0);
|
|
1757
|
+
});
|
|
1727
1758
|
return;
|
|
1728
1759
|
}
|
|
1729
|
-
if (this.
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
this
|
|
1734
|
-
|
|
1735
|
-
this._transientShowingValue = false;
|
|
1736
|
-
this._requestDisplayUpdate();
|
|
1737
|
-
}
|
|
1738
|
-
}, this.disappearDelay);
|
|
1739
|
-
return;
|
|
1760
|
+
if (changed.has(`items`) && this.provider === void 0 && this.items.length > 0) this.#rebuildFromObjects(this.items);
|
|
1761
|
+
this.#syncFromProvider();
|
|
1762
|
+
if (changed.has(`sort`) || changed.has(`sortBy`) || changed.has(`sortComparator`)) this.#applySort();
|
|
1763
|
+
if (changed.has(`filterPredicate`)) {
|
|
1764
|
+
this.#applyFilterPredicate();
|
|
1765
|
+
this.#scheduleGeometry();
|
|
1740
1766
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
if (this.
|
|
1745
|
-
this._clearTransientTimer();
|
|
1746
|
-
this._showModeWaitingToHide = false;
|
|
1747
|
-
this._requestDisplayUpdate();
|
|
1748
|
-
this._transientTimer = window.setTimeout(() => {
|
|
1749
|
-
this._showModeWaitingToHide = true;
|
|
1750
|
-
this._requestDisplayUpdate();
|
|
1751
|
-
}, this.disappearDelay);
|
|
1767
|
+
if (changed.has(`interactionMode`)) {
|
|
1768
|
+
this.selection.clearAnchor();
|
|
1769
|
+
this._syncItemStates();
|
|
1770
|
+
} else if (changed.has(`checkboxRenderer`) && this.interactionMode === `checked`) this._syncCheckboxes();
|
|
1752
1771
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
let valueEnd = normalized;
|
|
1761
|
-
if (this.linearStyle === `bipolar`) {
|
|
1762
|
-
if (normalized <= .5) {
|
|
1763
|
-
valueStart = normalized;
|
|
1764
|
-
valueEnd = .5;
|
|
1765
|
-
} else {
|
|
1766
|
-
valueStart = .5;
|
|
1767
|
-
valueEnd = normalized;
|
|
1768
|
-
}
|
|
1769
|
-
} else if (this.linearStyle === `unipolar`) {
|
|
1770
|
-
valueStart = .5 - normalized * .5;
|
|
1771
|
-
valueEnd = .5 + normalized * .5;
|
|
1772
|
-
}
|
|
1773
|
-
this.style.setProperty(`--value-start`, String(valueStart));
|
|
1774
|
-
this.style.setProperty(`--value-end`, String(valueEnd));
|
|
1775
|
-
if (this.fillStyle === `circular` && this.circularStyle === `offset`) {
|
|
1776
|
-
const h = this.offsetHeight;
|
|
1777
|
-
const w = this.offsetWidth;
|
|
1778
|
-
const r = h / 2 - 2;
|
|
1779
|
-
const leftEdge = 2;
|
|
1780
|
-
const rightEdge = w - 2;
|
|
1781
|
-
const valueCenterPx = leftEdge + r + normalized * (rightEdge - leftEdge - 2 * r);
|
|
1782
|
-
this.style.setProperty(`--radius`, `${r}px`);
|
|
1783
|
-
this.style.setProperty(`--value-center-px`, `${valueCenterPx}px`);
|
|
1784
|
-
this.style.setProperty(`--component-width-px`, `${w}px`);
|
|
1785
|
-
this.style.setProperty(`--component-height-px`, `${h}px`);
|
|
1786
|
-
}
|
|
1772
|
+
/** The most recently selected item, or `undefined` if nothing is selected. */
|
|
1773
|
+
get selectedItem() {
|
|
1774
|
+
return [...this.selection.selected].at(-1);
|
|
1775
|
+
}
|
|
1776
|
+
/** The full current selection set (read-only). */
|
|
1777
|
+
get selectedItems() {
|
|
1778
|
+
return this.selection.selected;
|
|
1787
1779
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1780
|
+
/**
|
|
1781
|
+
* Force an immediate geometry recompute. Called automatically on resize,
|
|
1782
|
+
* DOM changes, sort/filter/search changes — only needed when layout is
|
|
1783
|
+
* changed by outside forces (e.g. an external font load).
|
|
1784
|
+
*/
|
|
1785
|
+
refreshLayout() {
|
|
1786
|
+
this.#recomputeGeometry();
|
|
1787
|
+
}
|
|
1788
|
+
addItem(item, options) {
|
|
1789
|
+
let el;
|
|
1790
|
+
if (typeof item === `string`) {
|
|
1791
|
+
const li = document.createElement(`li`);
|
|
1792
|
+
this._setLabel(li, item);
|
|
1793
|
+
el = li;
|
|
1794
|
+
} else el = item;
|
|
1795
|
+
if (options?.notChecked) el.setAttribute(`not-checked`, ``);
|
|
1796
|
+
this.appendChild(el);
|
|
1797
|
+
this.#ensureIcon(el);
|
|
1798
|
+
if (this.sort !== `none`) this.#applySort();
|
|
1799
|
+
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
1800
|
+
}
|
|
1801
|
+
removeItem(item) {
|
|
1802
|
+
item.remove();
|
|
1803
|
+
}
|
|
1804
|
+
clearItems() {
|
|
1805
|
+
this.replaceChildren();
|
|
1806
|
+
this.selection.reset();
|
|
1807
|
+
this.#builtFromProvider = [];
|
|
1808
|
+
this.#providerBuilt = 0;
|
|
1809
|
+
this._syncItemStates();
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* Replace the entire item list in one call.
|
|
1813
|
+
* Equivalent to `clearItems()` followed by `addItem()` for each element.
|
|
1814
|
+
*/
|
|
1815
|
+
setItems(items) {
|
|
1816
|
+
this.clearItems();
|
|
1817
|
+
for (const item of items) {
|
|
1818
|
+
this.appendChild(item);
|
|
1819
|
+
this.#ensureIcon(item);
|
|
1793
1820
|
}
|
|
1794
|
-
if (this.
|
|
1795
|
-
|
|
1796
|
-
|
|
1821
|
+
if (this.sort !== `none`) this.#applySort();
|
|
1822
|
+
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
1823
|
+
}
|
|
1824
|
+
/**
|
|
1825
|
+
* Get the source object behind an `<li>` built from an `items` array or
|
|
1826
|
+
* provider. Returns `undefined` for declarative / programmatic elements.
|
|
1827
|
+
*/
|
|
1828
|
+
getItemData(item) {
|
|
1829
|
+
return this.#itemData.get(item);
|
|
1830
|
+
}
|
|
1831
|
+
/** Dispatch a command to this list. No-op by default (grouped-lister parity). */
|
|
1832
|
+
dispatch(_commandId, _args) {}
|
|
1833
|
+
select(item) {
|
|
1834
|
+
this.selection.replace(item);
|
|
1835
|
+
}
|
|
1836
|
+
selectMany(items) {
|
|
1837
|
+
this.selection.selectMany(items);
|
|
1838
|
+
}
|
|
1839
|
+
deselect(item) {
|
|
1840
|
+
this.selection.deselect(item);
|
|
1841
|
+
}
|
|
1842
|
+
clearSelection() {
|
|
1843
|
+
this.selection.clear();
|
|
1844
|
+
}
|
|
1845
|
+
selectAll() {
|
|
1846
|
+
if (this.selection.canSelectAll()) this.selection.selectAll();
|
|
1847
|
+
}
|
|
1848
|
+
*selectedFilter(kind) {
|
|
1849
|
+
const wanted = (el) => {
|
|
1850
|
+
switch (kind) {
|
|
1851
|
+
case `selected`: return el.hasAttribute(`data-selected`);
|
|
1852
|
+
case `checked`: return el.hasAttribute(`data-checked`);
|
|
1853
|
+
case `either`: return el.hasAttribute(`data-selected`) || el.hasAttribute(`data-checked`);
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
|
|
1857
|
+
if (el.hidden) continue;
|
|
1858
|
+
if (wanted(el)) yield el;
|
|
1797
1859
|
}
|
|
1798
|
-
return this._formatDisplay();
|
|
1799
1860
|
}
|
|
1800
|
-
|
|
1801
|
-
const
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1861
|
+
#rebuildFromObjects(items) {
|
|
1862
|
+
const keyOf = (el) => el.getAttribute(`data-key`) ?? el.getAttribute(`data-search-label`) ?? null;
|
|
1863
|
+
const previousKeys = new Set([...this.selection.selected].map(keyOf).filter((k) => k !== null));
|
|
1864
|
+
const anchorKey = this.selection.anchor ? keyOf(this.selection.anchor) : null;
|
|
1865
|
+
this.replaceChildren(...items.map((item, index) => this.#createItemEl(item, index)));
|
|
1866
|
+
if (previousKeys.size > 0 || anchorKey !== null) {
|
|
1867
|
+
const next = [];
|
|
1868
|
+
let anchor;
|
|
1869
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
|
|
1870
|
+
const key = keyOf(el);
|
|
1871
|
+
if (key !== null && previousKeys.has(key)) next.push(el);
|
|
1872
|
+
if (key !== null && key === anchorKey) anchor = el;
|
|
1873
|
+
}
|
|
1874
|
+
this.selection.seed(next, anchor);
|
|
1805
1875
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
case `Home`:
|
|
1826
|
-
delta = -(this._parseValue() - (this.min ?? 0));
|
|
1827
|
-
handled = true;
|
|
1828
|
-
break;
|
|
1829
|
-
case `End`:
|
|
1830
|
-
delta = (this.max ?? 0) - this._parseValue();
|
|
1831
|
-
handled = true;
|
|
1876
|
+
if (this.sort !== `none`) this.#applySort();
|
|
1877
|
+
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
1878
|
+
this._syncItemStates();
|
|
1879
|
+
this.#scheduleGeometry();
|
|
1880
|
+
}
|
|
1881
|
+
#createItemEl(item, index) {
|
|
1882
|
+
const li = document.createElement(`li`);
|
|
1883
|
+
this.#itemData.set(li, item);
|
|
1884
|
+
const content = this.formatter ? this.formatter(item, index) : this.displayProperty ? this.#propertyValue(item, this.displayProperty) : item;
|
|
1885
|
+
if (typeof content === `string`) {
|
|
1886
|
+
this._setLabel(li, content);
|
|
1887
|
+
if (this.iconProperty) {
|
|
1888
|
+
const icon = this.#propertyValue(item, this.iconProperty);
|
|
1889
|
+
if (typeof icon === `string` && icon.length > 0) li.setAttribute(`data-icon`, icon);
|
|
1890
|
+
}
|
|
1891
|
+
} else if (content instanceof Node) li.appendChild(content);
|
|
1892
|
+
if (this.keyProperty) {
|
|
1893
|
+
const key = this.#propertyValue(item, this.keyProperty);
|
|
1894
|
+
if (key !== void 0 && key !== null) li.setAttribute(`data-key`, String(key));
|
|
1832
1895
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1896
|
+
this.#ensureIcon(li);
|
|
1897
|
+
return li;
|
|
1898
|
+
}
|
|
1899
|
+
#propertyValue(item, prop) {
|
|
1900
|
+
if (typeof item !== `object` || item === null || !(prop in item)) return void 0;
|
|
1901
|
+
return item[prop];
|
|
1902
|
+
}
|
|
1903
|
+
#syncFromProvider() {
|
|
1904
|
+
if (this.provider === void 0) return;
|
|
1905
|
+
const items = this.#data.items;
|
|
1906
|
+
if (items === this.#builtFromProvider) return;
|
|
1907
|
+
let prefix = 0;
|
|
1908
|
+
while (prefix < this.#builtFromProvider.length && prefix < items.length && this.#builtFromProvider[prefix] === items[prefix]) prefix++;
|
|
1909
|
+
if (prefix === this.#builtFromProvider.length && prefix <= items.length) {
|
|
1910
|
+
const tail = items.slice(prefix);
|
|
1911
|
+
for (const item of tail) this.appendChild(this.#createItemEl(item, this.#providerBuilt++));
|
|
1912
|
+
if (this.sort !== `none`) this.#applySort();
|
|
1913
|
+
if (this.interactionMode === `checked`) this._syncCheckboxes();
|
|
1914
|
+
this._syncItemStates();
|
|
1915
|
+
this.#scheduleGeometry();
|
|
1916
|
+
} else {
|
|
1917
|
+
this.#providerBuilt = 0;
|
|
1918
|
+
this.#rebuildFromObjects(items);
|
|
1837
1919
|
}
|
|
1838
|
-
|
|
1920
|
+
this.#builtFromProvider = items;
|
|
1839
1921
|
}
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
const
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
this.
|
|
1850
|
-
this.
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1922
|
+
#applyFilterPredicate() {
|
|
1923
|
+
if (!this.filterPredicate) {
|
|
1924
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR)) el.removeAttribute(`hidden`);
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR)) if (this.filterPredicate(el)) el.removeAttribute(`hidden`);
|
|
1928
|
+
else el.setAttribute(`hidden`, ``);
|
|
1929
|
+
}
|
|
1930
|
+
#applySort() {
|
|
1931
|
+
if (this.sort === `none`) return;
|
|
1932
|
+
const rows = [...this.querySelectorAll(ITEM_SELECTOR)];
|
|
1933
|
+
if (rows.length < 2) return;
|
|
1934
|
+
const dir = this.sort === `descending` ? -1 : 1;
|
|
1935
|
+
const meta = (el) => ({
|
|
1936
|
+
element: el,
|
|
1937
|
+
data: this.getItemData(el),
|
|
1938
|
+
label: el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``
|
|
1939
|
+
});
|
|
1940
|
+
let cmp;
|
|
1941
|
+
if (this.sortComparator) {
|
|
1942
|
+
const user = this.sortComparator;
|
|
1943
|
+
cmp = (a, b) => user(meta(a), meta(b));
|
|
1944
|
+
} else if (this.sortBy) {
|
|
1945
|
+
const prop = this.sortBy;
|
|
1946
|
+
const key = (el) => {
|
|
1947
|
+
const v = this.#propertyValue(this.getItemData(el), prop);
|
|
1948
|
+
if (typeof v === `number`) return v;
|
|
1949
|
+
return v === void 0 || v === null ? `` : String(v);
|
|
1950
|
+
};
|
|
1951
|
+
cmp = (a, b) => {
|
|
1952
|
+
const ka = key(a);
|
|
1953
|
+
const kb = key(b);
|
|
1954
|
+
if (typeof ka === `number` && typeof kb === `number`) return ka - kb;
|
|
1955
|
+
return String(ka).localeCompare(String(kb), void 0, { numeric: true });
|
|
1956
|
+
};
|
|
1957
|
+
} else cmp = (a, b) => meta(a).label.localeCompare(meta(b).label, void 0, { numeric: true });
|
|
1958
|
+
const indexed = rows.map((el, i) => ({
|
|
1959
|
+
el,
|
|
1960
|
+
i
|
|
1856
1961
|
}));
|
|
1962
|
+
indexed.sort((a, b) => cmp(a.el, b.el) * dir || a.i - b.i);
|
|
1963
|
+
for (const { el } of indexed) this.appendChild(el);
|
|
1964
|
+
this.#scheduleGeometry();
|
|
1965
|
+
}
|
|
1966
|
+
#scheduleGeometry() {
|
|
1967
|
+
if (this.#geometryScheduled) return;
|
|
1968
|
+
this.#geometryScheduled = true;
|
|
1857
1969
|
requestAnimationFrame(() => {
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1970
|
+
this.#geometryScheduled = false;
|
|
1971
|
+
this.#recomputeGeometry();
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
#recomputeGeometry() {
|
|
1975
|
+
this.#grid = [];
|
|
1976
|
+
this.#positions.clear();
|
|
1977
|
+
if (!this.isConnected) return;
|
|
1978
|
+
const columns = [];
|
|
1979
|
+
for (const el of this._getVisibleItems()) {
|
|
1980
|
+
const rect = el.getBoundingClientRect();
|
|
1981
|
+
if (rect.width === 0 && rect.height === 0) continue;
|
|
1982
|
+
let ci = columns.findIndex((c) => Math.abs(c.left - rect.left) <= 1);
|
|
1983
|
+
if (ci === -1) {
|
|
1984
|
+
columns.push({
|
|
1985
|
+
left: rect.left,
|
|
1986
|
+
items: []
|
|
1987
|
+
});
|
|
1988
|
+
ci = columns.length - 1;
|
|
1989
|
+
}
|
|
1990
|
+
columns[ci].items.push(el);
|
|
1991
|
+
}
|
|
1992
|
+
columns.sort((a, b) => a.left - b.left);
|
|
1993
|
+
for (const col of columns) col.items.sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
1994
|
+
this.#grid = columns.map((c) => c.items);
|
|
1995
|
+
this.#grid.forEach((col, colIdx) => col.forEach((el, rowIdx) => this.#positions.set(el, {
|
|
1996
|
+
col: colIdx,
|
|
1997
|
+
row: rowIdx
|
|
1998
|
+
})));
|
|
1999
|
+
}
|
|
2000
|
+
#positionOf(el) {
|
|
2001
|
+
let pos = this.#positions.get(el);
|
|
2002
|
+
if (pos === void 0) {
|
|
2003
|
+
this.#recomputeGeometry();
|
|
2004
|
+
pos = this.#positions.get(el);
|
|
2005
|
+
}
|
|
2006
|
+
return pos;
|
|
2007
|
+
}
|
|
2008
|
+
#itemAt(col, row) {
|
|
2009
|
+
return this.#grid[col]?.[row];
|
|
2010
|
+
}
|
|
2011
|
+
#gridSize() {
|
|
2012
|
+
if (this.#grid.length === 0) this.#recomputeGeometry();
|
|
2013
|
+
return {
|
|
2014
|
+
cols: this.#grid.length,
|
|
2015
|
+
rows: Math.max(0, ...this.#grid.map((c) => c.length))
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
#navigateTo(target) {
|
|
2019
|
+
if (!target) return;
|
|
2020
|
+
this.tickled.handleKeyboardNavigation(target);
|
|
2021
|
+
this.tickled.tickledId?.scrollIntoView({
|
|
2022
|
+
block: `nearest`,
|
|
2023
|
+
inline: `nearest`
|
|
1863
2024
|
});
|
|
1864
2025
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2026
|
+
#findNextCheckable(fromCol, fromRow, deltaCol, deltaRow) {
|
|
2027
|
+
const { cols } = this.#gridSize();
|
|
2028
|
+
const maxIterations = this._getVisibleItems().length + 1;
|
|
2029
|
+
let col = deltaCol !== 0 ? fromCol + deltaCol : fromCol;
|
|
2030
|
+
let row = deltaRow !== 0 ? fromRow + deltaRow : fromRow;
|
|
2031
|
+
let wrappedColumns = false;
|
|
2032
|
+
for (let iterations = 0; iterations < maxIterations; iterations++) {
|
|
2033
|
+
const colLength = this.#grid[col]?.length ?? 0;
|
|
2034
|
+
if (row < 0 || row >= colLength) {
|
|
2035
|
+
if (deltaCol !== 0 || wrappedColumns) {
|
|
2036
|
+
if (this.itemWrap === `wrap`) {
|
|
2037
|
+
if (deltaCol < 0) col = cols - 1;
|
|
2038
|
+
else if (deltaCol > 0) col = 0;
|
|
2039
|
+
wrappedColumns = true;
|
|
2040
|
+
} else return;
|
|
2041
|
+
}
|
|
2042
|
+
if (deltaRow > 0) {
|
|
2043
|
+
if (col + 1 < cols) {
|
|
2044
|
+
col = col + 1;
|
|
2045
|
+
row = 0;
|
|
2046
|
+
wrappedColumns = true;
|
|
2047
|
+
} else if (this.itemWrap === `wrap`) {
|
|
2048
|
+
col = 0;
|
|
2049
|
+
row = 0;
|
|
2050
|
+
wrappedColumns = true;
|
|
2051
|
+
} else return;
|
|
2052
|
+
} else if (deltaRow < 0) {
|
|
2053
|
+
if (col - 1 >= 0) {
|
|
2054
|
+
col = col - 1;
|
|
2055
|
+
row = (this.#grid[col]?.length ?? 1) - 1;
|
|
2056
|
+
wrappedColumns = true;
|
|
2057
|
+
} else if (this.itemWrap === `wrap`) {
|
|
2058
|
+
col = cols - 1;
|
|
2059
|
+
row = (this.#grid[col]?.length ?? 1) - 1;
|
|
2060
|
+
wrappedColumns = true;
|
|
2061
|
+
} else return;
|
|
2062
|
+
} else if (deltaCol !== 0) {
|
|
2063
|
+
const targetColLength = this.#grid[col]?.length ?? 0;
|
|
2064
|
+
if (row >= targetColLength && targetColLength > 0) row = targetColLength - 1;
|
|
2065
|
+
}
|
|
2066
|
+
if (col === fromCol && row === fromRow + deltaRow) return;
|
|
2067
|
+
continue;
|
|
2068
|
+
}
|
|
2069
|
+
const item = this.#itemAt(col, row);
|
|
2070
|
+
if (item && this._isCheckable(item)) return item;
|
|
2071
|
+
row += deltaRow;
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
#findFirstCheckable() {
|
|
2075
|
+
return this._getVisibleItems().find((item) => this._isCheckable(item));
|
|
2076
|
+
}
|
|
2077
|
+
#findLastCheckable() {
|
|
2078
|
+
const visibleItems = this._getVisibleItems();
|
|
2079
|
+
for (let i = visibleItems.length - 1; i >= 0; i--) if (this._isCheckable(visibleItems[i])) return visibleItems[i];
|
|
2080
|
+
}
|
|
2081
|
+
#navigateDown() {
|
|
2082
|
+
this.#recomputeGeometry();
|
|
2083
|
+
const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
|
|
2084
|
+
if (!cur) {
|
|
2085
|
+
this.#navigateTo(this.#findFirstCheckable());
|
|
2086
|
+
return;
|
|
2087
|
+
}
|
|
2088
|
+
const next = this.#findNextCheckable(cur.col, cur.row, 0, 1);
|
|
2089
|
+
if (next) this.#navigateTo(next);
|
|
2090
|
+
}
|
|
2091
|
+
#navigateUp() {
|
|
2092
|
+
this.#recomputeGeometry();
|
|
2093
|
+
const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
|
|
2094
|
+
if (!cur) {
|
|
2095
|
+
this.#navigateTo(this.#findLastCheckable());
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2098
|
+
const next = this.#findNextCheckable(cur.col, cur.row, 0, -1);
|
|
2099
|
+
if (next) this.#navigateTo(next);
|
|
2100
|
+
}
|
|
2101
|
+
#navigateRight() {
|
|
2102
|
+
this.#recomputeGeometry();
|
|
2103
|
+
const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
|
|
2104
|
+
if (!cur) {
|
|
2105
|
+
this.#navigateTo(this.#findFirstCheckable());
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
const next = this.#findNextCheckable(cur.col, cur.row, 1, 0);
|
|
2109
|
+
if (next) this.#navigateTo(next);
|
|
2110
|
+
else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findFirstCheckable());
|
|
2111
|
+
}
|
|
2112
|
+
#navigateLeft() {
|
|
2113
|
+
this.#recomputeGeometry();
|
|
2114
|
+
const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
|
|
2115
|
+
if (!cur) {
|
|
2116
|
+
this.#navigateTo(this.#findFirstCheckable());
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
const next = this.#findNextCheckable(cur.col, cur.row, -1, 0);
|
|
2120
|
+
if (next) this.#navigateTo(next);
|
|
2121
|
+
else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findLastCheckable());
|
|
2122
|
+
}
|
|
2123
|
+
_getVisibleItems() {
|
|
2124
|
+
return Array.from(this.querySelectorAll(ITEM_SELECTOR)).filter((el) => {
|
|
2125
|
+
if (el.hidden) return false;
|
|
2126
|
+
if (this.filterPredicate && !this.filterPredicate(el)) return false;
|
|
2127
|
+
return true;
|
|
2128
|
+
});
|
|
1868
2129
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
const effectiveStep = resolveAxisStep(range, this.stops, this.step, event.shiftKey);
|
|
1873
|
-
this._applyDelta(direction * effectiveStep);
|
|
2130
|
+
_isCheckable(item) {
|
|
2131
|
+
if (this.interactionMode !== `checked`) return true;
|
|
2132
|
+
return !item.hasAttribute(`not-checked`);
|
|
1874
2133
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
2134
|
+
_setLabel(li, label) {
|
|
2135
|
+
const span = document.createElement(`span`);
|
|
2136
|
+
span.style.cssText = LABEL_SPAN_CSS;
|
|
2137
|
+
span.textContent = label;
|
|
2138
|
+
li.appendChild(span);
|
|
2139
|
+
li.setAttribute(`data-search-label`, label);
|
|
1877
2140
|
}
|
|
1878
2141
|
/**
|
|
1879
|
-
*
|
|
1880
|
-
*
|
|
1881
|
-
* and also respects trailing zeros written by drag storage (e.g. "5.0" stays "5.0").
|
|
2142
|
+
* Inject a leading `<ixfx-icon>` into an item carrying `data-icon`.
|
|
2143
|
+
* Idempotent — skips when the item already starts with an icon.
|
|
1882
2144
|
*/
|
|
1883
|
-
|
|
1884
|
-
const
|
|
1885
|
-
if (
|
|
1886
|
-
if (
|
|
1887
|
-
const
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
2145
|
+
#ensureIcon(li) {
|
|
2146
|
+
const name = li.getAttribute(`data-icon`);
|
|
2147
|
+
if (!name) return;
|
|
2148
|
+
if (li.firstElementChild?.tagName === `IXFX-ICON`) return;
|
|
2149
|
+
const icon = document.createElement(`ixfx-icon`);
|
|
2150
|
+
icon.setAttribute(`name`, name);
|
|
2151
|
+
icon.style.marginInlineEnd = `var(--detail-list-icon-gap, 6px)`;
|
|
2152
|
+
icon.style.flexShrink = `0`;
|
|
2153
|
+
li.prepend(icon);
|
|
2154
|
+
}
|
|
2155
|
+
/** Scan light-DOM children for unprocessed `data-icon` items. */
|
|
2156
|
+
#processChildren() {
|
|
2157
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR)) this.#ensureIcon(el);
|
|
2158
|
+
}
|
|
2159
|
+
_syncItemStates() {
|
|
2160
|
+
for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
|
|
2161
|
+
el.toggleAttribute(`data-tickled`, this.tickled.isTickled(el));
|
|
2162
|
+
el.toggleAttribute(`data-selected`, this.selection.selected.has(el));
|
|
2163
|
+
if (this.interactionMode === `checked`) el.toggleAttribute(`data-checked`, !el.hasAttribute(`not-checked`) && this.selection.selected.has(el));
|
|
2164
|
+
else el.removeAttribute(`data-checked`);
|
|
2165
|
+
}
|
|
2166
|
+
this.dataset.inputMode = this.tickled.inputMode;
|
|
2167
|
+
this._syncCheckboxes();
|
|
2168
|
+
}
|
|
2169
|
+
_syncCheckboxes() {
|
|
2170
|
+
const inCheckedMode = this.interactionMode === `checked`;
|
|
2171
|
+
const wantsCustom = typeof this.checkboxRenderer === `function`;
|
|
2172
|
+
for (const el of this.querySelectorAll(`li`)) {
|
|
2173
|
+
const existing = el.querySelector(`.ixfx-list-checkbox`);
|
|
2174
|
+
if (!inCheckedMode) {
|
|
2175
|
+
if (existing) existing.remove();
|
|
2176
|
+
continue;
|
|
2177
|
+
}
|
|
2178
|
+
if (el.hasAttribute(`not-checked`)) {
|
|
2179
|
+
if (existing) existing.remove();
|
|
2180
|
+
continue;
|
|
2181
|
+
}
|
|
2182
|
+
const existingIsCustom = existing?.classList.contains(`ixfx-list-checkbox-custom`) ?? false;
|
|
2183
|
+
if (existing && existingIsCustom !== wantsCustom) existing.remove();
|
|
2184
|
+
let cb = el.querySelector(`.ixfx-list-checkbox`);
|
|
2185
|
+
if (!cb) {
|
|
2186
|
+
if (wantsCustom) {
|
|
2187
|
+
const opts = this.checkboxRenderer;
|
|
2188
|
+
const margin = opts.margin ?? `0`;
|
|
2189
|
+
const width = opts.width ?? `24px`;
|
|
2190
|
+
cb = this._createCustomCheckbox(el, margin, width);
|
|
2191
|
+
} else cb = this._createNativeCheckboxCell(el);
|
|
2192
|
+
el.prepend(cb);
|
|
2193
|
+
}
|
|
2194
|
+
const checked = this.selection.selected.has(el);
|
|
2195
|
+
if (wantsCustom) {
|
|
2196
|
+
cb.setAttribute(`aria-checked`, String(checked));
|
|
2197
|
+
cb.toggleAttribute(`data-checked`, checked);
|
|
2198
|
+
if (this.checkboxRenderer) {
|
|
2199
|
+
const renderer = typeof this.checkboxRenderer === `function` ? this.checkboxRenderer : this.checkboxRenderer.renderer;
|
|
2200
|
+
render(renderer(checked), cb);
|
|
2201
|
+
}
|
|
2202
|
+
} else {
|
|
2203
|
+
const input = cb.querySelector(`input.ixfx-list-checkbox`);
|
|
2204
|
+
if (input) input.checked = checked;
|
|
2205
|
+
}
|
|
1901
2206
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
2207
|
+
}
|
|
2208
|
+
/**
|
|
2209
|
+
* Native checkbox cell: a span that stretches to the row's full cross-axis
|
|
2210
|
+
* (so the entire column height is a click target) with a real `<input>`
|
|
2211
|
+
* centred inside. The span owns the click; the input is purely visual and
|
|
2212
|
+
* has `pointer-events:none` so a single click produces one toggle.
|
|
2213
|
+
*/
|
|
2214
|
+
_createNativeCheckboxCell(el) {
|
|
2215
|
+
const cell = document.createElement(`span`);
|
|
2216
|
+
cell.className = `ixfx-list-checkbox ixfx-list-checkbox-native`;
|
|
2217
|
+
cell.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:18px;margin:0 6px 0 0;cursor:pointer;pointer-events:auto`;
|
|
2218
|
+
cell.tabIndex = -1;
|
|
2219
|
+
cell.setAttribute(`role`, `checkbox`);
|
|
2220
|
+
cell.setAttribute(`part`, `checkbox`);
|
|
2221
|
+
cell.addEventListener(`click`, (e) => {
|
|
2222
|
+
e.stopPropagation();
|
|
2223
|
+
this.selection.toggle(el);
|
|
2224
|
+
});
|
|
2225
|
+
const input = document.createElement(`input`);
|
|
2226
|
+
input.type = `checkbox`;
|
|
2227
|
+
input.className = `ixfx-list-checkbox`;
|
|
2228
|
+
input.style.cssText = `pointer-events:none;cursor:pointer;margin:0`;
|
|
2229
|
+
cell.appendChild(input);
|
|
2230
|
+
return cell;
|
|
2231
|
+
}
|
|
2232
|
+
/** Custom checkbox cell: same shape as the native cell; renderer content inside. */
|
|
2233
|
+
_createCustomCheckbox(el, margin, width) {
|
|
2234
|
+
const span = document.createElement(`span`);
|
|
2235
|
+
span.className = `ixfx-list-checkbox ixfx-list-checkbox-custom`;
|
|
2236
|
+
span.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:${width};margin:${margin};cursor:pointer;pointer-events:auto`;
|
|
2237
|
+
span.tabIndex = -1;
|
|
2238
|
+
span.setAttribute(`role`, `checkbox`);
|
|
2239
|
+
span.setAttribute(`part`, `checkbox`);
|
|
2240
|
+
span.addEventListener(`click`, (e) => {
|
|
2241
|
+
e.stopPropagation();
|
|
2242
|
+
this.selection.toggle(el);
|
|
2243
|
+
});
|
|
2244
|
+
return span;
|
|
2245
|
+
}
|
|
2246
|
+
_findListItem(path) {
|
|
2247
|
+
for (const target of path) {
|
|
2248
|
+
if (!(target instanceof Element)) continue;
|
|
2249
|
+
if (target === this) break;
|
|
2250
|
+
if (target.parentElement === this && target.matches(`li`)) return target;
|
|
1910
2251
|
}
|
|
1911
|
-
const s = String(n ?? this._parseValue());
|
|
1912
|
-
const dot = s.indexOf(`.`);
|
|
1913
|
-
return dot >= 0 ? s.length - dot - 1 : 0;
|
|
1914
2252
|
}
|
|
1915
|
-
|
|
1916
|
-
|
|
2253
|
+
_dispatch(name, detail) {
|
|
2254
|
+
this.dispatchEvent(new CustomEvent(name, {
|
|
2255
|
+
detail,
|
|
2256
|
+
bubbles: true,
|
|
2257
|
+
composed: true
|
|
2258
|
+
}));
|
|
2259
|
+
}
|
|
2260
|
+
_openSearch() {
|
|
2261
|
+
this._searchVisible = true;
|
|
2262
|
+
this.updateComplete.then(() => this._searchInputEl?.focus());
|
|
2263
|
+
}
|
|
2264
|
+
_closeSearch() {
|
|
2265
|
+
this._searchVisible = false;
|
|
2266
|
+
this._searchQuery = ``;
|
|
2267
|
+
this.#search?.clearQuery().then(() => this.#recomputeGeometry());
|
|
2268
|
+
this.focus();
|
|
1917
2269
|
}
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
if (this.max === this.min) return 0;
|
|
1922
|
-
return Math.max(0, Math.min(1, (n - this.min) / (this.max - this.min)));
|
|
2270
|
+
_activateItem(item) {
|
|
2271
|
+
if (this.interactionMode !== `manual`) this.selection.replace(item);
|
|
2272
|
+
this._dispatch(`list-activate`, { item });
|
|
1923
2273
|
}
|
|
1924
2274
|
static {
|
|
1925
2275
|
this.styles = [
|
|
1926
2276
|
themeFallbacks,
|
|
1927
|
-
|
|
1928
|
-
...EditableLabel.styles,
|
|
2277
|
+
scrollFadeStyles(`x`),
|
|
1929
2278
|
css`
|
|
1930
2279
|
:host {
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
--
|
|
1934
|
-
--
|
|
1935
|
-
--
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
2280
|
+
display: block;
|
|
2281
|
+
position: relative;
|
|
2282
|
+
background: var(--detail-list-bg, var(--surface-3));
|
|
2283
|
+
color: var(--detail-list-text, var(--surface-3-text));
|
|
2284
|
+
border-radius: var(--detail-list-radius, var(--radius-s));
|
|
2285
|
+
border: 1px solid var(--detail-list-border, var(--border));
|
|
2286
|
+
width: 100%;
|
|
2287
|
+
height: 100%;
|
|
2288
|
+
overflow: hidden;
|
|
2289
|
+
box-sizing: border-box;
|
|
2290
|
+
outline: none;
|
|
2291
|
+
--ixfx-detail-list-item-padding:
|
|
2292
|
+
var(--ixfx-detail-list-item-padding, var(--detail-list-item-padding, 0 var(--space-m, 8px)));
|
|
1940
2293
|
}
|
|
1941
2294
|
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
2295
|
+
:host(:focus-visible) {
|
|
2296
|
+
outline: 2px solid var(--accent);
|
|
2297
|
+
outline-offset: 2px;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
.container {
|
|
1945
2301
|
position: relative;
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
min-height: 1.5em;
|
|
1949
|
-
min-width: 5em;
|
|
2302
|
+
width: 100%;
|
|
2303
|
+
height: 100%;
|
|
1950
2304
|
}
|
|
1951
2305
|
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
2306
|
+
ul {
|
|
2307
|
+
list-style: none;
|
|
2308
|
+
margin: 0;
|
|
2309
|
+
padding: 0;
|
|
2310
|
+
/* keep the last column clear of the scrollbar / right edge */
|
|
2311
|
+
padding-inline-end: calc(var(--detail-list-column-gap, 8px) + 4px);
|
|
2312
|
+
width: 100%;
|
|
2313
|
+
height: 100%;
|
|
2314
|
+
display: flex;
|
|
2315
|
+
flex-flow: column wrap;
|
|
2316
|
+
align-content: flex-start;
|
|
2317
|
+
column-gap: var(--detail-list-column-gap, 8px);
|
|
2318
|
+
overflow-x: auto;
|
|
2319
|
+
overflow-y: hidden;
|
|
2320
|
+
box-sizing: border-box;
|
|
2321
|
+
/* Scrollbar theming: consumers override these CSS variables to control
|
|
2322
|
+
the native scrollbar (scrollbar-width, scrollbar-gutter, scrollbar-color). */
|
|
2323
|
+
scrollbar-width: var(--detail-list-scrollbar-width, auto);
|
|
2324
|
+
scrollbar-gutter: var(--detail-list-scrollbar-gutter, auto);
|
|
2325
|
+
scrollbar-color: var(--detail-list-scrollbar-color, auto);
|
|
1957
2326
|
}
|
|
1958
2327
|
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
z-index: 0;
|
|
1962
|
-
background: var(--surface-4, oklch(22% 0.02 240));
|
|
1963
|
-
border: var(--border, 1px solid oklch(25% 0.02 240));
|
|
2328
|
+
::slotted([hidden]) {
|
|
2329
|
+
display: none !important;
|
|
1964
2330
|
}
|
|
1965
2331
|
|
|
1966
|
-
|
|
1967
|
-
|
|
2332
|
+
::slotted(li) {
|
|
2333
|
+
box-sizing: border-box;
|
|
2334
|
+
display: flex;
|
|
2335
|
+
align-items: center;
|
|
2336
|
+
flex: none;
|
|
2337
|
+
width: var(--detail-list-column-width, 220px);
|
|
2338
|
+
height: var(--detail-list-item-height, 28px);
|
|
2339
|
+
padding: var(--ixfx-detail-list-item-padding);
|
|
2340
|
+
cursor: pointer;
|
|
2341
|
+
user-select: none;
|
|
2342
|
+
transition: background var(--transition, 0.1s ease-out),
|
|
2343
|
+
outline-color var(--transition, 0.1s ease-out);
|
|
2344
|
+
outline: 1px solid transparent;
|
|
1968
2345
|
}
|
|
1969
2346
|
|
|
1970
|
-
|
|
1971
|
-
|
|
2347
|
+
/* In checked mode, only the checkbox column is clickable; the row body
|
|
2348
|
+
gets a default cursor so it doesn't suggest otherwise. */
|
|
2349
|
+
:host([interaction-mode="checked"]) ::slotted(li) {
|
|
2350
|
+
cursor: default;
|
|
1972
2351
|
}
|
|
1973
2352
|
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2353
|
+
::slotted([data-tickled]) {
|
|
2354
|
+
background: var(--item-bg-tickled);
|
|
2355
|
+
outline: var(--item-border-tickled);
|
|
1977
2356
|
}
|
|
1978
2357
|
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
2358
|
+
::slotted([data-selected]) {
|
|
2359
|
+
background: var(--item-bg-selected);
|
|
2360
|
+
color: var(--item-text-selected);
|
|
2361
|
+
outline: 1px solid transparent;
|
|
1982
2362
|
}
|
|
1983
2363
|
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
left: calc(var(--value-start, 0) * 100%);
|
|
1989
|
-
width: calc((var(--value-end, 0) - var(--value-start, 0)) * 100%);
|
|
1990
|
-
background: var(--fill-image, var(--_fill-color-alpha));
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
/* Circular grow: filled circle growing from center */
|
|
1994
|
-
:host([fill-style="circular"][circular-style="grow"]) .value,
|
|
1995
|
-
:host([fill-style="circular"]:not([circular-style])) .value {
|
|
1996
|
-
background: var(--fill-image, var(--_fill-color-alpha));
|
|
1997
|
-
clip-path: circle(calc(var(--value) * 50%) at 50% 50%);
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
/* Circular offset: value circle slides left/right from center.
|
|
2001
|
-
Track circle is centered, filling the height.
|
|
2002
|
-
Value circle is same size, offset by --value-offset-px.
|
|
2003
|
-
At value=0.5: value circle centered on track (invisible when overlaid)
|
|
2004
|
-
At value=0: value circle at far left
|
|
2005
|
-
At value=1: value circle at far right
|
|
2006
|
-
*/
|
|
2007
|
-
:host([fill-style="circular"][circular-style="offset"]) {
|
|
2008
|
-
min-width: 80px;
|
|
2009
|
-
min-height: 26px;
|
|
2010
|
-
overflow: hidden;
|
|
2364
|
+
::slotted([data-selected][data-tickled]) {
|
|
2365
|
+
background: var(--item-bg-selected-tickled);
|
|
2366
|
+
outline: 2px dashed var(--surface-3);
|
|
2367
|
+
outline-offset: -2px;
|
|
2011
2368
|
}
|
|
2012
2369
|
|
|
2013
|
-
:
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
border: var(--border, 1px solid oklch(25% 0.02 240));
|
|
2019
|
-
width: calc(var(--radius, 11px) * 2);
|
|
2020
|
-
height: calc(var(--radius, 11px) * 2);
|
|
2021
|
-
left: 50%;
|
|
2022
|
-
transform: translate(-50%, -50%);
|
|
2370
|
+
/* checked mode: the injected checkbox communicates selection — suppress bg */
|
|
2371
|
+
:host([interaction-mode="checked"]) ::slotted([data-selected]) {
|
|
2372
|
+
background: transparent;
|
|
2373
|
+
color: inherit;
|
|
2374
|
+
outline: 1px solid transparent;
|
|
2023
2375
|
}
|
|
2024
2376
|
|
|
2025
|
-
:host([
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
border-radius: 50%;
|
|
2030
|
-
width: calc(var(--radius, 11px) * 2);
|
|
2031
|
-
height: calc(var(--radius, 11px) * 2);
|
|
2032
|
-
transform: translate(calc(var(--value-center-px, 13px) - var(--radius, 11px)), -50%);
|
|
2377
|
+
:host([interaction-mode="checked"]) ::slotted([data-selected][data-tickled]) {
|
|
2378
|
+
background: var(--item-bg-tickled);
|
|
2379
|
+
color: inherit;
|
|
2380
|
+
outline: var(--item-border-tickled);
|
|
2033
2381
|
}
|
|
2034
2382
|
|
|
2035
|
-
|
|
2036
|
-
|
|
2383
|
+
.loading {
|
|
2384
|
+
position: absolute;
|
|
2385
|
+
bottom: 8px;
|
|
2386
|
+
right: 8px;
|
|
2387
|
+
z-index: 10;
|
|
2388
|
+
background: var(--surface-4, #2a2a2a);
|
|
2389
|
+
border: 1px solid var(--border, #444);
|
|
2390
|
+
border-radius: var(--radius-m, 4px);
|
|
2391
|
+
padding: 2px 8px;
|
|
2392
|
+
font-size: var(--text-s, 11px);
|
|
2393
|
+
color: var(--surface-muted-text, #888);
|
|
2037
2394
|
}
|
|
2038
2395
|
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
background: var(--fill-image, var(--_fill-color-alpha));
|
|
2396
|
+
.loading[hidden] {
|
|
2397
|
+
display: none;
|
|
2042
2398
|
}
|
|
2043
2399
|
|
|
2044
|
-
|
|
2045
|
-
:host([fill-style]) .number {
|
|
2400
|
+
.search-overlay {
|
|
2046
2401
|
position: absolute;
|
|
2047
|
-
|
|
2048
|
-
|
|
2402
|
+
top: 8px;
|
|
2403
|
+
right: 8px;
|
|
2404
|
+
z-index: 10;
|
|
2049
2405
|
display: flex;
|
|
2050
2406
|
align-items: center;
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
padding:
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
:host([fill-style="circular"]) .number {
|
|
2059
|
-
border-radius: 50%;
|
|
2407
|
+
gap: 4px;
|
|
2408
|
+
background: var(--surface-4, #2a2a2a);
|
|
2409
|
+
border: 1px solid var(--border, #444);
|
|
2410
|
+
border-radius: var(--radius-m, 4px);
|
|
2411
|
+
padding: 4px 6px;
|
|
2412
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
2060
2413
|
}
|
|
2061
2414
|
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
cursor: ns-resize;
|
|
2065
|
-
user-select: none;
|
|
2066
|
-
-webkit-user-select: none;
|
|
2067
|
-
touch-action: none;
|
|
2415
|
+
.search-overlay[hidden] {
|
|
2416
|
+
display: none;
|
|
2068
2417
|
}
|
|
2069
2418
|
|
|
2070
|
-
|
|
2071
|
-
|
|
2419
|
+
.search-input {
|
|
2420
|
+
background: transparent;
|
|
2421
|
+
border: none;
|
|
2072
2422
|
outline: none;
|
|
2423
|
+
color: inherit;
|
|
2424
|
+
font-size: var(--text-m, 0.875em);
|
|
2425
|
+
min-width: 140px;
|
|
2073
2426
|
}
|
|
2074
2427
|
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2428
|
+
.search-close {
|
|
2429
|
+
background: transparent;
|
|
2430
|
+
border: none;
|
|
2431
|
+
color: var(--surface-muted-text, #888);
|
|
2432
|
+
cursor: pointer;
|
|
2433
|
+
font-size: 1.1em;
|
|
2434
|
+
line-height: 1;
|
|
2435
|
+
padding: 0 2px;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
.search-close:hover {
|
|
2439
|
+
color: inherit;
|
|
2078
2440
|
}
|
|
2441
|
+
`
|
|
2442
|
+
];
|
|
2443
|
+
}
|
|
2444
|
+
render() {
|
|
2445
|
+
const loading = this.#data.loading;
|
|
2446
|
+
const progress = this.#data.progress;
|
|
2447
|
+
return html`
|
|
2448
|
+
<div class="container">
|
|
2449
|
+
<ul part="list" class="scroll-fade-x">
|
|
2450
|
+
<slot></slot>
|
|
2451
|
+
</ul>
|
|
2452
|
+
<div class="loading" part="loading" ?hidden=${!loading}>
|
|
2453
|
+
${loading ? progress !== void 0 ? `${Math.round(progress * 100)}%` : `loading…` : nothing}
|
|
2454
|
+
</div>
|
|
2455
|
+
<div
|
|
2456
|
+
class="search-overlay"
|
|
2457
|
+
part="search-overlay"
|
|
2458
|
+
?hidden=${!this._searchVisible}
|
|
2459
|
+
>
|
|
2460
|
+
<input
|
|
2461
|
+
type="text"
|
|
2462
|
+
class="search-input"
|
|
2463
|
+
placeholder="Search…"
|
|
2464
|
+
.value=${this._searchQuery}
|
|
2465
|
+
@input=${this._onSearchInput}
|
|
2466
|
+
@keydown=${this._onSearchKeydown}
|
|
2467
|
+
/>
|
|
2468
|
+
<button class="search-close" @click=${this._closeSearch} title="Close search (Esc)">×</button>
|
|
2469
|
+
</div>
|
|
2470
|
+
</div>
|
|
2471
|
+
`;
|
|
2472
|
+
}
|
|
2473
|
+
};
|
|
2474
|
+
__decorate([property({
|
|
2475
|
+
type: String,
|
|
2476
|
+
attribute: `selection-mode`
|
|
2477
|
+
})], DetailListElement.prototype, "selectionMode", void 0);
|
|
2478
|
+
__decorate([property({
|
|
2479
|
+
type: String,
|
|
2480
|
+
attribute: `interaction-mode`
|
|
2481
|
+
})], DetailListElement.prototype, "interactionMode", void 0);
|
|
2482
|
+
__decorate([property({ type: String })], DetailListElement.prototype, "sort", void 0);
|
|
2483
|
+
__decorate([property({
|
|
2484
|
+
type: String,
|
|
2485
|
+
attribute: `sort-by`
|
|
2486
|
+
})], DetailListElement.prototype, "sortBy", void 0);
|
|
2487
|
+
__decorate([property({ attribute: false })], DetailListElement.prototype, "sortComparator", void 0);
|
|
2488
|
+
__decorate([property({ attribute: false })], DetailListElement.prototype, "items", void 0);
|
|
2489
|
+
__decorate([property({ attribute: false })], DetailListElement.prototype, "provider", void 0);
|
|
2490
|
+
__decorate([property({
|
|
2491
|
+
type: String,
|
|
2492
|
+
attribute: `display-property`
|
|
2493
|
+
})], DetailListElement.prototype, "displayProperty", void 0);
|
|
2494
|
+
__decorate([property({ attribute: false })], DetailListElement.prototype, "formatter", void 0);
|
|
2495
|
+
__decorate([property({
|
|
2496
|
+
type: String,
|
|
2497
|
+
attribute: `icon-property`
|
|
2498
|
+
})], DetailListElement.prototype, "iconProperty", void 0);
|
|
2499
|
+
__decorate([property({
|
|
2500
|
+
type: String,
|
|
2501
|
+
attribute: `key-property`
|
|
2502
|
+
})], DetailListElement.prototype, "keyProperty", void 0);
|
|
2503
|
+
__decorate([property({ attribute: false })], DetailListElement.prototype, "filterPredicate", void 0);
|
|
2504
|
+
__decorate([property({
|
|
2505
|
+
type: String,
|
|
2506
|
+
attribute: `item-wrap`
|
|
2507
|
+
})], DetailListElement.prototype, "itemWrap", void 0);
|
|
2508
|
+
__decorate([property({ attribute: false })], DetailListElement.prototype, "checkboxRenderer", void 0);
|
|
2509
|
+
__decorate([state()], DetailListElement.prototype, "_searchVisible", void 0);
|
|
2510
|
+
__decorate([state()], DetailListElement.prototype, "_searchQuery", void 0);
|
|
2511
|
+
__decorate([query(`.search-input`)], DetailListElement.prototype, "_searchInputEl", void 0);
|
|
2512
|
+
__decorate([query(`ul`)], DetailListElement.prototype, "_listEl", void 0);
|
|
2513
|
+
DetailListElement = __decorate([safeCustomElement(`ixfx-detail-list`)], DetailListElement);
|
|
2514
|
+
//#endregion
|
|
2515
|
+
//#region src/dock/dock-drag.ts
|
|
2516
|
+
let activeDrag = null;
|
|
2517
|
+
const dragHosts = /* @__PURE__ */ new Set();
|
|
2518
|
+
function registerDockHost(host) {
|
|
2519
|
+
dragHosts.add(host);
|
|
2520
|
+
}
|
|
2521
|
+
function unregisterDockHost(host) {
|
|
2522
|
+
dragHosts.delete(host);
|
|
2523
|
+
if (activeDrag !== null && activeDrag.sourceHost === host);
|
|
2524
|
+
}
|
|
2525
|
+
/**
|
|
2526
|
+
* Begin a dock-panel drag. Called by hosts in response to their own
|
|
2527
|
+
* `dock-panel-drag-start` events (titlebar or tab-item handles).
|
|
2528
|
+
*/
|
|
2529
|
+
function startDrag(panelId, sourceHost, pointerId, clientX, clientY) {
|
|
2530
|
+
if (activeDrag !== null);
|
|
2531
|
+
activeDrag = {
|
|
2532
|
+
panelId,
|
|
2533
|
+
sourceHost,
|
|
2534
|
+
pointerId,
|
|
2535
|
+
ghost: createGhostForPanel(sourceHost, panelId),
|
|
2536
|
+
outside: false
|
|
2537
|
+
};
|
|
2538
|
+
for (const host of dragHosts) host.setDragActive(true);
|
|
2539
|
+
activeDrag.ghost?.move(clientX, clientY);
|
|
2540
|
+
window.addEventListener(`pointermove`, onPointerMove);
|
|
2541
|
+
window.addEventListener(`pointerup`, onPointerUp);
|
|
2542
|
+
window.addEventListener(`pointercancel`, onPointerCancel);
|
|
2543
|
+
window.addEventListener(`keydown`, onKeyDown);
|
|
2544
|
+
}
|
|
2545
|
+
function createGhostForPanel(host, panelId) {
|
|
2546
|
+
const panel = Array.from(host.children).find((el) => el instanceof HTMLElement && el.panelId === panelId);
|
|
2547
|
+
if (!panel) return null;
|
|
2548
|
+
const content = document.createElement(`span`);
|
|
2549
|
+
content.style.display = `inline-flex`;
|
|
2550
|
+
content.style.alignItems = `center`;
|
|
2551
|
+
content.style.gap = `var(--space-xs, 4px)`;
|
|
2552
|
+
content.style.minWidth = `0`;
|
|
2553
|
+
if (panel.iconName) {
|
|
2554
|
+
const icon = document.createElement(`ixfx-icon`);
|
|
2555
|
+
icon.name = panel.iconName;
|
|
2556
|
+
content.appendChild(icon);
|
|
2557
|
+
}
|
|
2558
|
+
const label = document.createElement(`span`);
|
|
2559
|
+
label.textContent = panel.title || panelId;
|
|
2560
|
+
label.style.overflow = `hidden`;
|
|
2561
|
+
label.style.textOverflow = `ellipsis`;
|
|
2562
|
+
label.style.whiteSpace = `nowrap`;
|
|
2563
|
+
content.appendChild(label);
|
|
2564
|
+
try {
|
|
2565
|
+
return createDragGhost({
|
|
2566
|
+
anchor: panel,
|
|
2567
|
+
content
|
|
2568
|
+
});
|
|
2569
|
+
} catch {
|
|
2570
|
+
return null;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
function onPointerMove(event) {
|
|
2574
|
+
const drag = activeDrag;
|
|
2575
|
+
if (drag === null || event.pointerId !== drag.pointerId) return;
|
|
2576
|
+
drag.ghost?.move(event.clientX, event.clientY);
|
|
2577
|
+
const zone = findDropZone(drag, event.clientX, event.clientY);
|
|
2578
|
+
for (const host of dragHosts) host.setDropZone(zone !== null && zone.host === host ? zone : null);
|
|
2579
|
+
const outside = !pointerOverAnyHost(event.clientX, event.clientY);
|
|
2580
|
+
if (outside !== drag.outside) {
|
|
2581
|
+
drag.outside = outside;
|
|
2582
|
+
dispatchBoundary(drag, outside ? `dock-panel-drag-out` : `dock-panel-drag-in`, event);
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
function onPointerUp(event) {
|
|
2586
|
+
const drag = activeDrag;
|
|
2587
|
+
if (drag === null || event.pointerId !== drag.pointerId) return;
|
|
2588
|
+
const zone = findDropZone(drag, event.clientX, event.clientY);
|
|
2589
|
+
if (zone !== null) zone.host.handleDrop(drag.panelId, zone, drag.sourceHost);
|
|
2590
|
+
else if (!pointerOverAnyHost(event.clientX, event.clientY)) dispatchTearOut(drag, event);
|
|
2591
|
+
endDrag();
|
|
2592
|
+
}
|
|
2593
|
+
function onPointerCancel(event) {
|
|
2594
|
+
const drag = activeDrag;
|
|
2595
|
+
if (drag === null || event.pointerId !== drag.pointerId) return;
|
|
2596
|
+
endDrag();
|
|
2597
|
+
}
|
|
2598
|
+
function onKeyDown(event) {
|
|
2599
|
+
if (activeDrag === null) return;
|
|
2600
|
+
if (event.key !== `Escape`) return;
|
|
2601
|
+
endDrag();
|
|
2602
|
+
}
|
|
2603
|
+
function findDropZone(drag, clientX, clientY) {
|
|
2604
|
+
for (const host of dragHosts) {
|
|
2605
|
+
const zone = host.computeDropZone(clientX, clientY, drag.panelId);
|
|
2606
|
+
if (zone !== null) return zone;
|
|
2607
|
+
}
|
|
2608
|
+
return null;
|
|
2609
|
+
}
|
|
2610
|
+
/** True when the point lies within the bounding box of any registered host. */
|
|
2611
|
+
function pointerOverAnyHost(clientX, clientY) {
|
|
2612
|
+
for (const host of dragHosts) {
|
|
2613
|
+
const r = host.getBoundingClientRect();
|
|
2614
|
+
if (clientX >= r.left && clientX <= r.right && clientY >= r.top && clientY <= r.bottom) return true;
|
|
2615
|
+
}
|
|
2616
|
+
return false;
|
|
2617
|
+
}
|
|
2618
|
+
function dispatchBoundary(drag, type, event) {
|
|
2619
|
+
drag.sourceHost.dispatchEvent(new CustomEvent(type, {
|
|
2620
|
+
detail: {
|
|
2621
|
+
panelId: drag.panelId,
|
|
2622
|
+
clientX: event.clientX,
|
|
2623
|
+
clientY: event.clientY,
|
|
2624
|
+
screenX: event.screenX,
|
|
2625
|
+
screenY: event.screenY
|
|
2626
|
+
},
|
|
2627
|
+
bubbles: true,
|
|
2628
|
+
composed: true
|
|
2629
|
+
}));
|
|
2630
|
+
}
|
|
2631
|
+
function dispatchTearOut(drag, event) {
|
|
2632
|
+
const snapshot = drag.sourceHost.getPanelSnapshot(drag.panelId);
|
|
2633
|
+
if (snapshot === null) return;
|
|
2634
|
+
drag.sourceHost.dispatchEvent(new CustomEvent(`dock-panel-tear-out`, {
|
|
2635
|
+
detail: {
|
|
2636
|
+
panelId: drag.panelId,
|
|
2637
|
+
snapshot,
|
|
2638
|
+
clientX: event.clientX,
|
|
2639
|
+
clientY: event.clientY,
|
|
2640
|
+
screenX: event.screenX,
|
|
2641
|
+
screenY: event.screenY
|
|
2642
|
+
},
|
|
2643
|
+
bubbles: true,
|
|
2644
|
+
composed: true
|
|
2645
|
+
}));
|
|
2646
|
+
}
|
|
2647
|
+
function endDrag() {
|
|
2648
|
+
const drag = activeDrag;
|
|
2649
|
+
if (drag === null) return;
|
|
2650
|
+
for (const host of dragHosts) {
|
|
2651
|
+
host.setDropZone(null);
|
|
2652
|
+
host.setDragActive(false);
|
|
2653
|
+
}
|
|
2654
|
+
drag.ghost?.destroy();
|
|
2655
|
+
activeDrag = null;
|
|
2656
|
+
window.removeEventListener(`pointermove`, onPointerMove);
|
|
2657
|
+
window.removeEventListener(`pointerup`, onPointerUp);
|
|
2658
|
+
window.removeEventListener(`pointercancel`, onPointerCancel);
|
|
2659
|
+
window.removeEventListener(`keydown`, onKeyDown);
|
|
2660
|
+
}
|
|
2661
|
+
function isDragging() {
|
|
2662
|
+
return activeDrag !== null;
|
|
2663
|
+
}
|
|
2664
|
+
function getActiveDragPanelId() {
|
|
2665
|
+
return activeDrag?.panelId ?? null;
|
|
2666
|
+
}
|
|
2667
|
+
//#endregion
|
|
2668
|
+
//#region src/dock/dock-model.ts
|
|
2669
|
+
/**
|
|
2670
|
+
* The dock model is a plain mutable tree owned exclusively by the host
|
|
2671
|
+
* element. Mutators below change the tree in place; the host calls
|
|
2672
|
+
* `normalize(root)` then `requestUpdate()` after batching mutations.
|
|
2673
|
+
*/
|
|
2674
|
+
function isSplit(node) {
|
|
2675
|
+
return node.kind === `split`;
|
|
2676
|
+
}
|
|
2677
|
+
function isStack(node) {
|
|
2678
|
+
return node.kind === `stack`;
|
|
2679
|
+
}
|
|
2680
|
+
function createEmptyStack() {
|
|
2681
|
+
return {
|
|
2682
|
+
kind: `stack`,
|
|
2683
|
+
panels: [],
|
|
2684
|
+
active: ``
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
function createEmptyLayout() {
|
|
2688
|
+
return {
|
|
2689
|
+
version: 1,
|
|
2690
|
+
root: createEmptyStack()
|
|
2691
|
+
};
|
|
2692
|
+
}
|
|
2693
|
+
function createStack(panels, active) {
|
|
2694
|
+
return {
|
|
2695
|
+
kind: `stack`,
|
|
2696
|
+
panels: [...panels],
|
|
2697
|
+
active: active ?? panels[0] ?? ``
|
|
2698
|
+
};
|
|
2699
|
+
}
|
|
2700
|
+
function createSplit(direction, children, sizes) {
|
|
2701
|
+
const mean = children.length > 0 ? 1 / children.length : 1;
|
|
2702
|
+
return {
|
|
2703
|
+
kind: `split`,
|
|
2704
|
+
direction,
|
|
2705
|
+
children: [...children],
|
|
2706
|
+
sizes: sizes ? [...sizes] : children.map(() => mean)
|
|
2707
|
+
};
|
|
2708
|
+
}
|
|
2709
|
+
/**
|
|
2710
|
+
* Runs after every mutation:
|
|
2711
|
+
* - stack `active` fixed to a member of `panels` (or `''` when empty)
|
|
2712
|
+
* - empty stacks removed from their parent split (and their `sizes` entry)
|
|
2713
|
+
* - splits with ≤1 child replaced by that child (spliced into the parent)
|
|
2714
|
+
* - a child split whose direction equals its parent's is flattened into it,
|
|
2715
|
+
* spreading its sizes across the slot it vacated
|
|
2716
|
+
* - `sizes` kept parallel to `children` (padded with the mean weight)
|
|
2717
|
+
* - an empty root collapses to a single empty stack
|
|
2718
|
+
*/
|
|
2719
|
+
function normalize(root) {
|
|
2720
|
+
if (isStack(root)) {
|
|
2721
|
+
if (!root.panels.includes(root.active)) root.active = root.panels[0] ?? ``;
|
|
2722
|
+
return root;
|
|
2723
|
+
}
|
|
2724
|
+
for (let i = root.children.length - 1; i >= 0; i--) {
|
|
2725
|
+
normalize(root.children[i]);
|
|
2726
|
+
const child = root.children[i];
|
|
2727
|
+
if (isStack(child) && child.panels.length === 0) {
|
|
2728
|
+
root.children.splice(i, 1);
|
|
2729
|
+
root.sizes.splice(i, 1);
|
|
2730
|
+
continue;
|
|
2731
|
+
}
|
|
2732
|
+
if (isSplit(child) && child.children.length === 1) {
|
|
2733
|
+
root.children.splice(i, 1, child.children[0]);
|
|
2734
|
+
root.sizes.splice(i, 1, child.sizes[0] ?? root.sizes[i] ?? 1);
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
for (let i = root.children.length - 1; i >= 0; i--) {
|
|
2738
|
+
const child = root.children[i];
|
|
2739
|
+
if (isSplit(child) && child.direction === root.direction) {
|
|
2740
|
+
root.children.splice(i, 1, ...child.children);
|
|
2741
|
+
root.sizes.splice(i, 1, ...child.sizes);
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
if (root.children.length === 0) return replaceNodeContents(root, createEmptyStack());
|
|
2745
|
+
if (root.children.length === 1) return root.children[0];
|
|
2746
|
+
while (root.sizes.length < root.children.length) root.sizes.push(1 / root.children.length);
|
|
2747
|
+
if (root.sizes.length > root.children.length) root.sizes.length = root.children.length;
|
|
2748
|
+
return root;
|
|
2749
|
+
}
|
|
2750
|
+
/**
|
|
2751
|
+
* Turn `target` (a stack or split node object) into the shape of `source`'s
|
|
2752
|
+
* kind, used when the root itself must dissolve. Mutates `target` in place so
|
|
2753
|
+
* the host's root reference stays valid.
|
|
2754
|
+
*/
|
|
2755
|
+
function replaceNodeContents(target, source) {
|
|
2756
|
+
if (isStack(source)) {
|
|
2757
|
+
target.kind = `stack`;
|
|
2758
|
+
const t = target;
|
|
2759
|
+
t.panels = source.panels;
|
|
2760
|
+
t.active = source.active;
|
|
2761
|
+
delete t.collapsed;
|
|
2762
|
+
delete t.preCollapseSize;
|
|
2763
|
+
} else {
|
|
2764
|
+
target.kind = `split`;
|
|
2765
|
+
const t = target;
|
|
2766
|
+
t.direction = source.direction;
|
|
2767
|
+
t.children = source.children;
|
|
2768
|
+
t.sizes = source.sizes;
|
|
2769
|
+
}
|
|
2770
|
+
return target;
|
|
2771
|
+
}
|
|
2772
|
+
function findNode(root, path) {
|
|
2773
|
+
let current = root;
|
|
2774
|
+
for (const index of path) {
|
|
2775
|
+
if (!isSplit(current)) return null;
|
|
2776
|
+
const next = current.children[index];
|
|
2777
|
+
if (!next) return null;
|
|
2778
|
+
current = next;
|
|
2779
|
+
}
|
|
2780
|
+
return current;
|
|
2781
|
+
}
|
|
2782
|
+
/** Parent split of the node at `path`; null when path is empty or malformed. */
|
|
2783
|
+
function findParentOf(root, path) {
|
|
2784
|
+
if (path.length === 0) return null;
|
|
2785
|
+
const parent = findNode(root, path.slice(0, -1));
|
|
2786
|
+
return parent !== null && isSplit(parent) ? parent : null;
|
|
2787
|
+
}
|
|
2788
|
+
/** Path of the stack containing `panelId`, or null. */
|
|
2789
|
+
function findStackOf(root, panelId) {
|
|
2790
|
+
return findStackOfInner(root, panelId, []);
|
|
2791
|
+
}
|
|
2792
|
+
function findStackOfInner(node, panelId, path) {
|
|
2793
|
+
if (isStack(node)) return node.panels.includes(panelId) ? path : null;
|
|
2794
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
2795
|
+
const found = findStackOfInner(node.children[i], panelId, [...path, i]);
|
|
2796
|
+
if (found !== null) return found;
|
|
2797
|
+
}
|
|
2798
|
+
return null;
|
|
2799
|
+
}
|
|
2800
|
+
function collectPanelIds(root) {
|
|
2801
|
+
if (isStack(root)) return [...root.panels];
|
|
2802
|
+
return root.children.flatMap(collectPanelIds);
|
|
2803
|
+
}
|
|
2804
|
+
/**
|
|
2805
|
+
* Paths of every stack leaf, in tree order. `[]` is the root itself when it
|
|
2806
|
+
* is a stack.
|
|
2807
|
+
*/
|
|
2808
|
+
function collectStackPaths(root) {
|
|
2809
|
+
const result = [];
|
|
2810
|
+
collectStackPathsInner(root, [], result);
|
|
2811
|
+
return result;
|
|
2812
|
+
}
|
|
2813
|
+
function collectStackPathsInner(node, path, out) {
|
|
2814
|
+
if (isStack(node)) {
|
|
2815
|
+
out.push(path);
|
|
2816
|
+
return;
|
|
2817
|
+
}
|
|
2818
|
+
node.children.forEach((child, i) => collectStackPathsInner(child, [...path, i], out));
|
|
2819
|
+
}
|
|
2820
|
+
function addPanelToStack(stack, panelId, index) {
|
|
2821
|
+
const at = index === void 0 ? stack.panels.length : Math.max(0, Math.min(index, stack.panels.length));
|
|
2822
|
+
stack.panels.splice(at, 0, panelId);
|
|
2823
|
+
stack.active = panelId;
|
|
2824
|
+
}
|
|
2825
|
+
function removePanelFromStack(stack, panelId) {
|
|
2826
|
+
const index = stack.panels.indexOf(panelId);
|
|
2827
|
+
if (index < 0) return;
|
|
2828
|
+
stack.panels.splice(index, 1);
|
|
2829
|
+
if (stack.active === panelId) stack.active = stack.panels[Math.min(index, stack.panels.length - 1)] ?? ``;
|
|
2830
|
+
}
|
|
2831
|
+
function setActive(stack, panelId) {
|
|
2832
|
+
if (!stack.panels.includes(panelId) || stack.active === panelId) return false;
|
|
2833
|
+
stack.active = panelId;
|
|
2834
|
+
return true;
|
|
2835
|
+
}
|
|
2836
|
+
/**
|
|
2837
|
+
* Insert `node` beside the child at `targetPath`, into the parent split when
|
|
2838
|
+
* its direction matches `direction`, otherwise wrap the target in a new split.
|
|
2839
|
+
* `side` selects before/after; the neighbour's weight is split 50/50.
|
|
2840
|
+
* Returns the path of the inserted node, or null.
|
|
2841
|
+
*/
|
|
2842
|
+
function insertBeside(root, targetPath, direction, newNode, side) {
|
|
2843
|
+
const target = findNode(root, targetPath);
|
|
2844
|
+
if (target === null) return null;
|
|
2845
|
+
if (targetPath.length === 0) {
|
|
2846
|
+
if (isSplit(root) && root.direction === direction) {
|
|
2847
|
+
const edgeIndex = side === `before` ? 0 : root.children.length;
|
|
2848
|
+
const neighbourIndex = side === `before` ? 0 : root.children.length - 1;
|
|
2849
|
+
const neighbour = root.sizes[neighbourIndex];
|
|
2850
|
+
root.children.splice(edgeIndex, 0, newNode);
|
|
2851
|
+
root.sizes.splice(edgeIndex, 0, neighbour !== void 0 ? neighbour / 2 : 1);
|
|
2852
|
+
if (neighbour !== void 0) root.sizes[side === `before` ? neighbourIndex + 1 : neighbourIndex] = neighbour / 2;
|
|
2853
|
+
return [edgeIndex];
|
|
2854
|
+
}
|
|
2855
|
+
wrapRoot(root, direction, newNode, side);
|
|
2856
|
+
return [side === `before` ? 0 : 1];
|
|
2857
|
+
}
|
|
2858
|
+
const parent = findParentOf(root, targetPath);
|
|
2859
|
+
if (parent === null) return null;
|
|
2860
|
+
const index = targetPath[targetPath.length - 1];
|
|
2861
|
+
if (parent.direction === direction) {
|
|
2862
|
+
const insertAt = side === `before` ? index : index + 1;
|
|
2863
|
+
const w = parent.sizes[index] !== void 0 ? parent.sizes[index] / 2 : 1;
|
|
2864
|
+
parent.sizes[index] = w;
|
|
2865
|
+
parent.children.splice(insertAt, 0, newNode);
|
|
2866
|
+
parent.sizes.splice(insertAt, 0, w);
|
|
2867
|
+
const childPath = [...targetPath.slice(0, -1), insertAt];
|
|
2868
|
+
normalize(root);
|
|
2869
|
+
return childPath;
|
|
2870
|
+
}
|
|
2871
|
+
const wrapper = createSplit(direction, side === `before` ? [newNode, target] : [target, newNode]);
|
|
2872
|
+
parent.children[index] = wrapper;
|
|
2873
|
+
normalize(root);
|
|
2874
|
+
const childIndex = side === `before` ? 0 : 1;
|
|
2875
|
+
return [
|
|
2876
|
+
...targetPath.slice(0, -1),
|
|
2877
|
+
index,
|
|
2878
|
+
childIndex
|
|
2879
|
+
];
|
|
2880
|
+
}
|
|
2881
|
+
/**
|
|
2882
|
+
* Wrap the whole root in a new split of `direction`, replacing it in place so
|
|
2883
|
+
* the caller's reference stays valid. Returns true on success.
|
|
2884
|
+
*/
|
|
2885
|
+
function wrapRoot(root, direction, newNode, side) {
|
|
2886
|
+
replaceNodeContents(root, createSplit(direction, side === `before` ? [newNode, root] : [root, newNode]));
|
|
2887
|
+
normalize(root);
|
|
2888
|
+
return true;
|
|
2889
|
+
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Remove the stack at `stackPath` from the tree. Its panels are moved into
|
|
2892
|
+
* `fallbackStack` (if given and still attached) so they are not silently
|
|
2893
|
+
* lost; otherwise they are dropped from the model (elements return to the
|
|
2894
|
+
* pool). Returns the removed stack.
|
|
2895
|
+
*/
|
|
2896
|
+
function extractStack(root, stackPath, fallbackStack) {
|
|
2897
|
+
const stack = findNode(root, stackPath);
|
|
2898
|
+
if (stack === null || !isStack(stack)) return null;
|
|
2899
|
+
const isRoot = stackPath.length === 0;
|
|
2900
|
+
if (fallbackStack && fallbackStack !== stack) {
|
|
2901
|
+
for (const id of stack.panels) addPanelToStack(fallbackStack, id);
|
|
2902
|
+
if (!isRoot) {
|
|
2903
|
+
stack.panels = [];
|
|
2904
|
+
stack.active = ``;
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
if (isRoot) {
|
|
2908
|
+
replaceNodeContents(root, createEmptyStack());
|
|
2909
|
+
return stack;
|
|
2910
|
+
}
|
|
2911
|
+
const parent = findParentOf(root, stackPath);
|
|
2912
|
+
if (parent === null) return null;
|
|
2913
|
+
const index = stackPath[stackPath.length - 1];
|
|
2914
|
+
parent.children.splice(index, 1);
|
|
2915
|
+
parent.sizes.splice(index, 1);
|
|
2916
|
+
normalize(root);
|
|
2917
|
+
return stack;
|
|
2918
|
+
}
|
|
2919
|
+
function serializeLayout(root) {
|
|
2920
|
+
return {
|
|
2921
|
+
version: 1,
|
|
2922
|
+
root: serializeNode(root)
|
|
2923
|
+
};
|
|
2924
|
+
}
|
|
2925
|
+
function serializeNode(node) {
|
|
2926
|
+
if (isStack(node)) {
|
|
2927
|
+
const out = {
|
|
2928
|
+
kind: `stack`,
|
|
2929
|
+
panels: [...node.panels],
|
|
2930
|
+
active: node.active
|
|
2931
|
+
};
|
|
2932
|
+
if (node.collapsed !== void 0) out.collapsed = node.collapsed;
|
|
2933
|
+
if (node.preCollapseSize !== void 0) out.preCollapseSize = node.preCollapseSize;
|
|
2934
|
+
return out;
|
|
2935
|
+
}
|
|
2936
|
+
return {
|
|
2937
|
+
kind: `split`,
|
|
2938
|
+
direction: node.direction,
|
|
2939
|
+
sizes: [...node.sizes],
|
|
2940
|
+
children: node.children.map(serializeNode)
|
|
2941
|
+
};
|
|
2942
|
+
}
|
|
2943
|
+
/**
|
|
2944
|
+
* Parse layout JSON, validate loosely, and normalize. Returns null when the
|
|
2945
|
+
* JSON is malformed or the root is missing/invalid.
|
|
2946
|
+
*/
|
|
2947
|
+
function parseLayout(json) {
|
|
2948
|
+
try {
|
|
2949
|
+
const parsed = JSON.parse(json);
|
|
2950
|
+
if (parsed === null || typeof parsed !== `object`) return null;
|
|
2951
|
+
const root = parsed.root;
|
|
2952
|
+
if (root === null || typeof root !== `object` || !(`kind` in root)) return null;
|
|
2953
|
+
return {
|
|
2954
|
+
version: 1,
|
|
2955
|
+
root: normalize(root)
|
|
2956
|
+
};
|
|
2957
|
+
} catch {
|
|
2958
|
+
return null;
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
//#endregion
|
|
2962
|
+
//#region src/tabs/tab-list-item.ts
|
|
2963
|
+
let TabListItemElement = class TabListItemElement extends LitElement {
|
|
2964
|
+
constructor(..._args) {
|
|
2965
|
+
super(..._args);
|
|
2966
|
+
this.closeable = false;
|
|
2967
|
+
this.hideLabels = false;
|
|
2968
|
+
this.iconName = void 0;
|
|
2969
|
+
this.description = void 0;
|
|
2970
|
+
this.commandActive = void 0;
|
|
2971
|
+
this.registry = void 0;
|
|
2972
|
+
this._compact = false;
|
|
2973
|
+
this._hasToolbarContent = false;
|
|
2974
|
+
this._onToolbarSlotChange = () => {
|
|
2975
|
+
this._updateToolbarContent();
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
connectedCallback() {
|
|
2979
|
+
super.connectedCallback();
|
|
2980
|
+
this._resizeObserver = new ResizeObserver(([entry]) => {
|
|
2981
|
+
this._compact = entry.contentRect.width < 64;
|
|
2982
|
+
});
|
|
2983
|
+
this._resizeObserver.observe(this);
|
|
2984
|
+
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.addEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
2985
|
+
}
|
|
2986
|
+
disconnectedCallback() {
|
|
2987
|
+
super.disconnectedCallback();
|
|
2988
|
+
this._resizeObserver?.disconnect();
|
|
2989
|
+
this._resizeObserver = void 0;
|
|
2990
|
+
this._commandCleanup?.();
|
|
2991
|
+
this._commandCleanup = void 0;
|
|
2992
|
+
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.removeEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
2993
|
+
}
|
|
2994
|
+
updated(changed) {
|
|
2995
|
+
super.updated(changed);
|
|
2996
|
+
this._syncTooltipAttrs();
|
|
2997
|
+
if (changed.has(`registry`) || changed.has(`commandActive`)) this._syncCommandListener();
|
|
2998
|
+
}
|
|
2999
|
+
_syncTooltipAttrs() {
|
|
3000
|
+
if (this.description) {
|
|
3001
|
+
this.setAttribute(`data-tooltip`, this.description);
|
|
3002
|
+
this.setAttribute(`data-tooltip-anchor`, `top`);
|
|
3003
|
+
} else {
|
|
3004
|
+
this.removeAttribute(`data-tooltip`);
|
|
3005
|
+
this.removeAttribute(`data-tooltip-anchor`);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
_syncCommandListener() {
|
|
3009
|
+
this._commandCleanup?.();
|
|
3010
|
+
this._commandCleanup = void 0;
|
|
3011
|
+
if (this.registry && this.commandActive) this._commandCleanup = this.registry.addOnInvokeHandler((id) => {
|
|
3012
|
+
if (id === this.commandActive) this.click();
|
|
3013
|
+
});
|
|
3014
|
+
}
|
|
3015
|
+
_updateToolbarContent() {
|
|
3016
|
+
const slot = this.shadowRoot?.querySelector(`slot[name="toolbar"]`);
|
|
3017
|
+
this._hasToolbarContent = (slot?.assignedElements().length ?? 0) > 0;
|
|
3018
|
+
}
|
|
3019
|
+
firstUpdated() {
|
|
3020
|
+
this._updateToolbarContent();
|
|
3021
|
+
}
|
|
3022
|
+
render() {
|
|
3023
|
+
const showLabel = !this.hideLabels || !this.iconName;
|
|
3024
|
+
const hasActions = this.closeable || this._hasToolbarContent;
|
|
3025
|
+
return html`
|
|
3026
|
+
<div>
|
|
3027
|
+
${this.iconName ? html`<ixfx-icon name="${this.iconName}"></ixfx-icon>` : nothing}
|
|
3028
|
+
${showLabel ? html`<span class="label"><slot></slot></span>` : nothing}
|
|
3029
|
+
${hasActions ? html`
|
|
3030
|
+
<div class="actions">
|
|
3031
|
+
<div class="toolbar"><slot name="toolbar"></slot></div>
|
|
3032
|
+
${this.closeable ? html`
|
|
3033
|
+
<button
|
|
3034
|
+
class="close-btn"
|
|
3035
|
+
type="button"
|
|
3036
|
+
aria-label="Close tab"
|
|
3037
|
+
@click=${this._onCloseClick}
|
|
3038
|
+
>
|
|
3039
|
+
<ixfx-icon name="close"></ixfx-icon>
|
|
3040
|
+
</button>
|
|
3041
|
+
` : nothing}
|
|
3042
|
+
</div>
|
|
3043
|
+
` : nothing}
|
|
3044
|
+
</div>
|
|
3045
|
+
`;
|
|
3046
|
+
}
|
|
3047
|
+
_onCloseClick(e) {
|
|
3048
|
+
e.stopPropagation();
|
|
3049
|
+
this.dispatchEvent(new CustomEvent(`close`, {
|
|
3050
|
+
bubbles: true,
|
|
3051
|
+
composed: true
|
|
3052
|
+
}));
|
|
3053
|
+
this.remove();
|
|
3054
|
+
}
|
|
3055
|
+
static {
|
|
3056
|
+
this.styles = [
|
|
3057
|
+
themeFallbacks,
|
|
3058
|
+
prominenceStyles,
|
|
3059
|
+
css`
|
|
3060
|
+
:host {
|
|
3061
|
+
--selected-tab-color: var(--ixfx-selected-tab-color, var(--surface-2-text));
|
|
3062
|
+
--selected-tab-border-width: var(--ixfx-selected-tab-border-width, 1px);
|
|
3063
|
+
--selected-tab-weight: var(--ixfx-selected-tab-weight, bold);
|
|
3064
|
+
--selected-background: var(--ixfx-tab-selected-background, var(--surface-5));
|
|
3065
|
+
--tab-padding: var(--ixfx-tab-padding, var(--space-s));
|
|
3066
|
+
display: inline-flex;
|
|
3067
|
+
min-width: 0;
|
|
3068
|
+
user-select: none;
|
|
3069
|
+
box-sizing: border-box;
|
|
3070
|
+
font-size: var(--text-xs);
|
|
3071
|
+
/* No explicit height: items stretch (align-self: stretch via the
|
|
3072
|
+
list's flex container) so every tab in a strip shares one height
|
|
3073
|
+
even when toolbars give some items taller content. */
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
:host > div {
|
|
3077
|
+
display: flex;
|
|
3078
|
+
align-items: center;
|
|
3079
|
+
padding: var(--tab-padding) calc(2 * var(--tab-padding-horizontal, var(--tab-padding)));
|
|
3080
|
+
min-width: 0;
|
|
3081
|
+
width: 100%;
|
|
3082
|
+
font-weight: 500;
|
|
3083
|
+
border: var(--selected-tab-border-width) solid transparent;
|
|
3084
|
+
gap: var(--space-s);
|
|
3085
|
+
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
:host([selected]) > div {
|
|
3089
|
+
color: var(--selected-tab-color);
|
|
3090
|
+
border-top-color: var(--indicator-color-top, transparent);
|
|
3091
|
+
border-bottom-color: var(--indicator-color-bottom, transparent);
|
|
3092
|
+
border-left-color: var(--indicator-color-left, transparent);
|
|
3093
|
+
border-right-color: var(--indicator-color-right, transparent);
|
|
3094
|
+
background: var(--selected-background);
|
|
3095
|
+
font-weight: var(--selected-tab-weight);
|
|
3096
|
+
border-radius:var(--radius-m);
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
.label {
|
|
3100
|
+
overflow: var(--allow-overflow, hidden);
|
|
3101
|
+
text-overflow: ellipsis;
|
|
3102
|
+
white-space: nowrap;
|
|
3103
|
+
min-width: 0;
|
|
3104
|
+
flex: 1 1 auto;
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
ixfx-icon {
|
|
3108
|
+
flex-shrink: 0;
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
.actions {
|
|
3112
|
+
display: flex;
|
|
3113
|
+
align-items: center;
|
|
3114
|
+
gap: var(--space-xs);
|
|
3115
|
+
margin-left: auto;
|
|
3116
|
+
flex-shrink: 0;
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
.toolbar {
|
|
3120
|
+
/* Always laid out (display: flex) so the tab's width doesn't shift when
|
|
3121
|
+
the toolbar appears on selection. visibility: hidden keeps the
|
|
3122
|
+
toolbar from receiving pointer events or painting, while preserving
|
|
3123
|
+
the space its contents take up. */
|
|
3124
|
+
display: flex;
|
|
3125
|
+
align-items: center;
|
|
3126
|
+
gap: var(--space-xs);
|
|
3127
|
+
visibility: hidden;
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
:host([selected]) .toolbar {
|
|
3131
|
+
visibility: visible;
|
|
3132
|
+
}
|
|
3133
|
+
|
|
3134
|
+
slot[name="toolbar"] {
|
|
3135
|
+
display: contents;
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
/* Slotted buttons inherit these through the shadow boundary; keeps a
|
|
3139
|
+
toolbar button from inflating the strip height. */
|
|
3140
|
+
.toolbar ::slotted(ixfx-button) {
|
|
3141
|
+
--ixfx-button-padding-block: var(--space-xs);
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
.close-btn {
|
|
3145
|
+
display: inline-flex;
|
|
3146
|
+
align-items: center;
|
|
3147
|
+
justify-content: center;
|
|
3148
|
+
background: none;
|
|
3149
|
+
border: none;
|
|
3150
|
+
padding: var(--space-xs);
|
|
3151
|
+
cursor: pointer;
|
|
3152
|
+
color: inherit;
|
|
3153
|
+
border-radius: var(--radius-s);
|
|
3154
|
+
opacity: var(--prominence-idle-opacity);
|
|
3155
|
+
transition: opacity var(--transition), background var(--transition);
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
.close-btn:hover {
|
|
3159
|
+
background: var(--surface-5);
|
|
3160
|
+
opacity: var(--prominence-tickled-opacity);
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
.close-btn:focus-visible {
|
|
3164
|
+
outline: 2px solid var(--accent);
|
|
3165
|
+
outline-offset: 1px;
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
:host([selected]) .close-btn,
|
|
3169
|
+
:host(:hover) .close-btn {
|
|
3170
|
+
opacity: var(--prominence-tickled-opacity);
|
|
3171
|
+
}
|
|
3172
|
+
`
|
|
3173
|
+
];
|
|
3174
|
+
}
|
|
3175
|
+
};
|
|
3176
|
+
__decorate([property({ type: Boolean })], TabListItemElement.prototype, "closeable", void 0);
|
|
3177
|
+
__decorate([property({
|
|
3178
|
+
type: Boolean,
|
|
3179
|
+
attribute: `hide-labels`
|
|
3180
|
+
})], TabListItemElement.prototype, "hideLabels", void 0);
|
|
3181
|
+
__decorate([property({
|
|
3182
|
+
type: String,
|
|
3183
|
+
attribute: `icon-name`
|
|
3184
|
+
})], TabListItemElement.prototype, "iconName", void 0);
|
|
3185
|
+
__decorate([property({ type: String })], TabListItemElement.prototype, "description", void 0);
|
|
3186
|
+
__decorate([property({
|
|
3187
|
+
type: String,
|
|
3188
|
+
attribute: `command-active`
|
|
3189
|
+
})], TabListItemElement.prototype, "commandActive", void 0);
|
|
3190
|
+
__decorate([property({ attribute: false })], TabListItemElement.prototype, "registry", void 0);
|
|
3191
|
+
__decorate([state()], TabListItemElement.prototype, "_compact", void 0);
|
|
3192
|
+
__decorate([state()], TabListItemElement.prototype, "_hasToolbarContent", void 0);
|
|
3193
|
+
TabListItemElement = __decorate([safeCustomElement(`ixfx-tab-list-item`)], TabListItemElement);
|
|
3194
|
+
//#endregion
|
|
3195
|
+
//#region src/dock/dock-panel.ts
|
|
3196
|
+
/** Pointer movement threshold before a titlebar press becomes a dock drag. */
|
|
3197
|
+
const DRAG_THRESHOLD_PX$1 = 5;
|
|
3198
|
+
let DockPanelElement = class DockPanelElement extends LitElement {
|
|
3199
|
+
constructor(..._args) {
|
|
3200
|
+
super(..._args);
|
|
3201
|
+
this.panelId = ``;
|
|
3202
|
+
this.title = ``;
|
|
3203
|
+
this.iconName = ``;
|
|
3204
|
+
this.closable = false;
|
|
3205
|
+
this.collapsible = false;
|
|
3206
|
+
this.accepts = ``;
|
|
3207
|
+
this.presentation = `solo`;
|
|
3208
|
+
this._collapsed = false;
|
|
3209
|
+
this._dragPointerId = null;
|
|
3210
|
+
this._dragStartX = 0;
|
|
3211
|
+
this._dragStartY = 0;
|
|
3212
|
+
this._dragStarted = false;
|
|
3213
|
+
this._onPointerDown = (event) => {
|
|
3214
|
+
if (event.button !== 0) return;
|
|
3215
|
+
if (this.presentation === `tabbed`) return;
|
|
3216
|
+
if (!this._isDragHandle(event)) return;
|
|
3217
|
+
this._dragPointerId = event.pointerId;
|
|
3218
|
+
this._dragStartX = event.clientX;
|
|
3219
|
+
this._dragStartY = event.clientY;
|
|
3220
|
+
this._dragStarted = false;
|
|
3221
|
+
window.addEventListener(`pointermove`, this._onPointerMove);
|
|
3222
|
+
window.addEventListener(`pointerup`, this._onPointerUp);
|
|
3223
|
+
window.addEventListener(`pointercancel`, this._onPointerUp);
|
|
3224
|
+
};
|
|
3225
|
+
this._onPointerMove = (event) => {
|
|
3226
|
+
if (this._dragPointerId === null || event.pointerId !== this._dragPointerId) return;
|
|
3227
|
+
if (!this._dragStarted) {
|
|
3228
|
+
if (Math.hypot(event.clientX - this._dragStartX, event.clientY - this._dragStartY) < DRAG_THRESHOLD_PX$1) return;
|
|
3229
|
+
this._dragStarted = true;
|
|
3230
|
+
const pointerId = this._dragPointerId;
|
|
3231
|
+
this._teardownDragListeners();
|
|
3232
|
+
this._dispatchDragStart(pointerId, event.clientX, event.clientY);
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
this._onPointerUp = (event) => {
|
|
3236
|
+
if (this._dragPointerId === null || event.pointerId !== this._dragPointerId) return;
|
|
3237
|
+
this._teardownDragListeners();
|
|
3238
|
+
};
|
|
3239
|
+
this._onTitlebarToggle = (event) => {
|
|
3240
|
+
event.stopPropagation();
|
|
3241
|
+
this._collapsed = event.detail.collapsed;
|
|
3242
|
+
this.dispatchEvent(new CustomEvent(`toggle`, {
|
|
3243
|
+
detail: event.detail,
|
|
3244
|
+
bubbles: true,
|
|
3245
|
+
composed: true
|
|
3246
|
+
}));
|
|
3247
|
+
};
|
|
3248
|
+
this._onTitlebarClose = (event) => {
|
|
3249
|
+
event.stopPropagation();
|
|
3250
|
+
this.dispatchEvent(new CustomEvent(`close`, {
|
|
3251
|
+
bubbles: true,
|
|
3252
|
+
composed: true
|
|
3253
|
+
}));
|
|
3254
|
+
this.remove();
|
|
3255
|
+
};
|
|
3256
|
+
}
|
|
3257
|
+
connectedCallback() {
|
|
3258
|
+
super.connectedCallback();
|
|
3259
|
+
this.addEventListener(`pointerdown`, this._onPointerDown);
|
|
3260
|
+
}
|
|
3261
|
+
disconnectedCallback() {
|
|
3262
|
+
super.disconnectedCallback();
|
|
3263
|
+
this._teardownDragListeners();
|
|
3264
|
+
this.removeEventListener(`pointerdown`, this._onPointerDown);
|
|
3265
|
+
}
|
|
3266
|
+
_teardownDragListeners() {
|
|
3267
|
+
this._dragPointerId = null;
|
|
3268
|
+
this._dragStarted = false;
|
|
3269
|
+
window.removeEventListener(`pointermove`, this._onPointerMove);
|
|
3270
|
+
window.removeEventListener(`pointerup`, this._onPointerUp);
|
|
3271
|
+
window.removeEventListener(`pointercancel`, this._onPointerUp);
|
|
3272
|
+
}
|
|
3273
|
+
/** Clicks that begin on the close / caret buttons or the toolbar are not drags. */
|
|
3274
|
+
_isDragHandle(event) {
|
|
3275
|
+
for (const node of event.composedPath()) {
|
|
3276
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
3277
|
+
if (node.classList.contains(`close-btn`) || node.classList.contains(`caret-btn`) || node.classList.contains(`toolbar`)) return false;
|
|
3278
|
+
}
|
|
3279
|
+
return true;
|
|
3280
|
+
}
|
|
3281
|
+
_dispatchDragStart(pointerId, clientX, clientY) {
|
|
3282
|
+
if (!this.panelId) return;
|
|
3283
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-drag-start`, {
|
|
3284
|
+
detail: {
|
|
3285
|
+
panelId: this.panelId,
|
|
3286
|
+
pointerId,
|
|
3287
|
+
clientX,
|
|
3288
|
+
clientY
|
|
3289
|
+
},
|
|
3290
|
+
bubbles: true,
|
|
3291
|
+
composed: true
|
|
3292
|
+
}));
|
|
3293
|
+
}
|
|
3294
|
+
render() {
|
|
3295
|
+
const tabbed = this.presentation === `tabbed`;
|
|
3296
|
+
const collapsed = !tabbed && this._collapsed;
|
|
3297
|
+
return html`
|
|
3298
|
+
${tabbed ? nothing : html`
|
|
3299
|
+
<ixfx-titlebar
|
|
3300
|
+
.title=${this.title}
|
|
3301
|
+
.iconName=${this.iconName}
|
|
3302
|
+
.closable=${this.closable}
|
|
3303
|
+
.collapsible=${this.collapsible}
|
|
3304
|
+
.collapsed=${collapsed}
|
|
3305
|
+
.toggleInteraction=${this.collapsible ? `implicit` : `disabled`}
|
|
3306
|
+
@toggle=${this._onTitlebarToggle}
|
|
3307
|
+
@close=${this._onTitlebarClose}
|
|
3308
|
+
>
|
|
3309
|
+
<slot name="toolbar" slot="toolbar"></slot>
|
|
3310
|
+
</ixfx-titlebar>
|
|
3311
|
+
`}
|
|
3312
|
+
<div class="content" ?collapsed=${collapsed}>
|
|
3313
|
+
<slot></slot>
|
|
3314
|
+
</div>
|
|
3315
|
+
`;
|
|
3316
|
+
}
|
|
3317
|
+
static {
|
|
3318
|
+
this.styles = [themeFallbacks, css`
|
|
3319
|
+
:host {
|
|
3320
|
+
display: flex;
|
|
3321
|
+
flex-direction: column;
|
|
3322
|
+
width: 100%;
|
|
3323
|
+
height: 100%;
|
|
3324
|
+
min-width: 0;
|
|
3325
|
+
min-height: 0;
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3328
|
+
ixfx-titlebar {
|
|
3329
|
+
flex-shrink: 0;
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
.content {
|
|
3333
|
+
flex: 1;
|
|
3334
|
+
min-height: 0;
|
|
3335
|
+
min-width: 0;
|
|
3336
|
+
overflow: hidden;
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
.content[collapsed] {
|
|
3340
|
+
display: none;
|
|
3341
|
+
}
|
|
3342
|
+
`];
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
__decorate([property({
|
|
3346
|
+
type: String,
|
|
3347
|
+
attribute: `panel-id`,
|
|
3348
|
+
reflect: true
|
|
3349
|
+
})], DockPanelElement.prototype, "panelId", void 0);
|
|
3350
|
+
__decorate([property({ type: String })], DockPanelElement.prototype, "title", void 0);
|
|
3351
|
+
__decorate([property({
|
|
3352
|
+
type: String,
|
|
3353
|
+
attribute: `icon-name`
|
|
3354
|
+
})], DockPanelElement.prototype, "iconName", void 0);
|
|
3355
|
+
__decorate([property({ type: Boolean })], DockPanelElement.prototype, "closable", void 0);
|
|
3356
|
+
__decorate([property({ type: Boolean })], DockPanelElement.prototype, "collapsible", void 0);
|
|
3357
|
+
__decorate([property({
|
|
3358
|
+
type: String,
|
|
3359
|
+
attribute: `accepts`
|
|
3360
|
+
})], DockPanelElement.prototype, "accepts", void 0);
|
|
3361
|
+
__decorate([property({
|
|
3362
|
+
type: String,
|
|
3363
|
+
reflect: true
|
|
3364
|
+
})], DockPanelElement.prototype, "presentation", void 0);
|
|
3365
|
+
__decorate([state()], DockPanelElement.prototype, "_collapsed", void 0);
|
|
3366
|
+
DockPanelElement = __decorate([safeCustomElement(`ixfx-dock-panel`)], DockPanelElement);
|
|
3367
|
+
//#endregion
|
|
3368
|
+
//#region src/dock/dock-host.ts
|
|
3369
|
+
/** Fraction of the leaf rect (from each edge) that forms the split bands. */
|
|
3370
|
+
const BAND = .25;
|
|
3371
|
+
/** Pointer movement threshold before a press becomes a dock drag (matches tab-list). */
|
|
3372
|
+
const DRAG_THRESHOLD_PX = 5;
|
|
3373
|
+
/** Tolerance (px) when matching a child gutter endpoint to a parent gutter line. */
|
|
3374
|
+
const JUNCTION_TOL_PX = 6;
|
|
3375
|
+
/** Junction points closer than this merge into one handle (a 4-way cross yields one per side). */
|
|
3376
|
+
const JUNCTION_MERGE_PX = 8;
|
|
3377
|
+
let DockHostElement = class DockHostElement extends LitElement {
|
|
3378
|
+
constructor(..._args) {
|
|
3379
|
+
super(..._args);
|
|
3380
|
+
this.layout = null;
|
|
3381
|
+
this.accepts = `tabs horizontal vertical`;
|
|
3382
|
+
this._model = createStack([]);
|
|
3383
|
+
this._pool = [];
|
|
3384
|
+
this._dropZone = null;
|
|
3385
|
+
this._dragActive = false;
|
|
3386
|
+
this._junctions = [];
|
|
3387
|
+
this._toolbarsOut = /* @__PURE__ */ new Map();
|
|
3388
|
+
this._tabDragPending = null;
|
|
3389
|
+
this._junctionsKey = ``;
|
|
3390
|
+
this._junctionsRaf = null;
|
|
3391
|
+
this._hostResizeObserver = null;
|
|
3392
|
+
this._onDragStart = (event) => {
|
|
3393
|
+
const { panelId, pointerId, clientX, clientY } = event.detail;
|
|
3394
|
+
startDrag(panelId, this, pointerId, clientX, clientY);
|
|
3395
|
+
};
|
|
3396
|
+
this._onPoolSlotChange = () => {
|
|
3397
|
+
this._syncPool();
|
|
3398
|
+
};
|
|
3399
|
+
this._onJunctionPointerDown = (event, junction) => {
|
|
3400
|
+
if (event.button !== 0) return;
|
|
3401
|
+
event.preventDefault();
|
|
3402
|
+
event.stopPropagation();
|
|
3403
|
+
let started = false;
|
|
3404
|
+
for (const target of junction.targets) {
|
|
3405
|
+
if (!target.split.isConnected) continue;
|
|
3406
|
+
if (target.split.beginGutterDrag(target.gutterIndex, event.pointerId, event.clientX, event.clientY)) started = true;
|
|
3407
|
+
}
|
|
3408
|
+
if (!started) return;
|
|
3409
|
+
this._suppressNextClick();
|
|
3410
|
+
const follow = () => this._scheduleJunctions();
|
|
3411
|
+
const stop = () => {
|
|
3412
|
+
window.removeEventListener(`pointermove`, follow);
|
|
3413
|
+
window.removeEventListener(`pointerup`, stop);
|
|
3414
|
+
window.removeEventListener(`pointercancel`, stop);
|
|
3415
|
+
};
|
|
3416
|
+
window.addEventListener(`pointermove`, follow);
|
|
3417
|
+
window.addEventListener(`pointerup`, stop);
|
|
3418
|
+
window.addEventListener(`pointercancel`, stop);
|
|
3419
|
+
};
|
|
3420
|
+
this._onSplitWeightsChanged = () => {
|
|
3421
|
+
this._foldLiveWeights();
|
|
3422
|
+
this._scheduleJunctions();
|
|
3423
|
+
};
|
|
3424
|
+
this._onSplitCollapsed = (event, collapsed) => {
|
|
3425
|
+
const detail = event.detail;
|
|
3426
|
+
if (!detail?.id) return;
|
|
3427
|
+
const pathStr = this.shadowRoot?.getElementById(detail.id)?.getAttribute(`data-dock-path`);
|
|
3428
|
+
if (pathStr === null || pathStr === void 0) return;
|
|
3429
|
+
const node = findNode(this._model, parsePath(pathStr));
|
|
3430
|
+
if (node === null || !isStack(node)) return;
|
|
3431
|
+
if (collapsed) node.collapsed = true;
|
|
3432
|
+
else delete node.collapsed;
|
|
3433
|
+
};
|
|
3434
|
+
this._onSplitClose = (event) => {
|
|
3435
|
+
event.preventDefault();
|
|
3436
|
+
const detail = event.detail;
|
|
3437
|
+
if (!detail?.id) return;
|
|
3438
|
+
const pathStr = this.shadowRoot?.getElementById(detail.id)?.getAttribute(`data-dock-path`);
|
|
3439
|
+
if (pathStr === null || pathStr === void 0) return;
|
|
3440
|
+
const path = parsePath(pathStr);
|
|
3441
|
+
const paths = collectStackPaths(this._model).filter((p) => p.join(`-`) !== path.join(`-`));
|
|
3442
|
+
const fallbackNode = paths.length > 0 ? findNode(this._model, paths[0]) : null;
|
|
3443
|
+
const fallback = fallbackNode !== null && isStack(fallbackNode) ? fallbackNode : void 0;
|
|
3444
|
+
this._returnAllToolbars();
|
|
3445
|
+
extractStack(this._model, path, fallback);
|
|
3446
|
+
this._commit();
|
|
3447
|
+
};
|
|
3448
|
+
this._onTabChange = (event, path) => {
|
|
3449
|
+
const panelId = event.detail.selected;
|
|
3450
|
+
const stack = findNode(this._model, path);
|
|
3451
|
+
if (stack === null || !isStack(stack)) return;
|
|
3452
|
+
if (!setActive(stack, panelId)) return;
|
|
3453
|
+
this.requestUpdate();
|
|
3454
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-activated`, {
|
|
3455
|
+
detail: { panelId },
|
|
3456
|
+
bubbles: true,
|
|
3457
|
+
composed: true
|
|
3458
|
+
}));
|
|
3459
|
+
};
|
|
3460
|
+
this._onStripPointerDown = (event) => {
|
|
3461
|
+
if (event.button !== 0) return;
|
|
3462
|
+
const path = event.composedPath();
|
|
3463
|
+
for (const node of path) {
|
|
3464
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
3465
|
+
if (node.classList.contains(`list-toolbar`) || node.classList.contains(`close-btn`)) return;
|
|
3466
|
+
}
|
|
3467
|
+
const item = path.find((node) => node instanceof HTMLElement && node.tagName === `IXFX-TAB-LIST-ITEM`) ?? null;
|
|
3468
|
+
if (item === null || !item.id) return;
|
|
3469
|
+
this._tabDragPending = {
|
|
3470
|
+
panelId: item.id,
|
|
3471
|
+
pointerId: event.pointerId,
|
|
3472
|
+
startX: event.clientX,
|
|
3473
|
+
startY: event.clientY
|
|
3474
|
+
};
|
|
3475
|
+
window.addEventListener(`pointermove`, this._onTabDragMove);
|
|
3476
|
+
window.addEventListener(`pointerup`, this._onTabDragUp);
|
|
3477
|
+
window.addEventListener(`pointercancel`, this._onTabDragUp);
|
|
3478
|
+
};
|
|
3479
|
+
this._onTabDragMove = (event) => {
|
|
3480
|
+
const pending = this._tabDragPending;
|
|
3481
|
+
if (pending === null || event.pointerId !== pending.pointerId) return;
|
|
3482
|
+
if (Math.hypot(event.clientX - pending.startX, event.clientY - pending.startY) < DRAG_THRESHOLD_PX) return;
|
|
3483
|
+
this._tabDragPending = null;
|
|
3484
|
+
this._teardownTabDragListeners();
|
|
3485
|
+
this._suppressNextClick();
|
|
3486
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-drag-start`, {
|
|
3487
|
+
detail: {
|
|
3488
|
+
panelId: pending.panelId,
|
|
3489
|
+
pointerId: pending.pointerId,
|
|
3490
|
+
clientX: event.clientX,
|
|
3491
|
+
clientY: event.clientY
|
|
3492
|
+
},
|
|
3493
|
+
bubbles: true,
|
|
3494
|
+
composed: true
|
|
3495
|
+
}));
|
|
3496
|
+
};
|
|
3497
|
+
this._onTabDragUp = (event) => {
|
|
3498
|
+
if (this._tabDragPending === null || event.pointerId !== this._tabDragPending.pointerId) return;
|
|
3499
|
+
this._tabDragPending = null;
|
|
3500
|
+
this._teardownTabDragListeners();
|
|
3501
|
+
};
|
|
3502
|
+
}
|
|
3503
|
+
connectedCallback() {
|
|
3504
|
+
super.connectedCallback();
|
|
3505
|
+
this._buildInitialModel();
|
|
3506
|
+
registerDockHost(this);
|
|
3507
|
+
this.addEventListener(`dock-panel-drag-start`, this._onDragStart);
|
|
3508
|
+
this._hostResizeObserver = new ResizeObserver(() => this._scheduleJunctions());
|
|
3509
|
+
this._hostResizeObserver.observe(this);
|
|
3510
|
+
}
|
|
3511
|
+
disconnectedCallback() {
|
|
3512
|
+
super.disconnectedCallback();
|
|
3513
|
+
unregisterDockHost(this);
|
|
3514
|
+
this.removeEventListener(`dock-panel-drag-start`, this._onDragStart);
|
|
3515
|
+
this._hostResizeObserver?.disconnect();
|
|
3516
|
+
this._hostResizeObserver = null;
|
|
3517
|
+
if (this._junctionsRaf !== null) {
|
|
3518
|
+
cancelAnimationFrame(this._junctionsRaf);
|
|
3519
|
+
this._junctionsRaf = null;
|
|
3520
|
+
}
|
|
3521
|
+
this._junctions = [];
|
|
3522
|
+
this._junctionsKey = ``;
|
|
3523
|
+
this._returnAllToolbars();
|
|
3524
|
+
}
|
|
3525
|
+
willUpdate(changed) {
|
|
3526
|
+
super.willUpdate(changed);
|
|
3527
|
+
if (changed.has(`layout`) && this.layout !== null) {
|
|
3528
|
+
const parsed = parseLayout(this.layout);
|
|
3529
|
+
if (parsed) {
|
|
3530
|
+
this._model = parsed.root;
|
|
3531
|
+
this._mergePoolIntoModel();
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
updated(changed) {
|
|
3536
|
+
super.updated(changed);
|
|
3537
|
+
this._syncSlots();
|
|
3538
|
+
this._syncToolbars();
|
|
3539
|
+
this._scheduleJunctions();
|
|
3540
|
+
}
|
|
3541
|
+
_buildInitialModel() {
|
|
3542
|
+
if (this.layout !== null) {
|
|
3543
|
+
const parsed = parseLayout(this.layout);
|
|
3544
|
+
if (parsed) {
|
|
3545
|
+
this._model = parsed.root;
|
|
3546
|
+
this._mergePoolIntoModel();
|
|
3547
|
+
return;
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
this._model = createStack([]);
|
|
3551
|
+
this._mergePoolIntoModel();
|
|
3552
|
+
}
|
|
3553
|
+
/** Ensure every pooled panel element appears in the model (appended to the first stack). */
|
|
3554
|
+
_mergePoolIntoModel() {
|
|
3555
|
+
const known = new Set(collectPanelIds(this._model));
|
|
3556
|
+
let changed = false;
|
|
3557
|
+
for (const el of this._scanPanels()) if (!known.has(el.panelId)) {
|
|
3558
|
+
this._appendPanelToFirstStack(el.panelId);
|
|
3559
|
+
known.add(el.panelId);
|
|
3560
|
+
changed = true;
|
|
3561
|
+
}
|
|
3562
|
+
if (changed) normalize(this._model);
|
|
3563
|
+
}
|
|
3564
|
+
_scanPanels() {
|
|
3565
|
+
return Array.from(this.children).filter((el) => el instanceof HTMLElement && el.tagName === `IXFX-DOCK-PANEL` && Boolean(el.panelId));
|
|
3566
|
+
}
|
|
3567
|
+
_findPanelEl(panelId) {
|
|
3568
|
+
return this._pool.find((p) => p.panelId === panelId) ?? this._scanPanels().find((p) => p.panelId === panelId);
|
|
3569
|
+
}
|
|
3570
|
+
_firstStack() {
|
|
3571
|
+
const paths = collectStackPaths(this._model);
|
|
3572
|
+
const node = paths.length > 0 ? findNode(this._model, paths[0]) : null;
|
|
3573
|
+
if (node !== null && isStack(node)) return node;
|
|
3574
|
+
const fresh = createStack([]);
|
|
3575
|
+
wrapRoot(this._model, `row`, fresh, `after`);
|
|
3576
|
+
normalize(this._model);
|
|
3577
|
+
return fresh;
|
|
3578
|
+
}
|
|
3579
|
+
_appendPanelToFirstStack(panelId) {
|
|
3580
|
+
addPanelToStack(this._model.kind === `stack` ? this._model : this._firstStack(), panelId);
|
|
3581
|
+
}
|
|
3582
|
+
_syncPool() {
|
|
3583
|
+
const current = this._scanPanels();
|
|
3584
|
+
const currentIds = new Set(current.map((p) => p.panelId));
|
|
3585
|
+
let structuralChange = false;
|
|
3586
|
+
for (const el of [...this._pool]) if (!currentIds.has(el.panelId)) {
|
|
3587
|
+
this._toolbarsOut.delete(el.panelId);
|
|
3588
|
+
this._removeFromModel(el.panelId);
|
|
3589
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-removed`, {
|
|
3590
|
+
detail: { panelId: el.panelId },
|
|
3591
|
+
bubbles: true,
|
|
3592
|
+
composed: true
|
|
3593
|
+
}));
|
|
3594
|
+
structuralChange = true;
|
|
3595
|
+
}
|
|
3596
|
+
for (const el of current) if (!collectPanelIds(this._model).includes(el.panelId)) {
|
|
3597
|
+
this._appendPanelToFirstStack(el.panelId);
|
|
3598
|
+
normalize(this._model);
|
|
3599
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-added`, {
|
|
3600
|
+
detail: { panelId: el.panelId },
|
|
3601
|
+
bubbles: true,
|
|
3602
|
+
composed: true
|
|
3603
|
+
}));
|
|
3604
|
+
structuralChange = true;
|
|
3605
|
+
}
|
|
3606
|
+
this._pool = current;
|
|
3607
|
+
this.requestUpdate();
|
|
3608
|
+
if (structuralChange) this._emitChange();
|
|
3609
|
+
}
|
|
3610
|
+
/** Serialize the current layout, folding live split weights from the DOM. */
|
|
3611
|
+
saveLayout() {
|
|
3612
|
+
this._foldLiveWeights();
|
|
3613
|
+
return serializeLayout(this._model);
|
|
3614
|
+
}
|
|
3615
|
+
loadLayout(layout) {
|
|
3616
|
+
this._returnAllToolbars();
|
|
3617
|
+
this._model = normalize(cloneNode(layout.root));
|
|
3618
|
+
this._mergePoolIntoModel();
|
|
3619
|
+
this.requestUpdate();
|
|
3620
|
+
this._emitChange();
|
|
3621
|
+
}
|
|
3622
|
+
addPanel(panel, target) {
|
|
3623
|
+
let el;
|
|
3624
|
+
if (panel instanceof HTMLElement) el = panel;
|
|
3625
|
+
else {
|
|
3626
|
+
el = document.createElement(`ixfx-dock-panel`);
|
|
3627
|
+
el.panelId = panel.panelId;
|
|
3628
|
+
if (panel.title !== void 0) el.title = panel.title;
|
|
3629
|
+
if (panel.iconName !== void 0) el.iconName = panel.iconName;
|
|
3630
|
+
if (panel.closable !== void 0) el.closable = panel.closable;
|
|
3631
|
+
if (panel.collapsible !== void 0) el.collapsible = panel.collapsible;
|
|
3632
|
+
}
|
|
3633
|
+
if (el.parentElement !== this) this.appendChild(el);
|
|
3634
|
+
if (!this._pool.some((p) => p.panelId === el.panelId)) this._pool = [...this._pool, el];
|
|
3635
|
+
this._returnAllToolbars();
|
|
3636
|
+
const op = target?.op ?? `tabs`;
|
|
3637
|
+
if (target?.stack) {
|
|
3638
|
+
const stackPath = findStackOf(this._model, target.stack);
|
|
3639
|
+
if (stackPath !== null) {
|
|
3640
|
+
if (op === `tabs`) {
|
|
3641
|
+
const stack = findNode(this._model, stackPath);
|
|
3642
|
+
if (stack !== null && isStack(stack)) addPanelToStack(stack, el.panelId, target.index);
|
|
3643
|
+
} else insertBeside(this._model, stackPath, opToDirection(op), createStack([el.panelId]), `after`);
|
|
3644
|
+
}
|
|
3645
|
+
} else this._appendPanelToFirstStack(el.panelId);
|
|
3646
|
+
normalize(this._model);
|
|
3647
|
+
this.requestUpdate();
|
|
3648
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-added`, {
|
|
3649
|
+
detail: { panelId: el.panelId },
|
|
3650
|
+
bubbles: true,
|
|
3651
|
+
composed: true
|
|
3652
|
+
}));
|
|
3653
|
+
this._emitChange();
|
|
3654
|
+
}
|
|
3655
|
+
removePanel(panelId) {
|
|
3656
|
+
if (!collectPanelIds(this._model).includes(panelId)) return false;
|
|
3657
|
+
this._returnAllToolbars();
|
|
3658
|
+
this._removeFromModel(panelId);
|
|
3659
|
+
this._pool = this._pool.filter((p) => p.panelId !== panelId);
|
|
3660
|
+
this._toolbarsOut.delete(panelId);
|
|
3661
|
+
this._scanPanels().find((p) => p.panelId === panelId)?.remove();
|
|
3662
|
+
normalize(this._model);
|
|
3663
|
+
this.requestUpdate();
|
|
3664
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-removed`, {
|
|
3665
|
+
detail: { panelId },
|
|
3666
|
+
bubbles: true,
|
|
3667
|
+
composed: true
|
|
3668
|
+
}));
|
|
3669
|
+
this._emitChange();
|
|
3670
|
+
return true;
|
|
3671
|
+
}
|
|
3672
|
+
activatePanel(panelId) {
|
|
3673
|
+
const stackPath = findStackOf(this._model, panelId);
|
|
3674
|
+
if (stackPath === null) return false;
|
|
3675
|
+
const stack = findNode(this._model, stackPath);
|
|
3676
|
+
if (stack === null || !isStack(stack)) return false;
|
|
3677
|
+
if (!setActive(stack, panelId)) return false;
|
|
3678
|
+
this.requestUpdate();
|
|
3679
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-activated`, {
|
|
3680
|
+
detail: { panelId },
|
|
3681
|
+
bubbles: true,
|
|
3682
|
+
composed: true
|
|
3683
|
+
}));
|
|
3684
|
+
return true;
|
|
3685
|
+
}
|
|
3686
|
+
getPanelIds() {
|
|
3687
|
+
return collectPanelIds(this._model);
|
|
3688
|
+
}
|
|
3689
|
+
/**
|
|
3690
|
+
* Capture a structured-clone-safe {@link DockPanelSnapshot} of a panel
|
|
3691
|
+
* (identity, chrome attributes, and a best-effort `innerHTML`). Returns
|
|
3692
|
+
* null when no such panel element is known to this host.
|
|
3693
|
+
*/
|
|
3694
|
+
getPanelSnapshot(panelId) {
|
|
3695
|
+
const el = this._findPanelEl(panelId);
|
|
3696
|
+
if (el === void 0) return null;
|
|
3697
|
+
return {
|
|
3698
|
+
panelId: el.panelId,
|
|
3699
|
+
title: el.title,
|
|
3700
|
+
iconName: el.iconName,
|
|
3701
|
+
closable: el.closable,
|
|
3702
|
+
collapsible: el.collapsible,
|
|
3703
|
+
accepts: el.accepts,
|
|
3704
|
+
contentHTML: el.innerHTML
|
|
3705
|
+
};
|
|
3706
|
+
}
|
|
3707
|
+
/**
|
|
3708
|
+
* {@link getPanelSnapshot} + {@link removePanel} in one step: take the
|
|
3709
|
+
* snapshot, then drop the panel (and its element) from this host. Call this
|
|
3710
|
+
* from a `dock-panel-tear-out` handler once the receiving window has
|
|
3711
|
+
* recreated the panel.
|
|
3712
|
+
*/
|
|
3713
|
+
detachPanel(panelId) {
|
|
3714
|
+
const snapshot = this.getPanelSnapshot(panelId);
|
|
3715
|
+
if (snapshot === null) return null;
|
|
3716
|
+
this.removePanel(panelId);
|
|
3717
|
+
return snapshot;
|
|
3718
|
+
}
|
|
3719
|
+
/**
|
|
3720
|
+
* Hit-test viewport coordinates against this host and return a plain,
|
|
3721
|
+
* serializable {@link DockDropTarget} (no element references) — or null when
|
|
3722
|
+
* the point is outside the host or the resulting zone is disallowed by
|
|
3723
|
+
* `accepts` / `canAccept`. Pure query: no model mutation, no overlay.
|
|
3724
|
+
*
|
|
3725
|
+
* `panelId` is passed to `canAccept`; use any stable id for an external
|
|
3726
|
+
* panel (defaults to `"__external__"`).
|
|
3727
|
+
*/
|
|
3728
|
+
queryDropTarget(clientX, clientY, panelId = `__external__`) {
|
|
3729
|
+
const zone = this.computeDropZone(clientX, clientY, panelId);
|
|
3730
|
+
return zone === null ? null : zoneToTarget(zone);
|
|
3731
|
+
}
|
|
3732
|
+
/**
|
|
3733
|
+
* Like {@link queryDropTarget}, but also shows the drop overlay so an
|
|
3734
|
+
* external (cross-window) drag gets the same visual feedback as an in-page
|
|
3735
|
+
* one. Call on every pointer update from the outer coordinator, then
|
|
3736
|
+
* {@link endExternalDrop} when the drag leaves or ends.
|
|
3737
|
+
*/
|
|
3738
|
+
previewExternalDrop(clientX, clientY, panelId = `__external__`) {
|
|
3739
|
+
this.setDragActive(true);
|
|
3740
|
+
const zone = this.computeDropZone(clientX, clientY, panelId);
|
|
3741
|
+
this.setDropZone(zone);
|
|
3742
|
+
return zone === null ? null : zoneToTarget(zone);
|
|
3743
|
+
}
|
|
3744
|
+
/** Clear any overlay left by {@link previewExternalDrop}. */
|
|
3745
|
+
endExternalDrop() {
|
|
3746
|
+
this.setDropZone(null);
|
|
3747
|
+
this.setDragActive(false);
|
|
3748
|
+
}
|
|
3749
|
+
/**
|
|
3750
|
+
* Recreate a panel from a {@link DockPanelSnapshot} (or a bare
|
|
3751
|
+
* {@link DockPanelInit}) and dock it. With `at` viewport coordinates the
|
|
3752
|
+
* panel lands where a drop there would put it (respecting `accepts` /
|
|
3753
|
+
* `canAccept`); without them, or when the point misses the host, it goes
|
|
3754
|
+
* into the first stack. Returns the created element, or null when a panel
|
|
3755
|
+
* with that id is already docked here.
|
|
3756
|
+
*/
|
|
3757
|
+
acceptExternalPanel(snapshot, at) {
|
|
3758
|
+
if (collectPanelIds(this._model).includes(snapshot.panelId)) return null;
|
|
3759
|
+
const el = document.createElement(`ixfx-dock-panel`);
|
|
3760
|
+
el.panelId = snapshot.panelId;
|
|
3761
|
+
if (snapshot.title !== void 0) el.title = snapshot.title;
|
|
3762
|
+
if (snapshot.iconName !== void 0) el.iconName = snapshot.iconName;
|
|
3763
|
+
if (snapshot.closable !== void 0) el.closable = snapshot.closable;
|
|
3764
|
+
if (snapshot.collapsible !== void 0) el.collapsible = snapshot.collapsible;
|
|
3765
|
+
if (`accepts` in snapshot && snapshot.accepts) el.setAttribute(`accepts`, snapshot.accepts);
|
|
3766
|
+
if (`contentHTML` in snapshot && snapshot.contentHTML) el.innerHTML = snapshot.contentHTML;
|
|
3767
|
+
this._returnAllToolbars();
|
|
3768
|
+
this.appendChild(el);
|
|
3769
|
+
this._pool = [...this._pool, el];
|
|
3770
|
+
this._appendPanelToFirstStack(el.panelId);
|
|
3771
|
+
normalize(this._model);
|
|
3772
|
+
this.endExternalDrop();
|
|
3773
|
+
const zone = at ? this.computeDropZone(at.clientX, at.clientY, el.panelId) : null;
|
|
3774
|
+
if (zone !== null) this._applyZone(el.panelId, zone);
|
|
3775
|
+
normalize(this._model);
|
|
3776
|
+
this.requestUpdate();
|
|
3777
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-added`, {
|
|
3778
|
+
detail: { panelId: el.panelId },
|
|
3779
|
+
bubbles: true,
|
|
3780
|
+
composed: true
|
|
3781
|
+
}));
|
|
3782
|
+
this._emitChange();
|
|
3783
|
+
return el;
|
|
3784
|
+
}
|
|
3785
|
+
/**
|
|
3786
|
+
* Hit-test a pointer position against this host's leaves and edges. Returns
|
|
3787
|
+
* a zone with rects relative to the host, or null when the pointer is
|
|
3788
|
+
* outside the host or the candidate zone is disallowed.
|
|
3789
|
+
*/
|
|
3790
|
+
computeDropZone(clientX, clientY, panelId) {
|
|
3791
|
+
const hostRect = this.getBoundingClientRect();
|
|
3792
|
+
if (clientX < hostRect.left || clientX > hostRect.right || clientY < hostRect.top || clientY > hostRect.bottom) return null;
|
|
3793
|
+
const rel = (r) => ({
|
|
3794
|
+
x: r.left - hostRect.left,
|
|
3795
|
+
y: r.top - hostRect.top,
|
|
3796
|
+
w: r.width,
|
|
3797
|
+
h: r.height
|
|
3798
|
+
});
|
|
3799
|
+
for (const path of collectStackPaths(this._model)) {
|
|
3800
|
+
const leaf = this.shadowRoot?.querySelector(`[data-dock-path="${path.join(`-`)}"]`);
|
|
3801
|
+
if (!leaf) continue;
|
|
3802
|
+
const leafRect = leaf.getBoundingClientRect();
|
|
3803
|
+
if (clientX < leafRect.left || clientX > leafRect.right || clientY < leafRect.top || clientY > leafRect.bottom) continue;
|
|
3804
|
+
const stack = findNode(this._model, path);
|
|
3805
|
+
if (stack === null || !isStack(stack)) continue;
|
|
3806
|
+
const strip = leaf.querySelector(`ixfx-tab-list`);
|
|
3807
|
+
if (strip !== null) {
|
|
3808
|
+
const stripRect = strip.getBoundingClientRect();
|
|
3809
|
+
if (clientX >= stripRect.left && clientX <= stripRect.right && clientY >= stripRect.top && clientY <= stripRect.bottom) {
|
|
3810
|
+
const gapIndex = computeStripGapIndex(strip, clientX, clientY);
|
|
3811
|
+
const zone = {
|
|
3812
|
+
host: this,
|
|
3813
|
+
operation: `tabs`,
|
|
3814
|
+
targetPath: path,
|
|
3815
|
+
side: `after`,
|
|
3816
|
+
gapIndex,
|
|
3817
|
+
rect: rel(leafRect),
|
|
3818
|
+
caretRect: this._stripCaretRect(strip, gapIndex, hostRect)
|
|
3819
|
+
};
|
|
3820
|
+
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
const relX = (clientX - leafRect.left) / Math.max(leafRect.width, 1);
|
|
3824
|
+
const relY = (clientY - leafRect.top) / Math.max(leafRect.height, 1);
|
|
3825
|
+
if (relX >= BAND && relX <= .75 && relY >= BAND && relY <= .75) {
|
|
3826
|
+
const zone = {
|
|
3827
|
+
host: this,
|
|
3828
|
+
operation: `tabs`,
|
|
3829
|
+
targetPath: path,
|
|
3830
|
+
side: `after`,
|
|
3831
|
+
gapIndex: stack.panels.length,
|
|
3832
|
+
rect: rel(leafRect)
|
|
3833
|
+
};
|
|
3834
|
+
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
3835
|
+
}
|
|
3836
|
+
const r = rel(leafRect);
|
|
3837
|
+
let operation;
|
|
3838
|
+
let side;
|
|
3839
|
+
let zoneRect;
|
|
3840
|
+
if (relX < BAND) {
|
|
3841
|
+
operation = `split-vertical`;
|
|
3842
|
+
side = `before`;
|
|
3843
|
+
zoneRect = {
|
|
3844
|
+
...r,
|
|
3845
|
+
w: r.w / 2
|
|
3846
|
+
};
|
|
3847
|
+
} else if (relX > .75) {
|
|
3848
|
+
operation = `split-vertical`;
|
|
3849
|
+
side = `after`;
|
|
3850
|
+
zoneRect = {
|
|
3851
|
+
x: r.x + r.w / 2,
|
|
3852
|
+
y: r.y,
|
|
3853
|
+
w: r.w / 2,
|
|
3854
|
+
h: r.h
|
|
3855
|
+
};
|
|
3856
|
+
} else if (relY < BAND) {
|
|
3857
|
+
operation = `split-horizontal`;
|
|
3858
|
+
side = `before`;
|
|
3859
|
+
zoneRect = {
|
|
3860
|
+
...r,
|
|
3861
|
+
h: r.h / 2
|
|
3862
|
+
};
|
|
3863
|
+
} else {
|
|
3864
|
+
operation = `split-horizontal`;
|
|
3865
|
+
side = `after`;
|
|
3866
|
+
zoneRect = {
|
|
3867
|
+
x: r.x,
|
|
3868
|
+
y: r.y + r.h / 2,
|
|
3869
|
+
w: r.w,
|
|
3870
|
+
h: r.h / 2
|
|
3871
|
+
};
|
|
3872
|
+
}
|
|
3873
|
+
const zone = {
|
|
3874
|
+
host: this,
|
|
3875
|
+
operation,
|
|
3876
|
+
targetPath: path,
|
|
3877
|
+
side,
|
|
3878
|
+
rect: zoneRect
|
|
3879
|
+
};
|
|
3880
|
+
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
3881
|
+
}
|
|
3882
|
+
const fromLeft = clientX - hostRect.left;
|
|
3883
|
+
const fromRight = hostRect.right - clientX;
|
|
3884
|
+
const fromTop = clientY - hostRect.top;
|
|
3885
|
+
const fromBottom = hostRect.bottom - clientY;
|
|
3886
|
+
const min = Math.min(fromLeft, fromRight, fromTop, fromBottom);
|
|
3887
|
+
const full = {
|
|
3888
|
+
x: 0,
|
|
3889
|
+
y: 0,
|
|
3890
|
+
w: hostRect.width,
|
|
3891
|
+
h: hostRect.height
|
|
3892
|
+
};
|
|
3893
|
+
let operation;
|
|
3894
|
+
let side;
|
|
3895
|
+
let zoneRect;
|
|
3896
|
+
if (min === fromLeft) {
|
|
3897
|
+
operation = `split-vertical`;
|
|
3898
|
+
side = `before`;
|
|
3899
|
+
zoneRect = {
|
|
3900
|
+
...full,
|
|
3901
|
+
w: full.w / 2
|
|
3902
|
+
};
|
|
3903
|
+
} else if (min === fromRight) {
|
|
3904
|
+
operation = `split-vertical`;
|
|
3905
|
+
side = `after`;
|
|
3906
|
+
zoneRect = {
|
|
3907
|
+
x: full.x + full.w / 2,
|
|
3908
|
+
y: full.y,
|
|
3909
|
+
w: full.w / 2,
|
|
3910
|
+
h: full.h
|
|
3911
|
+
};
|
|
3912
|
+
} else if (min === fromTop) {
|
|
3913
|
+
operation = `split-horizontal`;
|
|
3914
|
+
side = `before`;
|
|
3915
|
+
zoneRect = {
|
|
3916
|
+
...full,
|
|
3917
|
+
h: full.h / 2
|
|
3918
|
+
};
|
|
3919
|
+
} else {
|
|
3920
|
+
operation = `split-horizontal`;
|
|
3921
|
+
side = `after`;
|
|
3922
|
+
zoneRect = {
|
|
3923
|
+
x: full.x,
|
|
3924
|
+
y: full.y + full.h / 2,
|
|
3925
|
+
w: full.w,
|
|
3926
|
+
h: full.h / 2
|
|
3927
|
+
};
|
|
3928
|
+
}
|
|
3929
|
+
const zone = {
|
|
3930
|
+
host: this,
|
|
3931
|
+
operation,
|
|
3932
|
+
targetPath: [],
|
|
3933
|
+
side,
|
|
3934
|
+
rect: zoneRect
|
|
3935
|
+
};
|
|
3936
|
+
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
3937
|
+
}
|
|
3938
|
+
_zoneAllowed(panelId, zone) {
|
|
3939
|
+
if (!tokenAllows(this.accepts, operationToToken(zone.operation))) return false;
|
|
3940
|
+
const stack = findNode(this._model, zone.targetPath);
|
|
3941
|
+
if (stack !== null && isStack(stack) && stack.panels.length === 1) {
|
|
3942
|
+
const target = this._findPanelEl(stack.panels[0]);
|
|
3943
|
+
if (target && !panelAllows(target.accepts, operationToToken(zone.operation))) return false;
|
|
3944
|
+
}
|
|
3945
|
+
if (this.canAccept && !this.canAccept(panelId, zone.operation)) return false;
|
|
3946
|
+
return true;
|
|
3947
|
+
}
|
|
3948
|
+
/**
|
|
3949
|
+
* Apply a drop. `sourceHost` differs from `this` when the drag crossed
|
|
3950
|
+
* hosts; the panel element is adopted into this host first.
|
|
3951
|
+
*/
|
|
3952
|
+
handleDrop(panelId, zone, sourceHost) {
|
|
3953
|
+
if (sourceHost !== void 0 && sourceHost !== this) sourceHost.releasePanel(panelId, this);
|
|
3954
|
+
if (!collectPanelIds(this._model).includes(panelId)) {
|
|
3955
|
+
if (!this._findPanelEl(panelId)) return;
|
|
3956
|
+
this._appendPanelToFirstStack(panelId);
|
|
3957
|
+
normalize(this._model);
|
|
3958
|
+
}
|
|
3959
|
+
this._applyZone(panelId, zone);
|
|
3960
|
+
this._commit();
|
|
3961
|
+
}
|
|
3962
|
+
/**
|
|
3963
|
+
* Move `panelId` (already present somewhere in the model) to the position
|
|
3964
|
+
* described by `zone`. Shared by in-page drops and external panel adoption.
|
|
3965
|
+
*/
|
|
3966
|
+
_applyZone(panelId, zone) {
|
|
3967
|
+
const sourcePath = findStackOf(this._model, panelId);
|
|
3968
|
+
if (sourcePath !== null) {
|
|
3969
|
+
const source = findNode(this._model, sourcePath);
|
|
3970
|
+
if (source !== null && isStack(source)) removePanelFromStack(source, panelId);
|
|
3971
|
+
}
|
|
3972
|
+
if (zone.operation === `tabs`) {
|
|
3973
|
+
const target = findNode(this._model, zone.targetPath);
|
|
3974
|
+
if (target !== null && isStack(target)) addPanelToStack(target, panelId, zone.gapIndex);
|
|
3975
|
+
} else insertBeside(this._model, zone.targetPath, opToDirection(zone.operation), createStack([panelId]), zone.side);
|
|
3976
|
+
}
|
|
3977
|
+
/**
|
|
3978
|
+
* Move a panel element out of this host into `target`, dropping it from
|
|
3979
|
+
* this host's model. Used for cross-host drags.
|
|
3980
|
+
*/
|
|
3981
|
+
releasePanel(panelId, target) {
|
|
3982
|
+
this._returnAllToolbars();
|
|
3983
|
+
const el = this._pool.find((p) => p.panelId === panelId);
|
|
3984
|
+
const stackPath = findStackOf(this._model, panelId);
|
|
3985
|
+
if (stackPath !== null) {
|
|
3986
|
+
const stack = findNode(this._model, stackPath);
|
|
3987
|
+
if (stack !== null && isStack(stack)) removePanelFromStack(stack, panelId);
|
|
3988
|
+
}
|
|
3989
|
+
normalize(this._model);
|
|
3990
|
+
this._pool = this._pool.filter((p) => p !== el);
|
|
3991
|
+
if (el !== void 0) target.adoptPanelElement(el);
|
|
3992
|
+
this.requestUpdate();
|
|
3993
|
+
}
|
|
3994
|
+
/** Adopt a panel element from another host (the one sanctioned reparent). */
|
|
3995
|
+
adoptPanelElement(el) {
|
|
3996
|
+
this.appendChild(el);
|
|
3997
|
+
if (!this._pool.some((p) => p.panelId === el.panelId)) this._pool = [...this._pool, el];
|
|
3998
|
+
this._appendPanelToFirstStack(el.panelId);
|
|
3999
|
+
normalize(this._model);
|
|
4000
|
+
this.dispatchEvent(new CustomEvent(`dock-panel-added`, {
|
|
4001
|
+
detail: { panelId: el.panelId },
|
|
4002
|
+
bubbles: true,
|
|
4003
|
+
composed: true
|
|
4004
|
+
}));
|
|
4005
|
+
}
|
|
4006
|
+
setDragActive(active) {
|
|
4007
|
+
if (this._dragActive !== active) {
|
|
4008
|
+
this._dragActive = active;
|
|
4009
|
+
this.requestUpdate();
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
setDropZone(zone) {
|
|
4013
|
+
if (this._dropZone !== zone) {
|
|
4014
|
+
this._dropZone = zone;
|
|
4015
|
+
this.requestUpdate();
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
_scheduleJunctions() {
|
|
4019
|
+
if (this._junctionsRaf !== null) return;
|
|
4020
|
+
this._junctionsRaf = requestAnimationFrame(() => {
|
|
4021
|
+
this._junctionsRaf = null;
|
|
4022
|
+
this._computeJunctions();
|
|
4023
|
+
});
|
|
4024
|
+
}
|
|
4025
|
+
/**
|
|
4026
|
+
* Find points where a parent split's gutter line meets a nested split's
|
|
4027
|
+
* gutter (T and + crossings). Each junction handle starts both gutter drags
|
|
4028
|
+
* at once, like a corner resize in VS Code.
|
|
4029
|
+
*/
|
|
4030
|
+
_computeJunctions() {
|
|
4031
|
+
const shadow = this.shadowRoot;
|
|
4032
|
+
if (shadow === null) return;
|
|
4033
|
+
const hostRect = this.getBoundingClientRect();
|
|
4034
|
+
const found = [];
|
|
4035
|
+
for (const child of shadow.querySelectorAll(`ixfx-split-layout`)) {
|
|
4036
|
+
const parent = child.parentElement?.closest(`ixfx-split-layout`) ?? null;
|
|
4037
|
+
if (parent === null) continue;
|
|
4038
|
+
if (typeof child.getGutterRects !== `function` || typeof parent.getGutterRects !== `function`) continue;
|
|
4039
|
+
const childRect = child.getBoundingClientRect();
|
|
4040
|
+
for (const [pi, pg] of parent.getGutterRects().entries()) for (const [ci, cg] of child.getGutterRects().entries()) {
|
|
4041
|
+
const pgVertical = pg.width <= pg.height;
|
|
4042
|
+
if (pgVertical === cg.width <= cg.height) continue;
|
|
4043
|
+
let x;
|
|
4044
|
+
let y;
|
|
4045
|
+
if (pgVertical) {
|
|
4046
|
+
x = pg.left + pg.width / 2;
|
|
4047
|
+
y = cg.top + cg.height / 2;
|
|
4048
|
+
if (Math.abs(x - childRect.left) > JUNCTION_TOL_PX && Math.abs(x - childRect.right) > JUNCTION_TOL_PX) continue;
|
|
4049
|
+
if (y < pg.top || y > pg.bottom) continue;
|
|
4050
|
+
} else {
|
|
4051
|
+
y = pg.top + pg.height / 2;
|
|
4052
|
+
x = cg.left + cg.width / 2;
|
|
4053
|
+
if (Math.abs(y - childRect.top) > JUNCTION_TOL_PX && Math.abs(y - childRect.bottom) > JUNCTION_TOL_PX) continue;
|
|
4054
|
+
if (x < pg.left || x > pg.right) continue;
|
|
4055
|
+
}
|
|
4056
|
+
found.push({
|
|
4057
|
+
x: x - hostRect.left,
|
|
4058
|
+
y: y - hostRect.top,
|
|
4059
|
+
targets: [{
|
|
4060
|
+
split: parent,
|
|
4061
|
+
gutterIndex: pi
|
|
4062
|
+
}, {
|
|
4063
|
+
split: child,
|
|
4064
|
+
gutterIndex: ci
|
|
4065
|
+
}]
|
|
4066
|
+
});
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
const merged = [];
|
|
4070
|
+
for (const j of found) {
|
|
4071
|
+
const near = merged.find((m) => Math.abs(m.x - j.x) <= JUNCTION_MERGE_PX && Math.abs(m.y - j.y) <= JUNCTION_MERGE_PX);
|
|
4072
|
+
if (near === void 0) {
|
|
4073
|
+
merged.push(j);
|
|
4074
|
+
continue;
|
|
4075
|
+
}
|
|
4076
|
+
for (const t of j.targets) if (!near.targets.some((o) => o.split === t.split && o.gutterIndex === t.gutterIndex)) near.targets.push(t);
|
|
4077
|
+
}
|
|
4078
|
+
const key = merged.map((j) => `${Math.round(j.x)},${Math.round(j.y)}:${j.targets.map((t) => `${t.split.getAttribute(`data-dock-path`) ?? ``}#${t.gutterIndex}`).join(`|`)}`).join(`;`);
|
|
4079
|
+
if (key === this._junctionsKey) return;
|
|
4080
|
+
this._junctionsKey = key;
|
|
4081
|
+
this._junctions = merged;
|
|
4082
|
+
}
|
|
4083
|
+
/** Fold live weights from each rendered `ixfx-split-layout` into the model. */
|
|
4084
|
+
_foldLiveWeights() {
|
|
4085
|
+
const splits = this.shadowRoot?.querySelectorAll(`ixfx-split-layout[data-dock-path]`);
|
|
4086
|
+
if (splits === void 0) return;
|
|
4087
|
+
for (const el of splits) {
|
|
4088
|
+
const pathStr = el.getAttribute(`data-dock-path`);
|
|
4089
|
+
if (pathStr === null) continue;
|
|
4090
|
+
const node = findNode(this._model, parsePath(pathStr));
|
|
4091
|
+
if (node === null || !isSplit(node)) continue;
|
|
4092
|
+
const live = Object.values(el.getSizes());
|
|
4093
|
+
if (live.length === node.children.length && !weightsEqual(live, node.sizes)) node.sizes = live;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
_teardownTabDragListeners() {
|
|
4097
|
+
window.removeEventListener(`pointermove`, this._onTabDragMove);
|
|
4098
|
+
window.removeEventListener(`pointerup`, this._onTabDragUp);
|
|
4099
|
+
window.removeEventListener(`pointercancel`, this._onTabDragUp);
|
|
4100
|
+
}
|
|
4101
|
+
/** After a dock drag from a tab/titlebar, swallow the trailing click so it cannot re-select or toggle. */
|
|
4102
|
+
_suppressNextClick() {
|
|
4103
|
+
const swallow = (e) => {
|
|
4104
|
+
e.preventDefault();
|
|
4105
|
+
e.stopPropagation();
|
|
4106
|
+
window.removeEventListener(`click`, swallow, true);
|
|
4107
|
+
};
|
|
4108
|
+
window.addEventListener(`click`, swallow, true);
|
|
4109
|
+
}
|
|
4110
|
+
_splitStack(path, op) {
|
|
4111
|
+
const stack = findNode(this._model, path);
|
|
4112
|
+
if (stack === null || !isStack(stack) || stack.panels.length === 0) return;
|
|
4113
|
+
const activeId = stack.active;
|
|
4114
|
+
this._returnAllToolbars();
|
|
4115
|
+
removePanelFromStack(stack, activeId);
|
|
4116
|
+
normalize(this._model);
|
|
4117
|
+
insertBeside(this._model, path, opToDirection(op), createStack([activeId]), `after`);
|
|
4118
|
+
this._commit();
|
|
4119
|
+
}
|
|
4120
|
+
_removeFromModel(panelId) {
|
|
4121
|
+
const stackPath = findStackOf(this._model, panelId);
|
|
4122
|
+
if (stackPath === null) return;
|
|
4123
|
+
const stack = findNode(this._model, stackPath);
|
|
4124
|
+
if (stack !== null && isStack(stack)) removePanelFromStack(stack, panelId);
|
|
4125
|
+
normalize(this._model);
|
|
4126
|
+
}
|
|
4127
|
+
/** Normalize, re-render, and announce the structural change. */
|
|
4128
|
+
_commit() {
|
|
4129
|
+
normalize(this._model);
|
|
4130
|
+
this.requestUpdate();
|
|
4131
|
+
this._emitChange();
|
|
4132
|
+
}
|
|
4133
|
+
_syncSlots() {
|
|
4134
|
+
const inModel = /* @__PURE__ */ new Set();
|
|
4135
|
+
const visit = (node) => {
|
|
4136
|
+
if (isStack(node)) for (const id of node.panels) {
|
|
4137
|
+
inModel.add(id);
|
|
4138
|
+
const el = this._findPanelEl(id);
|
|
4139
|
+
if (el === void 0) continue;
|
|
4140
|
+
el.setAttribute(`slot`, `dock-panel-${id}`);
|
|
4141
|
+
el.presentation = `tabbed`;
|
|
4142
|
+
}
|
|
4143
|
+
else node.children.forEach(visit);
|
|
4144
|
+
};
|
|
4145
|
+
visit(this._model);
|
|
4146
|
+
for (const el of this._pool) if (!inModel.has(el.panelId)) {
|
|
4147
|
+
el.setAttribute(`slot`, ``);
|
|
4148
|
+
el.presentation = `solo`;
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
/**
|
|
4152
|
+
* Per-panel toolbar routing: docked panels render content-only, so the
|
|
4153
|
+
* active panel's toolbar nodes live in its generated tab item (shown
|
|
4154
|
+
* right-aligned while active). Node moves preserve element state. Panels
|
|
4155
|
+
* outside the model keep their toolbar in their own (solo) chrome.
|
|
4156
|
+
*/
|
|
4157
|
+
_syncToolbars() {
|
|
4158
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
4159
|
+
const visit = (node) => {
|
|
4160
|
+
if (isStack(node)) {
|
|
4161
|
+
if (node.panels.length > 0 && node.active) {
|
|
4162
|
+
const el = this._findPanelEl(node.active);
|
|
4163
|
+
if (el !== void 0) wanted.set(node.active, toolbarNodesOf(el));
|
|
4164
|
+
}
|
|
4165
|
+
} else node.children.forEach(visit);
|
|
4166
|
+
};
|
|
4167
|
+
visit(this._model);
|
|
4168
|
+
for (const [panelId, nodes] of this._toolbarsOut) if (!wanted.has(panelId)) this._returnToolbar(panelId, nodes);
|
|
4169
|
+
for (const [panelId, nodes] of wanted) {
|
|
4170
|
+
if (this._toolbarsOut.has(panelId) || nodes.length === 0) continue;
|
|
4171
|
+
const item = this.shadowRoot?.getElementById(panelId);
|
|
4172
|
+
if (item === null || item === void 0) continue;
|
|
4173
|
+
for (const node of nodes) item.appendChild(node);
|
|
4174
|
+
this._toolbarsOut.set(panelId, nodes);
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
_returnToolbar(panelId, nodes) {
|
|
4178
|
+
const el = this._findPanelEl(panelId);
|
|
4179
|
+
if (el !== void 0) {
|
|
4180
|
+
for (const node of nodes) if (node.isConnected) el.appendChild(node);
|
|
4181
|
+
}
|
|
4182
|
+
this._toolbarsOut.delete(panelId);
|
|
4183
|
+
}
|
|
4184
|
+
_returnAllToolbars() {
|
|
4185
|
+
for (const [panelId, nodes] of [...this._toolbarsOut]) this._returnToolbar(panelId, nodes);
|
|
4186
|
+
}
|
|
4187
|
+
_emitChange() {
|
|
4188
|
+
this.dispatchEvent(new CustomEvent(`dock-change`, {
|
|
4189
|
+
detail: { layout: this.saveLayout() },
|
|
4190
|
+
bubbles: true,
|
|
4191
|
+
composed: true
|
|
4192
|
+
}));
|
|
4193
|
+
}
|
|
4194
|
+
render() {
|
|
4195
|
+
return html`
|
|
4196
|
+
<div class="root">
|
|
4197
|
+
${this._renderNode(this._model, [])}
|
|
4198
|
+
</div>
|
|
4199
|
+
<div class="overlay" ?hidden=${!this._dragActive}>
|
|
4200
|
+
${this._dropZone === null ? nothing : html`
|
|
4201
|
+
<div class="zone-rect" style=${rectStyle(this._dropZone.rect)}></div>
|
|
4202
|
+
${this._dropZone.caretRect === void 0 ? nothing : html`<div class="zone-caret" style=${rectStyle(this._dropZone.caretRect)}></div>`}
|
|
4203
|
+
`}
|
|
4204
|
+
</div>
|
|
4205
|
+
<div class="junctions" ?hidden=${this._dragActive}>
|
|
4206
|
+
${this._junctions.map((junction) => html`
|
|
4207
|
+
<div
|
|
4208
|
+
class="junction-handle"
|
|
4209
|
+
style=${`left:${junction.x}px; top:${junction.y}px;`}
|
|
4210
|
+
role="separator"
|
|
4211
|
+
aria-label="Resize adjacent panels"
|
|
4212
|
+
@pointerdown=${(e) => this._onJunctionPointerDown(e, junction)}
|
|
4213
|
+
></div>
|
|
4214
|
+
`)}
|
|
4215
|
+
</div>
|
|
4216
|
+
<slot @slotchange=${this._onPoolSlotChange}></slot>
|
|
4217
|
+
`;
|
|
4218
|
+
}
|
|
4219
|
+
_renderNode(node, path, size) {
|
|
4220
|
+
if (isStack(node)) return this._renderLeaf(node, path, size);
|
|
4221
|
+
return this._renderSplit(node, path, size);
|
|
4222
|
+
}
|
|
4223
|
+
_renderSplit(node, path, size) {
|
|
4224
|
+
const pathStr = path.join(`-`);
|
|
4225
|
+
return html`
|
|
4226
|
+
<ixfx-split-layout
|
|
4227
|
+
class="split"
|
|
4228
|
+
direction=${node.direction}
|
|
4229
|
+
size=${size === void 0 ? nothing : String(size)}
|
|
4230
|
+
style=${`--split-gutter-size: var(--dock-split-bar-size, 2px)`}
|
|
4231
|
+
data-dock-path=${pathStr}
|
|
4232
|
+
@ixfx-split-resize=${this._onSplitWeightsChanged}
|
|
4233
|
+
@ixfx-split-change=${this._onSplitWeightsChanged}
|
|
4234
|
+
@ixfx-split-collapse=${(e) => this._onSplitCollapsed(e, true)}
|
|
4235
|
+
@ixfx-split-expand=${(e) => this._onSplitCollapsed(e, false)}
|
|
4236
|
+
@ixfx-split-close=${this._onSplitClose}
|
|
4237
|
+
>
|
|
4238
|
+
${node.children.map((child, i) => this._renderNode(child, [...path, i], node.sizes[i]))}
|
|
4239
|
+
</ixfx-split-layout>
|
|
4240
|
+
`;
|
|
4241
|
+
}
|
|
4242
|
+
_renderLeaf(stack, path, size) {
|
|
4243
|
+
const pathStr = path.join(`-`);
|
|
4244
|
+
const collapsible = stack.panels.length === 1 ? this._findPanelEl(stack.panels[0])?.collapsible ?? false : false;
|
|
4245
|
+
const leafAttrs = (content) => html`
|
|
4246
|
+
<div
|
|
4247
|
+
id=${`dock-leaf-${pathStr}`}
|
|
4248
|
+
class="leaf"
|
|
4249
|
+
data-dock-path=${pathStr}
|
|
4250
|
+
size=${size === void 0 ? nothing : String(size)}
|
|
4251
|
+
min=${collapsible ? `1px` : nothing}
|
|
4252
|
+
when-small=${collapsible ? `collapse` : nothing}
|
|
4253
|
+
?collapsed=${stack.collapsed === true}
|
|
4254
|
+
>${content}</div>
|
|
4255
|
+
`;
|
|
4256
|
+
if (stack.panels.length === 0) return leafAttrs(html`
|
|
4257
|
+
<div class="empty" part="empty">
|
|
4258
|
+
<slot name="empty"><span>Drop panels here</span></slot>
|
|
4259
|
+
</div>
|
|
4260
|
+
`);
|
|
4261
|
+
return leafAttrs(html`
|
|
4262
|
+
<ixfx-tab-list
|
|
4263
|
+
class="strip"
|
|
4264
|
+
orientation="horizontal"
|
|
4265
|
+
@change=${(e) => this._onTabChange(e, path)}
|
|
4266
|
+
@pointerdown=${this._onStripPointerDown}
|
|
4267
|
+
>
|
|
4268
|
+
${repeat(stack.panels, (id) => id, (id) => this._renderTabItem(stack, id))}
|
|
4269
|
+
<div slot="toolbar" class="group-toolbar">
|
|
4270
|
+
<button
|
|
4271
|
+
class="chrome-btn"
|
|
4272
|
+
title="Split horizontally (top/bottom)"
|
|
4273
|
+
@click=${() => this._splitStack(path, `split-horizontal`)}
|
|
4274
|
+
>↕</button>
|
|
4275
|
+
<button
|
|
4276
|
+
class="chrome-btn"
|
|
4277
|
+
title="Split vertically (left/right)"
|
|
4278
|
+
@click=${() => this._splitStack(path, `split-vertical`)}
|
|
4279
|
+
>↔</button>
|
|
4280
|
+
</div>
|
|
4281
|
+
</ixfx-tab-list>
|
|
4282
|
+
<div class="leaf-body">
|
|
4283
|
+
<slot name="dock-panel-${stack.active}"></slot>
|
|
4284
|
+
</div>
|
|
4285
|
+
`);
|
|
4286
|
+
}
|
|
4287
|
+
_renderTabItem(stack, panelId) {
|
|
4288
|
+
const el = this._findPanelEl(panelId);
|
|
4289
|
+
return html`
|
|
4290
|
+
<ixfx-tab-list-item
|
|
4291
|
+
id=${panelId}
|
|
4292
|
+
?selected=${panelId === stack.active}
|
|
4293
|
+
.iconName=${el?.iconName ?? ``}
|
|
4294
|
+
.closeable=${el?.closable ?? false}
|
|
4295
|
+
@close=${() => this.removePanel(panelId)}
|
|
4296
|
+
>${el?.title ?? panelId}</ixfx-tab-list-item>
|
|
4297
|
+
`;
|
|
4298
|
+
}
|
|
4299
|
+
_stripCaretRect(strip, gapIndex, hostRect) {
|
|
4300
|
+
const items = Array.from(strip.querySelectorAll(`ixfx-tab-list-item`));
|
|
4301
|
+
if (items.length === 0) return void 0;
|
|
4302
|
+
const index = Math.max(0, Math.min(gapIndex ?? items.length, items.length));
|
|
4303
|
+
const edge = index < items.length ? items[index].getBoundingClientRect().left : items[items.length - 1].getBoundingClientRect().right;
|
|
4304
|
+
const stripRect = strip.getBoundingClientRect();
|
|
4305
|
+
return {
|
|
4306
|
+
x: edge - hostRect.left - 1,
|
|
4307
|
+
y: stripRect.top - hostRect.top,
|
|
4308
|
+
w: 2,
|
|
4309
|
+
h: stripRect.height
|
|
4310
|
+
};
|
|
4311
|
+
}
|
|
4312
|
+
static {
|
|
4313
|
+
this.styles = [themeFallbacks, css`
|
|
4314
|
+
:host {
|
|
4315
|
+
display: block;
|
|
4316
|
+
width: 100%;
|
|
4317
|
+
height: 100%;
|
|
4318
|
+
--dock-drop-zone-color: var(--accent);
|
|
4319
|
+
--dock-drop-zone-opacity: 0.25;
|
|
4320
|
+
--dock-collapsed-size: 40px;
|
|
4321
|
+
--dock-split-bar-size: 2px;
|
|
4322
|
+
position: relative;
|
|
4323
|
+
}
|
|
2079
4324
|
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
4325
|
+
.root {
|
|
4326
|
+
width: 100%;
|
|
4327
|
+
height: 100%;
|
|
4328
|
+
}
|
|
4329
|
+
|
|
4330
|
+
ixfx-split-layout.split {
|
|
4331
|
+
width: 100%;
|
|
4332
|
+
height: 100%;
|
|
4333
|
+
}
|
|
4334
|
+
|
|
4335
|
+
.leaf {
|
|
4336
|
+
display: flex;
|
|
4337
|
+
flex-direction: column;
|
|
4338
|
+
width: 100%;
|
|
4339
|
+
height: 100%;
|
|
4340
|
+
min-width: 0;
|
|
4341
|
+
min-height: 0;
|
|
4342
|
+
box-sizing: border-box;
|
|
4343
|
+
overflow: hidden;
|
|
4344
|
+
border: 1px solid var(--surface-4);
|
|
4345
|
+
border-radius: var(--radius-m);
|
|
4346
|
+
}
|
|
4347
|
+
|
|
4348
|
+
.leaf[collapsed] {
|
|
4349
|
+
pointer-events: none;
|
|
4350
|
+
}
|
|
4351
|
+
|
|
4352
|
+
.leaf[collapsed] .leaf-body,
|
|
4353
|
+
.leaf[collapsed] .empty {
|
|
4354
|
+
visibility: hidden;
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
.strip {
|
|
4358
|
+
flex-shrink: 0;
|
|
4359
|
+
border: 0;
|
|
4360
|
+
/* The leaf background shows through selected tabs; the accent
|
|
4361
|
+
underline indicator carries the selection. */
|
|
4362
|
+
--ixfx-tab-selected-background: transparent;
|
|
4363
|
+
}
|
|
4364
|
+
|
|
4365
|
+
/* Uniform tab height: a panel toolbar button would otherwise inflate
|
|
4366
|
+
its tab item, giving strips of different stacks different heights. */
|
|
4367
|
+
.strip ixfx-tab-list-item {
|
|
4368
|
+
height: var(--dock-tab-height, 30px);
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4371
|
+
.leaf-body {
|
|
4372
|
+
flex: 1;
|
|
4373
|
+
min-height: 0;
|
|
4374
|
+
min-width: 0;
|
|
4375
|
+
box-sizing: border-box;
|
|
4376
|
+
padding: var(--space-s);
|
|
4377
|
+
}
|
|
4378
|
+
|
|
4379
|
+
.empty {
|
|
4380
|
+
flex: 1;
|
|
4381
|
+
display: flex;
|
|
4382
|
+
align-items: center;
|
|
4383
|
+
justify-content: center;
|
|
4384
|
+
color: var(--surface-3-text);
|
|
4385
|
+
opacity: 0.6;
|
|
4386
|
+
font-size: var(--text-m);
|
|
4387
|
+
border: 1px dashed var(--border-subtle);
|
|
4388
|
+
border-radius: var(--radius-m);
|
|
4389
|
+
margin: var(--space-s);
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
.group-toolbar {
|
|
4393
|
+
display: flex;
|
|
4394
|
+
gap: 2px;
|
|
4395
|
+
padding: 0 var(--space-xs);
|
|
4396
|
+
}
|
|
4397
|
+
|
|
4398
|
+
.chrome-btn {
|
|
4399
|
+
display: inline-flex;
|
|
4400
|
+
align-items: center;
|
|
4401
|
+
justify-content: center;
|
|
4402
|
+
width: 1.6em;
|
|
4403
|
+
height: 1.6em;
|
|
4404
|
+
padding: 0;
|
|
4405
|
+
background: none;
|
|
4406
|
+
border: none;
|
|
4407
|
+
border-radius: var(--radius-s);
|
|
4408
|
+
color: inherit;
|
|
4409
|
+
cursor: pointer;
|
|
4410
|
+
opacity: var(--prominence-idle-opacity);
|
|
4411
|
+
transition: background var(--transition), opacity var(--transition);
|
|
4412
|
+
}
|
|
4413
|
+
|
|
4414
|
+
.chrome-btn:hover {
|
|
4415
|
+
background: var(--surface-5);
|
|
4416
|
+
opacity: var(--prominence-tickled-opacity);
|
|
4417
|
+
}
|
|
4418
|
+
|
|
4419
|
+
.chrome-btn:focus-visible {
|
|
4420
|
+
outline: 2px solid var(--accent);
|
|
4421
|
+
outline-offset: 1px;
|
|
4422
|
+
}
|
|
4423
|
+
|
|
4424
|
+
.overlay {
|
|
4425
|
+
position: absolute;
|
|
4426
|
+
inset: 0;
|
|
4427
|
+
pointer-events: none;
|
|
4428
|
+
z-index: 100;
|
|
4429
|
+
}
|
|
4430
|
+
|
|
4431
|
+
.zone-rect {
|
|
4432
|
+
position: absolute;
|
|
4433
|
+
background: var(--dock-drop-zone-color);
|
|
4434
|
+
opacity: var(--dock-drop-zone-opacity);
|
|
4435
|
+
border-radius: var(--radius-s);
|
|
4436
|
+
}
|
|
4437
|
+
|
|
4438
|
+
.zone-caret {
|
|
4439
|
+
position: absolute;
|
|
4440
|
+
background: var(--dock-drop-zone-color);
|
|
4441
|
+
border-radius: 1px;
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4444
|
+
.junctions {
|
|
4445
|
+
position: absolute;
|
|
4446
|
+
inset: 0;
|
|
4447
|
+
pointer-events: none;
|
|
4448
|
+
z-index: 99;
|
|
4449
|
+
}
|
|
4450
|
+
|
|
4451
|
+
.junctions[hidden] {
|
|
4452
|
+
display: none;
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
.junction-handle {
|
|
4456
|
+
position: absolute;
|
|
4457
|
+
width: 14px;
|
|
4458
|
+
height: 14px;
|
|
4459
|
+
transform: translate(-50%, -50%);
|
|
4460
|
+
border-radius: 50%;
|
|
4461
|
+
cursor: nwse-resize;
|
|
4462
|
+
pointer-events: auto;
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
.junction-handle::after {
|
|
4466
|
+
content: '';
|
|
4467
|
+
position: absolute;
|
|
4468
|
+
inset: 4px;
|
|
4469
|
+
border-radius: 50%;
|
|
4470
|
+
background: var(--dock-junction-color, var(--accent));
|
|
4471
|
+
opacity: 0;
|
|
4472
|
+
transition: opacity var(--transition);
|
|
4473
|
+
}
|
|
4474
|
+
|
|
4475
|
+
.junction-handle:hover::after {
|
|
4476
|
+
opacity: 0.9;
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4479
|
+
slot:not([name]) {
|
|
4480
|
+
display: none;
|
|
4481
|
+
}
|
|
4482
|
+
`];
|
|
2087
4483
|
}
|
|
2088
4484
|
};
|
|
2089
|
-
__decorate([property({ type: Number })], EditableNumber.prototype, "min", void 0);
|
|
2090
|
-
__decorate([property({ type: Number })], EditableNumber.prototype, "max", void 0);
|
|
2091
|
-
__decorate([property({ type: Number })], EditableNumber.prototype, "step", void 0);
|
|
2092
|
-
__decorate([property({ type: Number })], EditableNumber.prototype, "stops", void 0);
|
|
2093
4485
|
__decorate([property({
|
|
2094
|
-
type:
|
|
2095
|
-
attribute: `
|
|
2096
|
-
})],
|
|
2097
|
-
__decorate([property({ attribute: `fill-style` })], EditableNumber.prototype, "fillStyle", void 0);
|
|
2098
|
-
__decorate([property({ attribute: `linear-style` })], EditableNumber.prototype, "linearStyle", void 0);
|
|
2099
|
-
__decorate([property({ attribute: `circular-style` })], EditableNumber.prototype, "circularStyle", void 0);
|
|
4486
|
+
type: String,
|
|
4487
|
+
attribute: `layout`
|
|
4488
|
+
})], DockHostElement.prototype, "layout", void 0);
|
|
2100
4489
|
__decorate([property({
|
|
2101
|
-
type:
|
|
2102
|
-
attribute: `
|
|
2103
|
-
})],
|
|
2104
|
-
__decorate([property({
|
|
2105
|
-
|
|
4490
|
+
type: String,
|
|
4491
|
+
attribute: `accepts`
|
|
4492
|
+
})], DockHostElement.prototype, "accepts", void 0);
|
|
4493
|
+
__decorate([property({ attribute: false })], DockHostElement.prototype, "canAccept", void 0);
|
|
4494
|
+
__decorate([state()], DockHostElement.prototype, "_model", void 0);
|
|
4495
|
+
__decorate([state()], DockHostElement.prototype, "_pool", void 0);
|
|
4496
|
+
__decorate([state()], DockHostElement.prototype, "_dropZone", void 0);
|
|
4497
|
+
__decorate([state()], DockHostElement.prototype, "_dragActive", void 0);
|
|
4498
|
+
__decorate([state()], DockHostElement.prototype, "_junctions", void 0);
|
|
4499
|
+
DockHostElement = __decorate([safeCustomElement(`ixfx-dock-host`)], DockHostElement);
|
|
4500
|
+
/** `split-horizontal` = horizontal divider = panels stacked vertically. */
|
|
4501
|
+
function opToDirection(op) {
|
|
4502
|
+
return op === `split-horizontal` ? `column` : `row`;
|
|
4503
|
+
}
|
|
4504
|
+
function operationToToken(op) {
|
|
4505
|
+
if (op === `tabs`) return `tabs`;
|
|
4506
|
+
return op === `split-horizontal` ? `horizontal` : `vertical`;
|
|
4507
|
+
}
|
|
4508
|
+
/** Host-level `accepts`: empty or `none` rejects everything. */
|
|
4509
|
+
function tokenAllows(tokens, token) {
|
|
4510
|
+
const list = tokens.trim().split(/\s+/).filter((t) => t.length > 0);
|
|
4511
|
+
if (list.length === 0 || list.length === 1 && list[0] === `none`) return false;
|
|
4512
|
+
return list.includes(`all`) || list.includes(token);
|
|
4513
|
+
}
|
|
4514
|
+
/** Panel-level `accepts`: empty means unrestricted (host rules still apply). */
|
|
4515
|
+
function panelAllows(tokens, token) {
|
|
4516
|
+
const list = tokens.trim().split(/\s+/).filter((t) => t.length > 0);
|
|
4517
|
+
if (list.length === 0) return true;
|
|
4518
|
+
if (list.length === 1 && list[0] === `none`) return false;
|
|
4519
|
+
return list.includes(`all`) || list.includes(token);
|
|
4520
|
+
}
|
|
4521
|
+
function weightsEqual(a, b) {
|
|
4522
|
+
if (a.length !== b.length) return false;
|
|
4523
|
+
return a.every((v, i) => Math.abs(v - b[i]) < 1e-4);
|
|
4524
|
+
}
|
|
4525
|
+
function rectStyle(r) {
|
|
4526
|
+
return `left:${r.x}px; top:${r.y}px; width:${r.w}px; height:${r.h}px;`;
|
|
4527
|
+
}
|
|
4528
|
+
/** Strip a live `DockDropZone` down to its serializable {@link DockDropTarget}. */
|
|
4529
|
+
function zoneToTarget(zone) {
|
|
4530
|
+
return {
|
|
4531
|
+
operation: zone.operation,
|
|
4532
|
+
targetPath: [...zone.targetPath],
|
|
4533
|
+
side: zone.side,
|
|
4534
|
+
...zone.gapIndex === void 0 ? {} : { gapIndex: zone.gapIndex }
|
|
4535
|
+
};
|
|
4536
|
+
}
|
|
4537
|
+
function cloneNode(node) {
|
|
4538
|
+
return JSON.parse(JSON.stringify(node));
|
|
4539
|
+
}
|
|
4540
|
+
function parsePath(pathStr) {
|
|
4541
|
+
return pathStr.length === 0 ? [] : pathStr.split(`-`).filter((s) => s.length > 0).map(Number);
|
|
4542
|
+
}
|
|
4543
|
+
/** Toolbar-slot light children of a dock panel. */
|
|
4544
|
+
function toolbarNodesOf(el) {
|
|
4545
|
+
return Array.from(el.children).filter((c) => c instanceof HTMLElement && c.getAttribute(`slot`) === `toolbar`);
|
|
4546
|
+
}
|
|
4547
|
+
/** Nearest strip gap to the pointer (reuses the tab-list midpoint rule). */
|
|
4548
|
+
function computeStripGapIndex(strip, clientX, _clientY) {
|
|
4549
|
+
const items = Array.from(strip.querySelectorAll(`ixfx-tab-list-item`));
|
|
4550
|
+
if (items.length === 0) return 0;
|
|
4551
|
+
for (let i = 0; i < items.length; i++) {
|
|
4552
|
+
const r = items[i].getBoundingClientRect();
|
|
4553
|
+
if (clientX < (r.left + r.right) / 2) return i;
|
|
4554
|
+
}
|
|
4555
|
+
return items.length;
|
|
4556
|
+
}
|
|
2106
4557
|
//#endregion
|
|
2107
4558
|
//#region src/form/form-group.ts
|
|
2108
4559
|
let FormGroupElement = class FormGroupElement extends LitElement {
|
|
@@ -2766,6 +5217,7 @@ let PanelGroupElement = class PanelGroupElement extends LitElement {
|
|
|
2766
5217
|
this.orientation = `horizontal`;
|
|
2767
5218
|
this.variant = `titlebar`;
|
|
2768
5219
|
this.toggleInteraction = `implicit`;
|
|
5220
|
+
this.collapsible = true;
|
|
2769
5221
|
this.allowMultiple = false;
|
|
2770
5222
|
this.gap = `var(--space-m)`;
|
|
2771
5223
|
}
|
|
@@ -2782,7 +5234,7 @@ let PanelGroupElement = class PanelGroupElement extends LitElement {
|
|
|
2782
5234
|
}
|
|
2783
5235
|
updated(changed) {
|
|
2784
5236
|
super.updated(changed);
|
|
2785
|
-
if (changed.has(`variant`) || changed.has(`toggleInteraction`) || changed.has(`orientation`)) this.updateComplete.then(() => {
|
|
5237
|
+
if (changed.has(`variant`) || changed.has(`toggleInteraction`) || changed.has(`orientation`) || changed.has(`collapsible`)) this.updateComplete.then(() => {
|
|
2786
5238
|
this._propagateAll();
|
|
2787
5239
|
});
|
|
2788
5240
|
}
|
|
@@ -2792,6 +5244,8 @@ let PanelGroupElement = class PanelGroupElement extends LitElement {
|
|
|
2792
5244
|
panel.setAttribute(`variant`, this.variant);
|
|
2793
5245
|
panel.setAttribute(`toggle-interaction`, this.toggleInteraction);
|
|
2794
5246
|
panel.setAttribute(`orientation`, invertedOrientation);
|
|
5247
|
+
if (this.collapsible) panel.setAttribute(`collapsible`, ``);
|
|
5248
|
+
else panel.removeAttribute(`collapsible`);
|
|
2795
5249
|
}
|
|
2796
5250
|
}
|
|
2797
5251
|
render() {
|
|
@@ -2822,132 +5276,31 @@ let PanelGroupElement = class PanelGroupElement extends LitElement {
|
|
|
2822
5276
|
::slotted(ixfx-panel) {
|
|
2823
5277
|
flex-shrink: 1;
|
|
2824
5278
|
}
|
|
2825
|
-
|
|
2826
|
-
:host([orientation="horizontal"]) ::slotted(ixfx-panel) {
|
|
2827
|
-
flex-basis: content;
|
|
2828
|
-
min-width: 0;
|
|
2829
|
-
}
|
|
2830
|
-
`;
|
|
2831
|
-
}
|
|
2832
|
-
};
|
|
2833
|
-
__decorate([property({
|
|
2834
|
-
type: String,
|
|
2835
|
-
reflect: true
|
|
2836
|
-
})], PanelGroupElement.prototype, "orientation", void 0);
|
|
2837
|
-
__decorate([property({ type: String })], PanelGroupElement.prototype, "variant", void 0);
|
|
2838
|
-
__decorate([property({
|
|
2839
|
-
type: String,
|
|
2840
|
-
attribute: `toggle-interaction`
|
|
2841
|
-
})], PanelGroupElement.prototype, "toggleInteraction", void 0);
|
|
2842
|
-
__decorate([property({
|
|
2843
|
-
type: Boolean,
|
|
2844
|
-
attribute: `allow-multiple`
|
|
2845
|
-
})], PanelGroupElement.prototype, "allowMultiple", void 0);
|
|
2846
|
-
__decorate([property()], PanelGroupElement.prototype, "gap", void 0);
|
|
2847
|
-
__decorate([queryAssignedElements({ slot: `` })], PanelGroupElement.prototype, "_panels", void 0);
|
|
2848
|
-
PanelGroupElement = __decorate([safeCustomElement(`ixfx-panel-group`)], PanelGroupElement);
|
|
2849
|
-
//#endregion
|
|
2850
|
-
//#region src/util/is-numeric.ts
|
|
2851
|
-
function isNumeric(str) {
|
|
2852
|
-
if (typeof str === `number`) return true;
|
|
2853
|
-
if (typeof str !== `string`) return false;
|
|
2854
|
-
const trimmed = str.trim();
|
|
2855
|
-
return trimmed !== `` && !Number.isNaN(Number(trimmed));
|
|
2856
|
-
}
|
|
2857
|
-
//#endregion
|
|
2858
|
-
//#region src/util/tooltip.ts
|
|
2859
|
-
var _TooltipElement;
|
|
2860
|
-
let _baseStylesInjected = false;
|
|
2861
|
-
function ensureBaseTooltipStyles() {
|
|
2862
|
-
if (_baseStylesInjected) return;
|
|
2863
|
-
_baseStylesInjected = true;
|
|
2864
|
-
const style = document.createElement(`style`);
|
|
2865
|
-
style.textContent = `
|
|
2866
|
-
ixfx-tooltip {
|
|
2867
|
-
display: inline-block;
|
|
2868
|
-
background: var(--surface-5);
|
|
2869
|
-
color: var(--surface-5-text);
|
|
2870
|
-
border: var(--border);
|
|
2871
|
-
border-radius: var(--radius-l, 6px);
|
|
2872
|
-
box-shadow: var(--shadow-m);
|
|
2873
|
-
padding: var(--space-s, 4px);
|
|
2874
|
-
font-size: 10px;
|
|
2875
|
-
font-family: var(--font-family, inherit);
|
|
2876
|
-
}
|
|
2877
|
-
ixfx-tooltip .tooltip-type-number {
|
|
2878
|
-
font-family: var(--font-mono, ui-monospace, monospace);
|
|
2879
|
-
}
|
|
2880
|
-
`;
|
|
2881
|
-
document.head.appendChild(style);
|
|
2882
|
-
}
|
|
2883
|
-
let TooltipElement = class TooltipElement extends LitElement {
|
|
2884
|
-
static {
|
|
2885
|
-
_TooltipElement = this;
|
|
2886
|
-
}
|
|
2887
|
-
constructor(..._args) {
|
|
2888
|
-
super(..._args);
|
|
2889
|
-
this.valueFormatTooltip = _TooltipElement.defaultFormatter;
|
|
2890
|
-
this.orientation = `top`;
|
|
2891
|
-
this.anchor = `--tooltip`;
|
|
2892
|
-
}
|
|
2893
|
-
connectedCallback() {
|
|
2894
|
-
super.connectedCallback();
|
|
2895
|
-
ensureBaseTooltipStyles();
|
|
2896
|
-
}
|
|
2897
|
-
static {
|
|
2898
|
-
this.numberFormatter = (v) => Math.floor(v).toString();
|
|
2899
|
-
}
|
|
2900
|
-
static {
|
|
2901
|
-
this.defaultFormatter = (v) => {
|
|
2902
|
-
if (typeof v === `number`) {
|
|
2903
|
-
if (Number.isInteger(v)) return v.toString();
|
|
2904
|
-
return v.toFixed(2);
|
|
2905
|
-
} else if (typeof v === `string`) return v;
|
|
2906
|
-
else return JSON.stringify(v);
|
|
2907
|
-
};
|
|
2908
|
-
}
|
|
2909
|
-
#renderIcon() {
|
|
2910
|
-
if (!this.icon) return nothing;
|
|
2911
|
-
if (this.icon.startsWith(`<svg`)) return html`<span class="tooltip-icon" style="display:inline-flex;align-items:center;margin-right:0.25em">${unsafeHTML(this.icon)}</span>`;
|
|
2912
|
-
if (!getIcon(this.icon)) return nothing;
|
|
2913
|
-
return html`<ixfx-icon class="tooltip-icon" style="margin-right:0.25em" name="${this.icon}"></ixfx-icon>`;
|
|
2914
|
-
}
|
|
2915
|
-
render() {
|
|
2916
|
-
const classes = {
|
|
2917
|
-
"tooltip": true,
|
|
2918
|
-
"tooltip-orientation-top": this.orientation === `top`,
|
|
2919
|
-
"tooltip-orientation-bottom": this.orientation === `bottom`,
|
|
2920
|
-
"tooltip-orientation-right": this.orientation === `right`,
|
|
2921
|
-
"tooltip-orientation-left": this.orientation === `left`,
|
|
2922
|
-
"tooltip-type-text": true,
|
|
2923
|
-
"tooltip-type-number": false,
|
|
2924
|
-
"tooltip-style-bubble": true
|
|
2925
|
-
};
|
|
2926
|
-
let value = ``;
|
|
2927
|
-
switch (typeof this.value) {
|
|
2928
|
-
case `number`:
|
|
2929
|
-
classes[`tooltip-type-number`] = true;
|
|
2930
|
-
value = this.valueFormatTooltip(this.value);
|
|
2931
|
-
break;
|
|
2932
|
-
case `string`:
|
|
2933
|
-
if (isNumeric(this.value)) {
|
|
2934
|
-
classes[`tooltip-type-number`] = true;
|
|
2935
|
-
value = this.valueFormatTooltip(Number.parseFloat(this.value));
|
|
2936
|
-
} else value = this.value;
|
|
2937
|
-
break;
|
|
2938
|
-
default: value = JSON.stringify(this.value);
|
|
2939
|
-
}
|
|
2940
|
-
return html`<output style="--anchor-id: ${this.anchor}" class=${classMap(classes)}>${this.#renderIcon()}${value}</output>`;
|
|
2941
|
-
}
|
|
2942
|
-
createRenderRoot() {
|
|
2943
|
-
return this;
|
|
5279
|
+
|
|
5280
|
+
:host([orientation="horizontal"]) ::slotted(ixfx-panel) {
|
|
5281
|
+
flex-basis: content;
|
|
5282
|
+
min-width: 0;
|
|
5283
|
+
}
|
|
5284
|
+
`;
|
|
2944
5285
|
}
|
|
2945
5286
|
};
|
|
2946
|
-
__decorate([property({
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
5287
|
+
__decorate([property({
|
|
5288
|
+
type: String,
|
|
5289
|
+
reflect: true
|
|
5290
|
+
})], PanelGroupElement.prototype, "orientation", void 0);
|
|
5291
|
+
__decorate([property({ type: String })], PanelGroupElement.prototype, "variant", void 0);
|
|
5292
|
+
__decorate([property({
|
|
5293
|
+
type: String,
|
|
5294
|
+
attribute: `toggle-interaction`
|
|
5295
|
+
})], PanelGroupElement.prototype, "toggleInteraction", void 0);
|
|
5296
|
+
__decorate([property({ type: Boolean })], PanelGroupElement.prototype, "collapsible", void 0);
|
|
5297
|
+
__decorate([property({
|
|
5298
|
+
type: Boolean,
|
|
5299
|
+
attribute: `allow-multiple`
|
|
5300
|
+
})], PanelGroupElement.prototype, "allowMultiple", void 0);
|
|
5301
|
+
__decorate([property()], PanelGroupElement.prototype, "gap", void 0);
|
|
5302
|
+
__decorate([queryAssignedElements({ slot: `` })], PanelGroupElement.prototype, "_panels", void 0);
|
|
5303
|
+
PanelGroupElement = __decorate([safeCustomElement(`ixfx-panel-group`)], PanelGroupElement);
|
|
2951
5304
|
//#endregion
|
|
2952
5305
|
//#region src/util/drag.ts
|
|
2953
5306
|
function getCoordinates(event) {
|
|
@@ -4006,278 +6359,54 @@ let SlidingNumberElement = class SlidingNumberElement extends LitElement {
|
|
|
4006
6359
|
opacity: 0;
|
|
4007
6360
|
}
|
|
4008
6361
|
|
|
4009
|
-
.cell.digit[data-phase='enter'],
|
|
4010
|
-
.cell.digit[data-phase='leave'] {
|
|
4011
|
-
width: 0;
|
|
4012
|
-
}
|
|
4013
|
-
|
|
4014
|
-
.cell.symbol[data-phase='enter'],
|
|
4015
|
-
.cell.symbol[data-phase='leave'] {
|
|
4016
|
-
max-width: 0;
|
|
4017
|
-
}
|
|
4018
|
-
|
|
4019
|
-
.reel {
|
|
4020
|
-
position: absolute;
|
|
4021
|
-
inset: 0 0 auto 0;
|
|
4022
|
-
display: flex;
|
|
4023
|
-
flex-direction: column;
|
|
4024
|
-
will-change: transform;
|
|
4025
|
-
transform: translateY(calc(-1em * var(--o, 10)));
|
|
4026
|
-
}
|
|
4027
|
-
|
|
4028
|
-
.reel > span {
|
|
4029
|
-
display: block;
|
|
4030
|
-
height: 1em;
|
|
4031
|
-
line-height: 1;
|
|
4032
|
-
text-align: center;
|
|
4033
|
-
}
|
|
4034
|
-
|
|
4035
|
-
:host([data-static]) .cell {
|
|
4036
|
-
transition: none;
|
|
4037
|
-
}
|
|
4038
|
-
`;
|
|
4039
|
-
}
|
|
4040
|
-
};
|
|
4041
|
-
__decorate([property({ type: Number })], SlidingNumberElement.prototype, "value", void 0);
|
|
4042
|
-
__decorate([property({ type: String })], SlidingNumberElement.prototype, "locales", void 0);
|
|
4043
|
-
__decorate([property({ type: Object })], SlidingNumberElement.prototype, "format", void 0);
|
|
4044
|
-
__decorate([property({ type: String })], SlidingNumberElement.prototype, "prefix", void 0);
|
|
4045
|
-
__decorate([property({ type: String })], SlidingNumberElement.prototype, "suffix", void 0);
|
|
4046
|
-
__decorate([property({ type: String })], SlidingNumberElement.prototype, "trend", void 0);
|
|
4047
|
-
__decorate([property({
|
|
4048
|
-
type: Boolean,
|
|
4049
|
-
attribute: `no-animate`
|
|
4050
|
-
})], SlidingNumberElement.prototype, "noAnimate", void 0);
|
|
4051
|
-
__decorate([property({
|
|
4052
|
-
type: Boolean,
|
|
4053
|
-
attribute: false
|
|
4054
|
-
})], SlidingNumberElement.prototype, "respectMotionPreference", void 0);
|
|
4055
|
-
__decorate([state()], SlidingNumberElement.prototype, "_cells", void 0);
|
|
4056
|
-
SlidingNumberElement = __decorate([safeCustomElement(`ixfx-sliding-number`)], SlidingNumberElement);
|
|
4057
|
-
//#endregion
|
|
4058
|
-
//#region src/tabs/tab-list-item.ts
|
|
4059
|
-
let TabListItemElement = class TabListItemElement extends LitElement {
|
|
4060
|
-
constructor(..._args) {
|
|
4061
|
-
super(..._args);
|
|
4062
|
-
this.closeable = false;
|
|
4063
|
-
this.hideLabels = false;
|
|
4064
|
-
this.iconName = void 0;
|
|
4065
|
-
this.description = void 0;
|
|
4066
|
-
this.commandActive = void 0;
|
|
4067
|
-
this.registry = void 0;
|
|
4068
|
-
this._compact = false;
|
|
4069
|
-
this._hasToolbarContent = false;
|
|
4070
|
-
this._onToolbarSlotChange = () => {
|
|
4071
|
-
this._updateToolbarContent();
|
|
4072
|
-
};
|
|
4073
|
-
}
|
|
4074
|
-
connectedCallback() {
|
|
4075
|
-
super.connectedCallback();
|
|
4076
|
-
this._resizeObserver = new ResizeObserver(([entry]) => {
|
|
4077
|
-
this._compact = entry.contentRect.width < 64;
|
|
4078
|
-
});
|
|
4079
|
-
this._resizeObserver.observe(this);
|
|
4080
|
-
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.addEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
4081
|
-
}
|
|
4082
|
-
disconnectedCallback() {
|
|
4083
|
-
super.disconnectedCallback();
|
|
4084
|
-
this._resizeObserver?.disconnect();
|
|
4085
|
-
this._resizeObserver = void 0;
|
|
4086
|
-
this._commandCleanup?.();
|
|
4087
|
-
this._commandCleanup = void 0;
|
|
4088
|
-
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.removeEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
4089
|
-
}
|
|
4090
|
-
updated(changed) {
|
|
4091
|
-
super.updated(changed);
|
|
4092
|
-
this._syncTooltipAttrs();
|
|
4093
|
-
if (changed.has(`registry`) || changed.has(`commandActive`)) this._syncCommandListener();
|
|
4094
|
-
}
|
|
4095
|
-
_syncTooltipAttrs() {
|
|
4096
|
-
if (this.description) {
|
|
4097
|
-
this.setAttribute(`data-tooltip`, this.description);
|
|
4098
|
-
this.setAttribute(`data-tooltip-anchor`, `top`);
|
|
4099
|
-
} else {
|
|
4100
|
-
this.removeAttribute(`data-tooltip`);
|
|
4101
|
-
this.removeAttribute(`data-tooltip-anchor`);
|
|
4102
|
-
}
|
|
4103
|
-
}
|
|
4104
|
-
_syncCommandListener() {
|
|
4105
|
-
this._commandCleanup?.();
|
|
4106
|
-
this._commandCleanup = void 0;
|
|
4107
|
-
if (this.registry && this.commandActive) this._commandCleanup = this.registry.addOnInvokeHandler((id) => {
|
|
4108
|
-
if (id === this.commandActive) this.click();
|
|
4109
|
-
});
|
|
4110
|
-
}
|
|
4111
|
-
_updateToolbarContent() {
|
|
4112
|
-
const slot = this.shadowRoot?.querySelector(`slot[name="toolbar"]`);
|
|
4113
|
-
this._hasToolbarContent = (slot?.assignedElements().length ?? 0) > 0;
|
|
4114
|
-
}
|
|
4115
|
-
firstUpdated() {
|
|
4116
|
-
this._updateToolbarContent();
|
|
4117
|
-
}
|
|
4118
|
-
render() {
|
|
4119
|
-
const showLabel = !this.hideLabels || !this.iconName;
|
|
4120
|
-
const hasActions = this.closeable || this._hasToolbarContent;
|
|
4121
|
-
return html`
|
|
4122
|
-
<div>
|
|
4123
|
-
${this.iconName ? html`<ixfx-icon name="${this.iconName}"></ixfx-icon>` : nothing}
|
|
4124
|
-
${showLabel ? html`<span class="label"><slot></slot></span>` : nothing}
|
|
4125
|
-
${hasActions ? html`
|
|
4126
|
-
<div class="actions">
|
|
4127
|
-
<div class="toolbar"><slot name="toolbar"></slot></div>
|
|
4128
|
-
${this.closeable ? html`
|
|
4129
|
-
<button
|
|
4130
|
-
class="close-btn"
|
|
4131
|
-
type="button"
|
|
4132
|
-
aria-label="Close tab"
|
|
4133
|
-
@click=${this._onCloseClick}
|
|
4134
|
-
>
|
|
4135
|
-
<ixfx-icon name="close"></ixfx-icon>
|
|
4136
|
-
</button>
|
|
4137
|
-
` : nothing}
|
|
4138
|
-
</div>
|
|
4139
|
-
` : nothing}
|
|
4140
|
-
</div>
|
|
4141
|
-
`;
|
|
4142
|
-
}
|
|
4143
|
-
_onCloseClick(e) {
|
|
4144
|
-
e.stopPropagation();
|
|
4145
|
-
this.dispatchEvent(new CustomEvent(`close`, {
|
|
4146
|
-
bubbles: true,
|
|
4147
|
-
composed: true
|
|
4148
|
-
}));
|
|
4149
|
-
this.remove();
|
|
4150
|
-
}
|
|
4151
|
-
static {
|
|
4152
|
-
this.styles = [
|
|
4153
|
-
themeFallbacks,
|
|
4154
|
-
prominenceStyles,
|
|
4155
|
-
css`
|
|
4156
|
-
:host {
|
|
4157
|
-
--selected-tab-color: var(--ixfx-selected-tab-color, var(--surface-2-text));
|
|
4158
|
-
--selected-tab-border-width: var(--ixfx-selected-tab-border-width, 1px);
|
|
4159
|
-
--selected-tab-weight: var(--ixfx-selected-tab-weight, bold);
|
|
4160
|
-
--selected-background: var(--ixfx-tab-selected-background, var(--surface-5));
|
|
4161
|
-
--tab-padding: var(--ixfx-tab-padding, var(--space-m));
|
|
4162
|
-
display: inline-flex;
|
|
4163
|
-
min-width: 0;
|
|
4164
|
-
user-select: none;
|
|
4165
|
-
box-sizing: border-box;
|
|
4166
|
-
height:fit-content;
|
|
4167
|
-
}
|
|
4168
|
-
|
|
4169
|
-
:host > div {
|
|
4170
|
-
display: flex;
|
|
4171
|
-
align-items: center;
|
|
4172
|
-
padding: var(--tab-padding) calc(2 * var(--tab-padding-horizontal, var(--tab-padding)));
|
|
4173
|
-
min-width: 0;
|
|
4174
|
-
width: 100%;
|
|
4175
|
-
font-weight: 500;
|
|
4176
|
-
border: var(--selected-tab-border-width) solid transparent;
|
|
4177
|
-
gap: var(--space-s);
|
|
4178
|
-
|
|
4179
|
-
}
|
|
4180
|
-
|
|
4181
|
-
:host([selected]) > div {
|
|
4182
|
-
color: var(--selected-tab-color);
|
|
4183
|
-
border-top-color: var(--indicator-color-top, transparent);
|
|
4184
|
-
border-bottom-color: var(--indicator-color-bottom, transparent);
|
|
4185
|
-
border-left-color: var(--indicator-color-left, transparent);
|
|
4186
|
-
border-right-color: var(--indicator-color-right, transparent);
|
|
4187
|
-
background: var(--selected-background);
|
|
4188
|
-
font-weight: var(--selected-tab-weight);
|
|
4189
|
-
border-radius:var(--radius-m);
|
|
4190
|
-
}
|
|
4191
|
-
|
|
4192
|
-
.label {
|
|
4193
|
-
overflow: var(--allow-overflow, hidden);
|
|
4194
|
-
text-overflow: ellipsis;
|
|
4195
|
-
white-space: nowrap;
|
|
4196
|
-
min-width: 0;
|
|
4197
|
-
flex: 1 1 auto;
|
|
4198
|
-
}
|
|
4199
|
-
|
|
4200
|
-
ixfx-icon {
|
|
4201
|
-
flex-shrink: 0;
|
|
4202
|
-
}
|
|
4203
|
-
|
|
4204
|
-
.actions {
|
|
4205
|
-
display: flex;
|
|
4206
|
-
align-items: center;
|
|
4207
|
-
gap: var(--space-xs);
|
|
4208
|
-
margin-left: auto;
|
|
4209
|
-
flex-shrink: 0;
|
|
4210
|
-
}
|
|
4211
|
-
|
|
4212
|
-
.toolbar {
|
|
4213
|
-
/* Always laid out (display: flex) so the tab's width doesn't shift when
|
|
4214
|
-
the toolbar appears on selection. visibility: hidden keeps the
|
|
4215
|
-
toolbar from receiving pointer events or painting, while preserving
|
|
4216
|
-
the space its contents take up. */
|
|
4217
|
-
display: flex;
|
|
4218
|
-
align-items: center;
|
|
4219
|
-
gap: var(--space-xs);
|
|
4220
|
-
visibility: hidden;
|
|
4221
|
-
}
|
|
4222
|
-
|
|
4223
|
-
:host([selected]) .toolbar {
|
|
4224
|
-
visibility: visible;
|
|
4225
|
-
}
|
|
4226
|
-
|
|
4227
|
-
slot[name="toolbar"] {
|
|
4228
|
-
display: contents;
|
|
4229
|
-
}
|
|
4230
|
-
|
|
4231
|
-
.close-btn {
|
|
4232
|
-
display: inline-flex;
|
|
4233
|
-
align-items: center;
|
|
4234
|
-
justify-content: center;
|
|
4235
|
-
background: none;
|
|
4236
|
-
border: none;
|
|
4237
|
-
padding: var(--space-xs);
|
|
4238
|
-
cursor: pointer;
|
|
4239
|
-
color: inherit;
|
|
4240
|
-
border-radius: var(--radius-s);
|
|
4241
|
-
opacity: var(--prominence-idle-opacity);
|
|
4242
|
-
transition: opacity var(--transition), background var(--transition);
|
|
4243
|
-
}
|
|
4244
|
-
|
|
4245
|
-
.close-btn:hover {
|
|
4246
|
-
background: var(--surface-5);
|
|
4247
|
-
opacity: var(--prominence-tickled-opacity);
|
|
4248
|
-
}
|
|
6362
|
+
.cell.digit[data-phase='enter'],
|
|
6363
|
+
.cell.digit[data-phase='leave'] {
|
|
6364
|
+
width: 0;
|
|
6365
|
+
}
|
|
4249
6366
|
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
6367
|
+
.cell.symbol[data-phase='enter'],
|
|
6368
|
+
.cell.symbol[data-phase='leave'] {
|
|
6369
|
+
max-width: 0;
|
|
6370
|
+
}
|
|
4254
6371
|
|
|
4255
|
-
|
|
4256
|
-
:
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
6372
|
+
.reel {
|
|
6373
|
+
position: absolute;
|
|
6374
|
+
inset: 0 0 auto 0;
|
|
6375
|
+
display: flex;
|
|
6376
|
+
flex-direction: column;
|
|
6377
|
+
will-change: transform;
|
|
6378
|
+
transform: translateY(calc(-1em * var(--o, 10)));
|
|
6379
|
+
}
|
|
6380
|
+
|
|
6381
|
+
.reel > span {
|
|
6382
|
+
display: block;
|
|
6383
|
+
height: 1em;
|
|
6384
|
+
line-height: 1;
|
|
6385
|
+
text-align: center;
|
|
6386
|
+
}
|
|
6387
|
+
|
|
6388
|
+
:host([data-static]) .cell {
|
|
6389
|
+
transition: none;
|
|
6390
|
+
}
|
|
6391
|
+
`;
|
|
4261
6392
|
}
|
|
4262
6393
|
};
|
|
4263
|
-
__decorate([property({ type:
|
|
6394
|
+
__decorate([property({ type: Number })], SlidingNumberElement.prototype, "value", void 0);
|
|
6395
|
+
__decorate([property({ type: String })], SlidingNumberElement.prototype, "locales", void 0);
|
|
6396
|
+
__decorate([property({ type: Object })], SlidingNumberElement.prototype, "format", void 0);
|
|
6397
|
+
__decorate([property({ type: String })], SlidingNumberElement.prototype, "prefix", void 0);
|
|
6398
|
+
__decorate([property({ type: String })], SlidingNumberElement.prototype, "suffix", void 0);
|
|
6399
|
+
__decorate([property({ type: String })], SlidingNumberElement.prototype, "trend", void 0);
|
|
4264
6400
|
__decorate([property({
|
|
4265
6401
|
type: Boolean,
|
|
4266
|
-
attribute: `
|
|
4267
|
-
})],
|
|
4268
|
-
__decorate([property({
|
|
4269
|
-
type: String,
|
|
4270
|
-
attribute: `icon-name`
|
|
4271
|
-
})], TabListItemElement.prototype, "iconName", void 0);
|
|
4272
|
-
__decorate([property({ type: String })], TabListItemElement.prototype, "description", void 0);
|
|
6402
|
+
attribute: `no-animate`
|
|
6403
|
+
})], SlidingNumberElement.prototype, "noAnimate", void 0);
|
|
4273
6404
|
__decorate([property({
|
|
4274
|
-
type:
|
|
4275
|
-
attribute:
|
|
4276
|
-
})],
|
|
4277
|
-
__decorate([
|
|
4278
|
-
__decorate([
|
|
4279
|
-
__decorate([state()], TabListItemElement.prototype, "_hasToolbarContent", void 0);
|
|
4280
|
-
TabListItemElement = __decorate([safeCustomElement(`ixfx-tab-list-item`)], TabListItemElement);
|
|
6405
|
+
type: Boolean,
|
|
6406
|
+
attribute: false
|
|
6407
|
+
})], SlidingNumberElement.prototype, "respectMotionPreference", void 0);
|
|
6408
|
+
__decorate([state()], SlidingNumberElement.prototype, "_cells", void 0);
|
|
6409
|
+
SlidingNumberElement = __decorate([safeCustomElement(`ixfx-sliding-number`)], SlidingNumberElement);
|
|
4281
6410
|
//#endregion
|
|
4282
6411
|
//#region src/tabs/tab-panel.ts
|
|
4283
6412
|
let TabPanelElement = class TabPanelElement extends LitElement {
|
|
@@ -4793,7 +6922,7 @@ let ToolbarGroupElement = class ToolbarGroupElement extends LitElement {
|
|
|
4793
6922
|
align-items: var(--toolbar-group-align-items, center);
|
|
4794
6923
|
justify-content: var(--toolbar-group-justify-content, flex-start);
|
|
4795
6924
|
gap: var(--toolbar-gap, var(--space-s));
|
|
4796
|
-
padding: var(--space-xs);
|
|
6925
|
+
padding: var(--toolbar-group-padding, var(--space-xs));
|
|
4797
6926
|
border: 1px solid var(--border-subtle, var(--border));
|
|
4798
6927
|
border-radius: var(--radius-s);
|
|
4799
6928
|
background: var(--surface-3);
|
|
@@ -4849,6 +6978,7 @@ let ToolbarElement = class ToolbarElement extends LitElement {
|
|
|
4849
6978
|
this.orientation = `horizontal`;
|
|
4850
6979
|
this.layout = `one-line`;
|
|
4851
6980
|
this.verticalWriting = `auto`;
|
|
6981
|
+
this.toolbarAlign = `near`;
|
|
4852
6982
|
this.showGroupLabels = false;
|
|
4853
6983
|
this.invisibleGroups = false;
|
|
4854
6984
|
this._overflowCount = 0;
|
|
@@ -4868,6 +6998,7 @@ let ToolbarElement = class ToolbarElement extends LitElement {
|
|
|
4868
6998
|
:host {
|
|
4869
6999
|
--toolbar-gap: var(--space-s);
|
|
4870
7000
|
--toolbar-group-gap: var(--space-m);
|
|
7001
|
+
--toolbar-overflow: auto;
|
|
4871
7002
|
display: block;
|
|
4872
7003
|
container-type: inline-size;
|
|
4873
7004
|
}
|
|
@@ -4894,6 +7025,7 @@ let ToolbarElement = class ToolbarElement extends LitElement {
|
|
|
4894
7025
|
:host([orientation="vertical-right"]) .inner { flex-direction: column; align-items: stretch; }
|
|
4895
7026
|
:host([layout="wrap"]) .inner { flex-wrap: wrap; }
|
|
4896
7027
|
:host([layout="one-line"]) .inner { flex-wrap: nowrap; overflow: hidden; }
|
|
7028
|
+
:host([toolbar-align="far"][orientation="horizontal"]) .inner { justify-content: flex-end; }
|
|
4897
7029
|
:host([orientation="vertical-left"][vertical-writing="auto"]) ::slotted(*) { writing-mode: sideways-lr; }
|
|
4898
7030
|
:host([orientation="vertical-right"][vertical-writing="auto"]) ::slotted(*) { writing-mode: vertical-lr; }
|
|
4899
7031
|
:host([vertical-writing="up"]) ::slotted(*) { writing-mode: vertical-rl; text-orientation: upright; }
|
|
@@ -4906,14 +7038,8 @@ let ToolbarElement = class ToolbarElement extends LitElement {
|
|
|
4906
7038
|
.overflow-btn {
|
|
4907
7039
|
anchor-name: var(--anchor-name);
|
|
4908
7040
|
flex-shrink: 0;
|
|
4909
|
-
background: var(--surface-3);
|
|
4910
|
-
border: var(--border);
|
|
4911
|
-
border-radius: var(--radius-s);
|
|
4912
|
-
padding: var(--space-xs) var(--space-s);
|
|
4913
|
-
cursor: pointer;
|
|
4914
|
-
display: none;
|
|
4915
7041
|
}
|
|
4916
|
-
.overflow-btn[
|
|
7042
|
+
.overflow-btn[hidden] { display: none; }
|
|
4917
7043
|
.overflow-popover {
|
|
4918
7044
|
position: fixed;
|
|
4919
7045
|
top: anchor(bottom);
|
|
@@ -4973,7 +7099,8 @@ let ToolbarElement = class ToolbarElement extends LitElement {
|
|
|
4973
7099
|
const isGrow = (el) => el.hasAttribute(`grow`) || el.matches(`[grow]`);
|
|
4974
7100
|
const overflowable = children.filter((el) => !isGrow(el));
|
|
4975
7101
|
const isHoriz = this.orientation === `horizontal`;
|
|
4976
|
-
|
|
7102
|
+
const overflowMode = getComputedStyle(this).getPropertyValue(`--toolbar-overflow`).trim();
|
|
7103
|
+
if (this.layout !== `one-line` || overflowMode === `disabled`) {
|
|
4977
7104
|
let mutated = false;
|
|
4978
7105
|
for (const el of children) {
|
|
4979
7106
|
if (el.getAttribute(`slot`) === `overflow`) {
|
|
@@ -5005,8 +7132,23 @@ let ToolbarElement = class ToolbarElement extends LitElement {
|
|
|
5005
7132
|
if (s > 0) this._sizes.set(el, s);
|
|
5006
7133
|
cum += (i ? gap : 0) + (this._sizes.get(el) ?? s);
|
|
5007
7134
|
});
|
|
5008
|
-
const btnAllowance = inOverflow.length > 0 ? gap + btnSize : 0;
|
|
5009
7135
|
let mutated = false;
|
|
7136
|
+
if (overflowMode === `collapsed`) {
|
|
7137
|
+
if (inMain.length > 0) {
|
|
7138
|
+
for (const el of inMain) el.setAttribute(`slot`, `overflow`);
|
|
7139
|
+
inOverflow.push(...inMain);
|
|
7140
|
+
inMain.length = 0;
|
|
7141
|
+
mutated = true;
|
|
7142
|
+
}
|
|
7143
|
+
const newCount = inOverflow.length;
|
|
7144
|
+
if (newCount !== this._overflowCount) {
|
|
7145
|
+
this._overflowCount = newCount;
|
|
7146
|
+
mutated = true;
|
|
7147
|
+
}
|
|
7148
|
+
if (mutated) this.requestUpdate();
|
|
7149
|
+
return;
|
|
7150
|
+
}
|
|
7151
|
+
const btnAllowance = inOverflow.length > 0 ? gap + btnSize : 0;
|
|
5010
7152
|
while (inMain.length > 0 && cum + btnAllowance > available) {
|
|
5011
7153
|
const last = inMain[inMain.length - 1];
|
|
5012
7154
|
const s = this._sizes.get(last) ?? 0;
|
|
@@ -5044,11 +7186,12 @@ let ToolbarElement = class ToolbarElement extends LitElement {
|
|
|
5044
7186
|
}
|
|
5045
7187
|
}
|
|
5046
7188
|
render() {
|
|
5047
|
-
const
|
|
7189
|
+
const overflowMode = getComputedStyle(this).getPropertyValue(`--toolbar-overflow`).trim();
|
|
7190
|
+
const hasOverflow = this._overflowCount > 0 || overflowMode === `collapsed`;
|
|
5048
7191
|
return html`
|
|
5049
7192
|
<div class="inner" role="toolbar" aria-orientation=${this.orientation}>
|
|
5050
7193
|
<slot></slot>
|
|
5051
|
-
<button class="overflow-btn" ?
|
|
7194
|
+
<ixfx-button class="overflow-btn" ?hidden=${!hasOverflow} @click=${this._toggleOverflow} aria-label="More" aria-haspopup="true" aria-expanded=${this._popover?.matches(`:popover-open`) ?? false}>⋯</ixfx-button>
|
|
5052
7195
|
</div>
|
|
5053
7196
|
<div class="overflow-popover popup-surface" popover="auto" @click=${(e) => e.stopPropagation()}>
|
|
5054
7197
|
<slot name="overflow"></slot>
|
|
@@ -5069,6 +7212,11 @@ __decorate([property({
|
|
|
5069
7212
|
attribute: `vertical-writing`,
|
|
5070
7213
|
reflect: true
|
|
5071
7214
|
})], ToolbarElement.prototype, "verticalWriting", void 0);
|
|
7215
|
+
__decorate([property({
|
|
7216
|
+
type: String,
|
|
7217
|
+
attribute: `toolbar-align`,
|
|
7218
|
+
reflect: true
|
|
7219
|
+
})], ToolbarElement.prototype, "toolbarAlign", void 0);
|
|
5072
7220
|
__decorate([property({
|
|
5073
7221
|
type: Boolean,
|
|
5074
7222
|
attribute: `show-group-labels`,
|
|
@@ -5085,6 +7233,616 @@ __decorate([query(`.overflow-popover`)], ToolbarElement.prototype, "_popover", v
|
|
|
5085
7233
|
__decorate([query(`slot`)], ToolbarElement.prototype, "_slot", void 0);
|
|
5086
7234
|
ToolbarElement = _ToolbarElement = __decorate([safeCustomElement(`ixfx-toolbar`)], ToolbarElement);
|
|
5087
7235
|
//#endregion
|
|
7236
|
+
//#region src/twosplit-layout/split-layout.ts
|
|
7237
|
+
let TwoSplitLayoutElement = class TwoSplitLayoutElement extends LitElement {
|
|
7238
|
+
constructor(..._args) {
|
|
7239
|
+
super(..._args);
|
|
7240
|
+
this.layout = `vertical`;
|
|
7241
|
+
this.sizeA = ``;
|
|
7242
|
+
this.sizeB = ``;
|
|
7243
|
+
this.fixedA = false;
|
|
7244
|
+
this.fixedB = false;
|
|
7245
|
+
this.collapsible = false;
|
|
7246
|
+
this.collapsedA = false;
|
|
7247
|
+
this.collapsedB = false;
|
|
7248
|
+
this.minSizeA = ``;
|
|
7249
|
+
this.maxSizeA = ``;
|
|
7250
|
+
this.minSizeB = ``;
|
|
7251
|
+
this.maxSizeB = ``;
|
|
7252
|
+
this._hasCorner = false;
|
|
7253
|
+
this.#startSizeA = ``;
|
|
7254
|
+
this.#startSizeB = ``;
|
|
7255
|
+
this.#cornerStartX = 0;
|
|
7256
|
+
this.#cornerStartY = 0;
|
|
7257
|
+
this.#nestedSplitInitialSizeA = ``;
|
|
7258
|
+
this.#pendingNestedUpdate = null;
|
|
7259
|
+
this.#hysteresisThreshold = .1;
|
|
7260
|
+
this.#onResizeHandleDown = (event) => {
|
|
7261
|
+
event.preventDefault();
|
|
7262
|
+
const zoneEl = this.getZone();
|
|
7263
|
+
if (zoneEl) zoneEl.classList.add(`user-resizing`);
|
|
7264
|
+
this.#elementBounds = this.getBoundingClientRect();
|
|
7265
|
+
this.#startSizeA = this.sizeA;
|
|
7266
|
+
this.#startSizeB = this.sizeB;
|
|
7267
|
+
window.addEventListener(`pointerup`, this.#onResizeHandleUp);
|
|
7268
|
+
window.addEventListener(`pointermove`, this.#onResizeHandleMove);
|
|
7269
|
+
};
|
|
7270
|
+
this.#onResizeHandleUp = (event) => {
|
|
7271
|
+
event.preventDefault();
|
|
7272
|
+
const zoneEl = this.getZone();
|
|
7273
|
+
if (zoneEl) zoneEl.classList.remove(`user-resizing`);
|
|
7274
|
+
window.removeEventListener(`pointerup`, this.#onResizeHandleUp);
|
|
7275
|
+
window.removeEventListener(`pointermove`, this.#onResizeHandleMove);
|
|
7276
|
+
};
|
|
7277
|
+
this.#onResizeHandleMove = (event) => {
|
|
7278
|
+
const bounds = this.#elementBounds;
|
|
7279
|
+
if (!bounds) return;
|
|
7280
|
+
const threshold = 5;
|
|
7281
|
+
if (this.layout === `horizontal`) {
|
|
7282
|
+
const y = event.y - bounds.y;
|
|
7283
|
+
const relativeY = clamp(y / bounds.height);
|
|
7284
|
+
if (this.collapsible && this.collapsedA) {
|
|
7285
|
+
if (relativeY > (this.#collapsedAPointerAtCollapse ?? .05) + this.#hysteresisThreshold) {
|
|
7286
|
+
this.collapsedA = false;
|
|
7287
|
+
this.#dispatchCollapsedEvent(`A`, false);
|
|
7288
|
+
this.#collapsedAPointerAtCollapse = void 0;
|
|
7289
|
+
const { sizeA, sizeB } = this.#applyConstraints(`${relativeY * 100}%`, `${(1 - relativeY) * 100}%`, bounds.height);
|
|
7290
|
+
this.sizeA = sizeA;
|
|
7291
|
+
this.sizeB = sizeB;
|
|
7292
|
+
}
|
|
7293
|
+
} else if (this.collapsible && !this.collapsedA && relativeY < threshold / bounds.height) {
|
|
7294
|
+
this.collapsedA = true;
|
|
7295
|
+
this.#dispatchCollapsedEvent(`A`, true);
|
|
7296
|
+
this.sizeA = `0px`;
|
|
7297
|
+
this.sizeB = `100%`;
|
|
7298
|
+
this.#collapsedAPointerAtCollapse = relativeY;
|
|
7299
|
+
} else if (!this.collapsedA) {
|
|
7300
|
+
const { sizeA, sizeB } = this.#applyConstraints(`${relativeY * 100}%`, `${(1 - relativeY) * 100}%`, bounds.height);
|
|
7301
|
+
this.sizeA = sizeA;
|
|
7302
|
+
this.sizeB = sizeB;
|
|
7303
|
+
}
|
|
7304
|
+
if (this.collapsible && this.collapsedB) {
|
|
7305
|
+
if (relativeY < (this.#collapsedBPointerAtCollapse ?? .95) - this.#hysteresisThreshold) {
|
|
7306
|
+
this.collapsedB = false;
|
|
7307
|
+
this.#dispatchCollapsedEvent(`B`, false);
|
|
7308
|
+
this.#collapsedBPointerAtCollapse = void 0;
|
|
7309
|
+
const { sizeA, sizeB } = this.#applyConstraints(`${relativeY * 100}%`, `${(1 - relativeY) * 100}%`, bounds.height);
|
|
7310
|
+
this.sizeA = sizeA;
|
|
7311
|
+
this.sizeB = sizeB;
|
|
7312
|
+
}
|
|
7313
|
+
} else if (this.collapsible && !this.collapsedB && relativeY > 1 - threshold / bounds.height) {
|
|
7314
|
+
this.collapsedB = true;
|
|
7315
|
+
this.#dispatchCollapsedEvent(`B`, true);
|
|
7316
|
+
this.sizeA = `100%`;
|
|
7317
|
+
this.sizeB = `0px`;
|
|
7318
|
+
this.#collapsedBPointerAtCollapse = relativeY;
|
|
7319
|
+
}
|
|
7320
|
+
} else {
|
|
7321
|
+
const x = event.x - bounds.x;
|
|
7322
|
+
const relativeX = clamp(x / bounds.width);
|
|
7323
|
+
if (this.collapsible && this.collapsedA) {
|
|
7324
|
+
if (relativeX > (this.#collapsedAPointerAtCollapse ?? .05) + this.#hysteresisThreshold) {
|
|
7325
|
+
this.collapsedA = false;
|
|
7326
|
+
this.#dispatchCollapsedEvent(`A`, false);
|
|
7327
|
+
this.#collapsedAPointerAtCollapse = void 0;
|
|
7328
|
+
const { sizeA, sizeB } = this.#applyConstraints(`${relativeX * 100}%`, `${(1 - relativeX) * 100}%`, bounds.width);
|
|
7329
|
+
this.sizeA = sizeA;
|
|
7330
|
+
this.sizeB = sizeB;
|
|
7331
|
+
}
|
|
7332
|
+
} else if (this.collapsible && !this.collapsedA && relativeX < threshold / bounds.width) {
|
|
7333
|
+
this.collapsedA = true;
|
|
7334
|
+
this.#dispatchCollapsedEvent(`A`, true);
|
|
7335
|
+
this.sizeA = `0px`;
|
|
7336
|
+
this.sizeB = `100%`;
|
|
7337
|
+
this.#collapsedAPointerAtCollapse = relativeX;
|
|
7338
|
+
} else if (!this.collapsedA) {
|
|
7339
|
+
const { sizeA, sizeB } = this.#applyConstraints(`${relativeX * 100}%`, `${(1 - relativeX) * 100}%`, bounds.width);
|
|
7340
|
+
this.sizeA = sizeA;
|
|
7341
|
+
this.sizeB = sizeB;
|
|
7342
|
+
}
|
|
7343
|
+
if (this.collapsible && this.collapsedB) {
|
|
7344
|
+
if (relativeX < (this.#collapsedBPointerAtCollapse ?? .95) - this.#hysteresisThreshold) {
|
|
7345
|
+
this.collapsedB = false;
|
|
7346
|
+
this.#dispatchCollapsedEvent(`B`, false);
|
|
7347
|
+
this.#collapsedBPointerAtCollapse = void 0;
|
|
7348
|
+
const { sizeA, sizeB } = this.#applyConstraints(`${relativeX * 100}%`, `${(1 - relativeX) * 100}%`, bounds.width);
|
|
7349
|
+
this.sizeA = sizeA;
|
|
7350
|
+
this.sizeB = sizeB;
|
|
7351
|
+
}
|
|
7352
|
+
} else if (this.collapsible && !this.collapsedB && relativeX > 1 - threshold / bounds.width) {
|
|
7353
|
+
this.collapsedB = true;
|
|
7354
|
+
this.#dispatchCollapsedEvent(`B`, true);
|
|
7355
|
+
this.sizeA = `100%`;
|
|
7356
|
+
this.sizeB = `0px`;
|
|
7357
|
+
this.#collapsedBPointerAtCollapse = relativeX;
|
|
7358
|
+
}
|
|
7359
|
+
}
|
|
7360
|
+
event.preventDefault();
|
|
7361
|
+
};
|
|
7362
|
+
this.#onCornerResizeDown = (event) => {
|
|
7363
|
+
event.preventDefault();
|
|
7364
|
+
event.stopPropagation();
|
|
7365
|
+
const zoneEl = this.getZone();
|
|
7366
|
+
if (zoneEl) zoneEl.classList.add(`user-resizing`);
|
|
7367
|
+
this.#elementBounds = this.getBoundingClientRect();
|
|
7368
|
+
this.#cornerStartX = event.clientX;
|
|
7369
|
+
this.#cornerStartY = event.clientY;
|
|
7370
|
+
this.#startSizeA = this.sizeA;
|
|
7371
|
+
this.#startSizeB = this.sizeB;
|
|
7372
|
+
const nestedSplit = this.#getNestedSplit();
|
|
7373
|
+
if (nestedSplit) {
|
|
7374
|
+
this.#nestedSplitInitialSizeA = nestedSplit.sizeA;
|
|
7375
|
+
const nestedBounds = nestedSplit.getBoundingClientRect();
|
|
7376
|
+
const nestedHandle = nestedSplit.shadowRoot?.querySelector(`.split-bar`);
|
|
7377
|
+
if (nestedHandle) {
|
|
7378
|
+
this.#nestedSplitBounds = nestedBounds;
|
|
7379
|
+
this.#nestedHandleBounds = nestedHandle.getBoundingClientRect();
|
|
7380
|
+
}
|
|
7381
|
+
const nestedZoneEl = nestedSplit.getZone();
|
|
7382
|
+
if (nestedZoneEl) nestedZoneEl.classList.add(`user-resizing`);
|
|
7383
|
+
}
|
|
7384
|
+
window.addEventListener(`pointerup`, this.#onCornerResizeUp);
|
|
7385
|
+
window.addEventListener(`pointermove`, this.#onCornerResizeMove);
|
|
7386
|
+
};
|
|
7387
|
+
this.#onCornerResizeUp = (event) => {
|
|
7388
|
+
event.preventDefault();
|
|
7389
|
+
const zoneEl = this.getZone();
|
|
7390
|
+
if (zoneEl) zoneEl.classList.remove(`user-resizing`);
|
|
7391
|
+
const nestedSplit = this.#getNestedSplit();
|
|
7392
|
+
if (nestedSplit) {
|
|
7393
|
+
const nestedZoneEl = nestedSplit.getZone();
|
|
7394
|
+
if (nestedZoneEl) nestedZoneEl.classList.remove(`user-resizing`);
|
|
7395
|
+
}
|
|
7396
|
+
window.removeEventListener(`pointerup`, this.#onCornerResizeUp);
|
|
7397
|
+
window.removeEventListener(`pointermove`, this.#onCornerResizeMove);
|
|
7398
|
+
};
|
|
7399
|
+
this.#onCornerResizeMove = (event) => {
|
|
7400
|
+
const outerBounds = this.#elementBounds;
|
|
7401
|
+
const zoneEl = this.getZone();
|
|
7402
|
+
const nestedSplit = this.#getNestedSplit();
|
|
7403
|
+
if (!outerBounds || !zoneEl || !nestedSplit) return;
|
|
7404
|
+
const zoneBounds = zoneEl.getBoundingClientRect();
|
|
7405
|
+
const cursorZoneLocalX = event.clientX - zoneBounds.left;
|
|
7406
|
+
const cursorZoneLocalY = event.clientY - zoneBounds.top;
|
|
7407
|
+
if (this.layout === `vertical` && nestedSplit.layout === `horizontal`) {
|
|
7408
|
+
let newOuterFraction = cursorZoneLocalX / outerBounds.width;
|
|
7409
|
+
const newNestedFraction = cursorZoneLocalY / outerBounds.height;
|
|
7410
|
+
const { sizeA: constrainedA, sizeB: constrainedB } = this.#applyConstraints(`${newOuterFraction * 100}%`, `${(1 - newOuterFraction) * 100}%`, outerBounds.width);
|
|
7411
|
+
newOuterFraction = this.#parseSizeAsFraction(constrainedA, outerBounds.width);
|
|
7412
|
+
this.sizeA = constrainedA;
|
|
7413
|
+
this.sizeB = constrainedB;
|
|
7414
|
+
this.#pendingNestedUpdate = {
|
|
7415
|
+
sizeA: newNestedFraction,
|
|
7416
|
+
sizeB: 1 - newNestedFraction
|
|
7417
|
+
};
|
|
7418
|
+
requestAnimationFrame(() => this.#applyPendingNestedUpdate());
|
|
7419
|
+
const cornerZone = this.shadowRoot?.querySelector(`.corner-zone`);
|
|
7420
|
+
if (cornerZone) {
|
|
7421
|
+
cornerZone.style.left = `${cursorZoneLocalX - 6}px`;
|
|
7422
|
+
cornerZone.style.top = `${cursorZoneLocalY - 6}px`;
|
|
7423
|
+
}
|
|
7424
|
+
} else if (this.layout === `horizontal` && nestedSplit.layout === `vertical`) {
|
|
7425
|
+
const newNestedFraction = cursorZoneLocalX / outerBounds.width;
|
|
7426
|
+
let newOuterFraction = cursorZoneLocalY / outerBounds.height;
|
|
7427
|
+
const { sizeA: constrainedA, sizeB: constrainedB } = this.#applyConstraints(`${newOuterFraction * 100}%`, `${(1 - newOuterFraction) * 100}%`, outerBounds.height);
|
|
7428
|
+
newOuterFraction = this.#parseSizeAsFraction(constrainedA, outerBounds.height);
|
|
7429
|
+
this.sizeA = constrainedA;
|
|
7430
|
+
this.sizeB = constrainedB;
|
|
7431
|
+
this.#pendingNestedUpdate = {
|
|
7432
|
+
sizeA: newNestedFraction,
|
|
7433
|
+
sizeB: 1 - newNestedFraction
|
|
7434
|
+
};
|
|
7435
|
+
requestAnimationFrame(() => this.#applyPendingNestedUpdate());
|
|
7436
|
+
const cornerZone = this.shadowRoot?.querySelector(`.corner-zone`);
|
|
7437
|
+
if (cornerZone) {
|
|
7438
|
+
cornerZone.style.left = `${cursorZoneLocalX - 6}px`;
|
|
7439
|
+
cornerZone.style.top = `${cursorZoneLocalY - 6}px`;
|
|
7440
|
+
}
|
|
7441
|
+
}
|
|
7442
|
+
event.preventDefault();
|
|
7443
|
+
};
|
|
7444
|
+
}
|
|
7445
|
+
get showGrabHandle() {
|
|
7446
|
+
const size = getComputedStyle(this).getPropertyValue(`--handle-size`).trim();
|
|
7447
|
+
const num = Number.parseFloat(size);
|
|
7448
|
+
return !Number.isNaN(num) && num > 3;
|
|
7449
|
+
}
|
|
7450
|
+
#elementBounds;
|
|
7451
|
+
#startSizeA;
|
|
7452
|
+
#startSizeB;
|
|
7453
|
+
#cornerStartX;
|
|
7454
|
+
#cornerStartY;
|
|
7455
|
+
#nestedSplitInitialSizeA;
|
|
7456
|
+
#cornerZone;
|
|
7457
|
+
#nestedSplit;
|
|
7458
|
+
#nestedSplitBounds;
|
|
7459
|
+
#nestedHandleBounds;
|
|
7460
|
+
#observer;
|
|
7461
|
+
#pendingNestedUpdate;
|
|
7462
|
+
#collapsedAPointerAtCollapse;
|
|
7463
|
+
#collapsedBPointerAtCollapse;
|
|
7464
|
+
#hysteresisThreshold;
|
|
7465
|
+
disconnectedCallback() {
|
|
7466
|
+
super.disconnectedCallback();
|
|
7467
|
+
this.#observer?.disconnect();
|
|
7468
|
+
}
|
|
7469
|
+
firstUpdated() {
|
|
7470
|
+
this.#setupNestedSplitObserver();
|
|
7471
|
+
}
|
|
7472
|
+
updated(changedProperties) {
|
|
7473
|
+
if (changedProperties.has(`sizeA`) || changedProperties.has(`sizeB`) || changedProperties.has(`collapsedA`) || changedProperties.has(`collapsedB`)) this.#updateCornerPosition();
|
|
7474
|
+
if (changedProperties.has(`sizeA`) || changedProperties.has(`sizeB`)) this.#updateDataCollapsed();
|
|
7475
|
+
}
|
|
7476
|
+
#updateDataCollapsed() {
|
|
7477
|
+
if (this.collapsedA) this.setAttribute(`data-collapsed-a`, ``);
|
|
7478
|
+
else this.removeAttribute(`data-collapsed-a`);
|
|
7479
|
+
if (this.collapsedB) this.setAttribute(`data-collapsed-b`, ``);
|
|
7480
|
+
else this.removeAttribute(`data-collapsed-b`);
|
|
7481
|
+
}
|
|
7482
|
+
#dispatchCollapsedEvent(panel, collapsed) {
|
|
7483
|
+
this.dispatchEvent(new CustomEvent(`ixfx-split-collapsed`, {
|
|
7484
|
+
detail: {
|
|
7485
|
+
panel,
|
|
7486
|
+
collapsed
|
|
7487
|
+
},
|
|
7488
|
+
bubbles: true,
|
|
7489
|
+
composed: true
|
|
7490
|
+
}));
|
|
7491
|
+
}
|
|
7492
|
+
#setupNestedSplitObserver() {
|
|
7493
|
+
const slotA = this.shadowRoot?.querySelector(`slot[name="a"]`);
|
|
7494
|
+
if (!slotA) return;
|
|
7495
|
+
this.#observer = new MutationObserver(() => {
|
|
7496
|
+
this.#checkForNestedSplit();
|
|
7497
|
+
});
|
|
7498
|
+
this.#observer.observe(slotA, {
|
|
7499
|
+
characterData: true,
|
|
7500
|
+
childList: true,
|
|
7501
|
+
subtree: true
|
|
7502
|
+
});
|
|
7503
|
+
this.#checkForNestedSplit();
|
|
7504
|
+
}
|
|
7505
|
+
#checkForNestedSplit() {
|
|
7506
|
+
const slotA = this.shadowRoot?.querySelector(`slot[name="a"]`);
|
|
7507
|
+
if (!slotA) return;
|
|
7508
|
+
const assignedElements = slotA.assignedElements();
|
|
7509
|
+
const findNestedSplit = (elements) => {
|
|
7510
|
+
for (const el of elements) {
|
|
7511
|
+
if (el.tagName === `IXFX-SPLIT-LAYOUT`) return el;
|
|
7512
|
+
const nested = findNestedSplit(Array.from(el.children));
|
|
7513
|
+
if (nested) return nested;
|
|
7514
|
+
}
|
|
7515
|
+
};
|
|
7516
|
+
const nestedSplit = findNestedSplit(assignedElements);
|
|
7517
|
+
if (nestedSplit) {
|
|
7518
|
+
this.#nestedSplit = nestedSplit;
|
|
7519
|
+
this._hasCorner = true;
|
|
7520
|
+
requestAnimationFrame(() => this.#updateCornerPosition());
|
|
7521
|
+
} else {
|
|
7522
|
+
this.#nestedSplit = void 0;
|
|
7523
|
+
this._hasCorner = false;
|
|
7524
|
+
}
|
|
7525
|
+
}
|
|
7526
|
+
#getNestedSplit() {
|
|
7527
|
+
if (this.#nestedSplit) return this.#nestedSplit;
|
|
7528
|
+
const slotA = this.shadowRoot?.querySelector(`slot[name="a"]`);
|
|
7529
|
+
if (!slotA) return void 0;
|
|
7530
|
+
return slotA.assignedElements().find((el) => el.tagName === `IXFX-SPLIT-LAYOUT`);
|
|
7531
|
+
}
|
|
7532
|
+
#parseSizeAsFraction(size, total) {
|
|
7533
|
+
if (size.endsWith(`px`)) return Number.parseFloat(size.replace(`px`, ``)) / total;
|
|
7534
|
+
else if (size.endsWith(`%`)) return Number.parseFloat(size.replace(`%`, ``)) / 100;
|
|
7535
|
+
return .5;
|
|
7536
|
+
}
|
|
7537
|
+
#parseSizeToPx(size, total) {
|
|
7538
|
+
if (size.endsWith(`px`)) return Number.parseFloat(size.replace(`px`, ``));
|
|
7539
|
+
else if (size.endsWith(`%`)) return Number.parseFloat(size.replace(`%`, ``)) / 100 * total;
|
|
7540
|
+
return null;
|
|
7541
|
+
}
|
|
7542
|
+
#applyConstraints(sizeA, sizeB, total) {
|
|
7543
|
+
let fractionA = this.#parseSizeAsFraction(sizeA, total);
|
|
7544
|
+
let fractionB = this.#parseSizeAsFraction(sizeB, total);
|
|
7545
|
+
const minA = this.#minSizeToFraction(this.minSizeA, total);
|
|
7546
|
+
const minB = this.#minSizeToFraction(this.minSizeB, total);
|
|
7547
|
+
const maxA = this.#maxSizeToFraction(this.maxSizeA, total);
|
|
7548
|
+
const maxB = this.#maxSizeToFraction(this.maxSizeB, total);
|
|
7549
|
+
if (minA !== null || minB !== null) {
|
|
7550
|
+
const minSum = (minA ?? 0) + (minB ?? 0);
|
|
7551
|
+
if (minSum > 1) {
|
|
7552
|
+
const scale = 1 / minSum;
|
|
7553
|
+
if (minA !== null) minA !== null ? fractionA = minA * scale : fractionA = 0;
|
|
7554
|
+
if (minB !== null) minB !== null ? fractionB = minB * scale : fractionB = 0;
|
|
7555
|
+
}
|
|
7556
|
+
if (fractionA < (minA ?? 0)) fractionA = minA ?? 0;
|
|
7557
|
+
if (fractionB < (minB ?? 0)) fractionB = minB ?? 0;
|
|
7558
|
+
}
|
|
7559
|
+
if (maxA !== null) {
|
|
7560
|
+
if (fractionA > maxA) fractionA = maxA;
|
|
7561
|
+
}
|
|
7562
|
+
if (maxB !== null) {
|
|
7563
|
+
if (fractionB > maxB) fractionB = maxB;
|
|
7564
|
+
}
|
|
7565
|
+
fractionB = 1 - fractionA;
|
|
7566
|
+
const isPercentA = !this.sizeA.endsWith(`px`);
|
|
7567
|
+
this.sizeB.endsWith(`px`);
|
|
7568
|
+
if (isPercentA) return {
|
|
7569
|
+
sizeA: `${fractionA * 100}%`,
|
|
7570
|
+
sizeB: `${fractionB * 100}%`
|
|
7571
|
+
};
|
|
7572
|
+
else return {
|
|
7573
|
+
sizeA: `${fractionA * total}px`,
|
|
7574
|
+
sizeB: `${fractionB * total}px`
|
|
7575
|
+
};
|
|
7576
|
+
}
|
|
7577
|
+
#minSizeToFraction(minSize, total) {
|
|
7578
|
+
if (minSize.length === 0) return null;
|
|
7579
|
+
const px = this.#parseSizeToPx(minSize, total);
|
|
7580
|
+
if (px !== null) return px / total;
|
|
7581
|
+
if (minSize.endsWith(`%`)) return Number.parseFloat(minSize.replace(`%`, ``)) / 100;
|
|
7582
|
+
return null;
|
|
7583
|
+
}
|
|
7584
|
+
#maxSizeToFraction(maxSize, total) {
|
|
7585
|
+
if (maxSize.length === 0) return null;
|
|
7586
|
+
const px = this.#parseSizeToPx(maxSize, total);
|
|
7587
|
+
if (px !== null) return px / total;
|
|
7588
|
+
if (maxSize.endsWith(`%`)) return Number.parseFloat(maxSize.replace(`%`, ``)) / 100;
|
|
7589
|
+
return null;
|
|
7590
|
+
}
|
|
7591
|
+
#getIntersectionPoint() {
|
|
7592
|
+
const outerBounds = this.getBoundingClientRect();
|
|
7593
|
+
const zoneEl = this.getZone();
|
|
7594
|
+
if (!zoneEl) return null;
|
|
7595
|
+
const nestedSplit = this.#getNestedSplit();
|
|
7596
|
+
if (!nestedSplit) return null;
|
|
7597
|
+
zoneEl.getBoundingClientRect();
|
|
7598
|
+
const outerFraction = this.#parseSizeAsFraction(this.sizeA || `50%`, outerBounds.width);
|
|
7599
|
+
let x, y;
|
|
7600
|
+
if (this.layout === `vertical` && nestedSplit.layout === `horizontal`) {
|
|
7601
|
+
x = outerFraction * outerBounds.width;
|
|
7602
|
+
y = this.#parseSizeAsFraction(nestedSplit.sizeA || `50%`, outerBounds.height) * outerBounds.height;
|
|
7603
|
+
} else if (this.layout === `horizontal` && nestedSplit.layout === `vertical`) {
|
|
7604
|
+
x = this.#parseSizeAsFraction(nestedSplit.sizeA || `50%`, outerBounds.width) * outerBounds.width;
|
|
7605
|
+
y = outerFraction * outerBounds.height;
|
|
7606
|
+
} else return null;
|
|
7607
|
+
return {
|
|
7608
|
+
x,
|
|
7609
|
+
y
|
|
7610
|
+
};
|
|
7611
|
+
}
|
|
7612
|
+
#updateCornerPosition() {
|
|
7613
|
+
if (!this._hasCorner) return;
|
|
7614
|
+
const cornerZone = this.shadowRoot?.querySelector(`.corner-zone`);
|
|
7615
|
+
if (!cornerZone) return;
|
|
7616
|
+
if (!this.getZone()) return;
|
|
7617
|
+
const intersection = this.#getIntersectionPoint();
|
|
7618
|
+
if (!intersection) {
|
|
7619
|
+
cornerZone.style.display = `none`;
|
|
7620
|
+
return;
|
|
7621
|
+
}
|
|
7622
|
+
const cornerX = intersection.x - 6;
|
|
7623
|
+
const cornerY = intersection.y - 6;
|
|
7624
|
+
cornerZone.style.left = `${cornerX}px`;
|
|
7625
|
+
cornerZone.style.top = `${cornerY}px`;
|
|
7626
|
+
cornerZone.style.display = `block`;
|
|
7627
|
+
const nestedSplit = this.#getNestedSplit();
|
|
7628
|
+
if (nestedSplit) {
|
|
7629
|
+
if (this.layout === `vertical` && nestedSplit.layout === `horizontal`) cornerZone.style.cursor = `nwse-resize`;
|
|
7630
|
+
else if (this.layout === `horizontal` && nestedSplit.layout === `vertical`) cornerZone.style.cursor = `nesw-resize`;
|
|
7631
|
+
}
|
|
7632
|
+
}
|
|
7633
|
+
getZone() {
|
|
7634
|
+
if (!this.shadowRoot) return;
|
|
7635
|
+
const el = this.shadowRoot.querySelector(`#zone`);
|
|
7636
|
+
if (!el) return void 0;
|
|
7637
|
+
return el;
|
|
7638
|
+
}
|
|
7639
|
+
#onResizeHandleDown;
|
|
7640
|
+
#onResizeHandleUp;
|
|
7641
|
+
#onResizeHandleMove;
|
|
7642
|
+
#onCornerResizeDown;
|
|
7643
|
+
#onCornerResizeUp;
|
|
7644
|
+
#applyPendingNestedUpdate() {
|
|
7645
|
+
if (!this.#pendingNestedUpdate) return;
|
|
7646
|
+
const { sizeA, sizeB } = this.#pendingNestedUpdate;
|
|
7647
|
+
const nested = this.#getNestedSplit();
|
|
7648
|
+
if (nested) {
|
|
7649
|
+
nested.sizeA = `${sizeA * 100}%`;
|
|
7650
|
+
nested.sizeB = `${sizeB * 100}%`;
|
|
7651
|
+
}
|
|
7652
|
+
this.#pendingNestedUpdate = null;
|
|
7653
|
+
}
|
|
7654
|
+
#onCornerResizeMove;
|
|
7655
|
+
render() {
|
|
7656
|
+
const sizePrefix = this.layout === `vertical` ? `width` : `height`;
|
|
7657
|
+
let sizeA = this.sizeA;
|
|
7658
|
+
let sizeB = this.sizeB;
|
|
7659
|
+
if (this.collapsedA) sizeA = `0px`;
|
|
7660
|
+
else if (sizeA.length === 0 && sizeB.length === 0) sizeB = sizeA = `50%`;
|
|
7661
|
+
else if (sizeA.length === 0) sizeA = `auto`;
|
|
7662
|
+
else if (sizeB.length === 0) sizeB = `auto`;
|
|
7663
|
+
if (this.collapsedB) sizeB = `0px`;
|
|
7664
|
+
sizeA = `${sizePrefix}:${sizeA}`;
|
|
7665
|
+
sizeB = `${sizePrefix}:${sizeB}`;
|
|
7666
|
+
return html`
|
|
7667
|
+
<div id="zone">
|
|
7668
|
+
<slot style=${sizeA} name="a"></slot>
|
|
7669
|
+
${this._hasCorner ? html`<div class="corner-zone" @pointerdown=${this.#onCornerResizeDown}></div>` : ``}
|
|
7670
|
+
<div @pointerdown=${this.#onResizeHandleDown} class="split-bar">
|
|
7671
|
+
${this.showGrabHandle ? html`
|
|
7672
|
+
<div class="grab-handle">
|
|
7673
|
+
<span class="dot"></span>
|
|
7674
|
+
<span class="dot"></span>
|
|
7675
|
+
<span class="dot"></span>
|
|
7676
|
+
</div>
|
|
7677
|
+
` : ``}
|
|
7678
|
+
</div>
|
|
7679
|
+
<slot style=${sizeB} name="b"></slot>
|
|
7680
|
+
</div>`;
|
|
7681
|
+
}
|
|
7682
|
+
static {
|
|
7683
|
+
this.styles = [themeFallbacks, css`
|
|
7684
|
+
:host {
|
|
7685
|
+
--handle-size: 2px;
|
|
7686
|
+
--handle-colour: transparent;
|
|
7687
|
+
--handle-hover-colour: var(--surface-6);
|
|
7688
|
+
--handle-active-colour: var(--accent);
|
|
7689
|
+
--handle-bg: var(--handle-colour);
|
|
7690
|
+
--handle-grab-colour: var(--border-subtle-color);
|
|
7691
|
+
--handle-grab-dot-size: 3px;
|
|
7692
|
+
--handle-grab-size: 16px;
|
|
7693
|
+
--handle-grab-thickness: 8px;
|
|
7694
|
+
|
|
7695
|
+
display: block;
|
|
7696
|
+
box-sizing: border-box;
|
|
7697
|
+
width: 100%;
|
|
7698
|
+
height: 100%;
|
|
7699
|
+
}
|
|
7700
|
+
* {
|
|
7701
|
+
box-sizing: border-box;
|
|
7702
|
+
}
|
|
7703
|
+
#zone {
|
|
7704
|
+
display: flex;
|
|
7705
|
+
height: 100%;
|
|
7706
|
+
position: relative;
|
|
7707
|
+
}
|
|
7708
|
+
slot {
|
|
7709
|
+
display: block;
|
|
7710
|
+
overflow: hidden;
|
|
7711
|
+
border: var(--border-subtle);
|
|
7712
|
+
border-radius: var(--radius-m);
|
|
7713
|
+
}
|
|
7714
|
+
:slotted(*) {
|
|
7715
|
+
width: 100%;
|
|
7716
|
+
height: 100%;
|
|
7717
|
+
}
|
|
7718
|
+
|
|
7719
|
+
.split-bar {
|
|
7720
|
+
position: relative;
|
|
7721
|
+
background-color: var(--handle-colour);
|
|
7722
|
+
transition: background-color 0.3s ease 0s;
|
|
7723
|
+
width: var(--handle-size);
|
|
7724
|
+
height: 100%;
|
|
7725
|
+
cursor: ew-resize;
|
|
7726
|
+
flex-shrink: 0;
|
|
7727
|
+
border-radius: var(--radius-m);
|
|
7728
|
+
|
|
7729
|
+
}
|
|
7730
|
+
|
|
7731
|
+
@keyframes handle-hover {
|
|
7732
|
+
from { background-color: var(--handle-colour); }
|
|
7733
|
+
to { background-color: var(--handle-hover-colour); }
|
|
7734
|
+
}
|
|
7735
|
+
|
|
7736
|
+
.grab-handle {
|
|
7737
|
+
position: absolute;
|
|
7738
|
+
top: 50%;
|
|
7739
|
+
left: 50%;
|
|
7740
|
+
transform: translate(-50%, -50%);
|
|
7741
|
+
display: flex;
|
|
7742
|
+
align-items: center;
|
|
7743
|
+
justify-content: center;
|
|
7744
|
+
gap: var(--handle-grab-dot-size);
|
|
7745
|
+
background: var(--handle-bg);
|
|
7746
|
+
border-radius: var(--radius-s);
|
|
7747
|
+
}
|
|
7748
|
+
|
|
7749
|
+
.grab-handle .dot {
|
|
7750
|
+
width: var(--handle-grab-dot-size);
|
|
7751
|
+
height: var(--handle-grab-dot-size);
|
|
7752
|
+
border-radius: 50%;
|
|
7753
|
+
background: var(--handle-grab-colour);
|
|
7754
|
+
flex-shrink: 0;
|
|
7755
|
+
}
|
|
7756
|
+
|
|
7757
|
+
:host([layout="vertical"]) .grab-handle {
|
|
7758
|
+
flex-direction: column;
|
|
7759
|
+
width: var(--handle-grab-thickness);
|
|
7760
|
+
height: var(--handle-grab-size);
|
|
7761
|
+
}
|
|
7762
|
+
|
|
7763
|
+
:host([layout="horizontal"]) .grab-handle {
|
|
7764
|
+
flex-direction: row;
|
|
7765
|
+
width: var(--handle-grab-size);
|
|
7766
|
+
height: var(--handle-grab-thickness);
|
|
7767
|
+
}
|
|
7768
|
+
|
|
7769
|
+
#zone.user-resizing .grab-handle {
|
|
7770
|
+
display: none;
|
|
7771
|
+
}
|
|
7772
|
+
#zone.user-resizing {
|
|
7773
|
+
slot {
|
|
7774
|
+
opacity: 0.5;
|
|
7775
|
+
}
|
|
7776
|
+
.split-bar {
|
|
7777
|
+
background-color: var(--handle-active-colour);
|
|
7778
|
+
transition: none;
|
|
7779
|
+
animation: none !important;
|
|
7780
|
+
}
|
|
7781
|
+
}
|
|
7782
|
+
|
|
7783
|
+
#zone:not(.user-resizing) .split-bar:hover {
|
|
7784
|
+
animation: handle-hover 0.001s ease-out 0.7s both;
|
|
7785
|
+
}
|
|
7786
|
+
|
|
7787
|
+
.corner-zone {
|
|
7788
|
+
position: absolute;
|
|
7789
|
+
width: 12px;
|
|
7790
|
+
height: 12px;
|
|
7791
|
+
z-index: 10;
|
|
7792
|
+
border-radius: 50%;
|
|
7793
|
+
background: transparent;
|
|
7794
|
+
}
|
|
7795
|
+
.corner-zone:hover {
|
|
7796
|
+
background: transparent;
|
|
7797
|
+
}
|
|
7798
|
+
|
|
7799
|
+
:host([layout='horizontal']) {
|
|
7800
|
+
#zone {
|
|
7801
|
+
flex-direction: column;
|
|
7802
|
+
}
|
|
7803
|
+
.split-bar {
|
|
7804
|
+
position: relative;
|
|
7805
|
+
background-color: var(--handle-colour);
|
|
7806
|
+
transition: background-color 0.3s ease 0s;
|
|
7807
|
+
height: var(--handle-size);
|
|
7808
|
+
width: 100%;
|
|
7809
|
+
cursor: ns-resize;
|
|
7810
|
+
}
|
|
7811
|
+
#zone.user-resizing .split-bar {
|
|
7812
|
+
background-color: var(--handle-active-colour);
|
|
7813
|
+
transition: none;
|
|
7814
|
+
animation: none !important;
|
|
7815
|
+
}
|
|
7816
|
+
}
|
|
7817
|
+
|
|
7818
|
+
:host([data-collapsed-a]) slot[name="a"],
|
|
7819
|
+
:host([data-collapsed-b]) slot[name="b"] {
|
|
7820
|
+
display: none;
|
|
7821
|
+
}
|
|
7822
|
+
`];
|
|
7823
|
+
}
|
|
7824
|
+
};
|
|
7825
|
+
__decorate([property({ type: String })], TwoSplitLayoutElement.prototype, "layout", void 0);
|
|
7826
|
+
__decorate([property({ type: String })], TwoSplitLayoutElement.prototype, "sizeA", void 0);
|
|
7827
|
+
__decorate([property({ type: String })], TwoSplitLayoutElement.prototype, "sizeB", void 0);
|
|
7828
|
+
__decorate([property({ type: Boolean })], TwoSplitLayoutElement.prototype, "fixedA", void 0);
|
|
7829
|
+
__decorate([property({ type: Boolean })], TwoSplitLayoutElement.prototype, "fixedB", void 0);
|
|
7830
|
+
__decorate([property({ type: Boolean })], TwoSplitLayoutElement.prototype, "collapsible", void 0);
|
|
7831
|
+
__decorate([property({
|
|
7832
|
+
type: Boolean,
|
|
7833
|
+
reflect: true
|
|
7834
|
+
})], TwoSplitLayoutElement.prototype, "collapsedA", void 0);
|
|
7835
|
+
__decorate([property({
|
|
7836
|
+
type: Boolean,
|
|
7837
|
+
reflect: true
|
|
7838
|
+
})], TwoSplitLayoutElement.prototype, "collapsedB", void 0);
|
|
7839
|
+
__decorate([property({ type: String })], TwoSplitLayoutElement.prototype, "minSizeA", void 0);
|
|
7840
|
+
__decorate([property({ type: String })], TwoSplitLayoutElement.prototype, "maxSizeA", void 0);
|
|
7841
|
+
__decorate([property({ type: String })], TwoSplitLayoutElement.prototype, "minSizeB", void 0);
|
|
7842
|
+
__decorate([property({ type: String })], TwoSplitLayoutElement.prototype, "maxSizeB", void 0);
|
|
7843
|
+
__decorate([state()], TwoSplitLayoutElement.prototype, "_hasCorner", void 0);
|
|
7844
|
+
TwoSplitLayoutElement = __decorate([safeCustomElement(`ixfx-twosplit-layout`)], TwoSplitLayoutElement);
|
|
7845
|
+
//#endregion
|
|
5088
7846
|
//#region src/util/checkbox.ts
|
|
5089
7847
|
function isCheckboxLike(el) {
|
|
5090
7848
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -5142,6 +7900,6 @@ function init() {
|
|
|
5142
7900
|
console.log(`Init`);
|
|
5143
7901
|
}
|
|
5144
7902
|
//#endregion
|
|
5145
|
-
export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TimelineController, TimelineTrackTabElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, VerticalListElement, XyPad, applyAgeModification, applyHighlights, attachTimelineInteractions, buildHighlightTargets, clearHighlights, collectDataResult, computeColour, createAcCompletionProvider, createElementIncrSearch, createFuzzySearch, createIncrSearch, createPillRenderer, createTreeListAdapter, createVerticalListAdapter, getIcon, hasIcon, iconBus, init, monitorIndeterminate, parseColourScaleAttr, readBaseColour, registerIcon, resolveCssVars, resolveGrid, svgToDataUri, timelineStyles };
|
|
7903
|
+
export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, applyAgeModification, applyHighlights, applySnapshot, attachTimelineInteractions, buildHighlightTargets, buildModel, clearHighlights, collectDataResult, collectPanelIds, collectStackPaths, computeColour, createAcCompletionProvider, createElementIncrSearch, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, getActiveDragPanelId, getIcon, hasIcon, iconBus, init, insertBeside, isDragging, isSplit, isStack, monitorIndeterminate, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, readBaseColour, registerIcon, removePanelFromStack, resolveCssVars, resolveGrid, resolveTracks, serializeLayout, setActive, svgToDataUri, timelineStyles, toSnapshot, walk, wrapRoot };
|
|
5146
7904
|
|
|
5147
7905
|
//# sourceMappingURL=index.js.map
|