@acorex-platform/framework-client 0.0.38 → 0.0.46

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.
Files changed (103) hide show
  1. package/fesm2022/acorex-platform-framework-client-auth.mjs +212 -44
  2. package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-framework-client-common-common-settings.provider-CzBr0cRi.mjs → acorex-platform-framework-client-common-common-settings.provider-ChU2tvyt.mjs} +31 -1
  4. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-ChU2tvyt.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-framework-client-common.mjs +824 -72
  6. package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-framework-client-core.mjs +212 -94
  8. package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +292 -11
  10. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs.map +1 -1
  11. package/fesm2022/{acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-Bp2cNCPO.mjs → acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-BqPaHKPM.mjs} +10 -6
  12. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-BqPaHKPM.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-framework-client-layout-components-property-viewer-inline-code-editor.component-CcpUTEVQ.mjs +66 -0
  14. package/fesm2022/acorex-platform-framework-client-layout-components-property-viewer-inline-code-editor.component-CcpUTEVQ.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-framework-client-layout-components-resource-appointment-board.component-DCkpgsFH.mjs +195 -0
  16. package/fesm2022/acorex-platform-framework-client-layout-components-resource-appointment-board.component-DCkpgsFH.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-framework-client-layout-components.mjs +3987 -1020
  18. package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +1179 -304
  20. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs.map +1 -1
  21. package/fesm2022/{acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-CvaOWbJx.mjs → acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-Dc_NpBx6.mjs} +1619 -246
  22. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-Dc_NpBx6.mjs.map +1 -0
  23. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-Ds63Rt2n.mjs → acorex-platform-framework-client-layout-entity-attachments-page.component-Cx-n7ZuA.mjs} +2 -2
  24. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-Ds63Rt2n.mjs.map → acorex-platform-framework-client-layout-entity-attachments-page.component-Cx-n7ZuA.mjs.map} +1 -1
  25. package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-C0pohwdo.mjs → acorex-platform-framework-client-layout-entity-bulk-entity.command-BONV249y.mjs} +2 -2
  26. package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-C0pohwdo.mjs.map → acorex-platform-framework-client-layout-entity-bulk-entity.command-BONV249y.mjs.map} +1 -1
  27. package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-o2HuZrDN.mjs → acorex-platform-framework-client-layout-entity-gallery-page.component-uTG2nFly.mjs} +2 -2
  28. package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-o2HuZrDN.mjs.map → acorex-platform-framework-client-layout-entity-gallery-page.component-uTG2nFly.mjs.map} +1 -1
  29. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +1 -1
  30. package/fesm2022/acorex-platform-framework-client-layout-views.mjs +3 -3
  31. package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -1
  32. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +3854 -3278
  33. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -1
  34. package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-CDpzvn6z.mjs +33191 -0
  35. package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-CDpzvn6z.mjs.map +1 -0
  36. package/fesm2022/{acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BDOdcoyN.mjs → acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BkrPu1wO.mjs} +8 -5
  37. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BkrPu1wO.mjs.map +1 -0
  38. package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-value.util-Cb7R1qLO.mjs +45 -0
  39. package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-value.util-Cb7R1qLO.mjs.map +1 -0
  40. package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-column.component-BL621E1r.mjs +68 -0
  41. package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-column.component-BL621E1r.mjs.map +1 -0
  42. package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-edit.component-CVsPKhNl.mjs +88 -0
  43. package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-edit.component-CVsPKhNl.mjs.map +1 -0
  44. package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-view.component-BEq4O4al.mjs +71 -0
  45. package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-view.component-BEq4O4al.mjs.map +1 -0
  46. package/fesm2022/acorex-platform-framework-client-layout-widgets-condition-builder-edit-popup.component-WpiEHusZ.mjs +81 -0
  47. package/fesm2022/acorex-platform-framework-client-layout-widgets-condition-builder-edit-popup.component-WpiEHusZ.mjs.map +1 -0
  48. package/fesm2022/{acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs → acorex-platform-framework-client-layout-widgets-page-widget-designer.component-Cadl6guA.mjs} +41 -23
  49. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-Cadl6guA.mjs.map +1 -0
  50. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-4mX8aYzA.mjs → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-Cw3NhWL8.mjs} +3 -3
  51. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-4mX8aYzA.mjs.map → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-Cw3NhWL8.mjs.map} +1 -1
  52. package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-column.component-Clb27Loe.mjs +71 -0
  53. package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-column.component-Clb27Loe.mjs.map +1 -0
  54. package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-edit.component-l7q8A-D3.mjs +111 -0
  55. package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-edit.component-l7q8A-D3.mjs.map +1 -0
  56. package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-view.component-Cc1AH09b.mjs +77 -0
  57. package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-view.component-Cc1AH09b.mjs.map +1 -0
  58. package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-TWxthl21.mjs → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-BCLxS3rS.mjs} +3 -3
  59. package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-TWxthl21.mjs.map → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-BCLxS3rS.mjs.map} +1 -1
  60. package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-BEa-vD6O.mjs} +21 -8
  61. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-BEa-vD6O.mjs.map +1 -0
  62. package/fesm2022/acorex-platform-framework-client-layout-widgets-trigger-expression-editor-popup.component-C2PGoC7r.mjs +124 -0
  63. package/fesm2022/acorex-platform-framework-client-layout-widgets-trigger-expression-editor-popup.component-C2PGoC7r.mjs.map +1 -0
  64. package/fesm2022/acorex-platform-framework-client-layout-widgets-triggers-widget-editor.component-DDDoPoQa.mjs +345 -0
  65. package/fesm2022/acorex-platform-framework-client-layout-widgets-triggers-widget-editor.component-DDDoPoQa.mjs.map +1 -0
  66. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +1 -31985
  67. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -1
  68. package/fesm2022/acorex-platform-framework-client-realtime.mjs +2 -1
  69. package/fesm2022/acorex-platform-framework-client-realtime.mjs.map +1 -1
  70. package/fesm2022/acorex-platform-framework-client-runtime.mjs +2 -2
  71. package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -1
  72. package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-single-view.component-DBUwSzvg.mjs → acorex-platform-framework-client-themes-default-entity-master-single-view.component-CHLsBvBE.mjs} +2 -2
  73. package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-single-view.component-DBUwSzvg.mjs.map → acorex-platform-framework-client-themes-default-entity-master-single-view.component-CHLsBvBE.mjs.map} +1 -1
  74. package/fesm2022/acorex-platform-framework-client-themes-default.mjs +373 -70
  75. package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -1
  76. package/fesm2022/{acorex-platform-framework-client-themes-shared-settings.provider-CpsAIsxj.mjs → acorex-platform-framework-client-themes-shared-settings.provider-DIkjSTU7.mjs} +102 -1
  77. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DIkjSTU7.mjs.map +1 -0
  78. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +174 -22
  79. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -1
  80. package/package.json +2 -2
  81. package/realtime/README.md +1 -1
  82. package/types/acorex-platform-framework-client-auth.d.ts +18 -1
  83. package/types/acorex-platform-framework-client-common.d.ts +204 -8
  84. package/types/acorex-platform-framework-client-core.d.ts +149 -114
  85. package/types/acorex-platform-framework-client-layout-builder.d.ts +29 -0
  86. package/types/acorex-platform-framework-client-layout-components.d.ts +699 -146
  87. package/types/acorex-platform-framework-client-layout-designer.d.ts +192 -14
  88. package/types/acorex-platform-framework-client-layout-entity.d.ts +155 -91
  89. package/types/acorex-platform-framework-client-layout-views.d.ts +16 -0
  90. package/types/acorex-platform-framework-client-layout-widget-core.d.ts +140 -37
  91. package/types/acorex-platform-framework-client-layout-widgets.d.ts +235 -117
  92. package/types/acorex-platform-framework-client-realtime.d.ts +16 -3
  93. package/types/acorex-platform-framework-client-themes-default.d.ts +62 -11
  94. package/types/acorex-platform-framework-client-themes-shared.d.ts +25 -2
  95. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CzBr0cRi.mjs.map +0 -1
  96. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-Bp2cNCPO.mjs.map +0 -1
  97. package/fesm2022/acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs +0 -77
  98. package/fesm2022/acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs.map +0 -1
  99. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-CvaOWbJx.mjs.map +0 -1
  100. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BDOdcoyN.mjs.map +0 -1
  101. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs.map +0 -1
  102. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs.map +0 -1
  103. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-CpsAIsxj.mjs.map +0 -1
@@ -3,20 +3,20 @@ import { AXButtonModule } from '@acorex/components/button';
3
3
  import * as i1 from '@acorex/components/decorators';
4
4
  import { AXDecoratorModule } from '@acorex/components/decorators';
5
5
  import * as i1$3 from '@acorex-platform/framework-client/layout/widget-core';
6
- import { AXPWidgetRegistryService, AXPLayoutBaseWidgetComponent, AXPValueWidgetComponent, AXPWidgetCoreModule } from '@acorex-platform/framework-client/layout/widget-core';
6
+ import { AXPWidgetRegistryService, AXP_LOCAL_DATA_SOURCES_OPTION, createDefaultLocalDataSourceColumns, readLocalDataSourcesFromNode, AXPLayoutBaseWidgetComponent, AXPValueWidgetComponent, AXPWidgetCoreModule, AXP_LOCAL_DATA_SOURCE_STORE, AXP_LAYOUT_PROFILE } from '@acorex-platform/framework-client/layout/widget-core';
7
7
  import * as i0 from '@angular/core';
8
8
  import { signal, computed, Injectable, inject, effect, ViewEncapsulation, ChangeDetectionStrategy, Component, input, output, EventEmitter, HostBinding, Output, model, HostListener, Injector, viewChild, afterNextRender, NgZone, ViewContainerRef, Directive, ElementRef, ViewChild, DestroyRef } from '@angular/core';
9
- import { AXPWidgetGroupEnum, AXP_WIDGET_TOKEN, AXPWidgetStatus } from '@acorex-platform/framework-shared/widget-core';
10
9
  import { AXLoadingDialogService } from '@acorex/components/loading-dialog';
11
10
  import { AXPopupService } from '@acorex/components/popup';
12
11
  import { AXToastService } from '@acorex/components/toast';
13
12
  import { AXFileService } from '@acorex/core/file';
14
- import { AXPWidgetsCatalog, AXPPlatformScope, runtimeQueryCollectionData, AXPKeyboardShortcutPriority } from '@acorex-platform/framework-shared/core';
15
- import { merge, cloneDeep, assign, sortBy, get, set, isNil, isEmpty, orderBy, castArray, uniqBy, isEqual } from 'lodash-es';
13
+ import { AXPWidgetsCatalog, AXPPlatformScope, AXPKeyboardShortcutPriority } from '@acorex-platform/framework-shared/core';
14
+ import { AXPBroadcastEventService, AXPDataGenerator, AXPHookService, AXPKeyboardShortcutRegistry, chordToKbdItemKeys, AXPLogoComponent } from '@acorex-platform/framework-client/core';
15
+ import { isEqual, cloneDeep, assign, sortBy, get, set, isNil, isEmpty, orderBy, castArray, uniqBy, merge } from 'lodash-es';
16
16
  import { Subject, distinctUntilChanged, filter, debounceTime, first, merge as merge$1 } from 'rxjs';
17
- import { AXPBroadcastEventService, AXPHookService, AXPKeyboardShortcutRegistry, chordToKbdItemKeys } from '@acorex-platform/framework-client/core';
18
- import * as i8 from '@acorex/core/translation';
17
+ import * as i4 from '@acorex/core/translation';
19
18
  import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
19
+ import { AXPWidgetGroupEnum, AXP_WIDGET_TOKEN, AXPWidgetStatus, designerPreviewSizeToLayoutSize } from '@acorex-platform/framework-shared/widget-core';
20
20
  import * as i1$1 from '@acorex/cdk/common';
21
21
  import { AXCommonModule } from '@acorex/cdk/common';
22
22
  import { AXBasePageComponent } from '@acorex/components/page';
@@ -25,7 +25,7 @@ import { AXSearchBoxModule } from '@acorex/components/search-box';
25
25
  import * as i5 from '@acorex/components/tabs';
26
26
  import { AXTabsModule } from '@acorex/components/tabs';
27
27
  import { AXPSettingsService, AXPStickyDirective, AXPClipBoardService } from '@acorex-platform/framework-client/common';
28
- import { AXPWidgetItemComponent, AXPStateMessageComponent, AXPWidgetPropertyViewerComponent } from '@acorex-platform/framework-client/layout/components';
28
+ import { AXPWidgetItemComponent, AXPLocalDataSourceEditorDialogService, AXPStateMessageComponent, AXPWidgetPropertyViewerComponent } from '@acorex-platform/framework-client/layout/components';
29
29
  import * as i1$2 from '@angular/common';
30
30
  import { CommonModule, AsyncPipe } from '@angular/common';
31
31
  import * as i2$1 from '@angular/forms';
@@ -34,7 +34,7 @@ import * as i1$5 from '@acorex/components/button-group';
34
34
  import { AXButtonGroupModule } from '@acorex/components/button-group';
35
35
  import * as i3 from '@acorex/components/drawer';
36
36
  import { AXDrawerModule } from '@acorex/components/drawer';
37
- import * as i4 from '@acorex/components/dropdown';
37
+ import * as i4$1 from '@acorex/components/dropdown';
38
38
  import { AXDropdownModule } from '@acorex/components/dropdown';
39
39
  import * as i5$1 from '@acorex/components/dropdown-button';
40
40
  import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
@@ -42,14 +42,13 @@ import * as i2$2 from '@acorex/components/menu';
42
42
  import { AXMenuModule } from '@acorex/components/menu';
43
43
  import * as i1$4 from '@acorex/components/breadcrumbs';
44
44
  import { AXBreadcrumbsModule } from '@acorex/components/breadcrumbs';
45
- import { AXPCommandService, AXPQueryService } from '@acorex-platform/framework-client/runtime';
46
45
  import { AXKBDComponent, AXKBDItemComponent } from '@acorex/components/kbd';
47
46
  import { AXUnsubscriber } from '@acorex/core/utils';
48
- import * as i4$1 from '@acorex/components/json-viewer';
47
+ import * as i4$2 from '@acorex/components/json-viewer';
49
48
  import { AXJsonViewerModule } from '@acorex/components/json-viewer';
50
- import { AXSafePipe } from '@acorex/core/pipes';
51
49
 
52
- // AddWidgetCommand
50
+ //#endregion
51
+ //#region ----- Commands -----
53
52
  class AddWidgetCommand {
54
53
  constructor(widget, parent, previousSelection) {
55
54
  this.widget = widget;
@@ -74,10 +73,10 @@ class AddWidgetCommand {
74
73
  return `fa-solid fa-square-plus`;
75
74
  }
76
75
  getExecutionSelection() {
77
- return this.widget; // After executing, select the newly added widget
76
+ return this.widget;
78
77
  }
79
78
  getUndoSelection() {
80
- return this.previousSelection; // After undo, select the previously selected widget
79
+ return this.previousSelection;
81
80
  }
82
81
  }
83
82
  class RemoveWidgetCommand {
@@ -86,16 +85,19 @@ class RemoveWidgetCommand {
86
85
  this.parent = parent;
87
86
  this.previousSelection = previousSelection;
88
87
  parent.children = parent.children || [];
89
- this.index = parent.children.indexOf(widget); // Capture the index for undo purposes
88
+ this.index = parent.children.indexOf(widget);
90
89
  }
91
90
  execute() {
92
91
  this.parent.children = this.parent.children || [];
93
- this.index = this.parent.children.indexOf(this.widget); // Re-assert the index in case it has changed
94
- this.parent.children.splice(this.index, 1);
92
+ this.index = this.parent.children.indexOf(this.widget);
93
+ if (this.index > -1) {
94
+ this.parent.children.splice(this.index, 1);
95
+ }
95
96
  }
96
97
  undo() {
97
98
  this.parent.children = this.parent.children || [];
98
- this.parent.children.splice(this.index, 0, this.widget); // Reinsert at the original index
99
+ const insertAt = Math.min(Math.max(this.index, 0), this.parent.children.length);
100
+ this.parent.children.splice(insertAt, 0, this.widget);
99
101
  }
100
102
  description() {
101
103
  return `Removed widget ${this.widget.name}`;
@@ -104,24 +106,25 @@ class RemoveWidgetCommand {
104
106
  return `fa-solid fa-square-minus`;
105
107
  }
106
108
  getExecutionSelection() {
107
- return this.previousSelection; // Typically the parent or a nearby sibling could be focused
109
+ return this.previousSelection;
108
110
  }
109
111
  getUndoSelection() {
110
- return this.widget; // Select the widget itself when the operation is undone
112
+ return this.widget;
111
113
  }
112
114
  }
113
115
  class UpdateWidgetCommand {
114
116
  constructor(widget, values, previousSelection) {
115
117
  this.widget = widget;
116
- this.newState = merge(cloneDeep(widget), values);
117
118
  this.previousSelection = previousSelection;
118
- this.previousState = cloneDeep(widget);
119
+ this.previousState = this.captureState(widget);
120
+ this.newState = this.captureState(widget);
121
+ this.applyPatch(this.newState, values);
119
122
  }
120
123
  execute() {
121
- merge(this.widget, this.newState);
124
+ this.restoreState(this.widget, this.newState);
122
125
  }
123
126
  undo() {
124
- this.deepPruneAndRestore(this.widget, this.previousState);
127
+ this.restoreState(this.widget, this.previousState);
125
128
  }
126
129
  description() {
127
130
  return `Updated widget ${this.widget.name}`;
@@ -130,111 +133,242 @@ class UpdateWidgetCommand {
130
133
  return `fa-solid fa-pen`;
131
134
  }
132
135
  getExecutionSelection() {
133
- return this.widget; // Focus the widget after it's updated
136
+ return this.widget;
134
137
  }
135
138
  getUndoSelection() {
136
- return this.previousSelection; // Restore the focus to where it was before the update
139
+ return this.widget;
137
140
  }
138
- deepPruneAndRestore(current, original) {
139
- Object.keys(current).forEach(key => {
140
- if (original.hasOwnProperty(key)) {
141
- if (typeof current[key] === 'object' && current[key] !== null && !(current[key] instanceof Array)) {
142
- this.deepPruneAndRestore(current[key], original[key]);
143
- }
141
+ canCoalesceWith(command) {
142
+ return command instanceof UpdateWidgetCommand && command.widget === this.widget;
143
+ }
144
+ coalesceWith(command) {
145
+ if (!(command instanceof UpdateWidgetCommand)) {
146
+ return;
147
+ }
148
+ this.newState = command.newState;
149
+ }
150
+ isNoOp() {
151
+ return isEqual(this.previousState, this.newState);
152
+ }
153
+ captureState(node) {
154
+ const { children: _children, ...rest } = node;
155
+ return cloneDeep(rest);
156
+ }
157
+ applyPatch(target, values) {
158
+ Object.keys(values).forEach((key) => {
159
+ if (key === 'children' || key === '__meta__') {
160
+ return;
144
161
  }
145
- else {
146
- delete current[key]; // Remove properties not present in original
162
+ const incoming = values[key];
163
+ const current = target[key];
164
+ if (incoming === undefined) {
165
+ return;
147
166
  }
167
+ if (incoming &&
168
+ typeof incoming === 'object' &&
169
+ !Array.isArray(incoming) &&
170
+ current &&
171
+ typeof current === 'object' &&
172
+ !Array.isArray(current)) {
173
+ this.applyPatch(current, incoming);
174
+ return;
175
+ }
176
+ target[key] = cloneDeep(incoming);
177
+ });
178
+ }
179
+ restoreState(target, source) {
180
+ const preservedChildren = target.children;
181
+ const preservedMeta = target.__meta__;
182
+ Object.keys(target).forEach((key) => {
183
+ if (key === 'children' || key === '__meta__') {
184
+ return;
185
+ }
186
+ if (!(key in source)) {
187
+ delete target[key];
188
+ }
189
+ });
190
+ Object.keys(source).forEach((key) => {
191
+ if (key === 'children' || key === '__meta__') {
192
+ return;
193
+ }
194
+ target[key] = cloneDeep(source[key]);
148
195
  });
149
- // Restore the original properties
150
- merge(current, original);
196
+ if (preservedChildren) {
197
+ target.children = preservedChildren;
198
+ }
199
+ if (preservedMeta) {
200
+ target.__meta__ = preservedMeta;
201
+ }
151
202
  }
152
203
  }
204
+ class CompositeCommand {
205
+ constructor(commands, label) {
206
+ this.commands = commands;
207
+ this.label = label;
208
+ }
209
+ execute() {
210
+ this.commands.forEach((command) => command.execute());
211
+ }
212
+ undo() {
213
+ for (let index = this.commands.length - 1; index >= 0; index--) {
214
+ this.commands[index].undo();
215
+ }
216
+ }
217
+ description() {
218
+ if (this.label) {
219
+ return this.label;
220
+ }
221
+ const addCount = this.commands.filter((command) => command instanceof AddWidgetCommand).length;
222
+ if (addCount > 1 && addCount === this.commands.length) {
223
+ return `Added ${addCount} widgets`;
224
+ }
225
+ return this.commands[this.commands.length - 1]?.description() ?? 'Multiple changes';
226
+ }
227
+ icon() {
228
+ return this.commands[0]?.icon() ?? 'fa-solid fa-layer-group';
229
+ }
230
+ getExecutionSelection() {
231
+ const update = this.commands.find((command) => command instanceof UpdateWidgetCommand);
232
+ if (update) {
233
+ return update.getExecutionSelection();
234
+ }
235
+ return this.commands[this.commands.length - 1].getExecutionSelection();
236
+ }
237
+ getUndoSelection() {
238
+ return this.commands[0].getUndoSelection();
239
+ }
240
+ }
241
+ //#endregion
242
+ //#region ----- Command manager -----
153
243
  class CommandManager {
154
244
  constructor() {
155
245
  this.undoStack = signal([], /* @ts-ignore */
156
246
  ...(ngDevMode ? [{ debugName: "undoStack" }] : /* istanbul ignore next */ []));
157
247
  this.currentIndex = signal(-1, /* @ts-ignore */
158
248
  ...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
249
+ this.transactionDepth = 0;
250
+ this.transactionCommands = [];
159
251
  this.selectedNode = signal(null, /* @ts-ignore */
160
252
  ...(ngDevMode ? [{ debugName: "selectedNode" }] : /* istanbul ignore next */ []));
161
253
  this.canUndo = computed(() => this.currentIndex() >= 0, /* @ts-ignore */
162
254
  ...(ngDevMode ? [{ debugName: "canUndo" }] : /* istanbul ignore next */ []));
163
255
  this.canRedo = computed(() => this.currentIndex() < this.undoStack().length - 1, /* @ts-ignore */
164
256
  ...(ngDevMode ? [{ debugName: "canRedo" }] : /* istanbul ignore next */ []));
257
+ this.isInTransaction = () => this.transactionDepth > 0;
165
258
  this.history = computed(() => this.undoStack().map((cmd, index) => ({
166
259
  description: cmd.description(),
167
260
  icon: cmd.icon(),
168
261
  isPast: this.currentIndex() > index,
169
262
  isFuture: this.currentIndex() < index,
170
- isCurrent: this.currentIndex() == index
263
+ isCurrent: this.currentIndex() == index,
171
264
  })), /* @ts-ignore */
172
265
  ...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
173
266
  }
174
- executeCommand(command) {
175
- // Check if the currentIndex is not at the end of the undoStack
267
+ beginTransaction(description) {
268
+ if (this.transactionDepth === 0) {
269
+ this.transactionCommands = [];
270
+ this.transactionDescription = description;
271
+ }
272
+ else if (description && !this.transactionDescription) {
273
+ this.transactionDescription = description;
274
+ }
275
+ this.transactionDepth++;
276
+ }
277
+ endTransaction() {
278
+ if (this.transactionDepth === 0) {
279
+ return;
280
+ }
281
+ this.transactionDepth--;
282
+ if (this.transactionDepth > 0) {
283
+ return;
284
+ }
285
+ const commands = this.transactionCommands;
286
+ const description = this.transactionDescription;
287
+ this.transactionCommands = [];
288
+ this.transactionDescription = undefined;
289
+ if (commands.length === 0) {
290
+ return;
291
+ }
176
292
  if (this.currentIndex() !== this.undoStack().length - 1) {
177
- // Remove all commands beyond the current index to discard the "future" commands
178
- const newUndoStack = this.undoStack().slice(0, this.currentIndex() + 1);
179
- this.undoStack.set(newUndoStack);
293
+ this.undoStack.set(this.undoStack().slice(0, this.currentIndex() + 1));
294
+ }
295
+ const command = commands.length === 1 ? commands[0] : new CompositeCommand(commands, description);
296
+ this.undoStack.set([...this.undoStack(), command]);
297
+ this.currentIndex.set(this.undoStack().length - 1);
298
+ this.selectedNode.set(command.getExecutionSelection());
299
+ }
300
+ executeCommand(command) {
301
+ if (command instanceof UpdateWidgetCommand && command.isNoOp()) {
302
+ return;
303
+ }
304
+ const lastCommand = this.transactionDepth > 0
305
+ ? this.transactionCommands[this.transactionCommands.length - 1]
306
+ : this.undoStack()[this.currentIndex()];
307
+ if (lastCommand?.canCoalesceWith?.(command) && lastCommand.coalesceWith) {
308
+ lastCommand.coalesceWith(command);
309
+ command.execute();
310
+ this.selectedNode.set(command.getExecutionSelection());
311
+ return;
312
+ }
313
+ if (this.transactionDepth === 0 && this.currentIndex() !== this.undoStack().length - 1) {
314
+ this.undoStack.set(this.undoStack().slice(0, this.currentIndex() + 1));
180
315
  }
181
316
  command.execute();
182
- this.undoStack.set([...this.undoStack(), command]); // Add the new command to the stack
183
- this.currentIndex.set(this.undoStack().length - 1); // Update currentIndex to the end of the stack
184
- this.selectedNode.set(command.getExecutionSelection()); // Update the selected node
317
+ if (this.transactionDepth > 0) {
318
+ this.transactionCommands.push(command);
319
+ this.selectedNode.set(command.getExecutionSelection());
320
+ return;
321
+ }
322
+ this.undoStack.set([...this.undoStack(), command]);
323
+ this.currentIndex.set(this.undoStack().length - 1);
324
+ this.selectedNode.set(command.getExecutionSelection());
185
325
  }
186
326
  undo() {
187
- if (this.currentIndex() >= 0) {
188
- const command = this.undoStack()[this.currentIndex()];
189
- command.undo();
190
- this.currentIndex.set(this.currentIndex() - 1);
191
- if (this.currentIndex() >= 0) {
192
- this.selectedNode.set(this.undoStack()[this.currentIndex()].getUndoSelection());
193
- }
194
- else {
195
- this.selectedNode.set(null); // No selection if all commands are undone
196
- }
327
+ if (this.currentIndex() < 0) {
328
+ return;
197
329
  }
330
+ const command = this.undoStack()[this.currentIndex()];
331
+ command.undo();
332
+ this.currentIndex.set(this.currentIndex() - 1);
333
+ this.selectedNode.set(command.getUndoSelection());
198
334
  }
199
335
  redo() {
200
- if (this.currentIndex() < this.undoStack().length - 1) {
201
- this.currentIndex.set(this.currentIndex() + 1);
202
- const command = this.undoStack()[this.currentIndex()];
203
- command.execute();
204
- this.selectedNode.set(command.getExecutionSelection());
336
+ if (this.currentIndex() >= this.undoStack().length - 1) {
337
+ return;
205
338
  }
339
+ this.currentIndex.set(this.currentIndex() + 1);
340
+ const command = this.undoStack()[this.currentIndex()];
341
+ command.execute();
342
+ this.selectedNode.set(command.getExecutionSelection());
206
343
  }
207
344
  goToCommand(index) {
208
- if (index < 0 || index >= this.undoStack().length || index === this.currentIndex()) {
209
- return; // Out of bounds or already at the desired index
345
+ if (index < -1 || index >= this.undoStack().length || index === this.currentIndex()) {
346
+ return;
210
347
  }
211
348
  if (index > this.currentIndex()) {
212
- // Need to redo until reaching the desired index
213
349
  while (this.currentIndex() < index) {
214
350
  this.redo();
215
351
  }
216
352
  }
217
353
  else {
218
- // Need to undo until reaching the desired index
219
354
  while (this.currentIndex() > index) {
220
355
  this.undo();
221
356
  }
222
357
  }
223
- // Optionally update any UI components or state here
224
- this.selectedNode.set(this.undoStack()[this.currentIndex()].getExecutionSelection());
225
358
  }
226
359
  clear() {
227
360
  this.undoStack.set([]);
228
361
  this.currentIndex.set(-1);
362
+ this.selectedNode.set(null);
363
+ this.transactionDepth = 0;
364
+ this.transactionCommands = [];
365
+ this.transactionDescription = undefined;
229
366
  }
230
367
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CommandManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
231
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CommandManager, providedIn: 'root' }); }
368
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CommandManager }); }
232
369
  }
233
370
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CommandManager, decorators: [{
234
- type: Injectable,
235
- args: [{
236
- providedIn: 'root'
237
- }]
371
+ type: Injectable
238
372
  }] });
239
373
 
240
374
  class AXPDesignerConnector {
@@ -323,15 +457,75 @@ const AXPDesignerMessageKeys = {
323
457
  FormSaved: 'designer-form-saved-content',
324
458
  };
325
459
 
460
+ //#region ----- Constants -----
461
+ const PREVIEW_DOCUMENT_STORAGE_KEY = 'axp-layout-designer-preview-document';
462
+ //#endregion
463
+ //#region ----- Preview session -----
464
+ function storeDesignerPreviewDocument(document) {
465
+ try {
466
+ sessionStorage.setItem(PREVIEW_DOCUMENT_STORAGE_KEY, JSON.stringify(document));
467
+ }
468
+ catch {
469
+ // sessionStorage can be unavailable in some iframe / privacy modes
470
+ }
471
+ }
472
+ function readDesignerPreviewDocument() {
473
+ try {
474
+ const raw = sessionStorage.getItem(PREVIEW_DOCUMENT_STORAGE_KEY);
475
+ if (!raw) {
476
+ return null;
477
+ }
478
+ return JSON.parse(raw);
479
+ }
480
+ catch {
481
+ return null;
482
+ }
483
+ }
484
+ //#endregion
485
+
326
486
  /**
327
487
  * Suffixes for `forms:widgets:picker:last-tab:{id}` user settings keys.
328
488
  * Use one id per distinct widget-picker host (different tab strips / widget sets).
329
489
  */
330
490
  const AXP_WIDGET_PICKER_TAB_CONTEXT = {
331
491
  layoutDesignerFormElements: 'layout-designer-form-elements',
492
+ layoutDesignerWidgetTemplate: 'layout-designer-widget-template',
493
+ layoutDesignerPage: 'layout-designer-page',
332
494
  dashboardHome: 'dashboard-home',
333
495
  };
334
496
 
497
+ //#endregion
498
+ //#region ----- Constants -----
499
+ const DEFAULT_FORM_DRAWERS = ['pages', 'dataSources', 'outline', 'history'];
500
+ //#endregion
501
+ //#region ----- Filter -----
502
+ /**
503
+ * Include if any include rule hits, then subtract `excludeNames`.
504
+ * An empty include set matches nothing (fail closed).
505
+ */
506
+ function widgetMatchesDesignerSurface(widget, profile) {
507
+ const filter = profile.widgets;
508
+ if (filter?.excludeNames?.includes(widget.name)) {
509
+ return false;
510
+ }
511
+ const includeGroups = filter?.includeGroups ?? [];
512
+ const includeTypes = filter?.includeTypes ?? [];
513
+ const includeNames = filter?.includeNames ?? [];
514
+ const hasInclude = includeGroups.length > 0 || includeTypes.length > 0 || includeNames.length > 0;
515
+ if (!hasInclude) {
516
+ return false;
517
+ }
518
+ const groupHit = includeGroups.some((group) => widget.groups?.includes(group));
519
+ const typeHit = includeTypes.includes(widget.type);
520
+ const nameHit = includeNames.includes(widget.name);
521
+ return groupHit || typeHit || nameHit;
522
+ }
523
+ function designerSurfaceHasDrawer(profile, drawer) {
524
+ const drawers = profile.chrome?.drawers ?? DEFAULT_FORM_DRAWERS;
525
+ return drawers.includes(drawer);
526
+ }
527
+ //#endregion
528
+
335
529
  class AXPWidgetPickerService {
336
530
  constructor() {
337
531
  this.popupService = inject(AXPopupService);
@@ -341,7 +535,7 @@ class AXPWidgetPickerService {
341
535
  async showPicker(options) {
342
536
  const com = await Promise.resolve().then(function () { return widgetPicker_component; }).then((c) => c.AXPDesignerWidgetPickerComponent);
343
537
  const widgets = this.widgetRegistryService.all();
344
- const filteredWidgets = widgets.filter((c) => options.groups.some((d) => c.groups?.includes(d)));
538
+ const filteredWidgets = widgets.filter((widget) => this.matchesPickerOptions(widget, options));
345
539
  //
346
540
  const result = await this.popupService.open(com, {
347
541
  title: await this.translationService.translateAsync('@form-template-management:designer.components.widget-picker.title'),
@@ -361,6 +555,12 @@ class AXPWidgetPickerService {
361
555
  meta: c.meta,
362
556
  }));
363
557
  }
558
+ matchesPickerOptions(widget, options) {
559
+ if (options.profile) {
560
+ return widgetMatchesDesignerSurface(widget, options.profile);
561
+ }
562
+ return options.groups?.some((group) => widget.groups?.includes(group)) ?? false;
563
+ }
364
564
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPWidgetPickerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
365
565
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPWidgetPickerService, providedIn: 'root' }); }
366
566
  }
@@ -371,6 +571,105 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
371
571
  }]
372
572
  }] });
