@vectoriox/iox-builder 2.12.1 → 3.1.0
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.
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { Injectable, inject, NgZone, EventEmitter, createComponent, Output, Input, Inject, Directive, ChangeDetectionStrategy, Component, ViewChild, effect, Optional, InjectionToken, ContentChildren, NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
|
|
3
3
|
import * as i2 from '@angular/common';
|
|
4
4
|
import { DOCUMENT, CommonModule } from '@angular/common';
|
|
5
|
-
import * as
|
|
5
|
+
import * as i14 from '@angular/forms';
|
|
6
6
|
import { FormsModule } from '@angular/forms';
|
|
7
7
|
import * as i4 from '@angular/cdk/drag-drop';
|
|
8
8
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
@@ -12,7 +12,6 @@ import { ButtonModule } from 'primeng/button';
|
|
|
12
12
|
import * as i2$1 from 'primeng/popover';
|
|
13
13
|
import { PopoverModule } from 'primeng/popover';
|
|
14
14
|
import { InputTextModule } from 'primeng/inputtext';
|
|
15
|
-
import * as i5 from 'primeng/select';
|
|
16
15
|
import { SelectModule } from 'primeng/select';
|
|
17
16
|
import { TooltipModule } from 'primeng/tooltip';
|
|
18
17
|
import { DialogModule } from 'primeng/dialog';
|
|
@@ -58,6 +57,11 @@ var DataSourceType;
|
|
|
58
57
|
* `$related`. Not fetched: computed by the shared `computeRelative` from other resolved sources.
|
|
59
58
|
* See relative-data-sources-plan.md. */
|
|
60
59
|
DataSourceType["Relative"] = "relative";
|
|
60
|
+
/** A page-local VARIABLE: a named resource whose value is WRITTEN at runtime by an interaction
|
|
61
|
+
* (`setContext`), never fetched. Starts empty; elements bind to it like any other alias.
|
|
62
|
+
* `request.variable.itemsFrom` names the source whose items it holds, for field suggestions.
|
|
63
|
+
* See element-context-state-plan.md. */
|
|
64
|
+
DataSourceType["Variable"] = "variable";
|
|
61
65
|
})(DataSourceType || (DataSourceType = {}));
|
|
62
66
|
/**
|
|
63
67
|
* Maps each action type to its logical inverse for auto-populating toggle
|
|
@@ -5615,7 +5619,7 @@ class BuilderComponent {
|
|
|
5615
5619
|
}
|
|
5616
5620
|
}
|
|
5617
5621
|
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 }, { token: MoveableCanvasService }, { token: SelectoCanvasService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5618
|
-
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", moveableMode: "moveableMode" }, 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 = 'pages'\" [class.active]=\"activeSidebar === 'pages'\" title=\"Pages\" aria-label=\"Pages\">\n <i class=\"ph-thin ph-browsers\"></i>\n </button>\n <button (click)=\"activeSidebar = 'components'\" [class.active]=\"activeSidebar === 'components'\" title=\"Components\" aria-label=\"Components\">\n <i class=\"ph-thin ph-cube\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tree'\" [class.active]=\"activeSidebar === 'tree'\" title=\"Layers\" aria-label=\"Layers\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tokens'\" [class.active]=\"activeSidebar === 'tokens'\" title=\"Design Tokens\" aria-label=\"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\" aria-label=\"Page settings\">\n <i class=\"ph-thin ph-gear-six\"></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 === 'pages'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderPages]\"></ng-content>\n </div>\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 <!-- WRAPPER COLLAPSE (task 9c): top-level nodes render DIRECTLY into the\n canvas viewport \u2014 no .preview-node wrapper; iox-outer + drag move to the\n host (RenderDirective, via ioxDragSourceId). -->\n <ng-container *ngFor=\"let item of layout\"\n [ioxRender]=\"item\"\n [ioxDragSourceId]=\"'canvas-preview'\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\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:#cb9090;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:260px;min-width:260px;background:var(--p-surface-0);border-inline-end: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}::ng-deep .moveable-control-box.iox-moveable-silent{z-index:1000!important}::ng-deep .moveable-control-box.iox-moveable-silent .moveable-line,::ng-deep .moveable-control-box.iox-moveable-silent .moveable-rotation,::ng-deep .moveable-control-box.iox-moveable-silent .moveable-rotation-line{display:none!important}::ng-deep .moveable-control-box.iox-moveable-silent .moveable-control{display:block!important;width:10px!important;height:10px!important;margin-top:-5px!important;margin-left:-5px!important;background:#fff!important;border:1.5px solid #cb9090!important;border-radius:2px!important;box-shadow:0 1px 3px #0003!important}\n"], dependencies: [{ 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: i4$1.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: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i15.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: i16.IoxPageComponent, selector: "iox-page-component", inputs: ["class"], outputs: ["scrollEvent"] }, { kind: "directive", type: RenderDirective, selector: "[ioxRender]", inputs: ["ioxRender", "ioxDragSourceId"], outputs: ["onClick", "onMouseEnter", "onMouseLeave"] }, { 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"] }] }); }
|
|
5622
|
+
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", moveableMode: "moveableMode" }, 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 = 'pages'\" [class.active]=\"activeSidebar === 'pages'\" title=\"Pages\" aria-label=\"Pages\">\n <i class=\"ph-thin ph-browsers\"></i>\n </button>\n <button (click)=\"activeSidebar = 'components'\" [class.active]=\"activeSidebar === 'components'\" title=\"Components\" aria-label=\"Components\">\n <i class=\"ph-thin ph-cube\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tree'\" [class.active]=\"activeSidebar === 'tree'\" title=\"Layers\" aria-label=\"Layers\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n </button>\n <button (click)=\"activeSidebar = 'tokens'\" [class.active]=\"activeSidebar === 'tokens'\" title=\"Design Tokens\" aria-label=\"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\" aria-label=\"Page settings\">\n <i class=\"ph-thin ph-gear-six\"></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 === 'pages'\" class=\"sidebar-slot\">\n <ng-content select=\"[builderPages]\"></ng-content>\n </div>\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 <!-- WRAPPER COLLAPSE (task 9c): top-level nodes render DIRECTLY into the\n canvas viewport \u2014 no .preview-node wrapper; iox-outer + drag move to the\n host (RenderDirective, via ioxDragSourceId). -->\n <ng-container *ngFor=\"let item of layout\"\n [ioxRender]=\"item\"\n [ioxDragSourceId]=\"'canvas-preview'\"\n (onClick)=\"handleClick($event)\"\n (onMouseEnter)=\"handleMouseEnter($event)\"\n (onMouseLeave)=\"handleMouseLeave()\">\n </ng-container>\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:#cb9090;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:260px;min-width:260px;background:var(--p-surface-0);border-inline-end: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}::ng-deep .moveable-control-box.iox-moveable-silent{z-index:1000!important}::ng-deep .moveable-control-box.iox-moveable-silent .moveable-line,::ng-deep .moveable-control-box.iox-moveable-silent .moveable-rotation,::ng-deep .moveable-control-box.iox-moveable-silent .moveable-rotation-line{display:none!important}::ng-deep .moveable-control-box.iox-moveable-silent .moveable-control{display:block!important;width:10px!important;height:10px!important;margin-top:-5px!important;margin-left:-5px!important;background:#fff!important;border:1.5px solid #cb9090!important;border-radius:2px!important;box-shadow:0 1px 3px #0003!important}\n"], dependencies: [{ 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: i14.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: i14.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i14.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i15.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: i16.IoxPageComponent, selector: "iox-page-component", inputs: ["class"], outputs: ["scrollEvent"] }, { kind: "directive", type: RenderDirective, selector: "[ioxRender]", inputs: ["ioxRender", "ioxDragSourceId"], outputs: ["onClick", "onMouseEnter", "onMouseLeave"] }, { 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"] }] }); }
|
|
5619
5623
|
}
|
|
5620
5624
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: BuilderComponent, decorators: [{
|
|
5621
5625
|
type: Component,
|
|
@@ -7664,324 +7668,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
7664
7668
|
args: [PanelChildComponent]
|
|
7665
7669
|
}] } });
|
|
7666
7670
|
|
|
7667
|
-
class InteractionsPanelComponent {
|
|
7668
|
-
constructor(overlayService, componentRegistry) {
|
|
7669
|
-
this.overlayService = overlayService;
|
|
7670
|
-
this.componentRegistry = componentRegistry;
|
|
7671
|
-
this.node = null;
|
|
7672
|
-
/** Base + component-specific trigger options for the selected node. */
|
|
7673
|
-
this.triggerOptions = [...TRIGGER_OPTIONS];
|
|
7674
|
-
/** Base + component-specific action type options. */
|
|
7675
|
-
this.actionTypeOptions = [...ACTION_TYPE_OPTIONS];
|
|
7676
|
-
this.easingOptions = EASING_OPTIONS$1;
|
|
7677
|
-
this.interactions = [];
|
|
7678
|
-
/** All canvas nodes available as animation targets. */
|
|
7679
|
-
this.nodeOptions = [];
|
|
7680
|
-
/** Slot names already used anywhere on the canvas — offered as suggestions, not a closed list.
|
|
7681
|
-
* A slot needs no declaration to work: naming it in a setContext action creates it. */
|
|
7682
|
-
this.knownSlots = [];
|
|
7683
|
-
/** Add-form state for a context action. */
|
|
7684
|
-
this.newSlot = 'currentItem';
|
|
7685
|
-
this.newValueKind = 'item';
|
|
7686
|
-
this.newValueSource = '';
|
|
7687
|
-
this.newValuePath = '';
|
|
7688
|
-
/** Defaults ON — a row that cleared itself on mouseleave would blink on every row-to-row move. */
|
|
7689
|
-
this.newKeepAfterLeave = true;
|
|
7690
|
-
/** Data-source aliases, for the "Pick a value…" mode. */
|
|
7691
|
-
this.sourceOptions = [];
|
|
7692
|
-
this.dsRegistry = inject(DataSourceRegistryService);
|
|
7693
|
-
/** State for the "add interaction" form */
|
|
7694
|
-
this.isAdding = false;
|
|
7695
|
-
this.newTrigger = 'pageLoad';
|
|
7696
|
-
this.newActionType = 'fadeIn';
|
|
7697
|
-
this.newTarget = 'self';
|
|
7698
|
-
this.newDuration = 600;
|
|
7699
|
-
this.newDelay = 0;
|
|
7700
|
-
this.newEasing = 'ease-out';
|
|
7701
|
-
this.newOnce = true;
|
|
7702
|
-
/** Slide index param for swiperGoToSlide action. */
|
|
7703
|
-
this.newSlideIndex = 0;
|
|
7704
|
-
/** Expanded interaction ID for editing */
|
|
7705
|
-
this.expandedId = null;
|
|
7706
|
-
}
|
|
7707
|
-
get isSwiperGoToSlide() {
|
|
7708
|
-
return this.newActionType === 'swiperGoToSlide';
|
|
7709
|
-
}
|
|
7710
|
-
get isSwiperAction() {
|
|
7711
|
-
return this.newActionType.startsWith('swiper');
|
|
7712
|
-
}
|
|
7713
|
-
/** The add-form is building a context action — it needs a slot, not a target/duration/easing. */
|
|
7714
|
-
get isNewContextAction() {
|
|
7715
|
-
return isContextAction({ type: this.newActionType });
|
|
7716
|
-
}
|
|
7717
|
-
ngOnInit() {
|
|
7718
|
-
this.selectSub = this.overlayService.select$.subscribe(entry => {
|
|
7719
|
-
this.node = entry?.node ?? null;
|
|
7720
|
-
this.interactions = this.node?.interactions ?? [];
|
|
7721
|
-
this.isAdding = false;
|
|
7722
|
-
this.expandedId = null;
|
|
7723
|
-
this.refreshNodeOptions();
|
|
7724
|
-
this.refreshDynamicOptions();
|
|
7725
|
-
this.refreshKnownSlots();
|
|
7726
|
-
});
|
|
7727
|
-
}
|
|
7728
|
-
/** Merge component-specific events into trigger options and methods into action options. */
|
|
7729
|
-
refreshDynamicOptions() {
|
|
7730
|
-
// Build trigger options: base + events from selected node's component type
|
|
7731
|
-
const baseTriggers = [...TRIGGER_OPTIONS];
|
|
7732
|
-
if (this.node?.type) {
|
|
7733
|
-
const config = this.componentRegistry.createConfig(this.node.type);
|
|
7734
|
-
if (config?.componentEvents?.length) {
|
|
7735
|
-
for (const ev of config.componentEvents) {
|
|
7736
|
-
baseTriggers.push({ value: ev.value, label: ev.label, icon: ev.icon ?? 'ph-thin ph-lightning' });
|
|
7737
|
-
}
|
|
7738
|
-
}
|
|
7739
|
-
}
|
|
7740
|
-
this.triggerOptions = baseTriggers;
|
|
7741
|
-
// Build action options: base + methods from any SliderContainer-type labeled nodes in the tree
|
|
7742
|
-
const baseActions = [...ACTION_TYPE_OPTIONS];
|
|
7743
|
-
const swiperMethodsAdded = new Set();
|
|
7744
|
-
for (const [n] of this.overlayService.getAllNodeEntries()) {
|
|
7745
|
-
if (!n.id || !n.label)
|
|
7746
|
-
continue;
|
|
7747
|
-
const config = this.componentRegistry.createConfig(n.type);
|
|
7748
|
-
if (!config?.componentMethods?.length)
|
|
7749
|
-
continue;
|
|
7750
|
-
for (const method of config.componentMethods) {
|
|
7751
|
-
if (!swiperMethodsAdded.has(method.value)) {
|
|
7752
|
-
baseActions.push({ value: method.value, label: method.label });
|
|
7753
|
-
swiperMethodsAdded.add(method.value);
|
|
7754
|
-
}
|
|
7755
|
-
}
|
|
7756
|
-
}
|
|
7757
|
-
this.actionTypeOptions = baseActions;
|
|
7758
|
-
}
|
|
7759
|
-
refreshNodeOptions() {
|
|
7760
|
-
const opts = [{ label: 'Self', value: 'self' }];
|
|
7761
|
-
for (const [node] of this.overlayService.getAllNodeEntries()) {
|
|
7762
|
-
if (!node.id || !node.label || node === this.node)
|
|
7763
|
-
continue;
|
|
7764
|
-
opts.push({ label: node.label, value: node.id });
|
|
7765
|
-
}
|
|
7766
|
-
this.nodeOptions = opts;
|
|
7767
|
-
}
|
|
7768
|
-
getTargetLabel(value) {
|
|
7769
|
-
return this.nodeOptions.find(o => o.value === value)?.label ?? value;
|
|
7770
|
-
}
|
|
7771
|
-
ngOnDestroy() {
|
|
7772
|
-
this.selectSub?.unsubscribe();
|
|
7773
|
-
}
|
|
7774
|
-
getTriggerLabel(trigger) {
|
|
7775
|
-
return this.triggerOptions.find(t => t.value === trigger)?.label ?? trigger;
|
|
7776
|
-
}
|
|
7777
|
-
getTriggerIcon(trigger) {
|
|
7778
|
-
return this.triggerOptions.find(t => t.value === trigger)?.icon ?? 'ph-thin ph-lightning';
|
|
7779
|
-
}
|
|
7780
|
-
getActionLabel(type) {
|
|
7781
|
-
return this.actionTypeOptions.find(a => a.value === type)?.label ?? type;
|
|
7782
|
-
}
|
|
7783
|
-
getActionsSummary(interaction) {
|
|
7784
|
-
return interaction.actions.map(a => this.getActionLabel(a.type)).join(', ');
|
|
7785
|
-
}
|
|
7786
|
-
toggleExpand(id) {
|
|
7787
|
-
this.expandedId = this.expandedId === id ? null : id;
|
|
7788
|
-
}
|
|
7789
|
-
// ── Add interaction ──────────────────────────────────────
|
|
7790
|
-
startAdd() {
|
|
7791
|
-
this.isAdding = true;
|
|
7792
|
-
this.newTrigger = 'pageLoad';
|
|
7793
|
-
this.newActionType = 'fadeIn';
|
|
7794
|
-
this.newTarget = 'self';
|
|
7795
|
-
this.newDuration = 600;
|
|
7796
|
-
this.newDelay = 0;
|
|
7797
|
-
this.newEasing = 'ease-out';
|
|
7798
|
-
this.newOnce = true;
|
|
7799
|
-
this.newSlideIndex = 0;
|
|
7800
|
-
this.refreshNodeOptions();
|
|
7801
|
-
this.refreshDynamicOptions();
|
|
7802
|
-
}
|
|
7803
|
-
cancelAdd() {
|
|
7804
|
-
this.isAdding = false;
|
|
7805
|
-
}
|
|
7806
|
-
confirmAdd() {
|
|
7807
|
-
if (!this.node)
|
|
7808
|
-
return;
|
|
7809
|
-
// A context action carries SetContextParams. Without this the add-form produced an action
|
|
7810
|
-
// with no params at all — contextActionEffect would return null and the interaction would
|
|
7811
|
-
// silently do nothing.
|
|
7812
|
-
const params = this.isNewContextAction
|
|
7813
|
-
? {
|
|
7814
|
-
slot: (this.newSlot ?? '').trim(),
|
|
7815
|
-
...(this.newActionType === 'setContext' ? {
|
|
7816
|
-
value: this.newValueKind === 'source'
|
|
7817
|
-
? { kind: 'source', source: this.newValueSource, path: this.newValuePath }
|
|
7818
|
-
: { kind: 'item' },
|
|
7819
|
-
noReverse: this.newKeepAfterLeave,
|
|
7820
|
-
} : {}),
|
|
7821
|
-
}
|
|
7822
|
-
: this.isSwiperGoToSlide
|
|
7823
|
-
? { index: this.newSlideIndex }
|
|
7824
|
-
: undefined;
|
|
7825
|
-
const action = {
|
|
7826
|
-
type: this.newActionType,
|
|
7827
|
-
target: this.newTarget,
|
|
7828
|
-
duration: this.newDuration,
|
|
7829
|
-
delay: this.newDelay,
|
|
7830
|
-
easing: this.newEasing,
|
|
7831
|
-
once: this.newOnce,
|
|
7832
|
-
...(params ? { params } : {}),
|
|
7833
|
-
};
|
|
7834
|
-
const interaction = {
|
|
7835
|
-
id: generateNodeId(),
|
|
7836
|
-
trigger: this.newTrigger,
|
|
7837
|
-
actions: [action],
|
|
7838
|
-
};
|
|
7839
|
-
if (!this.node.interactions) {
|
|
7840
|
-
this.node.interactions = [];
|
|
7841
|
-
}
|
|
7842
|
-
this.node.interactions.push(interaction);
|
|
7843
|
-
this.interactions = this.node.interactions;
|
|
7844
|
-
this.isAdding = false;
|
|
7845
|
-
this.refreshKnownSlots();
|
|
7846
|
-
}
|
|
7847
|
-
// ── Remove interaction ───────────────────────────────────
|
|
7848
|
-
removeInteraction(id) {
|
|
7849
|
-
if (!this.node?.interactions)
|
|
7850
|
-
return;
|
|
7851
|
-
this.node.interactions = this.node.interactions.filter(i => i.id !== id);
|
|
7852
|
-
this.interactions = this.node.interactions;
|
|
7853
|
-
if (this.expandedId === id) {
|
|
7854
|
-
this.expandedId = null;
|
|
7855
|
-
}
|
|
7856
|
-
}
|
|
7857
|
-
// ── Add action to existing interaction ───────────────────
|
|
7858
|
-
addAction(interaction) {
|
|
7859
|
-
interaction.actions.push({
|
|
7860
|
-
type: 'fadeIn',
|
|
7861
|
-
target: 'self',
|
|
7862
|
-
duration: 600,
|
|
7863
|
-
delay: 0,
|
|
7864
|
-
easing: 'ease-out',
|
|
7865
|
-
once: true,
|
|
7866
|
-
});
|
|
7867
|
-
}
|
|
7868
|
-
removeAction(interaction, index) {
|
|
7869
|
-
interaction.actions.splice(index, 1);
|
|
7870
|
-
// Remove the interaction if it has no actions left
|
|
7871
|
-
if (interaction.actions.length === 0) {
|
|
7872
|
-
this.removeInteraction(interaction.id);
|
|
7873
|
-
}
|
|
7874
|
-
}
|
|
7875
|
-
// ── Context actions ──────────────────────────────────────
|
|
7876
|
-
/** True for setContext/clearContext — they get a slot editor instead of a target + easing. */
|
|
7877
|
-
isContextAction(action) {
|
|
7878
|
-
return isContextAction(action);
|
|
7879
|
-
}
|
|
7880
|
-
ctxParams(action) {
|
|
7881
|
-
action.params ??= {};
|
|
7882
|
-
return action.params;
|
|
7883
|
-
}
|
|
7884
|
-
slotOf(action) {
|
|
7885
|
-
return this.ctxParams(action).slot ?? '';
|
|
7886
|
-
}
|
|
7887
|
-
setSlot(action, slot) {
|
|
7888
|
-
this.ctxParams(action).slot = (slot ?? '').trim();
|
|
7889
|
-
this.refreshKnownSlots();
|
|
7890
|
-
}
|
|
7891
|
-
valueKindOf(action) {
|
|
7892
|
-
return this.ctxParams(action).value?.kind ?? 'item';
|
|
7893
|
-
}
|
|
7894
|
-
setValueKind(action, kind) {
|
|
7895
|
-
const params = this.ctxParams(action);
|
|
7896
|
-
params.value = kind === 'source'
|
|
7897
|
-
? { kind: 'source', source: this.sourceOptions[0]?.value ?? '', path: '' }
|
|
7898
|
-
: { kind: 'item' };
|
|
7899
|
-
}
|
|
7900
|
-
valueSourceOf(action) {
|
|
7901
|
-
const value = this.ctxParams(action).value;
|
|
7902
|
-
return value?.source ?? '';
|
|
7903
|
-
}
|
|
7904
|
-
setValueSource(action, source) {
|
|
7905
|
-
const params = this.ctxParams(action);
|
|
7906
|
-
params.value = { kind: 'source', source, path: this.valuePathOf(action) };
|
|
7907
|
-
}
|
|
7908
|
-
valuePathOf(action) {
|
|
7909
|
-
const value = this.ctxParams(action).value;
|
|
7910
|
-
return value?.path ?? '';
|
|
7911
|
-
}
|
|
7912
|
-
setValuePath(action, path) {
|
|
7913
|
-
const params = this.ctxParams(action);
|
|
7914
|
-
params.value = { kind: 'source', source: this.valueSourceOf(action), path };
|
|
7915
|
-
}
|
|
7916
|
-
/**
|
|
7917
|
-
* "Keep after leave" — ON means the slot survives mouseleave (stored as `noReverse`).
|
|
7918
|
-
* Defaults to ON: a repeater row that cleared itself on leave would blink on every row-to-row
|
|
7919
|
-
* move. Turning it OFF is the list-container behaviour (leave clears).
|
|
7920
|
-
*/
|
|
7921
|
-
keepAfterLeave(action) {
|
|
7922
|
-
return this.ctxParams(action).noReverse !== false;
|
|
7923
|
-
}
|
|
7924
|
-
setKeepAfterLeave(action, keep) {
|
|
7925
|
-
this.ctxParams(action).noReverse = keep;
|
|
7926
|
-
}
|
|
7927
|
-
refreshKnownSlots() {
|
|
7928
|
-
const slots = new Set();
|
|
7929
|
-
for (const [node] of this.overlayService.getAllNodeEntries()) {
|
|
7930
|
-
for (const ix of node.interactions ?? []) {
|
|
7931
|
-
for (const a of [...(ix.actions ?? []), ...(ix.reverseActions ?? [])]) {
|
|
7932
|
-
const slot = a.params?.slot;
|
|
7933
|
-
if (slot)
|
|
7934
|
-
slots.add(slot);
|
|
7935
|
-
}
|
|
7936
|
-
}
|
|
7937
|
-
}
|
|
7938
|
-
this.knownSlots = [...slots].sort();
|
|
7939
|
-
this.sourceOptions = this.dsRegistry.getAliasOptions?.() ?? [];
|
|
7940
|
-
}
|
|
7941
|
-
// ── Toggle (reverse actions) ─────────────────────────────
|
|
7942
|
-
hasToggle(interaction) {
|
|
7943
|
-
return !!interaction.reverseActions?.length;
|
|
7944
|
-
}
|
|
7945
|
-
enableToggle(interaction) {
|
|
7946
|
-
// Auto-populate reverse actions as the logical inverse of primary actions.
|
|
7947
|
-
interaction.reverseActions = interaction.actions.map(a => ({
|
|
7948
|
-
...a,
|
|
7949
|
-
type: (INVERSE_ACTION[a.type] ?? a.type),
|
|
7950
|
-
}));
|
|
7951
|
-
}
|
|
7952
|
-
disableToggle(interaction) {
|
|
7953
|
-
interaction.reverseActions = undefined;
|
|
7954
|
-
}
|
|
7955
|
-
addReverseAction(interaction) {
|
|
7956
|
-
if (!interaction.reverseActions)
|
|
7957
|
-
interaction.reverseActions = [];
|
|
7958
|
-
interaction.reverseActions.push({
|
|
7959
|
-
type: 'fadeOut',
|
|
7960
|
-
target: 'self',
|
|
7961
|
-
duration: 600,
|
|
7962
|
-
delay: 0,
|
|
7963
|
-
easing: 'ease-out',
|
|
7964
|
-
once: false,
|
|
7965
|
-
});
|
|
7966
|
-
}
|
|
7967
|
-
removeReverseAction(interaction, index) {
|
|
7968
|
-
if (!interaction.reverseActions)
|
|
7969
|
-
return;
|
|
7970
|
-
interaction.reverseActions.splice(index, 1);
|
|
7971
|
-
if (interaction.reverseActions.length === 0) {
|
|
7972
|
-
interaction.reverseActions = undefined;
|
|
7973
|
-
}
|
|
7974
|
-
}
|
|
7975
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: InteractionsPanelComponent, deps: [{ token: OverlayService }, { token: ComponentRegistryService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7976
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: InteractionsPanelComponent, isStandalone: false, selector: "app-interactions-panel", inputs: { node: "node" }, ngImport: i0, template: "<div class=\"interactions-panel\">\n <!-- No element selected -->\n <div *ngIf=\"!node\" class=\"section-empty\">\n Select an element to manage interactions\n </div>\n\n <ng-container *ngIf=\"node\">\n <!-- Interaction cards -->\n <div *ngFor=\"let ix of interactions\" class=\"ix-card\" [class.is-expanded]=\"expandedId === ix.id\">\n <!-- Summary row -->\n <div class=\"ix-card-header\" (click)=\"toggleExpand(ix.id)\">\n <i [class]=\"getTriggerIcon(ix.trigger)\" class=\"ix-trigger-icon\"></i>\n <div class=\"ix-card-summary\">\n <span class=\"ix-trigger-label\">{{ getTriggerLabel(ix.trigger) }}</span>\n <span class=\"ix-arrow\">\u2192</span>\n <span class=\"ix-action-label\">{{ getActionsSummary(ix) }}</span>\n </div>\n <button class=\"ix-remove-btn\" title=\"Remove\" (click)=\"removeInteraction(ix.id); $event.stopPropagation()\">\n <i class=\"ph-thin ph-x\"></i>\n </button>\n </div>\n\n <!-- Expanded detail -->\n <div *ngIf=\"expandedId === ix.id\" class=\"ix-card-body\">\n <div class=\"ix-field\">\n <label>Trigger</label>\n <select [(ngModel)]=\"ix.trigger\">\n <option *ngFor=\"let t of triggerOptions\" [value]=\"t.value\">{{ t.label }}</option>\n </select>\n </div>\n\n <div *ngFor=\"let action of ix.actions; let ai = index\" class=\"ix-action-row\">\n <div class=\"ix-action-header\">\n <span class=\"ix-action-index\">\n Target {{ ai + 1 }}\n <span *ngIf=\"ix.actions.length > 1\" class=\"ix-action-target-badge\">\n \u2192 {{ getTargetLabel(action.target) }}\n </span>\n </span>\n <button *ngIf=\"ix.actions.length > 1\"\n class=\"ix-remove-btn small\"\n title=\"Remove action\"\n (click)=\"removeAction(ix, ai)\">\n <i class=\"ph-thin ph-x\"></i>\n </button>\n </div>\n\n <div class=\"ix-field\">\n <label>Type</label>\n <select [(ngModel)]=\"action.type\">\n <option *ngFor=\"let a of actionTypeOptions\" [value]=\"a.value\">{{ a.label }}</option>\n </select>\n </div>\n\n <!-- Context actions: slot + what to write. Not an animation, so duration/easing\n below are irrelevant to them. -->\n <ng-container *ngIf=\"isContextAction(action)\">\n <div class=\"ix-field\">\n <label>Slot</label>\n <input type=\"text\"\n [ngModel]=\"slotOf(action)\"\n (ngModelChange)=\"setSlot(action, $event)\"\n list=\"ix-slot-options\"\n placeholder=\"currentItem\">\n <datalist id=\"ix-slot-options\">\n <option *ngFor=\"let s of knownSlots\" [value]=\"s\"></option>\n </datalist>\n <span class=\"ix-target-hint\">\n Elements bind to it as <strong>{{ slotOf(action) || 'slot' }}.field</strong>\n </span>\n </div>\n\n <ng-container *ngIf=\"action.type === 'setContext'\">\n <div class=\"ix-field\">\n <label>Value</label>\n <select [ngModel]=\"valueKindOf(action)\" (ngModelChange)=\"setValueKind(action, $event)\">\n <option value=\"item\">This item \u2014 the row being hovered</option>\n <option value=\"source\">Pick a value\u2026</option>\n </select>\n </div>\n\n <div class=\"ix-field-row\" *ngIf=\"valueKindOf(action) === 'source'\">\n <div class=\"ix-field\">\n <label>Holds an item from</label>\n <p-select\n size=\"small\"\n [ngModel]=\"valueSourceOf(action)\"\n (ngModelChange)=\"setValueSource(action, $event)\"\n [options]=\"sourceOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n appendTo=\"body\">\n </p-select>\n </div>\n <div class=\"ix-field\">\n <label>Path</label>\n <input type=\"text\"\n [ngModel]=\"valuePathOf(action)\"\n (ngModelChange)=\"setValuePath(action, $event)\"\n placeholder=\"(whole item)\">\n </div>\n </div>\n\n <div class=\"ix-field ix-field-inline\">\n <label>\n <input type=\"checkbox\"\n [ngModel]=\"keepAfterLeave(action)\"\n (ngModelChange)=\"setKeepAfterLeave(action, $event)\">\n Keep after leave\n </label>\n <span class=\"ix-target-hint\">\n On for rows in a list \u2014 otherwise moving between rows blinks.\n </span>\n </div>\n </ng-container>\n </ng-container>\n\n <div class=\"ix-field\" *ngIf=\"!isContextAction(action)\">\n <label>Target</label>\n <p-select\n size=\"small\"\n [(ngModel)]=\"action.target\"\n [options]=\"nodeOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [filter]=\"true\"\n filterPlaceholder=\"Search\u2026\"\n styleClass=\"ix-target-select\"\n appendTo=\"body\">\n </p-select>\n <span *ngIf=\"nodeOptions.length === 1\" class=\"ix-target-hint\">\n Name elements in the Layers panel to target them\n </span>\n </div>\n\n <div class=\"ix-field-row\">\n <div class=\"ix-field\">\n <label>Duration (ms)</label>\n <input type=\"number\" [(ngModel)]=\"action.duration\" min=\"0\" step=\"50\">\n </div>\n <div class=\"ix-field\">\n <label>Delay (ms)</label>\n <input type=\"number\" [(ngModel)]=\"action.delay\" min=\"0\" step=\"50\">\n </div>\n </div>\n\n <div class=\"ix-field\">\n <label>Easing</label>\n <select [(ngModel)]=\"action.easing\">\n <option *ngFor=\"let e of easingOptions\" [value]=\"e\">{{ e }}</option>\n </select>\n </div>\n\n <div class=\"ix-field ix-checkbox-field\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"action.once\">\n Play once\n </label>\n </div>\n </div>\n\n <button class=\"ix-add-action-btn\" (click)=\"addAction(ix)\">\n <i class=\"ph-thin ph-plus\"></i> Add target\n </button>\n\n <!-- Toggle section (click only) -->\n <ng-container *ngIf=\"ix.trigger === 'click'\">\n <div class=\"ix-toggle-header\">\n <span class=\"ix-toggle-label\">\n <i class=\"ph-thin ph-arrows-clockwise\"></i> 2nd click\n </span>\n <label class=\"ix-toggle-switch\">\n <input type=\"checkbox\"\n [checked]=\"hasToggle(ix)\"\n (change)=\"hasToggle(ix) ? disableToggle(ix) : enableToggle(ix)\">\n <span class=\"ix-toggle-track\"></span>\n </label>\n </div>\n\n <ng-container *ngIf=\"hasToggle(ix)\">\n <div *ngFor=\"let ra of ix.reverseActions; let ri = index\" class=\"ix-action-row ix-action-row--reverse\">\n <div class=\"ix-action-header\">\n <span class=\"ix-action-index\">\n Target {{ ri + 1 }}\n <span *ngIf=\"(ix.reverseActions?.length ?? 0) > 1\" class=\"ix-action-target-badge\">\n \u2192 {{ getTargetLabel(ra.target) }}\n </span>\n </span>\n <button *ngIf=\"(ix.reverseActions?.length ?? 0) > 1\"\n class=\"ix-remove-btn small\"\n title=\"Remove\"\n (click)=\"removeReverseAction(ix, ri)\">\n <i class=\"ph-thin ph-x\"></i>\n </button>\n </div>\n\n <div class=\"ix-field\">\n <label>Type</label>\n <select [(ngModel)]=\"ra.type\">\n <option *ngFor=\"let a of actionTypeOptions\" [value]=\"a.value\">{{ a.label }}</option>\n </select>\n </div>\n\n <div class=\"ix-field\">\n <label>Target</label>\n <p-select\n size=\"small\"\n [(ngModel)]=\"ra.target\"\n [options]=\"nodeOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [filter]=\"true\"\n filterPlaceholder=\"Search\u2026\"\n styleClass=\"ix-target-select\"\n appendTo=\"body\">\n </p-select>\n </div>\n\n <div class=\"ix-field-row\">\n <div class=\"ix-field\">\n <label>Duration (ms)</label>\n <input type=\"number\" [(ngModel)]=\"ra.duration\" min=\"0\" step=\"50\">\n </div>\n <div class=\"ix-field\">\n <label>Delay (ms)</label>\n <input type=\"number\" [(ngModel)]=\"ra.delay\" min=\"0\" step=\"50\">\n </div>\n </div>\n\n <div class=\"ix-field\">\n <label>Easing</label>\n <select [(ngModel)]=\"ra.easing\">\n <option *ngFor=\"let e of easingOptions\" [value]=\"e\">{{ e }}</option>\n </select>\n </div>\n </div>\n\n <button class=\"ix-add-action-btn\" (click)=\"addReverseAction(ix)\">\n <i class=\"ph-thin ph-plus\"></i> Add target\n </button>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <!-- Add interaction form -->\n <div *ngIf=\"isAdding\" class=\"ix-add-form\">\n <div class=\"ix-field\">\n <label>Trigger</label>\n <select [(ngModel)]=\"newTrigger\">\n <option *ngFor=\"let t of triggerOptions\" [value]=\"t.value\">{{ t.label }}</option>\n </select>\n </div>\n <div class=\"ix-field\">\n <label>Action</label>\n <select [(ngModel)]=\"newActionType\">\n <option *ngFor=\"let a of actionTypeOptions\" [value]=\"a.value\">{{ a.label }}</option>\n </select>\n </div>\n <!-- Context actions write STATE: they need a slot, not a target/duration/easing. -->\n <ng-container *ngIf=\"isNewContextAction\">\n <div class=\"ix-field\">\n <label>Slot</label>\n <input type=\"text\" [(ngModel)]=\"newSlot\" list=\"ix-slot-options-add\" placeholder=\"currentItem\">\n <datalist id=\"ix-slot-options-add\">\n <option *ngFor=\"let s of knownSlots\" [value]=\"s\"></option>\n </datalist>\n <span class=\"ix-target-hint\">\n Elements bind to it as <strong>{{ newSlot || 'slot' }}.field</strong>\n </span>\n </div>\n <ng-container *ngIf=\"newActionType === 'setContext'\">\n <div class=\"ix-field\">\n <label>Value</label>\n <select [(ngModel)]=\"newValueKind\">\n <option value=\"item\">This item \u2014 the row being hovered</option>\n <option value=\"source\">Pick a value\u2026</option>\n </select>\n </div>\n <div class=\"ix-field-row\" *ngIf=\"newValueKind === 'source'\">\n <div class=\"ix-field\">\n <label>Holds an item from</label>\n <p-select\n size=\"small\"\n [(ngModel)]=\"newValueSource\"\n [options]=\"sourceOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n appendTo=\"body\">\n </p-select>\n </div>\n <div class=\"ix-field\">\n <label>Path</label>\n <input type=\"text\" [(ngModel)]=\"newValuePath\" placeholder=\"(whole item)\">\n </div>\n </div>\n <div class=\"ix-field ix-checkbox-field\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"newKeepAfterLeave\">\n Keep after leave\n </label>\n <span class=\"ix-target-hint\">\n On for rows in a list \u2014 otherwise moving between rows blinks.\n </span>\n </div>\n </ng-container>\n </ng-container>\n\n <div class=\"ix-field\" *ngIf=\"!isNewContextAction\">\n <label>Target</label>\n <p-select\n size=\"small\"\n [(ngModel)]=\"newTarget\"\n [options]=\"nodeOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [filter]=\"true\"\n filterPlaceholder=\"Search\u2026\"\n styleClass=\"ix-target-select\"\n appendTo=\"body\">\n </p-select>\n <span *ngIf=\"nodeOptions.length === 1\" class=\"ix-target-hint\">\n Name elements in the Layers panel to target them\n </span>\n </div>\n <div *ngIf=\"isSwiperGoToSlide\" class=\"ix-field\">\n <label>Slide index (0-based)</label>\n <input type=\"number\" [(ngModel)]=\"newSlideIndex\" min=\"0\" step=\"1\">\n </div>\n <div *ngIf=\"!isSwiperAction && !isNewContextAction\" class=\"ix-field-row\">\n <div class=\"ix-field\">\n <label>Duration (ms)</label>\n <input type=\"number\" [(ngModel)]=\"newDuration\" min=\"0\" step=\"50\">\n </div>\n <div class=\"ix-field\">\n <label>Delay (ms)</label>\n <input type=\"number\" [(ngModel)]=\"newDelay\" min=\"0\" step=\"50\">\n </div>\n </div>\n <div class=\"ix-field\" *ngIf=\"!isNewContextAction\">\n <label>Easing</label>\n <select [(ngModel)]=\"newEasing\">\n <option *ngFor=\"let e of easingOptions\" [value]=\"e\">{{ e }}</option>\n </select>\n </div>\n <div class=\"ix-field ix-checkbox-field\" *ngIf=\"!isNewContextAction\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"newOnce\">\n Play once\n </label>\n </div>\n <div class=\"ix-form-actions\">\n <button class=\"ix-btn ix-btn--secondary\" (click)=\"cancelAdd()\">Cancel</button>\n <button class=\"ix-btn ix-btn--primary\" (click)=\"confirmAdd()\">Add</button>\n </div>\n </div>\n\n <!-- Add button -->\n <button *ngIf=\"!isAdding\" class=\"ix-add-btn\" (click)=\"startAdd()\">\n <i class=\"ph-thin ph-plus\"></i> Add interaction\n </button>\n </ng-container>\n</div>\n", styles: [".interactions-panel{padding:.5rem}.section-empty{color:var(--p-text-muted-color);font-size:12px;text-align:center;padding:1rem}.ix-card{border:1px solid var(--p-surface-200);border-radius:8px;margin-bottom:6px;overflow:hidden;transition:border-color .15s}.ix-card.is-expanded{border-color:#cb9090}.ix-card-header{display:flex;align-items:center;gap:8px;padding:8px 10px;cursor:pointer;font-size:12px;transition:background .12s}.ix-card-header:hover{background:var(--p-surface-50)}.ix-trigger-icon{font-size:14px;color:#cb9090;flex-shrink:0}.ix-card-summary{flex:1;min-width:0;display:flex;align-items:center;gap:4px;overflow:hidden}.ix-trigger-label{font-weight:500;white-space:nowrap}.ix-arrow{color:var(--p-text-muted-color);flex-shrink:0}.ix-action-label{color:var(--p-text-muted-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ix-remove-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:4px;flex-shrink:0;font-size:12px}.ix-remove-btn:hover{background:#dc26261a;color:#dc2626}.ix-remove-btn.small{width:16px;height:16px;font-size:10px}.ix-card-body{padding:0 10px 10px;border-top:1px solid var(--p-surface-100)}.ix-action-row{padding:8px 0;border-bottom:1px dashed var(--p-surface-100)}.ix-action-row:last-of-type{border-bottom:none}.ix-action-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}.ix-action-index{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:600;color:var(--p-text-muted-color);text-transform:uppercase;letter-spacing:.03em}.ix-action-target-badge{font-weight:400;text-transform:none;letter-spacing:0;color:#cb9090}.ix-action-row--reverse{background:#638fcb0a;border-radius:6px;padding:6px 8px;border-bottom:none!important;margin-bottom:4px}.ix-toggle-header{display:flex;align-items:center;justify-content:space-between;padding:8px 0 4px;border-top:1px solid var(--p-surface-200);margin-top:4px}.ix-toggle-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color);display:flex;align-items:center;gap:5px}.ix-toggle-label i{font-size:12px}.ix-toggle-switch{position:relative;display:inline-flex;align-items:center;cursor:pointer}.ix-toggle-switch input[type=checkbox]{position:absolute;opacity:0;width:0;height:0}.ix-toggle-track{display:block;width:28px;height:16px;border-radius:8px;background:var(--p-surface-300);transition:background .2s;position:relative}.ix-toggle-track:after{content:\"\";position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:#fff;transition:transform .2s}.ix-toggle-switch input:checked~.ix-toggle-track{background:#638fcb}.ix-toggle-switch input:checked~.ix-toggle-track:after{transform:translate(12px)}.ix-add-action-btn{display:flex;align-items:center;gap:4px;margin-top:8px;padding:4px 8px;border:1px dashed var(--p-surface-300);background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:6px;font-size:11px;transition:border-color .15s,color .15s}.ix-add-action-btn:hover{border-color:#cb9090;color:#cb9090}.ix-field{margin-bottom:8px}.ix-field label{display:block;font-size:11px;color:var(--p-text-muted-color);margin-bottom:3px;font-weight:500}.ix-field select,.ix-field input[type=number]{width:100%;padding:5px 8px;border:1px solid var(--p-surface-200);border-radius:6px;font-size:12px;background:var(--p-surface-0);color:var(--p-text-color);outline:none;transition:border-color .15s}.ix-field select:focus,.ix-field input[type=number]:focus{border-color:#cb9090}.ix-field-row{display:flex;gap:8px}.ix-field-row .ix-field{flex:1}.ix-checkbox-field label{display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;color:var(--p-text-color)}.ix-checkbox-field input[type=checkbox]{accent-color:#cb9090}.ix-add-form{border:1px solid var(--p-surface-200);border-radius:8px;padding:10px;margin-bottom:6px}.ix-form-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:4px}.ix-btn{padding:5px 12px;border:none;border-radius:6px;font-size:12px;cursor:pointer;transition:background .12s,color .12s}.ix-btn--primary{background:#cb9090;color:#fff}.ix-btn--primary:hover{background:#be7474}.ix-btn--secondary{background:transparent;color:var(--p-text-muted-color)}.ix-btn--secondary:hover{background:var(--p-surface-100);color:var(--p-text-color)}.ix-add-btn{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:8px;border:1px dashed var(--p-surface-300);background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:8px;font-size:12px;transition:border-color .15s,color .15s}.ix-add-btn:hover{border-color:#cb9090;color:#cb9090}.ix-target-hint{display:block;font-size:10px;color:var(--p-text-muted-color);margin-top:4px;font-style:italic}::ng-deep .ix-target-select{width:100%}::ng-deep .ix-target-select .p-select-label{font-size:12px;padding:4px 8px}::ng-deep .ix-target-select .p-select{border-radius:6px}\n"], dependencies: [{ 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: i4$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4$1.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: i4$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i4$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }] }); }
|
|
7977
|
-
}
|
|
7978
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: InteractionsPanelComponent, decorators: [{
|
|
7979
|
-
type: Component,
|
|
7980
|
-
args: [{ selector: 'app-interactions-panel', standalone: false, template: "<div class=\"interactions-panel\">\n <!-- No element selected -->\n <div *ngIf=\"!node\" class=\"section-empty\">\n Select an element to manage interactions\n </div>\n\n <ng-container *ngIf=\"node\">\n <!-- Interaction cards -->\n <div *ngFor=\"let ix of interactions\" class=\"ix-card\" [class.is-expanded]=\"expandedId === ix.id\">\n <!-- Summary row -->\n <div class=\"ix-card-header\" (click)=\"toggleExpand(ix.id)\">\n <i [class]=\"getTriggerIcon(ix.trigger)\" class=\"ix-trigger-icon\"></i>\n <div class=\"ix-card-summary\">\n <span class=\"ix-trigger-label\">{{ getTriggerLabel(ix.trigger) }}</span>\n <span class=\"ix-arrow\">\u2192</span>\n <span class=\"ix-action-label\">{{ getActionsSummary(ix) }}</span>\n </div>\n <button class=\"ix-remove-btn\" title=\"Remove\" (click)=\"removeInteraction(ix.id); $event.stopPropagation()\">\n <i class=\"ph-thin ph-x\"></i>\n </button>\n </div>\n\n <!-- Expanded detail -->\n <div *ngIf=\"expandedId === ix.id\" class=\"ix-card-body\">\n <div class=\"ix-field\">\n <label>Trigger</label>\n <select [(ngModel)]=\"ix.trigger\">\n <option *ngFor=\"let t of triggerOptions\" [value]=\"t.value\">{{ t.label }}</option>\n </select>\n </div>\n\n <div *ngFor=\"let action of ix.actions; let ai = index\" class=\"ix-action-row\">\n <div class=\"ix-action-header\">\n <span class=\"ix-action-index\">\n Target {{ ai + 1 }}\n <span *ngIf=\"ix.actions.length > 1\" class=\"ix-action-target-badge\">\n \u2192 {{ getTargetLabel(action.target) }}\n </span>\n </span>\n <button *ngIf=\"ix.actions.length > 1\"\n class=\"ix-remove-btn small\"\n title=\"Remove action\"\n (click)=\"removeAction(ix, ai)\">\n <i class=\"ph-thin ph-x\"></i>\n </button>\n </div>\n\n <div class=\"ix-field\">\n <label>Type</label>\n <select [(ngModel)]=\"action.type\">\n <option *ngFor=\"let a of actionTypeOptions\" [value]=\"a.value\">{{ a.label }}</option>\n </select>\n </div>\n\n <!-- Context actions: slot + what to write. Not an animation, so duration/easing\n below are irrelevant to them. -->\n <ng-container *ngIf=\"isContextAction(action)\">\n <div class=\"ix-field\">\n <label>Slot</label>\n <input type=\"text\"\n [ngModel]=\"slotOf(action)\"\n (ngModelChange)=\"setSlot(action, $event)\"\n list=\"ix-slot-options\"\n placeholder=\"currentItem\">\n <datalist id=\"ix-slot-options\">\n <option *ngFor=\"let s of knownSlots\" [value]=\"s\"></option>\n </datalist>\n <span class=\"ix-target-hint\">\n Elements bind to it as <strong>{{ slotOf(action) || 'slot' }}.field</strong>\n </span>\n </div>\n\n <ng-container *ngIf=\"action.type === 'setContext'\">\n <div class=\"ix-field\">\n <label>Value</label>\n <select [ngModel]=\"valueKindOf(action)\" (ngModelChange)=\"setValueKind(action, $event)\">\n <option value=\"item\">This item \u2014 the row being hovered</option>\n <option value=\"source\">Pick a value\u2026</option>\n </select>\n </div>\n\n <div class=\"ix-field-row\" *ngIf=\"valueKindOf(action) === 'source'\">\n <div class=\"ix-field\">\n <label>Holds an item from</label>\n <p-select\n size=\"small\"\n [ngModel]=\"valueSourceOf(action)\"\n (ngModelChange)=\"setValueSource(action, $event)\"\n [options]=\"sourceOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n appendTo=\"body\">\n </p-select>\n </div>\n <div class=\"ix-field\">\n <label>Path</label>\n <input type=\"text\"\n [ngModel]=\"valuePathOf(action)\"\n (ngModelChange)=\"setValuePath(action, $event)\"\n placeholder=\"(whole item)\">\n </div>\n </div>\n\n <div class=\"ix-field ix-field-inline\">\n <label>\n <input type=\"checkbox\"\n [ngModel]=\"keepAfterLeave(action)\"\n (ngModelChange)=\"setKeepAfterLeave(action, $event)\">\n Keep after leave\n </label>\n <span class=\"ix-target-hint\">\n On for rows in a list \u2014 otherwise moving between rows blinks.\n </span>\n </div>\n </ng-container>\n </ng-container>\n\n <div class=\"ix-field\" *ngIf=\"!isContextAction(action)\">\n <label>Target</label>\n <p-select\n size=\"small\"\n [(ngModel)]=\"action.target\"\n [options]=\"nodeOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [filter]=\"true\"\n filterPlaceholder=\"Search\u2026\"\n styleClass=\"ix-target-select\"\n appendTo=\"body\">\n </p-select>\n <span *ngIf=\"nodeOptions.length === 1\" class=\"ix-target-hint\">\n Name elements in the Layers panel to target them\n </span>\n </div>\n\n <div class=\"ix-field-row\">\n <div class=\"ix-field\">\n <label>Duration (ms)</label>\n <input type=\"number\" [(ngModel)]=\"action.duration\" min=\"0\" step=\"50\">\n </div>\n <div class=\"ix-field\">\n <label>Delay (ms)</label>\n <input type=\"number\" [(ngModel)]=\"action.delay\" min=\"0\" step=\"50\">\n </div>\n </div>\n\n <div class=\"ix-field\">\n <label>Easing</label>\n <select [(ngModel)]=\"action.easing\">\n <option *ngFor=\"let e of easingOptions\" [value]=\"e\">{{ e }}</option>\n </select>\n </div>\n\n <div class=\"ix-field ix-checkbox-field\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"action.once\">\n Play once\n </label>\n </div>\n </div>\n\n <button class=\"ix-add-action-btn\" (click)=\"addAction(ix)\">\n <i class=\"ph-thin ph-plus\"></i> Add target\n </button>\n\n <!-- Toggle section (click only) -->\n <ng-container *ngIf=\"ix.trigger === 'click'\">\n <div class=\"ix-toggle-header\">\n <span class=\"ix-toggle-label\">\n <i class=\"ph-thin ph-arrows-clockwise\"></i> 2nd click\n </span>\n <label class=\"ix-toggle-switch\">\n <input type=\"checkbox\"\n [checked]=\"hasToggle(ix)\"\n (change)=\"hasToggle(ix) ? disableToggle(ix) : enableToggle(ix)\">\n <span class=\"ix-toggle-track\"></span>\n </label>\n </div>\n\n <ng-container *ngIf=\"hasToggle(ix)\">\n <div *ngFor=\"let ra of ix.reverseActions; let ri = index\" class=\"ix-action-row ix-action-row--reverse\">\n <div class=\"ix-action-header\">\n <span class=\"ix-action-index\">\n Target {{ ri + 1 }}\n <span *ngIf=\"(ix.reverseActions?.length ?? 0) > 1\" class=\"ix-action-target-badge\">\n \u2192 {{ getTargetLabel(ra.target) }}\n </span>\n </span>\n <button *ngIf=\"(ix.reverseActions?.length ?? 0) > 1\"\n class=\"ix-remove-btn small\"\n title=\"Remove\"\n (click)=\"removeReverseAction(ix, ri)\">\n <i class=\"ph-thin ph-x\"></i>\n </button>\n </div>\n\n <div class=\"ix-field\">\n <label>Type</label>\n <select [(ngModel)]=\"ra.type\">\n <option *ngFor=\"let a of actionTypeOptions\" [value]=\"a.value\">{{ a.label }}</option>\n </select>\n </div>\n\n <div class=\"ix-field\">\n <label>Target</label>\n <p-select\n size=\"small\"\n [(ngModel)]=\"ra.target\"\n [options]=\"nodeOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [filter]=\"true\"\n filterPlaceholder=\"Search\u2026\"\n styleClass=\"ix-target-select\"\n appendTo=\"body\">\n </p-select>\n </div>\n\n <div class=\"ix-field-row\">\n <div class=\"ix-field\">\n <label>Duration (ms)</label>\n <input type=\"number\" [(ngModel)]=\"ra.duration\" min=\"0\" step=\"50\">\n </div>\n <div class=\"ix-field\">\n <label>Delay (ms)</label>\n <input type=\"number\" [(ngModel)]=\"ra.delay\" min=\"0\" step=\"50\">\n </div>\n </div>\n\n <div class=\"ix-field\">\n <label>Easing</label>\n <select [(ngModel)]=\"ra.easing\">\n <option *ngFor=\"let e of easingOptions\" [value]=\"e\">{{ e }}</option>\n </select>\n </div>\n </div>\n\n <button class=\"ix-add-action-btn\" (click)=\"addReverseAction(ix)\">\n <i class=\"ph-thin ph-plus\"></i> Add target\n </button>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <!-- Add interaction form -->\n <div *ngIf=\"isAdding\" class=\"ix-add-form\">\n <div class=\"ix-field\">\n <label>Trigger</label>\n <select [(ngModel)]=\"newTrigger\">\n <option *ngFor=\"let t of triggerOptions\" [value]=\"t.value\">{{ t.label }}</option>\n </select>\n </div>\n <div class=\"ix-field\">\n <label>Action</label>\n <select [(ngModel)]=\"newActionType\">\n <option *ngFor=\"let a of actionTypeOptions\" [value]=\"a.value\">{{ a.label }}</option>\n </select>\n </div>\n <!-- Context actions write STATE: they need a slot, not a target/duration/easing. -->\n <ng-container *ngIf=\"isNewContextAction\">\n <div class=\"ix-field\">\n <label>Slot</label>\n <input type=\"text\" [(ngModel)]=\"newSlot\" list=\"ix-slot-options-add\" placeholder=\"currentItem\">\n <datalist id=\"ix-slot-options-add\">\n <option *ngFor=\"let s of knownSlots\" [value]=\"s\"></option>\n </datalist>\n <span class=\"ix-target-hint\">\n Elements bind to it as <strong>{{ newSlot || 'slot' }}.field</strong>\n </span>\n </div>\n <ng-container *ngIf=\"newActionType === 'setContext'\">\n <div class=\"ix-field\">\n <label>Value</label>\n <select [(ngModel)]=\"newValueKind\">\n <option value=\"item\">This item \u2014 the row being hovered</option>\n <option value=\"source\">Pick a value\u2026</option>\n </select>\n </div>\n <div class=\"ix-field-row\" *ngIf=\"newValueKind === 'source'\">\n <div class=\"ix-field\">\n <label>Holds an item from</label>\n <p-select\n size=\"small\"\n [(ngModel)]=\"newValueSource\"\n [options]=\"sourceOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n appendTo=\"body\">\n </p-select>\n </div>\n <div class=\"ix-field\">\n <label>Path</label>\n <input type=\"text\" [(ngModel)]=\"newValuePath\" placeholder=\"(whole item)\">\n </div>\n </div>\n <div class=\"ix-field ix-checkbox-field\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"newKeepAfterLeave\">\n Keep after leave\n </label>\n <span class=\"ix-target-hint\">\n On for rows in a list \u2014 otherwise moving between rows blinks.\n </span>\n </div>\n </ng-container>\n </ng-container>\n\n <div class=\"ix-field\" *ngIf=\"!isNewContextAction\">\n <label>Target</label>\n <p-select\n size=\"small\"\n [(ngModel)]=\"newTarget\"\n [options]=\"nodeOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [filter]=\"true\"\n filterPlaceholder=\"Search\u2026\"\n styleClass=\"ix-target-select\"\n appendTo=\"body\">\n </p-select>\n <span *ngIf=\"nodeOptions.length === 1\" class=\"ix-target-hint\">\n Name elements in the Layers panel to target them\n </span>\n </div>\n <div *ngIf=\"isSwiperGoToSlide\" class=\"ix-field\">\n <label>Slide index (0-based)</label>\n <input type=\"number\" [(ngModel)]=\"newSlideIndex\" min=\"0\" step=\"1\">\n </div>\n <div *ngIf=\"!isSwiperAction && !isNewContextAction\" class=\"ix-field-row\">\n <div class=\"ix-field\">\n <label>Duration (ms)</label>\n <input type=\"number\" [(ngModel)]=\"newDuration\" min=\"0\" step=\"50\">\n </div>\n <div class=\"ix-field\">\n <label>Delay (ms)</label>\n <input type=\"number\" [(ngModel)]=\"newDelay\" min=\"0\" step=\"50\">\n </div>\n </div>\n <div class=\"ix-field\" *ngIf=\"!isNewContextAction\">\n <label>Easing</label>\n <select [(ngModel)]=\"newEasing\">\n <option *ngFor=\"let e of easingOptions\" [value]=\"e\">{{ e }}</option>\n </select>\n </div>\n <div class=\"ix-field ix-checkbox-field\" *ngIf=\"!isNewContextAction\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"newOnce\">\n Play once\n </label>\n </div>\n <div class=\"ix-form-actions\">\n <button class=\"ix-btn ix-btn--secondary\" (click)=\"cancelAdd()\">Cancel</button>\n <button class=\"ix-btn ix-btn--primary\" (click)=\"confirmAdd()\">Add</button>\n </div>\n </div>\n\n <!-- Add button -->\n <button *ngIf=\"!isAdding\" class=\"ix-add-btn\" (click)=\"startAdd()\">\n <i class=\"ph-thin ph-plus\"></i> Add interaction\n </button>\n </ng-container>\n</div>\n", styles: [".interactions-panel{padding:.5rem}.section-empty{color:var(--p-text-muted-color);font-size:12px;text-align:center;padding:1rem}.ix-card{border:1px solid var(--p-surface-200);border-radius:8px;margin-bottom:6px;overflow:hidden;transition:border-color .15s}.ix-card.is-expanded{border-color:#cb9090}.ix-card-header{display:flex;align-items:center;gap:8px;padding:8px 10px;cursor:pointer;font-size:12px;transition:background .12s}.ix-card-header:hover{background:var(--p-surface-50)}.ix-trigger-icon{font-size:14px;color:#cb9090;flex-shrink:0}.ix-card-summary{flex:1;min-width:0;display:flex;align-items:center;gap:4px;overflow:hidden}.ix-trigger-label{font-weight:500;white-space:nowrap}.ix-arrow{color:var(--p-text-muted-color);flex-shrink:0}.ix-action-label{color:var(--p-text-muted-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ix-remove-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:4px;flex-shrink:0;font-size:12px}.ix-remove-btn:hover{background:#dc26261a;color:#dc2626}.ix-remove-btn.small{width:16px;height:16px;font-size:10px}.ix-card-body{padding:0 10px 10px;border-top:1px solid var(--p-surface-100)}.ix-action-row{padding:8px 0;border-bottom:1px dashed var(--p-surface-100)}.ix-action-row:last-of-type{border-bottom:none}.ix-action-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}.ix-action-index{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:600;color:var(--p-text-muted-color);text-transform:uppercase;letter-spacing:.03em}.ix-action-target-badge{font-weight:400;text-transform:none;letter-spacing:0;color:#cb9090}.ix-action-row--reverse{background:#638fcb0a;border-radius:6px;padding:6px 8px;border-bottom:none!important;margin-bottom:4px}.ix-toggle-header{display:flex;align-items:center;justify-content:space-between;padding:8px 0 4px;border-top:1px solid var(--p-surface-200);margin-top:4px}.ix-toggle-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color);display:flex;align-items:center;gap:5px}.ix-toggle-label i{font-size:12px}.ix-toggle-switch{position:relative;display:inline-flex;align-items:center;cursor:pointer}.ix-toggle-switch input[type=checkbox]{position:absolute;opacity:0;width:0;height:0}.ix-toggle-track{display:block;width:28px;height:16px;border-radius:8px;background:var(--p-surface-300);transition:background .2s;position:relative}.ix-toggle-track:after{content:\"\";position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:#fff;transition:transform .2s}.ix-toggle-switch input:checked~.ix-toggle-track{background:#638fcb}.ix-toggle-switch input:checked~.ix-toggle-track:after{transform:translate(12px)}.ix-add-action-btn{display:flex;align-items:center;gap:4px;margin-top:8px;padding:4px 8px;border:1px dashed var(--p-surface-300);background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:6px;font-size:11px;transition:border-color .15s,color .15s}.ix-add-action-btn:hover{border-color:#cb9090;color:#cb9090}.ix-field{margin-bottom:8px}.ix-field label{display:block;font-size:11px;color:var(--p-text-muted-color);margin-bottom:3px;font-weight:500}.ix-field select,.ix-field input[type=number]{width:100%;padding:5px 8px;border:1px solid var(--p-surface-200);border-radius:6px;font-size:12px;background:var(--p-surface-0);color:var(--p-text-color);outline:none;transition:border-color .15s}.ix-field select:focus,.ix-field input[type=number]:focus{border-color:#cb9090}.ix-field-row{display:flex;gap:8px}.ix-field-row .ix-field{flex:1}.ix-checkbox-field label{display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;color:var(--p-text-color)}.ix-checkbox-field input[type=checkbox]{accent-color:#cb9090}.ix-add-form{border:1px solid var(--p-surface-200);border-radius:8px;padding:10px;margin-bottom:6px}.ix-form-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:4px}.ix-btn{padding:5px 12px;border:none;border-radius:6px;font-size:12px;cursor:pointer;transition:background .12s,color .12s}.ix-btn--primary{background:#cb9090;color:#fff}.ix-btn--primary:hover{background:#be7474}.ix-btn--secondary{background:transparent;color:var(--p-text-muted-color)}.ix-btn--secondary:hover{background:var(--p-surface-100);color:var(--p-text-color)}.ix-add-btn{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:8px;border:1px dashed var(--p-surface-300);background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:8px;font-size:12px;transition:border-color .15s,color .15s}.ix-add-btn:hover{border-color:#cb9090;color:#cb9090}.ix-target-hint{display:block;font-size:10px;color:var(--p-text-muted-color);margin-top:4px;font-style:italic}::ng-deep .ix-target-select{width:100%}::ng-deep .ix-target-select .p-select-label{font-size:12px;padding:4px 8px}::ng-deep .ix-target-select .p-select{border-radius:6px}\n"] }]
|
|
7981
|
-
}], ctorParameters: () => [{ type: OverlayService }, { type: ComponentRegistryService }], propDecorators: { node: [{
|
|
7982
|
-
type: Input
|
|
7983
|
-
}] } });
|
|
7984
|
-
|
|
7985
7671
|
const MAX_HISTORY = 50;
|
|
7986
7672
|
/**
|
|
7987
7673
|
* BuilderHistoryService — snapshot-based undo/redo for the page builder.
|
|
@@ -8048,7 +7734,6 @@ const DECLARATIONS = [
|
|
|
8048
7734
|
PanelComponent,
|
|
8049
7735
|
PanelChildComponent,
|
|
8050
7736
|
LayerTreeComponent,
|
|
8051
|
-
InteractionsPanelComponent,
|
|
8052
7737
|
];
|
|
8053
7738
|
class IoxBuilderModule {
|
|
8054
7739
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: IoxBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
@@ -8067,8 +7752,7 @@ class IoxBuilderModule {
|
|
|
8067
7752
|
SliderSlideComponent,
|
|
8068
7753
|
PanelComponent,
|
|
8069
7754
|
PanelChildComponent,
|
|
8070
|
-
LayerTreeComponent,
|
|
8071
|
-
InteractionsPanelComponent], imports: [CommonModule,
|
|
7755
|
+
LayerTreeComponent], imports: [CommonModule,
|
|
8072
7756
|
FormsModule,
|
|
8073
7757
|
DragDropModule,
|
|
8074
7758
|
AccordionModule,
|
|
@@ -8107,8 +7791,7 @@ class IoxBuilderModule {
|
|
|
8107
7791
|
SliderSlideComponent,
|
|
8108
7792
|
PanelComponent,
|
|
8109
7793
|
PanelChildComponent,
|
|
8110
|
-
LayerTreeComponent
|
|
8111
|
-
InteractionsPanelComponent] }); }
|
|
7794
|
+
LayerTreeComponent] }); }
|
|
8112
7795
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: IoxBuilderModule, providers: [
|
|
8113
7796
|
DragEngineService,
|
|
8114
7797
|
OverlayService,
|
|
@@ -8211,7 +7894,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
8211
7894
|
PanelComponent,
|
|
8212
7895
|
PanelChildComponent,
|
|
8213
7896
|
LayerTreeComponent,
|
|
8214
|
-
InteractionsPanelComponent,
|
|
8215
7897
|
],
|
|
8216
7898
|
schemas: [NO_ERRORS_SCHEMA],
|
|
8217
7899
|
}]
|
|
@@ -8780,5 +8462,5 @@ class BuilderSpacerComponentConfig extends ComponentConfig {
|
|
|
8780
8462
|
* Generated bundle index. Do not edit.
|
|
8781
8463
|
*/
|
|
8782
8464
|
|
|
8783
|
-
export { ACTION_TYPE_OPTIONS, BackgroundGroupStyleConfig, BorderGroupStyleConfig, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDataSourceService, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, DEVICE_OPTIONS, DataSourceRegistryService, DataSourceType, DeviceMode, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, EffectsGroupStyleConfig, GalleryConfig, GroupStyleConfig, INTERACTION_STATES, INVERSE_ACTION, IOX_DATA_SOURCE_ENDPOINTS, IOX_FONT_MANAGER, InteractionEngineService, InteractionPresetRegistryService,
|
|
8465
|
+
export { ACTION_TYPE_OPTIONS, BackgroundGroupStyleConfig, BorderGroupStyleConfig, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDataSourceService, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, DEVICE_OPTIONS, DataSourceRegistryService, DataSourceType, DeviceMode, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, EffectsGroupStyleConfig, GalleryConfig, GroupStyleConfig, INTERACTION_STATES, INVERSE_ACTION, IOX_DATA_SOURCE_ENDPOINTS, IOX_FONT_MANAGER, InteractionEngineService, InteractionPresetRegistryService, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PositionGroupStyleConfig, PresetRegistryService, RELATIONAL_STATES, RELATIONAL_STATE_BASE, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, SCREEN_WIDTH_OPTIONS, STRUCTURAL_STATES, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, SizeGroupStyleConfig, SliderContainerComponent, SliderContainerConfig, SliderSlideComponent, SliderSlideConfig, SpacingGroupStyleConfig, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, SymbolRegistryService, TARGETED_STATES, TARGETED_STATE_BASE, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, TypographyGroupStyleConfig, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, ViewportService, ZOOM_OPTIONS, arrayItemSchemaFields, arraySchemaFields, buildFullStyleTraits, buildPresetStyleTraits, defaultPageSettings, findNodeLocation, flattenSchemaFields, generateNodeId, resolveTraitControllerType, resolveTraitOptions, wrapNode };
|
|
8784
8466
|
//# sourceMappingURL=vectoriox-iox-builder.mjs.map
|