@acorex/platform 20.7.12 → 20.7.15

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 (49) hide show
  1. package/common/index.d.ts +10 -1
  2. package/core/index.d.ts +94 -4
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-BwBLG0Hl.mjs → acorex-platform-common-common-settings.provider-gyb6ohAE.mjs} +15 -1
  4. package/fesm2022/acorex-platform-common-common-settings.provider-gyb6ohAE.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +3 -2
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +428 -157
  8. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  10. package/fesm2022/acorex-platform-layout-builder.mjs +90 -74
  11. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  12. package/fesm2022/acorex-platform-layout-components.mjs +954 -1223
  13. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  14. package/fesm2022/acorex-platform-layout-entity.mjs +601 -193
  15. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  16. package/fesm2022/acorex-platform-layout-views.mjs +7 -7
  17. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  18. package/fesm2022/{acorex-platform-layout-widgets-file-list-popup.component-BfV3spe3.mjs → acorex-platform-layout-widgets-file-list-popup.component-B0omAUil.mjs} +18 -3
  19. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-B0omAUil.mjs.map +1 -0
  20. package/fesm2022/{acorex-platform-layout-widgets-repeater-widget-column.component-DnhR00cH.mjs → acorex-platform-layout-widgets-repeater-widget-column.component-fcCirNxz.mjs} +2 -2
  21. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-fcCirNxz.mjs.map +1 -0
  22. package/fesm2022/acorex-platform-layout-widgets.mjs +165 -64
  23. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  24. package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-CJcbkSBF.mjs → acorex-platform-themes-default-entity-master-create-view.component-CCiYPMhz.mjs} +29 -26
  25. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CCiYPMhz.mjs.map +1 -0
  26. package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-HBr-ZTSt.mjs → acorex-platform-themes-default-entity-master-list-view.component-BQODc73e.mjs} +2 -2
  27. package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-HBr-ZTSt.mjs.map → acorex-platform-themes-default-entity-master-list-view.component-BQODc73e.mjs.map} +1 -1
  28. package/fesm2022/{acorex-platform-themes-default-entity-master-modify-view.component-DAFQ4UI9.mjs → acorex-platform-themes-default-entity-master-modify-view.component-CgLUnYRq.mjs} +3 -4
  29. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-CgLUnYRq.mjs.map +1 -0
  30. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-CwHHYmiK.mjs → acorex-platform-themes-default-entity-master-single-view.component-di5w_3K2.mjs} +4 -4
  31. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-di5w_3K2.mjs.map +1 -0
  32. package/fesm2022/acorex-platform-themes-default.mjs +11 -11
  33. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  34. package/fesm2022/acorex-platform-themes-shared.mjs +244 -246
  35. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  36. package/fesm2022/acorex-platform-workflow.mjs +0 -3
  37. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  38. package/layout/components/index.d.ts +159 -248
  39. package/layout/entity/index.d.ts +42 -1
  40. package/layout/widgets/index.d.ts +43 -5
  41. package/package.json +5 -5
  42. package/themes/shared/index.d.ts +1 -1
  43. package/workflow/index.d.ts +33 -30
  44. package/fesm2022/acorex-platform-common-common-settings.provider-BwBLG0Hl.mjs.map +0 -1
  45. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-BfV3spe3.mjs.map +0 -1
  46. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-DnhR00cH.mjs.map +0 -1
  47. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CJcbkSBF.mjs.map +0 -1
  48. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-DAFQ4UI9.mjs.map +0 -1
  49. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-CwHHYmiK.mjs.map +0 -1
@@ -10,12 +10,12 @@ import * as i2 from '@acorex/components/skeleton';
10
10
  import { AXSkeletonModule } from '@acorex/components/skeleton';
11
11
  import * as i4 from '@acorex/core/format';
12
12
  import { AXFormatModule } from '@acorex/core/format';
13
- import { getSystemActions, AXPExpressionEvaluatorService, AXPContextStore, AXPColumnWidthService, AXPDataGenerator, AXPIconLogoConfig, AXPComponentLogoConfig, AXPImageUrlLogoConfig } from '@acorex/platform/core';
13
+ import { getSystemActions, AXPExpressionEvaluatorService, AXPColumnWidthService, AXPIconLogoConfig, AXPComponentLogoConfig, AXPImageUrlLogoConfig, AXPDataGenerator } from '@acorex/platform/core';
14
14
  import { AXPWorkflowService } from '@acorex/platform/workflow';
15
15
  import * as i1 from '@angular/common';
16
16
  import { CommonModule, NgTemplateOutlet, AsyncPipe } from '@angular/common';
17
17
  import * as i0 from '@angular/core';
18
- import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, signal, effect, InjectionToken, computed, Injectable, Directive, viewChild, contentChild, ElementRef, output, afterNextRender, model, Injector, ViewContainerRef, runInInjectionContext, Optional, Inject, NgModule, linkedSignal, untracked, HostListener, ViewChildren, DestroyRef, Input, EventEmitter, Output } from '@angular/core';
18
+ import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, signal, effect, InjectionToken, computed, Injectable, Directive, viewChild, contentChild, ElementRef, output, afterNextRender, model, linkedSignal, untracked, HostListener, ViewChildren, Input, DestroyRef, EventEmitter, Output } from '@angular/core';
19
19
  import { AXAccordionCdkModule } from '@acorex/cdk/accordion';
20
20
  import { AXTagModule } from '@acorex/components/tag';
21
21
  import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals';
@@ -34,7 +34,7 @@ import { AXSwitchModule } from '@acorex/components/switch';
34
34
  import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
35
35
  import * as i1$3 from '@angular/forms';
36
36
  import { FormsModule } from '@angular/forms';
37
- import { cloneDeep, sortBy, isNil, isEmpty, isEqual, get, set, merge, unionBy, isArray, capitalize } from 'lodash-es';
37
+ import { cloneDeep, isNil, isEmpty, isEqual, merge, get, set, unionBy, sortBy, isArray, capitalize } from 'lodash-es';
38
38
  import * as i2$5 from '@acorex/components/form';
39
39
  import { AXFormModule } from '@acorex/components/form';
40
40
  import { AXTextBoxModule } from '@acorex/components/text-box';
@@ -44,7 +44,6 @@ import * as i1$4 from '@acorex/components/label';
44
44
  import { AXLabelModule } from '@acorex/components/label';
45
45
  import * as i6$1 from '@acorex/platform/layout/widget-core';
46
46
  import { AXPWidgetCoreModule, AXPWidgetsCatalog, AXPWidgetRegistryService, AXPWidgetGroupEnum } from '@acorex/platform/layout/widget-core';
47
- import { AXPSessionService } from '@acorex/platform/auth';
48
47
  import * as i4$1 from '@acorex/components/data-table';
49
48
  import { AXDataTableModule } from '@acorex/components/data-table';
50
49
  import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
@@ -62,13 +61,13 @@ import * as i1$6 from '@acorex/components/image-editor';
62
61
  import { AXImageEditorContainerComponent, AXImageEditorModule } from '@acorex/components/image-editor';
63
62
  import * as i3$1 from '@acorex/components/toolbar';
64
63
  import { AXToolBarModule } from '@acorex/components/toolbar';
64
+ import * as i2$6 from '@acorex/components/image';
65
+ import { AXImageModule } from '@acorex/components/image';
66
+ import { AXPlatform } from '@acorex/core/platform';
65
67
  import { AXDialogService } from '@acorex/components/dialog';
66
68
  import { AXToastService } from '@acorex/components/toast';
67
69
  import { AXTreeViewComponent } from '@acorex/components/tree-view';
68
70
  import { AXPLayoutBuilderService, AXPLayoutRendererComponent } from '@acorex/platform/layout/builder';
69
- import * as i2$6 from '@acorex/components/image';
70
- import { AXImageModule } from '@acorex/components/image';
71
- import { AXPlatform } from '@acorex/core/platform';
72
71
  import * as i2$7 from '@acorex/components/accordion';
73
72
  import { AXAccordionModule } from '@acorex/components/accordion';
74
73
  import { AXButtonGroupModule } from '@acorex/components/button-group';
@@ -1230,7 +1229,7 @@ class AXPColumnItemListComponent {
1230
1229
  </div>
1231
1230
  </div>
1232
1231
  </ax-popover>
1233
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2$3.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "forceDisableActionSheetStyle", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1232
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2$3.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1234
1233
  }
1235
1234
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPColumnItemListComponent, decorators: [{
1236
1235
  type: Component,
@@ -1468,274 +1467,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1468
1467
  ], encapsulation: ViewEncapsulation.None, providers: [], template: "<!--\n #region ---- Compare View Table Layout ----\n-->\n<div class=\"axp-compare-view\">\n <div class=\"__table-container\">\n <table class=\"__table\">\n <thead class=\"__thead\">\n <tr class=\"__header-row\">\n <th class=\"__field-header\">\n <ax-check-box\n [value]=\"showOnlyChanges()\"\n (valueChange)=\"toggleShowOnlyChanges()\"\n class=\"__show-changes-toggle\"\n >\n <ax-label>\n {{ '@activity-log:components.compare-view.show-only-differences' | translate | async }}\n </ax-label>\n </ax-check-box>\n </th>\n @for (obj of remainingObjects(); track obj.id) {\n <th class=\"__object-header\" [attr.data-id]=\"obj.id\">\n <div class=\"__object-title\" [title]=\"obj.description\">\n <span>{{ obj.title | translate | async }}</span>\n @if (remainingObjects().length > 2) {\n <span class=\"__remove-object-button\" (click)=\"removeObject(obj.id)\">\n <i class=\"fa-light fa-times\"></i>\n </span>\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody class=\"__tbody\">\n @for (field of filteredFields(); track field.path; let i = $index) {\n <tr\n class=\"__row\"\n [class.__row--odd]=\"i % 2 === 1\"\n [class.__row--has-differences]=\"hasUnequalValues(field.path)\"\n [class.__row--has-equal-values]=\"hasEqualValues(field.path)\"\n >\n <td class=\"__field-cell\">\n <div class=\"__field-content\">\n <div class=\"__field-title\">\n {{ field.title | translate | async }}\n </div>\n @if (field.description) {\n <div class=\"__field-description\">{{ field.description }}</div>\n }\n </div>\n </td>\n @for (obj of remainingObjects(); track obj.id; let objIndex = $index) {\n <td\n class=\"__object-cell\"\n [attr.data-id]=\"obj.id\"\n [class.__cell--changed]=\"hasValueChangedFromPrevious(field.path, objIndex) && isChangesMode()\"\n >\n @if (obj.context[field.path] !== undefined) {\n <div class=\"__object-content\">\n <axp-widgets-container [context]=\"obj.context\">\n <ng-container axp-widget-renderer [node]=\"field.widget\" [mode]=\"'view'\"></ng-container>\n </axp-widgets-container>\n </div>\n } @else {\n <div class=\"__object-placeholder\">---</div>\n }\n @if (hasValueChangingToNext(field.path, objIndex) && isChangesMode()) {\n <div class=\"__change-indicator\">\n <i class=\"fa-light fa-arrow-right\"></i>\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n<!--\n #endregion\n-->\n", styles: [".axp-compare-view{display:flex;height:100%;width:100%;flex-direction:column;border-radius:.375rem;border-width:1px;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));position:relative}.axp-compare-view .__filter-controls{flex-shrink:0;border-bottom-width:1px;padding:1rem;background-color:rgb(var(--ax-sys-color-light-surface));color:rgb(var(--ax-sys-color-on-light-surface));border-color:rgb(var(--ax-sys-color-border-light-surface))}.axp-compare-view .__filter-controls .__show-changes-toggle{font-size:.875rem;line-height:1.25rem}.axp-compare-view .__table-container{flex:1 1 0%;overflow:auto;position:relative}.axp-compare-view .__table{width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.axp-compare-view .__table .__thead{position:sticky;top:0;z-index:20}.axp-compare-view .__table .__thead .__header-row .__field-header{width:14rem;border-bottom-width:1px;border-right-width:1px;padding:.75rem 1rem;text-align:left;font-weight:700;background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface));position:sticky;left:0;z-index:25}.axp-compare-view .__table .__thead .__header-row .__object-header{position:relative;border-bottom-width:1px;padding:.75rem 1rem;text-align:center;background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface));min-width:250px;width:250px}.axp-compare-view .__table .__thead .__header-row .__object-header .__object-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;line-height:1.25rem;font-weight:600}.axp-compare-view .__table .__thead .__header-row .__object-header .__object-description{margin-top:.25rem;font-size:.75rem;line-height:1rem;opacity:.75}.axp-compare-view .__table .__thead .__header-row .__object-header .__remove-object-button{display:flex;width:1.5rem;height:1.5rem;cursor:pointer;align-items:center;justify-content:center;border-radius:9999px;--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-light-surface),var(--tw-bg-opacity, 1));font-size:.875rem;line-height:1.25rem;position:absolute;top:50%;inset-inline-end:0px;--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.axp-compare-view .__table .__thead .__header-row .__object-header .__remove-object-button:hover{--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-100),var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-danger-600),var(--tw-text-opacity, 1))}.axp-compare-view .__table .__tbody .__row{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}.axp-compare-view .__table .__tbody .__row.__row--odd{background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.axp-compare-view .__table .__tbody .__row.__row--has-differences{border-left-width:4px;--tw-border-opacity: 1;border-left-color:rgba(var(--ax-sys-color-warning-500),var(--tw-border-opacity, 1))}.axp-compare-view .__table .__tbody .__row.__row--has-differences .__field-cell{background-color:rgba(var(--ax-sys-color-warning-500),.05)}.axp-compare-view .__table .__tbody .__row.__row--has-equal-values{border-left-width:4px;--tw-border-opacity: 1;border-left-color:rgba(var(--ax-sys-color-success-500),var(--tw-border-opacity, 1))}.axp-compare-view .__table .__tbody .__row.__row--has-equal-values .__field-cell{background-color:rgba(var(--ax-sys-color-success-500),.05)}.axp-compare-view .__table .__tbody .__row .__field-cell{width:14rem;border-bottom-width:1px;border-right-width:1px;padding:.75rem 1rem;vertical-align:top;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));position:sticky;left:0;z-index:15}.axp-compare-view .__table .__tbody .__row .__field-cell .__field-content{display:flex;flex-direction:column;gap:.25rem}.axp-compare-view .__table .__tbody .__row .__field-cell .__field-content .__field-title{font-weight:500}.axp-compare-view .__table .__tbody .__row .__field-cell .__field-content .__field-description{font-size:.75rem;line-height:1rem;opacity:.75}.axp-compare-view .__table .__tbody .__row .__object-cell{border-bottom-width:1px;padding:.75rem 1rem;text-align:center;vertical-align:middle;position:relative}.axp-compare-view .__table .__tbody .__row .__object-cell.__cell--changed .__object-content{border-width:1px;border-style:dashed;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-warning-200),var(--tw-border-opacity, 1));padding:.5rem}.axp-compare-view .__table .__tbody .__row .__object-cell .__object-content{position:relative;display:flex;align-items:center;justify-content:center}.axp-compare-view .__table .__tbody .__row .__object-cell .__change-indicator{position:absolute;inset-inline-end:-.25rem;top:50%;--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));line-height:1rem;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-warning-500),var(--tw-text-opacity, 1));font-size:.875rem}.axp-compare-view .__table .__tbody .__row .__object-cell .__object-placeholder{opacity:.75}.axp-compare-view .__table .__tbody .__row.__row--has-differences .__field-cell{border-inline-start-width:4px;--tw-border-opacity: 1;border-inline-start-color:rgba(var(--ax-sys-color-warning-500),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-warning-50),var(--tw-bg-opacity, 1))}.axp-compare-view .__table .__tbody .__row.__row--has-differences .__field-cell:is(.ax-dark *){--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-warning-900),var(--tw-bg-opacity, 1))}.axp-compare-view .__table .__tbody .__row.__row--has-equal-values .__field-cell{border-inline-start-width:4px;--tw-border-opacity: 1;border-inline-start-color:rgba(var(--ax-sys-color-success-500),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-success-50),var(--tw-bg-opacity, 1))}.axp-compare-view .__table .__tbody .__row.__row--has-equal-values .__field-cell:is(.ax-dark *){--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-success-900),var(--tw-bg-opacity, 1))}\n"] }]