373
573
 
574
+ //#region ----- Imports -----
575
+ //#endregion
576
+ //#region ----- Constants -----
577
+ /** Child cells and document shells: registered, not pickable. */
578
+ const DESIGNER_CHILD_AND_SHELL_WIDGETS = [
579
+ 'tabset-item',
580
+ 'flex-item-layout',
581
+ 'grid-item-layout',
582
+ 'table-item-layout',
583
+ 'advanced-grid-item-layout',
584
+ 'page-layout',
585
+ 'document-layout',
586
+ ];
587
+ /** Field widgets that belong on forms but are not tagged `form-element`. */
588
+ const FORM_EXTRA_WIDGET_NAMES = [
589
+ 'signature',
590
+ 'password-editor',
591
+ 'qrcode',
592
+ 'icon',
593
+ 'table-editor',
594
+ 'template-box-editor',
595
+ 'meta-data-editor',
596
+ 'lookup-editor',
597
+ ];
598
+ const FORM_WIDGETS = {
599
+ includeGroups: [AXPWidgetGroupEnum.FormElement],
600
+ includeNames: [...FORM_EXTRA_WIDGET_NAMES],
601
+ excludeNames: [...DESIGNER_CHILD_AND_SHELL_WIDGETS],
602
+ };
603
+ /**
604
+ * Default Form Builder surface. Hosts may pass this explicitly or omit `profile`.
605
+ */
606
+ const AXP_FORM_DESIGNER_SURFACE_PROFILE = {
607
+ id: 'form',
608
+ documentKind: 'widget-tree',
609
+ rootWidget: 'page-layout',
610
+ widgets: FORM_WIDGETS,
611
+ chrome: {
612
+ drawers: ['pages', 'dataSources', 'outline', 'history'],
613
+ },
614
+ preview: {
615
+ player: 'angular',
616
+ },
617
+ pickerTabContext: AXP_WIDGET_PICKER_TAB_CONTEXT.layoutDesignerFormElements,
618
+ };
619
+ /**
620
+ * Composed widget template: same offer list as form, no Pages drawer.
621
+ */
622
+ const AXP_WIDGET_DESIGNER_SURFACE_PROFILE = {
623
+ id: 'widget',
624
+ documentKind: 'widget-tree',
625
+ rootWidget: 'page-layout',
626
+ widgets: FORM_WIDGETS,
627
+ chrome: {
628
+ drawers: ['dataSources', 'outline', 'history'],
629
+ },
630
+ preview: {
631
+ player: 'angular',
632
+ },
633
+ pickerTabContext: AXP_WIDGET_PICKER_TAB_CONTEXT.layoutDesignerWidgetTemplate,
634
+ };
635
+ /**
636
+ * Website page surface. Palette chrome is declared; walker preview is Phase 2.
637
+ * Offer list is name-based until a `page-widget` group exists.
638
+ */
639
+ const AXP_PAGE_DESIGNER_SURFACE_PROFILE = {
640
+ id: 'page',
641
+ documentKind: 'widget-tree',
642
+ widgets: {
643
+ includeNames: [
644
+ 'flex-layout',
645
+ 'grid-layout',
646
+ 'block-layout',
647
+ 'panel-layout',
648
+ 'fieldset-layout',
649
+ 'table-layout',
650
+ 'advanced-grid-layout',
651
+ 'text-block-layout',
652
+ 'alert-box-layout',
653
+ 'badge-layout',
654
+ 'image',
655
+ 'icon',
656
+ 'button-action',
657
+ 'tabset-layout',
658
+ 'step-wizard',
659
+ ],
660
+ excludeNames: [...DESIGNER_CHILD_AND_SHELL_WIDGETS, 'password-editor', 'signature', 'lookup-editor'],
661
+ },
662
+ chrome: {
663
+ drawers: ['palette', 'outline', 'history'],
664
+ },
665
+ preview: {
666
+ player: 'html-walker',
667
+ walkerChannel: 'html',
668
+ },
669
+ pickerTabContext: AXP_WIDGET_PICKER_TAB_CONTEXT.layoutDesignerPage,
670
+ };
671
+ //#endregion
672
+
374
673
  class AXPDesignerService {
375
674
  #ef;
376
675
  constructor() {
@@ -388,6 +687,9 @@ class AXPDesignerService {
388
687
  ...(ngDevMode ? [{ debugName: "currentPageIndex" }] : /* istanbul ignore next */ []));
389
688
  this.currentPage = signal(this.document().children?.[0], /* @ts-ignore */
390
689
  ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
690
+ /** Bumps when the board must remount the page without cloning it off `document`. */
691
+ this.pageRenderEpoch = signal(0, /* @ts-ignore */
692
+ ...(ngDevMode ? [{ debugName: "pageRenderEpoch" }] : /* istanbul ignore next */ []));
391
693
  this.commandManager = inject(CommandManager);
392
694
  this.clipboard = signal(null, /* @ts-ignore */
393
695
  ...(ngDevMode ? [{ debugName: "clipboard" }] : /* istanbul ignore next */ []));
@@ -412,12 +714,22 @@ class AXPDesignerService {
412
714
  ...(ngDevMode ? [{ debugName: "canSave" }] : /* istanbul ignore next */ []));
413
715
  this.unsavedChanges = signal(false, /* @ts-ignore */
414
716
  ...(ngDevMode ? [{ debugName: "unsavedChanges" }] : /* istanbul ignore next */ []));
717
+ this.surfaceProfile = signal(AXP_FORM_DESIGNER_SURFACE_PROFILE, /* @ts-ignore */
718
+ ...(ngDevMode ? [{ debugName: "surfaceProfile" }] : /* istanbul ignore next */ []));
719
+ this.localDataSources = signal([], /* @ts-ignore */
720
+ ...(ngDevMode ? [{ debugName: "localDataSources" }] : /* istanbul ignore next */ []));
721
+ this.sources = this.localDataSources.asReadonly();
415
722
  this.breadcrumbs = computed(() => {
416
723
  return this.selectedNode() ? this.findBreadcrumbs(this.currentPage(), this.selectedNode()) ?? [] : [];
417
724
  }, /* @ts-ignore */
418
725
  ...(ngDevMode ? [{ debugName: "breadcrumbs" }] : /* istanbul ignore next */ []));
419
726
  this.selectedNode = signal(null, /* @ts-ignore */
420
727
  ...(ngDevMode ? [{ debugName: "selectedNode" }] : /* istanbul ignore next */ []));
728
+ this.widgetInstances = new Map();
729
+ /** Property-panel writes are recorded only after the user focuses the panel. */
730
+ this.acceptPropertyPanelHistory = false;
731
+ /** Last panel payload applied to the canvas. Equal echoes must not republish the preview. */
732
+ this.lastAppliedPropertyPanelValues = null;
421
733
  this.selectedNodeConfig = computed(() => get(this.selectedNode(), '__meta__.config'), /* @ts-ignore */
422
734
  ...(ngDevMode ? [{ debugName: "selectedNodeConfig" }] : /* istanbul ignore next */ []));
423
735
  this._focusedNode = null;
@@ -475,20 +787,52 @@ class AXPDesignerService {
475
787
  });
476
788
  }
477
789
  register(command) {
478
- this.select({ widget: command.widget });
790
+ const key = this.widgetInstanceKey(command.widget, command.id);
791
+ if (key) {
792
+ this.widgetInstances.set(key, command.instance);
793
+ }
794
+ if (!this.selectedNode()) {
795
+ this.select({ widget: command.widget });
796
+ }
797
+ }
798
+ unregister(widget) {
799
+ const key = this.widgetInstanceKey(widget);
800
+ if (key) {
801
+ this.widgetInstances.delete(key);
802
+ }
803
+ }
804
+ getWidgetInstance(widget) {
805
+ const key = this.widgetInstanceKey(widget);
806
+ return key ? this.widgetInstances.get(key) ?? null : null;
807
+ }
808
+ widgetInstanceKey(widget, id) {
809
+ const key = id ?? get(widget, '__meta__.id');
810
+ return key == null ? null : String(key);
479
811
  }
480
812
  select(command) {
813
+ this.acceptPropertyPanelHistory = false;
814
+ this.lastAppliedPropertyPanelValues = null;
481
815
  this.selectedNode.set(command.widget);
482
816
  this.onSelected.next(command);
483
817
  }
818
+ armPropertyPanelHistory() {
819
+ this.acceptPropertyPanelHistory = true;
820
+ }
821
+ applyPropertyPanelChange(event) {
822
+ // Init is hydration. update() always republishes the preview.
823
+ if (event.mode !== 'update' || isEqual(event.values, this.lastAppliedPropertyPanelValues)) {
824
+ return;
825
+ }
826
+ this.lastAppliedPropertyPanelValues = event.values;
827
+ this.update({ values: event.values, mode: 'update' });
828
+ }
484
829
  update(command) {
485
830
  const node = command.widget ?? this.selectedNode();
486
831
  if (command.mode == 'update' && node) {
487
832
  this.commandManager.executeCommand(new UpdateWidgetCommand(node, command.values, node));
833
+ this.unsavedChanges.set(true);
488
834
  }
489
- //this.selectedNode.update((c) => merge(node, command.values));
490
835
  this.onUpdate.next({ widget: node, values: command.values });
491
- this.unsavedChanges.set(true);
492
836
  this.updatePreview();
493
837
  }
494
838
  focus(command) {
@@ -521,21 +865,31 @@ class AXPDesignerService {
521
865
  return undefined;
522
866
  }
523
867
  async showPicker(currentNode = this.selectedNode()) {
868
+ const profile = this.surfaceProfile();
524
869
  const widgets = await this.widgetPickerService.showPicker({
525
- groups: [AXPWidgetGroupEnum.FormElement],
526
- tabPersistenceContext: AXP_WIDGET_PICKER_TAB_CONTEXT.layoutDesignerFormElements,
870
+ profile,
871
+ tabPersistenceContext: profile.pickerTabContext ?? AXP_WIDGET_PICKER_TAB_CONTEXT.layoutDesignerFormElements,
527
872
  });
528
873
  const result = [];
529
874
  if (widgets.length) {
530
- widgets.forEach((c) => {
531
- result.push(this.addWidget(currentNode, { type: c.type }));
532
- });
875
+ const addAll = () => {
876
+ widgets.forEach((c) => {
877
+ result.push(this.addWidget(currentNode, { type: c.type }));
878
+ });
879
+ };
880
+ if (widgets.length > 1) {
881
+ this.runTransaction(addAll, 'Added widgets');
882
+ }
883
+ else {
884
+ addAll();
885
+ }
533
886
  }
534
887
  return result;
535
888
  }
536
889
  new() {
537
890
  this.commandManager.clear();
538
891
  this.document.set(this.createDoc());
892
+ this.localDataSources.set([]);
539
893
  this.currentPage.set(this.document().children?.[0]);
540
894
  this.reRenderPage();
541
895
  }
@@ -544,10 +898,72 @@ class AXPDesignerService {
544
898
  type: AXPWidgetsCatalog.document,
545
899
  children: [this.createPage()],
546
900
  name: 'document',
901
+ options: { [AXP_LOCAL_DATA_SOURCES_OPTION]: [] },
547
902
  };
548
903
  set(doc, '__meta__.id', this.generateUniqueId());
549
904
  return doc;
550
905
  }
906
+ //#region ----- Local data sources -----
907
+ toLocalDataSourceStore() {
908
+ return {
909
+ sources: this.sources,
910
+ get: (id) => this.getLocalDataSource(id),
911
+ create: (name) => this.createLocalDataSource(name),
912
+ update: (id, patch) => this.updateLocalDataSource(id, patch),
913
+ remove: (id) => this.removeLocalDataSource(id),
914
+ };
915
+ }
916
+ getLocalDataSource(id) {
917
+ return this.localDataSources().find((row) => row.id === id);
918
+ }
919
+ createLocalDataSource(name) {
920
+ const trimmed = name.trim();
921
+ const source = {
922
+ id: AXPDataGenerator.uuid(),
923
+ name: trimmed.length > 0 ? trimmed : `List ${this.localDataSources().length + 1}`,
924
+ columns: createDefaultLocalDataSourceColumns(),
925
+ items: [],
926
+ };
927
+ this.localDataSources.update((list) => [...list, source]);
928
+ this.persistLocalDataSources(true);
929
+ return source;
930
+ }
931
+ updateLocalDataSource(id, patch) {
932
+ this.localDataSources.update((list) => list.map((row) => (row.id === id ? { ...row, ...patch } : row)));
933
+ this.persistLocalDataSources(true);
934
+ }
935
+ removeLocalDataSource(id) {
936
+ this.localDataSources.update((list) => list.filter((row) => row.id !== id));
937
+ this.persistLocalDataSources(true);
938
+ }
939
+ hydrateLocalDataSources(root) {
940
+ const fromDoc = readLocalDataSourcesFromNode(root);
941
+ const fromPages = fromDoc.length > 0
942
+ ? []
943
+ : (root.children ?? []).flatMap((page) => readLocalDataSourcesFromNode(page));
944
+ this.localDataSources.set(fromDoc.length > 0 ? fromDoc : fromPages);
945
+ this.persistLocalDataSources(false);
946
+ }
947
+ persistLocalDataSources(markDirty) {
948
+ const list = this.localDataSources();
949
+ const doc = this.document();
950
+ doc.options = { ...(doc.options ?? {}), [AXP_LOCAL_DATA_SOURCES_OPTION]: list };
951
+ this.stripLocalDataSourcesFromPages(doc);
952
+ if (markDirty) {
953
+ this.unsavedChanges.set(true);
954
+ }
955
+ }
956
+ stripLocalDataSourcesFromPages(doc) {
957
+ for (const page of doc.children ?? []) {
958
+ const options = page.options;
959
+ if (!options || !(AXP_LOCAL_DATA_SOURCES_OPTION in options)) {
960
+ continue;
961
+ }
962
+ const { [AXP_LOCAL_DATA_SOURCES_OPTION]: _removed, ...rest } = options;
963
+ page.options = rest;
964
+ }
965
+ }
966
+ //#endregion
551
967
  createPage() {
552
968
  const config = this.widgetRegisteryService.resolve('page-layout');
553
969
  const page = {
@@ -579,9 +995,20 @@ class AXPDesignerService {
579
995
  this.select({ widget: this.currentPage() });
580
996
  }
581
997
  reRenderPage() {
582
- this.currentPage.set({ ...this.currentPage() });
998
+ const page = this.document().children?.[this.currentPageIndex()];
999
+ if (!page) {
1000
+ return;
1001
+ }
1002
+ // Keep the same object as `document.children[i]`. A shallow copy made the
1003
+ // board edit a detached page, so page options never appeared in export.
1004
+ this.currentPage.set(page);
1005
+ this.pageRenderEpoch.update((epoch) => epoch + 1);
1006
+ const selected = this.selectedNode();
1007
+ if (selected?.type === 'page-layout' && selected !== page) {
1008
+ this.select({ widget: page });
1009
+ }
583
1010
  setTimeout(() => {
584
- this.refresh({ widget: this.currentPage() });
1011
+ this.refresh({ widget: page });
585
1012
  });
586
1013
  }
587
1014
  findWidgetById(root, id) {
@@ -638,22 +1065,61 @@ class AXPDesignerService {
638
1065
  }
639
1066
  return null; // Return null if no container found
640
1067
  }
641
- addWidget(currentNode, node) {
642
- //
1068
+ createWidgetNode(node) {
643
1069
  const cloned = cloneDeep(node);
644
- const config = this.widgetRegisteryService.resolve(node.type);
645
- //
1070
+ this.stampNewWidgetTree(cloned, { keepNames: true, reservedNames: this.collectDocumentNameSet() });
1071
+ return cloned;
1072
+ }
1073
+ /** Fills missing `__meta__.id`, config, and name on an already-on-canvas tree (e.g. AI-added children). */
1074
+ ensureWidgetTreeMeta(root = this.document()) {
1075
+ this.stampMissingWidgetMeta(root, this.collectDocumentNameSet());
1076
+ }
1077
+ /**
1078
+ * Assigns unique `__meta__.id`, registry config, and a name on every node.
1079
+ * Nested AI add-widget trees must be stamped the same way paste already does;
1080
+ * otherwise children share `undefined` ids and the designer selects them all.
1081
+ */
1082
+ stampNewWidgetTree(node, options) {
1083
+ const existing = typeof node.name === 'string' ? node.name.trim() : '';
1084
+ if (!options.keepNames || !existing || options.reservedNames.has(existing)) {
1085
+ node.name = this.nextUniqueName(node.type, options.reservedNames);
1086
+ }
1087
+ else {
1088
+ node.name = existing;
1089
+ }
1090
+ options.reservedNames.add(node.name);
1091
+ set(node, '__meta__.id', this.generateUniqueId());
1092
+ set(node, '__meta__.config', this.widgetRegisteryService.resolve(node.type));
1093
+ set(node, '__meta__.added', true);
1094
+ node.children?.forEach((child) => this.stampNewWidgetTree(child, options));
1095
+ }
1096
+ stampMissingWidgetMeta(node, reservedNames) {
1097
+ if (get(node, '__meta__.id') == null) {
1098
+ set(node, '__meta__.id', this.generateUniqueId());
1099
+ }
1100
+ if (!get(node, '__meta__.config') && node.type) {
1101
+ set(node, '__meta__.config', this.widgetRegisteryService.resolve(node.type));
1102
+ }
1103
+ const existing = typeof node.name === 'string' ? node.name.trim() : '';
1104
+ if (!existing) {
1105
+ node.name = this.nextUniqueName(node.type, reservedNames);
1106
+ }
1107
+ else {
1108
+ node.name = existing;
1109
+ }
1110
+ reservedNames.add(node.name);
1111
+ node.children?.forEach((child) => this.stampMissingWidgetMeta(child, reservedNames));
1112
+ }
1113
+ addWidget(currentNode, node) {
1114
+ const cloned = this.createWidgetNode(node);
646
1115
  this.commandManager.executeCommand(new AddWidgetCommand(cloned, currentNode, currentNode));
1116
+ this.ensureDefaultTabsetChild(cloned);
647
1117
  //
648
- cloned.name = this.generateUniqueName(node.type);
649
- set(cloned, '__meta__.id', this.generateUniqueId());
650
- set(cloned, '__meta__.config', config);
651
- set(cloned, '__meta__.added', true);
652
- //
653
- this.reRenderPage();
654
- //
655
- this.refresh({ widget: currentNode });
656
- this.select({ widget: cloned });
1118
+ if (!this.commandManager.isInTransaction()) {
1119
+ this.reRenderPage();
1120
+ this.refresh({ widget: currentNode });
1121
+ this.select({ widget: cloned });
1122
+ }
657
1123
  return cloned;
658
1124
  }
659
1125
  removeWidget(node = this.selectedNode()) {
@@ -679,24 +1145,44 @@ class AXPDesignerService {
679
1145
  }
680
1146
  navigateTo(index) {
681
1147
  this.commandManager.goToCommand(index);
682
- this.reRenderPage();
683
- const currentSelection = this.commandManager.selectedNode();
684
- this.refresh({ widget: this.currentPage() });
685
- this.select({ widget: currentSelection });
1148
+ this.syncCanvasAfterHistory();
686
1149
  }
687
1150
  redo() {
1151
+ if (!this.canRedo()) {
1152
+ return;
1153
+ }
688
1154
  this.commandManager.redo();
689
- this.reRenderPage();
690
- const currentSelection = this.commandManager.selectedNode();
691
- this.refresh({ widget: this.currentPage() });
692
- this.select({ widget: currentSelection });
1155
+ this.syncCanvasAfterHistory();
693
1156
  }
694
1157
  undo() {
1158
+ if (!this.canUndo()) {
1159
+ return;
1160
+ }
695
1161
  this.commandManager.undo();
696
- this.reRenderPage();
697
- const currentSelection = this.commandManager.selectedNode();
1162
+ this.syncCanvasAfterHistory();
1163
+ }
1164
+ isInTransaction() {
1165
+ return this.commandManager.isInTransaction();
1166
+ }
1167
+ runTransaction(action, description) {
1168
+ this.commandManager.beginTransaction(description);
1169
+ try {
1170
+ return action();
1171
+ }
1172
+ finally {
1173
+ this.commandManager.endTransaction();
1174
+ if (!this.commandManager.isInTransaction()) {
1175
+ this.syncCanvasAfterHistory();
1176
+ }
1177
+ }
1178
+ }
1179
+ syncCanvasAfterHistory() {
1180
+ const selected = this.commandManager.selectedNode() ?? this.currentPage();
698
1181
  this.refresh({ widget: this.currentPage() });
699
- this.select({ widget: currentSelection });
1182
+ if (selected) {
1183
+ this.onUpdate.next({ widget: selected, values: { options: selected.options ?? {} } });
1184
+ }
1185
+ this.select({ widget: selected });
700
1186
  }
701
1187
  clearHistory() {
702
1188
  this.commandManager.clear();
@@ -721,19 +1207,22 @@ class AXPDesignerService {
721
1207
  // If the value is not an object or array, return it as is
722
1208
  return obj;
723
1209
  }
724
- async addMetaToNodes(node, processedNodes, progressCallback) {
725
- // Apply your meta logic here
1210
+ async addMetaToNodes(node, processedNodes, progressCallback, reservedNames) {
726
1211
  const config = this.widgetRegisteryService.resolve(node.type);
727
- //
1212
+ const existing = typeof node.name === 'string' ? node.name.trim() : '';
1213
+ if (!existing) {
1214
+ node.name = this.nextUniqueName(node.type, reservedNames);
1215
+ }
1216
+ else {
1217
+ node.name = existing;
1218
+ }
1219
+ reservedNames.add(node.name);
728
1220
  set(node, '__meta__.id', this.generateUniqueId());
729
1221
  set(node, '__meta__.config', config);
730
- //
731
- // Increment the processed node count and update the progress dialog
732
1222
  processedNodes.count++;
733
1223
  progressCallback(processedNodes.count);
734
- // Recursively process child nodes using Promise.all
735
1224
  if (node.children && node.children.length > 0) {
736
- node.children = await Promise.all(node.children.map(async (child) => await this.addMetaToNodes(child, processedNodes, progressCallback)));
1225
+ node.children = await Promise.all(node.children.map(async (child) => this.addMetaToNodes(child, processedNodes, progressCallback, reservedNames)));
737
1226
  }
738
1227
  return node;
739
1228
  }
@@ -775,14 +1264,7 @@ class AXPDesignerService {
775
1264
  nearestContainer.children = [];
776
1265
  }
777
1266
  //
778
- const setNameId = (node) => {
779
- set(node, 'name', this.generateUniqueName(node.type));
780
- set(node, '__meta__.id', this.generateUniqueId());
781
- node.children?.forEach((n) => {
782
- setNameId(n);
783
- });
784
- };
785
- setNameId(newNode);
1267
+ this.stampNewWidgetTree(newNode, { keepNames: false, reservedNames: this.collectDocumentNameSet() });
786
1268
  //
787
1269
  this.commandManager.executeCommand(new AddWidgetCommand(newNode, nearestContainer, nearestContainer));
788
1270
  //
@@ -888,13 +1370,16 @@ class AXPDesignerService {
888
1370
  buttons: [button],
889
1371
  });
890
1372
  try {
1373
+ const reservedNames = new Set();
891
1374
  const root = await this.addMetaToNodes(rootNode, processedNodes, (count) => {
892
1375
  dialog.setProgressValue(Math.ceil((count / totalNode) * 100));
893
1376
  dialog.setProgressStatus(`${count} of ${totalNode}`);
894
- });
1377
+ }, reservedNames);
895
1378
  const page = root.children?.[0];
1379
+ this.commandManager.clear();
896
1380
  this.document.set(root);
897
1381
  this.currentPage.set(page);
1382
+ this.hydrateLocalDataSources(root);
898
1383
  dialog.setProgressColor('success');
899
1384
  dialog.setProgressText('Content loaded successfully');
900
1385
  button.text = 'Okay';
@@ -919,15 +1404,36 @@ class AXPDesignerService {
919
1404
  }
920
1405
  return count;
921
1406
  }
1407
+ ensureDefaultTabsetChild(node) {
1408
+ if (node.type !== 'tabset-layout' || (node.children?.length ?? 0) > 0) {
1409
+ return;
1410
+ }
1411
+ const tabType = 'tabset-item';
1412
+ const tabConfig = this.widgetRegisteryService.resolve(tabType);
1413
+ const tab = {
1414
+ type: tabType,
1415
+ name: this.generateUniqueName(tabType),
1416
+ options: {
1417
+ title: '@platform-layout-widgets:widgets.tabset-item.default-title',
1418
+ },
1419
+ children: [],
1420
+ };
1421
+ set(tab, '__meta__.id', this.generateUniqueId());
1422
+ set(tab, '__meta__.config', tabConfig);
1423
+ node.children = [tab];
1424
+ }
922
1425
  generateUniqueName(type) {
923
- // Normalize the type to follow camelCase convention
924
- const baseName = type.charAt(0).toLowerCase() + type.slice(1);
1426
+ return this.nextUniqueName(type, this.collectDocumentNameSet());
1427
+ }
1428
+ collectDocumentNameSet() {
1429
+ const names = new Set();
1430
+ this.collectAllNames(this.document(), names);
1431
+ return names;
1432
+ }
1433
+ nextUniqueName(type, existingNames) {
1434
+ const baseName = (type || 'widget').charAt(0).toLowerCase() + (type || 'widget').slice(1);
925
1435
  let index = 1;
926
1436
  let uniqueName = `${baseName}${index}`;
927
- // Gather all existing names to check against
928
- const existingNames = new Set();
929
- this.collectAllNames(this.document(), existingNames);
930
- // Increment the index until a unique name is found
931
1437
  while (existingNames.has(uniqueName)) {
932
1438
  index++;
933
1439
  uniqueName = `${baseName}${index}`;
@@ -948,12 +1454,15 @@ class AXPDesignerService {
948
1454
  }
949
1455
  }
950
1456
  updatePreview() {
1457
+ const document = this.removeMetaProp(this.document());
1458
+ storeDesignerPreviewDocument(document);
951
1459
  this.eventService.publish(AXPDesignerMessageKeys.Preview, {
952
1460
  id: this.templateId(),
953
- document: this.removeMetaProp(this.document())
1461
+ document,
954
1462
  });
955
1463
  }
956
1464
  async openPreviewDialog() {
1465
+ this.updatePreview();
957
1466
  const com = await Promise.resolve().then(function () { return preview_component; }).then((c) => c.AXPLayoutDesignerPreviewComponent);
958
1467
  await this.popupService.open(com, {
959
1468
  header: false,
@@ -965,30 +1474,30 @@ class AXPDesignerService {
965
1474
  });
966
1475
  }
967
1476
  openPreviewWindow() {
1477
+ this.updatePreview();
968
1478
  window.open('/forms/preview', '_blank');
969
1479
  }
970
1480
  getContextMenuItems(instance, isRoot = false) {
971
1481
  const widget = instance;
972
- const node = widget.node;
1482
+ return this.buildContextMenuItems(widget.node, instance, isRoot);
1483
+ }
1484
+ getContextMenuItemsForNode(node, isRoot = false) {
1485
+ return this.buildContextMenuItems(node, this.getWidgetInstance(node), isRoot);
1486
+ }
1487
+ buildContextMenuItems(node, instance, isRoot = false) {
973
1488
  const items = [];
974
- //
975
1489
  if (this.can(node).insert) {
976
1490
  items.push(cloneWidgetMenu(AXP_DESIGNER_ADD_WIDGET_MENU, {
977
1491
  break: true,
978
1492
  }));
979
1493
  }
980
- //
981
1494
  items.push(cloneWidgetMenu(AXP_DESIGNER_CLONE_MENU, { disabled: !this.can(node).copyOrCut }));
982
1495
  items.push(cloneWidgetMenu(AXP_DESIGNER_CUT_MENU, { disabled: !this.can(node).copyOrCut }));
983
1496
  items.push(cloneWidgetMenu(AXP_DESIGNER_COPY_MENU, { disabled: !this.can(node).copyOrCut }));
984
1497
  items.push(cloneWidgetMenu(AXP_DESIGNER_PASTE_MENU, { disabled: !this.can(node).paste, break: true }));
985
- //
986
1498
  items.push(cloneWidgetMenu(AXP_DESIGNER_DELETE_WIDGET_MENU, { disabled: !this.can(node).delete, break: true }));
987
- //
988
1499
  items.push(cloneWidgetMenu(AXP_DESIGNER_SAVE_AS_WIDGET_MENU));
989
- if (instance.onContextMenu) {
990
- instance.onContextMenu({ items });
991
- }
1500
+ instance?.onContextMenu?.({ items });
992
1501
  if (!isRoot) {
993
1502
  items.forEach(item => {
994
1503
  item.suffix = undefined;
@@ -997,49 +1506,51 @@ class AXPDesignerService {
997
1506
  return items;
998
1507
  }
999
1508
  async executeCommand(context) {
1000
- const widget = context.instance;
1001
- if (context.instance.executeCommand) {
1509
+ const instance = context.instance ?? (context.node ? this.getWidgetInstance(context.node) : null);
1510
+ const widget = instance;
1511
+ const node = widget?.node ?? context.node;
1512
+ if (!node) {
1513
+ return;
1514
+ }
1515
+ if (instance?.executeCommand) {
1002
1516
  const command = {
1003
1517
  name: context.name,
1004
1518
  data: context.data,
1005
1519
  handled: false
1006
1520
  };
1007
- await context.instance.executeCommand(command);
1521
+ await instance.executeCommand(command);
1008
1522
  if (command.handled)
1009
1523
  return;
1010
1524
  }
1011
1525
  switch (context.name) {
1012
1526
  case AXP_DESIGNER_SELECT_WIDGET_MENU.name: {
1013
- this.select({ widget: widget.node });
1527
+ this.select({ widget: node });
1014
1528
  break;
1015
1529
  }
1016
1530
  case AXP_DESIGNER_ADD_WIDGET_MENU.name: {
1017
- this.showPicker(widget.node);
1531
+ this.showPicker(node);
1018
1532
  break;
1019
1533
  }
1020
- //
1021
1534
  case AXP_DESIGNER_DELETE_WIDGET_MENU.name: {
1022
- this.removeWidget(widget.node);
1535
+ this.removeWidget(node);
1023
1536
  break;
1024
1537
  }
1025
- //
1026
1538
  case AXP_DESIGNER_CLONE_MENU.name: {
1027
- this.clone(widget.node);
1539
+ this.clone(node);
1028
1540
  break;
1029
1541
  }
1030
1542
  case AXP_DESIGNER_CUT_MENU.name: {
1031
- this.cut(widget.node);
1543
+ this.cut(node);
1032
1544
  break;
1033
1545
  }
1034
1546
  case AXP_DESIGNER_COPY_MENU.name: {
1035
- this.copy(widget.node);
1547
+ this.copy(node);
1036
1548
  break;
1037
1549
  }
1038
1550
  case AXP_DESIGNER_PASTE_MENU.name: {
1039
- this.paste(widget.node);
1551
+ this.paste(node);
1040
1552
  break;
1041
1553
  }
1042
- //
1043
1554
  }
1044
1555
  }
1045
1556
  unsubscribeEvents() {
@@ -1219,12 +1730,12 @@ class AXPDesignerFlexDrawerComponent {
1219
1730
  </tr>
1220
1731
  <tr>
1221
1732
  <td [attr.colspan]="items().length" class="text-center">
1222
- <small>{{ range() }}</small>
1733
+ <small>{{ range() > 0 ? range() : ' ' }}</small>
1223
1734
  </td>
1224
1735
  </tr>
1225
1736
  </tbody>
1226
1737
  </table>
1227
- `, isInline: true, styles: ["@layer properties;:host{width:100%}:host table{width:100%;border-collapse:separate;--tw-border-spacing-x: 0px;--tw-border-spacing-y: 0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}:host table td{height:calc(var(--spacing, .25rem) * 10);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default)}:host table td:after{content:\"\";display:block}:host table td.picked{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);background-color:rgba(var(--ax-sys-color-primary-100))}@property --tw-border-spacing-x{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-spacing-y{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1738
+ `, isInline: true, styles: ["@layer properties;:host{display:block;width:100%}:host table{margin-inline:auto;width:auto;border-collapse:separate;--tw-border-spacing-x: var(--spacing, .25rem);--tw-border-spacing-y: var(--spacing, .25rem);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);empty-cells:show}:host table td{height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default)}:host table td[colspan]{height:auto;width:auto;border-style:var(--tw-border-style);border-width:0px}:host table td.picked{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);background-color:rgba(var(--ax-sys-color-primary-100))}@property --tw-border-spacing-x{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-spacing-y{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1228
1739
  }
1229
1740
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerFlexDrawerComponent, decorators: [{
1230
1741
  type: Component,
@@ -1238,12 +1749,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1238
1749
  </tr>
1239
1750
  <tr>
1240
1751
  <td [attr.colspan]="items().length" class="text-center">
1241
- <small>{{ range() }}</small>
1752
+ <small>{{ range() > 0 ? range() : ' ' }}</small>
1242
1753
  </td>
1243
1754
  </tr>
1244
1755
  </tbody>
1245
1756
  </table>
1246
- `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [], styles: ["@layer properties;:host{width:100%}:host table{width:100%;border-collapse:separate;--tw-border-spacing-x: 0px;--tw-border-spacing-y: 0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}:host table td{height:calc(var(--spacing, .25rem) * 10);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default)}:host table td:after{content:\"\";display:block}:host table td.picked{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);background-color:rgba(var(--ax-sys-color-primary-100))}@property --tw-border-spacing-x{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-spacing-y{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
1757
+ `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [], styles: ["@layer properties;:host{display:block;width:100%}:host table{margin-inline:auto;width:auto;border-collapse:separate;--tw-border-spacing-x: var(--spacing, .25rem);--tw-border-spacing-y: var(--spacing, .25rem);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);empty-cells:show}:host table td{height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default)}:host table td[colspan]{height:auto;width:auto;border-style:var(--tw-border-style);border-width:0px}:host table td.picked{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);background-color:rgba(var(--ax-sys-color-primary-100))}@property --tw-border-spacing-x{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-spacing-y{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
1247
1758
  }], propDecorators: { itemsCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }, { type: i0.Output, args: ["itemsChange"] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], handleMouseLeave: [{
1248
1759
  type: HostListener,
1249
1760
  args: ['mouseleave', ['$event']]
@@ -1274,13 +1785,8 @@ class AXPDesignerGridDrawerComponent {
1274
1785
  this.rangeR.set(0);
1275
1786
  this.rangeC.set(0);
1276
1787
  }
1277
- get __style() {
1278
- return {
1279
- '--cols-width': `${this.colsCount() == 0 ? 0 : 100 / this.colsCount()}%`,
1280
- };
1281
- }
1282
1788
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerGridDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1283
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerGridDrawerComponent, isStandalone: true, selector: "axp-designer-grid-drawer", inputs: { rowsCount: { classPropertyName: "rowsCount", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, colsCount: { classPropertyName: "colsCount", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowsCount: "rowsChange", colsCount: "columnsChange", onSelect: "onSelect" }, host: { listeners: { "mouseleave": "handleMouseLeave($event)" }, properties: { "style": "this.__style" } }, ngImport: i0, template: `
1789
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerGridDrawerComponent, isStandalone: true, selector: "axp-designer-grid-drawer", inputs: { rowsCount: { classPropertyName: "rowsCount", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, colsCount: { classPropertyName: "colsCount", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowsCount: "rowsChange", colsCount: "columnsChange", onSelect: "onSelect" }, host: { listeners: { "mouseleave": "handleMouseLeave($event)" } }, ngImport: i0, template: `
1284
1790
  <table class="table-picker">
1285
1791
  <tbody>
1286
1792
  @for (r of rows(); track $index) {
@@ -1296,12 +1802,12 @@ class AXPDesignerGridDrawerComponent {
1296
1802
  }
1297
1803
  <tr>
1298
1804
  <td [attr.colspan]="columns().length" class="text-center">
1299
- <small>{{ rangeC() }}×{{ rangeR() }}</small>
1805
+ <small>{{ rangeC() > 0 && rangeR() > 0 ? rangeC() + '×' + rangeR() : ' ' }}</small>
1300
1806
  </td>
1301
1807
  </tr>
1302
1808
  </tbody>
1303
1809
  </table>
1304
- `, isInline: true, styles: ["@layer properties;:host{width:100%}:host table{width:100%;border-collapse:separate;--tw-border-spacing-x: 0px;--tw-border-spacing-y: 0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}:host table td{height:calc(var(--spacing, .25rem) * 10);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);width:var(--cols-width)}:host table td:after{content:\"\";display:block}:host table td.picked{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);background-color:rgba(var(--ax-sys-color-primary-100))}@property --tw-border-spacing-x{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-spacing-y{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1810
+ `, isInline: true, styles: ["@layer properties;:host{display:block;width:100%}:host table{margin-inline:auto;width:auto;border-collapse:separate;--tw-border-spacing-x: var(--spacing, .25rem);--tw-border-spacing-y: var(--spacing, .25rem);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);empty-cells:show}:host table td{height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default)}:host table td[colspan]{height:auto;width:auto;border-style:var(--tw-border-style);border-width:0px}:host table td.picked{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);background-color:rgba(var(--ax-sys-color-primary-100))}@property --tw-border-spacing-x{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-spacing-y{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1305
1811
  }
1306
1812
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerGridDrawerComponent, decorators: [{
1307
1813
  type: Component,
@@ -1321,18 +1827,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1321
1827
  }
1322
1828
  <tr>
1323
1829
  <td [attr.colspan]="columns().length" class="text-center">
1324
- <small>{{ rangeC() }}×{{ rangeR() }}</small>
1830
+ <small>{{ rangeC() > 0 && rangeR() > 0 ? rangeC() + '×' + rangeR() : ' ' }}</small>
1325
1831
  </td>
1326
1832
  </tr>
1327
1833
  </tbody>
1328
1834
  </table>
1329
- `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [], styles: ["@layer properties;:host{width:100%}:host table{width:100%;border-collapse:separate;--tw-border-spacing-x: 0px;--tw-border-spacing-y: 0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}:host table td{height:calc(var(--spacing, .25rem) * 10);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);width:var(--cols-width)}:host table td:after{content:\"\";display:block}:host table td.picked{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);background-color:rgba(var(--ax-sys-color-primary-100))}@property --tw-border-spacing-x{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-spacing-y{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
1835
+ `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [], styles: ["@layer properties;:host{display:block;width:100%}:host table{margin-inline:auto;width:auto;border-collapse:separate;--tw-border-spacing-x: var(--spacing, .25rem);--tw-border-spacing-y: var(--spacing, .25rem);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);empty-cells:show}:host table td{height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default)}:host table td[colspan]{height:auto;width:auto;border-style:var(--tw-border-style);border-width:0px}:host table td.picked{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border-default);background-color:rgba(var(--ax-sys-color-primary-100))}@property --tw-border-spacing-x{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-spacing-y{syntax: \"<length>\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
1330
1836
  }], propDecorators: { rowsCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }, { type: i0.Output, args: ["rowsChange"] }], colsCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }, { type: i0.Output, args: ["columnsChange"] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], handleMouseLeave: [{
1331
1837
  type: HostListener,
1332
1838
  args: ['mouseleave', ['$event']]
1333
- }], __style: [{
1334
- type: HostBinding,
1335
- args: ['style']
1336
1839
  }] } });
1337
1840
 
1338
1841
  const WIDGET_PICKER_I18N = {
@@ -1613,7 +2116,7 @@ class AXPDesignerWidgetPickerComponent extends AXBasePageComponent {
1613
2116
  return this.isWidgetSelected(originalWidget);
1614
2117
  }
1615
2118
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerWidgetPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1616
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerWidgetPickerComponent, isStandalone: true, selector: "ng-component", inputs: { searchTerm: { classPropertyName: "searchTerm", publicName: "searchTerm", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchTerm: "searchTermChange" }, viewQueries: [{ propertyName: "tabsHost", first: true, predicate: ["tabsHost"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div [class]=\"pickerShellClass()\">\n <div>\n <ax-tabs #tabsHost [look]=\"'with-line'\" (onActiveTabChanged)=\"handleTabChange($event)\">\n @for (w of tabs(); track w.name; let i = $index) {\n <ax-tab-item\n [text]=\"(w.title | translate | async)!\"\n [key]=\"w.name\"\n [active]=\"currentTabIndex() === i\"\n ></ax-tab-item>\n }\n </ax-tabs>\n </div>\n <div class=\"overflow-auto\" #content>\n <div\n class=\"p-4 sticky top-0 z-10 transition-all\"\n #sticky=\"axpSticky\"\n [axpSticky]=\"'bg-lightest shadow-xl'\"\n [stickyParent]=\"content\"\n [stickyOffset]=\"10\"\n >\n <ax-search-box\n look=\"fill\"\n [placeholder]=\"(i18n.searchPlaceholder | translate | async) ?? ''\"\n [(ngModel)]=\"searchTerm\"\n [delayTime]=\"300\"\n [axAutoFocus]=\"true\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n <div class=\"flex flex-col p-4 pt-0\">\n @if (pinnedWidgetItems().length) {\n <div class=\"axp-designer-widget-picker__section axp-designer-widget-picker__section--first\">\n <div class=\"axp-designer-widget-picker__group-heading\">\n <span class=\"axp-designer-widget-picker__group-title\">{{\n (i18n.pinnedSectionTitle | translate | async) ?? ''\n }}</span>\n <span class=\"axp-designer-widget-picker__group-line\"></span>\n </div>\n <div class=\"grid grid-cols-2 gap-2\">\n @for (widgetItem of pinnedWidgetItems(); track $any(widgetItem.data).name) {\n <axp-widget-item\n [widget]=\"widgetItem\"\n [isSelected]=\"isWidgetItemSelected(widgetItem)\"\n (onWidgetClick)=\"handleWidgetClick($event)\"\n (onPinClick)=\"handleWidgetPinClick($event)\"\n >\n </axp-widget-item>\n }\n </div>\n </div>\n }\n @for (group of regularGroupedSections(); track group.trackKey; let isFirst = $first) {\n <div\n class=\"axp-designer-widget-picker__section\"\n [class.axp-designer-widget-picker__section--first]=\"isFirst && pinnedWidgetItems().length === 0\"\n >\n @if (group.title) {\n <div class=\"axp-designer-widget-picker__group-heading\">\n <span class=\"axp-designer-widget-picker__group-title\">{{\n (group.title | translate | async) ?? ''\n }}</span>\n <span class=\"axp-designer-widget-picker__group-line\"></span>\n </div>\n }\n <div class=\"grid grid-cols-2 gap-2\">\n @for (widgetItem of group.widgetItems; track $any(widgetItem.data).name) {\n <axp-widget-item\n [widget]=\"widgetItem\"\n [isSelected]=\"isWidgetItemSelected(widgetItem)\"\n (onWidgetClick)=\"handleWidgetClick($event)\"\n (onPinClick)=\"handleWidgetPinClick($event)\"\n >\n </axp-widget-item>\n }\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n@if (selectedWidgets().length) {\n <ax-footer>\n <ax-suffix>\n <ax-button\n look=\"solid\"\n color=\"primary\"\n [text]=\"(i18n.selectedCount | translate: { params: { count: selectedWidgets().length } } | async) ?? ''\"\n (onClick)=\"handleConfirmSelection()\"\n ></ax-button>\n </ax-suffix>\n </ax-footer>\n}\n", styles: ["@layer properties;.axp-designer-widget-picker__group-heading{margin-bottom:calc(var(--spacing, .25rem) * 3);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-bottom:var(--spacing, .25rem)}.axp-designer-widget-picker__group-title{flex-shrink:0;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}.axp-designer-widget-picker__group-line{min-width:calc(var(--spacing, .25rem) * 4);flex:1;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-border-default)}.axp-designer-widget-picker__section{margin-top:calc(var(--spacing, .25rem) * 6);padding-top:var(--spacing, .25rem)}.axp-designer-widget-picker__section.axp-designer-widget-picker__section--first{margin-top:0;padding-top:0}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXCommonModule }, { kind: "directive", type: i1$1.AXAutoFocusDirective, selector: "[axAutoFocus]", inputs: ["axAutoFocus", "axAutoFocusTime"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTabsModule }, { kind: "component", type: i5.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "reorderable", "reorderDelay", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged", "onTabsReordered"] }, { kind: "component", type: i5.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i6.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "directive", type: AXPStickyDirective, selector: "[axpSticky]", inputs: ["axpSticky", "stickyOffset", "stickyParent", "stickyTarget"], outputs: ["isStickyChange"], exportAs: ["axpSticky"] }, { kind: "component", type: AXPWidgetItemComponent, selector: "axp-widget-item", inputs: ["widget", "isSelected", "showPinButton", "customClasses"], outputs: ["onWidgetClick", "onPinClick"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i8.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager, encapsulation: i0.ViewEncapsulation.None }); }
2119
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerWidgetPickerComponent, isStandalone: true, selector: "ng-component", inputs: { searchTerm: { classPropertyName: "searchTerm", publicName: "searchTerm", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchTerm: "searchTermChange" }, viewQueries: [{ propertyName: "tabsHost", first: true, predicate: ["tabsHost"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div [class]=\"pickerShellClass()\">\n <div>\n <ax-tabs #tabsHost [look]=\"'with-line'\" (onActiveTabChanged)=\"handleTabChange($event)\">\n @for (w of tabs(); track w.name; let i = $index) {\n <ax-tab-item\n [text]=\"(w.title | translate | async)!\"\n [key]=\"w.name\"\n [active]=\"currentTabIndex() === i\"\n ></ax-tab-item>\n }\n </ax-tabs>\n </div>\n <div class=\"overflow-auto\" #content>\n <div\n class=\"p-4 sticky top-0 z-10 transition-all\"\n #sticky=\"axpSticky\"\n [axpSticky]=\"'lightest-surface shadow-xl'\"\n [stickyParent]=\"content\"\n [stickyOffset]=\"10\"\n >\n <ax-search-box\n look=\"fill\"\n [placeholder]=\"(i18n.searchPlaceholder | translate | async) ?? ''\"\n [(ngModel)]=\"searchTerm\"\n [delayTime]=\"300\"\n [axAutoFocus]=\"true\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n <div class=\"flex flex-col p-4 pt-0\">\n @if (pinnedWidgetItems().length) {\n <div class=\"axp-designer-widget-picker__section axp-designer-widget-picker__section--first\">\n <div class=\"axp-designer-widget-picker__group-heading\">\n <span class=\"axp-designer-widget-picker__group-title\">{{\n (i18n.pinnedSectionTitle | translate | async) ?? ''\n }}</span>\n <span class=\"axp-designer-widget-picker__group-line\"></span>\n </div>\n <div class=\"grid grid-cols-2 gap-2\">\n @for (widgetItem of pinnedWidgetItems(); track $any(widgetItem.data).name) {\n <axp-widget-item\n [widget]=\"widgetItem\"\n [isSelected]=\"isWidgetItemSelected(widgetItem)\"\n (onWidgetClick)=\"handleWidgetClick($event)\"\n (onPinClick)=\"handleWidgetPinClick($event)\"\n >\n </axp-widget-item>\n }\n </div>\n </div>\n }\n @for (group of regularGroupedSections(); track group.trackKey; let isFirst = $first) {\n <div\n class=\"axp-designer-widget-picker__section\"\n [class.axp-designer-widget-picker__section--first]=\"isFirst && pinnedWidgetItems().length === 0\"\n >\n @if (group.title) {\n <div class=\"axp-designer-widget-picker__group-heading\">\n <span class=\"axp-designer-widget-picker__group-title\">{{\n (group.title | translate | async) ?? ''\n }}</span>\n <span class=\"axp-designer-widget-picker__group-line\"></span>\n </div>\n }\n <div class=\"grid grid-cols-2 gap-2\">\n @for (widgetItem of group.widgetItems; track $any(widgetItem.data).name) {\n <axp-widget-item\n [widget]=\"widgetItem\"\n [isSelected]=\"isWidgetItemSelected(widgetItem)\"\n (onWidgetClick)=\"handleWidgetClick($event)\"\n (onPinClick)=\"handleWidgetPinClick($event)\"\n >\n </axp-widget-item>\n }\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n@if (selectedWidgets().length) {\n <ax-footer>\n <ax-suffix>\n <ax-button\n look=\"solid\"\n color=\"primary\"\n [text]=\"(i18n.selectedCount | translate: { params: { count: selectedWidgets().length } } | async) ?? ''\"\n (onClick)=\"handleConfirmSelection()\"\n ></ax-button>\n </ax-suffix>\n </ax-footer>\n}\n", styles: ["@layer properties;.axp-designer-widget-picker__group-heading{margin-bottom:calc(var(--spacing, .25rem) * 3);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-bottom:var(--spacing, .25rem)}.axp-designer-widget-picker__group-title{flex-shrink:0;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}.axp-designer-widget-picker__group-line{min-width:calc(var(--spacing, .25rem) * 4);flex:1;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-border-default)}.axp-designer-widget-picker__section{margin-top:calc(var(--spacing, .25rem) * 6);padding-top:var(--spacing, .25rem)}.axp-designer-widget-picker__section.axp-designer-widget-picker__section--first{margin-top:0;padding-top:0}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXCommonModule }, { kind: "directive", type: i1$1.AXAutoFocusDirective, selector: "[axAutoFocus]", inputs: ["axAutoFocus", "axAutoFocusTime"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTabsModule }, { kind: "component", type: i5.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "reorderable", "reorderDelay", "renderMode", "stacked", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged", "onTabsReordered"] }, { kind: "component", type: i5.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i6.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "directive", type: AXPStickyDirective, selector: "[axpSticky]", inputs: ["axpSticky", "stickyOffset", "stickyParent", "stickyTarget"], outputs: ["isStickyChange"], exportAs: ["axpSticky"] }, { kind: "component", type: AXPWidgetItemComponent, selector: "axp-widget-item", inputs: ["widget", "isSelected", "showPinButton", "customClasses"], outputs: ["onWidgetClick", "onPinClick"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager, encapsulation: i0.ViewEncapsulation.None }); }
1617
2120
  }
1618
2121
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerWidgetPickerComponent, decorators: [{
1619
2122
  type: Component,
@@ -1628,7 +2131,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1628
2131
  AXPStickyDirective,
1629
2132
  AXPWidgetItemComponent,
1630
2133
  AXTranslationModule,
1631
- ], changeDetection: ChangeDetectionStrategy.Eager, encapsulation: ViewEncapsulation.None, template: "<div [class]=\"pickerShellClass()\">\n <div>\n <ax-tabs #tabsHost [look]=\"'with-line'\" (onActiveTabChanged)=\"handleTabChange($event)\">\n @for (w of tabs(); track w.name; let i = $index) {\n <ax-tab-item\n [text]=\"(w.title | translate | async)!\"\n [key]=\"w.name\"\n [active]=\"currentTabIndex() === i\"\n ></ax-tab-item>\n }\n </ax-tabs>\n </div>\n <div class=\"overflow-auto\" #content>\n <div\n class=\"p-4 sticky top-0 z-10 transition-all\"\n #sticky=\"axpSticky\"\n [axpSticky]=\"'bg-lightest shadow-xl'\"\n [stickyParent]=\"content\"\n [stickyOffset]=\"10\"\n >\n <ax-search-box\n look=\"fill\"\n [placeholder]=\"(i18n.searchPlaceholder | translate | async) ?? ''\"\n [(ngModel)]=\"searchTerm\"\n [delayTime]=\"300\"\n [axAutoFocus]=\"true\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n <div class=\"flex flex-col p-4 pt-0\">\n @if (pinnedWidgetItems().length) {\n <div class=\"axp-designer-widget-picker__section axp-designer-widget-picker__section--first\">\n <div class=\"axp-designer-widget-picker__group-heading\">\n <span class=\"axp-designer-widget-picker__group-title\">{{\n (i18n.pinnedSectionTitle | translate | async) ?? ''\n }}</span>\n <span class=\"axp-designer-widget-picker__group-line\"></span>\n </div>\n <div class=\"grid grid-cols-2 gap-2\">\n @for (widgetItem of pinnedWidgetItems(); track $any(widgetItem.data).name) {\n <axp-widget-item\n [widget]=\"widgetItem\"\n [isSelected]=\"isWidgetItemSelected(widgetItem)\"\n (onWidgetClick)=\"handleWidgetClick($event)\"\n (onPinClick)=\"handleWidgetPinClick($event)\"\n >\n </axp-widget-item>\n }\n </div>\n </div>\n }\n @for (group of regularGroupedSections(); track group.trackKey; let isFirst = $first) {\n <div\n class=\"axp-designer-widget-picker__section\"\n [class.axp-designer-widget-picker__section--first]=\"isFirst && pinnedWidgetItems().length === 0\"\n >\n @if (group.title) {\n <div class=\"axp-designer-widget-picker__group-heading\">\n <span class=\"axp-designer-widget-picker__group-title\">{{\n (group.title | translate | async) ?? ''\n }}</span>\n <span class=\"axp-designer-widget-picker__group-line\"></span>\n </div>\n }\n <div class=\"grid grid-cols-2 gap-2\">\n @for (widgetItem of group.widgetItems; track $any(widgetItem.data).name) {\n <axp-widget-item\n [widget]=\"widgetItem\"\n [isSelected]=\"isWidgetItemSelected(widgetItem)\"\n (onWidgetClick)=\"handleWidgetClick($event)\"\n (onPinClick)=\"handleWidgetPinClick($event)\"\n >\n </axp-widget-item>\n }\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n@if (selectedWidgets().length) {\n <ax-footer>\n <ax-suffix>\n <ax-button\n look=\"solid\"\n color=\"primary\"\n [text]=\"(i18n.selectedCount | translate: { params: { count: selectedWidgets().length } } | async) ?? ''\"\n (onClick)=\"handleConfirmSelection()\"\n ></ax-button>\n </ax-suffix>\n </ax-footer>\n}\n", styles: ["@layer properties;.axp-designer-widget-picker__group-heading{margin-bottom:calc(var(--spacing, .25rem) * 3);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-bottom:var(--spacing, .25rem)}.axp-designer-widget-picker__group-title{flex-shrink:0;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}.axp-designer-widget-picker__group-line{min-width:calc(var(--spacing, .25rem) * 4);flex:1;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-border-default)}.axp-designer-widget-picker__section{margin-top:calc(var(--spacing, .25rem) * 6);padding-top:var(--spacing, .25rem)}.axp-designer-widget-picker__section.axp-designer-widget-picker__section--first{margin-top:0;padding-top:0}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
2134
+ ], changeDetection: ChangeDetectionStrategy.Eager, encapsulation: ViewEncapsulation.None, template: "<div [class]=\"pickerShellClass()\">\n <div>\n <ax-tabs #tabsHost [look]=\"'with-line'\" (onActiveTabChanged)=\"handleTabChange($event)\">\n @for (w of tabs(); track w.name; let i = $index) {\n <ax-tab-item\n [text]=\"(w.title | translate | async)!\"\n [key]=\"w.name\"\n [active]=\"currentTabIndex() === i\"\n ></ax-tab-item>\n }\n </ax-tabs>\n </div>\n <div class=\"overflow-auto\" #content>\n <div\n class=\"p-4 sticky top-0 z-10 transition-all\"\n #sticky=\"axpSticky\"\n [axpSticky]=\"'lightest-surface shadow-xl'\"\n [stickyParent]=\"content\"\n [stickyOffset]=\"10\"\n >\n <ax-search-box\n look=\"fill\"\n [placeholder]=\"(i18n.searchPlaceholder | translate | async) ?? ''\"\n [(ngModel)]=\"searchTerm\"\n [delayTime]=\"300\"\n [axAutoFocus]=\"true\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n <div class=\"flex flex-col p-4 pt-0\">\n @if (pinnedWidgetItems().length) {\n <div class=\"axp-designer-widget-picker__section axp-designer-widget-picker__section--first\">\n <div class=\"axp-designer-widget-picker__group-heading\">\n <span class=\"axp-designer-widget-picker__group-title\">{{\n (i18n.pinnedSectionTitle | translate | async) ?? ''\n }}</span>\n <span class=\"axp-designer-widget-picker__group-line\"></span>\n </div>\n <div class=\"grid grid-cols-2 gap-2\">\n @for (widgetItem of pinnedWidgetItems(); track $any(widgetItem.data).name) {\n <axp-widget-item\n [widget]=\"widgetItem\"\n [isSelected]=\"isWidgetItemSelected(widgetItem)\"\n (onWidgetClick)=\"handleWidgetClick($event)\"\n (onPinClick)=\"handleWidgetPinClick($event)\"\n >\n </axp-widget-item>\n }\n </div>\n </div>\n }\n @for (group of regularGroupedSections(); track group.trackKey; let isFirst = $first) {\n <div\n class=\"axp-designer-widget-picker__section\"\n [class.axp-designer-widget-picker__section--first]=\"isFirst && pinnedWidgetItems().length === 0\"\n >\n @if (group.title) {\n <div class=\"axp-designer-widget-picker__group-heading\">\n <span class=\"axp-designer-widget-picker__group-title\">{{\n (group.title | translate | async) ?? ''\n }}</span>\n <span class=\"axp-designer-widget-picker__group-line\"></span>\n </div>\n }\n <div class=\"grid grid-cols-2 gap-2\">\n @for (widgetItem of group.widgetItems; track $any(widgetItem.data).name) {\n <axp-widget-item\n [widget]=\"widgetItem\"\n [isSelected]=\"isWidgetItemSelected(widgetItem)\"\n (onWidgetClick)=\"handleWidgetClick($event)\"\n (onPinClick)=\"handleWidgetPinClick($event)\"\n >\n </axp-widget-item>\n }\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n@if (selectedWidgets().length) {\n <ax-footer>\n <ax-suffix>\n <ax-button\n look=\"solid\"\n color=\"primary\"\n [text]=\"(i18n.selectedCount | translate: { params: { count: selectedWidgets().length } } | async) ?? ''\"\n (onClick)=\"handleConfirmSelection()\"\n ></ax-button>\n </ax-suffix>\n </ax-footer>\n}\n", styles: ["@layer properties;.axp-designer-widget-picker__group-heading{margin-bottom:calc(var(--spacing, .25rem) * 3);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-bottom:var(--spacing, .25rem)}.axp-designer-widget-picker__group-title{flex-shrink:0;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}.axp-designer-widget-picker__group-line{min-width:calc(var(--spacing, .25rem) * 4);flex:1;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-border-default)}.axp-designer-widget-picker__section{margin-top:calc(var(--spacing, .25rem) * 6);padding-top:var(--spacing, .25rem)}.axp-designer-widget-picker__section.axp-designer-widget-picker__section--first{margin-top:0;padding-top:0}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
1632
2135
  }], ctorParameters: () => [], propDecorators: { tabsHost: [{ type: i0.ViewChild, args: ['tabsHost', { isSignal: true }] }], searchTerm: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTerm", required: false }] }, { type: i0.Output, args: ["searchTermChange"] }] } });
1633
2136
 
1634
2137
  var widgetPicker_component = /*#__PURE__*/Object.freeze({
@@ -1637,6 +2140,38 @@ var widgetPicker_component = /*#__PURE__*/Object.freeze({
1637
2140
  });
1638
2141
 
1639
2142
  class AXPWidgetDesignerRendererDirective {
2143
+ /** Prefer `__meta__.id`; fall back to object identity so nameless AI children are not all treated as the same widget. */
2144
+ sameWidget(a, b) {
2145
+ if (!a || !b) {
2146
+ return a === b;
2147
+ }
2148
+ const idA = this.getId(a);
2149
+ const idB = this.getId(b);
2150
+ if (idA != null && idB != null) {
2151
+ return idA == idB;
2152
+ }
2153
+ return a === b;
2154
+ }
2155
+ /**
2156
+ * Drawer inserts (flex, grid, table) run inside a transaction that refreshes the page only.
2157
+ * Each widget copies `node.children` into its own signal at creation, so a page refresh must
2158
+ * also sync every nested container or the size picker stays on screen after cells are added.
2159
+ */
2160
+ shouldSyncChildren(widget) {
2161
+ return this.isCurrentWidget(widget) || this.isNestedUnder(widget, this.node());
2162
+ }
2163
+ isNestedUnder(ancestor, node) {
2164
+ const children = ancestor.children;
2165
+ if (!children?.length) {
2166
+ return false;
2167
+ }
2168
+ for (const child of children) {
2169
+ if (this.sameWidget(child, node) || this.isNestedUnder(child, node)) {
2170
+ return true;
2171
+ }
2172
+ }
2173
+ return false;
2174
+ }
1640
2175
  /**
1641
2176
  * @ignore
1642
2177
  */
@@ -1668,18 +2203,18 @@ class AXPWidgetDesignerRendererDirective {
1668
2203
  ...(ngDevMode ? [{ debugName: "node" }] : /* istanbul ignore next */ []));
1669
2204
  this.hostElement = () => this.instance?.host;
1670
2205
  this.getId = (w) => get(w, '__meta__.id');
1671
- this.isCurrentWidget = (w) => this.getId(w) == this.getId(this.node());
2206
+ this.isCurrentWidget = (w) => this.sameWidget(w, this.node());
1672
2207
  this.subs = [];
1673
2208
  this.subs[0] = this.service.onUpdate
1674
2209
  .pipe(
1675
2210
  //debounceTime(time),
1676
- distinctUntilChanged((p, c) => this.getId(p.widget) == this.getId(c.widget) && isEqual(p.values?.options, c.values?.options)), filter((c) => this.isCurrentWidget(c.widget) && !this.locked()))
2211
+ distinctUntilChanged((p, c) => this.sameWidget(p.widget, c.widget) && isEqual(p.values?.options, c.values?.options)), filter((c) => this.isCurrentWidget(c.widget) && !this.locked()))
1677
2212
  .subscribe((c) => {
1678
2213
  this.applyOptions(c.values?.options);
1679
2214
  });
1680
2215
  //
1681
2216
  this.subs[1] = this.service.onSelected
1682
- .pipe(distinctUntilChanged((p, c) => this.getId(p.widget) == this.getId(c.widget)))
2217
+ .pipe(distinctUntilChanged((p, c) => this.sameWidget(p.widget, c.widget)))
1683
2218
  .subscribe((c) => {
1684
2219
  this.zone.runOutsideAngular(() => {
1685
2220
  if (this.isCurrentWidget(c.widget) && !this.locked()) {
@@ -1692,7 +2227,7 @@ class AXPWidgetDesignerRendererDirective {
1692
2227
  });
1693
2228
  //
1694
2229
  this.subs[2] = this.service.onFocused
1695
- .pipe(distinctUntilChanged((p, c) => !isNil(p.widget) && !isNil(c.widget) && this.getId(p.widget) == this.getId(c.widget)))
2230
+ .pipe(distinctUntilChanged((p, c) => this.sameWidget(p.widget, c.widget)))
1696
2231
  .subscribe((c) => {
1697
2232
  if (c.widget && this.isCurrentWidget(c.widget)) {
1698
2233
  this.addOverlay();
@@ -1703,9 +2238,12 @@ class AXPWidgetDesignerRendererDirective {
1703
2238
  });
1704
2239
  //
1705
2240
  this.subs[3] = this.service.onRefresh
1706
- .pipe(debounceTime(200), filter((c) => this.isCurrentWidget(c.widget)))
2241
+ .pipe(debounceTime(200), filter((c) => this.shouldSyncChildren(c.widget)))
1707
2242
  .subscribe((c) => {
1708
2243
  this.instance?.setChildren(this.node().children ?? []);
2244
+ if (this.isCurrentWidget(c.widget)) {
2245
+ this.applyOptions(this.node().options);
2246
+ }
1709
2247
  });
1710
2248
  //
1711
2249
  }
@@ -1725,6 +2263,9 @@ class AXPWidgetDesignerRendererDirective {
1725
2263
  this.subs.forEach((s) => {
1726
2264
  s.unsubscribe();
1727
2265
  });
2266
+ if (this.node()) {
2267
+ this.service.unregister(this.node());
2268
+ }
1728
2269
  // Clear loading state and reset tracking variables
1729
2270
  this.isLoadingComponent = false;
1730
2271
  this.currentNodeId = null;
@@ -1745,6 +2286,9 @@ class AXPWidgetDesignerRendererDirective {
1745
2286
  // this.isLoadingComponent = false;
1746
2287
  // return;
1747
2288
  // }
2289
+ if (this.instance && this.node()) {
2290
+ this.service.unregister(this.node());
2291
+ }
1748
2292
  this.viewContainerRef.clear();
1749
2293
  this.isLoading.set(true);
1750
2294
  this.currentNodeId = nodeId;
@@ -1774,7 +2318,13 @@ class AXPWidgetDesignerRendererDirective {
1774
2318
  },
1775
2319
  ],
1776
2320
  });
1777
- var com = await this.config?.components[this.mode()]?.component();
2321
+ const loadComponent = this.config?.components[this.mode()]?.component ??
2322
+ this.config?.components['edit']?.component ??
2323
+ this.config?.components['view']?.component;
2324
+ const com = await loadComponent?.();
2325
+ if (!com) {
2326
+ throw new Error(`No designer/edit/view component registered for widget "${this.node().type}"`);
2327
+ }
1778
2328
  this.instance = this.viewContainerRef.createComponent(com, { injector: token }).instance;
1779
2329
  this.instance.locked = this.locked();
1780
2330
  this.instance.parent = this.parentNode();
@@ -1848,9 +2398,28 @@ class AXPWidgetDesignerRendererDirective {
1848
2398
  applySelectedStyle() {
1849
2399
  if (this.locked())
1850
2400
  return;
1851
- //
1852
- this.hostElement()?.classList.add('axp-state-selected');
1853
- this.hostElement()?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
2401
+ const host = this.hostElement();
2402
+ if (!host) {
2403
+ return;
2404
+ }
2405
+ host.classList.add('axp-state-selected');
2406
+ if (!this.isHostFullyVisible(host)) {
2407
+ host.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
2408
+ }
2409
+ }
2410
+ isHostFullyVisible(host) {
2411
+ const canvas = host.closest('.axp-designer-canvas');
2412
+ const board = host.closest('.axp-designer-board');
2413
+ const container = canvas instanceof HTMLElement ? canvas : board instanceof HTMLElement ? board : host.parentElement;
2414
+ if (!container) {
2415
+ return true;
2416
+ }
2417
+ const hostRect = host.getBoundingClientRect();
2418
+ const containerRect = container.getBoundingClientRect();
2419
+ return (hostRect.top >= containerRect.top &&
2420
+ hostRect.bottom <= containerRect.bottom &&
2421
+ hostRect.left >= containerRect.left &&
2422
+ hostRect.right <= containerRect.right);
1854
2423
  }
1855
2424
  removeSelectedStyle() {
1856
2425
  this.hostElement()?.classList.remove('axp-state-selected');
@@ -2003,6 +2572,24 @@ class AXPDesignerBoardComponent {
2003
2572
  this.contextMenu?.refresh();
2004
2573
  });
2005
2574
  }
2575
+ handleBoardContextMenu(event) {
2576
+ const target = event.target;
2577
+ if (!(target instanceof Element)) {
2578
+ return;
2579
+ }
2580
+ const host = target.closest('.axp-widget-host');
2581
+ if (!(host instanceof HTMLElement)) {
2582
+ return;
2583
+ }
2584
+ const renderer = get(host, '__meta__.renderer');
2585
+ const instance = get(host, '__meta__.instance');
2586
+ if (!renderer || !instance) {
2587
+ return;
2588
+ }
2589
+ event.preventDefault();
2590
+ event.stopPropagation();
2591
+ this.contextMenu?.showAt({ x: event.clientX, y: event.clientY }, host);
2592
+ }
2006
2593
  handleContextMenuOpening(e) {
2007
2594
  const renderer = get(e.targetElement, '__meta__.renderer');
2008
2595
  const instance = get(e.targetElement, '__meta__.instance');
@@ -2093,26 +2680,32 @@ class AXPDesignerBoardComponent {
2093
2680
  const cls = {};
2094
2681
  cls[`width`] = this.width();
2095
2682
  cls[`max-width`] = '1280px';
2683
+ cls[`min-height`] = 'calc(100% - 5rem)';
2096
2684
  return cls;
2097
2685
  }
2098
2686
  get __class() {
2099
2687
  const cls = {};
2100
2688
  cls[`axp-designer-board`] = true;
2101
2689
  cls[`axp-state-design`] = true;
2102
- cls['h-full'] = true;
2690
+ cls['min-h-full'] = true;
2691
+ cls['shrink-0'] = true;
2692
+ cls['self-start'] = true;
2103
2693
  cls['overflow-auto'] = true;
2104
2694
  cls[`axp-preview-${this.service.size()}`] = true;
2105
2695
  return cls;
2106
2696
  }
2107
2697
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerBoardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2108
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXPDesignerBoardComponent, isStandalone: true, selector: "axp-designer-board", host: { properties: { "style": "this.__style", "class": "this.__class" } }, viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["contextMenu"], descendants: true }], ngImport: i0, template: "<axp-widgets-container [context]=\"context\">\n <ng-container axp-widget-designer-renderer [node]=\"service.currentPage()\" [mode]=\"'designer'\"></ng-container>\n</axp-widgets-container>\n\n<ax-context-menu class=\"ax-dark\" #contextMenu [target]=\"'.axp-widget-host'\" [orientation]=\"'vertical'\"\n [closeOn]=\"'leave'\" (onOpening)=\"handleContextMenuOpening($event)\" (onItemClick)=\"handleContextMenuItemClick($event)\">\n</ax-context-menu>", dependencies: [{ kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1$3.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: AXPWidgetDesignerRendererDirective, selector: "[axp-widget-designer-renderer]", inputs: ["parentNode", "parentElement", "parentComponent", "index", "locked", "mode", "node"] }, { kind: "ngmodule", type: AXMenuModule }, { kind: "component", type: i2$2.AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "closeOnRouteChange", "items", "target"], outputs: ["onItemClick", "onOpening", "onClose"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2698
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerBoardComponent, isStandalone: true, selector: "axp-designer-board", host: { listeners: { "contextmenu": "handleBoardContextMenu($event)" }, properties: { "style": "this.__style", "class": "this.__class" } }, viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["contextMenu"], descendants: true }], ngImport: i0, template: "<axp-widgets-container [context]=\"context\">\n <!-- ----- Remount the page when epoch changes; keep the document node identity ----- -->\n @for (epoch of [service.pageRenderEpoch()]; track epoch) {\n <ng-container axp-widget-designer-renderer [node]=\"service.currentPage()\" [mode]=\"'designer'\"></ng-container>\n }\n</axp-widgets-container>\n\n<!-- Opened from the board host contextmenu; [target] cannot bind hosts that render after the menu. -->\n<ax-context-menu\n class=\"ax-dark\"\n #contextMenu\n [orientation]=\"'vertical'\"\n [closeOn]=\"'leave'\"\n (onOpening)=\"handleContextMenuOpening($event)\"\n (onItemClick)=\"handleContextMenuItemClick($event)\"\n>\n</ax-context-menu>", dependencies: [{ kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1$3.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: AXPWidgetDesignerRendererDirective, selector: "[axp-widget-designer-renderer]", inputs: ["parentNode", "parentElement", "parentComponent", "index", "locked", "mode", "node"] }, { kind: "ngmodule", type: AXMenuModule }, { kind: "component", type: i2$2.AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "closeOnRouteChange", "items", "target"], outputs: ["onItemClick", "onOpening", "onClose"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2109
2699
  }
2110
2700
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerBoardComponent, decorators: [{
2111
2701
  type: Component,
2112
- args: [{ selector: 'axp-designer-board', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXPWidgetCoreModule, AXPWidgetDesignerRendererDirective, AXMenuModule], template: "<axp-widgets-container [context]=\"context\">\n <ng-container axp-widget-designer-renderer [node]=\"service.currentPage()\" [mode]=\"'designer'\"></ng-container>\n</axp-widgets-container>\n\n<ax-context-menu class=\"ax-dark\" #contextMenu [target]=\"'.axp-widget-host'\" [orientation]=\"'vertical'\"\n [closeOn]=\"'leave'\" (onOpening)=\"handleContextMenuOpening($event)\" (onItemClick)=\"handleContextMenuItemClick($event)\">\n</ax-context-menu>" }]
2702
+ args: [{ selector: 'axp-designer-board', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXPWidgetCoreModule, AXPWidgetDesignerRendererDirective, AXMenuModule], template: "<axp-widgets-container [context]=\"context\">\n <!-- ----- Remount the page when epoch changes; keep the document node identity ----- -->\n @for (epoch of [service.pageRenderEpoch()]; track epoch) {\n <ng-container axp-widget-designer-renderer [node]=\"service.currentPage()\" [mode]=\"'designer'\"></ng-container>\n }\n</axp-widgets-container>\n\n<!-- Opened from the board host contextmenu; [target] cannot bind hosts that render after the menu. -->\n<ax-context-menu\n class=\"ax-dark\"\n #contextMenu\n [orientation]=\"'vertical'\"\n [closeOn]=\"'leave'\"\n (onOpening)=\"handleContextMenuOpening($event)\"\n (onItemClick)=\"handleContextMenuItemClick($event)\"\n>\n</ax-context-menu>" }]
2113
2703
  }], ctorParameters: () => [], propDecorators: { contextMenu: [{
2114
2704
  type: ViewChild,
2115
2705
  args: ['contextMenu']
2706
+ }], handleBoardContextMenu: [{
2707
+ type: HostListener,
2708
+ args: ['contextmenu', ['$event']]
2116
2709
  }], __style: [{
2117
2710
  type: HostBinding,
2118
2711
  args: ['style']
@@ -2146,7 +2739,7 @@ class AXPDesignerBreadcrumbsComponent {
2146
2739
  <i class="ax-icon ax-icon-chevron-right"></i>
2147
2740
  </ng-template>
2148
2741
  </ax-breadcrumbs>
2149
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "ngmodule", type: AXBreadcrumbsModule }, { kind: "component", type: i1$4.AXBreadCrumbsComponent, selector: "ax-breadcrumbs" }, { kind: "component", type: i1$4.AXBreadCrumbsItemComponent, selector: "ax-breadcrumbs-item", inputs: ["disabled", "active"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i8.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2742
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "ngmodule", type: AXBreadcrumbsModule }, { kind: "component", type: i1$4.AXBreadCrumbsComponent, selector: "ax-breadcrumbs" }, { kind: "component", type: i1$4.AXBreadCrumbsItemComponent, selector: "ax-breadcrumbs-item", inputs: ["disabled", "active"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2150
2743
  }
2151
2744
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerBreadcrumbsComponent, decorators: [{
2152
2745
  type: Component,
@@ -2174,73 +2767,66 @@ class AXPLayoutDesignerDrawerContentComponent {
2174
2767
  constructor() { }
2175
2768
  ngOnInit() { }
2176
2769
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLayoutDesignerDrawerContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2177
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXPLayoutDesignerDrawerContentComponent, isStandalone: true, selector: "axp-designer-drawer-content", ngImport: i0, template: "<div class=\"flex flex-col border-e w-72 h-full\">\n <div class=\"flex flex-row w-full justify-between p-2 border-b\">\n <div class=\"flex flex-row gap-2 items-center font-semibold\">\n <ng-content select=\"ax-title\"></ng-content>\n </div>\n <div class=\"flex gap-1 items-center\">\n <ng-content select=\"ax-content[header]\">\n </ng-content>\n <ax-close-button></ax-close-button>\n </div>\n </div>\n <div class=\"flex-1 h-full overflow-auto\">\n <ng-content select=\"ax-content[body]\">\n\n </ng-content>\n </div>\n</div>", dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorCloseButtonComponent, selector: "ax-close-button", inputs: ["closeAll", "icon"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
2770
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXPLayoutDesignerDrawerContentComponent, isStandalone: true, selector: "axp-designer-drawer-content", ngImport: i0, template: "<div class=\"flex flex-col border-e w-72 h-full\">\n <div class=\"flex flex-row w-full justify-between p-2 border-b\">\n <div class=\"flex flex-row gap-2 items-center font-semibold\">\n <ng-content select=\"ax-title\"></ng-content>\n </div>\n <div class=\"flex gap-1 items-center\">\n <ng-content select=\"ax-content[header]\">\n </ng-content>\n <ax-close-button class=\"ax-sm\"></ax-close-button>\n </div>\n </div>\n <div class=\"flex-1 h-full overflow-auto\">\n <ng-content select=\"ax-content[body]\">\n\n </ng-content>\n </div>\n</div>", dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXDecoratorCloseButtonComponent, selector: "ax-close-button", inputs: ["closeAll", "icon"] }, { kind: "ngmodule", type: AXDecoratorModule }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
2178
2771
  }
2179
2772
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLayoutDesignerDrawerContentComponent, decorators: [{
2180
2773
  type: Component,
2181
- args: [{ imports: [AXDecoratorModule], selector: 'axp-designer-drawer-content', changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"flex flex-col border-e w-72 h-full\">\n <div class=\"flex flex-row w-full justify-between p-2 border-b\">\n <div class=\"flex flex-row gap-2 items-center font-semibold\">\n <ng-content select=\"ax-title\"></ng-content>\n </div>\n <div class=\"flex gap-1 items-center\">\n <ng-content select=\"ax-content[header]\">\n </ng-content>\n <ax-close-button></ax-close-button>\n </div>\n </div>\n <div class=\"flex-1 h-full overflow-auto\">\n <ng-content select=\"ax-content[body]\">\n\n </ng-content>\n </div>\n</div>" }]
2774
+ args: [{ imports: [AXButtonModule, AXDecoratorModule], selector: 'axp-designer-drawer-content', changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"flex flex-col border-e w-72 h-full\">\n <div class=\"flex flex-row w-full justify-between p-2 border-b\">\n <div class=\"flex flex-row gap-2 items-center font-semibold\">\n <ng-content select=\"ax-title\"></ng-content>\n </div>\n <div class=\"flex gap-1 items-center\">\n <ng-content select=\"ax-content[header]\">\n </ng-content>\n <ax-close-button class=\"ax-sm\"></ax-close-button>\n </div>\n </div>\n <div class=\"flex-1 h-full overflow-auto\">\n <ng-content select=\"ax-content[body]\">\n\n </ng-content>\n </div>\n</div>" }]
2182
2775
  }], ctorParameters: () => [] });
2183
2776
 
2184
- const LIST_REGISTERED_QUERIES_KEY = 'PlatformManagement:ListRegisteredQueries';
2777
+ //#region ----- Imports -----
2778
+ //#endregion
2779
+ //#region ----- Component -----
2185
2780
  class AXPDesignerDataSourcesComponent {
2186
2781
  constructor() {
2187
- this.service = inject(AXPDesignerService);
2188
- this.commandService = inject(AXPCommandService);
2189
- this.queryService = inject(AXPQueryService);
2190
- this.list = signal([], /* @ts-ignore */
2191
- ...(ngDevMode ? [{ debugName: "list" }] : /* istanbul ignore next */ []));
2782
+ //#region ----- Services & Dependencies -----
2783
+ this.store = inject(AXP_LOCAL_DATA_SOURCE_STORE);
2784
+ this.listDialog = inject(AXPLocalDataSourceEditorDialogService);
2192
2785
  }
2193
- async ngOnInit() {
2194
- try {
2195
- const result = await this.queryService.fetch(LIST_REGISTERED_QUERIES_KEY, {
2196
- skip: 0,
2197
- take: 5000,
2198
- });
2199
- const page = runtimeQueryCollectionData(result);
2200
- const items = (page?.items ?? []).map((c) => {
2201
- const row = c;
2202
- return { name: row.name, title: row.title };
2203
- });
2204
- this.list.set(items);
2205
- }
2206
- catch {
2207
- this.list.set([]);
2208
- }
2786
+ //#endregion
2787
+ //#region ----- Event Handlers -----
2788
+ handleAddClick() {
2789
+ void this.openListDialog();
2209
2790
  }
2210
- async handleAddClick() {
2211
- await this.commandService.execute('Entity:Create', {
2212
- entity: 'PlatformManagement.QueryDefinition',
2213
- entityInfo: {
2214
- name: 'QueryDefinition',
2215
- module: 'PlatformManagement',
2216
- title: '@platform-management:query-definitions.entities.query-definition.title',
2217
- },
2218
- data: {},
2219
- options: {
2220
- process: {
2221
- canCreateNewOne: false,
2222
- redirect: false,
2223
- },
2224
- layout: {
2225
- size: 'lg',
2226
- },
2227
- },
2228
- metadata: {
2229
- formBuilder: true,
2230
- },
2791
+ handleEdit(id) {
2792
+ void this.openListDialog(id);
2793
+ }
2794
+ handleRemove(id) {
2795
+ this.store.remove(id);
2796
+ }
2797
+ //#endregion
2798
+ //#region ----- Private -----
2799
+ async openListDialog(id) {
2800
+ const existing = id ? this.store.get(id) : undefined;
2801
+ const form = await this.listDialog.open({
2802
+ mode: existing ? 'edit' : 'create',
2803
+ name: existing?.name,
2804
+ columns: existing?.columns,
2805
+ items: existing?.items,
2231
2806
  });
2807
+ if (!form) {
2808
+ return;
2809
+ }
2810
+ if (existing && id) {
2811
+ this.store.update(id, { name: form.name, items: form.items, columns: form.columns });
2812
+ return;
2813
+ }
2814
+ const created = this.store.create(form.name);
2815
+ this.store.update(created.id, { items: form.items, columns: form.columns });
2232
2816
  }
2233
2817
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerDataSourcesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2234
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerDataSourcesComponent, isStandalone: true, selector: "axp-designer-data-sources", ngImport: i0, template: "<axp-designer-drawer-content>\n <ax-title>Data Sources</ax-title>\n\n <ax-content header>\n <ax-button look=\"blank\" class=\"ax-sm\" title=\"Add New Data Source\" (onClick)=\"handleAddClick()\">\n <ax-icon class=\"fa-light fa-plus\"></ax-icon>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @if (list().length) {\n <ul class=\"axp-designer-drawer-list\">\n @for (item of list(); track $index) {\n <li>\n <i class=\"me-1 fa-solid fa-table\"></i>\n {{ item.title }}\n </li>\n }\n </ul>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-database\"\n [title]=\"'@layout-designer:components.data-sources.empty-states.no-data-sources.title'\"\n [description]=\"'@layout-designer:components.data-sources.empty-states.no-data-sources.description'\"\n >\n </axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n", dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXPLayoutDesignerDrawerContentComponent, selector: "axp-designer-drawer-content" }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2818
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerDataSourcesComponent, isStandalone: true, selector: "axp-designer-data-sources", ngImport: i0, template: "<axp-designer-drawer-content>\n <ax-title>{{ '@layout-designer:components.data-sources.title' | translate | async }}</ax-title>\n\n <ax-content header>\n <ax-button\n look=\"blank\"\n class=\"ax-sm\"\n [title]=\"('@layout-designer:components.data-sources.add' | translate | async) ?? ''\"\n (onClick)=\"handleAddClick()\"\n >\n <ax-icon class=\"fa-light fa-plus\"></ax-icon>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @if (store.sources().length) {\n <!-- ----- Form-local named lists ----- -->\n <ul class=\"axp-designer-drawer-list\">\n @for (item of store.sources(); track item.id) {\n <li class=\"flex items-center gap-1\">\n <i class=\"me-1 fa-solid fa-table\"></i>\n <span class=\"flex-1 truncate\">{{ item.name || ('@layout-designer:components.data-sources.untitled' | translate | async) }}</span>\n <ax-button\n look=\"blank\"\n class=\"ax-sm\"\n [title]=\"('@general:actions.edit.title' | translate | async) ?? ''\"\n (onClick)=\"handleEdit(item.id)\"\n >\n <ax-icon class=\"fa-light fa-pen\"></ax-icon>\n </ax-button>\n <ax-button\n look=\"blank\"\n class=\"ax-sm\"\n [title]=\"('@layout-designer:components.data-sources.delete' | translate | async) ?? ''\"\n (onClick)=\"handleRemove(item.id)\"\n >\n <ax-icon class=\"fa-light fa-trash\"></ax-icon>\n </ax-button>\n </li>\n }\n </ul>\n }\n <!-- ----- Empty local bag ----- -->\n @else {\n <axp-state-message\n icon=\"fa-light fa-database\"\n [title]=\"'@layout-designer:components.data-sources.empty-states.no-data-sources.title'\"\n [description]=\"'@layout-designer:components.data-sources.empty-states.no-data-sources.description'\"\n >\n </axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n", dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPLayoutDesignerDrawerContentComponent, selector: "axp-designer-drawer-content" }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2235
2819
  }
2236
2820
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerDataSourcesComponent, decorators: [{
2237
2821
  type: Component,
2238
2822
  args: [{ selector: 'axp-designer-data-sources', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
2823
+ AsyncPipe,
2239
2824
  AXDecoratorModule,
2240
2825
  AXButtonModule,
2826
+ AXTranslationModule,
2241
2827
  AXPLayoutDesignerDrawerContentComponent,
2242
- AXPStateMessageComponent
2243
- ], template: "<axp-designer-drawer-content>\n <ax-title>Data Sources</ax-title>\n\n <ax-content header>\n <ax-button look=\"blank\" class=\"ax-sm\" title=\"Add New Data Source\" (onClick)=\"handleAddClick()\">\n <ax-icon class=\"fa-light fa-plus\"></ax-icon>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @if (list().length) {\n <ul class=\"axp-designer-drawer-list\">\n @for (item of list(); track $index) {\n <li>\n <i class=\"me-1 fa-solid fa-table\"></i>\n {{ item.title }}\n </li>\n }\n </ul>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-database\"\n [title]=\"'@layout-designer:components.data-sources.empty-states.no-data-sources.title'\"\n [description]=\"'@layout-designer:components.data-sources.empty-states.no-data-sources.description'\"\n >\n </axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n" }]
2828
+ AXPStateMessageComponent,
2829
+ ], template: "<axp-designer-drawer-content>\n <ax-title>{{ '@layout-designer:components.data-sources.title' | translate | async }}</ax-title>\n\n <ax-content header>\n <ax-button\n look=\"blank\"\n class=\"ax-sm\"\n [title]=\"('@layout-designer:components.data-sources.add' | translate | async) ?? ''\"\n (onClick)=\"handleAddClick()\"\n >\n <ax-icon class=\"fa-light fa-plus\"></ax-icon>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @if (store.sources().length) {\n <!-- ----- Form-local named lists ----- -->\n <ul class=\"axp-designer-drawer-list\">\n @for (item of store.sources(); track item.id) {\n <li class=\"flex items-center gap-1\">\n <i class=\"me-1 fa-solid fa-table\"></i>\n <span class=\"flex-1 truncate\">{{ item.name || ('@layout-designer:components.data-sources.untitled' | translate | async) }}</span>\n <ax-button\n look=\"blank\"\n class=\"ax-sm\"\n [title]=\"('@general:actions.edit.title' | translate | async) ?? ''\"\n (onClick)=\"handleEdit(item.id)\"\n >\n <ax-icon class=\"fa-light fa-pen\"></ax-icon>\n </ax-button>\n <ax-button\n look=\"blank\"\n class=\"ax-sm\"\n [title]=\"('@layout-designer:components.data-sources.delete' | translate | async) ?? ''\"\n (onClick)=\"handleRemove(item.id)\"\n >\n <ax-icon class=\"fa-light fa-trash\"></ax-icon>\n </ax-button>\n </li>\n }\n </ul>\n }\n <!-- ----- Empty local bag ----- -->\n @else {\n <axp-state-message\n icon=\"fa-light fa-database\"\n [title]=\"'@layout-designer:components.data-sources.empty-states.no-data-sources.title'\"\n [description]=\"'@layout-designer:components.data-sources.empty-states.no-data-sources.description'\"\n >\n </axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n" }]
2244
2830
  }] });
2245
2831
 
2246
2832
  class AXPDesignerHistoryComponent {
@@ -2248,7 +2834,7 @@ class AXPDesignerHistoryComponent {
2248
2834
  this.service = inject(AXPDesignerService);
2249
2835
  }
2250
2836
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerHistoryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2251
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerHistoryComponent, isStandalone: true, selector: "axp-designer-history", ngImport: i0, template: "<axp-designer-drawer-content>\n <ax-title>History</ax-title>\n\n <ax-content header>\n <ax-button look=\"blank\" class=\"ax-sm\">\n <ax-icon class=\"fa-light fa-ellipsis\"></ax-icon>\n <ax-dropdown-panel class=\"ax-sm\">\n <ax-button-item-list>\n <ax-button-item text=\"Clear\" name=\"add\" (onClick)=\"service.clearHistory()\"> </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @if (service.history().length) {\n <ul class=\"axp-designer-drawer-list\">\n @for (step of service.history(); track $index) {\n <li\n (click)=\"service.navigateTo($index)\"\n [class.italic]=\"step.isFuture\"\n [class.text-neutral-400]=\"step.isFuture\"\n [class.state-selected]=\"step.isCurrent\"\n >\n <i class=\"me-1\" [ngClass]=\"step.icon\"></i>\n {{ step.description }}\n </li>\n }\n </ul>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-history\"\n [title]=\"'@layout-designer:components.history.empty-states.no-changes.title'\"\n [description]=\"'@layout-designer:components.history.empty-states.no-changes.description'\"\n >\n </axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i2.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i2.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i4.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "component", type: AXPLayoutDesignerDrawerContentComponent, selector: "axp-designer-drawer-content" }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2837
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerHistoryComponent, isStandalone: true, selector: "axp-designer-history", ngImport: i0, template: "<axp-designer-drawer-content>\n <ax-title>History</ax-title>\n\n <ax-content header>\n <ax-button look=\"blank\" class=\"ax-sm\">\n <ax-icon class=\"fa-light fa-ellipsis\"></ax-icon>\n <ax-dropdown-panel class=\"ax-sm\">\n <ax-button-item-list>\n <ax-button-item text=\"Clear\" name=\"add\" (onClick)=\"service.clearHistory()\"> </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @if (service.history().length) {\n <ul class=\"axp-designer-drawer-list\">\n @for (step of service.history(); track $index) {\n <li\n (click)=\"service.navigateTo($index)\"\n [class.italic]=\"step.isFuture\"\n [class.text-neutral-400]=\"step.isFuture\"\n [class.state-selected]=\"step.isCurrent\"\n >\n <i class=\"me-1\" [ngClass]=\"step.icon\"></i>\n {{ step.description }}\n </li>\n }\n </ul>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-history\"\n [title]=\"'@layout-designer:components.history.empty-states.no-changes.title'\"\n [description]=\"'@layout-designer:components.history.empty-states.no-changes.description'\"\n >\n </axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i2.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i2.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i4$1.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "component", type: AXPLayoutDesignerDrawerContentComponent, selector: "axp-designer-drawer-content" }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2252
2838
  }
2253
2839
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerHistoryComponent, decorators: [{
2254
2840
  type: Component,
@@ -2263,8 +2849,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2263
2849
  }] });
2264
2850
 
2265
2851
  class AXPDesignerOutlineComponent {
2852
+ //#endregion
2853
+ //#region ----- Lifecycle -----
2266
2854
  constructor() {
2855
+ //#region ----- Services & Dependencies -----
2267
2856
  this.service = inject(AXPDesignerService);
2857
+ this.contextMenu = viewChild('outlineContextMenu', /* @ts-ignore */
2858
+ ...(ngDevMode ? [{ debugName: "contextMenu" }] : /* istanbul ignore next */ []));
2859
+ //#endregion
2860
+ //#region ----- State Signals -----
2268
2861
  this.collapsedStates = new Map();
2269
2862
  this.root = signal(this.getTree(), /* @ts-ignore */
2270
2863
  ...(ngDevMode ? [{ debugName: "root" }] : /* istanbul ignore next */ []));
@@ -2272,6 +2865,8 @@ class AXPDesignerOutlineComponent {
2272
2865
  this.root.update((c) => ({ ...this.getTree() }));
2273
2866
  });
2274
2867
  }
2868
+ //#endregion
2869
+ //#region ----- Tree -----
2275
2870
  getTree() {
2276
2871
  return {
2277
2872
  type: AXPWidgetsCatalog.document,
@@ -2279,64 +2874,209 @@ class AXPDesignerOutlineComponent {
2279
2874
  };
2280
2875
  }
2281
2876
  toggleCollapse(node) {
2282
- // Toggle the current state or set to true if undefined
2283
2877
  this.collapsedStates.set(node, !this.collapsedStates.get(node));
2284
2878
  }
2285
2879
  isCollapsed(node) {
2286
- // Return the collapsed state, default is false (expanded)
2287
2880
  return !!this.collapsedStates.get(node);
2288
2881
  }
2289
2882
  getConfig(node) {
2290
2883
  return get(node, '__meta__.config');
2291
2884
  }
2885
+ /** Solid, type-colored icon so outline rows stay scannable on a dark drawer. */
2886
+ getOutlineIconClass(config) {
2887
+ const icon = (config?.icon ?? 'fa-solid fa-cube')
2888
+ .replace(/\bfa-light\b/g, 'fa-solid')
2889
+ .replace(/\bfa-regular\b/g, 'fa-solid');
2890
+ return `${icon} ${this.getOutlineIconColor(config)}`;
2891
+ }
2892
+ getOutlineIconColor(config) {
2893
+ switch (config?.type) {
2894
+ case 'container':
2895
+ return 'text-primary';
2896
+ case 'editor':
2897
+ return 'text-success';
2898
+ case 'action':
2899
+ return 'text-warning';
2900
+ case 'view':
2901
+ return 'text-secondary';
2902
+ case 'filter':
2903
+ return 'text-primary';
2904
+ case 'validation':
2905
+ return 'text-danger';
2906
+ case 'dashboard':
2907
+ return 'text-warning';
2908
+ default:
2909
+ return 'text-primary';
2910
+ }
2911
+ }
2912
+ /** Caption, text-block content, or the widget type title. Type-slug names stay off this string. */
2913
+ getOutlineTitle(node) {
2914
+ return this.readOutlineCaption(node) ?? this.getConfig(node)?.title ?? node.type ?? '';
2915
+ }
2916
+ /** Plain captions render as-is. Type titles and multi-language captions go through translate. */
2917
+ usesOutlineTranslation(title) {
2918
+ return typeof title !== 'string' || title.startsWith('@');
2919
+ }
2920
+ outlinePlainTitle(title) {
2921
+ return typeof title === 'string' ? title : '';
2922
+ }
2923
+ /** Field names such as `txtlocation`. Generated slugs (`text-block-layout2`, `page`) stay off the row. */
2924
+ getOutlineNameSuffix(node) {
2925
+ const name = typeof node.name === 'string' ? node.name.trim() : '';
2926
+ if (!name || this.isGeneratedWidgetName(node.type, name)) {
2927
+ return '';
2928
+ }
2929
+ return ` : ${name}`;
2930
+ }
2931
+ /** Full row text, plus the generated name when the visible label hides it. */
2932
+ outlineTooltip(node, title, suffix) {
2933
+ const visible = `${title ?? ''}${suffix}`.trim();
2934
+ const name = typeof node.name === 'string' ? node.name.trim() : '';
2935
+ if (name && !visible.includes(name)) {
2936
+ return visible ? `${visible} (${name})` : name;
2937
+ }
2938
+ return visible;
2939
+ }
2940
+ readOutlineCaption(node) {
2941
+ const options = node.options;
2942
+ if (!options || typeof options !== 'object' || Array.isArray(options)) {
2943
+ return undefined;
2944
+ }
2945
+ const record = options;
2946
+ const caption = record['label'] ?? record['title'] ?? record['caption'] ?? record['text'];
2947
+ if (typeof caption === 'string' && caption.trim()) {
2948
+ return caption.trim();
2949
+ }
2950
+ if (caption && typeof caption === 'object' && !Array.isArray(caption)) {
2951
+ return caption;
2952
+ }
2953
+ if (node.type !== 'text-block-layout' || typeof record['content'] !== 'string') {
2954
+ return undefined;
2955
+ }
2956
+ const plain = this.stripOutlineHtml(record['content']).replace(/\s+/g, ' ').trim();
2957
+ return plain || undefined;
2958
+ }
2959
+ isGeneratedWidgetName(type, name) {
2960
+ const widgetType = (type ?? '').trim();
2961
+ if (this.matchesTypeSlug(name, widgetType)) {
2962
+ return true;
2963
+ }
2964
+ const shortType = widgetType.replace(/-layout$/, '');
2965
+ return shortType !== widgetType && this.matchesTypeSlug(name, shortType);
2966
+ }
2967
+ matchesTypeSlug(name, base) {
2968
+ if (!base) {
2969
+ return false;
2970
+ }
2971
+ if (name === base) {
2972
+ return true;
2973
+ }
2974
+ if (!name.startsWith(base)) {
2975
+ return false;
2976
+ }
2977
+ return /^[0-9]+$/.test(name.slice(base.length));
2978
+ }
2979
+ stripOutlineHtml(value) {
2980
+ return value
2981
+ .replace(/<br\s*\/?>/gi, ' ')
2982
+ .replace(/<[^>]+>/g, ' ')
2983
+ .replace(/&nbsp;/gi, ' ')
2984
+ .replace(/&amp;/gi, '&')
2985
+ .replace(/&lt;/gi, '<')
2986
+ .replace(/&gt;/gi, '>');
2987
+ }
2988
+ //#endregion
2989
+ //#region ----- Event Handlers -----
2292
2990
  handleClick(e, node) {
2293
2991
  e.stopPropagation();
2294
2992
  this.service.select({ widget: node });
2295
2993
  }
2296
2994
  handleToggleClick(event, item) {
2297
- event.stopPropagation(); // Prevents the event from bubbling up to parent elements
2995
+ event.stopPropagation();
2298
2996
  this.toggleCollapse(item);
2299
2997
  }
2300
2998
  handleMouseOver(e, node) {
2301
2999
  e.stopPropagation();
2302
3000
  this.service.focus({ widget: node });
2303
3001
  }
3002
+ handleContextMenu(event, node) {
3003
+ event.preventDefault();
3004
+ event.stopPropagation();
3005
+ this.service.select({ widget: node });
3006
+ const menu = this.contextMenu();
3007
+ if (!menu) {
3008
+ return;
3009
+ }
3010
+ const instance = this.service.getWidgetInstance(node);
3011
+ const items = this.service.getContextMenuItemsForNode(node, true);
3012
+ this.bindMenuData(items, instance, node);
3013
+ const targetElement = event.currentTarget instanceof HTMLElement ? event.currentTarget : undefined;
3014
+ menu.showAt({ x: event.clientX, y: event.clientY }, targetElement, {
3015
+ sender: menu,
3016
+ canceled: false,
3017
+ targetElement,
3018
+ items,
3019
+ });
3020
+ }
3021
+ async handleContextMenuItemClick(e) {
3022
+ if (e.item.name) {
3023
+ const instance = get(e.item.data, 'instance');
3024
+ const node = get(e.item.data, 'node');
3025
+ await this.service.executeCommand({ instance, node, name: e.item.name });
3026
+ }
3027
+ }
3028
+ //#endregion
3029
+ //#region ----- Context Menu -----
3030
+ bindMenuData(items, instance, node) {
3031
+ items.forEach((item) => {
3032
+ item.data = { ...(item.data ?? {}), instance, node };
3033
+ if (item.items) {
3034
+ this.bindMenuData(item.items, instance, node);
3035
+ }
3036
+ });
3037
+ }
2304
3038
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerOutlineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2305
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerOutlineComponent, isStandalone: true, selector: "axp-designer-outline", ngImport: i0, template: "<axp-designer-drawer-content>\n <ax-title>Outline</ax-title>\n\n <ax-content body>\n @if (root().children?.length) {\n <ul class=\"space-y-1 p-1 text-white text-sm\">\n @for (node of root().children; track $index) {\n <ng-container [ngTemplateOutlet]=\"sideMenu\" [ngTemplateOutletContext]=\"{ $implicit: node }\"> </ng-container>\n }\n </ul>\n <ng-template #sideMenu let-item>\n <li (click)=\"handleClick($event, item)\" (mouseover)=\"handleMouseOver($event, item)\">\n <div\n [class.bg-neutral-700]=\"item === service.selectedNode()\"\n class=\"flex flex-row items-center cursor-pointer transition duration-150 ease-in-out hover:bg-gray-700 py-1 px-2 rounded-sm group\"\n >\n <span class=\"w-5\">\n @if (item.children?.length) {\n <i\n class=\"fa-solid\"\n [class.fa-chevron-right]=\"isCollapsed(item)\"\n (click)=\"handleToggleClick($event, item)\"\n [class.fa-chevron-down]=\"!isCollapsed(item)\"\n ></i>\n }\n </span>\n @let config = getConfig(item);\n <span class=\"w-5\">\n @if (config.icon) {\n <i [ngClass]=\"config.icon\"></i>\n }\n </span>\n <span class=\"truncate flex-1 pe-2\">{{ config.title }} : {{ item.name }}</span>\n <div class=\"invisible group-hover:visible flex gap-2 justify-end\">\n @if (config.type == 'container') {\n <i class=\"fa-light fa-plus hover:text-primary-500\" (click)=\"service.showPicker(item)\"></i>\n }\n @if (config.name != 'page-layout') {\n <i class=\"fa-light fa-xmark hover:text-danger-500\" (click)=\"service.removeWidget(item)\"></i>\n }\n </div>\n </div>\n @if (item.children?.length && !isCollapsed(item)) {\n <ul class=\"space-y-1 ps-3 text-white text-sm\">\n @for (child of item.children; track child) {\n <ng-container [ngTemplateOutlet]=\"sideMenu\" [ngTemplateOutletContext]=\"{ $implicit: child }\">\n </ng-container>\n }\n </ul>\n }\n </li>\n </ng-template>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-shapes\"\n [title]=\"'@layout-designer:components.outline.empty-states.no-widgets.title'\"\n [description]=\"'@layout-designer:components.outline.empty-states.no-widgets.description'\"\n >\n </axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: AXPLayoutDesignerDrawerContentComponent, selector: "axp-designer-drawer-content" }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3039
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerOutlineComponent, isStandalone: true, selector: "axp-designer-outline", viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["outlineContextMenu"], descendants: true, isSignal: true }], ngImport: i0, template: "<axp-designer-drawer-content>\n <ax-title>{{ '@layout-designer:components.outline.title' | translate | async }}</ax-title>\n\n <ax-content body>\n @if (root().children?.length) {\n <!-- ----- Outline tree of the current page ----- -->\n <ul class=\"space-y-1 p-1 text-sm\">\n @for (node of root().children; track $index) {\n <ng-container [ngTemplateOutlet]=\"sideMenu\" [ngTemplateOutletContext]=\"{ $implicit: node }\"></ng-container>\n }\n </ul>\n <ng-template #sideMenu let-item>\n <li (click)=\"handleClick($event, item)\" (mouseover)=\"handleMouseOver($event, item)\">\n <div [class.darker-surface]=\"item === service.selectedNode()\" class=\"flex flex-row items-center gap-1 cursor-pointer transition duration-150 ease-in-out hover:dark-surface py-1 px-2 rounded-sm\" (contextmenu)=\"handleContextMenu($event, item)\">\n <span class=\"w-3 shrink-0 flex items-center justify-center\">\n <!--->\n <!-- ----- Collapse / expand when this node has children ----- -->\n @if (item.children?.length) {\n <i class=\"fa-solid fa-xs opacity-60\" [class.fa-chevron-right]=\"isCollapsed(item)\" (click)=\"handleToggleClick($event, item)\" [class.fa-chevron-down]=\"!isCollapsed(item)\"></i>\n }\n </span>\n @let config = getConfig(item);\n <span class=\"w-4 shrink-0 flex items-center justify-center\">\n <!--->\n <!-- ----- Widget icon from config, colored by widget type ----- -->\n @if (config?.icon) {\n <i [ngClass]=\"getOutlineIconClass(config)\"></i>\n }\n </span>\n @let title = getOutlineTitle(item);\n @let suffix = getOutlineNameSuffix(item);\n <!--------------------------------------------------------------->\n <!-- ----- Translate type titles; plain captions and text-block content stay as stored ----- -->\n @if (usesOutlineTranslation(title)) {\n @let translated = (title | translate | async) ?? '';\n <span class=\"truncate flex-1 pe-2\" [attr.title]=\"outlineTooltip(item, translated, suffix)\">{{ translated }}{{ suffix }}</span>\n }\n <!-- ----- Plain caption or stripped text-block content ----- -->\n @else {\n <span class=\"truncate flex-1 pe-2\" [attr.title]=\"outlineTooltip(item, outlinePlainTitle(title), suffix)\">{{ outlinePlainTitle(title) }}{{ suffix }}</span>\n }\n </div>\n <!--->\n <!-- ----- Nested outline children ----- -->\n @if (item.children?.length && !isCollapsed(item)) {\n <ul class=\"space-y-1 ps-3 text-sm\">\n @for (child of item.children; track child) {\n <ng-container [ngTemplateOutlet]=\"sideMenu\" [ngTemplateOutletContext]=\"{ $implicit: child }\"></ng-container>\n }\n </ul>\n }\n </li>\n </ng-template>\n }\n <!-- ----- Empty outline when the page has no widgets ----- -->\n @else {\n <axp-state-message icon=\"fa-light fa-shapes\" [title]=\"'@layout-designer:components.outline.empty-states.no-widgets.title'\" [description]=\"'@layout-designer:components.outline.empty-states.no-widgets.description'\"></axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n\n<!-- Opened from each outline row; same commands as the canvas widget menu. -->\n<ax-context-menu class=\"ax-dark\" #outlineContextMenu [orientation]=\"'vertical'\" [closeOn]=\"'leave'\" (onItemClick)=\"handleContextMenuItemClick($event)\"></ax-context-menu>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXMenuModule }, { kind: "component", type: i2$2.AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "closeOnRouteChange", "items", "target"], outputs: ["onItemClick", "onOpening", "onClose"] }, { kind: "component", type: AXPLayoutDesignerDrawerContentComponent, selector: "axp-designer-drawer-content" }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2306
3040
  }
2307
3041
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerOutlineComponent, decorators: [{
2308
3042
  type: Component,
2309
3043
  args: [{ selector: 'axp-designer-outline', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
2310
3044
  CommonModule,
2311
3045
  AXDecoratorModule,
3046
+ AXTranslationModule,
3047
+ AXMenuModule,
2312
3048
  AXPLayoutDesignerDrawerContentComponent,
2313
- AXPStateMessageComponent
2314
- ], template: "<axp-designer-drawer-content>\n <ax-title>Outline</ax-title>\n\n <ax-content body>\n @if (root().children?.length) {\n <ul class=\"space-y-1 p-1 text-white text-sm\">\n @for (node of root().children; track $index) {\n <ng-container [ngTemplateOutlet]=\"sideMenu\" [ngTemplateOutletContext]=\"{ $implicit: node }\"> </ng-container>\n }\n </ul>\n <ng-template #sideMenu let-item>\n <li (click)=\"handleClick($event, item)\" (mouseover)=\"handleMouseOver($event, item)\">\n <div\n [class.bg-neutral-700]=\"item === service.selectedNode()\"\n class=\"flex flex-row items-center cursor-pointer transition duration-150 ease-in-out hover:bg-gray-700 py-1 px-2 rounded-sm group\"\n >\n <span class=\"w-5\">\n @if (item.children?.length) {\n <i\n class=\"fa-solid\"\n [class.fa-chevron-right]=\"isCollapsed(item)\"\n (click)=\"handleToggleClick($event, item)\"\n [class.fa-chevron-down]=\"!isCollapsed(item)\"\n ></i>\n }\n </span>\n @let config = getConfig(item);\n <span class=\"w-5\">\n @if (config.icon) {\n <i [ngClass]=\"config.icon\"></i>\n }\n </span>\n <span class=\"truncate flex-1 pe-2\">{{ config.title }} : {{ item.name }}</span>\n <div class=\"invisible group-hover:visible flex gap-2 justify-end\">\n @if (config.type == 'container') {\n <i class=\"fa-light fa-plus hover:text-primary-500\" (click)=\"service.showPicker(item)\"></i>\n }\n @if (config.name != 'page-layout') {\n <i class=\"fa-light fa-xmark hover:text-danger-500\" (click)=\"service.removeWidget(item)\"></i>\n }\n </div>\n </div>\n @if (item.children?.length && !isCollapsed(item)) {\n <ul class=\"space-y-1 ps-3 text-white text-sm\">\n @for (child of item.children; track child) {\n <ng-container [ngTemplateOutlet]=\"sideMenu\" [ngTemplateOutletContext]=\"{ $implicit: child }\">\n </ng-container>\n }\n </ul>\n }\n </li>\n </ng-template>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-shapes\"\n [title]=\"'@layout-designer:components.outline.empty-states.no-widgets.title'\"\n [description]=\"'@layout-designer:components.outline.empty-states.no-widgets.description'\"\n >\n </axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n" }]
2315
- }], ctorParameters: () => [] });
3049
+ AXPStateMessageComponent,
3050
+ ], template: "<axp-designer-drawer-content>\n <ax-title>{{ '@layout-designer:components.outline.title' | translate | async }}</ax-title>\n\n <ax-content body>\n @if (root().children?.length) {\n <!-- ----- Outline tree of the current page ----- -->\n <ul class=\"space-y-1 p-1 text-sm\">\n @for (node of root().children; track $index) {\n <ng-container [ngTemplateOutlet]=\"sideMenu\" [ngTemplateOutletContext]=\"{ $implicit: node }\"></ng-container>\n }\n </ul>\n <ng-template #sideMenu let-item>\n <li (click)=\"handleClick($event, item)\" (mouseover)=\"handleMouseOver($event, item)\">\n <div [class.darker-surface]=\"item === service.selectedNode()\" class=\"flex flex-row items-center gap-1 cursor-pointer transition duration-150 ease-in-out hover:dark-surface py-1 px-2 rounded-sm\" (contextmenu)=\"handleContextMenu($event, item)\">\n <span class=\"w-3 shrink-0 flex items-center justify-center\">\n <!--->\n <!-- ----- Collapse / expand when this node has children ----- -->\n @if (item.children?.length) {\n <i class=\"fa-solid fa-xs opacity-60\" [class.fa-chevron-right]=\"isCollapsed(item)\" (click)=\"handleToggleClick($event, item)\" [class.fa-chevron-down]=\"!isCollapsed(item)\"></i>\n }\n </span>\n @let config = getConfig(item);\n <span class=\"w-4 shrink-0 flex items-center justify-center\">\n <!--->\n <!-- ----- Widget icon from config, colored by widget type ----- -->\n @if (config?.icon) {\n <i [ngClass]=\"getOutlineIconClass(config)\"></i>\n }\n </span>\n @let title = getOutlineTitle(item);\n @let suffix = getOutlineNameSuffix(item);\n <!--------------------------------------------------------------->\n <!-- ----- Translate type titles; plain captions and text-block content stay as stored ----- -->\n @if (usesOutlineTranslation(title)) {\n @let translated = (title | translate | async) ?? '';\n <span class=\"truncate flex-1 pe-2\" [attr.title]=\"outlineTooltip(item, translated, suffix)\">{{ translated }}{{ suffix }}</span>\n }\n <!-- ----- Plain caption or stripped text-block content ----- -->\n @else {\n <span class=\"truncate flex-1 pe-2\" [attr.title]=\"outlineTooltip(item, outlinePlainTitle(title), suffix)\">{{ outlinePlainTitle(title) }}{{ suffix }}</span>\n }\n </div>\n <!--->\n <!-- ----- Nested outline children ----- -->\n @if (item.children?.length && !isCollapsed(item)) {\n <ul class=\"space-y-1 ps-3 text-sm\">\n @for (child of item.children; track child) {\n <ng-container [ngTemplateOutlet]=\"sideMenu\" [ngTemplateOutletContext]=\"{ $implicit: child }\"></ng-container>\n }\n </ul>\n }\n </li>\n </ng-template>\n }\n <!-- ----- Empty outline when the page has no widgets ----- -->\n @else {\n <axp-state-message icon=\"fa-light fa-shapes\" [title]=\"'@layout-designer:components.outline.empty-states.no-widgets.title'\" [description]=\"'@layout-designer:components.outline.empty-states.no-widgets.description'\"></axp-state-message>\n }\n </ax-content>\n</axp-designer-drawer-content>\n\n<!-- Opened from each outline row; same commands as the canvas widget menu. -->\n<ax-context-menu class=\"ax-dark\" #outlineContextMenu [orientation]=\"'vertical'\" [closeOn]=\"'leave'\" (onItemClick)=\"handleContextMenuItemClick($event)\"></ax-context-menu>\n" }]
3051
+ }], ctorParameters: () => [], propDecorators: { contextMenu: [{ type: i0.ViewChild, args: ['outlineContextMenu', { isSignal: true }] }] } });
2316
3052
 
2317
3053
  class AXPDesignerPagesComponent {
2318
3054
  constructor() {
2319
3055
  this.service = inject(AXPDesignerService);
2320
3056
  }
2321
3057
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerPagesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2322
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerPagesComponent, isStandalone: true, selector: "axp-designer-pages", ngImport: i0, template: "<axp-designer-drawer-content class=\"text-on-lightest\">\n <ax-title>Pages</ax-title>\n <ax-content header>\n <ax-button look=\"blank\" class=\"ax-sm\" (onClick)=\"service.addNewPage()\" title=\"Add New Page\">\n <ax-icon class=\"fa-light fa-file-circle-plus\"></ax-icon>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @let count = service.document().children?.length ?? 0; @if(count) {\n <ul class=\"text-sm p-2 rounded ax-md space-y-1\">\n @for( page of service.document().children;track $index) {\n <li (click)=\"service.goToPage($index)\"\n class=\"cursor-pointer transition duration-150 ease-in-out hover:bg-dark py-1 px-2 rounded-sm group flex justify-between items-center\"\n [class.bg-darkest]=\"$index == service.currentPageIndex()\">\n <div>\n <i class=\"fa-light fa-file me-1\"></i>\n Page {{ $index + 1 }}\n </div>\n <div class=\"invisible group-hover:visible flex gap-2\">\n @if(count>1) {\n <i class=\"fa-light fa-xmark hover:text-danger-500\" (click)=\"service.removePage($index)\"></i>\n }\n <i class=\"fa-light fa-clone hover:text-primary-500\" (click)=\"service.clonePage($index)\"></i>\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"flex justify-center items-center text-gray-300 p-4\">No pages found in the document</div>\n }\n </ax-content>\n</axp-designer-drawer-content>\n", dependencies: [{ kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXPLayoutDesignerDrawerContentComponent, selector: "axp-designer-drawer-content" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3058
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerPagesComponent, isStandalone: true, selector: "axp-designer-pages", ngImport: i0, template: "<axp-designer-drawer-content>\n <ax-title>{{ '@layout-designer:components.pages.title' | translate | async }}</ax-title>\n <ax-content header>\n <ax-button look=\"blank\" class=\"ax-sm\" (onClick)=\"service.addNewPage()\" [title]=\"'@layout-designer:components.pages.add' | translate | async\">\n <ax-icon class=\"fa-light fa-file-circle-plus\"></ax-icon>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @let count = service.document().children?.length ?? 0; @if(count) {\n <ul class=\"text-sm p-2 rounded ax-md space-y-1\">\n @for( page of service.document().children;track $index) {\n <li (click)=\"service.goToPage($index)\"\n class=\"cursor-pointer transition duration-150 ease-in-out hover:dark-surface py-1 px-2 rounded-sm group flex justify-between items-center\"\n [class.darker-surface]=\"$index == service.currentPageIndex()\">\n <div>\n <i class=\"fa-light fa-file me-1\"></i>\n {{ '@layout-designer:components.pages.page-n' | translate: { params: { n: $index + 1 } } | async }}\n </div>\n <div class=\"invisible group-hover:visible flex gap-2\">\n @if(count>1) {\n <i class=\"fa-light fa-trash hover:text-danger-500\" (click)=\"service.removePage($index)\"></i>\n }\n <i class=\"fa-light fa-clone hover:text-primary-500\" (click)=\"service.clonePage($index)\"></i>\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"axp-designer-drawer-empty\">{{ '@layout-designer:components.pages.empty' | translate | async }}</div>\n }\n </ax-content>\n</axp-designer-drawer-content>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPLayoutDesignerDrawerContentComponent, selector: "axp-designer-drawer-content" }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2323
3059
  }
2324
3060
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerPagesComponent, decorators: [{
2325
3061
  type: Component,
2326
3062
  args: [{ selector: 'axp-designer-pages', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
3063
+ CommonModule,
2327
3064
  AXPWidgetCoreModule,
2328
3065
  AXDecoratorModule,
2329
3066
  AXButtonModule,
3067
+ AXTranslationModule,
2330
3068
  AXPLayoutDesignerDrawerContentComponent
2331
- ], template: "<axp-designer-drawer-content class=\"text-on-lightest\">\n <ax-title>Pages</ax-title>\n <ax-content header>\n <ax-button look=\"blank\" class=\"ax-sm\" (onClick)=\"service.addNewPage()\" title=\"Add New Page\">\n <ax-icon class=\"fa-light fa-file-circle-plus\"></ax-icon>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @let count = service.document().children?.length ?? 0; @if(count) {\n <ul class=\"text-sm p-2 rounded ax-md space-y-1\">\n @for( page of service.document().children;track $index) {\n <li (click)=\"service.goToPage($index)\"\n class=\"cursor-pointer transition duration-150 ease-in-out hover:bg-dark py-1 px-2 rounded-sm group flex justify-between items-center\"\n [class.bg-darkest]=\"$index == service.currentPageIndex()\">\n <div>\n <i class=\"fa-light fa-file me-1\"></i>\n Page {{ $index + 1 }}\n </div>\n <div class=\"invisible group-hover:visible flex gap-2\">\n @if(count>1) {\n <i class=\"fa-light fa-xmark hover:text-danger-500\" (click)=\"service.removePage($index)\"></i>\n }\n <i class=\"fa-light fa-clone hover:text-primary-500\" (click)=\"service.clonePage($index)\"></i>\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"flex justify-center items-center text-gray-300 p-4\">No pages found in the document</div>\n }\n </ax-content>\n</axp-designer-drawer-content>\n" }]
3069
+ ], template: "<axp-designer-drawer-content>\n <ax-title>{{ '@layout-designer:components.pages.title' | translate | async }}</ax-title>\n <ax-content header>\n <ax-button look=\"blank\" class=\"ax-sm\" (onClick)=\"service.addNewPage()\" [title]=\"'@layout-designer:components.pages.add' | translate | async\">\n <ax-icon class=\"fa-light fa-file-circle-plus\"></ax-icon>\n </ax-button>\n </ax-content>\n\n <ax-content body>\n @let count = service.document().children?.length ?? 0; @if(count) {\n <ul class=\"text-sm p-2 rounded ax-md space-y-1\">\n @for( page of service.document().children;track $index) {\n <li (click)=\"service.goToPage($index)\"\n class=\"cursor-pointer transition duration-150 ease-in-out hover:dark-surface py-1 px-2 rounded-sm group flex justify-between items-center\"\n [class.darker-surface]=\"$index == service.currentPageIndex()\">\n <div>\n <i class=\"fa-light fa-file me-1\"></i>\n {{ '@layout-designer:components.pages.page-n' | translate: { params: { n: $index + 1 } } | async }}\n </div>\n <div class=\"invisible group-hover:visible flex gap-2\">\n @if(count>1) {\n <i class=\"fa-light fa-trash hover:text-danger-500\" (click)=\"service.removePage($index)\"></i>\n }\n <i class=\"fa-light fa-clone hover:text-primary-500\" (click)=\"service.clonePage($index)\"></i>\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"axp-designer-drawer-empty\">{{ '@layout-designer:components.pages.empty' | translate | async }}</div>\n }\n </ax-content>\n</axp-designer-drawer-content>\n" }]
2332
3070
  }] });
2333
3071
 
3072
+ //#region ---- Imports ----
3073
+ //#endregion
2334
3074
  //#region ---- Hook keys ----
2335
3075
  /**
2336
3076
  * Distributed hooks for extending the layout designer header menu without coupling the designer library to feature modules.
2337
3077
  *
2338
3078
  * - {@link AXPLayoutDesignerHeaderMenuHookKeys.root}: top-level bar items (siblings of File, Edit, …).
2339
- * - {@link AXPLayoutDesignerHeaderMenuHookKeys.submenu}: extra items inside a top-level menu (`file`, `edit`, `run`, `help`).
3079
+ * - {@link AXPLayoutDesignerHeaderMenuHookKeys.submenu}: extra items inside a menu (`file`, `import`, `edit`, `run`, `help`).
2340
3080
  */
2341
3081
  const AXPLayoutDesignerHeaderMenuHookKeys = {
2342
3082
  root: 'layout-designer.header-menu.root',
@@ -2365,13 +3105,13 @@ class AXPDesignerHeaderMenuComponent {
2365
3105
  this.registerDesignerKeyboardShortcuts();
2366
3106
  }
2367
3107
  async ngOnInit() {
2368
- const rootPayload = await this.hooks.runAsync(AXPLayoutDesignerHeaderMenuHookKeys.root, { designer: this.service, items: [] });
3108
+ const rootPayload = await this.hooks.runAsync(AXPLayoutDesignerHeaderMenuHookKeys.root, { designer: this.service, owner: this.destroyRef, items: [] });
2369
3109
  rootPayload.items.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
2370
3110
  this.rootMenuContributions.set(await this.resolveMenuLabels(rootPayload.items));
2371
- const parents = ['file', 'edit', 'run', 'help'];
3111
+ const parents = ['file', 'import', 'edit', 'run', 'help'];
2372
3112
  const map = {};
2373
3113
  for (const parentMenuId of parents) {
2374
- const subPayload = await this.hooks.runAsync(AXPLayoutDesignerHeaderMenuHookKeys.submenu, { designer: this.service, parentMenuId, items: [] });
3114
+ const subPayload = await this.hooks.runAsync(AXPLayoutDesignerHeaderMenuHookKeys.submenu, { designer: this.service, owner: this.destroyRef, parentMenuId, items: [] });
2375
3115
  subPayload.items.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
2376
3116
  map[parentMenuId] = await this.resolveMenuLabels(subPayload.items);
2377
3117
  }
@@ -2515,7 +3255,7 @@ class AXPDesignerHeaderMenuComponent {
2515
3255
  when: () => this.service.canRedo(),
2516
3256
  shortcuts: [
2517
3257
  {
2518
- keys: ['ctrl+y'],
3258
+ keys: ['ctrl+y', 'ctrl+shift+z'],
2519
3259
  title: '@layout-designer:keyboard-shortcuts.redo',
2520
3260
  group: '@layout-designer:keyboard-shortcuts.groups.edit',
2521
3261
  handler: (event) => {
@@ -2585,11 +3325,11 @@ class AXPDesignerHeaderMenuComponent {
2585
3325
  return event.target === document.body;
2586
3326
  }
2587
3327
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerHeaderMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2588
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerHeaderMenuComponent, isStandalone: true, selector: "axp-designer-header-menu", ngImport: i0, template: "<ax-menu [openOn]=\"'hover'\" [hasArrow]=\"false\">\n <ax-menu-item>\n <ax-text>File</ax-text>\n <ax-menu-item (onClick)=\"service.new()\">\n <ax-text>New</ax-text>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('shift+n')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n @if (service.canSave()) {\n <ng-container>\n <ax-menu-item (onClick)=\"service.save()\">\n <ax-text>Save</ax-text>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+s')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item (onClick)=\"service.saveAs()\">\n <ax-text>Save As...</ax-text>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+shift+s')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n </ng-container>\n }\n <ax-menu-item>\n <ax-text>Export</ax-text>\n <ax-menu-item (onClick)=\"service.downloadJson()\">\n <ax-text>Download JSON</ax-text>\n </ax-menu-item>\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Import</ax-text>\n <ax-menu-item (onClick)=\"service.importJSon()\">\n <ax-text>Import JSON</ax-text>\n </ax-menu-item>\n </ax-menu-item>\n @for (item of submenuFor('file'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Edit</ax-text>\n <ax-menu-item [disabled]=\"!service.canUndo()\" (onClick)=\"service.undo()\">\n <ax-text>Undo</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-rotate-left\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+z')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canRedo()\" (onClick)=\"service.redo()\">\n <ax-text>Redo</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-rotate-right\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+y')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n <ax-menu-item\n [disabled]=\"!service.canCutCopy()\"\n (onClick)=\"service.clone()\"\n >\n <ax-text>Clone</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-clone\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+d')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canCutCopy()\" (onClick)=\"service.cut()\">\n <ax-text>Cut</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-cut\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+x')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canCutCopy()\" (onClick)=\"service.copy()\">\n <ax-text>Copy</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-copy\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+c')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canPaste()\" (onClick)=\"service.paste()\">\n <ax-text>Paste</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-paste\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+v')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n <ax-menu-item\n [disabled]=\"!service.canDelete()\"\n (onClick)=\"service.removeWidget()\"\n >\n <ax-text>Delete</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-trash\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('delete')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n @for (item of submenuFor('edit'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Run</ax-text>\n <ax-menu-item (onClick)=\"service.openPreviewDialog()\">\n <ax-text>Preview</ax-text>\n <ax-prefix> </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+r')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item (onClick)=\"service.openPreviewWindow()\">\n <ax-text>Preview in seperate window</ax-text>\n <ax-prefix> </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+shift+r')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n @for (item of submenuFor('run'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Help</ax-text>\n <ax-menu-item>\n <ax-text>About</ax-text>\n </ax-menu-item>\n @for (item of submenuFor('help'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n @for (item of rootMenuContributions(); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n</ax-menu>\n", dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: AXKBDComponent, selector: "ax-kbd", inputs: ["look"] }, { kind: "component", type: AXKBDItemComponent, selector: "ax-kbd-item", inputs: ["keys", "join", "look"], outputs: ["lookChange"] }, { kind: "ngmodule", type: AXMenuModule }, { kind: "component", type: i2$2.AXMenuItemComponent, selector: "ax-menu-item", inputs: ["subItems", "name", "data", "disabled", "color"], outputs: ["onClick"] }, { kind: "component", type: i2$2.AXMenuComponent, selector: "ax-menu", inputs: ["orientation", "openOn", "closeOn", "items", "hasArrow"], outputs: ["onItemClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3328
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPDesignerHeaderMenuComponent, isStandalone: true, selector: "axp-designer-header-menu", ngImport: i0, template: "<ax-menu [openOn]=\"'hover'\" [hasArrow]=\"false\">\n <ax-menu-item>\n <ax-text>File</ax-text>\n <ax-menu-item (onClick)=\"service.new()\">\n <ax-text>New</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-file\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('shift+n')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n @if (service.canSave()) {\n <ng-container>\n <ax-menu-item (onClick)=\"service.save()\">\n <ax-text>Save</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-floppy-disk\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+s')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item (onClick)=\"service.saveAs()\">\n <ax-text>Save As...</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-file-pen\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+shift+s')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n </ng-container>\n }\n <ax-menu-item>\n <ax-text>Export</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-file-export\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-menu-item (onClick)=\"service.downloadJson()\">\n <ax-text>Download JSON</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-download\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-menu-item>\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Import</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-file-import\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-menu-item (onClick)=\"service.importJSon()\">\n <ax-text>Import JSON</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-upload\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-menu-item>\n @for (item of submenuFor('import'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n @for (item of submenuFor('file'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Edit</ax-text>\n <ax-menu-item [disabled]=\"!service.canUndo()\" (onClick)=\"service.undo()\">\n <ax-text>Undo</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-undo\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+z')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canRedo()\" (onClick)=\"service.redo()\">\n <ax-text>Redo</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-rotate-right\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+y')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n <ax-menu-item\n [disabled]=\"!service.canCutCopy()\"\n (onClick)=\"service.clone()\"\n >\n <ax-text>Clone</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-clone\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+d')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canCutCopy()\" (onClick)=\"service.cut()\">\n <ax-text>Cut</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-cut\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+x')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canCutCopy()\" (onClick)=\"service.copy()\">\n <ax-text>Copy</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-copy\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+c')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canPaste()\" (onClick)=\"service.paste()\">\n <ax-text>Paste</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-paste\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+v')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n <ax-menu-item\n [disabled]=\"!service.canDelete()\"\n (onClick)=\"service.removeWidget()\"\n >\n <ax-text>Delete</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-trash\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('delete')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n @for (item of submenuFor('edit'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Run</ax-text>\n <ax-menu-item (onClick)=\"service.openPreviewDialog()\">\n <ax-text>Preview</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-eye\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+r')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item (onClick)=\"service.openPreviewWindow()\">\n <ax-text>Preview in seperate window</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-window-restore\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+shift+r')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n @for (item of submenuFor('run'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Help</ax-text>\n @for (item of submenuFor('help'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n <ax-divider></ax-divider>\n <ax-menu-item>\n <ax-text>About</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-circle-info\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-menu-item>\n </ax-menu-item>\n @for (item of rootMenuContributions(); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n</ax-menu>\n", dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: AXKBDComponent, selector: "ax-kbd", inputs: ["look"] }, { kind: "component", type: AXKBDItemComponent, selector: "ax-kbd-item", inputs: ["keys", "join", "look"], outputs: ["lookChange"] }, { kind: "ngmodule", type: AXMenuModule }, { kind: "component", type: i2$2.AXMenuItemComponent, selector: "ax-menu-item", inputs: ["subItems", "name", "data", "disabled", "color"], outputs: ["onClick"] }, { kind: "component", type: i2$2.AXMenuComponent, selector: "ax-menu", inputs: ["orientation", "openOn", "closeOn", "items", "hasArrow"], outputs: ["onItemClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2589
3329
  }
2590
3330
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPDesignerHeaderMenuComponent, decorators: [{
2591
3331
  type: Component,
2592
- args: [{ imports: [AXDecoratorModule, AXKBDComponent, AXKBDItemComponent, AXMenuModule], selector: 'axp-designer-header-menu', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ax-menu [openOn]=\"'hover'\" [hasArrow]=\"false\">\n <ax-menu-item>\n <ax-text>File</ax-text>\n <ax-menu-item (onClick)=\"service.new()\">\n <ax-text>New</ax-text>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('shift+n')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n @if (service.canSave()) {\n <ng-container>\n <ax-menu-item (onClick)=\"service.save()\">\n <ax-text>Save</ax-text>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+s')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item (onClick)=\"service.saveAs()\">\n <ax-text>Save As...</ax-text>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+shift+s')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n </ng-container>\n }\n <ax-menu-item>\n <ax-text>Export</ax-text>\n <ax-menu-item (onClick)=\"service.downloadJson()\">\n <ax-text>Download JSON</ax-text>\n </ax-menu-item>\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Import</ax-text>\n <ax-menu-item (onClick)=\"service.importJSon()\">\n <ax-text>Import JSON</ax-text>\n </ax-menu-item>\n </ax-menu-item>\n @for (item of submenuFor('file'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Edit</ax-text>\n <ax-menu-item [disabled]=\"!service.canUndo()\" (onClick)=\"service.undo()\">\n <ax-text>Undo</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-rotate-left\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+z')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canRedo()\" (onClick)=\"service.redo()\">\n <ax-text>Redo</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-rotate-right\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+y')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n <ax-menu-item\n [disabled]=\"!service.canCutCopy()\"\n (onClick)=\"service.clone()\"\n >\n <ax-text>Clone</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-clone\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+d')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canCutCopy()\" (onClick)=\"service.cut()\">\n <ax-text>Cut</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-cut\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+x')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canCutCopy()\" (onClick)=\"service.copy()\">\n <ax-text>Copy</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-copy\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+c')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canPaste()\" (onClick)=\"service.paste()\">\n <ax-text>Paste</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-paste\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+v')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n <ax-menu-item\n [disabled]=\"!service.canDelete()\"\n (onClick)=\"service.removeWidget()\"\n >\n <ax-text>Delete</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-trash\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('delete')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n @for (item of submenuFor('edit'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Run</ax-text>\n <ax-menu-item (onClick)=\"service.openPreviewDialog()\">\n <ax-text>Preview</ax-text>\n <ax-prefix> </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+r')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item (onClick)=\"service.openPreviewWindow()\">\n <ax-text>Preview in seperate window</ax-text>\n <ax-prefix> </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+shift+r')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n @for (item of submenuFor('run'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Help</ax-text>\n <ax-menu-item>\n <ax-text>About</ax-text>\n </ax-menu-item>\n @for (item of submenuFor('help'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n @for (item of rootMenuContributions(); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n</ax-menu>\n" }]
3332
+ args: [{ imports: [AXDecoratorModule, AXKBDComponent, AXKBDItemComponent, AXMenuModule], selector: 'axp-designer-header-menu', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ax-menu [openOn]=\"'hover'\" [hasArrow]=\"false\">\n <ax-menu-item>\n <ax-text>File</ax-text>\n <ax-menu-item (onClick)=\"service.new()\">\n <ax-text>New</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-file\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('shift+n')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n @if (service.canSave()) {\n <ng-container>\n <ax-menu-item (onClick)=\"service.save()\">\n <ax-text>Save</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-floppy-disk\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+s')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item (onClick)=\"service.saveAs()\">\n <ax-text>Save As...</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-file-pen\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+shift+s')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n </ng-container>\n }\n <ax-menu-item>\n <ax-text>Export</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-file-export\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-menu-item (onClick)=\"service.downloadJson()\">\n <ax-text>Download JSON</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-download\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-menu-item>\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Import</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-file-import\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-menu-item (onClick)=\"service.importJSon()\">\n <ax-text>Import JSON</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-upload\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-menu-item>\n @for (item of submenuFor('import'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n @for (item of submenuFor('file'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Edit</ax-text>\n <ax-menu-item [disabled]=\"!service.canUndo()\" (onClick)=\"service.undo()\">\n <ax-text>Undo</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-undo\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+z')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canRedo()\" (onClick)=\"service.redo()\">\n <ax-text>Redo</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-rotate-right\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+y')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n <ax-menu-item\n [disabled]=\"!service.canCutCopy()\"\n (onClick)=\"service.clone()\"\n >\n <ax-text>Clone</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-clone\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+d')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canCutCopy()\" (onClick)=\"service.cut()\">\n <ax-text>Cut</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-cut\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+x')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canCutCopy()\" (onClick)=\"service.copy()\">\n <ax-text>Copy</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-copy\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+c')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item [disabled]=\"!service.canPaste()\" (onClick)=\"service.paste()\">\n <ax-text>Paste</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-paste\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+v')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-divider></ax-divider>\n <ax-menu-item\n [disabled]=\"!service.canDelete()\"\n (onClick)=\"service.removeWidget()\"\n >\n <ax-text>Delete</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-trash\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('delete')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n @for (item of submenuFor('edit'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Run</ax-text>\n <ax-menu-item (onClick)=\"service.openPreviewDialog()\">\n <ax-text>Preview</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-eye\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+r')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n <ax-menu-item (onClick)=\"service.openPreviewWindow()\">\n <ax-text>Preview in seperate window</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-window-restore\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-suffix>\n <ax-kbd look=\"blank\">\n <ax-kbd-item [keys]=\"shortcutKeys('ctrl+shift+r')\"></ax-kbd-item>\n </ax-kbd>\n </ax-suffix>\n </ax-menu-item>\n @for (item of submenuFor('run'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n </ax-menu-item>\n <ax-menu-item>\n <ax-text>Help</ax-text>\n @for (item of submenuFor('help'); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n <ax-divider></ax-divider>\n <ax-menu-item>\n <ax-text>About</ax-text>\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-circle-info\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-menu-item>\n </ax-menu-item>\n @for (item of rootMenuContributions(); track item.id) {\n <ax-menu-item (onClick)=\"runMenuEntry(item)\">\n @if (item.iconClass) {\n <ax-prefix>\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n </ax-prefix>\n }\n @if (item.resolvedLabel) {\n <ax-text>{{ item.resolvedLabel }}</ax-text>\n }\n </ax-menu-item>\n }\n</ax-menu>\n" }]
2593
3333
  }], ctorParameters: () => [] });
2594
3334
 
2595
3335
  class AXPDesignerViewSizeToolbarComponent {
@@ -2634,31 +3374,115 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2634
3374
  }]
2635
3375
  }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }, { type: i0.Output, args: ["sizeChange"] }] } });
2636
3376
 
3377
+ //#region ----- Imports -----
3378
+ //#endregion
3379
+ //#region ----- Hook keys -----
3380
+ /**
3381
+ * Distributed hooks for extending the layout designer footer without coupling the designer library to feature modules.
3382
+ *
3383
+ * - {@link AXPLayoutDesignerFooterHookKeys.end}: icon actions at the trailing end of the footer (after breadcrumbs).
3384
+ */
3385
+ const AXPLayoutDesignerFooterHookKeys = {
3386
+ end: 'layout-designer.footer.end',
3387
+ };
3388
+ //#endregion
3389
+
3390
+ //#region ----- Imports -----
3391
+ //#endregion
2637
3392
  class AXPLayoutDesignerComponent {
3393
+ //#endregion
3394
+ //#region ----- Lifecycle -----
2638
3395
  constructor() {
3396
+ //#region ----- Services & Dependencies -----
2639
3397
  this.service = inject(AXPDesignerService);
3398
+ this.hooks = inject(AXPHookService);
3399
+ this.translationService = inject(AXTranslationService);
3400
+ this.destroyRef = inject(DestroyRef);
3401
+ //#endregion
3402
+ //#region ----- State Signals -----
3403
+ this.footerEndActions = signal([], /* @ts-ignore */
3404
+ ...(ngDevMode ? [{ debugName: "footerEndActions" }] : /* istanbul ignore next */ []));
3405
+ //#endregion
3406
+ //#region ----- Inputs -----
2640
3407
  this.id = input(/* @ts-ignore */
2641
3408
  ...(ngDevMode ? [undefined, { debugName: "id" }] : /* istanbul ignore next */ []));
2642
3409
  this.strategy = input(/* @ts-ignore */
2643
3410
  ...(ngDevMode ? [undefined, { debugName: "strategy" }] : /* istanbul ignore next */ []));
3411
+ /** Host surface policy. Form Builder may omit this; the form profile is the default. */
3412
+ this.profile = input(AXP_FORM_DESIGNER_SURFACE_PROFILE, /* @ts-ignore */
3413
+ ...(ngDevMode ? [{ debugName: "profile" }] : /* istanbul ignore next */ []));
3414
+ //#region ----- Computed Properties -----
3415
+ this.layoutProfileContext = computed(() => ({
3416
+ size: designerPreviewSizeToLayoutSize(this.service.size()),
3417
+ medium: 'screen',
3418
+ }), /* @ts-ignore */
3419
+ ...(ngDevMode ? [{ debugName: "layoutProfileContext" }] : /* istanbul ignore next */ []));
2644
3420
  this.nodeConfig = computed(() => this.service.selectedNode() ? get(this.service.selectedNode(), '__meta__.config') : null, /* @ts-ignore */
2645
3421
  ...(ngDevMode ? [{ debugName: "nodeConfig" }] : /* istanbul ignore next */ []));
3422
+ this.hasPagesDrawer = computed(() => this.hasDrawer('pages'), /* @ts-ignore */
3423
+ ...(ngDevMode ? [{ debugName: "hasPagesDrawer" }] : /* istanbul ignore next */ []));
3424
+ this.hasDataSourcesDrawer = computed(() => this.hasDrawer('dataSources'), /* @ts-ignore */
3425
+ ...(ngDevMode ? [{ debugName: "hasDataSourcesDrawer" }] : /* istanbul ignore next */ []));
3426
+ this.hasOutlineDrawer = computed(() => this.hasDrawer('outline'), /* @ts-ignore */
3427
+ ...(ngDevMode ? [{ debugName: "hasOutlineDrawer" }] : /* istanbul ignore next */ []));
3428
+ this.hasHistoryDrawer = computed(() => this.hasDrawer('history'), /* @ts-ignore */
3429
+ ...(ngDevMode ? [{ debugName: "hasHistoryDrawer" }] : /* istanbul ignore next */ []));
3430
+ this.hasPaletteDrawer = computed(() => this.hasDrawer('palette'), /* @ts-ignore */
3431
+ ...(ngDevMode ? [{ debugName: "hasPaletteDrawer" }] : /* istanbul ignore next */ []));
3432
+ //#endregion
3433
+ this.designerDirection = 'ltr';
2646
3434
  this.service.onUpdate.subscribe((e) => {
2647
3435
  this.propertyViewer.update(e.values);
2648
3436
  });
3437
+ effect(() => {
3438
+ this.service.surfaceProfile.set(this.profile());
3439
+ });
2649
3440
  }
2650
3441
  ngOnInit() {
2651
3442
  if (this.id()) {
2652
3443
  this.service.loadById(this.id(), { strategy: (this.strategy() ?? AXPTemplateLoadingStrategy.Connector) });
2653
3444
  }
3445
+ void this.loadFooterEndActions();
3446
+ }
3447
+ //#region ----- Chrome -----
3448
+ hasDrawer(drawer) {
3449
+ return designerSurfaceHasDrawer(this.profile(), drawer);
2654
3450
  }
2655
- get __styles() {
2656
- const styles = {};
2657
- styles[`direction`] = 'ltr';
2658
- return styles;
3451
+ runFooterEndAction(entry) {
3452
+ void Promise.resolve(entry.run());
3453
+ }
3454
+ //#endregion
3455
+ //#region ----- Footer hooks -----
3456
+ async loadFooterEndActions() {
3457
+ const payload = await this.hooks.runAsync(AXPLayoutDesignerFooterHookKeys.end, { designer: this.service, owner: this.destroyRef, items: [] });
3458
+ payload.items.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
3459
+ this.footerEndActions.set(await Promise.all(payload.items.map(async (item) => ({
3460
+ ...item,
3461
+ resolvedLabel: item.labelKey
3462
+ ? await this.translationService.translateAsync(item.labelKey)
3463
+ : item.label,
3464
+ }))));
2659
3465
  }
2660
3466
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLayoutDesignerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2661
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: AXPLayoutDesignerComponent, isStandalone: true, selector: "axp-layout-designer", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, strategy: { classPropertyName: "strategy", publicName: "strategy", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style": "this.__styles" } }, providers: [AXPDesignerService], viewQueries: [{ propertyName: "propertyViewer", first: true, predicate: AXPWidgetPropertyViewerComponent, descendants: true, static: true }], ngImport: i0, template: "<ax-drawer-container class=\"h-full w-full flex relative overflow-hidden\">\n <ax-content class=\"contents\">\n <!-- Toolbar Side -->\n <div\n class=\"min-w-10 bg-lightest border-e ax-sm flex flex-col items-center ax-dark\"\n >\n <div class=\"min-w-10 h-10 flex items-center justify-center border-b\">\n <img src=\"assets/images/brand/platform/platform-icon-light.svg\" class=\"w-6\" />\n </div>\n <div class=\"py-1\"></div>\n <div class=\"flex flex-col gap-2\">\n <!-- <ax-button look=\"blank\">\n <ax-icon class=\"fa-solid fa-puzzle text-sm\"> </ax-icon>\n </ax-button> -->\n <ax-button look=\"blank\" (onClick)=\"pages.toggle()\" title=\"Pages\">\n <ax-icon class=\"fa-solid fa-page text-sm\"> </ax-icon>\n </ax-button>\n <ax-button\n look=\"blank\"\n (onClick)=\"datasources.toggle()\"\n title=\"Data Sources\"\n >\n <ax-icon class=\"fa-solid fa-database text-sm\"> </ax-icon>\n </ax-button>\n <ax-button look=\"blank\" (onClick)=\"outline.toggle()\" title=\"Outline\">\n <ax-icon class=\"fa-solid fa-list-tree text-sm\"> </ax-icon>\n </ax-button>\n <ax-button look=\"blank\" (onClick)=\"history.toggle()\" title=\"History\">\n <ax-icon class=\"fa-solid fa-clock-rotate-left text-sm\"> </ax-icon>\n </ax-button>\n <!-- <ax-button look=\"blank\" title=\"Timeline\">\n <ax-icon class=\"fa-solid fa-diagram-sankey text-sm\"> </ax-icon>\n </ax-button> -->\n </div>\n </div>\n <!-- Main Side -->\n <div class=\"col-span-10 flex flex-col flex-1\">\n <!-- Header Toolbar -->\n <div class=\"h-10 bg-lightest border-b flex justify-between px-2 ax-dark\">\n <div class=\"flex flex-1 items-center text-white justify-start\">\n <axp-designer-header-menu></axp-designer-header-menu>\n </div>\n <div\n class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\"\n >\n <axp-designer-view-sizes\n [(size)]=\"service.size\"\n ></axp-designer-view-sizes>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-end\">\n <ax-dropdown-button\n mode=\"split\"\n look=\"solid\"\n text=\"Preview\"\n class=\"ax-sm\"\n (onClick)=\"service.openPreviewDialog()\"\n >\n <ax-button-item-list>\n <ax-button-item\n text=\"Open in new window\"\n (onClick)=\"service.openPreviewWindow()\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-window-restore\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-button>\n </div>\n </div>\n <!-- Board -->\n <ax-drawer-container class=\"w-full flex-1\">\n <!-- Pages -->\n <ax-drawer\n #pages\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark bg-lightest\"\n >\n <ax-content>\n <axp-designer-pages></axp-designer-pages>\n </ax-content>\n </ax-drawer>\n <!-- Datasources -->\n <ax-drawer\n #datasources\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark bg-lightest\"\n >\n <ax-content>\n <axp-designer-data-sources></axp-designer-data-sources>\n </ax-content>\n </ax-drawer>\n <!-- Outline Pnael -->\n <ax-drawer\n #outline\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark bg-lightest\"\n >\n <ax-content>\n <axp-designer-outline></axp-designer-outline>\n </ax-content>\n </ax-drawer>\n <!-- History Pnael -->\n <ax-drawer\n #history\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark bg-lightest\"\n >\n <ax-content>\n <axp-designer-history></axp-designer-history>\n </ax-content>\n </ax-drawer>\n <!-- Content of board -->\n <ax-content class=\"light-surface bg-lighter flex justify-center p-10\">\n <axp-designer-board class=\"rounded-lg bg-lightest shadow-md\">\n </axp-designer-board>\n </ax-content>\n </ax-drawer-container>\n <!-- Footer -->\n <div class=\"h-10 bg-lightest border-t flex items-center ax-dark\">\n <axp-designer-breadcrumbs class=\"border-default p-2 font-normal\">\n </axp-designer-breadcrumbs>\n </div>\n </div>\n </ax-content>\n <!-- Property Side -->\n <ax-drawer\n class=\"ax-dark axp-designer-prop-side\"\n location=\"end\"\n mode=\"push\"\n [collapsed]=\"false\"\n #pd\n >\n <ax-content class=\"flex flex-col h-full overflow-hidden\">\n <div\n class=\"flex flex-row w-full justify-between text-white px-4 border-b h-10\"\n >\n <div class=\"flex flex-row gap-2 items-center\">\n <i [ngClass]=\"$safeNavigationMigration(nodeConfig()?.icon)\"></i>\n <span class=\"font-semibold\">{{\n $safeNavigationMigration(nodeConfig()?.title) | translate | async\n }}</span>\n </div>\n </div>\n <axp-widget-property-viewer\n [mode]=\"'advanced'\"\n [widget]=\"service.selectedNode()!\"\n (onChanged)=\"\n service.update({ values: $event.values, mode: $event.mode })\n \"\n class=\"flex-1 overflow-auto\"\n >\n </axp-widget-property-viewer>\n </ax-content>\n </ax-drawer>\n</ax-drawer-container>\n", styles: ["@layer properties;axp-layout-designer .axp-designer-board{background-color:#fff;background-image:linear-gradient(45deg,#f0f0f0 25%,transparent 25%,transparent 75%,#f0f0f0 75%,#f0f0f0),linear-gradient(45deg,#f0f0f0 25%,#fff 25% 75%,#f0f0f0 75%,#f0f0f0);background-size:20px 20px;background-position:0 0,10px 10px;display:block;height:100%;width:100%;overflow:auto;background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 3)}axp-layout-designer .axp-designer-board ax-drawer{background-color:var(--color-light);color:var(--color-on-light);border-color:var(--color-border-light)}axp-layout-designer .axp-designer-board.axp-state-design .widget-outline{outline-style:var(--tw-outline-style);outline-width:1px;outline-offset:2px;outline-color:var(--color-gray-300, oklch(87.2% .01 258.338));--tw-outline-style: dashed;outline-style:dashed}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host{position:relative;cursor:pointer}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected{outline-width:1px;outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:1px;outline-color:var(--color-blue-400, oklch(70.7% .165 254.624))}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected.widget-container{outline-offset:2px}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:var(--color-fuchsia-500, oklch(66.7% .295 322.15));--tw-outline-style: dashed;outline-style:dashed}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.axp-state-selected{outline-color:var(--color-blue-400, oklch(70.7% .165 254.624))}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.widget-container{outline-offset:4px}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host .axp-widget-overlay{position:absolute;inset:0;z-index:2000;height:100%;width:100%}axp-layout-designer .axp-designer-board .axp-empty-space{display:flex;flex:1;cursor:default;align-items:center;justify-content:center;background-color:rgba(var(--ax-sys-color-primary-300));background-image:linear-gradient(135deg,rgba(var(--ax-sys-color-primary-300)) 10%,transparent 0,transparent 50%,rgba(var(--ax-sys-color-primary-300)) 0,rgba(var(--ax-sys-color-primary-300)) 60%,transparent 0,transparent);background-size:7.5px 7.5px;height:100%}@supports (color: color-mix(in lab,red,red)){axp-layout-designer .axp-designer-board .axp-empty-space{background-color:color-mix(in oklab,rgba(var(--ax-sys-color-primary-300)) 10%,transparent)}}@supports (color: color-mix(in lab,red,red)){axp-layout-designer .axp-designer-board .axp-empty-space{background-image:linear-gradient(135deg,color-mix(in oklab,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 10%,transparent 0,transparent 50%,color-mix(in oklab,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 0,color-mix(in oklab,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 60%,transparent 0,transparent)}}axp-layout-designer .axp-designer-board .axp-page-widget-header,axp-layout-designer .axp-designer-board .axp-page-widget-footer{filter:grayscale(100%);opacity:.5}:is(axp-layout-designer .axp-designer-board .axp-page-widget-header,axp-layout-designer .axp-designer-board .axp-page-widget-footer):hover{filter:grayscale(0);opacity:1}axp-layout-designer .axp-designer-board .axp-page-widget-header{margin-bottom:calc(var(--spacing, .25rem) * 2)}axp-layout-designer .axp-designer-board .axp-page-widget-footer{margin-top:calc(var(--spacing, .25rem) * 2)}axp-layout-designer .axp-designer-prop-side{width:calc(var(--spacing, .25rem) * 80);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;background-color:var(--color-light);color:var(--color-on-light);border-color:var(--color-border-light)}axp-designer-drawer-content ul.axp-designer-drawer-list{padding:var(--spacing, .25rem);font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:var(--color-white, #fff)}:where(axp-designer-drawer-content ul.axp-designer-drawer-list>:not(:last-child)){--tw-space-y-reverse: 0;margin-block-start:calc(var(--spacing, .25rem) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing, .25rem) * calc(1 - var(--tw-space-y-reverse)))}axp-designer-drawer-content ul.axp-designer-drawer-list li{cursor:pointer;border-radius:var(--radius-sm, .25rem);padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:var(--spacing, .25rem);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .15s;transition-duration:.15s;--tw-ease: var(--ease-in-out, cubic-bezier(.4, 0, .2, 1));transition-timing-function:var(--ease-in-out, cubic-bezier(.4, 0, .2, 1))}axp-designer-drawer-content ul.axp-designer-drawer-list li:hover:not(.axp-designer-drawer-list){background-color:var(--color-gray-700, oklch(37.3% .034 259.733))}axp-designer-drawer-content ul.axp-designer-drawer-list li.state-selected{background-color:var(--color-neutral-700, oklch(37.1% 0 none))}axp-designer-drawer-content .axp-designer-drawer-empty{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 4);color:var(--color-gray-300, oklch(87.2% .01 258.338))}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-space-y-reverse{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-duration{syntax: \"*\"; inherits: false;}@property --tw-ease{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-border-style: solid;--tw-space-y-reverse: 0;--tw-duration: initial;--tw-ease: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: AXPDesignerBoardComponent, selector: "axp-designer-board" }, { kind: "component", type: AXPWidgetPropertyViewerComponent, selector: "axp-widget-property-viewer", inputs: ["widget", "mode"], outputs: ["onChanged"] }, { kind: "component", type: AXPDesignerBreadcrumbsComponent, selector: "axp-designer-breadcrumbs" }, { kind: "component", type: AXPDesignerOutlineComponent, selector: "axp-designer-outline" }, { kind: "component", type: AXPDesignerHeaderMenuComponent, selector: "axp-designer-header-menu" }, { kind: "component", type: AXPDesignerHistoryComponent, selector: "axp-designer-history" }, { kind: "component", type: AXPDesignerDataSourcesComponent, selector: "axp-designer-data-sources" }, { kind: "component", type: AXPDesignerPagesComponent, selector: "axp-designer-pages" }, { kind: "component", type: AXPDesignerViewSizeToolbarComponent, selector: "axp-designer-view-sizes", inputs: ["size"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXTabsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i2.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i2.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDrawerModule }, { kind: "component", type: i3.AXDrawerComponent, selector: "ax-drawer", inputs: ["location", "showBackdrop", "mode", "transition", "closeOnBackdropClick", "backdropClass", "collapsed", "singleOpenMode"], outputs: ["onBackdropClick", "collapsedStateChanged"] }, { kind: "component", type: i3.AXDrawerContainerComponent, selector: "ax-drawer-container" }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXMenuModule }, { kind: "ngmodule", type: AXButtonGroupModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: i5$1.AXDropdownButtonComponent, selector: "ax-dropdown-button", inputs: ["disabled", "size", "text", "color", "look", "type", "mode"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "lookChange", "colorChange", "disabledChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i8.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager, encapsulation: i0.ViewEncapsulation.None }); }
3467
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPLayoutDesignerComponent, isStandalone: true, selector: "axp-layout-designer", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, strategy: { classPropertyName: "strategy", publicName: "strategy", isSignal: true, isRequired: false, transformFunction: null }, profile: { classPropertyName: "profile", publicName: "profile", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.direction": "this.designerDirection" } }, providers: [
3468
+ AXPDesignerService,
3469
+ CommandManager,
3470
+ {
3471
+ provide: AXP_LOCAL_DATA_SOURCE_STORE,
3472
+ useFactory: (svc) => svc.toLocalDataSourceStore(),
3473
+ deps: [AXPDesignerService],
3474
+ },
3475
+ {
3476
+ provide: AXP_LAYOUT_PROFILE,
3477
+ useFactory: () => {
3478
+ const service = inject(AXPDesignerService);
3479
+ return computed(() => ({
3480
+ size: designerPreviewSizeToLayoutSize(service.size()),
3481
+ medium: 'screen',
3482
+ }));
3483
+ },
3484
+ },
3485
+ ], viewQueries: [{ propertyName: "propertyViewer", first: true, predicate: AXPWidgetPropertyViewerComponent, descendants: true, static: true }], ngImport: i0, template: "<ax-drawer-container class=\"h-full w-full flex relative overflow-hidden\">\n <ax-content class=\"contents\">\n <!-- Toolbar Side -->\n <div\n class=\"ax-dark min-w-10 lightest-surface border-e ax-sm flex flex-col items-center\"\n >\n <div class=\"min-w-10 h-10 flex items-center justify-center border-b\">\n <axp-logo logoKey=\"platform\" appearance=\"onDark\" format=\"icon\" [size]=\"24\"></axp-logo>\n </div>\n <div class=\"py-1\"></div>\n <div class=\"flex flex-col gap-2\">\n <!-- ----- Palette opens the widget picker when the surface has no dedicated drawer ----- -->\n @if (hasPaletteDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"service.showPicker()\"\n [title]=\"('@layout-designer:components.palette.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-puzzle text-sm\"> </ax-icon>\n </ax-button>\n }\n <!-- ----- Pages drawer toggle ----- -->\n @if (hasPagesDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"pages.toggle()\"\n [title]=\"('@layout-designer:components.pages.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-page text-sm\"> </ax-icon>\n </ax-button>\n }\n <!-- ----- Data sources drawer toggle ----- -->\n @if (hasDataSourcesDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"datasources.toggle()\"\n [title]=\"('@layout-designer:components.data-sources.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-database text-sm\"> </ax-icon>\n </ax-button>\n }\n <!-- ----- Outline drawer toggle ----- -->\n @if (hasOutlineDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"outline.toggle()\"\n [title]=\"('@layout-designer:components.outline.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-list-tree text-sm\"> </ax-icon>\n </ax-button>\n }\n <!-- ----- History drawer toggle ----- -->\n @if (hasHistoryDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"history.toggle()\"\n [title]=\"('@layout-designer:components.history.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-clock-rotate-left text-sm\"> </ax-icon>\n </ax-button>\n }\n </div>\n </div>\n <!-- Main Side -->\n <div class=\"col-span-10 flex flex-col flex-1\">\n <!-- Header Toolbar -->\n <div class=\"ax-dark h-10 lightest-surface border-b flex justify-between px-2\">\n <div class=\"flex flex-1 items-center justify-start text-on-lightest\">\n <axp-designer-header-menu></axp-designer-header-menu>\n </div>\n <div\n class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\"\n >\n <axp-designer-view-sizes\n [(size)]=\"service.size\"\n ></axp-designer-view-sizes>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-end\">\n <ax-dropdown-button\n mode=\"split\"\n look=\"solid\"\n [text]=\"('@layout-designer:keyboard-shortcuts.preview' | translate | async) ?? ''\"\n class=\"ax-sm\"\n (onClick)=\"service.openPreviewDialog()\"\n >\n <ax-button-item-list>\n <ax-button-item\n [text]=\"('@layout-designer:keyboard-shortcuts.preview-window' | translate | async) ?? ''\"\n (onClick)=\"service.openPreviewWindow()\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-window-restore\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-button>\n </div>\n </div>\n <!-- Board -->\n <ax-drawer-container class=\"w-full flex-1\">\n <!-- ----- Pages drawer (rail button is profile-gated; panel stays mounted for #pages) ----- -->\n <ax-drawer\n #pages\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark lightest-surface\"\n >\n <ax-content>\n <axp-designer-pages></axp-designer-pages>\n </ax-content>\n </ax-drawer>\n <!-- ----- Data sources drawer ----- -->\n <ax-drawer\n #datasources\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark lightest-surface\"\n >\n <ax-content>\n <axp-designer-data-sources></axp-designer-data-sources>\n </ax-content>\n </ax-drawer>\n <!-- ----- Outline drawer ----- -->\n <ax-drawer\n #outline\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark lightest-surface\"\n >\n <ax-content>\n <axp-designer-outline></axp-designer-outline>\n </ax-content>\n </ax-drawer>\n <!-- ----- History drawer ----- -->\n <ax-drawer\n #history\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark lightest-surface\"\n >\n <ax-content>\n <axp-designer-history></axp-designer-history>\n </ax-content>\n </ax-drawer>\n <!-- Content of board -->\n <ax-content class=\"ax-light lighter-surface axp-designer-canvas flex h-full min-h-0 items-start justify-center overflow-auto p-10\">\n <axp-designer-board class=\"rounded-lg lightest-surface shadow-md\">\n </axp-designer-board>\n </ax-content>\n </ax-drawer-container>\n <!-- Footer -->\n <div class=\"ax-dark h-10 lightest-surface border-t flex items-center text-on-lightest\">\n <axp-designer-breadcrumbs class=\"border-default p-2 font-normal min-w-0 flex-1\">\n </axp-designer-breadcrumbs>\n <!-- ----- Hook-contributed actions at the footer end ----- -->\n @if (footerEndActions().length) {\n <div class=\"ms-auto flex items-center gap-1 pe-2\">\n @for (item of footerEndActions(); track item.id) {\n <ax-button\n class=\"ax-sm\"\n look=\"blank\"\n [iconOnly]=\"true\"\n [title]=\"item.resolvedLabel ?? ''\"\n (onClick)=\"runFooterEndAction(item)\"\n >\n @if (item.iconClass) {\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n }\n </ax-button>\n }\n </div>\n }\n </div>\n </div>\n </ax-content>\n <!-- Property Side -->\n <ax-drawer\n class=\"ax-dark axp-designer-prop-side lightest-surface\"\n location=\"end\"\n mode=\"push\"\n [collapsed]=\"false\"\n #pd\n >\n <ax-content class=\"flex flex-col h-full overflow-hidden\">\n <div\n class=\"flex flex-row w-full justify-between px-4 border-b h-10 text-on-lightest\"\n >\n <div class=\"flex flex-row gap-2 items-center\">\n <i [ngClass]=\"$safeNavigationMigration(nodeConfig()?.icon)\"></i>\n <span class=\"font-semibold\">{{\n $safeNavigationMigration(nodeConfig()?.title) | translate | async\n }}</span>\n </div>\n </div>\n <axp-widget-property-viewer\n [mode]=\"'advanced'\"\n [bindingExpressionEditorMode]=\"'popup'\"\n [widget]=\"service.selectedNode()!\"\n [layoutProfile]=\"layoutProfileContext()\"\n (onChanged)=\"service.applyPropertyPanelChange($event)\"\n (pointerdown)=\"service.armPropertyPanelHistory()\"\n (focusin)=\"service.armPropertyPanelHistory()\"\n class=\"flex-1 overflow-auto\"\n >\n </axp-widget-property-viewer>\n </ax-content>\n </ax-drawer>\n</ax-drawer-container>\n", styles: ["@layer properties;axp-layout-designer .axp-designer-board{display:block;min-height:100%;width:100%;overflow:auto;background-color:var(--color-lightest);color:var(--color-on-lightest);border-color:var(--color-border-lightest);padding:calc(var(--spacing, .25rem) * 3);background-image:linear-gradient(45deg,rgb(var(--ax-sys-color-light-surface)) 25%,transparent 25%,transparent 75%,rgb(var(--ax-sys-color-light-surface)) 75%,rgb(var(--ax-sys-color-light-surface))),linear-gradient(45deg,rgb(var(--ax-sys-color-light-surface)) 25%,rgb(var(--ax-sys-color-lightest-surface)) 25% 75%,rgb(var(--ax-sys-color-light-surface)) 75%,rgb(var(--ax-sys-color-light-surface)));background-size:20px 20px;background-position:0 0,10px 10px}axp-layout-designer .axp-designer-board.axp-state-design .widget-outline,axp-layout-designer .axp-designer-board.axp-state-design .ax-widget-outline{outline-style:var(--tw-outline-style);outline-width:1px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-border-light-surface));--tw-outline-style: dashed;outline-style:dashed}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host{position:relative;cursor:pointer}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected{outline-width:1px;outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:1px;outline-color:rgba(var(--ax-sys-color-primary-400))}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected.widget-container,axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected.ax-widget-container{outline-offset:2px}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-accent1-500));--tw-outline-style: dashed;outline-style:dashed}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.axp-state-selected{outline-color:rgba(var(--ax-sys-color-primary-400))}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.widget-container,axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.ax-widget-container{outline-offset:4px}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host .axp-widget-overlay{position:absolute;inset:0;z-index:2000;height:100%;width:100%}axp-layout-designer .axp-designer-board .axp-empty-space{display:flex;flex:1;cursor:default;align-items:center;justify-content:center;background-color:color-mix(in srgb,rgba(var(--ax-sys-color-primary-300)) 10%,transparent);background-image:linear-gradient(135deg,color-mix(in srgb,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 10%,transparent 0,transparent 50%,color-mix(in srgb,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 0,color-mix(in srgb,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 60%,transparent 0,transparent);background-size:7.5px 7.5px;height:100%}@supports (color: color-mix(in lab,red,red)){axp-layout-designer .axp-designer-board .axp-empty-space{background-color:color-mix(in srgb,var(--color-primary-300) 10%,transparent)}}@supports (color: color-mix(in lab,red,red)){axp-layout-designer .axp-designer-board .axp-empty-space{background-image:linear-gradient(135deg,color-mix(in srgb,var(--color-primary-300) 50%,transparent) 10%,transparent 0,transparent 50%,color-mix(in srgb,var(--color-primary-300) 50%,transparent) 0,color-mix(in srgb,var(--color-primary-300) 50%,transparent) 60%,transparent 0,transparent)}}axp-layout-designer .axp-designer-board .axp-page-widget-header,axp-layout-designer .axp-designer-board .axp-page-widget-footer{filter:grayscale(100%);opacity:.5}:is(axp-layout-designer .axp-designer-board .axp-page-widget-header,axp-layout-designer .axp-designer-board .axp-page-widget-footer):hover{filter:grayscale(0);opacity:1}axp-layout-designer .axp-designer-board .axp-page-widget-header{margin-bottom:calc(var(--spacing, .25rem) * 2)}axp-layout-designer .axp-designer-board .axp-page-widget-footer{margin-top:calc(var(--spacing, .25rem) * 2)}axp-layout-designer .axp-designer-prop-side{width:calc(var(--spacing, .25rem) * 80);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px}axp-designer-drawer-content ul.axp-designer-drawer-list{padding:var(--spacing, .25rem);font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-lightest-surface))}:where(axp-designer-drawer-content ul.axp-designer-drawer-list>:not(:last-child)){--tw-space-y-reverse: 0;margin-block-start:calc(var(--spacing, .25rem) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing, .25rem) * calc(1 - var(--tw-space-y-reverse)))}axp-designer-drawer-content ul.axp-designer-drawer-list li{cursor:pointer;border-radius:var(--radius-sm, .25rem);padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:var(--spacing, .25rem);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .15s;transition-duration:.15s;--tw-ease: var(--ease-in-out, cubic-bezier(.4, 0, .2, 1));transition-timing-function:var(--ease-in-out, cubic-bezier(.4, 0, .2, 1))}axp-designer-drawer-content ul.axp-designer-drawer-list li:hover:not(.axp-designer-drawer-list){background-color:var(--color-dark);color:var(--color-on-dark);border-color:var(--color-border-dark)}axp-designer-drawer-content ul.axp-designer-drawer-list li.state-selected{background-color:var(--color-darker);color:var(--color-on-darker);border-color:var(--color-border-darker)}axp-designer-drawer-content .axp-designer-drawer-empty{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 4);color:rgba(var(--ax-sys-color-on-lightest-surface));opacity:70%}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-space-y-reverse{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-duration{syntax: \"*\"; inherits: false;}@property --tw-ease{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-border-style: solid;--tw-space-y-reverse: 0;--tw-duration: initial;--tw-ease: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: AXPDesignerBoardComponent, selector: "axp-designer-board" }, { kind: "component", type: AXPWidgetPropertyViewerComponent, selector: "axp-widget-property-viewer", inputs: ["widget", "mode", "layoutProfile", "bindingExpressionEditorMode"], outputs: ["onChanged"] }, { kind: "component", type: AXPDesignerBreadcrumbsComponent, selector: "axp-designer-breadcrumbs" }, { kind: "component", type: AXPDesignerOutlineComponent, selector: "axp-designer-outline" }, { kind: "component", type: AXPDesignerHeaderMenuComponent, selector: "axp-designer-header-menu" }, { kind: "component", type: AXPDesignerHistoryComponent, selector: "axp-designer-history" }, { kind: "component", type: AXPDesignerDataSourcesComponent, selector: "axp-designer-data-sources" }, { kind: "component", type: AXPDesignerPagesComponent, selector: "axp-designer-pages" }, { kind: "component", type: AXPDesignerViewSizeToolbarComponent, selector: "axp-designer-view-sizes", inputs: ["size"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXTabsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i2.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i2.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDrawerModule }, { kind: "component", type: i3.AXDrawerComponent, selector: "ax-drawer", inputs: ["location", "showBackdrop", "mode", "transition", "closeOnBackdropClick", "backdropClass", "collapsed", "singleOpenMode"], outputs: ["onBackdropClick", "collapsedStateChanged"] }, { kind: "component", type: i3.AXDrawerContainerComponent, selector: "ax-drawer-container" }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXMenuModule }, { kind: "ngmodule", type: AXButtonGroupModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: i5$1.AXDropdownButtonComponent, selector: "ax-dropdown-button", inputs: ["disabled", "size", "text", "color", "look", "type", "mode"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "lookChange", "colorChange", "disabledChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPLogoComponent, selector: "axp-logo", inputs: ["logoKey", "appearance", "size", "format"] }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager, encapsulation: i0.ViewEncapsulation.None }); }
2662
3486
  }
2663
3487
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLayoutDesignerComponent, decorators: [{
2664
3488
  type: Component,
@@ -2683,18 +3507,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2683
3507
  AXDropdownModule,
2684
3508
  AXDropdownButtonModule,
2685
3509
  AXTranslationModule,
2686
- ], changeDetection: ChangeDetectionStrategy.Eager, providers: [AXPDesignerService], template: "<ax-drawer-container class=\"h-full w-full flex relative overflow-hidden\">\n <ax-content class=\"contents\">\n <!-- Toolbar Side -->\n <div\n class=\"min-w-10 bg-lightest border-e ax-sm flex flex-col items-center ax-dark\"\n >\n <div class=\"min-w-10 h-10 flex items-center justify-center border-b\">\n <img src=\"assets/images/brand/platform/platform-icon-light.svg\" class=\"w-6\" />\n </div>\n <div class=\"py-1\"></div>\n <div class=\"flex flex-col gap-2\">\n <!-- <ax-button look=\"blank\">\n <ax-icon class=\"fa-solid fa-puzzle text-sm\"> </ax-icon>\n </ax-button> -->\n <ax-button look=\"blank\" (onClick)=\"pages.toggle()\" title=\"Pages\">\n <ax-icon class=\"fa-solid fa-page text-sm\"> </ax-icon>\n </ax-button>\n <ax-button\n look=\"blank\"\n (onClick)=\"datasources.toggle()\"\n title=\"Data Sources\"\n >\n <ax-icon class=\"fa-solid fa-database text-sm\"> </ax-icon>\n </ax-button>\n <ax-button look=\"blank\" (onClick)=\"outline.toggle()\" title=\"Outline\">\n <ax-icon class=\"fa-solid fa-list-tree text-sm\"> </ax-icon>\n </ax-button>\n <ax-button look=\"blank\" (onClick)=\"history.toggle()\" title=\"History\">\n <ax-icon class=\"fa-solid fa-clock-rotate-left text-sm\"> </ax-icon>\n </ax-button>\n <!-- <ax-button look=\"blank\" title=\"Timeline\">\n <ax-icon class=\"fa-solid fa-diagram-sankey text-sm\"> </ax-icon>\n </ax-button> -->\n </div>\n </div>\n <!-- Main Side -->\n <div class=\"col-span-10 flex flex-col flex-1\">\n <!-- Header Toolbar -->\n <div class=\"h-10 bg-lightest border-b flex justify-between px-2 ax-dark\">\n <div class=\"flex flex-1 items-center text-white justify-start\">\n <axp-designer-header-menu></axp-designer-header-menu>\n </div>\n <div\n class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\"\n >\n <axp-designer-view-sizes\n [(size)]=\"service.size\"\n ></axp-designer-view-sizes>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-end\">\n <ax-dropdown-button\n mode=\"split\"\n look=\"solid\"\n text=\"Preview\"\n class=\"ax-sm\"\n (onClick)=\"service.openPreviewDialog()\"\n >\n <ax-button-item-list>\n <ax-button-item\n text=\"Open in new window\"\n (onClick)=\"service.openPreviewWindow()\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-window-restore\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-button>\n </div>\n </div>\n <!-- Board -->\n <ax-drawer-container class=\"w-full flex-1\">\n <!-- Pages -->\n <ax-drawer\n #pages\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark bg-lightest\"\n >\n <ax-content>\n <axp-designer-pages></axp-designer-pages>\n </ax-content>\n </ax-drawer>\n <!-- Datasources -->\n <ax-drawer\n #datasources\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark bg-lightest\"\n >\n <ax-content>\n <axp-designer-data-sources></axp-designer-data-sources>\n </ax-content>\n </ax-drawer>\n <!-- Outline Pnael -->\n <ax-drawer\n #outline\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark bg-lightest\"\n >\n <ax-content>\n <axp-designer-outline></axp-designer-outline>\n </ax-content>\n </ax-drawer>\n <!-- History Pnael -->\n <ax-drawer\n #history\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark bg-lightest\"\n >\n <ax-content>\n <axp-designer-history></axp-designer-history>\n </ax-content>\n </ax-drawer>\n <!-- Content of board -->\n <ax-content class=\"light-surface bg-lighter flex justify-center p-10\">\n <axp-designer-board class=\"rounded-lg bg-lightest shadow-md\">\n </axp-designer-board>\n </ax-content>\n </ax-drawer-container>\n <!-- Footer -->\n <div class=\"h-10 bg-lightest border-t flex items-center ax-dark\">\n <axp-designer-breadcrumbs class=\"border-default p-2 font-normal\">\n </axp-designer-breadcrumbs>\n </div>\n </div>\n </ax-content>\n <!-- Property Side -->\n <ax-drawer\n class=\"ax-dark axp-designer-prop-side\"\n location=\"end\"\n mode=\"push\"\n [collapsed]=\"false\"\n #pd\n >\n <ax-content class=\"flex flex-col h-full overflow-hidden\">\n <div\n class=\"flex flex-row w-full justify-between text-white px-4 border-b h-10\"\n >\n <div class=\"flex flex-row gap-2 items-center\">\n <i [ngClass]=\"$safeNavigationMigration(nodeConfig()?.icon)\"></i>\n <span class=\"font-semibold\">{{\n $safeNavigationMigration(nodeConfig()?.title) | translate | async\n }}</span>\n </div>\n </div>\n <axp-widget-property-viewer\n [mode]=\"'advanced'\"\n [widget]=\"service.selectedNode()!\"\n (onChanged)=\"\n service.update({ values: $event.values, mode: $event.mode })\n \"\n class=\"flex-1 overflow-auto\"\n >\n </axp-widget-property-viewer>\n </ax-content>\n </ax-drawer>\n</ax-drawer-container>\n", styles: ["@layer properties;axp-layout-designer .axp-designer-board{background-color:#fff;background-image:linear-gradient(45deg,#f0f0f0 25%,transparent 25%,transparent 75%,#f0f0f0 75%,#f0f0f0),linear-gradient(45deg,#f0f0f0 25%,#fff 25% 75%,#f0f0f0 75%,#f0f0f0);background-size:20px 20px;background-position:0 0,10px 10px;display:block;height:100%;width:100%;overflow:auto;background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 3)}axp-layout-designer .axp-designer-board ax-drawer{background-color:var(--color-light);color:var(--color-on-light);border-color:var(--color-border-light)}axp-layout-designer .axp-designer-board.axp-state-design .widget-outline{outline-style:var(--tw-outline-style);outline-width:1px;outline-offset:2px;outline-color:var(--color-gray-300, oklch(87.2% .01 258.338));--tw-outline-style: dashed;outline-style:dashed}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host{position:relative;cursor:pointer}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected{outline-width:1px;outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:1px;outline-color:var(--color-blue-400, oklch(70.7% .165 254.624))}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected.widget-container{outline-offset:2px}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:var(--color-fuchsia-500, oklch(66.7% .295 322.15));--tw-outline-style: dashed;outline-style:dashed}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.axp-state-selected{outline-color:var(--color-blue-400, oklch(70.7% .165 254.624))}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.widget-container{outline-offset:4px}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host .axp-widget-overlay{position:absolute;inset:0;z-index:2000;height:100%;width:100%}axp-layout-designer .axp-designer-board .axp-empty-space{display:flex;flex:1;cursor:default;align-items:center;justify-content:center;background-color:rgba(var(--ax-sys-color-primary-300));background-image:linear-gradient(135deg,rgba(var(--ax-sys-color-primary-300)) 10%,transparent 0,transparent 50%,rgba(var(--ax-sys-color-primary-300)) 0,rgba(var(--ax-sys-color-primary-300)) 60%,transparent 0,transparent);background-size:7.5px 7.5px;height:100%}@supports (color: color-mix(in lab,red,red)){axp-layout-designer .axp-designer-board .axp-empty-space{background-color:color-mix(in oklab,rgba(var(--ax-sys-color-primary-300)) 10%,transparent)}}@supports (color: color-mix(in lab,red,red)){axp-layout-designer .axp-designer-board .axp-empty-space{background-image:linear-gradient(135deg,color-mix(in oklab,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 10%,transparent 0,transparent 50%,color-mix(in oklab,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 0,color-mix(in oklab,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 60%,transparent 0,transparent)}}axp-layout-designer .axp-designer-board .axp-page-widget-header,axp-layout-designer .axp-designer-board .axp-page-widget-footer{filter:grayscale(100%);opacity:.5}:is(axp-layout-designer .axp-designer-board .axp-page-widget-header,axp-layout-designer .axp-designer-board .axp-page-widget-footer):hover{filter:grayscale(0);opacity:1}axp-layout-designer .axp-designer-board .axp-page-widget-header{margin-bottom:calc(var(--spacing, .25rem) * 2)}axp-layout-designer .axp-designer-board .axp-page-widget-footer{margin-top:calc(var(--spacing, .25rem) * 2)}axp-layout-designer .axp-designer-prop-side{width:calc(var(--spacing, .25rem) * 80);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;background-color:var(--color-light);color:var(--color-on-light);border-color:var(--color-border-light)}axp-designer-drawer-content ul.axp-designer-drawer-list{padding:var(--spacing, .25rem);font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:var(--color-white, #fff)}:where(axp-designer-drawer-content ul.axp-designer-drawer-list>:not(:last-child)){--tw-space-y-reverse: 0;margin-block-start:calc(var(--spacing, .25rem) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing, .25rem) * calc(1 - var(--tw-space-y-reverse)))}axp-designer-drawer-content ul.axp-designer-drawer-list li{cursor:pointer;border-radius:var(--radius-sm, .25rem);padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:var(--spacing, .25rem);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .15s;transition-duration:.15s;--tw-ease: var(--ease-in-out, cubic-bezier(.4, 0, .2, 1));transition-timing-function:var(--ease-in-out, cubic-bezier(.4, 0, .2, 1))}axp-designer-drawer-content ul.axp-designer-drawer-list li:hover:not(.axp-designer-drawer-list){background-color:var(--color-gray-700, oklch(37.3% .034 259.733))}axp-designer-drawer-content ul.axp-designer-drawer-list li.state-selected{background-color:var(--color-neutral-700, oklch(37.1% 0 none))}axp-designer-drawer-content .axp-designer-drawer-empty{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 4);color:var(--color-gray-300, oklch(87.2% .01 258.338))}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-space-y-reverse{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-duration{syntax: \"*\"; inherits: false;}@property --tw-ease{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-border-style: solid;--tw-space-y-reverse: 0;--tw-duration: initial;--tw-ease: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
2687
- }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], strategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "strategy", required: false }] }], propertyViewer: [{
3510
+ AXPLogoComponent,
3511
+ ], changeDetection: ChangeDetectionStrategy.Eager, providers: [
3512
+ AXPDesignerService,
3513
+ CommandManager,
3514
+ {
3515
+ provide: AXP_LOCAL_DATA_SOURCE_STORE,
3516
+ useFactory: (svc) => svc.toLocalDataSourceStore(),
3517
+ deps: [AXPDesignerService],
3518
+ },
3519
+ {
3520
+ provide: AXP_LAYOUT_PROFILE,
3521
+ useFactory: () => {
3522
+ const service = inject(AXPDesignerService);
3523
+ return computed(() => ({
3524
+ size: designerPreviewSizeToLayoutSize(service.size()),
3525
+ medium: 'screen',
3526
+ }));
3527
+ },
3528
+ },
3529
+ ], template: "<ax-drawer-container class=\"h-full w-full flex relative overflow-hidden\">\n <ax-content class=\"contents\">\n <!-- Toolbar Side -->\n <div\n class=\"ax-dark min-w-10 lightest-surface border-e ax-sm flex flex-col items-center\"\n >\n <div class=\"min-w-10 h-10 flex items-center justify-center border-b\">\n <axp-logo logoKey=\"platform\" appearance=\"onDark\" format=\"icon\" [size]=\"24\"></axp-logo>\n </div>\n <div class=\"py-1\"></div>\n <div class=\"flex flex-col gap-2\">\n <!-- ----- Palette opens the widget picker when the surface has no dedicated drawer ----- -->\n @if (hasPaletteDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"service.showPicker()\"\n [title]=\"('@layout-designer:components.palette.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-puzzle text-sm\"> </ax-icon>\n </ax-button>\n }\n <!-- ----- Pages drawer toggle ----- -->\n @if (hasPagesDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"pages.toggle()\"\n [title]=\"('@layout-designer:components.pages.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-page text-sm\"> </ax-icon>\n </ax-button>\n }\n <!-- ----- Data sources drawer toggle ----- -->\n @if (hasDataSourcesDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"datasources.toggle()\"\n [title]=\"('@layout-designer:components.data-sources.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-database text-sm\"> </ax-icon>\n </ax-button>\n }\n <!-- ----- Outline drawer toggle ----- -->\n @if (hasOutlineDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"outline.toggle()\"\n [title]=\"('@layout-designer:components.outline.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-list-tree text-sm\"> </ax-icon>\n </ax-button>\n }\n <!-- ----- History drawer toggle ----- -->\n @if (hasHistoryDrawer()) {\n <ax-button\n look=\"blank\"\n (onClick)=\"history.toggle()\"\n [title]=\"('@layout-designer:components.history.title' | translate | async) ?? ''\"\n >\n <ax-icon class=\"fa-solid fa-clock-rotate-left text-sm\"> </ax-icon>\n </ax-button>\n }\n </div>\n </div>\n <!-- Main Side -->\n <div class=\"col-span-10 flex flex-col flex-1\">\n <!-- Header Toolbar -->\n <div class=\"ax-dark h-10 lightest-surface border-b flex justify-between px-2\">\n <div class=\"flex flex-1 items-center justify-start text-on-lightest\">\n <axp-designer-header-menu></axp-designer-header-menu>\n </div>\n <div\n class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\"\n >\n <axp-designer-view-sizes\n [(size)]=\"service.size\"\n ></axp-designer-view-sizes>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-end\">\n <ax-dropdown-button\n mode=\"split\"\n look=\"solid\"\n [text]=\"('@layout-designer:keyboard-shortcuts.preview' | translate | async) ?? ''\"\n class=\"ax-sm\"\n (onClick)=\"service.openPreviewDialog()\"\n >\n <ax-button-item-list>\n <ax-button-item\n [text]=\"('@layout-designer:keyboard-shortcuts.preview-window' | translate | async) ?? ''\"\n (onClick)=\"service.openPreviewWindow()\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-window-restore\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-button>\n </div>\n </div>\n <!-- Board -->\n <ax-drawer-container class=\"w-full flex-1\">\n <!-- ----- Pages drawer (rail button is profile-gated; panel stays mounted for #pages) ----- -->\n <ax-drawer\n #pages\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark lightest-surface\"\n >\n <ax-content>\n <axp-designer-pages></axp-designer-pages>\n </ax-content>\n </ax-drawer>\n <!-- ----- Data sources drawer ----- -->\n <ax-drawer\n #datasources\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark lightest-surface\"\n >\n <ax-content>\n <axp-designer-data-sources></axp-designer-data-sources>\n </ax-content>\n </ax-drawer>\n <!-- ----- Outline drawer ----- -->\n <ax-drawer\n #outline\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark lightest-surface\"\n >\n <ax-content>\n <axp-designer-outline></axp-designer-outline>\n </ax-content>\n </ax-drawer>\n <!-- ----- History drawer ----- -->\n <ax-drawer\n #history\n location=\"start\"\n mode=\"push\"\n class=\"ax-dark lightest-surface\"\n >\n <ax-content>\n <axp-designer-history></axp-designer-history>\n </ax-content>\n </ax-drawer>\n <!-- Content of board -->\n <ax-content class=\"ax-light lighter-surface axp-designer-canvas flex h-full min-h-0 items-start justify-center overflow-auto p-10\">\n <axp-designer-board class=\"rounded-lg lightest-surface shadow-md\">\n </axp-designer-board>\n </ax-content>\n </ax-drawer-container>\n <!-- Footer -->\n <div class=\"ax-dark h-10 lightest-surface border-t flex items-center text-on-lightest\">\n <axp-designer-breadcrumbs class=\"border-default p-2 font-normal min-w-0 flex-1\">\n </axp-designer-breadcrumbs>\n <!-- ----- Hook-contributed actions at the footer end ----- -->\n @if (footerEndActions().length) {\n <div class=\"ms-auto flex items-center gap-1 pe-2\">\n @for (item of footerEndActions(); track item.id) {\n <ax-button\n class=\"ax-sm\"\n look=\"blank\"\n [iconOnly]=\"true\"\n [title]=\"item.resolvedLabel ?? ''\"\n (onClick)=\"runFooterEndAction(item)\"\n >\n @if (item.iconClass) {\n <ax-icon>\n <i [class]=\"item.iconClass\"></i>\n </ax-icon>\n }\n </ax-button>\n }\n </div>\n }\n </div>\n </div>\n </ax-content>\n <!-- Property Side -->\n <ax-drawer\n class=\"ax-dark axp-designer-prop-side lightest-surface\"\n location=\"end\"\n mode=\"push\"\n [collapsed]=\"false\"\n #pd\n >\n <ax-content class=\"flex flex-col h-full overflow-hidden\">\n <div\n class=\"flex flex-row w-full justify-between px-4 border-b h-10 text-on-lightest\"\n >\n <div class=\"flex flex-row gap-2 items-center\">\n <i [ngClass]=\"$safeNavigationMigration(nodeConfig()?.icon)\"></i>\n <span class=\"font-semibold\">{{\n $safeNavigationMigration(nodeConfig()?.title) | translate | async\n }}</span>\n </div>\n </div>\n <axp-widget-property-viewer\n [mode]=\"'advanced'\"\n [bindingExpressionEditorMode]=\"'popup'\"\n [widget]=\"service.selectedNode()!\"\n [layoutProfile]=\"layoutProfileContext()\"\n (onChanged)=\"service.applyPropertyPanelChange($event)\"\n (pointerdown)=\"service.armPropertyPanelHistory()\"\n (focusin)=\"service.armPropertyPanelHistory()\"\n class=\"flex-1 overflow-auto\"\n >\n </axp-widget-property-viewer>\n </ax-content>\n </ax-drawer>\n</ax-drawer-container>\n", styles: ["@layer properties;axp-layout-designer .axp-designer-board{display:block;min-height:100%;width:100%;overflow:auto;background-color:var(--color-lightest);color:var(--color-on-lightest);border-color:var(--color-border-lightest);padding:calc(var(--spacing, .25rem) * 3);background-image:linear-gradient(45deg,rgb(var(--ax-sys-color-light-surface)) 25%,transparent 25%,transparent 75%,rgb(var(--ax-sys-color-light-surface)) 75%,rgb(var(--ax-sys-color-light-surface))),linear-gradient(45deg,rgb(var(--ax-sys-color-light-surface)) 25%,rgb(var(--ax-sys-color-lightest-surface)) 25% 75%,rgb(var(--ax-sys-color-light-surface)) 75%,rgb(var(--ax-sys-color-light-surface)));background-size:20px 20px;background-position:0 0,10px 10px}axp-layout-designer .axp-designer-board.axp-state-design .widget-outline,axp-layout-designer .axp-designer-board.axp-state-design .ax-widget-outline{outline-style:var(--tw-outline-style);outline-width:1px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-border-light-surface));--tw-outline-style: dashed;outline-style:dashed}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host{position:relative;cursor:pointer}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected{outline-width:1px;outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:1px;outline-color:rgba(var(--ax-sys-color-primary-400))}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected.widget-container,axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-selected.ax-widget-container{outline-offset:2px}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-accent1-500));--tw-outline-style: dashed;outline-style:dashed}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.axp-state-selected{outline-color:rgba(var(--ax-sys-color-primary-400))}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.widget-container,axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host.axp-state-hover.ax-widget-container{outline-offset:4px}axp-layout-designer .axp-designer-board.axp-state-design .axp-widget-host .axp-widget-overlay{position:absolute;inset:0;z-index:2000;height:100%;width:100%}axp-layout-designer .axp-designer-board .axp-empty-space{display:flex;flex:1;cursor:default;align-items:center;justify-content:center;background-color:color-mix(in srgb,rgba(var(--ax-sys-color-primary-300)) 10%,transparent);background-image:linear-gradient(135deg,color-mix(in srgb,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 10%,transparent 0,transparent 50%,color-mix(in srgb,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 0,color-mix(in srgb,rgba(var(--ax-sys-color-primary-300)) 50%,transparent) 60%,transparent 0,transparent);background-size:7.5px 7.5px;height:100%}@supports (color: color-mix(in lab,red,red)){axp-layout-designer .axp-designer-board .axp-empty-space{background-color:color-mix(in srgb,var(--color-primary-300) 10%,transparent)}}@supports (color: color-mix(in lab,red,red)){axp-layout-designer .axp-designer-board .axp-empty-space{background-image:linear-gradient(135deg,color-mix(in srgb,var(--color-primary-300) 50%,transparent) 10%,transparent 0,transparent 50%,color-mix(in srgb,var(--color-primary-300) 50%,transparent) 0,color-mix(in srgb,var(--color-primary-300) 50%,transparent) 60%,transparent 0,transparent)}}axp-layout-designer .axp-designer-board .axp-page-widget-header,axp-layout-designer .axp-designer-board .axp-page-widget-footer{filter:grayscale(100%);opacity:.5}:is(axp-layout-designer .axp-designer-board .axp-page-widget-header,axp-layout-designer .axp-designer-board .axp-page-widget-footer):hover{filter:grayscale(0);opacity:1}axp-layout-designer .axp-designer-board .axp-page-widget-header{margin-bottom:calc(var(--spacing, .25rem) * 2)}axp-layout-designer .axp-designer-board .axp-page-widget-footer{margin-top:calc(var(--spacing, .25rem) * 2)}axp-layout-designer .axp-designer-prop-side{width:calc(var(--spacing, .25rem) * 80);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px}axp-designer-drawer-content ul.axp-designer-drawer-list{padding:var(--spacing, .25rem);font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-lightest-surface))}:where(axp-designer-drawer-content ul.axp-designer-drawer-list>:not(:last-child)){--tw-space-y-reverse: 0;margin-block-start:calc(var(--spacing, .25rem) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing, .25rem) * calc(1 - var(--tw-space-y-reverse)))}axp-designer-drawer-content ul.axp-designer-drawer-list li{cursor:pointer;border-radius:var(--radius-sm, .25rem);padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:var(--spacing, .25rem);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .15s;transition-duration:.15s;--tw-ease: var(--ease-in-out, cubic-bezier(.4, 0, .2, 1));transition-timing-function:var(--ease-in-out, cubic-bezier(.4, 0, .2, 1))}axp-designer-drawer-content ul.axp-designer-drawer-list li:hover:not(.axp-designer-drawer-list){background-color:var(--color-dark);color:var(--color-on-dark);border-color:var(--color-border-dark)}axp-designer-drawer-content ul.axp-designer-drawer-list li.state-selected{background-color:var(--color-darker);color:var(--color-on-darker);border-color:var(--color-border-darker)}axp-designer-drawer-content .axp-designer-drawer-empty{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 4);color:rgba(var(--ax-sys-color-on-lightest-surface));opacity:70%}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-space-y-reverse{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-duration{syntax: \"*\"; inherits: false;}@property --tw-ease{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-border-style: solid;--tw-space-y-reverse: 0;--tw-duration: initial;--tw-ease: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
3530
+ }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], strategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "strategy", required: false }] }], profile: [{ type: i0.Input, args: [{ isSignal: true, alias: "profile", required: false }] }], propertyViewer: [{
2688
3531
  type: ViewChild,
2689
3532
  args: [AXPWidgetPropertyViewerComponent, { static: true }]
2690
- }], __styles: [{
3533
+ }], designerDirection: [{
2691
3534
  type: HostBinding,
2692
- args: ['style']
3535
+ args: ['style.direction']
2693
3536
  }] } });
