@acorex/platform 21.0.0-next.64 → 21.0.0-next.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/fesm2022/acorex-platform-common.mjs +94 -18
  2. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  3. package/fesm2022/acorex-platform-core.mjs +42 -1
  4. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  5. package/fesm2022/acorex-platform-layout-builder.mjs +29 -7
  6. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-layout-components.mjs +282 -108
  8. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-layout-designer.mjs +1 -1
  10. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-entity-attachments-page.component-BaTS183I.mjs +383 -0
  12. package/fesm2022/acorex-platform-layout-entity-attachments-page.component-BaTS183I.mjs.map +1 -0
  13. package/fesm2022/{acorex-platform-layout-widgets-file-list-popup.component-CDYAGBku.mjs → acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs} +15 -18
  14. package/fesm2022/acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-layout-entity.mjs +3339 -301
  16. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-layout-views.mjs +0 -1
  18. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-widget-core.mjs +1377 -4
  20. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-widgets.mjs +8938 -11693
  22. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  23. package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-Cx1lLUaR.mjs → acorex-platform-themes-default-entity-master-create-view.component-CWLfNqV0.mjs} +3 -3
  24. package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-Cx1lLUaR.mjs.map → acorex-platform-themes-default-entity-master-create-view.component-CWLfNqV0.mjs.map} +1 -1
  25. package/fesm2022/{acorex-platform-themes-default-entity-master-modify-view.component-AOrcgjDF.mjs → acorex-platform-themes-default-entity-master-modify-view.component-C7cT82K2.mjs} +3 -3
  26. package/fesm2022/{acorex-platform-themes-default-entity-master-modify-view.component-AOrcgjDF.mjs.map → acorex-platform-themes-default-entity-master-modify-view.component-C7cT82K2.mjs.map} +1 -1
  27. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-BfCeUU5F.mjs → acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs} +4 -4
  28. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-BfCeUU5F.mjs.map → acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs.map} +1 -1
  29. package/fesm2022/acorex-platform-themes-default.mjs +685 -287
  30. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  31. package/fesm2022/{acorex-platform-themes-shared-settings.provider-BgXYCFia.mjs → acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs} +29 -2
  32. package/fesm2022/acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-themes-shared.mjs +94 -24
  34. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  35. package/fesm2022/acorex-platform-workflow.mjs +176 -26
  36. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  37. package/package.json +1 -1
  38. package/types/acorex-platform-common.d.ts +74 -10
  39. package/types/acorex-platform-core.d.ts +63 -2
  40. package/types/acorex-platform-layout-builder.d.ts +7 -1
  41. package/types/acorex-platform-layout-components.d.ts +162 -36
  42. package/types/acorex-platform-layout-entity.d.ts +704 -14
  43. package/types/acorex-platform-layout-views.d.ts +28 -0
  44. package/types/acorex-platform-layout-widget-core.d.ts +156 -3
  45. package/types/acorex-platform-layout-widgets.d.ts +29 -393
  46. package/types/acorex-platform-themes-default.d.ts +137 -30
  47. package/types/acorex-platform-themes-shared.d.ts +23 -1
  48. package/types/acorex-platform-workflow.d.ts +91 -4
  49. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CDYAGBku.mjs.map +0 -1
  50. package/fesm2022/acorex-platform-themes-shared-settings.provider-BgXYCFia.mjs.map +0 -1
@@ -16,7 +16,7 @@ import { AXPLayoutRendererComponent, AXPLayoutBuilderService, AXP_PREVIEW_WIDGET
16
16
  import * as i1 from '@angular/common';
17
17
  import { CommonModule, NgTemplateOutlet, isPlatformBrowser, AsyncPipe } from '@angular/common';
18
18
  import * as i0 from '@angular/core';
19
- import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, signal, effect, computed, InjectionToken, Injectable, Directive, viewChild, contentChild, ElementRef, output, afterNextRender, model, untracked, DestroyRef, PLATFORM_ID, linkedSignal, HostListener, ViewChildren, EventEmitter, Output, TemplateRef, contentChildren, Input } from '@angular/core';
19
+ import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, signal, effect, computed, InjectionToken, Injectable, Directive, viewChild, contentChild, ElementRef, output, afterNextRender, model, untracked, DestroyRef, PLATFORM_ID, linkedSignal, HostListener, ViewChildren, EventEmitter, Output, TemplateRef, contentChildren, ChangeDetectorRef, Input } from '@angular/core';
20
20
  import { AXAccordionCdkModule } from '@acorex/cdk/accordion';
21
21
  import { AXTagModule } from '@acorex/components/tag';
22
22
  import * as i1$1 from '@acorex/components/avatar';
@@ -24,9 +24,10 @@ import { AXAvatarModule } from '@acorex/components/avatar';
24
24
  import * as i3$1 from '@acorex/components/image';
25
25
  import { AXImageModule } from '@acorex/components/image';
26
26
  import { isNil, isEmpty, cloneDeep, isEqual, get, unionBy, sortBy, isArray, set, merge, capitalize } from 'lodash-es';
27
- import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals';
28
27
  import * as i1$3 from '@acorex/components/badge';
29
28
  import { AXBadgeComponent, AXBadgeModule } from '@acorex/components/badge';
29
+ import { AXPLayoutThemeService, AXPMenuVerticalMode } from '@acorex/platform/themes/shared';
30
+ import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals';
30
31
  import * as i6 from '@acorex/components/dropdown';
31
32
  import { AXDropdownModule } from '@acorex/components/dropdown';
32
33
  import * as i5 from '@acorex/components/search-box';
@@ -64,7 +65,7 @@ import { AXSelectionListModule } from '@acorex/components/selection-list';
64
65
  import * as i5$1 from '@acorex/components/tag-box';
65
66
  import { AXTagBoxModule } from '@acorex/components/tag-box';
66
67
  import { AXCalendarService } from '@acorex/core/date-time';
67
- import { AXPFilterOperatorMiddlewareService, AXPSettingsService, ALL_DEFAULT_OPERATORS, AXPFileStorageService, withDefaultMultiLanguageOnWidgetProperty, AXPDefaultMultiLanguageConfigService } from '@acorex/platform/common';
68
+ import { AXPFilterOperatorMiddlewareService, AXPSettingsService, ALL_DEFAULT_OPERATORS, AXPFileStorageService, withDefaultMultiLanguageOnWidgetProperty, AXPDefaultMultiLanguageConfigService, AXP_PLATFORM_CONFIG_TOKEN } from '@acorex/platform/common';
68
69
  import { Subject, filter, BehaviorSubject, timer, takeUntil } from 'rxjs';
69
70
  import * as i1$7 from '@acorex/components/image-editor';
70
71
  import { AXImageEditorContainerComponent, AXImageEditorModule } from '@acorex/components/image-editor';
