@ixfx/components 0.7.0 → 0.7.2
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 +259 -44
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +828 -125
- 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-CG2vIgPv.d.ts → index-UvnoZUAo.d.ts} +4 -4
- package/dist/{index-CG2vIgPv.d.ts.map → index-UvnoZUAo.d.ts.map} +1 -1
- package/dist/index.d.ts +231 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +701 -110
- package/dist/index.js.map +1 -1
- package/dist/labelled-radial-input.d.ts +1 -1
- package/dist/labelled-radial-input.js +1 -1
- package/dist/{labelled-slider-input-DzigjMwP.js → labelled-slider-input-BInB5GKL.js} +3 -3
- package/dist/{labelled-slider-input-DzigjMwP.js.map → labelled-slider-input-BInB5GKL.js.map} +1 -1
- package/dist/labelled-slider-input.d.ts +1 -1
- package/dist/labelled-slider-input.js +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/polar-pad.d.ts +2 -2
- package/dist/polar-pad.js +1 -1
- package/dist/{radial-input-Dp5qz0ly.js → radial-input-BfA3GO0H.js} +2 -2
- package/dist/{radial-input-Dp5qz0ly.js.map → radial-input-BfA3GO0H.js.map} +1 -1
- package/dist/{radial-input-DXMN55Nz.d.ts → radial-input-DU6t2hYO.d.ts} +3 -3
- package/dist/{radial-input-DXMN55Nz.d.ts.map → radial-input-DU6t2hYO.d.ts.map} +1 -1
- package/dist/radial-input.d.ts +1 -1
- package/dist/radial-input.js +1 -1
- package/dist/range-input.d.ts +2 -2
- package/dist/range-input.js +1 -1
- package/dist/range.d.ts +2 -2
- package/dist/range.js +1 -1
- package/dist/registry-BEtb8_wB.js +46 -0
- package/dist/registry-BEtb8_wB.js.map +1 -0
- package/dist/select-horiz.d.ts +2 -2
- package/dist/select-horiz.js +1 -1
- package/dist/{slider-input-De4eoT11.js → slider-input-Bl1hFE89.js} +2 -2
- package/dist/{slider-input-De4eoT11.js.map → slider-input-Bl1hFE89.js.map} +1 -1
- package/dist/{slider-input-BfRUW2ZB.d.ts → slider-input-BxURAAmD.d.ts} +3 -3
- package/dist/{slider-input-BfRUW2ZB.d.ts.map → slider-input-BxURAAmD.d.ts.map} +1 -1
- package/dist/slider-input.d.ts +1 -1
- package/dist/slider-input.js +1 -1
- package/dist/swipe.d.ts +2 -2
- package/dist/swipe.js +1 -1
- 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/wheel-nudge-By5s_ioH.d.ts +31 -0
- package/dist/wheel-nudge-By5s_ioH.d.ts.map +1 -0
- package/dist/{wheel-nudge-BlSCe_hD.js → wheel-nudge-D5rmht3v.js} +6 -1
- package/dist/wheel-nudge-D5rmht3v.js.map +1 -0
- package/dist/{xy-axis-CxHC8z5E.js → xy-axis-DWizs0bt.js} +3 -3
- package/dist/xy-axis-DWizs0bt.js.map +1 -0
- package/dist/xy-pad.d.ts +2 -2
- package/dist/xy-pad.js +1 -1
- 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/wheel-nudge-BlSCe_hD.js.map +0 -1
- package/dist/wheel-nudge-DGrNzWuE.d.ts +0 -29
- package/dist/wheel-nudge-DGrNzWuE.d.ts.map +0 -1
- package/dist/xy-axis-CxHC8z5E.js.map +0 -1
package/bundle/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { customElement, eventOptions, property, query, queryAssignedElements, state } from "lit/decorators.js";
|
|
3
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
3
4
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
4
5
|
import { Fzf } from "fzf";
|
|
5
6
|
import { Colour } from "@ixfx/visual";
|
|
@@ -7,7 +8,6 @@ import { createCommandRegistry } from "@clinth/ui-commands";
|
|
|
7
8
|
import { Trees } from "@ixfx/collections";
|
|
8
9
|
import { clamp, clamper, rangeStream, round, scaler } from "@ixfx/numbers";
|
|
9
10
|
import { repeat } from "lit/directives/repeat.js";
|
|
10
|
-
import { styleMap } from "lit/directives/style-map.js";
|
|
11
11
|
import { Easings } from "@ixfx/modulation";
|
|
12
12
|
import { classMap } from "lit/directives/class-map.js";
|
|
13
13
|
import "@clinth/vcanvas";
|
|
@@ -1405,18 +1405,43 @@ function safeCustomElement(name) {
|
|
|
1405
1405
|
//#endregion
|
|
1406
1406
|
//#region src/icons/registry.ts
|
|
1407
1407
|
const _registry = /* @__PURE__ */ new Map();
|
|
1408
|
+
let _iconFont;
|
|
1408
1409
|
const iconBus = new EventTarget();
|
|
1409
1410
|
function registerIcon(name, svg) {
|
|
1410
|
-
_registry.set(name,
|
|
1411
|
+
_registry.set(name, {
|
|
1412
|
+
kind: `svg`,
|
|
1413
|
+
svg
|
|
1414
|
+
});
|
|
1415
|
+
iconBus.dispatchEvent(new CustomEvent(`ixfx-icon-change`, { detail: { name } }));
|
|
1416
|
+
}
|
|
1417
|
+
function registerFontIcon(name, glyph) {
|
|
1418
|
+
let char;
|
|
1419
|
+
if (typeof glyph === `number`) char = String.fromCodePoint(glyph);
|
|
1420
|
+
else char = glyph;
|
|
1421
|
+
if (char.length === 0) throw new Error(`registerFontIcon: glyph must be non-empty`);
|
|
1422
|
+
_registry.set(name, {
|
|
1423
|
+
kind: `font`,
|
|
1424
|
+
char
|
|
1425
|
+
});
|
|
1411
1426
|
iconBus.dispatchEvent(new CustomEvent(`ixfx-icon-change`, { detail: { name } }));
|
|
1412
1427
|
}
|
|
1413
1428
|
function getIcon(name) {
|
|
1429
|
+
const entry = _registry.get(name);
|
|
1430
|
+
return entry?.kind === `svg` ? entry.svg : void 0;
|
|
1431
|
+
}
|
|
1432
|
+
function getIconEntry(name) {
|
|
1414
1433
|
return _registry.get(name);
|
|
1415
1434
|
}
|
|
1416
1435
|
function hasIcon(name) {
|
|
1417
1436
|
return _registry.has(name);
|
|
1418
1437
|
}
|
|
1419
|
-
|
|
1438
|
+
function setIconFont(fontFamily) {
|
|
1439
|
+
_iconFont = fontFamily;
|
|
1440
|
+
iconBus.dispatchEvent(new CustomEvent(`ixfx-icon-font-change`, { detail: { name: `` } }));
|
|
1441
|
+
}
|
|
1442
|
+
function getIconFont() {
|
|
1443
|
+
return _iconFont;
|
|
1444
|
+
}
|
|
1420
1445
|
function svgToDataUri(svg) {
|
|
1421
1446
|
return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
|
|
1422
1447
|
}
|
|
@@ -1440,26 +1465,43 @@ let IxfxIconElement = class IxfxIconElement extends i {
|
|
|
1440
1465
|
this.#onIconChange = (e) => {
|
|
1441
1466
|
if (e.detail.name === this.name) this.requestUpdate();
|
|
1442
1467
|
};
|
|
1468
|
+
this.#onFontChange = () => {
|
|
1469
|
+
if (getIconEntry(this.name)?.kind === `font`) this.requestUpdate();
|
|
1470
|
+
};
|
|
1443
1471
|
}
|
|
1444
1472
|
#onIconChange;
|
|
1473
|
+
#onFontChange;
|
|
1445
1474
|
connectedCallback() {
|
|
1446
1475
|
super.connectedCallback();
|
|
1447
1476
|
iconBus.addEventListener(`ixfx-icon-change`, this.#onIconChange);
|
|
1477
|
+
iconBus.addEventListener(`ixfx-icon-font-change`, this.#onFontChange);
|
|
1448
1478
|
}
|
|
1449
1479
|
disconnectedCallback() {
|
|
1450
1480
|
super.disconnectedCallback();
|
|
1451
1481
|
iconBus.removeEventListener(`ixfx-icon-change`, this.#onIconChange);
|
|
1482
|
+
iconBus.removeEventListener(`ixfx-icon-font-change`, this.#onFontChange);
|
|
1452
1483
|
}
|
|
1453
1484
|
render() {
|
|
1454
|
-
const
|
|
1455
|
-
if (!
|
|
1485
|
+
const entry = getIconEntry(this.name);
|
|
1486
|
+
if (!entry) return A;
|
|
1456
1487
|
const hasLabel = this.label.length > 0;
|
|
1488
|
+
if (entry.kind === `font`) {
|
|
1489
|
+
const fontFamily = getIconFont();
|
|
1490
|
+
return b`
|
|
1491
|
+
<span
|
|
1492
|
+
class="font-icon"
|
|
1493
|
+
style=${styleMap({ fontFamily: fontFamily ?? `` })}
|
|
1494
|
+
aria-hidden=${hasLabel ? A : `true`}
|
|
1495
|
+
aria-label=${hasLabel ? this.label : A}
|
|
1496
|
+
>${entry.char}</span>
|
|
1497
|
+
`;
|
|
1498
|
+
}
|
|
1457
1499
|
return b`
|
|
1458
1500
|
<span
|
|
1459
1501
|
class="icon-wrapper"
|
|
1460
1502
|
aria-hidden=${hasLabel ? A : `true`}
|
|
1461
1503
|
aria-label=${hasLabel ? this.label : A}
|
|
1462
|
-
>${unsafeHTML(svg)}</span>
|
|
1504
|
+
>${unsafeHTML(entry.svg)}</span>
|
|
1463
1505
|
`;
|
|
1464
1506
|
}
|
|
1465
1507
|
updated(changed) {
|
|
@@ -1492,17 +1534,28 @@ let IxfxIconElement = class IxfxIconElement extends i {
|
|
|
1492
1534
|
opacity: var(--prominence-disabled-opacity, 0.5);
|
|
1493
1535
|
}
|
|
1494
1536
|
|
|
1495
|
-
.icon-wrapper
|
|
1537
|
+
.icon-wrapper,
|
|
1538
|
+
.font-icon {
|
|
1496
1539
|
display: contents;
|
|
1497
1540
|
}
|
|
1498
1541
|
|
|
1499
|
-
:host svg
|
|
1542
|
+
:host svg,
|
|
1543
|
+
:host .font-icon {
|
|
1500
1544
|
width: 100%;
|
|
1501
1545
|
height: 100%;
|
|
1502
1546
|
transition: transform var(--transition, 0.15s ease-out);
|
|
1503
1547
|
}
|
|
1504
1548
|
|
|
1505
|
-
:host
|
|
1549
|
+
:host .font-icon {
|
|
1550
|
+
font-family: inherit;
|
|
1551
|
+
line-height: 1;
|
|
1552
|
+
display: inline-flex;
|
|
1553
|
+
align-items: center;
|
|
1554
|
+
justify-content: center;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
:host([rotate]) svg,
|
|
1558
|
+
:host([rotate]) .font-icon {
|
|
1506
1559
|
transform: rotate(var(--rotate));
|
|
1507
1560
|
}
|
|
1508
1561
|
`];
|
|
@@ -3164,6 +3217,11 @@ const ICON_CHECK = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="tr
|
|
|
3164
3217
|
*/
|
|
3165
3218
|
const ICON_CLOSE = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>`;
|
|
3166
3219
|
/**
|
|
3220
|
+
* Plus / add icon. Pairs with `close` for add/remove actions.
|
|
3221
|
+
* fill="currentColor" so it inherits CSS color.
|
|
3222
|
+
*/
|
|
3223
|
+
const ICON_ADD = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>`;
|
|
3224
|
+
/**
|
|
3167
3225
|
* Error / warning icon. Useful for error notifications.
|
|
3168
3226
|
* fill="currentColor" so it inherits CSS color.
|
|
3169
3227
|
*/
|
|
@@ -3188,12 +3246,31 @@ const ICON_CHEVRON_RIGHT = `<svg viewBox="0 0 20 20" fill="currentColor" aria-hi
|
|
|
3188
3246
|
* fill="currentColor" so it inherits CSS color.
|
|
3189
3247
|
*/
|
|
3190
3248
|
const ICON_FOLDER_UP = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 4L4 12h5v8h6v-8h5L12 4z"/></svg>`;
|
|
3249
|
+
/**
|
|
3250
|
+
* Folder. Useful for file explorers and sidebars.
|
|
3251
|
+
* fill="currentColor" so it inherits CSS color.
|
|
3252
|
+
*/
|
|
3253
|
+
const ICON_FOLDER = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 1.99-.9 1.99-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>`;
|
|
3254
|
+
/**
|
|
3255
|
+
* Document / file page. Useful for file lists and document tabs.
|
|
3256
|
+
* fill="currentColor" so it inherits CSS color.
|
|
3257
|
+
*/
|
|
3258
|
+
const ICON_FILE = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>`;
|
|
3259
|
+
/**
|
|
3260
|
+
* Magnifying glass. Useful for search panels and inputs.
|
|
3261
|
+
* fill="currentColor" so it inherits CSS color.
|
|
3262
|
+
*/
|
|
3263
|
+
const ICON_SEARCH = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>`;
|
|
3191
3264
|
function registerDefaults() {
|
|
3192
3265
|
registerIcon(`chevron-down`, ICON_CHEVRON_DOWN);
|
|
3193
3266
|
registerIcon(`chevron-right`, ICON_CHEVRON_RIGHT);
|
|
3194
3267
|
registerIcon(`caret-right`, ICON_CARET_RIGHT);
|
|
3195
3268
|
registerIcon(`check`, ICON_CHECK);
|
|
3196
3269
|
registerIcon(`close`, ICON_CLOSE);
|
|
3270
|
+
registerIcon(`file`, ICON_FILE);
|
|
3271
|
+
registerIcon(`folder`, ICON_FOLDER);
|
|
3272
|
+
registerIcon(`search`, ICON_SEARCH);
|
|
3273
|
+
registerIcon(`add`, ICON_ADD);
|
|
3197
3274
|
registerIcon(`error`, ICON_ERROR);
|
|
3198
3275
|
registerIcon(`folder-up`, ICON_FOLDER_UP);
|
|
3199
3276
|
registerIcon(`info`, ICON_INFO);
|
|
@@ -3409,7 +3486,7 @@ let ButtonElement = class ButtonElement extends i {
|
|
|
3409
3486
|
_renderIcon() {
|
|
3410
3487
|
if (this.hideIcon || !this.icon) return A;
|
|
3411
3488
|
if (this.icon.startsWith(`<svg`)) return b`<span class="button-icon">${unsafeHTML(this.icon)}</span>`;
|
|
3412
|
-
if (!
|
|
3489
|
+
if (!hasIcon(this.icon)) return A;
|
|
3413
3490
|
return b`<ixfx-icon class="button-icon" name="${this.icon}"></ixfx-icon>`;
|
|
3414
3491
|
}
|
|
3415
3492
|
render() {
|
|
@@ -4244,6 +4321,7 @@ let MenuTrigger = class MenuTrigger extends i {
|
|
|
4244
4321
|
super(..._args);
|
|
4245
4322
|
this.mode = `trigger`;
|
|
4246
4323
|
this.label = `⋮`;
|
|
4324
|
+
this.iconName = ``;
|
|
4247
4325
|
this.placement = `bottom-start`;
|
|
4248
4326
|
this.tabStop = true;
|
|
4249
4327
|
this._isOpen = false;
|
|
@@ -4453,11 +4531,18 @@ let MenuTrigger = class MenuTrigger extends i {
|
|
|
4453
4531
|
|
|
4454
4532
|
.trigger-area {
|
|
4455
4533
|
anchor-name: var(--anchor-name);
|
|
4456
|
-
|
|
4534
|
+
/* Block flex (not inline-flex): as an inline-level box this would join
|
|
4535
|
+
the host's inline formatting context and pick up strut/baseline gaps
|
|
4536
|
+
above and below the button, offsetting the glyph from center. */
|
|
4537
|
+
display: flex;
|
|
4457
4538
|
align-items: stretch;
|
|
4458
4539
|
}
|
|
4459
4540
|
|
|
4460
4541
|
.trigger-btn {
|
|
4542
|
+
display: inline-flex;
|
|
4543
|
+
align-items: center;
|
|
4544
|
+
justify-content: center;
|
|
4545
|
+
line-height: 1;
|
|
4461
4546
|
background: none;
|
|
4462
4547
|
border: none;
|
|
4463
4548
|
color: var(--surface-3-text);
|
|
@@ -4522,7 +4607,7 @@ let MenuTrigger = class MenuTrigger extends i {
|
|
|
4522
4607
|
@click=${this._handleTriggerClick}
|
|
4523
4608
|
@contextmenu=${this._handleContextMenu}
|
|
4524
4609
|
>
|
|
4525
|
-
${showButton ? b`<button class="trigger-btn" part="base" tabindex=${this.tabStop ? `0` : `-1`}>${this.label}</button>` : null}
|
|
4610
|
+
${showButton ? b`<button class="trigger-btn" part="base" tabindex=${this.tabStop ? `0` : `-1`}>${this.iconName ? b`<ixfx-icon name=${this.iconName}></ixfx-icon>` : this.label}</button>` : null}
|
|
4526
4611
|
<slot name="trigger" @slotchange=${this._handleTriggerSlotChange}></slot>
|
|
4527
4612
|
</div>
|
|
4528
4613
|
<div
|
|
@@ -4537,6 +4622,10 @@ let MenuTrigger = class MenuTrigger extends i {
|
|
|
4537
4622
|
};
|
|
4538
4623
|
__decorate([property({ type: String })], MenuTrigger.prototype, "mode", void 0);
|
|
4539
4624
|
__decorate([property({ type: String })], MenuTrigger.prototype, "label", void 0);
|
|
4625
|
+
__decorate([property({
|
|
4626
|
+
type: String,
|
|
4627
|
+
attribute: `icon-name`
|
|
4628
|
+
})], MenuTrigger.prototype, "iconName", void 0);
|
|
4540
4629
|
__decorate([property({ attribute: false })], MenuTrigger.prototype, "registry", void 0);
|
|
4541
4630
|
__decorate([property({ attribute: false })], MenuTrigger.prototype, "keyboardManager", void 0);
|
|
4542
4631
|
__decorate([property({ type: String })], MenuTrigger.prototype, "placement", void 0);
|
|
@@ -12807,10 +12896,11 @@ function unregisterDockHost(host) {
|
|
|
12807
12896
|
* Begin a dock-panel drag. Called by hosts in response to their own
|
|
12808
12897
|
* `dock-panel-drag-start` events (titlebar or tab-item handles).
|
|
12809
12898
|
*/
|
|
12810
|
-
function startDrag(panelId, sourceHost, pointerId, clientX, clientY) {
|
|
12899
|
+
function startDrag(panelId, sourceHost, pointerId, clientX, clientY, kind) {
|
|
12811
12900
|
if (activeDrag !== null);
|
|
12812
12901
|
activeDrag = {
|
|
12813
12902
|
panelId,
|
|
12903
|
+
kind,
|
|
12814
12904
|
sourceHost,
|
|
12815
12905
|
pointerId,
|
|
12816
12906
|
ghost: createGhostForPanel(sourceHost, panelId),
|
|
@@ -12882,8 +12972,10 @@ function onKeyDown(event) {
|
|
|
12882
12972
|
endDrag();
|
|
12883
12973
|
}
|
|
12884
12974
|
function findDropZone(drag, clientX, clientY) {
|
|
12975
|
+
const sourceIsolated = drag.sourceHost.isolated;
|
|
12885
12976
|
for (const host of dragHosts) {
|
|
12886
|
-
|
|
12977
|
+
if (sourceIsolated && host !== drag.sourceHost) continue;
|
|
12978
|
+
const zone = host.computeDropZone(clientX, clientY, drag.panelId, drag.kind);
|
|
12887
12979
|
if (zone !== null) return zone;
|
|
12888
12980
|
}
|
|
12889
12981
|
return null;
|
|
@@ -12971,12 +13063,15 @@ function createEmptyLayout() {
|
|
|
12971
13063
|
root: createEmptyStack()
|
|
12972
13064
|
};
|
|
12973
13065
|
}
|
|
12974
|
-
function createStack(panels, active) {
|
|
12975
|
-
|
|
13066
|
+
function createStack(panels, active, name) {
|
|
13067
|
+
const stack = {
|
|
12976
13068
|
kind: `stack`,
|
|
12977
13069
|
panels: [...panels],
|
|
12978
13070
|
active: active ?? panels[0] ?? ``
|
|
12979
13071
|
};
|
|
13072
|
+
const trimmed = name?.trim();
|
|
13073
|
+
if (trimmed) stack.name = trimmed;
|
|
13074
|
+
return stack;
|
|
12980
13075
|
}
|
|
12981
13076
|
function createSplit(direction, children, sizes) {
|
|
12982
13077
|
const mean = children.length > 0 ? 1 / children.length : 1;
|
|
@@ -12990,7 +13085,8 @@ function createSplit(direction, children, sizes) {
|
|
|
12990
13085
|
/**
|
|
12991
13086
|
* Runs after every mutation:
|
|
12992
13087
|
* - stack `active` fixed to a member of `panels` (or `''` when empty)
|
|
12993
|
-
* - empty stacks removed from their parent split (and their
|
|
13088
|
+
* - empty *unnamed* stacks removed from their parent split (and their
|
|
13089
|
+
* `sizes` entry); named empty stacks are kept so the name stays addressable
|
|
12994
13090
|
* - splits with ≤1 child replaced by that child (spliced into the parent)
|
|
12995
13091
|
* - a child split whose direction equals its parent's is flattened into it,
|
|
12996
13092
|
* spreading its sizes across the slot it vacated
|
|
@@ -12999,13 +13095,14 @@ function createSplit(direction, children, sizes) {
|
|
|
12999
13095
|
*/
|
|
13000
13096
|
function normalize(root) {
|
|
13001
13097
|
if (isStack(root)) {
|
|
13098
|
+
sanitizeStackName(root);
|
|
13002
13099
|
if (!root.panels.includes(root.active)) root.active = root.panels[0] ?? ``;
|
|
13003
13100
|
return root;
|
|
13004
13101
|
}
|
|
13005
13102
|
for (let i = root.children.length - 1; i >= 0; i--) {
|
|
13006
13103
|
normalize(root.children[i]);
|
|
13007
13104
|
const child = root.children[i];
|
|
13008
|
-
if (isStack(child) && child.panels.length === 0) {
|
|
13105
|
+
if (isStack(child) && child.panels.length === 0 && !child.name) {
|
|
13009
13106
|
root.children.splice(i, 1);
|
|
13010
13107
|
root.sizes.splice(i, 1);
|
|
13011
13108
|
continue;
|
|
@@ -13039,17 +13136,36 @@ function replaceNodeContents(target, source) {
|
|
|
13039
13136
|
const t = target;
|
|
13040
13137
|
t.panels = source.panels;
|
|
13041
13138
|
t.active = source.active;
|
|
13139
|
+
if (source.name !== void 0) t.name = source.name;
|
|
13140
|
+
else delete t.name;
|
|
13141
|
+
if (source.hideLabels !== void 0) t.hideLabels = source.hideLabels;
|
|
13142
|
+
else delete t.hideLabels;
|
|
13042
13143
|
delete t.collapsed;
|
|
13043
13144
|
delete t.preCollapseSize;
|
|
13145
|
+
if (source.collapsed !== void 0) t.collapsed = source.collapsed;
|
|
13146
|
+
if (source.preCollapseSize !== void 0) t.preCollapseSize = source.preCollapseSize;
|
|
13044
13147
|
} else {
|
|
13045
13148
|
target.kind = `split`;
|
|
13046
13149
|
const t = target;
|
|
13047
13150
|
t.direction = source.direction;
|
|
13048
13151
|
t.children = source.children;
|
|
13049
13152
|
t.sizes = source.sizes;
|
|
13153
|
+
const stale = t;
|
|
13154
|
+
delete stale.panels;
|
|
13155
|
+
delete stale.active;
|
|
13156
|
+
delete stale.name;
|
|
13157
|
+
delete stale.collapsed;
|
|
13158
|
+
delete stale.preCollapseSize;
|
|
13050
13159
|
}
|
|
13051
13160
|
return target;
|
|
13052
13161
|
}
|
|
13162
|
+
/** Trim `name`; drop it when empty / non-string so lookups stay predictable. */
|
|
13163
|
+
function sanitizeStackName(stack) {
|
|
13164
|
+
if (stack.name === void 0) return;
|
|
13165
|
+
const trimmed = typeof stack.name === `string` ? stack.name.trim() : ``;
|
|
13166
|
+
if (trimmed) stack.name = trimmed;
|
|
13167
|
+
else delete stack.name;
|
|
13168
|
+
}
|
|
13053
13169
|
function findNode(root, path) {
|
|
13054
13170
|
let current = root;
|
|
13055
13171
|
for (const index of path) {
|
|
@@ -13083,6 +13199,36 @@ function collectPanelIds(root) {
|
|
|
13083
13199
|
return root.children.flatMap(collectPanelIds);
|
|
13084
13200
|
}
|
|
13085
13201
|
/**
|
|
13202
|
+
* Path of the first stack (tree order) whose `name` matches, or null.
|
|
13203
|
+
* Names are expected to be unique per layout; duplicates resolve first-wins.
|
|
13204
|
+
*/
|
|
13205
|
+
function findStackPathByName(root, name) {
|
|
13206
|
+
const wanted = name.trim();
|
|
13207
|
+
if (!wanted) return null;
|
|
13208
|
+
return findStackPathByNameInner(root, wanted, []);
|
|
13209
|
+
}
|
|
13210
|
+
function findStackPathByNameInner(node, name, path) {
|
|
13211
|
+
if (isStack(node)) return node.name === name ? path : null;
|
|
13212
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
13213
|
+
const found = findStackPathByNameInner(node.children[i], name, [...path, i]);
|
|
13214
|
+
if (found !== null) return found;
|
|
13215
|
+
}
|
|
13216
|
+
return null;
|
|
13217
|
+
}
|
|
13218
|
+
/** Names of every named stack leaf, in tree order. */
|
|
13219
|
+
function collectContainerNames(root) {
|
|
13220
|
+
const out = [];
|
|
13221
|
+
const visit = (node) => {
|
|
13222
|
+
if (isStack(node)) {
|
|
13223
|
+
if (node.name) out.push(node.name);
|
|
13224
|
+
return;
|
|
13225
|
+
}
|
|
13226
|
+
node.children.forEach(visit);
|
|
13227
|
+
};
|
|
13228
|
+
visit(root);
|
|
13229
|
+
return out;
|
|
13230
|
+
}
|
|
13231
|
+
/**
|
|
13086
13232
|
* Paths of every stack leaf, in tree order. `[]` is the root itself when it
|
|
13087
13233
|
* is a stack.
|
|
13088
13234
|
*/
|
|
@@ -13164,11 +13310,27 @@ function insertBeside(root, targetPath, direction, newNode, side) {
|
|
|
13164
13310
|
* the caller's reference stays valid. Returns true on success.
|
|
13165
13311
|
*/
|
|
13166
13312
|
function wrapRoot(root, direction, newNode, side) {
|
|
13167
|
-
|
|
13313
|
+
const inner = cloneNodeShallow(root);
|
|
13314
|
+
replaceNodeContents(root, createSplit(direction, side === `before` ? [newNode, inner] : [inner, newNode]));
|
|
13168
13315
|
normalize(root);
|
|
13169
13316
|
return true;
|
|
13170
13317
|
}
|
|
13171
13318
|
/**
|
|
13319
|
+
* Detached copy of `node` (own arrays copied) so it can become a child of a
|
|
13320
|
+
* wrapper that then replaces `node` in place.
|
|
13321
|
+
*/
|
|
13322
|
+
function cloneNodeShallow(node) {
|
|
13323
|
+
if (isStack(node)) return {
|
|
13324
|
+
...node,
|
|
13325
|
+
panels: [...node.panels]
|
|
13326
|
+
};
|
|
13327
|
+
return {
|
|
13328
|
+
...node,
|
|
13329
|
+
children: [...node.children],
|
|
13330
|
+
sizes: [...node.sizes]
|
|
13331
|
+
};
|
|
13332
|
+
}
|
|
13333
|
+
/**
|
|
13172
13334
|
* Remove the stack at `stackPath` from the tree. Its panels are moved into
|
|
13173
13335
|
* `fallbackStack` (if given and still attached) so they are not silently
|
|
13174
13336
|
* lost; otherwise they are dropped from the model (elements return to the
|
|
@@ -13197,6 +13359,251 @@ function extractStack(root, stackPath, fallbackStack) {
|
|
|
13197
13359
|
normalize(root);
|
|
13198
13360
|
return stack;
|
|
13199
13361
|
}
|
|
13362
|
+
/** Tolerance for edge-touch comparisons in unit-square geometry. */
|
|
13363
|
+
const GEOM_EPS = 1e-9;
|
|
13364
|
+
function pathsEqual(a, b) {
|
|
13365
|
+
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
13366
|
+
}
|
|
13367
|
+
/**
|
|
13368
|
+
* Resolve a {@link DockLeafRef} to a stack path. Strings match container
|
|
13369
|
+
* `name` first-wins; arrays must index an existing stack leaf. Returns null
|
|
13370
|
+
* when unresolvable.
|
|
13371
|
+
*/
|
|
13372
|
+
function resolveLeafPath(root, ref) {
|
|
13373
|
+
if (typeof ref === `string`) return findStackPathByName(root, ref);
|
|
13374
|
+
if (!Array.isArray(ref)) return null;
|
|
13375
|
+
let current = root;
|
|
13376
|
+
for (const index of ref) {
|
|
13377
|
+
if (!Number.isInteger(index) || index < 0) return null;
|
|
13378
|
+
if (!isSplit(current)) return null;
|
|
13379
|
+
const next = current.children[index];
|
|
13380
|
+
if (!next) return null;
|
|
13381
|
+
current = next;
|
|
13382
|
+
}
|
|
13383
|
+
return isStack(current) ? [...ref] : null;
|
|
13384
|
+
}
|
|
13385
|
+
/** Absolute (unit-square) rect of every stack leaf, in tree order. */
|
|
13386
|
+
function computeLeafGeoms(root) {
|
|
13387
|
+
const out = [];
|
|
13388
|
+
const visit = (node, path, rect) => {
|
|
13389
|
+
if (isStack(node)) {
|
|
13390
|
+
out.push({
|
|
13391
|
+
path,
|
|
13392
|
+
name: node.name ?? null,
|
|
13393
|
+
rect
|
|
13394
|
+
});
|
|
13395
|
+
return;
|
|
13396
|
+
}
|
|
13397
|
+
const total = node.sizes.reduce((sum, w) => sum + w, 0);
|
|
13398
|
+
const safe = total > 0 ? total : node.children.length || 1;
|
|
13399
|
+
let offset = 0;
|
|
13400
|
+
node.children.forEach((child, i) => {
|
|
13401
|
+
const frac = (node.sizes[i] ?? safe / node.children.length) / safe;
|
|
13402
|
+
if (node.direction === `row`) {
|
|
13403
|
+
const w = rect.w * frac;
|
|
13404
|
+
visit(child, [...path, i], {
|
|
13405
|
+
x: rect.x + offset,
|
|
13406
|
+
y: rect.y,
|
|
13407
|
+
w,
|
|
13408
|
+
h: rect.h
|
|
13409
|
+
});
|
|
13410
|
+
offset += w;
|
|
13411
|
+
} else {
|
|
13412
|
+
const h = rect.h * frac;
|
|
13413
|
+
visit(child, [...path, i], {
|
|
13414
|
+
x: rect.x,
|
|
13415
|
+
y: rect.y + offset,
|
|
13416
|
+
w: rect.w,
|
|
13417
|
+
h
|
|
13418
|
+
});
|
|
13419
|
+
offset += h;
|
|
13420
|
+
}
|
|
13421
|
+
});
|
|
13422
|
+
};
|
|
13423
|
+
visit(root, [], {
|
|
13424
|
+
x: 0,
|
|
13425
|
+
y: 0,
|
|
13426
|
+
w: 1,
|
|
13427
|
+
h: 1
|
|
13428
|
+
});
|
|
13429
|
+
return out;
|
|
13430
|
+
}
|
|
13431
|
+
function intervalOverlap(a0, a1, b0, b1) {
|
|
13432
|
+
return Math.min(a1, b1) - Math.max(a0, b0);
|
|
13433
|
+
}
|
|
13434
|
+
/**
|
|
13435
|
+
* Geometric neighbours of a leaf — what the user sees above / below / left /
|
|
13436
|
+
* right of it. Each list holds `{ name, path }` (name is null for unnamed
|
|
13437
|
+
* leaves), ordered most-overlapping first. Returns null when `ref` is
|
|
13438
|
+
* unresolvable.
|
|
13439
|
+
*/
|
|
13440
|
+
function getLeafNeighbours(root, ref) {
|
|
13441
|
+
const path = resolveLeafPath(root, ref);
|
|
13442
|
+
if (path === null) return null;
|
|
13443
|
+
const geoms = computeLeafGeoms(root);
|
|
13444
|
+
const self = geoms.find((g) => pathsEqual(g.path, path));
|
|
13445
|
+
if (!self) return null;
|
|
13446
|
+
const s = self.rect;
|
|
13447
|
+
const above = [];
|
|
13448
|
+
const below = [];
|
|
13449
|
+
const left = [];
|
|
13450
|
+
const right = [];
|
|
13451
|
+
geoms.forEach((g, order) => {
|
|
13452
|
+
if (g === self) return;
|
|
13453
|
+
const r = g.rect;
|
|
13454
|
+
const info = {
|
|
13455
|
+
name: g.name,
|
|
13456
|
+
path: [...g.path]
|
|
13457
|
+
};
|
|
13458
|
+
if (Math.abs(r.y + r.h - s.y) < GEOM_EPS) {
|
|
13459
|
+
const o = intervalOverlap(r.x, r.x + r.w, s.x, s.x + s.w);
|
|
13460
|
+
if (o > GEOM_EPS) above.push({
|
|
13461
|
+
info,
|
|
13462
|
+
overlap: o,
|
|
13463
|
+
order
|
|
13464
|
+
});
|
|
13465
|
+
}
|
|
13466
|
+
if (Math.abs(s.y + s.h - r.y) < GEOM_EPS) {
|
|
13467
|
+
const o = intervalOverlap(r.x, r.x + r.w, s.x, s.x + s.w);
|
|
13468
|
+
if (o > GEOM_EPS) below.push({
|
|
13469
|
+
info,
|
|
13470
|
+
overlap: o,
|
|
13471
|
+
order
|
|
13472
|
+
});
|
|
13473
|
+
}
|
|
13474
|
+
if (Math.abs(r.x + r.w - s.x) < GEOM_EPS) {
|
|
13475
|
+
const o = intervalOverlap(r.y, r.y + r.h, s.y, s.y + s.h);
|
|
13476
|
+
if (o > GEOM_EPS) left.push({
|
|
13477
|
+
info,
|
|
13478
|
+
overlap: o,
|
|
13479
|
+
order
|
|
13480
|
+
});
|
|
13481
|
+
}
|
|
13482
|
+
if (Math.abs(s.x + s.w - r.x) < GEOM_EPS) {
|
|
13483
|
+
const o = intervalOverlap(r.y, r.y + r.h, s.y, s.y + s.h);
|
|
13484
|
+
if (o > GEOM_EPS) right.push({
|
|
13485
|
+
info,
|
|
13486
|
+
overlap: o,
|
|
13487
|
+
order
|
|
13488
|
+
});
|
|
13489
|
+
}
|
|
13490
|
+
});
|
|
13491
|
+
const rank = (hits) => hits.sort((a, b) => b.overlap - a.overlap || a.order - b.order).map((h) => h.info);
|
|
13492
|
+
return {
|
|
13493
|
+
above: rank(above),
|
|
13494
|
+
below: rank(below),
|
|
13495
|
+
left: rank(left),
|
|
13496
|
+
right: rank(right)
|
|
13497
|
+
};
|
|
13498
|
+
}
|
|
13499
|
+
/**
|
|
13500
|
+
* Exchange two leaves' positions in the tree, each carrying its size slot so
|
|
13501
|
+
* the remaining structure and proportions are untouched. Returns false when
|
|
13502
|
+
* either path is not a stack leaf.
|
|
13503
|
+
*/
|
|
13504
|
+
function swapLeaves(root, pathA, pathB) {
|
|
13505
|
+
if (pathsEqual(pathA, pathB)) return false;
|
|
13506
|
+
const nodeA = findNode(root, pathA);
|
|
13507
|
+
const nodeB = findNode(root, pathB);
|
|
13508
|
+
if (nodeA === null || nodeB === null || !isStack(nodeA) || !isStack(nodeB)) return false;
|
|
13509
|
+
const parentA = findParentOf(root, pathA);
|
|
13510
|
+
const parentB = findParentOf(root, pathB);
|
|
13511
|
+
if (parentA === null || parentB === null) return false;
|
|
13512
|
+
const indexA = pathA[pathA.length - 1];
|
|
13513
|
+
const indexB = pathB[pathB.length - 1];
|
|
13514
|
+
if (parentA === parentB) {
|
|
13515
|
+
const p = parentA;
|
|
13516
|
+
[p.children[indexA], p.children[indexB]] = [p.children[indexB], p.children[indexA]];
|
|
13517
|
+
[p.sizes[indexA], p.sizes[indexB]] = [p.sizes[indexB] ?? 1, p.sizes[indexA] ?? 1];
|
|
13518
|
+
return true;
|
|
13519
|
+
}
|
|
13520
|
+
const carriedNode = parentA.children[indexA];
|
|
13521
|
+
const carriedSize = parentA.sizes[indexA] ?? 1;
|
|
13522
|
+
parentA.children[indexA] = parentB.children[indexB];
|
|
13523
|
+
parentA.sizes[indexA] = parentB.sizes[indexB] ?? 1;
|
|
13524
|
+
parentB.children[indexB] = carriedNode;
|
|
13525
|
+
parentB.sizes[indexB] = carriedSize;
|
|
13526
|
+
return true;
|
|
13527
|
+
}
|
|
13528
|
+
/**
|
|
13529
|
+
* Move a leaf one position in `direction`, swapping with the neighbour it
|
|
13530
|
+
* shares the longest edge with. Returns false when `ref` is unresolvable or
|
|
13531
|
+
* nothing adjoins in that direction. Run `normalize()` afterwards.
|
|
13532
|
+
*/
|
|
13533
|
+
function moveLeafStep(root, ref, direction) {
|
|
13534
|
+
const path = resolveLeafPath(root, ref);
|
|
13535
|
+
if (path === null) return false;
|
|
13536
|
+
const neighbours = getLeafNeighbours(root, ref);
|
|
13537
|
+
if (neighbours === null) return false;
|
|
13538
|
+
const list = direction === `up` ? neighbours.above : direction === `down` ? neighbours.below : direction === `left` ? neighbours.left : neighbours.right;
|
|
13539
|
+
if (list.length === 0) return false;
|
|
13540
|
+
return swapLeaves(root, path, list[0].path);
|
|
13541
|
+
}
|
|
13542
|
+
/**
|
|
13543
|
+
* Move a leaf to an edge of the overall layout, keeping the rest of the
|
|
13544
|
+
* tree intact as a single subtree and preserving the leaf's absolute size
|
|
13545
|
+
* along the target axis. Returns false when `ref` is unresolvable or the
|
|
13546
|
+
* layout holds a single leaf. Run `normalize()` afterwards (also safe to
|
|
13547
|
+
* call it twice — the host's commit does).
|
|
13548
|
+
*/
|
|
13549
|
+
function moveLeafNodeToEdge(root, ref, edge) {
|
|
13550
|
+
const path = resolveLeafPath(root, ref);
|
|
13551
|
+
if (path === null || path.length === 0) return false;
|
|
13552
|
+
const self = computeLeafGeoms(root).find((g) => pathsEqual(g.path, path));
|
|
13553
|
+
if (!self) return false;
|
|
13554
|
+
const horizontal = edge === `left` || edge === `right`;
|
|
13555
|
+
const frac = Math.min(Math.max(horizontal ? self.rect.w : self.rect.h, .05), .95);
|
|
13556
|
+
if (!(frac > 0) || frac >= 1) return false;
|
|
13557
|
+
const parent = findParentOf(root, path);
|
|
13558
|
+
if (parent === null) return false;
|
|
13559
|
+
const [node] = parent.children.splice(path[path.length - 1], 1);
|
|
13560
|
+
parent.sizes.splice(path[path.length - 1], 1);
|
|
13561
|
+
if (!node) return false;
|
|
13562
|
+
normalize(root);
|
|
13563
|
+
const atStart = edge === `left` || edge === `top`;
|
|
13564
|
+
const direction = horizontal ? `row` : `column`;
|
|
13565
|
+
if (isStack(root)) {
|
|
13566
|
+
const rest = root;
|
|
13567
|
+
const remaining = {
|
|
13568
|
+
kind: `stack`,
|
|
13569
|
+
panels: [...rest.panels],
|
|
13570
|
+
active: rest.active
|
|
13571
|
+
};
|
|
13572
|
+
if (rest.name !== void 0) remaining.name = rest.name;
|
|
13573
|
+
if (rest.collapsed !== void 0) remaining.collapsed = rest.collapsed;
|
|
13574
|
+
if (rest.preCollapseSize !== void 0) remaining.preCollapseSize = rest.preCollapseSize;
|
|
13575
|
+
replaceNodeContents(root, {
|
|
13576
|
+
kind: `split`,
|
|
13577
|
+
direction,
|
|
13578
|
+
children: atStart ? [node, remaining] : [remaining, node],
|
|
13579
|
+
sizes: atStart ? [frac, 1 - frac] : [1 - frac, frac]
|
|
13580
|
+
});
|
|
13581
|
+
return true;
|
|
13582
|
+
}
|
|
13583
|
+
if (isSplit(root) && root.direction === direction) {
|
|
13584
|
+
const size = frac * (root.sizes.reduce((total, w) => total + w, 0) || root.children.length || 1) / (1 - frac);
|
|
13585
|
+
const at = atStart ? 0 : root.children.length;
|
|
13586
|
+
root.children.splice(at, 0, node);
|
|
13587
|
+
root.sizes.splice(at, 0, size);
|
|
13588
|
+
return true;
|
|
13589
|
+
}
|
|
13590
|
+
if (isSplit(root)) {
|
|
13591
|
+
const inner = {
|
|
13592
|
+
kind: `split`,
|
|
13593
|
+
direction: root.direction,
|
|
13594
|
+
children: root.children,
|
|
13595
|
+
sizes: root.sizes
|
|
13596
|
+
};
|
|
13597
|
+
replaceNodeContents(root, {
|
|
13598
|
+
kind: `split`,
|
|
13599
|
+
direction,
|
|
13600
|
+
children: atStart ? [node, inner] : [inner, node],
|
|
13601
|
+
sizes: atStart ? [frac, 1 - frac] : [1 - frac, frac]
|
|
13602
|
+
});
|
|
13603
|
+
return true;
|
|
13604
|
+
}
|
|
13605
|
+
return false;
|
|
13606
|
+
}
|
|
13200
13607
|
function serializeLayout(root) {
|
|
13201
13608
|
return {
|
|
13202
13609
|
version: 1,
|
|
@@ -13210,6 +13617,8 @@ function serializeNode(node) {
|
|
|
13210
13617
|
panels: [...node.panels],
|
|
13211
13618
|
active: node.active
|
|
13212
13619
|
};
|
|
13620
|
+
if (node.name !== void 0) out.name = node.name;
|
|
13621
|
+
if (node.hideLabels !== void 0) out.hideLabels = node.hideLabels;
|
|
13213
13622
|
if (node.collapsed !== void 0) out.collapsed = node.collapsed;
|
|
13214
13623
|
if (node.preCollapseSize !== void 0) out.preCollapseSize = node.preCollapseSize;
|
|
13215
13624
|
return out;
|
|
@@ -14259,14 +14668,12 @@ let TabListElement = class TabListElement extends i {
|
|
|
14259
14668
|
if (!el) return;
|
|
14260
14669
|
if (!this.#isVertical) {
|
|
14261
14670
|
if (event.deltaY === 0 || el.scrollWidth <= el.clientWidth) return;
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
if (el.scrollLeft !== prev) event.preventDefault();
|
|
14671
|
+
el.scrollLeft = el.scrollLeft + event.deltaY;
|
|
14672
|
+
event.preventDefault();
|
|
14265
14673
|
} else {
|
|
14266
14674
|
if (event.deltaX === 0 || el.scrollHeight <= el.clientHeight) return;
|
|
14267
|
-
|
|
14268
|
-
|
|
14269
|
-
if (el.scrollTop !== prev) event.preventDefault();
|
|
14675
|
+
el.scrollTop = el.scrollTop + event.deltaX;
|
|
14676
|
+
event.preventDefault();
|
|
14270
14677
|
}
|
|
14271
14678
|
};
|
|
14272
14679
|
this.#onSlotClick = (event) => {
|
|
@@ -14353,8 +14760,11 @@ let TabListElement = class TabListElement extends i {
|
|
|
14353
14760
|
return this.orientation === `vertical-stack` || this.orientation === `vertical-side`;
|
|
14354
14761
|
}
|
|
14355
14762
|
get #containerClass() {
|
|
14356
|
-
|
|
14357
|
-
|
|
14763
|
+
return [
|
|
14764
|
+
this.scrollable ? `${this.orientation} scrollable` : this.orientation,
|
|
14765
|
+
this.scrollable ? this.#isVertical ? `scroll-fade-y` : `scroll-fade-x` : ``,
|
|
14766
|
+
this.dragMode === `reorder` ? `reorder` : ``
|
|
14767
|
+
].filter(Boolean).join(` `);
|
|
14358
14768
|
}
|
|
14359
14769
|
render() {
|
|
14360
14770
|
const caretStyle = this.#computeCaretStyle();
|
|
@@ -14636,7 +15046,11 @@ let TabListElement = class TabListElement extends i {
|
|
|
14636
15046
|
return this.shadowRoot?.getElementById(`container`) ?? null;
|
|
14637
15047
|
}
|
|
14638
15048
|
static {
|
|
14639
|
-
this.styles = [
|
|
15049
|
+
this.styles = [
|
|
15050
|
+
themeFallbacks,
|
|
15051
|
+
scrollFadeStyles(`x`),
|
|
15052
|
+
scrollFadeStyles(`y`),
|
|
15053
|
+
i$3`
|
|
14640
15054
|
:host {
|
|
14641
15055
|
display: inline-block;
|
|
14642
15056
|
--activated-colour: var(--accent);
|
|
@@ -14719,8 +15133,12 @@ let TabListElement = class TabListElement extends i {
|
|
|
14719
15133
|
overflow: hidden;
|
|
14720
15134
|
}
|
|
14721
15135
|
#container.horizontal.scrollable {
|
|
14722
|
-
overflow-x:
|
|
15136
|
+
overflow-x: scroll;
|
|
14723
15137
|
--allow-overflow: visible;
|
|
15138
|
+
scrollbar-width: none;
|
|
15139
|
+
}
|
|
15140
|
+
#container.horizontal.scrollable::-webkit-scrollbar {
|
|
15141
|
+
display: none;
|
|
14724
15142
|
}
|
|
14725
15143
|
#container.horizontal.scrollable ::slotted(ixfx-tab-list-item) {
|
|
14726
15144
|
flex-shrink: 0;
|
|
@@ -14733,9 +15151,13 @@ let TabListElement = class TabListElement extends i {
|
|
|
14733
15151
|
flex-direction: column-reverse;
|
|
14734
15152
|
}
|
|
14735
15153
|
#container.vertical-stack.scrollable {
|
|
14736
|
-
overflow-y:
|
|
15154
|
+
overflow-y: scroll;
|
|
14737
15155
|
max-height: var(--scroll-max-height);
|
|
14738
15156
|
--allow-overflow: visible;
|
|
15157
|
+
scrollbar-width: none;
|
|
15158
|
+
}
|
|
15159
|
+
#container.vertical-stack.scrollable::-webkit-scrollbar {
|
|
15160
|
+
display: none;
|
|
14739
15161
|
}
|
|
14740
15162
|
#container.vertical-side {
|
|
14741
15163
|
flex-direction: column;
|
|
@@ -14745,9 +15167,13 @@ let TabListElement = class TabListElement extends i {
|
|
|
14745
15167
|
flex-direction: column-reverse;
|
|
14746
15168
|
}
|
|
14747
15169
|
#container.vertical-side.scrollable {
|
|
14748
|
-
overflow-y:
|
|
15170
|
+
overflow-y: scroll;
|
|
14749
15171
|
max-height: var(--scroll-max-height);
|
|
14750
15172
|
--allow-overflow: visible;
|
|
15173
|
+
scrollbar-width: none;
|
|
15174
|
+
}
|
|
15175
|
+
#container.vertical-side.scrollable::-webkit-scrollbar {
|
|
15176
|
+
display: none;
|
|
14751
15177
|
}
|
|
14752
15178
|
|
|
14753
15179
|
::slotted(ixfx-tab-list-item) {
|
|
@@ -14790,7 +15216,8 @@ let TabListElement = class TabListElement extends i {
|
|
|
14790
15216
|
::slotted(.activated) {
|
|
14791
15217
|
--colour: var(--activated-colour);
|
|
14792
15218
|
}
|
|
14793
|
-
`
|
|
15219
|
+
`
|
|
15220
|
+
];
|
|
14794
15221
|
}
|
|
14795
15222
|
};
|
|
14796
15223
|
__decorate([property({ type: String })], TabListElement.prototype, "orientation", void 0);
|
|
@@ -14822,9 +15249,13 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14822
15249
|
this.registry = void 0;
|
|
14823
15250
|
this._compact = false;
|
|
14824
15251
|
this._hasToolbarContent = false;
|
|
15252
|
+
this._labelObserver = null;
|
|
14825
15253
|
this._onToolbarSlotChange = () => {
|
|
14826
15254
|
this._updateToolbarContent();
|
|
14827
15255
|
};
|
|
15256
|
+
this._onLabelSlotChange = () => {
|
|
15257
|
+
this._syncLabelSizer();
|
|
15258
|
+
};
|
|
14828
15259
|
}
|
|
14829
15260
|
connectedCallback() {
|
|
14830
15261
|
super.connectedCallback();
|
|
@@ -14833,6 +15264,13 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14833
15264
|
});
|
|
14834
15265
|
this._resizeObserver.observe(this);
|
|
14835
15266
|
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.addEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
15267
|
+
(this.shadowRoot?.querySelector(`slot:not([name])`))?.addEventListener(`slotchange`, this._onLabelSlotChange);
|
|
15268
|
+
this._labelObserver = new MutationObserver(() => this._syncLabelSizer());
|
|
15269
|
+
this._labelObserver.observe(this, {
|
|
15270
|
+
childList: true,
|
|
15271
|
+
characterData: true,
|
|
15272
|
+
subtree: true
|
|
15273
|
+
});
|
|
14836
15274
|
}
|
|
14837
15275
|
disconnectedCallback() {
|
|
14838
15276
|
super.disconnectedCallback();
|
|
@@ -14840,7 +15278,10 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14840
15278
|
this._resizeObserver = void 0;
|
|
14841
15279
|
this._commandCleanup?.();
|
|
14842
15280
|
this._commandCleanup = void 0;
|
|
15281
|
+
this._labelObserver?.disconnect();
|
|
15282
|
+
this._labelObserver = null;
|
|
14843
15283
|
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.removeEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
15284
|
+
(this.shadowRoot?.querySelector(`slot:not([name])`))?.removeEventListener(`slotchange`, this._onLabelSlotChange);
|
|
14844
15285
|
}
|
|
14845
15286
|
updated(changed) {
|
|
14846
15287
|
super.updated(changed);
|
|
@@ -14863,12 +15304,25 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14863
15304
|
if (id === this.commandActive) this.click();
|
|
14864
15305
|
});
|
|
14865
15306
|
}
|
|
15307
|
+
/**
|
|
15308
|
+
* Mirror the default-slot text into `.label[data-label]` for the `::after`
|
|
15309
|
+
* width sizer. Slotted toolbar content is excluded.
|
|
15310
|
+
*/
|
|
15311
|
+
_syncLabelSizer() {
|
|
15312
|
+
const slot = this.shadowRoot?.querySelector(`slot:not([name])`);
|
|
15313
|
+
const label = this.shadowRoot?.querySelector(`.label`);
|
|
15314
|
+
if (slot === null || slot === void 0 || label === null || label === void 0) return;
|
|
15315
|
+
const text = slot.assignedNodes().filter((node) => !(node instanceof HTMLElement && node.hasAttribute(`slot`))).map((node) => node.textContent ?? ``).join(``);
|
|
15316
|
+
if (text) label.setAttribute(`data-label`, text);
|
|
15317
|
+
else label.removeAttribute(`data-label`);
|
|
15318
|
+
}
|
|
14866
15319
|
_updateToolbarContent() {
|
|
14867
15320
|
const slot = this.shadowRoot?.querySelector(`slot[name="toolbar"]`);
|
|
14868
15321
|
this._hasToolbarContent = (slot?.assignedElements().length ?? 0) > 0;
|
|
14869
15322
|
}
|
|
14870
15323
|
firstUpdated() {
|
|
14871
15324
|
this._updateToolbarContent();
|
|
15325
|
+
this._syncLabelSizer();
|
|
14872
15326
|
}
|
|
14873
15327
|
render() {
|
|
14874
15328
|
const showLabel = !this.hideLabels || !this.iconName;
|
|
@@ -14910,15 +15364,21 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14910
15364
|
i$3`
|
|
14911
15365
|
:host {
|
|
14912
15366
|
--selected-tab-color: var(--ixfx-selected-tab-color, var(--surface-2-text));
|
|
15367
|
+
--selected-tab-icon-color: var(--ixfx-selected-tab-icon-color, var(--accent));
|
|
14913
15368
|
--selected-tab-border-width: var(--ixfx-selected-tab-border-width, 1px);
|
|
14914
15369
|
--selected-tab-weight: var(--ixfx-selected-tab-weight, bold);
|
|
15370
|
+
--selected-tab-text-decoration: var(--ixfx-selected-tab-text-decoration, none);
|
|
15371
|
+
--selected-tab-text-decoration-color: var(--ixfx-selected-tab-text-decoration-color, var(--accent));
|
|
15372
|
+
--selected-tab-text-decoration-thickness: var(--ixfx-selected-tab-text-decoration-thickness, 0px);
|
|
15373
|
+
--selected-tab-text-decoration-offset: var(--ixfx-selected-tab-text-decoration-offset, 2px);
|
|
14915
15374
|
--selected-background: var(--ixfx-tab-selected-background, var(--surface-5));
|
|
14916
15375
|
--tab-padding: var(--ixfx-tab-padding, var(--space-s));
|
|
14917
15376
|
display: inline-flex;
|
|
14918
15377
|
min-width: 0;
|
|
14919
15378
|
user-select: none;
|
|
14920
15379
|
box-sizing: border-box;
|
|
14921
|
-
font-size: var(--text-
|
|
15380
|
+
font-size: var(--text-m);
|
|
15381
|
+
color: inherit;
|
|
14922
15382
|
/* No explicit height: items stretch (align-self: stretch via the
|
|
14923
15383
|
list's flex container) so every tab in a strip shares one height
|
|
14924
15384
|
even when toolbars give some items taller content. */
|
|
@@ -14933,7 +15393,18 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14933
15393
|
font-weight: 500;
|
|
14934
15394
|
border: var(--selected-tab-border-width) solid transparent;
|
|
14935
15395
|
gap: var(--space-s);
|
|
15396
|
+
transition: background var(--transition, 0.15s);
|
|
15397
|
+
}
|
|
14936
15398
|
|
|
15399
|
+
/* Hover ("tickle") background. When --ixfx-tab-hover-background is
|
|
15400
|
+
unset each state keeps its resting background, so existing
|
|
15401
|
+
consumers see no change. */
|
|
15402
|
+
:host(:hover:not([selected])) > div {
|
|
15403
|
+
background: var(--ixfx-tab-hover-background, transparent);
|
|
15404
|
+
}
|
|
15405
|
+
|
|
15406
|
+
:host([selected]:hover) > div {
|
|
15407
|
+
background: var(--ixfx-tab-hover-background, var(--selected-background));
|
|
14937
15408
|
}
|
|
14938
15409
|
|
|
14939
15410
|
:host([selected]) > div {
|
|
@@ -14947,6 +15418,13 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14947
15418
|
border-radius:var(--radius-m);
|
|
14948
15419
|
}
|
|
14949
15420
|
|
|
15421
|
+
:host([selected]) .label {
|
|
15422
|
+
text-decoration: var(--selected-tab-text-decoration);
|
|
15423
|
+
text-decoration-color: var(--selected-tab-text-decoration-color);
|
|
15424
|
+
text-decoration-thickness: var(--selected-tab-text-decoration-thickness);
|
|
15425
|
+
text-decoration-offset: var(--selected-tab-text-decoration-offset);
|
|
15426
|
+
}
|
|
15427
|
+
|
|
14950
15428
|
.label {
|
|
14951
15429
|
overflow: var(--allow-overflow, hidden);
|
|
14952
15430
|
text-overflow: ellipsis;
|
|
@@ -14955,10 +15433,29 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14955
15433
|
flex: 1 1 auto;
|
|
14956
15434
|
}
|
|
14957
15435
|
|
|
15436
|
+
/* Width sizer: an invisible copy of the label, always set at the
|
|
15437
|
+
selected weight, so activating a tab (bold) never shifts the strip.
|
|
15438
|
+
Text is mirrored from the default slot via the data-label attribute
|
|
15439
|
+
(see _syncLabelSizer). */
|
|
15440
|
+
.label::after {
|
|
15441
|
+
content: attr(data-label);
|
|
15442
|
+
display: block;
|
|
15443
|
+
height: 0;
|
|
15444
|
+
overflow: hidden;
|
|
15445
|
+
visibility: hidden;
|
|
15446
|
+
font-weight: var(--selected-tab-weight);
|
|
15447
|
+
}
|
|
15448
|
+
|
|
14958
15449
|
ixfx-icon {
|
|
14959
15450
|
flex-shrink: 0;
|
|
14960
15451
|
}
|
|
14961
15452
|
|
|
15453
|
+
/* Leading icon (direct child — not close/toolbar icons) picks up the
|
|
15454
|
+
accent when selected, so icon-only tabs read as active. */
|
|
15455
|
+
:host([selected]) > div > ixfx-icon {
|
|
15456
|
+
color: var(--selected-tab-icon-color);
|
|
15457
|
+
}
|
|
15458
|
+
|
|
14962
15459
|
.actions {
|
|
14963
15460
|
display: flex;
|
|
14964
15461
|
align-items: center;
|
|
@@ -15370,6 +15867,7 @@ let DockPanelElement = class DockPanelElement extends i {
|
|
|
15370
15867
|
this.iconName = ``;
|
|
15371
15868
|
this.closable = false;
|
|
15372
15869
|
this.collapsible = false;
|
|
15870
|
+
this.kind = `any`;
|
|
15373
15871
|
this.accepts = ``;
|
|
15374
15872
|
this.presentation = `solo`;
|
|
15375
15873
|
this._collapsed = false;
|
|
@@ -15423,6 +15921,10 @@ let DockPanelElement = class DockPanelElement extends i {
|
|
|
15423
15921
|
}
|
|
15424
15922
|
connectedCallback() {
|
|
15425
15923
|
super.connectedCallback();
|
|
15924
|
+
if (this.hasAttribute(`title`)) {
|
|
15925
|
+
if (!this.hasAttribute(`label`)) this.title = this.getAttribute(`title`) ?? ``;
|
|
15926
|
+
this.removeAttribute(`title`);
|
|
15927
|
+
}
|
|
15426
15928
|
this.addEventListener(`pointerdown`, this._onPointerDown);
|
|
15427
15929
|
}
|
|
15428
15930
|
disconnectedCallback() {
|
|
@@ -15514,13 +16016,20 @@ __decorate([property({
|
|
|
15514
16016
|
attribute: `panel-id`,
|
|
15515
16017
|
reflect: true
|
|
15516
16018
|
})], DockPanelElement.prototype, "panelId", void 0);
|
|
15517
|
-
__decorate([property({
|
|
16019
|
+
__decorate([property({
|
|
16020
|
+
type: String,
|
|
16021
|
+
attribute: `label`
|
|
16022
|
+
})], DockPanelElement.prototype, "title", void 0);
|
|
15518
16023
|
__decorate([property({
|
|
15519
16024
|
type: String,
|
|
15520
16025
|
attribute: `icon-name`
|
|
15521
16026
|
})], DockPanelElement.prototype, "iconName", void 0);
|
|
15522
16027
|
__decorate([property({ type: Boolean })], DockPanelElement.prototype, "closable", void 0);
|
|
15523
16028
|
__decorate([property({ type: Boolean })], DockPanelElement.prototype, "collapsible", void 0);
|
|
16029
|
+
__decorate([property({
|
|
16030
|
+
type: String,
|
|
16031
|
+
reflect: true
|
|
16032
|
+
})], DockPanelElement.prototype, "kind", void 0);
|
|
15524
16033
|
__decorate([property({
|
|
15525
16034
|
type: String,
|
|
15526
16035
|
attribute: `accepts`
|
|
@@ -15546,19 +16055,23 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15546
16055
|
super(..._args);
|
|
15547
16056
|
this.layout = null;
|
|
15548
16057
|
this.accepts = `tabs horizontal vertical`;
|
|
16058
|
+
this.isolated = false;
|
|
15549
16059
|
this._model = createStack([]);
|
|
15550
16060
|
this._pool = [];
|
|
15551
16061
|
this._dropZone = null;
|
|
15552
16062
|
this._dragActive = false;
|
|
15553
16063
|
this._junctions = [];
|
|
15554
16064
|
this._toolbarsOut = /* @__PURE__ */ new Map();
|
|
16065
|
+
this._leafContentRules = /* @__PURE__ */ new Map();
|
|
15555
16066
|
this._tabDragPending = null;
|
|
15556
16067
|
this._junctionsKey = ``;
|
|
15557
16068
|
this._junctionsRaf = null;
|
|
15558
16069
|
this._hostResizeObserver = null;
|
|
16070
|
+
this._toolbarSlots = /* @__PURE__ */ new Set();
|
|
16071
|
+
this._toolbarObserver = null;
|
|
15559
16072
|
this._onDragStart = (event) => {
|
|
15560
16073
|
const { panelId, pointerId, clientX, clientY } = event.detail;
|
|
15561
|
-
startDrag(panelId, this, pointerId, clientX, clientY);
|
|
16074
|
+
startDrag(panelId, this, pointerId, clientX, clientY, this._findPanelEl(panelId)?.kind);
|
|
15562
16075
|
};
|
|
15563
16076
|
this._onPoolSlotChange = () => {
|
|
15564
16077
|
this._syncPool();
|
|
@@ -15605,11 +16118,12 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15605
16118
|
const pathStr = this.shadowRoot?.getElementById(detail.id)?.getAttribute(`data-dock-path`);
|
|
15606
16119
|
if (pathStr === null || pathStr === void 0) return;
|
|
15607
16120
|
const path = parsePath(pathStr);
|
|
15608
|
-
const
|
|
15609
|
-
|
|
15610
|
-
const
|
|
16121
|
+
const closing = findNode(this._model, path);
|
|
16122
|
+
if (closing === null || !isStack(closing)) return;
|
|
16123
|
+
const moving = [...closing.panels];
|
|
15611
16124
|
this._returnAllToolbars();
|
|
15612
|
-
extractStack(this._model, path
|
|
16125
|
+
extractStack(this._model, path);
|
|
16126
|
+
for (const panelId of moving) addPanelToStack(this._firstStackAllowing(this._panelKind(panelId)), panelId);
|
|
15613
16127
|
this._commit();
|
|
15614
16128
|
};
|
|
15615
16129
|
this._onTabChange = (event, path) => {
|
|
@@ -15674,11 +16188,21 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15674
16188
|
this.addEventListener(`dock-panel-drag-start`, this._onDragStart);
|
|
15675
16189
|
this._hostResizeObserver = new ResizeObserver(() => this._scheduleJunctions());
|
|
15676
16190
|
this._hostResizeObserver.observe(this);
|
|
16191
|
+
this._scanToolbarSlots();
|
|
16192
|
+
this._toolbarObserver = new MutationObserver(() => this._scanToolbarSlots());
|
|
16193
|
+
this._toolbarObserver.observe(this, {
|
|
16194
|
+
childList: true,
|
|
16195
|
+
subtree: true,
|
|
16196
|
+
attributes: true,
|
|
16197
|
+
attributeFilter: [`slot`]
|
|
16198
|
+
});
|
|
15677
16199
|
}
|
|
15678
16200
|
disconnectedCallback() {
|
|
15679
16201
|
super.disconnectedCallback();
|
|
15680
16202
|
unregisterDockHost(this);
|
|
15681
16203
|
this.removeEventListener(`dock-panel-drag-start`, this._onDragStart);
|
|
16204
|
+
this._toolbarObserver?.disconnect();
|
|
16205
|
+
this._toolbarObserver = null;
|
|
15682
16206
|
this._hostResizeObserver?.disconnect();
|
|
15683
16207
|
this._hostResizeObserver = null;
|
|
15684
16208
|
if (this._junctionsRaf !== null) {
|
|
@@ -15731,6 +16255,19 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15731
16255
|
_scanPanels() {
|
|
15732
16256
|
return Array.from(this.children).filter((el) => el instanceof HTMLElement && el.tagName === `IXFX-DOCK-PANEL` && Boolean(el.panelId));
|
|
15733
16257
|
}
|
|
16258
|
+
/** Refresh the set of provided `toolbar-<name>` slots; re-render on change. */
|
|
16259
|
+
_scanToolbarSlots() {
|
|
16260
|
+
const found = /* @__PURE__ */ new Set();
|
|
16261
|
+
for (const child of this.children) {
|
|
16262
|
+
if (!(child instanceof HTMLElement)) continue;
|
|
16263
|
+
const slot = child.getAttribute(`slot`) ?? ``;
|
|
16264
|
+
if (slot.startsWith(`toolbar-`) && slot.length > `toolbar-`.length) found.add(slot);
|
|
16265
|
+
}
|
|
16266
|
+
if (found.size !== this._toolbarSlots.size || [...found].some((s) => !this._toolbarSlots.has(s))) {
|
|
16267
|
+
this._toolbarSlots = found;
|
|
16268
|
+
this.requestUpdate();
|
|
16269
|
+
}
|
|
16270
|
+
}
|
|
15734
16271
|
_findPanelEl(panelId) {
|
|
15735
16272
|
return this._pool.find((p) => p.panelId === panelId) ?? this._scanPanels().find((p) => p.panelId === panelId);
|
|
15736
16273
|
}
|
|
@@ -15744,7 +16281,33 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15744
16281
|
return fresh;
|
|
15745
16282
|
}
|
|
15746
16283
|
_appendPanelToFirstStack(panelId) {
|
|
15747
|
-
|
|
16284
|
+
const kind = this._panelKind(panelId);
|
|
16285
|
+
if (this._model.kind === `stack` && this._stackAllowsKind(this._model, kind)) {
|
|
16286
|
+
addPanelToStack(this._model, panelId);
|
|
16287
|
+
return;
|
|
16288
|
+
}
|
|
16289
|
+
addPanelToStack(this._firstStackAllowing(kind), panelId);
|
|
16290
|
+
}
|
|
16291
|
+
/** Normalized content kind of a panel (`'any'` when unknown/unset). */
|
|
16292
|
+
_panelKind(panelId) {
|
|
16293
|
+
return normalizeKind(this._findPanelEl(panelId)?.kind);
|
|
16294
|
+
}
|
|
16295
|
+
/** Whether `kind` may live in `stack` under the current leaf rules. */
|
|
16296
|
+
_stackAllowsKind(stack, kind) {
|
|
16297
|
+
if (stack.name === void 0) return true;
|
|
16298
|
+
const rules = this._leafContentRules.get(stack.name);
|
|
16299
|
+
if (rules === void 0) return true;
|
|
16300
|
+
if (rules.deny.has(kind)) return false;
|
|
16301
|
+
return rules.allow === null || rules.allow.has(kind);
|
|
16302
|
+
}
|
|
16303
|
+
/** First stack (tree order) whose rules admit `kind`, else the first stack. */
|
|
16304
|
+
_firstStackAllowing(kind) {
|
|
16305
|
+
for (const path of collectStackPaths(this._model)) {
|
|
16306
|
+
const node = findNode(this._model, path);
|
|
16307
|
+
if (node !== null && isStack(node) && this._stackAllowsKind(node, kind)) return node;
|
|
16308
|
+
}
|
|
16309
|
+
console.warn(`[ixfx-dock-host] no leaf admits kind "${kind}", using first stack`);
|
|
16310
|
+
return this._firstStack();
|
|
15748
16311
|
}
|
|
15749
16312
|
_syncPool() {
|
|
15750
16313
|
const current = this._scanPanels();
|
|
@@ -15786,7 +16349,34 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15786
16349
|
this.requestUpdate();
|
|
15787
16350
|
this._emitChange();
|
|
15788
16351
|
}
|
|
16352
|
+
/**
|
|
16353
|
+
* Dock a panel. Returns false (without touching the DOM) when the
|
|
16354
|
+
* explicitly targeted container denies the panel's `kind`.
|
|
16355
|
+
*/
|
|
15789
16356
|
addPanel(panel, target) {
|
|
16357
|
+
const kind = panel instanceof HTMLElement ? normalizeKind(panel.kind) : normalizeKind(panel.kind);
|
|
16358
|
+
const op = target?.op ?? `tabs`;
|
|
16359
|
+
let stackPath = null;
|
|
16360
|
+
let splitTarget = null;
|
|
16361
|
+
if (target?.container !== void 0) {
|
|
16362
|
+
const namedPath = findStackPathByName(this._model, target.container);
|
|
16363
|
+
if (namedPath === null) console.warn(`[ixfx-dock-host] addPanel: unknown container "${target.container}", appending to first admitting stack`);
|
|
16364
|
+
else if (op === `tabs` && !this._stackAllowsKindAt(namedPath, kind)) {
|
|
16365
|
+
console.warn(`[ixfx-dock-host] addPanel: container "${target.container}" does not admit kind "${kind}"`);
|
|
16366
|
+
return false;
|
|
16367
|
+
} else stackPath = namedPath;
|
|
16368
|
+
} else if (target?.stack) {
|
|
16369
|
+
const found = findStackOf(this._model, target.stack);
|
|
16370
|
+
if (found === null) console.warn(`[ixfx-dock-host] addPanel: unknown stack panel "${target.stack}", appending to first admitting stack`);
|
|
16371
|
+
else if (op === `tabs` && !this._stackAllowsKindAt(found, kind)) {
|
|
16372
|
+
console.warn(`[ixfx-dock-host] addPanel: target stack does not admit kind "${kind}"`);
|
|
16373
|
+
return false;
|
|
16374
|
+
} else stackPath = found;
|
|
16375
|
+
}
|
|
16376
|
+
if (stackPath !== null && op !== `tabs`) {
|
|
16377
|
+
splitTarget = stackPath;
|
|
16378
|
+
stackPath = null;
|
|
16379
|
+
}
|
|
15790
16380
|
let el;
|
|
15791
16381
|
if (panel instanceof HTMLElement) el = panel;
|
|
15792
16382
|
else {
|
|
@@ -15796,19 +16386,16 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15796
16386
|
if (panel.iconName !== void 0) el.iconName = panel.iconName;
|
|
15797
16387
|
if (panel.closable !== void 0) el.closable = panel.closable;
|
|
15798
16388
|
if (panel.collapsible !== void 0) el.collapsible = panel.collapsible;
|
|
16389
|
+
if (panel.kind !== void 0) el.kind = panel.kind;
|
|
15799
16390
|
}
|
|
15800
16391
|
if (el.parentElement !== this) this.appendChild(el);
|
|
15801
16392
|
if (!this._pool.some((p) => p.panelId === el.panelId)) this._pool = [...this._pool, el];
|
|
15802
16393
|
this._returnAllToolbars();
|
|
15803
|
-
|
|
15804
|
-
if (
|
|
15805
|
-
|
|
15806
|
-
|
|
15807
|
-
|
|
15808
|
-
const stack = findNode(this._model, stackPath);
|
|
15809
|
-
if (stack !== null && isStack(stack)) addPanelToStack(stack, el.panelId, target.index);
|
|
15810
|
-
} else insertBeside(this._model, stackPath, opToDirection(op), createStack([el.panelId]), `after`);
|
|
15811
|
-
}
|
|
16394
|
+
if (collectPanelIds(this._model).includes(el.panelId)) this._removeFromModel(el.panelId);
|
|
16395
|
+
if (splitTarget !== null) insertBeside(this._model, splitTarget, opToDirection(op), createStack([el.panelId]), `after`);
|
|
16396
|
+
else if (stackPath !== null) {
|
|
16397
|
+
const stack = findNode(this._model, stackPath);
|
|
16398
|
+
if (stack !== null && isStack(stack)) addPanelToStack(stack, el.panelId, target?.index);
|
|
15812
16399
|
} else this._appendPanelToFirstStack(el.panelId);
|
|
15813
16400
|
normalize(this._model);
|
|
15814
16401
|
this.requestUpdate();
|
|
@@ -15818,6 +16405,12 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15818
16405
|
composed: true
|
|
15819
16406
|
}));
|
|
15820
16407
|
this._emitChange();
|
|
16408
|
+
return true;
|
|
16409
|
+
}
|
|
16410
|
+
/** Whether the stack at `stackPath` admits `kind` under the leaf rules. */
|
|
16411
|
+
_stackAllowsKindAt(stackPath, kind) {
|
|
16412
|
+
const stack = findNode(this._model, stackPath);
|
|
16413
|
+
return stack !== null && isStack(stack) && this._stackAllowsKind(stack, kind);
|
|
15821
16414
|
}
|
|
15822
16415
|
removePanel(panelId) {
|
|
15823
16416
|
if (!collectPanelIds(this._model).includes(panelId)) return false;
|
|
@@ -15853,6 +16446,123 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15853
16446
|
getPanelIds() {
|
|
15854
16447
|
return collectPanelIds(this._model);
|
|
15855
16448
|
}
|
|
16449
|
+
/** Names of every named stack leaf, in tree order. */
|
|
16450
|
+
getContainerNames() {
|
|
16451
|
+
return collectContainerNames(this._model);
|
|
16452
|
+
}
|
|
16453
|
+
/**
|
|
16454
|
+
* Snapshot of a named container (`{ name, panels, active }`), or null when
|
|
16455
|
+
* no stack carries that name. First match wins when duplicated.
|
|
16456
|
+
*/
|
|
16457
|
+
getContainer(name) {
|
|
16458
|
+
const path = findStackPathByName(this._model, name);
|
|
16459
|
+
if (path === null) return null;
|
|
16460
|
+
const node = findNode(this._model, path);
|
|
16461
|
+
if (node === null || !isStack(node) || node.name === void 0) return null;
|
|
16462
|
+
return {
|
|
16463
|
+
name: node.name,
|
|
16464
|
+
panels: [...node.panels],
|
|
16465
|
+
active: node.active
|
|
16466
|
+
};
|
|
16467
|
+
}
|
|
16468
|
+
/** Panel-ids in a named container, in tab order (`[]` when unknown/empty). */
|
|
16469
|
+
getContainerPanels(name) {
|
|
16470
|
+
return this.getContainer(name)?.panels ?? [];
|
|
16471
|
+
}
|
|
16472
|
+
/** Panel elements in a named container, in tab order — for iterating children. */
|
|
16473
|
+
getContainerElements(name) {
|
|
16474
|
+
return this.getContainerPanels(name).map((id) => this._findPanelEl(id)).filter((el) => el !== void 0);
|
|
16475
|
+
}
|
|
16476
|
+
/**
|
|
16477
|
+
* Restrict a named container to only the given panel kinds — e.g.
|
|
16478
|
+
* `setLeafExclusiveContents('editor', ['document'])`. `tabs` drops,
|
|
16479
|
+
* `addPanel`, and re-homing into the leaf then refuse other kinds, while
|
|
16480
|
+
* edge splits beside the leaf stay allowed (they create a new leaf).
|
|
16481
|
+
* Pass `[]` to seal the leaf (nothing admitted). Warns when no stack
|
|
16482
|
+
* currently carries `name`, but the rule is still stored and applies once
|
|
16483
|
+
* one appears. Rules are host policy: kept across `loadLayout()`, never
|
|
16484
|
+
* serialized.
|
|
16485
|
+
*/
|
|
16486
|
+
setLeafExclusiveContents(name, kinds) {
|
|
16487
|
+
const key = name.trim();
|
|
16488
|
+
if (!key) {
|
|
16489
|
+
console.warn(`[ixfx-dock-host] setLeafExclusiveContents: empty container name`);
|
|
16490
|
+
return;
|
|
16491
|
+
}
|
|
16492
|
+
const allow = new Set(kinds.map((k) => k.trim()).filter((k) => k.length > 0));
|
|
16493
|
+
const entry = this._leafContentRules.get(key) ?? {
|
|
16494
|
+
allow: null,
|
|
16495
|
+
deny: /* @__PURE__ */ new Set()
|
|
16496
|
+
};
|
|
16497
|
+
entry.allow = allow;
|
|
16498
|
+
this._leafContentRules.set(key, entry);
|
|
16499
|
+
if (findStackPathByName(this._model, key) === null) console.warn(`[ixfx-dock-host] setLeafExclusiveContents: no container named "${key}" yet; rule stored`);
|
|
16500
|
+
}
|
|
16501
|
+
/**
|
|
16502
|
+
* Forbid the given panel kinds in a named container — e.g.
|
|
16503
|
+
* `setLeafDenyContents('footer', ['files', 'document'])`. Everything else
|
|
16504
|
+
* stays admitted. Pass `[]` to lift the denial. See
|
|
16505
|
+
* {@link setLeafExclusiveContents} for scope/ordering notes.
|
|
16506
|
+
*/
|
|
16507
|
+
setLeafDenyContents(name, kinds) {
|
|
16508
|
+
const key = name.trim();
|
|
16509
|
+
if (!key) {
|
|
16510
|
+
console.warn(`[ixfx-dock-host] setLeafDenyContents: empty container name`);
|
|
16511
|
+
return;
|
|
16512
|
+
}
|
|
16513
|
+
const deny = new Set(kinds.map((k) => k.trim()).filter((k) => k.length > 0));
|
|
16514
|
+
const entry = this._leafContentRules.get(key) ?? {
|
|
16515
|
+
allow: null,
|
|
16516
|
+
deny: /* @__PURE__ */ new Set()
|
|
16517
|
+
};
|
|
16518
|
+
entry.deny = deny;
|
|
16519
|
+
if (entry.allow === null && entry.deny.size === 0) this._leafContentRules.delete(key);
|
|
16520
|
+
else this._leafContentRules.set(key, entry);
|
|
16521
|
+
if (findStackPathByName(this._model, key) === null) console.warn(`[ixfx-dock-host] setLeafDenyContents: no container named "${key}" yet; rule stored`);
|
|
16522
|
+
}
|
|
16523
|
+
/** Remove every content rule for a named container. */
|
|
16524
|
+
clearLeafContentRestrictions(name) {
|
|
16525
|
+
this._leafContentRules.delete(name.trim());
|
|
16526
|
+
}
|
|
16527
|
+
/** Copy of a container's rules, or null when unrestricted. */
|
|
16528
|
+
getLeafContentRestrictions(name) {
|
|
16529
|
+
const rules = this._leafContentRules.get(name.trim());
|
|
16530
|
+
if (rules === void 0) return null;
|
|
16531
|
+
return {
|
|
16532
|
+
allow: rules.allow === null ? null : [...rules.allow],
|
|
16533
|
+
deny: [...rules.deny]
|
|
16534
|
+
};
|
|
16535
|
+
}
|
|
16536
|
+
/**
|
|
16537
|
+
* Geometric neighbours of a leaf (`name` or numeric `path`) — what sits
|
|
16538
|
+
* above / below / left / right of it on screen. Each entry is
|
|
16539
|
+
* `{ name, path }` (`name` is null for unnamed leaves), ordered
|
|
16540
|
+
* most-overlapping first. Returns null when the leaf is unknown.
|
|
16541
|
+
*/
|
|
16542
|
+
getNeighbours(leaf) {
|
|
16543
|
+
return getLeafNeighbours(this._model, leaf);
|
|
16544
|
+
}
|
|
16545
|
+
/**
|
|
16546
|
+
* Move a leaf one position in `direction`, swapping with the neighbour it
|
|
16547
|
+
* shares the longest edge with (both keep the remaining structure intact).
|
|
16548
|
+
* Returns false when the leaf is unknown or nothing adjoins that way.
|
|
16549
|
+
*/
|
|
16550
|
+
moveLeaf(leaf, direction) {
|
|
16551
|
+
if (!moveLeafStep(this._model, leaf, direction)) return false;
|
|
16552
|
+
this._commit();
|
|
16553
|
+
return true;
|
|
16554
|
+
}
|
|
16555
|
+
/**
|
|
16556
|
+
* Move a leaf to an edge of the overall layout (e.g. toggle a sidebar from
|
|
16557
|
+
* `left` to `right`). The rest of the layout stays intact as a single
|
|
16558
|
+
* subtree and the leaf keeps its absolute size along the target axis.
|
|
16559
|
+
* Returns false when the leaf is unknown or the layout holds one leaf.
|
|
16560
|
+
*/
|
|
16561
|
+
moveLeafToEdge(leaf, edge) {
|
|
16562
|
+
if (!moveLeafNodeToEdge(this._model, leaf, edge)) return false;
|
|
16563
|
+
this._commit();
|
|
16564
|
+
return true;
|
|
16565
|
+
}
|
|
15856
16566
|
/**
|
|
15857
16567
|
* Capture a structured-clone-safe {@link DockPanelSnapshot} of a panel
|
|
15858
16568
|
* (identity, chrome attributes, and a best-effort `innerHTML`). Returns
|
|
@@ -15867,6 +16577,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15867
16577
|
iconName: el.iconName,
|
|
15868
16578
|
closable: el.closable,
|
|
15869
16579
|
collapsible: el.collapsible,
|
|
16580
|
+
kind: normalizeKind(el.kind),
|
|
15870
16581
|
accepts: el.accepts,
|
|
15871
16582
|
contentHTML: el.innerHTML
|
|
15872
16583
|
};
|
|
@@ -15890,10 +16601,11 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15890
16601
|
* `accepts` / `canAccept`. Pure query: no model mutation, no overlay.
|
|
15891
16602
|
*
|
|
15892
16603
|
* `panelId` is passed to `canAccept`; use any stable id for an external
|
|
15893
|
-
* panel (defaults to `"__external__"`).
|
|
16604
|
+
* panel (defaults to `"__external__"`). `kind` covers leaf content rules
|
|
16605
|
+
* for panels unknown to this host (defaults to `'any'`).
|
|
15894
16606
|
*/
|
|
15895
|
-
queryDropTarget(clientX, clientY, panelId = `__external__
|
|
15896
|
-
const zone = this.computeDropZone(clientX, clientY, panelId);
|
|
16607
|
+
queryDropTarget(clientX, clientY, panelId = `__external__`, kind) {
|
|
16608
|
+
const zone = this.computeDropZone(clientX, clientY, panelId, kind);
|
|
15897
16609
|
return zone === null ? null : zoneToTarget(zone);
|
|
15898
16610
|
}
|
|
15899
16611
|
/**
|
|
@@ -15902,9 +16614,9 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15902
16614
|
* one. Call on every pointer update from the outer coordinator, then
|
|
15903
16615
|
* {@link endExternalDrop} when the drag leaves or ends.
|
|
15904
16616
|
*/
|
|
15905
|
-
previewExternalDrop(clientX, clientY, panelId = `__external__
|
|
16617
|
+
previewExternalDrop(clientX, clientY, panelId = `__external__`, kind) {
|
|
15906
16618
|
this.setDragActive(true);
|
|
15907
|
-
const zone = this.computeDropZone(clientX, clientY, panelId);
|
|
16619
|
+
const zone = this.computeDropZone(clientX, clientY, panelId, kind);
|
|
15908
16620
|
this.setDropZone(zone);
|
|
15909
16621
|
return zone === null ? null : zoneToTarget(zone);
|
|
15910
16622
|
}
|
|
@@ -15917,8 +16629,9 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15917
16629
|
* Recreate a panel from a {@link DockPanelSnapshot} (or a bare
|
|
15918
16630
|
* {@link DockPanelInit}) and dock it. With `at` viewport coordinates the
|
|
15919
16631
|
* panel lands where a drop there would put it (respecting `accepts` /
|
|
15920
|
-
* `canAccept`); without them, or when the point
|
|
15921
|
-
*
|
|
16632
|
+
* `canAccept` and leaf content rules); without them, or when the point
|
|
16633
|
+
* misses the host, it goes into the first stack admitting its kind.
|
|
16634
|
+
* Returns the created element, or null when a panel
|
|
15922
16635
|
* with that id is already docked here.
|
|
15923
16636
|
*/
|
|
15924
16637
|
acceptExternalPanel(snapshot, at) {
|
|
@@ -15929,6 +16642,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15929
16642
|
if (snapshot.iconName !== void 0) el.iconName = snapshot.iconName;
|
|
15930
16643
|
if (snapshot.closable !== void 0) el.closable = snapshot.closable;
|
|
15931
16644
|
if (snapshot.collapsible !== void 0) el.collapsible = snapshot.collapsible;
|
|
16645
|
+
if (`kind` in snapshot && snapshot.kind !== void 0) el.kind = snapshot.kind;
|
|
15932
16646
|
if (`accepts` in snapshot && snapshot.accepts) el.setAttribute(`accepts`, snapshot.accepts);
|
|
15933
16647
|
if (`contentHTML` in snapshot && snapshot.contentHTML) el.innerHTML = snapshot.contentHTML;
|
|
15934
16648
|
this._returnAllToolbars();
|
|
@@ -15954,9 +16668,10 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15954
16668
|
* a zone with rects relative to the host, or null when the pointer is
|
|
15955
16669
|
* outside the host or the candidate zone is disallowed.
|
|
15956
16670
|
*/
|
|
15957
|
-
computeDropZone(clientX, clientY, panelId) {
|
|
16671
|
+
computeDropZone(clientX, clientY, panelId, kind) {
|
|
15958
16672
|
const hostRect = this.getBoundingClientRect();
|
|
15959
16673
|
if (clientX < hostRect.left || clientX > hostRect.right || clientY < hostRect.top || clientY > hostRect.bottom) return null;
|
|
16674
|
+
const resolvedKind = kind?.trim() ? kind.trim() : this._panelKind(panelId);
|
|
15960
16675
|
const rel = (r) => ({
|
|
15961
16676
|
x: r.left - hostRect.left,
|
|
15962
16677
|
y: r.top - hostRect.top,
|
|
@@ -15984,7 +16699,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15984
16699
|
rect: rel(leafRect),
|
|
15985
16700
|
caretRect: this._stripCaretRect(strip, gapIndex, hostRect)
|
|
15986
16701
|
};
|
|
15987
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
16702
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
15988
16703
|
}
|
|
15989
16704
|
}
|
|
15990
16705
|
const relX = (clientX - leafRect.left) / Math.max(leafRect.width, 1);
|
|
@@ -15998,7 +16713,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15998
16713
|
gapIndex: stack.panels.length,
|
|
15999
16714
|
rect: rel(leafRect)
|
|
16000
16715
|
};
|
|
16001
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
16716
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
16002
16717
|
}
|
|
16003
16718
|
const r = rel(leafRect);
|
|
16004
16719
|
let operation;
|
|
@@ -16044,7 +16759,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16044
16759
|
side,
|
|
16045
16760
|
rect: zoneRect
|
|
16046
16761
|
};
|
|
16047
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
16762
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
16048
16763
|
}
|
|
16049
16764
|
const fromLeft = clientX - hostRect.left;
|
|
16050
16765
|
const fromRight = hostRect.right - clientX;
|
|
@@ -16100,15 +16815,18 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16100
16815
|
side,
|
|
16101
16816
|
rect: zoneRect
|
|
16102
16817
|
};
|
|
16103
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
16818
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
16104
16819
|
}
|
|
16105
|
-
_zoneAllowed(panelId, zone) {
|
|
16820
|
+
_zoneAllowed(panelId, zone, kind) {
|
|
16106
16821
|
if (!tokenAllows(this.accepts, operationToToken(zone.operation))) return false;
|
|
16107
16822
|
const stack = findNode(this._model, zone.targetPath);
|
|
16108
16823
|
if (stack !== null && isStack(stack) && stack.panels.length === 1) {
|
|
16109
16824
|
const target = this._findPanelEl(stack.panels[0]);
|
|
16110
16825
|
if (target && !panelAllows(target.accepts, operationToToken(zone.operation))) return false;
|
|
16111
16826
|
}
|
|
16827
|
+
if (zone.operation === `tabs` && stack !== null && isStack(stack)) {
|
|
16828
|
+
if (!this._stackAllowsKind(stack, kind?.trim() ? kind.trim() : this._panelKind(panelId))) return false;
|
|
16829
|
+
}
|
|
16112
16830
|
if (this.canAccept && !this.canAccept(panelId, zone.operation)) return false;
|
|
16113
16831
|
return true;
|
|
16114
16832
|
}
|
|
@@ -16123,22 +16841,32 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16123
16841
|
this._appendPanelToFirstStack(panelId);
|
|
16124
16842
|
normalize(this._model);
|
|
16125
16843
|
}
|
|
16126
|
-
this._applyZone(panelId, zone);
|
|
16844
|
+
this._applyZone(panelId, zone, sourceHost === void 0 || sourceHost === this);
|
|
16127
16845
|
this._commit();
|
|
16128
16846
|
}
|
|
16129
16847
|
/**
|
|
16130
16848
|
* Move `panelId` (already present somewhere in the model) to the position
|
|
16131
16849
|
* described by `zone`. Shared by in-page drops and external panel adoption.
|
|
16132
16850
|
*/
|
|
16133
|
-
_applyZone(panelId, zone) {
|
|
16851
|
+
_applyZone(panelId, zone, transferName = false) {
|
|
16134
16852
|
const sourcePath = findStackOf(this._model, panelId);
|
|
16853
|
+
let vacated = null;
|
|
16135
16854
|
if (sourcePath !== null) {
|
|
16136
16855
|
const source = findNode(this._model, sourcePath);
|
|
16137
|
-
if (source !== null && isStack(source))
|
|
16856
|
+
if (source !== null && isStack(source)) {
|
|
16857
|
+
removePanelFromStack(source, panelId);
|
|
16858
|
+
if (source.panels.length === 0) vacated = source;
|
|
16859
|
+
}
|
|
16138
16860
|
}
|
|
16139
16861
|
if (zone.operation === `tabs`) {
|
|
16140
16862
|
const target = findNode(this._model, zone.targetPath);
|
|
16141
|
-
if (target !== null && isStack(target))
|
|
16863
|
+
if (target !== null && isStack(target)) {
|
|
16864
|
+
addPanelToStack(target, panelId, zone.gapIndex);
|
|
16865
|
+
if (transferName && vacated !== null && vacated !== target && vacated.name !== void 0 && target.name === void 0) {
|
|
16866
|
+
target.name = vacated.name;
|
|
16867
|
+
delete vacated.name;
|
|
16868
|
+
}
|
|
16869
|
+
}
|
|
16142
16870
|
} else insertBeside(this._model, zone.targetPath, opToDirection(zone.operation), createStack([panelId]), zone.side);
|
|
16143
16871
|
}
|
|
16144
16872
|
/**
|
|
@@ -16274,16 +17002,6 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16274
17002
|
};
|
|
16275
17003
|
window.addEventListener(`click`, swallow, true);
|
|
16276
17004
|
}
|
|
16277
|
-
_splitStack(path, op) {
|
|
16278
|
-
const stack = findNode(this._model, path);
|
|
16279
|
-
if (stack === null || !isStack(stack) || stack.panels.length === 0) return;
|
|
16280
|
-
const activeId = stack.active;
|
|
16281
|
-
this._returnAllToolbars();
|
|
16282
|
-
removePanelFromStack(stack, activeId);
|
|
16283
|
-
normalize(this._model);
|
|
16284
|
-
insertBeside(this._model, path, opToDirection(op), createStack([activeId]), `after`);
|
|
16285
|
-
this._commit();
|
|
16286
|
-
}
|
|
16287
17005
|
_removeFromModel(panelId) {
|
|
16288
17006
|
const stackPath = findStackOf(this._model, panelId);
|
|
16289
17007
|
if (stackPath === null) return;
|
|
@@ -16414,6 +17132,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16414
17132
|
id=${`dock-leaf-${pathStr}`}
|
|
16415
17133
|
class="leaf"
|
|
16416
17134
|
data-dock-path=${pathStr}
|
|
17135
|
+
data-dock-name=${stack.name ?? A}
|
|
16417
17136
|
size=${size === void 0 ? A : String(size)}
|
|
16418
17137
|
min=${collapsible ? `1px` : A}
|
|
16419
17138
|
when-small=${collapsible ? `collapse` : A}
|
|
@@ -16425,26 +17144,19 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16425
17144
|
<slot name="empty"><span>Drop panels here</span></slot>
|
|
16426
17145
|
</div>
|
|
16427
17146
|
`);
|
|
17147
|
+
const toolbarSlot = stack.name !== void 0 && this._toolbarSlots.has(`toolbar-${stack.name}`) ? b`<slot name="toolbar-${stack.name}" slot="toolbar"></slot>` : A;
|
|
16428
17148
|
return leafAttrs(b`
|
|
16429
17149
|
<ixfx-tab-list
|
|
16430
17150
|
class="strip"
|
|
16431
17151
|
orientation="horizontal"
|
|
17152
|
+
scrollable
|
|
17153
|
+
shrink
|
|
17154
|
+
?hide-labels=${stack.hideLabels === true}
|
|
16432
17155
|
@change=${(e) => this._onTabChange(e, path)}
|
|
16433
17156
|
@pointerdown=${this._onStripPointerDown}
|
|
16434
17157
|
>
|
|
16435
17158
|
${repeat(stack.panels, (id) => id, (id) => this._renderTabItem(stack, id))}
|
|
16436
|
-
|
|
16437
|
-
<button
|
|
16438
|
-
class="chrome-btn"
|
|
16439
|
-
title="Split horizontally (top/bottom)"
|
|
16440
|
-
@click=${() => this._splitStack(path, `split-horizontal`)}
|
|
16441
|
-
>↕</button>
|
|
16442
|
-
<button
|
|
16443
|
-
class="chrome-btn"
|
|
16444
|
-
title="Split vertically (left/right)"
|
|
16445
|
-
@click=${() => this._splitStack(path, `split-vertical`)}
|
|
16446
|
-
>↔</button>
|
|
16447
|
-
</div>
|
|
17159
|
+
${toolbarSlot}
|
|
16448
17160
|
</ixfx-tab-list>
|
|
16449
17161
|
<div class="leaf-body">
|
|
16450
17162
|
<slot name="dock-panel-${stack.active}"></slot>
|
|
@@ -16524,9 +17236,14 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16524
17236
|
.strip {
|
|
16525
17237
|
flex-shrink: 0;
|
|
16526
17238
|
border: 0;
|
|
16527
|
-
/* The leaf background shows through selected tabs;
|
|
16528
|
-
|
|
17239
|
+
/* The leaf background shows through selected tabs; a text underline
|
|
17240
|
+
(not the border indicator) carries the selection. */
|
|
16529
17241
|
--ixfx-tab-selected-background: transparent;
|
|
17242
|
+
--ixfx-selected-tab-border-width: 0;
|
|
17243
|
+
--ixfx-selected-tab-text-decoration: underline;
|
|
17244
|
+
--ixfx-selected-tab-text-decoration-thickness: 2px;
|
|
17245
|
+
--ixfx-selected-tab-text-decoration-offset: 4px;
|
|
17246
|
+
--ixfx-tab-hover-background: var(--surface-4);
|
|
16530
17247
|
}
|
|
16531
17248
|
|
|
16532
17249
|
/* Uniform tab height: a panel toolbar button would otherwise inflate
|
|
@@ -16535,6 +17252,11 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16535
17252
|
height: var(--dock-tab-height, 30px);
|
|
16536
17253
|
}
|
|
16537
17254
|
|
|
17255
|
+
/* Breathing room between strip toolbars and the leaf edge. */
|
|
17256
|
+
.strip::part(list-toolbar) {
|
|
17257
|
+
padding-right: var(--space-xs);
|
|
17258
|
+
}
|
|
17259
|
+
|
|
16538
17260
|
.leaf-body {
|
|
16539
17261
|
flex: 1;
|
|
16540
17262
|
min-height: 0;
|
|
@@ -16556,38 +17278,6 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16556
17278
|
margin: var(--space-s);
|
|
16557
17279
|
}
|
|
16558
17280
|
|
|
16559
|
-
.group-toolbar {
|
|
16560
|
-
display: flex;
|
|
16561
|
-
gap: 2px;
|
|
16562
|
-
padding: 0 var(--space-xs);
|
|
16563
|
-
}
|
|
16564
|
-
|
|
16565
|
-
.chrome-btn {
|
|
16566
|
-
display: inline-flex;
|
|
16567
|
-
align-items: center;
|
|
16568
|
-
justify-content: center;
|
|
16569
|
-
width: 1.6em;
|
|
16570
|
-
height: 1.6em;
|
|
16571
|
-
padding: 0;
|
|
16572
|
-
background: none;
|
|
16573
|
-
border: none;
|
|
16574
|
-
border-radius: var(--radius-s);
|
|
16575
|
-
color: inherit;
|
|
16576
|
-
cursor: pointer;
|
|
16577
|
-
opacity: var(--prominence-idle-opacity);
|
|
16578
|
-
transition: background var(--transition), opacity var(--transition);
|
|
16579
|
-
}
|
|
16580
|
-
|
|
16581
|
-
.chrome-btn:hover {
|
|
16582
|
-
background: var(--surface-5);
|
|
16583
|
-
opacity: var(--prominence-tickled-opacity);
|
|
16584
|
-
}
|
|
16585
|
-
|
|
16586
|
-
.chrome-btn:focus-visible {
|
|
16587
|
-
outline: 2px solid var(--accent);
|
|
16588
|
-
outline-offset: 1px;
|
|
16589
|
-
}
|
|
16590
|
-
|
|
16591
17281
|
.overlay {
|
|
16592
17282
|
position: absolute;
|
|
16593
17283
|
inset: 0;
|
|
@@ -16657,6 +17347,10 @@ __decorate([property({
|
|
|
16657
17347
|
type: String,
|
|
16658
17348
|
attribute: `accepts`
|
|
16659
17349
|
})], DockHostElement.prototype, "accepts", void 0);
|
|
17350
|
+
__decorate([property({
|
|
17351
|
+
type: Boolean,
|
|
17352
|
+
attribute: `isolated`
|
|
17353
|
+
})], DockHostElement.prototype, "isolated", void 0);
|
|
16660
17354
|
__decorate([property({ attribute: false })], DockHostElement.prototype, "canAccept", void 0);
|
|
16661
17355
|
__decorate([state()], DockHostElement.prototype, "_model", void 0);
|
|
16662
17356
|
__decorate([state()], DockHostElement.prototype, "_pool", void 0);
|
|
@@ -16704,6 +17398,10 @@ function zoneToTarget(zone) {
|
|
|
16704
17398
|
function cloneNode(node) {
|
|
16705
17399
|
return JSON.parse(JSON.stringify(node));
|
|
16706
17400
|
}
|
|
17401
|
+
/** Panel content kind, normalized (`'any'` when unset/blank). */
|
|
17402
|
+
function normalizeKind(kind) {
|
|
17403
|
+
return (kind?.trim() ?? ``) || `any`;
|
|
17404
|
+
}
|
|
16707
17405
|
function parsePath(pathStr) {
|
|
16708
17406
|
return pathStr.length === 0 ? [] : pathStr.split(`-`).filter((s) => s.length > 0).map(Number);
|
|
16709
17407
|
}
|
|
@@ -17148,6 +17846,11 @@ function WheelNudgeMixin(superClass) {
|
|
|
17148
17846
|
if (root && root.activeElement !== null) return true;
|
|
17149
17847
|
return false;
|
|
17150
17848
|
}
|
|
17849
|
+
/**
|
|
17850
|
+
* Apply one wheel tick. No-op by default — consumers override this to run
|
|
17851
|
+
* their arrow-key step math (see {@link WheelNudgeConsumer}).
|
|
17852
|
+
*/
|
|
17853
|
+
_applyWheelDelta(_direction, _event) {}
|
|
17151
17854
|
}
|
|
17152
17855
|
__decorate([property({
|
|
17153
17856
|
type: Boolean,
|
|
@@ -26003,7 +26706,7 @@ let NotificationPillElement = class NotificationPillElement extends i {
|
|
|
26003
26706
|
#renderIcon() {
|
|
26004
26707
|
if (!this.icon) return A;
|
|
26005
26708
|
if (this.icon.startsWith(`<svg`)) return b`<span class="pill-icon">${unsafeHTML(this.icon)}</span>`;
|
|
26006
|
-
if (!
|
|
26709
|
+
if (!hasIcon(this.icon)) return A;
|
|
26007
26710
|
return b`<ixfx-icon class="pill-icon" name="${this.icon}"></ixfx-icon>`;
|
|
26008
26711
|
}
|
|
26009
26712
|
render() {
|
|
@@ -26802,7 +27505,7 @@ let TooltipElement = class TooltipElement extends i {
|
|
|
26802
27505
|
#renderIcon() {
|
|
26803
27506
|
if (!this.icon) return A;
|
|
26804
27507
|
if (this.icon.startsWith(`<svg`)) return b`<span class="tooltip-icon" style="display:inline-flex;align-items:center;margin-right:0.25em">${unsafeHTML(this.icon)}</span>`;
|
|
26805
|
-
if (!
|
|
27508
|
+
if (!hasIcon(this.icon)) return A;
|
|
26806
27509
|
return b`<ixfx-icon class="tooltip-icon" style="margin-right:0.25em" name="${this.icon}"></ixfx-icon>`;
|
|
26807
27510
|
}
|
|
26808
27511
|
render() {
|
|
@@ -35813,6 +36516,6 @@ function init() {
|
|
|
35813
36516
|
console.log(`Init`);
|
|
35814
36517
|
}
|
|
35815
36518
|
//#endregion
|
|
35816
|
-
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 };
|
|
36519
|
+
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 };
|
|
35817
36520
|
|
|
35818
36521
|
//# sourceMappingURL=index.js.map
|