@ixfx/components 0.7.0 → 0.7.1
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 +235 -27
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +799 -124
- package/bundle/index.js.map +1 -1
- package/dist/ac-text.d.ts +1 -1
- package/dist/ac-text.js +1 -1
- package/dist/{ac-token-BAa6ke09.d.ts → ac-token--_vCHXWw.d.ts} +2 -2
- package/dist/{ac-token-BAa6ke09.d.ts.map → ac-token--_vCHXWw.d.ts.map} +1 -1
- package/dist/ac-token.d.ts +1 -1
- package/dist/ac-token.js +1 -1
- package/dist/{button-Dat2yHAU.js → button-DsFbV4XD.js} +3 -3
- package/dist/{button-Dat2yHAU.js.map → button-DsFbV4XD.js.map} +1 -1
- package/dist/button.d.ts.map +1 -1
- package/dist/button.js +1 -1
- package/dist/{colour-picker-BSDBL-QL.js → colour-picker-Ddsk-7E_.js} +2 -2
- package/dist/{colour-picker-BSDBL-QL.js.map → colour-picker-Ddsk-7E_.js.map} +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/crumbs.js +1 -1
- package/dist/{data-grid-C8qIo1-u.d.ts → data-grid-Bh4YrQGK.d.ts} +2 -2
- package/dist/{data-grid-C8qIo1-u.d.ts.map → data-grid-Bh4YrQGK.d.ts.map} +1 -1
- package/dist/data-grid.d.ts +1 -1
- package/dist/data-grid.js +1 -1
- package/dist/{defaults-BkWCPJFL.js → defaults-BJwDsMH4.js} +26 -2
- package/dist/defaults-BJwDsMH4.js.map +1 -0
- package/dist/{hex-editor-Bk6rxhfg.js → hex-editor-BiRfwTz1.js} +2 -2
- package/dist/{hex-editor-Bk6rxhfg.js.map → hex-editor-BiRfwTz1.js.map} +1 -1
- package/dist/hex.js +1 -1
- package/dist/{icon-DpA9x2Ve.d.ts → icon-CDGprSdE.d.ts} +1 -12
- package/dist/icon-CDGprSdE.d.ts.map +1 -0
- package/dist/{icon-CvnPanhA.js → icon-DcEEGQa_.js} +37 -8
- package/dist/icon-DcEEGQa_.js.map +1 -0
- package/dist/icons.d.ts +3 -3
- package/dist/icons.js +4 -4
- package/dist/icons.js.map +1 -1
- package/dist/{index-CKtLhgYj.d.ts → index-9AhaHIT2.d.ts} +3 -3
- package/dist/{index-CKtLhgYj.d.ts.map → index-9AhaHIT2.d.ts.map} +1 -1
- package/dist/{index-BtGFYfaK.d.ts → index-CS9yM8L-.d.ts} +14 -4
- package/dist/index-CS9yM8L-.d.ts.map +1 -0
- package/dist/index.d.ts +223 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +674 -106
- package/dist/index.js.map +1 -1
- package/dist/{menu-BjRvlgPy.js → menu-CXDFJxNm.js} +3 -3
- package/dist/{menu-BjRvlgPy.js.map → menu-CXDFJxNm.js.map} +1 -1
- package/dist/{menu-item-Bs2MYyHq.d.ts → menu-item-CZi9vy5O.d.ts} +4 -2
- package/dist/menu-item-CZi9vy5O.d.ts.map +1 -0
- package/dist/{menu-item-B-zYrTTi.js → menu-item-D2S4i8u1.js} +16 -4
- package/dist/menu-item-D2S4i8u1.js.map +1 -0
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +2 -2
- package/dist/notification-pill-DyZydXoZ.d.ts.map +1 -1
- package/dist/notifier.js +2 -2
- package/dist/notifier.js.map +1 -1
- package/dist/panel.d.ts +1 -1
- package/dist/plots.js +1 -1
- package/dist/registry-BEtb8_wB.js +46 -0
- package/dist/registry-BEtb8_wB.js.map +1 -0
- package/dist/{tab-list-MPwq_1Jj.d.ts → tab-list-CGyPt07Z.d.ts} +9 -2
- package/dist/tab-list-CGyPt07Z.d.ts.map +1 -0
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +32 -13
- package/dist/tabs.js.map +1 -1
- package/dist/{titlebar-DaCfpliv.d.ts → titlebar-Bj5pfdw_.d.ts} +2 -2
- package/dist/{titlebar-DaCfpliv.d.ts.map → titlebar-Bj5pfdw_.d.ts.map} +1 -1
- package/dist/titlebar.d.ts +1 -1
- package/dist/titlebar.js +1 -1
- package/dist/tooltip-BlvBUQxx.d.ts.map +1 -1
- package/dist/{xy-axis-CxHC8z5E.js → xy-axis-DWizs0bt.js} +3 -3
- package/dist/xy-axis-DWizs0bt.js.map +1 -0
- package/docs-user/dock.md +166 -15
- package/docs-user/icons.md +4 -0
- package/docs-user/index.json +2 -2
- package/docs-user/tabs.md +20 -0
- package/docs-user/user-theming.md +109 -0
- package/package.json +1 -1
- package/dist/defaults-BkWCPJFL.js.map +0 -1
- package/dist/icon-CvnPanhA.js.map +0 -1
- package/dist/icon-DpA9x2Ve.d.ts.map +0 -1
- package/dist/index-BtGFYfaK.d.ts.map +0 -1
- package/dist/menu-item-B-zYrTTi.js.map +0 -1
- package/dist/menu-item-Bs2MYyHq.d.ts.map +0 -1
- package/dist/registry-CkW7q09Q.js +0 -21
- package/dist/registry-CkW7q09Q.js.map +0 -1
- package/dist/tab-list-MPwq_1Jj.d.ts.map +0 -1
- package/dist/xy-axis-CxHC8z5E.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,21 +3,21 @@ import { t as TickledController } from "./tickled-controller-h9GmJ_bU.js";
|
|
|
3
3
|
import { t as themeFallbacks } from "./fallbacks-DNI9xt0F.js";
|
|
4
4
|
import { t as popupSurfaceStyles } from "./popup-D80sj2QC.js";
|
|
5
5
|
import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
|
|
6
|
-
import { a as
|
|
7
|
-
import { t as IxfxIconElement } from "./icon-
|
|
6
|
+
import { a as iconBus, c as setIconFont, i as hasIcon, l as svgToDataUri, n as getIconEntry, o as registerFontIcon, r as getIconFont, s as registerIcon, t as getIcon } from "./registry-BEtb8_wB.js";
|
|
7
|
+
import { t as IxfxIconElement } from "./icon-DcEEGQa_.js";
|
|
8
8
|
import { AcTextElement } from "./ac-text.js";
|
|
9
9
|
import { t as DataController } from "./data-controller-DOlmqNKb.js";
|
|
10
10
|
import { n as createIncrSearch, r as createFuzzySearch, t as createElementIncrSearch } from "./element-search-Y8SRfPGM.js";
|
|
11
11
|
import { n as createAcCompletionProvider, t as IncrSearchTreeController } from "./incr-search-vCzuao-2.js";
|
|
12
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-
|
|
13
|
+
import { i as ICON_CLOSE, n as ICON_CHECK, r as ICON_CHEVRON_DOWN, t as ICON_CARET_RIGHT } from "./defaults-BJwDsMH4.js";
|
|
14
14
|
import "./icons.js";
|
|
15
15
|
import { t as prominenceStyles } from "./prominence-DlFShic3.js";
|
|
16
|
-
import { n as buttonStyles, t as ButtonElement } from "./button-
|
|
16
|
+
import { n as buttonStyles, t as ButtonElement } from "./button-DsFbV4XD.js";
|
|
17
17
|
import { n as ListSelectionController, t as VerticalListElement } from "./vertical-list-ClnqTkF3.js";
|
|
18
|
-
import "./menu-item-
|
|
18
|
+
import "./menu-item-D2S4i8u1.js";
|
|
19
19
|
import { Checkbox } from "./checkbox.js";
|
|
20
|
-
import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-
|
|
20
|
+
import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-Ddsk-7E_.js";
|
|
21
21
|
import { LabelledRangeInput } from "./labelled-range-input.js";
|
|
22
22
|
import { t as collectDataResult } from "./data-provider-35TyWclX.js";
|
|
23
23
|
import { i as TreeDataModel, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-Ch4W8qXQ.js";
|
|
@@ -26,7 +26,7 @@ import { NarrowedTextElement } from "./narrowed-text.js";
|
|
|
26
26
|
import { CrumbNavigationElement } from "./crumbs.js";
|
|
27
27
|
import { DataDisplayComponent } from "./data-display.js";
|
|
28
28
|
import { t as createDragGhost } from "./drag-ghost-DtSDvjri.js";
|
|
29
|
-
import { t as menu_exports } from "./menu-
|
|
29
|
+
import { t as menu_exports } from "./menu-CXDFJxNm.js";
|
|
30
30
|
import { DataGridElement } from "./data-grid.js";
|
|
31
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
32
|
import { TabListElement } from "./tabs.js";
|
|
@@ -36,14 +36,14 @@ import { n as EditableNumber, t as LabelledSliderInput } from "./labelled-slider
|
|
|
36
36
|
import { t as FormElement } from "./form-CsXJX15p.js";
|
|
37
37
|
import { PanelElement } from "./panel.js";
|
|
38
38
|
import { GroupedItemListerElement } from "./grouped-item-lister.js";
|
|
39
|
-
import { t as IxfxHexEditorElement } from "./hex-editor-
|
|
39
|
+
import { t as IxfxHexEditorElement } from "./hex-editor-BiRfwTz1.js";
|
|
40
40
|
import { t as RadialInputElement } from "./radial-input-Dp5qz0ly.js";
|
|
41
41
|
import { LabelledRadialInput } from "./labelled-radial-input.js";
|
|
42
42
|
import { t as SliderInputElement } from "./slider-input-De4eoT11.js";
|
|
43
43
|
import { LedElement } from "./led.js";
|
|
44
44
|
import { MillerBaseElement, MillerListElement, MillerTreeController } from "./miller.js";
|
|
45
45
|
import { NotificationPillElement } from "./notifier.js";
|
|
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-
|
|
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-DWizs0bt.js";
|
|
47
47
|
import { PolarPad } from "./polar-pad.js";
|
|
48
48
|
import { t as tooltipValueStyles } from "./tooltip-CJt3WLmN.js";
|
|
49
49
|
import { RangeInput } from "./range-input.js";
|
|
@@ -2526,10 +2526,11 @@ function unregisterDockHost(host) {
|
|
|
2526
2526
|
* Begin a dock-panel drag. Called by hosts in response to their own
|
|
2527
2527
|
* `dock-panel-drag-start` events (titlebar or tab-item handles).
|
|
2528
2528
|
*/
|
|
2529
|
-
function startDrag(panelId, sourceHost, pointerId, clientX, clientY) {
|
|
2529
|
+
function startDrag(panelId, sourceHost, pointerId, clientX, clientY, kind) {
|
|
2530
2530
|
if (activeDrag !== null);
|
|
2531
2531
|
activeDrag = {
|
|
2532
2532
|
panelId,
|
|
2533
|
+
kind,
|
|
2533
2534
|
sourceHost,
|
|
2534
2535
|
pointerId,
|
|
2535
2536
|
ghost: createGhostForPanel(sourceHost, panelId),
|
|
@@ -2602,7 +2603,7 @@ function onKeyDown(event) {
|
|
|
2602
2603
|
}
|
|
2603
2604
|
function findDropZone(drag, clientX, clientY) {
|
|
2604
2605
|
for (const host of dragHosts) {
|
|
2605
|
-
const zone = host.computeDropZone(clientX, clientY, drag.panelId);
|
|
2606
|
+
const zone = host.computeDropZone(clientX, clientY, drag.panelId, drag.kind);
|
|
2606
2607
|
if (zone !== null) return zone;
|
|
2607
2608
|
}
|
|
2608
2609
|
return null;
|
|
@@ -2690,12 +2691,15 @@ function createEmptyLayout() {
|
|
|
2690
2691
|
root: createEmptyStack()
|
|
2691
2692
|
};
|
|
2692
2693
|
}
|
|
2693
|
-
function createStack(panels, active) {
|
|
2694
|
-
|
|
2694
|
+
function createStack(panels, active, name) {
|
|
2695
|
+
const stack = {
|
|
2695
2696
|
kind: `stack`,
|
|
2696
2697
|
panels: [...panels],
|
|
2697
2698
|
active: active ?? panels[0] ?? ``
|
|
2698
2699
|
};
|
|
2700
|
+
const trimmed = name?.trim();
|
|
2701
|
+
if (trimmed) stack.name = trimmed;
|
|
2702
|
+
return stack;
|
|
2699
2703
|
}
|
|
2700
2704
|
function createSplit(direction, children, sizes) {
|
|
2701
2705
|
const mean = children.length > 0 ? 1 / children.length : 1;
|
|
@@ -2709,7 +2713,8 @@ function createSplit(direction, children, sizes) {
|
|
|
2709
2713
|
/**
|
|
2710
2714
|
* Runs after every mutation:
|
|
2711
2715
|
* - stack `active` fixed to a member of `panels` (or `''` when empty)
|
|
2712
|
-
* - empty stacks removed from their parent split (and their
|
|
2716
|
+
* - empty *unnamed* stacks removed from their parent split (and their
|
|
2717
|
+
* `sizes` entry); named empty stacks are kept so the name stays addressable
|
|
2713
2718
|
* - splits with ≤1 child replaced by that child (spliced into the parent)
|
|
2714
2719
|
* - a child split whose direction equals its parent's is flattened into it,
|
|
2715
2720
|
* spreading its sizes across the slot it vacated
|
|
@@ -2718,13 +2723,14 @@ function createSplit(direction, children, sizes) {
|
|
|
2718
2723
|
*/
|
|
2719
2724
|
function normalize(root) {
|
|
2720
2725
|
if (isStack(root)) {
|
|
2726
|
+
sanitizeStackName(root);
|
|
2721
2727
|
if (!root.panels.includes(root.active)) root.active = root.panels[0] ?? ``;
|
|
2722
2728
|
return root;
|
|
2723
2729
|
}
|
|
2724
2730
|
for (let i = root.children.length - 1; i >= 0; i--) {
|
|
2725
2731
|
normalize(root.children[i]);
|
|
2726
2732
|
const child = root.children[i];
|
|
2727
|
-
if (isStack(child) && child.panels.length === 0) {
|
|
2733
|
+
if (isStack(child) && child.panels.length === 0 && !child.name) {
|
|
2728
2734
|
root.children.splice(i, 1);
|
|
2729
2735
|
root.sizes.splice(i, 1);
|
|
2730
2736
|
continue;
|
|
@@ -2758,17 +2764,36 @@ function replaceNodeContents(target, source) {
|
|
|
2758
2764
|
const t = target;
|
|
2759
2765
|
t.panels = source.panels;
|
|
2760
2766
|
t.active = source.active;
|
|
2767
|
+
if (source.name !== void 0) t.name = source.name;
|
|
2768
|
+
else delete t.name;
|
|
2769
|
+
if (source.hideLabels !== void 0) t.hideLabels = source.hideLabels;
|
|
2770
|
+
else delete t.hideLabels;
|
|
2761
2771
|
delete t.collapsed;
|
|
2762
2772
|
delete t.preCollapseSize;
|
|
2773
|
+
if (source.collapsed !== void 0) t.collapsed = source.collapsed;
|
|
2774
|
+
if (source.preCollapseSize !== void 0) t.preCollapseSize = source.preCollapseSize;
|
|
2763
2775
|
} else {
|
|
2764
2776
|
target.kind = `split`;
|
|
2765
2777
|
const t = target;
|
|
2766
2778
|
t.direction = source.direction;
|
|
2767
2779
|
t.children = source.children;
|
|
2768
2780
|
t.sizes = source.sizes;
|
|
2781
|
+
const stale = t;
|
|
2782
|
+
delete stale.panels;
|
|
2783
|
+
delete stale.active;
|
|
2784
|
+
delete stale.name;
|
|
2785
|
+
delete stale.collapsed;
|
|
2786
|
+
delete stale.preCollapseSize;
|
|
2769
2787
|
}
|
|
2770
2788
|
return target;
|
|
2771
2789
|
}
|
|
2790
|
+
/** Trim `name`; drop it when empty / non-string so lookups stay predictable. */
|
|
2791
|
+
function sanitizeStackName(stack) {
|
|
2792
|
+
if (stack.name === void 0) return;
|
|
2793
|
+
const trimmed = typeof stack.name === `string` ? stack.name.trim() : ``;
|
|
2794
|
+
if (trimmed) stack.name = trimmed;
|
|
2795
|
+
else delete stack.name;
|
|
2796
|
+
}
|
|
2772
2797
|
function findNode(root, path) {
|
|
2773
2798
|
let current = root;
|
|
2774
2799
|
for (const index of path) {
|
|
@@ -2802,6 +2827,36 @@ function collectPanelIds(root) {
|
|
|
2802
2827
|
return root.children.flatMap(collectPanelIds);
|
|
2803
2828
|
}
|
|
2804
2829
|
/**
|
|
2830
|
+
* Path of the first stack (tree order) whose `name` matches, or null.
|
|
2831
|
+
* Names are expected to be unique per layout; duplicates resolve first-wins.
|
|
2832
|
+
*/
|
|
2833
|
+
function findStackPathByName(root, name) {
|
|
2834
|
+
const wanted = name.trim();
|
|
2835
|
+
if (!wanted) return null;
|
|
2836
|
+
return findStackPathByNameInner(root, wanted, []);
|
|
2837
|
+
}
|
|
2838
|
+
function findStackPathByNameInner(node, name, path) {
|
|
2839
|
+
if (isStack(node)) return node.name === name ? path : null;
|
|
2840
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
2841
|
+
const found = findStackPathByNameInner(node.children[i], name, [...path, i]);
|
|
2842
|
+
if (found !== null) return found;
|
|
2843
|
+
}
|
|
2844
|
+
return null;
|
|
2845
|
+
}
|
|
2846
|
+
/** Names of every named stack leaf, in tree order. */
|
|
2847
|
+
function collectContainerNames(root) {
|
|
2848
|
+
const out = [];
|
|
2849
|
+
const visit = (node) => {
|
|
2850
|
+
if (isStack(node)) {
|
|
2851
|
+
if (node.name) out.push(node.name);
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
node.children.forEach(visit);
|
|
2855
|
+
};
|
|
2856
|
+
visit(root);
|
|
2857
|
+
return out;
|
|
2858
|
+
}
|
|
2859
|
+
/**
|
|
2805
2860
|
* Paths of every stack leaf, in tree order. `[]` is the root itself when it
|
|
2806
2861
|
* is a stack.
|
|
2807
2862
|
*/
|
|
@@ -2916,6 +2971,251 @@ function extractStack(root, stackPath, fallbackStack) {
|
|
|
2916
2971
|
normalize(root);
|
|
2917
2972
|
return stack;
|
|
2918
2973
|
}
|
|
2974
|
+
/** Tolerance for edge-touch comparisons in unit-square geometry. */
|
|
2975
|
+
const GEOM_EPS = 1e-9;
|
|
2976
|
+
function pathsEqual(a, b) {
|
|
2977
|
+
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
2978
|
+
}
|
|
2979
|
+
/**
|
|
2980
|
+
* Resolve a {@link DockLeafRef} to a stack path. Strings match container
|
|
2981
|
+
* `name` first-wins; arrays must index an existing stack leaf. Returns null
|
|
2982
|
+
* when unresolvable.
|
|
2983
|
+
*/
|
|
2984
|
+
function resolveLeafPath(root, ref) {
|
|
2985
|
+
if (typeof ref === `string`) return findStackPathByName(root, ref);
|
|
2986
|
+
if (!Array.isArray(ref)) return null;
|
|
2987
|
+
let current = root;
|
|
2988
|
+
for (const index of ref) {
|
|
2989
|
+
if (!Number.isInteger(index) || index < 0) return null;
|
|
2990
|
+
if (!isSplit(current)) return null;
|
|
2991
|
+
const next = current.children[index];
|
|
2992
|
+
if (!next) return null;
|
|
2993
|
+
current = next;
|
|
2994
|
+
}
|
|
2995
|
+
return isStack(current) ? [...ref] : null;
|
|
2996
|
+
}
|
|
2997
|
+
/** Absolute (unit-square) rect of every stack leaf, in tree order. */
|
|
2998
|
+
function computeLeafGeoms(root) {
|
|
2999
|
+
const out = [];
|
|
3000
|
+
const visit = (node, path, rect) => {
|
|
3001
|
+
if (isStack(node)) {
|
|
3002
|
+
out.push({
|
|
3003
|
+
path,
|
|
3004
|
+
name: node.name ?? null,
|
|
3005
|
+
rect
|
|
3006
|
+
});
|
|
3007
|
+
return;
|
|
3008
|
+
}
|
|
3009
|
+
const total = node.sizes.reduce((sum, w) => sum + w, 0);
|
|
3010
|
+
const safe = total > 0 ? total : node.children.length || 1;
|
|
3011
|
+
let offset = 0;
|
|
3012
|
+
node.children.forEach((child, i) => {
|
|
3013
|
+
const frac = (node.sizes[i] ?? safe / node.children.length) / safe;
|
|
3014
|
+
if (node.direction === `row`) {
|
|
3015
|
+
const w = rect.w * frac;
|
|
3016
|
+
visit(child, [...path, i], {
|
|
3017
|
+
x: rect.x + offset,
|
|
3018
|
+
y: rect.y,
|
|
3019
|
+
w,
|
|
3020
|
+
h: rect.h
|
|
3021
|
+
});
|
|
3022
|
+
offset += w;
|
|
3023
|
+
} else {
|
|
3024
|
+
const h = rect.h * frac;
|
|
3025
|
+
visit(child, [...path, i], {
|
|
3026
|
+
x: rect.x,
|
|
3027
|
+
y: rect.y + offset,
|
|
3028
|
+
w: rect.w,
|
|
3029
|
+
h
|
|
3030
|
+
});
|
|
3031
|
+
offset += h;
|
|
3032
|
+
}
|
|
3033
|
+
});
|
|
3034
|
+
};
|
|
3035
|
+
visit(root, [], {
|
|
3036
|
+
x: 0,
|
|
3037
|
+
y: 0,
|
|
3038
|
+
w: 1,
|
|
3039
|
+
h: 1
|
|
3040
|
+
});
|
|
3041
|
+
return out;
|
|
3042
|
+
}
|
|
3043
|
+
function intervalOverlap(a0, a1, b0, b1) {
|
|
3044
|
+
return Math.min(a1, b1) - Math.max(a0, b0);
|
|
3045
|
+
}
|
|
3046
|
+
/**
|
|
3047
|
+
* Geometric neighbours of a leaf — what the user sees above / below / left /
|
|
3048
|
+
* right of it. Each list holds `{ name, path }` (name is null for unnamed
|
|
3049
|
+
* leaves), ordered most-overlapping first. Returns null when `ref` is
|
|
3050
|
+
* unresolvable.
|
|
3051
|
+
*/
|
|
3052
|
+
function getLeafNeighbours(root, ref) {
|
|
3053
|
+
const path = resolveLeafPath(root, ref);
|
|
3054
|
+
if (path === null) return null;
|
|
3055
|
+
const geoms = computeLeafGeoms(root);
|
|
3056
|
+
const self = geoms.find((g) => pathsEqual(g.path, path));
|
|
3057
|
+
if (!self) return null;
|
|
3058
|
+
const s = self.rect;
|
|
3059
|
+
const above = [];
|
|
3060
|
+
const below = [];
|
|
3061
|
+
const left = [];
|
|
3062
|
+
const right = [];
|
|
3063
|
+
geoms.forEach((g, order) => {
|
|
3064
|
+
if (g === self) return;
|
|
3065
|
+
const r = g.rect;
|
|
3066
|
+
const info = {
|
|
3067
|
+
name: g.name,
|
|
3068
|
+
path: [...g.path]
|
|
3069
|
+
};
|
|
3070
|
+
if (Math.abs(r.y + r.h - s.y) < GEOM_EPS) {
|
|
3071
|
+
const o = intervalOverlap(r.x, r.x + r.w, s.x, s.x + s.w);
|
|
3072
|
+
if (o > GEOM_EPS) above.push({
|
|
3073
|
+
info,
|
|
3074
|
+
overlap: o,
|
|
3075
|
+
order
|
|
3076
|
+
});
|
|
3077
|
+
}
|
|
3078
|
+
if (Math.abs(s.y + s.h - r.y) < GEOM_EPS) {
|
|
3079
|
+
const o = intervalOverlap(r.x, r.x + r.w, s.x, s.x + s.w);
|
|
3080
|
+
if (o > GEOM_EPS) below.push({
|
|
3081
|
+
info,
|
|
3082
|
+
overlap: o,
|
|
3083
|
+
order
|
|
3084
|
+
});
|
|
3085
|
+
}
|
|
3086
|
+
if (Math.abs(r.x + r.w - s.x) < GEOM_EPS) {
|
|
3087
|
+
const o = intervalOverlap(r.y, r.y + r.h, s.y, s.y + s.h);
|
|
3088
|
+
if (o > GEOM_EPS) left.push({
|
|
3089
|
+
info,
|
|
3090
|
+
overlap: o,
|
|
3091
|
+
order
|
|
3092
|
+
});
|
|
3093
|
+
}
|
|
3094
|
+
if (Math.abs(s.x + s.w - r.x) < GEOM_EPS) {
|
|
3095
|
+
const o = intervalOverlap(r.y, r.y + r.h, s.y, s.y + s.h);
|
|
3096
|
+
if (o > GEOM_EPS) right.push({
|
|
3097
|
+
info,
|
|
3098
|
+
overlap: o,
|
|
3099
|
+
order
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
});
|
|
3103
|
+
const rank = (hits) => hits.sort((a, b) => b.overlap - a.overlap || a.order - b.order).map((h) => h.info);
|
|
3104
|
+
return {
|
|
3105
|
+
above: rank(above),
|
|
3106
|
+
below: rank(below),
|
|
3107
|
+
left: rank(left),
|
|
3108
|
+
right: rank(right)
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
/**
|
|
3112
|
+
* Exchange two leaves' positions in the tree, each carrying its size slot so
|
|
3113
|
+
* the remaining structure and proportions are untouched. Returns false when
|
|
3114
|
+
* either path is not a stack leaf.
|
|
3115
|
+
*/
|
|
3116
|
+
function swapLeaves(root, pathA, pathB) {
|
|
3117
|
+
if (pathsEqual(pathA, pathB)) return false;
|
|
3118
|
+
const nodeA = findNode(root, pathA);
|
|
3119
|
+
const nodeB = findNode(root, pathB);
|
|
3120
|
+
if (nodeA === null || nodeB === null || !isStack(nodeA) || !isStack(nodeB)) return false;
|
|
3121
|
+
const parentA = findParentOf(root, pathA);
|
|
3122
|
+
const parentB = findParentOf(root, pathB);
|
|
3123
|
+
if (parentA === null || parentB === null) return false;
|
|
3124
|
+
const indexA = pathA[pathA.length - 1];
|
|
3125
|
+
const indexB = pathB[pathB.length - 1];
|
|
3126
|
+
if (parentA === parentB) {
|
|
3127
|
+
const p = parentA;
|
|
3128
|
+
[p.children[indexA], p.children[indexB]] = [p.children[indexB], p.children[indexA]];
|
|
3129
|
+
[p.sizes[indexA], p.sizes[indexB]] = [p.sizes[indexB] ?? 1, p.sizes[indexA] ?? 1];
|
|
3130
|
+
return true;
|
|
3131
|
+
}
|
|
3132
|
+
const carriedNode = parentA.children[indexA];
|
|
3133
|
+
const carriedSize = parentA.sizes[indexA] ?? 1;
|
|
3134
|
+
parentA.children[indexA] = parentB.children[indexB];
|
|
3135
|
+
parentA.sizes[indexA] = parentB.sizes[indexB] ?? 1;
|
|
3136
|
+
parentB.children[indexB] = carriedNode;
|
|
3137
|
+
parentB.sizes[indexB] = carriedSize;
|
|
3138
|
+
return true;
|
|
3139
|
+
}
|
|
3140
|
+
/**
|
|
3141
|
+
* Move a leaf one position in `direction`, swapping with the neighbour it
|
|
3142
|
+
* shares the longest edge with. Returns false when `ref` is unresolvable or
|
|
3143
|
+
* nothing adjoins in that direction. Run `normalize()` afterwards.
|
|
3144
|
+
*/
|
|
3145
|
+
function moveLeafStep(root, ref, direction) {
|
|
3146
|
+
const path = resolveLeafPath(root, ref);
|
|
3147
|
+
if (path === null) return false;
|
|
3148
|
+
const neighbours = getLeafNeighbours(root, ref);
|
|
3149
|
+
if (neighbours === null) return false;
|
|
3150
|
+
const list = direction === `up` ? neighbours.above : direction === `down` ? neighbours.below : direction === `left` ? neighbours.left : neighbours.right;
|
|
3151
|
+
if (list.length === 0) return false;
|
|
3152
|
+
return swapLeaves(root, path, list[0].path);
|
|
3153
|
+
}
|
|
3154
|
+
/**
|
|
3155
|
+
* Move a leaf to an edge of the overall layout, keeping the rest of the
|
|
3156
|
+
* tree intact as a single subtree and preserving the leaf's absolute size
|
|
3157
|
+
* along the target axis. Returns false when `ref` is unresolvable or the
|
|
3158
|
+
* layout holds a single leaf. Run `normalize()` afterwards (also safe to
|
|
3159
|
+
* call it twice — the host's commit does).
|
|
3160
|
+
*/
|
|
3161
|
+
function moveLeafNodeToEdge(root, ref, edge) {
|
|
3162
|
+
const path = resolveLeafPath(root, ref);
|
|
3163
|
+
if (path === null || path.length === 0) return false;
|
|
3164
|
+
const self = computeLeafGeoms(root).find((g) => pathsEqual(g.path, path));
|
|
3165
|
+
if (!self) return false;
|
|
3166
|
+
const horizontal = edge === `left` || edge === `right`;
|
|
3167
|
+
const frac = Math.min(Math.max(horizontal ? self.rect.w : self.rect.h, .05), .95);
|
|
3168
|
+
if (!(frac > 0) || frac >= 1) return false;
|
|
3169
|
+
const parent = findParentOf(root, path);
|
|
3170
|
+
if (parent === null) return false;
|
|
3171
|
+
const [node] = parent.children.splice(path[path.length - 1], 1);
|
|
3172
|
+
parent.sizes.splice(path[path.length - 1], 1);
|
|
3173
|
+
if (!node) return false;
|
|
3174
|
+
normalize(root);
|
|
3175
|
+
const atStart = edge === `left` || edge === `top`;
|
|
3176
|
+
const direction = horizontal ? `row` : `column`;
|
|
3177
|
+
if (isStack(root)) {
|
|
3178
|
+
const rest = root;
|
|
3179
|
+
const remaining = {
|
|
3180
|
+
kind: `stack`,
|
|
3181
|
+
panels: [...rest.panels],
|
|
3182
|
+
active: rest.active
|
|
3183
|
+
};
|
|
3184
|
+
if (rest.name !== void 0) remaining.name = rest.name;
|
|
3185
|
+
if (rest.collapsed !== void 0) remaining.collapsed = rest.collapsed;
|
|
3186
|
+
if (rest.preCollapseSize !== void 0) remaining.preCollapseSize = rest.preCollapseSize;
|
|
3187
|
+
replaceNodeContents(root, {
|
|
3188
|
+
kind: `split`,
|
|
3189
|
+
direction,
|
|
3190
|
+
children: atStart ? [node, remaining] : [remaining, node],
|
|
3191
|
+
sizes: atStart ? [frac, 1 - frac] : [1 - frac, frac]
|
|
3192
|
+
});
|
|
3193
|
+
return true;
|
|
3194
|
+
}
|
|
3195
|
+
if (isSplit(root) && root.direction === direction) {
|
|
3196
|
+
const size = frac * (root.sizes.reduce((total, w) => total + w, 0) || root.children.length || 1) / (1 - frac);
|
|
3197
|
+
const at = atStart ? 0 : root.children.length;
|
|
3198
|
+
root.children.splice(at, 0, node);
|
|
3199
|
+
root.sizes.splice(at, 0, size);
|
|
3200
|
+
return true;
|
|
3201
|
+
}
|
|
3202
|
+
if (isSplit(root)) {
|
|
3203
|
+
const inner = {
|
|
3204
|
+
kind: `split`,
|
|
3205
|
+
direction: root.direction,
|
|
3206
|
+
children: root.children,
|
|
3207
|
+
sizes: root.sizes
|
|
3208
|
+
};
|
|
3209
|
+
replaceNodeContents(root, {
|
|
3210
|
+
kind: `split`,
|
|
3211
|
+
direction,
|
|
3212
|
+
children: atStart ? [node, inner] : [inner, node],
|
|
3213
|
+
sizes: atStart ? [frac, 1 - frac] : [1 - frac, frac]
|
|
3214
|
+
});
|
|
3215
|
+
return true;
|
|
3216
|
+
}
|
|
3217
|
+
return false;
|
|
3218
|
+
}
|
|
2919
3219
|
function serializeLayout(root) {
|
|
2920
3220
|
return {
|
|
2921
3221
|
version: 1,
|
|
@@ -2929,6 +3229,8 @@ function serializeNode(node) {
|
|
|
2929
3229
|
panels: [...node.panels],
|
|
2930
3230
|
active: node.active
|
|
2931
3231
|
};
|
|
3232
|
+
if (node.name !== void 0) out.name = node.name;
|
|
3233
|
+
if (node.hideLabels !== void 0) out.hideLabels = node.hideLabels;
|
|
2932
3234
|
if (node.collapsed !== void 0) out.collapsed = node.collapsed;
|
|
2933
3235
|
if (node.preCollapseSize !== void 0) out.preCollapseSize = node.preCollapseSize;
|
|
2934
3236
|
return out;
|
|
@@ -2971,9 +3273,13 @@ let TabListItemElement = class TabListItemElement extends LitElement {
|
|
|
2971
3273
|
this.registry = void 0;
|
|
2972
3274
|
this._compact = false;
|
|
2973
3275
|
this._hasToolbarContent = false;
|
|
3276
|
+
this._labelObserver = null;
|
|
2974
3277
|
this._onToolbarSlotChange = () => {
|
|
2975
3278
|
this._updateToolbarContent();
|
|
2976
3279
|
};
|
|
3280
|
+
this._onLabelSlotChange = () => {
|
|
3281
|
+
this._syncLabelSizer();
|
|
3282
|
+
};
|
|
2977
3283
|
}
|
|
2978
3284
|
connectedCallback() {
|
|
2979
3285
|
super.connectedCallback();
|
|
@@ -2982,6 +3288,13 @@ let TabListItemElement = class TabListItemElement extends LitElement {
|
|
|
2982
3288
|
});
|
|
2983
3289
|
this._resizeObserver.observe(this);
|
|
2984
3290
|
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.addEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
3291
|
+
(this.shadowRoot?.querySelector(`slot:not([name])`))?.addEventListener(`slotchange`, this._onLabelSlotChange);
|
|
3292
|
+
this._labelObserver = new MutationObserver(() => this._syncLabelSizer());
|
|
3293
|
+
this._labelObserver.observe(this, {
|
|
3294
|
+
childList: true,
|
|
3295
|
+
characterData: true,
|
|
3296
|
+
subtree: true
|
|
3297
|
+
});
|
|
2985
3298
|
}
|
|
2986
3299
|
disconnectedCallback() {
|
|
2987
3300
|
super.disconnectedCallback();
|
|
@@ -2989,7 +3302,10 @@ let TabListItemElement = class TabListItemElement extends LitElement {
|
|
|
2989
3302
|
this._resizeObserver = void 0;
|
|
2990
3303
|
this._commandCleanup?.();
|
|
2991
3304
|
this._commandCleanup = void 0;
|
|
3305
|
+
this._labelObserver?.disconnect();
|
|
3306
|
+
this._labelObserver = null;
|
|
2992
3307
|
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.removeEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
3308
|
+
(this.shadowRoot?.querySelector(`slot:not([name])`))?.removeEventListener(`slotchange`, this._onLabelSlotChange);
|
|
2993
3309
|
}
|
|
2994
3310
|
updated(changed) {
|
|
2995
3311
|
super.updated(changed);
|
|
@@ -3012,12 +3328,25 @@ let TabListItemElement = class TabListItemElement extends LitElement {
|
|
|
3012
3328
|
if (id === this.commandActive) this.click();
|
|
3013
3329
|
});
|
|
3014
3330
|
}
|
|
3331
|
+
/**
|
|
3332
|
+
* Mirror the default-slot text into `.label[data-label]` for the `::after`
|
|
3333
|
+
* width sizer. Slotted toolbar content is excluded.
|
|
3334
|
+
*/
|
|
3335
|
+
_syncLabelSizer() {
|
|
3336
|
+
const slot = this.shadowRoot?.querySelector(`slot:not([name])`);
|
|
3337
|
+
const label = this.shadowRoot?.querySelector(`.label`);
|
|
3338
|
+
if (slot === null || slot === void 0 || label === null || label === void 0) return;
|
|
3339
|
+
const text = slot.assignedNodes().filter((node) => !(node instanceof HTMLElement && node.hasAttribute(`slot`))).map((node) => node.textContent ?? ``).join(``);
|
|
3340
|
+
if (text) label.setAttribute(`data-label`, text);
|
|
3341
|
+
else label.removeAttribute(`data-label`);
|
|
3342
|
+
}
|
|
3015
3343
|
_updateToolbarContent() {
|
|
3016
3344
|
const slot = this.shadowRoot?.querySelector(`slot[name="toolbar"]`);
|
|
3017
3345
|
this._hasToolbarContent = (slot?.assignedElements().length ?? 0) > 0;
|
|
3018
3346
|
}
|
|
3019
3347
|
firstUpdated() {
|
|
3020
3348
|
this._updateToolbarContent();
|
|
3349
|
+
this._syncLabelSizer();
|
|
3021
3350
|
}
|
|
3022
3351
|
render() {
|
|
3023
3352
|
const showLabel = !this.hideLabels || !this.iconName;
|
|
@@ -3059,15 +3388,21 @@ let TabListItemElement = class TabListItemElement extends LitElement {
|
|
|
3059
3388
|
css`
|
|
3060
3389
|
:host {
|
|
3061
3390
|
--selected-tab-color: var(--ixfx-selected-tab-color, var(--surface-2-text));
|
|
3391
|
+
--selected-tab-icon-color: var(--ixfx-selected-tab-icon-color, var(--accent));
|
|
3062
3392
|
--selected-tab-border-width: var(--ixfx-selected-tab-border-width, 1px);
|
|
3063
3393
|
--selected-tab-weight: var(--ixfx-selected-tab-weight, bold);
|
|
3394
|
+
--selected-tab-text-decoration: var(--ixfx-selected-tab-text-decoration, none);
|
|
3395
|
+
--selected-tab-text-decoration-color: var(--ixfx-selected-tab-text-decoration-color, var(--accent));
|
|
3396
|
+
--selected-tab-text-decoration-thickness: var(--ixfx-selected-tab-text-decoration-thickness, 0px);
|
|
3397
|
+
--selected-tab-text-decoration-offset: var(--ixfx-selected-tab-text-decoration-offset, 2px);
|
|
3064
3398
|
--selected-background: var(--ixfx-tab-selected-background, var(--surface-5));
|
|
3065
3399
|
--tab-padding: var(--ixfx-tab-padding, var(--space-s));
|
|
3066
3400
|
display: inline-flex;
|
|
3067
3401
|
min-width: 0;
|
|
3068
3402
|
user-select: none;
|
|
3069
3403
|
box-sizing: border-box;
|
|
3070
|
-
font-size: var(--text-
|
|
3404
|
+
font-size: var(--text-m);
|
|
3405
|
+
color: inherit;
|
|
3071
3406
|
/* No explicit height: items stretch (align-self: stretch via the
|
|
3072
3407
|
list's flex container) so every tab in a strip shares one height
|
|
3073
3408
|
even when toolbars give some items taller content. */
|
|
@@ -3082,7 +3417,18 @@ let TabListItemElement = class TabListItemElement extends LitElement {
|
|
|
3082
3417
|
font-weight: 500;
|
|
3083
3418
|
border: var(--selected-tab-border-width) solid transparent;
|
|
3084
3419
|
gap: var(--space-s);
|
|
3420
|
+
transition: background var(--transition, 0.15s);
|
|
3421
|
+
}
|
|
3422
|
+
|
|
3423
|
+
/* Hover ("tickle") background. When --ixfx-tab-hover-background is
|
|
3424
|
+
unset each state keeps its resting background, so existing
|
|
3425
|
+
consumers see no change. */
|
|
3426
|
+
:host(:hover:not([selected])) > div {
|
|
3427
|
+
background: var(--ixfx-tab-hover-background, transparent);
|
|
3428
|
+
}
|
|
3085
3429
|
|
|
3430
|
+
:host([selected]:hover) > div {
|
|
3431
|
+
background: var(--ixfx-tab-hover-background, var(--selected-background));
|
|
3086
3432
|
}
|
|
3087
3433
|
|
|
3088
3434
|
:host([selected]) > div {
|
|
@@ -3096,6 +3442,13 @@ let TabListItemElement = class TabListItemElement extends LitElement {
|
|
|
3096
3442
|
border-radius:var(--radius-m);
|
|
3097
3443
|
}
|
|
3098
3444
|
|
|
3445
|
+
:host([selected]) .label {
|
|
3446
|
+
text-decoration: var(--selected-tab-text-decoration);
|
|
3447
|
+
text-decoration-color: var(--selected-tab-text-decoration-color);
|
|
3448
|
+
text-decoration-thickness: var(--selected-tab-text-decoration-thickness);
|
|
3449
|
+
text-decoration-offset: var(--selected-tab-text-decoration-offset);
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3099
3452
|
.label {
|
|
3100
3453
|
overflow: var(--allow-overflow, hidden);
|
|
3101
3454
|
text-overflow: ellipsis;
|
|
@@ -3104,10 +3457,29 @@ let TabListItemElement = class TabListItemElement extends LitElement {
|
|
|
3104
3457
|
flex: 1 1 auto;
|
|
3105
3458
|
}
|
|
3106
3459
|
|
|
3460
|
+
/* Width sizer: an invisible copy of the label, always set at the
|
|
3461
|
+
selected weight, so activating a tab (bold) never shifts the strip.
|
|
3462
|
+
Text is mirrored from the default slot via the data-label attribute
|
|
3463
|
+
(see _syncLabelSizer). */
|
|
3464
|
+
.label::after {
|
|
3465
|
+
content: attr(data-label);
|
|
3466
|
+
display: block;
|
|
3467
|
+
height: 0;
|
|
3468
|
+
overflow: hidden;
|
|
3469
|
+
visibility: hidden;
|
|
3470
|
+
font-weight: var(--selected-tab-weight);
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3107
3473
|
ixfx-icon {
|
|
3108
3474
|
flex-shrink: 0;
|
|
3109
3475
|
}
|
|
3110
3476
|
|
|
3477
|
+
/* Leading icon (direct child — not close/toolbar icons) picks up the
|
|
3478
|
+
accent when selected, so icon-only tabs read as active. */
|
|
3479
|
+
:host([selected]) > div > ixfx-icon {
|
|
3480
|
+
color: var(--selected-tab-icon-color);
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3111
3483
|
.actions {
|
|
3112
3484
|
display: flex;
|
|
3113
3485
|
align-items: center;
|
|
@@ -3203,6 +3575,7 @@ let DockPanelElement = class DockPanelElement extends LitElement {
|
|
|
3203
3575
|
this.iconName = ``;
|
|
3204
3576
|
this.closable = false;
|
|
3205
3577
|
this.collapsible = false;
|
|
3578
|
+
this.kind = `any`;
|
|
3206
3579
|
this.accepts = ``;
|
|
3207
3580
|
this.presentation = `solo`;
|
|
3208
3581
|
this._collapsed = false;
|
|
@@ -3256,6 +3629,10 @@ let DockPanelElement = class DockPanelElement extends LitElement {
|
|
|
3256
3629
|
}
|
|
3257
3630
|
connectedCallback() {
|
|
3258
3631
|
super.connectedCallback();
|
|
3632
|
+
if (this.hasAttribute(`title`)) {
|
|
3633
|
+
if (!this.hasAttribute(`label`)) this.title = this.getAttribute(`title`) ?? ``;
|
|
3634
|
+
this.removeAttribute(`title`);
|
|
3635
|
+
}
|
|
3259
3636
|
this.addEventListener(`pointerdown`, this._onPointerDown);
|
|
3260
3637
|
}
|
|
3261
3638
|
disconnectedCallback() {
|
|
@@ -3347,13 +3724,20 @@ __decorate([property({
|
|
|
3347
3724
|
attribute: `panel-id`,
|
|
3348
3725
|
reflect: true
|
|
3349
3726
|
})], DockPanelElement.prototype, "panelId", void 0);
|
|
3350
|
-
__decorate([property({
|
|
3727
|
+
__decorate([property({
|
|
3728
|
+
type: String,
|
|
3729
|
+
attribute: `label`
|
|
3730
|
+
})], DockPanelElement.prototype, "title", void 0);
|
|
3351
3731
|
__decorate([property({
|
|
3352
3732
|
type: String,
|
|
3353
3733
|
attribute: `icon-name`
|
|
3354
3734
|
})], DockPanelElement.prototype, "iconName", void 0);
|
|
3355
3735
|
__decorate([property({ type: Boolean })], DockPanelElement.prototype, "closable", void 0);
|
|
3356
3736
|
__decorate([property({ type: Boolean })], DockPanelElement.prototype, "collapsible", void 0);
|
|
3737
|
+
__decorate([property({
|
|
3738
|
+
type: String,
|
|
3739
|
+
reflect: true
|
|
3740
|
+
})], DockPanelElement.prototype, "kind", void 0);
|
|
3357
3741
|
__decorate([property({
|
|
3358
3742
|
type: String,
|
|
3359
3743
|
attribute: `accepts`
|
|
@@ -3385,13 +3769,16 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3385
3769
|
this._dragActive = false;
|
|
3386
3770
|
this._junctions = [];
|
|
3387
3771
|
this._toolbarsOut = /* @__PURE__ */ new Map();
|
|
3772
|
+
this._leafContentRules = /* @__PURE__ */ new Map();
|
|
3388
3773
|
this._tabDragPending = null;
|
|
3389
3774
|
this._junctionsKey = ``;
|
|
3390
3775
|
this._junctionsRaf = null;
|
|
3391
3776
|
this._hostResizeObserver = null;
|
|
3777
|
+
this._toolbarSlots = /* @__PURE__ */ new Set();
|
|
3778
|
+
this._toolbarObserver = null;
|
|
3392
3779
|
this._onDragStart = (event) => {
|
|
3393
3780
|
const { panelId, pointerId, clientX, clientY } = event.detail;
|
|
3394
|
-
startDrag(panelId, this, pointerId, clientX, clientY);
|
|
3781
|
+
startDrag(panelId, this, pointerId, clientX, clientY, this._findPanelEl(panelId)?.kind);
|
|
3395
3782
|
};
|
|
3396
3783
|
this._onPoolSlotChange = () => {
|
|
3397
3784
|
this._syncPool();
|
|
@@ -3438,11 +3825,12 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3438
3825
|
const pathStr = this.shadowRoot?.getElementById(detail.id)?.getAttribute(`data-dock-path`);
|
|
3439
3826
|
if (pathStr === null || pathStr === void 0) return;
|
|
3440
3827
|
const path = parsePath(pathStr);
|
|
3441
|
-
const
|
|
3442
|
-
|
|
3443
|
-
const
|
|
3828
|
+
const closing = findNode(this._model, path);
|
|
3829
|
+
if (closing === null || !isStack(closing)) return;
|
|
3830
|
+
const moving = [...closing.panels];
|
|
3444
3831
|
this._returnAllToolbars();
|
|
3445
|
-
extractStack(this._model, path
|
|
3832
|
+
extractStack(this._model, path);
|
|
3833
|
+
for (const panelId of moving) addPanelToStack(this._firstStackAllowing(this._panelKind(panelId)), panelId);
|
|
3446
3834
|
this._commit();
|
|
3447
3835
|
};
|
|
3448
3836
|
this._onTabChange = (event, path) => {
|
|
@@ -3507,11 +3895,21 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3507
3895
|
this.addEventListener(`dock-panel-drag-start`, this._onDragStart);
|
|
3508
3896
|
this._hostResizeObserver = new ResizeObserver(() => this._scheduleJunctions());
|
|
3509
3897
|
this._hostResizeObserver.observe(this);
|
|
3898
|
+
this._scanToolbarSlots();
|
|
3899
|
+
this._toolbarObserver = new MutationObserver(() => this._scanToolbarSlots());
|
|
3900
|
+
this._toolbarObserver.observe(this, {
|
|
3901
|
+
childList: true,
|
|
3902
|
+
subtree: true,
|
|
3903
|
+
attributes: true,
|
|
3904
|
+
attributeFilter: [`slot`]
|
|
3905
|
+
});
|
|
3510
3906
|
}
|
|
3511
3907
|
disconnectedCallback() {
|
|
3512
3908
|
super.disconnectedCallback();
|
|
3513
3909
|
unregisterDockHost(this);
|
|
3514
3910
|
this.removeEventListener(`dock-panel-drag-start`, this._onDragStart);
|
|
3911
|
+
this._toolbarObserver?.disconnect();
|
|
3912
|
+
this._toolbarObserver = null;
|
|
3515
3913
|
this._hostResizeObserver?.disconnect();
|
|
3516
3914
|
this._hostResizeObserver = null;
|
|
3517
3915
|
if (this._junctionsRaf !== null) {
|
|
@@ -3564,6 +3962,19 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3564
3962
|
_scanPanels() {
|
|
3565
3963
|
return Array.from(this.children).filter((el) => el instanceof HTMLElement && el.tagName === `IXFX-DOCK-PANEL` && Boolean(el.panelId));
|
|
3566
3964
|
}
|
|
3965
|
+
/** Refresh the set of provided `toolbar-<name>` slots; re-render on change. */
|
|
3966
|
+
_scanToolbarSlots() {
|
|
3967
|
+
const found = /* @__PURE__ */ new Set();
|
|
3968
|
+
for (const child of this.children) {
|
|
3969
|
+
if (!(child instanceof HTMLElement)) continue;
|
|
3970
|
+
const slot = child.getAttribute(`slot`) ?? ``;
|
|
3971
|
+
if (slot.startsWith(`toolbar-`) && slot.length > `toolbar-`.length) found.add(slot);
|
|
3972
|
+
}
|
|
3973
|
+
if (found.size !== this._toolbarSlots.size || [...found].some((s) => !this._toolbarSlots.has(s))) {
|
|
3974
|
+
this._toolbarSlots = found;
|
|
3975
|
+
this.requestUpdate();
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3567
3978
|
_findPanelEl(panelId) {
|
|
3568
3979
|
return this._pool.find((p) => p.panelId === panelId) ?? this._scanPanels().find((p) => p.panelId === panelId);
|
|
3569
3980
|
}
|
|
@@ -3577,7 +3988,33 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3577
3988
|
return fresh;
|
|
3578
3989
|
}
|
|
3579
3990
|
_appendPanelToFirstStack(panelId) {
|
|
3580
|
-
|
|
3991
|
+
const kind = this._panelKind(panelId);
|
|
3992
|
+
if (this._model.kind === `stack` && this._stackAllowsKind(this._model, kind)) {
|
|
3993
|
+
addPanelToStack(this._model, panelId);
|
|
3994
|
+
return;
|
|
3995
|
+
}
|
|
3996
|
+
addPanelToStack(this._firstStackAllowing(kind), panelId);
|
|
3997
|
+
}
|
|
3998
|
+
/** Normalized content kind of a panel (`'any'` when unknown/unset). */
|
|
3999
|
+
_panelKind(panelId) {
|
|
4000
|
+
return normalizeKind(this._findPanelEl(panelId)?.kind);
|
|
4001
|
+
}
|
|
4002
|
+
/** Whether `kind` may live in `stack` under the current leaf rules. */
|
|
4003
|
+
_stackAllowsKind(stack, kind) {
|
|
4004
|
+
if (stack.name === void 0) return true;
|
|
4005
|
+
const rules = this._leafContentRules.get(stack.name);
|
|
4006
|
+
if (rules === void 0) return true;
|
|
4007
|
+
if (rules.deny.has(kind)) return false;
|
|
4008
|
+
return rules.allow === null || rules.allow.has(kind);
|
|
4009
|
+
}
|
|
4010
|
+
/** First stack (tree order) whose rules admit `kind`, else the first stack. */
|
|
4011
|
+
_firstStackAllowing(kind) {
|
|
4012
|
+
for (const path of collectStackPaths(this._model)) {
|
|
4013
|
+
const node = findNode(this._model, path);
|
|
4014
|
+
if (node !== null && isStack(node) && this._stackAllowsKind(node, kind)) return node;
|
|
4015
|
+
}
|
|
4016
|
+
console.warn(`[ixfx-dock-host] no leaf admits kind "${kind}", using first stack`);
|
|
4017
|
+
return this._firstStack();
|
|
3581
4018
|
}
|
|
3582
4019
|
_syncPool() {
|
|
3583
4020
|
const current = this._scanPanels();
|
|
@@ -3619,7 +4056,34 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3619
4056
|
this.requestUpdate();
|
|
3620
4057
|
this._emitChange();
|
|
3621
4058
|
}
|
|
4059
|
+
/**
|
|
4060
|
+
* Dock a panel. Returns false (without touching the DOM) when the
|
|
4061
|
+
* explicitly targeted container denies the panel's `kind`.
|
|
4062
|
+
*/
|
|
3622
4063
|
addPanel(panel, target) {
|
|
4064
|
+
const kind = panel instanceof HTMLElement ? normalizeKind(panel.kind) : normalizeKind(panel.kind);
|
|
4065
|
+
const op = target?.op ?? `tabs`;
|
|
4066
|
+
let stackPath = null;
|
|
4067
|
+
let splitTarget = null;
|
|
4068
|
+
if (target?.container !== void 0) {
|
|
4069
|
+
const namedPath = findStackPathByName(this._model, target.container);
|
|
4070
|
+
if (namedPath === null) console.warn(`[ixfx-dock-host] addPanel: unknown container "${target.container}", appending to first admitting stack`);
|
|
4071
|
+
else if (op === `tabs` && !this._stackAllowsKindAt(namedPath, kind)) {
|
|
4072
|
+
console.warn(`[ixfx-dock-host] addPanel: container "${target.container}" does not admit kind "${kind}"`);
|
|
4073
|
+
return false;
|
|
4074
|
+
} else stackPath = namedPath;
|
|
4075
|
+
} else if (target?.stack) {
|
|
4076
|
+
const found = findStackOf(this._model, target.stack);
|
|
4077
|
+
if (found === null) console.warn(`[ixfx-dock-host] addPanel: unknown stack panel "${target.stack}", appending to first admitting stack`);
|
|
4078
|
+
else if (op === `tabs` && !this._stackAllowsKindAt(found, kind)) {
|
|
4079
|
+
console.warn(`[ixfx-dock-host] addPanel: target stack does not admit kind "${kind}"`);
|
|
4080
|
+
return false;
|
|
4081
|
+
} else stackPath = found;
|
|
4082
|
+
}
|
|
4083
|
+
if (stackPath !== null && op !== `tabs`) {
|
|
4084
|
+
splitTarget = stackPath;
|
|
4085
|
+
stackPath = null;
|
|
4086
|
+
}
|
|
3623
4087
|
let el;
|
|
3624
4088
|
if (panel instanceof HTMLElement) el = panel;
|
|
3625
4089
|
else {
|
|
@@ -3629,19 +4093,16 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3629
4093
|
if (panel.iconName !== void 0) el.iconName = panel.iconName;
|
|
3630
4094
|
if (panel.closable !== void 0) el.closable = panel.closable;
|
|
3631
4095
|
if (panel.collapsible !== void 0) el.collapsible = panel.collapsible;
|
|
4096
|
+
if (panel.kind !== void 0) el.kind = panel.kind;
|
|
3632
4097
|
}
|
|
3633
4098
|
if (el.parentElement !== this) this.appendChild(el);
|
|
3634
4099
|
if (!this._pool.some((p) => p.panelId === el.panelId)) this._pool = [...this._pool, el];
|
|
3635
4100
|
this._returnAllToolbars();
|
|
3636
|
-
|
|
3637
|
-
if (
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
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
|
-
}
|
|
4101
|
+
if (collectPanelIds(this._model).includes(el.panelId)) this._removeFromModel(el.panelId);
|
|
4102
|
+
if (splitTarget !== null) insertBeside(this._model, splitTarget, opToDirection(op), createStack([el.panelId]), `after`);
|
|
4103
|
+
else if (stackPath !== null) {
|
|
4104
|
+
const stack = findNode(this._model, stackPath);
|
|
4105
|
+
if (stack !== null && isStack(stack)) addPanelToStack(stack, el.panelId, target?.index);
|
|
3645
4106
|
} else this._appendPanelToFirstStack(el.panelId);
|
|
3646
4107
|
normalize(this._model);
|
|
3647
4108
|
this.requestUpdate();
|
|
@@ -3651,6 +4112,12 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3651
4112
|
composed: true
|
|
3652
4113
|
}));
|
|
3653
4114
|
this._emitChange();
|
|
4115
|
+
return true;
|
|
4116
|
+
}
|
|
4117
|
+
/** Whether the stack at `stackPath` admits `kind` under the leaf rules. */
|
|
4118
|
+
_stackAllowsKindAt(stackPath, kind) {
|
|
4119
|
+
const stack = findNode(this._model, stackPath);
|
|
4120
|
+
return stack !== null && isStack(stack) && this._stackAllowsKind(stack, kind);
|
|
3654
4121
|
}
|
|
3655
4122
|
removePanel(panelId) {
|
|
3656
4123
|
if (!collectPanelIds(this._model).includes(panelId)) return false;
|
|
@@ -3686,6 +4153,123 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3686
4153
|
getPanelIds() {
|
|
3687
4154
|
return collectPanelIds(this._model);
|
|
3688
4155
|
}
|
|
4156
|
+
/** Names of every named stack leaf, in tree order. */
|
|
4157
|
+
getContainerNames() {
|
|
4158
|
+
return collectContainerNames(this._model);
|
|
4159
|
+
}
|
|
4160
|
+
/**
|
|
4161
|
+
* Snapshot of a named container (`{ name, panels, active }`), or null when
|
|
4162
|
+
* no stack carries that name. First match wins when duplicated.
|
|
4163
|
+
*/
|
|
4164
|
+
getContainer(name) {
|
|
4165
|
+
const path = findStackPathByName(this._model, name);
|
|
4166
|
+
if (path === null) return null;
|
|
4167
|
+
const node = findNode(this._model, path);
|
|
4168
|
+
if (node === null || !isStack(node) || node.name === void 0) return null;
|
|
4169
|
+
return {
|
|
4170
|
+
name: node.name,
|
|
4171
|
+
panels: [...node.panels],
|
|
4172
|
+
active: node.active
|
|
4173
|
+
};
|
|
4174
|
+
}
|
|
4175
|
+
/** Panel-ids in a named container, in tab order (`[]` when unknown/empty). */
|
|
4176
|
+
getContainerPanels(name) {
|
|
4177
|
+
return this.getContainer(name)?.panels ?? [];
|
|
4178
|
+
}
|
|
4179
|
+
/** Panel elements in a named container, in tab order — for iterating children. */
|
|
4180
|
+
getContainerElements(name) {
|
|
4181
|
+
return this.getContainerPanels(name).map((id) => this._findPanelEl(id)).filter((el) => el !== void 0);
|
|
4182
|
+
}
|
|
4183
|
+
/**
|
|
4184
|
+
* Restrict a named container to only the given panel kinds — e.g.
|
|
4185
|
+
* `setLeafExclusiveContents('editor', ['document'])`. `tabs` drops,
|
|
4186
|
+
* `addPanel`, and re-homing into the leaf then refuse other kinds, while
|
|
4187
|
+
* edge splits beside the leaf stay allowed (they create a new leaf).
|
|
4188
|
+
* Pass `[]` to seal the leaf (nothing admitted). Warns when no stack
|
|
4189
|
+
* currently carries `name`, but the rule is still stored and applies once
|
|
4190
|
+
* one appears. Rules are host policy: kept across `loadLayout()`, never
|
|
4191
|
+
* serialized.
|
|
4192
|
+
*/
|
|
4193
|
+
setLeafExclusiveContents(name, kinds) {
|
|
4194
|
+
const key = name.trim();
|
|
4195
|
+
if (!key) {
|
|
4196
|
+
console.warn(`[ixfx-dock-host] setLeafExclusiveContents: empty container name`);
|
|
4197
|
+
return;
|
|
4198
|
+
}
|
|
4199
|
+
const allow = new Set(kinds.map((k) => k.trim()).filter((k) => k.length > 0));
|
|
4200
|
+
const entry = this._leafContentRules.get(key) ?? {
|
|
4201
|
+
allow: null,
|
|
4202
|
+
deny: /* @__PURE__ */ new Set()
|
|
4203
|
+
};
|
|
4204
|
+
entry.allow = allow;
|
|
4205
|
+
this._leafContentRules.set(key, entry);
|
|
4206
|
+
if (findStackPathByName(this._model, key) === null) console.warn(`[ixfx-dock-host] setLeafExclusiveContents: no container named "${key}" yet; rule stored`);
|
|
4207
|
+
}
|
|
4208
|
+
/**
|
|
4209
|
+
* Forbid the given panel kinds in a named container — e.g.
|
|
4210
|
+
* `setLeafDenyContents('footer', ['files', 'document'])`. Everything else
|
|
4211
|
+
* stays admitted. Pass `[]` to lift the denial. See
|
|
4212
|
+
* {@link setLeafExclusiveContents} for scope/ordering notes.
|
|
4213
|
+
*/
|
|
4214
|
+
setLeafDenyContents(name, kinds) {
|
|
4215
|
+
const key = name.trim();
|
|
4216
|
+
if (!key) {
|
|
4217
|
+
console.warn(`[ixfx-dock-host] setLeafDenyContents: empty container name`);
|
|
4218
|
+
return;
|
|
4219
|
+
}
|
|
4220
|
+
const deny = new Set(kinds.map((k) => k.trim()).filter((k) => k.length > 0));
|
|
4221
|
+
const entry = this._leafContentRules.get(key) ?? {
|
|
4222
|
+
allow: null,
|
|
4223
|
+
deny: /* @__PURE__ */ new Set()
|
|
4224
|
+
};
|
|
4225
|
+
entry.deny = deny;
|
|
4226
|
+
if (entry.allow === null && entry.deny.size === 0) this._leafContentRules.delete(key);
|
|
4227
|
+
else this._leafContentRules.set(key, entry);
|
|
4228
|
+
if (findStackPathByName(this._model, key) === null) console.warn(`[ixfx-dock-host] setLeafDenyContents: no container named "${key}" yet; rule stored`);
|
|
4229
|
+
}
|
|
4230
|
+
/** Remove every content rule for a named container. */
|
|
4231
|
+
clearLeafContentRestrictions(name) {
|
|
4232
|
+
this._leafContentRules.delete(name.trim());
|
|
4233
|
+
}
|
|
4234
|
+
/** Copy of a container's rules, or null when unrestricted. */
|
|
4235
|
+
getLeafContentRestrictions(name) {
|
|
4236
|
+
const rules = this._leafContentRules.get(name.trim());
|
|
4237
|
+
if (rules === void 0) return null;
|
|
4238
|
+
return {
|
|
4239
|
+
allow: rules.allow === null ? null : [...rules.allow],
|
|
4240
|
+
deny: [...rules.deny]
|
|
4241
|
+
};
|
|
4242
|
+
}
|
|
4243
|
+
/**
|
|
4244
|
+
* Geometric neighbours of a leaf (`name` or numeric `path`) — what sits
|
|
4245
|
+
* above / below / left / right of it on screen. Each entry is
|
|
4246
|
+
* `{ name, path }` (`name` is null for unnamed leaves), ordered
|
|
4247
|
+
* most-overlapping first. Returns null when the leaf is unknown.
|
|
4248
|
+
*/
|
|
4249
|
+
getNeighbours(leaf) {
|
|
4250
|
+
return getLeafNeighbours(this._model, leaf);
|
|
4251
|
+
}
|
|
4252
|
+
/**
|
|
4253
|
+
* Move a leaf one position in `direction`, swapping with the neighbour it
|
|
4254
|
+
* shares the longest edge with (both keep the remaining structure intact).
|
|
4255
|
+
* Returns false when the leaf is unknown or nothing adjoins that way.
|
|
4256
|
+
*/
|
|
4257
|
+
moveLeaf(leaf, direction) {
|
|
4258
|
+
if (!moveLeafStep(this._model, leaf, direction)) return false;
|
|
4259
|
+
this._commit();
|
|
4260
|
+
return true;
|
|
4261
|
+
}
|
|
4262
|
+
/**
|
|
4263
|
+
* Move a leaf to an edge of the overall layout (e.g. toggle a sidebar from
|
|
4264
|
+
* `left` to `right`). The rest of the layout stays intact as a single
|
|
4265
|
+
* subtree and the leaf keeps its absolute size along the target axis.
|
|
4266
|
+
* Returns false when the leaf is unknown or the layout holds one leaf.
|
|
4267
|
+
*/
|
|
4268
|
+
moveLeafToEdge(leaf, edge) {
|
|
4269
|
+
if (!moveLeafNodeToEdge(this._model, leaf, edge)) return false;
|
|
4270
|
+
this._commit();
|
|
4271
|
+
return true;
|
|
4272
|
+
}
|
|
3689
4273
|
/**
|
|
3690
4274
|
* Capture a structured-clone-safe {@link DockPanelSnapshot} of a panel
|
|
3691
4275
|
* (identity, chrome attributes, and a best-effort `innerHTML`). Returns
|
|
@@ -3700,6 +4284,7 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3700
4284
|
iconName: el.iconName,
|
|
3701
4285
|
closable: el.closable,
|
|
3702
4286
|
collapsible: el.collapsible,
|
|
4287
|
+
kind: normalizeKind(el.kind),
|
|
3703
4288
|
accepts: el.accepts,
|
|
3704
4289
|
contentHTML: el.innerHTML
|
|
3705
4290
|
};
|
|
@@ -3723,10 +4308,11 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3723
4308
|
* `accepts` / `canAccept`. Pure query: no model mutation, no overlay.
|
|
3724
4309
|
*
|
|
3725
4310
|
* `panelId` is passed to `canAccept`; use any stable id for an external
|
|
3726
|
-
* panel (defaults to `"__external__"`).
|
|
4311
|
+
* panel (defaults to `"__external__"`). `kind` covers leaf content rules
|
|
4312
|
+
* for panels unknown to this host (defaults to `'any'`).
|
|
3727
4313
|
*/
|
|
3728
|
-
queryDropTarget(clientX, clientY, panelId = `__external__
|
|
3729
|
-
const zone = this.computeDropZone(clientX, clientY, panelId);
|
|
4314
|
+
queryDropTarget(clientX, clientY, panelId = `__external__`, kind) {
|
|
4315
|
+
const zone = this.computeDropZone(clientX, clientY, panelId, kind);
|
|
3730
4316
|
return zone === null ? null : zoneToTarget(zone);
|
|
3731
4317
|
}
|
|
3732
4318
|
/**
|
|
@@ -3735,9 +4321,9 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3735
4321
|
* one. Call on every pointer update from the outer coordinator, then
|
|
3736
4322
|
* {@link endExternalDrop} when the drag leaves or ends.
|
|
3737
4323
|
*/
|
|
3738
|
-
previewExternalDrop(clientX, clientY, panelId = `__external__
|
|
4324
|
+
previewExternalDrop(clientX, clientY, panelId = `__external__`, kind) {
|
|
3739
4325
|
this.setDragActive(true);
|
|
3740
|
-
const zone = this.computeDropZone(clientX, clientY, panelId);
|
|
4326
|
+
const zone = this.computeDropZone(clientX, clientY, panelId, kind);
|
|
3741
4327
|
this.setDropZone(zone);
|
|
3742
4328
|
return zone === null ? null : zoneToTarget(zone);
|
|
3743
4329
|
}
|
|
@@ -3750,8 +4336,9 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3750
4336
|
* Recreate a panel from a {@link DockPanelSnapshot} (or a bare
|
|
3751
4337
|
* {@link DockPanelInit}) and dock it. With `at` viewport coordinates the
|
|
3752
4338
|
* panel lands where a drop there would put it (respecting `accepts` /
|
|
3753
|
-
* `canAccept`); without them, or when the point
|
|
3754
|
-
*
|
|
4339
|
+
* `canAccept` and leaf content rules); without them, or when the point
|
|
4340
|
+
* misses the host, it goes into the first stack admitting its kind.
|
|
4341
|
+
* Returns the created element, or null when a panel
|
|
3755
4342
|
* with that id is already docked here.
|
|
3756
4343
|
*/
|
|
3757
4344
|
acceptExternalPanel(snapshot, at) {
|
|
@@ -3762,6 +4349,7 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3762
4349
|
if (snapshot.iconName !== void 0) el.iconName = snapshot.iconName;
|
|
3763
4350
|
if (snapshot.closable !== void 0) el.closable = snapshot.closable;
|
|
3764
4351
|
if (snapshot.collapsible !== void 0) el.collapsible = snapshot.collapsible;
|
|
4352
|
+
if (`kind` in snapshot && snapshot.kind !== void 0) el.kind = snapshot.kind;
|
|
3765
4353
|
if (`accepts` in snapshot && snapshot.accepts) el.setAttribute(`accepts`, snapshot.accepts);
|
|
3766
4354
|
if (`contentHTML` in snapshot && snapshot.contentHTML) el.innerHTML = snapshot.contentHTML;
|
|
3767
4355
|
this._returnAllToolbars();
|
|
@@ -3787,9 +4375,10 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3787
4375
|
* a zone with rects relative to the host, or null when the pointer is
|
|
3788
4376
|
* outside the host or the candidate zone is disallowed.
|
|
3789
4377
|
*/
|
|
3790
|
-
computeDropZone(clientX, clientY, panelId) {
|
|
4378
|
+
computeDropZone(clientX, clientY, panelId, kind) {
|
|
3791
4379
|
const hostRect = this.getBoundingClientRect();
|
|
3792
4380
|
if (clientX < hostRect.left || clientX > hostRect.right || clientY < hostRect.top || clientY > hostRect.bottom) return null;
|
|
4381
|
+
const resolvedKind = kind?.trim() ? kind.trim() : this._panelKind(panelId);
|
|
3793
4382
|
const rel = (r) => ({
|
|
3794
4383
|
x: r.left - hostRect.left,
|
|
3795
4384
|
y: r.top - hostRect.top,
|
|
@@ -3817,7 +4406,7 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3817
4406
|
rect: rel(leafRect),
|
|
3818
4407
|
caretRect: this._stripCaretRect(strip, gapIndex, hostRect)
|
|
3819
4408
|
};
|
|
3820
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
4409
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
3821
4410
|
}
|
|
3822
4411
|
}
|
|
3823
4412
|
const relX = (clientX - leafRect.left) / Math.max(leafRect.width, 1);
|
|
@@ -3831,7 +4420,7 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3831
4420
|
gapIndex: stack.panels.length,
|
|
3832
4421
|
rect: rel(leafRect)
|
|
3833
4422
|
};
|
|
3834
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
4423
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
3835
4424
|
}
|
|
3836
4425
|
const r = rel(leafRect);
|
|
3837
4426
|
let operation;
|
|
@@ -3877,7 +4466,7 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3877
4466
|
side,
|
|
3878
4467
|
rect: zoneRect
|
|
3879
4468
|
};
|
|
3880
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
4469
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
3881
4470
|
}
|
|
3882
4471
|
const fromLeft = clientX - hostRect.left;
|
|
3883
4472
|
const fromRight = hostRect.right - clientX;
|
|
@@ -3933,15 +4522,18 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3933
4522
|
side,
|
|
3934
4523
|
rect: zoneRect
|
|
3935
4524
|
};
|
|
3936
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
4525
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
3937
4526
|
}
|
|
3938
|
-
_zoneAllowed(panelId, zone) {
|
|
4527
|
+
_zoneAllowed(panelId, zone, kind) {
|
|
3939
4528
|
if (!tokenAllows(this.accepts, operationToToken(zone.operation))) return false;
|
|
3940
4529
|
const stack = findNode(this._model, zone.targetPath);
|
|
3941
4530
|
if (stack !== null && isStack(stack) && stack.panels.length === 1) {
|
|
3942
4531
|
const target = this._findPanelEl(stack.panels[0]);
|
|
3943
4532
|
if (target && !panelAllows(target.accepts, operationToToken(zone.operation))) return false;
|
|
3944
4533
|
}
|
|
4534
|
+
if (zone.operation === `tabs` && stack !== null && isStack(stack)) {
|
|
4535
|
+
if (!this._stackAllowsKind(stack, kind?.trim() ? kind.trim() : this._panelKind(panelId))) return false;
|
|
4536
|
+
}
|
|
3945
4537
|
if (this.canAccept && !this.canAccept(panelId, zone.operation)) return false;
|
|
3946
4538
|
return true;
|
|
3947
4539
|
}
|
|
@@ -3956,22 +4548,32 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3956
4548
|
this._appendPanelToFirstStack(panelId);
|
|
3957
4549
|
normalize(this._model);
|
|
3958
4550
|
}
|
|
3959
|
-
this._applyZone(panelId, zone);
|
|
4551
|
+
this._applyZone(panelId, zone, sourceHost === void 0 || sourceHost === this);
|
|
3960
4552
|
this._commit();
|
|
3961
4553
|
}
|
|
3962
4554
|
/**
|
|
3963
4555
|
* Move `panelId` (already present somewhere in the model) to the position
|
|
3964
4556
|
* described by `zone`. Shared by in-page drops and external panel adoption.
|
|
3965
4557
|
*/
|
|
3966
|
-
_applyZone(panelId, zone) {
|
|
4558
|
+
_applyZone(panelId, zone, transferName = false) {
|
|
3967
4559
|
const sourcePath = findStackOf(this._model, panelId);
|
|
4560
|
+
let vacated = null;
|
|
3968
4561
|
if (sourcePath !== null) {
|
|
3969
4562
|
const source = findNode(this._model, sourcePath);
|
|
3970
|
-
if (source !== null && isStack(source))
|
|
4563
|
+
if (source !== null && isStack(source)) {
|
|
4564
|
+
removePanelFromStack(source, panelId);
|
|
4565
|
+
if (source.panels.length === 0) vacated = source;
|
|
4566
|
+
}
|
|
3971
4567
|
}
|
|
3972
4568
|
if (zone.operation === `tabs`) {
|
|
3973
4569
|
const target = findNode(this._model, zone.targetPath);
|
|
3974
|
-
if (target !== null && isStack(target))
|
|
4570
|
+
if (target !== null && isStack(target)) {
|
|
4571
|
+
addPanelToStack(target, panelId, zone.gapIndex);
|
|
4572
|
+
if (transferName && vacated !== null && vacated !== target && vacated.name !== void 0 && target.name === void 0) {
|
|
4573
|
+
target.name = vacated.name;
|
|
4574
|
+
delete vacated.name;
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
3975
4577
|
} else insertBeside(this._model, zone.targetPath, opToDirection(zone.operation), createStack([panelId]), zone.side);
|
|
3976
4578
|
}
|
|
3977
4579
|
/**
|
|
@@ -4107,16 +4709,6 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
4107
4709
|
};
|
|
4108
4710
|
window.addEventListener(`click`, swallow, true);
|
|
4109
4711
|
}
|
|
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
4712
|
_removeFromModel(panelId) {
|
|
4121
4713
|
const stackPath = findStackOf(this._model, panelId);
|
|
4122
4714
|
if (stackPath === null) return;
|
|
@@ -4247,6 +4839,7 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
4247
4839
|
id=${`dock-leaf-${pathStr}`}
|
|
4248
4840
|
class="leaf"
|
|
4249
4841
|
data-dock-path=${pathStr}
|
|
4842
|
+
data-dock-name=${stack.name ?? nothing}
|
|
4250
4843
|
size=${size === void 0 ? nothing : String(size)}
|
|
4251
4844
|
min=${collapsible ? `1px` : nothing}
|
|
4252
4845
|
when-small=${collapsible ? `collapse` : nothing}
|
|
@@ -4258,26 +4851,19 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
4258
4851
|
<slot name="empty"><span>Drop panels here</span></slot>
|
|
4259
4852
|
</div>
|
|
4260
4853
|
`);
|
|
4854
|
+
const toolbarSlot = stack.name !== void 0 && this._toolbarSlots.has(`toolbar-${stack.name}`) ? html`<slot name="toolbar-${stack.name}" slot="toolbar"></slot>` : nothing;
|
|
4261
4855
|
return leafAttrs(html`
|
|
4262
4856
|
<ixfx-tab-list
|
|
4263
4857
|
class="strip"
|
|
4264
4858
|
orientation="horizontal"
|
|
4859
|
+
scrollable
|
|
4860
|
+
shrink
|
|
4861
|
+
?hide-labels=${stack.hideLabels === true}
|
|
4265
4862
|
@change=${(e) => this._onTabChange(e, path)}
|
|
4266
4863
|
@pointerdown=${this._onStripPointerDown}
|
|
4267
4864
|
>
|
|
4268
4865
|
${repeat(stack.panels, (id) => id, (id) => this._renderTabItem(stack, id))}
|
|
4269
|
-
|
|
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>
|
|
4866
|
+
${toolbarSlot}
|
|
4281
4867
|
</ixfx-tab-list>
|
|
4282
4868
|
<div class="leaf-body">
|
|
4283
4869
|
<slot name="dock-panel-${stack.active}"></slot>
|
|
@@ -4357,9 +4943,14 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
4357
4943
|
.strip {
|
|
4358
4944
|
flex-shrink: 0;
|
|
4359
4945
|
border: 0;
|
|
4360
|
-
/* The leaf background shows through selected tabs;
|
|
4361
|
-
|
|
4946
|
+
/* The leaf background shows through selected tabs; a text underline
|
|
4947
|
+
(not the border indicator) carries the selection. */
|
|
4362
4948
|
--ixfx-tab-selected-background: transparent;
|
|
4949
|
+
--ixfx-selected-tab-border-width: 0;
|
|
4950
|
+
--ixfx-selected-tab-text-decoration: underline;
|
|
4951
|
+
--ixfx-selected-tab-text-decoration-thickness: 2px;
|
|
4952
|
+
--ixfx-selected-tab-text-decoration-offset: 4px;
|
|
4953
|
+
--ixfx-tab-hover-background: var(--surface-4);
|
|
4363
4954
|
}
|
|
4364
4955
|
|
|
4365
4956
|
/* Uniform tab height: a panel toolbar button would otherwise inflate
|
|
@@ -4368,6 +4959,11 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
4368
4959
|
height: var(--dock-tab-height, 30px);
|
|
4369
4960
|
}
|
|
4370
4961
|
|
|
4962
|
+
/* Breathing room between strip toolbars and the leaf edge. */
|
|
4963
|
+
.strip::part(list-toolbar) {
|
|
4964
|
+
padding-right: var(--space-xs);
|
|
4965
|
+
}
|
|
4966
|
+
|
|
4371
4967
|
.leaf-body {
|
|
4372
4968
|
flex: 1;
|
|
4373
4969
|
min-height: 0;
|
|
@@ -4389,38 +4985,6 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
4389
4985
|
margin: var(--space-s);
|
|
4390
4986
|
}
|
|
4391
4987
|
|
|
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
4988
|
.overlay {
|
|
4425
4989
|
position: absolute;
|
|
4426
4990
|
inset: 0;
|
|
@@ -4537,6 +5101,10 @@ function zoneToTarget(zone) {
|
|
|
4537
5101
|
function cloneNode(node) {
|
|
4538
5102
|
return JSON.parse(JSON.stringify(node));
|
|
4539
5103
|
}
|
|
5104
|
+
/** Panel content kind, normalized (`'any'` when unset/blank). */
|
|
5105
|
+
function normalizeKind(kind) {
|
|
5106
|
+
return (kind?.trim() ?? ``) || `any`;
|
|
5107
|
+
}
|
|
4540
5108
|
function parsePath(pathStr) {
|
|
4541
5109
|
return pathStr.length === 0 ? [] : pathStr.split(`-`).filter((s) => s.length > 0).map(Number);
|
|
4542
5110
|
}
|
|
@@ -7900,6 +8468,6 @@ function init() {
|
|
|
7900
8468
|
console.log(`Init`);
|
|
7901
8469
|
}
|
|
7902
8470
|
//#endregion
|
|
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 };
|
|
8471
|
+
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, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeColour, computeLeafGeoms, createAcCompletionProvider, createElementIncrSearch, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isSplit, isStack, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, readBaseColour, registerFontIcon, registerIcon, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, timelineStyles, toSnapshot, walk, wrapRoot };
|
|
7904
8472
|
|
|
7905
8473
|
//# sourceMappingURL=index.js.map
|