@vectoriox/iox-builder 1.4.65 → 1.4.68

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.
@@ -1,8 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, EventEmitter, Output, Input, Directive, ChangeDetectionStrategy, Component, ViewChild, Optional, Inject, InjectionToken, ContentChildren, NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
2
+ import { Injectable, inject, EventEmitter, Output, Input, Directive, ChangeDetectionStrategy, Component, ViewChild, Optional, Inject, InjectionToken, ContentChildren, NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
3
3
  import * as i2 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
- import * as i3 from '@angular/forms';
5
+ import * as i4$1 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,12 +12,13 @@ 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 i4$1 from 'primeng/select';
15
+ import * as i5$1 from 'primeng/select';
16
16
  import { SelectModule } from 'primeng/select';
17
17
  import { TooltipModule } from 'primeng/tooltip';
18
18
  import { DialogModule } from 'primeng/dialog';
19
19
  import * as i14 from '@vectoriox/iox-ui';
20
- import { IS_PREVIEW, IoxPageModule } from '@vectoriox/iox-ui';
20
+ import { ComponentInstanceRegistryService, IS_PREVIEW, IoxPageModule } from '@vectoriox/iox-ui';
21
+ export { ComponentInstanceRegistryService } from '@vectoriox/iox-ui';
21
22
  import Lenis from 'lenis';
22
23
  import { Subject, BehaviorSubject } from 'rxjs';
23
24
  import interact from 'interactjs';
@@ -133,6 +134,7 @@ var ToolbarAction;
133
134
  ToolbarAction["PasteInteractionPreset"] = "paste-interaction-preset";
134
135
  ToolbarAction["Copy"] = "copy";
135
136
  ToolbarAction["Paste"] = "paste";
137
+ ToolbarAction["AddSlide"] = "add-slide";
136
138
  })(ToolbarAction || (ToolbarAction = {}));
137
139
  var NodeAction;
138
140
  (function (NodeAction) {
@@ -1194,6 +1196,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
1194
1196
  type: Injectable
1195
1197
  }] });
1196
1198
 
1199
+ // Re-exported from the shared iox-ui package so iox-builder consumers
1200
+ // don't need to import from two different packages.
1201
+
1197
1202
  /**
1198
1203
  * Wires ComponentNode interactions to live DOM elements via the
1199
1204
  * OverlayService node-element registry.
@@ -1224,6 +1229,11 @@ class InteractionEngineService {
1224
1229
  ]);
1225
1230
  /** Action types that animate an element from visible → hidden. */
1226
1231
  this.EXIT_TYPES = new Set(['fadeOut', 'scaleOut', 'hide']);
1232
+ this.SWIPER_ACTIONS = new Set([
1233
+ 'swiperNext', 'swiperPrev', 'swiperGoToSlide',
1234
+ 'swiperAutoplayStart', 'swiperAutoplayStop', 'swiperAutoplayPause',
1235
+ ]);
1236
+ this.instanceRegistry = inject(ComponentInstanceRegistryService);
1227
1237
  }
1228
1238
  /** Wire all interactions for a node to its rendered DOM element. */
1229
1239
  attach(node) {
@@ -1357,6 +1367,23 @@ class InteractionEngineService {
1357
1367
  this.runActions(node, ix.actions);
1358
1368
  }
1359
1369
  }
1370
+ /**
1371
+ * Fire all interactions on the given node that match the specified trigger.
1372
+ * Used by Swiper (and future components) to dispatch their events into the
1373
+ * interaction engine without coupling to the engine's attach/detach lifecycle.
1374
+ */
1375
+ replayTrigger(nodeId, trigger) {
1376
+ for (const [node] of this.attached) {
1377
+ if (node.id !== nodeId)
1378
+ continue;
1379
+ for (const ix of (node.interactions ?? [])) {
1380
+ if (ix.trigger === trigger) {
1381
+ this.runActions(node, ix.actions);
1382
+ }
1383
+ }
1384
+ return;
1385
+ }
1386
+ }
1360
1387
  // ── Private ──────────────────────────────────────────────