1469
1468
  }], propDecorators: { inputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputs", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }] } });
1470
1469
 
1471
- class AXPComponentSlot {
1472
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1473
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: AXPComponentSlot, isStandalone: true, ngImport: i0 }); }
1474
- }
1475
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlot, decorators: [{
1476
- type: Directive
1477
- }] });
1478
-
1479
- class AXPComponentSlotRegistryService {
1480
- constructor() {
1481
- this.registry = new Map();
1482
- }
1483
- register(slotName, config) {
1484
- let configs = this.registry.get(slotName) || [];
1485
- // Check if the component is already registered in this slot
1486
- const isDuplicate = configs.some(existingConfig => existingConfig.name === config.name);
1487
- if (!isDuplicate) {
1488
- configs = [...configs, config]; // Add the new configuration
1489
- this.registry.set(slotName, configs);
1490
- }
1491
- }
1492
- get(slotName) {
1493
- return this.registry.get(slotName) || [];
1494
- }
1495
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1496
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotRegistryService, providedIn: 'root' }); }
1497
- }
1498
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotRegistryService, decorators: [{
1499
- type: Injectable,
1500
- args: [{
1501
- providedIn: 'root'
1502
- }]
1503
- }] });
1504
-
1505
- class AXPComponentSlotPlaceholderComponent {
1506
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotPlaceholderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1507
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: AXPComponentSlotPlaceholderComponent, isStandalone: true, selector: "axp-component-slot-placeholder", ngImport: i0, template: `<div>
1508
- <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1509
- </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1510
- }
1511
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotPlaceholderComponent, decorators: [{
1512
- type: Component,
1513
- args: [{
1514
- selector: 'axp-component-slot-placeholder',
1515
- template: `<div>
1516
- <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1517
- </div>`,
1518
- changeDetection: ChangeDetectionStrategy.OnPush,
1519
- imports: [AXSkeletonModule],
1520
- standalone: true,
1521
- }]
1522
- }] });
1523
-
1524
- class AXPComponentSlotDirective {
1525
- constructor() {
1526
- this.name = input.required(...(ngDevMode ? [{ debugName: "name" }] : []));
1527
- this.host = input(...(ngDevMode ? [undefined, { debugName: "host" }] : []));
1528
- this.context = input(...(ngDevMode ? [undefined, { debugName: "context" }] : []));
1529
- this.elementRef = inject(ElementRef);
1530
- this.registryService = inject(AXPComponentSlotRegistryService);
1531
- this.injector = inject(Injector);
1532
- this.evaluator = inject(AXPExpressionEvaluatorService);
1533
- this.sessionService = inject(AXPSessionService);
1534
- this.viewContainerRef = inject(ViewContainerRef);
1535
- this.contextStore = inject(AXPContextStore);
1536
- this.isEmpty = computed(() => this._viewCount() === 0, ...(ngDevMode ? [{ debugName: "isEmpty" }] : []));
1537
- // Create a signal to store the count of children
1538
- this._viewCount = signal(0, ...(ngDevMode ? [{ debugName: "_viewCount" }] : []));
1539
- // Track component references for updates
1540
- this.componentRefs = [];
1541
- // Track placeholder references for replacement
1542
- this.placeholderRefs = new Map();
1543
- // Watch for context changes and update component instances
1544
- effect(() => {
1545
- const currentContext = this.context();
1546
- const currentHost = this.host();
1547
- // Update all component references with new context and host
1548
- this.componentRefs.forEach(componentRef => {
1549
- if (componentRef.instance) {
1550
- Object.assign(componentRef.instance, {
1551
- host: currentHost,
1552
- context: currentContext,
1553
- });
1554
- }
1555
- });
1556
- });
1557
- }
1558
- async ngOnInit() {
1559
- await this.loadComponents();
1560
- // Update the signal after loading
1561
- this._viewCount.set(this.viewContainerRef.length);
1562
- }
1563
- async loadComponents() {
1564
- this.viewContainerRef.clear();
1565
- // Clear previous component references
1566
- this.componentRefs = [];
1567
- this.placeholderRefs.clear();
1568
- const configs = sortBy(this.registryService.get(this.name()), (c) => c.priority ?? 0);
1569
- if (!configs) {
1570
- console.error(`No component found for slot ${this.name()}`);
1571
- return;
1572
- }
1573
- // Evaluate conditions and check features in parallel for performance
1574
- const results = await Promise.all(configs.map(async (c) => {
1575
- // Check condition if provided
1576
- const conditionPassed = c.condition ? await this.evaluateCondition(c.condition) : true;
1577
- // Check features if provided
1578
- const featuresPassed = c.features && c.features.length > 0
1579
- ? this.sessionService.isFeatureEnabled(...c.features)
1580
- : true;
1581
- return {
1582
- config: c,
1583
- visible: conditionPassed && featuresPassed,
1584
- };
1585
- }));
1586
- // Filter visible components while preserving priority order
1587
- const slots = results.filter((r) => r.visible).map((r) => r.config);
1588
- // Create skeleton placeholders immediately in priority order
1589
- slots.forEach((config, index) => {
1590
- const placeholderRef = this.viewContainerRef.createComponent(AXPComponentSlotPlaceholderComponent);
1591
- this.placeholderRefs.set(index, placeholderRef);
1592
- });
1593
- // Load all components in parallel and replace placeholders as they become ready
1594
- const loadPromises = slots.map(async (config, index) => {
1595
- let component;
1596
- let options = {};
1597
- // load component
1598
- if (typeof config.loadComponent === 'function') {
1599
- await runInInjectionContext(this.injector, async () => {
1600
- component = await config.loadComponent?.();
1601
- });
1602
- }
1603
- else if (config.component) {
1604
- component = config.component;
1605
- }
1606
- // load options
1607
- if (typeof config.options === 'function') {
1608
- await runInInjectionContext(this.injector, async () => {
1609
- const fun = config.options;
1610
- options = await fun();
1611
- });
1612
- }
1613
- else if (config.options) {
1614
- options = await this.evaluator.evaluate(config.options, {});
1615
- }
1616
- // Replace placeholder with actual component as soon as it's ready
1617
- if (!component) {
1618
- console.warn(`Component failed to load for slot ${this.name()} at index ${index}`);
1619
- // Remove placeholder if component failed to load
1620
- const placeholderRef = this.placeholderRefs.get(index);
1621
- if (placeholderRef) {
1622
- placeholderRef.destroy();
1623
- this.placeholderRefs.delete(index);
1624
- }
1625
- return;
1626
- }
1627
- // Get the placeholder reference at this index
1628
- const placeholderRef = this.placeholderRefs.get(index);
1629
- if (!placeholderRef) {
1630
- console.warn(`Placeholder not found for index ${index}`);
1631
- return;
1632
- }
1633
- // Get the index of the placeholder in the view container
1634
- const placeholderIndex = this.viewContainerRef.indexOf(placeholderRef.hostView);
1635
- // Remove the placeholder
1636
- placeholderRef.destroy();
1637
- this.placeholderRefs.delete(index);
1638
- // Create the actual component at the same position
1639
- const componentRef = this.viewContainerRef.createComponent(component, { index: placeholderIndex });
1640
- // Store the component reference for future updates
1641
- this.componentRefs.push(componentRef);
1642
- Object.assign(componentRef.instance, {
1643
- host: this.host(),
1644
- context: this.context(),
1645
- ...options,
1646
- });
1647
- });
1648
- // Wait for all components to finish loading (they render as they become ready)
1649
- await Promise.all(loadPromises);
1650
- }
1651
- async evaluateCondition(condition) {
1652
- if (typeof condition === 'string') {
1653
- const result = await this.evaluator.evaluate(condition, {});
1654
- return result;
1655
- }
1656
- return condition(this.contextStore.data());
1657
- //return condition(this.context());
1658
- }
1659
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1660
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.16", type: AXPComponentSlotDirective, isStandalone: false, selector: "axp-component-slot", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, host: { classPropertyName: "host", publicName: "host", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["slot"], ngImport: i0 }); }
1661
- }
1662
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotDirective, decorators: [{
1663
- type: Directive,
1664
- args: [{
1665
- selector: 'axp-component-slot',
1666
- standalone: false,
1667
- exportAs: 'slot',
1668
- }]
1669
- }], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], host: [{ type: i0.Input, args: [{ isSignal: true, alias: "host", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }] } });
1670
-
1671
- class AXPComponentSlotModule {
1672
- static forRoot(configs) {
1673
- return {
1674
- ngModule: AXPComponentSlotModule,
1675
- providers: [
1676
- {
1677
- provide: 'AXPComponentSlotModuleFactory',
1678
- useFactory: (registry) => () => {
1679
- if (configs) {
1680
- for (const [key, value] of Object.entries(configs)) {
1681
- value.forEach(v => {
1682
- registry.register(key, v);
1683
- });
1684
- }
1685
- }
1686
- },
1687
- deps: [AXPComponentSlotRegistryService],
1688
- multi: true
1689
- }
1690
- ]
1691
- };
1692
- }
1693
- static forChild(configs) {
1694
- return {
1695
- ngModule: AXPComponentSlotModule,
1696
- providers: [
1697
- {
1698
- provide: 'AXPComponentSlotModuleFactory',
1699
- useFactory: (registry) => () => {
1700
- if (configs) {
1701
- for (const [key, value] of Object.entries(configs)) {
1702
- value.forEach(v => {
1703
- registry.register(key, v);
1704
- });
1705
- }
1706
- }
1707
- },
1708
- deps: [AXPComponentSlotRegistryService],
1709
- multi: true
1710
- }
1711
- ]
1712
- };
1713
- }
1714
- /**
1715
- * @ignore
1716
- */
1717
- constructor(instances) {
1718
- instances?.forEach(f => {
1719
- f();
1720
- });
1721
- }
1722
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotModule, deps: [{ token: 'AXPComponentSlotModuleFactory', optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
1723
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotModule, declarations: [AXPComponentSlotDirective], exports: [AXPComponentSlotDirective] }); }
1724
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotModule }); }
1725
- }
1726
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPComponentSlotModule, decorators: [{
1727
- type: NgModule,
1728
- args: [{
1729
- declarations: [AXPComponentSlotDirective],
1730
- exports: [AXPComponentSlotDirective]
1731
- }]
1732
- }], ctorParameters: () => [{ type: undefined, decorators: [{
1733
- type: Optional
1734
- }, {
1735
- type: Inject,
1736
- args: ['AXPComponentSlotModuleFactory']
1737
- }] }] });
1738
-
1739
1470
  //#endregion
1740
1471
  class AXPDataSelectorComponent extends AXBasePageComponent {
1741
1472
  //#endregion
@@ -2687,7 +2418,7 @@ class AXPQueryFiltersComponent {
2687
2418
  }
2688
2419
  }
2689
2420
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPQueryFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2690
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPQueryFiltersComponent, isStandalone: true, selector: "axp-query-filters", inputs: { filtersDefinitions: { classPropertyName: "filtersDefinitions", publicName: "filtersDefinitions", isSignal: true, isRequired: false, transformFunction: null }, initialFilters: { classPropertyName: "initialFilters", publicName: "initialFilters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onFiltersChanged: "onFiltersChanged" }, host: { listeners: { "document:keydown": "handleKeyboardEvent($event)" } }, viewQueries: [{ propertyName: "tagBox", first: true, predicate: ["tagBox"], descendants: true, isSignal: true }, { propertyName: "widgetRenderer", first: true, predicate: ["widgetRenderer"], descendants: true, isSignal: true }, { propertyName: "popover", first: true, predicate: ["popover"], descendants: true, isSignal: true }, { propertyName: "listItems", predicate: ["caseItem"], descendants: true }], ngImport: i0, template: "<div class=\"ax-flex ax-items-center ax-gap-2 ax-p-2\">\n <ax-button (keydown)=\"handleButtonKeyDown($event)\" (onClick)=\"popover.open()\" #filterButton [look]=\"'blank'\">\n <ax-icon class=\"far fa-bars-filter ax-cursor-pointer\"> </ax-icon>\n </ax-button>\n <ax-tag-box\n #tagBoxComponent\n [ngModel]=\"asyncTags()\"\n (onValueChanged)=\"handleSelectFilters($event)\"\n [textField]=\"'query'\"\n [valueField]=\"'id'\"\n [readonly]=\"filtersDefinitions().length === 0\"\n [look]=\"'none'\"\n [readonlyField]=\"'readOnly'\"\n [tagTemplate]=\"tagTemplate\"\n (onKeyDown)=\"handleKeyDown($event)\"\n [addOnEnter]=\"false\"\n [placeholder]=\"\n (filtersDefinitions().length === 0\n ? '@general:terms.interface.filter.no-filter-definitions'\n : '@general:terms.interface.filter.placeholder'\n )\n | translate\n | async\n \"\n #tagBox\n >\n <ax-suffix class=\"ax-hidden md:ax-block\"><span class=\"ax-text-gray-500\">Ctrl+F</span></ax-suffix>\n </ax-tag-box>\n\n <ng-template #tagTemplate let-item let-index=\"index\">\n <div\n class=\"ax-inline-flex ax-items-center ax-gap-1.5 hover:ax-bg-darkest ax-cursor-pointer ax-rounded-md ax-px-3 ax-py-1 ax-text-sm ax-surface\"\n [class.!ax-bg-primary]=\"item.field === 'all'\"\n [class.!ax-text-white]=\"item.field === 'all'\"\n (click)=\"handleTagClick(item)\"\n >\n <span>{{ item.query }}</span>\n <button (click)=\"handleRemoveTag($event, index)\" type=\"button\">\n <ax-icon class=\"ax-icon ax-icon-close\"></ax-icon>\n </button>\n </div>\n </ng-template>\n</div>\n\n<ax-popover\n [offsetY]=\"activeFilter() ? -30 : 0\"\n [target]=\"tagBoxInput\"\n [openOn]=\"'toggle'\"\n (onOpened)=\"onPopoverOpened($event)\"\n [closeOn]=\"'clickOut'\"\n (onClosed)=\"handlePopoverClosed($event)\"\n [adaptivityEnabled]=\"true\"\n #popover\n>\n <div\n class=\"md:ax-min-w-72 ax-border ax-surface ax-w-full ax-rounded-md md:ax-max-h-96 ax-max-w-80 md:ax-overflow-auto\"\n >\n <axp-widgets-container [context]=\"context()\" (onContextChanged)=\"onContextChanged($event)\">\n @if (activeFilter()) {\n <div class=\"ax-flex ax-flex-col ax-lightest-surface ax-shadow-md\" (keydown.enter)=\"onEnterKeyPressed($event)\">\n <ax-header class=\"ax-border-b ax-border-light ax-px-4 ax-py-2\">{{\n activeFilter()?.title! | translate | async\n }}</ax-header>\n <ax-content class=\"ax-p-4\">\n <div class=\"ax-mb-2\">\n <ax-badge [text]=\"(getActiveOperator(activeFilter())! | translate | async) || ''\"></ax-badge>\n </div>\n <ng-container\n #widgetRenderer=\"widgetRenderer\"\n axp-widget-renderer\n [node]=\"{\n type: activeFilter()?.widget?.type || 'text-editor',\n path: activeFilter()?.field,\n options: activeFilter()?.widget?.options,\n }\"\n [mode]=\"'edit'\"\n >\n </ng-container>\n </ax-content>\n <ax-footer class=\"ax-border-t ax-flex ax-justify-end ax-border-light ax-w-full ax-px-4 ax-py-2\">\n <ax-button\n class=\"ax-xs\"\n [text]=\"'@general:actions.apply.title' | translate | async\"\n (onClick)=\"handleApplyFilter()\"\n ></ax-button>\n </ax-footer>\n </div>\n } @else {\n <div axListNavigation #list=\"axListNavigation\" class=\"axp-list-items\">\n <!-- @if (tagBox.inputValue()) {\n @for (inlineFilter of inlineFilters(); track inlineFilter.field) {\n <div\n axListNavigationItem\n #caseItem=\"axListNavigationItem\"\n [class.axp-state-focused]=\"caseItem.isActive()\"\n tabindex=\"0\"\n (click)=\"handleSelectInlineFilter(inlineFilter)\"\n (keydown)=\"handleInlineFilterKeyDown($event, inlineFilter)\"\n >\n {{ inlineFilter.title | translate | async }} {{ getActiveOperator(inlineFilter) }} '{{\n tagBox.inputValue()\n }}'\n </div>\n }\n <span class=\"ax-w-full ax-border-t ax-border-light ax-my-1\"></span>\n } -->\n @for (field of filterFields(); track field.field) {\n <div\n axListNavigationItem\n #caseItem=\"axListNavigationItem\"\n [class.axp-state-focused]=\"caseItem.isActive()\"\n (click)=\"handleSelectField(field)\"\n (keydown)=\"handleFieldKeyDown($event, field)\"\n tabindex=\"0\"\n >\n <div class=\"ax-flex ax-items-end ax-gap-2\">\n <ax-icon class=\"ax-w-5\" [class]=\"'fa-light ' + field.icon\"> </ax-icon>\n {{ field.title | translate | async }}\n </div>\n </div>\n }\n </div>\n }\n </axp-widgets-container>\n </div>\n</ax-popover>\n", styles: ["axp-query-filters{width:100%}.axp-list-items{display:flex;min-width:10rem;flex-direction:column;border-radius:.375rem;border-width:1px;padding-top:1rem;padding-bottom:1rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.axp-list-items>div{min-width:7rem;cursor:pointer;padding:.5rem 1rem;text-align:start}.axp-list-items>div:focus{outline:none}.axp-list-items>div.axp-state-focused,.axp-list-items>div:hover{background-color:rgb(var(--ax-sys-color-surface));color:rgb(var(--ax-sys-color-on-surface));border-color:rgb(var(--ax-sys-color-border-surface))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", 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: AXTranslationModule }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2$3.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "forceDisableActionSheetStyle", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXSelectionListModule }, { kind: "ngmodule", type: AXTagBoxModule }, { kind: "component", type: i5$2.AXTagBoxComponent, selector: "ax-tag-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "allowNull", "type", "look", "addOnComma", "addOnEnter", "valueField", "textField", "readonlyField", "allowDuplicateValues", "tagTemplate"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress", "onTagClick", "onTagDblClick", "onTagContextMenu"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i6$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i6$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXListNavigationModule }, { kind: "directive", type: i7.AXListNavigationDirective, selector: "[axListNavigation]", inputs: ["orientation"], outputs: ["onNavigationChanged", "onKeypress"], exportAs: ["axListNavigation"] }, { kind: "directive", type: i7.AXListNavigationItemDirective, selector: "[axListNavigationItem]", outputs: ["onKeypress"], exportAs: ["axListNavigationItem"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2421
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPQueryFiltersComponent, isStandalone: true, selector: "axp-query-filters", inputs: { filtersDefinitions: { classPropertyName: "filtersDefinitions", publicName: "filtersDefinitions", isSignal: true, isRequired: false, transformFunction: null }, initialFilters: { classPropertyName: "initialFilters", publicName: "initialFilters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onFiltersChanged: "onFiltersChanged" }, host: { listeners: { "document:keydown": "handleKeyboardEvent($event)" } }, viewQueries: [{ propertyName: "tagBox", first: true, predicate: ["tagBox"], descendants: true, isSignal: true }, { propertyName: "widgetRenderer", first: true, predicate: ["widgetRenderer"], descendants: true, isSignal: true }, { propertyName: "popover", first: true, predicate: ["popover"], descendants: true, isSignal: true }, { propertyName: "listItems", predicate: ["caseItem"], descendants: true }], ngImport: i0, template: "<div class=\"ax-flex ax-items-center ax-gap-2 ax-p-2\">\n <ax-button (keydown)=\"handleButtonKeyDown($event)\" (onClick)=\"popover.open()\" #filterButton [look]=\"'blank'\">\n <ax-icon class=\"far fa-bars-filter ax-cursor-pointer\"> </ax-icon>\n </ax-button>\n <ax-tag-box\n #tagBoxComponent\n [ngModel]=\"asyncTags()\"\n (onValueChanged)=\"handleSelectFilters($event)\"\n [textField]=\"'query'\"\n [valueField]=\"'id'\"\n [readonly]=\"filtersDefinitions().length === 0\"\n [look]=\"'none'\"\n [readonlyField]=\"'readOnly'\"\n [tagTemplate]=\"tagTemplate\"\n (onKeyDown)=\"handleKeyDown($event)\"\n [addOnEnter]=\"false\"\n [placeholder]=\"\n (filtersDefinitions().length === 0\n ? '@general:terms.interface.filter.no-filter-definitions'\n : '@general:terms.interface.filter.placeholder'\n )\n | translate\n | async\n \"\n #tagBox\n >\n <ax-suffix class=\"ax-hidden md:ax-block\"><span class=\"ax-text-gray-500\">Ctrl+F</span></ax-suffix>\n </ax-tag-box>\n\n <ng-template #tagTemplate let-item let-index=\"index\">\n <div\n class=\"ax-inline-flex ax-items-center ax-gap-1.5 hover:ax-bg-darkest ax-cursor-pointer ax-rounded-md ax-px-3 ax-py-1 ax-text-sm ax-surface\"\n [class.!ax-bg-primary]=\"item.field === 'all'\"\n [class.!ax-text-white]=\"item.field === 'all'\"\n (click)=\"handleTagClick(item)\"\n >\n <span>{{ item.query }}</span>\n <button (click)=\"handleRemoveTag($event, index)\" type=\"button\">\n <ax-icon class=\"ax-icon ax-icon-close\"></ax-icon>\n </button>\n </div>\n </ng-template>\n</div>\n\n<ax-popover\n [offsetY]=\"activeFilter() ? -30 : 0\"\n [target]=\"tagBoxInput\"\n [openOn]=\"'toggle'\"\n (onOpened)=\"onPopoverOpened($event)\"\n [closeOn]=\"'clickOut'\"\n (onClosed)=\"handlePopoverClosed($event)\"\n [adaptivityEnabled]=\"true\"\n #popover\n>\n <div class=\"md:ax-min-w-72 ax-w-full ax-rounded-md md:ax-max-h-96 ax-max-w-80 md:ax-overflow-auto\">\n <axp-widgets-container [context]=\"context()\" (onContextChanged)=\"onContextChanged($event)\">\n @if (activeFilter()) {\n <div class=\"ax-flex ax-flex-col ax-lightest-surface ax-shadow-md\" (keydown.enter)=\"onEnterKeyPressed($event)\">\n <ax-header class=\"ax-border-b ax-border-light ax-px-4 ax-py-2\">{{\n activeFilter()?.title! | translate | async\n }}</ax-header>\n <ax-content class=\"ax-p-4\">\n <div class=\"ax-mb-2\">\n <ax-badge [text]=\"(getActiveOperator(activeFilter())! | translate | async) || ''\"></ax-badge>\n </div>\n <ng-container\n #widgetRenderer=\"widgetRenderer\"\n axp-widget-renderer\n [node]=\"{\n type: activeFilter()?.widget?.type || 'text-editor',\n path: activeFilter()?.field,\n options: activeFilter()?.widget?.options,\n }\"\n [mode]=\"'edit'\"\n >\n </ng-container>\n </ax-content>\n <ax-footer class=\"ax-border-t ax-flex ax-justify-end ax-border-light ax-w-full ax-px-4 ax-py-2\">\n <ax-button\n class=\"ax-xs\"\n [text]=\"'@general:actions.apply.title' | translate | async\"\n (onClick)=\"handleApplyFilter()\"\n ></ax-button>\n </ax-footer>\n </div>\n } @else {\n <div axListNavigation #list=\"axListNavigation\" class=\"axp-list-items\">\n <!-- @if (tagBox.inputValue()) {\n @for (inlineFilter of inlineFilters(); track inlineFilter.field) {\n <div\n axListNavigationItem\n #caseItem=\"axListNavigationItem\"\n [class.axp-state-focused]=\"caseItem.isActive()\"\n tabindex=\"0\"\n (click)=\"handleSelectInlineFilter(inlineFilter)\"\n (keydown)=\"handleInlineFilterKeyDown($event, inlineFilter)\"\n >\n {{ inlineFilter.title | translate | async }} {{ getActiveOperator(inlineFilter) }} '{{\n tagBox.inputValue()\n }}'\n </div>\n }\n <span class=\"ax-w-full ax-border-t ax-border-light ax-my-1\"></span>\n } -->\n @for (field of filterFields(); track field.field) {\n <div\n axListNavigationItem\n #caseItem=\"axListNavigationItem\"\n [class.axp-state-focused]=\"caseItem.isActive()\"\n (click)=\"handleSelectField(field)\"\n (keydown)=\"handleFieldKeyDown($event, field)\"\n tabindex=\"0\"\n >\n <div class=\"ax-flex ax-items-end ax-gap-2\">\n <ax-icon class=\"ax-w-5\" [class]=\"'fa-light ' + field.icon\"> </ax-icon>\n {{ field.title | translate | async }}\n </div>\n </div>\n }\n </div>\n }\n </axp-widgets-container>\n </div>\n</ax-popover>\n", styles: ["axp-query-filters{width:100%}.axp-list-items{display:flex;min-width:10rem;flex-direction:column;border-radius:.375rem;border-width:1px;padding-top:1rem;padding-bottom:1rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.axp-list-items>div{min-width:7rem;cursor:pointer;padding:.5rem 1rem;text-align:start}.axp-list-items>div:focus{outline:none}.axp-list-items>div.axp-state-focused,.axp-list-items>div:hover{background-color:rgb(var(--ax-sys-color-surface));color:rgb(var(--ax-sys-color-on-surface));border-color:rgb(var(--ax-sys-color-border-surface))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", 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: AXTranslationModule }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2$3.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXSelectionListModule }, { kind: "ngmodule", type: AXTagBoxModule }, { kind: "component", type: i5$2.AXTagBoxComponent, selector: "ax-tag-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "allowNull", "type", "look", "addOnComma", "addOnEnter", "valueField", "textField", "readonlyField", "allowDuplicateValues", "tagTemplate"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress", "onTagClick", "onTagDblClick", "onTagContextMenu"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i6$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i6$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXListNavigationModule }, { kind: "directive", type: i7.AXListNavigationDirective, selector: "[axListNavigation]", inputs: ["orientation"], outputs: ["onNavigationChanged", "onKeypress"], exportAs: ["axListNavigation"] }, { kind: "directive", type: i7.AXListNavigationItemDirective, selector: "[axListNavigationItem]", outputs: ["onKeypress"], exportAs: ["axListNavigationItem"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2691
2422
  }
2692
2423
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPQueryFiltersComponent, decorators: [{
2693
2424
  type: Component,
@@ -2703,7 +2434,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2703
2434
  AXPWidgetCoreModule,
2704
2435
  AXListNavigationModule,
2705
2436
  AXBadgeModule,
2706
- ], template: "<div class=\"ax-flex ax-items-center ax-gap-2 ax-p-2\">\n <ax-button (keydown)=\"handleButtonKeyDown($event)\" (onClick)=\"popover.open()\" #filterButton [look]=\"'blank'\">\n <ax-icon class=\"far fa-bars-filter ax-cursor-pointer\"> </ax-icon>\n </ax-button>\n <ax-tag-box\n #tagBoxComponent\n [ngModel]=\"asyncTags()\"\n (onValueChanged)=\"handleSelectFilters($event)\"\n [textField]=\"'query'\"\n [valueField]=\"'id'\"\n [readonly]=\"filtersDefinitions().length === 0\"\n [look]=\"'none'\"\n [readonlyField]=\"'readOnly'\"\n [tagTemplate]=\"tagTemplate\"\n (onKeyDown)=\"handleKeyDown($event)\"\n [addOnEnter]=\"false\"\n [placeholder]=\"\n (filtersDefinitions().length === 0\n ? '@general:terms.interface.filter.no-filter-definitions'\n : '@general:terms.interface.filter.placeholder'\n )\n | translate\n | async\n \"\n #tagBox\n >\n <ax-suffix class=\"ax-hidden md:ax-block\"><span class=\"ax-text-gray-500\">Ctrl+F</span></ax-suffix>\n </ax-tag-box>\n\n <ng-template #tagTemplate let-item let-index=\"index\">\n <div\n class=\"ax-inline-flex ax-items-center ax-gap-1.5 hover:ax-bg-darkest ax-cursor-pointer ax-rounded-md ax-px-3 ax-py-1 ax-text-sm ax-surface\"\n [class.!ax-bg-primary]=\"item.field === 'all'\"\n [class.!ax-text-white]=\"item.field === 'all'\"\n (click)=\"handleTagClick(item)\"\n >\n <span>{{ item.query }}</span>\n <button (click)=\"handleRemoveTag($event, index)\" type=\"button\">\n <ax-icon class=\"ax-icon ax-icon-close\"></ax-icon>\n </button>\n </div>\n </ng-template>\n</div>\n\n<ax-popover\n [offsetY]=\"activeFilter() ? -30 : 0\"\n [target]=\"tagBoxInput\"\n [openOn]=\"'toggle'\"\n (onOpened)=\"onPopoverOpened($event)\"\n [closeOn]=\"'clickOut'\"\n (onClosed)=\"handlePopoverClosed($event)\"\n [adaptivityEnabled]=\"true\"\n #popover\n>\n <div\n class=\"md:ax-min-w-72 ax-border ax-surface ax-w-full ax-rounded-md md:ax-max-h-96 ax-max-w-80 md:ax-overflow-auto\"\n >\n <axp-widgets-container [context]=\"context()\" (onContextChanged)=\"onContextChanged($event)\">\n @if (activeFilter()) {\n <div class=\"ax-flex ax-flex-col ax-lightest-surface ax-shadow-md\" (keydown.enter)=\"onEnterKeyPressed($event)\">\n <ax-header class=\"ax-border-b ax-border-light ax-px-4 ax-py-2\">{{\n activeFilter()?.title! | translate | async\n }}</ax-header>\n <ax-content class=\"ax-p-4\">\n <div class=\"ax-mb-2\">\n <ax-badge [text]=\"(getActiveOperator(activeFilter())! | translate | async) || ''\"></ax-badge>\n </div>\n <ng-container\n #widgetRenderer=\"widgetRenderer\"\n axp-widget-renderer\n [node]=\"{\n type: activeFilter()?.widget?.type || 'text-editor',\n path: activeFilter()?.field,\n options: activeFilter()?.widget?.options,\n }\"\n [mode]=\"'edit'\"\n >\n </ng-container>\n </ax-content>\n <ax-footer class=\"ax-border-t ax-flex ax-justify-end ax-border-light ax-w-full ax-px-4 ax-py-2\">\n <ax-button\n class=\"ax-xs\"\n [text]=\"'@general:actions.apply.title' | translate | async\"\n (onClick)=\"handleApplyFilter()\"\n ></ax-button>\n </ax-footer>\n </div>\n } @else {\n <div axListNavigation #list=\"axListNavigation\" class=\"axp-list-items\">\n <!-- @if (tagBox.inputValue()) {\n @for (inlineFilter of inlineFilters(); track inlineFilter.field) {\n <div\n axListNavigationItem\n #caseItem=\"axListNavigationItem\"\n [class.axp-state-focused]=\"caseItem.isActive()\"\n tabindex=\"0\"\n (click)=\"handleSelectInlineFilter(inlineFilter)\"\n (keydown)=\"handleInlineFilterKeyDown($event, inlineFilter)\"\n >\n {{ inlineFilter.title | translate | async }} {{ getActiveOperator(inlineFilter) }} '{{\n tagBox.inputValue()\n }}'\n </div>\n }\n <span class=\"ax-w-full ax-border-t ax-border-light ax-my-1\"></span>\n } -->\n @for (field of filterFields(); track field.field) {\n <div\n axListNavigationItem\n #caseItem=\"axListNavigationItem\"\n [class.axp-state-focused]=\"caseItem.isActive()\"\n (click)=\"handleSelectField(field)\"\n (keydown)=\"handleFieldKeyDown($event, field)\"\n tabindex=\"0\"\n >\n <div class=\"ax-flex ax-items-end ax-gap-2\">\n <ax-icon class=\"ax-w-5\" [class]=\"'fa-light ' + field.icon\"> </ax-icon>\n {{ field.title | translate | async }}\n </div>\n </div>\n }\n </div>\n }\n </axp-widgets-container>\n </div>\n</ax-popover>\n", styles: ["axp-query-filters{width:100%}.axp-list-items{display:flex;min-width:10rem;flex-direction:column;border-radius:.375rem;border-width:1px;padding-top:1rem;padding-bottom:1rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.axp-list-items>div{min-width:7rem;cursor:pointer;padding:.5rem 1rem;text-align:start}.axp-list-items>div:focus{outline:none}.axp-list-items>div.axp-state-focused,.axp-list-items>div:hover{background-color:rgb(var(--ax-sys-color-surface));color:rgb(var(--ax-sys-color-on-surface));border-color:rgb(var(--ax-sys-color-border-surface))}\n"] }]
2437
+ ], template: "<div class=\"ax-flex ax-items-center ax-gap-2 ax-p-2\">\n <ax-button (keydown)=\"handleButtonKeyDown($event)\" (onClick)=\"popover.open()\" #filterButton [look]=\"'blank'\">\n <ax-icon class=\"far fa-bars-filter ax-cursor-pointer\"> </ax-icon>\n </ax-button>\n <ax-tag-box\n #tagBoxComponent\n [ngModel]=\"asyncTags()\"\n (onValueChanged)=\"handleSelectFilters($event)\"\n [textField]=\"'query'\"\n [valueField]=\"'id'\"\n [readonly]=\"filtersDefinitions().length === 0\"\n [look]=\"'none'\"\n [readonlyField]=\"'readOnly'\"\n [tagTemplate]=\"tagTemplate\"\n (onKeyDown)=\"handleKeyDown($event)\"\n [addOnEnter]=\"false\"\n [placeholder]=\"\n (filtersDefinitions().length === 0\n ? '@general:terms.interface.filter.no-filter-definitions'\n : '@general:terms.interface.filter.placeholder'\n )\n | translate\n | async\n \"\n #tagBox\n >\n <ax-suffix class=\"ax-hidden md:ax-block\"><span class=\"ax-text-gray-500\">Ctrl+F</span></ax-suffix>\n </ax-tag-box>\n\n <ng-template #tagTemplate let-item let-index=\"index\">\n <div\n class=\"ax-inline-flex ax-items-center ax-gap-1.5 hover:ax-bg-darkest ax-cursor-pointer ax-rounded-md ax-px-3 ax-py-1 ax-text-sm ax-surface\"\n [class.!ax-bg-primary]=\"item.field === 'all'\"\n [class.!ax-text-white]=\"item.field === 'all'\"\n (click)=\"handleTagClick(item)\"\n >\n <span>{{ item.query }}</span>\n <button (click)=\"handleRemoveTag($event, index)\" type=\"button\">\n <ax-icon class=\"ax-icon ax-icon-close\"></ax-icon>\n </button>\n </div>\n </ng-template>\n</div>\n\n<ax-popover\n [offsetY]=\"activeFilter() ? -30 : 0\"\n [target]=\"tagBoxInput\"\n [openOn]=\"'toggle'\"\n (onOpened)=\"onPopoverOpened($event)\"\n [closeOn]=\"'clickOut'\"\n (onClosed)=\"handlePopoverClosed($event)\"\n [adaptivityEnabled]=\"true\"\n #popover\n>\n <div class=\"md:ax-min-w-72 ax-w-full ax-rounded-md md:ax-max-h-96 ax-max-w-80 md:ax-overflow-auto\">\n <axp-widgets-container [context]=\"context()\" (onContextChanged)=\"onContextChanged($event)\">\n @if (activeFilter()) {\n <div class=\"ax-flex ax-flex-col ax-lightest-surface ax-shadow-md\" (keydown.enter)=\"onEnterKeyPressed($event)\">\n <ax-header class=\"ax-border-b ax-border-light ax-px-4 ax-py-2\">{{\n activeFilter()?.title! | translate | async\n }}</ax-header>\n <ax-content class=\"ax-p-4\">\n <div class=\"ax-mb-2\">\n <ax-badge [text]=\"(getActiveOperator(activeFilter())! | translate | async) || ''\"></ax-badge>\n </div>\n <ng-container\n #widgetRenderer=\"widgetRenderer\"\n axp-widget-renderer\n [node]=\"{\n type: activeFilter()?.widget?.type || 'text-editor',\n path: activeFilter()?.field,\n options: activeFilter()?.widget?.options,\n }\"\n [mode]=\"'edit'\"\n >\n </ng-container>\n </ax-content>\n <ax-footer class=\"ax-border-t ax-flex ax-justify-end ax-border-light ax-w-full ax-px-4 ax-py-2\">\n <ax-button\n class=\"ax-xs\"\n [text]=\"'@general:actions.apply.title' | translate | async\"\n (onClick)=\"handleApplyFilter()\"\n ></ax-button>\n </ax-footer>\n </div>\n } @else {\n <div axListNavigation #list=\"axListNavigation\" class=\"axp-list-items\">\n <!-- @if (tagBox.inputValue()) {\n @for (inlineFilter of inlineFilters(); track inlineFilter.field) {\n <div\n axListNavigationItem\n #caseItem=\"axListNavigationItem\"\n [class.axp-state-focused]=\"caseItem.isActive()\"\n tabindex=\"0\"\n (click)=\"handleSelectInlineFilter(inlineFilter)\"\n (keydown)=\"handleInlineFilterKeyDown($event, inlineFilter)\"\n >\n {{ inlineFilter.title | translate | async }} {{ getActiveOperator(inlineFilter) }} '{{\n tagBox.inputValue()\n }}'\n </div>\n }\n <span class=\"ax-w-full ax-border-t ax-border-light ax-my-1\"></span>\n } -->\n @for (field of filterFields(); track field.field) {\n <div\n axListNavigationItem\n #caseItem=\"axListNavigationItem\"\n [class.axp-state-focused]=\"caseItem.isActive()\"\n (click)=\"handleSelectField(field)\"\n (keydown)=\"handleFieldKeyDown($event, field)\"\n tabindex=\"0\"\n >\n <div class=\"ax-flex ax-items-end ax-gap-2\">\n <ax-icon class=\"ax-w-5\" [class]=\"'fa-light ' + field.icon\"> </ax-icon>\n {{ field.title | translate | async }}\n </div>\n </div>\n }\n </div>\n }\n </axp-widgets-container>\n </div>\n</ax-popover>\n", styles: ["axp-query-filters{width:100%}.axp-list-items{display:flex;min-width:10rem;flex-direction:column;border-radius:.375rem;border-width:1px;padding-top:1rem;padding-bottom:1rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.axp-list-items>div{min-width:7rem;cursor:pointer;padding:.5rem 1rem;text-align:start}.axp-list-items>div:focus{outline:none}.axp-list-items>div.axp-state-focused,.axp-list-items>div:hover{background-color:rgb(var(--ax-sys-color-surface));color:rgb(var(--ax-sys-color-on-surface));border-color:rgb(var(--ax-sys-color-border-surface))}\n"] }]
2707
2438
  }], propDecorators: { filtersDefinitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtersDefinitions", required: false }] }], initialFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialFilters", required: false }] }], onFiltersChanged: [{ type: i0.Output, args: ["onFiltersChanged"] }], tagBox: [{ type: i0.ViewChild, args: ['tagBox', { isSignal: true }] }], widgetRenderer: [{ type: i0.ViewChild, args: ['widgetRenderer', { isSignal: true }] }], listItems: [{
2708
2439
  type: ViewChildren,
2709
2440
  args: ['caseItem']
@@ -2877,6 +2608,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2877
2608
  args: [{ providedIn: 'root' }]
2878
2609
  }] });
2879
2610
 
2611
+ class AXPLogoComponent {
2612
+ constructor() {
2613
+ this.platform = inject(AXPlatform);
2614
+ }
2615
+ ngOnInit() {
2616
+ this.setLogoType();
2617
+ this.platform.themeMode$.subscribe(() => {
2618
+ this.setLogoTheme();
2619
+ });
2620
+ }
2621
+ ngOnChanges() {
2622
+ this.setLogoTheme();
2623
+ this.setLogoType();
2624
+ }
2625
+ setLogoType() {
2626
+ switch (true) {
2627
+ case this.source instanceof AXPImageUrlLogoConfig:
2628
+ this.logoType = 'url';
2629
+ break;
2630
+ case this.source instanceof AXPComponentLogoConfig:
2631
+ this.logoType = 'component';
2632
+ break;
2633
+ case this.source instanceof AXPIconLogoConfig:
2634
+ this.logoType = 'icon';
2635
+ break;
2636
+ default:
2637
+ break;
2638
+ }
2639
+ }
2640
+ setLogoTheme() {
2641
+ if (this.source && this.source.dark && this.source.light) {
2642
+ this.source = this.platform.isDark() ? this.source.dark : this.source.light;
2643
+ }
2644
+ }
2645
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPLogoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2646
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", 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", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i2$6.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXDecoratorModule }] }); }
2647
+ }
2648
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPLogoComponent, decorators: [{
2649
+ type: Component,
2650
+ args: [{ selector: 'axp-logo', imports: [CommonModule, AXImageModule, AXDecoratorModule], host: { class: 'ax-flex ax-justify-center' }, 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" }]
2651
+ }], propDecorators: { source: [{
2652
+ type: Input
2653
+ }] } });
2654
+
2880
2655
  //#region ---- Imports ----
2881
2656
  //#endregion
2882
2657
  //#region ---- Service Interface ----
@@ -3440,1092 +3215,1048 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3440
3215
  ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'axp-menu-customizer' }, template: "<!-- i18n path constants -->\n@let i18nBase = '@platform-management:menu-management';\n@let i18nEmptyStates = i18nBase + '.components.menu-list.empty-states';\n@let i18nActions = i18nBase + '.actions';\n\n<!-- Loading State -->\n@if (isLoading()) {\n <axp-state-message\n mode=\"loading\"\n icon=\"fa-light fa-spinner-third fa-spin\"\n [title]=\"(i18nEmptyStates + '.loading.title' | translate | async)!\"\n [description]=\"(i18nEmptyStates + '.loading.description' | translate | async)!\"\n />\n}\n\n<!-- Error State -->\n@else if (hasError()) {\n <axp-state-message\n mode=\"error\"\n icon=\"fa-light fa-circle-exclamation\"\n [title]=\"(i18nEmptyStates + '.error.title' | translate | async)!\"\n [description]=\"(i18nEmptyStates + '.error.description' | translate | async)!\"\n >\n <ax-button\n slot=\"actions\"\n [text]=\"'@general:actions.retry.title' | translate | async\"\n look=\"outline\"\n color=\"primary\"\n (onClick)=\"loadMenuItems()\"\n >\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-button>\n </axp-state-message>\n}\n\n<!-- Empty State -->\n@else if (showEmptyState()) {\n <axp-state-message\n mode=\"empty\"\n icon=\"fa-light fa-bars\"\n [title]=\"(i18nEmptyStates + '.no-data.title' | translate | async)!\"\n [description]=\"(i18nEmptyStates + '.no-data.description' | translate | async)!\"\n >\n @if (allowAddItems()) {\n <ax-button\n slot=\"actions\"\n [text]=\"i18nActions + '.add-root.title' | translate | async\"\n look=\"solid\"\n color=\"primary\"\n (onClick)=\"addRootMenuItem()\"\n >\n <i class=\"fa-light fa-plus\"></i>\n </ax-button>\n }\n </axp-state-message>\n}\n\n<!-- Main Content -->\n@else if (showContent()) {\n <!-- Toolbar -->\n @if (showToolbar()) {\n <div class=\"axp-menu-customizer__toolbar\">\n @if (allowAddItems()) {\n <ax-button\n [text]=\"i18nActions + '.add-root.title' | translate | async\"\n look=\"solid\"\n color=\"primary\"\n (onClick)=\"addRootMenuItem()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button>\n }\n\n <div class=\"axp-menu-customizer__toolbar-spacer\"></div>\n <!-- \n <ax-button\n [text]=\"i18nActions + '.collapse.title' | translate | async\"\n look=\"outline\"\n (onClick)=\"collapseAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-minus-square\"></i>\n </ax-prefix>\n </ax-button>\n\n <ax-button\n [text]=\"i18nActions + '.expand.title' | translate | async\"\n look=\"outline\"\n (onClick)=\"expandAll()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus-square\"></i>\n </ax-prefix>\n </ax-button> -->\n\n <ax-button\n [text]=\"i18nActions + '.reset.title' | translate | async\"\n look=\"outline\"\n color=\"danger\"\n (onClick)=\"resetCustomizations()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-rotate-left\"></i>\n </ax-prefix>\n </ax-button>\n </div>\n }\n\n <!-- Menu Tree -->\n <div class=\"axp-menu-customizer__container\">\n <div class=\"axp-menu-customizer__tree\">\n <ax-tree-view\n [(datasource)]=\"treeNodes\"\n look=\"default\"\n [nodeTemplate]=\"itemTemplate\"\n selectMode=\"none\"\n [showIcons]=\"false\"\n [showChildrenBadge]=\"false\"\n [dragBehavior]=\"dragBehavior()\"\n [dragArea]=\"dragArea()\"\n (onBeforeDrop)=\"handleBeforeDrop($event)\"\n (onOrderChange)=\"handleOrderChange($event)\"\n (onMoveChange)=\"handleMoveChange($event)\"\n />\n </div>\n </div>\n}\n\n<!-- Custom Item Template -->\n<ng-template #itemTemplate let-node=\"node\" let-level=\"level\">\n @let nodeData = node['data'];\n @let item = nodeData.menuItem;\n @let metadata = nodeData.metadata;\n @let isGroup = item.type === 'group';\n @let isBreak = item.type === 'break';\n @let isMenu = item.type === 'menu' || !item.type;\n @let canAddChild = !item.path && isMenu && allowAddItems();\n @let canToggleVisibility = item.name && metadata.isBuiltIn;\n @let canDelete = item.name && metadata.isCustom;\n @let i18nStates = '@platform-management:menu-management.states';\n\n <div\n class=\"axp-menu-customizer__item\"\n [class.axp-menu-customizer__item--hidden]=\"metadata.isHidden\"\n [class.axp-menu-customizer__item--custom]=\"metadata.isCustom\"\n [class.axp-menu-customizer__item--group]=\"isGroup\"\n >\n <!-- Icon (hidden for groups) -->\n @if (item.icon && !isGroup) {\n <i class=\"axp-menu-customizer__item-icon\" [class]=\"item.icon\"></i>\n }\n\n <!-- Content based on item type -->\n @if (isGroup) {\n <span class=\"axp-menu-customizer__item-group\">\n <i class=\"fa-light fa-layer-group\"></i>\n {{ item.text | translate | async }}\n </span>\n } @else if (isBreak) {\n <span class=\"axp-menu-customizer__item-divider\">\n <i class=\"fa-light fa-minus\"></i>\n {{ i18nStates + '.divider' | translate | async }}\n </span>\n } @else if (item.text) {\n <span class=\"axp-menu-customizer__item-text\">\n {{ item.text | translate | async }}\n </span>\n } @else if (item.path) {\n <span class=\"axp-menu-customizer__item-path\">\n <i class=\"fa-light fa-link\"></i>\n {{ item.path }}\n </span>\n }\n\n <!-- Menu Name/Key -->\n @if (item.name) {\n <code class=\"axp-menu-customizer__item-name\">{{ item.name }}</code>\n }\n\n <!-- Status Badges -->\n @if (metadata.isHidden) {\n <ax-badge [text]=\"(i18nStates + '.hidden' | translate | async)!\" color=\"danger\" />\n } @else if (metadata.isCustom) {\n <ax-badge [text]=\"(i18nStates + '.custom' | translate | async)!\" color=\"success\" />\n } @else if (metadata.isBuiltIn) {\n <ax-badge [text]=\"(i18nStates + '.built-in' | translate | async)!\" color=\"primary\" />\n }\n\n <!-- Actions Dropdown -->\n <ax-button class=\"axp-menu-customizer__item-actions\" look=\"blank\" size=\"sm\" [iconOnly]=\"true\">\n <ax-prefix>\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </ax-prefix>\n\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n <!-- Edit (only for items with names) -->\n @if (item.name) {\n <ax-button-item\n [text]=\"('@general:actions.edit.title' | translate | async)!\"\n (onClick)=\"onAction('edit', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-edit\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Show/Hide (only for built-in items with names) -->\n @if (canToggleVisibility) {\n @if (metadata.isHidden) {\n <ax-button-item\n [text]=\"(i18nActions + '.show.title' | translate | async)!\"\n (onClick)=\"onAction('show', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye\"></i>\n </ax-prefix>\n </ax-button-item>\n } @else {\n <ax-button-item\n [text]=\"(i18nActions + '.hide.title' | translate | async)!\"\n (onClick)=\"onAction('hide', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-eye-slash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n }\n\n <!-- Add Child -->\n @if (canAddChild) {\n <ax-button-item\n [text]=\"(i18nActions + '.add-child.title' | translate | async)!\"\n (onClick)=\"onAction('add-child', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-plus\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Delete (only for custom items with names) -->\n @if (canDelete) {\n <ax-divider />\n <ax-button-item\n color=\"danger\"\n [text]=\"('@general:actions.delete.title' | translate | async)!\"\n (onClick)=\"onAction('delete', nodeData); panel.close()\"\n >\n <ax-prefix>\n <i class=\"fa-light fa-trash\"></i>\n </ax-prefix>\n </ax-button-item>\n }\n\n <!-- Info message for items without names -->\n @if (!item.name) {\n <ax-divider />\n <div class=\"ax-p-2 ax-text-xs ax-text-neutral-500 ax-italic\">\n {{ i18nBase + '.messages.info.no-name' | translate | async }}\n </div>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n </div>\n</ng-template>\n", styles: [".axp-menu-customizer{display:flex;height:100%;min-height:0px;flex-direction:column}.axp-menu-customizer__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;padding:1rem;border-bottom-width:1px;border-style:solid;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));flex-shrink:0}.axp-menu-customizer__toolbar-spacer{min-width:1rem;flex:1 1 0%}.axp-menu-customizer__container{display:flex;flex-direction:column;gap:1rem;padding:1rem;min-height:0px;flex:1 1 0%;overflow:auto}.axp-menu-customizer__tree{display:flex;flex-direction:column;gap:.5rem}.axp-menu-customizer__tree ax-tree-view{flex:1 1 0%}.axp-menu-customizer__item{display:flex;flex:1 1 0%;align-items:center;gap:.75rem;border-radius:.375rem;padding:.5rem .75rem;border-width:1px;border-style:solid;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;animation-duration:.15s}.axp-menu-customizer__item:hover{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.axp-menu-customizer__item--hidden{opacity:.5}.axp-menu-customizer__item--hidden .axp-menu-customizer__item-text,.axp-menu-customizer__item--hidden .axp-menu-customizer__item-icon{text-decoration-line:line-through}.axp-menu-customizer__item--custom{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-primary-200),var(--tw-border-opacity, 1));background-color:rgba(var(--ax-sys-color-primary-50),.3)}.axp-menu-customizer__item--group{padding-top:.75rem;padding-bottom:.75rem;--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.axp-menu-customizer__item-icon{display:flex;align-items:center;justify-content:center;height:1.5rem;width:1.5rem;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-primary-600),var(--tw-text-opacity, 1));flex-shrink:0}.axp-menu-customizer__item-text{min-width:0px;flex:1 1 0%;font-size:.875rem;line-height:1.25rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axp-menu-customizer__item-group{display:flex;flex:1 1 0%;align-items:center;gap:.5rem;font-size:.75rem;line-height:1rem;text-transform:uppercase;font-style:italic;--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity, 1));letter-spacing:.025em}.axp-menu-customizer__item-group i{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-divider,.axp-menu-customizer__item-path{display:flex;flex:1 1 0%;align-items:center;gap:.5rem;font-size:.75rem;line-height:1rem;font-style:italic;--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity, 1));min-width:0px}.axp-menu-customizer__item-divider i,.axp-menu-customizer__item-path i{flex-shrink:0;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-menu-customizer__item-name{display:inline-flex;align-items:center;border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1));font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-weight:400;border-width:1px;--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1));-webkit-user-select:all;user-select:all;max-width:12rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0}.axp-menu-customizer__item-actions{margin-left:auto;flex-shrink:0;opacity:.6;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;animation-duration:.15s}.axp-menu-customizer__item:hover .axp-menu-customizer__item-actions{opacity:1}\n"] }]
3441
3216
  }], propDecorators: { scopeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "scopeKey", required: true }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], allowAddItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowAddItems", required: false }] }], dragBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragBehavior", required: false }] }], dragArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragArea", required: false }] }], saved: [{ type: i0.Output, args: ["saved"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], tree: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXTreeViewComponent), { isSignal: true }] }] } });
3442
3217
 
3443
- //#region ---- Component Definition ----
3444
- class AXPSpreadsheetComponent {
3218
+ class AXPPropertyViewerComponent {
3445
3219
  //#endregion
3446
- //#region ---- Lifecycle Methods ----
3447
3220
  constructor() {
3448
- //#endregion
3449
- //#region ---- Services & Dependencies ----
3450
- this.translationService = inject(AXTranslationService);
3451
- //#endregion
3452
- //#region ---- Computed Properties for Translations ----
3453
- /**
3454
- * Translated column titles
3455
- */
3456
- this.translatedColumnTitles = signal(new Map(), ...(ngDevMode ? [{ debugName: "translatedColumnTitles" }] : []));
3457
- //#endregion
3458
3221
  //#region ---- Inputs ----
3459
3222
  /**
3460
- * Title for the spreadsheet (displayed in corner cell)
3461
- */
3462
- this.title = input('', ...(ngDevMode ? [{ debugName: "title" }] : []));
3463
- /**
3464
- * Column definitions
3465
- */
3466
- this.columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : []));
3467
- /**
3468
- * Row mode: 'fixed' or 'dynamic'
3469
- */
3470
- this.rowMode = input('fixed', ...(ngDevMode ? [{ debugName: "rowMode" }] : []));
3471
- /**
3472
- * Rows data (for fixed mode - input only)
3473
- * Each row must have a 'name' property for identification
3474
- */
3475
- this.rowsInput = input([], ...(ngDevMode ? [{ debugName: "rowsInput" }] : []));
3476
- /**
3477
- * Rows data (for dynamic mode - two-way binding)
3478
- */
3479
- this.rowsModel = model([], ...(ngDevMode ? [{ debugName: "rowsModel" }] : []));
3480
- /**
3481
- * Whether the spreadsheet is in readonly mode
3482
- */
3483
- this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
3484
- /**
3485
- * Placeholder text for empty cells (default: "–")
3486
- */
3487
- this.emptyCellPlaceholder = input('–', ...(ngDevMode ? [{ debugName: "emptyCellPlaceholder" }] : []));
3488
- /**
3489
- * Path to property in row object to display as row header title
3490
- * Example: 'name', 'title', 'user.name'
3491
- */
3492
- this.rowTitlePath = input(null, ...(ngDevMode ? [{ debugName: "rowTitlePath" }] : []));
3493
- /**
3494
- * Path to property in row object to display as row header description
3495
- * Example: 'description', 'details', 'user.description'
3496
- */
3497
- this.rowDescriptionPath = input(null, ...(ngDevMode ? [{ debugName: "rowDescriptionPath" }] : []));
3498
- /**
3499
- * Whether to allow adding rows (dynamic mode only, default: true)
3223
+ * List of tabs that should be rendered inside the property viewer.
3500
3224
  */
3501
- this.allowAddRows = input(true, ...(ngDevMode ? [{ debugName: "allowAddRows" }] : []));
3225
+ this.tabsInput = input([], ...(ngDevMode ? [{ debugName: "tabsInput" }] : []));
3502
3226
  /**
3503
- * Whether to allow removing rows (default: true for dynamic mode, false for fixed mode)
3504
- * Can be enabled for fixed mode if needed
3227
+ * Determines rendering mode.
3505
3228
  */
3506
- this.allowRemoveRows = input(true, ...(ngDevMode ? [{ debugName: "allowRemoveRows" }] : []));
3229
+ this.mode = input('simple', ...(ngDevMode ? [{ debugName: "mode" }] : []));
3507
3230
  //#endregion
3508
3231
  //#region ---- Outputs ----
3509
3232
  /**
3510
- * Emitted when a single cell value changes
3511
- */
3512
- this.cellChange = output();
3513
- /**
3514
- * Emitted when rows are added or removed (dynamic mode only)
3515
- */
3516
- this.rowChange = output();
3517
- /**
3518
- * Emitted when the entire spreadsheet data changes
3233
+ * Emits when property context changes.
3519
3234
  */
3520
- this.spreadsheetChange = output();
3235
+ this.onChanged = new EventEmitter();
3521
3236
  //#endregion
3522
- //#region ---- Class Properties ----
3523
- /**
3524
- * Currently editing cell: { rowId, columnName } | null
3525
- */
3526
- this.editingCell = signal(null, ...(ngDevMode ? [{ debugName: "editingCell" }] : []));
3527
- /**
3528
- * Original value when entering edit mode (for cancel functionality)
3529
- */
3530
- this.originalCellValue = signal(null, ...(ngDevMode ? [{ debugName: "originalCellValue" }] : []));
3531
- /**
3532
- * Internal rows state (for fixed mode)
3533
- */
3534
- this.internalRows = signal([], ...(ngDevMode ? [{ debugName: "internalRows" }] : []));
3237
+ //#region ---- Signals ----
3238
+ this.currentTabIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentTabIndex" }] : []));
3239
+ this.tabs = computed(() => {
3240
+ const incomingTabs = this.tabsInput() ?? [];
3241
+ return incomingTabs
3242
+ .map((tab) => ({
3243
+ ...tab,
3244
+ groups: (tab.groups ?? [])
3245
+ .map((group) => ({
3246
+ ...group,
3247
+ isCollapsed: group.isCollapsed ?? false,
3248
+ props: (group.props ?? []).filter((prop) => prop.visible !== false),
3249
+ }))
3250
+ .filter((group) => group.props.length > 0),
3251
+ }))
3252
+ .filter((tab) => tab.groups.length > 0);
3253
+ }, ...(ngDevMode ? [{ debugName: "tabs" }] : []));
3254
+ this.groups = computed(() => {
3255
+ const preparedTabs = this.tabs();
3256
+ if (!preparedTabs.length) {
3257
+ return [];
3258
+ }
3259
+ const index = Math.min(this.currentTabIndex(), preparedTabs.length - 1);
3260
+ if (index !== this.currentTabIndex()) {
3261
+ this.currentTabIndex.set(index);
3262
+ }
3263
+ return preparedTabs[index].groups.map((group) => ({
3264
+ ...group,
3265
+ isCollapsed: this.groupCollapsedStates.get(group.name) ?? !!group.isCollapsed,
3266
+ }));
3267
+ }, ...(ngDevMode ? [{ debugName: "groups" }] : []));
3268
+ this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
3535
3269
  /**
3536
- * Row ID mapping: rowId -> row index (for tracking)
3270
+ * Per-expression-path override for "use expression" vs "use value" when binding.enabled.
3271
+ * Updated when user toggles the ax-switch; otherwise derived from isExpression(valueAtPath).
3537
3272
  */
3538
- this.rowIdMap = new Map();
3539
- /**
3540
- * Cache for widget nodes to prevent re-rendering
3541
- * Key: `${rowIndex}_${columnName}`, Value: AXPWidgetNode
3542
- */
3543
- this.widgetNodeCache = new Map();
3544
- /**
3545
- * Cache for cell values to detect actual changes
3546
- * Key: `${rowIndex}_${columnName}`, Value: any
3547
- */
3548
- this.cellValueCache = new Map();
3273
+ this.expressionModeState = signal({}, ...(ngDevMode ? [{ debugName: "expressionModeState" }] : []));
3549
3274
  //#endregion
3550
- //#region ---- Computed Properties ----
3551
- /**
3552
- * Get current rows based on mode
3553
- */
3554
- this.currentRows = computed(() => {
3555
- if (this.rowMode() === 'dynamic') {
3556
- return this.rowsModel();
3557
- }
3558
- else {
3559
- return this.internalRows();
3560
- }
3561
- }, ...(ngDevMode ? [{ debugName: "currentRows" }] : []));
3562
- /**
3563
- * Context for widgets-container (all rows as an object with rows array)
3564
- * This allows widgets to access their cell via path like: rows[index].column.path
3565
- */
3566
- this.spreadsheetContext = computed(() => {
3567
- return {
3568
- rows: this.currentRows(),
3569
- };
3570
- }, ...(ngDevMode ? [{ debugName: "spreadsheetContext" }] : []));
3571
- /**
3572
- * Get rows with IDs (ensure all rows have identifiers)
3573
- */
3574
- this.rowsWithIds = computed(() => {
3575
- const rows = this.currentRows();
3576
- const mode = this.rowMode();
3577
- const result = [];
3578
- rows.forEach((row, index) => {
3579
- let rowId;
3580
- if (mode === 'fixed') {
3581
- // Fixed mode: use 'name' property
3582
- rowId = row['name'] || `row_${index}`;
3583
- }
3584
- else {
3585
- // Dynamic mode: generate ID if not exists
3586
- if (!row['_id']) {
3587
- row['_id'] = this.generateRowId();
3588
- }
3589
- rowId = row['_id'];
3275
+ //#region ---- Properties ----
3276
+ this.groupCollapsedStates = new Map();
3277
+ this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
3278
+ this.popupService = inject(AXPopupService);
3279
+ effect(() => {
3280
+ const availableTabs = this.tabs();
3281
+ const availableGroups = availableTabs.flatMap((tab) => tab.groups);
3282
+ availableGroups.forEach((group) => {
3283
+ if (!this.groupCollapsedStates.has(group.name) && group.isCollapsed !== undefined) {
3284
+ this.groupCollapsedStates.set(group.name, !!group.isCollapsed);
3590
3285
  }
3591
- // Update row ID map
3592
- this.rowIdMap.set(rowId, index);
3593
- result.push({ rowId, row });
3594
3286
  });
3595
- return result;
3596
- }, ...(ngDevMode ? [{ debugName: "rowsWithIds" }] : []));
3597
- /**
3598
- * Check if spreadsheet is completely empty (both columns and rows)
3599
- */
3600
- this.isEmpty = computed(() => {
3601
- const columns = this.columns();
3602
- const rows = this.currentRows();
3603
- return (!columns || columns.length === 0) && (!rows || rows.length === 0);
3604
- }, ...(ngDevMode ? [{ debugName: "isEmpty" }] : []));
3605
- /**
3606
- * Check if columns are empty (regardless of rows)
3607
- * When columns are empty, we should show a message about missing columns, not rows
3608
- */
3609
- this.isColumnsEmpty = computed(() => {
3610
- const columns = this.columns();
3611
- return !columns || columns.length === 0;
3612
- }, ...(ngDevMode ? [{ debugName: "isColumnsEmpty" }] : []));
3613
- /**
3614
- * Check if only rows are empty (columns exist but no rows)
3615
- */
3616
- this.isRowsEmpty = computed(() => {
3617
- const columns = this.columns();
3618
- const rows = this.currentRows();
3619
- return (columns && columns.length > 0) && (!rows || rows.length === 0);
3620
- }, ...(ngDevMode ? [{ debugName: "isRowsEmpty" }] : []));
3621
- // Initialize rows from input (fixed mode)
3622
- this.internalRows.set(this.rowsInput());
3623
- // Watch for rowsInput changes and update internalRows (fixed mode only)
3624
- effect(() => {
3625
- const rowsInput = this.rowsInput();
3626
- const rowMode = this.rowMode();
3627
- // Only update internalRows in fixed mode
3628
- if (rowMode === 'fixed') {
3629
- this.internalRows.set(rowsInput);
3287
+ if (!availableTabs.length) {
3288
+ this.currentTabIndex.set(0);
3289
+ }
3290
+ else if (this.currentTabIndex() >= availableTabs.length) {
3291
+ this.currentTabIndex.set(0);
3630
3292
  }
3631
3293
  });
3632
3294
  }
3633
- //#endregion
3634
- //#region ---- Cell Value Methods ----
3295
+ //#region ---- Public Methods ----
3635
3296
  /**
3636
- * Get the value for a specific cell by row index
3297
+ * Replaces the current context with the provided value and emits an init event.
3637
3298
  */
3638
- getCellValue(rowIndex, columnName) {
3639
- const rows = this.currentRows();
3640
- const row = rows[rowIndex];
3641
- if (!row) {
3642
- return null;
3643
- }
3644
- const column = this.columns().find((col) => col.name === columnName);
3645
- if (!column) {
3646
- return null;
3647
- }
3648
- return get(row, column.path, null);
3299
+ initializeContext(value) {
3300
+ untracked(() => {
3301
+ this.context.set(cloneDeep(value ?? {}));
3302
+ this.onChanged.emit({ values: this.context(), mode: 'init' });
3303
+ });
3649
3304
  }
3650
3305
  /**
3651
- * Get row title from row object using rowTitlePath
3306
+ * Merges the provided value into the current context and emits an update event.
3652
3307
  */
3653
- getRowTitle(row, path) {
3654
- if (!row || !path) {
3655
- return '';
3656
- }
3657
- return get(row, path, '') || '';
3308
+ update(value) {
3309
+ untracked(() => {
3310
+ this.context.set(merge(cloneDeep(this.context()), value ?? {}));
3311
+ this.onChanged.emit({ values: this.context(), mode: 'update' });
3312
+ });
3658
3313
  }
3314
+ //#endregion
3315
+ //#region ---- Event Handlers ----
3659
3316
  /**
3660
- * Get row description from row object using rowDescriptionPath
3317
+ * Handles context changes produced by rendered widgets.
3661
3318
  */
3662
- getRowDescription(row, path) {
3663
- if (!row || !path) {
3664
- return '';
3665
- }
3666
- return get(row, path, '') || '';
3319
+ handleContextChange(event) {
3320
+ untracked(() => {
3321
+ this.context.set(event.data);
3322
+ this.onChanged.emit({ values: this.context(), mode: event.state === 'initiated' ? 'init' : 'update' });
3323
+ });
3667
3324
  }
3668
3325
  /**
3669
- * Get row by ID
3326
+ * Handles tab selection changes.
3670
3327
  */
3671
- getRowById(rowId) {
3672
- const rowsWithIds = this.rowsWithIds();
3673
- const found = rowsWithIds.find((item) => item.rowId === rowId);
3674
- return found ? found.row : null;
3328
+ handleTabChange(event) {
3329
+ const index = event.index;
3330
+ if (this.currentTabIndex() !== index) {
3331
+ this.currentTabIndex.set(index);
3332
+ }
3675
3333
  }
3676
3334
  /**
3677
- * Check if a cell is currently being edited
3335
+ * Stores collapsed state for a group.
3678
3336
  */
3679
- isCellEditing(rowId, columnName) {
3680
- const editing = this.editingCell();
3681
- return editing?.rowId === rowId && editing?.columnName === columnName;
3337
+ handleCollapsedChange(groupName, isCollapsed) {
3338
+ this.groupCollapsedStates.set(groupName, isCollapsed);
3682
3339
  }
3683
3340
  /**
3684
- * Check if a column is readonly
3341
+ * Returns the config path used for the binding value (and expression) for a property with binding.enabled.
3685
3342
  */
3686
- isColumnReadonly(column) {
3687
- return this.readonly() || column.readonly === true;
3343
+ getExpressionPath(prop) {
3344
+ const basePath = prop.schema?.interface?.path ?? prop.name;
3345
+ return prop.binding?.optionKey ? `${basePath}.${prop.binding.optionKey}` : basePath;
3688
3346
  }
3689
3347
  /**
3690
- * Get widget node for a cell with dynamic path based on row index
3691
- * Path format: rows[rowIndex].column.path
3692
- * Uses caching to prevent unnecessary re-renders
3348
+ * True when the property is in "expression" mode: show only the expression editor button, not the widget.
3349
+ * Uses expressionModeState when set; otherwise derives from isExpression(valueAtPath).
3693
3350
  */
3694
- getCellWidgetNode(column, rowIndex) {
3695
- const cacheKey = `${rowIndex}_${column.name}`;
3696
- const rows = this.currentRows();
3697
- const row = rows[rowIndex];
3698
- const currentValue = row ? get(row, column.path, null) : null;
3699
- const cachedValue = this.cellValueCache.get(cacheKey);
3700
- // Get or create widget node
3701
- let widgetNode = this.widgetNodeCache.get(cacheKey);
3702
- if (!widgetNode) {
3703
- // Create path: rows[index].column.path
3704
- const dynamicPath = `rows.${rowIndex}.${column.path}`;
3705
- // Create new widget node
3706
- widgetNode = {
3707
- ...column.widget,
3708
- path: dynamicPath,
3709
- defaultValue: currentValue !== null && currentValue !== undefined ? currentValue : column.widget.defaultValue,
3710
- };
3711
- // Cache the widget node
3712
- this.widgetNodeCache.set(cacheKey, widgetNode);
3713
- this.cellValueCache.set(cacheKey, currentValue);
3714
- }
3715
- else {
3716
- // Update path in case row index changed (shouldn't happen, but be safe)
3717
- widgetNode.path = `rows.${rowIndex}.${column.path}`;
3718
- // Only update defaultValue if value actually changed (to prevent re-renders)
3719
- if (cachedValue !== currentValue) {
3720
- widgetNode.defaultValue = currentValue !== null && currentValue !== undefined ? currentValue : column.widget.defaultValue;
3721
- this.cellValueCache.set(cacheKey, currentValue);
3722
- }
3351
+ isExpressionMode(prop) {
3352
+ this.expressionModeState(); // dependency for change detection
3353
+ const path = this.getExpressionPath(prop);
3354
+ const stored = this.expressionModeState()[path];
3355
+ if (stored !== undefined) {
3356
+ return stored;
3723
3357
  }
3724
- return widgetNode;
3358
+ const v = get(this.context(), path);
3359
+ return this.expressionEvaluator.isExpression(typeof v === 'string' ? v : '');
3725
3360
  }
3726
- //#endregion
3727
- //#region ---- Event Handlers ----
3728
3361
  /**
3729
- * Handle cell click to start editing
3362
+ * Returns the default value when switching from expression to value mode.
3363
+ * Source of truth is prop.schema.defaultValue (widget.types): plain value, or function (context) => value.
3364
+ * A minimal dataType fallback is used only when the schema provides no default.
3730
3365
  */
3731
- handleCellClick(rowId, columnName, event) {
3732
- if (this.readonly()) {
3733
- return;
3366
+ getDefaultValueForProperty(prop) {
3367
+ const def = prop.schema?.defaultValue;
3368
+ if (typeof def === 'function') {
3369
+ return def(this.context());
3734
3370
  }
3735
- // Don't open editor if clicking on toolbar or buttons
3736
- if (event) {
3737
- const target = event.target;
3738
- if (target.closest('.__cell-toolbar') || target.closest('ax-button')) {
3739
- return;
3740
- }
3371
+ if (def !== undefined && def !== null) {
3372
+ return def;
3741
3373
  }
3742
- const column = this.columns().find((col) => col.name === columnName);
3743
- if (!column || this.isColumnReadonly(column)) {
3744
- return;
3374
+ const dataType = prop.schema?.dataType ?? 'string';
3375
+ switch (dataType) {
3376
+ case 'boolean':
3377
+ return false;
3378
+ case 'number':
3379
+ return 0;
3380
+ case 'object':
3381
+ return {};
3382
+ case 'array':
3383
+ return [];
3384
+ default:
3385
+ return '';
3745
3386
  }
3746
- // Don't open if already editing this cell
3747
- const currentEditing = this.editingCell();
3748
- if (currentEditing?.rowId === rowId && currentEditing?.columnName === columnName) {
3749
- return;
3750
- }
3751
- // Store original value for cancel functionality
3752
- const row = this.getRowById(rowId);
3753
- if (row) {
3754
- const originalValue = get(row, column.path, null);
3755
- this.originalCellValue.set(originalValue);
3756
- }
3757
- this.editingCell.set({ rowId, columnName });
3758
3387
  }
3759
3388
  /**
3760
- * Handle cell value change from widget
3761
- * The widget sets the value at path like rows[index].column.path
3389
+ * Updates the expression/value toggle state for a binding-enabled property.
3390
+ * When switching to value: resets the value at path to a type-appropriate default so the
3391
+ * widget renderer does not receive the expression string (e.g. checkbox gets false, not a string).
3392
+ * When switching to expression: sets the value at path to '{{ }}' so the popup opens with
3393
+ * a clean expression placeholder instead of the previous literal (e.g. true/false).
3762
3394
  */
3763
- handleCellValueChange(rowIndex, columnName, event) {
3764
- // Only process if context has changed (not just initiated)
3765
- if (event.state !== 'changed') {
3766
- return;
3767
- }
3768
- const rows = this.currentRows();
3769
- const row = rows[rowIndex];
3770
- if (!row) {
3771
- return;
3395
+ setExpressionMode(prop, useExpression) {
3396
+ const path = this.getExpressionPath(prop);
3397
+ const currentVal = get(this.context(), path);
3398
+ const currentIsExpression = this.expressionEvaluator.isExpression(typeof currentVal === 'string' ? currentVal : '');
3399
+ this.expressionModeState.update((s) => ({ ...s, [path]: useExpression }));
3400
+ if (!useExpression && currentIsExpression) {
3401
+ const next = cloneDeep(this.context());
3402
+ set(next, path, this.getDefaultValueForProperty(prop));
3403
+ untracked(() => {
3404
+ this.context.set(next);
3405
+ this.onChanged.emit({ values: this.context(), mode: 'update' });
3406
+ });
3772
3407
  }
3773
- const column = this.columns().find((col) => col.name === columnName);
3774
- if (!column) {
3775
- return;
3408
+ else if (useExpression && !currentIsExpression) {
3409
+ const next = cloneDeep(this.context());
3410
+ set(next, path, '{{ }}');
3411
+ untracked(() => {
3412
+ this.context.set(next);
3413
+ this.onChanged.emit({ values: this.context(), mode: 'update' });
3414
+ });
3776
3415
  }
3777
- // Get the new value from the context (widget sets it at rows[index].column.path)
3778
- const dynamicPath = `rows.${rowIndex}.${column.path}`;
3779
- const newValue = get(event.data, dynamicPath);
3780
- // Update the row object directly
3781
- set(row, column.path, newValue);
3782
- // Update rows array
3783
- this.updateRows();
3784
- // Get rowId for events
3785
- const rowId = this.rowMode() === 'fixed' ? row['name'] : row['_id'];
3786
- // Emit events
3787
- this.cellChange.emit({
3788
- rowId: rowId || `row_${rowIndex}`,
3789
- columnName,
3790
- value: newValue,
3791
- row: { ...row },
3792
- });
3793
- this.spreadsheetChange.emit([...this.currentRows()]);
3794
- // Don't close edit mode automatically - let user finish editing
3795
- // Edit mode will close on blur or click outside
3796
3416
  }
3797
3417
  /**
3798
- * Handle spreadsheet context change from widgets-container
3799
- * This is called when any widget in the spreadsheet changes
3418
+ * Opens the binding/expression code editor for a property with binding.enabled.
3419
+ * Ensures the value passed to the popup is always a string to avoid code-editor crashes.
3800
3420
  */
3801
- handleSpreadsheetContextChange(event) {
3802
- if (event.state !== 'changed') {
3803
- return;
3804
- }
3805
- // Extract row index and column from the path (format: rows[index].column.path)
3806
- const path = event.path || '';
3807
- const pathMatch = path.match(/^rows\.(\d+)\.(.+)$/);
3808
- if (!pathMatch) {
3809
- return;
3810
- }
3811
- const rowIndex = parseInt(pathMatch[1], 10);
3812
- const columnPath = pathMatch[2];
3813
- // Find the column by path
3814
- const column = this.columns().find((col) => col.path === columnPath);
3815
- if (!column) {
3816
- return;
3817
- }
3818
- // Get the new value from event.data
3819
- const newValue = get(event.data, path);
3820
- // Update the row object (sync with context)
3821
- const rows = this.currentRows();
3822
- const row = rows[rowIndex];
3823
- if (row) {
3824
- // The context should already have the updated value, but sync row object to be sure
3825
- set(row, columnPath, newValue);
3826
- // Update cache (widget node will update defaultValue on next getCellWidgetNode call)
3827
- const cacheKey = `${rowIndex}_${column.name}`;
3828
- this.cellValueCache.set(cacheKey, newValue);
3829
- // Don't delete widget node cache - just update defaultValue on next access
3830
- // This prevents re-rendering while still reflecting the new value
3831
- this.updateRows();
3832
- // Get rowId for events
3833
- const rowId = this.rowMode() === 'fixed' ? row['name'] : row['_id'];
3834
- // Emit events
3835
- this.cellChange.emit({
3836
- rowId: rowId || `row_${rowIndex}`,
3837
- columnName: column.name,
3838
- value: newValue,
3839
- row: { ...row },
3421
+ async openBindingEditor(prop) {
3422
+ const expressionPath = this.getExpressionPath(prop);
3423
+ const raw = get(this.context(), expressionPath);
3424
+ const currentValue = typeof raw === 'string' ? raw : '';
3425
+ const { AXPBindingExpressionEditorPopupComponent } = await import('./acorex-platform-layout-components-binding-expression-editor-popup.component-Cb6Lk4Ch.mjs');
3426
+ const result = await this.popupService.open(AXPBindingExpressionEditorPopupComponent, {
3427
+ title: '@general:widgets.triggers.edit-binding-expression',
3428
+ size: 'lg',
3429
+ data: { initialValue: signal(currentValue) },
3430
+ });
3431
+ if (result?.data?.value !== undefined) {
3432
+ const next = cloneDeep(this.context());
3433
+ set(next, expressionPath, result.data.value);
3434
+ untracked(() => {
3435
+ this.context.set(next);
3436
+ this.onChanged.emit({ values: this.context(), mode: 'update' });
3840
3437
  });
3841
- this.spreadsheetChange.emit([...this.currentRows()]);
3842
3438
  }
3843
3439
  }
3844
- /**
3845
- * Handle blur event to stop editing (when user clicks away from cell)
3846
- */
3847
- handleCellBlur(rowId, columnName) {
3848
- // Small delay to allow click events to process first
3849
- setTimeout(() => {
3850
- const editing = this.editingCell();
3851
- if (editing?.rowId === rowId && editing?.columnName === columnName) {
3852
- // Don't close if clicking on the toolbar or buttons
3853
- const activeElement = document.activeElement;
3854
- const toolbarElement = activeElement?.closest('.__cell-toolbar');
3855
- const buttonElement = activeElement?.closest('ax-button');
3856
- // Only auto-commit if not clicking on toolbar or buttons
3857
- if (!toolbarElement && !buttonElement) {
3858
- this.commitCellEdit();
3859
- }
3440
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3441
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPPropertyViewerComponent, isStandalone: true, selector: "axp-property-viewer", inputs: { tabsInput: { classPropertyName: "tabsInput", publicName: "tabsInput", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onChanged: "onChanged" }, ngImport: i0, template: "<axp-widgets-container [context]=\"context()\" (onContextChanged)=\"handleContextChange($event)\">\n <!-- #region Shared property row template (used in both advanced and simple modes) -->\n <ng-template #propertyRow let-prop=\"prop\" let-translateTitle=\"translateTitle\">\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-py-2\">\n <!-- Property header: title + value/expression toggle (when binding.enabled) -->\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-w-full\">\n @if (prop.showLabel !== false) {\n @if (translateTitle) {\n <span class=\"ax-font-semibold ax-flex-1 ax-min-w-0\">{{ prop.title | translate | async }}</span>\n } @else {\n <span class=\"ax-font-semibold ax-flex-1 ax-min-w-0\">{{ prop.title }}</span>\n }\n }\n @if (prop.binding?.enabled) {\n <div class=\"ax-flex ax-gap-1 ax-shrink-0 ax-xs\">\n <ax-button [toggleable]=\"true\" [selected]=\"isExpressionMode(prop)\" look=\"twotone\"\n [color]=\"isExpressionMode(prop) ? 'primary' : 'default'\" class=\"ax-shrink-0\"\n [title]=\"(isExpressionMode(prop) ? '@general:terms.common.expression' : '@general:terms.common.value') | translate | async\"\n (selectedChange)=\"setExpressionMode(prop, $event)\">\n @if (isExpressionMode(prop)) {\n <ax-icon icon=\"fa-light fa-code\"></ax-icon>\n } @else {\n <ax-icon icon=\"fa-light fa-keyboard\"></ax-icon>\n }\n </ax-button>\n </div>\n }\n </div>\n <!-- Property content: Open Editor button (expression mode) or widget renderer (value mode) -->\n @if (prop.binding?.enabled && isExpressionMode(prop)) {\n <ax-button look=\"twotone\" color=\"primary\" size=\"sm\" class=\"ax-w-full\"\n [text]=\"'@general:widgets.triggers.open-editor' | translate | async\" (onClick)=\"openBindingEditor(prop)\"\n [title]=\"'@general:widgets.triggers.edit-binding-expression' | translate | async\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-code\"></ax-icon>\n </ax-prefix>\n </ax-button>\n } @else if (prop.schema.interface) {\n <ng-container axp-widget-renderer [node]=\"prop.schema.interface\" [mode]=\"'edit'\">\n </ng-container>\n }\n </div>\n </ng-template>\n <!-- #endregion -->\n\n <!-- #region Advanced mode: tabs + accordion groups -->\n @if (mode() === 'advanced') {\n <div class=\"ax-flex ax-flex-col\">\n @if (tabs().length > 1) {\n <div class=\"ax-pb-2\">\n <ax-tabs look=\"default\" (onActiveTabChanged)=\"handleTabChange($event)\" [look]=\"'with-line'\">\n @for (tab of tabs(); track $index) {\n <ax-tab-item [text]=\"tab.title\" [key]=\"$index.toString()\" [active]=\"currentTabIndex() === $index\">\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n }\n <div class=\"ax-flex-1 ax-overflow-auto\">\n <ax-accordion-group [accordion]=\"false\" [look]=\"'flat'\">\n @for (group of groups(); track $index) {\n <ax-accordion-item class=\"!ax-mb-0\" [caption]=\"group.title\" [isCollapsed]=\"group.isCollapsed\"\n (isCollapsedChange)=\"handleCollapsedChange(group.name, $event)\">\n <div class=\"ax-flex ax-flex-col\">\n @for (p of group.props; track $index) {\n <ng-container *ngTemplateOutlet=\"propertyRow; context: { prop: p, translateTitle: false }\">\n </ng-container>\n }\n </div>\n </ax-accordion-item>\n }\n </ax-accordion-group>\n </div>\n </div>\n }\n <!-- #endregion -->\n\n <!-- #region Simple mode: flat list of properties with padding -->\n @else {\n <div class=\"ax-flex ax-flex-col ax-p-4 ax-gap-4\">\n @for (group of groups(); track $index) {\n @for (p of group.props; track $index) {\n <ng-container *ngTemplateOutlet=\"propertyRow; context: { prop: p, translateTitle: true }\">\n </ng-container>\n }\n }\n </div>\n }\n <!-- #endregion -->\n</axp-widgets-container>", styles: ["axp-property-viewer ax-accordion-item .ax-accordion-header{font-weight:600!important;border-bottom-width:1px!important;background-color:rgb(var(--ax-sys-color-lighter-surface))!important;color:rgb(var(--ax-sys-color-on-lighter-surface))!important;border-color:rgb(var(--ax-sys-color-border-lighter-surface))!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: AXAccordionModule }, { kind: "component", type: i2$7.AXAccordionItemComponent, selector: "ax-accordion-item", inputs: ["isCollapsed", "icon", "caption", "isLoading", "headerTemplate", "look", "disabled"], outputs: ["isCollapsedChange", "isLoadingChange", "onClick"] }, { kind: "component", type: i2$7.AXAccordionGroupComponent, selector: "ax-accordion-group", inputs: ["accordion", "activeIndex", "look", "collapsedOnItemClick"], outputs: ["activeIndexChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXButtonGroupModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", 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: AXTabsModule }, { kind: "component", type: i5$3.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i5$3.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i6$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i6$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3442
+ }
3443
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerComponent, decorators: [{
3444
+ type: Component,
3445
+ args: [{ selector: 'axp-property-viewer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
3446
+ CommonModule,
3447
+ AXAccordionModule,
3448
+ AXButtonModule,
3449
+ AXButtonGroupModule,
3450
+ AXDecoratorModule,
3451
+ AXTabsModule,
3452
+ AXPWidgetCoreModule,
3453
+ AXTranslationModule,
3454
+ ], template: "<axp-widgets-container [context]=\"context()\" (onContextChanged)=\"handleContextChange($event)\">\n <!-- #region Shared property row template (used in both advanced and simple modes) -->\n <ng-template #propertyRow let-prop=\"prop\" let-translateTitle=\"translateTitle\">\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-py-2\">\n <!-- Property header: title + value/expression toggle (when binding.enabled) -->\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-w-full\">\n @if (prop.showLabel !== false) {\n @if (translateTitle) {\n <span class=\"ax-font-semibold ax-flex-1 ax-min-w-0\">{{ prop.title | translate | async }}</span>\n } @else {\n <span class=\"ax-font-semibold ax-flex-1 ax-min-w-0\">{{ prop.title }}</span>\n }\n }\n @if (prop.binding?.enabled) {\n <div class=\"ax-flex ax-gap-1 ax-shrink-0 ax-xs\">\n <ax-button [toggleable]=\"true\" [selected]=\"isExpressionMode(prop)\" look=\"twotone\"\n [color]=\"isExpressionMode(prop) ? 'primary' : 'default'\" class=\"ax-shrink-0\"\n [title]=\"(isExpressionMode(prop) ? '@general:terms.common.expression' : '@general:terms.common.value') | translate | async\"\n (selectedChange)=\"setExpressionMode(prop, $event)\">\n @if (isExpressionMode(prop)) {\n <ax-icon icon=\"fa-light fa-code\"></ax-icon>\n } @else {\n <ax-icon icon=\"fa-light fa-keyboard\"></ax-icon>\n }\n </ax-button>\n </div>\n }\n </div>\n <!-- Property content: Open Editor button (expression mode) or widget renderer (value mode) -->\n @if (prop.binding?.enabled && isExpressionMode(prop)) {\n <ax-button look=\"twotone\" color=\"primary\" size=\"sm\" class=\"ax-w-full\"\n [text]=\"'@general:widgets.triggers.open-editor' | translate | async\" (onClick)=\"openBindingEditor(prop)\"\n [title]=\"'@general:widgets.triggers.edit-binding-expression' | translate | async\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-code\"></ax-icon>\n </ax-prefix>\n </ax-button>\n } @else if (prop.schema.interface) {\n <ng-container axp-widget-renderer [node]=\"prop.schema.interface\" [mode]=\"'edit'\">\n </ng-container>\n }\n </div>\n </ng-template>\n <!-- #endregion -->\n\n <!-- #region Advanced mode: tabs + accordion groups -->\n @if (mode() === 'advanced') {\n <div class=\"ax-flex ax-flex-col\">\n @if (tabs().length > 1) {\n <div class=\"ax-pb-2\">\n <ax-tabs look=\"default\" (onActiveTabChanged)=\"handleTabChange($event)\" [look]=\"'with-line'\">\n @for (tab of tabs(); track $index) {\n <ax-tab-item [text]=\"tab.title\" [key]=\"$index.toString()\" [active]=\"currentTabIndex() === $index\">\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n }\n <div class=\"ax-flex-1 ax-overflow-auto\">\n <ax-accordion-group [accordion]=\"false\" [look]=\"'flat'\">\n @for (group of groups(); track $index) {\n <ax-accordion-item class=\"!ax-mb-0\" [caption]=\"group.title\" [isCollapsed]=\"group.isCollapsed\"\n (isCollapsedChange)=\"handleCollapsedChange(group.name, $event)\">\n <div class=\"ax-flex ax-flex-col\">\n @for (p of group.props; track $index) {\n <ng-container *ngTemplateOutlet=\"propertyRow; context: { prop: p, translateTitle: false }\">\n </ng-container>\n }\n </div>\n </ax-accordion-item>\n }\n </ax-accordion-group>\n </div>\n </div>\n }\n <!-- #endregion -->\n\n <!-- #region Simple mode: flat list of properties with padding -->\n @else {\n <div class=\"ax-flex ax-flex-col ax-p-4 ax-gap-4\">\n @for (group of groups(); track $index) {\n @for (p of group.props; track $index) {\n <ng-container *ngTemplateOutlet=\"propertyRow; context: { prop: p, translateTitle: true }\">\n </ng-container>\n }\n }\n </div>\n }\n <!-- #endregion -->\n</axp-widgets-container>", styles: ["axp-property-viewer ax-accordion-item .ax-accordion-header{font-weight:600!important;border-bottom-width:1px!important;background-color:rgb(var(--ax-sys-color-lighter-surface))!important;color:rgb(var(--ax-sys-color-on-lighter-surface))!important;border-color:rgb(var(--ax-sys-color-border-lighter-surface))!important}\n"] }]
3455
+ }], ctorParameters: () => [], propDecorators: { tabsInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabsInput", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], onChanged: [{
3456
+ type: Output
3457
+ }] } });
3458
+
3459
+ class AXPPropertyViewerPopupComponent extends AXBasePageComponent {
3460
+ //#endregion
3461
+ constructor() {
3462
+ super();
3463
+ //#region ---- Inputs ----
3464
+ this.tabs = input.required(...(ngDevMode ? [{ debugName: "tabs" }] : []));
3465
+ this.mode = input('simple', ...(ngDevMode ? [{ debugName: "mode" }] : []));
3466
+ this.context = input({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
3467
+ //#endregion
3468
+ //#region ---- Properties ----
3469
+ this.propertyViewer = viewChild(AXPPropertyViewerComponent, ...(ngDevMode ? [{ debugName: "propertyViewer" }] : []));
3470
+ this.currentValues = signal({}, ...(ngDevMode ? [{ debugName: "currentValues" }] : []));
3471
+ this.currentMode = 'init';
3472
+ effect(() => {
3473
+ const viewer = this.propertyViewer();
3474
+ if (!viewer) {
3475
+ return;
3860
3476
  }
3861
- }, 150);
3477
+ viewer.initializeContext(this.context() ?? {});
3478
+ });
3862
3479
  }
3863
- /**
3864
- * Handle Escape key to cancel editing
3865
- */
3866
- handleKeyDown(event) {
3867
- if (event.key === 'Escape' && this.editingCell()) {
3868
- event.preventDefault();
3869
- event.stopPropagation();
3870
- this.cancelCellEdit();
3871
- }
3480
+ //#region ---- Event Handlers ----
3481
+ handlePropertyChanged(event) {
3482
+ this.currentValues.set(event.values ?? {});
3483
+ this.currentMode = event.mode;
3872
3484
  }
3873
- /**
3874
- * Commit cell edit (save changes and close editing mode)
3875
- */
3876
- commitCellEdit() {
3877
- const editing = this.editingCell();
3878
- if (!editing) {
3879
- return;
3880
- }
3881
- // Changes are already saved via handleSpreadsheetContextChange
3882
- // Close the editor (same as escape, but keeps changes)
3883
- this.editingCell.set(null);
3884
- this.originalCellValue.set(null);
3485
+ handleCloseClick() {
3486
+ this.close();
3885
3487
  }
3886
- /**
3887
- * Cancel cell edit (discard changes and close editing mode - same as escape)
3888
- */
3889
- cancelCellEdit() {
3890
- const editing = this.editingCell();
3891
- if (!editing) {
3892
- return;
3893
- }
3894
- const row = this.getRowById(editing.rowId);
3895
- const column = this.columns().find((col) => col.name === editing.columnName);
3896
- const originalValue = this.originalCellValue();
3897
- if (row && column) {
3898
- // Restore original value in row
3899
- set(row, column.path, originalValue);
3900
- // Clear cache for this cell so widget re-renders with original value
3901
- const rowsWithIds = this.rowsWithIds();
3902
- const rowIndex = rowsWithIds.findIndex((item) => item.rowId === editing.rowId);
3903
- if (rowIndex !== -1) {
3904
- const cacheKey = `${rowIndex}_${editing.columnName}`;
3905
- this.cellValueCache.set(cacheKey, originalValue);
3906
- this.widgetNodeCache.delete(cacheKey);
3907
- }
3908
- this.updateRows();
3909
- }
3910
- // Close the editor (same as escape)
3911
- this.editingCell.set(null);
3912
- this.originalCellValue.set(null);
3488
+ handleApplyClick() {
3489
+ this.close({
3490
+ values: this.currentValues(),
3491
+ mode: this.currentMode,
3492
+ });
3913
3493
  }
3914
- /**
3915
- * Handle click outside to stop editing
3916
- */
3917
- handleClickOutside(event) {
3918
- const target = event.target;
3919
- // Don't close if clicking inside a widget or its elements
3920
- if (target.closest('[axp-widget-renderer]') ||
3921
- target.closest('.ax-dropdown') ||
3922
- target.closest('.ax-popup') ||
3923
- target.closest('.__cell-editor')) {
3924
- return;
3925
- }
3926
- this.editingCell.set(null);
3494
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3495
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.16", type: AXPPropertyViewerPopupComponent, isStandalone: true, selector: "axp-property-viewer-popup", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "propertyViewer", first: true, predicate: AXPPropertyViewerComponent, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
3496
+ <div class="ax-flex ax-flex-col ax-h-full ax-overflow-hidden">
3497
+ <axp-property-viewer
3498
+ [tabsInput]="tabs()"
3499
+ [mode]="mode()"
3500
+ (onChanged)="handlePropertyChanged($event)"
3501
+ ></axp-property-viewer>
3502
+ </div>
3503
+
3504
+ <ax-footer>
3505
+ <ax-suffix>
3506
+ <ax-button
3507
+ look="solid"
3508
+ [text]="'@general:actions.close.title' | translate | async"
3509
+ (onClick)="handleCloseClick()"
3510
+ ></ax-button>
3511
+ <ax-button
3512
+ look="solid"
3513
+ color="primary"
3514
+ [text]="'@general:actions.apply.title' | translate | async"
3515
+ (onClick)="handleApplyClick()"
3516
+ ></ax-button>
3517
+ </ax-suffix>
3518
+ </ax-footer>
3519
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPPropertyViewerComponent, selector: "axp-property-viewer", inputs: ["tabsInput", "mode"], outputs: ["onChanged"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3520
+ }
3521
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerPopupComponent, decorators: [{
3522
+ type: Component,
3523
+ args: [{
3524
+ selector: 'axp-property-viewer-popup',
3525
+ template: `
3526
+ <div class="ax-flex ax-flex-col ax-h-full ax-overflow-hidden">
3527
+ <axp-property-viewer
3528
+ [tabsInput]="tabs()"
3529
+ [mode]="mode()"
3530
+ (onChanged)="handlePropertyChanged($event)"
3531
+ ></axp-property-viewer>
3532
+ </div>
3533
+
3534
+ <ax-footer>
3535
+ <ax-suffix>
3536
+ <ax-button
3537
+ look="solid"
3538
+ [text]="'@general:actions.close.title' | translate | async"
3539
+ (onClick)="handleCloseClick()"
3540
+ ></ax-button>
3541
+ <ax-button
3542
+ look="solid"
3543
+ color="primary"
3544
+ [text]="'@general:actions.apply.title' | translate | async"
3545
+ (onClick)="handleApplyClick()"
3546
+ ></ax-button>
3547
+ </ax-suffix>
3548
+ </ax-footer>
3549
+ `,
3550
+ changeDetection: ChangeDetectionStrategy.OnPush,
3551
+ imports: [CommonModule, AXButtonModule, AXDecoratorModule, AXTranslationModule, AXPPropertyViewerComponent],
3552
+ }]
3553
+ }], ctorParameters: () => [], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: true }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }], propertyViewer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPPropertyViewerComponent), { isSignal: true }] }] } });
3554
+
3555
+ var propertyViewerPopup_component = /*#__PURE__*/Object.freeze({
3556
+ __proto__: null,
3557
+ AXPPropertyViewerPopupComponent: AXPPropertyViewerPopupComponent
3558
+ });
3559
+
3560
+ class AXPPropertyViewerService {
3561
+ constructor() {
3562
+ //#region ---- Services & Dependencies ----
3563
+ this.popupService = inject(AXPopupService);
3927
3564
  }
3565
+ //#endregion
3566
+ //#region ---- Public Methods ----
3928
3567
  /**
3929
- * Handle add row (dynamic mode only)
3568
+ * Open property viewer popup with predefined tabs and context.
3930
3569
  */
3931
- handleAddRow() {
3932
- if (this.rowMode() !== 'dynamic' || !this.allowAddRows() || this.readonly()) {
3933
- return;
3934
- }
3935
- const columns = this.columns();
3936
- const newRow = {
3937
- _id: this.generateRowId(),
3938
- };
3939
- // Initialize with default values from widgets
3940
- columns.forEach((column) => {
3941
- if (column.widget.defaultValue !== undefined && column.widget.defaultValue !== null) {
3942
- set(newRow, column.path, column.widget.defaultValue);
3943
- }
3944
- });
3945
- const currentRows = this.rowsModel();
3946
- const updatedRows = [...currentRows, newRow];
3947
- this.rowsModel.set(updatedRows);
3948
- this.rowChange.emit({
3949
- type: 'add',
3950
- rowId: newRow['_id'],
3951
- row: { ...newRow },
3952
- rows: updatedRows,
3570
+ async open(config) {
3571
+ const { AXPPropertyViewerPopupComponent } = await Promise.resolve().then(function () { return propertyViewerPopup_component; });
3572
+ const result = await this.popupService.open(AXPPropertyViewerPopupComponent, {
3573
+ title: config.title,
3574
+ size: config.size || 'md',
3575
+ data: {
3576
+ tabs: signal(config.tabs),
3577
+ mode: signal(config.mode || 'simple'),
3578
+ context: signal(config.context ?? {}),
3579
+ },
3953
3580
  });
3954
- this.spreadsheetChange.emit(updatedRows);
3581
+ return result.data || null;
3955
3582
  }
3956
- /**
3957
- * Handle remove row (works for both fixed and dynamic modes)
3958
- */
3959
- handleRemoveRow(rowId, _event) {
3960
- if (!this.allowRemoveRows() || this.readonly()) {
3961
- return;
3962
- }
3963
- const currentRows = this.currentRows();
3964
- let rowIndex = -1;
3965
- let removedRow;
3966
- if (this.rowMode() === 'fixed') {
3967
- // Fixed mode: find by 'name' property
3968
- rowIndex = currentRows.findIndex((row) => row['name'] === rowId);
3969
- }
3970
- else {
3971
- // Dynamic mode: find by '_id' property
3972
- rowIndex = currentRows.findIndex((row) => row['_id'] === rowId);
3973
- }
3974
- if (rowIndex === -1) {
3975
- return;
3976
- }
3977
- removedRow = currentRows[rowIndex];
3978
- if (this.rowMode() === 'dynamic') {
3979
- // Dynamic mode: update rowsModel
3980
- const updatedRows = currentRows.filter((_, index) => index !== rowIndex);
3981
- this.rowsModel.set(updatedRows);
3982
- this.rowChange.emit({
3983
- type: 'remove',
3984
- rowId,
3985
- row: { ...removedRow },
3986
- rows: updatedRows,
3987
- });
3988
- this.spreadsheetChange.emit(updatedRows);
3989
- }
3990
- else {
3991
- // Fixed mode: update internalRows and emit events
3992
- const updatedRows = currentRows.filter((_, index) => index !== rowIndex);
3993
- this.internalRows.set(updatedRows);
3994
- this.rowChange.emit({
3995
- type: 'remove',
3996
- rowId,
3997
- row: { ...removedRow },
3998
- rows: updatedRows,
3583
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3584
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerService, providedIn: 'root' }); }
3585
+ }
3586
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerService, decorators: [{
3587
+ type: Injectable,
3588
+ args: [{
3589
+ providedIn: 'root',
3590
+ }]
3591
+ }] });
3592
+
3593
+ class AXPQuerySortsComponent {
3594
+ constructor() {
3595
+ this.sortDefinitions = model([], ...(ngDevMode ? [{ debugName: "sortDefinitions" }] : []));
3596
+ this.sortQueries = signal([], ...(ngDevMode ? [{ debugName: "sortQueries", equal: isEqual }] : [{
3597
+ equal: isEqual,
3598
+ }]));
3599
+ this.initialSortQueries = input([], ...(ngDevMode ? [{ debugName: "initialSortQueries" }] : []));
3600
+ this.sortQueriesChange = output();
3601
+ // Recompute merged sort queries whenever inputs change
3602
+ effect(() => {
3603
+ const initialQueries = this.initialSortQueries();
3604
+ const definitions = this.sortDefinitions();
3605
+ const existingQueriesMap = new Map(initialQueries.map((q) => [q.name, q]));
3606
+ const mergedQueries = definitions.map((def) => {
3607
+ const existingQuery = existingQueriesMap.get(def.name);
3608
+ return existingQuery ? { ...def, dir: existingQuery.dir } : { ...def };
3999
3609
  });
4000
- this.spreadsheetChange.emit(updatedRows);
4001
- }
3610
+ this.sortQueries.set(mergedQueries);
3611
+ });
3612
+ effect(() => {
3613
+ this.sortQueriesChange.emit(this.sortQueries().filter((q) => q.dir !== undefined));
3614
+ });
4002
3615
  }
4003
- //#endregion
4004
- //#region ---- Utility Methods ----
4005
- /**
4006
- * Generate unique row ID for dynamic mode
4007
- */
4008
- generateRowId() {
4009
- return `row_${AXPDataGenerator.uuid()}`;
3616
+ drop(event) {
3617
+ const sd = this.sortDefinitions();
3618
+ moveItemInArray(sd, event.previousIndex, event.currentIndex);
3619
+ this.sortDefinitions.set([...sd]);
3620
+ // Reorder sortQueries based on new sortDefinitions order
3621
+ const sq = this.sortQueries();
3622
+ moveItemInArray(sq, event.previousIndex, event.currentIndex);
3623
+ this.sortQueries.set([...sq]);
4010
3624
  }
4011
- /**
4012
- * Update rows array (sync internal state)
4013
- */
4014
- updateRows() {
4015
- if (this.rowMode() === 'dynamic') {
4016
- // For dynamic mode, rowsModel is already updated (two-way binding)
4017
- // Just trigger change detection
4018
- this.rowsModel.set([...this.rowsModel()]);
4019
- }
4020
- else {
4021
- // For fixed mode, update internal state
4022
- this.internalRows.set([...this.internalRows()]);
4023
- }
3625
+ getSortDirection(item) {
3626
+ return this.sortQueries().find((i) => i.name === item.name)?.dir;
4024
3627
  }
4025
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPSpreadsheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4026
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPSpreadsheetComponent, isStandalone: true, selector: "axp-spreadsheet", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rowMode: { classPropertyName: "rowMode", publicName: "rowMode", isSignal: true, isRequired: false, transformFunction: null }, rowsInput: { classPropertyName: "rowsInput", publicName: "rowsInput", isSignal: true, isRequired: false, transformFunction: null }, rowsModel: { classPropertyName: "rowsModel", publicName: "rowsModel", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, emptyCellPlaceholder: { classPropertyName: "emptyCellPlaceholder", publicName: "emptyCellPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, rowTitlePath: { classPropertyName: "rowTitlePath", publicName: "rowTitlePath", isSignal: true, isRequired: false, transformFunction: null }, rowDescriptionPath: { classPropertyName: "rowDescriptionPath", publicName: "rowDescriptionPath", isSignal: true, isRequired: false, transformFunction: null }, allowAddRows: { classPropertyName: "allowAddRows", publicName: "allowAddRows", isSignal: true, isRequired: false, transformFunction: null }, allowRemoveRows: { classPropertyName: "allowRemoveRows", publicName: "allowRemoveRows", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowsModel: "rowsModelChange", cellChange: "cellChange", rowChange: "rowChange", spreadsheetChange: "spreadsheetChange" }, host: { listeners: { "document:keydown": "handleKeyDown($event)" }, classAttribute: "axp-spreadsheet" }, ngImport: i0, template: "<!--\n #region ---- Spreadsheet Component Template ----\n-->\n<div class=\"__spreadsheet-container\" (click)=\"handleClickOutside($event)\">\n @if (isColumnsEmpty()) {\n <!-- Empty State: Columns are empty (regardless of rows) -->\n <div class=\"__empty-state\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-table-columns\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty-columns.title' | translate | async }}</div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty-columns.description' | translate | async }}\n </div>\n </div>\n } @else if (isEmpty()) {\n <!-- Empty State: Both columns and rows are empty -->\n <div class=\"__empty-state\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-table\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty.title' | translate | async }}</div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty.description' | translate | async }}</div>\n </div>\n } @else if (isRowsEmpty()) {\n <!-- Empty State: Only rows are empty - Show columns -->\n <div class=\"__table-wrapper\">\n <table class=\"__table\">\n <!-- Header Row -->\n <thead class=\"__thead\">\n <tr class=\"__header-row\">\n <!-- Corner cell (title) -->\n <th class=\"__corner-cell\">\n @if (title()) {\n <div class=\"__corner-title\">{{ title() | translate | async }}</div>\n }\n @if (rowMode() === 'dynamic' && allowAddRows() && !readonly()) {\n <div class=\"__corner-actions\">\n <ax-button look=\"blank\" color=\"primary\" (onClick)=\"handleAddRow()\"\n [attr.aria-label]=\"'Add Row'\">\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </th>\n\n <!-- Column headers -->\n @for (column of columns(); track column.name) {\n <th class=\"__column-header\" [style.width]=\"column.width\">\n <div class=\"__header-content\">\n <div class=\"__header-title\">{{ column.title | translate | async }}</div>\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Empty State Row -->\n <tbody class=\"__tbody\">\n <tr class=\"__row __row--empty\">\n <td class=\"__row-header\" [attr.colspan]=\"columns().length + 1\">\n <div class=\"__empty-state-inline\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-list\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty-rows.title' | translate | async\n }}\n </div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty-rows.description' |\n translate |\n async }}</div>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n } @else {\n <!-- Full Spreadsheet: Both columns and rows are available -->\n <div class=\"__table-wrapper\">\n <table class=\"__table\">\n <!-- Header Row -->\n <thead class=\"__thead\">\n <tr class=\"__header-row\">\n <!-- Corner cell (title) -->\n <th class=\"__corner-cell\">\n @if (title()) {\n <div class=\"__corner-title\">{{ title() | translate | async }}</div>\n }\n @if (rowMode() === 'dynamic' && allowAddRows() && !readonly()) {\n <div class=\"__corner-actions\">\n <ax-button look=\"blank\" color=\"primary\" (onClick)=\"handleAddRow()\"\n [attr.aria-label]=\"'Add Row'\">\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </th>\n\n <!-- Column headers -->\n @for (column of columns(); track column.name) {\n <th class=\"__column-header\" [style.width]=\"column.width\">\n <div class=\"__header-content\">\n <div class=\"__header-title\">{{ column.title | translate | async }}</div>\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Body Rows -->\n <tbody class=\"__tbody\">\n <axp-widgets-container [context]=\"spreadsheetContext()\"\n (onContextChanged)=\"handleSpreadsheetContextChange($event)\">\n @for (rowItem of rowsWithIds(); track rowItem.rowId; let rowIndex = $index) {\n <tr class=\"__row\" [class.__row--odd]=\"rowIndex % 2 === 1\">\n <!-- Row header -->\n <td class=\"__row-header\">\n <div class=\"__header-content\">\n @if (rowTitlePath()) {\n <div class=\"__header-title\">{{ getRowTitle(rowItem.row, rowTitlePath()!) }}</div>\n }\n @if (rowDescriptionPath()) {\n <div class=\"__header-description\">{{ getRowDescription(rowItem.row,\n rowDescriptionPath()!) }}</div>\n }\n </div>\n @if (allowRemoveRows() && !readonly()) {\n <div class=\"__header-actions ax-xs ax-opacity-30 hover:ax-opacity-100\">\n <ax-button look=\"blank\" color=\"danger\"\n (onClick)=\"handleRemoveRow(rowItem.rowId, $event)\" [attr.aria-label]=\"'Remove Row'\">\n <ax-icon icon=\"far fa-times\"></ax-icon>\n </ax-button>\n </div>\n }\n </td>\n\n <!-- Spreadsheet cells -->\n @for (column of columns(); track column.name) {\n <td class=\"__cell\" [class.__cell--editing]=\"isCellEditing(rowItem.rowId, column.name)\"\n [class.__cell--readonly]=\"isColumnReadonly(column)\"\n (click)=\"$event.stopPropagation(); handleCellClick(rowItem.rowId, column.name, $event)\">\n @if (isCellEditing(rowItem.rowId, column.name) && !isColumnReadonly(column)) {\n <!-- Edit mode: Show widget in edit mode -->\n <div class=\"__cell-editor\" (blur)=\"handleCellBlur(rowItem.rowId, column.name)\"\n tabindex=\"-1\">\n <ng-container axp-widget-renderer [node]=\"getCellWidgetNode(column, rowIndex)\"\n [mode]=\"'edit'\"></ng-container>\n </div>\n <!-- Floating toolbar for commit/cancel -->\n <div class=\"__cell-toolbar ax-xs\" (click)=\"$event.stopPropagation()\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\" (focusin)=\"$event.stopPropagation()\">\n <ax-button look=\"blank\" color=\"success\" (onClick)=\"commitCellEdit()\"\n [attr.aria-label]=\"'Commit'\">\n <ax-icon icon=\"far fa-check\"></ax-icon>\n </ax-button>\n <ax-button look=\"blank\" color=\"danger\" (onClick)=\"cancelCellEdit()\"\n [attr.aria-label]=\"'Cancel'\">\n <ax-icon icon=\"far fa-times\"></ax-icon>\n </ax-button>\n </div>\n } @else {\n <!-- View mode: Show widget in view mode or placeholder -->\n <div class=\"__cell-value\">\n @if (getCellValue(rowIndex, column.name) !== null &&\n getCellValue(rowIndex, column.name) !== undefined) {\n <ng-container axp-widget-renderer [node]=\"getCellWidgetNode(column, rowIndex)\"\n [mode]=\"'view'\"></ng-container>\n } @else {\n <span class=\"__cell-placeholder\">{{ emptyCellPlaceholder() }}</span>\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </axp-widgets-container>\n </tbody>\n </table>\n </div>\n }\n</div>\n<!--\n #endregion\n-->", styles: [".axp-spreadsheet{display:flex;height:100%;width:100%;flex-direction:column}.axp-spreadsheet .__spreadsheet-container{position:relative;flex:1 1 0%;overflow:hidden;border-radius:.375rem;border-width:1px;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.axp-spreadsheet .__empty-state{display:flex;min-height:200px;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center}.axp-spreadsheet .__empty-state .__empty-icon{margin-bottom:1rem}.axp-spreadsheet .__empty-state .__empty-icon i{font-size:2.25rem;line-height:2.5rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state .__empty-title{margin-bottom:.5rem;font-size:1.125rem;line-height:1.75rem;font-weight:600;--tw-text-opacity: 1;color:rgb(38 38 38 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state .__empty-description{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:1.5rem;text-align:center}.axp-spreadsheet .__empty-state-inline .__empty-icon{margin-bottom:.75rem}.axp-spreadsheet .__empty-state-inline .__empty-icon i{font-size:1.5rem;line-height:2rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline .__empty-title{margin-bottom:.25rem;font-size:1rem;line-height:1.5rem;font-weight:600;--tw-text-opacity: 1;color:rgb(38 38 38 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline .__empty-description{font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__table-wrapper{height:100%;width:100%;overflow-x:auto;overflow-y:auto;-webkit-overflow-scrolling:touch}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar{height:8px;width:8px}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-thumb{border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgb(163 163 163 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(115 115 115 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table{min-width:100%;border-collapse:separate;--tw-border-spacing-x: 0px;--tw-border-spacing-y: 0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.axp-spreadsheet .__table .__thead{position:sticky;top:0;z-index:2}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell{position:relative;position:sticky;inset-inline-start:0px;width:200px;min-width:200px;border-width:1px;border-bottom-width:1px;padding:.5rem .75rem;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:3}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell .__corner-title{text-align:center;font-size:.875rem;line-height:1.25rem;font-weight:600}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell .__corner-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}@media (max-width: 768px){.axp-spreadsheet .__table .__thead .__header-row .__corner-cell{width:200px;min-width:200px;padding:.375rem .5rem}}.axp-spreadsheet .__table .__thead .__header-row .__column-header{position:sticky;top:0;width:200px;min-width:200px;white-space:nowrap;border-bottom-width:1px;padding:.5rem .75rem;text-align:center;font-weight:600;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:2}.axp-spreadsheet .__table .__thead .__header-row .__column-header.__column-header--empty{width:300px;min-width:300px;white-space:normal}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content{display:flex;flex-direction:column;gap:.25rem}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-title{font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-description{white-space:normal;overflow-wrap:break-word;font-size:.75rem;line-height:1rem;font-weight:400;opacity:.75}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}@media (max-width: 768px){.axp-spreadsheet .__table .__thead .__header-row .__column-header{width:120px;min-width:120px;padding:.375rem .5rem}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-title,.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-description{font-size:.75rem;line-height:1rem}}.axp-spreadsheet .__table .__tbody .__row.__row--empty .__row-header{text-align:center}.axp-spreadsheet .__table .__tbody .__row .__row-header{position:relative;position:sticky;inset-inline-start:0px;width:200px;min-width:200px;border-width:1px;border-bottom-width:1px;padding:.5rem .75rem;vertical-align:top;font-weight:600;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:2}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content{display:flex;flex-direction:column;gap:.25rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-title{font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-description{font-size:.75rem;line-height:1rem;font-weight:400;opacity:.75}@media (max-width: 768px){.axp-spreadsheet .__table .__tbody .__row .__row-header{width:200px;min-width:200px;padding:.375rem .5rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-title,.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-description{font-size:.75rem;line-height:1rem}}.axp-spreadsheet .__table .__tbody .__row .__cell{position:relative;min-height:40px;width:200px;min-width:200px;cursor:pointer;border-bottom-width:1px;padding:.5rem .75rem;text-align:center;vertical-align:middle}.axp-spreadsheet .__table .__tbody .__row .__cell:before{content:\"\";pointer-events:none;position:absolute;inset:0;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-bg-opacity: .05;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation-duration:.15s;animation-timing-function:cubic-bezier(.4,0,.2,1)}.axp-spreadsheet .__table .__tbody .__row .__cell:hover:not(.__cell--readonly):not(.__cell--editing):before{opacity:1}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--readonly{cursor:default}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--readonly:hover:before{opacity:0}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--empty{cursor:default}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--empty:hover:before{opacity:0}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing{position:relative;border-width:2px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-primary-500),var(--tw-border-opacity, 1));background-color:rgba(var(--ax-sys-color-primary-500),var(--tw-bg-opacity, 1));--tw-bg-opacity: .1;padding:1px}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing .__cell-editor{width:100%;padding:.5rem .75rem}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing .__cell-toolbar{position:absolute;bottom:-35px;inset-inline-end:0px;display:flex;gap:.125rem;white-space:nowrap;border-radius:.375rem;border-width:1px;padding:.25rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:100}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-value{display:flex;min-height:1.25rem;align-items:center;justify-content:center;overflow-wrap:break-word;padding:.125rem;font-size:.875rem;line-height:1.25rem}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-placeholder{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-editor{display:flex;width:100%;align-items:center;justify-content:center}@media (max-width: 768px){.axp-spreadsheet .__table .__tbody .__row .__cell{width:120px;min-width:120px;padding:.375rem .5rem}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-value{font-size:.75rem;line-height:1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i6$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i6$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3628
+ changeItemSort(item) {
3629
+ const itemDirection = this.getSortDirection(item);
3630
+ const newDirection = itemDirection === 'asc' ? 'desc' : itemDirection === 'desc' ? undefined : 'asc';
3631
+ this.sortQueries.update((prev) => {
3632
+ return prev.map((field) => {
3633
+ if (field.name === item.name) {
3634
+ return { ...field, dir: newDirection };
3635
+ }
3636
+ return field;
3637
+ });
3638
+ });
3639
+ }
3640
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPQuerySortsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3641
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPQuerySortsComponent, isStandalone: true, selector: "axp-query-sorts", inputs: { sortDefinitions: { classPropertyName: "sortDefinitions", publicName: "sortDefinitions", isSignal: true, isRequired: false, transformFunction: null }, initialSortQueries: { classPropertyName: "initialSortQueries", publicName: "initialSortQueries", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortDefinitions: "sortDefinitionsChange", sortQueriesChange: "sortQueriesChange" }, ngImport: i0, template: "<div class=\"ax-flex ax-flex-col ax-justify-center ax-gap-4 ax-select-none\">\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-select-none\" cdkDropList (cdkDropListDropped)=\"drop($event)\">\n <div class=\"ax-flex ax-flex-col ax-gap-3 ax-w-full ax-sorted-list ax-max-h-[calc(100vh-280px)] ax-overflow-auto\">\n @for (item of sortDefinitions(); track item.name) {\n <div class=\"ax-flex ax-py-1 ax-items-center ax-justify-between\" cdkDrag cdkDragBoundary=\".ax-sorted-list\">\n <div class=\"ax-flex ax-items-center ax-gap-3\" cdkDragHandle>\n <ax-icon class=\"fa-solid fa-grip-dots-vertical ax-cursor-move\"></ax-icon>\n <p class=\"ax-font-medium ax-text-sm\">{{ item.title | translate | async }}</p>\n </div>\n <ax-button [color]=\"'blank'\" class=\"ax-sm\" (click)=\"changeItemSort(item)\">\n <ax-icon\n [class.ax-text-primary]=\"getSortDirection(item) === 'asc'\"\n class=\"fa-solid fa-arrow-up-long ax-text-neutral-400\"\n ></ax-icon>\n <ax-icon\n [class.ax-text-primary]=\"getSortDirection(item) === 'desc'\"\n class=\"fa-solid fa-arrow-down-long ax-text-neutral-400\"\n ></ax-icon>\n </ax-button>\n </div>\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: AXTabsModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
4027
3642
  }
4028
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPSpreadsheetComponent, decorators: [{
3643
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPQuerySortsComponent, decorators: [{
4029
3644
  type: Component,
4030
- args: [{ selector: 'axp-spreadsheet', standalone: true, imports: [
4031
- CommonModule,
4032
- FormsModule,
3645
+ args: [{ selector: 'axp-query-sorts', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
3646
+ CdkDropList,
3647
+ CdkDrag,
3648
+ CdkDragHandle,
3649
+ AXTabsModule,
4033
3650
  AXTranslationModule,
4034
- AXButtonModule,
3651
+ CommonModule,
4035
3652
  AXDecoratorModule,
4036
- AXPWidgetCoreModule,
4037
- ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
4038
- class: 'axp-spreadsheet',
4039
- }, template: "<!--\n #region ---- Spreadsheet Component Template ----\n-->\n<div class=\"__spreadsheet-container\" (click)=\"handleClickOutside($event)\">\n @if (isColumnsEmpty()) {\n <!-- Empty State: Columns are empty (regardless of rows) -->\n <div class=\"__empty-state\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-table-columns\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty-columns.title' | translate | async }}</div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty-columns.description' | translate | async }}\n </div>\n </div>\n } @else if (isEmpty()) {\n <!-- Empty State: Both columns and rows are empty -->\n <div class=\"__empty-state\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-table\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty.title' | translate | async }}</div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty.description' | translate | async }}</div>\n </div>\n } @else if (isRowsEmpty()) {\n <!-- Empty State: Only rows are empty - Show columns -->\n <div class=\"__table-wrapper\">\n <table class=\"__table\">\n <!-- Header Row -->\n <thead class=\"__thead\">\n <tr class=\"__header-row\">\n <!-- Corner cell (title) -->\n <th class=\"__corner-cell\">\n @if (title()) {\n <div class=\"__corner-title\">{{ title() | translate | async }}</div>\n }\n @if (rowMode() === 'dynamic' && allowAddRows() && !readonly()) {\n <div class=\"__corner-actions\">\n <ax-button look=\"blank\" color=\"primary\" (onClick)=\"handleAddRow()\"\n [attr.aria-label]=\"'Add Row'\">\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </th>\n\n <!-- Column headers -->\n @for (column of columns(); track column.name) {\n <th class=\"__column-header\" [style.width]=\"column.width\">\n <div class=\"__header-content\">\n <div class=\"__header-title\">{{ column.title | translate | async }}</div>\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Empty State Row -->\n <tbody class=\"__tbody\">\n <tr class=\"__row __row--empty\">\n <td class=\"__row-header\" [attr.colspan]=\"columns().length + 1\">\n <div class=\"__empty-state-inline\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-list\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty-rows.title' | translate | async\n }}\n </div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty-rows.description' |\n translate |\n async }}</div>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n } @else {\n <!-- Full Spreadsheet: Both columns and rows are available -->\n <div class=\"__table-wrapper\">\n <table class=\"__table\">\n <!-- Header Row -->\n <thead class=\"__thead\">\n <tr class=\"__header-row\">\n <!-- Corner cell (title) -->\n <th class=\"__corner-cell\">\n @if (title()) {\n <div class=\"__corner-title\">{{ title() | translate | async }}</div>\n }\n @if (rowMode() === 'dynamic' && allowAddRows() && !readonly()) {\n <div class=\"__corner-actions\">\n <ax-button look=\"blank\" color=\"primary\" (onClick)=\"handleAddRow()\"\n [attr.aria-label]=\"'Add Row'\">\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </th>\n\n <!-- Column headers -->\n @for (column of columns(); track column.name) {\n <th class=\"__column-header\" [style.width]=\"column.width\">\n <div class=\"__header-content\">\n <div class=\"__header-title\">{{ column.title | translate | async }}</div>\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Body Rows -->\n <tbody class=\"__tbody\">\n <axp-widgets-container [context]=\"spreadsheetContext()\"\n (onContextChanged)=\"handleSpreadsheetContextChange($event)\">\n @for (rowItem of rowsWithIds(); track rowItem.rowId; let rowIndex = $index) {\n <tr class=\"__row\" [class.__row--odd]=\"rowIndex % 2 === 1\">\n <!-- Row header -->\n <td class=\"__row-header\">\n <div class=\"__header-content\">\n @if (rowTitlePath()) {\n <div class=\"__header-title\">{{ getRowTitle(rowItem.row, rowTitlePath()!) }}</div>\n }\n @if (rowDescriptionPath()) {\n <div class=\"__header-description\">{{ getRowDescription(rowItem.row,\n rowDescriptionPath()!) }}</div>\n }\n </div>\n @if (allowRemoveRows() && !readonly()) {\n <div class=\"__header-actions ax-xs ax-opacity-30 hover:ax-opacity-100\">\n <ax-button look=\"blank\" color=\"danger\"\n (onClick)=\"handleRemoveRow(rowItem.rowId, $event)\" [attr.aria-label]=\"'Remove Row'\">\n <ax-icon icon=\"far fa-times\"></ax-icon>\n </ax-button>\n </div>\n }\n </td>\n\n <!-- Spreadsheet cells -->\n @for (column of columns(); track column.name) {\n <td class=\"__cell\" [class.__cell--editing]=\"isCellEditing(rowItem.rowId, column.name)\"\n [class.__cell--readonly]=\"isColumnReadonly(column)\"\n (click)=\"$event.stopPropagation(); handleCellClick(rowItem.rowId, column.name, $event)\">\n @if (isCellEditing(rowItem.rowId, column.name) && !isColumnReadonly(column)) {\n <!-- Edit mode: Show widget in edit mode -->\n <div class=\"__cell-editor\" (blur)=\"handleCellBlur(rowItem.rowId, column.name)\"\n tabindex=\"-1\">\n <ng-container axp-widget-renderer [node]=\"getCellWidgetNode(column, rowIndex)\"\n [mode]=\"'edit'\"></ng-container>\n </div>\n <!-- Floating toolbar for commit/cancel -->\n <div class=\"__cell-toolbar ax-xs\" (click)=\"$event.stopPropagation()\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\" (focusin)=\"$event.stopPropagation()\">\n <ax-button look=\"blank\" color=\"success\" (onClick)=\"commitCellEdit()\"\n [attr.aria-label]=\"'Commit'\">\n <ax-icon icon=\"far fa-check\"></ax-icon>\n </ax-button>\n <ax-button look=\"blank\" color=\"danger\" (onClick)=\"cancelCellEdit()\"\n [attr.aria-label]=\"'Cancel'\">\n <ax-icon icon=\"far fa-times\"></ax-icon>\n </ax-button>\n </div>\n } @else {\n <!-- View mode: Show widget in view mode or placeholder -->\n <div class=\"__cell-value\">\n @if (getCellValue(rowIndex, column.name) !== null &&\n getCellValue(rowIndex, column.name) !== undefined) {\n <ng-container axp-widget-renderer [node]=\"getCellWidgetNode(column, rowIndex)\"\n [mode]=\"'view'\"></ng-container>\n } @else {\n <span class=\"__cell-placeholder\">{{ emptyCellPlaceholder() }}</span>\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </axp-widgets-container>\n </tbody>\n </table>\n </div>\n }\n</div>\n<!--\n #endregion\n-->", styles: [".axp-spreadsheet{display:flex;height:100%;width:100%;flex-direction:column}.axp-spreadsheet .__spreadsheet-container{position:relative;flex:1 1 0%;overflow:hidden;border-radius:.375rem;border-width:1px;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.axp-spreadsheet .__empty-state{display:flex;min-height:200px;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center}.axp-spreadsheet .__empty-state .__empty-icon{margin-bottom:1rem}.axp-spreadsheet .__empty-state .__empty-icon i{font-size:2.25rem;line-height:2.5rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state .__empty-title{margin-bottom:.5rem;font-size:1.125rem;line-height:1.75rem;font-weight:600;--tw-text-opacity: 1;color:rgb(38 38 38 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state .__empty-description{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:1.5rem;text-align:center}.axp-spreadsheet .__empty-state-inline .__empty-icon{margin-bottom:.75rem}.axp-spreadsheet .__empty-state-inline .__empty-icon i{font-size:1.5rem;line-height:2rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline .__empty-title{margin-bottom:.25rem;font-size:1rem;line-height:1.5rem;font-weight:600;--tw-text-opacity: 1;color:rgb(38 38 38 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline .__empty-description{font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__table-wrapper{height:100%;width:100%;overflow-x:auto;overflow-y:auto;-webkit-overflow-scrolling:touch}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar{height:8px;width:8px}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-thumb{border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgb(163 163 163 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(115 115 115 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table{min-width:100%;border-collapse:separate;--tw-border-spacing-x: 0px;--tw-border-spacing-y: 0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.axp-spreadsheet .__table .__thead{position:sticky;top:0;z-index:2}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell{position:relative;position:sticky;inset-inline-start:0px;width:200px;min-width:200px;border-width:1px;border-bottom-width:1px;padding:.5rem .75rem;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:3}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell .__corner-title{text-align:center;font-size:.875rem;line-height:1.25rem;font-weight:600}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell .__corner-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}@media (max-width: 768px){.axp-spreadsheet .__table .__thead .__header-row .__corner-cell{width:200px;min-width:200px;padding:.375rem .5rem}}.axp-spreadsheet .__table .__thead .__header-row .__column-header{position:sticky;top:0;width:200px;min-width:200px;white-space:nowrap;border-bottom-width:1px;padding:.5rem .75rem;text-align:center;font-weight:600;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:2}.axp-spreadsheet .__table .__thead .__header-row .__column-header.__column-header--empty{width:300px;min-width:300px;white-space:normal}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content{display:flex;flex-direction:column;gap:.25rem}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-title{font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-description{white-space:normal;overflow-wrap:break-word;font-size:.75rem;line-height:1rem;font-weight:400;opacity:.75}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}@media (max-width: 768px){.axp-spreadsheet .__table .__thead .__header-row .__column-header{width:120px;min-width:120px;padding:.375rem .5rem}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-title,.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-description{font-size:.75rem;line-height:1rem}}.axp-spreadsheet .__table .__tbody .__row.__row--empty .__row-header{text-align:center}.axp-spreadsheet .__table .__tbody .__row .__row-header{position:relative;position:sticky;inset-inline-start:0px;width:200px;min-width:200px;border-width:1px;border-bottom-width:1px;padding:.5rem .75rem;vertical-align:top;font-weight:600;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:2}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content{display:flex;flex-direction:column;gap:.25rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-title{font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-description{font-size:.75rem;line-height:1rem;font-weight:400;opacity:.75}@media (max-width: 768px){.axp-spreadsheet .__table .__tbody .__row .__row-header{width:200px;min-width:200px;padding:.375rem .5rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-title,.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-description{font-size:.75rem;line-height:1rem}}.axp-spreadsheet .__table .__tbody .__row .__cell{position:relative;min-height:40px;width:200px;min-width:200px;cursor:pointer;border-bottom-width:1px;padding:.5rem .75rem;text-align:center;vertical-align:middle}.axp-spreadsheet .__table .__tbody .__row .__cell:before{content:\"\";pointer-events:none;position:absolute;inset:0;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-bg-opacity: .05;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation-duration:.15s;animation-timing-function:cubic-bezier(.4,0,.2,1)}.axp-spreadsheet .__table .__tbody .__row .__cell:hover:not(.__cell--readonly):not(.__cell--editing):before{opacity:1}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--readonly{cursor:default}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--readonly:hover:before{opacity:0}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--empty{cursor:default}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--empty:hover:before{opacity:0}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing{position:relative;border-width:2px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-primary-500),var(--tw-border-opacity, 1));background-color:rgba(var(--ax-sys-color-primary-500),var(--tw-bg-opacity, 1));--tw-bg-opacity: .1;padding:1px}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing .__cell-editor{width:100%;padding:.5rem .75rem}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing .__cell-toolbar{position:absolute;bottom:-35px;inset-inline-end:0px;display:flex;gap:.125rem;white-space:nowrap;border-radius:.375rem;border-width:1px;padding:.25rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:100}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-value{display:flex;min-height:1.25rem;align-items:center;justify-content:center;overflow-wrap:break-word;padding:.125rem;font-size:.875rem;line-height:1.25rem}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-placeholder{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-editor{display:flex;width:100%;align-items:center;justify-content:center}@media (max-width: 768px){.axp-spreadsheet .__table .__tbody .__row .__cell{width:120px;min-width:120px;padding:.375rem .5rem}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-value{font-size:.75rem;line-height:1rem}}\n"] }]
4040
- }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rowMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowMode", required: false }] }], rowsInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsInput", required: false }] }], rowsModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsModel", required: false }] }, { type: i0.Output, args: ["rowsModelChange"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], emptyCellPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyCellPlaceholder", required: false }] }], rowTitlePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowTitlePath", required: false }] }], rowDescriptionPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDescriptionPath", required: false }] }], allowAddRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowAddRows", required: false }] }], allowRemoveRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowRemoveRows", required: false }] }], cellChange: [{ type: i0.Output, args: ["cellChange"] }], rowChange: [{ type: i0.Output, args: ["rowChange"] }], spreadsheetChange: [{ type: i0.Output, args: ["spreadsheetChange"] }], handleKeyDown: [{
4041
- type: HostListener,
4042
- args: ['document:keydown', ['$event']]
4043
- }] } });
4044
-
4045
- //#endregion
3653
+ AXButtonModule,
3654
+ ], template: "<div class=\"ax-flex ax-flex-col ax-justify-center ax-gap-4 ax-select-none\">\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-select-none\" cdkDropList (cdkDropListDropped)=\"drop($event)\">\n <div class=\"ax-flex ax-flex-col ax-gap-3 ax-w-full ax-sorted-list ax-max-h-[calc(100vh-280px)] ax-overflow-auto\">\n @for (item of sortDefinitions(); track item.name) {\n <div class=\"ax-flex ax-py-1 ax-items-center ax-justify-between\" cdkDrag cdkDragBoundary=\".ax-sorted-list\">\n <div class=\"ax-flex ax-items-center ax-gap-3\" cdkDragHandle>\n <ax-icon class=\"fa-solid fa-grip-dots-vertical ax-cursor-move\"></ax-icon>\n <p class=\"ax-font-medium ax-text-sm\">{{ item.title | translate | async }}</p>\n </div>\n <ax-button [color]=\"'blank'\" class=\"ax-sm\" (click)=\"changeItemSort(item)\">\n <ax-icon\n [class.ax-text-primary]=\"getSortDirection(item) === 'asc'\"\n class=\"fa-solid fa-arrow-up-long ax-text-neutral-400\"\n ></ax-icon>\n <ax-icon\n [class.ax-text-primary]=\"getSortDirection(item) === 'desc'\"\n class=\"fa-solid fa-arrow-down-long ax-text-neutral-400\"\n ></ax-icon>\n </ax-button>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
3655
+ }], ctorParameters: () => [], propDecorators: { sortDefinitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortDefinitions", required: false }] }, { type: i0.Output, args: ["sortDefinitionsChange"] }], initialSortQueries: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSortQueries", required: false }] }], sortQueriesChange: [{ type: i0.Output, args: ["sortQueriesChange"] }] } });
4046
3656
 
4047
- class AXPLogoComponent {
4048
- constructor() {
4049
- this.platform = inject(AXPlatform);
4050
- }
4051
- ngOnInit() {
4052
- this.setLogoType();
4053
- this.platform.themeMode$.subscribe(() => {
4054
- this.setLogoTheme();
4055
- });
4056
- }
4057
- ngOnChanges() {
4058
- this.setLogoTheme();
4059
- this.setLogoType();
4060
- }
4061
- setLogoType() {
4062
- switch (true) {
4063
- case this.source instanceof AXPImageUrlLogoConfig:
4064
- this.logoType = 'url';
4065
- break;
4066
- case this.source instanceof AXPComponentLogoConfig:
4067
- this.logoType = 'component';
4068
- break;
4069
- case this.source instanceof AXPIconLogoConfig:
4070
- this.logoType = 'icon';
4071
- break;
4072
- default:
4073
- break;
4074
- }
4075
- }
4076
- setLogoTheme() {
4077
- if (this.source && this.source.dark && this.source.light) {
4078
- this.source = this.platform.isDark() ? this.source.dark : this.source.light;
4079
- }
4080
- }
4081
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPLogoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4082
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", 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", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i2$6.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXDecoratorModule }] }); }
4083
- }
4084
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPLogoComponent, decorators: [{
4085
- type: Component,
4086
- args: [{ selector: 'axp-logo', imports: [CommonModule, AXImageModule, AXDecoratorModule], host: { class: 'ax-flex ax-justify-center' }, 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" }]
4087
- }], propDecorators: { source: [{
4088
- type: Input
4089
- }] } });
4090
-
4091
- class AXPPropertyViewerComponent {
3657
+ //#region ---- Component Definition ----
3658
+ class AXPSpreadsheetComponent {
4092
3659
  //#endregion
3660
+ //#region ---- Lifecycle Methods ----
4093
3661
  constructor() {
3662
+ //#endregion
3663
+ //#region ---- Services & Dependencies ----
3664
+ this.translationService = inject(AXTranslationService);
3665
+ //#endregion
3666
+ //#region ---- Computed Properties for Translations ----
3667
+ /**
3668
+ * Translated column titles
3669
+ */
3670
+ this.translatedColumnTitles = signal(new Map(), ...(ngDevMode ? [{ debugName: "translatedColumnTitles" }] : []));
3671
+ //#endregion
4094
3672
  //#region ---- Inputs ----
4095
3673
  /**
4096
- * List of tabs that should be rendered inside the property viewer.
3674
+ * Title for the spreadsheet (displayed in corner cell)
4097
3675
  */
4098
- this.tabsInput = input([], ...(ngDevMode ? [{ debugName: "tabsInput" }] : []));
3676
+ this.title = input('', ...(ngDevMode ? [{ debugName: "title" }] : []));
4099
3677
  /**
4100
- * Determines rendering mode.
3678
+ * Column definitions
4101
3679
  */
4102
- this.mode = input('simple', ...(ngDevMode ? [{ debugName: "mode" }] : []));
3680
+ this.columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : []));
3681
+ /**
3682
+ * Row mode: 'fixed' or 'dynamic'
3683
+ */
3684
+ this.rowMode = input('fixed', ...(ngDevMode ? [{ debugName: "rowMode" }] : []));
3685
+ /**
3686
+ * Rows data (for fixed mode - input only)
3687
+ * Each row must have a 'name' property for identification
3688
+ */
3689
+ this.rowsInput = input([], ...(ngDevMode ? [{ debugName: "rowsInput" }] : []));
3690
+ /**
3691
+ * Rows data (for dynamic mode - two-way binding)
3692
+ */
3693
+ this.rowsModel = model([], ...(ngDevMode ? [{ debugName: "rowsModel" }] : []));
3694
+ /**
3695
+ * Whether the spreadsheet is in readonly mode
3696
+ */
3697
+ this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
3698
+ /**
3699
+ * Placeholder text for empty cells (default: "–")
3700
+ */
3701
+ this.emptyCellPlaceholder = input('–', ...(ngDevMode ? [{ debugName: "emptyCellPlaceholder" }] : []));
3702
+ /**
3703
+ * Path to property in row object to display as row header title
3704
+ * Example: 'name', 'title', 'user.name'
3705
+ */
3706
+ this.rowTitlePath = input(null, ...(ngDevMode ? [{ debugName: "rowTitlePath" }] : []));
3707
+ /**
3708
+ * Path to property in row object to display as row header description
3709
+ * Example: 'description', 'details', 'user.description'
3710
+ */
3711
+ this.rowDescriptionPath = input(null, ...(ngDevMode ? [{ debugName: "rowDescriptionPath" }] : []));
3712
+ /**
3713
+ * Whether to allow adding rows (dynamic mode only, default: true)
3714
+ */
3715
+ this.allowAddRows = input(true, ...(ngDevMode ? [{ debugName: "allowAddRows" }] : []));
3716
+ /**
3717
+ * Whether to allow removing rows (default: true for dynamic mode, false for fixed mode)
3718
+ * Can be enabled for fixed mode if needed
3719
+ */
3720
+ this.allowRemoveRows = input(true, ...(ngDevMode ? [{ debugName: "allowRemoveRows" }] : []));
4103
3721
  //#endregion
4104
3722
  //#region ---- Outputs ----
4105
3723
  /**
4106
- * Emits when property context changes.
3724
+ * Emitted when a single cell value changes
4107
3725
  */
4108
- this.onChanged = new EventEmitter();
3726
+ this.cellChange = output();
3727
+ /**
3728
+ * Emitted when rows are added or removed (dynamic mode only)
3729
+ */
3730
+ this.rowChange = output();
3731
+ /**
3732
+ * Emitted when the entire spreadsheet data changes
3733
+ */
3734
+ this.spreadsheetChange = output();
4109
3735
  //#endregion
4110
- //#region ---- Signals ----
4111
- this.currentTabIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentTabIndex" }] : []));
4112
- this.tabs = computed(() => {
4113
- const incomingTabs = this.tabsInput() ?? [];
4114
- return incomingTabs
4115
- .map((tab) => ({
4116
- ...tab,
4117
- groups: (tab.groups ?? [])
4118
- .map((group) => ({
4119
- ...group,
4120
- isCollapsed: group.isCollapsed ?? false,
4121
- props: (group.props ?? []).filter((prop) => prop.visible !== false),
4122
- }))
4123
- .filter((group) => group.props.length > 0),
4124
- }))
4125
- .filter((tab) => tab.groups.length > 0);
4126
- }, ...(ngDevMode ? [{ debugName: "tabs" }] : []));
4127
- this.groups = computed(() => {
4128
- const preparedTabs = this.tabs();
4129
- if (!preparedTabs.length) {
4130
- return [];
3736
+ //#region ---- Class Properties ----
3737
+ /**
3738
+ * Currently editing cell: { rowId, columnName } | null
3739
+ */
3740
+ this.editingCell = signal(null, ...(ngDevMode ? [{ debugName: "editingCell" }] : []));
3741
+ /**
3742
+ * Original value when entering edit mode (for cancel functionality)
3743
+ */
3744
+ this.originalCellValue = signal(null, ...(ngDevMode ? [{ debugName: "originalCellValue" }] : []));
3745
+ /**
3746
+ * Internal rows state (for fixed mode)
3747
+ */
3748
+ this.internalRows = signal([], ...(ngDevMode ? [{ debugName: "internalRows" }] : []));
3749
+ /**
3750
+ * Row ID mapping: rowId -> row index (for tracking)
3751
+ */
3752
+ this.rowIdMap = new Map();
3753
+ /**
3754
+ * Cache for widget nodes to prevent re-rendering
3755
+ * Key: `${rowIndex}_${columnName}`, Value: AXPWidgetNode
3756
+ */
3757
+ this.widgetNodeCache = new Map();
3758
+ /**
3759
+ * Cache for cell values to detect actual changes
3760
+ * Key: `${rowIndex}_${columnName}`, Value: any
3761
+ */
3762
+ this.cellValueCache = new Map();
3763
+ //#endregion
3764
+ //#region ---- Computed Properties ----
3765
+ /**
3766
+ * Get current rows based on mode
3767
+ */
3768
+ this.currentRows = computed(() => {
3769
+ if (this.rowMode() === 'dynamic') {
3770
+ return this.rowsModel();
4131
3771
  }
4132
- const index = Math.min(this.currentTabIndex(), preparedTabs.length - 1);
4133
- if (index !== this.currentTabIndex()) {
4134
- this.currentTabIndex.set(index);
3772
+ else {
3773
+ return this.internalRows();
4135
3774
  }
4136
- return preparedTabs[index].groups.map((group) => ({
4137
- ...group,
4138
- isCollapsed: this.groupCollapsedStates.get(group.name) ?? !!group.isCollapsed,
4139
- }));
4140
- }, ...(ngDevMode ? [{ debugName: "groups" }] : []));
4141
- this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
3775
+ }, ...(ngDevMode ? [{ debugName: "currentRows" }] : []));
4142
3776
  /**
4143
- * Per-expression-path override for "use expression" vs "use value" when binding.enabled.
4144
- * Updated when user toggles the ax-switch; otherwise derived from isExpression(valueAtPath).
3777
+ * Context for widgets-container (all rows as an object with rows array)
3778
+ * This allows widgets to access their cell via path like: rows[index].column.path
4145
3779
  */
4146
- this.expressionModeState = signal({}, ...(ngDevMode ? [{ debugName: "expressionModeState" }] : []));
4147
- //#endregion
4148
- //#region ---- Properties ----
4149
- this.groupCollapsedStates = new Map();
4150
- this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
4151
- this.popupService = inject(AXPopupService);
4152
- effect(() => {
4153
- const availableTabs = this.tabs();
4154
- const availableGroups = availableTabs.flatMap((tab) => tab.groups);
4155
- availableGroups.forEach((group) => {
4156
- if (!this.groupCollapsedStates.has(group.name) && group.isCollapsed !== undefined) {
4157
- this.groupCollapsedStates.set(group.name, !!group.isCollapsed);
3780
+ this.spreadsheetContext = computed(() => {
3781
+ return {
3782
+ rows: this.currentRows(),
3783
+ };
3784
+ }, ...(ngDevMode ? [{ debugName: "spreadsheetContext" }] : []));
3785
+ /**
3786
+ * Get rows with IDs (ensure all rows have identifiers)
3787
+ */
3788
+ this.rowsWithIds = computed(() => {
3789
+ const rows = this.currentRows();
3790
+ const mode = this.rowMode();
3791
+ const result = [];
3792
+ rows.forEach((row, index) => {
3793
+ let rowId;
3794
+ if (mode === 'fixed') {
3795
+ // Fixed mode: use 'name' property
3796
+ rowId = row['name'] || `row_${index}`;
3797
+ }
3798
+ else {
3799
+ // Dynamic mode: generate ID if not exists
3800
+ if (!row['_id']) {
3801
+ row['_id'] = this.generateRowId();
3802
+ }
3803
+ rowId = row['_id'];
4158
3804
  }
3805
+ // Update row ID map
3806
+ this.rowIdMap.set(rowId, index);
3807
+ result.push({ rowId, row });
4159
3808
  });
4160
- if (!availableTabs.length) {
4161
- this.currentTabIndex.set(0);
4162
- }
4163
- else if (this.currentTabIndex() >= availableTabs.length) {
4164
- this.currentTabIndex.set(0);
4165
- }
4166
- });
4167
- }
4168
- //#region ---- Public Methods ----
4169
- /**
4170
- * Replaces the current context with the provided value and emits an init event.
4171
- */
4172
- initializeContext(value) {
4173
- untracked(() => {
4174
- this.context.set(cloneDeep(value ?? {}));
4175
- this.onChanged.emit({ values: this.context(), mode: 'init' });
4176
- });
4177
- }
4178
- /**
4179
- * Merges the provided value into the current context and emits an update event.
4180
- */
4181
- update(value) {
4182
- untracked(() => {
4183
- this.context.set(merge(cloneDeep(this.context()), value ?? {}));
4184
- this.onChanged.emit({ values: this.context(), mode: 'update' });
4185
- });
4186
- }
4187
- //#endregion
4188
- //#region ---- Event Handlers ----
3809
+ return result;
3810
+ }, ...(ngDevMode ? [{ debugName: "rowsWithIds" }] : []));
3811
+ /**
3812
+ * Check if spreadsheet is completely empty (both columns and rows)
3813
+ */
3814
+ this.isEmpty = computed(() => {
3815
+ const columns = this.columns();
3816
+ const rows = this.currentRows();
3817
+ return (!columns || columns.length === 0) && (!rows || rows.length === 0);
3818
+ }, ...(ngDevMode ? [{ debugName: "isEmpty" }] : []));
3819
+ /**
3820
+ * Check if columns are empty (regardless of rows)
3821
+ * When columns are empty, we should show a message about missing columns, not rows
3822
+ */
3823
+ this.isColumnsEmpty = computed(() => {
3824
+ const columns = this.columns();
3825
+ return !columns || columns.length === 0;
3826
+ }, ...(ngDevMode ? [{ debugName: "isColumnsEmpty" }] : []));
3827
+ /**
3828
+ * Check if only rows are empty (columns exist but no rows)
3829
+ */
3830
+ this.isRowsEmpty = computed(() => {
3831
+ const columns = this.columns();
3832
+ const rows = this.currentRows();
3833
+ return (columns && columns.length > 0) && (!rows || rows.length === 0);
3834
+ }, ...(ngDevMode ? [{ debugName: "isRowsEmpty" }] : []));
3835
+ // Initialize rows from input (fixed mode)
3836
+ this.internalRows.set(this.rowsInput());
3837
+ // Watch for rowsInput changes and update internalRows (fixed mode only)
3838
+ effect(() => {
3839
+ const rowsInput = this.rowsInput();
3840
+ const rowMode = this.rowMode();
3841
+ // Only update internalRows in fixed mode
3842
+ if (rowMode === 'fixed') {
3843
+ this.internalRows.set(rowsInput);
3844
+ }
3845
+ });
3846
+ }
3847
+ //#endregion
3848
+ //#region ---- Cell Value Methods ----
4189
3849
  /**
4190
- * Handles context changes produced by rendered widgets.
3850
+ * Get the value for a specific cell by row index
4191
3851
  */
4192
- handleContextChange(event) {
4193
- untracked(() => {
4194
- this.context.set(event.data);
4195
- this.onChanged.emit({ values: this.context(), mode: event.state === 'initiated' ? 'init' : 'update' });
4196
- });
3852
+ getCellValue(rowIndex, columnName) {
3853
+ const rows = this.currentRows();
3854
+ const row = rows[rowIndex];
3855
+ if (!row) {
3856
+ return null;
3857
+ }
3858
+ const column = this.columns().find((col) => col.name === columnName);
3859
+ if (!column) {
3860
+ return null;
3861
+ }
3862
+ return get(row, column.path, null);
4197
3863
  }
4198
3864
  /**
4199
- * Handles tab selection changes.
3865
+ * Get row title from row object using rowTitlePath
4200
3866
  */
4201
- handleTabChange(event) {
4202
- const index = event.index;
4203
- if (this.currentTabIndex() !== index) {
4204
- this.currentTabIndex.set(index);
3867
+ getRowTitle(row, path) {
3868
+ if (!row || !path) {
3869
+ return '';
4205
3870
  }
3871
+ return get(row, path, '') || '';
4206
3872
  }
4207
3873
  /**
4208
- * Stores collapsed state for a group.
3874
+ * Get row description from row object using rowDescriptionPath
4209
3875
  */
4210
- handleCollapsedChange(groupName, isCollapsed) {
4211
- this.groupCollapsedStates.set(groupName, isCollapsed);
3876
+ getRowDescription(row, path) {
3877
+ if (!row || !path) {
3878
+ return '';
3879
+ }
3880
+ return get(row, path, '') || '';
4212
3881
  }
4213
3882
  /**
4214
- * Returns the config path used for the binding value (and expression) for a property with binding.enabled.
3883
+ * Get row by ID
4215
3884
  */
4216
- getExpressionPath(prop) {
4217
- const basePath = prop.schema?.interface?.path ?? prop.name;
4218
- return prop.binding?.optionKey ? `${basePath}.${prop.binding.optionKey}` : basePath;
3885
+ getRowById(rowId) {
3886
+ const rowsWithIds = this.rowsWithIds();
3887
+ const found = rowsWithIds.find((item) => item.rowId === rowId);
3888
+ return found ? found.row : null;
4219
3889
  }
4220
3890
  /**
4221
- * True when the property is in "expression" mode: show only the expression editor button, not the widget.
4222
- * Uses expressionModeState when set; otherwise derives from isExpression(valueAtPath).
3891
+ * Check if a cell is currently being edited
4223
3892
  */
4224
- isExpressionMode(prop) {
4225
- this.expressionModeState(); // dependency for change detection
4226
- const path = this.getExpressionPath(prop);
4227
- const stored = this.expressionModeState()[path];
4228
- if (stored !== undefined) {
4229
- return stored;
3893
+ isCellEditing(rowId, columnName) {
3894
+ const editing = this.editingCell();
3895
+ return editing?.rowId === rowId && editing?.columnName === columnName;
3896
+ }
3897
+ /**
3898
+ * Check if a column is readonly
3899
+ */
3900
+ isColumnReadonly(column) {
3901
+ return this.readonly() || column.readonly === true;
3902
+ }
3903
+ /**
3904
+ * Get widget node for a cell with dynamic path based on row index
3905
+ * Path format: rows[rowIndex].column.path
3906
+ * Uses caching to prevent unnecessary re-renders
3907
+ */
3908
+ getCellWidgetNode(column, rowIndex) {
3909
+ const cacheKey = `${rowIndex}_${column.name}`;
3910
+ const rows = this.currentRows();
3911
+ const row = rows[rowIndex];
3912
+ const currentValue = row ? get(row, column.path, null) : null;
3913
+ const cachedValue = this.cellValueCache.get(cacheKey);
3914
+ // Get or create widget node
3915
+ let widgetNode = this.widgetNodeCache.get(cacheKey);
3916
+ if (!widgetNode) {
3917
+ // Create path: rows[index].column.path
3918
+ const dynamicPath = `rows.${rowIndex}.${column.path}`;
3919
+ // Create new widget node
3920
+ widgetNode = {
3921
+ ...column.widget,
3922
+ path: dynamicPath,
3923
+ defaultValue: currentValue !== null && currentValue !== undefined ? currentValue : column.widget.defaultValue,
3924
+ };
3925
+ // Cache the widget node
3926
+ this.widgetNodeCache.set(cacheKey, widgetNode);
3927
+ this.cellValueCache.set(cacheKey, currentValue);
4230
3928
  }
4231
- const v = get(this.context(), path);
4232
- return this.expressionEvaluator.isExpression(typeof v === 'string' ? v : '');
3929
+ else {
3930
+ // Update path in case row index changed (shouldn't happen, but be safe)
3931
+ widgetNode.path = `rows.${rowIndex}.${column.path}`;
3932
+ // Only update defaultValue if value actually changed (to prevent re-renders)
3933
+ if (cachedValue !== currentValue) {
3934
+ widgetNode.defaultValue = currentValue !== null && currentValue !== undefined ? currentValue : column.widget.defaultValue;
3935
+ this.cellValueCache.set(cacheKey, currentValue);
3936
+ }
3937
+ }
3938
+ return widgetNode;
4233
3939
  }
3940
+ //#endregion
3941
+ //#region ---- Event Handlers ----
4234
3942
  /**
4235
- * Returns the default value when switching from expression to value mode.
4236
- * Source of truth is prop.schema.defaultValue (widget.types): plain value, or function (context) => value.
4237
- * A minimal dataType fallback is used only when the schema provides no default.
3943
+ * Handle cell click to start editing
4238
3944
  */
4239
- getDefaultValueForProperty(prop) {
4240
- const def = prop.schema?.defaultValue;
4241
- if (typeof def === 'function') {
4242
- return def(this.context());
3945
+ handleCellClick(rowId, columnName, event) {
3946
+ if (this.readonly()) {
3947
+ return;
4243
3948
  }
4244
- if (def !== undefined && def !== null) {
4245
- return def;
3949
+ // Don't open editor if clicking on toolbar or buttons
3950
+ if (event) {
3951
+ const target = event.target;
3952
+ if (target.closest('.__cell-toolbar') || target.closest('ax-button')) {
3953
+ return;
3954
+ }
4246
3955
  }
4247
- const dataType = prop.schema?.dataType ?? 'string';
4248
- switch (dataType) {
4249
- case 'boolean':
4250
- return false;
4251
- case 'number':
4252
- return 0;
4253
- case 'object':
4254
- return {};
4255
- case 'array':
4256
- return [];
4257
- default:
4258
- return '';
3956
+ const column = this.columns().find((col) => col.name === columnName);
3957
+ if (!column || this.isColumnReadonly(column)) {
3958
+ return;
3959
+ }
3960
+ // Don't open if already editing this cell
3961
+ const currentEditing = this.editingCell();
3962
+ if (currentEditing?.rowId === rowId && currentEditing?.columnName === columnName) {
3963
+ return;
3964
+ }
3965
+ // Store original value for cancel functionality
3966
+ const row = this.getRowById(rowId);
3967
+ if (row) {
3968
+ const originalValue = get(row, column.path, null);
3969
+ this.originalCellValue.set(originalValue);
4259
3970
  }
3971
+ this.editingCell.set({ rowId, columnName });
4260
3972
  }
4261
3973
  /**
4262
- * Updates the expression/value toggle state for a binding-enabled property.
4263
- * When switching to value: resets the value at path to a type-appropriate default so the
4264
- * widget renderer does not receive the expression string (e.g. checkbox gets false, not a string).
4265
- * When switching to expression: sets the value at path to '{{ }}' so the popup opens with
4266
- * a clean expression placeholder instead of the previous literal (e.g. true/false).
3974
+ * Handle cell value change from widget
3975
+ * The widget sets the value at path like rows[index].column.path
4267
3976
  */
4268
- setExpressionMode(prop, useExpression) {
4269
- const path = this.getExpressionPath(prop);
4270
- const currentVal = get(this.context(), path);
4271
- const currentIsExpression = this.expressionEvaluator.isExpression(typeof currentVal === 'string' ? currentVal : '');
4272
- this.expressionModeState.update((s) => ({ ...s, [path]: useExpression }));
4273
- if (!useExpression && currentIsExpression) {
4274
- const next = cloneDeep(this.context());
4275
- set(next, path, this.getDefaultValueForProperty(prop));
4276
- untracked(() => {
4277
- this.context.set(next);
4278
- this.onChanged.emit({ values: this.context(), mode: 'update' });
4279
- });
3977
+ handleCellValueChange(rowIndex, columnName, event) {
3978
+ // Only process if context has changed (not just initiated)
3979
+ if (event.state !== 'changed') {
3980
+ return;
4280
3981
  }
4281
- else if (useExpression && !currentIsExpression) {
4282
- const next = cloneDeep(this.context());
4283
- set(next, path, '{{ }}');
4284
- untracked(() => {
4285
- this.context.set(next);
4286
- this.onChanged.emit({ values: this.context(), mode: 'update' });
4287
- });
3982
+ const rows = this.currentRows();
3983
+ const row = rows[rowIndex];
3984
+ if (!row) {
3985
+ return;
3986
+ }
3987
+ const column = this.columns().find((col) => col.name === columnName);
3988
+ if (!column) {
3989
+ return;
4288
3990
  }
3991
+ // Get the new value from the context (widget sets it at rows[index].column.path)
3992
+ const dynamicPath = `rows.${rowIndex}.${column.path}`;
3993
+ const newValue = get(event.data, dynamicPath);
3994
+ // Update the row object directly
3995
+ set(row, column.path, newValue);
3996
+ // Update rows array
3997
+ this.updateRows();
3998
+ // Get rowId for events
3999
+ const rowId = this.rowMode() === 'fixed' ? row['name'] : row['_id'];
4000
+ // Emit events
4001
+ this.cellChange.emit({
4002
+ rowId: rowId || `row_${rowIndex}`,
4003
+ columnName,
4004
+ value: newValue,
4005
+ row: { ...row },
4006
+ });
4007
+ this.spreadsheetChange.emit([...this.currentRows()]);
4008
+ // Don't close edit mode automatically - let user finish editing
4009
+ // Edit mode will close on blur or click outside
4289
4010
  }
4290
4011
  /**
4291
- * Opens the binding/expression code editor for a property with binding.enabled.
4292
- * Ensures the value passed to the popup is always a string to avoid code-editor crashes.
4012
+ * Handle spreadsheet context change from widgets-container
4013
+ * This is called when any widget in the spreadsheet changes
4293
4014
  */
4294
- async openBindingEditor(prop) {
4295
- const expressionPath = this.getExpressionPath(prop);
4296
- const raw = get(this.context(), expressionPath);
4297
- const currentValue = typeof raw === 'string' ? raw : '';
4298
- const { AXPBindingExpressionEditorPopupComponent } = await import('./acorex-platform-layout-components-binding-expression-editor-popup.component-Cb6Lk4Ch.mjs');
4299
- const result = await this.popupService.open(AXPBindingExpressionEditorPopupComponent, {
4300
- title: '@general:widgets.triggers.edit-binding-expression',
4301
- size: 'lg',
4302
- data: { initialValue: signal(currentValue) },
4303
- });
4304
- if (result?.data?.value !== undefined) {
4305
- const next = cloneDeep(this.context());
4306
- set(next, expressionPath, result.data.value);
4307
- untracked(() => {
4308
- this.context.set(next);
4309
- this.onChanged.emit({ values: this.context(), mode: 'update' });
4015
+ handleSpreadsheetContextChange(event) {
4016
+ if (event.state !== 'changed') {
4017
+ return;
4018
+ }
4019
+ // Extract row index and column from the path (format: rows[index].column.path)
4020
+ const path = event.path || '';
4021
+ const pathMatch = path.match(/^rows\.(\d+)\.(.+)$/);
4022
+ if (!pathMatch) {
4023
+ return;
4024
+ }
4025
+ const rowIndex = parseInt(pathMatch[1], 10);
4026
+ const columnPath = pathMatch[2];
4027
+ // Find the column by path
4028
+ const column = this.columns().find((col) => col.path === columnPath);
4029
+ if (!column) {
4030
+ return;
4031
+ }
4032
+ // Get the new value from event.data
4033
+ const newValue = get(event.data, path);
4034
+ // Update the row object (sync with context)
4035
+ const rows = this.currentRows();
4036
+ const row = rows[rowIndex];
4037
+ if (row) {
4038
+ // The context should already have the updated value, but sync row object to be sure
4039
+ set(row, columnPath, newValue);
4040
+ // Update cache (widget node will update defaultValue on next getCellWidgetNode call)
4041
+ const cacheKey = `${rowIndex}_${column.name}`;
4042
+ this.cellValueCache.set(cacheKey, newValue);
4043
+ // Don't delete widget node cache - just update defaultValue on next access
4044
+ // This prevents re-rendering while still reflecting the new value
4045
+ this.updateRows();
4046
+ // Get rowId for events
4047
+ const rowId = this.rowMode() === 'fixed' ? row['name'] : row['_id'];
4048
+ // Emit events
4049
+ this.cellChange.emit({
4050
+ rowId: rowId || `row_${rowIndex}`,
4051
+ columnName: column.name,
4052
+ value: newValue,
4053
+ row: { ...row },
4310
4054
  });
4055
+ this.spreadsheetChange.emit([...this.currentRows()]);
4311
4056
  }
4312
4057
  }
4313
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4314
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPPropertyViewerComponent, isStandalone: true, selector: "axp-property-viewer", inputs: { tabsInput: { classPropertyName: "tabsInput", publicName: "tabsInput", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onChanged: "onChanged" }, ngImport: i0, template: "<axp-widgets-container [context]=\"context()\" (onContextChanged)=\"handleContextChange($event)\">\n <!-- #region Shared property row template (used in both advanced and simple modes) -->\n <ng-template #propertyRow let-prop=\"prop\" let-translateTitle=\"translateTitle\">\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-py-2\">\n <!-- Property header: title + value/expression toggle (when binding.enabled) -->\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-w-full\">\n @if (prop.showLabel !== false) {\n @if (translateTitle) {\n <span class=\"ax-font-semibold ax-flex-1 ax-min-w-0\">{{ prop.title | translate | async }}</span>\n } @else {\n <span class=\"ax-font-semibold ax-flex-1 ax-min-w-0\">{{ prop.title }}</span>\n }\n }\n @if (prop.binding?.enabled) {\n <div class=\"ax-flex ax-gap-1 ax-shrink-0 ax-xs\">\n <ax-button [toggleable]=\"true\" [selected]=\"isExpressionMode(prop)\" look=\"twotone\"\n [color]=\"isExpressionMode(prop) ? 'primary' : 'default'\" class=\"ax-shrink-0\"\n [title]=\"(isExpressionMode(prop) ? '@general:terms.common.expression' : '@general:terms.common.value') | translate | async\"\n (selectedChange)=\"setExpressionMode(prop, $event)\">\n @if (isExpressionMode(prop)) {\n <ax-icon icon=\"fa-light fa-code\"></ax-icon>\n } @else {\n <ax-icon icon=\"fa-light fa-keyboard\"></ax-icon>\n }\n </ax-button>\n </div>\n }\n </div>\n <!-- Property content: Open Editor button (expression mode) or widget renderer (value mode) -->\n @if (prop.binding?.enabled && isExpressionMode(prop)) {\n <ax-button look=\"twotone\" color=\"primary\" size=\"sm\" class=\"ax-w-full\"\n [text]=\"'@general:widgets.triggers.open-editor' | translate | async\" (onClick)=\"openBindingEditor(prop)\"\n [title]=\"'@general:widgets.triggers.edit-binding-expression' | translate | async\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-code\"></ax-icon>\n </ax-prefix>\n </ax-button>\n } @else if (prop.schema.interface) {\n <ng-container axp-widget-renderer [node]=\"prop.schema.interface\" [mode]=\"'edit'\">\n </ng-container>\n }\n </div>\n </ng-template>\n <!-- #endregion -->\n\n <!-- #region Advanced mode: tabs + accordion groups -->\n @if (mode() === 'advanced') {\n <div class=\"ax-flex ax-flex-col\">\n @if (tabs().length > 1) {\n <div class=\"ax-pb-2\">\n <ax-tabs look=\"default\" (onActiveTabChanged)=\"handleTabChange($event)\" [look]=\"'with-line'\">\n @for (tab of tabs(); track $index) {\n <ax-tab-item [text]=\"tab.title\" [key]=\"$index.toString()\" [active]=\"currentTabIndex() === $index\">\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n }\n <div class=\"ax-flex-1 ax-overflow-auto\">\n <ax-accordion-group [accordion]=\"false\" [look]=\"'flat'\">\n @for (group of groups(); track $index) {\n <ax-accordion-item class=\"!ax-mb-0\" [caption]=\"group.title\" [isCollapsed]=\"group.isCollapsed\"\n (isCollapsedChange)=\"handleCollapsedChange(group.name, $event)\">\n <div class=\"ax-flex ax-flex-col\">\n @for (p of group.props; track $index) {\n <ng-container *ngTemplateOutlet=\"propertyRow; context: { prop: p, translateTitle: false }\">\n </ng-container>\n }\n </div>\n </ax-accordion-item>\n }\n </ax-accordion-group>\n </div>\n </div>\n }\n <!-- #endregion -->\n\n <!-- #region Simple mode: flat list of properties with padding -->\n @else {\n <div class=\"ax-flex ax-flex-col ax-p-4 ax-gap-4\">\n @for (group of groups(); track $index) {\n @for (p of group.props; track $index) {\n <ng-container *ngTemplateOutlet=\"propertyRow; context: { prop: p, translateTitle: true }\">\n </ng-container>\n }\n }\n </div>\n }\n <!-- #endregion -->\n</axp-widgets-container>", styles: ["axp-property-viewer ax-accordion-item .ax-accordion-header{font-weight:600!important;border-bottom-width:1px!important;background-color:rgb(var(--ax-sys-color-lighter-surface))!important;color:rgb(var(--ax-sys-color-on-lighter-surface))!important;border-color:rgb(var(--ax-sys-color-border-lighter-surface))!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: AXAccordionModule }, { kind: "component", type: i2$7.AXAccordionItemComponent, selector: "ax-accordion-item", inputs: ["isCollapsed", "icon", "caption", "isLoading", "headerTemplate", "look", "disabled"], outputs: ["isCollapsedChange", "isLoadingChange", "onClick"] }, { kind: "component", type: i2$7.AXAccordionGroupComponent, selector: "ax-accordion-group", inputs: ["accordion", "activeIndex", "look", "collapsedOnItemClick"], outputs: ["activeIndexChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXButtonGroupModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", 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: AXTabsModule }, { kind: "component", type: i5$3.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i5$3.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i6$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i6$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
4315
- }
4316
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerComponent, decorators: [{
4317
- type: Component,
4318
- args: [{ selector: 'axp-property-viewer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
4319
- CommonModule,
4320
- AXAccordionModule,
4321
- AXButtonModule,
4322
- AXButtonGroupModule,
4323
- AXDecoratorModule,
4324
- AXTabsModule,
4325
- AXPWidgetCoreModule,
4326
- AXTranslationModule,
4327
- ], template: "<axp-widgets-container [context]=\"context()\" (onContextChanged)=\"handleContextChange($event)\">\n <!-- #region Shared property row template (used in both advanced and simple modes) -->\n <ng-template #propertyRow let-prop=\"prop\" let-translateTitle=\"translateTitle\">\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-py-2\">\n <!-- Property header: title + value/expression toggle (when binding.enabled) -->\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-w-full\">\n @if (prop.showLabel !== false) {\n @if (translateTitle) {\n <span class=\"ax-font-semibold ax-flex-1 ax-min-w-0\">{{ prop.title | translate | async }}</span>\n } @else {\n <span class=\"ax-font-semibold ax-flex-1 ax-min-w-0\">{{ prop.title }}</span>\n }\n }\n @if (prop.binding?.enabled) {\n <div class=\"ax-flex ax-gap-1 ax-shrink-0 ax-xs\">\n <ax-button [toggleable]=\"true\" [selected]=\"isExpressionMode(prop)\" look=\"twotone\"\n [color]=\"isExpressionMode(prop) ? 'primary' : 'default'\" class=\"ax-shrink-0\"\n [title]=\"(isExpressionMode(prop) ? '@general:terms.common.expression' : '@general:terms.common.value') | translate | async\"\n (selectedChange)=\"setExpressionMode(prop, $event)\">\n @if (isExpressionMode(prop)) {\n <ax-icon icon=\"fa-light fa-code\"></ax-icon>\n } @else {\n <ax-icon icon=\"fa-light fa-keyboard\"></ax-icon>\n }\n </ax-button>\n </div>\n }\n </div>\n <!-- Property content: Open Editor button (expression mode) or widget renderer (value mode) -->\n @if (prop.binding?.enabled && isExpressionMode(prop)) {\n <ax-button look=\"twotone\" color=\"primary\" size=\"sm\" class=\"ax-w-full\"\n [text]=\"'@general:widgets.triggers.open-editor' | translate | async\" (onClick)=\"openBindingEditor(prop)\"\n [title]=\"'@general:widgets.triggers.edit-binding-expression' | translate | async\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-code\"></ax-icon>\n </ax-prefix>\n </ax-button>\n } @else if (prop.schema.interface) {\n <ng-container axp-widget-renderer [node]=\"prop.schema.interface\" [mode]=\"'edit'\">\n </ng-container>\n }\n </div>\n </ng-template>\n <!-- #endregion -->\n\n <!-- #region Advanced mode: tabs + accordion groups -->\n @if (mode() === 'advanced') {\n <div class=\"ax-flex ax-flex-col\">\n @if (tabs().length > 1) {\n <div class=\"ax-pb-2\">\n <ax-tabs look=\"default\" (onActiveTabChanged)=\"handleTabChange($event)\" [look]=\"'with-line'\">\n @for (tab of tabs(); track $index) {\n <ax-tab-item [text]=\"tab.title\" [key]=\"$index.toString()\" [active]=\"currentTabIndex() === $index\">\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n }\n <div class=\"ax-flex-1 ax-overflow-auto\">\n <ax-accordion-group [accordion]=\"false\" [look]=\"'flat'\">\n @for (group of groups(); track $index) {\n <ax-accordion-item class=\"!ax-mb-0\" [caption]=\"group.title\" [isCollapsed]=\"group.isCollapsed\"\n (isCollapsedChange)=\"handleCollapsedChange(group.name, $event)\">\n <div class=\"ax-flex ax-flex-col\">\n @for (p of group.props; track $index) {\n <ng-container *ngTemplateOutlet=\"propertyRow; context: { prop: p, translateTitle: false }\">\n </ng-container>\n }\n </div>\n </ax-accordion-item>\n }\n </ax-accordion-group>\n </div>\n </div>\n }\n <!-- #endregion -->\n\n <!-- #region Simple mode: flat list of properties with padding -->\n @else {\n <div class=\"ax-flex ax-flex-col ax-p-4 ax-gap-4\">\n @for (group of groups(); track $index) {\n @for (p of group.props; track $index) {\n <ng-container *ngTemplateOutlet=\"propertyRow; context: { prop: p, translateTitle: true }\">\n </ng-container>\n }\n }\n </div>\n }\n <!-- #endregion -->\n</axp-widgets-container>", styles: ["axp-property-viewer ax-accordion-item .ax-accordion-header{font-weight:600!important;border-bottom-width:1px!important;background-color:rgb(var(--ax-sys-color-lighter-surface))!important;color:rgb(var(--ax-sys-color-on-lighter-surface))!important;border-color:rgb(var(--ax-sys-color-border-lighter-surface))!important}\n"] }]
4328
- }], ctorParameters: () => [], propDecorators: { tabsInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabsInput", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], onChanged: [{
4329
- type: Output
4330
- }] } });
4331
-
4332
- class AXPPropertyViewerPopupComponent extends AXBasePageComponent {
4333
- //#endregion
4334
- constructor() {
4335
- super();
4336
- //#region ---- Inputs ----
4337
- this.tabs = input.required(...(ngDevMode ? [{ debugName: "tabs" }] : []));
4338
- this.mode = input('simple', ...(ngDevMode ? [{ debugName: "mode" }] : []));
4339
- this.context = input({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
4340
- //#endregion
4341
- //#region ---- Properties ----
4342
- this.propertyViewer = viewChild(AXPPropertyViewerComponent, ...(ngDevMode ? [{ debugName: "propertyViewer" }] : []));
4343
- this.currentValues = signal({}, ...(ngDevMode ? [{ debugName: "currentValues" }] : []));
4344
- this.currentMode = 'init';
4345
- effect(() => {
4346
- const viewer = this.propertyViewer();
4347
- if (!viewer) {
4348
- return;
4349
- }
4350
- viewer.initializeContext(this.context() ?? {});
4351
- });
4352
- }
4353
- //#region ---- Event Handlers ----
4354
- handlePropertyChanged(event) {
4355
- this.currentValues.set(event.values ?? {});
4356
- this.currentMode = event.mode;
4357
- }
4358
- handleCloseClick() {
4359
- this.close();
4360
- }
4361
- handleApplyClick() {
4362
- this.close({
4363
- values: this.currentValues(),
4364
- mode: this.currentMode,
4365
- });
4366
- }
4367
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4368
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.16", type: AXPPropertyViewerPopupComponent, isStandalone: true, selector: "axp-property-viewer-popup", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "propertyViewer", first: true, predicate: AXPPropertyViewerComponent, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
4369
- <div class="ax-flex ax-flex-col ax-h-full ax-overflow-hidden">
4370
- <axp-property-viewer
4371
- [tabsInput]="tabs()"
4372
- [mode]="mode()"
4373
- (onChanged)="handlePropertyChanged($event)"
4374
- ></axp-property-viewer>
4375
- </div>
4376
-
4377
- <ax-footer>
4378
- <ax-suffix>
4379
- <ax-button
4380
- look="solid"
4381
- [text]="'@general:actions.close.title' | translate | async"
4382
- (onClick)="handleCloseClick()"
4383
- ></ax-button>
4384
- <ax-button
4385
- look="solid"
4386
- color="primary"
4387
- [text]="'@general:actions.apply.title' | translate | async"
4388
- (onClick)="handleApplyClick()"
4389
- ></ax-button>
4390
- </ax-suffix>
4391
- </ax-footer>
4392
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPPropertyViewerComponent, selector: "axp-property-viewer", inputs: ["tabsInput", "mode"], outputs: ["onChanged"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4393
- }
4394
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerPopupComponent, decorators: [{
4395
- type: Component,
4396
- args: [{
4397
- selector: 'axp-property-viewer-popup',
4398
- template: `
4399
- <div class="ax-flex ax-flex-col ax-h-full ax-overflow-hidden">
4400
- <axp-property-viewer
4401
- [tabsInput]="tabs()"
4402
- [mode]="mode()"
4403
- (onChanged)="handlePropertyChanged($event)"
4404
- ></axp-property-viewer>
4405
- </div>
4406
-
4407
- <ax-footer>
4408
- <ax-suffix>
4409
- <ax-button
4410
- look="solid"
4411
- [text]="'@general:actions.close.title' | translate | async"
4412
- (onClick)="handleCloseClick()"
4413
- ></ax-button>
4414
- <ax-button
4415
- look="solid"
4416
- color="primary"
4417
- [text]="'@general:actions.apply.title' | translate | async"
4418
- (onClick)="handleApplyClick()"
4419
- ></ax-button>
4420
- </ax-suffix>
4421
- </ax-footer>
4422
- `,
4423
- changeDetection: ChangeDetectionStrategy.OnPush,
4424
- imports: [CommonModule, AXButtonModule, AXDecoratorModule, AXTranslationModule, AXPPropertyViewerComponent],
4425
- }]
4426
- }], ctorParameters: () => [], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: true }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }], propertyViewer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPPropertyViewerComponent), { isSignal: true }] }] } });
4427
-
4428
- var propertyViewerPopup_component = /*#__PURE__*/Object.freeze({
4429
- __proto__: null,
4430
- AXPPropertyViewerPopupComponent: AXPPropertyViewerPopupComponent
4431
- });
4432
-
4433
- class AXPPropertyViewerService {
4434
- constructor() {
4435
- //#region ---- Services & Dependencies ----
4436
- this.popupService = inject(AXPopupService);
4058
+ /**
4059
+ * Handle blur event to stop editing (when user clicks away from cell)
4060
+ */
4061
+ handleCellBlur(rowId, columnName) {
4062
+ // Small delay to allow click events to process first
4063
+ setTimeout(() => {
4064
+ const editing = this.editingCell();
4065
+ if (editing?.rowId === rowId && editing?.columnName === columnName) {
4066
+ // Don't close if clicking on the toolbar or buttons
4067
+ const activeElement = document.activeElement;
4068
+ const toolbarElement = activeElement?.closest('.__cell-toolbar');
4069
+ const buttonElement = activeElement?.closest('ax-button');
4070
+ // Only auto-commit if not clicking on toolbar or buttons
4071
+ if (!toolbarElement && !buttonElement) {
4072
+ this.commitCellEdit();
4073
+ }
4074
+ }
4075
+ }, 150);
4437
4076
  }
4438
- //#endregion
4439
- //#region ---- Public Methods ----
4440
4077
  /**
4441
- * Open property viewer popup with predefined tabs and context.
4078
+ * Handle Escape key to cancel editing
4442
4079
  */
4443
- async open(config) {
4444
- const { AXPPropertyViewerPopupComponent } = await Promise.resolve().then(function () { return propertyViewerPopup_component; });
4445
- const result = await this.popupService.open(AXPPropertyViewerPopupComponent, {
4446
- title: config.title,
4447
- size: config.size || 'md',
4448
- data: {
4449
- tabs: signal(config.tabs),
4450
- mode: signal(config.mode || 'simple'),
4451
- context: signal(config.context ?? {}),
4452
- },
4453
- });
4454
- return result.data || null;
4080
+ handleKeyDown(event) {
4081
+ if (event.key === 'Escape' && this.editingCell()) {
4082
+ event.preventDefault();
4083
+ event.stopPropagation();
4084
+ this.cancelCellEdit();
4085
+ }
4455
4086
  }
4456
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4457
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerService, providedIn: 'root' }); }
4458
- }
4459
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPPropertyViewerService, decorators: [{
4460
- type: Injectable,
4461
- args: [{
4462
- providedIn: 'root',
4463
- }]
4464
- }] });
4465
-
4466
- class AXPQuerySortsComponent {
4467
- constructor() {
4468
- this.sortDefinitions = model([], ...(ngDevMode ? [{ debugName: "sortDefinitions" }] : []));
4469
- this.sortQueries = signal([], ...(ngDevMode ? [{ debugName: "sortQueries", equal: isEqual }] : [{
4470
- equal: isEqual,
4471
- }]));
4472
- this.initialSortQueries = input([], ...(ngDevMode ? [{ debugName: "initialSortQueries" }] : []));
4473
- this.sortQueriesChange = output();
4474
- // Recompute merged sort queries whenever inputs change
4475
- effect(() => {
4476
- const initialQueries = this.initialSortQueries();
4477
- const definitions = this.sortDefinitions();
4478
- const existingQueriesMap = new Map(initialQueries.map((q) => [q.name, q]));
4479
- const mergedQueries = definitions.map((def) => {
4480
- const existingQuery = existingQueriesMap.get(def.name);
4481
- return existingQuery ? { ...def, dir: existingQuery.dir } : { ...def };
4482
- });
4483
- this.sortQueries.set(mergedQueries);
4087
+ /**
4088
+ * Commit cell edit (save changes and close editing mode)
4089
+ */
4090
+ commitCellEdit() {
4091
+ const editing = this.editingCell();
4092
+ if (!editing) {
4093
+ return;
4094
+ }
4095
+ // Changes are already saved via handleSpreadsheetContextChange
4096
+ // Close the editor (same as escape, but keeps changes)
4097
+ this.editingCell.set(null);
4098
+ this.originalCellValue.set(null);
4099
+ }
4100
+ /**
4101
+ * Cancel cell edit (discard changes and close editing mode - same as escape)
4102
+ */
4103
+ cancelCellEdit() {
4104
+ const editing = this.editingCell();
4105
+ if (!editing) {
4106
+ return;
4107
+ }
4108
+ const row = this.getRowById(editing.rowId);
4109
+ const column = this.columns().find((col) => col.name === editing.columnName);
4110
+ const originalValue = this.originalCellValue();
4111
+ if (row && column) {
4112
+ // Restore original value in row
4113
+ set(row, column.path, originalValue);
4114
+ // Clear cache for this cell so widget re-renders with original value
4115
+ const rowsWithIds = this.rowsWithIds();
4116
+ const rowIndex = rowsWithIds.findIndex((item) => item.rowId === editing.rowId);
4117
+ if (rowIndex !== -1) {
4118
+ const cacheKey = `${rowIndex}_${editing.columnName}`;
4119
+ this.cellValueCache.set(cacheKey, originalValue);
4120
+ this.widgetNodeCache.delete(cacheKey);
4121
+ }
4122
+ this.updateRows();
4123
+ }
4124
+ // Close the editor (same as escape)
4125
+ this.editingCell.set(null);
4126
+ this.originalCellValue.set(null);
4127
+ }
4128
+ /**
4129
+ * Handle click outside to stop editing
4130
+ */
4131
+ handleClickOutside(event) {
4132
+ const target = event.target;
4133
+ // Don't close if clicking inside a widget or its elements
4134
+ if (target.closest('[axp-widget-renderer]') ||
4135
+ target.closest('.ax-dropdown') ||
4136
+ target.closest('.ax-popup') ||
4137
+ target.closest('.__cell-editor')) {
4138
+ return;
4139
+ }
4140
+ this.editingCell.set(null);
4141
+ }
4142
+ /**
4143
+ * Handle add row (dynamic mode only)
4144
+ */
4145
+ handleAddRow() {
4146
+ if (this.rowMode() !== 'dynamic' || !this.allowAddRows() || this.readonly()) {
4147
+ return;
4148
+ }
4149
+ const columns = this.columns();
4150
+ const newRow = {
4151
+ _id: this.generateRowId(),
4152
+ };
4153
+ // Initialize with default values from widgets
4154
+ columns.forEach((column) => {
4155
+ if (column.widget.defaultValue !== undefined && column.widget.defaultValue !== null) {
4156
+ set(newRow, column.path, column.widget.defaultValue);
4157
+ }
4484
4158
  });
4485
- effect(() => {
4486
- this.sortQueriesChange.emit(this.sortQueries().filter((q) => q.dir !== undefined));
4159
+ const currentRows = this.rowsModel();
4160
+ const updatedRows = [...currentRows, newRow];
4161
+ this.rowsModel.set(updatedRows);
4162
+ this.rowChange.emit({
4163
+ type: 'add',
4164
+ rowId: newRow['_id'],
4165
+ row: { ...newRow },
4166
+ rows: updatedRows,
4487
4167
  });
4168
+ this.spreadsheetChange.emit(updatedRows);
4488
4169
  }
4489
- drop(event) {
4490
- const sd = this.sortDefinitions();
4491
- moveItemInArray(sd, event.previousIndex, event.currentIndex);
4492
- this.sortDefinitions.set([...sd]);
4493
- // Reorder sortQueries based on new sortDefinitions order
4494
- const sq = this.sortQueries();
4495
- moveItemInArray(sq, event.previousIndex, event.currentIndex);
4496
- this.sortQueries.set([...sq]);
4170
+ /**
4171
+ * Handle remove row (works for both fixed and dynamic modes)
4172
+ */
4173
+ handleRemoveRow(rowId, _event) {
4174
+ if (!this.allowRemoveRows() || this.readonly()) {
4175
+ return;
4176
+ }
4177
+ const currentRows = this.currentRows();
4178
+ let rowIndex = -1;
4179
+ let removedRow;
4180
+ if (this.rowMode() === 'fixed') {
4181
+ // Fixed mode: find by 'name' property
4182
+ rowIndex = currentRows.findIndex((row) => row['name'] === rowId);
4183
+ }
4184
+ else {
4185
+ // Dynamic mode: find by '_id' property
4186
+ rowIndex = currentRows.findIndex((row) => row['_id'] === rowId);
4187
+ }
4188
+ if (rowIndex === -1) {
4189
+ return;
4190
+ }
4191
+ removedRow = currentRows[rowIndex];
4192
+ if (this.rowMode() === 'dynamic') {
4193
+ // Dynamic mode: update rowsModel
4194
+ const updatedRows = currentRows.filter((_, index) => index !== rowIndex);
4195
+ this.rowsModel.set(updatedRows);
4196
+ this.rowChange.emit({
4197
+ type: 'remove',
4198
+ rowId,
4199
+ row: { ...removedRow },
4200
+ rows: updatedRows,
4201
+ });
4202
+ this.spreadsheetChange.emit(updatedRows);
4203
+ }
4204
+ else {
4205
+ // Fixed mode: update internalRows and emit events
4206
+ const updatedRows = currentRows.filter((_, index) => index !== rowIndex);
4207
+ this.internalRows.set(updatedRows);
4208
+ this.rowChange.emit({
4209
+ type: 'remove',
4210
+ rowId,
4211
+ row: { ...removedRow },
4212
+ rows: updatedRows,
4213
+ });
4214
+ this.spreadsheetChange.emit(updatedRows);
4215
+ }
4497
4216
  }
4498
- getSortDirection(item) {
4499
- return this.sortQueries().find((i) => i.name === item.name)?.dir;
4217
+ //#endregion
4218
+ //#region ---- Utility Methods ----
4219
+ /**
4220
+ * Generate unique row ID for dynamic mode
4221
+ */
4222
+ generateRowId() {
4223
+ return `row_${AXPDataGenerator.uuid()}`;
4500
4224
  }
4501
- changeItemSort(item) {
4502
- const itemDirection = this.getSortDirection(item);
4503
- const newDirection = itemDirection === 'asc' ? 'desc' : itemDirection === 'desc' ? undefined : 'asc';
4504
- this.sortQueries.update((prev) => {
4505
- return prev.map((field) => {
4506
- if (field.name === item.name) {
4507
- return { ...field, dir: newDirection };
4508
- }
4509
- return field;
4510
- });
4511
- });
4225
+ /**
4226
+ * Update rows array (sync internal state)
4227
+ */
4228
+ updateRows() {
4229
+ if (this.rowMode() === 'dynamic') {
4230
+ // For dynamic mode, rowsModel is already updated (two-way binding)
4231
+ // Just trigger change detection
4232
+ this.rowsModel.set([...this.rowsModel()]);
4233
+ }
4234
+ else {
4235
+ // For fixed mode, update internal state
4236
+ this.internalRows.set([...this.internalRows()]);
4237
+ }
4512
4238
  }
4513
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPQuerySortsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4514
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPQuerySortsComponent, isStandalone: true, selector: "axp-query-sorts", inputs: { sortDefinitions: { classPropertyName: "sortDefinitions", publicName: "sortDefinitions", isSignal: true, isRequired: false, transformFunction: null }, initialSortQueries: { classPropertyName: "initialSortQueries", publicName: "initialSortQueries", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortDefinitions: "sortDefinitionsChange", sortQueriesChange: "sortQueriesChange" }, ngImport: i0, template: "<div class=\"ax-flex ax-flex-col ax-justify-center ax-gap-4 ax-select-none\">\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-select-none\" cdkDropList (cdkDropListDropped)=\"drop($event)\">\n <div class=\"ax-flex ax-flex-col ax-gap-3 ax-w-full ax-sorted-list ax-max-h-[calc(100vh-280px)] ax-overflow-auto\">\n @for (item of sortDefinitions(); track item.name) {\n <div class=\"ax-flex ax-py-1 ax-items-center ax-justify-between\" cdkDrag cdkDragBoundary=\".ax-sorted-list\">\n <div class=\"ax-flex ax-items-center ax-gap-3\" cdkDragHandle>\n <ax-icon class=\"fa-solid fa-grip-dots-vertical ax-cursor-move\"></ax-icon>\n <p class=\"ax-font-medium ax-text-sm\">{{ item.title | translate | async }}</p>\n </div>\n <ax-button [color]=\"'blank'\" class=\"ax-sm\" (click)=\"changeItemSort(item)\">\n <ax-icon\n [class.ax-text-primary]=\"getSortDirection(item) === 'asc'\"\n class=\"fa-solid fa-arrow-up-long ax-text-neutral-400\"\n ></ax-icon>\n <ax-icon\n [class.ax-text-primary]=\"getSortDirection(item) === 'desc'\"\n class=\"fa-solid fa-arrow-down-long ax-text-neutral-400\"\n ></ax-icon>\n </ax-button>\n </div>\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: AXTabsModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
4239
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPSpreadsheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4240
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPSpreadsheetComponent, isStandalone: true, selector: "axp-spreadsheet", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rowMode: { classPropertyName: "rowMode", publicName: "rowMode", isSignal: true, isRequired: false, transformFunction: null }, rowsInput: { classPropertyName: "rowsInput", publicName: "rowsInput", isSignal: true, isRequired: false, transformFunction: null }, rowsModel: { classPropertyName: "rowsModel", publicName: "rowsModel", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, emptyCellPlaceholder: { classPropertyName: "emptyCellPlaceholder", publicName: "emptyCellPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, rowTitlePath: { classPropertyName: "rowTitlePath", publicName: "rowTitlePath", isSignal: true, isRequired: false, transformFunction: null }, rowDescriptionPath: { classPropertyName: "rowDescriptionPath", publicName: "rowDescriptionPath", isSignal: true, isRequired: false, transformFunction: null }, allowAddRows: { classPropertyName: "allowAddRows", publicName: "allowAddRows", isSignal: true, isRequired: false, transformFunction: null }, allowRemoveRows: { classPropertyName: "allowRemoveRows", publicName: "allowRemoveRows", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowsModel: "rowsModelChange", cellChange: "cellChange", rowChange: "rowChange", spreadsheetChange: "spreadsheetChange" }, host: { listeners: { "document:keydown": "handleKeyDown($event)" }, classAttribute: "axp-spreadsheet" }, ngImport: i0, template: "<!--\n #region ---- Spreadsheet Component Template ----\n-->\n<div class=\"__spreadsheet-container\" (click)=\"handleClickOutside($event)\">\n @if (isColumnsEmpty()) {\n <!-- Empty State: Columns are empty (regardless of rows) -->\n <div class=\"__empty-state\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-table-columns\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty-columns.title' | translate | async }}</div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty-columns.description' | translate | async }}\n </div>\n </div>\n } @else if (isEmpty()) {\n <!-- Empty State: Both columns and rows are empty -->\n <div class=\"__empty-state\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-table\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty.title' | translate | async }}</div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty.description' | translate | async }}</div>\n </div>\n } @else if (isRowsEmpty()) {\n <!-- Empty State: Only rows are empty - Show columns -->\n <div class=\"__table-wrapper\">\n <table class=\"__table\">\n <!-- Header Row -->\n <thead class=\"__thead\">\n <tr class=\"__header-row\">\n <!-- Corner cell (title) -->\n <th class=\"__corner-cell\">\n @if (title()) {\n <div class=\"__corner-title\">{{ title() | translate | async }}</div>\n }\n @if (rowMode() === 'dynamic' && allowAddRows() && !readonly()) {\n <div class=\"__corner-actions\">\n <ax-button look=\"blank\" color=\"primary\" (onClick)=\"handleAddRow()\"\n [attr.aria-label]=\"'Add Row'\">\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </th>\n\n <!-- Column headers -->\n @for (column of columns(); track column.name) {\n <th class=\"__column-header\" [style.width]=\"column.width\">\n <div class=\"__header-content\">\n <div class=\"__header-title\">{{ column.title | translate | async }}</div>\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Empty State Row -->\n <tbody class=\"__tbody\">\n <tr class=\"__row __row--empty\">\n <td class=\"__row-header\" [attr.colspan]=\"columns().length + 1\">\n <div class=\"__empty-state-inline\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-list\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty-rows.title' | translate | async\n }}\n </div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty-rows.description' |\n translate |\n async }}</div>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n } @else {\n <!-- Full Spreadsheet: Both columns and rows are available -->\n <div class=\"__table-wrapper\">\n <table class=\"__table\">\n <!-- Header Row -->\n <thead class=\"__thead\">\n <tr class=\"__header-row\">\n <!-- Corner cell (title) -->\n <th class=\"__corner-cell\">\n @if (title()) {\n <div class=\"__corner-title\">{{ title() | translate | async }}</div>\n }\n @if (rowMode() === 'dynamic' && allowAddRows() && !readonly()) {\n <div class=\"__corner-actions\">\n <ax-button look=\"blank\" color=\"primary\" (onClick)=\"handleAddRow()\"\n [attr.aria-label]=\"'Add Row'\">\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </th>\n\n <!-- Column headers -->\n @for (column of columns(); track column.name) {\n <th class=\"__column-header\" [style.width]=\"column.width\">\n <div class=\"__header-content\">\n <div class=\"__header-title\">{{ column.title | translate | async }}</div>\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Body Rows -->\n <tbody class=\"__tbody\">\n <axp-widgets-container [context]=\"spreadsheetContext()\"\n (onContextChanged)=\"handleSpreadsheetContextChange($event)\">\n @for (rowItem of rowsWithIds(); track rowItem.rowId; let rowIndex = $index) {\n <tr class=\"__row\" [class.__row--odd]=\"rowIndex % 2 === 1\">\n <!-- Row header -->\n <td class=\"__row-header\">\n <div class=\"__header-content\">\n @if (rowTitlePath()) {\n <div class=\"__header-title\">{{ getRowTitle(rowItem.row, rowTitlePath()!) }}</div>\n }\n @if (rowDescriptionPath()) {\n <div class=\"__header-description\">{{ getRowDescription(rowItem.row,\n rowDescriptionPath()!) }}</div>\n }\n </div>\n @if (allowRemoveRows() && !readonly()) {\n <div class=\"__header-actions ax-xs ax-opacity-30 hover:ax-opacity-100\">\n <ax-button look=\"blank\" color=\"danger\"\n (onClick)=\"handleRemoveRow(rowItem.rowId, $event)\" [attr.aria-label]=\"'Remove Row'\">\n <ax-icon icon=\"far fa-times\"></ax-icon>\n </ax-button>\n </div>\n }\n </td>\n\n <!-- Spreadsheet cells -->\n @for (column of columns(); track column.name) {\n <td class=\"__cell\" [class.__cell--editing]=\"isCellEditing(rowItem.rowId, column.name)\"\n [class.__cell--readonly]=\"isColumnReadonly(column)\"\n (click)=\"$event.stopPropagation(); handleCellClick(rowItem.rowId, column.name, $event)\">\n @if (isCellEditing(rowItem.rowId, column.name) && !isColumnReadonly(column)) {\n <!-- Edit mode: Show widget in edit mode -->\n <div class=\"__cell-editor\" (blur)=\"handleCellBlur(rowItem.rowId, column.name)\"\n tabindex=\"-1\">\n <ng-container axp-widget-renderer [node]=\"getCellWidgetNode(column, rowIndex)\"\n [mode]=\"'edit'\"></ng-container>\n </div>\n <!-- Floating toolbar for commit/cancel -->\n <div class=\"__cell-toolbar ax-xs\" (click)=\"$event.stopPropagation()\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\" (focusin)=\"$event.stopPropagation()\">\n <ax-button look=\"blank\" color=\"success\" (onClick)=\"commitCellEdit()\"\n [attr.aria-label]=\"'Commit'\">\n <ax-icon icon=\"far fa-check\"></ax-icon>\n </ax-button>\n <ax-button look=\"blank\" color=\"danger\" (onClick)=\"cancelCellEdit()\"\n [attr.aria-label]=\"'Cancel'\">\n <ax-icon icon=\"far fa-times\"></ax-icon>\n </ax-button>\n </div>\n } @else {\n <!-- View mode: Show widget in view mode or placeholder -->\n <div class=\"__cell-value\">\n @if (getCellValue(rowIndex, column.name) !== null &&\n getCellValue(rowIndex, column.name) !== undefined) {\n <ng-container axp-widget-renderer [node]=\"getCellWidgetNode(column, rowIndex)\"\n [mode]=\"'view'\"></ng-container>\n } @else {\n <span class=\"__cell-placeholder\">{{ emptyCellPlaceholder() }}</span>\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </axp-widgets-container>\n </tbody>\n </table>\n </div>\n }\n</div>\n<!--\n #endregion\n-->", styles: [".axp-spreadsheet{display:flex;height:100%;width:100%;flex-direction:column}.axp-spreadsheet .__spreadsheet-container{position:relative;flex:1 1 0%;overflow:hidden;border-radius:.375rem;border-width:1px;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.axp-spreadsheet .__empty-state{display:flex;min-height:200px;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center}.axp-spreadsheet .__empty-state .__empty-icon{margin-bottom:1rem}.axp-spreadsheet .__empty-state .__empty-icon i{font-size:2.25rem;line-height:2.5rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state .__empty-title{margin-bottom:.5rem;font-size:1.125rem;line-height:1.75rem;font-weight:600;--tw-text-opacity: 1;color:rgb(38 38 38 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state .__empty-description{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:1.5rem;text-align:center}.axp-spreadsheet .__empty-state-inline .__empty-icon{margin-bottom:.75rem}.axp-spreadsheet .__empty-state-inline .__empty-icon i{font-size:1.5rem;line-height:2rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline .__empty-title{margin-bottom:.25rem;font-size:1rem;line-height:1.5rem;font-weight:600;--tw-text-opacity: 1;color:rgb(38 38 38 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline .__empty-description{font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__table-wrapper{height:100%;width:100%;overflow-x:auto;overflow-y:auto;-webkit-overflow-scrolling:touch}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar{height:8px;width:8px}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-thumb{border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgb(163 163 163 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(115 115 115 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table{min-width:100%;border-collapse:separate;--tw-border-spacing-x: 0px;--tw-border-spacing-y: 0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.axp-spreadsheet .__table .__thead{position:sticky;top:0;z-index:2}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell{position:relative;position:sticky;inset-inline-start:0px;width:200px;min-width:200px;border-width:1px;border-bottom-width:1px;padding:.5rem .75rem;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:3}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell .__corner-title{text-align:center;font-size:.875rem;line-height:1.25rem;font-weight:600}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell .__corner-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}@media (max-width: 768px){.axp-spreadsheet .__table .__thead .__header-row .__corner-cell{width:200px;min-width:200px;padding:.375rem .5rem}}.axp-spreadsheet .__table .__thead .__header-row .__column-header{position:sticky;top:0;width:200px;min-width:200px;white-space:nowrap;border-bottom-width:1px;padding:.5rem .75rem;text-align:center;font-weight:600;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:2}.axp-spreadsheet .__table .__thead .__header-row .__column-header.__column-header--empty{width:300px;min-width:300px;white-space:normal}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content{display:flex;flex-direction:column;gap:.25rem}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-title{font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-description{white-space:normal;overflow-wrap:break-word;font-size:.75rem;line-height:1rem;font-weight:400;opacity:.75}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}@media (max-width: 768px){.axp-spreadsheet .__table .__thead .__header-row .__column-header{width:120px;min-width:120px;padding:.375rem .5rem}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-title,.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-description{font-size:.75rem;line-height:1rem}}.axp-spreadsheet .__table .__tbody .__row.__row--empty .__row-header{text-align:center}.axp-spreadsheet .__table .__tbody .__row .__row-header{position:relative;position:sticky;inset-inline-start:0px;width:200px;min-width:200px;border-width:1px;border-bottom-width:1px;padding:.5rem .75rem;vertical-align:top;font-weight:600;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:2}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content{display:flex;flex-direction:column;gap:.25rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-title{font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-description{font-size:.75rem;line-height:1rem;font-weight:400;opacity:.75}@media (max-width: 768px){.axp-spreadsheet .__table .__tbody .__row .__row-header{width:200px;min-width:200px;padding:.375rem .5rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-title,.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-description{font-size:.75rem;line-height:1rem}}.axp-spreadsheet .__table .__tbody .__row .__cell{position:relative;min-height:40px;width:200px;min-width:200px;cursor:pointer;border-bottom-width:1px;padding:.5rem .75rem;text-align:center;vertical-align:middle}.axp-spreadsheet .__table .__tbody .__row .__cell:before{content:\"\";pointer-events:none;position:absolute;inset:0;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-bg-opacity: .05;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation-duration:.15s;animation-timing-function:cubic-bezier(.4,0,.2,1)}.axp-spreadsheet .__table .__tbody .__row .__cell:hover:not(.__cell--readonly):not(.__cell--editing):before{opacity:1}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--readonly{cursor:default}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--readonly:hover:before{opacity:0}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--empty{cursor:default}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--empty:hover:before{opacity:0}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing{position:relative;border-width:2px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-primary-500),var(--tw-border-opacity, 1));background-color:rgba(var(--ax-sys-color-primary-500),var(--tw-bg-opacity, 1));--tw-bg-opacity: .1;padding:1px}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing .__cell-editor{width:100%;padding:.5rem .75rem}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing .__cell-toolbar{position:absolute;bottom:-35px;inset-inline-end:0px;display:flex;gap:.125rem;white-space:nowrap;border-radius:.375rem;border-width:1px;padding:.25rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:100}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-value{display:flex;min-height:1.25rem;align-items:center;justify-content:center;overflow-wrap:break-word;padding:.125rem;font-size:.875rem;line-height:1.25rem}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-placeholder{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-editor{display:flex;width:100%;align-items:center;justify-content:center}@media (max-width: 768px){.axp-spreadsheet .__table .__tbody .__row .__cell{width:120px;min-width:120px;padding:.375rem .5rem}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-value{font-size:.75rem;line-height:1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i6$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i6$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
4515
4241
  }
4516
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPQuerySortsComponent, decorators: [{
4242
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPSpreadsheetComponent, decorators: [{
4517
4243
  type: Component,
4518
- args: [{ selector: 'axp-query-sorts', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
4519
- CdkDropList,
4520
- CdkDrag,
4521
- CdkDragHandle,
4522
- AXTabsModule,
4523
- AXTranslationModule,
4244
+ args: [{ selector: 'axp-spreadsheet', standalone: true, imports: [
4524
4245
  CommonModule,
4525
- AXDecoratorModule,
4246
+ FormsModule,
4247
+ AXTranslationModule,
4526
4248
  AXButtonModule,
4527
- ], template: "<div class=\"ax-flex ax-flex-col ax-justify-center ax-gap-4 ax-select-none\">\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-select-none\" cdkDropList (cdkDropListDropped)=\"drop($event)\">\n <div class=\"ax-flex ax-flex-col ax-gap-3 ax-w-full ax-sorted-list ax-max-h-[calc(100vh-280px)] ax-overflow-auto\">\n @for (item of sortDefinitions(); track item.name) {\n <div class=\"ax-flex ax-py-1 ax-items-center ax-justify-between\" cdkDrag cdkDragBoundary=\".ax-sorted-list\">\n <div class=\"ax-flex ax-items-center ax-gap-3\" cdkDragHandle>\n <ax-icon class=\"fa-solid fa-grip-dots-vertical ax-cursor-move\"></ax-icon>\n <p class=\"ax-font-medium ax-text-sm\">{{ item.title | translate | async }}</p>\n </div>\n <ax-button [color]=\"'blank'\" class=\"ax-sm\" (click)=\"changeItemSort(item)\">\n <ax-icon\n [class.ax-text-primary]=\"getSortDirection(item) === 'asc'\"\n class=\"fa-solid fa-arrow-up-long ax-text-neutral-400\"\n ></ax-icon>\n <ax-icon\n [class.ax-text-primary]=\"getSortDirection(item) === 'desc'\"\n class=\"fa-solid fa-arrow-down-long ax-text-neutral-400\"\n ></ax-icon>\n </ax-button>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
4528
- }], ctorParameters: () => [], propDecorators: { sortDefinitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortDefinitions", required: false }] }, { type: i0.Output, args: ["sortDefinitionsChange"] }], initialSortQueries: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSortQueries", required: false }] }], sortQueriesChange: [{ type: i0.Output, args: ["sortQueriesChange"] }] } });
4249
+ AXDecoratorModule,
4250
+ AXPWidgetCoreModule,
4251
+ ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
4252
+ class: 'axp-spreadsheet',
4253
+ }, template: "<!--\n #region ---- Spreadsheet Component Template ----\n-->\n<div class=\"__spreadsheet-container\" (click)=\"handleClickOutside($event)\">\n @if (isColumnsEmpty()) {\n <!-- Empty State: Columns are empty (regardless of rows) -->\n <div class=\"__empty-state\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-table-columns\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty-columns.title' | translate | async }}</div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty-columns.description' | translate | async }}\n </div>\n </div>\n } @else if (isEmpty()) {\n <!-- Empty State: Both columns and rows are empty -->\n <div class=\"__empty-state\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-table\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty.title' | translate | async }}</div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty.description' | translate | async }}</div>\n </div>\n } @else if (isRowsEmpty()) {\n <!-- Empty State: Only rows are empty - Show columns -->\n <div class=\"__table-wrapper\">\n <table class=\"__table\">\n <!-- Header Row -->\n <thead class=\"__thead\">\n <tr class=\"__header-row\">\n <!-- Corner cell (title) -->\n <th class=\"__corner-cell\">\n @if (title()) {\n <div class=\"__corner-title\">{{ title() | translate | async }}</div>\n }\n @if (rowMode() === 'dynamic' && allowAddRows() && !readonly()) {\n <div class=\"__corner-actions\">\n <ax-button look=\"blank\" color=\"primary\" (onClick)=\"handleAddRow()\"\n [attr.aria-label]=\"'Add Row'\">\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </th>\n\n <!-- Column headers -->\n @for (column of columns(); track column.name) {\n <th class=\"__column-header\" [style.width]=\"column.width\">\n <div class=\"__header-content\">\n <div class=\"__header-title\">{{ column.title | translate | async }}</div>\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Empty State Row -->\n <tbody class=\"__tbody\">\n <tr class=\"__row __row--empty\">\n <td class=\"__row-header\" [attr.colspan]=\"columns().length + 1\">\n <div class=\"__empty-state-inline\">\n <div class=\"__empty-icon\">\n <i class=\"fa-light fa-list\"></i>\n </div>\n <div class=\"__empty-title\">{{ '@general:state-message.empty-rows.title' | translate | async\n }}\n </div>\n <div class=\"__empty-description\">{{ '@general:state-message.empty-rows.description' |\n translate |\n async }}</div>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n } @else {\n <!-- Full Spreadsheet: Both columns and rows are available -->\n <div class=\"__table-wrapper\">\n <table class=\"__table\">\n <!-- Header Row -->\n <thead class=\"__thead\">\n <tr class=\"__header-row\">\n <!-- Corner cell (title) -->\n <th class=\"__corner-cell\">\n @if (title()) {\n <div class=\"__corner-title\">{{ title() | translate | async }}</div>\n }\n @if (rowMode() === 'dynamic' && allowAddRows() && !readonly()) {\n <div class=\"__corner-actions\">\n <ax-button look=\"blank\" color=\"primary\" (onClick)=\"handleAddRow()\"\n [attr.aria-label]=\"'Add Row'\">\n <ax-icon icon=\"far fa-plus\"></ax-icon>\n </ax-button>\n </div>\n }\n </th>\n\n <!-- Column headers -->\n @for (column of columns(); track column.name) {\n <th class=\"__column-header\" [style.width]=\"column.width\">\n <div class=\"__header-content\">\n <div class=\"__header-title\">{{ column.title | translate | async }}</div>\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Body Rows -->\n <tbody class=\"__tbody\">\n <axp-widgets-container [context]=\"spreadsheetContext()\"\n (onContextChanged)=\"handleSpreadsheetContextChange($event)\">\n @for (rowItem of rowsWithIds(); track rowItem.rowId; let rowIndex = $index) {\n <tr class=\"__row\" [class.__row--odd]=\"rowIndex % 2 === 1\">\n <!-- Row header -->\n <td class=\"__row-header\">\n <div class=\"__header-content\">\n @if (rowTitlePath()) {\n <div class=\"__header-title\">{{ getRowTitle(rowItem.row, rowTitlePath()!) }}</div>\n }\n @if (rowDescriptionPath()) {\n <div class=\"__header-description\">{{ getRowDescription(rowItem.row,\n rowDescriptionPath()!) }}</div>\n }\n </div>\n @if (allowRemoveRows() && !readonly()) {\n <div class=\"__header-actions ax-xs ax-opacity-30 hover:ax-opacity-100\">\n <ax-button look=\"blank\" color=\"danger\"\n (onClick)=\"handleRemoveRow(rowItem.rowId, $event)\" [attr.aria-label]=\"'Remove Row'\">\n <ax-icon icon=\"far fa-times\"></ax-icon>\n </ax-button>\n </div>\n }\n </td>\n\n <!-- Spreadsheet cells -->\n @for (column of columns(); track column.name) {\n <td class=\"__cell\" [class.__cell--editing]=\"isCellEditing(rowItem.rowId, column.name)\"\n [class.__cell--readonly]=\"isColumnReadonly(column)\"\n (click)=\"$event.stopPropagation(); handleCellClick(rowItem.rowId, column.name, $event)\">\n @if (isCellEditing(rowItem.rowId, column.name) && !isColumnReadonly(column)) {\n <!-- Edit mode: Show widget in edit mode -->\n <div class=\"__cell-editor\" (blur)=\"handleCellBlur(rowItem.rowId, column.name)\"\n tabindex=\"-1\">\n <ng-container axp-widget-renderer [node]=\"getCellWidgetNode(column, rowIndex)\"\n [mode]=\"'edit'\"></ng-container>\n </div>\n <!-- Floating toolbar for commit/cancel -->\n <div class=\"__cell-toolbar ax-xs\" (click)=\"$event.stopPropagation()\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (mouseup)=\"$event.stopPropagation()\" (focusin)=\"$event.stopPropagation()\">\n <ax-button look=\"blank\" color=\"success\" (onClick)=\"commitCellEdit()\"\n [attr.aria-label]=\"'Commit'\">\n <ax-icon icon=\"far fa-check\"></ax-icon>\n </ax-button>\n <ax-button look=\"blank\" color=\"danger\" (onClick)=\"cancelCellEdit()\"\n [attr.aria-label]=\"'Cancel'\">\n <ax-icon icon=\"far fa-times\"></ax-icon>\n </ax-button>\n </div>\n } @else {\n <!-- View mode: Show widget in view mode or placeholder -->\n <div class=\"__cell-value\">\n @if (getCellValue(rowIndex, column.name) !== null &&\n getCellValue(rowIndex, column.name) !== undefined) {\n <ng-container axp-widget-renderer [node]=\"getCellWidgetNode(column, rowIndex)\"\n [mode]=\"'view'\"></ng-container>\n } @else {\n <span class=\"__cell-placeholder\">{{ emptyCellPlaceholder() }}</span>\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </axp-widgets-container>\n </tbody>\n </table>\n </div>\n }\n</div>\n<!--\n #endregion\n-->", styles: [".axp-spreadsheet{display:flex;height:100%;width:100%;flex-direction:column}.axp-spreadsheet .__spreadsheet-container{position:relative;flex:1 1 0%;overflow:hidden;border-radius:.375rem;border-width:1px;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.axp-spreadsheet .__empty-state{display:flex;min-height:200px;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center}.axp-spreadsheet .__empty-state .__empty-icon{margin-bottom:1rem}.axp-spreadsheet .__empty-state .__empty-icon i{font-size:2.25rem;line-height:2.5rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state .__empty-title{margin-bottom:.5rem;font-size:1.125rem;line-height:1.75rem;font-weight:600;--tw-text-opacity: 1;color:rgb(38 38 38 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state .__empty-description{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:1.5rem;text-align:center}.axp-spreadsheet .__empty-state-inline .__empty-icon{margin-bottom:.75rem}.axp-spreadsheet .__empty-state-inline .__empty-icon i{font-size:1.5rem;line-height:2rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline .__empty-title{margin-bottom:.25rem;font-size:1rem;line-height:1.5rem;font-weight:600;--tw-text-opacity: 1;color:rgb(38 38 38 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__empty-state-inline .__empty-description{font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__table-wrapper{height:100%;width:100%;overflow-x:auto;overflow-y:auto;-webkit-overflow-scrolling:touch}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar{height:8px;width:8px}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-thumb{border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgb(163 163 163 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table-wrapper::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(115 115 115 / var(--tw-bg-opacity, 1))}.axp-spreadsheet .__table{min-width:100%;border-collapse:separate;--tw-border-spacing-x: 0px;--tw-border-spacing-y: 0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.axp-spreadsheet .__table .__thead{position:sticky;top:0;z-index:2}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell{position:relative;position:sticky;inset-inline-start:0px;width:200px;min-width:200px;border-width:1px;border-bottom-width:1px;padding:.5rem .75rem;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:3}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell .__corner-title{text-align:center;font-size:.875rem;line-height:1.25rem;font-weight:600}.axp-spreadsheet .__table .__thead .__header-row .__corner-cell .__corner-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}@media (max-width: 768px){.axp-spreadsheet .__table .__thead .__header-row .__corner-cell{width:200px;min-width:200px;padding:.375rem .5rem}}.axp-spreadsheet .__table .__thead .__header-row .__column-header{position:sticky;top:0;width:200px;min-width:200px;white-space:nowrap;border-bottom-width:1px;padding:.5rem .75rem;text-align:center;font-weight:600;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:2}.axp-spreadsheet .__table .__thead .__header-row .__column-header.__column-header--empty{width:300px;min-width:300px;white-space:normal}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content{display:flex;flex-direction:column;gap:.25rem}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-title{font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-description{white-space:normal;overflow-wrap:break-word;font-size:.75rem;line-height:1rem;font-weight:400;opacity:.75}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}@media (max-width: 768px){.axp-spreadsheet .__table .__thead .__header-row .__column-header{width:120px;min-width:120px;padding:.375rem .5rem}.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-title,.axp-spreadsheet .__table .__thead .__header-row .__column-header .__header-content .__header-description{font-size:.75rem;line-height:1rem}}.axp-spreadsheet .__table .__tbody .__row.__row--empty .__row-header{text-align:center}.axp-spreadsheet .__table .__tbody .__row .__row-header{position:relative;position:sticky;inset-inline-start:0px;width:200px;min-width:200px;border-width:1px;border-bottom-width:1px;padding:.5rem .75rem;vertical-align:top;font-weight:600;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:2}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-actions{position:absolute;top:.25rem;inset-inline-end:.25rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content{display:flex;flex-direction:column;gap:.25rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-title{font-size:.875rem;line-height:1.25rem;font-weight:500}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-description{font-size:.75rem;line-height:1rem;font-weight:400;opacity:.75}@media (max-width: 768px){.axp-spreadsheet .__table .__tbody .__row .__row-header{width:200px;min-width:200px;padding:.375rem .5rem}.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-title,.axp-spreadsheet .__table .__tbody .__row .__row-header .__header-content .__header-description{font-size:.75rem;line-height:1rem}}.axp-spreadsheet .__table .__tbody .__row .__cell{position:relative;min-height:40px;width:200px;min-width:200px;cursor:pointer;border-bottom-width:1px;padding:.5rem .75rem;text-align:center;vertical-align:middle}.axp-spreadsheet .__table .__tbody .__row .__cell:before{content:\"\";pointer-events:none;position:absolute;inset:0;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-bg-opacity: .05;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation-duration:.15s;animation-timing-function:cubic-bezier(.4,0,.2,1)}.axp-spreadsheet .__table .__tbody .__row .__cell:hover:not(.__cell--readonly):not(.__cell--editing):before{opacity:1}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--readonly{cursor:default}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--readonly:hover:before{opacity:0}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--empty{cursor:default}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--empty:hover:before{opacity:0}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing{position:relative;border-width:2px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-primary-500),var(--tw-border-opacity, 1));background-color:rgba(var(--ax-sys-color-primary-500),var(--tw-bg-opacity, 1));--tw-bg-opacity: .1;padding:1px}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing .__cell-editor{width:100%;padding:.5rem .75rem}.axp-spreadsheet .__table .__tbody .__row .__cell.__cell--editing .__cell-toolbar{position:absolute;bottom:-35px;inset-inline-end:0px;display:flex;gap:.125rem;white-space:nowrap;border-radius:.375rem;border-width:1px;padding:.25rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));z-index:100}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-value{display:flex;min-height:1.25rem;align-items:center;justify-content:center;overflow-wrap:break-word;padding:.125rem;font-size:.875rem;line-height:1.25rem}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-placeholder{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity, 1))}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-editor{display:flex;width:100%;align-items:center;justify-content:center}@media (max-width: 768px){.axp-spreadsheet .__table .__tbody .__row .__cell{width:120px;min-width:120px;padding:.375rem .5rem}.axp-spreadsheet .__table .__tbody .__row .__cell .__cell-value{font-size:.75rem;line-height:1rem}}\n"] }]
4254
+ }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rowMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowMode", required: false }] }], rowsInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsInput", required: false }] }], rowsModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsModel", required: false }] }, { type: i0.Output, args: ["rowsModelChange"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], emptyCellPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyCellPlaceholder", required: false }] }], rowTitlePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowTitlePath", required: false }] }], rowDescriptionPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDescriptionPath", required: false }] }], allowAddRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowAddRows", required: false }] }], allowRemoveRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowRemoveRows", required: false }] }], cellChange: [{ type: i0.Output, args: ["cellChange"] }], rowChange: [{ type: i0.Output, args: ["rowChange"] }], spreadsheetChange: [{ type: i0.Output, args: ["spreadsheetChange"] }], handleKeyDown: [{
4255
+ type: HostListener,
4256
+ args: ['document:keydown', ['$event']]
4257
+ }] } });
4258
+
4259
+ //#endregion
4529
4260
 
4530
4261
  //#region ---- Imports ----
4531
4262
  //#endregion
@@ -6282,5 +6013,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
6282
6013
  * Generated bundle index. Do not edit.
6283
6014
  */
6284
6015
 
6285
- export { AXPActivityLogComponent, AXPCategoryTreeComponent, AXPColorPalettePickerComponent, AXPColumnItemListComponent, AXPCompareViewComponent, AXPComponentSlot, AXPComponentSlotDirective, AXPComponentSlotModule, AXPComponentSlotRegistryService, AXPDataSelectorComponent, AXPDataSelectorService, AXPDragDropListComponent, AXPImageEditorPopupComponent, AXPImageEditorService, AXPLogoComponent, AXPMenuBadgeHelper, AXPMenuCustomizerComponent, AXPMenuCustomizerService, AXPPropertyViewerComponent, AXPPropertyViewerPopupComponent, AXPPropertyViewerService, AXPQueryColumnsComponent, AXPQueryFiltersComponent, AXPQuerySortsComponent, AXPQueryViewsComponent, AXPSpreadsheetComponent, AXPStateMessageComponent, AXPStopwatchComponent, AXPTableColumnsEditorComponent, AXPTableColumnsEditorPopupComponent, AXPTableColumnsEditorService, AXPTableDataEditorComponent, AXPTableDataEditorPopupComponent, AXPTableDataEditorService, AXPTaskBadgeDirective, AXPTaskBadgeProvider, AXPTaskBadgeService, AXPTemplateViewerComponent, AXPTemplateViewerService, AXPThemeLayoutActionsComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutContainerComponent, AXPThemeLayoutEndSideComponent, AXPThemeLayoutFooterComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutListComponent, AXPThemeLayoutListItemComponent, AXPThemeLayoutListItemsGroupComponent, AXPThemeLayoutPageHeaderComponent, AXPThemeLayoutPagePrimaryActionsComponent, AXPThemeLayoutPageSecondaryActionsComponent, AXPThemeLayoutSectionComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutToolbarComponent, AXPUserAvatarComponent, AXPUserAvatarService, AXPWidgetFieldConfiguratorComponent, AXPWidgetItemComponent, AXPWidgetPropertyViewerComponent, AXPWidgetPropertyViewerPopupComponent, AXPWidgetPropertyViewerService, AXP_MENU_CUSTOMIZER_SERVICE, AXP_TASK_BADGE_PROVIDERS, AXP_USER_AVATAR_PROVIDER, buildTableColumnsEditorLayout };
6016
+ export { AXPActivityLogComponent, AXPCategoryTreeComponent, AXPColorPalettePickerComponent, AXPColumnItemListComponent, AXPCompareViewComponent, AXPDataSelectorComponent, AXPDataSelectorService, AXPDragDropListComponent, AXPImageEditorPopupComponent, AXPImageEditorService, AXPLogoComponent, AXPMenuBadgeHelper, AXPMenuCustomizerComponent, AXPMenuCustomizerService, AXPPropertyViewerComponent, AXPPropertyViewerPopupComponent, AXPPropertyViewerService, AXPQueryColumnsComponent, AXPQueryFiltersComponent, AXPQuerySortsComponent, AXPQueryViewsComponent, AXPSpreadsheetComponent, AXPStateMessageComponent, AXPStopwatchComponent, AXPTableColumnsEditorComponent, AXPTableColumnsEditorPopupComponent, AXPTableColumnsEditorService, AXPTableDataEditorComponent, AXPTableDataEditorPopupComponent, AXPTableDataEditorService, AXPTaskBadgeDirective, AXPTaskBadgeProvider, AXPTaskBadgeService, AXPTemplateViewerComponent, AXPTemplateViewerService, AXPThemeLayoutActionsComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutContainerComponent, AXPThemeLayoutEndSideComponent, AXPThemeLayoutFooterComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutListComponent, AXPThemeLayoutListItemComponent, AXPThemeLayoutListItemsGroupComponent, AXPThemeLayoutPageHeaderComponent, AXPThemeLayoutPagePrimaryActionsComponent, AXPThemeLayoutPageSecondaryActionsComponent, AXPThemeLayoutSectionComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutToolbarComponent, AXPUserAvatarComponent, AXPUserAvatarService, AXPWidgetFieldConfiguratorComponent, AXPWidgetItemComponent, AXPWidgetPropertyViewerComponent, AXPWidgetPropertyViewerPopupComponent, AXPWidgetPropertyViewerService, AXP_MENU_CUSTOMIZER_SERVICE, AXP_TASK_BADGE_PROVIDERS, AXP_USER_AVATAR_PROVIDER, buildTableColumnsEditorLayout };
6286
6017
  //# sourceMappingURL=acorex-platform-layout-components.mjs.map