@acorex/platform 20.6.0-next.17 → 20.6.0-next.19

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.
@@ -46,8 +46,15 @@ import { AXPDeviceService } from '@acorex/platform/core';
46
46
  import { AXPSettingService, AXPCommonSettings } from '@acorex/platform/common';
47
47
  import { isEqual } from 'lodash-es';
48
48
 
49
+ //#region ---- Constants ----
50
+ const ROOT_NODE_ID = 'all';
51
+ const LOADING_DELAY_MS = 300;
52
+ const DEFAULT_TEXT_FIELD = 'title';
53
+ const DEFAULT_VALUE_FIELD = 'id';
54
+ //#endregion
49
55
  class AXPEntityCategoryComponent {
50
56
  //#endregion
57
+ //#region ---- Constructor & Lifecycle ----
51
58
  constructor() {
52
59
  //#region ---- Services & Dependencies ----
53
60
  this.entityResolver = inject(AXPEntityResolver);
@@ -59,19 +66,31 @@ class AXPEntityCategoryComponent {
59
66
  this.vm = input.required(...(ngDevMode ? [{ debugName: "vm" }] : []));
60
67
  this.tree = viewChild('tree', ...(ngDevMode ? [{ debugName: "tree" }] : []));
61
68
  this.searchValue = input('', ...(ngDevMode ? [{ debugName: "searchValue" }] : []));
69
+ // Tree view inputs with defaults
70
+ this.selectMode = input('single', ...(ngDevMode ? [{ debugName: "selectMode" }] : []));
71
+ this.showCheckbox = input(false, ...(ngDevMode ? [{ debugName: "showCheckbox" }] : []));
72
+ this.selectionBehavior = input('intermediate', ...(ngDevMode ? [{ debugName: "selectionBehavior" }] : []));
73
+ this.dragArea = input('handler', ...(ngDevMode ? [{ debugName: "dragArea" }] : [])); //TODO FIX ACOREX
74
+ this.dragBehavior = input('none', ...(ngDevMode ? [{ debugName: "dragBehavior" }] : []));
75
+ this.showIcons = input(true, ...(ngDevMode ? [{ debugName: "showIcons" }] : []));
76
+ this.showChildrenBadge = input(false, ...(ngDevMode ? [{ debugName: "showChildrenBadge" }] : []));
77
+ this.expandedIcon = input('fa-solid fa-chevron-down', ...(ngDevMode ? [{ debugName: "expandedIcon" }] : []));
78
+ this.collapsedIcon = input('fa-solid fa-chevron-right', ...(ngDevMode ? [{ debugName: "collapsedIcon" }] : []));
79
+ this.indentSize = input(16, ...(ngDevMode ? [{ debugName: "indentSize" }] : []));
80
+ this.look = input('default', ...(ngDevMode ? [{ debugName: "look" }] : []));
62
81
  //#endregion
63
82
  //#region ---- Component State ----
64
83
  this.isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
65
84
  //#endregion
66
85
  //#region ---- Private Properties ----
67
86
  this.loadingTimeoutId = null;
68
- this.LOADING_DELAY = 300; // Show skeleton only if loading takes more than 300ms
69
87
  this.treeData = null;
70
88
  this.treeConfig = null;
71
89
  this.searchResults = null;
72
- // Computed values for field names to avoid repeated calculations
73
- this.textField = computed(() => this.vm().entityDef.category?.textField || 'title', ...(ngDevMode ? [{ debugName: "textField" }] : []));
74
- this.valueField = computed(() => this.vm().entityDef.category?.valueField || 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
90
+ //#endregion
91
+ //#region ---- Computed Properties ----
92
+ this.textField = computed(() => this.vm().entityDef.category?.textField || DEFAULT_TEXT_FIELD, ...(ngDevMode ? [{ debugName: "textField" }] : []));
93
+ this.valueField = computed(() => this.vm().entityDef.category?.valueField || DEFAULT_VALUE_FIELD, ...(ngDevMode ? [{ debugName: "valueField" }] : []));
75
94
  this.categoryEntityKey = computed(() => {
76
95
  const key = this.vm().entityDef.category?.entity;
77
96
  if (!key) {
@@ -79,57 +98,45 @@ class AXPEntityCategoryComponent {
79
98
  }
80
99
  return key;
81
100
  }, ...(ngDevMode ? [{ debugName: "categoryEntityKey" }] : []));
82
- //#region ---- Utility Methods ----
83
- /** Datasource callback for tree-view component. */
84
- this.datasource = async (id) => {
85
- if (!this.treeData || !this.treeConfig) {
101
+ //#endregion
102
+ //#region ---- Tree Data Source ----
103
+ /**
104
+ * Datasource callback for tree-view component.
105
+ * Provides lazy loading support for tree nodes.
106
+ */
107
+ this.datasource = async (parentId) => {
108
+ if (!this.isTreeInitialized()) {
86
109
  return [];
87
110
  }
88
- // If no id provided, return root nodes (or search results if available)
89
- if (!id) {
90
- // If we have search results, use them
91
- if (this.searchResults) {
92
- const rootNode = await this.categoryTreeService.createRootNode(this.searchResults, this.treeConfig);
93
- return [rootNode];
94
- }
95
- // Otherwise load root categories
96
- const items = await this.categoryTreeService.loadRootCategories(this.treeData, this.treeConfig);
97
- if (!items) {
98
- return [];
99
- }
100
- const rootNode = await this.categoryTreeService.createRootNode(items, this.treeConfig);
101
- return [rootNode];
111
+ // Load root nodes if no parent ID provided
112
+ if (!parentId) {
113
+ return await this.loadRootNodes();
102
114
  }
103
- // Create a minimal node object with just the id (loadChildren only needs node.id)
104
- const targetNode = {
105
- id,
106
- label: '',
107
- data: {},
108
- };
109
- return await this.categoryTreeService.loadChildren(targetNode, this.treeData, this.treeConfig);
115
+ // Load children for the specified parent
116
+ return await this.loadChildNodes(parentId);
110
117
  };
111
118
  afterNextRender(() => {
112
- this.getCategories();
119
+ this.initializeTree();
113
120
  });
114
121
  }
115
- async handleCategorySearchChange(e) {
116
- if (!this.treeData || !this.treeConfig) {
122
+ //#endregion
123
+ //#region ---- Public Methods ----
124
+ /**
125
+ * Handles category search input changes
126
+ */
127
+ async handleCategorySearchChange(event) {
128
+ if (!this.isTreeInitialized()) {
117
129
  return;
118
130
  }
119
131
  this.setLoadingWithDelay(true);
120
132
  try {
121
- const items = await this.categoryTreeService.searchCategories(e.value, this.treeData, this.treeConfig);
133
+ const items = await this.categoryTreeService.searchCategories(event.value, this.treeData, this.treeConfig);
122
134
  if (!items) {
123
135
  this.clearLoadingState();
124
136
  return;
125
137
  }
126
- // Store search results for datasource callback
127
138
  this.searchResults = items;
128
- // Trigger tree refresh
129
- const treeComponent = this.tree();
130
- if (treeComponent) {
131
- treeComponent.refresh?.();
132
- }
139
+ this.refreshTree();
133
140
  }
134
141
  catch (error) {
135
142
  console.error('Error searching categories:', error);
@@ -138,7 +145,89 @@ class AXPEntityCategoryComponent {
138
145
  this.clearLoadingState();
139
146
  }
140
147
  }
141
- async getCategories() {
148
+ /**
149
+ * Handles node click events to apply category filters
150
+ */
151
+ handleNodeClick(node) {
152
+ const nodeData = this.extractNodeData(node);
153
+ const applyConditions = this.vm().entityDef.category?.applyConditions || [];
154
+ const categoryFilters = this.buildCategoryFilters(nodeData, applyConditions);
155
+ const viewFilters = this.buildViewFilters();
156
+ this.vm().dataSource.filter({
157
+ filters: [...viewFilters, ...categoryFilters],
158
+ logic: 'and',
159
+ });
160
+ this.vm().dataSource.refresh();
161
+ }
162
+ /**
163
+ * Handles node toggle events (expansion/collapse)
164
+ */
165
+ async onNodeToggle(_event) {
166
+ // Tree component handles lazy loading via datasource callback
167
+ }
168
+ //#endregion
169
+ //#region ---- CRUD Operations ----
170
+ /**
171
+ * Creates a new root category node
172
+ */
173
+ async handleCreateRootClick(event) {
174
+ this.preventDefaultAndStopPropagation(event);
175
+ try {
176
+ const context = await this.executeCreateWorkflow(undefined);
177
+ await this.handleCreateResult(context, undefined);
178
+ }
179
+ catch (error) {
180
+ console.error('Error creating root category:', error);
181
+ }
182
+ }
183
+ /**
184
+ * Creates a new child category under the given parent node
185
+ */
186
+ async handleCreateChildClick(node, event) {
187
+ this.preventDefaultAndStopPropagation(event);
188
+ try {
189
+ const parentId = this.extractNodeId(node);
190
+ const context = await this.executeCreateWorkflow(parentId);
191
+ await this.handleCreateResult(context, parentId);
192
+ }
193
+ catch (error) {
194
+ console.error('Error creating child category:', error);
195
+ }
196
+ }
197
+ /**
198
+ * Updates an existing category node
199
+ */
200
+ async handleEditNodeClick(node, event) {
201
+ this.preventDefaultAndStopPropagation(event);
202
+ try {
203
+ const nodeData = this.extractNodeData(node);
204
+ const context = await this.executeModifyWorkflow(nodeData);
205
+ await this.handleModifyResult(context, node);
206
+ }
207
+ catch (error) {
208
+ console.error('Error editing category:', error);
209
+ }
210
+ }
211
+ /**
212
+ * Deletes a category node
213
+ */
214
+ async handleDeleteNodeClick(node, event) {
215
+ this.preventDefaultAndStopPropagation(event);
216
+ try {
217
+ const nodeData = this.extractNodeData(node);
218
+ const context = await this.executeDeleteWorkflow(nodeData);
219
+ await this.handleDeleteResult(context, node);
220
+ }
221
+ catch (error) {
222
+ console.error('Error deleting category:', error);
223
+ }
224
+ }
225
+ //#endregion
226
+ //#region ---- Private Tree Management Methods ----
227
+ /**
228
+ * Initializes the category tree data structure
229
+ */
230
+ async initializeTree() {
142
231
  this.setLoadingWithDelay(true);
143
232
  try {
144
233
  const entityKey = this.categoryEntityKey();
@@ -152,12 +241,10 @@ class AXPEntityCategoryComponent {
152
241
  this.clearLoadingState();
153
242
  return;
154
243
  }
155
- // Get parentKey from entity definition
244
+ // Set parent key from entity definition
156
245
  if (this.treeData.categoryEntityDef?.parentKey) {
157
246
  this.treeConfig.parentKey = this.treeData.categoryEntityDef.parentKey;
158
247
  }
159
- // Tree will be loaded via datasource callback
160
- // Clear any previous search results
161
248
  this.searchResults = null;
162
249
  }
163
250
  catch (error) {
@@ -167,170 +254,316 @@ class AXPEntityCategoryComponent {
167
254
  this.clearLoadingState();
168
255
  }
169
256
  }
170
- async onNodeToggle(event) {
171
- // Tree component handles lazy loading via datasource callback
172
- }
173
- handleNodeClick(node) {
174
- const nodeData = (node.data || node);
175
- const applyConditions = this.vm().entityDef.category?.applyConditions || [];
176
- const filters = applyConditions
177
- .map((f) => {
178
- const value = nodeData[f.value];
179
- return value !== 'all' && value != null
180
- ? {
181
- field: f.name,
182
- value,
183
- operator: f.operator,
184
- }
185
- : null;
186
- })
187
- .filter((item) => item != null);
188
- const viewFilters = this.vm()
189
- .view()
190
- .conditions.map((i) => ({ ...i, field: i.name }));
191
- this.vm().dataSource.filter({
192
- filters: [...viewFilters, ...filters],
193
- logic: 'and',
194
- });
195
- this.vm().dataSource.refresh();
257
+ /**
258
+ * Loads root nodes for the tree
259
+ */
260
+ async loadRootNodes() {
261
+ if (!this.isTreeInitialized()) {
262
+ return [];
263
+ }
264
+ // Use search results if available
265
+ if (this.searchResults) {
266
+ const rootNode = await this.categoryTreeService.createRootNode(this.searchResults, this.treeConfig);
267
+ return [rootNode];
268
+ }
269
+ // Load root categories
270
+ const items = await this.categoryTreeService.loadRootCategories(this.treeData, this.treeConfig);
271
+ if (!items) {
272
+ return [];
273
+ }
274
+ const rootNode = await this.categoryTreeService.createRootNode(items, this.treeConfig);
275
+ return [rootNode];
196
276
  }
197
- /** Loads children for a given node id. Used for refresh operations. */
198
- async loadChildrenAtNode(targetId) {
199
- // With the new datasource callback API, the tree component will automatically
200
- // reload data when needed. For manual refresh, we may need to trigger
201
- // the tree component's refresh mechanism if available.
202
- // For now, the tree should handle refresh automatically via the datasource callback.
277
+ /**
278
+ * Loads child nodes for a given parent ID
279
+ */
280
+ async loadChildNodes(parentId) {
281
+ if (!this.isTreeInitialized()) {
282
+ return [];
283
+ }
284
+ const targetNode = {
285
+ ['id']: parentId,
286
+ ['title']: '',
287
+ ['data']: {},
288
+ };
289
+ return await this.categoryTreeService.loadChildren(targetNode, this.treeData, this.treeConfig);
203
290
  }
204
- /** Returns the module.entity key for the configured category entity. */
205
- getCategoryEntityKey() {
206
- return this.categoryEntityKey();
291
+ /**
292
+ * Refreshes the tree view component
293
+ */
294
+ refreshTree() {
295
+ const treeComponent = this.tree();
296
+ if (treeComponent) {
297
+ treeComponent.refresh();
298
+ }
207
299
  }
208
- /** Refresh tree after CRUD operations. */
300
+ /**
301
+ * Refreshes tree after CRUD operations
302
+ */
209
303
  async refreshAfterChange(parentId) {
210
- // Clear search results to show all categories
211
304
  this.searchResults = null;
212
- // Trigger tree refresh
305
+ this.refreshTree();
306
+ }
307
+ /**
308
+ * Adds a new node to the tree after create operation
309
+ */
310
+ async addNodeToTree(entityData, parentId) {
311
+ if (!this.isTreeInitialized()) {
312
+ return;
313
+ }
213
314
  const treeComponent = this.tree();
214
- if (treeComponent) {
215
- treeComponent.refresh?.();
315
+ if (!treeComponent) {
316
+ return;
317
+ }
318
+ const newNode = this.categoryTreeService.convertToTreeNode(entityData, this.treeConfig);
319
+ if (parentId) {
320
+ await this.addChildNode(treeComponent, parentId, newNode);
321
+ }
322
+ else {
323
+ await this.addRootNode(treeComponent, newNode);
216
324
  }
217
325
  }
218
- //#endregion
219
- //#region ---- UI Handlers ----
220
- /** Create a new root category node. */
221
- async handleCreateRootClick(e) {
222
- e.nativeEvent.preventDefault();
223
- e.nativeEvent.stopPropagation();
224
- try {
225
- await this.workflow.execute('create-entity', {
226
- entity: this.getCategoryEntityKey(),
227
- options: { process: { redirect: false } },
228
- });
326
+ /**
327
+ * Adds a child node to a parent node
328
+ */
329
+ async addChildNode(treeComponent, parentId, newNode) {
330
+ const parentNode = treeComponent.findNode(parentId);
331
+ if (!parentNode) {
332
+ await this.refreshAfterChange(parentId);
333
+ return;
334
+ }
335
+ if (!treeComponent.isNodeExpanded(parentId)) {
336
+ await treeComponent.expandNode(parentId);
337
+ }
338
+ treeComponent.addChild(parentId, newNode);
339
+ }
340
+ /**
341
+ * Adds a root node to the tree
342
+ */
343
+ async addRootNode(treeComponent, newNode) {
344
+ const rootNode = treeComponent.findNode(ROOT_NODE_ID);
345
+ if (!rootNode) {
229
346
  await this.refreshAfterChange();
347
+ return;
230
348
  }
231
- catch (error) {
232
- console.error('Error creating root category:', error);
349
+ if (!treeComponent.isNodeExpanded(ROOT_NODE_ID)) {
350
+ await treeComponent.expandNode(ROOT_NODE_ID);
233
351
  }
352
+ treeComponent.addChild(ROOT_NODE_ID, newNode);
234
353
  }
235
- /** Create a new child under the given node. */
236
- async handleCreateChildClick(node, e) {
237
- e.nativeEvent.preventDefault();
238
- e.nativeEvent.stopPropagation();
239
- try {
240
- const parentKey = this.treeData?.categoryEntityDef?.parentKey;
241
- const data = {};
242
- if (parentKey) {
243
- data[parentKey] = node.id;
244
- }
245
- await this.workflow.execute('create-entity', {
246
- entity: this.getCategoryEntityKey(),
247
- data,
248
- options: { process: { redirect: false, canCreateNewOne: true } },
249
- });
250
- await this.refreshAfterChange(node.id);
354
+ /**
355
+ * Updates an existing node in the tree after edit operation
356
+ */
357
+ async updateNodeInTree(nodeId, entityData) {
358
+ if (!this.isTreeInitialized()) {
359
+ return;
251
360
  }
252
- catch (error) {
253
- console.error('Error creating child category:', error);
361
+ const treeComponent = this.tree();
362
+ if (!treeComponent) {
363
+ return;
254
364
  }
365
+ const existingNode = treeComponent.findNode(nodeId);
366
+ if (!existingNode) {
367
+ await this.refreshNodeParent(entityData);
368
+ return;
369
+ }
370
+ const updatedNode = this.categoryTreeService.convertToTreeNode(entityData, this.treeConfig);
371
+ const updates = {
372
+ ['title']: updatedNode['title'],
373
+ ['data']: updatedNode['data'],
374
+ ['childrenCount']: updatedNode['childrenCount'],
375
+ ['icon']: updatedNode['icon'],
376
+ };
377
+ treeComponent.editNode(nodeId, updates);
255
378
  }
256
- /** Update the given node. */
257
- async handleEditNodeClick(node, e) {
258
- e.nativeEvent.preventDefault();
259
- e.nativeEvent.stopPropagation();
260
- try {
261
- const nodeData = (node.data || node);
262
- await this.workflow.execute('quick-modify-entity', {
263
- entity: this.getCategoryEntityKey(),
264
- data: nodeData,
265
- options: { layout: { size: 'md' } },
266
- });
267
- const parentKey = this.treeData?.categoryEntityDef?.parentKey;
268
- if (parentKey && nodeData[parentKey]) {
269
- await this.refreshAfterChange(nodeData[parentKey]);
270
- }
271
- else {
272
- await this.refreshAfterChange();
273
- }
379
+ /**
380
+ * Removes a node from the tree after delete operation
381
+ */
382
+ async removeNodeFromTree(nodeId) {
383
+ const treeComponent = this.tree();
384
+ if (!treeComponent) {
385
+ return;
274
386
  }
275
- catch (error) {
276
- console.error('Error editing category:', error);
387
+ const nodeToDelete = treeComponent.findNode(nodeId);
388
+ if (!nodeToDelete) {
389
+ await this.refreshAfterChange();
390
+ return;
277
391
  }
392
+ treeComponent.removeNode(nodeId);
278
393
  }
279
- /** Delete the given node. */
280
- async handleDeleteNodeClick(node, e) {
281
- e.nativeEvent.preventDefault();
282
- e.nativeEvent.stopPropagation();
283
- try {
284
- const nodeData = (node.data || node);
285
- await this.workflow.execute('delete-entity', {
286
- entity: this.getCategoryEntityKey(),
287
- data: nodeData,
288
- options: { process: { showResult: true } },
289
- });
290
- const parentKey = this.treeData?.categoryEntityDef?.parentKey;
291
- if (parentKey && nodeData[parentKey]) {
292
- await this.refreshAfterChange(nodeData[parentKey]);
293
- }
294
- else {
295
- await this.refreshAfterChange();
296
- }
394
+ //#endregion
395
+ //#region ---- Workflow Execution Methods ----
396
+ /**
397
+ * Executes the create entity workflow
398
+ */
399
+ async executeCreateWorkflow(parentId) {
400
+ const entityKey = this.categoryEntityKey();
401
+ const workflowData = {};
402
+ if (parentId && this.treeData?.categoryEntityDef?.parentKey) {
403
+ workflowData[this.treeData.categoryEntityDef.parentKey] = parentId;
297
404
  }
298
- catch (error) {
299
- console.error('Error deleting category:', error);
405
+ return await this.workflow.execute('create-entity', {
406
+ entity: entityKey,
407
+ data: Object.keys(workflowData).length > 0 ? workflowData : undefined,
408
+ options: { process: { redirect: false, canCreateNewOne: !!parentId } },
409
+ });
410
+ }
411
+ /**
412
+ * Executes the modify entity workflow
413
+ */
414
+ async executeModifyWorkflow(nodeData) {
415
+ return await this.workflow.execute('quick-modify-entity', {
416
+ entity: this.categoryEntityKey(),
417
+ data: nodeData,
418
+ options: { layout: { size: 'md' } },
419
+ });
420
+ }
421
+ /**
422
+ * Executes the delete entity workflow
423
+ */
424
+ async executeDeleteWorkflow(nodeData) {
425
+ return await this.workflow.execute('delete-entity', {
426
+ entity: this.categoryEntityKey(),
427
+ data: nodeData,
428
+ options: { process: { showResult: true } },
429
+ });
430
+ }
431
+ /**
432
+ * Handles the result of a create operation
433
+ */
434
+ async handleCreateResult(context, parentId) {
435
+ const result = context.getOutput('result');
436
+ if (!result || !this.isTreeInitialized()) {
437
+ await this.refreshAfterChange(parentId);
438
+ return;
439
+ }
440
+ const newEntityData = context.getVariable('data');
441
+ if (newEntityData) {
442
+ await this.addNodeToTree(newEntityData, parentId);
443
+ }
444
+ else {
445
+ await this.refreshAfterChange(parentId);
446
+ }
447
+ }
448
+ /**
449
+ * Handles the result of a modify operation
450
+ */
451
+ async handleModifyResult(context, node) {
452
+ const result = context.getOutput('result');
453
+ if (!result || !this.isTreeInitialized()) {
454
+ await this.refreshNodeParent(this.extractNodeData(node));
455
+ return;
456
+ }
457
+ const updatedEntityData = context.getVariable('data');
458
+ if (updatedEntityData) {
459
+ await this.updateNodeInTree(this.extractNodeId(node), updatedEntityData);
460
+ }
461
+ else {
462
+ await this.refreshNodeParent(this.extractNodeData(node));
463
+ }
464
+ }
465
+ /**
466
+ * Handles the result of a delete operation
467
+ */
468
+ async handleDeleteResult(context, node) {
469
+ const result = context.getOutput('result');
470
+ if (result) {
471
+ await this.removeNodeFromTree(this.extractNodeId(node));
472
+ }
473
+ else {
474
+ await this.refreshNodeParent(this.extractNodeData(node));
300
475
  }
301
476
  }
302
477
  //#endregion
478
+ //#region ---- Utility Methods ----
479
+ /**
480
+ * Checks if tree data and config are initialized
481
+ */
482
+ isTreeInitialized() {
483
+ return !!(this.treeData && this.treeConfig);
484
+ }
485
+ /**
486
+ * Extracts node data from a tree node
487
+ */
488
+ extractNodeData(node) {
489
+ return (node['data'] || node);
490
+ }
491
+ /**
492
+ * Extracts node ID from a tree node
493
+ */
494
+ extractNodeId(node) {
495
+ return String(node['id'] ?? '');
496
+ }
497
+ /**
498
+ * Builds category filters from node data and apply conditions
499
+ */
500
+ buildCategoryFilters(nodeData, applyConditions) {
501
+ return applyConditions
502
+ .map((condition) => {
503
+ const value = nodeData[condition.value];
504
+ if (value === 'all' || value == null) {
505
+ return null;
506
+ }
507
+ return {
508
+ field: condition.name,
509
+ value,
510
+ operator: condition.operator,
511
+ };
512
+ })
513
+ .filter((item) => item !== null);
514
+ }
515
+ /**
516
+ * Builds view filters from view conditions
517
+ */
518
+ buildViewFilters() {
519
+ return this.vm()
520
+ .view()
521
+ .conditions.map((condition) => ({ ...condition, field: condition.name }));
522
+ }
523
+ /**
524
+ * Refreshes the parent node of the given entity data
525
+ */
526
+ async refreshNodeParent(entityData) {
527
+ const parentKey = this.treeData?.categoryEntityDef?.parentKey;
528
+ const parentId = parentKey && entityData[parentKey] ? entityData[parentKey] : undefined;
529
+ await this.refreshAfterChange(parentId);
530
+ }
531
+ /**
532
+ * Prevents default behavior and stops event propagation
533
+ */
534
+ preventDefaultAndStopPropagation(event) {
535
+ event.nativeEvent.preventDefault();
536
+ event.nativeEvent.stopPropagation();
537
+ }
538
+ //#endregion
303
539
  //#region ---- Loading State Management ----
304
540
  /**
305
- * Set loading state with delay to avoid flickering for fast responses
541
+ * Sets loading state with delay to avoid flickering for fast responses
306
542
  */
307
543
  setLoadingWithDelay(loading) {
308
- if (loading) {
309
- // Clear any existing timeout
310
- if (this.loadingTimeoutId) {
311
- clearTimeout(this.loadingTimeoutId);
312
- }
313
- // Only show skeleton if loading takes more than LOADING_DELAY ms
314
- this.loadingTimeoutId = setTimeout(() => {
315
- this.isLoading.set(true);
316
- this.loadingTimeoutId = null;
317
- }, this.LOADING_DELAY);
544
+ if (!loading) {
545
+ return;
546
+ }
547
+ if (this.loadingTimeoutId) {
548
+ clearTimeout(this.loadingTimeoutId);
318
549
  }
550
+ this.loadingTimeoutId = setTimeout(() => {
551
+ this.isLoading.set(true);
552
+ this.loadingTimeoutId = null;
553
+ }, LOADING_DELAY_MS);
319
554
  }
320
555
  /**
321
- * Clear loading state and cancel any pending timeout
556
+ * Clears loading state and cancels any pending timeout
322
557
  */
323
558
  clearLoadingState() {
324
- // Cancel the delayed loading state if it hasn't been set yet
325
559
  if (this.loadingTimeoutId) {
326
560
  clearTimeout(this.loadingTimeoutId);
327
561
  this.loadingTimeoutId = null;
328
562
  }
329
- // Clear the loading state
330
563
  this.isLoading.set(false);
331
564
  }
332
565
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
333
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityCategoryComponent, isStandalone: true, selector: "axp-entity-category", inputs: { vm: { classPropertyName: "vm", publicName: "vm", isSignal: true, isRequired: true, transformFunction: null }, searchValue: { classPropertyName: "searchValue", publicName: "searchValue", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true, isSignal: true }], ngImport: i0, template: "<axp-layout-header>\n <axp-layout-title>{{\n vm().entityDef.category?.title || '@general:terms.classification.category' | translate | async\n }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n (onValueChanged)=\"handleCategorySearchChange($event)\"\n [delayTime]=\"300\"\n [placeholder]=\"'@general:terms.interface.category.search.placeholder' | translate | async\"\n >\n </ax-search-box>\n </axp-layout-toolbar>\n</axp-layout-header>\n<axp-layout-content>\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-flex ax-flex-col ax-gap-3\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n </div>\n } @else if (treeData) {\n <div class=\"ax-px-4 ax-max-h-[calc(100vh-250px)] ax-overflow-auto\">\n <ax-tree-view\n [datasource]=\"datasource\"\n [showCheckbox]=\"false\"\n [showIcons]=\"true\"\n [itemTemplate]=\"itemTemplate\"\n (onNodeToggle)=\"onNodeToggle($event)\"\n #tree\n >\n </ax-tree-view>\n </div>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:terms.interface.category.search.no-records.title'\"\n [description]=\"'@general:terms.interface.category.search.no-records.description'\"\n >\n <ax-button\n slot=\"actions\"\n (onClick)=\"handleCreateRootClick($event)\"\n look=\"solid\"\n color=\"primary\"\n [text]=\"'@general:actions.add-new.title' | translate | async\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n </axp-state-message>\n }\n\n <ng-template #itemTemplate let-node=\"node\" let-level=\"level\">\n @let item = node.data || node;\n @let textField = vm().entityDef.category?.textField || 'title';\n @let valueField = vm().entityDef.category?.valueField || 'id';\n @let itemId = item[valueField] || node.id;\n @let itemTitle = item[textField] || node.label;\n <div\n class=\"ax-flex ax-items-center ax-justify-between ax-w-full ax-gap-2 ax-overflow-hidden\"\n (click)=\"handleNodeClick(node)\"\n >\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-min-w-0\">\n <ax-icon\n class=\"fas fa-folder\"\n [style.color]=\"item.color ?? 'rgba(var(--ax-sys-color-warning-500), 1)'\"\n ></ax-icon>\n <span class=\"ax-truncate\">{{ itemTitle }}</span>\n </div>\n @if (itemId && itemId !== 'all') {\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-button class=\"ax-xs\" color=\"default\" look=\"blank\" (onClick)=\"$event.nativeEvent.stopPropagation()\">\n <ax-icon class=\"fas fa-ellipsis-v\"></ax-icon>\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n (onClick)=\"handleCreateChildClick(node, $event)\"\n look=\"blank\"\n color=\"default\"\n text=\"Add New Child\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button-item>\n <ax-button-item (onClick)=\"handleEditNodeClick(node, $event)\" look=\"blank\" text=\"Edit\">\n <ax-icon class=\"fas fa-pen\"></ax-icon>\n </ax-button-item>\n <ax-button-item\n (onClick)=\"handleDeleteNodeClick(node, $event)\"\n color=\"danger\"\n look=\"blank\"\n text=\"Delete\"\n >\n <ax-icon class=\"fas fa-trash\"></ax-icon>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n } @else if (itemId === 'all') {\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-button class=\"ax-xs\" (onClick)=\"handleCreateRootClick($event)\" look=\"blank\" color=\"default\">\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </div>\n </ng-template>\n</axp-layout-content>\n", styles: ["ax-tree-view-item .ax-tree-view-items{width:100%;min-width:0px}ax-tree-view-item .ax-tree-view-items>div{width:100%}ax-tree-view-item .ax-state-tree-view-active{background-color:rgb(var(--ax-sys-color-light-surface))!important;color:rgb(var(--ax-sys-color-on-light-surface))!important;border-color:rgb(var(--ax-sys-color-border-light-surface))!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTreeViewModule }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "showCheckbox", "checkChildrenOnSelect", "intermediateState", "checkOnClick", "dragMode", "dragOperationType", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "nodeHeight", "look", "itemTemplate"], outputs: ["datasourceChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { 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: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "component", type: AXPThemeLayoutToolbarComponent, selector: "axp-layout-toolbar" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: i3.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i3.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i5.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "variant"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i10.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
566
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityCategoryComponent, isStandalone: true, selector: "axp-entity-category", inputs: { vm: { classPropertyName: "vm", publicName: "vm", isSignal: true, isRequired: true, transformFunction: null }, searchValue: { classPropertyName: "searchValue", publicName: "searchValue", isSignal: true, isRequired: false, transformFunction: null }, selectMode: { classPropertyName: "selectMode", publicName: "selectMode", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, selectionBehavior: { classPropertyName: "selectionBehavior", publicName: "selectionBehavior", isSignal: true, isRequired: false, transformFunction: null }, dragArea: { classPropertyName: "dragArea", publicName: "dragArea", isSignal: true, isRequired: false, transformFunction: null }, dragBehavior: { classPropertyName: "dragBehavior", publicName: "dragBehavior", isSignal: true, isRequired: false, transformFunction: null }, showIcons: { classPropertyName: "showIcons", publicName: "showIcons", isSignal: true, isRequired: false, transformFunction: null }, showChildrenBadge: { classPropertyName: "showChildrenBadge", publicName: "showChildrenBadge", isSignal: true, isRequired: false, transformFunction: null }, expandedIcon: { classPropertyName: "expandedIcon", publicName: "expandedIcon", isSignal: true, isRequired: false, transformFunction: null }, collapsedIcon: { classPropertyName: "collapsedIcon", publicName: "collapsedIcon", isSignal: true, isRequired: false, transformFunction: null }, indentSize: { classPropertyName: "indentSize", publicName: "indentSize", isSignal: true, isRequired: false, transformFunction: null }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true, isSignal: true }], ngImport: i0, template: "<axp-layout-header>\n <axp-layout-title>{{\n vm().entityDef.category?.title || '@general:terms.classification.category' | translate | async\n }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n (onValueChanged)=\"handleCategorySearchChange($event)\"\n [delayTime]=\"300\"\n [placeholder]=\"'@general:terms.interface.category.search.placeholder' | translate | async\"\n >\n </ax-search-box>\n </axp-layout-toolbar>\n</axp-layout-header>\n<axp-layout-content>\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-flex ax-flex-col ax-gap-3\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n </div>\n } @else if (treeData) {\n <div class=\"ax-px-4 ax-max-h-[calc(100vh-250px)] ax-overflow-auto\">\n <ax-tree-view\n [datasource]=\"datasource\"\n [selectMode]=\"selectMode()\"\n [showCheckbox]=\"showCheckbox()\"\n [selectionBehavior]=\"selectionBehavior()\"\n [dragArea]=\"dragArea()\"\n [dragBehavior]=\"dragBehavior()\"\n [showIcons]=\"showIcons()\"\n [showChildrenBadge]=\"showChildrenBadge()\"\n [expandedIcon]=\"expandedIcon()\"\n [collapsedIcon]=\"collapsedIcon()\"\n [indentSize]=\"indentSize()\"\n [look]=\"look()\"\n [titleField]=\"textField()\"\n [idField]=\"valueField()\"\n [nodeTemplate]=\"itemTemplate\"\n (onNodeToggle)=\"onNodeToggle($event)\"\n #tree\n >\n </ax-tree-view>\n </div>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:terms.interface.category.search.no-records.title'\"\n [description]=\"'@general:terms.interface.category.search.no-records.description'\"\n >\n <ax-button\n slot=\"actions\"\n (onClick)=\"handleCreateRootClick($event)\"\n look=\"solid\"\n color=\"primary\"\n [text]=\"'@general:actions.add-new.title' | translate | async\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n </axp-state-message>\n }\n\n <ng-template #itemTemplate let-node=\"node\" let-level=\"level\">\n @let item = node.data || node;\n @let textField = vm().entityDef.category?.textField || 'title';\n @let valueField = vm().entityDef.category?.valueField || 'id';\n @let itemId = item[valueField] || node.id;\n @let itemTitle = item[textField] || node.title;\n <div\n class=\"ax-flex ax-items-center ax-justify-between ax-w-full ax-gap-2 ax-overflow-hidden\"\n (click)=\"handleNodeClick(node)\"\n >\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-min-w-0\">\n <ax-icon\n class=\"fas fa-folder\"\n [style.color]=\"item.color ?? 'rgba(var(--ax-sys-color-warning-500), 1)'\"\n ></ax-icon>\n <span class=\"ax-truncate\">{{ itemTitle }}</span>\n </div>\n @if (itemId && itemId !== 'all') {\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-button class=\"ax-xs\" color=\"default\" look=\"blank\" (onClick)=\"$event.nativeEvent.stopPropagation()\">\n <ax-icon class=\"fas fa-ellipsis-v\"></ax-icon>\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n (onClick)=\"handleCreateChildClick(node, $event)\"\n look=\"blank\"\n color=\"default\"\n text=\"Add New Child\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button-item>\n <ax-button-item (onClick)=\"handleEditNodeClick(node, $event)\" look=\"blank\" text=\"Edit\">\n <ax-icon class=\"fas fa-pen\"></ax-icon>\n </ax-button-item>\n <ax-button-item\n (onClick)=\"handleDeleteNodeClick(node, $event)\"\n color=\"danger\"\n look=\"blank\"\n text=\"Delete\"\n >\n <ax-icon class=\"fas fa-trash\"></ax-icon>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n } @else if (itemId === 'all') {\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-button class=\"ax-xs\" (onClick)=\"handleCreateRootClick($event)\" look=\"blank\" color=\"default\">\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </div>\n </ng-template>\n</axp-layout-content>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTreeViewModule }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "showCheckbox", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look", "nodeTemplate", "idField", "titleField", "tooltipField", "iconField", "expandedField", "selectedField", "indeterminateField", "disabledField", "hiddenField", "childrenField", "childrenCountField", "dataField"], outputs: ["datasourceChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onSelectionChange", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { 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: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "component", type: AXPThemeLayoutToolbarComponent, selector: "axp-layout-toolbar" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: i3.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i3.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i5.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "variant"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i10.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
334
567
  }
335
568
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryComponent, decorators: [{
336
569
  type: Component,
@@ -348,8 +581,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
348
581
  AXButtonModule,
349
582
  AXDropdownModule,
350
583
  AXPStateMessageComponent,
351
- ], template: "<axp-layout-header>\n <axp-layout-title>{{\n vm().entityDef.category?.title || '@general:terms.classification.category' | translate | async\n }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n (onValueChanged)=\"handleCategorySearchChange($event)\"\n [delayTime]=\"300\"\n [placeholder]=\"'@general:terms.interface.category.search.placeholder' | translate | async\"\n >\n </ax-search-box>\n </axp-layout-toolbar>\n</axp-layout-header>\n<axp-layout-content>\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-flex ax-flex-col ax-gap-3\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n </div>\n } @else if (treeData) {\n <div class=\"ax-px-4 ax-max-h-[calc(100vh-250px)] ax-overflow-auto\">\n <ax-tree-view\n [datasource]=\"datasource\"\n [showCheckbox]=\"false\"\n [showIcons]=\"true\"\n [itemTemplate]=\"itemTemplate\"\n (onNodeToggle)=\"onNodeToggle($event)\"\n #tree\n >\n </ax-tree-view>\n </div>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:terms.interface.category.search.no-records.title'\"\n [description]=\"'@general:terms.interface.category.search.no-records.description'\"\n >\n <ax-button\n slot=\"actions\"\n (onClick)=\"handleCreateRootClick($event)\"\n look=\"solid\"\n color=\"primary\"\n [text]=\"'@general:actions.add-new.title' | translate | async\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n </axp-state-message>\n }\n\n <ng-template #itemTemplate let-node=\"node\" let-level=\"level\">\n @let item = node.data || node;\n @let textField = vm().entityDef.category?.textField || 'title';\n @let valueField = vm().entityDef.category?.valueField || 'id';\n @let itemId = item[valueField] || node.id;\n @let itemTitle = item[textField] || node.label;\n <div\n class=\"ax-flex ax-items-center ax-justify-between ax-w-full ax-gap-2 ax-overflow-hidden\"\n (click)=\"handleNodeClick(node)\"\n >\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-min-w-0\">\n <ax-icon\n class=\"fas fa-folder\"\n [style.color]=\"item.color ?? 'rgba(var(--ax-sys-color-warning-500), 1)'\"\n ></ax-icon>\n <span class=\"ax-truncate\">{{ itemTitle }}</span>\n </div>\n @if (itemId && itemId !== 'all') {\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-button class=\"ax-xs\" color=\"default\" look=\"blank\" (onClick)=\"$event.nativeEvent.stopPropagation()\">\n <ax-icon class=\"fas fa-ellipsis-v\"></ax-icon>\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n (onClick)=\"handleCreateChildClick(node, $event)\"\n look=\"blank\"\n color=\"default\"\n text=\"Add New Child\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button-item>\n <ax-button-item (onClick)=\"handleEditNodeClick(node, $event)\" look=\"blank\" text=\"Edit\">\n <ax-icon class=\"fas fa-pen\"></ax-icon>\n </ax-button-item>\n <ax-button-item\n (onClick)=\"handleDeleteNodeClick(node, $event)\"\n color=\"danger\"\n look=\"blank\"\n text=\"Delete\"\n >\n <ax-icon class=\"fas fa-trash\"></ax-icon>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n } @else if (itemId === 'all') {\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-button class=\"ax-xs\" (onClick)=\"handleCreateRootClick($event)\" look=\"blank\" color=\"default\">\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </div>\n </ng-template>\n</axp-layout-content>\n", styles: ["ax-tree-view-item .ax-tree-view-items{width:100%;min-width:0px}ax-tree-view-item .ax-tree-view-items>div{width:100%}ax-tree-view-item .ax-state-tree-view-active{background-color:rgb(var(--ax-sys-color-light-surface))!important;color:rgb(var(--ax-sys-color-on-light-surface))!important;border-color:rgb(var(--ax-sys-color-border-light-surface))!important}\n"] }]
352
- }], ctorParameters: () => [], propDecorators: { vm: [{ type: i0.Input, args: [{ isSignal: true, alias: "vm", required: true }] }], tree: [{ type: i0.ViewChild, args: ['tree', { isSignal: true }] }], searchValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchValue", required: false }] }] } });
584
+ ], template: "<axp-layout-header>\n <axp-layout-title>{{\n vm().entityDef.category?.title || '@general:terms.classification.category' | translate | async\n }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n (onValueChanged)=\"handleCategorySearchChange($event)\"\n [delayTime]=\"300\"\n [placeholder]=\"'@general:terms.interface.category.search.placeholder' | translate | async\"\n >\n </ax-search-box>\n </axp-layout-toolbar>\n</axp-layout-header>\n<axp-layout-content>\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-flex ax-flex-col ax-gap-3\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded-md\"></ax-skeleton>\n </div>\n } @else if (treeData) {\n <div class=\"ax-px-4 ax-max-h-[calc(100vh-250px)] ax-overflow-auto\">\n <ax-tree-view\n [datasource]=\"datasource\"\n [selectMode]=\"selectMode()\"\n [showCheckbox]=\"showCheckbox()\"\n [selectionBehavior]=\"selectionBehavior()\"\n [dragArea]=\"dragArea()\"\n [dragBehavior]=\"dragBehavior()\"\n [showIcons]=\"showIcons()\"\n [showChildrenBadge]=\"showChildrenBadge()\"\n [expandedIcon]=\"expandedIcon()\"\n [collapsedIcon]=\"collapsedIcon()\"\n [indentSize]=\"indentSize()\"\n [look]=\"look()\"\n [titleField]=\"textField()\"\n [idField]=\"valueField()\"\n [nodeTemplate]=\"itemTemplate\"\n (onNodeToggle)=\"onNodeToggle($event)\"\n #tree\n >\n </ax-tree-view>\n </div>\n } @else {\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:terms.interface.category.search.no-records.title'\"\n [description]=\"'@general:terms.interface.category.search.no-records.description'\"\n >\n <ax-button\n slot=\"actions\"\n (onClick)=\"handleCreateRootClick($event)\"\n look=\"solid\"\n color=\"primary\"\n [text]=\"'@general:actions.add-new.title' | translate | async\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n </axp-state-message>\n }\n\n <ng-template #itemTemplate let-node=\"node\" let-level=\"level\">\n @let item = node.data || node;\n @let textField = vm().entityDef.category?.textField || 'title';\n @let valueField = vm().entityDef.category?.valueField || 'id';\n @let itemId = item[valueField] || node.id;\n @let itemTitle = item[textField] || node.title;\n <div\n class=\"ax-flex ax-items-center ax-justify-between ax-w-full ax-gap-2 ax-overflow-hidden\"\n (click)=\"handleNodeClick(node)\"\n >\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-min-w-0\">\n <ax-icon\n class=\"fas fa-folder\"\n [style.color]=\"item.color ?? 'rgba(var(--ax-sys-color-warning-500), 1)'\"\n ></ax-icon>\n <span class=\"ax-truncate\">{{ itemTitle }}</span>\n </div>\n @if (itemId && itemId !== 'all') {\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-button class=\"ax-xs\" color=\"default\" look=\"blank\" (onClick)=\"$event.nativeEvent.stopPropagation()\">\n <ax-icon class=\"fas fa-ellipsis-v\"></ax-icon>\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n (onClick)=\"handleCreateChildClick(node, $event)\"\n look=\"blank\"\n color=\"default\"\n text=\"Add New Child\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button-item>\n <ax-button-item (onClick)=\"handleEditNodeClick(node, $event)\" look=\"blank\" text=\"Edit\">\n <ax-icon class=\"fas fa-pen\"></ax-icon>\n </ax-button-item>\n <ax-button-item\n (onClick)=\"handleDeleteNodeClick(node, $event)\"\n color=\"danger\"\n look=\"blank\"\n text=\"Delete\"\n >\n <ax-icon class=\"fas fa-trash\"></ax-icon>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n } @else if (itemId === 'all') {\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-button class=\"ax-xs\" (onClick)=\"handleCreateRootClick($event)\" look=\"blank\" color=\"default\">\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </div>\n </ng-template>\n</axp-layout-content>\n" }]
585
+ }], ctorParameters: () => [], propDecorators: { vm: [{ type: i0.Input, args: [{ isSignal: true, alias: "vm", required: true }] }], tree: [{ type: i0.ViewChild, args: ['tree', { isSignal: true }] }], searchValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchValue", required: false }] }], selectMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectMode", required: false }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], selectionBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionBehavior", required: false }] }], dragArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragArea", required: false }] }], dragBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragBehavior", required: false }] }], showIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcons", required: false }] }], showChildrenBadge: [{ type: i0.Input, args: [{ isSignal: true, alias: "showChildrenBadge", required: false }] }], expandedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIcon", required: false }] }], collapsedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedIcon", required: false }] }], indentSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "indentSize", required: false }] }], look: [{ type: i0.Input, args: [{ isSignal: true, alias: "look", required: false }] }] } });
353
586
 
354
587
  class AXPEntityMasterToolbarViewComponent {
355
588
  constructor() {
@@ -817,7 +1050,7 @@ class AXPEntityMasterListViewComponent extends AXPPageLayoutBaseComponent {
817
1050
  //
818
1051
  AXPEntityMasterToolbarViewComponent, selector: "axp-entity-master-toolbar-view", inputs: ["viewModel"] }, { kind: "component", type:
819
1052
  //
820
- AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXPEntityCategoryComponent, selector: "axp-entity-category", inputs: ["vm", "searchValue"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i10.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1053
+ AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXPEntityCategoryComponent, selector: "axp-entity-category", inputs: ["vm", "searchValue", "selectMode", "showCheckbox", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i10.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
821
1054
  }
822
1055
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityMasterListViewComponent, decorators: [{
823
1056
  type: Component,
@@ -864,4 +1097,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
864
1097
  }], ctorParameters: () => [{ type: i1$1.AXPlatform }], propDecorators: { grid: [{ type: i0.ViewChild, args: ['grid', { isSignal: true }] }] } });
865
1098
 
866
1099
  export { AXPEntityMasterListViewComponent };
867
- //# sourceMappingURL=acorex-platform-themes-default-entity-master-list-view.component-4nWLEm89.mjs.map
1100
+ //# sourceMappingURL=acorex-platform-themes-default-entity-master-list-view.component-BbACUabi.mjs.map