2694
3537
 
3538
+ //#endregion
3539
+
2695
3540
  class AXPLayoutDesignerPreviewViewerComponent {
3541
+ get(id) {
3542
+ return this.sources().find((row) => row.id === id);
3543
+ }
3544
+ create(name) {
3545
+ return { id: '', name, items: [] };
3546
+ }
3547
+ update() {
3548
+ return;
3549
+ }
3550
+ remove() {
3551
+ return;
3552
+ }
3553
+ get __class() {
3554
+ return 'block w-full min-h-full';
3555
+ }
2696
3556
  constructor() {
2697
3557
  this.context = {};
3558
+ this.initialDocument = input(null, /* @ts-ignore */
3559
+ ...(ngDevMode ? [{ debugName: "initialDocument" }] : /* istanbul ignore next */ []));
2698
3560
  this.document = signal({ type: AXPWidgetsCatalog.document }, /* @ts-ignore */
2699
3561
  ...(ngDevMode ? [{ debugName: "document" }] : /* istanbul ignore next */ []));
2700
3562
  this.currentPageIndex = signal(0, /* @ts-ignore */
@@ -2702,22 +3564,24 @@ class AXPLayoutDesignerPreviewViewerComponent {
2702
3564
  this.connector = inject(AXPDesignerConnector);
2703
3565
  this.eventService = inject(AXPBroadcastEventService);
2704
3566
  this.unsubscriber = inject(AXUnsubscriber);
3567
+ this.sources = computed(() => readLocalDataSourcesFromNode(this.document()), /* @ts-ignore */
3568
+ ...(ngDevMode ? [{ debugName: "sources" }] : /* istanbul ignore next */ []));
2705
3569
  this.currentPage = computed(() => {
2706
3570
  return (this.document().children?.[this.currentPageIndex()] ?? { type: AXPWidgetsCatalog.pageLayout });
2707
3571
  }, /* @ts-ignore */
2708
3572
  ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
3573
+ const seeded = readDesignerPreviewDocument();
3574
+ if (seeded) {
3575
+ this.document.set(seeded);
3576
+ }
2709
3577
  this.eventService
2710
3578
  .listen(AXPDesignerMessageKeys.Preview)
2711
3579
  .pipe(this.unsubscriber.takeUntilDestroy)
2712
3580
  .subscribe((event) => {
2713
- this.document.set(event.data.document);
3581
+ if (event.data?.document && !isEqual(this.document(), event.data.document)) {
3582
+ this.document.set(event.data.document);
3583
+ }
2714
3584
  });
2715
- //
2716
- if (document.body.parentElement) {
2717
- document.body.parentElement.classList.remove('ax-dark');
2718
- document.body.parentElement.style.backgroundColor = 'unset';
2719
- document.body.style.backgroundColor = 'unset';
2720
- }
2721
3585
  }
2722
3586
  ngAfterViewInit() {
2723
3587
  this.eventService.publish(AXPDesignerMessageKeys.Loaded);
@@ -2730,7 +3594,10 @@ class AXPLayoutDesignerPreviewViewerComponent {
2730
3594
  this.eventService.unsubscribe(AXPDesignerMessageKeys.Preview);
2731
3595
  }
2732
3596
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLayoutDesignerPreviewViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2733
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXPLayoutDesignerPreviewViewerComponent, isStandalone: true, selector: "axp-layout-builder-viewer", providers: [AXUnsubscriber], ngImport: i0, template: `
3597
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: AXPLayoutDesignerPreviewViewerComponent, isStandalone: true, selector: "axp-layout-builder-viewer", inputs: { initialDocument: { classPropertyName: "initialDocument", publicName: "initialDocument", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.__class" } }, providers: [
3598
+ AXUnsubscriber,
3599
+ { provide: AXP_LOCAL_DATA_SOURCE_STORE, useExisting: AXPLayoutDesignerPreviewViewerComponent },
3600
+ ], ngImport: i0, template: `
2734
3601
  <axp-widgets-container [context]="context" (onContextChanged)="handleContextChanged($event)">
2735
3602
  <ng-container axp-widget-renderer [node]="currentPage()" [mode]="'edit'"></ng-container>
2736
3603
  </axp-widgets-container>
@@ -2748,16 +3615,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2748
3615
  encapsulation: ViewEncapsulation.None,
2749
3616
  imports: [AXPWidgetCoreModule],
2750
3617
  changeDetection: ChangeDetectionStrategy.Eager,
2751
- providers: [AXUnsubscriber],
3618
+ providers: [
3619
+ AXUnsubscriber,
3620
+ { provide: AXP_LOCAL_DATA_SOURCE_STORE, useExisting: AXPLayoutDesignerPreviewViewerComponent },
3621
+ ],
2752
3622
  }]
2753
- }], ctorParameters: () => [] });
3623
+ }], ctorParameters: () => [], propDecorators: { initialDocument: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialDocument", required: false }] }], __class: [{
3624
+ type: HostBinding,
3625
+ args: ['class']
3626
+ }] } });
2754
3627
 
