@acorex/platform 20.7.10 → 20.7.12

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.
@@ -15,7 +15,7 @@ import { AXPWorkflowService } from '@acorex/platform/workflow';
15
15
  import * as i1 from '@angular/common';
16
16
  import { CommonModule, NgTemplateOutlet, AsyncPipe } from '@angular/common';
17
17
  import * as i0 from '@angular/core';
18
- import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, signal, effect, InjectionToken, computed, Injectable, Directive, viewChild, contentChild, ElementRef, output, afterNextRender, model, Injector, ViewContainerRef, runInInjectionContext, Optional, Inject, NgModule, linkedSignal, untracked, HostListener, ViewChildren, Input, EventEmitter, Output } from '@angular/core';
18
+ import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, signal, effect, InjectionToken, computed, Injectable, Directive, viewChild, contentChild, ElementRef, output, afterNextRender, model, Injector, ViewContainerRef, runInInjectionContext, Optional, Inject, NgModule, linkedSignal, untracked, HostListener, ViewChildren, DestroyRef, Input, EventEmitter, Output } from '@angular/core';
19
19
  import { AXAccordionCdkModule } from '@acorex/cdk/accordion';
20
20
  import { AXTagModule } from '@acorex/components/tag';
21
21
  import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals';
@@ -66,7 +66,6 @@ import { AXDialogService } from '@acorex/components/dialog';
66
66
  import { AXToastService } from '@acorex/components/toast';
67
67
  import { AXTreeViewComponent } from '@acorex/components/tree-view';
68
68
  import { AXPLayoutBuilderService, AXPLayoutRendererComponent } from '@acorex/platform/layout/builder';
69
- import { Subject, BehaviorSubject, timer, takeUntil } from 'rxjs';
70
69
  import * as i2$6 from '@acorex/components/image';
71
70
  import { AXImageModule } from '@acorex/components/image';
72
71
  import { AXPlatform } from '@acorex/core/platform';
@@ -75,6 +74,7 @@ import { AXAccordionModule } from '@acorex/components/accordion';
75
74
  import { AXButtonGroupModule } from '@acorex/components/button-group';
76
75
  import * as i5$3 from '@acorex/components/tabs';
77
76
  import { AXTabsModule } from '@acorex/components/tabs';
77
+ import { BehaviorSubject, timer, Subject, takeUntil } from 'rxjs';
78
78
  import { tap } from 'rxjs/operators';
79
79
  import * as i1$7 from '@acorex/components/avatar';
80
80
  import { AXAvatarModule } from '@acorex/components/avatar';