1361
1388
  attachInteraction(node, ix, element) {
1362
1389
  // In builder mode, automatic triggers don't auto-fire — use the play button instead.
@@ -1480,12 +1507,42 @@ class InteractionEngineService {
1480
1507
  // ── Actions ──────────────────────────────────────────────
1481
1508
  runActions(node, actions) {
1482
1509
  for (const action of actions) {
1510
+ if (this.SWIPER_ACTIONS.has(action.type)) {
1511
+ this.executeSwiperAction(action);
1512
+ continue;
1513
+ }
1483
1514
  const target = this.resolveTarget(node, action);
1484
1515
  if (!target)
1485
1516
  continue;
1486
1517
  this.executeAction(target, action);
1487
1518
  }
1488
1519
  }
1520
+ executeSwiperAction(action) {
1521
+ // Target is the nodeId of the SliderContainer to control.
1522
+ const swiper = this.instanceRegistry.get(action.target);
1523
+ if (!swiper)
1524
+ return;
1525
+ switch (action.type) {
1526
+ case 'swiperNext':
1527
+ swiper.slideNext();
1528
+ break;
1529
+ case 'swiperPrev':
1530
+ swiper.slidePrev();
1531
+ break;
1532
+ case 'swiperGoToSlide':
1533
+ swiper.slideTo(action.params?.['index'] ?? 0);
1534
+ break;
1535
+ case 'swiperAutoplayStart':
1536
+ swiper.autoplay?.start();
1537
+ break;
1538
+ case 'swiperAutoplayStop':
1539
+ swiper.autoplay?.stop();
1540
+ break;
1541
+ case 'swiperAutoplayPause':
1542
+ swiper.autoplay?.pause();
1543
+ break;
1544
+ }
1545
+ }
1489
1546
  resolveTarget(ownerNode, action) {
1490
1547
  if (action.target === 'self') {
1491
1548
  const ref = this.overlayService.getNodeRef(ownerNode);
@@ -1915,6 +1972,7 @@ var TraitInputType;
1915
1972
  TraitInputType["FontFamily"] = "fontFamily";
1916
1973
  TraitInputType["GridTemplate"] = "gridTemplate";
1917
1974
  TraitInputType["SectionLabel"] = "sectionLabel";
1975
+ TraitInputType["Textarea"] = "textarea";
1918
1976
  })(TraitInputType || (TraitInputType = {}));
1919
1977
  function generateNodeId() {
1920
1978
  return Math.random().toString(36).substr(2, 9);
@@ -1980,6 +2038,7 @@ function resolveTraitControllerType(type) {
1980
2038
  case TraitInputType.Media: return 'media';
1981
2039
  case TraitInputType.FontFamily: return 'select';
1982
2040
  case TraitInputType.GridTemplate: return 'gridTemplate';
2041
+ case TraitInputType.Textarea: return 'textarea';
1983
2042
  default: return 'text';
1984
2043
  }
1985
2044
  }
@@ -3121,6 +3180,14 @@ class OverlayComponent {
3121
3180
  this.toolbarAction.emit({ action: ToolbarAction.PasteInteractionPreset, entry: this.selectEntry });
3122
3181
  this.closeMoreMenu();
3123
3182
  }
3183
+ get isSliderContainer() {
3184
+ return this.selectEntry?.node?.type === 'SliderContainer';
3185
+ }
3186
+ onAddSlide(event) {
3187
+ event.stopPropagation();
3188
+ if (this.selectEntry)
3189
+ this.toolbarAction.emit({ action: ToolbarAction.AddSlide, entry: this.selectEntry });
3190
+ }
3124
3191
  onDetachSymbol(event) {
3125
3192
  event.stopPropagation();
3126
3193
  if (this.selectEntry)
@@ -3240,11 +3307,11 @@ class OverlayComponent {
3240
3307
  return `polygon(evenodd, 0px 0px, ${w}px 0px, ${w}px ${h}px, 0px ${h}px, 0px 0px, ${x1}px ${y1}px, ${x2}px ${y1}px, ${x2}px ${y2}px, ${x1}px ${y2}px, ${x1}px ${y1}px)`;
3241
3308
  }
3242
3309
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: OverlayComponent, deps: [{ token: OverlayService }, { token: ViewportService }, { token: PresetRegistryService }, { token: SymbolRegistryService }, { token: InteractionPresetRegistryService }, { token: InteractionEngineService }, { token: BuilderClipboardService }, { token: StyleRegistryService }, { token: PanelEventService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
3243
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: OverlayComponent, isStandalone: false, selector: "app-overlay", outputs: { toolbarAction: "toolbarAction" }, ngImport: i0, template: "<div class=\"overlay-root\">\n\n <!-- HOVER \u2014 Chrome DevTools-style rings (content area stays transparent) -->\n <ng-container *ngIf=\"hoverEntry\">\n <div class=\"hi-outline\" [ngStyle]=\"hiOutlineStyle\"></div>\n <div class=\"hi-margin\" [ngStyle]=\"hiMarginStyle\"></div>\n <div class=\"hi-padding\" [ngStyle]=\"hiPaddingStyle\"></div>\n </ng-container>\n\n <!-- SELECT \u2014 IOX pink outline + toolbar -->\n <div class=\"select-outline\" *ngIf=\"selectEntry\" [ngStyle]=\"selectOutlineStyle\">\n\n <!-- Preset badges container \u2014 below the outline, start-aligned -->\n <div class=\"preset-badge\" *ngIf=\"selectedPresetName || interactions.length\">\n <div *ngIf=\"selectedPresetName\"\n class=\"preset-badge__item\"\n [class.preset-badge__item--active]=\"activeBadgeType === 'style'\"\n title=\"Style preset: {{ selectedPresetName }} \u2014 click to edit, right-click to copy\"\n (mousedown)=\"onEditPreset($event)\"\n (contextmenu)=\"onBadgeContextMenu($event, 'style')\">\n <i class=\"ph-fill ph-paint-bucket\"></i>\n <span>{{ selectedPresetName }}</span>\n </div>\n <div *ngFor=\"let ix of interactions\"\n class=\"preset-badge__item preset-badge__item--interaction\"\n [class.preset-badge__item--active]=\"activeBadgeType === 'interaction'\"\n [title]=\"triggerLabel(ix.trigger) + ': ' + actionLabel(ix) + ' \u2014 click to edit, right-click to remove'\"\n (mousedown)=\"onEditInteractionPreset($event)\"\n (contextmenu)=\"onBadgeContextMenu($event, 'interaction', ix)\">\n <i [class]=\"triggerIcon(ix.trigger)\"></i>\n <span>{{ triggerLabel(ix.trigger) }}: {{ actionLabel(ix) }}</span>\n </div>\n </div>\n\n <div class=\"select-toolbar\" (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n\n <!-- Component name -->\n <span class=\"toolbar-name\">{{ selectEntry?.componentName }}</span>\n <span class=\"toolbar-sep\"></span>\n\n <!-- Quick actions -->\n <button class=\"toolbar-btn\" title=\"Duplicate\" (mousedown)=\"onDuplicate($event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n <button *ngIf=\"hasInteractions\" class=\"toolbar-btn toolbar-btn--play\" title=\"Play animations\" (mousedown)=\"onPlay($event)\">\n <i class=\"ph-thin ph-play\"></i>\n </button>\n <button *ngIf=\"isLinkedSymbol\" class=\"toolbar-btn toolbar-btn--symbol\"\n [title]=\"(linkedSymbolName ? linkedSymbolName : 'Symbol') + ' \u2014 click to detach'\"\n (mousedown)=\"onDetachSymbol($event)\">\n <i class=\"ph-thin ph-link\"></i>\n <span class=\"toolbar-btn__label\">{{ linkedSymbolName || 'Symbol' }}</span>\n </button>\n <button *ngIf=\"isDetachedSymbol\" class=\"toolbar-btn toolbar-btn--resync\"\n title=\"Detached \u2014 click to re-link\"\n (mousedown)=\"onResyncSymbol($event)\">\n <i class=\"ph-thin ph-link-break\"></i>\n <span class=\"toolbar-btn__label\">Detached</span>\n </button>\n <button class=\"toolbar-btn toolbar-btn--danger\" title=\"Delete\" (mousedown)=\"onDelete($event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n <span class=\"toolbar-sep\"></span>\n <button class=\"toolbar-btn\" title=\"Select parent\" (mousedown)=\"onSelectParent($event)\">\n <i class=\"ph-thin ph-arrow-up\"></i>\n </button>\n\n <!-- More menu trigger \u2014 opens a fixed-position dropdown at the cursor -->\n <button class=\"toolbar-btn toolbar-btn--more\" title=\"More actions\" (mousedown)=\"onToggleMore($event)\">\n <i class=\"ph-thin ph-dots-three\"></i>\n </button>\n </div>\n </div>\n\n <!-- Shared more/context menu \u2014 fixed-position, opened by \u22EF button or right-click -->\n <div *ngIf=\"moreMenuVisible\" class=\"toolbar-more-dropdown\" [ngStyle]=\"moreMenuStyle\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <button class=\"more-item\" (mousedown)=\"onSelectParent($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-arrow-up\"></i>\n Select parent\n </button>\n <button class=\"more-item\" (mousedown)=\"onDuplicate($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-copy\"></i>\n Duplicate\n </button>\n <button class=\"more-item more-item--danger\" (mousedown)=\"onDelete($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-trash\"></i>\n Delete\n </button>\n <div class=\"more-divider\"></div>\n <button class=\"more-item\" (mousedown)=\"onCopyNode($event)\">\n <i class=\"ph-thin ph-scissors\"></i>\n Copy element\n </button>\n <button *ngIf=\"hasCopiedNode\" class=\"more-item\" (mousedown)=\"onPasteNode($event)\">\n <i class=\"ph-thin ph-clipboard\"></i>\n Paste element\n </button>\n <div class=\"more-divider\"></div>\n <button *ngIf=\"selectEntry?.node?.stylePresetId\" class=\"more-item\" (mousedown)=\"onCopyPreset($event)\">\n <i class=\"ph-thin ph-paint-bucket\"></i>\n Copy style preset\n </button>\n <button *ngIf=\"hasPresetInClipboard\" class=\"more-item\" (mousedown)=\"onPastePreset($event)\">\n <i class=\"ph-thin ph-paint-bucket\"></i>\n Paste style preset\n </button>\n <button *ngIf=\"selectEntry?.node?.interactionPresetId\" class=\"more-item\" (mousedown)=\"onCopyInteractionPreset($event)\">\n <i class=\"ph-thin ph-lightning\"></i>\n Copy interaction preset\n </button>\n <button *ngIf=\"hasInteractionPresetInClipboard\" class=\"more-item\" (mousedown)=\"onPasteInteractionPreset($event)\">\n <i class=\"ph-thin ph-lightning\"></i>\n Paste interaction preset\n </button>\n <div class=\"more-divider\"></div>\n <button *ngIf=\"isLinkedSymbol\" class=\"more-item\" (mousedown)=\"onDetachSymbol($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-link-break\"></i>\n Detach from symbol<ng-container *ngIf=\"linkedSymbolName\"> ({{ linkedSymbolName }})</ng-container>\n </button>\n <button *ngIf=\"isDetachedSymbol\" class=\"more-item more-item--resync\" (mousedown)=\"onResyncSymbol($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-link\"></i>\n Re-link to symbol\n </button>\n <button class=\"more-item\" (mousedown)=\"onSaveAsBlock($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-bookmark\"></i>\n Save as reusable block\n </button>\n <button *ngIf=\"hasInteractions\" class=\"more-item more-item--play\" (mousedown)=\"onPlay($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-play\"></i>\n Play animations\n </button>\n <div class=\"more-divider\"></div>\n <ng-container *ngIf=\"!selectedNodeIsGlobal; else removeGlobalItem\">\n <button class=\"more-item\" (mousedown)=\"onMakeGlobal($event, 'before')\">\n <i class=\"ph-thin ph-globe\"></i>\n Make Global (Before Content)\n </button>\n <button class=\"more-item\" (mousedown)=\"onMakeGlobal($event, 'after')\">\n <i class=\"ph-thin ph-globe\"></i>\n Make Global (After Content)\n </button>\n </ng-container>\n <ng-template #removeGlobalItem>\n <button class=\"more-item\" (mousedown)=\"onRemoveGlobal($event)\">\n <i class=\"ph-thin ph-globe\"></i>\n Remove from Global\n </button>\n </ng-template>\n </div>\n\n <!-- Backdrop to close the more menu -->\n <div *ngIf=\"moreMenuVisible\" class=\"overlay-context-backdrop\"\n (mousedown)=\"closeMoreMenu()\">\n </div>\n\n <!-- Badge context menu \u2014 options differ by badge type -->\n <div *ngIf=\"badgeMenuVisible\" class=\"badge-context-menu\"\n [ngStyle]=\"{ top: badgeMenuPos.y + 'px', left: badgeMenuPos.x + 'px' }\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <ng-container *ngIf=\"badgeMenuType === 'style'\">\n <button class=\"more-item\" (mousedown)=\"onBadgeCopy()\">\n <i class=\"ph-thin ph-copy\"></i>\n Copy preset\n </button>\n <button class=\"more-item\" (mousedown)=\"onBadgeEdit()\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n Edit preset\n </button>\n </ng-container>\n <ng-container *ngIf=\"badgeMenuType === 'interaction'\">\n <button class=\"more-item\" (mousedown)=\"onBadgeEdit()\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n Edit in panel\n </button>\n <button class=\"more-item more-item--danger\" (mousedown)=\"onBadgeRemoveInteraction()\">\n <i class=\"ph-thin ph-trash\"></i>\n Remove interaction\n </button>\n </ng-container>\n </div>\n <div *ngIf=\"badgeMenuVisible\" class=\"overlay-context-backdrop\"\n (mousedown)=\"closeBadgeMenu()\">\n </div>\n\n</div>\n", styles: [":host{display:block;position:absolute;inset:0;pointer-events:none;z-index:12;overflow:visible}.hi-outline,.hi-margin,.hi-padding{position:fixed;pointer-events:none;transition:none;box-sizing:border-box}.hi-outline{box-shadow:inset 0 0 0 1.5px #cb9090}.hi-margin{background:#f6b23361}.hi-padding{background:#60c38366}.select-outline{position:fixed;z-index:999;box-shadow:inset 0 0 0 1px #cb9090;pointer-events:none;transition:none;overflow:visible;will-change:transform;box-sizing:border-box}.select-toolbar{position:absolute;top:0;left:0;transform:translateY(-100%);display:flex;align-items:center;background:#cb9090;border-radius:3px 3px 0 0;pointer-events:all;white-space:nowrap;overflow:visible}.select-toolbar .toolbar-name{padding:0 8px;font-size:11px;font-weight:500;color:#fff;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:22px}.select-toolbar .toolbar-sep{width:1px;height:14px;background:#ffffff59;margin:0 2px;flex-shrink:0}.select-toolbar .toolbar-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;padding:0;border:none;background:transparent;color:#fff;font-size:13px;cursor:pointer;transition:background .15s;flex-shrink:0}.select-toolbar .toolbar-btn:hover{background:#ffffff2e}.select-toolbar .toolbar-btn--danger:hover{background:#c8323259}.select-toolbar .toolbar-btn--symbol{width:auto;padding:0 7px;gap:4px;color:#fff;background:#f59e0b;border-radius:3px;font-size:11px}.select-toolbar .toolbar-btn--symbol:hover{background:#d97706}.select-toolbar .toolbar-btn--resync{width:auto;padding:0 7px;gap:4px;color:#fff;background:#22c55e;border-radius:3px;font-size:11px}.select-toolbar .toolbar-btn--resync:hover{background:#16a34a}.select-toolbar .toolbar-btn .toolbar-btn__label{font-size:11px;max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-toolbar .toolbar-btn--more{border-radius:0 3px 0 0}.toolbar-more-dropdown{position:fixed;z-index:1000;background:#fff;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:6px;box-shadow:0 4px 16px #0000001f;padding:4px 0;min-width:200px;pointer-events:all}.toolbar-more-dropdown .more-item{display:flex;align-items:center;gap:8px;width:100%;padding:8px 14px;border:none;background:transparent;font-size:13px;color:var(--p-text-color, #333);cursor:pointer;text-align:left;white-space:nowrap}.toolbar-more-dropdown .more-item i{font-size:14px}.toolbar-more-dropdown .more-item:hover{background:var(--p-surface-100, #f5f5f5)}.toolbar-more-dropdown .more-item--danger{color:#c84040}.toolbar-more-dropdown .more-item--danger:hover{background:#c8404014}.toolbar-more-dropdown .more-item--play{color:var(--p-text-color, #333)}.toolbar-more-dropdown .more-item--resync{color:#22c55e}.toolbar-more-dropdown .more-item--resync:hover{background:#22c55e14}.toolbar-more-dropdown .more-divider{height:1px;background:var(--p-surface-200, #e5e7eb);margin:4px 0}.preset-badge{position:absolute;bottom:-26px;inset-inline-start:0;display:flex;align-items:center;gap:4px;pointer-events:none;white-space:nowrap}.preset-badge__item{display:flex;align-items:center;gap:5px;padding:3px 9px;background:#cb9090;color:#fff;font-size:11px;font-weight:500;border-radius:20px;pointer-events:all;cursor:pointer;box-shadow:0 2px 6px #0000002e;transition:background .15s,outline-color .15s;outline:2px solid transparent;outline-offset:2px}.preset-badge__item i{font-size:12px}.preset-badge__item:hover{background:#be7474}.preset-badge__item--active{outline-color:#ffffffd9}.preset-badge__item--interaction{background:#7a9ecb}.preset-badge__item--interaction:hover{background:#5d89c0}.badge-context-menu{position:fixed;z-index:1001;background:#fff;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:6px;box-shadow:0 4px 16px #0000001f;padding:4px 0;min-width:160px;pointer-events:all}.badge-context-menu .more-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 14px;border:none;background:transparent;font-size:12px;color:#333;cursor:pointer;text-align:start;white-space:nowrap}.badge-context-menu .more-item:hover{background:var(--p-surface-50, #f9fafb)}.badge-context-menu .more-item i{font-size:13px;color:#888}.overlay-context-backdrop{position:fixed;inset:0;z-index:998;pointer-events:all}\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: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3310
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: OverlayComponent, isStandalone: false, selector: "app-overlay", outputs: { toolbarAction: "toolbarAction" }, ngImport: i0, template: "<div class=\"overlay-root\">\n\n <!-- HOVER \u2014 Chrome DevTools-style rings (content area stays transparent) -->\n <ng-container *ngIf=\"hoverEntry\">\n <div class=\"hi-outline\" [ngStyle]=\"hiOutlineStyle\"></div>\n <div class=\"hi-margin\" [ngStyle]=\"hiMarginStyle\"></div>\n <div class=\"hi-padding\" [ngStyle]=\"hiPaddingStyle\"></div>\n </ng-container>\n\n <!-- SELECT \u2014 IOX pink outline + toolbar -->\n <div class=\"select-outline\" *ngIf=\"selectEntry\" [ngStyle]=\"selectOutlineStyle\">\n\n <!-- Preset badges container \u2014 below the outline, start-aligned -->\n <div class=\"preset-badge\" *ngIf=\"selectedPresetName || interactions.length\">\n <div *ngIf=\"selectedPresetName\"\n class=\"preset-badge__item\"\n [class.preset-badge__item--active]=\"activeBadgeType === 'style'\"\n title=\"Style preset: {{ selectedPresetName }} \u2014 click to edit, right-click to copy\"\n (mousedown)=\"onEditPreset($event)\"\n (contextmenu)=\"onBadgeContextMenu($event, 'style')\">\n <i class=\"ph-fill ph-paint-bucket\"></i>\n <span>{{ selectedPresetName }}</span>\n </div>\n <div *ngFor=\"let ix of interactions\"\n class=\"preset-badge__item preset-badge__item--interaction\"\n [class.preset-badge__item--active]=\"activeBadgeType === 'interaction'\"\n [title]=\"triggerLabel(ix.trigger) + ': ' + actionLabel(ix) + ' \u2014 click to edit, right-click to remove'\"\n (mousedown)=\"onEditInteractionPreset($event)\"\n (contextmenu)=\"onBadgeContextMenu($event, 'interaction', ix)\">\n <i [class]=\"triggerIcon(ix.trigger)\"></i>\n <span>{{ triggerLabel(ix.trigger) }}: {{ actionLabel(ix) }}</span>\n </div>\n </div>\n\n <div class=\"select-toolbar\" (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n\n <!-- Component name -->\n <span class=\"toolbar-name\">{{ selectEntry?.componentName }}</span>\n <span class=\"toolbar-sep\"></span>\n\n <!-- Quick actions -->\n <button *ngIf=\"isSliderContainer\" class=\"toolbar-btn toolbar-btn--add-slide\" title=\"Add slide\" (mousedown)=\"onAddSlide($event)\">\n <i class=\"ph-thin ph-plus\"></i>\n <span class=\"toolbar-btn__label\">Add slide</span>\n </button>\n <button class=\"toolbar-btn\" title=\"Duplicate\" (mousedown)=\"onDuplicate($event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n <button *ngIf=\"hasInteractions\" class=\"toolbar-btn toolbar-btn--play\" title=\"Play animations\" (mousedown)=\"onPlay($event)\">\n <i class=\"ph-thin ph-play\"></i>\n </button>\n <button *ngIf=\"isLinkedSymbol\" class=\"toolbar-btn toolbar-btn--symbol\"\n [title]=\"(linkedSymbolName ? linkedSymbolName : 'Symbol') + ' \u2014 click to detach'\"\n (mousedown)=\"onDetachSymbol($event)\">\n <i class=\"ph-thin ph-link\"></i>\n <span class=\"toolbar-btn__label\">{{ linkedSymbolName || 'Symbol' }}</span>\n </button>\n <button *ngIf=\"isDetachedSymbol\" class=\"toolbar-btn toolbar-btn--resync\"\n title=\"Detached \u2014 click to re-link\"\n (mousedown)=\"onResyncSymbol($event)\">\n <i class=\"ph-thin ph-link-break\"></i>\n <span class=\"toolbar-btn__label\">Detached</span>\n </button>\n <button class=\"toolbar-btn toolbar-btn--danger\" title=\"Delete\" (mousedown)=\"onDelete($event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n <span class=\"toolbar-sep\"></span>\n <button class=\"toolbar-btn\" title=\"Select parent\" (mousedown)=\"onSelectParent($event)\">\n <i class=\"ph-thin ph-arrow-up\"></i>\n </button>\n\n <!-- More menu trigger \u2014 opens a fixed-position dropdown at the cursor -->\n <button class=\"toolbar-btn toolbar-btn--more\" title=\"More actions\" (mousedown)=\"onToggleMore($event)\">\n <i class=\"ph-thin ph-dots-three\"></i>\n </button>\n </div>\n </div>\n\n <!-- Shared more/context menu \u2014 fixed-position, opened by \u22EF button or right-click -->\n <div *ngIf=\"moreMenuVisible\" class=\"toolbar-more-dropdown\" [ngStyle]=\"moreMenuStyle\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <button class=\"more-item\" (mousedown)=\"onSelectParent($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-arrow-up\"></i>\n Select parent\n </button>\n <button class=\"more-item\" (mousedown)=\"onDuplicate($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-copy\"></i>\n Duplicate\n </button>\n <button class=\"more-item more-item--danger\" (mousedown)=\"onDelete($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-trash\"></i>\n Delete\n </button>\n <div class=\"more-divider\"></div>\n <button class=\"more-item\" (mousedown)=\"onCopyNode($event)\">\n <i class=\"ph-thin ph-scissors\"></i>\n Copy element\n </button>\n <button *ngIf=\"hasCopiedNode\" class=\"more-item\" (mousedown)=\"onPasteNode($event)\">\n <i class=\"ph-thin ph-clipboard\"></i>\n Paste element\n </button>\n <div class=\"more-divider\"></div>\n <button *ngIf=\"selectEntry?.node?.stylePresetId\" class=\"more-item\" (mousedown)=\"onCopyPreset($event)\">\n <i class=\"ph-thin ph-paint-bucket\"></i>\n Copy style preset\n </button>\n <button *ngIf=\"hasPresetInClipboard\" class=\"more-item\" (mousedown)=\"onPastePreset($event)\">\n <i class=\"ph-thin ph-paint-bucket\"></i>\n Paste style preset\n </button>\n <button *ngIf=\"selectEntry?.node?.interactionPresetId\" class=\"more-item\" (mousedown)=\"onCopyInteractionPreset($event)\">\n <i class=\"ph-thin ph-lightning\"></i>\n Copy interaction preset\n </button>\n <button *ngIf=\"hasInteractionPresetInClipboard\" class=\"more-item\" (mousedown)=\"onPasteInteractionPreset($event)\">\n <i class=\"ph-thin ph-lightning\"></i>\n Paste interaction preset\n </button>\n <div class=\"more-divider\"></div>\n <button *ngIf=\"isLinkedSymbol\" class=\"more-item\" (mousedown)=\"onDetachSymbol($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-link-break\"></i>\n Detach from symbol<ng-container *ngIf=\"linkedSymbolName\"> ({{ linkedSymbolName }})</ng-container>\n </button>\n <button *ngIf=\"isDetachedSymbol\" class=\"more-item more-item--resync\" (mousedown)=\"onResyncSymbol($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-link\"></i>\n Re-link to symbol\n </button>\n <button class=\"more-item\" (mousedown)=\"onSaveAsBlock($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-bookmark\"></i>\n Save as reusable block\n </button>\n <button *ngIf=\"hasInteractions\" class=\"more-item more-item--play\" (mousedown)=\"onPlay($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-play\"></i>\n Play animations\n </button>\n <div class=\"more-divider\"></div>\n <ng-container *ngIf=\"!selectedNodeIsGlobal; else removeGlobalItem\">\n <button class=\"more-item\" (mousedown)=\"onMakeGlobal($event, 'before')\">\n <i class=\"ph-thin ph-globe\"></i>\n Make Global (Before Content)\n </button>\n <button class=\"more-item\" (mousedown)=\"onMakeGlobal($event, 'after')\">\n <i class=\"ph-thin ph-globe\"></i>\n Make Global (After Content)\n </button>\n </ng-container>\n <ng-template #removeGlobalItem>\n <button class=\"more-item\" (mousedown)=\"onRemoveGlobal($event)\">\n <i class=\"ph-thin ph-globe\"></i>\n Remove from Global\n </button>\n </ng-template>\n </div>\n\n <!-- Backdrop to close the more menu -->\n <div *ngIf=\"moreMenuVisible\" class=\"overlay-context-backdrop\"\n (mousedown)=\"closeMoreMenu()\">\n </div>\n\n <!-- Badge context menu \u2014 options differ by badge type -->\n <div *ngIf=\"badgeMenuVisible\" class=\"badge-context-menu\"\n [ngStyle]=\"{ top: badgeMenuPos.y + 'px', left: badgeMenuPos.x + 'px' }\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <ng-container *ngIf=\"badgeMenuType === 'style'\">\n <button class=\"more-item\" (mousedown)=\"onBadgeCopy()\">\n <i class=\"ph-thin ph-copy\"></i>\n Copy preset\n </button>\n <button class=\"more-item\" (mousedown)=\"onBadgeEdit()\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n Edit preset\n </button>\n </ng-container>\n <ng-container *ngIf=\"badgeMenuType === 'interaction'\">\n <button class=\"more-item\" (mousedown)=\"onBadgeEdit()\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n Edit in panel\n </button>\n <button class=\"more-item more-item--danger\" (mousedown)=\"onBadgeRemoveInteraction()\">\n <i class=\"ph-thin ph-trash\"></i>\n Remove interaction\n </button>\n </ng-container>\n </div>\n <div *ngIf=\"badgeMenuVisible\" class=\"overlay-context-backdrop\"\n (mousedown)=\"closeBadgeMenu()\">\n </div>\n\n</div>\n", styles: [":host{display:block;position:absolute;inset:0;pointer-events:none;z-index:12;overflow:visible}.hi-outline,.hi-margin,.hi-padding{position:fixed;pointer-events:none;transition:none;box-sizing:border-box}.hi-outline{box-shadow:inset 0 0 0 1.5px #cb9090}.hi-margin{background:#f6b23361}.hi-padding{background:#60c38366}.select-outline{position:fixed;z-index:999;box-shadow:inset 0 0 0 1px #cb9090;pointer-events:none;transition:none;overflow:visible;will-change:transform;box-sizing:border-box}.select-toolbar{position:absolute;top:0;left:0;transform:translateY(-100%);display:flex;align-items:center;background:#cb9090;border-radius:3px 3px 0 0;pointer-events:all;white-space:nowrap;overflow:visible}.select-toolbar .toolbar-name{padding:0 8px;font-size:11px;font-weight:500;color:#fff;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:22px}.select-toolbar .toolbar-sep{width:1px;height:14px;background:#ffffff59;margin:0 2px;flex-shrink:0}.select-toolbar .toolbar-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;padding:0;border:none;background:transparent;color:#fff;font-size:13px;cursor:pointer;transition:background .15s;flex-shrink:0}.select-toolbar .toolbar-btn:hover{background:#ffffff2e}.select-toolbar .toolbar-btn--danger:hover{background:#c8323259}.select-toolbar .toolbar-btn--symbol{width:auto;padding:0 7px;gap:4px;color:#fff;background:#f59e0b;border-radius:3px;font-size:11px}.select-toolbar .toolbar-btn--symbol:hover{background:#d97706}.select-toolbar .toolbar-btn--resync{width:auto;padding:0 7px;gap:4px;color:#fff;background:#22c55e;border-radius:3px;font-size:11px}.select-toolbar .toolbar-btn--resync:hover{background:#16a34a}.select-toolbar .toolbar-btn .toolbar-btn__label{font-size:11px;max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-toolbar .toolbar-btn--more{border-radius:0 3px 0 0}.toolbar-more-dropdown{position:fixed;z-index:1000;background:#fff;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:6px;box-shadow:0 4px 16px #0000001f;padding:4px 0;min-width:200px;pointer-events:all}.toolbar-more-dropdown .more-item{display:flex;align-items:center;gap:8px;width:100%;padding:8px 14px;border:none;background:transparent;font-size:13px;color:var(--p-text-color, #333);cursor:pointer;text-align:left;white-space:nowrap}.toolbar-more-dropdown .more-item i{font-size:14px}.toolbar-more-dropdown .more-item:hover{background:var(--p-surface-100, #f5f5f5)}.toolbar-more-dropdown .more-item--danger{color:#c84040}.toolbar-more-dropdown .more-item--danger:hover{background:#c8404014}.toolbar-more-dropdown .more-item--play{color:var(--p-text-color, #333)}.toolbar-more-dropdown .more-item--resync{color:#22c55e}.toolbar-more-dropdown .more-item--resync:hover{background:#22c55e14}.toolbar-more-dropdown .more-divider{height:1px;background:var(--p-surface-200, #e5e7eb);margin:4px 0}.preset-badge{position:absolute;bottom:-26px;inset-inline-start:0;display:flex;align-items:center;gap:4px;pointer-events:none;white-space:nowrap}.preset-badge__item{display:flex;align-items:center;gap:5px;padding:3px 9px;background:#cb9090;color:#fff;font-size:11px;font-weight:500;border-radius:20px;pointer-events:all;cursor:pointer;box-shadow:0 2px 6px #0000002e;transition:background .15s,outline-color .15s;outline:2px solid transparent;outline-offset:2px}.preset-badge__item i{font-size:12px}.preset-badge__item:hover{background:#be7474}.preset-badge__item--active{outline-color:#ffffffd9}.preset-badge__item--interaction{background:#7a9ecb}.preset-badge__item--interaction:hover{background:#5d89c0}.badge-context-menu{position:fixed;z-index:1001;background:#fff;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:6px;box-shadow:0 4px 16px #0000001f;padding:4px 0;min-width:160px;pointer-events:all}.badge-context-menu .more-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 14px;border:none;background:transparent;font-size:12px;color:#333;cursor:pointer;text-align:start;white-space:nowrap}.badge-context-menu .more-item:hover{background:var(--p-surface-50, #f9fafb)}.badge-context-menu .more-item i{font-size:13px;color:#888}.overlay-context-backdrop{position:fixed;inset:0;z-index:998;pointer-events:all}\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: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3244
3311
  }
3245
3312
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: OverlayComponent, decorators: [{
3246
3313
  type: Component,
3247
- args: [{ selector: 'app-overlay', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"overlay-root\">\n\n <!-- HOVER \u2014 Chrome DevTools-style rings (content area stays transparent) -->\n <ng-container *ngIf=\"hoverEntry\">\n <div class=\"hi-outline\" [ngStyle]=\"hiOutlineStyle\"></div>\n <div class=\"hi-margin\" [ngStyle]=\"hiMarginStyle\"></div>\n <div class=\"hi-padding\" [ngStyle]=\"hiPaddingStyle\"></div>\n </ng-container>\n\n <!-- SELECT \u2014 IOX pink outline + toolbar -->\n <div class=\"select-outline\" *ngIf=\"selectEntry\" [ngStyle]=\"selectOutlineStyle\">\n\n <!-- Preset badges container \u2014 below the outline, start-aligned -->\n <div class=\"preset-badge\" *ngIf=\"selectedPresetName || interactions.length\">\n <div *ngIf=\"selectedPresetName\"\n class=\"preset-badge__item\"\n [class.preset-badge__item--active]=\"activeBadgeType === 'style'\"\n title=\"Style preset: {{ selectedPresetName }} \u2014 click to edit, right-click to copy\"\n (mousedown)=\"onEditPreset($event)\"\n (contextmenu)=\"onBadgeContextMenu($event, 'style')\">\n <i class=\"ph-fill ph-paint-bucket\"></i>\n <span>{{ selectedPresetName }}</span>\n </div>\n <div *ngFor=\"let ix of interactions\"\n class=\"preset-badge__item preset-badge__item--interaction\"\n [class.preset-badge__item--active]=\"activeBadgeType === 'interaction'\"\n [title]=\"triggerLabel(ix.trigger) + ': ' + actionLabel(ix) + ' \u2014 click to edit, right-click to remove'\"\n (mousedown)=\"onEditInteractionPreset($event)\"\n (contextmenu)=\"onBadgeContextMenu($event, 'interaction', ix)\">\n <i [class]=\"triggerIcon(ix.trigger)\"></i>\n <span>{{ triggerLabel(ix.trigger) }}: {{ actionLabel(ix) }}</span>\n </div>\n </div>\n\n <div class=\"select-toolbar\" (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n\n <!-- Component name -->\n <span class=\"toolbar-name\">{{ selectEntry?.componentName }}</span>\n <span class=\"toolbar-sep\"></span>\n\n <!-- Quick actions -->\n <button class=\"toolbar-btn\" title=\"Duplicate\" (mousedown)=\"onDuplicate($event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n <button *ngIf=\"hasInteractions\" class=\"toolbar-btn toolbar-btn--play\" title=\"Play animations\" (mousedown)=\"onPlay($event)\">\n <i class=\"ph-thin ph-play\"></i>\n </button>\n <button *ngIf=\"isLinkedSymbol\" class=\"toolbar-btn toolbar-btn--symbol\"\n [title]=\"(linkedSymbolName ? linkedSymbolName : 'Symbol') + ' \u2014 click to detach'\"\n (mousedown)=\"onDetachSymbol($event)\">\n <i class=\"ph-thin ph-link\"></i>\n <span class=\"toolbar-btn__label\">{{ linkedSymbolName || 'Symbol' }}</span>\n </button>\n <button *ngIf=\"isDetachedSymbol\" class=\"toolbar-btn toolbar-btn--resync\"\n title=\"Detached \u2014 click to re-link\"\n (mousedown)=\"onResyncSymbol($event)\">\n <i class=\"ph-thin ph-link-break\"></i>\n <span class=\"toolbar-btn__label\">Detached</span>\n </button>\n <button class=\"toolbar-btn toolbar-btn--danger\" title=\"Delete\" (mousedown)=\"onDelete($event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n <span class=\"toolbar-sep\"></span>\n <button class=\"toolbar-btn\" title=\"Select parent\" (mousedown)=\"onSelectParent($event)\">\n <i class=\"ph-thin ph-arrow-up\"></i>\n </button>\n\n <!-- More menu trigger \u2014 opens a fixed-position dropdown at the cursor -->\n <button class=\"toolbar-btn toolbar-btn--more\" title=\"More actions\" (mousedown)=\"onToggleMore($event)\">\n <i class=\"ph-thin ph-dots-three\"></i>\n </button>\n </div>\n </div>\n\n <!-- Shared more/context menu \u2014 fixed-position, opened by \u22EF button or right-click -->\n <div *ngIf=\"moreMenuVisible\" class=\"toolbar-more-dropdown\" [ngStyle]=\"moreMenuStyle\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <button class=\"more-item\" (mousedown)=\"onSelectParent($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-arrow-up\"></i>\n Select parent\n </button>\n <button class=\"more-item\" (mousedown)=\"onDuplicate($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-copy\"></i>\n Duplicate\n </button>\n <button class=\"more-item more-item--danger\" (mousedown)=\"onDelete($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-trash\"></i>\n Delete\n </button>\n <div class=\"more-divider\"></div>\n <button class=\"more-item\" (mousedown)=\"onCopyNode($event)\">\n <i class=\"ph-thin ph-scissors\"></i>\n Copy element\n </button>\n <button *ngIf=\"hasCopiedNode\" class=\"more-item\" (mousedown)=\"onPasteNode($event)\">\n <i class=\"ph-thin ph-clipboard\"></i>\n Paste element\n </button>\n <div class=\"more-divider\"></div>\n <button *ngIf=\"selectEntry?.node?.stylePresetId\" class=\"more-item\" (mousedown)=\"onCopyPreset($event)\">\n <i class=\"ph-thin ph-paint-bucket\"></i>\n Copy style preset\n </button>\n <button *ngIf=\"hasPresetInClipboard\" class=\"more-item\" (mousedown)=\"onPastePreset($event)\">\n <i class=\"ph-thin ph-paint-bucket\"></i>\n Paste style preset\n </button>\n <button *ngIf=\"selectEntry?.node?.interactionPresetId\" class=\"more-item\" (mousedown)=\"onCopyInteractionPreset($event)\">\n <i class=\"ph-thin ph-lightning\"></i>\n Copy interaction preset\n </button>\n <button *ngIf=\"hasInteractionPresetInClipboard\" class=\"more-item\" (mousedown)=\"onPasteInteractionPreset($event)\">\n <i class=\"ph-thin ph-lightning\"></i>\n Paste interaction preset\n </button>\n <div class=\"more-divider\"></div>\n <button *ngIf=\"isLinkedSymbol\" class=\"more-item\" (mousedown)=\"onDetachSymbol($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-link-break\"></i>\n Detach from symbol<ng-container *ngIf=\"linkedSymbolName\"> ({{ linkedSymbolName }})</ng-container>\n </button>\n <button *ngIf=\"isDetachedSymbol\" class=\"more-item more-item--resync\" (mousedown)=\"onResyncSymbol($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-link\"></i>\n Re-link to symbol\n </button>\n <button class=\"more-item\" (mousedown)=\"onSaveAsBlock($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-bookmark\"></i>\n Save as reusable block\n </button>\n <button *ngIf=\"hasInteractions\" class=\"more-item more-item--play\" (mousedown)=\"onPlay($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-play\"></i>\n Play animations\n </button>\n <div class=\"more-divider\"></div>\n <ng-container *ngIf=\"!selectedNodeIsGlobal; else removeGlobalItem\">\n <button class=\"more-item\" (mousedown)=\"onMakeGlobal($event, 'before')\">\n <i class=\"ph-thin ph-globe\"></i>\n Make Global (Before Content)\n </button>\n <button class=\"more-item\" (mousedown)=\"onMakeGlobal($event, 'after')\">\n <i class=\"ph-thin ph-globe\"></i>\n Make Global (After Content)\n </button>\n </ng-container>\n <ng-template #removeGlobalItem>\n <button class=\"more-item\" (mousedown)=\"onRemoveGlobal($event)\">\n <i class=\"ph-thin ph-globe\"></i>\n Remove from Global\n </button>\n </ng-template>\n </div>\n\n <!-- Backdrop to close the more menu -->\n <div *ngIf=\"moreMenuVisible\" class=\"overlay-context-backdrop\"\n (mousedown)=\"closeMoreMenu()\">\n </div>\n\n <!-- Badge context menu \u2014 options differ by badge type -->\n <div *ngIf=\"badgeMenuVisible\" class=\"badge-context-menu\"\n [ngStyle]=\"{ top: badgeMenuPos.y + 'px', left: badgeMenuPos.x + 'px' }\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <ng-container *ngIf=\"badgeMenuType === 'style'\">\n <button class=\"more-item\" (mousedown)=\"onBadgeCopy()\">\n <i class=\"ph-thin ph-copy\"></i>\n Copy preset\n </button>\n <button class=\"more-item\" (mousedown)=\"onBadgeEdit()\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n Edit preset\n </button>\n </ng-container>\n <ng-container *ngIf=\"badgeMenuType === 'interaction'\">\n <button class=\"more-item\" (mousedown)=\"onBadgeEdit()\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n Edit in panel\n </button>\n <button class=\"more-item more-item--danger\" (mousedown)=\"onBadgeRemoveInteraction()\">\n <i class=\"ph-thin ph-trash\"></i>\n Remove interaction\n </button>\n </ng-container>\n </div>\n <div *ngIf=\"badgeMenuVisible\" class=\"overlay-context-backdrop\"\n (mousedown)=\"closeBadgeMenu()\">\n </div>\n\n</div>\n", styles: [":host{display:block;position:absolute;inset:0;pointer-events:none;z-index:12;overflow:visible}.hi-outline,.hi-margin,.hi-padding{position:fixed;pointer-events:none;transition:none;box-sizing:border-box}.hi-outline{box-shadow:inset 0 0 0 1.5px #cb9090}.hi-margin{background:#f6b23361}.hi-padding{background:#60c38366}.select-outline{position:fixed;z-index:999;box-shadow:inset 0 0 0 1px #cb9090;pointer-events:none;transition:none;overflow:visible;will-change:transform;box-sizing:border-box}.select-toolbar{position:absolute;top:0;left:0;transform:translateY(-100%);display:flex;align-items:center;background:#cb9090;border-radius:3px 3px 0 0;pointer-events:all;white-space:nowrap;overflow:visible}.select-toolbar .toolbar-name{padding:0 8px;font-size:11px;font-weight:500;color:#fff;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:22px}.select-toolbar .toolbar-sep{width:1px;height:14px;background:#ffffff59;margin:0 2px;flex-shrink:0}.select-toolbar .toolbar-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;padding:0;border:none;background:transparent;color:#fff;font-size:13px;cursor:pointer;transition:background .15s;flex-shrink:0}.select-toolbar .toolbar-btn:hover{background:#ffffff2e}.select-toolbar .toolbar-btn--danger:hover{background:#c8323259}.select-toolbar .toolbar-btn--symbol{width:auto;padding:0 7px;gap:4px;color:#fff;background:#f59e0b;border-radius:3px;font-size:11px}.select-toolbar .toolbar-btn--symbol:hover{background:#d97706}.select-toolbar .toolbar-btn--resync{width:auto;padding:0 7px;gap:4px;color:#fff;background:#22c55e;border-radius:3px;font-size:11px}.select-toolbar .toolbar-btn--resync:hover{background:#16a34a}.select-toolbar .toolbar-btn .toolbar-btn__label{font-size:11px;max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-toolbar .toolbar-btn--more{border-radius:0 3px 0 0}.toolbar-more-dropdown{position:fixed;z-index:1000;background:#fff;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:6px;box-shadow:0 4px 16px #0000001f;padding:4px 0;min-width:200px;pointer-events:all}.toolbar-more-dropdown .more-item{display:flex;align-items:center;gap:8px;width:100%;padding:8px 14px;border:none;background:transparent;font-size:13px;color:var(--p-text-color, #333);cursor:pointer;text-align:left;white-space:nowrap}.toolbar-more-dropdown .more-item i{font-size:14px}.toolbar-more-dropdown .more-item:hover{background:var(--p-surface-100, #f5f5f5)}.toolbar-more-dropdown .more-item--danger{color:#c84040}.toolbar-more-dropdown .more-item--danger:hover{background:#c8404014}.toolbar-more-dropdown .more-item--play{color:var(--p-text-color, #333)}.toolbar-more-dropdown .more-item--resync{color:#22c55e}.toolbar-more-dropdown .more-item--resync:hover{background:#22c55e14}.toolbar-more-dropdown .more-divider{height:1px;background:var(--p-surface-200, #e5e7eb);margin:4px 0}.preset-badge{position:absolute;bottom:-26px;inset-inline-start:0;display:flex;align-items:center;gap:4px;pointer-events:none;white-space:nowrap}.preset-badge__item{display:flex;align-items:center;gap:5px;padding:3px 9px;background:#cb9090;color:#fff;font-size:11px;font-weight:500;border-radius:20px;pointer-events:all;cursor:pointer;box-shadow:0 2px 6px #0000002e;transition:background .15s,outline-color .15s;outline:2px solid transparent;outline-offset:2px}.preset-badge__item i{font-size:12px}.preset-badge__item:hover{background:#be7474}.preset-badge__item--active{outline-color:#ffffffd9}.preset-badge__item--interaction{background:#7a9ecb}.preset-badge__item--interaction:hover{background:#5d89c0}.badge-context-menu{position:fixed;z-index:1001;background:#fff;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:6px;box-shadow:0 4px 16px #0000001f;padding:4px 0;min-width:160px;pointer-events:all}.badge-context-menu .more-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 14px;border:none;background:transparent;font-size:12px;color:#333;cursor:pointer;text-align:start;white-space:nowrap}.badge-context-menu .more-item:hover{background:var(--p-surface-50, #f9fafb)}.badge-context-menu .more-item i{font-size:13px;color:#888}.overlay-context-backdrop{position:fixed;inset:0;z-index:998;pointer-events:all}\n"] }]
3314
+ args: [{ selector: 'app-overlay', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"overlay-root\">\n\n <!-- HOVER \u2014 Chrome DevTools-style rings (content area stays transparent) -->\n <ng-container *ngIf=\"hoverEntry\">\n <div class=\"hi-outline\" [ngStyle]=\"hiOutlineStyle\"></div>\n <div class=\"hi-margin\" [ngStyle]=\"hiMarginStyle\"></div>\n <div class=\"hi-padding\" [ngStyle]=\"hiPaddingStyle\"></div>\n </ng-container>\n\n <!-- SELECT \u2014 IOX pink outline + toolbar -->\n <div class=\"select-outline\" *ngIf=\"selectEntry\" [ngStyle]=\"selectOutlineStyle\">\n\n <!-- Preset badges container \u2014 below the outline, start-aligned -->\n <div class=\"preset-badge\" *ngIf=\"selectedPresetName || interactions.length\">\n <div *ngIf=\"selectedPresetName\"\n class=\"preset-badge__item\"\n [class.preset-badge__item--active]=\"activeBadgeType === 'style'\"\n title=\"Style preset: {{ selectedPresetName }} \u2014 click to edit, right-click to copy\"\n (mousedown)=\"onEditPreset($event)\"\n (contextmenu)=\"onBadgeContextMenu($event, 'style')\">\n <i class=\"ph-fill ph-paint-bucket\"></i>\n <span>{{ selectedPresetName }}</span>\n </div>\n <div *ngFor=\"let ix of interactions\"\n class=\"preset-badge__item preset-badge__item--interaction\"\n [class.preset-badge__item--active]=\"activeBadgeType === 'interaction'\"\n [title]=\"triggerLabel(ix.trigger) + ': ' + actionLabel(ix) + ' \u2014 click to edit, right-click to remove'\"\n (mousedown)=\"onEditInteractionPreset($event)\"\n (contextmenu)=\"onBadgeContextMenu($event, 'interaction', ix)\">\n <i [class]=\"triggerIcon(ix.trigger)\"></i>\n <span>{{ triggerLabel(ix.trigger) }}: {{ actionLabel(ix) }}</span>\n </div>\n </div>\n\n <div class=\"select-toolbar\" (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n\n <!-- Component name -->\n <span class=\"toolbar-name\">{{ selectEntry?.componentName }}</span>\n <span class=\"toolbar-sep\"></span>\n\n <!-- Quick actions -->\n <button *ngIf=\"isSliderContainer\" class=\"toolbar-btn toolbar-btn--add-slide\" title=\"Add slide\" (mousedown)=\"onAddSlide($event)\">\n <i class=\"ph-thin ph-plus\"></i>\n <span class=\"toolbar-btn__label\">Add slide</span>\n </button>\n <button class=\"toolbar-btn\" title=\"Duplicate\" (mousedown)=\"onDuplicate($event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n <button *ngIf=\"hasInteractions\" class=\"toolbar-btn toolbar-btn--play\" title=\"Play animations\" (mousedown)=\"onPlay($event)\">\n <i class=\"ph-thin ph-play\"></i>\n </button>\n <button *ngIf=\"isLinkedSymbol\" class=\"toolbar-btn toolbar-btn--symbol\"\n [title]=\"(linkedSymbolName ? linkedSymbolName : 'Symbol') + ' \u2014 click to detach'\"\n (mousedown)=\"onDetachSymbol($event)\">\n <i class=\"ph-thin ph-link\"></i>\n <span class=\"toolbar-btn__label\">{{ linkedSymbolName || 'Symbol' }}</span>\n </button>\n <button *ngIf=\"isDetachedSymbol\" class=\"toolbar-btn toolbar-btn--resync\"\n title=\"Detached \u2014 click to re-link\"\n (mousedown)=\"onResyncSymbol($event)\">\n <i class=\"ph-thin ph-link-break\"></i>\n <span class=\"toolbar-btn__label\">Detached</span>\n </button>\n <button class=\"toolbar-btn toolbar-btn--danger\" title=\"Delete\" (mousedown)=\"onDelete($event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n <span class=\"toolbar-sep\"></span>\n <button class=\"toolbar-btn\" title=\"Select parent\" (mousedown)=\"onSelectParent($event)\">\n <i class=\"ph-thin ph-arrow-up\"></i>\n </button>\n\n <!-- More menu trigger \u2014 opens a fixed-position dropdown at the cursor -->\n <button class=\"toolbar-btn toolbar-btn--more\" title=\"More actions\" (mousedown)=\"onToggleMore($event)\">\n <i class=\"ph-thin ph-dots-three\"></i>\n </button>\n </div>\n </div>\n\n <!-- Shared more/context menu \u2014 fixed-position, opened by \u22EF button or right-click -->\n <div *ngIf=\"moreMenuVisible\" class=\"toolbar-more-dropdown\" [ngStyle]=\"moreMenuStyle\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <button class=\"more-item\" (mousedown)=\"onSelectParent($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-arrow-up\"></i>\n Select parent\n </button>\n <button class=\"more-item\" (mousedown)=\"onDuplicate($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-copy\"></i>\n Duplicate\n </button>\n <button class=\"more-item more-item--danger\" (mousedown)=\"onDelete($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-trash\"></i>\n Delete\n </button>\n <div class=\"more-divider\"></div>\n <button class=\"more-item\" (mousedown)=\"onCopyNode($event)\">\n <i class=\"ph-thin ph-scissors\"></i>\n Copy element\n </button>\n <button *ngIf=\"hasCopiedNode\" class=\"more-item\" (mousedown)=\"onPasteNode($event)\">\n <i class=\"ph-thin ph-clipboard\"></i>\n Paste element\n </button>\n <div class=\"more-divider\"></div>\n <button *ngIf=\"selectEntry?.node?.stylePresetId\" class=\"more-item\" (mousedown)=\"onCopyPreset($event)\">\n <i class=\"ph-thin ph-paint-bucket\"></i>\n Copy style preset\n </button>\n <button *ngIf=\"hasPresetInClipboard\" class=\"more-item\" (mousedown)=\"onPastePreset($event)\">\n <i class=\"ph-thin ph-paint-bucket\"></i>\n Paste style preset\n </button>\n <button *ngIf=\"selectEntry?.node?.interactionPresetId\" class=\"more-item\" (mousedown)=\"onCopyInteractionPreset($event)\">\n <i class=\"ph-thin ph-lightning\"></i>\n Copy interaction preset\n </button>\n <button *ngIf=\"hasInteractionPresetInClipboard\" class=\"more-item\" (mousedown)=\"onPasteInteractionPreset($event)\">\n <i class=\"ph-thin ph-lightning\"></i>\n Paste interaction preset\n </button>\n <div class=\"more-divider\"></div>\n <button *ngIf=\"isLinkedSymbol\" class=\"more-item\" (mousedown)=\"onDetachSymbol($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-link-break\"></i>\n Detach from symbol<ng-container *ngIf=\"linkedSymbolName\"> ({{ linkedSymbolName }})</ng-container>\n </button>\n <button *ngIf=\"isDetachedSymbol\" class=\"more-item more-item--resync\" (mousedown)=\"onResyncSymbol($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-link\"></i>\n Re-link to symbol\n </button>\n <button class=\"more-item\" (mousedown)=\"onSaveAsBlock($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-bookmark\"></i>\n Save as reusable block\n </button>\n <button *ngIf=\"hasInteractions\" class=\"more-item more-item--play\" (mousedown)=\"onPlay($event); closeMoreMenu()\">\n <i class=\"ph-thin ph-play\"></i>\n Play animations\n </button>\n <div class=\"more-divider\"></div>\n <ng-container *ngIf=\"!selectedNodeIsGlobal; else removeGlobalItem\">\n <button class=\"more-item\" (mousedown)=\"onMakeGlobal($event, 'before')\">\n <i class=\"ph-thin ph-globe\"></i>\n Make Global (Before Content)\n </button>\n <button class=\"more-item\" (mousedown)=\"onMakeGlobal($event, 'after')\">\n <i class=\"ph-thin ph-globe\"></i>\n Make Global (After Content)\n </button>\n </ng-container>\n <ng-template #removeGlobalItem>\n <button class=\"more-item\" (mousedown)=\"onRemoveGlobal($event)\">\n <i class=\"ph-thin ph-globe\"></i>\n Remove from Global\n </button>\n </ng-template>\n </div>\n\n <!-- Backdrop to close the more menu -->\n <div *ngIf=\"moreMenuVisible\" class=\"overlay-context-backdrop\"\n (mousedown)=\"closeMoreMenu()\">\n </div>\n\n <!-- Badge context menu \u2014 options differ by badge type -->\n <div *ngIf=\"badgeMenuVisible\" class=\"badge-context-menu\"\n [ngStyle]=\"{ top: badgeMenuPos.y + 'px', left: badgeMenuPos.x + 'px' }\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <ng-container *ngIf=\"badgeMenuType === 'style'\">\n <button class=\"more-item\" (mousedown)=\"onBadgeCopy()\">\n <i class=\"ph-thin ph-copy\"></i>\n Copy preset\n </button>\n <button class=\"more-item\" (mousedown)=\"onBadgeEdit()\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n Edit preset\n </button>\n </ng-container>\n <ng-container *ngIf=\"badgeMenuType === 'interaction'\">\n <button class=\"more-item\" (mousedown)=\"onBadgeEdit()\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n Edit in panel\n </button>\n <button class=\"more-item more-item--danger\" (mousedown)=\"onBadgeRemoveInteraction()\">\n <i class=\"ph-thin ph-trash\"></i>\n Remove interaction\n </button>\n </ng-container>\n </div>\n <div *ngIf=\"badgeMenuVisible\" class=\"overlay-context-backdrop\"\n (mousedown)=\"closeBadgeMenu()\">\n </div>\n\n</div>\n", styles: [":host{display:block;position:absolute;inset:0;pointer-events:none;z-index:12;overflow:visible}.hi-outline,.hi-margin,.hi-padding{position:fixed;pointer-events:none;transition:none;box-sizing:border-box}.hi-outline{box-shadow:inset 0 0 0 1.5px #cb9090}.hi-margin{background:#f6b23361}.hi-padding{background:#60c38366}.select-outline{position:fixed;z-index:999;box-shadow:inset 0 0 0 1px #cb9090;pointer-events:none;transition:none;overflow:visible;will-change:transform;box-sizing:border-box}.select-toolbar{position:absolute;top:0;left:0;transform:translateY(-100%);display:flex;align-items:center;background:#cb9090;border-radius:3px 3px 0 0;pointer-events:all;white-space:nowrap;overflow:visible}.select-toolbar .toolbar-name{padding:0 8px;font-size:11px;font-weight:500;color:#fff;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:22px}.select-toolbar .toolbar-sep{width:1px;height:14px;background:#ffffff59;margin:0 2px;flex-shrink:0}.select-toolbar .toolbar-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;padding:0;border:none;background:transparent;color:#fff;font-size:13px;cursor:pointer;transition:background .15s;flex-shrink:0}.select-toolbar .toolbar-btn:hover{background:#ffffff2e}.select-toolbar .toolbar-btn--danger:hover{background:#c8323259}.select-toolbar .toolbar-btn--symbol{width:auto;padding:0 7px;gap:4px;color:#fff;background:#f59e0b;border-radius:3px;font-size:11px}.select-toolbar .toolbar-btn--symbol:hover{background:#d97706}.select-toolbar .toolbar-btn--resync{width:auto;padding:0 7px;gap:4px;color:#fff;background:#22c55e;border-radius:3px;font-size:11px}.select-toolbar .toolbar-btn--resync:hover{background:#16a34a}.select-toolbar .toolbar-btn .toolbar-btn__label{font-size:11px;max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-toolbar .toolbar-btn--more{border-radius:0 3px 0 0}.toolbar-more-dropdown{position:fixed;z-index:1000;background:#fff;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:6px;box-shadow:0 4px 16px #0000001f;padding:4px 0;min-width:200px;pointer-events:all}.toolbar-more-dropdown .more-item{display:flex;align-items:center;gap:8px;width:100%;padding:8px 14px;border:none;background:transparent;font-size:13px;color:var(--p-text-color, #333);cursor:pointer;text-align:left;white-space:nowrap}.toolbar-more-dropdown .more-item i{font-size:14px}.toolbar-more-dropdown .more-item:hover{background:var(--p-surface-100, #f5f5f5)}.toolbar-more-dropdown .more-item--danger{color:#c84040}.toolbar-more-dropdown .more-item--danger:hover{background:#c8404014}.toolbar-more-dropdown .more-item--play{color:var(--p-text-color, #333)}.toolbar-more-dropdown .more-item--resync{color:#22c55e}.toolbar-more-dropdown .more-item--resync:hover{background:#22c55e14}.toolbar-more-dropdown .more-divider{height:1px;background:var(--p-surface-200, #e5e7eb);margin:4px 0}.preset-badge{position:absolute;bottom:-26px;inset-inline-start:0;display:flex;align-items:center;gap:4px;pointer-events:none;white-space:nowrap}.preset-badge__item{display:flex;align-items:center;gap:5px;padding:3px 9px;background:#cb9090;color:#fff;font-size:11px;font-weight:500;border-radius:20px;pointer-events:all;cursor:pointer;box-shadow:0 2px 6px #0000002e;transition:background .15s,outline-color .15s;outline:2px solid transparent;outline-offset:2px}.preset-badge__item i{font-size:12px}.preset-badge__item:hover{background:#be7474}.preset-badge__item--active{outline-color:#ffffffd9}.preset-badge__item--interaction{background:#7a9ecb}.preset-badge__item--interaction:hover{background:#5d89c0}.badge-context-menu{position:fixed;z-index:1001;background:#fff;border:1px solid var(--p-surface-200, #e5e7eb);border-radius:6px;box-shadow:0 4px 16px #0000001f;padding:4px 0;min-width:160px;pointer-events:all}.badge-context-menu .more-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 14px;border:none;background:transparent;font-size:12px;color:#333;cursor:pointer;text-align:start;white-space:nowrap}.badge-context-menu .more-item:hover{background:var(--p-surface-50, #f9fafb)}.badge-context-menu .more-item i{font-size:13px;color:#888}.overlay-context-backdrop{position:fixed;inset:0;z-index:998;pointer-events:all}\n"] }]
3248
3315
  }], ctorParameters: () => [{ type: OverlayService }, { type: ViewportService }, { type: PresetRegistryService }, { type: SymbolRegistryService }, { type: InteractionPresetRegistryService }, { type: InteractionEngineService }, { type: BuilderClipboardService }, { type: StyleRegistryService }, { type: PanelEventService }, { type: i0.ChangeDetectorRef }], propDecorators: { toolbarAction: [{
3249
3316
  type: Output
3250
3317
  }] } });
@@ -4363,7 +4430,7 @@ class BuilderComponent {
4363
4430
  }
4364
4431
  }
4365
4432
  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 }); }
