@ixfx/components 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/index.d.ts +235 -27
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +799 -124
- package/bundle/index.js.map +1 -1
- package/dist/ac-text.d.ts +1 -1
- package/dist/ac-text.js +1 -1
- package/dist/{ac-token-BAa6ke09.d.ts → ac-token--_vCHXWw.d.ts} +2 -2
- package/dist/{ac-token-BAa6ke09.d.ts.map → ac-token--_vCHXWw.d.ts.map} +1 -1
- package/dist/ac-token.d.ts +1 -1
- package/dist/ac-token.js +1 -1
- package/dist/{button-Dat2yHAU.js → button-DsFbV4XD.js} +3 -3
- package/dist/{button-Dat2yHAU.js.map → button-DsFbV4XD.js.map} +1 -1
- package/dist/button.d.ts.map +1 -1
- package/dist/button.js +1 -1
- package/dist/{colour-picker-BSDBL-QL.js → colour-picker-Ddsk-7E_.js} +2 -2
- package/dist/{colour-picker-BSDBL-QL.js.map → colour-picker-Ddsk-7E_.js.map} +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/crumbs.js +1 -1
- package/dist/{data-grid-C8qIo1-u.d.ts → data-grid-Bh4YrQGK.d.ts} +2 -2
- package/dist/{data-grid-C8qIo1-u.d.ts.map → data-grid-Bh4YrQGK.d.ts.map} +1 -1
- package/dist/data-grid.d.ts +1 -1
- package/dist/data-grid.js +1 -1
- package/dist/{defaults-BkWCPJFL.js → defaults-BJwDsMH4.js} +26 -2
- package/dist/defaults-BJwDsMH4.js.map +1 -0
- package/dist/{hex-editor-Bk6rxhfg.js → hex-editor-BiRfwTz1.js} +2 -2
- package/dist/{hex-editor-Bk6rxhfg.js.map → hex-editor-BiRfwTz1.js.map} +1 -1
- package/dist/hex.js +1 -1
- package/dist/{icon-DpA9x2Ve.d.ts → icon-CDGprSdE.d.ts} +1 -12
- package/dist/icon-CDGprSdE.d.ts.map +1 -0
- package/dist/{icon-CvnPanhA.js → icon-DcEEGQa_.js} +37 -8
- package/dist/icon-DcEEGQa_.js.map +1 -0
- package/dist/icons.d.ts +3 -3
- package/dist/icons.js +4 -4
- package/dist/icons.js.map +1 -1
- package/dist/{index-CKtLhgYj.d.ts → index-9AhaHIT2.d.ts} +3 -3
- package/dist/{index-CKtLhgYj.d.ts.map → index-9AhaHIT2.d.ts.map} +1 -1
- package/dist/{index-BtGFYfaK.d.ts → index-CS9yM8L-.d.ts} +14 -4
- package/dist/index-CS9yM8L-.d.ts.map +1 -0
- package/dist/index.d.ts +223 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +674 -106
- package/dist/index.js.map +1 -1
- package/dist/{menu-BjRvlgPy.js → menu-CXDFJxNm.js} +3 -3
- package/dist/{menu-BjRvlgPy.js.map → menu-CXDFJxNm.js.map} +1 -1
- package/dist/{menu-item-Bs2MYyHq.d.ts → menu-item-CZi9vy5O.d.ts} +4 -2
- package/dist/menu-item-CZi9vy5O.d.ts.map +1 -0
- package/dist/{menu-item-B-zYrTTi.js → menu-item-D2S4i8u1.js} +16 -4
- package/dist/menu-item-D2S4i8u1.js.map +1 -0
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +2 -2
- package/dist/notification-pill-DyZydXoZ.d.ts.map +1 -1
- package/dist/notifier.js +2 -2
- package/dist/notifier.js.map +1 -1
- package/dist/panel.d.ts +1 -1
- package/dist/plots.js +1 -1
- package/dist/registry-BEtb8_wB.js +46 -0
- package/dist/registry-BEtb8_wB.js.map +1 -0
- package/dist/{tab-list-MPwq_1Jj.d.ts → tab-list-CGyPt07Z.d.ts} +9 -2
- package/dist/tab-list-CGyPt07Z.d.ts.map +1 -0
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +32 -13
- package/dist/tabs.js.map +1 -1
- package/dist/{titlebar-DaCfpliv.d.ts → titlebar-Bj5pfdw_.d.ts} +2 -2
- package/dist/{titlebar-DaCfpliv.d.ts.map → titlebar-Bj5pfdw_.d.ts.map} +1 -1
- package/dist/titlebar.d.ts +1 -1
- package/dist/titlebar.js +1 -1
- package/dist/tooltip-BlvBUQxx.d.ts.map +1 -1
- package/dist/{xy-axis-CxHC8z5E.js → xy-axis-DWizs0bt.js} +3 -3
- package/dist/xy-axis-DWizs0bt.js.map +1 -0
- package/docs-user/dock.md +166 -15
- package/docs-user/icons.md +4 -0
- package/docs-user/index.json +2 -2
- package/docs-user/tabs.md +20 -0
- package/docs-user/user-theming.md +109 -0
- package/package.json +1 -1
- package/dist/defaults-BkWCPJFL.js.map +0 -1
- package/dist/icon-CvnPanhA.js.map +0 -1
- package/dist/icon-DpA9x2Ve.d.ts.map +0 -1
- package/dist/index-BtGFYfaK.d.ts.map +0 -1
- package/dist/menu-item-B-zYrTTi.js.map +0 -1
- package/dist/menu-item-Bs2MYyHq.d.ts.map +0 -1
- package/dist/registry-CkW7q09Q.js +0 -21
- package/dist/registry-CkW7q09Q.js.map +0 -1
- package/dist/tab-list-MPwq_1Jj.d.ts.map +0 -1
- package/dist/xy-axis-CxHC8z5E.js.map +0 -1
package/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),
|
|
@@ -12883,7 +12973,7 @@ function onKeyDown(event) {
|
|
|
12883
12973
|
}
|
|
12884
12974
|
function findDropZone(drag, clientX, clientY) {
|
|
12885
12975
|
for (const host of dragHosts) {
|
|
12886
|
-
const zone = host.computeDropZone(clientX, clientY, drag.panelId);
|
|
12976
|
+
const zone = host.computeDropZone(clientX, clientY, drag.panelId, drag.kind);
|
|
12887
12977
|
if (zone !== null) return zone;
|
|
12888
12978
|
}
|
|
12889
12979
|
return null;
|
|
@@ -12971,12 +13061,15 @@ function createEmptyLayout() {
|
|
|
12971
13061
|
root: createEmptyStack()
|
|
12972
13062
|
};
|
|
12973
13063
|
}
|
|
12974
|
-
function createStack(panels, active) {
|
|
12975
|
-
|
|
13064
|
+
function createStack(panels, active, name) {
|
|
13065
|
+
const stack = {
|
|
12976
13066
|
kind: `stack`,
|
|
12977
13067
|
panels: [...panels],
|
|
12978
13068
|
active: active ?? panels[0] ?? ``
|
|
12979
13069
|
};
|
|
13070
|
+
const trimmed = name?.trim();
|
|
13071
|
+
if (trimmed) stack.name = trimmed;
|
|
13072
|
+
return stack;
|
|
12980
13073
|
}
|
|
12981
13074
|
function createSplit(direction, children, sizes) {
|
|
12982
13075
|
const mean = children.length > 0 ? 1 / children.length : 1;
|
|
@@ -12990,7 +13083,8 @@ function createSplit(direction, children, sizes) {
|
|
|
12990
13083
|
/**
|
|
12991
13084
|
* Runs after every mutation:
|
|
12992
13085
|
* - stack `active` fixed to a member of `panels` (or `''` when empty)
|
|
12993
|
-
* - empty stacks removed from their parent split (and their
|
|
13086
|
+
* - empty *unnamed* stacks removed from their parent split (and their
|
|
13087
|
+
* `sizes` entry); named empty stacks are kept so the name stays addressable
|
|
12994
13088
|
* - splits with ≤1 child replaced by that child (spliced into the parent)
|
|
12995
13089
|
* - a child split whose direction equals its parent's is flattened into it,
|
|
12996
13090
|
* spreading its sizes across the slot it vacated
|
|
@@ -12999,13 +13093,14 @@ function createSplit(direction, children, sizes) {
|
|
|
12999
13093
|
*/
|
|
13000
13094
|
function normalize(root) {
|
|
13001
13095
|
if (isStack(root)) {
|
|
13096
|
+
sanitizeStackName(root);
|
|
13002
13097
|
if (!root.panels.includes(root.active)) root.active = root.panels[0] ?? ``;
|
|
13003
13098
|
return root;
|
|
13004
13099
|
}
|
|
13005
13100
|
for (let i = root.children.length - 1; i >= 0; i--) {
|
|
13006
13101
|
normalize(root.children[i]);
|
|
13007
13102
|
const child = root.children[i];
|
|
13008
|
-
if (isStack(child) && child.panels.length === 0) {
|
|
13103
|
+
if (isStack(child) && child.panels.length === 0 && !child.name) {
|
|
13009
13104
|
root.children.splice(i, 1);
|
|
13010
13105
|
root.sizes.splice(i, 1);
|
|
13011
13106
|
continue;
|
|
@@ -13039,17 +13134,36 @@ function replaceNodeContents(target, source) {
|
|
|
13039
13134
|
const t = target;
|
|
13040
13135
|
t.panels = source.panels;
|
|
13041
13136
|
t.active = source.active;
|
|
13137
|
+
if (source.name !== void 0) t.name = source.name;
|
|
13138
|
+
else delete t.name;
|
|
13139
|
+
if (source.hideLabels !== void 0) t.hideLabels = source.hideLabels;
|
|
13140
|
+
else delete t.hideLabels;
|
|
13042
13141
|
delete t.collapsed;
|
|
13043
13142
|
delete t.preCollapseSize;
|
|
13143
|
+
if (source.collapsed !== void 0) t.collapsed = source.collapsed;
|
|
13144
|
+
if (source.preCollapseSize !== void 0) t.preCollapseSize = source.preCollapseSize;
|
|
13044
13145
|
} else {
|
|
13045
13146
|
target.kind = `split`;
|
|
13046
13147
|
const t = target;
|
|
13047
13148
|
t.direction = source.direction;
|
|
13048
13149
|
t.children = source.children;
|
|
13049
13150
|
t.sizes = source.sizes;
|
|
13151
|
+
const stale = t;
|
|
13152
|
+
delete stale.panels;
|
|
13153
|
+
delete stale.active;
|
|
13154
|
+
delete stale.name;
|
|
13155
|
+
delete stale.collapsed;
|
|
13156
|
+
delete stale.preCollapseSize;
|
|
13050
13157
|
}
|
|
13051
13158
|
return target;
|
|
13052
13159
|
}
|
|
13160
|
+
/** Trim `name`; drop it when empty / non-string so lookups stay predictable. */
|
|
13161
|
+
function sanitizeStackName(stack) {
|
|
13162
|
+
if (stack.name === void 0) return;
|
|
13163
|
+
const trimmed = typeof stack.name === `string` ? stack.name.trim() : ``;
|
|
13164
|
+
if (trimmed) stack.name = trimmed;
|
|
13165
|
+
else delete stack.name;
|
|
13166
|
+
}
|
|
13053
13167
|
function findNode(root, path) {
|
|
13054
13168
|
let current = root;
|
|
13055
13169
|
for (const index of path) {
|
|
@@ -13083,6 +13197,36 @@ function collectPanelIds(root) {
|
|
|
13083
13197
|
return root.children.flatMap(collectPanelIds);
|
|
13084
13198
|
}
|
|
13085
13199
|
/**
|
|
13200
|
+
* Path of the first stack (tree order) whose `name` matches, or null.
|
|
13201
|
+
* Names are expected to be unique per layout; duplicates resolve first-wins.
|
|
13202
|
+
*/
|
|
13203
|
+
function findStackPathByName(root, name) {
|
|
13204
|
+
const wanted = name.trim();
|
|
13205
|
+
if (!wanted) return null;
|
|
13206
|
+
return findStackPathByNameInner(root, wanted, []);
|
|
13207
|
+
}
|
|
13208
|
+
function findStackPathByNameInner(node, name, path) {
|
|
13209
|
+
if (isStack(node)) return node.name === name ? path : null;
|
|
13210
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
13211
|
+
const found = findStackPathByNameInner(node.children[i], name, [...path, i]);
|
|
13212
|
+
if (found !== null) return found;
|
|
13213
|
+
}
|
|
13214
|
+
return null;
|
|
13215
|
+
}
|
|
13216
|
+
/** Names of every named stack leaf, in tree order. */
|
|
13217
|
+
function collectContainerNames(root) {
|
|
13218
|
+
const out = [];
|
|
13219
|
+
const visit = (node) => {
|
|
13220
|
+
if (isStack(node)) {
|
|
13221
|
+
if (node.name) out.push(node.name);
|
|
13222
|
+
return;
|
|
13223
|
+
}
|
|
13224
|
+
node.children.forEach(visit);
|
|
13225
|
+
};
|
|
13226
|
+
visit(root);
|
|
13227
|
+
return out;
|
|
13228
|
+
}
|
|
13229
|
+
/**
|
|
13086
13230
|
* Paths of every stack leaf, in tree order. `[]` is the root itself when it
|
|
13087
13231
|
* is a stack.
|
|
13088
13232
|
*/
|
|
@@ -13197,6 +13341,251 @@ function extractStack(root, stackPath, fallbackStack) {
|
|
|
13197
13341
|
normalize(root);
|
|
13198
13342
|
return stack;
|
|
13199
13343
|
}
|
|
13344
|
+
/** Tolerance for edge-touch comparisons in unit-square geometry. */
|
|
13345
|
+
const GEOM_EPS = 1e-9;
|
|
13346
|
+
function pathsEqual(a, b) {
|
|
13347
|
+
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
13348
|
+
}
|
|
13349
|
+
/**
|
|
13350
|
+
* Resolve a {@link DockLeafRef} to a stack path. Strings match container
|
|
13351
|
+
* `name` first-wins; arrays must index an existing stack leaf. Returns null
|
|
13352
|
+
* when unresolvable.
|
|
13353
|
+
*/
|
|
13354
|
+
function resolveLeafPath(root, ref) {
|
|
13355
|
+
if (typeof ref === `string`) return findStackPathByName(root, ref);
|
|
13356
|
+
if (!Array.isArray(ref)) return null;
|
|
13357
|
+
let current = root;
|
|
13358
|
+
for (const index of ref) {
|
|
13359
|
+
if (!Number.isInteger(index) || index < 0) return null;
|
|
13360
|
+
if (!isSplit(current)) return null;
|
|
13361
|
+
const next = current.children[index];
|
|
13362
|
+
if (!next) return null;
|
|
13363
|
+
current = next;
|
|
13364
|
+
}
|
|
13365
|
+
return isStack(current) ? [...ref] : null;
|
|
13366
|
+
}
|
|
13367
|
+
/** Absolute (unit-square) rect of every stack leaf, in tree order. */
|
|
13368
|
+
function computeLeafGeoms(root) {
|
|
13369
|
+
const out = [];
|
|
13370
|
+
const visit = (node, path, rect) => {
|
|
13371
|
+
if (isStack(node)) {
|
|
13372
|
+
out.push({
|
|
13373
|
+
path,
|
|
13374
|
+
name: node.name ?? null,
|
|
13375
|
+
rect
|
|
13376
|
+
});
|
|
13377
|
+
return;
|
|
13378
|
+
}
|
|
13379
|
+
const total = node.sizes.reduce((sum, w) => sum + w, 0);
|
|
13380
|
+
const safe = total > 0 ? total : node.children.length || 1;
|
|
13381
|
+
let offset = 0;
|
|
13382
|
+
node.children.forEach((child, i) => {
|
|
13383
|
+
const frac = (node.sizes[i] ?? safe / node.children.length) / safe;
|
|
13384
|
+
if (node.direction === `row`) {
|
|
13385
|
+
const w = rect.w * frac;
|
|
13386
|
+
visit(child, [...path, i], {
|
|
13387
|
+
x: rect.x + offset,
|
|
13388
|
+
y: rect.y,
|
|
13389
|
+
w,
|
|
13390
|
+
h: rect.h
|
|
13391
|
+
});
|
|
13392
|
+
offset += w;
|
|
13393
|
+
} else {
|
|
13394
|
+
const h = rect.h * frac;
|
|
13395
|
+
visit(child, [...path, i], {
|
|
13396
|
+
x: rect.x,
|
|
13397
|
+
y: rect.y + offset,
|
|
13398
|
+
w: rect.w,
|
|
13399
|
+
h
|
|
13400
|
+
});
|
|
13401
|
+
offset += h;
|
|
13402
|
+
}
|
|
13403
|
+
});
|
|
13404
|
+
};
|
|
13405
|
+
visit(root, [], {
|
|
13406
|
+
x: 0,
|
|
13407
|
+
y: 0,
|
|
13408
|
+
w: 1,
|
|
13409
|
+
h: 1
|
|
13410
|
+
});
|
|
13411
|
+
return out;
|
|
13412
|
+
}
|
|
13413
|
+
function intervalOverlap(a0, a1, b0, b1) {
|
|
13414
|
+
return Math.min(a1, b1) - Math.max(a0, b0);
|
|
13415
|
+
}
|
|
13416
|
+
/**
|
|
13417
|
+
* Geometric neighbours of a leaf — what the user sees above / below / left /
|
|
13418
|
+
* right of it. Each list holds `{ name, path }` (name is null for unnamed
|
|
13419
|
+
* leaves), ordered most-overlapping first. Returns null when `ref` is
|
|
13420
|
+
* unresolvable.
|
|
13421
|
+
*/
|
|
13422
|
+
function getLeafNeighbours(root, ref) {
|
|
13423
|
+
const path = resolveLeafPath(root, ref);
|
|
13424
|
+
if (path === null) return null;
|
|
13425
|
+
const geoms = computeLeafGeoms(root);
|
|
13426
|
+
const self = geoms.find((g) => pathsEqual(g.path, path));
|
|
13427
|
+
if (!self) return null;
|
|
13428
|
+
const s = self.rect;
|
|
13429
|
+
const above = [];
|
|
13430
|
+
const below = [];
|
|
13431
|
+
const left = [];
|
|
13432
|
+
const right = [];
|
|
13433
|
+
geoms.forEach((g, order) => {
|
|
13434
|
+
if (g === self) return;
|
|
13435
|
+
const r = g.rect;
|
|
13436
|
+
const info = {
|
|
13437
|
+
name: g.name,
|
|
13438
|
+
path: [...g.path]
|
|
13439
|
+
};
|
|
13440
|
+
if (Math.abs(r.y + r.h - s.y) < GEOM_EPS) {
|
|
13441
|
+
const o = intervalOverlap(r.x, r.x + r.w, s.x, s.x + s.w);
|
|
13442
|
+
if (o > GEOM_EPS) above.push({
|
|
13443
|
+
info,
|
|
13444
|
+
overlap: o,
|
|
13445
|
+
order
|
|
13446
|
+
});
|
|
13447
|
+
}
|
|
13448
|
+
if (Math.abs(s.y + s.h - r.y) < GEOM_EPS) {
|
|
13449
|
+
const o = intervalOverlap(r.x, r.x + r.w, s.x, s.x + s.w);
|
|
13450
|
+
if (o > GEOM_EPS) below.push({
|
|
13451
|
+
info,
|
|
13452
|
+
overlap: o,
|
|
13453
|
+
order
|
|
13454
|
+
});
|
|
13455
|
+
}
|
|
13456
|
+
if (Math.abs(r.x + r.w - s.x) < GEOM_EPS) {
|
|
13457
|
+
const o = intervalOverlap(r.y, r.y + r.h, s.y, s.y + s.h);
|
|
13458
|
+
if (o > GEOM_EPS) left.push({
|
|
13459
|
+
info,
|
|
13460
|
+
overlap: o,
|
|
13461
|
+
order
|
|
13462
|
+
});
|
|
13463
|
+
}
|
|
13464
|
+
if (Math.abs(s.x + s.w - r.x) < GEOM_EPS) {
|
|
13465
|
+
const o = intervalOverlap(r.y, r.y + r.h, s.y, s.y + s.h);
|
|
13466
|
+
if (o > GEOM_EPS) right.push({
|
|
13467
|
+
info,
|
|
13468
|
+
overlap: o,
|
|
13469
|
+
order
|
|
13470
|
+
});
|
|
13471
|
+
}
|
|
13472
|
+
});
|
|
13473
|
+
const rank = (hits) => hits.sort((a, b) => b.overlap - a.overlap || a.order - b.order).map((h) => h.info);
|
|
13474
|
+
return {
|
|
13475
|
+
above: rank(above),
|
|
13476
|
+
below: rank(below),
|
|
13477
|
+
left: rank(left),
|
|
13478
|
+
right: rank(right)
|
|
13479
|
+
};
|
|
13480
|
+
}
|
|
13481
|
+
/**
|
|
13482
|
+
* Exchange two leaves' positions in the tree, each carrying its size slot so
|
|
13483
|
+
* the remaining structure and proportions are untouched. Returns false when
|
|
13484
|
+
* either path is not a stack leaf.
|
|
13485
|
+
*/
|
|
13486
|
+
function swapLeaves(root, pathA, pathB) {
|
|
13487
|
+
if (pathsEqual(pathA, pathB)) return false;
|
|
13488
|
+
const nodeA = findNode(root, pathA);
|
|
13489
|
+
const nodeB = findNode(root, pathB);
|
|
13490
|
+
if (nodeA === null || nodeB === null || !isStack(nodeA) || !isStack(nodeB)) return false;
|
|
13491
|
+
const parentA = findParentOf(root, pathA);
|
|
13492
|
+
const parentB = findParentOf(root, pathB);
|
|
13493
|
+
if (parentA === null || parentB === null) return false;
|
|
13494
|
+
const indexA = pathA[pathA.length - 1];
|
|
13495
|
+
const indexB = pathB[pathB.length - 1];
|
|
13496
|
+
if (parentA === parentB) {
|
|
13497
|
+
const p = parentA;
|
|
13498
|
+
[p.children[indexA], p.children[indexB]] = [p.children[indexB], p.children[indexA]];
|
|
13499
|
+
[p.sizes[indexA], p.sizes[indexB]] = [p.sizes[indexB] ?? 1, p.sizes[indexA] ?? 1];
|
|
13500
|
+
return true;
|
|
13501
|
+
}
|
|
13502
|
+
const carriedNode = parentA.children[indexA];
|
|
13503
|
+
const carriedSize = parentA.sizes[indexA] ?? 1;
|
|
13504
|
+
parentA.children[indexA] = parentB.children[indexB];
|
|
13505
|
+
parentA.sizes[indexA] = parentB.sizes[indexB] ?? 1;
|
|
13506
|
+
parentB.children[indexB] = carriedNode;
|
|
13507
|
+
parentB.sizes[indexB] = carriedSize;
|
|
13508
|
+
return true;
|
|
13509
|
+
}
|
|
13510
|
+
/**
|
|
13511
|
+
* Move a leaf one position in `direction`, swapping with the neighbour it
|
|
13512
|
+
* shares the longest edge with. Returns false when `ref` is unresolvable or
|
|
13513
|
+
* nothing adjoins in that direction. Run `normalize()` afterwards.
|
|
13514
|
+
*/
|
|
13515
|
+
function moveLeafStep(root, ref, direction) {
|
|
13516
|
+
const path = resolveLeafPath(root, ref);
|
|
13517
|
+
if (path === null) return false;
|
|
13518
|
+
const neighbours = getLeafNeighbours(root, ref);
|
|
13519
|
+
if (neighbours === null) return false;
|
|
13520
|
+
const list = direction === `up` ? neighbours.above : direction === `down` ? neighbours.below : direction === `left` ? neighbours.left : neighbours.right;
|
|
13521
|
+
if (list.length === 0) return false;
|
|
13522
|
+
return swapLeaves(root, path, list[0].path);
|
|
13523
|
+
}
|
|
13524
|
+
/**
|
|
13525
|
+
* Move a leaf to an edge of the overall layout, keeping the rest of the
|
|
13526
|
+
* tree intact as a single subtree and preserving the leaf's absolute size
|
|
13527
|
+
* along the target axis. Returns false when `ref` is unresolvable or the
|
|
13528
|
+
* layout holds a single leaf. Run `normalize()` afterwards (also safe to
|
|
13529
|
+
* call it twice — the host's commit does).
|
|
13530
|
+
*/
|
|
13531
|
+
function moveLeafNodeToEdge(root, ref, edge) {
|
|
13532
|
+
const path = resolveLeafPath(root, ref);
|
|
13533
|
+
if (path === null || path.length === 0) return false;
|
|
13534
|
+
const self = computeLeafGeoms(root).find((g) => pathsEqual(g.path, path));
|
|
13535
|
+
if (!self) return false;
|
|
13536
|
+
const horizontal = edge === `left` || edge === `right`;
|
|
13537
|
+
const frac = Math.min(Math.max(horizontal ? self.rect.w : self.rect.h, .05), .95);
|
|
13538
|
+
if (!(frac > 0) || frac >= 1) return false;
|
|
13539
|
+
const parent = findParentOf(root, path);
|
|
13540
|
+
if (parent === null) return false;
|
|
13541
|
+
const [node] = parent.children.splice(path[path.length - 1], 1);
|
|
13542
|
+
parent.sizes.splice(path[path.length - 1], 1);
|
|
13543
|
+
if (!node) return false;
|
|
13544
|
+
normalize(root);
|
|
13545
|
+
const atStart = edge === `left` || edge === `top`;
|
|
13546
|
+
const direction = horizontal ? `row` : `column`;
|
|
13547
|
+
if (isStack(root)) {
|
|
13548
|
+
const rest = root;
|
|
13549
|
+
const remaining = {
|
|
13550
|
+
kind: `stack`,
|
|
13551
|
+
panels: [...rest.panels],
|
|
13552
|
+
active: rest.active
|
|
13553
|
+
};
|
|
13554
|
+
if (rest.name !== void 0) remaining.name = rest.name;
|
|
13555
|
+
if (rest.collapsed !== void 0) remaining.collapsed = rest.collapsed;
|
|
13556
|
+
if (rest.preCollapseSize !== void 0) remaining.preCollapseSize = rest.preCollapseSize;
|
|
13557
|
+
replaceNodeContents(root, {
|
|
13558
|
+
kind: `split`,
|
|
13559
|
+
direction,
|
|
13560
|
+
children: atStart ? [node, remaining] : [remaining, node],
|
|
13561
|
+
sizes: atStart ? [frac, 1 - frac] : [1 - frac, frac]
|
|
13562
|
+
});
|
|
13563
|
+
return true;
|
|
13564
|
+
}
|
|
13565
|
+
if (isSplit(root) && root.direction === direction) {
|
|
13566
|
+
const size = frac * (root.sizes.reduce((total, w) => total + w, 0) || root.children.length || 1) / (1 - frac);
|
|
13567
|
+
const at = atStart ? 0 : root.children.length;
|
|
13568
|
+
root.children.splice(at, 0, node);
|
|
13569
|
+
root.sizes.splice(at, 0, size);
|
|
13570
|
+
return true;
|
|
13571
|
+
}
|
|
13572
|
+
if (isSplit(root)) {
|
|
13573
|
+
const inner = {
|
|
13574
|
+
kind: `split`,
|
|
13575
|
+
direction: root.direction,
|
|
13576
|
+
children: root.children,
|
|
13577
|
+
sizes: root.sizes
|
|
13578
|
+
};
|
|
13579
|
+
replaceNodeContents(root, {
|
|
13580
|
+
kind: `split`,
|
|
13581
|
+
direction,
|
|
13582
|
+
children: atStart ? [node, inner] : [inner, node],
|
|
13583
|
+
sizes: atStart ? [frac, 1 - frac] : [1 - frac, frac]
|
|
13584
|
+
});
|
|
13585
|
+
return true;
|
|
13586
|
+
}
|
|
13587
|
+
return false;
|
|
13588
|
+
}
|
|
13200
13589
|
function serializeLayout(root) {
|
|
13201
13590
|
return {
|
|
13202
13591
|
version: 1,
|
|
@@ -13210,6 +13599,8 @@ function serializeNode(node) {
|
|
|
13210
13599
|
panels: [...node.panels],
|
|
13211
13600
|
active: node.active
|
|
13212
13601
|
};
|
|
13602
|
+
if (node.name !== void 0) out.name = node.name;
|
|
13603
|
+
if (node.hideLabels !== void 0) out.hideLabels = node.hideLabels;
|
|
13213
13604
|
if (node.collapsed !== void 0) out.collapsed = node.collapsed;
|
|
13214
13605
|
if (node.preCollapseSize !== void 0) out.preCollapseSize = node.preCollapseSize;
|
|
13215
13606
|
return out;
|
|
@@ -14259,14 +14650,12 @@ let TabListElement = class TabListElement extends i {
|
|
|
14259
14650
|
if (!el) return;
|
|
14260
14651
|
if (!this.#isVertical) {
|
|
14261
14652
|
if (event.deltaY === 0 || el.scrollWidth <= el.clientWidth) return;
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
if (el.scrollLeft !== prev) event.preventDefault();
|
|
14653
|
+
el.scrollLeft = el.scrollLeft + event.deltaY;
|
|
14654
|
+
event.preventDefault();
|
|
14265
14655
|
} else {
|
|
14266
14656
|
if (event.deltaX === 0 || el.scrollHeight <= el.clientHeight) return;
|
|
14267
|
-
|
|
14268
|
-
|
|
14269
|
-
if (el.scrollTop !== prev) event.preventDefault();
|
|
14657
|
+
el.scrollTop = el.scrollTop + event.deltaX;
|
|
14658
|
+
event.preventDefault();
|
|
14270
14659
|
}
|
|
14271
14660
|
};
|
|
14272
14661
|
this.#onSlotClick = (event) => {
|
|
@@ -14353,8 +14742,11 @@ let TabListElement = class TabListElement extends i {
|
|
|
14353
14742
|
return this.orientation === `vertical-stack` || this.orientation === `vertical-side`;
|
|
14354
14743
|
}
|
|
14355
14744
|
get #containerClass() {
|
|
14356
|
-
|
|
14357
|
-
|
|
14745
|
+
return [
|
|
14746
|
+
this.scrollable ? `${this.orientation} scrollable` : this.orientation,
|
|
14747
|
+
this.scrollable ? this.#isVertical ? `scroll-fade-y` : `scroll-fade-x` : ``,
|
|
14748
|
+
this.dragMode === `reorder` ? `reorder` : ``
|
|
14749
|
+
].filter(Boolean).join(` `);
|
|
14358
14750
|
}
|
|
14359
14751
|
render() {
|
|
14360
14752
|
const caretStyle = this.#computeCaretStyle();
|
|
@@ -14636,7 +15028,11 @@ let TabListElement = class TabListElement extends i {
|
|
|
14636
15028
|
return this.shadowRoot?.getElementById(`container`) ?? null;
|
|
14637
15029
|
}
|
|
14638
15030
|
static {
|
|
14639
|
-
this.styles = [
|
|
15031
|
+
this.styles = [
|
|
15032
|
+
themeFallbacks,
|
|
15033
|
+
scrollFadeStyles(`x`),
|
|
15034
|
+
scrollFadeStyles(`y`),
|
|
15035
|
+
i$3`
|
|
14640
15036
|
:host {
|
|
14641
15037
|
display: inline-block;
|
|
14642
15038
|
--activated-colour: var(--accent);
|
|
@@ -14719,8 +15115,12 @@ let TabListElement = class TabListElement extends i {
|
|
|
14719
15115
|
overflow: hidden;
|
|
14720
15116
|
}
|
|
14721
15117
|
#container.horizontal.scrollable {
|
|
14722
|
-
overflow-x:
|
|
15118
|
+
overflow-x: scroll;
|
|
14723
15119
|
--allow-overflow: visible;
|
|
15120
|
+
scrollbar-width: none;
|
|
15121
|
+
}
|
|
15122
|
+
#container.horizontal.scrollable::-webkit-scrollbar {
|
|
15123
|
+
display: none;
|
|
14724
15124
|
}
|
|
14725
15125
|
#container.horizontal.scrollable ::slotted(ixfx-tab-list-item) {
|
|
14726
15126
|
flex-shrink: 0;
|
|
@@ -14733,9 +15133,13 @@ let TabListElement = class TabListElement extends i {
|
|
|
14733
15133
|
flex-direction: column-reverse;
|
|
14734
15134
|
}
|
|
14735
15135
|
#container.vertical-stack.scrollable {
|
|
14736
|
-
overflow-y:
|
|
15136
|
+
overflow-y: scroll;
|
|
14737
15137
|
max-height: var(--scroll-max-height);
|
|
14738
15138
|
--allow-overflow: visible;
|
|
15139
|
+
scrollbar-width: none;
|
|
15140
|
+
}
|
|
15141
|
+
#container.vertical-stack.scrollable::-webkit-scrollbar {
|
|
15142
|
+
display: none;
|
|
14739
15143
|
}
|
|
14740
15144
|
#container.vertical-side {
|
|
14741
15145
|
flex-direction: column;
|
|
@@ -14745,9 +15149,13 @@ let TabListElement = class TabListElement extends i {
|
|
|
14745
15149
|
flex-direction: column-reverse;
|
|
14746
15150
|
}
|
|
14747
15151
|
#container.vertical-side.scrollable {
|
|
14748
|
-
overflow-y:
|
|
15152
|
+
overflow-y: scroll;
|
|
14749
15153
|
max-height: var(--scroll-max-height);
|
|
14750
15154
|
--allow-overflow: visible;
|
|
15155
|
+
scrollbar-width: none;
|
|
15156
|
+
}
|
|
15157
|
+
#container.vertical-side.scrollable::-webkit-scrollbar {
|
|
15158
|
+
display: none;
|
|
14751
15159
|
}
|
|
14752
15160
|
|
|
14753
15161
|
::slotted(ixfx-tab-list-item) {
|
|
@@ -14790,7 +15198,8 @@ let TabListElement = class TabListElement extends i {
|
|
|
14790
15198
|
::slotted(.activated) {
|
|
14791
15199
|
--colour: var(--activated-colour);
|
|
14792
15200
|
}
|
|
14793
|
-
`
|
|
15201
|
+
`
|
|
15202
|
+
];
|
|
14794
15203
|
}
|
|
14795
15204
|
};
|
|
14796
15205
|
__decorate([property({ type: String })], TabListElement.prototype, "orientation", void 0);
|
|
@@ -14822,9 +15231,13 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14822
15231
|
this.registry = void 0;
|
|
14823
15232
|
this._compact = false;
|
|
14824
15233
|
this._hasToolbarContent = false;
|
|
15234
|
+
this._labelObserver = null;
|
|
14825
15235
|
this._onToolbarSlotChange = () => {
|
|
14826
15236
|
this._updateToolbarContent();
|
|
14827
15237
|
};
|
|
15238
|
+
this._onLabelSlotChange = () => {
|
|
15239
|
+
this._syncLabelSizer();
|
|
15240
|
+
};
|
|
14828
15241
|
}
|
|
14829
15242
|
connectedCallback() {
|
|
14830
15243
|
super.connectedCallback();
|
|
@@ -14833,6 +15246,13 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14833
15246
|
});
|
|
14834
15247
|
this._resizeObserver.observe(this);
|
|
14835
15248
|
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.addEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
15249
|
+
(this.shadowRoot?.querySelector(`slot:not([name])`))?.addEventListener(`slotchange`, this._onLabelSlotChange);
|
|
15250
|
+
this._labelObserver = new MutationObserver(() => this._syncLabelSizer());
|
|
15251
|
+
this._labelObserver.observe(this, {
|
|
15252
|
+
childList: true,
|
|
15253
|
+
characterData: true,
|
|
15254
|
+
subtree: true
|
|
15255
|
+
});
|
|
14836
15256
|
}
|
|
14837
15257
|
disconnectedCallback() {
|
|
14838
15258
|
super.disconnectedCallback();
|
|
@@ -14840,7 +15260,10 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14840
15260
|
this._resizeObserver = void 0;
|
|
14841
15261
|
this._commandCleanup?.();
|
|
14842
15262
|
this._commandCleanup = void 0;
|
|
15263
|
+
this._labelObserver?.disconnect();
|
|
15264
|
+
this._labelObserver = null;
|
|
14843
15265
|
(this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.removeEventListener(`slotchange`, this._onToolbarSlotChange);
|
|
15266
|
+
(this.shadowRoot?.querySelector(`slot:not([name])`))?.removeEventListener(`slotchange`, this._onLabelSlotChange);
|
|
14844
15267
|
}
|
|
14845
15268
|
updated(changed) {
|
|
14846
15269
|
super.updated(changed);
|
|
@@ -14863,12 +15286,25 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14863
15286
|
if (id === this.commandActive) this.click();
|
|
14864
15287
|
});
|
|
14865
15288
|
}
|
|
15289
|
+
/**
|
|
15290
|
+
* Mirror the default-slot text into `.label[data-label]` for the `::after`
|
|
15291
|
+
* width sizer. Slotted toolbar content is excluded.
|
|
15292
|
+
*/
|
|
15293
|
+
_syncLabelSizer() {
|
|
15294
|
+
const slot = this.shadowRoot?.querySelector(`slot:not([name])`);
|
|
15295
|
+
const label = this.shadowRoot?.querySelector(`.label`);
|
|
15296
|
+
if (slot === null || slot === void 0 || label === null || label === void 0) return;
|
|
15297
|
+
const text = slot.assignedNodes().filter((node) => !(node instanceof HTMLElement && node.hasAttribute(`slot`))).map((node) => node.textContent ?? ``).join(``);
|
|
15298
|
+
if (text) label.setAttribute(`data-label`, text);
|
|
15299
|
+
else label.removeAttribute(`data-label`);
|
|
15300
|
+
}
|
|
14866
15301
|
_updateToolbarContent() {
|
|
14867
15302
|
const slot = this.shadowRoot?.querySelector(`slot[name="toolbar"]`);
|
|
14868
15303
|
this._hasToolbarContent = (slot?.assignedElements().length ?? 0) > 0;
|
|
14869
15304
|
}
|
|
14870
15305
|
firstUpdated() {
|
|
14871
15306
|
this._updateToolbarContent();
|
|
15307
|
+
this._syncLabelSizer();
|
|
14872
15308
|
}
|
|
14873
15309
|
render() {
|
|
14874
15310
|
const showLabel = !this.hideLabels || !this.iconName;
|
|
@@ -14910,15 +15346,21 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14910
15346
|
i$3`
|
|
14911
15347
|
:host {
|
|
14912
15348
|
--selected-tab-color: var(--ixfx-selected-tab-color, var(--surface-2-text));
|
|
15349
|
+
--selected-tab-icon-color: var(--ixfx-selected-tab-icon-color, var(--accent));
|
|
14913
15350
|
--selected-tab-border-width: var(--ixfx-selected-tab-border-width, 1px);
|
|
14914
15351
|
--selected-tab-weight: var(--ixfx-selected-tab-weight, bold);
|
|
15352
|
+
--selected-tab-text-decoration: var(--ixfx-selected-tab-text-decoration, none);
|
|
15353
|
+
--selected-tab-text-decoration-color: var(--ixfx-selected-tab-text-decoration-color, var(--accent));
|
|
15354
|
+
--selected-tab-text-decoration-thickness: var(--ixfx-selected-tab-text-decoration-thickness, 0px);
|
|
15355
|
+
--selected-tab-text-decoration-offset: var(--ixfx-selected-tab-text-decoration-offset, 2px);
|
|
14915
15356
|
--selected-background: var(--ixfx-tab-selected-background, var(--surface-5));
|
|
14916
15357
|
--tab-padding: var(--ixfx-tab-padding, var(--space-s));
|
|
14917
15358
|
display: inline-flex;
|
|
14918
15359
|
min-width: 0;
|
|
14919
15360
|
user-select: none;
|
|
14920
15361
|
box-sizing: border-box;
|
|
14921
|
-
font-size: var(--text-
|
|
15362
|
+
font-size: var(--text-m);
|
|
15363
|
+
color: inherit;
|
|
14922
15364
|
/* No explicit height: items stretch (align-self: stretch via the
|
|
14923
15365
|
list's flex container) so every tab in a strip shares one height
|
|
14924
15366
|
even when toolbars give some items taller content. */
|
|
@@ -14933,7 +15375,18 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14933
15375
|
font-weight: 500;
|
|
14934
15376
|
border: var(--selected-tab-border-width) solid transparent;
|
|
14935
15377
|
gap: var(--space-s);
|
|
15378
|
+
transition: background var(--transition, 0.15s);
|
|
15379
|
+
}
|
|
14936
15380
|
|
|
15381
|
+
/* Hover ("tickle") background. When --ixfx-tab-hover-background is
|
|
15382
|
+
unset each state keeps its resting background, so existing
|
|
15383
|
+
consumers see no change. */
|
|
15384
|
+
:host(:hover:not([selected])) > div {
|
|
15385
|
+
background: var(--ixfx-tab-hover-background, transparent);
|
|
15386
|
+
}
|
|
15387
|
+
|
|
15388
|
+
:host([selected]:hover) > div {
|
|
15389
|
+
background: var(--ixfx-tab-hover-background, var(--selected-background));
|
|
14937
15390
|
}
|
|
14938
15391
|
|
|
14939
15392
|
:host([selected]) > div {
|
|
@@ -14947,6 +15400,13 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14947
15400
|
border-radius:var(--radius-m);
|
|
14948
15401
|
}
|
|
14949
15402
|
|
|
15403
|
+
:host([selected]) .label {
|
|
15404
|
+
text-decoration: var(--selected-tab-text-decoration);
|
|
15405
|
+
text-decoration-color: var(--selected-tab-text-decoration-color);
|
|
15406
|
+
text-decoration-thickness: var(--selected-tab-text-decoration-thickness);
|
|
15407
|
+
text-decoration-offset: var(--selected-tab-text-decoration-offset);
|
|
15408
|
+
}
|
|
15409
|
+
|
|
14950
15410
|
.label {
|
|
14951
15411
|
overflow: var(--allow-overflow, hidden);
|
|
14952
15412
|
text-overflow: ellipsis;
|
|
@@ -14955,10 +15415,29 @@ let TabListItemElement = class TabListItemElement extends i {
|
|
|
14955
15415
|
flex: 1 1 auto;
|
|
14956
15416
|
}
|
|
14957
15417
|
|
|
15418
|
+
/* Width sizer: an invisible copy of the label, always set at the
|
|
15419
|
+
selected weight, so activating a tab (bold) never shifts the strip.
|
|
15420
|
+
Text is mirrored from the default slot via the data-label attribute
|
|
15421
|
+
(see _syncLabelSizer). */
|
|
15422
|
+
.label::after {
|
|
15423
|
+
content: attr(data-label);
|
|
15424
|
+
display: block;
|
|
15425
|
+
height: 0;
|
|
15426
|
+
overflow: hidden;
|
|
15427
|
+
visibility: hidden;
|
|
15428
|
+
font-weight: var(--selected-tab-weight);
|
|
15429
|
+
}
|
|
15430
|
+
|
|
14958
15431
|
ixfx-icon {
|
|
14959
15432
|
flex-shrink: 0;
|
|
14960
15433
|
}
|
|
14961
15434
|
|
|
15435
|
+
/* Leading icon (direct child — not close/toolbar icons) picks up the
|
|
15436
|
+
accent when selected, so icon-only tabs read as active. */
|
|
15437
|
+
:host([selected]) > div > ixfx-icon {
|
|
15438
|
+
color: var(--selected-tab-icon-color);
|
|
15439
|
+
}
|
|
15440
|
+
|
|
14962
15441
|
.actions {
|
|
14963
15442
|
display: flex;
|
|
14964
15443
|
align-items: center;
|
|
@@ -15370,6 +15849,7 @@ let DockPanelElement = class DockPanelElement extends i {
|
|
|
15370
15849
|
this.iconName = ``;
|
|
15371
15850
|
this.closable = false;
|
|
15372
15851
|
this.collapsible = false;
|
|
15852
|
+
this.kind = `any`;
|
|
15373
15853
|
this.accepts = ``;
|
|
15374
15854
|
this.presentation = `solo`;
|
|
15375
15855
|
this._collapsed = false;
|
|
@@ -15423,6 +15903,10 @@ let DockPanelElement = class DockPanelElement extends i {
|
|
|
15423
15903
|
}
|
|
15424
15904
|
connectedCallback() {
|
|
15425
15905
|
super.connectedCallback();
|
|
15906
|
+
if (this.hasAttribute(`title`)) {
|
|
15907
|
+
if (!this.hasAttribute(`label`)) this.title = this.getAttribute(`title`) ?? ``;
|
|
15908
|
+
this.removeAttribute(`title`);
|
|
15909
|
+
}
|
|
15426
15910
|
this.addEventListener(`pointerdown`, this._onPointerDown);
|
|
15427
15911
|
}
|
|
15428
15912
|
disconnectedCallback() {
|
|
@@ -15514,13 +15998,20 @@ __decorate([property({
|
|
|
15514
15998
|
attribute: `panel-id`,
|
|
15515
15999
|
reflect: true
|
|
15516
16000
|
})], DockPanelElement.prototype, "panelId", void 0);
|
|
15517
|
-
__decorate([property({
|
|
16001
|
+
__decorate([property({
|
|
16002
|
+
type: String,
|
|
16003
|
+
attribute: `label`
|
|
16004
|
+
})], DockPanelElement.prototype, "title", void 0);
|
|
15518
16005
|
__decorate([property({
|
|
15519
16006
|
type: String,
|
|
15520
16007
|
attribute: `icon-name`
|
|
15521
16008
|
})], DockPanelElement.prototype, "iconName", void 0);
|
|
15522
16009
|
__decorate([property({ type: Boolean })], DockPanelElement.prototype, "closable", void 0);
|
|
15523
16010
|
__decorate([property({ type: Boolean })], DockPanelElement.prototype, "collapsible", void 0);
|
|
16011
|
+
__decorate([property({
|
|
16012
|
+
type: String,
|
|
16013
|
+
reflect: true
|
|
16014
|
+
})], DockPanelElement.prototype, "kind", void 0);
|
|
15524
16015
|
__decorate([property({
|
|
15525
16016
|
type: String,
|
|
15526
16017
|
attribute: `accepts`
|
|
@@ -15552,13 +16043,16 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15552
16043
|
this._dragActive = false;
|
|
15553
16044
|
this._junctions = [];
|
|
15554
16045
|
this._toolbarsOut = /* @__PURE__ */ new Map();
|
|
16046
|
+
this._leafContentRules = /* @__PURE__ */ new Map();
|
|
15555
16047
|
this._tabDragPending = null;
|
|
15556
16048
|
this._junctionsKey = ``;
|
|
15557
16049
|
this._junctionsRaf = null;
|
|
15558
16050
|
this._hostResizeObserver = null;
|
|
16051
|
+
this._toolbarSlots = /* @__PURE__ */ new Set();
|
|
16052
|
+
this._toolbarObserver = null;
|
|
15559
16053
|
this._onDragStart = (event) => {
|
|
15560
16054
|
const { panelId, pointerId, clientX, clientY } = event.detail;
|
|
15561
|
-
startDrag(panelId, this, pointerId, clientX, clientY);
|
|
16055
|
+
startDrag(panelId, this, pointerId, clientX, clientY, this._findPanelEl(panelId)?.kind);
|
|
15562
16056
|
};
|
|
15563
16057
|
this._onPoolSlotChange = () => {
|
|
15564
16058
|
this._syncPool();
|
|
@@ -15605,11 +16099,12 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15605
16099
|
const pathStr = this.shadowRoot?.getElementById(detail.id)?.getAttribute(`data-dock-path`);
|
|
15606
16100
|
if (pathStr === null || pathStr === void 0) return;
|
|
15607
16101
|
const path = parsePath(pathStr);
|
|
15608
|
-
const
|
|
15609
|
-
|
|
15610
|
-
const
|
|
16102
|
+
const closing = findNode(this._model, path);
|
|
16103
|
+
if (closing === null || !isStack(closing)) return;
|
|
16104
|
+
const moving = [...closing.panels];
|
|
15611
16105
|
this._returnAllToolbars();
|
|
15612
|
-
extractStack(this._model, path
|
|
16106
|
+
extractStack(this._model, path);
|
|
16107
|
+
for (const panelId of moving) addPanelToStack(this._firstStackAllowing(this._panelKind(panelId)), panelId);
|
|
15613
16108
|
this._commit();
|
|
15614
16109
|
};
|
|
15615
16110
|
this._onTabChange = (event, path) => {
|
|
@@ -15674,11 +16169,21 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15674
16169
|
this.addEventListener(`dock-panel-drag-start`, this._onDragStart);
|
|
15675
16170
|
this._hostResizeObserver = new ResizeObserver(() => this._scheduleJunctions());
|
|
15676
16171
|
this._hostResizeObserver.observe(this);
|
|
16172
|
+
this._scanToolbarSlots();
|
|
16173
|
+
this._toolbarObserver = new MutationObserver(() => this._scanToolbarSlots());
|
|
16174
|
+
this._toolbarObserver.observe(this, {
|
|
16175
|
+
childList: true,
|
|
16176
|
+
subtree: true,
|
|
16177
|
+
attributes: true,
|
|
16178
|
+
attributeFilter: [`slot`]
|
|
16179
|
+
});
|
|
15677
16180
|
}
|
|
15678
16181
|
disconnectedCallback() {
|
|
15679
16182
|
super.disconnectedCallback();
|
|
15680
16183
|
unregisterDockHost(this);
|
|
15681
16184
|
this.removeEventListener(`dock-panel-drag-start`, this._onDragStart);
|
|
16185
|
+
this._toolbarObserver?.disconnect();
|
|
16186
|
+
this._toolbarObserver = null;
|
|
15682
16187
|
this._hostResizeObserver?.disconnect();
|
|
15683
16188
|
this._hostResizeObserver = null;
|
|
15684
16189
|
if (this._junctionsRaf !== null) {
|
|
@@ -15731,6 +16236,19 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15731
16236
|
_scanPanels() {
|
|
15732
16237
|
return Array.from(this.children).filter((el) => el instanceof HTMLElement && el.tagName === `IXFX-DOCK-PANEL` && Boolean(el.panelId));
|
|
15733
16238
|
}
|
|
16239
|
+
/** Refresh the set of provided `toolbar-<name>` slots; re-render on change. */
|
|
16240
|
+
_scanToolbarSlots() {
|
|
16241
|
+
const found = /* @__PURE__ */ new Set();
|
|
16242
|
+
for (const child of this.children) {
|
|
16243
|
+
if (!(child instanceof HTMLElement)) continue;
|
|
16244
|
+
const slot = child.getAttribute(`slot`) ?? ``;
|
|
16245
|
+
if (slot.startsWith(`toolbar-`) && slot.length > `toolbar-`.length) found.add(slot);
|
|
16246
|
+
}
|
|
16247
|
+
if (found.size !== this._toolbarSlots.size || [...found].some((s) => !this._toolbarSlots.has(s))) {
|
|
16248
|
+
this._toolbarSlots = found;
|
|
16249
|
+
this.requestUpdate();
|
|
16250
|
+
}
|
|
16251
|
+
}
|
|
15734
16252
|
_findPanelEl(panelId) {
|
|
15735
16253
|
return this._pool.find((p) => p.panelId === panelId) ?? this._scanPanels().find((p) => p.panelId === panelId);
|
|
15736
16254
|
}
|
|
@@ -15744,7 +16262,33 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15744
16262
|
return fresh;
|
|
15745
16263
|
}
|
|
15746
16264
|
_appendPanelToFirstStack(panelId) {
|
|
15747
|
-
|
|
16265
|
+
const kind = this._panelKind(panelId);
|
|
16266
|
+
if (this._model.kind === `stack` && this._stackAllowsKind(this._model, kind)) {
|
|
16267
|
+
addPanelToStack(this._model, panelId);
|
|
16268
|
+
return;
|
|
16269
|
+
}
|
|
16270
|
+
addPanelToStack(this._firstStackAllowing(kind), panelId);
|
|
16271
|
+
}
|
|
16272
|
+
/** Normalized content kind of a panel (`'any'` when unknown/unset). */
|
|
16273
|
+
_panelKind(panelId) {
|
|
16274
|
+
return normalizeKind(this._findPanelEl(panelId)?.kind);
|
|
16275
|
+
}
|
|
16276
|
+
/** Whether `kind` may live in `stack` under the current leaf rules. */
|
|
16277
|
+
_stackAllowsKind(stack, kind) {
|
|
16278
|
+
if (stack.name === void 0) return true;
|
|
16279
|
+
const rules = this._leafContentRules.get(stack.name);
|
|
16280
|
+
if (rules === void 0) return true;
|
|
16281
|
+
if (rules.deny.has(kind)) return false;
|
|
16282
|
+
return rules.allow === null || rules.allow.has(kind);
|
|
16283
|
+
}
|
|
16284
|
+
/** First stack (tree order) whose rules admit `kind`, else the first stack. */
|
|
16285
|
+
_firstStackAllowing(kind) {
|
|
16286
|
+
for (const path of collectStackPaths(this._model)) {
|
|
16287
|
+
const node = findNode(this._model, path);
|
|
16288
|
+
if (node !== null && isStack(node) && this._stackAllowsKind(node, kind)) return node;
|
|
16289
|
+
}
|
|
16290
|
+
console.warn(`[ixfx-dock-host] no leaf admits kind "${kind}", using first stack`);
|
|
16291
|
+
return this._firstStack();
|
|
15748
16292
|
}
|
|
15749
16293
|
_syncPool() {
|
|
15750
16294
|
const current = this._scanPanels();
|
|
@@ -15786,7 +16330,34 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15786
16330
|
this.requestUpdate();
|
|
15787
16331
|
this._emitChange();
|
|
15788
16332
|
}
|
|
16333
|
+
/**
|
|
16334
|
+
* Dock a panel. Returns false (without touching the DOM) when the
|
|
16335
|
+
* explicitly targeted container denies the panel's `kind`.
|
|
16336
|
+
*/
|
|
15789
16337
|
addPanel(panel, target) {
|
|
16338
|
+
const kind = panel instanceof HTMLElement ? normalizeKind(panel.kind) : normalizeKind(panel.kind);
|
|
16339
|
+
const op = target?.op ?? `tabs`;
|
|
16340
|
+
let stackPath = null;
|
|
16341
|
+
let splitTarget = null;
|
|
16342
|
+
if (target?.container !== void 0) {
|
|
16343
|
+
const namedPath = findStackPathByName(this._model, target.container);
|
|
16344
|
+
if (namedPath === null) console.warn(`[ixfx-dock-host] addPanel: unknown container "${target.container}", appending to first admitting stack`);
|
|
16345
|
+
else if (op === `tabs` && !this._stackAllowsKindAt(namedPath, kind)) {
|
|
16346
|
+
console.warn(`[ixfx-dock-host] addPanel: container "${target.container}" does not admit kind "${kind}"`);
|
|
16347
|
+
return false;
|
|
16348
|
+
} else stackPath = namedPath;
|
|
16349
|
+
} else if (target?.stack) {
|
|
16350
|
+
const found = findStackOf(this._model, target.stack);
|
|
16351
|
+
if (found === null) console.warn(`[ixfx-dock-host] addPanel: unknown stack panel "${target.stack}", appending to first admitting stack`);
|
|
16352
|
+
else if (op === `tabs` && !this._stackAllowsKindAt(found, kind)) {
|
|
16353
|
+
console.warn(`[ixfx-dock-host] addPanel: target stack does not admit kind "${kind}"`);
|
|
16354
|
+
return false;
|
|
16355
|
+
} else stackPath = found;
|
|
16356
|
+
}
|
|
16357
|
+
if (stackPath !== null && op !== `tabs`) {
|
|
16358
|
+
splitTarget = stackPath;
|
|
16359
|
+
stackPath = null;
|
|
16360
|
+
}
|
|
15790
16361
|
let el;
|
|
15791
16362
|
if (panel instanceof HTMLElement) el = panel;
|
|
15792
16363
|
else {
|
|
@@ -15796,19 +16367,16 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15796
16367
|
if (panel.iconName !== void 0) el.iconName = panel.iconName;
|
|
15797
16368
|
if (panel.closable !== void 0) el.closable = panel.closable;
|
|
15798
16369
|
if (panel.collapsible !== void 0) el.collapsible = panel.collapsible;
|
|
16370
|
+
if (panel.kind !== void 0) el.kind = panel.kind;
|
|
15799
16371
|
}
|
|
15800
16372
|
if (el.parentElement !== this) this.appendChild(el);
|
|
15801
16373
|
if (!this._pool.some((p) => p.panelId === el.panelId)) this._pool = [...this._pool, el];
|
|
15802
16374
|
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
|
-
}
|
|
16375
|
+
if (collectPanelIds(this._model).includes(el.panelId)) this._removeFromModel(el.panelId);
|
|
16376
|
+
if (splitTarget !== null) insertBeside(this._model, splitTarget, opToDirection(op), createStack([el.panelId]), `after`);
|
|
16377
|
+
else if (stackPath !== null) {
|
|
16378
|
+
const stack = findNode(this._model, stackPath);
|
|
16379
|
+
if (stack !== null && isStack(stack)) addPanelToStack(stack, el.panelId, target?.index);
|
|
15812
16380
|
} else this._appendPanelToFirstStack(el.panelId);
|
|
15813
16381
|
normalize(this._model);
|
|
15814
16382
|
this.requestUpdate();
|
|
@@ -15818,6 +16386,12 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15818
16386
|
composed: true
|
|
15819
16387
|
}));
|
|
15820
16388
|
this._emitChange();
|
|
16389
|
+
return true;
|
|
16390
|
+
}
|
|
16391
|
+
/** Whether the stack at `stackPath` admits `kind` under the leaf rules. */
|
|
16392
|
+
_stackAllowsKindAt(stackPath, kind) {
|
|
16393
|
+
const stack = findNode(this._model, stackPath);
|
|
16394
|
+
return stack !== null && isStack(stack) && this._stackAllowsKind(stack, kind);
|
|
15821
16395
|
}
|
|
15822
16396
|
removePanel(panelId) {
|
|
15823
16397
|
if (!collectPanelIds(this._model).includes(panelId)) return false;
|
|
@@ -15853,6 +16427,123 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15853
16427
|
getPanelIds() {
|
|
15854
16428
|
return collectPanelIds(this._model);
|
|
15855
16429
|
}
|
|
16430
|
+
/** Names of every named stack leaf, in tree order. */
|
|
16431
|
+
getContainerNames() {
|
|
16432
|
+
return collectContainerNames(this._model);
|
|
16433
|
+
}
|
|
16434
|
+
/**
|
|
16435
|
+
* Snapshot of a named container (`{ name, panels, active }`), or null when
|
|
16436
|
+
* no stack carries that name. First match wins when duplicated.
|
|
16437
|
+
*/
|
|
16438
|
+
getContainer(name) {
|
|
16439
|
+
const path = findStackPathByName(this._model, name);
|
|
16440
|
+
if (path === null) return null;
|
|
16441
|
+
const node = findNode(this._model, path);
|
|
16442
|
+
if (node === null || !isStack(node) || node.name === void 0) return null;
|
|
16443
|
+
return {
|
|
16444
|
+
name: node.name,
|
|
16445
|
+
panels: [...node.panels],
|
|
16446
|
+
active: node.active
|
|
16447
|
+
};
|
|
16448
|
+
}
|
|
16449
|
+
/** Panel-ids in a named container, in tab order (`[]` when unknown/empty). */
|
|
16450
|
+
getContainerPanels(name) {
|
|
16451
|
+
return this.getContainer(name)?.panels ?? [];
|
|
16452
|
+
}
|
|
16453
|
+
/** Panel elements in a named container, in tab order — for iterating children. */
|
|
16454
|
+
getContainerElements(name) {
|
|
16455
|
+
return this.getContainerPanels(name).map((id) => this._findPanelEl(id)).filter((el) => el !== void 0);
|
|
16456
|
+
}
|
|
16457
|
+
/**
|
|
16458
|
+
* Restrict a named container to only the given panel kinds — e.g.
|
|
16459
|
+
* `setLeafExclusiveContents('editor', ['document'])`. `tabs` drops,
|
|
16460
|
+
* `addPanel`, and re-homing into the leaf then refuse other kinds, while
|
|
16461
|
+
* edge splits beside the leaf stay allowed (they create a new leaf).
|
|
16462
|
+
* Pass `[]` to seal the leaf (nothing admitted). Warns when no stack
|
|
16463
|
+
* currently carries `name`, but the rule is still stored and applies once
|
|
16464
|
+
* one appears. Rules are host policy: kept across `loadLayout()`, never
|
|
16465
|
+
* serialized.
|
|
16466
|
+
*/
|
|
16467
|
+
setLeafExclusiveContents(name, kinds) {
|
|
16468
|
+
const key = name.trim();
|
|
16469
|
+
if (!key) {
|
|
16470
|
+
console.warn(`[ixfx-dock-host] setLeafExclusiveContents: empty container name`);
|
|
16471
|
+
return;
|
|
16472
|
+
}
|
|
16473
|
+
const allow = new Set(kinds.map((k) => k.trim()).filter((k) => k.length > 0));
|
|
16474
|
+
const entry = this._leafContentRules.get(key) ?? {
|
|
16475
|
+
allow: null,
|
|
16476
|
+
deny: /* @__PURE__ */ new Set()
|
|
16477
|
+
};
|
|
16478
|
+
entry.allow = allow;
|
|
16479
|
+
this._leafContentRules.set(key, entry);
|
|
16480
|
+
if (findStackPathByName(this._model, key) === null) console.warn(`[ixfx-dock-host] setLeafExclusiveContents: no container named "${key}" yet; rule stored`);
|
|
16481
|
+
}
|
|
16482
|
+
/**
|
|
16483
|
+
* Forbid the given panel kinds in a named container — e.g.
|
|
16484
|
+
* `setLeafDenyContents('footer', ['files', 'document'])`. Everything else
|
|
16485
|
+
* stays admitted. Pass `[]` to lift the denial. See
|
|
16486
|
+
* {@link setLeafExclusiveContents} for scope/ordering notes.
|
|
16487
|
+
*/
|
|
16488
|
+
setLeafDenyContents(name, kinds) {
|
|
16489
|
+
const key = name.trim();
|
|
16490
|
+
if (!key) {
|
|
16491
|
+
console.warn(`[ixfx-dock-host] setLeafDenyContents: empty container name`);
|
|
16492
|
+
return;
|
|
16493
|
+
}
|
|
16494
|
+
const deny = new Set(kinds.map((k) => k.trim()).filter((k) => k.length > 0));
|
|
16495
|
+
const entry = this._leafContentRules.get(key) ?? {
|
|
16496
|
+
allow: null,
|
|
16497
|
+
deny: /* @__PURE__ */ new Set()
|
|
16498
|
+
};
|
|
16499
|
+
entry.deny = deny;
|
|
16500
|
+
if (entry.allow === null && entry.deny.size === 0) this._leafContentRules.delete(key);
|
|
16501
|
+
else this._leafContentRules.set(key, entry);
|
|
16502
|
+
if (findStackPathByName(this._model, key) === null) console.warn(`[ixfx-dock-host] setLeafDenyContents: no container named "${key}" yet; rule stored`);
|
|
16503
|
+
}
|
|
16504
|
+
/** Remove every content rule for a named container. */
|
|
16505
|
+
clearLeafContentRestrictions(name) {
|
|
16506
|
+
this._leafContentRules.delete(name.trim());
|
|
16507
|
+
}
|
|
16508
|
+
/** Copy of a container's rules, or null when unrestricted. */
|
|
16509
|
+
getLeafContentRestrictions(name) {
|
|
16510
|
+
const rules = this._leafContentRules.get(name.trim());
|
|
16511
|
+
if (rules === void 0) return null;
|
|
16512
|
+
return {
|
|
16513
|
+
allow: rules.allow === null ? null : [...rules.allow],
|
|
16514
|
+
deny: [...rules.deny]
|
|
16515
|
+
};
|
|
16516
|
+
}
|
|
16517
|
+
/**
|
|
16518
|
+
* Geometric neighbours of a leaf (`name` or numeric `path`) — what sits
|
|
16519
|
+
* above / below / left / right of it on screen. Each entry is
|
|
16520
|
+
* `{ name, path }` (`name` is null for unnamed leaves), ordered
|
|
16521
|
+
* most-overlapping first. Returns null when the leaf is unknown.
|
|
16522
|
+
*/
|
|
16523
|
+
getNeighbours(leaf) {
|
|
16524
|
+
return getLeafNeighbours(this._model, leaf);
|
|
16525
|
+
}
|
|
16526
|
+
/**
|
|
16527
|
+
* Move a leaf one position in `direction`, swapping with the neighbour it
|
|
16528
|
+
* shares the longest edge with (both keep the remaining structure intact).
|
|
16529
|
+
* Returns false when the leaf is unknown or nothing adjoins that way.
|
|
16530
|
+
*/
|
|
16531
|
+
moveLeaf(leaf, direction) {
|
|
16532
|
+
if (!moveLeafStep(this._model, leaf, direction)) return false;
|
|
16533
|
+
this._commit();
|
|
16534
|
+
return true;
|
|
16535
|
+
}
|
|
16536
|
+
/**
|
|
16537
|
+
* Move a leaf to an edge of the overall layout (e.g. toggle a sidebar from
|
|
16538
|
+
* `left` to `right`). The rest of the layout stays intact as a single
|
|
16539
|
+
* subtree and the leaf keeps its absolute size along the target axis.
|
|
16540
|
+
* Returns false when the leaf is unknown or the layout holds one leaf.
|
|
16541
|
+
*/
|
|
16542
|
+
moveLeafToEdge(leaf, edge) {
|
|
16543
|
+
if (!moveLeafNodeToEdge(this._model, leaf, edge)) return false;
|
|
16544
|
+
this._commit();
|
|
16545
|
+
return true;
|
|
16546
|
+
}
|
|
15856
16547
|
/**
|
|
15857
16548
|
* Capture a structured-clone-safe {@link DockPanelSnapshot} of a panel
|
|
15858
16549
|
* (identity, chrome attributes, and a best-effort `innerHTML`). Returns
|
|
@@ -15867,6 +16558,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15867
16558
|
iconName: el.iconName,
|
|
15868
16559
|
closable: el.closable,
|
|
15869
16560
|
collapsible: el.collapsible,
|
|
16561
|
+
kind: normalizeKind(el.kind),
|
|
15870
16562
|
accepts: el.accepts,
|
|
15871
16563
|
contentHTML: el.innerHTML
|
|
15872
16564
|
};
|
|
@@ -15890,10 +16582,11 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15890
16582
|
* `accepts` / `canAccept`. Pure query: no model mutation, no overlay.
|
|
15891
16583
|
*
|
|
15892
16584
|
* `panelId` is passed to `canAccept`; use any stable id for an external
|
|
15893
|
-
* panel (defaults to `"__external__"`).
|
|
16585
|
+
* panel (defaults to `"__external__"`). `kind` covers leaf content rules
|
|
16586
|
+
* for panels unknown to this host (defaults to `'any'`).
|
|
15894
16587
|
*/
|
|
15895
|
-
queryDropTarget(clientX, clientY, panelId = `__external__
|
|
15896
|
-
const zone = this.computeDropZone(clientX, clientY, panelId);
|
|
16588
|
+
queryDropTarget(clientX, clientY, panelId = `__external__`, kind) {
|
|
16589
|
+
const zone = this.computeDropZone(clientX, clientY, panelId, kind);
|
|
15897
16590
|
return zone === null ? null : zoneToTarget(zone);
|
|
15898
16591
|
}
|
|
15899
16592
|
/**
|
|
@@ -15902,9 +16595,9 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15902
16595
|
* one. Call on every pointer update from the outer coordinator, then
|
|
15903
16596
|
* {@link endExternalDrop} when the drag leaves or ends.
|
|
15904
16597
|
*/
|
|
15905
|
-
previewExternalDrop(clientX, clientY, panelId = `__external__
|
|
16598
|
+
previewExternalDrop(clientX, clientY, panelId = `__external__`, kind) {
|
|
15906
16599
|
this.setDragActive(true);
|
|
15907
|
-
const zone = this.computeDropZone(clientX, clientY, panelId);
|
|
16600
|
+
const zone = this.computeDropZone(clientX, clientY, panelId, kind);
|
|
15908
16601
|
this.setDropZone(zone);
|
|
15909
16602
|
return zone === null ? null : zoneToTarget(zone);
|
|
15910
16603
|
}
|
|
@@ -15917,8 +16610,9 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15917
16610
|
* Recreate a panel from a {@link DockPanelSnapshot} (or a bare
|
|
15918
16611
|
* {@link DockPanelInit}) and dock it. With `at` viewport coordinates the
|
|
15919
16612
|
* panel lands where a drop there would put it (respecting `accepts` /
|
|
15920
|
-
* `canAccept`); without them, or when the point
|
|
15921
|
-
*
|
|
16613
|
+
* `canAccept` and leaf content rules); without them, or when the point
|
|
16614
|
+
* misses the host, it goes into the first stack admitting its kind.
|
|
16615
|
+
* Returns the created element, or null when a panel
|
|
15922
16616
|
* with that id is already docked here.
|
|
15923
16617
|
*/
|
|
15924
16618
|
acceptExternalPanel(snapshot, at) {
|
|
@@ -15929,6 +16623,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15929
16623
|
if (snapshot.iconName !== void 0) el.iconName = snapshot.iconName;
|
|
15930
16624
|
if (snapshot.closable !== void 0) el.closable = snapshot.closable;
|
|
15931
16625
|
if (snapshot.collapsible !== void 0) el.collapsible = snapshot.collapsible;
|
|
16626
|
+
if (`kind` in snapshot && snapshot.kind !== void 0) el.kind = snapshot.kind;
|
|
15932
16627
|
if (`accepts` in snapshot && snapshot.accepts) el.setAttribute(`accepts`, snapshot.accepts);
|
|
15933
16628
|
if (`contentHTML` in snapshot && snapshot.contentHTML) el.innerHTML = snapshot.contentHTML;
|
|
15934
16629
|
this._returnAllToolbars();
|
|
@@ -15954,9 +16649,10 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15954
16649
|
* a zone with rects relative to the host, or null when the pointer is
|
|
15955
16650
|
* outside the host or the candidate zone is disallowed.
|
|
15956
16651
|
*/
|
|
15957
|
-
computeDropZone(clientX, clientY, panelId) {
|
|
16652
|
+
computeDropZone(clientX, clientY, panelId, kind) {
|
|
15958
16653
|
const hostRect = this.getBoundingClientRect();
|
|
15959
16654
|
if (clientX < hostRect.left || clientX > hostRect.right || clientY < hostRect.top || clientY > hostRect.bottom) return null;
|
|
16655
|
+
const resolvedKind = kind?.trim() ? kind.trim() : this._panelKind(panelId);
|
|
15960
16656
|
const rel = (r) => ({
|
|
15961
16657
|
x: r.left - hostRect.left,
|
|
15962
16658
|
y: r.top - hostRect.top,
|
|
@@ -15984,7 +16680,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15984
16680
|
rect: rel(leafRect),
|
|
15985
16681
|
caretRect: this._stripCaretRect(strip, gapIndex, hostRect)
|
|
15986
16682
|
};
|
|
15987
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
16683
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
15988
16684
|
}
|
|
15989
16685
|
}
|
|
15990
16686
|
const relX = (clientX - leafRect.left) / Math.max(leafRect.width, 1);
|
|
@@ -15998,7 +16694,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
15998
16694
|
gapIndex: stack.panels.length,
|
|
15999
16695
|
rect: rel(leafRect)
|
|
16000
16696
|
};
|
|
16001
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
16697
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
16002
16698
|
}
|
|
16003
16699
|
const r = rel(leafRect);
|
|
16004
16700
|
let operation;
|
|
@@ -16044,7 +16740,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16044
16740
|
side,
|
|
16045
16741
|
rect: zoneRect
|
|
16046
16742
|
};
|
|
16047
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
16743
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
16048
16744
|
}
|
|
16049
16745
|
const fromLeft = clientX - hostRect.left;
|
|
16050
16746
|
const fromRight = hostRect.right - clientX;
|
|
@@ -16100,15 +16796,18 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16100
16796
|
side,
|
|
16101
16797
|
rect: zoneRect
|
|
16102
16798
|
};
|
|
16103
|
-
return this._zoneAllowed(panelId, zone) ? zone : null;
|
|
16799
|
+
return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
|
|
16104
16800
|
}
|
|
16105
|
-
_zoneAllowed(panelId, zone) {
|
|
16801
|
+
_zoneAllowed(panelId, zone, kind) {
|
|
16106
16802
|
if (!tokenAllows(this.accepts, operationToToken(zone.operation))) return false;
|
|
16107
16803
|
const stack = findNode(this._model, zone.targetPath);
|
|
16108
16804
|
if (stack !== null && isStack(stack) && stack.panels.length === 1) {
|
|
16109
16805
|
const target = this._findPanelEl(stack.panels[0]);
|
|
16110
16806
|
if (target && !panelAllows(target.accepts, operationToToken(zone.operation))) return false;
|
|
16111
16807
|
}
|
|
16808
|
+
if (zone.operation === `tabs` && stack !== null && isStack(stack)) {
|
|
16809
|
+
if (!this._stackAllowsKind(stack, kind?.trim() ? kind.trim() : this._panelKind(panelId))) return false;
|
|
16810
|
+
}
|
|
16112
16811
|
if (this.canAccept && !this.canAccept(panelId, zone.operation)) return false;
|
|
16113
16812
|
return true;
|
|
16114
16813
|
}
|
|
@@ -16123,22 +16822,32 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16123
16822
|
this._appendPanelToFirstStack(panelId);
|
|
16124
16823
|
normalize(this._model);
|
|
16125
16824
|
}
|
|
16126
|
-
this._applyZone(panelId, zone);
|
|
16825
|
+
this._applyZone(panelId, zone, sourceHost === void 0 || sourceHost === this);
|
|
16127
16826
|
this._commit();
|
|
16128
16827
|
}
|
|
16129
16828
|
/**
|
|
16130
16829
|
* Move `panelId` (already present somewhere in the model) to the position
|
|
16131
16830
|
* described by `zone`. Shared by in-page drops and external panel adoption.
|
|
16132
16831
|
*/
|
|
16133
|
-
_applyZone(panelId, zone) {
|
|
16832
|
+
_applyZone(panelId, zone, transferName = false) {
|
|
16134
16833
|
const sourcePath = findStackOf(this._model, panelId);
|
|
16834
|
+
let vacated = null;
|
|
16135
16835
|
if (sourcePath !== null) {
|
|
16136
16836
|
const source = findNode(this._model, sourcePath);
|
|
16137
|
-
if (source !== null && isStack(source))
|
|
16837
|
+
if (source !== null && isStack(source)) {
|
|
16838
|
+
removePanelFromStack(source, panelId);
|
|
16839
|
+
if (source.panels.length === 0) vacated = source;
|
|
16840
|
+
}
|
|
16138
16841
|
}
|
|
16139
16842
|
if (zone.operation === `tabs`) {
|
|
16140
16843
|
const target = findNode(this._model, zone.targetPath);
|
|
16141
|
-
if (target !== null && isStack(target))
|
|
16844
|
+
if (target !== null && isStack(target)) {
|
|
16845
|
+
addPanelToStack(target, panelId, zone.gapIndex);
|
|
16846
|
+
if (transferName && vacated !== null && vacated !== target && vacated.name !== void 0 && target.name === void 0) {
|
|
16847
|
+
target.name = vacated.name;
|
|
16848
|
+
delete vacated.name;
|
|
16849
|
+
}
|
|
16850
|
+
}
|
|
16142
16851
|
} else insertBeside(this._model, zone.targetPath, opToDirection(zone.operation), createStack([panelId]), zone.side);
|
|
16143
16852
|
}
|
|
16144
16853
|
/**
|
|
@@ -16274,16 +16983,6 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16274
16983
|
};
|
|
16275
16984
|
window.addEventListener(`click`, swallow, true);
|
|
16276
16985
|
}
|
|
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
16986
|
_removeFromModel(panelId) {
|
|
16288
16987
|
const stackPath = findStackOf(this._model, panelId);
|
|
16289
16988
|
if (stackPath === null) return;
|
|
@@ -16414,6 +17113,7 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16414
17113
|
id=${`dock-leaf-${pathStr}`}
|
|
16415
17114
|
class="leaf"
|
|
16416
17115
|
data-dock-path=${pathStr}
|
|
17116
|
+
data-dock-name=${stack.name ?? A}
|
|
16417
17117
|
size=${size === void 0 ? A : String(size)}
|
|
16418
17118
|
min=${collapsible ? `1px` : A}
|
|
16419
17119
|
when-small=${collapsible ? `collapse` : A}
|
|
@@ -16425,26 +17125,19 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16425
17125
|
<slot name="empty"><span>Drop panels here</span></slot>
|
|
16426
17126
|
</div>
|
|
16427
17127
|
`);
|
|
17128
|
+
const toolbarSlot = stack.name !== void 0 && this._toolbarSlots.has(`toolbar-${stack.name}`) ? b`<slot name="toolbar-${stack.name}" slot="toolbar"></slot>` : A;
|
|
16428
17129
|
return leafAttrs(b`
|
|
16429
17130
|
<ixfx-tab-list
|
|
16430
17131
|
class="strip"
|
|
16431
17132
|
orientation="horizontal"
|
|
17133
|
+
scrollable
|
|
17134
|
+
shrink
|
|
17135
|
+
?hide-labels=${stack.hideLabels === true}
|
|
16432
17136
|
@change=${(e) => this._onTabChange(e, path)}
|
|
16433
17137
|
@pointerdown=${this._onStripPointerDown}
|
|
16434
17138
|
>
|
|
16435
17139
|
${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>
|
|
17140
|
+
${toolbarSlot}
|
|
16448
17141
|
</ixfx-tab-list>
|
|
16449
17142
|
<div class="leaf-body">
|
|
16450
17143
|
<slot name="dock-panel-${stack.active}"></slot>
|
|
@@ -16524,9 +17217,14 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16524
17217
|
.strip {
|
|
16525
17218
|
flex-shrink: 0;
|
|
16526
17219
|
border: 0;
|
|
16527
|
-
/* The leaf background shows through selected tabs;
|
|
16528
|
-
|
|
17220
|
+
/* The leaf background shows through selected tabs; a text underline
|
|
17221
|
+
(not the border indicator) carries the selection. */
|
|
16529
17222
|
--ixfx-tab-selected-background: transparent;
|
|
17223
|
+
--ixfx-selected-tab-border-width: 0;
|
|
17224
|
+
--ixfx-selected-tab-text-decoration: underline;
|
|
17225
|
+
--ixfx-selected-tab-text-decoration-thickness: 2px;
|
|
17226
|
+
--ixfx-selected-tab-text-decoration-offset: 4px;
|
|
17227
|
+
--ixfx-tab-hover-background: var(--surface-4);
|
|
16530
17228
|
}
|
|
16531
17229
|
|
|
16532
17230
|
/* Uniform tab height: a panel toolbar button would otherwise inflate
|
|
@@ -16535,6 +17233,11 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16535
17233
|
height: var(--dock-tab-height, 30px);
|
|
16536
17234
|
}
|
|
16537
17235
|
|
|
17236
|
+
/* Breathing room between strip toolbars and the leaf edge. */
|
|
17237
|
+
.strip::part(list-toolbar) {
|
|
17238
|
+
padding-right: var(--space-xs);
|
|
17239
|
+
}
|
|
17240
|
+
|
|
16538
17241
|
.leaf-body {
|
|
16539
17242
|
flex: 1;
|
|
16540
17243
|
min-height: 0;
|
|
@@ -16556,38 +17259,6 @@ let DockHostElement = class DockHostElement extends i {
|
|
|
16556
17259
|
margin: var(--space-s);
|
|
16557
17260
|
}
|
|
16558
17261
|
|
|
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
17262
|
.overlay {
|
|
16592
17263
|
position: absolute;
|
|
16593
17264
|
inset: 0;
|
|
@@ -16704,6 +17375,10 @@ function zoneToTarget(zone) {
|
|
|
16704
17375
|
function cloneNode(node) {
|
|
16705
17376
|
return JSON.parse(JSON.stringify(node));
|
|
16706
17377
|
}
|
|
17378
|
+
/** Panel content kind, normalized (`'any'` when unset/blank). */
|
|
17379
|
+
function normalizeKind(kind) {
|
|
17380
|
+
return (kind?.trim() ?? ``) || `any`;
|
|
17381
|
+
}
|
|
16707
17382
|
function parsePath(pathStr) {
|
|
16708
17383
|
return pathStr.length === 0 ? [] : pathStr.split(`-`).filter((s) => s.length > 0).map(Number);
|
|
16709
17384
|
}
|
|
@@ -26003,7 +26678,7 @@ let NotificationPillElement = class NotificationPillElement extends i {
|
|
|
26003
26678
|
#renderIcon() {
|
|
26004
26679
|
if (!this.icon) return A;
|
|
26005
26680
|
if (this.icon.startsWith(`<svg`)) return b`<span class="pill-icon">${unsafeHTML(this.icon)}</span>`;
|
|
26006
|
-
if (!
|
|
26681
|
+
if (!hasIcon(this.icon)) return A;
|
|
26007
26682
|
return b`<ixfx-icon class="pill-icon" name="${this.icon}"></ixfx-icon>`;
|
|
26008
26683
|
}
|
|
26009
26684
|
render() {
|
|
@@ -26802,7 +27477,7 @@ let TooltipElement = class TooltipElement extends i {
|
|
|
26802
27477
|
#renderIcon() {
|
|
26803
27478
|
if (!this.icon) return A;
|
|
26804
27479
|
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 (!
|
|
27480
|
+
if (!hasIcon(this.icon)) return A;
|
|
26806
27481
|
return b`<ixfx-icon class="tooltip-icon" style="margin-right:0.25em" name="${this.icon}"></ixfx-icon>`;
|
|
26807
27482
|
}
|
|
26808
27483
|
render() {
|
|
@@ -35813,6 +36488,6 @@ function init() {
|
|
|
35813
36488
|
console.log(`Init`);
|
|
35814
36489
|
}
|
|
35815
36490
|
//#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 };
|
|
36491
|
+
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
36492
|
|
|
35818
36493
|
//# sourceMappingURL=index.js.map
|