@@ -381,17 +382,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
381
382
  class AXPTaskBadgeDirective {
382
383
  constructor() {
383
384
  this.badgeService = inject(AXPTaskBadgeService);
384
- this.host = inject(AXBadgeComponent); // assumes it's applied on <ax-badge>
385
+ this.host = inject(AXBadgeComponent);
385
386
  this.hostElement = this.host.getHostElement();
387
+ this.themeStore = inject(AXPLayoutThemeService);
386
388
  this.badgeKey = input(undefined, ...(ngDevMode ? [{ debugName: "badgeKey" }] : /* istanbul ignore next */ []));
387
389
  this.count = input(0, { ...(ngDevMode ? { debugName: "count" } : /* istanbul ignore next */ {}), alias: 'axp-task-badge' });
388
- this.displayCount = computed(() => {
389
- const value = this.count() || (this.badgeKey() && this.badgeService.getCount(this.badgeKey())());
390
- return value ? value.toString() : '';
391
- }, ...(ngDevMode ? [{ debugName: "displayCount" }] : /* istanbul ignore next */ []));
390
+ this.actualCount = computed(() => {
391
+ const inputCount = this.count();
392
+ if (inputCount) {
393
+ return inputCount;
394
+ }
395
+ const key = this.badgeKey();
396
+ if (!key) {
397
+ return 0;
398
+ }
399
+ return this.badgeService.getCount(key)();
400
+ }, ...(ngDevMode ? [{ debugName: "actualCount" }] : /* istanbul ignore next */ []));
401
+ this.badgeText = computed(() => {
402
+ const count = this.actualCount();
403
+ if (!count) {
404
+ return '';
405
+ }
406
+ const isCompact = this.themeStore.menuVerticalMode() === 'compact';
407
+ return isCompact ? '' : count.toString();
408
+ }, ...(ngDevMode ? [{ debugName: "badgeText" }] : /* istanbul ignore next */ []));
392
409
  this.#eff = effect(() => {
393
- this.setValueOfInputSignal(this.host.text, this.displayCount());
394
- if (!this.displayCount()) {
410
+ const count = this.actualCount();
411
+ this.setValueOfInputSignal(this.host.text, this.badgeText());
412
+ if (!count) {
395
413
  this.hostElement.style.display = 'none';
396
414
  }
397
415
  else {
@@ -991,9 +1009,9 @@ class AXPCategoryTreeComponent {
991
1009
  };
992
1010
  this.collapseChange.emit(entity);
993
1011
  this.events().onCollapseChange?.(entity);
994
- // Load children if node is being expanded
995
- // Check both expand property and active property (active seems to be the actual expansion state)
996
- if ((node.expand || node.active) && this.dataSource().loadChildNodes) {
1012
+ // Load children when the node has not been loaded yet (independent of expand event timing).
1013
+ const hasUnloadedChildren = (node.childrenCount ?? 0) > 0 && (!node.children || node.children.length === 0);
1014
+ if (node.id !== 'root' && hasUnloadedChildren && this.dataSource().loadChildNodes) {
997
1015
  await this.loadChildNodes(node.id);
998
1016
  }
999
1017
  }
@@ -1123,7 +1141,7 @@ class AXPCategoryTreeComponent {
1123
1141
  this.isLoading.set(false);
1124
1142
  }
1125
1143
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCategoryTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1126
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPCategoryTreeComponent, isStandalone: true, selector: "axp-category-tree", inputs: { dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, events: { classPropertyName: "events", publicName: "events", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nodeClick: "nodeClick", nodeSelect: "nodeSelect", nodeCreate: "nodeCreate", nodeUpdate: "nodeUpdate", nodeDelete: "nodeDelete", searchChange: "searchChange", collapseChange: "collapseChange" }, viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true, isSignal: true }], ngImport: i0, template: "<axp-layout-header>\n <axp-layout-toolbar>\n @if (config().searchable) {\n <ax-search-box\n (onValueChanged)=\"handleSearchChange($event)\"\n [delayTime]=\"300\"\n [placeholder]=\"config().searchPlaceholder! | translate | async\"\n >\n </ax-search-box>\n }\n </axp-layout-toolbar>\n</axp-layout-header>\n\n<axp-layout-content>\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-flex ax-flex-col ax-gap-3\" [class.ax-pt-2]=\"config().searchable\">\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 (treeItems()[0].children?.length) {\n <div class=\"ax-px-4 ax-max-h-[calc(100vh-250px)] ax-overflow-auto\" [class.ax-pt-2]=\"config().searchable\">\n <ax-tree-view-legacy\n (onNodeClick)=\"handleNodeClick($event)\"\n (onCollapsedChanged)=\"handleCollapseChanged($event)\"\n [showCheckbox]=\"config().showCheckbox || false\"\n [itemTemplate]=\"itemTemplate\"\n #treeRef\n [textField]=\"config().textField || 'title'\"\n [valueField]=\"config().valueField || 'id'\"\n [expandedField]=\"config().expandedField || 'expand'\"\n [items]=\"treeItems()\"\n #tree\n >\n </ax-tree-view-legacy>\n </div>\n } @else {\n <axp-state-message\n [icon]=\"config().emptyStateIcon || 'fa-light fa-folder-open'\"\n [title]=\"emptyStateTitle()\"\n [description]=\"emptyStateDescription()\"\n >\n @if (actions().canCreate) {\n <ax-button\n slot=\"actions\"\n (onClick)=\"handleCreateRootClick($event)\"\n look=\"solid\"\n color=\"primary\"\n [text]=\"(actions().createLabel || '@general:actions.add-new.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n }\n </axp-state-message>\n }\n\n <ng-template #itemTemplate let-item>\n @let textField = config().textField || 'title';\n @let rawTitle = item[textField] ?? item.title;\n <div class=\"ax-flex ax-items-center ax-justify-between ax-w-full ax-gap-2 ax-overflow-hidden\">\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-min-w-0\">\n <ax-icon class=\"fas ax-text-warning fa-folder\"></ax-icon>\n <span class=\"ax-truncate\">\n @if (item.id === 'root') {\n {{ categoryTreeRootTitleI18nKey | translate | async }}\n } @else {\n {{ rawTitle | translate | async }}\n }\n </span>\n </div>\n @if (item.id && item.id !== 'root') {\n @if (actions().canCreateChild || actions().canUpdate || actions().canDelete) {\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 @if (actions().canCreateChild) {\n <ax-button-item\n (onClick)=\"handleCreateChildClick(item, $event)\"\n look=\"blank\"\n color=\"default\"\n [text]=\"\n (actions().createChildLabel || '@general:actions.add-new-child.title' | translate | async)!\n \"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button-item>\n }\n @if (actions().canUpdate) {\n <ax-button-item\n (onClick)=\"handleUpdateNodeClick(item, $event)\"\n look=\"blank\"\n [text]=\"(actions().updateLabel || '@general:actions.edit.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-pen\"></ax-icon>\n </ax-button-item>\n }\n @if (actions().canDelete) {\n <ax-button-item\n (onClick)=\"handleDeleteNodeClick(item, $event)\"\n color=\"danger\"\n look=\"blank\"\n [text]=\"(actions().deleteLabel || '@general:actions.delete.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-trash\"></ax-icon>\n </ax-button-item>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n }\n } @else if (item.id === 'root' && actions().canCreate) {\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$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTreeViewLegacyModule }, { kind: "component", type: i2$2.AXTreeViewLegacyComponent, selector: "ax-tree-view-legacy", inputs: ["items", "showCheckbox", "hasCheckboxField", "selectionMode", "selectionBehavior", "selectionScope", "focusNodeEnabled", "valueField", "textField", "visibleField", "disableField", "hasChildField", "selectedField", "expandedField", "tooltipField", "childrenField", "activeField", "indeterminateField", "parentField", "iconField", "toggleIcons", "look", "showEmptyNodeMassage", "itemTemplate", "emptyTemplate", "expandOn"], outputs: ["onSelectionChanged", "onItemSelectedChanged", "onNodeClick", "onCollapsedChanged", "onNodedbClick"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: "\n\n axp-page-content, \n axp-page-footer-container,\n axp-page-footer,\n axp-page-header,\n axp-page-header-container,\n axp-page-toolbar,\n\n axp-layout-content, \n axp-layout-page-content, \n\n axp-layout-sections,\n axp-layout-body,\n axp-layout-page-body,\n axp-layout-prefix,\n axp-layout-suffix,\n axp-layout-title-bar,\n axp-layout-title, \n axp-layout-title-actions, \n axp-layout-nav-button, \n axp-layout-description, \n axp-layout-breadcrumbs,\n axp-layout-list-action,\n " }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i5.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: i1$2.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$2.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1$2.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { 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: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
1144
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPCategoryTreeComponent, isStandalone: true, selector: "axp-category-tree", inputs: { dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, events: { classPropertyName: "events", publicName: "events", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nodeClick: "nodeClick", nodeSelect: "nodeSelect", nodeCreate: "nodeCreate", nodeUpdate: "nodeUpdate", nodeDelete: "nodeDelete", searchChange: "searchChange", collapseChange: "collapseChange" }, viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true, isSignal: true }], ngImport: i0, template: "<axp-layout-header>\n <axp-layout-toolbar>\n @if (config().searchable) {\n <ax-search-box\n (onValueChanged)=\"handleSearchChange($event)\"\n [delayTime]=\"300\"\n [placeholder]=\"config().searchPlaceholder! | translate | async\"\n >\n </ax-search-box>\n }\n </axp-layout-toolbar>\n</axp-layout-header>\n\n<axp-layout-content>\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-flex ax-flex-col ax-gap-3\" [class.ax-pt-2]=\"config().searchable\">\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 (treeItems()[0].children?.length) {\n <div class=\"ax-px-4 ax-flex-1 ax-min-h-0 ax-min-w-0 ax-overflow-y-auto ax-overflow-x-hidden\" [class.ax-pt-2]=\"config().searchable\">\n <ax-tree-view-legacy\n (onNodeClick)=\"handleNodeClick($event)\"\n (onCollapsedChanged)=\"handleCollapseChanged($event)\"\n [showCheckbox]=\"config().showCheckbox || false\"\n [itemTemplate]=\"itemTemplate\"\n #treeRef\n [textField]=\"config().textField || 'title'\"\n [valueField]=\"config().valueField || 'id'\"\n [expandedField]=\"config().expandedField || 'expand'\"\n [items]=\"treeItems()\"\n #tree\n >\n </ax-tree-view-legacy>\n </div>\n } @else {\n <axp-state-message\n [icon]=\"config().emptyStateIcon || 'fa-light fa-folder-open'\"\n [title]=\"emptyStateTitle()\"\n [description]=\"emptyStateDescription()\"\n >\n @if (actions().canCreate) {\n <ax-button\n slot=\"actions\"\n (onClick)=\"handleCreateRootClick($event)\"\n look=\"solid\"\n color=\"primary\"\n [text]=\"(actions().createLabel || '@general:actions.add-new.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n }\n </axp-state-message>\n }\n\n <ng-template #itemTemplate let-item>\n @let textField = config().textField || 'title';\n @let rawTitle = item[textField] ?? item.title;\n <div class=\"ax-flex ax-items-center ax-justify-between ax-w-full ax-gap-2 ax-overflow-hidden\">\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-min-w-0\">\n <ax-icon class=\"fas ax-text-warning fa-folder\"></ax-icon>\n <span class=\"ax-truncate\">\n @if (item.id === 'root') {\n {{ categoryTreeRootTitleI18nKey | translate | async }}\n } @else {\n {{ rawTitle | translate | async }}\n }\n </span>\n </div>\n @if (item.id && item.id !== 'root') {\n @if (actions().canCreateChild || actions().canUpdate || actions().canDelete) {\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 @if (actions().canCreateChild) {\n <ax-button-item\n (onClick)=\"handleCreateChildClick(item, $event)\"\n look=\"blank\"\n color=\"default\"\n [text]=\"\n (actions().createChildLabel || '@general:actions.add-new-child.title' | translate | async)!\n \"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button-item>\n }\n @if (actions().canUpdate) {\n <ax-button-item\n (onClick)=\"handleUpdateNodeClick(item, $event)\"\n look=\"blank\"\n [text]=\"(actions().updateLabel || '@general:actions.edit.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-pen\"></ax-icon>\n </ax-button-item>\n }\n @if (actions().canDelete) {\n <ax-button-item\n (onClick)=\"handleDeleteNodeClick(item, $event)\"\n color=\"danger\"\n look=\"blank\"\n [text]=\"(actions().deleteLabel || '@general:actions.delete.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-trash\"></ax-icon>\n </ax-button-item>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n }\n } @else if (item.id === 'root' && actions().canCreate) {\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: [":host{display:flex;flex-direction:column;height:100%;min-height:0;min-width:0;overflow:hidden}axp-layout-content{display:flex;flex:1;flex-direction:column;min-height:0;min-width:0;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTreeViewLegacyModule }, { kind: "component", type: i2$2.AXTreeViewLegacyComponent, selector: "ax-tree-view-legacy", inputs: ["items", "showCheckbox", "hasCheckboxField", "selectionMode", "selectionBehavior", "selectionScope", "focusNodeEnabled", "valueField", "textField", "visibleField", "disableField", "hasChildField", "selectedField", "expandedField", "tooltipField", "childrenField", "activeField", "indeterminateField", "parentField", "iconField", "toggleIcons", "look", "showEmptyNodeMassage", "itemTemplate", "emptyTemplate", "expandOn"], outputs: ["onSelectionChanged", "onItemSelectedChanged", "onNodeClick", "onCollapsedChanged", "onNodedbClick"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: "\n\n axp-page-content, \n axp-page-footer-container,\n axp-page-footer,\n axp-page-header,\n axp-page-header-container,\n axp-page-toolbar,\n\n axp-layout-content, \n axp-layout-page-content, \n\n axp-layout-sections,\n axp-layout-body,\n axp-layout-page-body,\n axp-layout-prefix,\n axp-layout-suffix,\n axp-layout-title-bar,\n axp-layout-title, \n axp-layout-title-actions, \n axp-layout-nav-button, \n axp-layout-description, \n axp-layout-breadcrumbs,\n axp-layout-list-action,\n " }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i5.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: i1$2.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$2.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1$2.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { 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: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
1127
1145
  }
1128
1146
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCategoryTreeComponent, decorators: [{
1129
1147
  type: Component,
@@ -1140,7 +1158,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1140
1158
  AXButtonModule,
1141
1159
  AXDropdownModule,
1142
1160
  AXPStateMessageComponent,
1143
- ], template: "<axp-layout-header>\n <axp-layout-toolbar>\n @if (config().searchable) {\n <ax-search-box\n (onValueChanged)=\"handleSearchChange($event)\"\n [delayTime]=\"300\"\n [placeholder]=\"config().searchPlaceholder! | translate | async\"\n >\n </ax-search-box>\n }\n </axp-layout-toolbar>\n</axp-layout-header>\n\n<axp-layout-content>\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-flex ax-flex-col ax-gap-3\" [class.ax-pt-2]=\"config().searchable\">\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 (treeItems()[0].children?.length) {\n <div class=\"ax-px-4 ax-max-h-[calc(100vh-250px)] ax-overflow-auto\" [class.ax-pt-2]=\"config().searchable\">\n <ax-tree-view-legacy\n (onNodeClick)=\"handleNodeClick($event)\"\n (onCollapsedChanged)=\"handleCollapseChanged($event)\"\n [showCheckbox]=\"config().showCheckbox || false\"\n [itemTemplate]=\"itemTemplate\"\n #treeRef\n [textField]=\"config().textField || 'title'\"\n [valueField]=\"config().valueField || 'id'\"\n [expandedField]=\"config().expandedField || 'expand'\"\n [items]=\"treeItems()\"\n #tree\n >\n </ax-tree-view-legacy>\n </div>\n } @else {\n <axp-state-message\n [icon]=\"config().emptyStateIcon || 'fa-light fa-folder-open'\"\n [title]=\"emptyStateTitle()\"\n [description]=\"emptyStateDescription()\"\n >\n @if (actions().canCreate) {\n <ax-button\n slot=\"actions\"\n (onClick)=\"handleCreateRootClick($event)\"\n look=\"solid\"\n color=\"primary\"\n [text]=\"(actions().createLabel || '@general:actions.add-new.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n }\n </axp-state-message>\n }\n\n <ng-template #itemTemplate let-item>\n @let textField = config().textField || 'title';\n @let rawTitle = item[textField] ?? item.title;\n <div class=\"ax-flex ax-items-center ax-justify-between ax-w-full ax-gap-2 ax-overflow-hidden\">\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-min-w-0\">\n <ax-icon class=\"fas ax-text-warning fa-folder\"></ax-icon>\n <span class=\"ax-truncate\">\n @if (item.id === 'root') {\n {{ categoryTreeRootTitleI18nKey | translate | async }}\n } @else {\n {{ rawTitle | translate | async }}\n }\n </span>\n </div>\n @if (item.id && item.id !== 'root') {\n @if (actions().canCreateChild || actions().canUpdate || actions().canDelete) {\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 @if (actions().canCreateChild) {\n <ax-button-item\n (onClick)=\"handleCreateChildClick(item, $event)\"\n look=\"blank\"\n color=\"default\"\n [text]=\"\n (actions().createChildLabel || '@general:actions.add-new-child.title' | translate | async)!\n \"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button-item>\n }\n @if (actions().canUpdate) {\n <ax-button-item\n (onClick)=\"handleUpdateNodeClick(item, $event)\"\n look=\"blank\"\n [text]=\"(actions().updateLabel || '@general:actions.edit.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-pen\"></ax-icon>\n </ax-button-item>\n }\n @if (actions().canDelete) {\n <ax-button-item\n (onClick)=\"handleDeleteNodeClick(item, $event)\"\n color=\"danger\"\n look=\"blank\"\n [text]=\"(actions().deleteLabel || '@general:actions.delete.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-trash\"></ax-icon>\n </ax-button-item>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n }\n } @else if (item.id === 'root' && actions().canCreate) {\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" }]
1161
+ ], template: "<axp-layout-header>\n <axp-layout-toolbar>\n @if (config().searchable) {\n <ax-search-box\n (onValueChanged)=\"handleSearchChange($event)\"\n [delayTime]=\"300\"\n [placeholder]=\"config().searchPlaceholder! | translate | async\"\n >\n </ax-search-box>\n }\n </axp-layout-toolbar>\n</axp-layout-header>\n\n<axp-layout-content>\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-flex ax-flex-col ax-gap-3\" [class.ax-pt-2]=\"config().searchable\">\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 (treeItems()[0].children?.length) {\n <div class=\"ax-px-4 ax-flex-1 ax-min-h-0 ax-min-w-0 ax-overflow-y-auto ax-overflow-x-hidden\" [class.ax-pt-2]=\"config().searchable\">\n <ax-tree-view-legacy\n (onNodeClick)=\"handleNodeClick($event)\"\n (onCollapsedChanged)=\"handleCollapseChanged($event)\"\n [showCheckbox]=\"config().showCheckbox || false\"\n [itemTemplate]=\"itemTemplate\"\n #treeRef\n [textField]=\"config().textField || 'title'\"\n [valueField]=\"config().valueField || 'id'\"\n [expandedField]=\"config().expandedField || 'expand'\"\n [items]=\"treeItems()\"\n #tree\n >\n </ax-tree-view-legacy>\n </div>\n } @else {\n <axp-state-message\n [icon]=\"config().emptyStateIcon || 'fa-light fa-folder-open'\"\n [title]=\"emptyStateTitle()\"\n [description]=\"emptyStateDescription()\"\n >\n @if (actions().canCreate) {\n <ax-button\n slot=\"actions\"\n (onClick)=\"handleCreateRootClick($event)\"\n look=\"solid\"\n color=\"primary\"\n [text]=\"(actions().createLabel || '@general:actions.add-new.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button>\n }\n </axp-state-message>\n }\n\n <ng-template #itemTemplate let-item>\n @let textField = config().textField || 'title';\n @let rawTitle = item[textField] ?? item.title;\n <div class=\"ax-flex ax-items-center ax-justify-between ax-w-full ax-gap-2 ax-overflow-hidden\">\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-min-w-0\">\n <ax-icon class=\"fas ax-text-warning fa-folder\"></ax-icon>\n <span class=\"ax-truncate\">\n @if (item.id === 'root') {\n {{ categoryTreeRootTitleI18nKey | translate | async }}\n } @else {\n {{ rawTitle | translate | async }}\n }\n </span>\n </div>\n @if (item.id && item.id !== 'root') {\n @if (actions().canCreateChild || actions().canUpdate || actions().canDelete) {\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 @if (actions().canCreateChild) {\n <ax-button-item\n (onClick)=\"handleCreateChildClick(item, $event)\"\n look=\"blank\"\n color=\"default\"\n [text]=\"\n (actions().createChildLabel || '@general:actions.add-new-child.title' | translate | async)!\n \"\n >\n <ax-icon class=\"fas fa-plus\"></ax-icon>\n </ax-button-item>\n }\n @if (actions().canUpdate) {\n <ax-button-item\n (onClick)=\"handleUpdateNodeClick(item, $event)\"\n look=\"blank\"\n [text]=\"(actions().updateLabel || '@general:actions.edit.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-pen\"></ax-icon>\n </ax-button-item>\n }\n @if (actions().canDelete) {\n <ax-button-item\n (onClick)=\"handleDeleteNodeClick(item, $event)\"\n color=\"danger\"\n look=\"blank\"\n [text]=\"(actions().deleteLabel || '@general:actions.delete.title' | translate | async)!\"\n >\n <ax-icon class=\"fas fa-trash\"></ax-icon>\n </ax-button-item>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n }\n } @else if (item.id === 'root' && actions().canCreate) {\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: [":host{display:flex;flex-direction:column;height:100%;min-height:0;min-width:0;overflow:hidden}axp-layout-content{display:flex;flex:1;flex-direction:column;min-height:0;min-width:0;overflow:hidden}\n"] }]
1144
1162
  }], propDecorators: { dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: true }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], events: [{ type: i0.Input, args: [{ isSignal: true, alias: "events", required: false }] }], nodeClick: [{ type: i0.Output, args: ["nodeClick"] }], nodeSelect: [{ type: i0.Output, args: ["nodeSelect"] }], nodeCreate: [{ type: i0.Output, args: ["nodeCreate"] }], nodeUpdate: [{ type: i0.Output, args: ["nodeUpdate"] }], nodeDelete: [{ type: i0.Output, args: ["nodeDelete"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], collapseChange: [{ type: i0.Output, args: ["collapseChange"] }], tree: [{ type: i0.ViewChild, args: ['tree', { isSignal: true }] }] } });