4366
- 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:#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:220px;min-width:220px;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}\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"] }] }); }
4433
+ 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:#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:220px;min-width:220px;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}\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: 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: 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"] }] }); }
4367
4434
  }
4368
4435
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: BuilderComponent, decorators: [{
4369
4436
  type: Component,
@@ -5289,6 +5356,538 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
5289
5356
  type: Output
5290
5357
  }] } });
5291
5358
 
5359
+ class SliderContainerComponent {
5360
+ constructor(dragEngine, cdr, isPreview) {
5361
+ this.dragEngine = dragEngine;
5362
+ this.cdr = cdr;
5363
+ this.isPreview = isPreview;
5364
+ this.children = [];
5365
+ this.style = {};
5366
+ this.nodeId = '';
5367
+ this.dropListId = '';
5368
+ // Swiper core config
5369
+ this.direction = 'horizontal';
5370
+ this.loop = false;
5371
+ this.slidesPerView = 1;
5372
+ this.spaceBetween = 0;
5373
+ this.speed = 300;
5374
+ this.effect = 'slide';
5375
+ this.grabCursor = true;
5376
+ this.centeredSlides = false;
5377
+ this.initialSlide = 0;
5378
+ // Module flags
5379
+ this.enableNavigation = false;
5380
+ this.enablePagination = false;
5381
+ this.enableScrollbar = false;
5382
+ this.enableAutoplay = false;
5383
+ this.enableKeyboard = false;
5384
+ this.enableMousewheel = false;
5385
+ this.enableZoom = false;
5386
+ this.enableGrid = false;
5387
+ this.enableParallax = false;
5388
+ // Autoplay sub-options
5389
+ this.autoplayDelay = 3000;
5390
+ this.autoplayPauseOnMouseEnter = true;
5391
+ this.autoplayDisableOnInteraction = false;
5392
+ // Pagination sub-options
5393
+ this.paginationType = 'bullets';
5394
+ this.paginationClickable = true;
5395
+ // Grid sub-options
5396
+ this.gridRows = 1;
5397
+ // Responsive breakpoints (JSON string)
5398
+ this.breakpoints = '';
5399
+ this.childClick = new EventEmitter();
5400
+ this.childMouseEnter = new EventEmitter();
5401
+ this.childMouseLeave = new EventEmitter();
5402
+ this._swiper = null;
5403
+ this._initRafId = 0;
5404
+ this.instanceRegistry = inject(ComponentInstanceRegistryService);
5405
+ this.interactionEngine = inject(InteractionEngineService);
5406
+ this.isPreview = !!isPreview;
5407
+ }
5408
+ ngOnInit() { }
5409
+ ngOnChanges(changes) {
5410
+ if (!this.isPreview || !this._swiper)
5411
+ return;
5412
+ // Re-init Swiper when config inputs change in preview mode
5413
+ const swiperKeys = new Set([
5414
+ 'direction', 'loop', 'slidesPerView', 'spaceBetween', 'speed',
5415
+ 'effect', 'grabCursor', 'centeredSlides', 'initialSlide', 'breakpoints',
5416
+ 'enableNavigation', 'enablePagination', 'enableScrollbar', 'enableAutoplay',
5417
+ 'enableKeyboard', 'enableMousewheel', 'enableZoom', 'enableGrid', 'enableParallax',
5418
+ 'autoplayDelay', 'autoplayPauseOnMouseEnter', 'autoplayDisableOnInteraction',
5419
+ 'paginationType', 'paginationClickable', 'gridRows',
5420
+ ]);
5421
+ const hasConfigChange = Object.keys(changes).some(k => swiperKeys.has(k));
5422
+ if (hasConfigChange) {
5423
+ this._destroySwiper();
5424
+ this.cdr.detectChanges();
5425
+ this._initRafId = requestAnimationFrame(() => this._initSwiper());
5426
+ }
5427
+ }
5428
+ ngAfterViewInit() {
5429
+ if (this.isPreview) {
5430
+ this._initRafId = requestAnimationFrame(() => this._initSwiper());
5431
+ }
5432
+ }
5433
+ ngOnDestroy() {
5434
+ cancelAnimationFrame(this._initRafId);
5435
+ this._destroySwiper();
5436
+ }
5437
+ onDrop(event) {
5438
+ this.dragEngine.handleDrop(this.children, event, this.dropListId, this.cdr);
5439
+ }
5440
+ async _initSwiper() {
5441
+ if (!this.swiperEl?.nativeElement)
5442
+ return;
5443
+ const [{ default: Swiper }, swiperModules] = await Promise.all([
5444
+ import('swiper'),
5445
+ import('swiper/modules'),
5446
+ ]);
5447
+ const { Navigation, Pagination, Scrollbar, Autoplay, Keyboard, Mousewheel, Zoom, Grid, Parallax, EffectFade, EffectCube, EffectFlip, EffectCoverflow, EffectCards, EffectCreative, } = swiperModules;
5448
+ const modules = [];
5449
+ if (this.enableNavigation)
5450
+ modules.push(Navigation);
5451
+ if (this.enablePagination)
5452
+ modules.push(Pagination);
5453
+ if (this.enableScrollbar)
5454
+ modules.push(Scrollbar);
5455
+ if (this.enableAutoplay)
5456
+ modules.push(Autoplay);
5457
+ if (this.enableKeyboard)
5458
+ modules.push(Keyboard);
5459
+ if (this.enableMousewheel)
5460
+ modules.push(Mousewheel);
5461
+ if (this.enableZoom)
5462
+ modules.push(Zoom);
5463
+ if (this.enableGrid)
5464
+ modules.push(Grid);
5465
+ if (this.enableParallax)
5466
+ modules.push(Parallax);
5467
+ const effectMap = {
5468
+ fade: EffectFade, cube: EffectCube, flip: EffectFlip,
5469
+ coverflow: EffectCoverflow, cards: EffectCards, creative: EffectCreative,
5470
+ };
5471
+ if (this.effect !== 'slide' && effectMap[this.effect]) {
5472
+ modules.push(effectMap[this.effect]);
5473
+ }
5474
+ const config = {
5475
+ modules,
5476
+ direction: this.direction,
5477
+ loop: this.loop,
5478
+ slidesPerView: this.slidesPerView,
5479
+ spaceBetween: this.spaceBetween,
5480
+ speed: this.speed,
5481
+ effect: this.effect,
5482
+ grabCursor: this.grabCursor,
5483
+ centeredSlides: this.centeredSlides,
5484
+ initialSlide: this.initialSlide,
5485
+ };
5486
+ if (this.enableNavigation) {
5487
+ config['navigation'] = { prevEl: '.swiper-button-prev', nextEl: '.swiper-button-next' };
5488
+ }
5489
+ if (this.enablePagination) {
5490
+ config['pagination'] = { el: '.swiper-pagination', type: this.paginationType, clickable: this.paginationClickable };
5491
+ }
5492
+ if (this.enableScrollbar) {
5493
+ config['scrollbar'] = { el: '.swiper-scrollbar', draggable: true };
5494
+ }
5495
+ if (this.enableAutoplay) {
5496
+ config['autoplay'] = {
5497
+ delay: this.autoplayDelay,
5498
+ pauseOnMouseEnter: this.autoplayPauseOnMouseEnter,
5499
+ disableOnInteraction: this.autoplayDisableOnInteraction,
5500
+ };
5501
+ }
5502
+ if (this.enableGrid && this.gridRows > 1) {
5503
+ config['grid'] = { rows: this.gridRows };
5504
+ }
5505
+ if (this.breakpoints) {
5506
+ try {
5507
+ config['breakpoints'] = JSON.parse(this.breakpoints);
5508
+ }
5509
+ catch { /* ignore invalid JSON */ }
5510
+ }
5511
+ config['on'] = {
5512
+ slideChange: (swiper) => this._onSwiperEvent('swiperSlideChange', swiper),
5513
+ reachBeginning: (swiper) => this._onSwiperEvent('swiperReachBeginning', swiper),
5514
+ reachEnd: (swiper) => this._onSwiperEvent('swiperReachEnd', swiper),
5515
+ autoplayStart: (swiper) => this._onSwiperEvent('swiperAutoplayStart', swiper),
5516
+ autoplayStop: (swiper) => this._onSwiperEvent('swiperAutoplayStop', swiper),
5517
+ };
5518
+ this._swiper = new Swiper(this.swiperEl.nativeElement, config);
5519
+ this.instanceRegistry.register(this.nodeId, this._swiper);
5520
+ }
5521
+ _destroySwiper() {
5522
+ if (this._swiper) {
5523
+ this._swiper.destroy(true, true);
5524
+ this._swiper = null;
5525
+ this.instanceRegistry.unregister(this.nodeId);
5526
+ }
5527
+ }
5528
+ _onSwiperEvent(eventName, swiper) {
5529
+ this.interactionEngine.replayTrigger(this.nodeId, eventName);
5530
+ if (eventName === 'swiperSlideChange') {
5531
+ const prevIdx = swiper.previousIndex;
5532
+ const activeIdx = swiper.activeIndex;
5533
+ if (prevIdx !== undefined && this.children[prevIdx]) {
5534
+ this.interactionEngine.replayTrigger(this.children[prevIdx].id, 'swiperSlideInactive');
5535
+ }
5536
+ if (this.children[activeIdx]) {
5537
+ this.interactionEngine.replayTrigger(this.children[activeIdx].id, 'swiperSlideActive');
5538
+ }
5539
+ }
5540
+ }
5541
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SliderContainerComponent, deps: [{ token: DragEngineService }, { token: i0.ChangeDetectorRef }, { token: IS_PREVIEW, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
5542
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: SliderContainerComponent, isStandalone: false, selector: "app-slider-container", inputs: { children: "children", style: "style", nodeId: "nodeId", dropListId: "dropListId", direction: "direction", loop: "loop", slidesPerView: "slidesPerView", spaceBetween: "spaceBetween", speed: "speed", effect: "effect", grabCursor: "grabCursor", centeredSlides: "centeredSlides", initialSlide: "initialSlide", enableNavigation: "enableNavigation", enablePagination: "enablePagination", enableScrollbar: "enableScrollbar", enableAutoplay: "enableAutoplay", enableKeyboard: "enableKeyboard", enableMousewheel: "enableMousewheel", enableZoom: "enableZoom", enableGrid: "enableGrid", enableParallax: "enableParallax", autoplayDelay: "autoplayDelay", autoplayPauseOnMouseEnter: "autoplayPauseOnMouseEnter", autoplayDisableOnInteraction: "autoplayDisableOnInteraction", paginationType: "paginationType", paginationClickable: "paginationClickable", gridRows: "gridRows", breakpoints: "breakpoints" }, outputs: { childClick: "childClick", childMouseEnter: "childMouseEnter", childMouseLeave: "childMouseLeave" }, viewQueries: [{ propertyName: "swiperEl", first: true, predicate: ["swiperEl"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
5543
+ <!-- EDIT MODE: flat scrollable strip with drag-drop -->
5544
+ <div *ngIf="!isPreview"
5545
+ class="slider-edit-root"
5546
+ [ngClass]="['iox-node-' + nodeId]"
5547
+ [ngStyle]="style"
5548
+ ioxDropzone
5549
+ [ioxDropzoneId]="dropListId"
5550
+ [ioxDropzoneData]="children"
5551
+ (ioxDrop)="onDrop($event)">
5552
+
5553
+ <div class="slider-edit-track">
5554
+ <div *ngFor="let child of children"
5555
+ class="slider-edit-slide"
5556
+ [ngClass]="['iox-outer-' + (child.styleId ?? child.id)]"
5557
+ ioxDraggable
5558
+ [ioxDragData]="child"
5559
+ [ioxDragSourceId]="dropListId">
5560
+ <ng-container
5561
+ [ioxRender]="child"
5562
+ (onClick)="childClick.emit($event)"
5563
+ (onMouseEnter)="childMouseEnter.emit($event)"
5564
+ (onMouseLeave)="childMouseLeave.emit()">
5565
+ </ng-container>
5566
+ </div>
5567
+
5568
+ <div *ngIf="!children.length" class="slider-edit-placeholder">
5569
+ <i class="ph-thin ph-sliders-horizontal"></i>
5570
+ <span>Click + in toolbar to add a slide</span>
5571
+ </div>
5572
+ </div>
5573
+ </div>
5574
+
5575
+ <!-- PREVIEW MODE: real Swiper DOM structure -->
5576
+ <div *ngIf="isPreview"
5577
+ #swiperEl
5578
+ class="swiper"
5579
+ [ngClass]="['iox-node-' + nodeId]"
5580
+ [ngStyle]="style">
5581
+ <div class="swiper-wrapper">
5582
+ <ng-container *ngFor="let child of children">
5583
+ <ng-container
5584
+ [ioxRender]="child"
5585
+ (onClick)="childClick.emit($event)"
5586
+ (onMouseEnter)="childMouseEnter.emit($event)"
5587
+ (onMouseLeave)="childMouseLeave.emit()">
5588
+ </ng-container>
5589
+ </ng-container>
5590
+ </div>
5591
+ <div *ngIf="enableNavigation" class="swiper-button-prev"></div>
5592
+ <div *ngIf="enableNavigation" class="swiper-button-next"></div>
5593
+ <div *ngIf="enablePagination" class="swiper-pagination"></div>
5594
+ <div *ngIf="enableScrollbar" class="swiper-scrollbar"></div>
5595
+ </div>
5596
+ `, isInline: true, styles: [".slider-edit-root{position:relative;box-sizing:border-box;overflow:hidden}.slider-edit-track{display:flex;flex-direction:row;gap:8px;overflow-x:auto;padding:4px;scrollbar-width:thin;min-height:100px}.slider-edit-slide{flex:0 0 auto;min-width:120px;position:relative}.slider-edit-placeholder{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;border:1.5px dashed rgba(100,116,139,.35);border-radius:4px;color:#64748b80;font-size:12px;pointer-events:none;-webkit-user-select:none;user-select:none}.slider-edit-placeholder i{font-size:22px}.swiper{width:100%;height:100%;box-sizing:border-box}\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: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: RenderDirective, selector: "[ioxRender]", inputs: ["ioxRender"], outputs: ["onClick", "onMouseEnter", "onMouseLeave"] }, { kind: "directive", type: IoxDraggableDirective, selector: "[ioxDraggable]", inputs: ["ioxDragData", "ioxDragSourceId"] }, { kind: "directive", type: IoxDropzoneDirective, selector: "[ioxDropzone]", inputs: ["ioxDropzoneId", "ioxDropzoneData", "ioxDropzonePostDrop"], outputs: ["ioxDrop"] }] }); }
5597
+ }
5598
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SliderContainerComponent, decorators: [{
5599
+ type: Component,
5600
+ args: [{ selector: 'app-slider-container', template: `
5601
+ <!-- EDIT MODE: flat scrollable strip with drag-drop -->
5602
+ <div *ngIf="!isPreview"
5603
+ class="slider-edit-root"
5604
+ [ngClass]="['iox-node-' + nodeId]"
5605
+ [ngStyle]="style"
5606
+ ioxDropzone
5607
+ [ioxDropzoneId]="dropListId"
5608
+ [ioxDropzoneData]="children"
5609
+ (ioxDrop)="onDrop($event)">
5610
+
5611
+ <div class="slider-edit-track">
5612
+ <div *ngFor="let child of children"
5613
+ class="slider-edit-slide"
5614
+ [ngClass]="['iox-outer-' + (child.styleId ?? child.id)]"
5615
+ ioxDraggable
5616
+ [ioxDragData]="child"
5617
+ [ioxDragSourceId]="dropListId">
5618
+ <ng-container
5619
+ [ioxRender]="child"
5620
+ (onClick)="childClick.emit($event)"
5621
+ (onMouseEnter)="childMouseEnter.emit($event)"
5622
+ (onMouseLeave)="childMouseLeave.emit()">
5623
+ </ng-container>
5624
+ </div>
5625
+
5626
+ <div *ngIf="!children.length" class="slider-edit-placeholder">
5627
+ <i class="ph-thin ph-sliders-horizontal"></i>
5628
+ <span>Click + in toolbar to add a slide</span>
5629
+ </div>
5630
+ </div>
5631
+ </div>
5632
+
5633
+ <!-- PREVIEW MODE: real Swiper DOM structure -->
5634
+ <div *ngIf="isPreview"
5635
+ #swiperEl
5636
+ class="swiper"
5637
+ [ngClass]="['iox-node-' + nodeId]"
5638
+ [ngStyle]="style">
5639
+ <div class="swiper-wrapper">
5640
+ <ng-container *ngFor="let child of children">
5641
+ <ng-container
5642
+ [ioxRender]="child"
5643
+ (onClick)="childClick.emit($event)"
5644
+ (onMouseEnter)="childMouseEnter.emit($event)"
5645
+ (onMouseLeave)="childMouseLeave.emit()">
5646
+ </ng-container>
5647
+ </ng-container>
5648
+ </div>
5649
+ <div *ngIf="enableNavigation" class="swiper-button-prev"></div>
5650
+ <div *ngIf="enableNavigation" class="swiper-button-next"></div>
5651
+ <div *ngIf="enablePagination" class="swiper-pagination"></div>
5652
+ <div *ngIf="enableScrollbar" class="swiper-scrollbar"></div>
5653
+ </div>
5654
+ `, standalone: false, styles: [".slider-edit-root{position:relative;box-sizing:border-box;overflow:hidden}.slider-edit-track{display:flex;flex-direction:row;gap:8px;overflow-x:auto;padding:4px;scrollbar-width:thin;min-height:100px}.slider-edit-slide{flex:0 0 auto;min-width:120px;position:relative}.slider-edit-placeholder{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;border:1.5px dashed rgba(100,116,139,.35);border-radius:4px;color:#64748b80;font-size:12px;pointer-events:none;-webkit-user-select:none;user-select:none}.slider-edit-placeholder i{font-size:22px}.swiper{width:100%;height:100%;box-sizing:border-box}\n"] }]
5655
+ }], ctorParameters: () => [{ type: DragEngineService }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
5656
+ type: Optional
5657
+ }, {
5658
+ type: Inject,
5659
+ args: [IS_PREVIEW]
5660
+ }] }], propDecorators: { children: [{
5661
+ type: Input
5662
+ }], style: [{
5663
+ type: Input
5664
+ }], nodeId: [{
5665
+ type: Input
5666
+ }], dropListId: [{
5667
+ type: Input
5668
+ }], direction: [{
5669
+ type: Input
5670
+ }], loop: [{
5671
+ type: Input
5672
+ }], slidesPerView: [{
5673
+ type: Input
5674
+ }], spaceBetween: [{
5675
+ type: Input
5676
+ }], speed: [{
5677
+ type: Input
5678
+ }], effect: [{
5679
+ type: Input
5680
+ }], grabCursor: [{
5681
+ type: Input
5682
+ }], centeredSlides: [{
5683
+ type: Input
5684
+ }], initialSlide: [{
5685
+ type: Input
5686
+ }], enableNavigation: [{
5687
+ type: Input
5688
+ }], enablePagination: [{
5689
+ type: Input
5690
+ }], enableScrollbar: [{
5691
+ type: Input
5692
+ }], enableAutoplay: [{
5693
+ type: Input
5694
+ }], enableKeyboard: [{
5695
+ type: Input
5696
+ }], enableMousewheel: [{
5697
+ type: Input
5698
+ }], enableZoom: [{
5699
+ type: Input
5700
+ }], enableGrid: [{
5701
+ type: Input
5702
+ }], enableParallax: [{
5703
+ type: Input
5704
+ }], autoplayDelay: [{
5705
+ type: Input
5706
+ }], autoplayPauseOnMouseEnter: [{
5707
+ type: Input
5708
+ }], autoplayDisableOnInteraction: [{
5709
+ type: Input
5710
+ }], paginationType: [{
5711
+ type: Input
5712
+ }], paginationClickable: [{
5713
+ type: Input
5714
+ }], gridRows: [{
5715
+ type: Input
5716
+ }], breakpoints: [{
5717
+ type: Input
5718
+ }], childClick: [{
5719
+ type: Output
5720
+ }], childMouseEnter: [{
5721
+ type: Output
5722
+ }], childMouseLeave: [{
5723
+ type: Output
5724
+ }], swiperEl: [{
5725
+ type: ViewChild,
5726
+ args: ['swiperEl', { static: false }]
5727
+ }] } });
5728
+
5729
+ class SliderSlideComponent {
5730
+ constructor(dragEngine, cdr, isPreview) {
5731
+ this.dragEngine = dragEngine;
5732
+ this.cdr = cdr;
5733
+ this.isPreview = isPreview;
5734
+ this.children = [];
5735
+ this.style = {};
5736
+ this.nodeId = '';
5737
+ this.dropListId = '';
5738
+ this.childClick = new EventEmitter();
5739
+ this.childMouseEnter = new EventEmitter();
5740
+ this.childMouseLeave = new EventEmitter();
5741
+ this.isPreview = !!isPreview;
5742
+ }
5743
+ ngOnInit() { }
5744
+ ngOnDestroy() { }
5745
+ onDrop(event) {
5746
+ this.dragEngine.handleDrop(this.children, event, this.dropListId, this.cdr);
5747
+ }
5748
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SliderSlideComponent, deps: [{ token: DragEngineService }, { token: i0.ChangeDetectorRef }, { token: IS_PREVIEW, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
5749
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: SliderSlideComponent, isStandalone: false, selector: "app-slider-slide", inputs: { children: "children", style: "style", nodeId: "nodeId", dropListId: "dropListId" }, outputs: { childClick: "childClick", childMouseEnter: "childMouseEnter", childMouseLeave: "childMouseLeave" }, ngImport: i0, template: `
5750
+ <!-- PREVIEW: native Swiper slide wrapper -->
5751
+ <div *ngIf="isPreview"
5752
+ class="swiper-slide"
5753
+ [ngClass]="['iox-node-' + nodeId]"
5754
+ [ngStyle]="style"
5755
+ ioxDropzone
5756
+ [ioxDropzoneId]="dropListId"
5757
+ [ioxDropzoneData]="children"
5758
+ (ioxDrop)="onDrop($event)">
5759
+
5760
+ <div *ngIf="!children.length" class="slide-placeholder">
5761
+ <i class="ph-thin ph-image"></i>
5762
+ <span>Drop components here</span>
5763
+ </div>
5764
+
5765
+ <div *ngFor="let child of children"
5766
+ [ngClass]="['iox-outer-' + (child.styleId ?? child.id)]"
5767
+ ioxDraggable
5768
+ [ioxDragData]="child"
5769
+ [ioxDragSourceId]="dropListId">
5770
+ <ng-container
5771
+ [ioxRender]="child"
5772
+ (onClick)="childClick.emit($event)"
5773
+ (onMouseEnter)="childMouseEnter.emit($event)"
5774
+ (onMouseLeave)="childMouseLeave.emit()">
5775
+ </ng-container>
5776
+ </div>
5777
+ </div>
5778
+
5779
+ <!-- EDIT: plain block so it sits in the horizontal strip -->
5780
+ <div *ngIf="!isPreview"
5781
+ class="slide-edit-block"
5782
+ [ngClass]="['iox-node-' + nodeId]"
5783
+ [ngStyle]="style"
5784
+ ioxDropzone
5785
+ [ioxDropzoneId]="dropListId"
5786
+ [ioxDropzoneData]="children"
5787
+ (ioxDrop)="onDrop($event)">
5788
+
5789
+ <div *ngIf="!children.length" class="slide-placeholder">
5790
+ <i class="ph-thin ph-image"></i>
5791
+ <span>Drop components here</span>
5792
+ </div>
5793
+
5794
+ <div *ngFor="let child of children"
5795
+ [ngClass]="['iox-outer-' + (child.styleId ?? child.id)]"
5796
+ ioxDraggable
5797
+ [ioxDragData]="child"
5798
+ [ioxDragSourceId]="dropListId">
5799
+ <ng-container
5800
+ [ioxRender]="child"
5801
+ (onClick)="childClick.emit($event)"
5802
+ (onMouseEnter)="childMouseEnter.emit($event)"
5803
+ (onMouseLeave)="childMouseLeave.emit()">
5804
+ </ng-container>
5805
+ </div>
5806
+ </div>
5807
+ `, isInline: true, styles: [".swiper-slide,.slide-edit-block{position:relative;box-sizing:border-box;min-height:200px}.slide-placeholder{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;border:1.5px dashed rgba(100,116,139,.35);border-radius:4px;color:#64748b80;font-size:12px;pointer-events:none;-webkit-user-select:none;user-select:none;opacity:0;transition:opacity .15s}:host:hover .slide-placeholder,:host.iox-drag-over .slide-placeholder{opacity:1}.slide-placeholder i{font-size:22px}\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: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: RenderDirective, selector: "[ioxRender]", inputs: ["ioxRender"], outputs: ["onClick", "onMouseEnter", "onMouseLeave"] }, { kind: "directive", type: IoxDraggableDirective, selector: "[ioxDraggable]", inputs: ["ioxDragData", "ioxDragSourceId"] }, { kind: "directive", type: IoxDropzoneDirective, selector: "[ioxDropzone]", inputs: ["ioxDropzoneId", "ioxDropzoneData", "ioxDropzonePostDrop"], outputs: ["ioxDrop"] }] }); }
5808
+ }
5809
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SliderSlideComponent, decorators: [{
5810
+ type: Component,
5811
+ args: [{ selector: 'app-slider-slide', template: `
5812
+ <!-- PREVIEW: native Swiper slide wrapper -->
5813
+ <div *ngIf="isPreview"
5814
+ class="swiper-slide"
5815
+ [ngClass]="['iox-node-' + nodeId]"
5816
+ [ngStyle]="style"
5817
+ ioxDropzone
5818
+ [ioxDropzoneId]="dropListId"
5819
+ [ioxDropzoneData]="children"
5820
+ (ioxDrop)="onDrop($event)">
5821
+
5822
+ <div *ngIf="!children.length" class="slide-placeholder">
5823
+ <i class="ph-thin ph-image"></i>
5824
+ <span>Drop components here</span>
5825
+ </div>
5826
+
5827
+ <div *ngFor="let child of children"
5828
+ [ngClass]="['iox-outer-' + (child.styleId ?? child.id)]"
5829
+ ioxDraggable
5830
+ [ioxDragData]="child"
5831
+ [ioxDragSourceId]="dropListId">
5832
+ <ng-container
5833
+ [ioxRender]="child"
5834
+ (onClick)="childClick.emit($event)"
5835
+ (onMouseEnter)="childMouseEnter.emit($event)"
5836
+ (onMouseLeave)="childMouseLeave.emit()">
5837
+ </ng-container>
5838
+ </div>
5839
+ </div>
5840
+
5841
+ <!-- EDIT: plain block so it sits in the horizontal strip -->
5842
+ <div *ngIf="!isPreview"
5843
+ class="slide-edit-block"
5844
+ [ngClass]="['iox-node-' + nodeId]"
5845
+ [ngStyle]="style"
5846
+ ioxDropzone
5847
+ [ioxDropzoneId]="dropListId"
5848
+ [ioxDropzoneData]="children"
5849
+ (ioxDrop)="onDrop($event)">
5850
+
5851
+ <div *ngIf="!children.length" class="slide-placeholder">
5852
+ <i class="ph-thin ph-image"></i>
5853
+ <span>Drop components here</span>
5854
+ </div>
5855
+
5856
+ <div *ngFor="let child of children"
5857
+ [ngClass]="['iox-outer-' + (child.styleId ?? child.id)]"
5858
+ ioxDraggable
5859
+ [ioxDragData]="child"
5860
+ [ioxDragSourceId]="dropListId">
5861
+ <ng-container
5862
+ [ioxRender]="child"
5863
+ (onClick)="childClick.emit($event)"
5864
+ (onMouseEnter)="childMouseEnter.emit($event)"
5865
+ (onMouseLeave)="childMouseLeave.emit()">
5866
+ </ng-container>
5867
+ </div>
5868
+ </div>
5869
+ `, standalone: false, styles: [".swiper-slide,.slide-edit-block{position:relative;box-sizing:border-box;min-height:200px}.slide-placeholder{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;border:1.5px dashed rgba(100,116,139,.35);border-radius:4px;color:#64748b80;font-size:12px;pointer-events:none;-webkit-user-select:none;user-select:none;opacity:0;transition:opacity .15s}:host:hover .slide-placeholder,:host.iox-drag-over .slide-placeholder{opacity:1}.slide-placeholder i{font-size:22px}\n"] }]
5870
+ }], ctorParameters: () => [{ type: DragEngineService }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
5871
+ type: Optional
5872
+ }, {
5873
+ type: Inject,
5874
+ args: [IS_PREVIEW]
5875
+ }] }], propDecorators: { children: [{
5876
+ type: Input
5877
+ }], style: [{
5878
+ type: Input
5879
+ }], nodeId: [{
5880
+ type: Input
5881
+ }], dropListId: [{
5882
+ type: Input
5883
+ }], childClick: [{
5884
+ type: Output
5885
+ }], childMouseEnter: [{
5886
+ type: Output
5887
+ }], childMouseLeave: [{
5888
+ type: Output
5889
+ }] } });
5890
+
5292
5891
  class PanelChildComponent {
5293
5892
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: PanelChildComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5294
5893
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: PanelChildComponent, isStandalone: false, selector: "panel-child", inputs: { name: "name" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["tpl"], descendants: true, static: true }], ngImport: i0, template: '<ng-template #tpl><ng-content></ng-content></ng-template>', isInline: true }); }
@@ -5340,11 +5939,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
5340
5939
  }] } });
5341
5940
 
5342
5941
  class InteractionsPanelComponent {
5343
- constructor(overlayService) {
5942
+ constructor(overlayService, componentRegistry) {
5344
5943
  this.overlayService = overlayService;
5944
+ this.componentRegistry = componentRegistry;
5345
5945
  this.node = null;
5346
- this.triggerOptions = TRIGGER_OPTIONS;
5347
- this.actionTypeOptions = ACTION_TYPE_OPTIONS;
5946
+ /** Base + component-specific trigger options for the selected node. */
5947
+ this.triggerOptions = [...TRIGGER_OPTIONS];
5948
+ /** Base + component-specific action type options. */
5949
+ this.actionTypeOptions = [...ACTION_TYPE_OPTIONS];
5348
5950
  this.easingOptions = EASING_OPTIONS;
5349
5951
  this.interactions = [];
5350
5952
  /** All canvas nodes available as animation targets. */
@@ -5358,9 +5960,17 @@ class InteractionsPanelComponent {
5358
5960
  this.newDelay = 0;
5359
5961
  this.newEasing = 'ease-out';
5360
5962
  this.newOnce = true;
5963
+ /** Slide index param for swiperGoToSlide action. */
5964
+ this.newSlideIndex = 0;
5361
5965
  /** Expanded interaction ID for editing */
5362
5966
  this.expandedId = null;
5363
5967
  }
5968
+ get isSwiperGoToSlide() {
5969
+ return this.newActionType === 'swiperGoToSlide';
5970
+ }
5971
+ get isSwiperAction() {
5972
+ return this.newActionType.startsWith('swiper');
5973
+ }
5364
5974
  ngOnInit() {
5365
5975
  this.selectSub = this.overlayService.select$.subscribe(entry => {
5366
5976
  this.node = entry?.node ?? null;
@@ -5368,8 +5978,40 @@ class InteractionsPanelComponent {
5368
5978
  this.isAdding = false;
5369
5979
  this.expandedId = null;
5370
5980
  this.refreshNodeOptions();
5981
+ this.refreshDynamicOptions();
5371
5982
  });
5372
5983
  }
5984
+ /** Merge component-specific events into trigger options and methods into action options. */
5985
+ refreshDynamicOptions() {
5986
+ // Build trigger options: base + events from selected node's component type
5987
+ const baseTriggers = [...TRIGGER_OPTIONS];
5988
+ if (this.node?.type) {
5989
+ const config = this.componentRegistry.createConfig(this.node.type);
5990
+ if (config?.componentEvents?.length) {
5991
+ for (const ev of config.componentEvents) {
5992
+ baseTriggers.push({ value: ev.value, label: ev.label, icon: ev.icon ?? 'ph-thin ph-lightning' });
5993
+ }
5994
+ }
5995
+ }
5996
+ this.triggerOptions = baseTriggers;
5997
+ // Build action options: base + methods from any SliderContainer-type labeled nodes in the tree
5998
+ const baseActions = [...ACTION_TYPE_OPTIONS];
5999
+ const swiperMethodsAdded = new Set();
6000
+ for (const [n] of this.overlayService.getAllNodeEntries()) {
6001
+ if (!n.id || !n.label)
6002
+ continue;
6003
+ const config = this.componentRegistry.createConfig(n.type);
6004
+ if (!config?.componentMethods?.length)
6005
+ continue;
6006
+ for (const method of config.componentMethods) {
6007
+ if (!swiperMethodsAdded.has(method.value)) {
6008
+ baseActions.push({ value: method.value, label: method.label });
6009
+ swiperMethodsAdded.add(method.value);
6010
+ }
6011
+ }
6012
+ }
6013
+ this.actionTypeOptions = baseActions;
6014
+ }
5373
6015
  refreshNodeOptions() {
5374
6016
  const opts = [{ label: 'Self', value: 'self' }];
5375
6017
  for (const [node] of this.overlayService.getAllNodeEntries()) {
@@ -5410,7 +6052,9 @@ class InteractionsPanelComponent {
5410
6052
  this.newDelay = 0;
5411
6053
  this.newEasing = 'ease-out';
5412
6054
  this.newOnce = true;
6055
+ this.newSlideIndex = 0;
5413
6056
  this.refreshNodeOptions();
6057
+ this.refreshDynamicOptions();
5414
6058
  }
5415
6059
  cancelAdd() {
5416
6060
  this.isAdding = false;
@@ -5418,6 +6062,9 @@ class InteractionsPanelComponent {
5418
6062
  confirmAdd() {
5419
6063
  if (!this.node)
5420
6064
  return;
6065
+ const params = this.isSwiperGoToSlide
6066
+ ? { index: this.newSlideIndex }
6067
+ : undefined;
5421
6068
  const action = {
5422
6069
  type: this.newActionType,
5423
6070
  target: this.newTarget,
@@ -5425,6 +6072,7 @@ class InteractionsPanelComponent {
5425
6072
  delay: this.newDelay,
5426
6073
  easing: this.newEasing,
5427
6074
  once: this.newOnce,
6075
+ ...(params ? { params } : {}),
5428
6076
  };
5429
6077
  const interaction = {
5430
6078
  id: generateNodeId(),
@@ -5500,13 +6148,13 @@ class InteractionsPanelComponent {
5500
6148
  interaction.reverseActions = undefined;
5501
6149
  }
5502
6150
  }
5503
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: InteractionsPanelComponent, deps: [{ token: OverlayService }], target: i0.ɵɵFactoryTarget.Component }); }
5504
- 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 <div class=\"ix-field\">\n <label>Target</label>\n <p-select\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 [(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 <div class=\"ix-field\">\n <label>Target</label>\n <p-select\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 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\">\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\">\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: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4$1.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"] }] }); }
6151
+ 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 }); }
6152
+ 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 <div class=\"ix-field\">\n <label>Target</label>\n <p-select\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 [(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 <div class=\"ix-field\">\n <label>Target</label>\n <p-select\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\" 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\">\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\">\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$1.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"] }] }); }
5505
6153
  }
5506
6154
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: InteractionsPanelComponent, decorators: [{
5507
6155
  type: Component,
5508
- 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 <div class=\"ix-field\">\n <label>Target</label>\n <p-select\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 [(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 <div class=\"ix-field\">\n <label>Target</label>\n <p-select\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 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\">\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\">\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"] }]
5509
- }], ctorParameters: () => [{ type: OverlayService }], propDecorators: { node: [{
6156
+ 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 <div class=\"ix-field\">\n <label>Target</label>\n <p-select\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 [(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 <div class=\"ix-field\">\n <label>Target</label>\n <p-select\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\" 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\">\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\">\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"] }]
6157
+ }], ctorParameters: () => [{ type: OverlayService }, { type: ComponentRegistryService }], propDecorators: { node: [{
5510
6158
  type: Input
5511
6159
  }] } });
5512
6160
 
@@ -5571,6 +6219,8 @@ const DECLARATIONS = [
5571
6219
  BuilderLinkedContainerComponent,
5572
6220
  BuilderRepeaterComponent,
5573
6221
  BuilderButtonBlockComponent,
6222
+ SliderContainerComponent,
6223
+ SliderSlideComponent,
5574
6224
  PanelComponent,
5575
6225
  PanelChildComponent,
5576
6226
  LayerTreeComponent,
@@ -5589,6 +6239,8 @@ class IoxBuilderModule {
5589
6239
  BuilderLinkedContainerComponent,
5590
6240
  BuilderRepeaterComponent,
5591
6241
  BuilderButtonBlockComponent,
6242
+ SliderContainerComponent,
6243
+ SliderSlideComponent,
5592
6244
  PanelComponent,
5593
6245
  PanelChildComponent,
5594
6246
  LayerTreeComponent,
@@ -5627,6 +6279,8 @@ class IoxBuilderModule {
5627
6279
  BuilderLinkedContainerComponent,
5628
6280
  BuilderRepeaterComponent,
5629
6281
  BuilderButtonBlockComponent,
6282
+ SliderContainerComponent,
6283
+ SliderSlideComponent,
5630
6284
  PanelComponent,
5631
6285
  PanelChildComponent,
5632
6286
  LayerTreeComponent,
@@ -5726,6 +6380,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
5726
6380
  BuilderLinkedContainerComponent,
5727
6381
  BuilderRepeaterComponent,
5728
6382
  BuilderButtonBlockComponent,
6383
+ SliderContainerComponent,
6384
+ SliderSlideComponent,
5729
6385
  PanelComponent,
5730
6386
  PanelChildComponent,
5731
6387
  LayerTreeComponent,
@@ -6073,6 +6729,117 @@ class SectionComponentConfig extends ComponentConfig {
6073
6729
  }
6074
6730
  }
6075
6731
 
6732
+ class SliderContainerConfig extends ComponentConfig {
6733
+ constructor() {
6734
+ super('SliderContainer', 'app-slider-container', 'ph-thin ph-slideshow', 'Media');
6735
+ this.children = [];
6736
+ this.componentEvents = [
6737
+ { value: 'swiperSlideChange', label: 'Slide Changes', icon: 'ph-thin ph-arrows-left-right' },
6738
+ { value: 'swiperReachBeginning', label: 'Reaches First Slide', icon: 'ph-thin ph-skip-back' },
6739
+ { value: 'swiperReachEnd', label: 'Reaches Last Slide', icon: 'ph-thin ph-skip-forward' },
6740
+ { value: 'swiperAutoplayStart', label: 'Autoplay Starts', icon: 'ph-thin ph-play' },
6741
+ { value: 'swiperAutoplayStop', label: 'Autoplay Stops', icon: 'ph-thin ph-stop' },
6742
+ ];
6743
+ this.componentMethods = [
6744
+ { value: 'swiperNext', label: 'Next Slide' },
6745
+ { value: 'swiperPrev', label: 'Previous Slide' },
6746
+ { value: 'swiperGoToSlide', label: 'Go to Slide', params: [{ name: 'index', type: 'number', label: 'Slide Index (0-based)' }] },
6747
+ { value: 'swiperAutoplayStart', label: 'Start Autoplay' },
6748
+ { value: 'swiperAutoplayStop', label: 'Stop Autoplay' },
6749
+ { value: 'swiperAutoplayPause', label: 'Pause Autoplay' },
6750
+ ];
6751
+ this.traits = [
6752
+ // ── Core ──────────────────────────────────────────
6753
+ new TraitConfig('direction', 'Direction', TraitInputType.Select, [{ label: 'Horizontal', value: 'horizontal' }, { label: 'Vertical', value: 'vertical' }], 'horizontal'),
6754
+ new TraitConfig('effect', 'Effect', TraitInputType.Select, [
6755
+ { label: 'Slide', value: 'slide' },
6756
+ { label: 'Fade', value: 'fade' },
6757
+ { label: 'Cube', value: 'cube' },
6758
+ { label: 'Coverflow', value: 'coverflow' },
6759
+ { label: 'Flip', value: 'flip' },
6760
+ { label: 'Cards', value: 'cards' },
6761
+ { label: 'Creative', value: 'creative' },
6762
+ ], 'slide'),
6763
+ new TraitConfig('slidesPerView', 'Slides per view', TraitInputType.Number, undefined, 1),
6764
+ new TraitConfig('spaceBetween', 'Space between (px)', TraitInputType.Number, undefined, 0),
6765
+ new TraitConfig('speed', 'Transition speed (ms)', TraitInputType.Number, undefined, 300),
6766
+ new TraitConfig('initialSlide', 'Initial slide', TraitInputType.Number, undefined, 0),
6767
+ new TraitConfig('loop', 'Loop', TraitInputType.Checkbox, undefined, false),
6768
+ new TraitConfig('centeredSlides', 'Centered slides', TraitInputType.Checkbox, undefined, false),
6769
+ new TraitConfig('grabCursor', 'Grab cursor', TraitInputType.Checkbox, undefined, true),
6770
+ // ── Section label ─────────────────────────────────
6771
+ new TraitConfig('__modulesLabel', 'Modules', TraitInputType.SectionLabel),
6772
+ // ── Modules ──────────────────────────────────────
6773
+ new TraitConfig('enableNavigation', 'Navigation arrows', TraitInputType.Checkbox, undefined, false),
6774
+ new TraitConfig('enablePagination', 'Pagination', TraitInputType.Checkbox, undefined, false),
6775
+ new TraitConfig('enableScrollbar', 'Scrollbar', TraitInputType.Checkbox, undefined, false),
6776
+ new TraitConfig('enableAutoplay', 'Autoplay', TraitInputType.Checkbox, undefined, false),
6777
+ new TraitConfig('enableKeyboard', 'Keyboard control', TraitInputType.Checkbox, undefined, false),
6778
+ new TraitConfig('enableMousewheel', 'Mousewheel control', TraitInputType.Checkbox, undefined, false),
6779
+ new TraitConfig('enableZoom', 'Zoom', TraitInputType.Checkbox, undefined, false),
6780
+ new TraitConfig('enableGrid', 'Grid (multi-row)', TraitInputType.Checkbox, undefined, false),
6781
+ new TraitConfig('enableParallax', 'Parallax', TraitInputType.Checkbox, undefined, false),
6782
+ // ── Section label ─────────────────────────────────
6783
+ new TraitConfig('__paginationLabel', 'Pagination options', TraitInputType.SectionLabel),
6784
+ // ── Pagination sub-options ────────────────────────
6785
+ new TraitConfig('paginationType', 'Type', TraitInputType.Select, [{ label: 'Bullets', value: 'bullets' }, { label: 'Fraction', value: 'fraction' }, { label: 'Progress bar', value: 'progressbar' }], 'bullets'),
6786
+ new TraitConfig('paginationClickable', 'Clickable', TraitInputType.Checkbox, undefined, true),
6787
+ // ── Section label ─────────────────────────────────
6788
+ new TraitConfig('__autoplayLabel', 'Autoplay options', TraitInputType.SectionLabel),
6789
+ // ── Autoplay sub-options ──────────────────────────
6790
+ new TraitConfig('autoplayDelay', 'Delay (ms)', TraitInputType.Number, undefined, 3000),
6791
+ new TraitConfig('autoplayPauseOnMouseEnter', 'Pause on hover', TraitInputType.Checkbox, undefined, true),
6792
+ new TraitConfig('autoplayDisableOnInteraction', 'Disable on interact', TraitInputType.Checkbox, undefined, false),
6793
+ // ── Section label ─────────────────────────────────
6794
+ new TraitConfig('__gridLabel', 'Grid options', TraitInputType.SectionLabel),
6795
+ // ── Grid sub-options ──────────────────────────────
6796
+ new TraitConfig('gridRows', 'Rows', TraitInputType.Number, undefined, 1),
6797
+ // ── Section label ─────────────────────────────────
6798
+ new TraitConfig('__responsiveLabel', 'Responsive breakpoints', TraitInputType.SectionLabel),
6799
+ // ── Responsive ────────────────────────────────────
6800
+ new TraitConfig('breakpoints', 'Breakpoints (JSON)', TraitInputType.Textarea, undefined, ''),
6801
+ ];
6802
+ this.styleTraits = [
6803
+ new PositionGroupStyleConfig(),
6804
+ new SizeGroupStyleConfig(),
6805
+ new SpacingGroupStyleConfig(),
6806
+ new BorderGroupStyleConfig(),
6807
+ new BackgroundGroupStyleConfig(),
6808
+ new EffectsGroupStyleConfig(),
6809
+ ];
6810
+ this.applyStyleDefaults({
6811
+ width: '100%',
6812
+ height: '400px',
6813
+ });
6814
+ }
6815
+ }
6816
+
6817
+ class SliderSlideConfig extends ComponentConfig {
6818
+ constructor() {
6819
+ super('SliderSlide', 'app-slider-slide', 'ph-thin ph-rectangle', 'Media');
6820
+ this.children = [];
6821
+ // Hidden: SliderSlide is created only via the "Add Slide" toolbar button,
6822
+ // not by dragging from the Components panel.
6823
+ this.hidden = true;
6824
+ this.componentEvents = [
6825
+ { value: 'swiperSlideActive', label: 'This Slide Becomes Active', icon: 'ph-thin ph-eye' },
6826
+ { value: 'swiperSlideInactive', label: 'This Slide Becomes Inactive', icon: 'ph-thin ph-eye-slash' },
6827
+ ];
6828
+ this.traits = [];
6829
+ this.styleTraits = [
6830
+ new PositionGroupStyleConfig(),
6831
+ new SizeGroupStyleConfig(),
6832
+ new SpacingGroupStyleConfig(),
6833
+ new BorderGroupStyleConfig(),
6834
+ new BackgroundGroupStyleConfig(),
6835
+ ];
6836
+ this.applyStyleDefaults({
6837
+ width: '100%',
6838
+ height: '100%',
6839
+ });
6840
+ }
6841
+ }
6842
+
6076
6843
  class BuilderSpacerComponentConfig extends ComponentConfig {
6077
6844
  constructor() {
6078
6845
  super('Spacer', 'app-builder-spacer', 'ph-thin ph-arrows-vertical', 'Layout');
@@ -6093,5 +6860,5 @@ class BuilderSpacerComponentConfig extends ComponentConfig {
6093
6860
  * Generated bundle index. Do not edit.
6094
6861
  */
6095
6862
 
6096
- export { ACTION_TYPE_OPTIONS, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CardComponentConfig, ComponentConfig, ComponentRegistryService, DEVICE_OPTIONS, DataSourceRegistryService, DataSourceType, DeviceMode, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, GroupStyleConfig, INTERACTION_STATES, INVERSE_ACTION, IOX_CONTENT_SERVICE, IOX_FONT_MANAGER, IOX_SHOP_SERVICE, InteractionEngineService, InteractionPresetRegistryService, InteractionsPanelComponent, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LayerTreeComponent, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PresetRegistryService, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, SCREEN_WIDTH_OPTIONS, STRUCTURAL_STATES, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, SymbolRegistryService, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, VIRTUAL_TRAIT_KEYS, ViewportService, ZOOM_OPTIONS, buildFullStyleTraits, buildPresetStyleTraits, composeVirtualTraits, defaultPageSettings, generateNodeId, resolveTraitControllerType, resolveTraitOptions };
6863
+ export { ACTION_TYPE_OPTIONS, BackgroundGroupStyleConfig, BorderGroupStyleConfig, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CardComponentConfig, ComponentConfig, ComponentRegistryService, DEVICE_OPTIONS, DataSourceRegistryService, DataSourceType, DeviceMode, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS, EffectsGroupStyleConfig, GroupStyleConfig, INTERACTION_STATES, INVERSE_ACTION, IOX_CONTENT_SERVICE, IOX_FONT_MANAGER, IOX_SHOP_SERVICE, InteractionEngineService, InteractionPresetRegistryService, InteractionsPanelComponent, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PositionGroupStyleConfig, PresetRegistryService, 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, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, TypographyGroupStyleConfig, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, VIRTUAL_TRAIT_KEYS, ViewportService, ZOOM_OPTIONS, buildFullStyleTraits, buildPresetStyleTraits, composeVirtualTraits, defaultPageSettings, flattenSchemaFields, generateNodeId, resolveTraitControllerType, resolveTraitOptions };
6097
6864
  //# sourceMappingURL=vectoriox-iox-builder.mjs.map