@vectoriox/iox-builder 1.4.58 → 1.4.60
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.
|
@@ -318,6 +318,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
318
318
|
class SymbolRegistryService {
|
|
319
319
|
constructor() {
|
|
320
320
|
this.symbols = new Map();
|
|
321
|
+
/** symbolIds that have a live reusable block in the DB — used to guard isDetachedSymbol. */
|
|
322
|
+
this.blockSymbolIds = new Set();
|
|
321
323
|
}
|
|
322
324
|
setSymbols(symbols) {
|
|
323
325
|
this.symbols.clear();
|
|
@@ -337,6 +339,18 @@ class SymbolRegistryService {
|
|
|
337
339
|
remove(id) {
|
|
338
340
|
this.symbols.delete(id);
|
|
339
341
|
}
|
|
342
|
+
/** Set the symbolIds that have a live reusable block. Called on page load. */
|
|
343
|
+
setBlockSymbolIds(ids) {
|
|
344
|
+
this.blockSymbolIds = new Set(ids);
|
|
345
|
+
}
|
|
346
|
+
/** Returns true only when the symbol has a live backing reusable block. */
|
|
347
|
+
isBlockSymbol(id) {
|
|
348
|
+
return this.blockSymbolIds.has(id);
|
|
349
|
+
}
|
|
350
|
+
/** Remove a symbolId from the block-backed set (called when a block is deleted). */
|
|
351
|
+
removeBlockSymbolId(id) {
|
|
352
|
+
this.blockSymbolIds.delete(id);
|
|
353
|
+
}
|
|
340
354
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SymbolRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
341
355
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SymbolRegistryService }); }
|
|
342
356
|
}
|
|
@@ -2953,7 +2967,11 @@ class OverlayComponent {
|
|
|
2953
2967
|
return !!this.selectEntry?.node?.symbolId;
|
|
2954
2968
|
}
|
|
2955
2969
|
get isDetachedSymbol() {
|
|
2956
|
-
|
|
2970
|
+
const node = this.selectEntry?.node;
|
|
2971
|
+
if (!node || node.symbolId || !node.detachedSymbolId)
|
|
2972
|
+
return false;
|
|
2973
|
+
// Only show the re-link button when the backing block still exists.
|
|
2974
|
+
return this.symbolRegistry.isBlockSymbol(node.detachedSymbolId);
|
|
2957
2975
|
}
|
|
2958
2976
|
get linkedSymbolName() {
|
|
2959
2977
|
const id = this.selectEntry?.node?.symbolId;
|
|
@@ -3320,11 +3338,11 @@ class ToolbarComponent {
|
|
|
3320
3338
|
this.zoomPopover.hide();
|
|
3321
3339
|
}
|
|
3322
3340
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3323
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: ToolbarComponent, isStandalone: false, selector: "app-toolbar", inputs: { activeMode: "activeMode", activeDevice: "activeDevice", activeZoom: "activeZoom", activeScreenWidth: "activeScreenWidth", canUndo: "canUndo", canRedo: "canRedo", isSaving: "isSaving", pageStatus: "pageStatus" }, outputs: { modeChange: "modeChange", deviceChange: "deviceChange", zoomChange: "zoomChange", screenWidthChange: "screenWidthChange", undo: "undo", redo: "redo", save: "save", preview: "preview", publishToggle: "publishToggle" }, viewQueries: [{ propertyName: "modePopover", first: true, predicate: ["modePopover"], descendants: true }, { propertyName: "devicePopover", first: true, predicate: ["devicePopover"], descendants: true }, { propertyName: "zoomPopover", first: true, predicate: ["zoomPopover"], descendants: true }, { propertyName: "widthPopover", first: true, predicate: ["widthPopover"], descendants: true }], ngImport: i0, template: "<div class=\"builder-toolbar\">\n <!-- \u2500\u2500 Mode switcher (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful\"\n (click)=\"toggleModePopover($event)\"\n [title]=\"activeModeLabel\">\n <i [class]=\"activeModeIcon\"></i>\n </button>\n\n <p-popover #modePopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Select\"\n (click)=\"selectMode(modes.Select)\">\n <i class=\"ph-thin ph-cursor\"></i>\n <span>Select</span>\n </button>\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Pan\"\n (click)=\"selectMode(modes.Pan)\">\n <i class=\"ph-thin ph-hand\"></i>\n <span>Pan</span>\n </button>\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Style\"\n (click)=\"selectMode(modes.Style)\">\n <i class=\"ph-thin ph-paint-brush-broad\"></i>\n <span>Style</span>\n </button>\n </div>\n </p-popover>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Undo / Redo (action) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn\" title=\"Undo\" [disabled]=\"!canUndo\" (click)=\"undo.emit()\">\n <i class=\"ph-thin ph-arrow-counter-clockwise\"></i>\n </button>\n <button class=\"tool-btn\" title=\"Redo\" [disabled]=\"!canRedo\" (click)=\"redo.emit()\">\n <i class=\"ph-thin ph-arrow-clockwise\"></i>\n </button>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Device switcher (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful\"\n (click)=\"toggleDevicePopover($event)\"\n [title]=\"activeDeviceOption.label\">\n <i [class]=\"activeDeviceOption.icon\"></i>\n </button>\n\n <p-popover #devicePopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let d of deviceOptions\"\n [class.is-active]=\"activeDevice === d.mode\"\n (click)=\"selectDevice(d.mode)\">\n <i [class]=\"d.icon\"></i>\n <span>{{ d.label }}</span>\n </button>\n </div>\n </p-popover>\n\n <!-- \u2500\u2500 Screen width \u2014 Desktop only \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeDevice === deviceModes.Desktop) {\n <button class=\"tool-btn stateful width-btn\"\n (click)=\"toggleWidthPopover($event)\"\n title=\"Screen width\">\n <span class=\"width-label\">{{ activeScreenWidth }}</span>\n </button>\n }\n\n <p-popover #widthPopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let w of screenWidthOptions\"\n [class.is-active]=\"activeScreenWidth === w.width\"\n (click)=\"selectScreenWidth(w.width)\">\n <span>{{ w.label }}</span>\n </button>\n </div>\n </p-popover>\n\n <!-- \u2500\u2500 Zoom (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful zoom-btn\"\n (click)=\"toggleZoomPopover($event)\"\n title=\"Zoom\">\n <span class=\"zoom-label\">{{ zoomLabel }}</span>\n </button>\n\n <p-popover #zoomPopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"zoom-popover\">\n <div class=\"zoom-slider-row\">\n <input class=\"zoom-slider\"\n type=\"range\"\n min=\"20\" max=\"100\" step=\"1\"\n [value]=\"sliderZoom\"\n (input)=\"onSliderChange($event)\">\n <span class=\"zoom-slider-value\">{{ zoomLabel }}</span>\n </div>\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let z of zoomOptions\"\n [class.is-active]=\"z.value === activeZoom\"\n (click)=\"selectZoom(z.value)\">\n <span>{{ z.label }}</span>\n </button>\n </div>\n </div>\n </p-popover>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Save \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn save-btn\"\n [class.is-saving]=\"isSaving\"\n [disabled]=\"isSaving\"\n title=\"Save\"\n (click)=\"save.emit()\">\n <i class=\"ph-thin ph-floppy-disk\"></i>\n </button>\n\n <!-- \u2500\u2500 Preview \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn preview-btn\"\n title=\"Preview\"\n (click)=\"preview.emit()\">\n <i class=\"ph-thin ph-eye\"></i>\n </button>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Publish / Unpublish \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn publish-btn\"\n [class.is-published]=\"isPublished\"\n [title]=\"isPublished ? 'Unpublish' : 'Publish'\"\n (click)=\"publishToggle.emit()\">\n <i [class]=\"isPublished ? 'ph-thin ph-cloud-slash' : 'ph-thin ph-cloud-arrow-up'\"></i>\n <span class=\"publish-label\">{{ isPublished ? 'Unpublish' : 'Publish' }}</span>\n </button>\n</div>\n", styles: [".builder-toolbar{display:flex;align-items:center;gap:.25rem;background:#0f172ae0;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid rgba(148,163,184,.2);border-radius:999px;padding:.3rem .5rem;box-shadow:0 8px 24px #0f172a47}.divider{width:1px;height:1.25rem;background:#94a3b833;margin:0 .2rem;flex-shrink:0}.tool-btn{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:50%;border:none;background:transparent;color:#ffffffbf;cursor:pointer;font-size:1.1rem;transition:background .15s,color .15s}.tool-btn:hover:not(:disabled){background:#ffffff1f;color:#fff}.tool-btn:disabled{opacity:.3;cursor:default}.tool-btn.stateful{position:relative}.tool-btn.stateful:after{content:\"\";position:absolute;inset-block-end:2px;width:4px;height:4px;border-radius:50%;background:#cb9090;opacity:.6}.zoom-btn{width:auto;border-radius:999px;padding:0 .5rem;min-width:2.5rem}.zoom-btn .zoom-label{font-size:.7rem;font-weight:500;letter-spacing:.02em}::ng-deep .toolbar-popover .zoom-popover{display:flex;flex-direction:column;gap:4px;padding:.15rem 0}::ng-deep .toolbar-popover .zoom-slider-row{display:flex;align-items:center;gap:.5rem;padding:.3rem .65rem .1rem}::ng-deep .toolbar-popover .zoom-slider{flex:1;-webkit-appearance:none;appearance:none;height:3px;border-radius:999px;background:#fff3;outline:none;cursor:pointer}::ng-deep .toolbar-popover .zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;border-radius:50%;background:#cb9090;cursor:pointer;transition:transform .12s}::ng-deep .toolbar-popover .zoom-slider::-webkit-slider-thumb:hover{transform:scale(1.2)}::ng-deep .toolbar-popover .zoom-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;border:none;background:#cb9090;cursor:pointer}::ng-deep .toolbar-popover .zoom-slider-value{font-size:.68rem;font-weight:500;color:#ffffffbf;min-width:2.4rem;text-align:right;letter-spacing:.02em}.width-btn{width:auto;border-radius:999px;padding:0 .5rem;min-width:2.8rem}.width-btn .width-label{font-size:.7rem;font-weight:500;letter-spacing:.02em}.save-btn{background:#cb9090;color:#fff}.save-btn:hover:not(:disabled){background:color-mix(in srgb,#cb9090 85%,#fff);color:#fff}.save-btn.is-saving{opacity:.7}.publish-btn{width:auto;border-radius:999px;padding:0 .65rem;gap:.35rem;font-size:.75rem;font-weight:500;background:#22c55e;color:#fff;border:none}.publish-btn .publish-label{font-size:.72rem;letter-spacing:.02em}.publish-btn:hover:not(:disabled){background:color-mix(in srgb,#22c55e 85%,#fff);color:#fff}.publish-btn.is-published{background:transparent;color:#94a3b8;border:1px solid rgba(255,255,255,.15)}.publish-btn.is-published:hover:not(:disabled){background:#ef444426;color:#f87171;border-color:#ef44444d}.preview-btn{color:#ffffffbf;border:1px solid rgba(255,255,255,.15)}.preview-btn:hover:not(:disabled){background:#ffffff1a;color:#fff;border-color:#ffffff40}::ng-deep .toolbar-popover .p-popover-content{padding:.3rem!important;background:#0f172ae0!important;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid rgba(148,163,184,.2)!important;border-radius:12px!important;box-shadow:0 12px 32px #0f172a59!important}.popover-strip{display:flex;flex-direction:column;gap:2px;min-width:120px}.popover-option{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4rem .65rem;border:none;background:transparent;color:#ffffffbf;cursor:pointer;font-size:.8rem;border-radius:8px;transition:background .12s,color .12s;white-space:nowrap}.popover-option i{font-size:1rem}.popover-option:hover{background:#ffffff1a;color:#fff}.popover-option.is-active{background:#cb9090;color:#fff}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: i2$1.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }] }); }
|
|
3341
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: ToolbarComponent, isStandalone: false, selector: "app-toolbar", inputs: { activeMode: "activeMode", activeDevice: "activeDevice", activeZoom: "activeZoom", activeScreenWidth: "activeScreenWidth", canUndo: "canUndo", canRedo: "canRedo", isSaving: "isSaving", pageStatus: "pageStatus" }, outputs: { modeChange: "modeChange", deviceChange: "deviceChange", zoomChange: "zoomChange", screenWidthChange: "screenWidthChange", undo: "undo", redo: "redo", save: "save", preview: "preview", publishToggle: "publishToggle" }, viewQueries: [{ propertyName: "modePopover", first: true, predicate: ["modePopover"], descendants: true }, { propertyName: "devicePopover", first: true, predicate: ["devicePopover"], descendants: true }, { propertyName: "zoomPopover", first: true, predicate: ["zoomPopover"], descendants: true }, { propertyName: "widthPopover", first: true, predicate: ["widthPopover"], descendants: true }], ngImport: i0, template: "<div class=\"builder-toolbar\">\n <!-- \u2500\u2500 Mode switcher (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful\"\n (click)=\"toggleModePopover($event)\"\n [title]=\"activeModeLabel\">\n <i [class]=\"activeModeIcon\"></i>\n </button>\n\n <p-popover #modePopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Select\"\n (click)=\"selectMode(modes.Select)\">\n <i class=\"ph-thin ph-cursor\"></i>\n <span>Select</span>\n </button>\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Pan\"\n (click)=\"selectMode(modes.Pan)\">\n <i class=\"ph-thin ph-hand\"></i>\n <span>Pan</span>\n </button>\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Style\"\n (click)=\"selectMode(modes.Style)\">\n <i class=\"ph-thin ph-paint-brush-broad\"></i>\n <span>Style</span>\n </button>\n </div>\n </p-popover>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Undo / Redo (action) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn\" title=\"Undo\" [disabled]=\"!canUndo\" (click)=\"undo.emit()\">\n <i class=\"ph-thin ph-arrow-counter-clockwise\"></i>\n </button>\n <button class=\"tool-btn\" title=\"Redo\" [disabled]=\"!canRedo\" (click)=\"redo.emit()\">\n <i class=\"ph-thin ph-arrow-clockwise\"></i>\n </button>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Device switcher (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful\"\n (click)=\"toggleDevicePopover($event)\"\n [title]=\"activeDeviceOption.label\">\n <i [class]=\"activeDeviceOption.icon\"></i>\n </button>\n\n <p-popover #devicePopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let d of deviceOptions\"\n [class.is-active]=\"activeDevice === d.mode\"\n (click)=\"selectDevice(d.mode)\">\n <i [class]=\"d.icon\"></i>\n <span>{{ d.label }}</span>\n </button>\n </div>\n </p-popover>\n\n <!-- \u2500\u2500 Screen width \u2014 Desktop only \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeDevice === deviceModes.Desktop) {\n <button class=\"tool-btn stateful width-btn\"\n (click)=\"toggleWidthPopover($event)\"\n title=\"Screen width\">\n <span class=\"width-label\">{{ activeScreenWidth }}</span>\n </button>\n }\n\n <p-popover #widthPopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let w of screenWidthOptions\"\n [class.is-active]=\"activeScreenWidth === w.width\"\n (click)=\"selectScreenWidth(w.width)\">\n <span>{{ w.label }}</span>\n </button>\n </div>\n </p-popover>\n\n <!-- \u2500\u2500 Zoom (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful zoom-btn\"\n (click)=\"toggleZoomPopover($event)\"\n title=\"Zoom\">\n <span class=\"zoom-label\">{{ zoomLabel }}</span>\n </button>\n\n <p-popover #zoomPopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"zoom-popover\">\n <div class=\"zoom-slider-row\">\n <input class=\"zoom-slider\"\n type=\"range\"\n min=\"20\" max=\"100\" step=\"1\"\n [value]=\"sliderZoom\"\n (input)=\"onSliderChange($event)\">\n <span class=\"zoom-slider-value\">{{ zoomLabel }}</span>\n </div>\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let z of zoomOptions\"\n [class.is-active]=\"z.value === activeZoom\"\n (click)=\"selectZoom(z.value)\">\n <span>{{ z.label }}</span>\n </button>\n </div>\n </div>\n </p-popover>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Save \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn save-btn\"\n [class.is-saving]=\"isSaving\"\n [disabled]=\"isSaving\"\n title=\"Save\"\n (click)=\"save.emit()\">\n <i class=\"ph-thin ph-floppy-disk\"></i>\n </button>\n\n <!-- \u2500\u2500 Preview \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn preview-btn\"\n title=\"Preview\"\n (click)=\"preview.emit()\">\n <i class=\"ph-thin ph-eye\"></i>\n </button>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Publish / Unpublish \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn publish-btn\"\n [class.is-published]=\"isPublished\"\n [title]=\"isPublished ? 'Unpublish' : 'Publish'\"\n (click)=\"publishToggle.emit()\">\n <i [class]=\"isPublished ? 'ph-thin ph-cloud-slash' : 'ph-thin ph-cloud-arrow-up'\"></i>\n <span class=\"publish-label\">{{ isPublished ? 'Unpublish' : 'Publish' }}</span>\n </button>\n</div>\n", styles: [".builder-toolbar{display:flex;align-items:center;gap:.125rem}.divider{width:1px;height:1.25rem;background:var(--p-surface-200);margin:0 .375rem;flex-shrink:0}.tool-btn{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:6px;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;font-size:1rem;transition:background .15s ease-out,color .15s ease-out}.tool-btn:hover:not(:disabled){background:var(--p-surface-50);color:var(--p-text-color)}.tool-btn:disabled{opacity:.35;cursor:default}.tool-btn.stateful{position:relative}.tool-btn.stateful:after{content:\"\";position:absolute;inset-block-end:3px;width:4px;height:4px;border-radius:50%;background:var(--p-primary-color)}.zoom-btn{width:auto;border-radius:999px;padding:0 .5rem;min-width:2.5rem}.zoom-btn .zoom-label{font-size:.7rem;font-weight:500;letter-spacing:.02em}.width-btn{width:auto;border-radius:999px;padding:0 .5rem;min-width:2.8rem}.width-btn .width-label{font-size:.7rem;font-weight:500;letter-spacing:.02em}.save-btn{background:var(--p-primary-color);color:#fff}.save-btn:hover:not(:disabled){background:color-mix(in srgb,var(--p-primary-color) 85%,#000);color:#fff}.save-btn.is-saving{opacity:.7}.preview-btn{border:1px solid var(--p-surface-200)}.preview-btn:hover:not(:disabled){background:var(--p-surface-50);color:var(--p-text-color);border-color:var(--p-surface-100)}.publish-btn{width:auto;border-radius:999px;padding:0 .65rem;gap:.35rem;font-size:.75rem;font-weight:500;background:#22c55e;color:#fff;border:none}.publish-btn .publish-label{font-size:.72rem;letter-spacing:.02em}.publish-btn:hover:not(:disabled){background:color-mix(in srgb,#22c55e 85%,#000);color:#fff}.publish-btn.is-published{background:transparent;color:var(--p-text-muted-color);border:1px solid var(--p-surface-200)}.publish-btn.is-published:hover:not(:disabled){background:color-mix(in srgb,#ef4444 10%,transparent);color:#ef4444;border-color:color-mix(in srgb,#ef4444 40%,transparent)}::ng-deep .toolbar-popover .p-popover-content{padding:.25rem!important;background:var(--p-surface-0)!important;border:1px solid var(--p-surface-200)!important;border-radius:10px!important;box-shadow:0 4px 16px #0f172a1a!important}.popover-strip{display:flex;flex-direction:column;gap:2px;min-width:120px}.popover-option{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4rem .65rem;border:none;background:transparent;color:var(--p-text-color);cursor:pointer;font-size:.8rem;border-radius:6px;transition:background .12s ease-out,color .12s ease-out;white-space:nowrap}.popover-option i{font-size:1rem;color:var(--p-text-muted-color)}.popover-option:hover{background:var(--p-surface-50)}.popover-option.is-active{background:var(--p-primary-color);color:#fff}.popover-option.is-active i{color:#fff}::ng-deep .toolbar-popover .zoom-popover{display:flex;flex-direction:column;gap:4px;padding:.15rem 0}::ng-deep .toolbar-popover .zoom-slider-row{display:flex;align-items:center;gap:.5rem;padding:.3rem .65rem .1rem}::ng-deep .toolbar-popover .zoom-slider{flex:1;-webkit-appearance:none;appearance:none;height:3px;border-radius:999px;background:var(--p-surface-200);outline:none;cursor:pointer}::ng-deep .toolbar-popover .zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;border-radius:50%;background:var(--p-primary-color);cursor:pointer;transition:transform .12s ease-out}::ng-deep .toolbar-popover .zoom-slider::-webkit-slider-thumb:hover{transform:scale(1.2)}::ng-deep .toolbar-popover .zoom-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;border:none;background:var(--p-primary-color);cursor:pointer}::ng-deep .toolbar-popover .zoom-slider-value{font-size:.68rem;font-weight:500;color:var(--p-text-muted-color);min-width:2.4rem;text-align:right;letter-spacing:.02em}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: i2$1.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }] }); }
|
|
3324
3342
|
}
|
|
3325
3343
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ToolbarComponent, decorators: [{
|
|
3326
3344
|
type: Component,
|
|
3327
|
-
args: [{ selector: 'app-toolbar', standalone: false, template: "<div class=\"builder-toolbar\">\n <!-- \u2500\u2500 Mode switcher (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful\"\n (click)=\"toggleModePopover($event)\"\n [title]=\"activeModeLabel\">\n <i [class]=\"activeModeIcon\"></i>\n </button>\n\n <p-popover #modePopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Select\"\n (click)=\"selectMode(modes.Select)\">\n <i class=\"ph-thin ph-cursor\"></i>\n <span>Select</span>\n </button>\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Pan\"\n (click)=\"selectMode(modes.Pan)\">\n <i class=\"ph-thin ph-hand\"></i>\n <span>Pan</span>\n </button>\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Style\"\n (click)=\"selectMode(modes.Style)\">\n <i class=\"ph-thin ph-paint-brush-broad\"></i>\n <span>Style</span>\n </button>\n </div>\n </p-popover>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Undo / Redo (action) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn\" title=\"Undo\" [disabled]=\"!canUndo\" (click)=\"undo.emit()\">\n <i class=\"ph-thin ph-arrow-counter-clockwise\"></i>\n </button>\n <button class=\"tool-btn\" title=\"Redo\" [disabled]=\"!canRedo\" (click)=\"redo.emit()\">\n <i class=\"ph-thin ph-arrow-clockwise\"></i>\n </button>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Device switcher (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful\"\n (click)=\"toggleDevicePopover($event)\"\n [title]=\"activeDeviceOption.label\">\n <i [class]=\"activeDeviceOption.icon\"></i>\n </button>\n\n <p-popover #devicePopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let d of deviceOptions\"\n [class.is-active]=\"activeDevice === d.mode\"\n (click)=\"selectDevice(d.mode)\">\n <i [class]=\"d.icon\"></i>\n <span>{{ d.label }}</span>\n </button>\n </div>\n </p-popover>\n\n <!-- \u2500\u2500 Screen width \u2014 Desktop only \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeDevice === deviceModes.Desktop) {\n <button class=\"tool-btn stateful width-btn\"\n (click)=\"toggleWidthPopover($event)\"\n title=\"Screen width\">\n <span class=\"width-label\">{{ activeScreenWidth }}</span>\n </button>\n }\n\n <p-popover #widthPopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let w of screenWidthOptions\"\n [class.is-active]=\"activeScreenWidth === w.width\"\n (click)=\"selectScreenWidth(w.width)\">\n <span>{{ w.label }}</span>\n </button>\n </div>\n </p-popover>\n\n <!-- \u2500\u2500 Zoom (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful zoom-btn\"\n (click)=\"toggleZoomPopover($event)\"\n title=\"Zoom\">\n <span class=\"zoom-label\">{{ zoomLabel }}</span>\n </button>\n\n <p-popover #zoomPopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"zoom-popover\">\n <div class=\"zoom-slider-row\">\n <input class=\"zoom-slider\"\n type=\"range\"\n min=\"20\" max=\"100\" step=\"1\"\n [value]=\"sliderZoom\"\n (input)=\"onSliderChange($event)\">\n <span class=\"zoom-slider-value\">{{ zoomLabel }}</span>\n </div>\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let z of zoomOptions\"\n [class.is-active]=\"z.value === activeZoom\"\n (click)=\"selectZoom(z.value)\">\n <span>{{ z.label }}</span>\n </button>\n </div>\n </div>\n </p-popover>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Save \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn save-btn\"\n [class.is-saving]=\"isSaving\"\n [disabled]=\"isSaving\"\n title=\"Save\"\n (click)=\"save.emit()\">\n <i class=\"ph-thin ph-floppy-disk\"></i>\n </button>\n\n <!-- \u2500\u2500 Preview \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn preview-btn\"\n title=\"Preview\"\n (click)=\"preview.emit()\">\n <i class=\"ph-thin ph-eye\"></i>\n </button>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Publish / Unpublish \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn publish-btn\"\n [class.is-published]=\"isPublished\"\n [title]=\"isPublished ? 'Unpublish' : 'Publish'\"\n (click)=\"publishToggle.emit()\">\n <i [class]=\"isPublished ? 'ph-thin ph-cloud-slash' : 'ph-thin ph-cloud-arrow-up'\"></i>\n <span class=\"publish-label\">{{ isPublished ? 'Unpublish' : 'Publish' }}</span>\n </button>\n</div>\n", styles: [".builder-toolbar{display:flex;align-items:center;gap:.25rem;background:#0f172ae0;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid rgba(148,163,184,.2);border-radius:999px;padding:.3rem .5rem;box-shadow:0 8px 24px #0f172a47}.divider{width:1px;height:1.25rem;background:#94a3b833;margin:0 .2rem;flex-shrink:0}.tool-btn{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:50%;border:none;background:transparent;color:#ffffffbf;cursor:pointer;font-size:1.1rem;transition:background .15s,color .15s}.tool-btn:hover:not(:disabled){background:#ffffff1f;color:#fff}.tool-btn:disabled{opacity:.3;cursor:default}.tool-btn.stateful{position:relative}.tool-btn.stateful:after{content:\"\";position:absolute;inset-block-end:2px;width:4px;height:4px;border-radius:50%;background:#cb9090;opacity:.6}.zoom-btn{width:auto;border-radius:999px;padding:0 .5rem;min-width:2.5rem}.zoom-btn .zoom-label{font-size:.7rem;font-weight:500;letter-spacing:.02em}::ng-deep .toolbar-popover .zoom-popover{display:flex;flex-direction:column;gap:4px;padding:.15rem 0}::ng-deep .toolbar-popover .zoom-slider-row{display:flex;align-items:center;gap:.5rem;padding:.3rem .65rem .1rem}::ng-deep .toolbar-popover .zoom-slider{flex:1;-webkit-appearance:none;appearance:none;height:3px;border-radius:999px;background:#fff3;outline:none;cursor:pointer}::ng-deep .toolbar-popover .zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;border-radius:50%;background:#cb9090;cursor:pointer;transition:transform .12s}::ng-deep .toolbar-popover .zoom-slider::-webkit-slider-thumb:hover{transform:scale(1.2)}::ng-deep .toolbar-popover .zoom-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;border:none;background:#cb9090;cursor:pointer}::ng-deep .toolbar-popover .zoom-slider-value{font-size:.68rem;font-weight:500;color:#ffffffbf;min-width:2.4rem;text-align:right;letter-spacing:.02em}.width-btn{width:auto;border-radius:999px;padding:0 .5rem;min-width:2.8rem}.width-btn .width-label{font-size:.7rem;font-weight:500;letter-spacing:.02em}.save-btn{background:#cb9090;color:#fff}.save-btn:hover:not(:disabled){background:color-mix(in srgb,#cb9090 85%,#fff);color:#fff}.save-btn.is-saving{opacity:.7}.publish-btn{width:auto;border-radius:999px;padding:0 .65rem;gap:.35rem;font-size:.75rem;font-weight:500;background:#22c55e;color:#fff;border:none}.publish-btn .publish-label{font-size:.72rem;letter-spacing:.02em}.publish-btn:hover:not(:disabled){background:color-mix(in srgb,#22c55e 85%,#fff);color:#fff}.publish-btn.is-published{background:transparent;color:#94a3b8;border:1px solid rgba(255,255,255,.15)}.publish-btn.is-published:hover:not(:disabled){background:#ef444426;color:#f87171;border-color:#ef44444d}.preview-btn{color:#ffffffbf;border:1px solid rgba(255,255,255,.15)}.preview-btn:hover:not(:disabled){background:#ffffff1a;color:#fff;border-color:#ffffff40}::ng-deep .toolbar-popover .p-popover-content{padding:.3rem!important;background:#0f172ae0!important;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid rgba(148,163,184,.2)!important;border-radius:12px!important;box-shadow:0 12px 32px #0f172a59!important}.popover-strip{display:flex;flex-direction:column;gap:2px;min-width:120px}.popover-option{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4rem .65rem;border:none;background:transparent;color:#ffffffbf;cursor:pointer;font-size:.8rem;border-radius:8px;transition:background .12s,color .12s;white-space:nowrap}.popover-option i{font-size:1rem}.popover-option:hover{background:#ffffff1a;color:#fff}.popover-option.is-active{background:#cb9090;color:#fff}\n"] }]
|
|
3345
|
+
args: [{ selector: 'app-toolbar', standalone: false, template: "<div class=\"builder-toolbar\">\n <!-- \u2500\u2500 Mode switcher (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful\"\n (click)=\"toggleModePopover($event)\"\n [title]=\"activeModeLabel\">\n <i [class]=\"activeModeIcon\"></i>\n </button>\n\n <p-popover #modePopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Select\"\n (click)=\"selectMode(modes.Select)\">\n <i class=\"ph-thin ph-cursor\"></i>\n <span>Select</span>\n </button>\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Pan\"\n (click)=\"selectMode(modes.Pan)\">\n <i class=\"ph-thin ph-hand\"></i>\n <span>Pan</span>\n </button>\n <button class=\"popover-option\"\n [class.is-active]=\"activeMode === modes.Style\"\n (click)=\"selectMode(modes.Style)\">\n <i class=\"ph-thin ph-paint-brush-broad\"></i>\n <span>Style</span>\n </button>\n </div>\n </p-popover>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Undo / Redo (action) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn\" title=\"Undo\" [disabled]=\"!canUndo\" (click)=\"undo.emit()\">\n <i class=\"ph-thin ph-arrow-counter-clockwise\"></i>\n </button>\n <button class=\"tool-btn\" title=\"Redo\" [disabled]=\"!canRedo\" (click)=\"redo.emit()\">\n <i class=\"ph-thin ph-arrow-clockwise\"></i>\n </button>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Device switcher (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful\"\n (click)=\"toggleDevicePopover($event)\"\n [title]=\"activeDeviceOption.label\">\n <i [class]=\"activeDeviceOption.icon\"></i>\n </button>\n\n <p-popover #devicePopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let d of deviceOptions\"\n [class.is-active]=\"activeDevice === d.mode\"\n (click)=\"selectDevice(d.mode)\">\n <i [class]=\"d.icon\"></i>\n <span>{{ d.label }}</span>\n </button>\n </div>\n </p-popover>\n\n <!-- \u2500\u2500 Screen width \u2014 Desktop only \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (activeDevice === deviceModes.Desktop) {\n <button class=\"tool-btn stateful width-btn\"\n (click)=\"toggleWidthPopover($event)\"\n title=\"Screen width\">\n <span class=\"width-label\">{{ activeScreenWidth }}</span>\n </button>\n }\n\n <p-popover #widthPopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let w of screenWidthOptions\"\n [class.is-active]=\"activeScreenWidth === w.width\"\n (click)=\"selectScreenWidth(w.width)\">\n <span>{{ w.label }}</span>\n </button>\n </div>\n </p-popover>\n\n <!-- \u2500\u2500 Zoom (stateful) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn stateful zoom-btn\"\n (click)=\"toggleZoomPopover($event)\"\n title=\"Zoom\">\n <span class=\"zoom-label\">{{ zoomLabel }}</span>\n </button>\n\n <p-popover #zoomPopover [appendTo]=\"'body'\" styleClass=\"toolbar-popover\">\n <div class=\"zoom-popover\">\n <div class=\"zoom-slider-row\">\n <input class=\"zoom-slider\"\n type=\"range\"\n min=\"20\" max=\"100\" step=\"1\"\n [value]=\"sliderZoom\"\n (input)=\"onSliderChange($event)\">\n <span class=\"zoom-slider-value\">{{ zoomLabel }}</span>\n </div>\n <div class=\"popover-strip\">\n <button class=\"popover-option\"\n *ngFor=\"let z of zoomOptions\"\n [class.is-active]=\"z.value === activeZoom\"\n (click)=\"selectZoom(z.value)\">\n <span>{{ z.label }}</span>\n </button>\n </div>\n </div>\n </p-popover>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Save \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn save-btn\"\n [class.is-saving]=\"isSaving\"\n [disabled]=\"isSaving\"\n title=\"Save\"\n (click)=\"save.emit()\">\n <i class=\"ph-thin ph-floppy-disk\"></i>\n </button>\n\n <!-- \u2500\u2500 Preview \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn preview-btn\"\n title=\"Preview\"\n (click)=\"preview.emit()\">\n <i class=\"ph-thin ph-eye\"></i>\n </button>\n\n <span class=\"divider\"></span>\n\n <!-- \u2500\u2500 Publish / Unpublish \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button class=\"tool-btn publish-btn\"\n [class.is-published]=\"isPublished\"\n [title]=\"isPublished ? 'Unpublish' : 'Publish'\"\n (click)=\"publishToggle.emit()\">\n <i [class]=\"isPublished ? 'ph-thin ph-cloud-slash' : 'ph-thin ph-cloud-arrow-up'\"></i>\n <span class=\"publish-label\">{{ isPublished ? 'Unpublish' : 'Publish' }}</span>\n </button>\n</div>\n", styles: [".builder-toolbar{display:flex;align-items:center;gap:.125rem}.divider{width:1px;height:1.25rem;background:var(--p-surface-200);margin:0 .375rem;flex-shrink:0}.tool-btn{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:6px;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;font-size:1rem;transition:background .15s ease-out,color .15s ease-out}.tool-btn:hover:not(:disabled){background:var(--p-surface-50);color:var(--p-text-color)}.tool-btn:disabled{opacity:.35;cursor:default}.tool-btn.stateful{position:relative}.tool-btn.stateful:after{content:\"\";position:absolute;inset-block-end:3px;width:4px;height:4px;border-radius:50%;background:var(--p-primary-color)}.zoom-btn{width:auto;border-radius:999px;padding:0 .5rem;min-width:2.5rem}.zoom-btn .zoom-label{font-size:.7rem;font-weight:500;letter-spacing:.02em}.width-btn{width:auto;border-radius:999px;padding:0 .5rem;min-width:2.8rem}.width-btn .width-label{font-size:.7rem;font-weight:500;letter-spacing:.02em}.save-btn{background:var(--p-primary-color);color:#fff}.save-btn:hover:not(:disabled){background:color-mix(in srgb,var(--p-primary-color) 85%,#000);color:#fff}.save-btn.is-saving{opacity:.7}.preview-btn{border:1px solid var(--p-surface-200)}.preview-btn:hover:not(:disabled){background:var(--p-surface-50);color:var(--p-text-color);border-color:var(--p-surface-100)}.publish-btn{width:auto;border-radius:999px;padding:0 .65rem;gap:.35rem;font-size:.75rem;font-weight:500;background:#22c55e;color:#fff;border:none}.publish-btn .publish-label{font-size:.72rem;letter-spacing:.02em}.publish-btn:hover:not(:disabled){background:color-mix(in srgb,#22c55e 85%,#000);color:#fff}.publish-btn.is-published{background:transparent;color:var(--p-text-muted-color);border:1px solid var(--p-surface-200)}.publish-btn.is-published:hover:not(:disabled){background:color-mix(in srgb,#ef4444 10%,transparent);color:#ef4444;border-color:color-mix(in srgb,#ef4444 40%,transparent)}::ng-deep .toolbar-popover .p-popover-content{padding:.25rem!important;background:var(--p-surface-0)!important;border:1px solid var(--p-surface-200)!important;border-radius:10px!important;box-shadow:0 4px 16px #0f172a1a!important}.popover-strip{display:flex;flex-direction:column;gap:2px;min-width:120px}.popover-option{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4rem .65rem;border:none;background:transparent;color:var(--p-text-color);cursor:pointer;font-size:.8rem;border-radius:6px;transition:background .12s ease-out,color .12s ease-out;white-space:nowrap}.popover-option i{font-size:1rem;color:var(--p-text-muted-color)}.popover-option:hover{background:var(--p-surface-50)}.popover-option.is-active{background:var(--p-primary-color);color:#fff}.popover-option.is-active i{color:#fff}::ng-deep .toolbar-popover .zoom-popover{display:flex;flex-direction:column;gap:4px;padding:.15rem 0}::ng-deep .toolbar-popover .zoom-slider-row{display:flex;align-items:center;gap:.5rem;padding:.3rem .65rem .1rem}::ng-deep .toolbar-popover .zoom-slider{flex:1;-webkit-appearance:none;appearance:none;height:3px;border-radius:999px;background:var(--p-surface-200);outline:none;cursor:pointer}::ng-deep .toolbar-popover .zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;border-radius:50%;background:var(--p-primary-color);cursor:pointer;transition:transform .12s ease-out}::ng-deep .toolbar-popover .zoom-slider::-webkit-slider-thumb:hover{transform:scale(1.2)}::ng-deep .toolbar-popover .zoom-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;border:none;background:var(--p-primary-color);cursor:pointer}::ng-deep .toolbar-popover .zoom-slider-value{font-size:.68rem;font-weight:500;color:var(--p-text-muted-color);min-width:2.4rem;text-align:right;letter-spacing:.02em}\n"] }]
|
|
3328
3346
|
}], propDecorators: { activeMode: [{
|
|
3329
3347
|
type: Input
|
|
3330
3348
|
}], activeDevice: [{
|
|
@@ -4337,11 +4355,11 @@ class BuilderComponent {
|
|
|
4337
4355
|
}
|
|
4338
4356
|
}
|
|
4339
4357
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: BuilderComponent, deps: [{ token: ComponentRegistryService }, { token: OverlayService }, { token: PanelEventService }, { token: DragEngineService }, { token: DataSourceRegistryService }, { token: ViewportService }, { token: InteractionEngineService }, { token: InteractionPresetRegistryService }, { token: BuilderClipboardService }, { token: SymbolRegistryService }, { token: i0.ChangeDetectorRef }, { token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4340
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: BuilderComponent, isStandalone: false, selector: "app-builder", inputs: { layout: "layout", dataSources: "dataSources", orgId: "orgId", routeParams: "routeParams", pageName: "pageName", isSaving: "isSaving", pageStatus: "pageStatus", pageSettings: "pageSettings", globalElements: "globalElements", canUndo: "canUndo", canRedo: "canRedo" }, outputs: { save: "save", undo: "undo", redo: "redo", back: "back", preview: "preview", publishToggle: "publishToggle", pageSettingsChange: "pageSettingsChange", applyAnimationToAll: "applyAnimationToAll", saveBlock: "saveBlock", globalElementsChange: "globalElementsChange" }, viewQueries: [{ propertyName: "previewCanvas", first: true, predicate: ["previewCanvas"], descendants: true }, { propertyName: "previewScrollRef", first: true, predicate: ["previewScroll"], descendants: true }, { propertyName: "lenisContentRef", first: true, predicate: ["lenisContent"], descendants: true }, { propertyName: "globalOverlayRef", first: true, predicate: ["globalOverlay"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"builder\">\n <header class=\"builder-header\">\n <div class=\"sidebar-switcher\">\n <button (click)=\"activeSidebar = 'components'\" [class.active]=\"activeSidebar === 'components'\" title=\"Components\">\n <i class=\"ph-thin ph-cube\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tree'\" [class.active]=\"activeSidebar === 'tree'\" title=\"Layers\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tokens'\" [class.active]=\"activeSidebar === 'tokens'\" title=\"Design Tokens\">\n <i class=\"ph-thin ph-circles-three\"></i>\n </button>\n </div>\n <div class=\"panel-switcher\">\n <button (click)=\"selectPanel(panelTypes.BINDINGS)\" [class.active]=\"activePanel === panelTypes.BINDINGS\">\n <i class=\"ph-thin ph-plugs-connected\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.STYLES)\" [class.active]=\"activePanel === panelTypes.STYLES\">\n <i class=\"ph-thin ph-paint-brush\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.PAGE)\" [class.active]=\"activePanel === panelTypes.PAGE\">\n <i class=\"ph-thin ph-file\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.INTERACTIONS)\" [class.active]=\"activePanel === panelTypes.INTERACTIONS\">\n <i class=\"ph-thin ph-lightning\"></i>\n </button>\n </div>\n </header>\n\n <div class=\"builder-main\" [class.panel-open]=\"isPanelOpen\">\n <aside class=\"sidebar\">\n <div *ngIf=\"activeSidebar === 'components'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderComponents]\"></ng-content>\n </div>\n <div *ngIf=\"activeSidebar === 'tokens'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderTokens]\"></ng-content>\n </div>\n <app-layer-tree *ngIf=\"activeSidebar === 'tree'\"\n [layout]=\"layout\"\n [globalElements]=\"globalElements\"\n (nodeSelect)=\"onTreeNodeSelect($event)\"\n (nodeAction)=\"onTreeNodeAction($event)\"\n (nodeMove)=\"onTreeNodeMove()\"></app-layer-tree>\n </aside>\n\n <main #previewCanvas class=\"preview\"\n [class.grab-mode]=\"activeMode === builderModes.Pan\"\n [class.grabbing]=\"isPanning\"\n (click)=\"handleCanvasClick()\"\n (mousedown)=\"onBoardMouseDown($event)\"\n (mousemove)=\"onBoardMouseMove($event)\"\n (mouseup)=\"onBoardMouseUp()\"\n (mouseleave)=\"onBoardMouseUp()\">\n <app-overlay (toolbarAction)=\"onToolbarAction($event)\"></app-overlay>\n\n <!-- Custom Lenis scrollbar \u2014 lives in .preview (not .preview-scroll) so it\n stays in the right gutter and never overlaps the canvas at large widths.\n No translateY counteract needed since it isn't inside the Lenis wrapper. -->\n <div class=\"canvas-scrollbar\"\n [class.visible]=\"isScrollbarVisible\">\n <div class=\"canvas-scrollbar-thumb\"\n [style.height.px]=\"scrollThumbHeight\"\n [style.top.px]=\"scrollThumbTop\">\n </div>\n </div>\n\n <!-- Global elements overlay \u2014 outside Lenis so position:fixed children\n stay pinned to the canvas viewport rather than scrolling with content.\n Shares viewportTransform so elements scale correctly at any zoom level.\n Height matches viewportHeight so position:fixed + inset:0 fills the\n simulated viewport correctly (CSS calc(100%) would give the wrong\n containing-block height and break full-screen fixed overlays). -->\n <div *ngIf=\"globalElements.length\"\n #globalOverlay\n class=\"global-elements-overlay\"\n [style.width.px]=\"viewportWidth\"\n [style.height.px]=\"canvasFrameHeight || viewportHeight\"\n [style.transform]=\"viewportTransform\">\n <ng-container *ngFor=\"let item of globalElementsBefore\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </ng-container>\n <ng-container *ngFor=\"let item of globalElementsAfter\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </ng-container>\n </div>\n\n <div class=\"preview-scroll\"\n #previewScroll\n [style.width.px]=\"viewportScaledWidth\"\n [class.pan-mode]=\"activeMode === builderModes.Pan\">\n\n <!-- Lenis content proxy: height = realContentHeight \u00D7 scale.\n Lenis scrolls against this value so scroll range is always in\n visual px \u2014 correct at any zoom level. -->\n <div class=\"preview-lenis-content\" #lenisContent\n [style.height.px]=\"scaledContentHeight\">\n\n <iox-page-component\n [style.width.px]=\"viewportWidth\"\n [style.height.px]=\"canvasFrameHeight || viewportHeight\"\n [style.transform]=\"viewportTransform\">\n\n <div class=\"canvas-viewport\"\n id=\"canvas-preview\"\n [style.min-height.px]=\"canvasMinHeight\">\n\n <div *ngFor=\"let item of layout\"\n [ngClass]=\"['preview-node', 'iox-outer-' + (item.styleId ?? item.id)]\"\n [class.is-selected]=\"selectedItem === item\"\n ioxDraggable\n [ioxDragData]=\"item\"\n [ioxDragSourceId]=\"'canvas-preview'\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </div>\n </div>\n </iox-page-component>\n </div>\n </div>\n </main>\n\n <app-toolbar\n [activeMode]=\"activeMode\"\n [activeDevice]=\"activeDevice\"\n [activeZoom]=\"activeZoom\"\n [activeScreenWidth]=\"activeScreenWidth\"\n [isSaving]=\"isSaving\"\n [pageStatus]=\"pageStatus\"\n [canUndo]=\"canUndo\"\n [canRedo]=\"canRedo\"\n (modeChange)=\"handleToolbarModeChange($event)\"\n (deviceChange)=\"handleToolbarDeviceChange($event)\"\n (zoomChange)=\"handleToolbarZoomChange($event)\"\n (screenWidthChange)=\"handleToolbarScreenWidthChange($event)\"\n (save)=\"save.emit()\"\n (preview)=\"preview.emit()\"\n (publishToggle)=\"publishToggle.emit()\"\n (undo)=\"undo.emit()\"\n (redo)=\"redo.emit()\">\n </app-toolbar>\n\n <ng-content select=\"[builderPanel]\"></ng-content>\n </div>\n\n <!-- Save as Reusable Block dialog -->\n @if (showSaveBlockDialog) {\n <div class=\"save-block-backdrop\" (click)=\"cancelSaveBlock()\"></div>\n <div class=\"save-block-dialog\" (click)=\"$event.stopPropagation()\">\n <h4 class=\"save-block-title\">Save as Reusable Block</h4>\n <input class=\"save-block-input\"\n type=\"text\"\n placeholder=\"Block name...\"\n [(ngModel)]=\"saveBlockName\"\n (keydown.enter)=\"confirmSaveBlock()\"\n (keydown.escape)=\"cancelSaveBlock()\"\n autofocus>\n <div class=\"save-block-actions\">\n <p-button size=\"small\" severity=\"secondary\" label=\"Cancel\" (click)=\"cancelSaveBlock()\"></p-button>\n <p-button size=\"small\" label=\"Save\" [disabled]=\"!saveBlockName.trim()\" (click)=\"confirmSaveBlock()\"></p-button>\n </div>\n </div>\n }\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;flex:1;min-height:0;overflow:hidden}.builder{display:flex;flex-direction:column;direction:ltr;width:100%;height:100%;min-height:0;border-radius:0;overflow:hidden;position:relative}.builder .builder-header{background:#fff;padding:.5rem 1rem;border-bottom:1px solid var(--p-surface-200);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.builder .builder-header .header-back{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:transparent;border-radius:6px;cursor:pointer;color:var(--p-text-muted-color);font-size:16px;transition:background .15s;margin-inline-end:.25rem}.builder .builder-header .header-back:hover{background:var(--p-surface-100, #f1f5f9);color:var(--p-text-color)}.builder .builder-header .header-page-name{flex:1;text-align:center;font-size:.8rem;font-weight:500;color:var(--p-text-muted-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-inline:1rem}.builder .sidebar-switcher,.builder .panel-switcher{display:flex;gap:.5rem}.builder .sidebar-switcher p-button ::ng-deep .p-button,.builder .panel-switcher p-button ::ng-deep .p-button{color:#000;background:transparent;border:1px solid transparent;transition:border-color .2s}.builder .sidebar-switcher p-button ::ng-deep .p-button:hover,.builder .panel-switcher p-button ::ng-deep .p-button:hover{border-color:#cb9090}.builder .sidebar-switcher p-button.active ::ng-deep .p-button,.builder .panel-switcher p-button.active ::ng-deep .p-button{background:#cb9090;color:#fff;border-color:#cb9090}.builder .builder-main{display:flex;flex:1 1 0;min-height:0;position:relative;overflow:hidden}.builder .builder-main app-toolbar{position:fixed;bottom:1.25rem;left:50%;transform:translate(-50%);z-index:1000;pointer-events:auto}.builder .sidebar{width:220px;min-width:220px;background:var(--p-surface-0);border-right:1px solid var(--p-surface-200);overflow-y:auto;overflow-x:hidden;flex-shrink:0}.builder .preview{flex:1 1 0;min-width:0;min-height:0;position:relative;overflow:hidden}.builder .preview.grab-mode{cursor:grab}.builder .preview.grab-mode iox-page-component{pointer-events:none}.builder .preview.grabbing{cursor:grabbing}.builder .canvas-scrollbar{position:absolute;right:4px;top:20px;bottom:20px;width:6px;z-index:20;opacity:0;transition:opacity .2s ease;pointer-events:none}.builder .canvas-scrollbar.visible{opacity:1}.builder .canvas-scrollbar-thumb{position:absolute;left:0;width:100%;background:#00000059;border-radius:3px;transition:top .05s linear}.builder .preview-scroll{position:absolute;inset:20px auto;inset-inline-start:50%;transform:translate(-50%);max-width:calc(100% - 40px);height:calc(100% - 40px);overflow:hidden}.builder .preview-lenis-content{position:relative;width:100%;overflow:hidden;contain:paint}.builder iox-page-component{position:absolute!important;top:0;bottom:auto!important;right:auto!important;left:50%;transform-origin:top center;overflow:visible!important;box-shadow:0 1px 4px #00000014,0 4px 16px #0000000a;transition:width .25s ease,transform .25s ease}.builder iox-page-component ::ng-deep .iox-page-wrapper{height:auto!important;overflow:visible!important}.builder #canvas-preview{background:#fff}.builder .global-elements-overlay{position:absolute;top:20px;left:50%;transform-origin:top center;pointer-events:none;z-index:9;overflow:hidden}.builder .global-elements-overlay ::ng-deep>*{pointer-events:auto}.builder .preview-node.is-selected{cursor:grab}.builder .empty-state{min-height:240px;border:1px dashed var(--p-surface-200);border-radius:10px;display:flex;align-items:center;justify-content:center;color:var(--p-text-muted-color);text-align:center;padding:1rem}.save-block-backdrop{position:fixed;inset:0;z-index:1000;background:#00000040}.save-block-dialog{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1001;background:#fff;border-radius:8px;box-shadow:0 8px 32px #0000002e;padding:24px;width:320px}.save-block-dialog .save-block-title{margin:0 0 16px;font-size:14px;font-weight:600;color:var(--p-text-color, #333)}.save-block-dialog .save-block-input{width:100%;padding:8px 10px;border:1px solid var(--p-surface-300, #d1d5db);border-radius:6px;font-size:13px;outline:none;box-sizing:border-box;margin-bottom:16px}.save-block-dialog .save-block-input:focus{border-color:#cb9090;box-shadow:0 0 0 2px #cb909033}.save-block-dialog .save-block-actions{display:flex;justify-content:flex-end;gap:8px}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i13.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i14.IoxPageComponent, selector: "iox-page-component", inputs: ["class"], outputs: ["scrollEvent"] }, { kind: "directive", type: RenderDirective, selector: "[ioxRender]", inputs: ["ioxRender"], outputs: ["onClick", "onMouseEnter", "onMouseLeave"] }, { kind: "directive", type: IoxDraggableDirective, selector: "[ioxDraggable]", inputs: ["ioxDragData", "ioxDragSourceId"] }, { kind: "component", type: OverlayComponent, selector: "app-overlay", outputs: ["toolbarAction"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["activeMode", "activeDevice", "activeZoom", "activeScreenWidth", "canUndo", "canRedo", "isSaving", "pageStatus"], outputs: ["modeChange", "deviceChange", "zoomChange", "screenWidthChange", "undo", "redo", "save", "preview", "publishToggle"] }, { kind: "component", type: LayerTreeComponent, selector: "app-layer-tree", inputs: ["layout", "globalElements"], outputs: ["nodeSelect", "nodeAction", "nodeMove"] }] }); }
|
|
4358
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: BuilderComponent, isStandalone: false, selector: "app-builder", inputs: { layout: "layout", dataSources: "dataSources", orgId: "orgId", routeParams: "routeParams", pageName: "pageName", isSaving: "isSaving", pageStatus: "pageStatus", pageSettings: "pageSettings", globalElements: "globalElements", canUndo: "canUndo", canRedo: "canRedo" }, outputs: { save: "save", undo: "undo", redo: "redo", back: "back", preview: "preview", publishToggle: "publishToggle", pageSettingsChange: "pageSettingsChange", applyAnimationToAll: "applyAnimationToAll", saveBlock: "saveBlock", globalElementsChange: "globalElementsChange" }, viewQueries: [{ propertyName: "previewCanvas", first: true, predicate: ["previewCanvas"], descendants: true }, { propertyName: "previewScrollRef", first: true, predicate: ["previewScroll"], descendants: true }, { propertyName: "lenisContentRef", first: true, predicate: ["lenisContent"], descendants: true }, { propertyName: "globalOverlayRef", first: true, predicate: ["globalOverlay"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"builder\">\n <header class=\"builder-header\">\n <div class=\"sidebar-switcher\">\n <button (click)=\"activeSidebar = 'components'\" [class.active]=\"activeSidebar === 'components'\" title=\"Components\">\n <i class=\"ph-thin ph-cube\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tree'\" [class.active]=\"activeSidebar === 'tree'\" title=\"Layers\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tokens'\" [class.active]=\"activeSidebar === 'tokens'\" title=\"Design Tokens\">\n <i class=\"ph-thin ph-circles-three\"></i>\n </button>\n </div>\n\n <app-toolbar\n [activeMode]=\"activeMode\"\n [activeDevice]=\"activeDevice\"\n [activeZoom]=\"activeZoom\"\n [activeScreenWidth]=\"activeScreenWidth\"\n [isSaving]=\"isSaving\"\n [pageStatus]=\"pageStatus\"\n [canUndo]=\"canUndo\"\n [canRedo]=\"canRedo\"\n (modeChange)=\"handleToolbarModeChange($event)\"\n (deviceChange)=\"handleToolbarDeviceChange($event)\"\n (zoomChange)=\"handleToolbarZoomChange($event)\"\n (screenWidthChange)=\"handleToolbarScreenWidthChange($event)\"\n (save)=\"save.emit()\"\n (preview)=\"preview.emit()\"\n (publishToggle)=\"publishToggle.emit()\"\n (undo)=\"undo.emit()\"\n (redo)=\"redo.emit()\">\n </app-toolbar>\n\n <div class=\"panel-switcher\">\n <button (click)=\"selectPanel(panelTypes.BINDINGS)\" [class.active]=\"activePanel === panelTypes.BINDINGS\" title=\"Bindings\">\n <i class=\"ph-thin ph-plugs-connected\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.STYLES)\" [class.active]=\"activePanel === panelTypes.STYLES\" title=\"Styles\">\n <i class=\"ph-thin ph-paint-brush\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.PAGE)\" [class.active]=\"activePanel === panelTypes.PAGE\" title=\"Page settings\">\n <i class=\"ph-thin ph-file\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.INTERACTIONS)\" [class.active]=\"activePanel === panelTypes.INTERACTIONS\" title=\"Interactions\">\n <i class=\"ph-thin ph-lightning\"></i>\n </button>\n </div>\n </header>\n\n <div class=\"builder-main\" [class.panel-open]=\"isPanelOpen\">\n <aside class=\"sidebar\">\n <div *ngIf=\"activeSidebar === 'components'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderComponents]\"></ng-content>\n </div>\n <div *ngIf=\"activeSidebar === 'tokens'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderTokens]\"></ng-content>\n </div>\n <app-layer-tree *ngIf=\"activeSidebar === 'tree'\"\n [layout]=\"layout\"\n [globalElements]=\"globalElements\"\n (nodeSelect)=\"onTreeNodeSelect($event)\"\n (nodeAction)=\"onTreeNodeAction($event)\"\n (nodeMove)=\"onTreeNodeMove()\"></app-layer-tree>\n </aside>\n\n <main #previewCanvas class=\"preview\"\n [class.grab-mode]=\"activeMode === builderModes.Pan\"\n [class.grabbing]=\"isPanning\"\n (click)=\"handleCanvasClick()\"\n (mousedown)=\"onBoardMouseDown($event)\"\n (mousemove)=\"onBoardMouseMove($event)\"\n (mouseup)=\"onBoardMouseUp()\"\n (mouseleave)=\"onBoardMouseUp()\">\n <app-overlay (toolbarAction)=\"onToolbarAction($event)\"></app-overlay>\n\n <!-- Custom Lenis scrollbar \u2014 lives in .preview (not .preview-scroll) so it\n stays in the right gutter and never overlaps the canvas at large widths.\n No translateY counteract needed since it isn't inside the Lenis wrapper. -->\n <div class=\"canvas-scrollbar\"\n [class.visible]=\"isScrollbarVisible\">\n <div class=\"canvas-scrollbar-thumb\"\n [style.height.px]=\"scrollThumbHeight\"\n [style.top.px]=\"scrollThumbTop\">\n </div>\n </div>\n\n <!-- Global elements overlay \u2014 outside Lenis so position:fixed children\n stay pinned to the canvas viewport rather than scrolling with content.\n Shares viewportTransform so elements scale correctly at any zoom level.\n Height matches viewportHeight so position:fixed + inset:0 fills the\n simulated viewport correctly (CSS calc(100%) would give the wrong\n containing-block height and break full-screen fixed overlays). -->\n <div *ngIf=\"globalElements.length\"\n #globalOverlay\n class=\"global-elements-overlay\"\n [style.width.px]=\"viewportWidth\"\n [style.height.px]=\"canvasFrameHeight || viewportHeight\"\n [style.transform]=\"viewportTransform\">\n <ng-container *ngFor=\"let item of globalElementsBefore\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </ng-container>\n <ng-container *ngFor=\"let item of globalElementsAfter\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </ng-container>\n </div>\n\n <div class=\"preview-scroll\"\n #previewScroll\n [style.width.px]=\"viewportScaledWidth\"\n [class.pan-mode]=\"activeMode === builderModes.Pan\">\n\n <!-- Lenis content proxy: height = realContentHeight \u00D7 scale.\n Lenis scrolls against this value so scroll range is always in\n visual px \u2014 correct at any zoom level. -->\n <div class=\"preview-lenis-content\" #lenisContent\n [style.height.px]=\"scaledContentHeight\">\n\n <iox-page-component\n [style.width.px]=\"viewportWidth\"\n [style.height.px]=\"canvasFrameHeight || viewportHeight\"\n [style.transform]=\"viewportTransform\">\n\n <div class=\"canvas-viewport\"\n id=\"canvas-preview\"\n [style.min-height.px]=\"canvasMinHeight\">\n\n <div *ngFor=\"let item of layout\"\n [ngClass]=\"['preview-node', 'iox-outer-' + (item.styleId ?? item.id)]\"\n [class.is-selected]=\"selectedItem === item\"\n ioxDraggable\n [ioxDragData]=\"item\"\n [ioxDragSourceId]=\"'canvas-preview'\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </div>\n </div>\n </iox-page-component>\n </div>\n </div>\n </main>\n\n <ng-content select=\"[builderPanel]\"></ng-content>\n </div>\n\n <!-- Save as Reusable Block dialog -->\n @if (showSaveBlockDialog) {\n <div class=\"save-block-backdrop\" (click)=\"cancelSaveBlock()\"></div>\n <div class=\"save-block-dialog\" (click)=\"$event.stopPropagation()\">\n <h4 class=\"save-block-title\">Save as Reusable Block</h4>\n <input class=\"save-block-input\"\n type=\"text\"\n placeholder=\"Block name...\"\n [(ngModel)]=\"saveBlockName\"\n (keydown.enter)=\"confirmSaveBlock()\"\n (keydown.escape)=\"cancelSaveBlock()\"\n autofocus>\n <div class=\"save-block-actions\">\n <p-button size=\"small\" severity=\"secondary\" label=\"Cancel\" (click)=\"cancelSaveBlock()\"></p-button>\n <p-button size=\"small\" label=\"Save\" [disabled]=\"!saveBlockName.trim()\" (click)=\"confirmSaveBlock()\"></p-button>\n </div>\n </div>\n }\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;flex:1;min-height:0;overflow:hidden}.builder{display:flex;flex-direction:column;direction:ltr;width:100%;height:100%;min-height:0;border-radius:0;overflow:hidden;position:relative}.builder .builder-header{background:var(--p-surface-0);padding:.375rem .75rem;border-bottom:1px solid var(--p-surface-200);display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:.5rem;flex-shrink:0}.builder .builder-header .header-back{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:transparent;border-radius:6px;cursor:pointer;color:var(--p-text-muted-color);font-size:16px;transition:background .15s ease-out;margin-inline-end:.25rem}.builder .builder-header .header-back:hover{background:var(--p-surface-50);color:var(--p-text-color)}.builder .builder-header .header-page-name{flex:1;text-align:center;font-size:.8rem;font-weight:500;color:var(--p-text-muted-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-inline:1rem}.builder .sidebar-switcher,.builder .panel-switcher{display:flex;gap:.25rem}.builder .sidebar-switcher button,.builder .panel-switcher button{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:6px;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;font-size:1rem;transition:background .15s ease-out,color .15s ease-out}.builder .sidebar-switcher button i,.builder .panel-switcher button i{pointer-events:none}.builder .sidebar-switcher button:hover,.builder .panel-switcher button:hover{background:var(--p-surface-50);color:var(--p-text-color)}.builder .sidebar-switcher button.active,.builder .panel-switcher button.active{background:var(--p-primary-color);color:#fff}.builder .panel-switcher{justify-self:end}.builder .builder-main{display:flex;flex:1 1 0;min-height:0;position:relative;overflow:hidden}.builder .sidebar{width:220px;min-width:220px;background:var(--p-surface-0);border-right:1px solid var(--p-surface-200);overflow-y:auto;overflow-x:hidden;flex-shrink:0}.builder .preview{flex:1 1 0;min-width:0;min-height:0;position:relative;overflow:hidden}.builder .preview.grab-mode{cursor:grab}.builder .preview.grab-mode iox-page-component{pointer-events:none}.builder .preview.grabbing{cursor:grabbing}.builder .canvas-scrollbar{position:absolute;right:4px;top:20px;bottom:20px;width:6px;z-index:20;opacity:0;transition:opacity .2s ease;pointer-events:none}.builder .canvas-scrollbar.visible{opacity:1}.builder .canvas-scrollbar-thumb{position:absolute;left:0;width:100%;background:#00000059;border-radius:3px;transition:top .05s linear}.builder .preview-scroll{position:absolute;inset:20px auto;inset-inline-start:50%;transform:translate(-50%);max-width:calc(100% - 40px);height:calc(100% - 40px);overflow:hidden}.builder .preview-lenis-content{position:relative;width:100%;overflow:hidden;contain:paint}.builder iox-page-component{position:absolute!important;top:0;bottom:auto!important;right:auto!important;left:50%;transform-origin:top center;overflow:visible!important;box-shadow:0 1px 4px #00000014,0 4px 16px #0000000a;transition:width .25s ease,transform .25s ease}.builder iox-page-component ::ng-deep .iox-page-wrapper{height:auto!important;overflow:visible!important}.builder #canvas-preview{background:#fff}.builder .global-elements-overlay{position:absolute;top:20px;left:50%;transform-origin:top center;pointer-events:none;z-index:9;overflow:hidden}.builder .global-elements-overlay ::ng-deep>*{pointer-events:auto}.builder .preview-node.is-selected{cursor:grab}.builder .empty-state{min-height:240px;border:1px dashed var(--p-surface-200);border-radius:10px;display:flex;align-items:center;justify-content:center;color:var(--p-text-muted-color);text-align:center;padding:1rem}.save-block-backdrop{position:fixed;inset:0;z-index:1000;background:#00000040}.save-block-dialog{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1001;background:#fff;border-radius:8px;box-shadow:0 8px 32px #0000002e;padding:24px;width:320px}.save-block-dialog .save-block-title{margin:0 0 16px;font-size:14px;font-weight:600;color:var(--p-text-color, #333)}.save-block-dialog .save-block-input{width:100%;padding:8px 10px;border:1px solid var(--p-surface-300, #d1d5db);border-radius:6px;font-size:13px;outline:none;box-sizing:border-box;margin-bottom:16px}.save-block-dialog .save-block-input:focus{border-color:#cb9090;box-shadow:0 0 0 2px #cb909033}.save-block-dialog .save-block-actions{display:flex;justify-content:flex-end;gap:8px}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i13.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i14.IoxPageComponent, selector: "iox-page-component", inputs: ["class"], outputs: ["scrollEvent"] }, { kind: "directive", type: RenderDirective, selector: "[ioxRender]", inputs: ["ioxRender"], outputs: ["onClick", "onMouseEnter", "onMouseLeave"] }, { kind: "directive", type: IoxDraggableDirective, selector: "[ioxDraggable]", inputs: ["ioxDragData", "ioxDragSourceId"] }, { kind: "component", type: OverlayComponent, selector: "app-overlay", outputs: ["toolbarAction"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["activeMode", "activeDevice", "activeZoom", "activeScreenWidth", "canUndo", "canRedo", "isSaving", "pageStatus"], outputs: ["modeChange", "deviceChange", "zoomChange", "screenWidthChange", "undo", "redo", "save", "preview", "publishToggle"] }, { kind: "component", type: LayerTreeComponent, selector: "app-layer-tree", inputs: ["layout", "globalElements"], outputs: ["nodeSelect", "nodeAction", "nodeMove"] }] }); }
|
|
4341
4359
|
}
|
|
4342
4360
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: BuilderComponent, decorators: [{
|
|
4343
4361
|
type: Component,
|
|
4344
|
-
args: [{ selector: 'app-builder', standalone: false, template: "<div class=\"builder\">\n <header class=\"builder-header\">\n <div class=\"sidebar-switcher\">\n <button (click)=\"activeSidebar = 'components'\" [class.active]=\"activeSidebar === 'components'\" title=\"Components\">\n <i class=\"ph-thin ph-cube\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tree'\" [class.active]=\"activeSidebar === 'tree'\" title=\"Layers\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tokens'\" [class.active]=\"activeSidebar === 'tokens'\" title=\"Design Tokens\">\n <i class=\"ph-thin ph-circles-three\"></i>\n </button>\n </div>\n <div class=\"panel-switcher\">\n <button (click)=\"selectPanel(panelTypes.BINDINGS)\" [class.active]=\"activePanel === panelTypes.BINDINGS\">\n <i class=\"ph-thin ph-plugs-connected\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.STYLES)\" [class.active]=\"activePanel === panelTypes.STYLES\">\n <i class=\"ph-thin ph-paint-brush\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.PAGE)\" [class.active]=\"activePanel === panelTypes.PAGE\">\n <i class=\"ph-thin ph-file\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.INTERACTIONS)\" [class.active]=\"activePanel === panelTypes.INTERACTIONS\">\n <i class=\"ph-thin ph-lightning\"></i>\n </button>\n </div>\n </header>\n\n <div class=\"builder-main\" [class.panel-open]=\"isPanelOpen\">\n <aside class=\"sidebar\">\n <div *ngIf=\"activeSidebar === 'components'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderComponents]\"></ng-content>\n </div>\n <div *ngIf=\"activeSidebar === 'tokens'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderTokens]\"></ng-content>\n </div>\n <app-layer-tree *ngIf=\"activeSidebar === 'tree'\"\n [layout]=\"layout\"\n [globalElements]=\"globalElements\"\n (nodeSelect)=\"onTreeNodeSelect($event)\"\n (nodeAction)=\"onTreeNodeAction($event)\"\n (nodeMove)=\"onTreeNodeMove()\"></app-layer-tree>\n </aside>\n\n <main #previewCanvas class=\"preview\"\n [class.grab-mode]=\"activeMode === builderModes.Pan\"\n [class.grabbing]=\"isPanning\"\n (click)=\"handleCanvasClick()\"\n (mousedown)=\"onBoardMouseDown($event)\"\n (mousemove)=\"onBoardMouseMove($event)\"\n (mouseup)=\"onBoardMouseUp()\"\n (mouseleave)=\"onBoardMouseUp()\">\n <app-overlay (toolbarAction)=\"onToolbarAction($event)\"></app-overlay>\n\n <!-- Custom Lenis scrollbar \u2014 lives in .preview (not .preview-scroll) so it\n stays in the right gutter and never overlaps the canvas at large widths.\n No translateY counteract needed since it isn't inside the Lenis wrapper. -->\n <div class=\"canvas-scrollbar\"\n [class.visible]=\"isScrollbarVisible\">\n <div class=\"canvas-scrollbar-thumb\"\n [style.height.px]=\"scrollThumbHeight\"\n [style.top.px]=\"scrollThumbTop\">\n </div>\n </div>\n\n <!-- Global elements overlay \u2014 outside Lenis so position:fixed children\n stay pinned to the canvas viewport rather than scrolling with content.\n Shares viewportTransform so elements scale correctly at any zoom level.\n Height matches viewportHeight so position:fixed + inset:0 fills the\n simulated viewport correctly (CSS calc(100%) would give the wrong\n containing-block height and break full-screen fixed overlays). -->\n <div *ngIf=\"globalElements.length\"\n #globalOverlay\n class=\"global-elements-overlay\"\n [style.width.px]=\"viewportWidth\"\n [style.height.px]=\"canvasFrameHeight || viewportHeight\"\n [style.transform]=\"viewportTransform\">\n <ng-container *ngFor=\"let item of globalElementsBefore\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </ng-container>\n <ng-container *ngFor=\"let item of globalElementsAfter\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </ng-container>\n </div>\n\n <div class=\"preview-scroll\"\n #previewScroll\n [style.width.px]=\"viewportScaledWidth\"\n [class.pan-mode]=\"activeMode === builderModes.Pan\">\n\n <!-- Lenis content proxy: height = realContentHeight \u00D7 scale.\n Lenis scrolls against this value so scroll range is always in\n visual px \u2014 correct at any zoom level. -->\n <div class=\"preview-lenis-content\" #lenisContent\n [style.height.px]=\"scaledContentHeight\">\n\n <iox-page-component\n [style.width.px]=\"viewportWidth\"\n [style.height.px]=\"canvasFrameHeight || viewportHeight\"\n [style.transform]=\"viewportTransform\">\n\n <div class=\"canvas-viewport\"\n id=\"canvas-preview\"\n [style.min-height.px]=\"canvasMinHeight\">\n\n <div *ngFor=\"let item of layout\"\n [ngClass]=\"['preview-node', 'iox-outer-' + (item.styleId ?? item.id)]\"\n [class.is-selected]=\"selectedItem === item\"\n ioxDraggable\n [ioxDragData]=\"item\"\n [ioxDragSourceId]=\"'canvas-preview'\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </div>\n </div>\n </iox-page-component>\n </div>\n </div>\n </main>\n\n <app-toolbar\n [activeMode]=\"activeMode\"\n [activeDevice]=\"activeDevice\"\n [activeZoom]=\"activeZoom\"\n [activeScreenWidth]=\"activeScreenWidth\"\n [isSaving]=\"isSaving\"\n [pageStatus]=\"pageStatus\"\n [canUndo]=\"canUndo\"\n [canRedo]=\"canRedo\"\n (modeChange)=\"handleToolbarModeChange($event)\"\n (deviceChange)=\"handleToolbarDeviceChange($event)\"\n (zoomChange)=\"handleToolbarZoomChange($event)\"\n (screenWidthChange)=\"handleToolbarScreenWidthChange($event)\"\n (save)=\"save.emit()\"\n (preview)=\"preview.emit()\"\n (publishToggle)=\"publishToggle.emit()\"\n (undo)=\"undo.emit()\"\n (redo)=\"redo.emit()\">\n </app-toolbar>\n\n <ng-content select=\"[builderPanel]\"></ng-content>\n </div>\n\n <!-- Save as Reusable Block dialog -->\n @if (showSaveBlockDialog) {\n <div class=\"save-block-backdrop\" (click)=\"cancelSaveBlock()\"></div>\n <div class=\"save-block-dialog\" (click)=\"$event.stopPropagation()\">\n <h4 class=\"save-block-title\">Save as Reusable Block</h4>\n <input class=\"save-block-input\"\n type=\"text\"\n placeholder=\"Block name...\"\n [(ngModel)]=\"saveBlockName\"\n (keydown.enter)=\"confirmSaveBlock()\"\n (keydown.escape)=\"cancelSaveBlock()\"\n autofocus>\n <div class=\"save-block-actions\">\n <p-button size=\"small\" severity=\"secondary\" label=\"Cancel\" (click)=\"cancelSaveBlock()\"></p-button>\n <p-button size=\"small\" label=\"Save\" [disabled]=\"!saveBlockName.trim()\" (click)=\"confirmSaveBlock()\"></p-button>\n </div>\n </div>\n }\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;flex:1;min-height:0;overflow:hidden}.builder{display:flex;flex-direction:column;direction:ltr;width:100%;height:100%;min-height:0;border-radius:0;overflow:hidden;position:relative}.builder .builder-header{background:#fff;padding:.5rem 1rem;border-bottom:1px solid var(--p-surface-200);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.builder .builder-header .header-back{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:transparent;border-radius:6px;cursor:pointer;color:var(--p-text-muted-color);font-size:16px;transition:background .15s;margin-inline-end:.25rem}.builder .builder-header .header-back:hover{background:var(--p-surface-100, #f1f5f9);color:var(--p-text-color)}.builder .builder-header .header-page-name{flex:1;text-align:center;font-size:.8rem;font-weight:500;color:var(--p-text-muted-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-inline:1rem}.builder .sidebar-switcher,.builder .panel-switcher{display:flex;gap:.5rem}.builder .sidebar-switcher p-button ::ng-deep .p-button,.builder .panel-switcher p-button ::ng-deep .p-button{color:#000;background:transparent;border:1px solid transparent;transition:border-color .2s}.builder .sidebar-switcher p-button ::ng-deep .p-button:hover,.builder .panel-switcher p-button ::ng-deep .p-button:hover{border-color:#cb9090}.builder .sidebar-switcher p-button.active ::ng-deep .p-button,.builder .panel-switcher p-button.active ::ng-deep .p-button{background:#cb9090;color:#fff;border-color:#cb9090}.builder .builder-main{display:flex;flex:1 1 0;min-height:0;position:relative;overflow:hidden}.builder .builder-main app-toolbar{position:fixed;bottom:1.25rem;left:50%;transform:translate(-50%);z-index:1000;pointer-events:auto}.builder .sidebar{width:220px;min-width:220px;background:var(--p-surface-0);border-right:1px solid var(--p-surface-200);overflow-y:auto;overflow-x:hidden;flex-shrink:0}.builder .preview{flex:1 1 0;min-width:0;min-height:0;position:relative;overflow:hidden}.builder .preview.grab-mode{cursor:grab}.builder .preview.grab-mode iox-page-component{pointer-events:none}.builder .preview.grabbing{cursor:grabbing}.builder .canvas-scrollbar{position:absolute;right:4px;top:20px;bottom:20px;width:6px;z-index:20;opacity:0;transition:opacity .2s ease;pointer-events:none}.builder .canvas-scrollbar.visible{opacity:1}.builder .canvas-scrollbar-thumb{position:absolute;left:0;width:100%;background:#00000059;border-radius:3px;transition:top .05s linear}.builder .preview-scroll{position:absolute;inset:20px auto;inset-inline-start:50%;transform:translate(-50%);max-width:calc(100% - 40px);height:calc(100% - 40px);overflow:hidden}.builder .preview-lenis-content{position:relative;width:100%;overflow:hidden;contain:paint}.builder iox-page-component{position:absolute!important;top:0;bottom:auto!important;right:auto!important;left:50%;transform-origin:top center;overflow:visible!important;box-shadow:0 1px 4px #00000014,0 4px 16px #0000000a;transition:width .25s ease,transform .25s ease}.builder iox-page-component ::ng-deep .iox-page-wrapper{height:auto!important;overflow:visible!important}.builder #canvas-preview{background:#fff}.builder .global-elements-overlay{position:absolute;top:20px;left:50%;transform-origin:top center;pointer-events:none;z-index:9;overflow:hidden}.builder .global-elements-overlay ::ng-deep>*{pointer-events:auto}.builder .preview-node.is-selected{cursor:grab}.builder .empty-state{min-height:240px;border:1px dashed var(--p-surface-200);border-radius:10px;display:flex;align-items:center;justify-content:center;color:var(--p-text-muted-color);text-align:center;padding:1rem}.save-block-backdrop{position:fixed;inset:0;z-index:1000;background:#00000040}.save-block-dialog{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1001;background:#fff;border-radius:8px;box-shadow:0 8px 32px #0000002e;padding:24px;width:320px}.save-block-dialog .save-block-title{margin:0 0 16px;font-size:14px;font-weight:600;color:var(--p-text-color, #333)}.save-block-dialog .save-block-input{width:100%;padding:8px 10px;border:1px solid var(--p-surface-300, #d1d5db);border-radius:6px;font-size:13px;outline:none;box-sizing:border-box;margin-bottom:16px}.save-block-dialog .save-block-input:focus{border-color:#cb9090;box-shadow:0 0 0 2px #cb909033}.save-block-dialog .save-block-actions{display:flex;justify-content:flex-end;gap:8px}\n"] }]
|
|
4362
|
+
args: [{ selector: 'app-builder', standalone: false, template: "<div class=\"builder\">\n <header class=\"builder-header\">\n <div class=\"sidebar-switcher\">\n <button (click)=\"activeSidebar = 'components'\" [class.active]=\"activeSidebar === 'components'\" title=\"Components\">\n <i class=\"ph-thin ph-cube\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tree'\" [class.active]=\"activeSidebar === 'tree'\" title=\"Layers\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tokens'\" [class.active]=\"activeSidebar === 'tokens'\" title=\"Design Tokens\">\n <i class=\"ph-thin ph-circles-three\"></i>\n </button>\n </div>\n\n <app-toolbar\n [activeMode]=\"activeMode\"\n [activeDevice]=\"activeDevice\"\n [activeZoom]=\"activeZoom\"\n [activeScreenWidth]=\"activeScreenWidth\"\n [isSaving]=\"isSaving\"\n [pageStatus]=\"pageStatus\"\n [canUndo]=\"canUndo\"\n [canRedo]=\"canRedo\"\n (modeChange)=\"handleToolbarModeChange($event)\"\n (deviceChange)=\"handleToolbarDeviceChange($event)\"\n (zoomChange)=\"handleToolbarZoomChange($event)\"\n (screenWidthChange)=\"handleToolbarScreenWidthChange($event)\"\n (save)=\"save.emit()\"\n (preview)=\"preview.emit()\"\n (publishToggle)=\"publishToggle.emit()\"\n (undo)=\"undo.emit()\"\n (redo)=\"redo.emit()\">\n </app-toolbar>\n\n <div class=\"panel-switcher\">\n <button (click)=\"selectPanel(panelTypes.BINDINGS)\" [class.active]=\"activePanel === panelTypes.BINDINGS\" title=\"Bindings\">\n <i class=\"ph-thin ph-plugs-connected\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.STYLES)\" [class.active]=\"activePanel === panelTypes.STYLES\" title=\"Styles\">\n <i class=\"ph-thin ph-paint-brush\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.PAGE)\" [class.active]=\"activePanel === panelTypes.PAGE\" title=\"Page settings\">\n <i class=\"ph-thin ph-file\"></i>\n </button>\n <button (click)=\"selectPanel(panelTypes.INTERACTIONS)\" [class.active]=\"activePanel === panelTypes.INTERACTIONS\" title=\"Interactions\">\n <i class=\"ph-thin ph-lightning\"></i>\n </button>\n </div>\n </header>\n\n <div class=\"builder-main\" [class.panel-open]=\"isPanelOpen\">\n <aside class=\"sidebar\">\n <div *ngIf=\"activeSidebar === 'components'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderComponents]\"></ng-content>\n </div>\n <div *ngIf=\"activeSidebar === 'tokens'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderTokens]\"></ng-content>\n </div>\n <app-layer-tree *ngIf=\"activeSidebar === 'tree'\"\n [layout]=\"layout\"\n [globalElements]=\"globalElements\"\n (nodeSelect)=\"onTreeNodeSelect($event)\"\n (nodeAction)=\"onTreeNodeAction($event)\"\n (nodeMove)=\"onTreeNodeMove()\"></app-layer-tree>\n </aside>\n\n <main #previewCanvas class=\"preview\"\n [class.grab-mode]=\"activeMode === builderModes.Pan\"\n [class.grabbing]=\"isPanning\"\n (click)=\"handleCanvasClick()\"\n (mousedown)=\"onBoardMouseDown($event)\"\n (mousemove)=\"onBoardMouseMove($event)\"\n (mouseup)=\"onBoardMouseUp()\"\n (mouseleave)=\"onBoardMouseUp()\">\n <app-overlay (toolbarAction)=\"onToolbarAction($event)\"></app-overlay>\n\n <!-- Custom Lenis scrollbar \u2014 lives in .preview (not .preview-scroll) so it\n stays in the right gutter and never overlaps the canvas at large widths.\n No translateY counteract needed since it isn't inside the Lenis wrapper. -->\n <div class=\"canvas-scrollbar\"\n [class.visible]=\"isScrollbarVisible\">\n <div class=\"canvas-scrollbar-thumb\"\n [style.height.px]=\"scrollThumbHeight\"\n [style.top.px]=\"scrollThumbTop\">\n </div>\n </div>\n\n <!-- Global elements overlay \u2014 outside Lenis so position:fixed children\n stay pinned to the canvas viewport rather than scrolling with content.\n Shares viewportTransform so elements scale correctly at any zoom level.\n Height matches viewportHeight so position:fixed + inset:0 fills the\n simulated viewport correctly (CSS calc(100%) would give the wrong\n containing-block height and break full-screen fixed overlays). -->\n <div *ngIf=\"globalElements.length\"\n #globalOverlay\n class=\"global-elements-overlay\"\n [style.width.px]=\"viewportWidth\"\n [style.height.px]=\"canvasFrameHeight || viewportHeight\"\n [style.transform]=\"viewportTransform\">\n <ng-container *ngFor=\"let item of globalElementsBefore\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </ng-container>\n <ng-container *ngFor=\"let item of globalElementsAfter\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </ng-container>\n </div>\n\n <div class=\"preview-scroll\"\n #previewScroll\n [style.width.px]=\"viewportScaledWidth\"\n [class.pan-mode]=\"activeMode === builderModes.Pan\">\n\n <!-- Lenis content proxy: height = realContentHeight \u00D7 scale.\n Lenis scrolls against this value so scroll range is always in\n visual px \u2014 correct at any zoom level. -->\n <div class=\"preview-lenis-content\" #lenisContent\n [style.height.px]=\"scaledContentHeight\">\n\n <iox-page-component\n [style.width.px]=\"viewportWidth\"\n [style.height.px]=\"canvasFrameHeight || viewportHeight\"\n [style.transform]=\"viewportTransform\">\n\n <div class=\"canvas-viewport\"\n id=\"canvas-preview\"\n [style.min-height.px]=\"canvasMinHeight\">\n\n <div *ngFor=\"let item of layout\"\n [ngClass]=\"['preview-node', 'iox-outer-' + (item.styleId ?? item.id)]\"\n [class.is-selected]=\"selectedItem === item\"\n ioxDraggable\n [ioxDragData]=\"item\"\n [ioxDragSourceId]=\"'canvas-preview'\">\n <ng-container [ioxRender]=\"item\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\n </div>\n </div>\n </iox-page-component>\n </div>\n </div>\n </main>\n\n <ng-content select=\"[builderPanel]\"></ng-content>\n </div>\n\n <!-- Save as Reusable Block dialog -->\n @if (showSaveBlockDialog) {\n <div class=\"save-block-backdrop\" (click)=\"cancelSaveBlock()\"></div>\n <div class=\"save-block-dialog\" (click)=\"$event.stopPropagation()\">\n <h4 class=\"save-block-title\">Save as Reusable Block</h4>\n <input class=\"save-block-input\"\n type=\"text\"\n placeholder=\"Block name...\"\n [(ngModel)]=\"saveBlockName\"\n (keydown.enter)=\"confirmSaveBlock()\"\n (keydown.escape)=\"cancelSaveBlock()\"\n autofocus>\n <div class=\"save-block-actions\">\n <p-button size=\"small\" severity=\"secondary\" label=\"Cancel\" (click)=\"cancelSaveBlock()\"></p-button>\n <p-button size=\"small\" label=\"Save\" [disabled]=\"!saveBlockName.trim()\" (click)=\"confirmSaveBlock()\"></p-button>\n </div>\n </div>\n }\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;flex:1;min-height:0;overflow:hidden}.builder{display:flex;flex-direction:column;direction:ltr;width:100%;height:100%;min-height:0;border-radius:0;overflow:hidden;position:relative}.builder .builder-header{background:var(--p-surface-0);padding:.375rem .75rem;border-bottom:1px solid var(--p-surface-200);display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:.5rem;flex-shrink:0}.builder .builder-header .header-back{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:transparent;border-radius:6px;cursor:pointer;color:var(--p-text-muted-color);font-size:16px;transition:background .15s ease-out;margin-inline-end:.25rem}.builder .builder-header .header-back:hover{background:var(--p-surface-50);color:var(--p-text-color)}.builder .builder-header .header-page-name{flex:1;text-align:center;font-size:.8rem;font-weight:500;color:var(--p-text-muted-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-inline:1rem}.builder .sidebar-switcher,.builder .panel-switcher{display:flex;gap:.25rem}.builder .sidebar-switcher button,.builder .panel-switcher button{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:6px;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;font-size:1rem;transition:background .15s ease-out,color .15s ease-out}.builder .sidebar-switcher button i,.builder .panel-switcher button i{pointer-events:none}.builder .sidebar-switcher button:hover,.builder .panel-switcher button:hover{background:var(--p-surface-50);color:var(--p-text-color)}.builder .sidebar-switcher button.active,.builder .panel-switcher button.active{background:var(--p-primary-color);color:#fff}.builder .panel-switcher{justify-self:end}.builder .builder-main{display:flex;flex:1 1 0;min-height:0;position:relative;overflow:hidden}.builder .sidebar{width:220px;min-width:220px;background:var(--p-surface-0);border-right:1px solid var(--p-surface-200);overflow-y:auto;overflow-x:hidden;flex-shrink:0}.builder .preview{flex:1 1 0;min-width:0;min-height:0;position:relative;overflow:hidden}.builder .preview.grab-mode{cursor:grab}.builder .preview.grab-mode iox-page-component{pointer-events:none}.builder .preview.grabbing{cursor:grabbing}.builder .canvas-scrollbar{position:absolute;right:4px;top:20px;bottom:20px;width:6px;z-index:20;opacity:0;transition:opacity .2s ease;pointer-events:none}.builder .canvas-scrollbar.visible{opacity:1}.builder .canvas-scrollbar-thumb{position:absolute;left:0;width:100%;background:#00000059;border-radius:3px;transition:top .05s linear}.builder .preview-scroll{position:absolute;inset:20px auto;inset-inline-start:50%;transform:translate(-50%);max-width:calc(100% - 40px);height:calc(100% - 40px);overflow:hidden}.builder .preview-lenis-content{position:relative;width:100%;overflow:hidden;contain:paint}.builder iox-page-component{position:absolute!important;top:0;bottom:auto!important;right:auto!important;left:50%;transform-origin:top center;overflow:visible!important;box-shadow:0 1px 4px #00000014,0 4px 16px #0000000a;transition:width .25s ease,transform .25s ease}.builder iox-page-component ::ng-deep .iox-page-wrapper{height:auto!important;overflow:visible!important}.builder #canvas-preview{background:#fff}.builder .global-elements-overlay{position:absolute;top:20px;left:50%;transform-origin:top center;pointer-events:none;z-index:9;overflow:hidden}.builder .global-elements-overlay ::ng-deep>*{pointer-events:auto}.builder .preview-node.is-selected{cursor:grab}.builder .empty-state{min-height:240px;border:1px dashed var(--p-surface-200);border-radius:10px;display:flex;align-items:center;justify-content:center;color:var(--p-text-muted-color);text-align:center;padding:1rem}.save-block-backdrop{position:fixed;inset:0;z-index:1000;background:#00000040}.save-block-dialog{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1001;background:#fff;border-radius:8px;box-shadow:0 8px 32px #0000002e;padding:24px;width:320px}.save-block-dialog .save-block-title{margin:0 0 16px;font-size:14px;font-weight:600;color:var(--p-text-color, #333)}.save-block-dialog .save-block-input{width:100%;padding:8px 10px;border:1px solid var(--p-surface-300, #d1d5db);border-radius:6px;font-size:13px;outline:none;box-sizing:border-box;margin-bottom:16px}.save-block-dialog .save-block-input:focus{border-color:#cb9090;box-shadow:0 0 0 2px #cb909033}.save-block-dialog .save-block-actions{display:flex;justify-content:flex-end;gap:8px}\n"] }]
|
|
4345
4363
|
}], ctorParameters: () => [{ type: ComponentRegistryService }, { type: OverlayService }, { type: PanelEventService }, { type: DragEngineService }, { type: DataSourceRegistryService }, { type: ViewportService }, { type: InteractionEngineService }, { type: InteractionPresetRegistryService }, { type: BuilderClipboardService }, { type: SymbolRegistryService }, { type: i0.ChangeDetectorRef }, { type: i0.ApplicationRef }], propDecorators: { layout: [{
|
|
4346
4364
|
type: Input
|
|
4347
4365
|
}], dataSources: [{
|