1145
1163
 
1146
1164
  //#region ---- Tree Node Interface ----
@@ -1574,14 +1592,9 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1574
1592
  this.config = signal({
1575
1593
  title: '',
1576
1594
  dataSource: null,
1577
- columns: [],
1578
- selectionMode: 'single',
1579
- searchFields: [],
1580
- initialSearchTerm: undefined,
1581
- parentField: undefined,
1582
- allowCreate: 'none',
1583
- filters: undefined,
1584
- selectedItemIds: undefined
1595
+ grid: { columns: [] },
1596
+ selection: { mode: 'single' },
1597
+ create: { mode: 'none' },
1585
1598
  }, ...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
1586
1599
  this.searchTerm = '';
1587
1600
  this.searchPlaceholderText = signal('', ...(ngDevMode ? [{ debugName: "searchPlaceholderText" }] : /* istanbul ignore next */ []));
@@ -1590,6 +1603,8 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1590
1603
  this.initialSelectionApplied = false;
1591
1604
  this.userHasInteracted = false;
1592
1605
  this.isApplyingInitialSelection = false;
1606
+ /** Suppresses stale grid selection events while a single-mode row click is being applied. */
1607
+ this.rowClickSelectionSync = false;
1593
1608
  this.initialSelectedItems = []; // Track initial selection to calculate difference
1594
1609
  // Category Filter State
1595
1610
  this.activeCategoryFilter = signal(null, ...(ngDevMode ? [{ debugName: "activeCategoryFilter" }] : /* istanbul ignore next */ []));
@@ -1599,12 +1614,23 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1599
1614
  //#region ---- Services & Dependencies ----
1600
1615
  this.translationService = inject(AXTranslationService);
1601
1616
  //#region ---- Computed Properties ----
1602
- this.allowSelect = computed(() => this.selectedItems().length > 0, ...(ngDevMode ? [{ debugName: "allowSelect" }] : /* istanbul ignore next */ []));
1617
+ this.allowUnselect = computed(() => this.config().selection?.allowUnselect ?? true, ...(ngDevMode ? [{ debugName: "allowUnselect" }] : /* istanbul ignore next */ []));
1618
+ this.allowSelect = computed(() => {
1619
+ if (this.allowUnselect()) {
1620
+ return true;
1621
+ }
1622
+ return this.selectedItems().length > 0;
1623
+ }, ...(ngDevMode ? [{ debugName: "allowSelect" }] : /* istanbul ignore next */ []));
1603
1624
  this.selectedCount = computed(() => this.selectedItems().length, ...(ngDevMode ? [{ debugName: "selectedCount" }] : /* istanbul ignore next */ []));
1604
1625
  this.hasSearch = computed(() => {
1605
- const searchFields = this.config().searchFields;
1626
+ const searchFields = this.config().search?.fields;
1606
1627
  return searchFields && searchFields.length > 0;
1607
1628
  }, ...(ngDevMode ? [{ debugName: "hasSearch" }] : /* istanbul ignore next */ []));
1629
+ this.selectionMode = computed(() => this.config().selection?.mode ?? 'single', ...(ngDevMode ? [{ debugName: "selectionMode" }] : /* istanbul ignore next */ []));
1630
+ this.categoryEnabled = computed(() => {
1631
+ const category = this.config().category;
1632
+ return !!category && category.enabled !== false && !!category.dataSource;
1633
+ }, ...(ngDevMode ? [{ debugName: "categoryEnabled" }] : /* istanbul ignore next */ []));
1608
1634
  // Category Tree Configuration
1609
1635
  this.categoryTreeConfig = computed(() => ({
1610
1636
  textField: 'title',
@@ -1623,12 +1649,26 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1623
1649
  canDelete: false,
1624
1650
  canCreateChild: false,
1625
1651
  }), ...(ngDevMode ? [{ debugName: "categoryTreeActions" }] : /* istanbul ignore next */ []));
1652
+ this.hasRowActionPrimary = computed(() => this.config().rowActions?.hasPrimary() ?? false, ...(ngDevMode ? [{ debugName: "hasRowActionPrimary" }] : /* istanbul ignore next */ []));
1653
+ this.hasRowActionDropdown = computed(() => this.config().rowActions?.hasDropdown() ?? false, ...(ngDevMode ? [{ debugName: "hasRowActionDropdown" }] : /* istanbul ignore next */ []));
1654
+ this.primaryRowActionItems = computed(() => (this.config().rowActions?.getPrimaryItems() ?? []), ...(ngDevMode ? [{ debugName: "primaryRowActionItems" }] : /* istanbul ignore next */ []));
1655
+ this.primaryRowActionColumnWidth = computed(() => {
1656
+ const count = this.primaryRowActionItems().length;
1657
+ return count > 0 ? `${count * 70}px` : '60px';
1658
+ }, ...(ngDevMode ? [{ debugName: "primaryRowActionColumnWidth" }] : /* istanbul ignore next */ []));
1659
+ this.getDropdownRowActionItems = (rowData) => {
1660
+ const handler = this.config().rowActions;
1661
+ if (!handler) {
1662
+ return Promise.resolve([]);
1663
+ }
1664
+ return handler.getDropdownItems(rowData).then((items) => items);
1665
+ };
1626
1666
  effect(() => {
1627
- const searchFields = this.config().searchFields;
1667
+ const searchFields = this.config().search?.fields;
1628
1668
  void this.updateSearchPlaceholder(searchFields);
1629
1669
  });
1630
1670
  effect(() => {
1631
- const initialTerm = this.config().initialSearchTerm ?? '';
1671
+ const initialTerm = this.config().search?.initial ?? '';
1632
1672
  if (!this.initialSearchApplied && initialTerm && initialTerm.trim().length > 0) {
1633
1673
  this.initialSearchApplied = true;
1634
1674
  this.searchTerm = initialTerm;
@@ -1658,23 +1698,32 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1658
1698
  this.close({ items: [e.data] });
1659
1699
  }
1660
1700
  handleRowClick(e) {
1661
- // Mark user interaction
1662
- this.userHasInteracted = true;
1663
- // In single selection mode, update selectedItems directly since grid doesn't fire selectedRowsChange
1664
- if (this.config().selectionMode === 'single') {
1665
- this.selectedItems.set([e.data]);
1701
+ if (!this.isSingleSelectionMode()) {
1702
+ return;
1666
1703
  }
1667
- // In multiple mode, selection is handled by the grid via checkboxes
1668
- // The grid will fire handleSelectedRowsChange which updates our signal
1704
+ const clicked = e.data;
1705
+ const current = this.selectedItems();
1706
+ const clickedKey = this.getRowKey(clicked);
1707
+ const isAlreadySelected = current.length === 1 && this.getRowKey(current[0]) === clickedKey;
1708
+ const nextSelection = this.allowUnselect() && isAlreadySelected ? [] : [clicked];
1709
+ this.rowClickSelectionSync = true;
1710
+ this.applySingleSelection(nextSelection, true);
1669
1711
  }
1670
1712
  async handleSelectedRowsChange(rows) {
1671
- const newSelection = rows || [];
1672
- // If we're applying initial selection, just update the signal
1673
- // Otherwise, mark as user interaction and update
1713
+ if (this.rowClickSelectionSync) {
1714
+ return;
1715
+ }
1716
+ let newSelection = rows || [];
1717
+ if (this.isSingleSelectionMode()) {
1718
+ const clamped = this.clampSingleSelection(newSelection);
1719
+ if (!isEqual(clamped, newSelection) && !this.isApplyingInitialSelection) {
1720
+ this.syncGridSingleSelection(clamped);
1721
+ }
1722
+ newSelection = clamped;
1723
+ }
1674
1724
  if (!this.isApplyingInitialSelection) {
1675
1725
  this.userHasInteracted = true;
1676
1726
  }
1677
- // Always update selectedItems from data table selection
1678
1727
  this.selectedItems.set(newSelection);
1679
1728
  }
1680
1729
  handleChangeSearchValue(e) {
@@ -1685,12 +1734,12 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1685
1734
  }
1686
1735
  }
1687
1736
  async handleCreateNewClick(mode) {
1688
- const onCreate = this.config().onCreate;
1737
+ const onCreate = this.config().create?.onCreate;
1689
1738
  if (!onCreate)
1690
1739
  return;
1691
1740
  const createdItem = await onCreate(mode);
1692
1741
  if (createdItem) {
1693
- if (this.config().selectionMode === 'multiple') {
1742
+ if (this.selectionMode() === 'multiple') {
1694
1743
  const currentSelection = this.selectedItems();
1695
1744
  const newSelection = [...currentSelection, createdItem];
1696
1745
  this.selectedItems.set(newSelection);
@@ -1718,9 +1767,26 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1718
1767
  await this.grid().expandRow(row);
1719
1768
  }
1720
1769
  }
1770
+ async handleRowActionClick(event) {
1771
+ const handler = this.config().rowActions;
1772
+ if (!handler || this.grid()?.dataSource.isLoading) {
1773
+ return;
1774
+ }
1775
+ if (event.setLoading) {
1776
+ event.setLoading(true);
1777
+ }
1778
+ try {
1779
+ await handler.execute(event.name, event.data);
1780
+ }
1781
+ finally {
1782
+ if (event.setLoading) {
1783
+ event.setLoading(false);
1784
+ }
1785
+ }
1786
+ }
1721
1787
  // Category Filter Event Handlers
1722
1788
  handleCategoryFilterClick(node) {
1723
- const categoryConfig = this.config().categoryFilter;
1789
+ const categoryConfig = this.config().category;
1724
1790
  // Get the filter value from the node
1725
1791
  const filterValue = node.id;
1726
1792
  // Store active filter state
@@ -1734,7 +1800,7 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1734
1800
  clearCategoryFilter() {
1735
1801
  this.activeCategoryFilter.set(null);
1736
1802
  // Apply only base filters (without category filter)
1737
- const baseFilters = this.config().filters;
1803
+ const baseFilters = this.config().filter?.value;
1738
1804
  if (baseFilters) {
1739
1805
  this.config().dataSource.filter(baseFilters);
1740
1806
  }
@@ -1745,15 +1811,75 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1745
1811
  }
1746
1812
  //#endregion
1747
1813
  //#region ---- Private Methods ----
1814
+ getRowKey(row) {
1815
+ const dataSource = this.config().dataSource;
1816
+ const keyField = dataSource.config?.key ?? 'id';
1817
+ return String(row?.[keyField] ?? '');
1818
+ }
1819
+ isSingleSelectionMode() {
1820
+ return this.selectionMode() === 'single';
1821
+ }
1822
+ applySingleSelection(selection, force = false) {
1823
+ const clamped = this.clampSingleSelection(selection);
1824
+ if (!force && isEqual(clamped, this.selectedItems())) {
1825
+ this.clearRowClickSelectionSync();
1826
+ return;
1827
+ }
1828
+ this.userHasInteracted = true;
1829
+ this.selectedItems.set(clamped);
1830
+ this.syncGridSingleSelection(clamped);
1831
+ }
1832
+ clearRowClickSelectionSync() {
1833
+ this.rowClickSelectionSync = false;
1834
+ }
1835
+ /**
1836
+ * Keeps at most one row when single mode uses checkboxes.
1837
+ */
1838
+ clampSingleSelection(newSelection) {
1839
+ if (newSelection.length <= 1) {
1840
+ return newSelection;
1841
+ }
1842
+ const prev = this.selectedItems();
1843
+ const prevKeys = new Set(prev.map((row) => this.getRowKey(row)));
1844
+ const added = newSelection.filter((row) => !prevKeys.has(this.getRowKey(row)));
1845
+ if (added.length > 0) {
1846
+ return [added[added.length - 1]];
1847
+ }
1848
+ return [newSelection[newSelection.length - 1]];
1849
+ }
1850
+ syncGridSingleSelection(selection) {
1851
+ const grid = this.grid();
1852
+ if (!grid) {
1853
+ this.clearRowClickSelectionSync();
1854
+ return;
1855
+ }
1856
+ queueMicrotask(() => {
1857
+ this.isApplyingInitialSelection = true;
1858
+ try {
1859
+ if (selection.length === 1 && grid.selectRows) {
1860
+ grid.selectRows(selection[0]);
1861
+ }
1862
+ else if (selection.length === 0) {
1863
+ grid.selectedRows = [];
1864
+ }
1865
+ }
1866
+ finally {
1867
+ setTimeout(() => {
1868
+ this.isApplyingInitialSelection = false;
1869
+ this.clearRowClickSelectionSync();
1870
+ }, 0);
1871
+ }
1872
+ });
1873
+ }
1748
1874
  applySearchFilter(searchTerm) {
1749
- const searchFields = this.config().searchFields;
1875
+ const searchFields = this.config().search?.fields;
1750
1876
  if (!searchFields || searchFields.length === 0) {
1751
1877
  return;
1752
1878
  }
1753
1879
  const normalizedTerm = isNil(searchTerm) ? '' : `${searchTerm}`.trim();
1754
1880
  if (isEmpty(normalizedTerm)) {
1755
1881
  // Apply only base filters when search is cleared
1756
- const baseFilters = this.config().filters;
1882
+ const baseFilters = this.config().filter?.value;
1757
1883
  if (baseFilters) {
1758
1884
  this.config().dataSource.filter(baseFilters);
1759
1885
  }
@@ -1764,7 +1890,7 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1764
1890
  return;
1765
1891
  }
1766
1892
  // Get base filters from config
1767
- const baseFilters = this.config().filters;
1893
+ const baseFilters = this.config().filter?.value;
1768
1894
  // Create search filters for all configured fields
1769
1895
  const searchFilters = searchFields.map((field) => ({
1770
1896
  field: field.name,
@@ -1791,7 +1917,7 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1791
1917
  }
1792
1918
  applyFilterAndSort() {
1793
1919
  // Apply base filters from config
1794
- const baseFilters = this.config().filters;
1920
+ const baseFilters = this.config().filter?.value;
1795
1921
  if (baseFilters) {
1796
1922
  this.config().dataSource.filter(baseFilters);
1797
1923
  this.config().dataSource.refresh();
@@ -1799,7 +1925,7 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1799
1925
  }
1800
1926
  applyCategoryFilter(filterValue, categoryConfig) {
1801
1927
  if (isNil(filterValue) || filterValue === 'root') {
1802
- const baseFilters = this.config().filters;
1928
+ const baseFilters = this.config().filter?.value;
1803
1929
  if (baseFilters) {
1804
1930
  this.config().dataSource.filter(baseFilters);
1805
1931
  }
@@ -1810,12 +1936,12 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1810
1936
  return;
1811
1937
  }
1812
1938
  // Get base filters from config
1813
- const baseFilters = this.config().filters;
1939
+ const baseFilters = this.config().filter?.value;
1814
1940
  // Create category filter
1815
1941
  const categoryFilter = {
1816
- field: categoryConfig.filterField,
1942
+ field: categoryConfig.filter.field,
1817
1943
  value: filterValue,
1818
- operator: { type: categoryConfig.filterOperator || 'contains' }
1944
+ operator: { type: categoryConfig.filter.operator || 'contains' }
1819
1945
  };
1820
1946
  // Merge base filters with category filter
1821
1947
  const allFilters = baseFilters ? [baseFilters, categoryFilter] : [categoryFilter];
@@ -1866,7 +1992,7 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1866
1992
  if (this.initialSelectionApplied || this.userHasInteracted) {
1867
1993
  return;
1868
1994
  }
1869
- const selectedIds = this.config().selectedItemIds;
1995
+ const selectedIds = this.config().selection?.selectedItemIds;
1870
1996
  if (!selectedIds || selectedIds.length === 0) {
1871
1997
  // No initial selection, so initialSelectedItems should be empty
1872
1998
  this.initialSelectedItems = [];
@@ -1885,7 +2011,7 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1885
2011
  const keyField = dataSourceAny.config?.key || 'id';
1886
2012
  const byKeyMethod = dataSourceAny.config?.byKey;
1887
2013
  // First, try to find items in already loaded grid data
1888
- const itemsToSelect = [];
2014
+ let itemsToSelect = [];
1889
2015
  const maxAttempts = 10; // Try for up to 1 second (10 * 100ms)
1890
2016
  let attempts = 0;
1891
2017
  const missingIds = [];
@@ -1949,28 +2075,28 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1949
2075
  }
1950
2076
  }
1951
2077
  }
2078
+ if (this.isSingleSelectionMode() && itemsToSelect.length > 1) {
2079
+ itemsToSelect = [itemsToSelect[0]];
2080
+ }
1952
2081
  // Only mark as applied if we found items to select
1953
2082
  if (itemsToSelect.length > 0) {
1954
- // Store initial selected items for later comparison
1955
2083
  this.initialSelectedItems = [...itemsToSelect];
1956
- // Set flag to indicate we're applying initial selection
1957
2084
  this.isApplyingInitialSelection = true;
1958
2085
  try {
1959
- // Update selectedItems signal first
1960
- this.selectedItems.set(itemsToSelect);
1961
- // Select the rows using the grid's selectRows method
1962
- if (this.grid()?.selectRows) {
1963
- if (itemsToSelect.length === 1) {
1964
- this.grid().selectRows(itemsToSelect[0]);
1965
- }
1966
- else {
2086
+ if (this.isSingleSelectionMode()) {
2087
+ this.rowClickSelectionSync = true;
2088
+ this.selectedItems.set(itemsToSelect);
2089
+ this.syncGridSingleSelection(itemsToSelect);
2090
+ }
2091
+ else {
2092
+ this.selectedItems.set(itemsToSelect);
2093
+ if (this.grid()?.selectRows) {
1967
2094
  this.grid().selectRows(...itemsToSelect);
1968
2095
  }
1969
2096
  }
1970
2097
  this.initialSelectionApplied = true;
1971
2098
  }
1972
2099
  finally {
1973
- // Reset flag after a short delay to allow the event to process
1974
2100
  setTimeout(() => {
1975
2101
  this.isApplyingInitialSelection = false;
1976
2102
  }, 50);
@@ -1988,11 +2114,11 @@ class AXPDataSelectorComponent extends AXBasePageComponent {
1988
2114
  }
1989
2115
  }
1990
2116
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1991
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPDataSelectorComponent, isStandalone: true, selector: "axp-data-selector", viewQueries: [{ propertyName: "grid", first: true, predicate: ["grid"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"axp-data-selector\">\n <!-- Main Content Area -->\n <div class=\"axp-data-selector-content\" [class.with-category-filter]=\"config().categoryFilter?.enabled\">\n <!-- Category Filter Side Panel -->\n @if (config().categoryFilter?.enabled) {\n <div class=\"axp-category-filter-panel\" [style.width]=\"config().categoryFilter?.width || '300px'\">\n <axp-category-tree [dataSource]=\"config().categoryFilter!.dataSource\" [config]=\"categoryTreeConfig()\"\n [actions]=\"categoryTreeActions()\" (nodeClick)=\"handleCategoryFilterClick($event)\">\n </axp-category-tree>\n </div>\n }\n\n <!-- Main Data Area -->\n <div class=\"axp-main-data-area\">\n @if (hasSearch()) {\n <div class=\"ax-w-full ax-mb-2\">\n <ax-search-box [placeholder]=\"searchPlaceholderText()\" [value]=\"searchTerm\"\n (onValueChanged)=\"handleChangeSearchValue($event)\" [axAutoFocus]=\"true\">\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n }\n <!-- TODO: needs to show checkbox for single selection mode too -->\n <ax-data-table #grid [showFooter]=\"false\" class=\"ax-h-[50vh]\" [paging]=\"true\"\n [loading]=\"{ enabled: true, animation: true }\" [dataSource]=\"config().dataSource\"\n [parentField]=\"config().parentField\" (selectedRowsChange)=\"handleSelectedRowsChange($event)\"\n (onRowClick)=\"handleRowClick($event)\" (onRowDbClick)=\"handleRowDbClick($event)\">\n @if (config().selectionMode === 'multiple') {\n <ax-select-column fixed=\"start\" [width]=\"'50px'\"></ax-select-column>\n }\n\n @for (col of config().columns; track col.name) {\n @if (col.visible) {\n <axp-widget-column-renderer [expandHandler]=\"$index === 0 && config().parentField ? true : false\"\n [caption]=\"col.title | translate | async\" [customWidth]=\"col.width || ''\" [node]=\"{\n path: col.dataPath || col.name,\n type: col.widget.type,\n options: col.widget.options\n }\">\n </axp-widget-column-renderer>\n }\n }\n </ax-data-table>\n </div>\n </div>\n</div>\n\n<ax-footer>\n <ax-prefix>\n @if ((config().allowCreate === 'full' || config().allowCreate === 'quick') && config().onCreate) {\n <ax-button look=\"twotone\" color=\"primary\" [text]=\"'@general:actions.create.title' | translate | async\"\n (onClick)=\"handleCreateNewClick(config().allowCreate === 'quick' ? 'quick' : 'full')\">\n <ax-prefix>\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </ax-prefix>\n <ax-suffix>\n <ax-button look=\"solid\" [text]=\"('@general:actions.close.title' | translate | async)!\"\n (onClick)=\"handleCloseClick()\">\n </ax-button>\n <ax-button look=\"solid\" color=\"primary\" [text]=\"('@general:actions.select.title' | translate | async)!\"\n (onClick)=\"handleSelectClick()\" [disabled]=\"allowSelect() === false\">\n <!-- @if (config().selectionMode === 'multiple' && selectedCount() > 0) {\n <ax-badge [text]=\"selectedCount().toString()\" color=\"accent1\"\n class=\"ax-absolute ax-end-[2px] ax-top-[2px]\"></ax-badge>\n } -->\n </ax-button>\n </ax-suffix>\n</ax-footer>", styles: [":host{display:block;height:100%}.ax-expand-handler{cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:8px}.ax-expand-handler:hover{opacity:.8}.ax-expand-handler.ax-invisible{visibility:hidden}.ax-h-\\[50vh\\]{height:50vh}.ax-overflow-hidden{overflow:hidden}.ax-flex{display:flex}.ax-flex-col{flex-direction:column}.ax-gap-4{gap:1rem}.ax-p-4{padding:1rem}.ax-w-full{width:100%}.axp-data-selector{display:flex;flex-direction:column;height:100%;overflow:hidden}.axp-data-selector-header{display:flex;justify-content:space-between;align-items:center;padding:1rem;border-bottom:1px solid var(--border-color);background:var(--surface-color);flex-shrink:0}.axp-data-selector-header h3{margin:0;font-size:1.25rem;font-weight:600;color:var(--text-color)}.axp-data-selector-content{display:flex;flex:1;overflow:hidden}.axp-data-selector-content.with-category-filter .axp-main-data-area{flex:1;overflow:hidden}.axp-data-selector .axp-category-filter-panel{border-right:1px solid var(--border-color);padding:1rem;overflow-y:auto;background:var(--surface-color);flex-shrink:0}.axp-data-selector .axp-main-data-area{flex:1;overflow:hidden;padding:1rem;display:flex;flex-direction:column;gap:1rem}.axp-data-selector .axp-main-data-area .ax-data-table{flex:1;min-height:0}.axp-data-selector .axp-main-data-area .ax-search-box{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXCommonModule }, { kind: "directive", type: i1$6.AXAutoFocusDirective, selector: "[axAutoFocus]", inputs: ["axAutoFocus", "axAutoFocusTime"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$2.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: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$1.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { 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: AXDataTableModule }, { kind: "component", type: i4$1.AXDataTableComponent, selector: "ax-data-table", inputs: ["dataSource", "selectedRows", "parentField", "hasChildrenField", "rowDetailsTemplate", "rowTemplate", "emptyTemplate", "noDataTemplate", "alternative", "showHeader", "fixedHeader", "showFooter", "fixedFooter", "itemHeight", "allowReordering", "paging", "fetchDataMode", "loading", "focusedRow"], outputs: ["selectedRowsChange", "focusedRowChange", "onRowClick", "onRowDbClick", "onColumnsOrderChanged", "onColumnSizeChanged", "onPageChanged"] }, { kind: "component", type: i4$1.AXRowSelectColumnComponent, selector: "ax-select-column", inputs: ["width", "caption", "fixed"] }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i5.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: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i3$3.AXPWidgetColumnRendererComponent, selector: "axp-widget-column-renderer", inputs: ["caption", "customExpandIcon", "customCollapseIcon", "customWidth", "node", "footerTemplate", "expandHandler", "sortEnabled", "headerSortDirection", "headerSortPriority", "cellTemplate", "headerTemplate"], outputs: ["sortToggle"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: AXPCategoryTreeComponent, selector: "axp-category-tree", inputs: ["dataSource", "config", "actions", "events"], outputs: ["nodeClick", "nodeSelect", "nodeCreate", "nodeUpdate", "nodeDelete", "searchChange", "collapseChange"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2117
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPDataSelectorComponent, isStandalone: true, selector: "axp-data-selector", viewQueries: [{ propertyName: "grid", first: true, predicate: ["grid"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"axp-data-selector\">\n <!-- Main Content Area -->\n <div class=\"axp-data-selector-content\" [class.with-category-filter]=\"categoryEnabled()\">\n <!-- Category Filter Side Panel -->\n @if (categoryEnabled()) {\n <div class=\"axp-category-filter-panel\" [style.width]=\"config().category?.width || '300px'\">\n <axp-category-tree [dataSource]=\"config().category!.dataSource\" [config]=\"categoryTreeConfig()\"\n [actions]=\"categoryTreeActions()\" (nodeClick)=\"handleCategoryFilterClick($event)\">\n </axp-category-tree>\n </div>\n }\n\n <!-- Main Data Area -->\n <div class=\"axp-main-data-area\">\n @if (hasSearch()) {\n <div class=\"ax-w-full ax-mb-2\">\n <ax-search-box [placeholder]=\"searchPlaceholderText()\" [value]=\"searchTerm\"\n (onValueChanged)=\"handleChangeSearchValue($event)\" [axAutoFocus]=\"true\">\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n }\n <ax-data-table #grid [showFooter]=\"false\" class=\"ax-flex-1 ax-min-h-0\" [paging]=\"true\"\n [loading]=\"{ enabled: true, animation: true }\" [dataSource]=\"config().dataSource\"\n [parentField]=\"config().grid.parentField\" [selectedRows]=\"selectedItems()\"\n (selectedRowsChange)=\"handleSelectedRowsChange($event)\" (onRowClick)=\"handleRowClick($event)\"\n (onRowDbClick)=\"handleRowDbClick($event)\">\n <ax-select-column fixed=\"start\" [width]=\"'50px'\"></ax-select-column>\n\n @for (col of config().grid.columns; track col.name) {\n @if (col.visible) {\n <axp-widget-column-renderer [expandHandler]=\"$index === 0 && config().grid.parentField ? true : false\"\n [caption]=\"col.title | translate | async\" [customWidth]=\"col.width || ''\" [node]=\"{\n path: col.dataPath || col.name,\n type: col.widget.type,\n options: col.widget.options\n }\">\n </axp-widget-column-renderer>\n }\n }\n @if (hasRowActionPrimary()) {\n <ax-command-column fixed=\"end\" [width]=\"primaryRowActionColumnWidth()\" [items]=\"primaryRowActionItems()\"\n (onItemClick)=\"handleRowActionClick($event)\">\n </ax-command-column>\n }\n @if (hasRowActionDropdown()) {\n <ax-dropdown-command-column fixed=\"end\" [width]=\"'60px'\" [items]=\"getDropdownRowActionItems\"\n (onItemClick)=\"handleRowActionClick($event)\">\n </ax-dropdown-command-column>\n }\n </ax-data-table>\n </div>\n </div>\n</div>\n\n<ax-footer>\n <ax-prefix>\n @if ((config().create?.mode === 'full' || config().create?.mode === 'quick') && config().create?.onCreate) {\n <ax-button look=\"twotone\" color=\"primary\" [text]=\"'@general:actions.create.title' | translate | async\"\n (onClick)=\"handleCreateNewClick(config().create?.mode === 'quick' ? 'quick' : 'full')\">\n <ax-prefix>\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </ax-prefix>\n <ax-suffix>\n <ax-button look=\"solid\" [text]=\"('@general:actions.close.title' | translate | async)!\"\n (onClick)=\"handleCloseClick()\">\n </ax-button>\n <ax-button look=\"solid\" color=\"primary\" [text]=\"('@general:actions.select.title' | translate | async)!\"\n (onClick)=\"handleSelectClick()\" [disabled]=\"allowSelect() === false\">\n <!-- @if (selectionMode() === 'multiple' && selectedCount() > 0) {\n <ax-badge [text]=\"selectedCount().toString()\" color=\"accent1\"\n class=\"ax-absolute ax-end-[2px] ax-top-[2px]\"></ax-badge>\n } -->\n </ax-button>\n </ax-suffix>\n</ax-footer>\n", styles: ["axp-data-selector{display:flex;flex-direction:column;flex:1;min-height:0;height:100%;overflow:hidden}.ax-expand-handler{cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:8px}.ax-expand-handler:hover{opacity:.8}.ax-expand-handler.ax-invisible{visibility:hidden}.ax-overflow-hidden{overflow:hidden}.ax-flex{display:flex}.ax-flex-col{flex-direction:column}.ax-gap-4{gap:1rem}.ax-p-4{padding:1rem}.ax-w-full{width:100%}.axp-data-selector{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden}.axp-data-selector-content{display:flex;flex:1;min-height:0;overflow:hidden}.axp-data-selector-content.with-category-filter .axp-main-data-area{flex:1;min-width:0;min-height:0;overflow:hidden}.axp-data-selector .axp-category-filter-panel{display:flex;flex-direction:column;border-right:1px solid var(--border-color);padding:1rem;background:var(--surface-color);flex-shrink:0;min-height:0;min-width:0;overflow:hidden}.axp-data-selector .axp-category-filter-panel axp-category-tree{display:flex;flex:1;flex-direction:column;min-height:0;min-width:0;overflow:hidden}.axp-data-selector .axp-main-data-area{flex:1;min-width:0;min-height:0;overflow:hidden;padding:1rem;display:flex;flex-direction:column;gap:1rem}.axp-data-selector .axp-main-data-area ax-data-table{flex:1;min-height:0;height:100%;display:flex;flex-direction:column;overflow:hidden}.axp-data-selector .axp-main-data-area .ax-search-box{width:100%;flex-shrink:0}.ax-popup:has(axp-data-selector){display:flex;flex-direction:column;height:90vh;max-height:90vh;overflow:hidden}.ax-popup:has(axp-data-selector) .ax-popup-main-container{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:hidden}.ax-popup:has(axp-data-selector) .ax-popup-body-container{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:hidden!important}.ax-popup:has(axp-data-selector) .ax-popup-footer-container{flex-shrink:0}@media(min-width:320px)and (max-width:640px){.ax-popup.ax-popup-sm:has(axp-data-selector),.ax-popup.ax-popup-md:has(axp-data-selector),.ax-popup.ax-popup-lg:has(axp-data-selector){height:90vh!important;max-height:90vh!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXCommonModule }, { kind: "directive", type: i1$6.AXAutoFocusDirective, selector: "[axAutoFocus]", inputs: ["axAutoFocus", "axAutoFocusTime"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$2.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: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$1.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { 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: AXDataTableModule }, { kind: "component", type: i4$1.AXDataTableComponent, selector: "ax-data-table", inputs: ["dataSource", "selectedRows", "parentField", "hasChildrenField", "rowDetailsTemplate", "rowTemplate", "emptyTemplate", "noDataTemplate", "alternative", "showHeader", "fixedHeader", "showFooter", "fixedFooter", "itemHeight", "allowReordering", "paging", "fetchDataMode", "loading", "focusedRow"], outputs: ["selectedRowsChange", "focusedRowChange", "onRowClick", "onRowDbClick", "onColumnsOrderChanged", "onColumnSizeChanged", "onPageChanged"] }, { kind: "component", type: i4$1.AXRowSelectColumnComponent, selector: "ax-select-column", inputs: ["width", "caption", "fixed"] }, { kind: "component", type: i4$1.AXRowCommandColumnComponent, selector: "ax-command-column", inputs: ["width", "caption", "fixed", "footerTemplate", "items"], outputs: ["onItemClick"] }, { kind: "component", type: i4$1.AXRowDropdownCommandColumnComponent, selector: "ax-dropdown-command-column", inputs: ["width", "caption", "fixed", "footerTemplate", "emptyStateTemplate", "emptyStateText", "items"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i5.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: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i3$3.AXPWidgetColumnRendererComponent, selector: "axp-widget-column-renderer", inputs: ["caption", "customExpandIcon", "customCollapseIcon", "customWidth", "node", "footerTemplate", "expandHandler", "sortEnabled", "headerSortDirection", "headerSortPriority", "cellTemplate", "headerTemplate"], outputs: ["sortToggle"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: AXPCategoryTreeComponent, selector: "axp-category-tree", inputs: ["dataSource", "config", "actions", "events"], outputs: ["nodeClick", "nodeSelect", "nodeCreate", "nodeUpdate", "nodeDelete", "searchChange", "collapseChange"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1992
2118
  }
1993
2119
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataSelectorComponent, decorators: [{
1994
2120
  type: Component,
1995
- args: [{ selector: 'axp-data-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
2121
+ args: [{ selector: 'axp-data-selector', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
1996
2122
  CommonModule,
1997
2123
  AXCommonModule,
1998
2124
  AXButtonModule,
@@ -2005,7 +2131,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2005
2131
  AXTranslationModule,
2006
2132
  AXBadgeModule,
2007
2133
  AXPCategoryTreeComponent,
2008
- ], inputs: [], template: "<div class=\"axp-data-selector\">\n <!-- Main Content Area -->\n <div class=\"axp-data-selector-content\" [class.with-category-filter]=\"config().categoryFilter?.enabled\">\n <!-- Category Filter Side Panel -->\n @if (config().categoryFilter?.enabled) {\n <div class=\"axp-category-filter-panel\" [style.width]=\"config().categoryFilter?.width || '300px'\">\n <axp-category-tree [dataSource]=\"config().categoryFilter!.dataSource\" [config]=\"categoryTreeConfig()\"\n [actions]=\"categoryTreeActions()\" (nodeClick)=\"handleCategoryFilterClick($event)\">\n </axp-category-tree>\n </div>\n }\n\n <!-- Main Data Area -->\n <div class=\"axp-main-data-area\">\n @if (hasSearch()) {\n <div class=\"ax-w-full ax-mb-2\">\n <ax-search-box [placeholder]=\"searchPlaceholderText()\" [value]=\"searchTerm\"\n (onValueChanged)=\"handleChangeSearchValue($event)\" [axAutoFocus]=\"true\">\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n }\n <!-- TODO: needs to show checkbox for single selection mode too -->\n <ax-data-table #grid [showFooter]=\"false\" class=\"ax-h-[50vh]\" [paging]=\"true\"\n [loading]=\"{ enabled: true, animation: true }\" [dataSource]=\"config().dataSource\"\n [parentField]=\"config().parentField\" (selectedRowsChange)=\"handleSelectedRowsChange($event)\"\n (onRowClick)=\"handleRowClick($event)\" (onRowDbClick)=\"handleRowDbClick($event)\">\n @if (config().selectionMode === 'multiple') {\n <ax-select-column fixed=\"start\" [width]=\"'50px'\"></ax-select-column>\n }\n\n @for (col of config().columns; track col.name) {\n @if (col.visible) {\n <axp-widget-column-renderer [expandHandler]=\"$index === 0 && config().parentField ? true : false\"\n [caption]=\"col.title | translate | async\" [customWidth]=\"col.width || ''\" [node]=\"{\n path: col.dataPath || col.name,\n type: col.widget.type,\n options: col.widget.options\n }\">\n </axp-widget-column-renderer>\n }\n }\n </ax-data-table>\n </div>\n </div>\n</div>\n\n<ax-footer>\n <ax-prefix>\n @if ((config().allowCreate === 'full' || config().allowCreate === 'quick') && config().onCreate) {\n <ax-button look=\"twotone\" color=\"primary\" [text]=\"'@general:actions.create.title' | translate | async\"\n (onClick)=\"handleCreateNewClick(config().allowCreate === 'quick' ? 'quick' : 'full')\">\n <ax-prefix>\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </ax-prefix>\n <ax-suffix>\n <ax-button look=\"solid\" [text]=\"('@general:actions.close.title' | translate | async)!\"\n (onClick)=\"handleCloseClick()\">\n </ax-button>\n <ax-button look=\"solid\" color=\"primary\" [text]=\"('@general:actions.select.title' | translate | async)!\"\n (onClick)=\"handleSelectClick()\" [disabled]=\"allowSelect() === false\">\n <!-- @if (config().selectionMode === 'multiple' && selectedCount() > 0) {\n <ax-badge [text]=\"selectedCount().toString()\" color=\"accent1\"\n class=\"ax-absolute ax-end-[2px] ax-top-[2px]\"></ax-badge>\n } -->\n </ax-button>\n </ax-suffix>\n</ax-footer>", styles: [":host{display:block;height:100%}.ax-expand-handler{cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:8px}.ax-expand-handler:hover{opacity:.8}.ax-expand-handler.ax-invisible{visibility:hidden}.ax-h-\\[50vh\\]{height:50vh}.ax-overflow-hidden{overflow:hidden}.ax-flex{display:flex}.ax-flex-col{flex-direction:column}.ax-gap-4{gap:1rem}.ax-p-4{padding:1rem}.ax-w-full{width:100%}.axp-data-selector{display:flex;flex-direction:column;height:100%;overflow:hidden}.axp-data-selector-header{display:flex;justify-content:space-between;align-items:center;padding:1rem;border-bottom:1px solid var(--border-color);background:var(--surface-color);flex-shrink:0}.axp-data-selector-header h3{margin:0;font-size:1.25rem;font-weight:600;color:var(--text-color)}.axp-data-selector-content{display:flex;flex:1;overflow:hidden}.axp-data-selector-content.with-category-filter .axp-main-data-area{flex:1;overflow:hidden}.axp-data-selector .axp-category-filter-panel{border-right:1px solid var(--border-color);padding:1rem;overflow-y:auto;background:var(--surface-color);flex-shrink:0}.axp-data-selector .axp-main-data-area{flex:1;overflow:hidden;padding:1rem;display:flex;flex-direction:column;gap:1rem}.axp-data-selector .axp-main-data-area .ax-data-table{flex:1;min-height:0}.axp-data-selector .axp-main-data-area .ax-search-box{width:100%}\n"] }]
2134
+ ], inputs: [], template: "<div class=\"axp-data-selector\">\n <!-- Main Content Area -->\n <div class=\"axp-data-selector-content\" [class.with-category-filter]=\"categoryEnabled()\">\n <!-- Category Filter Side Panel -->\n @if (categoryEnabled()) {\n <div class=\"axp-category-filter-panel\" [style.width]=\"config().category?.width || '300px'\">\n <axp-category-tree [dataSource]=\"config().category!.dataSource\" [config]=\"categoryTreeConfig()\"\n [actions]=\"categoryTreeActions()\" (nodeClick)=\"handleCategoryFilterClick($event)\">\n </axp-category-tree>\n </div>\n }\n\n <!-- Main Data Area -->\n <div class=\"axp-main-data-area\">\n @if (hasSearch()) {\n <div class=\"ax-w-full ax-mb-2\">\n <ax-search-box [placeholder]=\"searchPlaceholderText()\" [value]=\"searchTerm\"\n (onValueChanged)=\"handleChangeSearchValue($event)\" [axAutoFocus]=\"true\">\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n }\n <ax-data-table #grid [showFooter]=\"false\" class=\"ax-flex-1 ax-min-h-0\" [paging]=\"true\"\n [loading]=\"{ enabled: true, animation: true }\" [dataSource]=\"config().dataSource\"\n [parentField]=\"config().grid.parentField\" [selectedRows]=\"selectedItems()\"\n (selectedRowsChange)=\"handleSelectedRowsChange($event)\" (onRowClick)=\"handleRowClick($event)\"\n (onRowDbClick)=\"handleRowDbClick($event)\">\n <ax-select-column fixed=\"start\" [width]=\"'50px'\"></ax-select-column>\n\n @for (col of config().grid.columns; track col.name) {\n @if (col.visible) {\n <axp-widget-column-renderer [expandHandler]=\"$index === 0 && config().grid.parentField ? true : false\"\n [caption]=\"col.title | translate | async\" [customWidth]=\"col.width || ''\" [node]=\"{\n path: col.dataPath || col.name,\n type: col.widget.type,\n options: col.widget.options\n }\">\n </axp-widget-column-renderer>\n }\n }\n @if (hasRowActionPrimary()) {\n <ax-command-column fixed=\"end\" [width]=\"primaryRowActionColumnWidth()\" [items]=\"primaryRowActionItems()\"\n (onItemClick)=\"handleRowActionClick($event)\">\n </ax-command-column>\n }\n @if (hasRowActionDropdown()) {\n <ax-dropdown-command-column fixed=\"end\" [width]=\"'60px'\" [items]=\"getDropdownRowActionItems\"\n (onItemClick)=\"handleRowActionClick($event)\">\n </ax-dropdown-command-column>\n }\n </ax-data-table>\n </div>\n </div>\n</div>\n\n<ax-footer>\n <ax-prefix>\n @if ((config().create?.mode === 'full' || config().create?.mode === 'quick') && config().create?.onCreate) {\n <ax-button look=\"twotone\" color=\"primary\" [text]=\"'@general:actions.create.title' | translate | async\"\n (onClick)=\"handleCreateNewClick(config().create?.mode === 'quick' ? 'quick' : 'full')\">\n <ax-prefix>\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </ax-prefix>\n <ax-suffix>\n <ax-button look=\"solid\" [text]=\"('@general:actions.close.title' | translate | async)!\"\n (onClick)=\"handleCloseClick()\">\n </ax-button>\n <ax-button look=\"solid\" color=\"primary\" [text]=\"('@general:actions.select.title' | translate | async)!\"\n (onClick)=\"handleSelectClick()\" [disabled]=\"allowSelect() === false\">\n <!-- @if (selectionMode() === 'multiple' && selectedCount() > 0) {\n <ax-badge [text]=\"selectedCount().toString()\" color=\"accent1\"\n class=\"ax-absolute ax-end-[2px] ax-top-[2px]\"></ax-badge>\n } -->\n </ax-button>\n </ax-suffix>\n</ax-footer>\n", styles: ["axp-data-selector{display:flex;flex-direction:column;flex:1;min-height:0;height:100%;overflow:hidden}.ax-expand-handler{cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:8px}.ax-expand-handler:hover{opacity:.8}.ax-expand-handler.ax-invisible{visibility:hidden}.ax-overflow-hidden{overflow:hidden}.ax-flex{display:flex}.ax-flex-col{flex-direction:column}.ax-gap-4{gap:1rem}.ax-p-4{padding:1rem}.ax-w-full{width:100%}.axp-data-selector{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden}.axp-data-selector-content{display:flex;flex:1;min-height:0;overflow:hidden}.axp-data-selector-content.with-category-filter .axp-main-data-area{flex:1;min-width:0;min-height:0;overflow:hidden}.axp-data-selector .axp-category-filter-panel{display:flex;flex-direction:column;border-right:1px solid var(--border-color);padding:1rem;background:var(--surface-color);flex-shrink:0;min-height:0;min-width:0;overflow:hidden}.axp-data-selector .axp-category-filter-panel axp-category-tree{display:flex;flex:1;flex-direction:column;min-height:0;min-width:0;overflow:hidden}.axp-data-selector .axp-main-data-area{flex:1;min-width:0;min-height:0;overflow:hidden;padding:1rem;display:flex;flex-direction:column;gap:1rem}.axp-data-selector .axp-main-data-area ax-data-table{flex:1;min-height:0;height:100%;display:flex;flex-direction:column;overflow:hidden}.axp-data-selector .axp-main-data-area .ax-search-box{width:100%;flex-shrink:0}.ax-popup:has(axp-data-selector){display:flex;flex-direction:column;height:90vh;max-height:90vh;overflow:hidden}.ax-popup:has(axp-data-selector) .ax-popup-main-container{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:hidden}.ax-popup:has(axp-data-selector) .ax-popup-body-container{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:hidden!important}.ax-popup:has(axp-data-selector) .ax-popup-footer-container{flex-shrink:0}@media(min-width:320px)and (max-width:640px){.ax-popup.ax-popup-sm:has(axp-data-selector),.ax-popup.ax-popup-md:has(axp-data-selector),.ax-popup.ax-popup-lg:has(axp-data-selector){height:90vh!important;max-height:90vh!important}}\n"] }]
2009
2135
  }], ctorParameters: () => [], propDecorators: { grid: [{ type: i0.ViewChild, args: ['grid', { isSignal: true }] }] } });
2010
2136
 
2011
2137
  var dataSelector_component = /*#__PURE__*/Object.freeze({
@@ -2027,38 +2153,29 @@ class AXPDataSelectorService {
2027
2153
  */
2028
2154
  async open(config) {
2029
2155
  const component = await Promise.resolve().then(function () { return dataSelector_component; }).then(c => c.AXPDataSelectorComponent);
2030
- // Create a copy of config to avoid mutating the original
2031
2156
  const processedConfig = { ...config };
2032
- // Evaluate filters if they exist
2033
- if (processedConfig.filters) {
2034
- processedConfig.filters = await this.expressionService.evaluate(processedConfig.filters);
2157
+ if (processedConfig.filter?.value) {
2158
+ processedConfig.filter = {
2159
+ ...processedConfig.filter,
2160
+ value: await this.expressionService.evaluate(processedConfig.filter.value),
2161
+ };
2035
2162
  }
2036
- // Apply column width resolution to all columns
2037
- processedConfig.columns = this.applyColumnWidths(processedConfig.columns);
2163
+ processedConfig.grid = {
2164
+ ...processedConfig.grid,
2165
+ columns: this.applyColumnWidths(processedConfig.grid.columns),
2166
+ };
2167
+ const categoryActive = processedConfig.category?.enabled !== false && !!processedConfig.category?.dataSource;
2038
2168
  const result = await this.popupService.open(component, {
2039
2169
  title: processedConfig.title,
2040
- size: processedConfig.categoryFilter?.enabled ?
2041
- 'lg'
2042
- : (processedConfig.columns.length > 3 ? 'lg' : 'md'),
2170
+ size: categoryActive
2171
+ ? 'lg'
2172
+ : (processedConfig.grid.columns.length > 3 ? 'lg' : 'md'),
2043
2173
  data: {
2044
2174
  config: signal(processedConfig)
2045
2175
  }
2046
2176
  });
2047
2177
  return result.data || null;
2048
2178
  }
2049
- /**
2050
- * Open data selector with category filter
2051
- */
2052
- async openWithCategoryFilter(config, categoryFilterConfig) {
2053
- const enhancedConfig = {
2054
- ...config,
2055
- categoryFilter: {
2056
- enabled: true,
2057
- ...categoryFilterConfig
2058
- }
2059
- };
2060
- return this.open(enhancedConfig);
2061
- }
2062
2179
  //#endregion
2063
2180
  //#region ---- Private Methods ----
2064
2181
  /**
@@ -2066,11 +2183,9 @@ class AXPDataSelectorService {
2066
2183
  */
2067
2184
  applyColumnWidths(columns) {
2068
2185
  return columns.map((column) => {
2069
- // Skip if column already has a width
2070
2186
  if (column.width) {
2071
2187
  return column;
2072
2188
  }
2073
- // Convert to IColumnWithWidth interface for width resolution
2074
2189
  const columnWithWidth = {
2075
2190
  name: column.name,
2076
2191
  width: column.width,
@@ -2078,7 +2193,6 @@ class AXPDataSelectorService {
2078
2193
  type: column.widget.type,
2079
2194
  },
2080
2195
  };
2081
- // Resolve width using column width service
2082
2196
  const resolvedWidth = this.columnWidthService.resolveWidth(columnWithWidth);
2083
2197
  if (resolvedWidth) {
2084
2198
  return {
@@ -4769,45 +4883,69 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4769
4883
 
4770
4884
  class AXPLogoComponent {
4771
4885
  constructor() {
4886
+ this.isCompactLogo = false;
4887
+ this.compactLogoSize = 32;
4772
4888
  this.platform = inject(AXPlatform);
4889
+ this.themeStore = inject(AXPLayoutThemeService);
4890
+ this.platformConfig = inject(AXP_PLATFORM_CONFIG_TOKEN);
4891
+ this.cdr = inject(ChangeDetectorRef);
4892
+ effect(() => {
4893
+ this.themeStore.menuVerticalMode();
4894
+ this.updateActiveSource();
4895
+ });
4773
4896
  }
4774
4897
  ngOnInit() {
4775
- this.setLogoType();
4776
4898
  this.platform.themeMode$.subscribe(() => {
4777
- this.setLogoTheme();
4899
+ this.updateActiveSource();
4778
4900
  });
4779
4901
  }
4780
4902
  ngOnChanges() {
4781
- this.setLogoTheme();
4903
+ this.inputSource = this.source;
4904
+ this.updateActiveSource();
4905
+ }
4906
+ updateActiveSource() {
4907
+ const compactIcon = this.themeStore.menuVerticalMode() === AXPMenuVerticalMode.Compact
4908
+ ? this.platformConfig.logo?.icon
4909
+ : undefined;
4910
+ if (compactIcon?.dark) {
4911
+ this.activeSource = compactIcon.dark;
4912
+ }
4913
+ else if (compactIcon) {
4914
+ this.activeSource = compactIcon.light;
4915
+ }
4916
+ else if (this.inputSource?.dark && this.inputSource?.light) {
4917
+ this.activeSource = this.platform.isDark() ? this.inputSource.dark : this.inputSource.light;
4918
+ }
4919
+ else {
4920
+ this.activeSource = this.inputSource;
4921
+ }
4922
+ this.isCompactLogo =
4923
+ this.themeStore.menuVerticalMode() === AXPMenuVerticalMode.Compact && !!compactIcon;
4782
4924
  this.setLogoType();
4925
+ this.cdr.markForCheck();
4783
4926
  }
4784
4927
  setLogoType() {
4785
4928
  switch (true) {
4786
- case this.source instanceof AXPImageUrlLogoConfig:
4929
+ case this.activeSource instanceof AXPImageUrlLogoConfig:
4787
4930
  this.logoType = 'url';
4788
4931
  break;
4789
- case this.source instanceof AXPComponentLogoConfig:
4932
+ case this.activeSource instanceof AXPComponentLogoConfig:
4790
4933
  this.logoType = 'component';
4791
4934
  break;
4792
- case this.source instanceof AXPIconLogoConfig:
4935
+ case this.activeSource instanceof AXPIconLogoConfig:
4793
4936
  this.logoType = 'icon';
4794
4937
  break;
4795
4938
  default:
4796
4939
  break;
4797
4940
  }
4798
4941
  }
4799
- setLogoTheme() {
4800
- if (this.source && this.source.dark && this.source.light) {
4801
- this.source = this.platform.isDark() ? this.source.dark : this.source.light;
4802
- }
4803
- }
4804
4942
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLogoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4805
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLogoComponent, isStandalone: true, selector: "axp-logo", inputs: { source: "source" }, host: { classAttribute: "ax-flex ax-justify-center" }, usesOnChanges: true, ngImport: i0, template: "@switch (logoType) { \n @case ('url') {\n <ax-image [width]=\"source.width\" [height]=\"source.height\" [src]=\"source.url\"></ax-image>\n } \n @case ('component') {\n <ng-container *ngComponentOutlet=\"source.component\"></ng-container>\n } \n @case ('icon') {\n <i [class]=\"source.icon\" [style.color]=\"source.color\"></i>\n } \n}\n\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i3$1.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXDecoratorModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4943
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLogoComponent, isStandalone: true, selector: "axp-logo", inputs: { source: "source" }, host: { classAttribute: "ax-flex ax-justify-center" }, usesOnChanges: true, ngImport: i0, template: "@switch (logoType) {\n @case ('url') {\n <ax-image\n [width]=\"isCompactLogo ? compactLogoSize : activeSource.width\"\n [height]=\"isCompactLogo ? compactLogoSize : activeSource.height\"\n [src]=\"activeSource.url\"\n ></ax-image>\n }\n @case ('component') {\n <ng-container *ngComponentOutlet=\"activeSource.component\"></ng-container>\n }\n @case ('icon') {\n <i [class]=\"activeSource.icon\" [style.color]=\"activeSource.color\"></i>\n }\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i3$1.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXDecoratorModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4806
4944
  }
4807
4945
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLogoComponent, decorators: [{
4808
4946
  type: Component,
4809
- args: [{ selector: 'axp-logo', imports: [CommonModule, AXImageModule, AXDecoratorModule], host: { class: 'ax-flex ax-justify-center' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@switch (logoType) { \n @case ('url') {\n <ax-image [width]=\"source.width\" [height]=\"source.height\" [src]=\"source.url\"></ax-image>\n } \n @case ('component') {\n <ng-container *ngComponentOutlet=\"source.component\"></ng-container>\n } \n @case ('icon') {\n <i [class]=\"source.icon\" [style.color]=\"source.color\"></i>\n } \n}\n\n" }]
4810
- }], propDecorators: { source: [{
4947
+ args: [{ selector: 'axp-logo', imports: [CommonModule, AXImageModule, AXDecoratorModule], host: { class: 'ax-flex ax-justify-center' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@switch (logoType) {\n @case ('url') {\n <ax-image\n [width]=\"isCompactLogo ? compactLogoSize : activeSource.width\"\n [height]=\"isCompactLogo ? compactLogoSize : activeSource.height\"\n [src]=\"activeSource.url\"\n ></ax-image>\n }\n @case ('component') {\n <ng-container *ngComponentOutlet=\"activeSource.component\"></ng-container>\n }\n @case ('icon') {\n <i [class]=\"activeSource.icon\" [style.color]=\"activeSource.color\"></i>\n }\n}\n" }]
4948
+ }], ctorParameters: () => [], propDecorators: { source: [{
4811
4949
  type: Input
4812
4950
  }] } });
4813
4951
 
@@ -6213,7 +6351,7 @@ class AXPResourceAppointmentComponent {
6213
6351
  this.resourceAppointmentService.executeCommand(event.item.data?.command, providerName);
6214
6352
  }
6215
6353
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPResourceAppointmentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6216
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPResourceAppointmentComponent, isStandalone: true, selector: "axp-resource-appointment", inputs: { showResourceHeaders: { classPropertyName: "showResourceHeaders", publicName: "showResourceHeaders", isSignal: true, isRequired: false, transformFunction: null }, showUnassignedAppointments: { classPropertyName: "showUnassignedAppointments", publicName: "showUnassignedAppointments", isSignal: true, isRequired: false, transformFunction: null }, providerName: { classPropertyName: "providerName", publicName: "providerName", isSignal: true, isRequired: false, transformFunction: null } }, providers: [AXPResourceAppointmentService], viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["contextMenu"], descendants: true, isSignal: true }, { propertyName: "schedulerRef", first: true, predicate: ["scheduler"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"axp-resource-appointment__container ax-flex ax-flex-col ax-min-h-0 ax-h-full\">\n @if (isReady()) {\n <!-- TODO: Add weekly view 'timeline-weekly' -->\n <ax-scheduler\n #scheduler\n [views]=\"['timeline-day', 'timeline-month']\"\n [dataSource]=\"schedulerAppointments()\"\n [resources]=\"schedulerResources()\"\n [startingDate]=\"currentDate()\"\n [selectedView]=\"currentView()\"\n [draggable]=\"false\"\n (onRangeChanged)=\"handleRangeChanged($event)\"\n class=\"axp-resource-appointment__scheduler ax-bg-lightest dark:ax-bg-darkest ax-flex-1\"\n ></ax-scheduler>\n\n <ax-context-menu\n #contextMenu\n [closeOn]=\"'leave'\"\n [orientation]=\"'vertical'\"\n (onOpening)=\"handleContextMenuOpening($event)\"\n (onItemClick)=\"handleContextMenuItemClick($event)\"\n ></ax-context-menu>\n } @else {\n <div class=\"axp-resource-appointment__loading ax-flex ax-items-center ax-justify-center ax-w-full ax-h-full\">\n <ax-loading></ax-loading>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.axp-resource-appointment__scheduler{--ax-comp-scheduler-width: 100%;width:100%;height:100%;min-height:400px}.axp-resource-appointment__loading{min-height:300px}\n"], dependencies: [{ kind: "ngmodule", type: AXCommonModule }, { kind: "ngmodule", type: AXMenuModule }, { kind: "component", type: i1$8.AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "items", "target"], outputs: ["onItemClick", "onOpening", "onClose"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i2$8.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: AXSchedulerComponent, selector: "ax-scheduler", inputs: ["calendar", "startingDate", "endDayHour", "startDayHour", "hasHeader", "readonly", "draggable", "hasActions", "dragStartDelay", "weekend", "allowFullScreen", "multiDayViewDaysCount", "showResourceHeaders", "showCurrentTimeIndicator", "scrollToCurrentTimeIndicator", "showUnassignedAppointments", "resources", "resourceTemplate", "firstDayOfWeek", "tooltipTemplate", "dataSource", "holidays", "views", "selectedView"], outputs: ["selectedViewChange", "onDataLoaded", "onRangeChanged", "onSlotClicked", "onSlotDblClicked", "onSlotRightClick", "onAppointmentDrop", "onActionClick", "onAppointmentClicked", "onAppointmentDblClicked", "onAppointmentRightClick"] }, { kind: "ngmodule", type: AXButtonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
6354
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPResourceAppointmentComponent, isStandalone: true, selector: "axp-resource-appointment", inputs: { showResourceHeaders: { classPropertyName: "showResourceHeaders", publicName: "showResourceHeaders", isSignal: true, isRequired: false, transformFunction: null }, showUnassignedAppointments: { classPropertyName: "showUnassignedAppointments", publicName: "showUnassignedAppointments", isSignal: true, isRequired: false, transformFunction: null }, providerName: { classPropertyName: "providerName", publicName: "providerName", isSignal: true, isRequired: false, transformFunction: null } }, providers: [AXPResourceAppointmentService], viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["contextMenu"], descendants: true, isSignal: true }, { propertyName: "schedulerRef", first: true, predicate: ["scheduler"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"axp-resource-appointment__container ax-flex ax-flex-col ax-min-h-0 ax-h-full\">\n @if (isReady()) {\n <!-- TODO: Add weekly view 'timeline-weekly' -->\n <ax-scheduler\n #scheduler\n [views]=\"['timeline-day', 'timeline-month']\"\n [dataSource]=\"schedulerAppointments()\"\n [resources]=\"schedulerResources()\"\n [startingDate]=\"currentDate()\"\n [selectedView]=\"currentView()\"\n [draggable]=\"false\"\n (onRangeChanged)=\"handleRangeChanged($event)\"\n class=\"axp-resource-appointment__scheduler ax-bg-lightest dark:ax-bg-darkest ax-flex-1\"\n ></ax-scheduler>\n\n <ax-context-menu\n #contextMenu\n [closeOn]=\"'leave'\"\n [orientation]=\"'vertical'\"\n (onOpening)=\"handleContextMenuOpening($event)\"\n (onItemClick)=\"handleContextMenuItemClick($event)\"\n ></ax-context-menu>\n } @else {\n <div class=\"axp-resource-appointment__loading ax-flex ax-items-center ax-justify-center ax-w-full ax-h-full\">\n <ax-loading></ax-loading>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.axp-resource-appointment__scheduler{--ax-comp-scheduler-width: 100%;width:100%;height:100%;min-height:400px}.axp-resource-appointment__loading{min-height:300px}\n"], dependencies: [{ kind: "ngmodule", type: AXCommonModule }, { kind: "ngmodule", type: AXMenuModule }, { kind: "component", type: i1$8.AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "closeOnRouteChange", "items", "target"], outputs: ["onItemClick", "onOpening", "onClose"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i2$8.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: AXSchedulerComponent, selector: "ax-scheduler", inputs: ["calendar", "startingDate", "endDayHour", "startDayHour", "hasHeader", "readonly", "draggable", "hasActions", "dragStartDelay", "weekend", "allowFullScreen", "multiDayViewDaysCount", "showResourceHeaders", "showCurrentTimeIndicator", "scrollToCurrentTimeIndicator", "showUnassignedAppointments", "resources", "resourceTemplate", "firstDayOfWeek", "tooltipTemplate", "dataSource", "holidays", "views", "selectedView"], outputs: ["selectedViewChange", "onDataLoaded", "onRangeChanged", "onSlotClicked", "onSlotDblClicked", "onSlotRightClick", "onAppointmentDrop", "onActionClick", "onAppointmentClicked", "onAppointmentDblClicked", "onAppointmentRightClick"] }, { kind: "ngmodule", type: AXButtonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
6217
6355
  }
6218
6356
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPResourceAppointmentComponent, decorators: [{
6219
6357
  type: Component,
@@ -6232,6 +6370,12 @@ class AXPSectionItemsBuilderComponent {
6232
6370
  this.readonlyMode = input(false, { ...(ngDevMode ? { debugName: "readonlyMode" } : /* istanbul ignore next */ {}), alias: 'readonly' });
6233
6371
  this.valueChange = output();
6234
6372
  this.vm = signal({ sections: [] }, ...(ngDevMode ? [{ debugName: "vm" }] : /* istanbul ignore next */ []));
6373
+ /** Snapshot from the last external `[value]` sync (load, discard, save refresh). */
6374
+ this.baseline = signal({ sections: [] }, ...(ngDevMode ? [{ debugName: "baseline" }] : /* istanbul ignore next */ []));
6375
+ /** Set on local {@link commit}; cleared on external sync or {@link markClean}. */
6376
+ this.pendingCommitSnapshot = null;
6377
+ /** True when the live model differs from the external baseline. */
6378
+ this.isDirty = computed(() => !isEqual(this.normalizeValue(this.vm()), this.baseline()), ...(ngDevMode ? [{ debugName: "isDirty" }] : /* istanbul ignore next */ []));
6235
6379
  this.sectionsModel = computed(() => this.normalizeValue(this.vm()), ...(ngDevMode ? [{ debugName: "sectionsModel" }] : /* istanbul ignore next */ []));
6236
6380
  this.sectionRows = computed(() => {
6237
6381
  const def = this.definition();
@@ -6251,10 +6395,31 @@ class AXPSectionItemsBuilderComponent {
6251
6395
  return this.sectionsModel().sections.length > min;
6252
6396
  }, ...(ngDevMode ? [{ debugName: "canRemoveSection" }] : /* istanbul ignore next */ []));
6253
6397
  effect(() => {
6254
- const next = cloneDeep(this.value());
6255
- untracked(() => this.vm.set(this.normalizeValue(next)));
6398
+ const normalized = this.normalizeValue(cloneDeep(this.value()));
6399
+ const current = untracked(() => this.normalizeValue(this.vm()));
6400
+ if (isEqual(normalized, current)) {
6401
+ this.pendingCommitSnapshot = null;
6402
+ return;
6403
+ }
6404
+ if (this.pendingCommitSnapshot != null) {
6405
+ untracked(() => this.vm.set(normalized));
6406
+ if (isEqual(normalized, this.baseline())) {
6407
+ this.pendingCommitSnapshot = null;
6408
+ this.baseline.set(cloneDeep(normalized));
6409
+ }
6410
+ return;
6411
+ }
6412
+ untracked(() => {
6413
+ this.vm.set(normalized);
6414
+ this.baseline.set(cloneDeep(normalized));
6415
+ });
6256
6416
  });
6257
6417
  }
6418
+ /** Align dirty baseline to the current model (call after save). */
6419
+ markClean() {
6420
+ this.baseline.set(cloneDeep(this.normalizeValue(this.vm())));
6421
+ this.pendingCommitSnapshot = null;
6422
+ }
6258
6423
  /**
6259
6424
  * Opens add-section flow (same as primary "Add section" control). For page commands / ViewChild.
6260
6425
  */
@@ -6489,6 +6654,7 @@ class AXPSectionItemsBuilderComponent {
6489
6654
  commit(next) {
6490
6655
  const normalized = this.normalizeValue(next);
6491
6656
  this.vm.set(normalized);
6657
+ this.pendingCommitSnapshot = cloneDeep(normalized);
6492
6658
  this.valueChange.emit(normalized);
6493
6659
  }
6494
6660
  normalizeValue(v) {
@@ -7396,6 +7562,14 @@ class AXPStandardSectionItemsBuilderComponent {
7396
7562
  async addSection() {
7397
7563
  await this.inner()?.addSection();
7398
7564
  }
7565
+ /** Whether the builder model differs from the last external baseline. */
7566
+ isDirty() {
7567
+ return this.inner()?.isDirty() ?? false;
7568
+ }
7569
+ /** Clears dirty state by aligning baseline to the current model. */
7570
+ markClean() {
7571
+ this.inner()?.markClean();
7572
+ }
7399
7573
  isDuplicateSectionName(value, key, excludeSectionId) {
7400
7574
  const n = key.trim().toLowerCase();
7401
7575
  if (!n)