2755
3628
  class AXPLayoutDesignerPreviewFrameComponent {
2756
3629
  constructor() {
2757
3630
  this.size = input('xl', /* @ts-ignore */
2758
3631
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
2759
3632
  this.width = computed(() => {
2760
- ;
2761
3633
  switch (this.size()) {
2762
3634
  case 'sm':
2763
3635
  return '340px';
@@ -2765,39 +3637,34 @@ class AXPLayoutDesignerPreviewFrameComponent {
2765
3637
  case 'lg':
2766
3638
  return '768px';
2767
3639
  default:
2768
- return '100%';
3640
+ return '1280px';
2769
3641
  }
2770
3642
  }, /* @ts-ignore */
2771
3643
  ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
2772
3644
  }
2773
3645
  get __class() {
2774
- const cls = {};
2775
- cls['flex'] = true;
2776
- cls['flex-col'] = true;
2777
- cls['h-full'] = true;
2778
- cls['W-full'] = true;
2779
- cls['lg:p-20'] = true;
2780
- cls['bg-[#f3f3fe]'] = true;
2781
- cls['w-full'] = true;
2782
- cls['items-center'] = true;
2783
- cls['overflow-auto'] = true;
2784
- return cls;
3646
+ return 'lighter-surface flex flex-col h-full w-full items-center overflow-auto p-6';
2785
3647
  }
2786
3648
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLayoutDesignerPreviewFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2787
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: AXPLayoutDesignerPreviewFrameComponent, isStandalone: true, selector: "axp-layout-preview-frame", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.__class" } }, ngImport: i0, template: `
3649
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: AXPLayoutDesignerPreviewFrameComponent, isStandalone: true, selector: "axp-layout-preview-frame", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.__class" } }, providers: [
3650
+ {
3651
+ provide: AXP_LAYOUT_PROFILE,
3652
+ useFactory: () => {
3653
+ const frame = inject(AXPLayoutDesignerPreviewFrameComponent);
3654
+ return computed(() => ({
3655
+ size: designerPreviewSizeToLayoutSize(frame.size()),
3656
+ medium: 'screen',
3657
+ }));
3658
+ },
3659
+ },
3660
+ ], ngImport: i0, template: `
2788
3661
  <div
2789
- class="flex-1 lg:rounded-lg bg-white lg:shadow-md p-2"
2790
- [style.width]="width()"
2791
- style="max-width: 1280px;"
3662
+ class="lightest-surface min-h-full w-full shrink-0 rounded-lg shadow-md p-4"
3663
+ [style.max-width]="width()"
2792
3664
  >
2793
- <iframe
2794
- class="w-full h-full bg-transparent"
2795
- [frameBorder]="0"
2796
- [src]="'/forms/viewer' | safe : 'resourceUrl'"
2797
- >
2798
- </iframe>
3665
+ <axp-layout-builder-viewer></axp-layout-builder-viewer>
2799
3666
  </div>
2800
- `, isInline: true, dependencies: [{ kind: "pipe", type: AXSafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.Eager, encapsulation: i0.ViewEncapsulation.None }); }
3667
+ `, isInline: true, dependencies: [{ kind: "component", type: AXPLayoutDesignerPreviewViewerComponent, selector: "axp-layout-builder-viewer", inputs: ["initialDocument"] }], changeDetection: i0.ChangeDetectionStrategy.Eager, encapsulation: i0.ViewEncapsulation.None }); }
2801
3668
  }
2802
3669
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLayoutDesignerPreviewFrameComponent, decorators: [{
2803
3670
  type: Component,
@@ -2805,21 +3672,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2805
3672
  selector: 'axp-layout-preview-frame',
2806
3673
  template: `
2807
3674
  <div
2808
- class="flex-1 lg:rounded-lg bg-white lg:shadow-md p-2"
2809
- [style.width]="width()"
2810
- style="max-width: 1280px;"
3675
+ class="lightest-surface min-h-full w-full shrink-0 rounded-lg shadow-md p-4"
3676
+ [style.max-width]="width()"
2811
3677
  >
2812
- <iframe
2813
- class="w-full h-full bg-transparent"
2814
- [frameBorder]="0"
2815
- [src]="'/forms/viewer' | safe : 'resourceUrl'"
2816
- >
2817
- </iframe>
3678
+ <axp-layout-builder-viewer></axp-layout-builder-viewer>
2818
3679
  </div>
2819
3680
  `,
2820
3681
  encapsulation: ViewEncapsulation.None,
2821
3682
  changeDetection: ChangeDetectionStrategy.Eager,
2822
- imports: [AXSafePipe]
3683
+ imports: [AXPLayoutDesignerPreviewViewerComponent],
3684
+ providers: [
3685
+ {
3686
+ provide: AXP_LAYOUT_PROFILE,
3687
+ useFactory: () => {
3688
+ const frame = inject(AXPLayoutDesignerPreviewFrameComponent);
3689
+ return computed(() => ({
3690
+ size: designerPreviewSizeToLayoutSize(frame.size()),
3691
+ medium: 'screen',
3692
+ }));
3693
+ },
3694
+ },
3695
+ ],
2823
3696
  }]
2824
3697
  }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], __class: [{
2825
3698
  type: HostBinding,
@@ -2871,7 +3744,7 @@ class AXPLayoutDesignerPreviewComponent extends AXBasePageComponent {
2871
3744
  await this.settings.scope(AXPPlatformScope.User).set(this.jsonSettingsKey, !value);
2872
3745
  }
2873
3746
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLayoutDesignerPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2874
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPLayoutDesignerPreviewComponent, isStandalone: true, selector: "axp-layout-preview", usesInheritance: true, ngImport: i0, template: "<ax-drawer-container class=\"ax-dark h-full w-full flex relative overflow-hidden\">\n <ax-content>\n <div class=\"ax-dark flex flex-col h-full relative bg-default\">\n <div class=\"flex items-center justify-between h-12 bg-lightest shadow ax-sm px-2\">\n <div class=\"flex flex-1 w-16 justify-start\">\n <ax-button look=\"blank\" text=\"Back to Designer\" (onClick)=\"handleCloseButton()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-chevron-left\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\">\n <axp-designer-view-sizes [(size)]=\"size\"></axp-designer-view-sizes>\n </div>\n <div class=\"flex flex-1 w-16 justify-end\">\n @if(isDialogWindow) {\n <ax-button look=\"blank\" [title]=\"'Preview in to seperate window'\" (onClick)=\"handlePreviewWindow()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-window-restore\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }@else { }\n <ax-button look=\"blank\" (onClick)=\"jsonView.toggle()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-list-tree\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n\n <div class=\"bg-[#f3f3fe] flex flex-1 justify-center overflow-auto\">\n <axp-layout-preview-frame [size]=\"size()\"></axp-layout-preview-frame>\n </div>\n </div>\n </ax-content>\n <!-- [collapsed]=\"jsonViewCollapsed()\"\n (collapsedChange)=\"handleCollapsedChange($event)\" -->\n <ax-drawer class=\"w-1/3 border-s\" location=\"end\" mode=\"push\" #jsonView>\n <ax-content>\n <div class=\"ax-dark flex flex-col h-full relative bg-default\">\n <div class=\"flex items-center justify-between h-12 bg-lightest shadow ax-sm px-2\">\n <div class=\"flex flex-1 w-16 justify-start\">\n <span class=\"text-surface-fore font-semibold\">Form Data</span>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\"></div>\n <div class=\"flex flex-1 w-16 justify-end\">\n <ax-button look=\"blank\" (onClick)=\"handleCopyJson()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-copy\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n <div class=\"px-6 text-white\">\n <ax-json-viewer [json]=\"context()\"></ax-json-viewer>\n </div>\n </div>\n </ax-content>\n </ax-drawer>\n</ax-drawer-container>", styles: ["/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXButtonGroupModule }, { kind: "ngmodule", type: AXDrawerModule }, { kind: "component", type: i3.AXDrawerComponent, selector: "ax-drawer", inputs: ["location", "showBackdrop", "mode", "transition", "closeOnBackdropClick", "backdropClass", "collapsed", "singleOpenMode"], outputs: ["onBackdropClick", "collapsedStateChanged"] }, { kind: "component", type: i3.AXDrawerContainerComponent, selector: "ax-drawer-container" }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXJsonViewerModule }, { kind: "component", type: i4$1.JsonViewerComponent, selector: "ax-json-viewer", inputs: ["json"] }, { kind: "component", type: AXPLayoutDesignerPreviewFrameComponent, selector: "axp-layout-preview-frame", inputs: ["size"] }, { kind: "component", type: AXPDesignerViewSizeToolbarComponent, selector: "axp-designer-view-sizes", inputs: ["size"], outputs: ["sizeChange"] }], changeDetection: i0.ChangeDetectionStrategy.Eager, encapsulation: i0.ViewEncapsulation.None }); }
3747
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPLayoutDesignerPreviewComponent, isStandalone: true, selector: "axp-layout-preview", usesInheritance: true, ngImport: i0, template: "<ax-drawer-container class=\"ax-dark h-full w-full flex relative overflow-hidden\">\n <ax-content>\n <div class=\"ax-dark flex flex-col h-full relative\">\n <div class=\"flex items-center justify-between h-12 lightest-surface shadow ax-sm px-2\">\n <div class=\"flex flex-1 w-16 justify-start\">\n <ax-button look=\"blank\" [text]=\"'@layout-designer:preview.back' | translate | async\" (onClick)=\"handleCloseButton()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-chevron-left\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\">\n <axp-designer-view-sizes [(size)]=\"size\"></axp-designer-view-sizes>\n </div>\n <div class=\"flex flex-1 w-16 justify-end\">\n @if (isDialogWindow) {\n <ax-button look=\"blank\" [title]=\"'@layout-designer:keyboard-shortcuts.preview-window' | translate | async\" (onClick)=\"handlePreviewWindow()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-window-restore\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n <ax-button look=\"blank\" (onClick)=\"jsonView.toggle()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-list-tree\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n\n <div class=\"ax-light lighter-surface flex flex-1 justify-center overflow-auto min-h-0\">\n <axp-layout-preview-frame [size]=\"size()\"></axp-layout-preview-frame>\n </div>\n </div>\n </ax-content>\n <ax-drawer class=\"ax-dark w-1/3 border-s\" location=\"end\" mode=\"push\" #jsonView>\n <ax-content>\n <div class=\"ax-dark flex flex-col h-full relative\">\n <div class=\"flex items-center justify-between h-12 lightest-surface border-b shadow ax-sm px-2\">\n <div class=\"flex flex-1 w-16 justify-start\">\n <span class=\"font-semibold text-on-lightest\">{{ '@layout-designer:preview.form-data' | translate | async }}</span>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\"></div>\n <div class=\"flex flex-1 w-16 justify-end\">\n <ax-button look=\"blank\" (onClick)=\"handleCopyJson()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-copy\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n <div class=\"px-6\">\n <ax-json-viewer [json]=\"context()\"></ax-json-viewer>\n </div>\n </div>\n </ax-content>\n </ax-drawer>\n</ax-drawer-container>\n", styles: ["/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXButtonGroupModule }, { kind: "ngmodule", type: AXDrawerModule }, { kind: "component", type: i3.AXDrawerComponent, selector: "ax-drawer", inputs: ["location", "showBackdrop", "mode", "transition", "closeOnBackdropClick", "backdropClass", "collapsed", "singleOpenMode"], outputs: ["onBackdropClick", "collapsedStateChanged"] }, { kind: "component", type: i3.AXDrawerContainerComponent, selector: "ax-drawer-container" }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXJsonViewerModule }, { kind: "component", type: i4$2.JsonViewerComponent, selector: "ax-json-viewer", inputs: ["json"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXPLayoutDesignerPreviewFrameComponent, selector: "axp-layout-preview-frame", inputs: ["size"] }, { kind: "component", type: AXPDesignerViewSizeToolbarComponent, selector: "axp-designer-view-sizes", inputs: ["size"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager, encapsulation: i0.ViewEncapsulation.None }); }
2875
3748
  }
2876
3749
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLayoutDesignerPreviewComponent, decorators: [{
2877
3750
  type: Component,
@@ -2881,9 +3754,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2881
3754
  AXDrawerModule,
2882
3755
  AXDecoratorModule,
2883
3756
  AXJsonViewerModule,
3757
+ CommonModule,
2884
3758
  AXPLayoutDesignerPreviewFrameComponent,
2885
- AXPDesignerViewSizeToolbarComponent
2886
- ], template: "<ax-drawer-container class=\"ax-dark h-full w-full flex relative overflow-hidden\">\n <ax-content>\n <div class=\"ax-dark flex flex-col h-full relative bg-default\">\n <div class=\"flex items-center justify-between h-12 bg-lightest shadow ax-sm px-2\">\n <div class=\"flex flex-1 w-16 justify-start\">\n <ax-button look=\"blank\" text=\"Back to Designer\" (onClick)=\"handleCloseButton()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-chevron-left\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\">\n <axp-designer-view-sizes [(size)]=\"size\"></axp-designer-view-sizes>\n </div>\n <div class=\"flex flex-1 w-16 justify-end\">\n @if(isDialogWindow) {\n <ax-button look=\"blank\" [title]=\"'Preview in to seperate window'\" (onClick)=\"handlePreviewWindow()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-window-restore\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }@else { }\n <ax-button look=\"blank\" (onClick)=\"jsonView.toggle()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-list-tree\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n\n <div class=\"bg-[#f3f3fe] flex flex-1 justify-center overflow-auto\">\n <axp-layout-preview-frame [size]=\"size()\"></axp-layout-preview-frame>\n </div>\n </div>\n </ax-content>\n <!-- [collapsed]=\"jsonViewCollapsed()\"\n (collapsedChange)=\"handleCollapsedChange($event)\" -->\n <ax-drawer class=\"w-1/3 border-s\" location=\"end\" mode=\"push\" #jsonView>\n <ax-content>\n <div class=\"ax-dark flex flex-col h-full relative bg-default\">\n <div class=\"flex items-center justify-between h-12 bg-lightest shadow ax-sm px-2\">\n <div class=\"flex flex-1 w-16 justify-start\">\n <span class=\"text-surface-fore font-semibold\">Form Data</span>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\"></div>\n <div class=\"flex flex-1 w-16 justify-end\">\n <ax-button look=\"blank\" (onClick)=\"handleCopyJson()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-copy\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n <div class=\"px-6 text-white\">\n <ax-json-viewer [json]=\"context()\"></ax-json-viewer>\n </div>\n </div>\n </ax-content>\n </ax-drawer>\n</ax-drawer-container>", styles: ["/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
3759
+ AXPDesignerViewSizeToolbarComponent,
3760
+ AXTranslationModule,
3761
+ ], template: "<ax-drawer-container class=\"ax-dark h-full w-full flex relative overflow-hidden\">\n <ax-content>\n <div class=\"ax-dark flex flex-col h-full relative\">\n <div class=\"flex items-center justify-between h-12 lightest-surface shadow ax-sm px-2\">\n <div class=\"flex flex-1 w-16 justify-start\">\n <ax-button look=\"blank\" [text]=\"'@layout-designer:preview.back' | translate | async\" (onClick)=\"handleCloseButton()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-chevron-left\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\">\n <axp-designer-view-sizes [(size)]=\"size\"></axp-designer-view-sizes>\n </div>\n <div class=\"flex flex-1 w-16 justify-end\">\n @if (isDialogWindow) {\n <ax-button look=\"blank\" [title]=\"'@layout-designer:keyboard-shortcuts.preview-window' | translate | async\" (onClick)=\"handlePreviewWindow()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-window-restore\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n <ax-button look=\"blank\" (onClick)=\"jsonView.toggle()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-list-tree\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n\n <div class=\"ax-light lighter-surface flex flex-1 justify-center overflow-auto min-h-0\">\n <axp-layout-preview-frame [size]=\"size()\"></axp-layout-preview-frame>\n </div>\n </div>\n </ax-content>\n <ax-drawer class=\"ax-dark w-1/3 border-s\" location=\"end\" mode=\"push\" #jsonView>\n <ax-content>\n <div class=\"ax-dark flex flex-col h-full relative\">\n <div class=\"flex items-center justify-between h-12 lightest-surface border-b shadow ax-sm px-2\">\n <div class=\"flex flex-1 w-16 justify-start\">\n <span class=\"font-semibold text-on-lightest\">{{ '@layout-designer:preview.form-data' | translate | async }}</span>\n </div>\n <div class=\"flex flex-1 flex-row gap-2 ax-sm items-center justify-center\"></div>\n <div class=\"flex flex-1 w-16 justify-end\">\n <ax-button look=\"blank\" (onClick)=\"handleCopyJson()\">\n <ax-prefix>\n <ax-icon class=\"fa-regular fa-copy\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n <div class=\"px-6\">\n <ax-json-viewer [json]=\"context()\"></ax-json-viewer>\n </div>\n </div>\n </ax-content>\n </ax-drawer>\n</ax-drawer-container>\n", styles: ["/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
2887
3762
  }], ctorParameters: () => [] });
2888
3763
 
2889
3764
  var preview_component = /*#__PURE__*/Object.freeze({
@@ -2895,5 +3770,5 @@ var preview_component = /*#__PURE__*/Object.freeze({
2895
3770
  * Generated bundle index. Do not edit.
2896
3771
  */
2897
3772
 
2898
- export { AXPDesignerAddWidgetButtonComponent, AXPDesignerAddWidgetLineButtonComponent, AXPDesignerAddWidgetMiniButtonComponent, AXPDesignerConnector, AXPDesignerFlexDrawerComponent, AXPDesignerGridDrawerComponent, AXPDesignerMessageKeys, AXPDesignerModes, AXPDesignerService, AXPDesignerWidgetPickerComponent, AXPLayoutDesignerComponent, AXPLayoutDesignerHeaderMenuHookKeys, AXPLayoutDesignerPreviewComponent, AXPLayoutDesignerPreviewViewerComponent, AXPTemplateLoadingStrategy, AXPWidgetDesignerRendererDirective, AXPWidgetPickerService, AXP_DESIGNER_ADD_WIDGET_MENU, AXP_DESIGNER_CLONE_MENU, AXP_DESIGNER_COPY_MENU, AXP_DESIGNER_CUT_MENU, AXP_DESIGNER_DELETE_WIDGET_MENU, AXP_DESIGNER_PASTE_MENU, AXP_DESIGNER_SAVE_AS_WIDGET_MENU, AXP_DESIGNER_SELECT_WIDGET_MENU, AXP_WIDGET_PICKER_TAB_CONTEXT, cloneWidgetMenu };
3773
+ export { AXPDesignerAddWidgetButtonComponent, AXPDesignerAddWidgetLineButtonComponent, AXPDesignerAddWidgetMiniButtonComponent, AXPDesignerConnector, AXPDesignerFlexDrawerComponent, AXPDesignerGridDrawerComponent, AXPDesignerMessageKeys, AXPDesignerModes, AXPDesignerService, AXPDesignerWidgetPickerComponent, AXPLayoutDesignerComponent, AXPLayoutDesignerFooterHookKeys, AXPLayoutDesignerHeaderMenuHookKeys, AXPLayoutDesignerPreviewComponent, AXPLayoutDesignerPreviewViewerComponent, AXPTemplateLoadingStrategy, AXPWidgetDesignerRendererDirective, AXPWidgetPickerService, AXP_DESIGNER_ADD_WIDGET_MENU, AXP_DESIGNER_CLONE_MENU, AXP_DESIGNER_COPY_MENU, AXP_DESIGNER_CUT_MENU, AXP_DESIGNER_DELETE_WIDGET_MENU, AXP_DESIGNER_PASTE_MENU, AXP_DESIGNER_SAVE_AS_WIDGET_MENU, AXP_DESIGNER_SELECT_WIDGET_MENU, AXP_FORM_DESIGNER_SURFACE_PROFILE, AXP_PAGE_DESIGNER_SURFACE_PROFILE, AXP_WIDGET_DESIGNER_SURFACE_PROFILE, AXP_WIDGET_PICKER_TAB_CONTEXT, cloneWidgetMenu, designerSurfaceHasDrawer, widgetMatchesDesignerSurface };
2899
3774
  //# sourceMappingURL=acorex-platform-framework-client-layout-designer.mjs.map