@@ -2900,7 +2900,26 @@ const AXP_MENU_CUSTOMIZER_SERVICE = new InjectionToken('AXP_MENU_CUSTOMIZER_SERV
2900
2900
  * and manage menu items using a tree-like interface.
2901
2901
  * Can be used standalone or within popups/dialogs.
2902
2902
  *
2903
- * Requires providing AXP_MENU_CUSTOMIZER_SERVICE with an implementation of AXPMenuCustomizerService.
2903
+ * @description
2904
+ * This component provides a visual interface for customizing menu structures.
2905
+ * It supports:
2906
+ * - Drag and drop reordering of menu items
2907
+ * - Showing/hiding menu items
2908
+ * - Adding custom menu items
2909
+ * - Editing menu item properties
2910
+ * - Resetting customizations to defaults
2911
+ *
2912
+ * @example
2913
+ * ```html
2914
+ * <axp-menu-customizer
2915
+ * [scopeKey]="'edition:my-edition'"
2916
+ * [showToolbar]="true"
2917
+ * [allowAddItems]="true"
2918
+ * (saved)="onSaved()"
2919
+ * />
2920
+ * ```
2921
+ *
2922
+ * @requires AXP_MENU_CUSTOMIZER_SERVICE - Must provide an implementation of AXPMenuCustomizerService
2904
2923
  */
2905
2924
  class AXPMenuCustomizerComponent {
2906
2925
  constructor() {
@@ -2910,6 +2929,7 @@ class AXPMenuCustomizerComponent {
2910
2929
  this.dialogService = inject(AXDialogService);
2911
2930
  this.toastService = inject(AXToastService);
2912
2931
  this.translationService = inject(AXTranslationService);
2932
+ this.destroyRef = inject(DestroyRef);
2913
2933
  //#endregion
2914
2934
  //#region ---- Inputs & Outputs ----
2915
2935
  /** The scope key for loading/saving menu customizations (e.g., 'edition:{id}', 'tenant:{id}') */
@@ -2918,9 +2938,9 @@ class AXPMenuCustomizerComponent {
2918
2938
  this.showToolbar = input(true, ...(ngDevMode ? [{ debugName: "showToolbar" }] : []));
2919
2939
  /** Whether to allow adding custom menu items */
2920
2940
  this.allowAddItems = input(true, ...(ngDevMode ? [{ debugName: "allowAddItems" }] : []));
2921
- /** Drag behavior for the tree view: 'none' | 'move' | 'copy' */
2941
+ /** Drag behavior for the tree view: 'none' | 'order-only' | 'move' | 'both' */
2922
2942
  this.dragBehavior = input('both', ...(ngDevMode ? [{ debugName: "dragBehavior" }] : []));
2923
- /** Drag area for the tree view: 'node' | 'handler' */
2943
+ /** Drag area for the tree view: 'handler' | 'item' */
2924
2944
  this.dragArea = input('handler', ...(ngDevMode ? [{ debugName: "dragArea" }] : []));
2925
2945
  /** Emitted when changes are saved */
2926
2946
  this.saved = output();
@@ -2928,88 +2948,136 @@ class AXPMenuCustomizerComponent {
2928
2948
  this.cancelled = output();
2929
2949
  //#endregion
2930
2950
  //#region ---- Component State ----
2951
+ /** Reference to the tree view component */
2931
2952
  this.tree = viewChild(AXTreeViewComponent, ...(ngDevMode ? [{ debugName: "tree" }] : []));
2953
+ /** Tree nodes data source */
2932
2954
  this.treeNodes = signal([], ...(ngDevMode ? [{ debugName: "treeNodes" }] : []));
2933
- this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2934
- this.error = signal('', ...(ngDevMode ? [{ debugName: "error" }] : []));
2955
+ /** Current loading/error state */
2956
+ this.state = signal({ status: 'idle' }, ...(ngDevMode ? [{ debugName: "state" }] : []));
2957
+ /** Flag to prevent concurrent sync operations */
2935
2958
  this.isSyncing = false;
2936
- this.destroy$ = new Subject();
2959
+ //#endregion
2960
+ //#region ---- Computed Properties ----
2961
+ /** Whether the component is currently loading */
2962
+ this.isLoading = computed(() => this.state().status === 'loading', ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2963
+ /** Whether there is an error state */
2964
+ this.hasError = computed(() => this.state().status === 'error', ...(ngDevMode ? [{ debugName: "hasError" }] : []));
2965
+ /** The current error message, if any */
2966
+ this.errorMessage = computed(() => {
2967
+ const currentState = this.state();
2968
+ return currentState.status === 'error' ? currentState.message : '';
2969
+ }, ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
2970
+ /** Whether the tree has nodes to display */
2971
+ this.hasNodes = computed(() => this.treeNodes().length > 0, ...(ngDevMode ? [{ debugName: "hasNodes" }] : []));
2972
+ /** Whether to show the content (tree view) */
2973
+ this.showContent = computed(() => !this.isLoading() && !this.hasError() && this.hasNodes(), ...(ngDevMode ? [{ debugName: "showContent" }] : []));
2974
+ /** Whether to show the empty state */
2975
+ this.showEmptyState = computed(() => !this.isLoading() && !this.hasError() && !this.hasNodes(), ...(ngDevMode ? [{ debugName: "showEmptyState" }] : []));
2976
+ this.eff = effect(() => {
2977
+ console.log('treeNodes', this.treeNodes());
2978
+ }, ...(ngDevMode ? [{ debugName: "eff" }] : []));
2979
+ this.eff2 = effect(() => {
2980
+ console.log('datasource', this.tree()?.datasource());
2981
+ }, ...(ngDevMode ? [{ debugName: "eff2" }] : []));
2937
2982
  }
2938
2983
  //#endregion
2939
2984
  //#region ---- Lifecycle ----
2940
2985
  async ngOnInit() {
2941
2986
  await this.loadMenuItems();
2942
2987
  }
2943
- ngOnDestroy() {
2944
- this.destroy$.next();
2945
- this.destroy$.complete();
2946
- }
2947
2988
  //#endregion
2948
2989
  //#region ---- Event Handlers ----
2949
2990
  /**
2950
- * Handle before drop validation
2991
+ * Handle before drop validation.
2992
+ * Prevents dropping into items with paths (leaf nodes that can't have children).
2993
+ *
2994
+ * @param event - The before drop event from tree view
2951
2995
  */
2952
- async handleBeforeDrop(e) {
2953
- const targetParent = e.currentParent?.['data'] || null;
2996
+ handleBeforeDrop(event) {
2997
+ const targetParent = event.currentParent?.['data'];
2954
2998
  // Prevent dropping into items with paths (leaf nodes)
2955
2999
  if (targetParent?.menuItem?.path) {
2956
- e.canceled = true;
2957
- const message = await this.translationService.translateAsync('@platform-management:menu-management.messages.error.move');
2958
- this.toastService.warning(message);
2959
- return;
3000
+ event.canceled = true;
3001
+ this.showWarningMessage('@platform-management:menu-management.messages.error.move');
2960
3002
  }
2961
3003
  }
2962
3004
  /**
2963
- * Handle after drop event to sync changes
2964
- * The event contains information about the moved node and its new position
3005
+ * Handle order change event (reordering within the same parent).
3006
+ *
3007
+ * @param event - The drop event from tree view
2965
3008
  */
2966
- async handleAfterDrop(event) {
2967
- await this.syncTreeChanges();
3009
+ async handleOrderChange(event) {
3010
+ console.log('Order changed:', {
3011
+ node: event.node?.['id'],
3012
+ previousIndex: event.previousIndex,
3013
+ currentIndex: event.currentIndex,
3014
+ parent: event.currentParent?.['id'] ?? 'root',
3015
+ });
3016
+ // Use setTimeout to ensure tree view has finished updating the datasource
3017
+ setTimeout(() => this.syncTreeChanges(), 0);
3018
+ }
3019
+ /**
3020
+ * Handle move change event (moving to a different parent).
3021
+ *
3022
+ * @param event - The drop event from tree view
3023
+ */
3024
+ async handleMoveChange(event) {
3025
+ console.log('Move changed:', {
3026
+ node: event.node?.['id'],
3027
+ previousParent: event.previousParent?.['id'] ?? 'root',
3028
+ currentParent: event.currentParent?.['id'] ?? 'root',
3029
+ currentIndex: event.currentIndex,
3030
+ });
3031
+ // Use setTimeout to ensure tree view has finished updating the datasource
3032
+ setTimeout(() => this.syncTreeChanges(), 0);
2968
3033
  }
2969
3034
  //#endregion
2970
3035
  //#region ---- Data Loading ----
2971
3036
  /**
2972
- * Load menu tree for current scope
3037
+ * Load menu tree for current scope.
3038
+ * Updates the component state to loading, then success or error.
3039
+ * Creates new array reference to ensure UI updates.
2973
3040
  */
2974
3041
  async loadMenuItems() {
2975
3042
  try {
2976
- this.isLoading.set(true);
2977
- this.error.set('');
3043
+ this.state.set({ status: 'loading' });
2978
3044
  const treeNodes = await this.menuCustomizerService.getMenuTree(this.scopeKey());
2979
- this.treeNodes.set(treeNodes);
3045
+ // Create new array reference to ensure tree view detects the change
3046
+ this.treeNodes.set([...treeNodes]);
3047
+ this.state.set({ status: 'success' });
2980
3048
  }
2981
3049
  catch (error) {
2982
- this.error.set(error instanceof Error ? error.message : 'Failed to load menu items');
2983
- const message = await this.translationService.translateAsync('@platform-management:menu-management.messages.error.load');
2984
- this.toastService.danger(message);
2985
- }
2986
- finally {
2987
- this.isLoading.set(false);
3050
+ const message = error instanceof Error ? error.message : 'Failed to load menu items';
3051
+ this.state.set({ status: 'error', message });
3052
+ this.showErrorMessage('@platform-management:menu-management.messages.error.load');
2988
3053
  }
2989
3054
  }
2990
3055
  /**
2991
- * Sync tree changes to backend
3056
+ * Sync tree changes to backend.
3057
+ * Prevents concurrent sync operations using the isSyncing flag.
3058
+ * Reloads data with new reference after successful sync.
2992
3059
  */
2993
3060
  async syncTreeChanges() {
2994
3061
  if (this.isSyncing) {
2995
3062
  return;
2996
3063
  }
3064
+ // Get nodes from the tree component's datasource directly
3065
+ // This ensures we get the most up-to-date structure after drag-drop
3066
+ const treeComponent = this.tree();
3067
+ const nodes = treeComponent?.getRootNodes() ?? this.treeNodes();
3068
+ if (!nodes || nodes.length === 0) {
3069
+ return;
3070
+ }
2997
3071
  try {
2998
3072
  this.isSyncing = true;
2999
- // Get nodes from signal - the tree component mutates the array in place
3000
- // so this.treeNodes() will have the updated structure after drag-drop
3001
- const nodes = this.treeNodes();
3002
- if (!nodes || nodes.length === 0) {
3003
- return;
3004
- }
3005
3073
  await this.menuCustomizerService.syncTreeChanges(this.scopeKey(), nodes);
3006
- const message = await this.translationService.translateAsync('@platform-management:menu-management.messages.success.reorder');
3007
- this.toastService.success(message);
3074
+ this.showSuccessMessage('@platform-management:menu-management.messages.success.reorder');
3075
+ // Reload with new array reference to ensure UI is in sync
3076
+ await this.loadMenuItems();
3008
3077
  }
3009
3078
  catch (error) {
3010
3079
  console.error('Failed to sync tree changes:', error);
3011
- const message = await this.translationService.translateAsync('@platform-management:menu-management.messages.error.sync');
3012
- this.toastService.danger(message);
3080
+ this.showErrorMessage('@platform-management:menu-management.messages.error.sync');
3013
3081
  }
3014
3082
  finally {
3015
3083
  this.isSyncing = false;
@@ -3018,72 +3086,77 @@ class AXPMenuCustomizerComponent {
3018
3086
  //#endregion
3019
3087
  //#region ---- Public Methods ----
3020
3088
  /**
3021
- * Collapse all tree nodes
3089
+ * Collapse all tree nodes.
3022
3090
  */
3023
3091
  collapseAll() {
3024
3092
  this.tree()?.collapseAll();
3025
3093
  }
3026
3094
  /**
3027
- * Expand all tree nodes
3095
+ * Expand all tree nodes.
3028
3096
  */
3029
- expandAll() {
3030
- this.tree()?.expandAll();
3097
+ async expandAll() {
3098
+ await this.tree()?.expandAll();
3031
3099
  }
3032
3100
  /**
3033
- * Reset all customizations
3101
+ * Reset all customizations to defaults.
3102
+ * Shows a confirmation dialog before resetting.
3034
3103
  */
3035
3104
  async resetCustomizations() {
3036
- const title = await this.translationService.translateAsync('@platform-management:menu-management.actions.reset.confirm.title');
3037
- const message = await this.translationService.translateAsync('@platform-management:menu-management.actions.reset.confirm.message');
3038
- const confirmed = await this.dialogService.confirm(title, message);
3105
+ const confirmed = await this.confirmAction('@platform-management:menu-management.actions.reset.confirm.title', '@platform-management:menu-management.actions.reset.confirm.message');
3039
3106
  if (!confirmed)
3040
3107
  return;
3041
3108
  try {
3042
3109
  await this.menuCustomizerService.resetCustomizations(this.scopeKey());
3043
- const successMessage = await this.translationService.translateAsync('@platform-management:menu-management.messages.success.reset');
3044
- this.toastService.success(successMessage);
3110
+ this.showSuccessMessage('@platform-management:menu-management.messages.success.reset');
3045
3111
  }
3046
3112
  catch (error) {
3047
3113
  console.error('Failed to reset customizations:', error);
3048
- const errorMessage = await this.translationService.translateAsync('@platform-management:menu-management.messages.error.reset');
3049
- this.toastService.danger(errorMessage);
3114
+ this.showErrorMessage('@platform-management:menu-management.messages.error.reset');
3050
3115
  }
3051
3116
  finally {
3052
3117
  await this.loadMenuItems();
3053
3118
  }
3054
3119
  }
3055
3120
  /**
3056
- * Add new root menu item
3121
+ * Add new root menu item.
3122
+ * Opens the menu item dialog without a parent.
3057
3123
  */
3058
3124
  async addRootMenuItem() {
3059
3125
  await this.showMenuItemDialog(null, null);
3060
3126
  }
3127
+ /**
3128
+ * Reload the menu items from the service.
3129
+ * Useful for external refresh triggers.
3130
+ */
3131
+ async refresh() {
3132
+ await this.loadMenuItems();
3133
+ }
3061
3134
  //#endregion
3062
3135
  //#region ---- Action Handlers ----
3063
3136
  /**
3064
- * Handle menu item action
3137
+ * Handle menu item action dispatched from the template.
3138
+ *
3139
+ * @param action - The action to perform
3140
+ * @param nodeData - The node data to perform the action on
3065
3141
  */
3066
3142
  async onAction(action, nodeData) {
3067
- switch (action) {
3068
- case 'show':
3069
- await this.showMenuItem(nodeData);
3070
- break;
3071
- case 'hide':
3072
- await this.hideMenuItem(nodeData);
3073
- break;
3074
- case 'edit':
3075
- await this.editMenuItem(nodeData);
3076
- break;
3077
- case 'delete':
3078
- await this.deleteMenuItem(nodeData);
3079
- break;
3080
- case 'add-child':
3081
- await this.addChildMenuItem(nodeData);
3082
- break;
3143
+ const actionHandlers = {
3144
+ show: () => this.showMenuItem(nodeData),
3145
+ hide: () => this.hideMenuItem(nodeData),
3146
+ edit: () => this.editMenuItem(nodeData),
3147
+ delete: () => this.deleteMenuItem(nodeData),
3148
+ 'add-child': () => this.addChildMenuItem(nodeData),
3149
+ move: () => Promise.resolve(), // Move is handled by drag-drop
3150
+ };
3151
+ const handler = actionHandlers[action];
3152
+ if (handler) {
3153
+ await handler();
3083
3154
  }
3084
3155
  }
3085
3156
  /**
3086
- * Add new child menu item
3157
+ * Add new child menu item to a parent.
3158
+ *
3159
+ * @param parentNodeData - The parent node data
3087
3160
  */
3088
3161
  async addChildMenuItem(parentNodeData) {
3089
3162
  await this.showMenuItemDialog(null, parentNodeData.menuItem.name ?? null);
@@ -3091,66 +3164,66 @@ class AXPMenuCustomizerComponent {
3091
3164
  //#endregion
3092
3165
  //#region ---- Menu Item Actions ----
3093
3166
  /**
3094
- * Show menu item
3167
+ * Show a hidden menu item.
3168
+ *
3169
+ * @param nodeData - The node data containing the menu item to show
3095
3170
  */
3096
3171
  async showMenuItem(nodeData) {
3097
- await this.executeMenuAction(() => this.menuCustomizerService.showMenuItem(this.scopeKey(), nodeData.menuItem.name), '@platform-management:menu-management.messages.success.show', '@platform-management:menu-management.messages.error.show');
3172
+ const menuName = nodeData.menuItem.name;
3173
+ if (!menuName)
3174
+ return;
3175
+ await this.executeMenuAction(() => this.menuCustomizerService.showMenuItem(this.scopeKey(), menuName), '@platform-management:menu-management.messages.success.show', '@platform-management:menu-management.messages.error.show');
3098
3176
  }
3099
3177
  /**
3100
- * Hide menu item
3178
+ * Hide a menu item.
3179
+ *
3180
+ * @param nodeData - The node data containing the menu item to hide
3101
3181
  */
3102
3182
  async hideMenuItem(nodeData) {
3103
- await this.executeMenuAction(() => this.menuCustomizerService.hideMenuItem(this.scopeKey(), nodeData.menuItem.name), '@platform-management:menu-management.messages.success.hide', '@platform-management:menu-management.messages.error.hide');
3183
+ const menuName = nodeData.menuItem.name;
3184
+ if (!menuName)
3185
+ return;
3186
+ await this.executeMenuAction(() => this.menuCustomizerService.hideMenuItem(this.scopeKey(), menuName), '@platform-management:menu-management.messages.success.hide', '@platform-management:menu-management.messages.error.hide');
3104
3187
  }
3105
3188
  /**
3106
- * Edit menu item
3189
+ * Edit a menu item.
3190
+ *
3191
+ * @param nodeData - The node data containing the menu item to edit
3107
3192
  */
3108
3193
  async editMenuItem(nodeData) {
3109
3194
  await this.showMenuItemDialog(nodeData);
3110
3195
  }
3111
3196
  /**
3112
- * Delete custom menu item
3197
+ * Delete a custom menu item.
3198
+ * Shows a confirmation dialog before deleting.
3199
+ *
3200
+ * @param nodeData - The node data containing the menu item to delete
3113
3201
  */
3114
3202
  async deleteMenuItem(nodeData) {
3115
- const title = await this.translationService.translateAsync('@general:actions.delete.title');
3116
- const message = await this.translationService.translateAsync('@platform-management:menu-management.actions.delete.confirm.message');
3117
- const confirmed = await this.dialogService.confirm(title, message);
3203
+ const item = nodeData.menuItem;
3204
+ if (!item.name)
3205
+ return;
3206
+ const confirmed = await this.confirmAction('@general:actions.delete.title', '@platform-management:menu-management.actions.delete.confirm.message');
3118
3207
  if (!confirmed)
3119
3208
  return;
3120
- try {
3121
- const item = nodeData.menuItem;
3122
- if (!item.name)
3123
- return;
3124
- await this.menuCustomizerService.deleteCustomMenuItem(this.scopeKey(), item.name);
3125
- const successMessage = await this.translationService.translateAsync('@platform-management:menu-management.messages.success.delete');
3126
- this.toastService.success(successMessage);
3127
- await this.loadMenuItems();
3128
- }
3129
- catch (error) {
3130
- const errorMessage = await this.translationService.translateAsync('@platform-management:menu-management.messages.error.delete');
3131
- this.toastService.danger(errorMessage);
3132
- }
3209
+ await this.executeMenuAction(() => this.menuCustomizerService.deleteCustomMenuItem(this.scopeKey(), item.name), '@platform-management:menu-management.messages.success.delete', '@platform-management:menu-management.messages.error.delete');
3133
3210
  }
3134
3211
  //#endregion
3135
3212
  //#region ---- Dialog Helpers ----
3136
3213
  /**
3137
- * Show menu item dialog (add/edit)
3214
+ * Show menu item dialog (add/edit).
3215
+ *
3216
+ * @param nodeData - The node data for editing, or null for adding
3217
+ * @param parentName - The parent menu name for adding child items
3138
3218
  */
3139
3219
  async showMenuItemDialog(nodeData, parentName = null) {
3140
3220
  const item = nodeData?.menuItem;
3141
3221
  const isEdit = !!item;
3222
+ const isBuiltIn = nodeData?.metadata?.isBuiltIn ?? false;
3142
3223
  const title = isEdit
3143
3224
  ? '@platform-management:menu-management.components.edit-dialog.title'
3144
3225
  : '@platform-management:menu-management.components.add-dialog.title';
3145
- const context = {
3146
- name: item?.name || '',
3147
- text: item?.text || '',
3148
- type: item?.type || 'menu',
3149
- icon: item?.icon || '',
3150
- path: item?.path || '',
3151
- priority: item?.priority || 0,
3152
- description: item?.description || '',
3153
- };
3226
+ const context = this.buildDialogContext(item);
3154
3227
  const dialogRef = await this.layoutBuilder
3155
3228
  .create()
3156
3229
  .dialog((dialog) => {
@@ -3158,48 +3231,7 @@ class AXPMenuCustomizerComponent {
3158
3231
  .setTitle(title)
3159
3232
  .setContext(context)
3160
3233
  .content((flex) => {
3161
- flex
3162
- .setDirection('column')
3163
- .formField('@platform-management:menu-management.fields.name', (field) => {
3164
- field.path('name');
3165
- field.textBox({
3166
- placeholder: '@platform-management:menu-management.fields.name',
3167
- validations: [{ rule: 'required' }],
3168
- disabled: isEdit && nodeData?.metadata?.isBuiltIn,
3169
- });
3170
- })
3171
- .formField('@platform-management:menu-management.fields.text', (field) => {
3172
- field.path('text');
3173
- field.textBox({
3174
- placeholder: '@platform-management:menu-management.fields.text',
3175
- validations: [{ rule: 'required' }],
3176
- });
3177
- })
3178
- .formField('@platform-management:menu-management.fields.icon', (field) => {
3179
- field.path('icon');
3180
- field.customWidget('icon-chooser', {});
3181
- })
3182
- .formField('@platform-management:menu-management.fields.path', (field) => {
3183
- field.path('path');
3184
- field.textBox({
3185
- placeholder: '@platform-management:menu-management.fields.path',
3186
- disabled: isEdit && nodeData?.metadata?.isBuiltIn,
3187
- });
3188
- })
3189
- .formField('@platform-management:menu-management.fields.priority', (field) => {
3190
- field.path('priority');
3191
- field.numberBox({
3192
- placeholder: '@platform-management:menu-management.fields.priority',
3193
- step: 10,
3194
- });
3195
- })
3196
- .formField('@platform-management:menu-management.fields.description', (field) => {
3197
- field.path('description');
3198
- field.largeTextBox({
3199
- placeholder: '@platform-management:menu-management.fields.description',
3200
- rows: 3,
3201
- });
3202
- });
3234
+ this.buildDialogFields(flex, isEdit, isBuiltIn);
3203
3235
  })
3204
3236
  .setActions((actions) => {
3205
3237
  actions.cancel('@general:actions.cancel.title').submit('@general:actions.save.title');
@@ -3213,60 +3245,189 @@ class AXPMenuCustomizerComponent {
3213
3245
  }
3214
3246
  const formData = dialogRef.context();
3215
3247
  try {
3216
- if (isEdit && item?.name) {
3217
- // Update existing item
3218
- if (nodeData?.metadata?.isCustom) {
3219
- await this.menuCustomizerService.updateCustomMenuItem(this.scopeKey(), item.name, formData);
3220
- }
3221
- else {
3222
- await this.menuCustomizerService.updateMenuProperties(this.scopeKey(), item.name, formData);
3223
- }
3248
+ await this.saveMenuItem(formData, nodeData, parentName, isEdit);
3249
+ this.showSuccessMessage('@platform-management:menu-management.messages.success.save');
3250
+ await this.loadMenuItems();
3251
+ dialogRef.close();
3252
+ }
3253
+ catch (error) {
3254
+ console.error('Failed to save menu item:', error);
3255
+ this.showErrorMessage('@platform-management:menu-management.messages.error.save');
3256
+ }
3257
+ }
3258
+ /**
3259
+ * Build the dialog context for add/edit dialog.
3260
+ *
3261
+ * @param item - The menu item to build context for, or undefined for new items
3262
+ */
3263
+ buildDialogContext(item) {
3264
+ return {
3265
+ name: item?.name ?? '',
3266
+ text: item?.text ?? '',
3267
+ type: item?.type ?? 'menu',
3268
+ icon: item?.icon ?? '',
3269
+ path: item?.path ?? '',
3270
+ description: item?.description ?? '',
3271
+ };
3272
+ }
3273
+ /**
3274
+ * Build the dialog form fields.
3275
+ * Note: Priority is not included as item order is controlled by drag-drop.
3276
+ *
3277
+ * @param flex - The flex layout builder
3278
+ * @param isEdit - Whether this is an edit operation
3279
+ * @param isBuiltIn - Whether the item is built-in
3280
+ */
3281
+ buildDialogFields(flex, isEdit, isBuiltIn) {
3282
+ const I18N_PREFIX = '@platform-management:menu-management.fields';
3283
+ flex
3284
+ .setDirection('column')
3285
+ .formField(`${I18N_PREFIX}.name`, (field) => {
3286
+ field.path('name');
3287
+ field.textBox({
3288
+ placeholder: `${I18N_PREFIX}.name`,
3289
+ validations: [{ rule: 'required' }],
3290
+ disabled: isEdit && isBuiltIn,
3291
+ });
3292
+ })
3293
+ .formField(`${I18N_PREFIX}.text`, (field) => {
3294
+ field.path('text');
3295
+ field.textBox({
3296
+ placeholder: `${I18N_PREFIX}.text`,
3297
+ validations: [{ rule: 'required' }],
3298
+ });
3299
+ })
3300
+ .formField(`${I18N_PREFIX}.icon`, (field) => {
3301
+ field.path('icon');
3302
+ field.customWidget('icon-chooser', {});
3303
+ })
3304
+ .formField(`${I18N_PREFIX}.path`, (field) => {
3305
+ field.path('path');
3306
+ field.textBox({
3307
+ placeholder: `${I18N_PREFIX}.path`,
3308
+ disabled: isEdit && isBuiltIn,
3309
+ });
3310
+ })
3311
+ .formField(`${I18N_PREFIX}.description`, (field) => {
3312
+ field.path('description');
3313
+ field.largeTextBox({
3314
+ placeholder: `${I18N_PREFIX}.description`,
3315
+ rows: 3,
3316
+ });
3317
+ });
3318
+ }
3319
+ /**
3320
+ * Save menu item (create or update).
3321
+ *
3322
+ * @param formData - The form data from the dialog
3323
+ * @param nodeData - The original node data (for edit) or null (for add)
3324
+ * @param parentName - The parent menu name (for add child)
3325
+ * @param isEdit - Whether this is an edit operation
3326
+ */
3327
+ async saveMenuItem(formData, nodeData, parentName, isEdit) {
3328
+ const scopeKey = this.scopeKey();
3329
+ if (isEdit && nodeData?.menuItem?.name) {
3330
+ const menuName = nodeData.menuItem.name;
3331
+ if (nodeData.metadata?.isCustom) {
3332
+ // For custom items, update the entire item (preserving priority from current position)
3333
+ const updatedItem = {
3334
+ ...nodeData.menuItem,
3335
+ text: formData.text,
3336
+ icon: formData.icon,
3337
+ path: formData.path,
3338
+ description: formData.description,
3339
+ };
3340
+ await this.menuCustomizerService.updateCustomMenuItem(scopeKey, menuName, updatedItem);
3224
3341
  }
3225
3342
  else {
3226
- // Add new custom item
3227
- const newItem = {
3228
- name: formData.name,
3343
+ // For built-in items, only update allowed properties (text, icon, description)
3344
+ const properties = {
3229
3345
  text: formData.text,
3230
3346
  icon: formData.icon,
3231
- path: formData.path,
3232
- priority: formData.priority,
3233
3347
  description: formData.description,
3234
3348
  };
3235
- await this.menuCustomizerService.addCustomMenuItem(this.scopeKey(), newItem, parentName);
3349
+ await this.menuCustomizerService.updateMenuProperties(scopeKey, menuName, properties);
3236
3350
  }
3237
- const successMessage = await this.translationService.translateAsync('@platform-management:menu-management.messages.success.save');
3238
- this.toastService.success(successMessage);
3239
- await this.loadMenuItems();
3240
- dialogRef.close();
3241
3351
  }
3242
- catch (error) {
3243
- const errorMessage = await this.translationService.translateAsync('@platform-management:menu-management.messages.error.save');
3244
- this.toastService.danger(errorMessage);
3352
+ else {
3353
+ // Add new custom item
3354
+ const newItem = {
3355
+ name: formData.name,
3356
+ text: formData.text,
3357
+ icon: formData.icon,
3358
+ path: formData.path,
3359
+ description: formData.description,
3360
+ };
3361
+ await this.menuCustomizerService.addCustomMenuItem(scopeKey, newItem, parentName);
3245
3362
  }
3246
3363
  }
3364
+ //#endregion
3365
+ //#region ---- Utility Methods ----
3247
3366
  /**
3248
- * Execute a menu action with common error handling
3367
+ * Execute a menu action with common error handling and reload.
3368
+ *
3369
+ * @param action - The action to execute
3370
+ * @param successMessageKey - The i18n key for success message
3371
+ * @param errorMessageKey - The i18n key for error message
3249
3372
  */
3250
3373
  async executeMenuAction(action, successMessageKey, errorMessageKey) {
3251
3374
  try {
3252
3375
  await action();
3253
- const message = await this.translationService.translateAsync(successMessageKey);
3254
- this.toastService.success(message);
3376
+ this.showSuccessMessage(successMessageKey);
3255
3377
  await this.loadMenuItems();
3256
3378
  }
3257
3379
  catch (error) {
3258
3380
  console.error('Menu action failed:', error);
3259
- const message = await this.translationService.translateAsync(errorMessageKey);
3260
- this.toastService.danger(message);
3381
+ this.showErrorMessage(errorMessageKey);
3261
3382
  }
3262
3383
  }
3384
+ /**
3385
+ * Show a confirmation dialog.
3386
+ *
3387
+ * @param titleKey - The i18n key for the dialog title
3388
+ * @param messageKey - The i18n key for the dialog message
3389
+ * @returns True if confirmed, false otherwise
3390
+ */
3391
+ async confirmAction(titleKey, messageKey) {
3392
+ const [title, message] = await Promise.all([
3393
+ this.translationService.translateAsync(titleKey),
3394
+ this.translationService.translateAsync(messageKey),
3395
+ ]);
3396
+ return (await this.dialogService.confirm(title, message)).result;
3397
+ }
3398
+ /**
3399
+ * Show a success toast message.
3400
+ *
3401
+ * @param messageKey - The i18n key for the message
3402
+ */
3403
+ async showSuccessMessage(messageKey) {
3404
+ const message = await this.translationService.translateAsync(messageKey);
3405
+ this.toastService.success(message);
3406
+ }
3407
+ /**
3408
+ * Show an error toast message.
3409
+ *
3410
+ * @param messageKey - The i18n key for the message
3411
+ */
3412
+ async showErrorMessage(messageKey) {
3413
+ const message = await this.translationService.translateAsync(messageKey);
3414
+ this.toastService.danger(message);
3415
+ }
3416
+ /**
3417
+ * Show a warning toast message.
3418
+ *
3419
+ * @param messageKey - The i18n key for the message
3420
+ */
3421
+ async showWarningMessage(messageKey) {
3422
+ const message = await this.translationService.translateAsync(messageKey);
3423
+ this.toastService.warning(message);
3424
+ }
3263
3425
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPMenuCustomizerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3264
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPMenuCustomizerComponent, isStandalone: true, selector: "axp-menu-customizer", inputs: { scopeKey: { classPropertyName: "scopeKey", publicName: "scopeKey", isSignal: true, isRequired: true, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, allowAddItems: { classPropertyName: "allowAddItems", publicName: "allowAddItems", isSignal: true, isRequired: false, transformFunction: null }, dragBehavior: { classPropertyName: "dragBehavior", publicName: "dragBehavior", isSignal: true, isRequired: false, transformFunction: null }, dragArea: { classPropertyName: "dragArea", publicName: "dragArea", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { saved: "saved", cancelled: "cancelled" }, host: { classAttribute: "axp-menu-customizer" }, viewQueries: [{ propertyName: "tree", first: true, predicate: AXTreeViewComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (isLoading()) {\n <!-- Loading State -->\n <axp-state-message\n mode=\"loading\"\n icon=\"fa-light fa-spinner-third fa-spin\"\n [title]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.loading.title' | translate | async)!\n \"\n [description]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.loading.description'\n | translate\n | async)!\n \"\n />\n} @else if (error()) {\n <!-- Error State -->\n <axp-state-message\n mode=\"error\"\n icon=\"fa-light fa-circle-exclamation\"\n [title]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.error.title' | translate | async)!\n \"\n [description]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.error.description' | translate | async)!\n \"\n >\n <ax-button\n slot=\"actions\"\n [text]=\"'@general:actions.retry.title' | translate | async\"\n [look]=\"'outline'\"\n [color]=\"'primary'\"\n (onClick)=\"loadMenuItems()\"\n >\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-button>\n </axp-state-message>\n} @else if (treeNodes().length === 0) {\n <!-- Empty State -->\n <axp-state-message\n mode=\"empty\"\n icon=\"fa-light fa-bars\"\n [title]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.no-data.title' | translate | async)!\n \"\n [description]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.no-data.description'\n | translate\n | async)!\n \"\n >\n @if (allowAddItems()) {\n <ax-button\n slot=\"actions\"\n [text]=\"'@platform-management:menu-management.actions.add-root.title' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n (onClick)=\"addRootMenuItem()\"\n >\n <i class=\"fa-light fa-plus\"></i>\n </ax-button>\n }\n </axp-state-message>\n} @else {\n <!-- Toolbar -->\n @if (showToolbar()) {\n <div class=\"axp-menu-customizer__toolbar\">\n @if (allowAddItems()) {\n <ax-button\n [text]=\"'@platform-management:menu-management.actions.add-root.title' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n (onClick)=\"addRootMenuItem()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button>\n }\n\n <div class=\"axp-menu-customizer__toolbar-spacer\"></div>\n\n <ax-button\n [text]=\"'@platform-management:menu-management.actions.collapse.title' | translate | async\"\n [look]=\"'outline'\"\n (onClick)=\"collapseAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-minus-square\"></i>\n </ax-prefix>\n </ax-button>\n\n <ax-button\n [text]=\"'@platform-management:menu-management.actions.expand.title' | translate | async\"\n [look]=\"'outline'\"\n (onClick)=\"expandAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus-square\"></i>\n </ax-prefix>\n </ax-button>\n\n <ax-button\n [text]=\"'@platform-management:menu-management.actions.reset.title' | translate | async\"\n [look]=\"'outline'\"\n [color]=\"'danger'\"\n (onClick)=\"resetCustomizations()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-prefix>\n </ax-button>\n </div>\n }\n\n <!-- Menu Tree Content -->\n <div class=\"axp-menu-customizer__container\">\n <div class=\"axp-menu-customizer__tree\">\n <ax-tree-view\n [(datasource)]=\"treeNodes\"\n [look]=\"'default'\"\n [nodeTemplate]=\"itemTemplate\"\n [selectMode]=\"'none'\"\n [showIcons]=\"false\"\n [showChildrenBadge]=\"false\"\n [dragBehavior]=\"dragBehavior()\"\n [dragArea]=\"dragArea()\"\n (onItemsChange)=\"handleAfterDrop($event)\"\n ></ax-tree-view>\n </div>\n </div>\n}\n\n<!-- Custom Item Template -->\n<ng-template #itemTemplate let-node=\"node\" let-level=\"level\">\n @let nodeData = node['data'];\n @let item = nodeData.menuItem;\n @let metadata = nodeData.metadata;\n <div\n class=\"axp-menu-customizer__item\"\n [class.axp-menu-customizer__item--hidden]=\"metadata.isHidden\"\n [class.axp-menu-customizer__item--custom]=\"metadata.isCustom\"\n [class.axp-menu-customizer__item--group]=\"item.type === 'group'\"\n >\n <!-- Icon (hidden for groups) -->\n @if (item.icon && item.type !== 'group') {\n <i class=\"axp-menu-customizer__item-icon\" [class]=\"item.icon\"></i>\n }\n\n <!-- Text or Info -->\n @if (item.type === 'group') {\n <!-- Group Heading -->\n <span class=\"axp-menu-customizer__item-group\">\n <i class=\"fa-light fa-layer-group\"></i>\n {{ item.text | translate | async }}\n </span>\n } @else if (item.type === 'break') {\n <!-- Divider -->\n <span class=\"axp-menu-customizer__item-divider\">\n <i class=\"fa-light fa-minus\"></i>\n {{ '@platform-management:menu-management.states.divider' | translate | async }}\n </span>\n } @else if (item.text) {\n <!-- Regular Menu Item -->\n <span class=\"axp-menu-customizer__item-text\">\n {{ item.text | translate | async }}\n </span>\n } @else if (item.path) {\n <!-- Path-only Item -->\n <span class=\"axp-menu-customizer__item-path\">\n <i class=\"fa-light fa-link\"></i>\n {{ item.path }}\n </span>\n }\n\n <!-- Menu Name/Key -->\n @if (item.name) {\n <code class=\"axp-menu-customizer__item-name\">{{ item.name }}</code>\n }\n\n <!-- Status Badges -->\n @if (metadata.isHidden) {\n <ax-badge\n [text]=\"('@platform-management:menu-management.states.hidden' | translate | async)!\"\n color=\"danger\"\n ></ax-badge>\n } @else if (metadata.isCustom) {\n <ax-badge\n [text]=\"('@platform-management:menu-management.states.custom' | translate | async)!\"\n color=\"success\"\n ></ax-badge>\n } @else if (metadata.isBuiltIn) {\n <ax-badge\n [text]=\"('@platform-management:menu-management.states.built-in' | translate | async)!\"\n color=\"primary\"\n ></ax-badge>\n }\n\n <!-- Actions Dropdown -->\n <ax-button class=\"axp-menu-customizer__item-actions\" [look]=\"'blank'\" [size]=\"'sm'\" [iconOnly]=\"true\">\n <ax-prefix>\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </ax-prefix>\n\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n <!-- Edit (only for items with names) -->\n @if (item.name) {\n <ax-button-item\n [text]=\"('@general:actions.edit.title' | translate | async)!\"\n (onClick)=\"onAction('edit', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-edit\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Show/Hide (only for items with names) -->\n @if (item.name && metadata.isBuiltIn) {\n @if (metadata.isHidden) {\n <ax-button-item\n [text]=\"('@platform-management:menu-management.actions.show.title' | translate | async)!\"\n (onClick)=\"onAction('show', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye\"></i>\n </ax-prefix>\n </ax-button-item>\n } @else {\n <ax-button-item\n [text]=\"('@platform-management:menu-management.actions.hide.title' | translate | async)!\"\n (onClick)=\"onAction('hide', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye-slash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n }\n\n @if (!item.path && (item.type === 'menu' || !item.type) && allowAddItems()) {\n <!-- Add Child -->\n <ax-button-item\n [text]=\"('@platform-management:menu-management.actions.add-child.title' | translate | async)!\"\n (onClick)=\"onAction('add-child', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Delete (only for custom items with names) -->\n @if (item.name && metadata.isCustom) {\n <ax-divider></ax-divider>\n <ax-button-item\n [color]=\"'danger'\"\n [text]=\"('@general:actions.delete.title' | translate | async)!\"\n (onClick)=\"onAction('delete', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-trash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Info message for items without names -->\n @if (!item.name) {\n <ax-divider></ax-divider>\n <div class=\"ax-p-2 ax-text-xs ax-text-neutral-500 ax-italic\">\n {{ '@platform-management:menu-management.messages.info.no-name' | translate | async }}\n </div>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n</ng-template>\n", styles: [".axp-menu-customizer{display:flex;height:100%;flex-direction:column}.axp-menu-customizer__toolbar{display:flex;align-items:center;gap:.5rem;padding:1rem;border-bottom-width:1px;border-style:solid;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1))}.axp-menu-customizer__toolbar-spacer{flex:1 1 0%}.axp-menu-customizer__container{display:flex;flex:1 1 0%;flex-direction:column;gap:1rem;overflow:auto;padding:1rem}.axp-menu-customizer__tree{display:flex;flex-direction:column;gap:.5rem}.axp-menu-customizer__item{display:flex;flex:1 1 0%;align-items:center;gap:1rem;border-radius:.375rem;padding:.5rem;border-width:1px;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.axp-menu-customizer__item:hover{background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}.axp-menu-customizer__item--hidden{opacity:.5}.axp-menu-customizer__item--custom{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-primary-500),var(--tw-border-opacity, 1))}.axp-menu-customizer__item--group{padding-top:.75rem;padding-bottom:.75rem}.axp-menu-customizer__item-icon{display:flex;align-items:center;justify-content:center;height:1.5rem;width:1.5rem;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-primary-500),var(--tw-text-opacity, 1))}.axp-menu-customizer__item-text{flex:1 1 0%;font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-menu-customizer__item-group,.axp-menu-customizer__item-divider,.axp-menu-customizer__item-path{display:flex;flex:1 1 0%;align-items:center;gap:.5rem;font-size:.75rem;line-height:1rem;font-style:italic;--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-divider i,.axp-menu-customizer__item-path i{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-name{display:flex;align-items:center;border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(64 64 64 / var(--tw-text-opacity, 1));font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-weight:400;border-width:1px;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));white-space:nowrap;-webkit-user-select:all;user-select:all}.axp-menu-customizer__item-actions{margin-left:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i1$1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1$1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.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: AXDropdownButtonModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i6.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look", "nodeTemplate", "idField", "titleField", "tooltipField", "iconField", "expandedField", "selectedField", "indeterminateField", "disabledField", "hiddenField", "childrenField", "childrenCountField", "dataField", "inheritDisabled", "expandOnDoubleClick", "doubleClickDuration", "tooltipDelay"], outputs: ["datasourceChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onNodeDoubleClick", "onNodeClick", "onSelectionChange", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "variant"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3426
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPMenuCustomizerComponent, isStandalone: true, selector: "axp-menu-customizer", inputs: { scopeKey: { classPropertyName: "scopeKey", publicName: "scopeKey", isSignal: true, isRequired: true, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, allowAddItems: { classPropertyName: "allowAddItems", publicName: "allowAddItems", isSignal: true, isRequired: false, transformFunction: null }, dragBehavior: { classPropertyName: "dragBehavior", publicName: "dragBehavior", isSignal: true, isRequired: false, transformFunction: null }, dragArea: { classPropertyName: "dragArea", publicName: "dragArea", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { saved: "saved", cancelled: "cancelled" }, host: { classAttribute: "axp-menu-customizer" }, viewQueries: [{ propertyName: "tree", first: true, predicate: AXTreeViewComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- i18n path constants -->\n@let i18nBase = '@platform-management:menu-management';\n@let i18nEmptyStates = i18nBase + '.components.menu-list.empty-states';\n@let i18nActions = i18nBase + '.actions';\n\n<!-- Loading State -->\n@if (isLoading()) {\n <axp-state-message\n mode=\"loading\"\n icon=\"fa-light fa-spinner-third fa-spin\"\n [title]=\"(i18nEmptyStates + '.loading.title' | translate | async)!\"\n [description]=\"(i18nEmptyStates + '.loading.description' | translate | async)!\"\n />\n}\n\n<!-- Error State -->\n@else if (hasError()) {\n <axp-state-message\n mode=\"error\"\n icon=\"fa-light fa-circle-exclamation\"\n [title]=\"(i18nEmptyStates + '.error.title' | translate | async)!\"\n [description]=\"(i18nEmptyStates + '.error.description' | translate | async)!\"\n >\n <ax-button\n slot=\"actions\"\n [text]=\"'@general:actions.retry.title' | translate | async\"\n look=\"outline\"\n color=\"primary\"\n (onClick)=\"loadMenuItems()\"\n >\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-button>\n </axp-state-message>\n}\n\n<!-- Empty State -->\n@else if (showEmptyState()) {\n <axp-state-message\n mode=\"empty\"\n icon=\"fa-light fa-bars\"\n [title]=\"(i18nEmptyStates + '.no-data.title' | translate | async)!\"\n [description]=\"(i18nEmptyStates + '.no-data.description' | translate | async)!\"\n >\n @if (allowAddItems()) {\n <ax-button\n slot=\"actions\"\n [text]=\"i18nActions + '.add-root.title' | translate | async\"\n look=\"solid\"\n color=\"primary\"\n (onClick)=\"addRootMenuItem()\"\n >\n <i class=\"fa-light fa-plus\"></i>\n </ax-button>\n }\n </axp-state-message>\n}\n\n<!-- Main Content -->\n@else if (showContent()) {\n <!-- Toolbar -->\n @if (showToolbar()) {\n <div class=\"axp-menu-customizer__toolbar\">\n @if (allowAddItems()) {\n <ax-button\n [text]=\"i18nActions + '.add-root.title' | translate | async\"\n look=\"solid\"\n color=\"primary\"\n (onClick)=\"addRootMenuItem()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button>\n }\n\n <div class=\"axp-menu-customizer__toolbar-spacer\"></div>\n <!-- \n <ax-button\n [text]=\"i18nActions + '.collapse.title' | translate | async\"\n look=\"outline\"\n (onClick)=\"collapseAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-minus-square\"></i>\n </ax-prefix>\n </ax-button>\n\n <ax-button\n [text]=\"i18nActions + '.expand.title' | translate | async\"\n look=\"outline\"\n (onClick)=\"expandAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus-square\"></i>\n </ax-prefix>\n </ax-button> -->\n\n <ax-button\n [text]=\"i18nActions + '.reset.title' | translate | async\"\n look=\"outline\"\n color=\"danger\"\n (onClick)=\"resetCustomizations()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-prefix>\n </ax-button>\n </div>\n }\n\n <!-- Menu Tree -->\n <div class=\"axp-menu-customizer__container\">\n <div class=\"axp-menu-customizer__tree\">\n <ax-tree-view\n [(datasource)]=\"treeNodes\"\n look=\"default\"\n [nodeTemplate]=\"itemTemplate\"\n selectMode=\"none\"\n [showIcons]=\"false\"\n [showChildrenBadge]=\"false\"\n [dragBehavior]=\"dragBehavior()\"\n [dragArea]=\"dragArea()\"\n (onBeforeDrop)=\"handleBeforeDrop($event)\"\n (onOrderChange)=\"handleOrderChange($event)\"\n (onMoveChange)=\"handleMoveChange($event)\"\n />\n </div>\n </div>\n}\n\n<!-- Custom Item Template -->\n<ng-template #itemTemplate let-node=\"node\" let-level=\"level\">\n @let nodeData = node['data'];\n @let item = nodeData.menuItem;\n @let metadata = nodeData.metadata;\n @let isGroup = item.type === 'group';\n @let isBreak = item.type === 'break';\n @let isMenu = item.type === 'menu' || !item.type;\n @let canAddChild = !item.path && isMenu && allowAddItems();\n @let canToggleVisibility = item.name && metadata.isBuiltIn;\n @let canDelete = item.name && metadata.isCustom;\n @let i18nStates = '@platform-management:menu-management.states';\n\n <div\n class=\"axp-menu-customizer__item\"\n [class.axp-menu-customizer__item--hidden]=\"metadata.isHidden\"\n [class.axp-menu-customizer__item--custom]=\"metadata.isCustom\"\n [class.axp-menu-customizer__item--group]=\"isGroup\"\n >\n <!-- Icon (hidden for groups) -->\n @if (item.icon && !isGroup) {\n <i class=\"axp-menu-customizer__item-icon\" [class]=\"item.icon\"></i>\n }\n\n <!-- Content based on item type -->\n @if (isGroup) {\n <span class=\"axp-menu-customizer__item-group\">\n <i class=\"fa-light fa-layer-group\"></i>\n {{ item.text | translate | async }}\n </span>\n } @else if (isBreak) {\n <span class=\"axp-menu-customizer__item-divider\">\n <i class=\"fa-light fa-minus\"></i>\n {{ i18nStates + '.divider' | translate | async }}\n </span>\n } @else if (item.text) {\n <span class=\"axp-menu-customizer__item-text\">\n {{ item.text | translate | async }}\n </span>\n } @else if (item.path) {\n <span class=\"axp-menu-customizer__item-path\">\n <i class=\"fa-light fa-link\"></i>\n {{ item.path }}\n </span>\n }\n\n <!-- Menu Name/Key -->\n @if (item.name) {\n <code class=\"axp-menu-customizer__item-name\">{{ item.name }}</code>\n }\n\n <!-- Status Badges -->\n @if (metadata.isHidden) {\n <ax-badge [text]=\"(i18nStates + '.hidden' | translate | async)!\" color=\"danger\" />\n } @else if (metadata.isCustom) {\n <ax-badge [text]=\"(i18nStates + '.custom' | translate | async)!\" color=\"success\" />\n } @else if (metadata.isBuiltIn) {\n <ax-badge [text]=\"(i18nStates + '.built-in' | translate | async)!\" color=\"primary\" />\n }\n\n <!-- Actions Dropdown -->\n <ax-button class=\"axp-menu-customizer__item-actions\" look=\"blank\" size=\"sm\" [iconOnly]=\"true\">\n <ax-prefix>\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </ax-prefix>\n\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n <!-- Edit (only for items with names) -->\n @if (item.name) {\n <ax-button-item\n [text]=\"('@general:actions.edit.title' | translate | async)!\"\n (onClick)=\"onAction('edit', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-edit\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Show/Hide (only for built-in items with names) -->\n @if (canToggleVisibility) {\n @if (metadata.isHidden) {\n <ax-button-item\n [text]=\"(i18nActions + '.show.title' | translate | async)!\"\n (onClick)=\"onAction('show', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye\"></i>\n </ax-prefix>\n </ax-button-item>\n } @else {\n <ax-button-item\n [text]=\"(i18nActions + '.hide.title' | translate | async)!\"\n (onClick)=\"onAction('hide', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye-slash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n }\n\n <!-- Add Child -->\n @if (canAddChild) {\n <ax-button-item\n [text]=\"(i18nActions + '.add-child.title' | translate | async)!\"\n (onClick)=\"onAction('add-child', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Delete (only for custom items with names) -->\n @if (canDelete) {\n <ax-divider />\n <ax-button-item\n color=\"danger\"\n [text]=\"('@general:actions.delete.title' | translate | async)!\"\n (onClick)=\"onAction('delete', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-trash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Info message for items without names -->\n @if (!item.name) {\n <ax-divider />\n <div class=\"ax-p-2 ax-text-xs ax-text-neutral-500 ax-italic\">\n {{ i18nBase + '.messages.info.no-name' | translate | async }}\n </div>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n</ng-template>\n", styles: [".axp-menu-customizer{display:flex;height:100%;min-height:0px;flex-direction:column}.axp-menu-customizer__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;padding:1rem;border-bottom-width:1px;border-style:solid;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));flex-shrink:0}.axp-menu-customizer__toolbar-spacer{min-width:1rem;flex:1 1 0%}.axp-menu-customizer__container{display:flex;flex-direction:column;gap:1rem;padding:1rem;min-height:0px;flex:1 1 0%;overflow:auto}.axp-menu-customizer__tree{display:flex;flex-direction:column;gap:.5rem}.axp-menu-customizer__tree ax-tree-view{flex:1 1 0%}.axp-menu-customizer__item{display:flex;flex:1 1 0%;align-items:center;gap:.75rem;border-radius:.375rem;padding:.5rem .75rem;border-width:1px;border-style:solid;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;animation-duration:.15s}.axp-menu-customizer__item:hover{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.axp-menu-customizer__item--hidden{opacity:.5}.axp-menu-customizer__item--hidden .axp-menu-customizer__item-text,.axp-menu-customizer__item--hidden .axp-menu-customizer__item-icon{text-decoration-line:line-through}.axp-menu-customizer__item--custom{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-primary-200),var(--tw-border-opacity, 1));background-color:rgba(var(--ax-sys-color-primary-50),.3)}.axp-menu-customizer__item--group{padding-top:.75rem;padding-bottom:.75rem;--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.axp-menu-customizer__item-icon{display:flex;align-items:center;justify-content:center;height:1.5rem;width:1.5rem;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-primary-600),var(--tw-text-opacity, 1));flex-shrink:0}.axp-menu-customizer__item-text{min-width:0px;flex:1 1 0%;font-size:.875rem;line-height:1.25rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axp-menu-customizer__item-group{display:flex;flex:1 1 0%;align-items:center;gap:.5rem;font-size:.75rem;line-height:1rem;text-transform:uppercase;font-style:italic;--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity, 1));letter-spacing:.025em}.axp-menu-customizer__item-group i{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-divider,.axp-menu-customizer__item-path{display:flex;flex:1 1 0%;align-items:center;gap:.5rem;font-size:.75rem;line-height:1rem;font-style:italic;--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity, 1));min-width:0px}.axp-menu-customizer__item-divider i,.axp-menu-customizer__item-path i{flex-shrink:0;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-name{display:inline-flex;align-items:center;border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1));font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-weight:400;border-width:1px;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));-webkit-user-select:all;user-select:all;max-width:12rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0}.axp-menu-customizer__item-actions{margin-left:auto;flex-shrink:0;opacity:.6;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;animation-duration:.15s}.axp-menu-customizer__item:hover .axp-menu-customizer__item-actions{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i1$1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1$1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.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: AXDropdownButtonModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i6.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look", "nodeTemplate", "idField", "titleField", "tooltipField", "iconField", "expandedField", "selectedField", "indeterminateField", "disabledField", "hiddenField", "childrenField", "childrenCountField", "dataField", "inheritDisabled", "expandOnDoubleClick", "doubleClickDuration", "tooltipDelay"], outputs: ["datasourceChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onNodeDoubleClick", "onNodeClick", "onSelectionChange", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "variant"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3265
3427
  }
3266
3428
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPMenuCustomizerComponent, decorators: [{
3267
3429
  type: Component,
3268
3430
  args: [{ selector: 'axp-menu-customizer', imports: [
3269
- CommonModule,
3270
3431
  AXButtonModule,
3271
3432
  AXDecoratorModule,
3272
3433
  AXDropdownButtonModule,
@@ -3275,7 +3436,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3275
3436
  AXTreeViewComponent,
3276
3437
  AXPStateMessageComponent,
3277
3438
  AXBadgeModule,
3278
- ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'axp-menu-customizer' }, template: "@if (isLoading()) {\n <!-- Loading State -->\n <axp-state-message\n mode=\"loading\"\n icon=\"fa-light fa-spinner-third fa-spin\"\n [title]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.loading.title' | translate | async)!\n \"\n [description]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.loading.description'\n | translate\n | async)!\n \"\n />\n} @else if (error()) {\n <!-- Error State -->\n <axp-state-message\n mode=\"error\"\n icon=\"fa-light fa-circle-exclamation\"\n [title]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.error.title' | translate | async)!\n \"\n [description]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.error.description' | translate | async)!\n \"\n >\n <ax-button\n slot=\"actions\"\n [text]=\"'@general:actions.retry.title' | translate | async\"\n [look]=\"'outline'\"\n [color]=\"'primary'\"\n (onClick)=\"loadMenuItems()\"\n >\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-button>\n </axp-state-message>\n} @else if (treeNodes().length === 0) {\n <!-- Empty State -->\n <axp-state-message\n mode=\"empty\"\n icon=\"fa-light fa-bars\"\n [title]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.no-data.title' | translate | async)!\n \"\n [description]=\"\n ('@platform-management:menu-management.components.menu-list.empty-states.no-data.description'\n | translate\n | async)!\n \"\n >\n @if (allowAddItems()) {\n <ax-button\n slot=\"actions\"\n [text]=\"'@platform-management:menu-management.actions.add-root.title' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n (onClick)=\"addRootMenuItem()\"\n >\n <i class=\"fa-light fa-plus\"></i>\n </ax-button>\n }\n </axp-state-message>\n} @else {\n <!-- Toolbar -->\n @if (showToolbar()) {\n <div class=\"axp-menu-customizer__toolbar\">\n @if (allowAddItems()) {\n <ax-button\n [text]=\"'@platform-management:menu-management.actions.add-root.title' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n (onClick)=\"addRootMenuItem()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button>\n }\n\n <div class=\"axp-menu-customizer__toolbar-spacer\"></div>\n\n <ax-button\n [text]=\"'@platform-management:menu-management.actions.collapse.title' | translate | async\"\n [look]=\"'outline'\"\n (onClick)=\"collapseAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-minus-square\"></i>\n </ax-prefix>\n </ax-button>\n\n <ax-button\n [text]=\"'@platform-management:menu-management.actions.expand.title' | translate | async\"\n [look]=\"'outline'\"\n (onClick)=\"expandAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus-square\"></i>\n </ax-prefix>\n </ax-button>\n\n <ax-button\n [text]=\"'@platform-management:menu-management.actions.reset.title' | translate | async\"\n [look]=\"'outline'\"\n [color]=\"'danger'\"\n (onClick)=\"resetCustomizations()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-prefix>\n </ax-button>\n </div>\n }\n\n <!-- Menu Tree Content -->\n <div class=\"axp-menu-customizer__container\">\n <div class=\"axp-menu-customizer__tree\">\n <ax-tree-view\n [(datasource)]=\"treeNodes\"\n [look]=\"'default'\"\n [nodeTemplate]=\"itemTemplate\"\n [selectMode]=\"'none'\"\n [showIcons]=\"false\"\n [showChildrenBadge]=\"false\"\n [dragBehavior]=\"dragBehavior()\"\n [dragArea]=\"dragArea()\"\n (onItemsChange)=\"handleAfterDrop($event)\"\n ></ax-tree-view>\n </div>\n </div>\n}\n\n<!-- Custom Item Template -->\n<ng-template #itemTemplate let-node=\"node\" let-level=\"level\">\n @let nodeData = node['data'];\n @let item = nodeData.menuItem;\n @let metadata = nodeData.metadata;\n <div\n class=\"axp-menu-customizer__item\"\n [class.axp-menu-customizer__item--hidden]=\"metadata.isHidden\"\n [class.axp-menu-customizer__item--custom]=\"metadata.isCustom\"\n [class.axp-menu-customizer__item--group]=\"item.type === 'group'\"\n >\n <!-- Icon (hidden for groups) -->\n @if (item.icon && item.type !== 'group') {\n <i class=\"axp-menu-customizer__item-icon\" [class]=\"item.icon\"></i>\n }\n\n <!-- Text or Info -->\n @if (item.type === 'group') {\n <!-- Group Heading -->\n <span class=\"axp-menu-customizer__item-group\">\n <i class=\"fa-light fa-layer-group\"></i>\n {{ item.text | translate | async }}\n </span>\n } @else if (item.type === 'break') {\n <!-- Divider -->\n <span class=\"axp-menu-customizer__item-divider\">\n <i class=\"fa-light fa-minus\"></i>\n {{ '@platform-management:menu-management.states.divider' | translate | async }}\n </span>\n } @else if (item.text) {\n <!-- Regular Menu Item -->\n <span class=\"axp-menu-customizer__item-text\">\n {{ item.text | translate | async }}\n </span>\n } @else if (item.path) {\n <!-- Path-only Item -->\n <span class=\"axp-menu-customizer__item-path\">\n <i class=\"fa-light fa-link\"></i>\n {{ item.path }}\n </span>\n }\n\n <!-- Menu Name/Key -->\n @if (item.name) {\n <code class=\"axp-menu-customizer__item-name\">{{ item.name }}</code>\n }\n\n <!-- Status Badges -->\n @if (metadata.isHidden) {\n <ax-badge\n [text]=\"('@platform-management:menu-management.states.hidden' | translate | async)!\"\n color=\"danger\"\n ></ax-badge>\n } @else if (metadata.isCustom) {\n <ax-badge\n [text]=\"('@platform-management:menu-management.states.custom' | translate | async)!\"\n color=\"success\"\n ></ax-badge>\n } @else if (metadata.isBuiltIn) {\n <ax-badge\n [text]=\"('@platform-management:menu-management.states.built-in' | translate | async)!\"\n color=\"primary\"\n ></ax-badge>\n }\n\n <!-- Actions Dropdown -->\n <ax-button class=\"axp-menu-customizer__item-actions\" [look]=\"'blank'\" [size]=\"'sm'\" [iconOnly]=\"true\">\n <ax-prefix>\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </ax-prefix>\n\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n <!-- Edit (only for items with names) -->\n @if (item.name) {\n <ax-button-item\n [text]=\"('@general:actions.edit.title' | translate | async)!\"\n (onClick)=\"onAction('edit', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-edit\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Show/Hide (only for items with names) -->\n @if (item.name && metadata.isBuiltIn) {\n @if (metadata.isHidden) {\n <ax-button-item\n [text]=\"('@platform-management:menu-management.actions.show.title' | translate | async)!\"\n (onClick)=\"onAction('show', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye\"></i>\n </ax-prefix>\n </ax-button-item>\n } @else {\n <ax-button-item\n [text]=\"('@platform-management:menu-management.actions.hide.title' | translate | async)!\"\n (onClick)=\"onAction('hide', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye-slash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n }\n\n @if (!item.path && (item.type === 'menu' || !item.type) && allowAddItems()) {\n <!-- Add Child -->\n <ax-button-item\n [text]=\"('@platform-management:menu-management.actions.add-child.title' | translate | async)!\"\n (onClick)=\"onAction('add-child', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Delete (only for custom items with names) -->\n @if (item.name && metadata.isCustom) {\n <ax-divider></ax-divider>\n <ax-button-item\n [color]=\"'danger'\"\n [text]=\"('@general:actions.delete.title' | translate | async)!\"\n (onClick)=\"onAction('delete', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-trash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Info message for items without names -->\n @if (!item.name) {\n <ax-divider></ax-divider>\n <div class=\"ax-p-2 ax-text-xs ax-text-neutral-500 ax-italic\">\n {{ '@platform-management:menu-management.messages.info.no-name' | translate | async }}\n </div>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n</ng-template>\n", styles: [".axp-menu-customizer{display:flex;height:100%;flex-direction:column}.axp-menu-customizer__toolbar{display:flex;align-items:center;gap:.5rem;padding:1rem;border-bottom-width:1px;border-style:solid;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1))}.axp-menu-customizer__toolbar-spacer{flex:1 1 0%}.axp-menu-customizer__container{display:flex;flex:1 1 0%;flex-direction:column;gap:1rem;overflow:auto;padding:1rem}.axp-menu-customizer__tree{display:flex;flex-direction:column;gap:.5rem}.axp-menu-customizer__item{display:flex;flex:1 1 0%;align-items:center;gap:1rem;border-radius:.375rem;padding:.5rem;border-width:1px;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.axp-menu-customizer__item:hover{background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}.axp-menu-customizer__item--hidden{opacity:.5}.axp-menu-customizer__item--custom{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-primary-500),var(--tw-border-opacity, 1))}.axp-menu-customizer__item--group{padding-top:.75rem;padding-bottom:.75rem}.axp-menu-customizer__item-icon{display:flex;align-items:center;justify-content:center;height:1.5rem;width:1.5rem;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-primary-500),var(--tw-text-opacity, 1))}.axp-menu-customizer__item-text{flex:1 1 0%;font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-menu-customizer__item-group,.axp-menu-customizer__item-divider,.axp-menu-customizer__item-path{display:flex;flex:1 1 0%;align-items:center;gap:.5rem;font-size:.75rem;line-height:1rem;font-style:italic;--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-divider i,.axp-menu-customizer__item-path i{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-name{display:flex;align-items:center;border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(64 64 64 / var(--tw-text-opacity, 1));font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-weight:400;border-width:1px;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));white-space:nowrap;-webkit-user-select:all;user-select:all}.axp-menu-customizer__item-actions{margin-left:.5rem}\n"] }]
3439
+ AsyncPipe,
3440
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'axp-menu-customizer' }, template: "<!-- i18n path constants -->\n@let i18nBase = '@platform-management:menu-management';\n@let i18nEmptyStates = i18nBase + '.components.menu-list.empty-states';\n@let i18nActions = i18nBase + '.actions';\n\n<!-- Loading State -->\n@if (isLoading()) {\n <axp-state-message\n mode=\"loading\"\n icon=\"fa-light fa-spinner-third fa-spin\"\n [title]=\"(i18nEmptyStates + '.loading.title' | translate | async)!\"\n [description]=\"(i18nEmptyStates + '.loading.description' | translate | async)!\"\n />\n}\n\n<!-- Error State -->\n@else if (hasError()) {\n <axp-state-message\n mode=\"error\"\n icon=\"fa-light fa-circle-exclamation\"\n [title]=\"(i18nEmptyStates + '.error.title' | translate | async)!\"\n [description]=\"(i18nEmptyStates + '.error.description' | translate | async)!\"\n >\n <ax-button\n slot=\"actions\"\n [text]=\"'@general:actions.retry.title' | translate | async\"\n look=\"outline\"\n color=\"primary\"\n (onClick)=\"loadMenuItems()\"\n >\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-button>\n </axp-state-message>\n}\n\n<!-- Empty State -->\n@else if (showEmptyState()) {\n <axp-state-message\n mode=\"empty\"\n icon=\"fa-light fa-bars\"\n [title]=\"(i18nEmptyStates + '.no-data.title' | translate | async)!\"\n [description]=\"(i18nEmptyStates + '.no-data.description' | translate | async)!\"\n >\n @if (allowAddItems()) {\n <ax-button\n slot=\"actions\"\n [text]=\"i18nActions + '.add-root.title' | translate | async\"\n look=\"solid\"\n color=\"primary\"\n (onClick)=\"addRootMenuItem()\"\n >\n <i class=\"fa-light fa-plus\"></i>\n </ax-button>\n }\n </axp-state-message>\n}\n\n<!-- Main Content -->\n@else if (showContent()) {\n <!-- Toolbar -->\n @if (showToolbar()) {\n <div class=\"axp-menu-customizer__toolbar\">\n @if (allowAddItems()) {\n <ax-button\n [text]=\"i18nActions + '.add-root.title' | translate | async\"\n look=\"solid\"\n color=\"primary\"\n (onClick)=\"addRootMenuItem()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button>\n }\n\n <div class=\"axp-menu-customizer__toolbar-spacer\"></div>\n <!-- \n <ax-button\n [text]=\"i18nActions + '.collapse.title' | translate | async\"\n look=\"outline\"\n (onClick)=\"collapseAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-minus-square\"></i>\n </ax-prefix>\n </ax-button>\n\n <ax-button\n [text]=\"i18nActions + '.expand.title' | translate | async\"\n look=\"outline\"\n (onClick)=\"expandAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus-square\"></i>\n </ax-prefix>\n </ax-button> -->\n\n <ax-button\n [text]=\"i18nActions + '.reset.title' | translate | async\"\n look=\"outline\"\n color=\"danger\"\n (onClick)=\"resetCustomizations()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-prefix>\n </ax-button>\n </div>\n }\n\n <!-- Menu Tree -->\n <div class=\"axp-menu-customizer__container\">\n <div class=\"axp-menu-customizer__tree\">\n <ax-tree-view\n [(datasource)]=\"treeNodes\"\n look=\"default\"\n [nodeTemplate]=\"itemTemplate\"\n selectMode=\"none\"\n [showIcons]=\"false\"\n [showChildrenBadge]=\"false\"\n [dragBehavior]=\"dragBehavior()\"\n [dragArea]=\"dragArea()\"\n (onBeforeDrop)=\"handleBeforeDrop($event)\"\n (onOrderChange)=\"handleOrderChange($event)\"\n (onMoveChange)=\"handleMoveChange($event)\"\n />\n </div>\n </div>\n}\n\n<!-- Custom Item Template -->\n<ng-template #itemTemplate let-node=\"node\" let-level=\"level\">\n @let nodeData = node['data'];\n @let item = nodeData.menuItem;\n @let metadata = nodeData.metadata;\n @let isGroup = item.type === 'group';\n @let isBreak = item.type === 'break';\n @let isMenu = item.type === 'menu' || !item.type;\n @let canAddChild = !item.path && isMenu && allowAddItems();\n @let canToggleVisibility = item.name && metadata.isBuiltIn;\n @let canDelete = item.name && metadata.isCustom;\n @let i18nStates = '@platform-management:menu-management.states';\n\n <div\n class=\"axp-menu-customizer__item\"\n [class.axp-menu-customizer__item--hidden]=\"metadata.isHidden\"\n [class.axp-menu-customizer__item--custom]=\"metadata.isCustom\"\n [class.axp-menu-customizer__item--group]=\"isGroup\"\n >\n <!-- Icon (hidden for groups) -->\n @if (item.icon && !isGroup) {\n <i class=\"axp-menu-customizer__item-icon\" [class]=\"item.icon\"></i>\n }\n\n <!-- Content based on item type -->\n @if (isGroup) {\n <span class=\"axp-menu-customizer__item-group\">\n <i class=\"fa-light fa-layer-group\"></i>\n {{ item.text | translate | async }}\n </span>\n } @else if (isBreak) {\n <span class=\"axp-menu-customizer__item-divider\">\n <i class=\"fa-light fa-minus\"></i>\n {{ i18nStates + '.divider' | translate | async }}\n </span>\n } @else if (item.text) {\n <span class=\"axp-menu-customizer__item-text\">\n {{ item.text | translate | async }}\n </span>\n } @else if (item.path) {\n <span class=\"axp-menu-customizer__item-path\">\n <i class=\"fa-light fa-link\"></i>\n {{ item.path }}\n </span>\n }\n\n <!-- Menu Name/Key -->\n @if (item.name) {\n <code class=\"axp-menu-customizer__item-name\">{{ item.name }}</code>\n }\n\n <!-- Status Badges -->\n @if (metadata.isHidden) {\n <ax-badge [text]=\"(i18nStates + '.hidden' | translate | async)!\" color=\"danger\" />\n } @else if (metadata.isCustom) {\n <ax-badge [text]=\"(i18nStates + '.custom' | translate | async)!\" color=\"success\" />\n } @else if (metadata.isBuiltIn) {\n <ax-badge [text]=\"(i18nStates + '.built-in' | translate | async)!\" color=\"primary\" />\n }\n\n <!-- Actions Dropdown -->\n <ax-button class=\"axp-menu-customizer__item-actions\" look=\"blank\" size=\"sm\" [iconOnly]=\"true\">\n <ax-prefix>\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </ax-prefix>\n\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n <!-- Edit (only for items with names) -->\n @if (item.name) {\n <ax-button-item\n [text]=\"('@general:actions.edit.title' | translate | async)!\"\n (onClick)=\"onAction('edit', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-edit\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Show/Hide (only for built-in items with names) -->\n @if (canToggleVisibility) {\n @if (metadata.isHidden) {\n <ax-button-item\n [text]=\"(i18nActions + '.show.title' | translate | async)!\"\n (onClick)=\"onAction('show', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye\"></i>\n </ax-prefix>\n </ax-button-item>\n } @else {\n <ax-button-item\n [text]=\"(i18nActions + '.hide.title' | translate | async)!\"\n (onClick)=\"onAction('hide', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye-slash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n }\n\n <!-- Add Child -->\n @if (canAddChild) {\n <ax-button-item\n [text]=\"(i18nActions + '.add-child.title' | translate | async)!\"\n (onClick)=\"onAction('add-child', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Delete (only for custom items with names) -->\n @if (canDelete) {\n <ax-divider />\n <ax-button-item\n color=\"danger\"\n [text]=\"('@general:actions.delete.title' | translate | async)!\"\n (onClick)=\"onAction('delete', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-trash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Info message for items without names -->\n @if (!item.name) {\n <ax-divider />\n <div class=\"ax-p-2 ax-text-xs ax-text-neutral-500 ax-italic\">\n {{ i18nBase + '.messages.info.no-name' | translate | async }}\n </div>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n</ng-template>\n", styles: [".axp-menu-customizer{display:flex;height:100%;min-height:0px;flex-direction:column}.axp-menu-customizer__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;padding:1rem;border-bottom-width:1px;border-style:solid;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));flex-shrink:0}.axp-menu-customizer__toolbar-spacer{min-width:1rem;flex:1 1 0%}.axp-menu-customizer__container{display:flex;flex-direction:column;gap:1rem;padding:1rem;min-height:0px;flex:1 1 0%;overflow:auto}.axp-menu-customizer__tree{display:flex;flex-direction:column;gap:.5rem}.axp-menu-customizer__tree ax-tree-view{flex:1 1 0%}.axp-menu-customizer__item{display:flex;flex:1 1 0%;align-items:center;gap:.75rem;border-radius:.375rem;padding:.5rem .75rem;border-width:1px;border-style:solid;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;animation-duration:.15s}.axp-menu-customizer__item:hover{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.axp-menu-customizer__item--hidden{opacity:.5}.axp-menu-customizer__item--hidden .axp-menu-customizer__item-text,.axp-menu-customizer__item--hidden .axp-menu-customizer__item-icon{text-decoration-line:line-through}.axp-menu-customizer__item--custom{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-primary-200),var(--tw-border-opacity, 1));background-color:rgba(var(--ax-sys-color-primary-50),.3)}.axp-menu-customizer__item--group{padding-top:.75rem;padding-bottom:.75rem;--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.axp-menu-customizer__item-icon{display:flex;align-items:center;justify-content:center;height:1.5rem;width:1.5rem;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-primary-600),var(--tw-text-opacity, 1));flex-shrink:0}.axp-menu-customizer__item-text{min-width:0px;flex:1 1 0%;font-size:.875rem;line-height:1.25rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axp-menu-customizer__item-group{display:flex;flex:1 1 0%;align-items:center;gap:.5rem;font-size:.75rem;line-height:1rem;text-transform:uppercase;font-style:italic;--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity, 1));letter-spacing:.025em}.axp-menu-customizer__item-group i{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-divider,.axp-menu-customizer__item-path{display:flex;flex:1 1 0%;align-items:center;gap:.5rem;font-size:.75rem;line-height:1rem;font-style:italic;--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity, 1));min-width:0px}.axp-menu-customizer__item-divider i,.axp-menu-customizer__item-path i{flex-shrink:0;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-name{display:inline-flex;align-items:center;border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1));font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-weight:400;border-width:1px;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));-webkit-user-select:all;user-select:all;max-width:12rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0}.axp-menu-customizer__item-actions{margin-left:auto;flex-shrink:0;opacity:.6;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;animation-duration:.15s}.axp-menu-customizer__item:hover .axp-menu-customizer__item-actions{opacity:1}\n"] }]
3279
3441
  }], propDecorators: { scopeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "scopeKey", required: true }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], allowAddItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowAddItems", required: false }] }], dragBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragBehavior", required: false }] }], dragArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragArea", required: false }] }], saved: [{ type: i0.Output, args: ["saved"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], tree: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXTreeViewComponent), { isSignal: true }] }] } });
3280
3442
 
3281
3443
  //#region ---- Component Definition ----