@vectoriox/iox-builder 9.5.1 → 9.5.3
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.
|
@@ -984,6 +984,23 @@ const HIDDEN_AT_REST_TYPES = ['Dialog'];
|
|
|
984
984
|
function isHiddenAtRest(node) {
|
|
985
985
|
return !!node?.type && HIDDEN_AT_REST_TYPES.includes(node.type);
|
|
986
986
|
}
|
|
987
|
+
/**
|
|
988
|
+
* How the selection shows a node hidden at rest — the two approved rules, by what the node IS:
|
|
989
|
+
* - a PART of a composite (it carries a `role`: the mobile menu's drawer) follows R2 (board 3): the
|
|
990
|
+
* selection is the reveal, and moving it out of the part hides it again;
|
|
991
|
+
* - a free-standing overlay (a plain Dialog) follows board A4: selecting it opens it, and it stays open
|
|
992
|
+
* while the author works elsewhere, until its X, its backdrop or the eye closes it.
|
|
993
|
+
*/
|
|
994
|
+
var SelectionReveal;
|
|
995
|
+
(function (SelectionReveal) {
|
|
996
|
+
SelectionReveal["WhileSelected"] = "while-selected";
|
|
997
|
+
SelectionReveal["UntilClosed"] = "until-closed";
|
|
998
|
+
})(SelectionReveal || (SelectionReveal = {}));
|
|
999
|
+
function selectionRevealOf(node) {
|
|
1000
|
+
if (!isHiddenAtRest(node))
|
|
1001
|
+
return null;
|
|
1002
|
+
return node?.role ? SelectionReveal.WhileSelected : SelectionReveal.UntilClosed;
|
|
1003
|
+
}
|
|
987
1004
|
/** Root → `target`, inclusive, across `roots` (page layout and globals), or [] when it is not there. */
|
|
988
1005
|
function pathToNode(roots, target) {
|
|
989
1006
|
for (const root of roots) {
|
|
@@ -1654,6 +1671,13 @@ const BUILDER_CHROME_EN = Object.freeze({
|
|
|
1654
1671
|
'builder.chrome.repeaterMoreRows': '{count} more rows from {alias}',
|
|
1655
1672
|
'builder.chrome.repeaterMoreRow': '{count} more row from {alias}',
|
|
1656
1673
|
'builder.chrome.repeaterMoreRowsHint': 'Rows from the source are not layers: edit the template above',
|
|
1674
|
+
'builder.chrome.layersPage': 'Page',
|
|
1675
|
+
'builder.chrome.layersGlobal': 'Global',
|
|
1676
|
+
'builder.chrome.layersEmpty': 'No layers yet',
|
|
1677
|
+
'builder.chrome.layersGlobalEmpty': 'No global elements',
|
|
1678
|
+
'builder.chrome.layerRename': 'Rename',
|
|
1679
|
+
'builder.chrome.layerDuplicate': 'Duplicate',
|
|
1680
|
+
'builder.chrome.layerDelete': 'Delete',
|
|
1657
1681
|
});
|
|
1658
1682
|
/** The layer tree's eye-slash title on a part hidden at rest (a closed drawer, R2). */
|
|
1659
1683
|
const HIDDEN_AT_REST_LABEL_KEY = 'builder.chrome.hiddenAtRest';
|
|
@@ -4016,7 +4040,7 @@ var CanvasShowState;
|
|
|
4016
4040
|
*/
|
|
4017
4041
|
class CanvasDesignStateService {
|
|
4018
4042
|
constructor() {
|
|
4019
|
-
this._state$ = new BehaviorSubject({ revealed: new Set(), shown: new Map() });
|
|
4043
|
+
this._state$ = new BehaviorSubject({ revealed: new Set(), pinned: new Set(), shown: new Map() });
|
|
4020
4044
|
this.state$ = this._state$.asObservable();
|
|
4021
4045
|
this._pagePath$ = new BehaviorSubject(null);
|
|
4022
4046
|
/** The app path of the page on the canvas (`/work`), or null when unknown. */
|
|
@@ -4033,13 +4057,31 @@ class CanvasDesignStateService {
|
|
|
4033
4057
|
if (next !== this._pagePath$.value)
|
|
4034
4058
|
this._pagePath$.next(next);
|
|
4035
4059
|
}
|
|
4036
|
-
/**
|
|
4060
|
+
/**
|
|
4061
|
+
* The selection moved. The hidden-at-rest PARTS on its path are revealed and every other part is not
|
|
4062
|
+
* (R2); a free-standing overlay on the path is pinned open until it is closed (A4).
|
|
4063
|
+
*/
|
|
4037
4064
|
setSelectionPath(path) {
|
|
4038
|
-
const
|
|
4039
|
-
const
|
|
4040
|
-
|
|
4065
|
+
const ids = (reveal) => path.filter(n => selectionRevealOf(n) === reveal).map(n => n.id).filter((id) => !!id);
|
|
4066
|
+
const revealed = new Set(ids(SelectionReveal.WhileSelected));
|
|
4067
|
+
const { revealed: was, pinned: held } = this._state$.value;
|
|
4068
|
+
const pinned = new Set([...held, ...ids(SelectionReveal.UntilClosed)]);
|
|
4069
|
+
if (sameSet(revealed, was) && sameSet(pinned, held))
|
|
4070
|
+
return;
|
|
4071
|
+
this._state$.next({ ...this._state$.value, revealed, pinned });
|
|
4072
|
+
}
|
|
4073
|
+
/** Closed on the canvas (its X, its backdrop, the eye): no longer pinned or switched open. */
|
|
4074
|
+
dismiss(nodeId) {
|
|
4075
|
+
if (!nodeId)
|
|
4041
4076
|
return;
|
|
4042
|
-
|
|
4077
|
+
const { pinned, shown } = this._state$.value;
|
|
4078
|
+
if (!pinned.has(nodeId) && !shown.has(nodeId))
|
|
4079
|
+
return;
|
|
4080
|
+
const nextPinned = new Set(pinned);
|
|
4081
|
+
nextPinned.delete(nodeId);
|
|
4082
|
+
const nextShown = new Map(shown);
|
|
4083
|
+
nextShown.delete(nodeId);
|
|
4084
|
+
this._state$.next({ ...this._state$.value, pinned: nextPinned, shown: nextShown });
|
|
4043
4085
|
}
|
|
4044
4086
|
/** The switcher's choice for one part (null = back to the saved default). */
|
|
4045
4087
|
setShown(nodeId, state) {
|
|
@@ -4058,8 +4100,8 @@ class CanvasDesignStateService {
|
|
|
4058
4100
|
isShown(nodeId) {
|
|
4059
4101
|
if (!nodeId)
|
|
4060
4102
|
return false;
|
|
4061
|
-
const { revealed, shown } = this._state$.value;
|
|
4062
|
-
return revealed.has(nodeId) || shown.get(nodeId) === CanvasShowState.Open;
|
|
4103
|
+
const { revealed, pinned, shown } = this._state$.value;
|
|
4104
|
+
return revealed.has(nodeId) || pinned.has(nodeId) || shown.get(nodeId) === CanvasShowState.Open;
|
|
4063
4105
|
}
|
|
4064
4106
|
/** True when the part is shown only because the selection is inside it (the switcher's hint line). */
|
|
4065
4107
|
isRevealedBySelection(nodeId) {
|
|
@@ -4067,7 +4109,7 @@ class CanvasDesignStateService {
|
|
|
4067
4109
|
}
|
|
4068
4110
|
/** A new page on the canvas: nothing is revealed or switched open. */
|
|
4069
4111
|
reset() {
|
|
4070
|
-
this._state$.next({ revealed: new Set(), shown: new Map() });
|
|
4112
|
+
this._state$.next({ revealed: new Set(), pinned: new Set(), shown: new Map() });
|
|
4071
4113
|
}
|
|
4072
4114
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: CanvasDesignStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4073
4115
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: CanvasDesignStateService }); }
|
|
@@ -4075,6 +4117,9 @@ class CanvasDesignStateService {
|
|
|
4075
4117
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: CanvasDesignStateService, decorators: [{
|
|
4076
4118
|
type: Injectable
|
|
4077
4119
|
}] });
|
|
4120
|
+
function sameSet(a, b) {
|
|
4121
|
+
return a.size === b.size && [...a].every(id => b.has(id));
|
|
4122
|
+
}
|
|
4078
4123
|
|
|
4079
4124
|
// Re-exported from the shared iox-ui package so iox-builder consumers
|
|
4080
4125
|
// don't need to import from two different packages.
|
|
@@ -7172,6 +7217,11 @@ const MORE_ROW_KEY = 'builder.chrome.repeaterMoreRow';
|
|
|
7172
7217
|
const MORE_ROWS_HINT_KEY = 'builder.chrome.repeaterMoreRowsHint';
|
|
7173
7218
|
/** Panel events after which a row's extras change without the tree's STRUCTURE changing. */
|
|
7174
7219
|
const EXTRAS_EVENTS = new Set([PanelEventTypes.REPEATER_ROWS_CHANGED, PanelEventTypes.BINDING_CHANGED]);
|
|
7220
|
+
const CHROME_WORDS = [
|
|
7221
|
+
['page', 'builder.chrome.layersPage'], ['global', 'builder.chrome.layersGlobal'], ['empty', 'builder.chrome.layersEmpty'],
|
|
7222
|
+
['globalEmpty', 'builder.chrome.layersGlobalEmpty'], ['rename', 'builder.chrome.layerRename'],
|
|
7223
|
+
['duplicate', 'builder.chrome.layerDuplicate'], ['delete', 'builder.chrome.layerDelete'],
|
|
7224
|
+
];
|
|
7175
7225
|
class LayerTreeComponent {
|
|
7176
7226
|
constructor(registry, overlayService, dragEngine) {
|
|
7177
7227
|
this.registry = registry;
|
|
@@ -7196,6 +7246,8 @@ class LayerTreeComponent {
|
|
|
7196
7246
|
this.templateLabel = this.labels(TEMPLATE_KEY, BUILDER_CHROME_EN[TEMPLATE_KEY]);
|
|
7197
7247
|
this.templateTitle = this.labels(TEMPLATE_HINT_KEY, BUILDER_CHROME_EN[TEMPLATE_HINT_KEY]);
|
|
7198
7248
|
this.moreRowsTitle = this.labels(MORE_ROWS_HINT_KEY, BUILDER_CHROME_EN[MORE_ROWS_HINT_KEY]);
|
|
7249
|
+
/** The tree's own chrome words, through the host's i18n (builder.chrome.*). */
|
|
7250
|
+
this.words = Object.fromEntries(CHROME_WORDS.map(([k, key]) => [k, this.labels(key, BUILDER_CHROME_EN[key])]));
|
|
7199
7251
|
/** Cache: component type → icon class */
|
|
7200
7252
|
this.iconMap = new Map();
|
|
7201
7253
|
/** Last-seen structural signature — used by ngDoCheck to skip no-op rebuilds. */
|
|
@@ -7226,9 +7278,11 @@ class LayerTreeComponent {
|
|
|
7226
7278
|
const key = more.count === 1 ? MORE_ROW_KEY : MORE_ROWS_KEY;
|
|
7227
7279
|
return this.labels(key, BUILDER_CHROME_EN[key]).replace('{count}', String(more.count)).replace('{alias}', more.alias);
|
|
7228
7280
|
}
|
|
7229
|
-
/** The
|
|
7230
|
-
|
|
7231
|
-
|
|
7281
|
+
/** The name's title — the full text a narrow sidebar truncates: the name, its sub-label, and every
|
|
7282
|
+
* binding a bound part holds. */
|
|
7283
|
+
rowTitle(tn) {
|
|
7284
|
+
const bindings = tn.bound ? bindingLabels(tn.node) : [];
|
|
7285
|
+
return [tn.label, ...(bindings.length ? bindings : tn.sub ? [tn.sub] : [])].join(' · ');
|
|
7232
7286
|
}
|
|
7233
7287
|
// Runs on every CD cycle — exit immediately if structure hasn't changed.
|
|
7234
7288
|
ngDoCheck() {
|
|
@@ -7363,11 +7417,11 @@ class LayerTreeComponent {
|
|
|
7363
7417
|
return instance instanceof BuilderRepeaterComponent ? { alias: instance.source, count: instance.items.length } : null;
|
|
7364
7418
|
}
|
|
7365
7419
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: LayerTreeComponent, deps: [{ token: ComponentRegistryService }, { token: OverlayService }, { token: DragEngineService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7366
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: LayerTreeComponent, isStandalone: false, selector: "app-layer-tree", inputs: { layout: "layout", globalElements: "globalElements" }, outputs: { nodeSelect: "nodeSelect", nodeAction: "nodeAction", nodeMove: "nodeMove" }, ngImport: i0, template: "<div class=\"layer-tree\">\n <div class=\"tree-header\">\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'page'\" (click)=\"activeSection = 'page'\">\n <i class=\"ph-thin ph-stack\"></i> Page\n </button>\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'global'\" (click)=\"activeSection = 'global'\">\n <i class=\"ph-thin ph-globe\"></i> Global\n @if (flatGlobalTree.length) {\n <span class=\"section-tab-badge\">{{ flatGlobalTree.length }}</span>\n }\n </button>\n </div>\n\n <!-- A node's label with what the shipped Repeater adds to it (boards 4 / 8): its sub-label, the\n template chip, a composite part's role and lock, the hidden-at-rest glyph. -->\n <ng-template #rowLabel let-tn>\n <span class=\"node-label\" [class.node-label--named]=\"tn.node.label\" [attr.title]=\"boundTitle(tn)\">{{ tn.label }}@if (tn.sub) {<em class=\"node-sub\"> \u00B7 {{ tn.sub }}</em>}</span>\n @if (tn.template) {\n <span class=\"node-template\" [title]=\"templateTitle\">{{ templateLabel }}</span>\n }\n @if (tn.node.role) {\n <span class=\"node-role\" [title]=\"roleTitle + ': ' + tn.node.role\">{{ tn.node.role }}</span>\n }\n @if (tn.locked) {\n <i class=\"ph-thin ph-lock-simple node-lock\" [title]=\"lockedPartTitle\" [attr.aria-label]=\"lockedPartTitle\"></i>\n }\n @if (tn.hiddenAtRest) {\n <span class=\"node-rest\" [title]=\"hiddenAtRestTitle\" [attr.aria-label]=\"hiddenAtRestTitle\"><i class=\"ph-thin ph-eye-slash\"></i></span>\n }\n </ng-template>\n\n <!-- The rows a Repeater's source adds are not nodes: ONE muted line after its template, never selectable. -->\n <ng-template #moreRowsLine let-tn let-handle=\"handle\">\n <div class=\"tree-more\" aria-disabled=\"true\" [title]=\"moreRowsTitle\"\n [style.padding-inline-start.px]=\"12 + tn.moreRows.depth * 16\">\n @if (handle) {\n <i class=\"ph-thin ph-dots-six-vertical drag-handle tree-more-handle\" aria-hidden=\"true\"></i>\n }\n <span class=\"expand-spacer\"></span>\n <i class=\"ph-thin ph-rows node-icon\"></i>\n <span class=\"node-label\">{{ moreRowsText(tn.moreRows) }}</span>\n </div>\n </ng-template>\n\n <!-- \u2500\u2500 Page elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeSection === 'page') {\n <div class=\"tree-body\"\n cdkDropList\n [cdkDropListData]=\"flatTree\"\n (cdkDropListDropped)=\"onTreeDrop($event)\">\n @if (!flatTree.length) {\n <div class=\"tree-empty\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n <span>No layers yet</span>\n </div>\n }\n\n @for (tn of flatTree; track tn.node) {\n <div class=\"tree-row\"\n cdkDrag\n [cdkDragData]=\"tn\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <i class=\"ph-thin ph-dots-six-vertical drag-handle\" cdkDragHandle></i>\n\n @if (tn.hasChildren) {\n <button class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n } @else {\n <span class=\"expand-spacer\"></span>\n }\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n @if (editingNodeId === tn.node.id) {\n <input class=\"node-rename-input\"\n [value]=\"editingValue\"\n (input)=\"editingValue = $any($event.target).value\"\n (keydown.enter)=\"saveRename(tn)\"\n (keydown.escape)=\"cancelRename()\"\n (blur)=\"saveRename(tn)\"\n (click)=\"$event.stopPropagation()\"\n autofocus>\n } @else {\n <ng-container *ngTemplateOutlet=\"rowLabel; context: { $implicit: tn }\"></ng-container>\n }\n\n <span class=\"node-actions\">\n <button class=\"action-btn\" title=\"Rename\" (click)=\"startRename(tn, $event)\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n </button>\n <button class=\"action-btn\" title=\"Duplicate\" (click)=\"onDuplicateNode(tn, $event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n @if (!tn.locked) {\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n }\n </span>\n </div>\n @if (tn.moreRows) {\n <ng-container *ngTemplateOutlet=\"moreRowsLine; context: { $implicit: tn, handle: true }\"></ng-container>\n }\n }\n </div>\n }\n\n <!-- \u2500\u2500 Global elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeSection === 'global') {\n <div class=\"tree-body\">\n @if (!flatGlobalTree.length) {\n <div class=\"tree-empty\">\n <i class=\"ph-thin ph-globe\"></i>\n <span>No global elements</span>\n </div>\n }\n\n @for (tn of flatGlobalTree; track tn.node) {\n <div class=\"tree-row tree-row--global\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n @if (tn.hasChildren) {\n <button class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n } @else {\n <span class=\"expand-spacer\"></span>\n }\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <ng-container *ngTemplateOutlet=\"rowLabel; context: { $implicit: tn }\"></ng-container>\n @if (tn.depth === 0) {\n <span class=\"node-position-badge\">{{ tn.node.globalPosition }}</span>\n }\n\n <span class=\"node-actions\">\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n @if (tn.moreRows) {\n <ng-container *ngTemplateOutlet=\"moreRowsLine; context: { $implicit: tn }\"></ng-container>\n }\n }\n </div>\n }\n</div>\n", styles: [".layer-tree{display:flex;flex-direction:column;height:100%;overflow:hidden}.layer-tree .tree-header{display:flex;gap:2px;padding:6px 8px;border-bottom:1px solid var(--p-surface-200);flex-shrink:0}.layer-tree .section-tab{display:inline-flex;align-items:center;gap:4px;flex:1;justify-content:center;padding:4px 8px;font-size:11px;font-weight:500;border:1px solid var(--p-surface-200);border-radius:4px;background:transparent;color:var(--p-text-muted-color);cursor:pointer;transition:background .12s,color .12s,border-color .12s;white-space:nowrap}.layer-tree .section-tab i{font-size:11px}.layer-tree .section-tab:hover{background:var(--p-surface-100);color:var(--p-text-color)}.layer-tree .section-tab--active{background:#cb909026;border-color:#cb9090;color:#cb9090}.layer-tree .section-tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;font-size:10px;font-weight:600;background:#cb9090;color:#fff;border-radius:8px}.layer-tree .tree-body{flex:1 1 0;overflow-y:auto;overflow-x:hidden;padding-block:4px}.layer-tree .tree-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:2rem 1rem;color:var(--p-text-muted-color);font-size:12px}.layer-tree .tree-empty i{font-size:22px}.layer-tree .tree-row{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;cursor:pointer;font-size:12px;color:var(--p-text-color);border-inline-start:2px solid transparent;transition:background .12s,border-color .12s;-webkit-user-select:none;user-select:none}.layer-tree .tree-row:hover,.layer-tree .tree-row.is-hovered{background:var(--p-surface-100)}.layer-tree .tree-row.is-selected{background:#cb90901a;border-inline-start-color:#cb9090;font-weight:500}.layer-tree .drag-handle{font-size:10px;color:var(--p-text-muted-color);opacity:0;cursor:grab;flex-shrink:0;transition:opacity .12s}.layer-tree .tree-row:hover .drag-handle{opacity:.5}.layer-tree .drag-handle:hover{opacity:1!important}.layer-tree .cdk-drag-preview{box-sizing:border-box;display:flex;align-items:center;gap:6px;height:28px;padding:0 8px 0 12px;border-radius:6px;background:var(--p-surface-0);border:1px solid #cb9090;box-shadow:0 4px 12px #0000001f;font-size:12px}.layer-tree .cdk-drag-placeholder{height:2px;background:#cb9090;border-radius:1px;opacity:.6}.layer-tree .cdk-drag-placeholder>*{visibility:hidden}.layer-tree .cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.layer-tree .expand-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;flex-shrink:0;border-radius:2px}.layer-tree .expand-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .expand-btn i{font-size:10px}.layer-tree .expand-spacer{width:16px;flex-shrink:0}.layer-tree .node-icon{font-size:14px;color:var(--p-text-muted-color);flex-shrink:0}.layer-tree .node-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto;min-width:4.5em}.layer-tree .node-label.node-label--named{color:var(--p-primary-color, #cb9090);font-weight:500}.layer-tree .node-sub{font-style:normal;font-weight:400;color:#535d6e}.layer-tree .node-template{flex:none;padding:0 4px;border-radius:4px;background:#8a3a72;color:#fff;font:500 10px Fira Code,Fira Mono,monospace;line-height:16px}.layer-tree .tree-more{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;font-size:12px;font-style:italic;color:#64748b;border-inline-start:2px solid transparent;cursor:default;-webkit-user-select:none;user-select:none}.layer-tree .tree-more-handle{visibility:hidden}.layer-tree .node-role{flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 4px;border:1px dashed #c9a1be;border-radius:4px;background:#f7eef5;color:#8a3a72;font:500 10px Fira Code,Fira Mono,monospace;line-height:16px}.layer-tree .node-lock{flex:none;color:#64748b;font-size:12px}.layer-tree .node-rename-input{flex:1;min-width:0;border:1px solid var(--p-primary-color, #cb9090);border-radius:4px;background:var(--p-surface-0, #fff);color:var(--p-text-color);font-family:inherit;font-size:.72rem;padding:1px 4px;outline:none}.layer-tree .node-actions{display:flex;gap:2px;margin-inline-start:auto;opacity:0;transition:opacity .12s;flex-shrink:0}.layer-tree .tree-row:hover .node-actions,.layer-tree .tree-row.is-selected .node-actions{opacity:1}.layer-tree .tree-section-header{display:flex;align-items:center;gap:6px;padding:6px 12px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color);border-top:1px solid var(--p-surface-200);margin-top:4px}.layer-tree .tree-section-header i{font-size:12px}.layer-tree .tree-body--global{flex:0 0 auto}.layer-tree .tree-row--global{border-inline-start-color:transparent}.layer-tree .tree-row--global.is-selected{background:#638fcb1a;border-inline-start-color:#638fcb}.layer-tree .node-rest{display:inline-flex;align-items:center;flex-shrink:0;font-size:12px;color:var(--p-text-muted-color)}.layer-tree .node-position-badge{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#638fcb;background:#638fcb1f;padding:1px 5px;border-radius:3px;flex-shrink:0}.layer-tree .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:4px;font-size:12px;transition:background .12s,color .12s}.layer-tree .action-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .action-btn--delete:hover{background:#dc26261a;color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i5.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }] }); }
|
|
7420
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: LayerTreeComponent, isStandalone: false, selector: "app-layer-tree", inputs: { layout: "layout", globalElements: "globalElements" }, outputs: { nodeSelect: "nodeSelect", nodeAction: "nodeAction", nodeMove: "nodeMove" }, ngImport: i0, template: "<div class=\"layer-tree\">\n <div class=\"tree-header\">\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'page'\" (click)=\"activeSection = 'page'\">\n <i class=\"ph-thin ph-stack\"></i> {{ words.page }}\n </button>\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'global'\" (click)=\"activeSection = 'global'\">\n <i class=\"ph-thin ph-globe\"></i> {{ words.global }}\n @if (flatGlobalTree.length) {\n <span class=\"section-tab-badge\">{{ flatGlobalTree.length }}</span>\n }\n </button>\n </div>\n\n <!-- A node's label with what the shipped Repeater adds to it (boards 4 / 8): its sub-label, the\n template chip, a composite part's role and lock, the hidden-at-rest glyph. -->\n <ng-template #rowLabel let-tn>\n <span class=\"node-label\" [class.node-label--named]=\"tn.node.label\" [attr.title]=\"rowTitle(tn)\">{{ tn.label }}@if (tn.sub) {<em class=\"node-sub\"> \u00B7 {{ tn.sub }}</em>}</span>\n @if (tn.template) {\n <span class=\"node-template\" [title]=\"templateTitle\">{{ templateLabel }}</span>\n }\n @if (tn.node.role) {\n <span class=\"node-role\" [title]=\"roleTitle + ': ' + tn.node.role\">{{ tn.node.role }}</span>\n }\n @if (tn.locked) {\n <i class=\"ph-thin ph-lock-simple node-lock\" [title]=\"lockedPartTitle\" [attr.aria-label]=\"lockedPartTitle\"></i>\n }\n @if (tn.hiddenAtRest) {\n <span class=\"node-rest\" [title]=\"hiddenAtRestTitle\" [attr.aria-label]=\"hiddenAtRestTitle\"><i class=\"ph-thin ph-eye-slash\"></i></span>\n }\n </ng-template>\n\n <!-- The rows a Repeater's source adds are not nodes: ONE muted line after its template, never selectable. -->\n <ng-template #moreRowsLine let-tn let-handle=\"handle\">\n <div class=\"tree-more\" aria-disabled=\"true\" [title]=\"moreRowsTitle\"\n [style.padding-inline-start.px]=\"12 + tn.moreRows.depth * 16\">\n @if (handle) {\n <i class=\"ph-thin ph-dots-six-vertical drag-handle tree-more-handle\" aria-hidden=\"true\"></i>\n }\n <span class=\"expand-spacer\"></span>\n <i class=\"ph-thin ph-rows node-icon\"></i>\n <span class=\"node-label\">{{ moreRowsText(tn.moreRows) }}</span>\n </div>\n </ng-template>\n\n <!-- \u2500\u2500 Page elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeSection === 'page') {\n <div class=\"tree-body\"\n cdkDropList\n [cdkDropListData]=\"flatTree\"\n (cdkDropListDropped)=\"onTreeDrop($event)\">\n @if (!flatTree.length) {\n <div class=\"tree-empty\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n <span>{{ words.empty }}</span>\n </div>\n }\n\n @for (tn of flatTree; track tn.node) {\n <div class=\"tree-row\"\n cdkDrag\n [cdkDragData]=\"tn\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <i class=\"ph-thin ph-dots-six-vertical drag-handle\" cdkDragHandle></i>\n\n @if (tn.hasChildren) {\n <button class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n } @else {\n <span class=\"expand-spacer\"></span>\n }\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n @if (editingNodeId === tn.node.id) {\n <input class=\"node-rename-input\"\n [value]=\"editingValue\"\n (input)=\"editingValue = $any($event.target).value\"\n (keydown.enter)=\"saveRename(tn)\"\n (keydown.escape)=\"cancelRename()\"\n (blur)=\"saveRename(tn)\"\n (click)=\"$event.stopPropagation()\"\n autofocus>\n } @else {\n <ng-container *ngTemplateOutlet=\"rowLabel; context: { $implicit: tn }\"></ng-container>\n }\n\n <span class=\"node-actions\">\n <button class=\"action-btn\" [title]=\"words.rename\" (click)=\"startRename(tn, $event)\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n </button>\n <button class=\"action-btn\" [title]=\"words.duplicate\" (click)=\"onDuplicateNode(tn, $event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n @if (!tn.locked) {\n <button class=\"action-btn action-btn--delete\" [title]=\"words.delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n }\n </span>\n </div>\n @if (tn.moreRows) {\n <ng-container *ngTemplateOutlet=\"moreRowsLine; context: { $implicit: tn, handle: true }\"></ng-container>\n }\n }\n </div>\n }\n\n <!-- \u2500\u2500 Global elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeSection === 'global') {\n <div class=\"tree-body\">\n @if (!flatGlobalTree.length) {\n <div class=\"tree-empty\">\n <i class=\"ph-thin ph-globe\"></i>\n <span>{{ words.globalEmpty }}</span>\n </div>\n }\n\n @for (tn of flatGlobalTree; track tn.node) {\n <div class=\"tree-row tree-row--global\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n @if (tn.hasChildren) {\n <button class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n } @else {\n <span class=\"expand-spacer\"></span>\n }\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <ng-container *ngTemplateOutlet=\"rowLabel; context: { $implicit: tn }\"></ng-container>\n @if (tn.depth === 0) {\n <span class=\"node-position-badge\">{{ tn.node.globalPosition }}</span>\n }\n\n <span class=\"node-actions\">\n <button class=\"action-btn action-btn--delete\" [title]=\"words.delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n @if (tn.moreRows) {\n <ng-container *ngTemplateOutlet=\"moreRowsLine; context: { $implicit: tn }\"></ng-container>\n }\n }\n </div>\n }\n</div>\n", styles: [".layer-tree{display:flex;flex-direction:column;height:100%;overflow:hidden}.layer-tree .tree-header{display:flex;gap:2px;padding:6px 8px;border-bottom:1px solid var(--p-surface-200);flex-shrink:0}.layer-tree .section-tab{display:inline-flex;align-items:center;gap:4px;flex:1;justify-content:center;padding:4px 8px;font-size:11px;font-weight:500;border:1px solid var(--p-surface-200);border-radius:4px;background:transparent;color:var(--p-text-muted-color);cursor:pointer;transition:background .12s,color .12s,border-color .12s;white-space:nowrap}.layer-tree .section-tab i{font-size:11px}.layer-tree .section-tab:hover{background:var(--p-surface-100);color:var(--p-text-color)}.layer-tree .section-tab--active{background:#cb909026;border-color:#cb9090;color:#cb9090}.layer-tree .section-tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;font-size:10px;font-weight:600;background:#cb9090;color:#fff;border-radius:8px}.layer-tree .tree-body{flex:1 1 0;overflow-y:auto;overflow-x:hidden;padding-block:4px}.layer-tree .tree-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:2rem 1rem;color:var(--p-text-muted-color);font-size:12px}.layer-tree .tree-empty i{font-size:22px}.layer-tree .tree-row{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;cursor:pointer;font-size:12px;color:var(--p-text-color);border-inline-start:2px solid transparent;transition:background .12s,border-color .12s;-webkit-user-select:none;user-select:none}.layer-tree .tree-row:hover,.layer-tree .tree-row.is-hovered{background:var(--p-surface-100)}.layer-tree .tree-row.is-selected{background:#cb90901a;border-inline-start-color:#cb9090;font-weight:500}.layer-tree .drag-handle{font-size:10px;color:var(--p-text-muted-color);opacity:0;cursor:grab;flex-shrink:0;transition:opacity .12s}.layer-tree .tree-row:hover .drag-handle{opacity:.5}.layer-tree .drag-handle:hover{opacity:1!important}.layer-tree .cdk-drag-preview{box-sizing:border-box;display:flex;align-items:center;gap:6px;height:28px;padding:0 8px 0 12px;border-radius:6px;background:var(--p-surface-0);border:1px solid #cb9090;box-shadow:0 4px 12px #0000001f;font-size:12px}.layer-tree .cdk-drag-placeholder{height:2px;background:#cb9090;border-radius:1px;opacity:.6}.layer-tree .cdk-drag-placeholder>*{visibility:hidden}.layer-tree .cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.layer-tree .expand-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;flex-shrink:0;border-radius:2px}.layer-tree .expand-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .expand-btn i{font-size:10px}.layer-tree .expand-spacer{width:16px;flex-shrink:0}.layer-tree .node-icon{font-size:14px;color:var(--p-text-muted-color);flex-shrink:0}.layer-tree .node-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto;min-width:4.5em}.layer-tree .node-label.node-label--named{color:var(--p-primary-color, #cb9090);font-weight:500}.layer-tree .node-sub{font-style:normal;font-weight:400;color:#535d6e}.layer-tree .node-template{flex:none;padding:0 4px;border-radius:4px;background:#8a3a72;color:#fff;font:500 10px Fira Code,Fira Mono,monospace;line-height:16px}.layer-tree .tree-more{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;font-size:12px;font-style:italic;color:#64748b;border-inline-start:2px solid transparent;cursor:default;-webkit-user-select:none;user-select:none}.layer-tree .tree-more-handle{visibility:hidden}.layer-tree .node-role{flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 4px;border:1px dashed #c9a1be;border-radius:4px;background:#f7eef5;color:#8a3a72;font:500 10px Fira Code,Fira Mono,monospace;line-height:16px}.layer-tree .node-lock{flex:none;color:#64748b;font-size:12px}.layer-tree .node-rename-input{flex:1;min-width:0;border:1px solid var(--p-primary-color, #cb9090);border-radius:4px;background:var(--p-surface-0, #fff);color:var(--p-text-color);font-family:inherit;font-size:.72rem;padding:1px 4px;outline:none}.layer-tree .node-actions{display:flex;gap:2px;margin-inline-start:auto;opacity:0;transition:opacity .12s;flex-shrink:0}.layer-tree .tree-row:hover .node-actions,.layer-tree .tree-row.is-selected .node-actions{opacity:1}.layer-tree .tree-section-header{display:flex;align-items:center;gap:6px;padding:6px 12px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color);border-top:1px solid var(--p-surface-200);margin-top:4px}.layer-tree .tree-section-header i{font-size:12px}.layer-tree .tree-body--global{flex:0 0 auto}.layer-tree .tree-row--global{border-inline-start-color:transparent}.layer-tree .tree-row--global.is-selected{background:#638fcb1a;border-inline-start-color:#638fcb}.layer-tree .node-rest{display:inline-flex;align-items:center;flex-shrink:0;font-size:12px;color:var(--p-text-muted-color)}.layer-tree .node-position-badge{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#638fcb;background:#638fcb1f;padding:1px 5px;border-radius:3px;flex-shrink:0}.layer-tree .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:4px;font-size:12px;transition:background .12s,color .12s}.layer-tree .action-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .action-btn--delete:hover{background:#dc26261a;color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i5.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }] }); }
|
|
7367
7421
|
}
|
|
7368
7422
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: LayerTreeComponent, decorators: [{
|
|
7369
7423
|
type: Component,
|
|
7370
|
-
args: [{ selector: 'app-layer-tree', standalone: false, template: "<div class=\"layer-tree\">\n <div class=\"tree-header\">\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'page'\" (click)=\"activeSection = 'page'\">\n <i class=\"ph-thin ph-stack\"></i> Page\n </button>\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'global'\" (click)=\"activeSection = 'global'\">\n <i class=\"ph-thin ph-globe\"></i> Global\n @if (flatGlobalTree.length) {\n <span class=\"section-tab-badge\">{{ flatGlobalTree.length }}</span>\n }\n </button>\n </div>\n\n <!-- A node's label with what the shipped Repeater adds to it (boards 4 / 8): its sub-label, the\n template chip, a composite part's role and lock, the hidden-at-rest glyph. -->\n <ng-template #rowLabel let-tn>\n <span class=\"node-label\" [class.node-label--named]=\"tn.node.label\" [attr.title]=\"boundTitle(tn)\">{{ tn.label }}@if (tn.sub) {<em class=\"node-sub\"> \u00B7 {{ tn.sub }}</em>}</span>\n @if (tn.template) {\n <span class=\"node-template\" [title]=\"templateTitle\">{{ templateLabel }}</span>\n }\n @if (tn.node.role) {\n <span class=\"node-role\" [title]=\"roleTitle + ': ' + tn.node.role\">{{ tn.node.role }}</span>\n }\n @if (tn.locked) {\n <i class=\"ph-thin ph-lock-simple node-lock\" [title]=\"lockedPartTitle\" [attr.aria-label]=\"lockedPartTitle\"></i>\n }\n @if (tn.hiddenAtRest) {\n <span class=\"node-rest\" [title]=\"hiddenAtRestTitle\" [attr.aria-label]=\"hiddenAtRestTitle\"><i class=\"ph-thin ph-eye-slash\"></i></span>\n }\n </ng-template>\n\n <!-- The rows a Repeater's source adds are not nodes: ONE muted line after its template, never selectable. -->\n <ng-template #moreRowsLine let-tn let-handle=\"handle\">\n <div class=\"tree-more\" aria-disabled=\"true\" [title]=\"moreRowsTitle\"\n [style.padding-inline-start.px]=\"12 + tn.moreRows.depth * 16\">\n @if (handle) {\n <i class=\"ph-thin ph-dots-six-vertical drag-handle tree-more-handle\" aria-hidden=\"true\"></i>\n }\n <span class=\"expand-spacer\"></span>\n <i class=\"ph-thin ph-rows node-icon\"></i>\n <span class=\"node-label\">{{ moreRowsText(tn.moreRows) }}</span>\n </div>\n </ng-template>\n\n <!-- \u2500\u2500 Page elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeSection === 'page') {\n <div class=\"tree-body\"\n cdkDropList\n [cdkDropListData]=\"flatTree\"\n (cdkDropListDropped)=\"onTreeDrop($event)\">\n @if (!flatTree.length) {\n <div class=\"tree-empty\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n <span>No layers yet</span>\n </div>\n }\n\n @for (tn of flatTree; track tn.node) {\n <div class=\"tree-row\"\n cdkDrag\n [cdkDragData]=\"tn\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <i class=\"ph-thin ph-dots-six-vertical drag-handle\" cdkDragHandle></i>\n\n @if (tn.hasChildren) {\n <button class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n } @else {\n <span class=\"expand-spacer\"></span>\n }\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n @if (editingNodeId === tn.node.id) {\n <input class=\"node-rename-input\"\n [value]=\"editingValue\"\n (input)=\"editingValue = $any($event.target).value\"\n (keydown.enter)=\"saveRename(tn)\"\n (keydown.escape)=\"cancelRename()\"\n (blur)=\"saveRename(tn)\"\n (click)=\"$event.stopPropagation()\"\n autofocus>\n } @else {\n <ng-container *ngTemplateOutlet=\"rowLabel; context: { $implicit: tn }\"></ng-container>\n }\n\n <span class=\"node-actions\">\n <button class=\"action-btn\" title=\"Rename\" (click)=\"startRename(tn, $event)\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n </button>\n <button class=\"action-btn\" title=\"Duplicate\" (click)=\"onDuplicateNode(tn, $event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n @if (!tn.locked) {\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n }\n </span>\n </div>\n @if (tn.moreRows) {\n <ng-container *ngTemplateOutlet=\"moreRowsLine; context: { $implicit: tn, handle: true }\"></ng-container>\n }\n }\n </div>\n }\n\n <!-- \u2500\u2500 Global elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeSection === 'global') {\n <div class=\"tree-body\">\n @if (!flatGlobalTree.length) {\n <div class=\"tree-empty\">\n <i class=\"ph-thin ph-globe\"></i>\n <span>No global elements</span>\n </div>\n }\n\n @for (tn of flatGlobalTree; track tn.node) {\n <div class=\"tree-row tree-row--global\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n @if (tn.hasChildren) {\n <button class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n } @else {\n <span class=\"expand-spacer\"></span>\n }\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <ng-container *ngTemplateOutlet=\"rowLabel; context: { $implicit: tn }\"></ng-container>\n @if (tn.depth === 0) {\n <span class=\"node-position-badge\">{{ tn.node.globalPosition }}</span>\n }\n\n <span class=\"node-actions\">\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n @if (tn.moreRows) {\n <ng-container *ngTemplateOutlet=\"moreRowsLine; context: { $implicit: tn }\"></ng-container>\n }\n }\n </div>\n }\n</div>\n", styles: [".layer-tree{display:flex;flex-direction:column;height:100%;overflow:hidden}.layer-tree .tree-header{display:flex;gap:2px;padding:6px 8px;border-bottom:1px solid var(--p-surface-200);flex-shrink:0}.layer-tree .section-tab{display:inline-flex;align-items:center;gap:4px;flex:1;justify-content:center;padding:4px 8px;font-size:11px;font-weight:500;border:1px solid var(--p-surface-200);border-radius:4px;background:transparent;color:var(--p-text-muted-color);cursor:pointer;transition:background .12s,color .12s,border-color .12s;white-space:nowrap}.layer-tree .section-tab i{font-size:11px}.layer-tree .section-tab:hover{background:var(--p-surface-100);color:var(--p-text-color)}.layer-tree .section-tab--active{background:#cb909026;border-color:#cb9090;color:#cb9090}.layer-tree .section-tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;font-size:10px;font-weight:600;background:#cb9090;color:#fff;border-radius:8px}.layer-tree .tree-body{flex:1 1 0;overflow-y:auto;overflow-x:hidden;padding-block:4px}.layer-tree .tree-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:2rem 1rem;color:var(--p-text-muted-color);font-size:12px}.layer-tree .tree-empty i{font-size:22px}.layer-tree .tree-row{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;cursor:pointer;font-size:12px;color:var(--p-text-color);border-inline-start:2px solid transparent;transition:background .12s,border-color .12s;-webkit-user-select:none;user-select:none}.layer-tree .tree-row:hover,.layer-tree .tree-row.is-hovered{background:var(--p-surface-100)}.layer-tree .tree-row.is-selected{background:#cb90901a;border-inline-start-color:#cb9090;font-weight:500}.layer-tree .drag-handle{font-size:10px;color:var(--p-text-muted-color);opacity:0;cursor:grab;flex-shrink:0;transition:opacity .12s}.layer-tree .tree-row:hover .drag-handle{opacity:.5}.layer-tree .drag-handle:hover{opacity:1!important}.layer-tree .cdk-drag-preview{box-sizing:border-box;display:flex;align-items:center;gap:6px;height:28px;padding:0 8px 0 12px;border-radius:6px;background:var(--p-surface-0);border:1px solid #cb9090;box-shadow:0 4px 12px #0000001f;font-size:12px}.layer-tree .cdk-drag-placeholder{height:2px;background:#cb9090;border-radius:1px;opacity:.6}.layer-tree .cdk-drag-placeholder>*{visibility:hidden}.layer-tree .cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.layer-tree .expand-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;flex-shrink:0;border-radius:2px}.layer-tree .expand-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .expand-btn i{font-size:10px}.layer-tree .expand-spacer{width:16px;flex-shrink:0}.layer-tree .node-icon{font-size:14px;color:var(--p-text-muted-color);flex-shrink:0}.layer-tree .node-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto;min-width:4.5em}.layer-tree .node-label.node-label--named{color:var(--p-primary-color, #cb9090);font-weight:500}.layer-tree .node-sub{font-style:normal;font-weight:400;color:#535d6e}.layer-tree .node-template{flex:none;padding:0 4px;border-radius:4px;background:#8a3a72;color:#fff;font:500 10px Fira Code,Fira Mono,monospace;line-height:16px}.layer-tree .tree-more{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;font-size:12px;font-style:italic;color:#64748b;border-inline-start:2px solid transparent;cursor:default;-webkit-user-select:none;user-select:none}.layer-tree .tree-more-handle{visibility:hidden}.layer-tree .node-role{flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 4px;border:1px dashed #c9a1be;border-radius:4px;background:#f7eef5;color:#8a3a72;font:500 10px Fira Code,Fira Mono,monospace;line-height:16px}.layer-tree .node-lock{flex:none;color:#64748b;font-size:12px}.layer-tree .node-rename-input{flex:1;min-width:0;border:1px solid var(--p-primary-color, #cb9090);border-radius:4px;background:var(--p-surface-0, #fff);color:var(--p-text-color);font-family:inherit;font-size:.72rem;padding:1px 4px;outline:none}.layer-tree .node-actions{display:flex;gap:2px;margin-inline-start:auto;opacity:0;transition:opacity .12s;flex-shrink:0}.layer-tree .tree-row:hover .node-actions,.layer-tree .tree-row.is-selected .node-actions{opacity:1}.layer-tree .tree-section-header{display:flex;align-items:center;gap:6px;padding:6px 12px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color);border-top:1px solid var(--p-surface-200);margin-top:4px}.layer-tree .tree-section-header i{font-size:12px}.layer-tree .tree-body--global{flex:0 0 auto}.layer-tree .tree-row--global{border-inline-start-color:transparent}.layer-tree .tree-row--global.is-selected{background:#638fcb1a;border-inline-start-color:#638fcb}.layer-tree .node-rest{display:inline-flex;align-items:center;flex-shrink:0;font-size:12px;color:var(--p-text-muted-color)}.layer-tree .node-position-badge{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#638fcb;background:#638fcb1f;padding:1px 5px;border-radius:3px;flex-shrink:0}.layer-tree .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:4px;font-size:12px;transition:background .12s,color .12s}.layer-tree .action-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .action-btn--delete:hover{background:#dc26261a;color:#dc2626}\n"] }]
|
|
7424
|
+
args: [{ selector: 'app-layer-tree', standalone: false, template: "<div class=\"layer-tree\">\n <div class=\"tree-header\">\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'page'\" (click)=\"activeSection = 'page'\">\n <i class=\"ph-thin ph-stack\"></i> {{ words.page }}\n </button>\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'global'\" (click)=\"activeSection = 'global'\">\n <i class=\"ph-thin ph-globe\"></i> {{ words.global }}\n @if (flatGlobalTree.length) {\n <span class=\"section-tab-badge\">{{ flatGlobalTree.length }}</span>\n }\n </button>\n </div>\n\n <!-- A node's label with what the shipped Repeater adds to it (boards 4 / 8): its sub-label, the\n template chip, a composite part's role and lock, the hidden-at-rest glyph. -->\n <ng-template #rowLabel let-tn>\n <span class=\"node-label\" [class.node-label--named]=\"tn.node.label\" [attr.title]=\"rowTitle(tn)\">{{ tn.label }}@if (tn.sub) {<em class=\"node-sub\"> \u00B7 {{ tn.sub }}</em>}</span>\n @if (tn.template) {\n <span class=\"node-template\" [title]=\"templateTitle\">{{ templateLabel }}</span>\n }\n @if (tn.node.role) {\n <span class=\"node-role\" [title]=\"roleTitle + ': ' + tn.node.role\">{{ tn.node.role }}</span>\n }\n @if (tn.locked) {\n <i class=\"ph-thin ph-lock-simple node-lock\" [title]=\"lockedPartTitle\" [attr.aria-label]=\"lockedPartTitle\"></i>\n }\n @if (tn.hiddenAtRest) {\n <span class=\"node-rest\" [title]=\"hiddenAtRestTitle\" [attr.aria-label]=\"hiddenAtRestTitle\"><i class=\"ph-thin ph-eye-slash\"></i></span>\n }\n </ng-template>\n\n <!-- The rows a Repeater's source adds are not nodes: ONE muted line after its template, never selectable. -->\n <ng-template #moreRowsLine let-tn let-handle=\"handle\">\n <div class=\"tree-more\" aria-disabled=\"true\" [title]=\"moreRowsTitle\"\n [style.padding-inline-start.px]=\"12 + tn.moreRows.depth * 16\">\n @if (handle) {\n <i class=\"ph-thin ph-dots-six-vertical drag-handle tree-more-handle\" aria-hidden=\"true\"></i>\n }\n <span class=\"expand-spacer\"></span>\n <i class=\"ph-thin ph-rows node-icon\"></i>\n <span class=\"node-label\">{{ moreRowsText(tn.moreRows) }}</span>\n </div>\n </ng-template>\n\n <!-- \u2500\u2500 Page elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeSection === 'page') {\n <div class=\"tree-body\"\n cdkDropList\n [cdkDropListData]=\"flatTree\"\n (cdkDropListDropped)=\"onTreeDrop($event)\">\n @if (!flatTree.length) {\n <div class=\"tree-empty\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n <span>{{ words.empty }}</span>\n </div>\n }\n\n @for (tn of flatTree; track tn.node) {\n <div class=\"tree-row\"\n cdkDrag\n [cdkDragData]=\"tn\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <i class=\"ph-thin ph-dots-six-vertical drag-handle\" cdkDragHandle></i>\n\n @if (tn.hasChildren) {\n <button class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n } @else {\n <span class=\"expand-spacer\"></span>\n }\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n @if (editingNodeId === tn.node.id) {\n <input class=\"node-rename-input\"\n [value]=\"editingValue\"\n (input)=\"editingValue = $any($event.target).value\"\n (keydown.enter)=\"saveRename(tn)\"\n (keydown.escape)=\"cancelRename()\"\n (blur)=\"saveRename(tn)\"\n (click)=\"$event.stopPropagation()\"\n autofocus>\n } @else {\n <ng-container *ngTemplateOutlet=\"rowLabel; context: { $implicit: tn }\"></ng-container>\n }\n\n <span class=\"node-actions\">\n <button class=\"action-btn\" [title]=\"words.rename\" (click)=\"startRename(tn, $event)\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n </button>\n <button class=\"action-btn\" [title]=\"words.duplicate\" (click)=\"onDuplicateNode(tn, $event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n @if (!tn.locked) {\n <button class=\"action-btn action-btn--delete\" [title]=\"words.delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n }\n </span>\n </div>\n @if (tn.moreRows) {\n <ng-container *ngTemplateOutlet=\"moreRowsLine; context: { $implicit: tn, handle: true }\"></ng-container>\n }\n }\n </div>\n }\n\n <!-- \u2500\u2500 Global elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeSection === 'global') {\n <div class=\"tree-body\">\n @if (!flatGlobalTree.length) {\n <div class=\"tree-empty\">\n <i class=\"ph-thin ph-globe\"></i>\n <span>{{ words.globalEmpty }}</span>\n </div>\n }\n\n @for (tn of flatGlobalTree; track tn.node) {\n <div class=\"tree-row tree-row--global\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n @if (tn.hasChildren) {\n <button class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n } @else {\n <span class=\"expand-spacer\"></span>\n }\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <ng-container *ngTemplateOutlet=\"rowLabel; context: { $implicit: tn }\"></ng-container>\n @if (tn.depth === 0) {\n <span class=\"node-position-badge\">{{ tn.node.globalPosition }}</span>\n }\n\n <span class=\"node-actions\">\n <button class=\"action-btn action-btn--delete\" [title]=\"words.delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n @if (tn.moreRows) {\n <ng-container *ngTemplateOutlet=\"moreRowsLine; context: { $implicit: tn }\"></ng-container>\n }\n }\n </div>\n }\n</div>\n", styles: [".layer-tree{display:flex;flex-direction:column;height:100%;overflow:hidden}.layer-tree .tree-header{display:flex;gap:2px;padding:6px 8px;border-bottom:1px solid var(--p-surface-200);flex-shrink:0}.layer-tree .section-tab{display:inline-flex;align-items:center;gap:4px;flex:1;justify-content:center;padding:4px 8px;font-size:11px;font-weight:500;border:1px solid var(--p-surface-200);border-radius:4px;background:transparent;color:var(--p-text-muted-color);cursor:pointer;transition:background .12s,color .12s,border-color .12s;white-space:nowrap}.layer-tree .section-tab i{font-size:11px}.layer-tree .section-tab:hover{background:var(--p-surface-100);color:var(--p-text-color)}.layer-tree .section-tab--active{background:#cb909026;border-color:#cb9090;color:#cb9090}.layer-tree .section-tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;font-size:10px;font-weight:600;background:#cb9090;color:#fff;border-radius:8px}.layer-tree .tree-body{flex:1 1 0;overflow-y:auto;overflow-x:hidden;padding-block:4px}.layer-tree .tree-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:2rem 1rem;color:var(--p-text-muted-color);font-size:12px}.layer-tree .tree-empty i{font-size:22px}.layer-tree .tree-row{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;cursor:pointer;font-size:12px;color:var(--p-text-color);border-inline-start:2px solid transparent;transition:background .12s,border-color .12s;-webkit-user-select:none;user-select:none}.layer-tree .tree-row:hover,.layer-tree .tree-row.is-hovered{background:var(--p-surface-100)}.layer-tree .tree-row.is-selected{background:#cb90901a;border-inline-start-color:#cb9090;font-weight:500}.layer-tree .drag-handle{font-size:10px;color:var(--p-text-muted-color);opacity:0;cursor:grab;flex-shrink:0;transition:opacity .12s}.layer-tree .tree-row:hover .drag-handle{opacity:.5}.layer-tree .drag-handle:hover{opacity:1!important}.layer-tree .cdk-drag-preview{box-sizing:border-box;display:flex;align-items:center;gap:6px;height:28px;padding:0 8px 0 12px;border-radius:6px;background:var(--p-surface-0);border:1px solid #cb9090;box-shadow:0 4px 12px #0000001f;font-size:12px}.layer-tree .cdk-drag-placeholder{height:2px;background:#cb9090;border-radius:1px;opacity:.6}.layer-tree .cdk-drag-placeholder>*{visibility:hidden}.layer-tree .cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.layer-tree .expand-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;flex-shrink:0;border-radius:2px}.layer-tree .expand-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .expand-btn i{font-size:10px}.layer-tree .expand-spacer{width:16px;flex-shrink:0}.layer-tree .node-icon{font-size:14px;color:var(--p-text-muted-color);flex-shrink:0}.layer-tree .node-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto;min-width:4.5em}.layer-tree .node-label.node-label--named{color:var(--p-primary-color, #cb9090);font-weight:500}.layer-tree .node-sub{font-style:normal;font-weight:400;color:#535d6e}.layer-tree .node-template{flex:none;padding:0 4px;border-radius:4px;background:#8a3a72;color:#fff;font:500 10px Fira Code,Fira Mono,monospace;line-height:16px}.layer-tree .tree-more{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;font-size:12px;font-style:italic;color:#64748b;border-inline-start:2px solid transparent;cursor:default;-webkit-user-select:none;user-select:none}.layer-tree .tree-more-handle{visibility:hidden}.layer-tree .node-role{flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 4px;border:1px dashed #c9a1be;border-radius:4px;background:#f7eef5;color:#8a3a72;font:500 10px Fira Code,Fira Mono,monospace;line-height:16px}.layer-tree .node-lock{flex:none;color:#64748b;font-size:12px}.layer-tree .node-rename-input{flex:1;min-width:0;border:1px solid var(--p-primary-color, #cb9090);border-radius:4px;background:var(--p-surface-0, #fff);color:var(--p-text-color);font-family:inherit;font-size:.72rem;padding:1px 4px;outline:none}.layer-tree .node-actions{display:flex;gap:2px;margin-inline-start:auto;opacity:0;transition:opacity .12s;flex-shrink:0}.layer-tree .tree-row:hover .node-actions,.layer-tree .tree-row.is-selected .node-actions{opacity:1}.layer-tree .tree-section-header{display:flex;align-items:center;gap:6px;padding:6px 12px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color);border-top:1px solid var(--p-surface-200);margin-top:4px}.layer-tree .tree-section-header i{font-size:12px}.layer-tree .tree-body--global{flex:0 0 auto}.layer-tree .tree-row--global{border-inline-start-color:transparent}.layer-tree .tree-row--global.is-selected{background:#638fcb1a;border-inline-start-color:#638fcb}.layer-tree .node-rest{display:inline-flex;align-items:center;flex-shrink:0;font-size:12px;color:var(--p-text-muted-color)}.layer-tree .node-position-badge{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#638fcb;background:#638fcb1f;padding:1px 5px;border-radius:3px;flex-shrink:0}.layer-tree .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:4px;font-size:12px;transition:background .12s,color .12s}.layer-tree .action-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .action-btn--delete:hover{background:#dc26261a;color:#dc2626}\n"] }]
|
|
7371
7425
|
}], ctorParameters: () => [{ type: ComponentRegistryService }, { type: OverlayService }, { type: DragEngineService }], propDecorators: { layout: [{
|
|
7372
7426
|
type: Input
|
|
7373
7427
|
}], globalElements: [{
|
|
@@ -9601,7 +9655,14 @@ class BuilderDialogComponent {
|
|
|
9601
9655
|
this.cdr.markForCheck();
|
|
9602
9656
|
}
|
|
9603
9657
|
openRuntime() { this.runtimeOpen = true; this.dismissed = false; this.recompute(); }
|
|
9604
|
-
closeRuntime() {
|
|
9658
|
+
closeRuntime() {
|
|
9659
|
+
this.runtimeOpen = false;
|
|
9660
|
+
// Unpin it first (a free-standing dialog the selection opened, A4) — that emits and clears
|
|
9661
|
+
// `dismissed`; then hide it even while the selection still sits inside a revealed part (R2).
|
|
9662
|
+
this.designState?.dismiss(this.nodeId);
|
|
9663
|
+
this.dismissed = true;
|
|
9664
|
+
this.recompute();
|
|
9665
|
+
}
|
|
9605
9666
|
ngOnInit() {
|
|
9606
9667
|
// Board A4: "Closed later? Click it [in the tree] and it reopens." Selection IS the
|
|
9607
9668
|
// reopen signal — tree row, breadcrumb crumb, target-picker jump all land here.
|
|
@@ -9620,8 +9681,10 @@ class BuilderDialogComponent {
|
|
|
9620
9681
|
// stays shut because nothing is selected on load.
|
|
9621
9682
|
//
|
|
9622
9683
|
// CC1 (R2, board 3): with the canvas design state present, "selected" means the selection is the
|
|
9623
|
-
// dialog OR ANY PART INSIDE IT (a drawer link picked from the tree)
|
|
9624
|
-
//
|
|
9684
|
+
// dialog OR ANY PART INSIDE IT (a drawer link picked from the tree). A composite PART (the
|
|
9685
|
+
// drawer, it carries a role) closes again when the selection leaves it; a free-standing Dialog
|
|
9686
|
+
// stays open until its X / backdrop / the eye closes it, as A4 has it (selectionRevealOf).
|
|
9687
|
+
// The "Showing on canvas" switcher holds a part open.
|
|
9625
9688
|
if (this.designState) {
|
|
9626
9689
|
this.stateSub = this.designState.state$.subscribe(() => { this.dismissed = false; this.recompute(); });
|
|
9627
9690
|
}
|
|
@@ -12480,5 +12543,5 @@ class BuilderSpacerComponentConfig extends ComponentConfig {
|
|
|
12480
12543
|
* Generated bundle index. Do not edit.
|
|
12481
12544
|
*/
|
|
12482
12545
|
|
|
12483
|
-
export { ACTION_TYPE_OPTIONS, AuditCode, AuditPanelComponent, AuditSeverity, BODY_CRUMB_LABEL, BOUND_ICON, BREAKPOINT_PRESENTATION, BUILDER_CANVAS_CONTAINER, BUILDER_CHROME_EN, BUILDER_FONT_LINK_ATTR, BUILDER_TOKEN_SCOPE_CLASS, BackgroundGroupStyleConfig, BorderGroupStyleConfig, BreadcrumbBarComponent, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDataSourceService, BuilderDialogComponent, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CANVAS_EMPTY_IMAGE_LABEL_EN, CANVAS_EMPTY_IMAGE_LABEL_KEY, CANVAS_ROOT_RESET_CSS, COMPOSITE_PRESETS, CanvasDesignStateService, CanvasShowState, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, CompositePresetId, DEFAULT_ESSENTIAL_TRAITS, DRAG_HANDLE_ATTR, DROP_REFUSED_CLASS, DataSourceRegistryService, DataSourceType, DialogComponentConfig, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, EDITOR_CHROME_ATTRIBUTE, ESSENTIAL_TRAITS_BY_TYPE, EffectsGroupStyleConfig, FIELD_KIND_PRESETS, FIELD_PARTS, FORM_STATE_BLOCK_TYPES, FORM_TYPE, FieldComponentConfig, FieldGroupComponentConfig, FormComponentConfig, FormErrorComponentConfig, FormStateService, FormSuccessComponentConfig, GalleryConfig, GroupStyleConfig, HIDDEN_AT_REST_LABEL_KEY, HIDDEN_AT_REST_TYPES, INSERT_REFUSAL_LABEL_KEYS, INTERACTION_STATES, INVERSE_ACTION, IOX_BUILDER_CANVAS, IOX_BUILDER_LABELS, IOX_DATA_SOURCE_ENDPOINTS, InsertRefusal, InteractionEngineService, InteractionPresetRegistryService, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LAYOUT_PRESETS, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, PALETTE_PRESET_DEFS, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PositionGroupStyleConfig, PresetRegistryService, RELATIONAL_STATES, RELATIONAL_STATE_BASE, REPEATER_SOURCE_PROP, REPEATER_TYPE, ROOT_ESCAPE_PX, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, RootZone, SCREEN_WIDTH_OPTIONS, STRUCTURAL_STATES, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, SizeGroupStyleConfig, SliderContainerComponent, SliderContainerConfig, SliderSlideComponent, SliderSlideConfig, SpacingGroupStyleConfig, StructureFix, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, SubmitButtonComponentConfig, SymbolRegistryService, TARGETED_STATES, TARGETED_STATE_BASE, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, TypographyGroupStyleConfig, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, VALIDATION_STATES, VALIDATION_STATE_CLASS, ViewportService, ZOOM_OPTIONS, arrayItemSchemaFields, arraySchemaFields, auditNodes, bindingLabel, bindingLabels, buildCompositePreset, buildConsentPreset, buildElementStyleSpecimen, buildFieldKindPreset, buildFlavorSpecimen, buildFormPreset, buildFullStyleTraits, buildLayoutPreset, buildPalettePreset, buildPresetStyleTraits, builderCompositeFactory, canFluidise, canvasEmptyImageLabel, collapseColumnsProps, columnCountOf, defaultBuilderLabels, defaultPageSettings, dropTargetLabel, escapesRoot, essentialTraitsFor, findNodeLocation, findParentOf, flattenLayerTree, flattenSchemaFields, fluidiseProps, generateNodeId, insertRefusal, insertRefusalLabel, isHiddenAt, isHiddenAtRest, isSpecimen, markSpecimen, matchesControlSearch, nearestFormNode, nodeAncestorPath, nodeAncestorPathInEither, nodeStyleMap, pathToNode, refreshSubtree, removalRefusal, reorderProps, repeaterSourceAlias, repeaterSubLabel, resolveInsertTarget, resolveTraitControllerType, resolveTraitOptions, rootEscapeBand, rootZone, setStyleTrait, setTrait, stackChildrenProps, summarise, syncEmptyImagePlaceholder, syncNodeDesign, syncNodeInputs, textNode, upsertNodeStyles, visibilityProps, walkNodes, wrapNode };
|
|
12546
|
+
export { ACTION_TYPE_OPTIONS, AuditCode, AuditPanelComponent, AuditSeverity, BODY_CRUMB_LABEL, BOUND_ICON, BREAKPOINT_PRESENTATION, BUILDER_CANVAS_CONTAINER, BUILDER_CHROME_EN, BUILDER_FONT_LINK_ATTR, BUILDER_TOKEN_SCOPE_CLASS, BackgroundGroupStyleConfig, BorderGroupStyleConfig, BreadcrumbBarComponent, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDataSourceService, BuilderDialogComponent, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CANVAS_EMPTY_IMAGE_LABEL_EN, CANVAS_EMPTY_IMAGE_LABEL_KEY, CANVAS_ROOT_RESET_CSS, COMPOSITE_PRESETS, CanvasDesignStateService, CanvasShowState, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, CompositePresetId, DEFAULT_ESSENTIAL_TRAITS, DRAG_HANDLE_ATTR, DROP_REFUSED_CLASS, DataSourceRegistryService, DataSourceType, DialogComponentConfig, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, EDITOR_CHROME_ATTRIBUTE, ESSENTIAL_TRAITS_BY_TYPE, EffectsGroupStyleConfig, FIELD_KIND_PRESETS, FIELD_PARTS, FORM_STATE_BLOCK_TYPES, FORM_TYPE, FieldComponentConfig, FieldGroupComponentConfig, FormComponentConfig, FormErrorComponentConfig, FormStateService, FormSuccessComponentConfig, GalleryConfig, GroupStyleConfig, HIDDEN_AT_REST_LABEL_KEY, HIDDEN_AT_REST_TYPES, INSERT_REFUSAL_LABEL_KEYS, INTERACTION_STATES, INVERSE_ACTION, IOX_BUILDER_CANVAS, IOX_BUILDER_LABELS, IOX_DATA_SOURCE_ENDPOINTS, InsertRefusal, InteractionEngineService, InteractionPresetRegistryService, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LAYOUT_PRESETS, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, PALETTE_PRESET_DEFS, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PositionGroupStyleConfig, PresetRegistryService, RELATIONAL_STATES, RELATIONAL_STATE_BASE, REPEATER_SOURCE_PROP, REPEATER_TYPE, ROOT_ESCAPE_PX, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, RootZone, SCREEN_WIDTH_OPTIONS, STRUCTURAL_STATES, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, SelectionReveal, SizeGroupStyleConfig, SliderContainerComponent, SliderContainerConfig, SliderSlideComponent, SliderSlideConfig, SpacingGroupStyleConfig, StructureFix, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, SubmitButtonComponentConfig, SymbolRegistryService, TARGETED_STATES, TARGETED_STATE_BASE, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, TypographyGroupStyleConfig, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, VALIDATION_STATES, VALIDATION_STATE_CLASS, ViewportService, ZOOM_OPTIONS, arrayItemSchemaFields, arraySchemaFields, auditNodes, bindingLabel, bindingLabels, buildCompositePreset, buildConsentPreset, buildElementStyleSpecimen, buildFieldKindPreset, buildFlavorSpecimen, buildFormPreset, buildFullStyleTraits, buildLayoutPreset, buildPalettePreset, buildPresetStyleTraits, builderCompositeFactory, canFluidise, canvasEmptyImageLabel, collapseColumnsProps, columnCountOf, defaultBuilderLabels, defaultPageSettings, dropTargetLabel, escapesRoot, essentialTraitsFor, findNodeLocation, findParentOf, flattenLayerTree, flattenSchemaFields, fluidiseProps, generateNodeId, insertRefusal, insertRefusalLabel, isHiddenAt, isHiddenAtRest, isSpecimen, markSpecimen, matchesControlSearch, nearestFormNode, nodeAncestorPath, nodeAncestorPathInEither, nodeStyleMap, pathToNode, refreshSubtree, removalRefusal, reorderProps, repeaterSourceAlias, repeaterSubLabel, resolveInsertTarget, resolveTraitControllerType, resolveTraitOptions, rootEscapeBand, rootZone, selectionRevealOf, setStyleTrait, setTrait, stackChildrenProps, summarise, syncEmptyImagePlaceholder, syncNodeDesign, syncNodeInputs, textNode, upsertNodeStyles, visibilityProps, walkNodes, wrapNode };
|
|
12484
12547
|
//# sourceMappingURL=vectoriox-iox-builder.mjs.map
|