@acorex/platform 21.0.0-next.88 → 21.0.0-next.89

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 (52) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +8 -2
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-VfmZct9H.mjs → acorex-platform-common-common-settings.provider-DilV4dql.mjs} +15 -42
  4. package/fesm2022/acorex-platform-common-common-settings.provider-DilV4dql.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +3299 -3200
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +621 -26
  8. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-layout-builder.mjs +98 -189
  10. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  11. package/fesm2022/{acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs → acorex-platform-layout-components-binding-expression-editor-popup.component-RttFwaw9.mjs} +17 -4
  12. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-RttFwaw9.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-layout-components.mjs +125 -26
  14. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  15. package/fesm2022/acorex-platform-layout-designer.mjs +4 -4
  16. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-layout-entity-contracts.mjs +106 -12
  18. package/fesm2022/acorex-platform-layout-entity-contracts.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-entity.mjs +419 -147
  20. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-views.mjs +851 -162
  22. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  23. package/fesm2022/acorex-platform-layout-widget-core.mjs +378 -103
  24. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  25. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-D4MOHEpI.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-CZc2zFlg.mjs} +2 -2
  26. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-D4MOHEpI.mjs.map → acorex-platform-layout-widgets-tabular-data-edit-popup.component-CZc2zFlg.mjs.map} +1 -1
  27. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-XWRUVoL4.mjs} +2 -2
  28. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs.map → acorex-platform-layout-widgets-tabular-data-view-popup.component-XWRUVoL4.mjs.map} +1 -1
  29. package/fesm2022/acorex-platform-layout-widgets.mjs +575 -239
  30. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  31. package/fesm2022/acorex-platform-themes-default.mjs +221 -24
  32. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  33. package/fesm2022/{acorex-platform-themes-shared-settings.provider-BUwPqwUS.mjs → acorex-platform-themes-shared-settings.provider-UyKdkeyk.mjs} +48 -1
  34. package/fesm2022/acorex-platform-themes-shared-settings.provider-UyKdkeyk.mjs.map +1 -0
  35. package/fesm2022/acorex-platform-themes-shared.mjs +103 -24
  36. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  37. package/package.json +1 -1
  38. package/types/acorex-platform-auth.d.ts +1 -0
  39. package/types/acorex-platform-common.d.ts +202 -151
  40. package/types/acorex-platform-core.d.ts +161 -6
  41. package/types/acorex-platform-layout-builder.d.ts +10 -20
  42. package/types/acorex-platform-layout-components.d.ts +14 -1
  43. package/types/acorex-platform-layout-entity-contracts.d.ts +27 -2
  44. package/types/acorex-platform-layout-entity.d.ts +61 -12
  45. package/types/acorex-platform-layout-views.d.ts +101 -19
  46. package/types/acorex-platform-layout-widget-core.d.ts +65 -9
  47. package/types/acorex-platform-layout-widgets.d.ts +48 -26
  48. package/types/acorex-platform-themes-default.d.ts +56 -5
  49. package/types/acorex-platform-themes-shared.d.ts +4 -0
  50. package/fesm2022/acorex-platform-common-common-settings.provider-VfmZct9H.mjs.map +0 -1
  51. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs.map +0 -1
  52. package/fesm2022/acorex-platform-themes-shared-settings.provider-BUwPqwUS.mjs.map +0 -1
@@ -22,7 +22,7 @@ import { AXTextBoxModule } from '@acorex/components/text-box';
22
22
  import * as i6 from '@acorex/core/translation';
23
23
  import { AXTranslationModule, AXTranslationService, resolveMultiLanguageString } from '@acorex/core/translation';
24
24
  import { AXP_LOCALE_MANAGEMENT_PORT, AXPFilterOperatorMiddlewareService, AXPCleanNestedFilters, AXPClipBoardService, AXPSettingsService, ALL_DEFAULT_OPERATORS, DATE_OPERATORS, BOOLEAN_OPERATORS, NUMBER_OPERATORS, STRING_OPERATORS, resolveDateFilterPresets, findManualPresetIdByOperation, applyDateFilterPreset, AXPFileStorageService, AXPFileActionsService, AXPIntegrationProviderRegistryService, normalizePositiveNumberOption, resolveEntityRowDisplayText, resolveEntityRowDisplayTitle, downloadGraphicFromElement, printGraphicFromElement, AXPStatusDefinitionProviderService, AXPRefreshEvent, AXPCommonSettings } from '@acorex/platform/common';
25
- import { AXPDeviceService, setSmart, AXPColorPaletteService, extractValue, shouldUseLocaleMapShape, buildLocaleTextMapValue, AXPTagService, AXPExpressionEvaluatorService, AXPHookService, AXPDataSourceDefinitionProviderService, sortByMultiLanguageString, applyQueryArray, chordToKbdItemKeys, objectKeyValueTransforms } from '@acorex/platform/core';
25
+ import { AXPDeviceService, setupPopupFooterKeyboardShortcuts, setSmart, AXPColorPaletteService, extractValue, shouldUseLocaleMapShape, buildLocaleTextMapValue, AXPDebugService, FORM_DIRTY_TRACE_NS, isSelectionValueEqual, AXPTagService, AXPExpressionEvaluatorService, AXPHookService, AXPDataSourceDefinitionProviderService, sortByMultiLanguageString, isFormValueEqual, applyQueryArray, chordToKbdItemKeys, objectKeyValueTransforms } from '@acorex/platform/core';
26
26
  import * as i1$4 from '@angular/common';
27
27
  import { CommonModule, AsyncPipe } from '@angular/common';
28
28
  import * as i0 from '@angular/core';
@@ -31,7 +31,7 @@ import * as i1$5 from '@angular/forms';
31
31
  import { FormsModule } from '@angular/forms';
32
32
  import * as i3 from '@acorex/components/select-box';
33
33
  import { AXSelectBoxComponent, AXSelectBoxModule } from '@acorex/components/select-box';
34
- import { set, get, castArray, isNil, isNull, isEmpty, isNumber, defaultTo, isEqual, cloneDeep } from 'lodash-es';
34
+ import { set, get, castArray, isNil, isNull, isEmpty, first, isNumber, defaultTo, has, isEqual, cloneDeep } from 'lodash-es';
35
35
  import * as i2$2 from '@acorex/components/loading';
36
36
  import { AXLoadingModule } from '@acorex/components/loading';
37
37
  import { AXPCommandService, AXPQueryService, provideQuerySetups } from '@acorex/platform/runtime';
@@ -73,7 +73,7 @@ import * as i2$5 from '@acorex/components/wysiwyg';
73
73
  import { AXWysiwygModule } from '@acorex/components/wysiwyg';
74
74
  import * as i5$1 from '@acorex/components/search-box';
75
75
  import { AXSearchBoxModule } from '@acorex/components/search-box';
76
- import { first } from 'rxjs';
76
+ import { first as first$1 } from 'rxjs';
77
77
  import * as i2$6 from '@acorex/components/autocomplete';
78
78
  import { AXAutocompleteModule } from '@acorex/components/autocomplete';
79
79
  import * as i1$9 from '@acorex/components/tag-box';
@@ -165,6 +165,18 @@ class AXPSelectLanguagePopup extends AXBasePageComponent {
165
165
  }
166
166
  return list.filter((l) => l.isLocaleProfile || this.hasTranslationContent(l.context?.value));
167
167
  }, ...(ngDevMode ? [{ debugName: "visibleLanguageList" }] : /* istanbul ignore next */ []));
168
+ setupPopupFooterKeyboardShortcuts(() => [
169
+ {
170
+ title: '@general:actions.cancel.title',
171
+ command: 'cancel',
172
+ handler: () => this.close(),
173
+ },
174
+ {
175
+ title: '@general:actions.save.title',
176
+ command: 'submit',
177
+ handler: () => this.onSaveButtonClick(),
178
+ },
179
+ ], { id: 'select-language-popup' });
168
180
  }
169
181
  //#region ---- Lifecycle ----
170
182
  ngOnInit() {
@@ -3171,42 +3183,80 @@ class AXPAddressWidgetService {
3171
3183
  */
3172
3184
  buildAddressFormContent(flex, options) {
3173
3185
  const { labels = [], hasMap, addressData } = options;
3174
- flex
3175
- .mode('edit')
3176
- .setDirection('column')
3177
- .setGap('16px');
3178
- if (labels.length > 0) {
3179
- flex.formField('@location-management:locations.entities.location.fields.label', (field) => {
3180
- field.path('label');
3186
+ const hasLabel = labels.length > 0;
3187
+ flex.mode('edit').setDirection('column').setGap('12px');
3188
+ //#region ---- Row 1: label + show map ----
3189
+ if (hasLabel || hasMap) {
3190
+ flex.grid((metaRow) => {
3191
+ metaRow.setColumns(12).setGap('12px');
3192
+ if (hasLabel) {
3193
+ const defaultLabelId = this.resolveDefaultLabelId(labels);
3194
+ metaRow.formField('@location-management:locations.entities.location.fields.label', (field) => {
3195
+ field.path('label');
3196
+ field.layout(3);
3197
+ if (defaultLabelId) {
3198
+ field.defaultValue(defaultLabelId);
3199
+ }
3200
+ field.selectBox({
3201
+ valueField: 'id',
3202
+ textField: 'name',
3203
+ dataSource: labels,
3204
+ validations: [{ rule: 'required', message: '@general:validation.messages.required' }],
3205
+ });
3206
+ });
3207
+ }
3208
+ if (hasMap) {
3209
+ metaRow.formField('@location-management:locations.entities.location.fields.show-map', (field) => {
3210
+ field.path('showMap');
3211
+ field.layout(3);
3212
+ field.toggleSwitch({
3213
+ defaultValue: !!(addressData?.coordinates),
3214
+ });
3215
+ });
3216
+ }
3217
+ });
3218
+ }
3219
+ //#endregion
3220
+ //#region ---- Row 2: street address + address line 2 ----
3221
+ flex.grid((streetRow) => {
3222
+ streetRow.setColumns(12).setGap('12px');
3223
+ streetRow
3224
+ .formField('@location-management:locations.entities.location.fields.street1', (field) => {
3225
+ field.path('street1');
3181
3226
  field.layout(6);
3182
- field.selectBox({
3183
- valueField: 'id',
3184
- textField: 'name',
3185
- dataSource: labels,
3227
+ field.textBox({
3228
+ validations: [{ rule: 'required', message: '@general:validation.messages.required' }],
3186
3229
  });
3230
+ })
3231
+ .formField('@location-management:locations.entities.location.fields.street2', (field) => {
3232
+ field.path('street2');
3233
+ field.layout(6);
3234
+ field.textBox({});
3187
3235
  });
3188
- }
3189
- flex
3190
- .formField('@location-management:locations.entities.location.fields.street1', (field) => {
3191
- field.path('street1');
3192
- field.textBox({});
3193
- })
3194
- .formField('@location-management:locations.entities.location.fields.street2', (field) => {
3195
- field.path('street2');
3196
- field.textBox({});
3197
- })
3198
- .flex((row) => {
3199
- row
3200
- .setDirection('row')
3201
- .setGap('16px')
3236
+ });
3237
+ //#endregion
3238
+ //#region ---- Row 3: postal code + country + state + city ----
3239
+ flex.grid((geoRow) => {
3240
+ geoRow.setColumns(12).setGap('12px');
3241
+ geoRow
3242
+ .formField('@location-management:locations.entities.location.fields.postal-code', (field) => {
3243
+ field.path('postalCode');
3244
+ field.layout(3);
3245
+ field.textBox({ placeholder: 'Enter postal code' });
3246
+ })
3202
3247
  .formField('@location-management:locations.entities.location.fields.country', (field) => {
3203
- field.path('country.id');
3248
+ field.path('countryId');
3249
+ field.layout(3);
3204
3250
  field.customWidget('lookup-editor', {
3205
3251
  entity: 'LocationManagement.GeographicRegion',
3206
3252
  look: 'select',
3207
3253
  textField: 'title',
3208
3254
  valueField: 'id',
3209
- allowClear: true,
3255
+ hasClearButton: true,
3256
+ allowCreate: 'quick',
3257
+ create: {
3258
+ enable: true,
3259
+ },
3210
3260
  expose: [
3211
3261
  { source: 'id', target: 'country.id' },
3212
3262
  { source: 'title', target: 'country.title' },
@@ -3218,12 +3268,23 @@ class AXPAddressWidgetService {
3218
3268
  });
3219
3269
  })
3220
3270
  .formField('@location-management:locations.entities.location.fields.state', (field) => {
3221
- field.path('state.id');
3271
+ field.path('stateId');
3272
+ field.layout(3);
3222
3273
  field.customWidget('lookup-editor', {
3223
3274
  entity: 'LocationManagement.GeographicRegion',
3224
3275
  look: 'select',
3225
3276
  textField: 'title',
3226
3277
  valueField: 'id',
3278
+ hasClearButton: true,
3279
+ allowCreate: 'quick',
3280
+ disabled: '{{ !context.eval("countryId") }}',
3281
+ create: {
3282
+ enable: true,
3283
+ context: {
3284
+ parentId: '{{ context.eval("countryId") }}',
3285
+ type: { code: 'state' },
3286
+ },
3287
+ },
3227
3288
  expose: [
3228
3289
  { source: 'id', target: 'state.id' },
3229
3290
  { source: 'title', target: 'state.title' },
@@ -3231,20 +3292,36 @@ class AXPAddressWidgetService {
3231
3292
  filter: {
3232
3293
  logic: 'and',
3233
3294
  filters: [
3234
- { field: 'parentId', operator: { type: 'equal' }, value: '{{ context.eval("country.id") }}' },
3295
+ { field: 'parentId', operator: { type: 'equal' }, value: '{{ context.eval("countryId") }}' },
3235
3296
  { field: 'type.code', operator: { type: 'equal' }, value: 'state' },
3236
3297
  ],
3237
3298
  },
3238
- triggers: [{ event: "{{ events.context('country.id') }}", action: '{{ widget.refresh() }}' }],
3299
+ triggers: [
3300
+ {
3301
+ event: "{{ events.context('countryId') }}",
3302
+ action: "{{ widget.setValue(null); context.set('state.title', null); context.set('cityId', null); context.set('city.id', null); context.set('city.title', null); widget.refresh(); }}",
3303
+ },
3304
+ ],
3239
3305
  });
3240
3306
  })
3241
3307
  .formField('@location-management:locations.entities.location.fields.city', (field) => {
3242
- field.path('city.id');
3308
+ field.path('cityId');
3309
+ field.layout(3);
3243
3310
  field.customWidget('lookup-editor', {
3244
3311
  entity: 'LocationManagement.GeographicRegion',
3245
3312
  look: 'select',
3246
3313
  textField: 'title',
3247
3314
  valueField: 'id',
3315
+ hasClearButton: true,
3316
+ allowCreate: 'quick',
3317
+ disabled: '{{ !context.eval("stateId") }}',
3318
+ create: {
3319
+ enable: true,
3320
+ context: {
3321
+ parentId: '{{ context.eval("stateId") }}',
3322
+ type: { code: 'city' },
3323
+ },
3324
+ },
3248
3325
  expose: [
3249
3326
  { source: 'id', target: 'city.id' },
3250
3327
  { source: 'title', target: 'city.title' },
@@ -3252,32 +3329,37 @@ class AXPAddressWidgetService {
3252
3329
  filter: {
3253
3330
  logic: 'and',
3254
3331
  filters: [
3255
- { field: 'parentId', operator: { type: 'equal' }, value: '{{ context.eval("state.id") }}' },
3332
+ { field: 'parentId', operator: { type: 'equal' }, value: '{{ context.eval("stateId") }}' },
3256
3333
  { field: 'type.code', operator: { type: 'equal' }, value: 'city' },
3257
3334
  ],
3258
3335
  },
3259
- triggers: [{ event: "{{ events.context('state.id') }}", action: '{{ widget.refresh() }}' }],
3336
+ triggers: [
3337
+ {
3338
+ event: "{{ events.context('stateId') }}",
3339
+ action: "{{ widget.setValue(null); context.set('city.title', null); widget.refresh(); }}",
3340
+ },
3341
+ {
3342
+ event: "{{ events.context('countryId') }}",
3343
+ action: "{{ widget.setValue(null); context.set('city.title', null); widget.refresh(); }}",
3344
+ },
3345
+ ],
3260
3346
  });
3261
3347
  });
3262
- })
3263
- .formField('@location-management:locations.entities.location.fields.postal-code', (field) => {
3264
- field.path('postalCode');
3265
- field.textBox({ placeholder: 'Enter postal code' });
3266
3348
  });
3349
+ //#endregion
3350
+ //#region ---- Map picker (when show map is enabled) ----
3267
3351
  if (hasMap) {
3268
- flex
3269
- .formField('@location-management:locations.entities.location.fields.show-map', (field) => {
3270
- field.path('showMap');
3271
- field.toggleSwitch({
3272
- defaultValue: !!(addressData?.coordinates),
3352
+ flex.grid((mapRow) => {
3353
+ mapRow.setColumns(12).setGap('12px');
3354
+ mapRow.formField('@location-management:locations.entities.location.fields.coordinates', (field) => {
3355
+ field.path('coordinates');
3356
+ field.layout(12);
3357
+ field.visible('{{ context.eval("showMap") }}');
3358
+ field.customWidget('map', { hasDraw: true, maxPolygon: 0 });
3273
3359
  });
3274
- })
3275
- .formField('@location-management:locations.entities.location.fields.coordinates', (field) => {
3276
- field.path('coordinates');
3277
- field.visible('{{ context.eval("showMap") }}');
3278
- field.customWidget('map', { hasDraw: true, maxPolygon: 0 });
3279
3360
  });
3280
3361
  }
3362
+ //#endregion
3281
3363
  }
3282
3364
  /**
3283
3365
  * Returns the address form layout node using layoutBuilder.create().flex().build() for use in axp-layout-renderer.
@@ -3289,6 +3371,45 @@ class AXPAddressWidgetService {
3289
3371
  .build();
3290
3372
  }
3291
3373
  //#endregion
3374
+ //#region ---- Geographic Cascade Methods ----
3375
+ /**
3376
+ * Builds layout context with flat lookup ids (countryId/stateId/cityId) required for cascade triggers.
3377
+ * Same pattern as LocationManagement.Location entity fields.
3378
+ */
3379
+ buildAddressFormContext(addressData, options) {
3380
+ const labels = options?.labels ?? [];
3381
+ const dataWithLabelDefault = labels.length > 0 ? this.applyLabelDefaults(addressData, labels) : addressData;
3382
+ return {
3383
+ ...dataWithLabelDefault,
3384
+ countryId: dataWithLabelDefault.country?.id,
3385
+ stateId: dataWithLabelDefault.state?.id,
3386
+ cityId: dataWithLabelDefault.city?.id,
3387
+ showMap: (options?.hasMap ?? false) && !!dataWithLabelDefault.coordinates,
3388
+ };
3389
+ }
3390
+ /**
3391
+ * Clears dependent geographic fields when a parent selection changes.
3392
+ * Mirrors Location entity cascade: state depends on country, city depends on state.
3393
+ */
3394
+ applyGeographicCascade(previous, next) {
3395
+ const result = { ...next };
3396
+ const previousCountryId = previous.countryId ?? previous.country?.id;
3397
+ const nextCountryId = result.countryId ?? result.country?.id;
3398
+ const previousStateId = previous.stateId ?? previous.state?.id;
3399
+ const nextStateId = result.stateId ?? result.state?.id;
3400
+ if (previousCountryId !== nextCountryId) {
3401
+ result.state = undefined;
3402
+ result.city = undefined;
3403
+ result.stateId = undefined;
3404
+ result.cityId = undefined;
3405
+ }
3406
+ else if (previousStateId !== nextStateId) {
3407
+ result.city = undefined;
3408
+ result.cityId = undefined;
3409
+ }
3410
+ return result;
3411
+ }
3412
+ //#endregion
3292
3413
  //#region ---- Address Formatting Methods ----
3293
3414
  /**
3294
3415
  * Formats address data into full address string
@@ -3326,16 +3447,30 @@ class AXPAddressWidgetService {
3326
3447
  // Explicitly exclude coordinates from formatted address
3327
3448
  return parts.filter(Boolean).join(', ');
3328
3449
  }
3450
+ /**
3451
+ * Collapse header parts: translated label (semibold in template) + full address.
3452
+ */
3453
+ async resolveCollapsibleHeader(value, options) {
3454
+ const full = (value.full?.trim() || this.formatAddress(value)).trim();
3455
+ const addressText = full !== '' ? full : options.placeholder;
3456
+ const labelId = extractValue(value.label);
3457
+ if (!labelId || options.labels.length === 0) {
3458
+ return { labelText: null, addressText };
3459
+ }
3460
+ const entry = options.labels.find((item) => item.id === labelId);
3461
+ if (!entry) {
3462
+ return { labelText: null, addressText };
3463
+ }
3464
+ const labelText = await this.translationService.translateAsync(entry.name);
3465
+ return { labelText, addressText };
3466
+ }
3329
3467
  //#endregion
3330
3468
  //#region ---- Dialog & UI Methods ----
3331
3469
  /**
3332
3470
  * Shows address details popup dialog using the shared address form layout.
3333
3471
  */
3334
3472
  async showAddressDetailsPopup(addressData, labels, hasMap = true) {
3335
- const initialContext = {
3336
- ...addressData,
3337
- showMap: hasMap && !!addressData.coordinates,
3338
- };
3473
+ const initialContext = this.buildAddressFormContext(addressData, { hasMap, labels: labels ?? [] });
3339
3474
  const dialogTitle = await this.translationService.translateAsync('@general:terms.common.address');
3340
3475
  const ref = await this.layoutBuilder
3341
3476
  .create()
@@ -3360,11 +3495,32 @@ class AXPAddressWidgetService {
3360
3495
  ref.close();
3361
3496
  return null;
3362
3497
  }
3363
- const result = ref.context();
3498
+ const result = this.applyGeographicCascade(initialContext, this.normalizeAddressData(ref.context()));
3364
3499
  ref.close();
3365
3500
  return result;
3366
3501
  }
3367
3502
  //#endregion
3503
+ //#region ---- Label Defaults ----
3504
+ /** Returns the first configured label id, used as the default selection. */
3505
+ resolveDefaultLabelId(labels) {
3506
+ return first(labels)?.id;
3507
+ }
3508
+ /** Applies the default label when labels are configured and none is set yet. */
3509
+ applyLabelDefaults(value, labels) {
3510
+ if (labels.length === 0) {
3511
+ return value;
3512
+ }
3513
+ const labelId = extractValue(value.label);
3514
+ if (labelId) {
3515
+ return value;
3516
+ }
3517
+ const defaultLabelId = this.resolveDefaultLabelId(labels);
3518
+ if (!defaultLabelId) {
3519
+ return value;
3520
+ }
3521
+ return { ...value, label: defaultLabelId };
3522
+ }
3523
+ //#endregion
3368
3524
  //#region ---- Data Normalization Methods ----
3369
3525
  /**
3370
3526
  * Normalizes address data from various input formats
@@ -3380,7 +3536,7 @@ class AXPAddressWidgetService {
3380
3536
  postalCode: value.postalCode,
3381
3537
  coordinates: value.coordinates,
3382
3538
  full: value.full ?? this.formatAddress(value),
3383
- label: value.label,
3539
+ label: extractValue(value.label) ?? undefined,
3384
3540
  };
3385
3541
  }
3386
3542
  // Simple string format
@@ -3410,10 +3566,46 @@ class AXPAddressWidgetEditComponent extends AXPValueWidgetComponent {
3410
3566
  this.addressService = inject(AXPAddressWidgetService);
3411
3567
  /** Collapsible mode: true = panel collapsed (only full address visible), false = expanded (edit button visible) */
3412
3568
  this.collapsibleCollapsed = signal(true, ...(ngDevMode ? [{ debugName: "collapsibleCollapsed" }] : /* istanbul ignore next */ []));
3413
- this.collapsibleCaption = computed(() => {
3414
- const full = this.formattedAddress();
3415
- return full && full.trim() !== '' ? full : this.placeholder();
3416
- }, ...(ngDevMode ? [{ debugName: "collapsibleCaption" }] : /* istanbul ignore next */ []));
3569
+ this.collapsibleHeaderLabel = signal(null, ...(ngDevMode ? [{ debugName: "collapsibleHeaderLabel" }] : /* istanbul ignore next */ []));
3570
+ this.collapsibleHeaderAddress = signal('', ...(ngDevMode ? [{ debugName: "collapsibleHeaderAddress" }] : /* istanbul ignore next */ []));
3571
+ /** Bumped on parent discard so the nested layout renderer remounts with reverted context. */
3572
+ this.layoutRendererGeneration = signal(0, ...(ngDevMode ? [{ debugName: "layoutRendererGeneration" }] : /* istanbul ignore next */ []));
3573
+ this.parentContextWasRestored = false;
3574
+ this.#resetNestedFormOnDiscard = effect(() => {
3575
+ if (this.addressMode() !== 'expandable') {
3576
+ return;
3577
+ }
3578
+ const isRestored = this.contextService.changeEvent().state === 'restored';
3579
+ if (isRestored && !this.parentContextWasRestored) {
3580
+ this.layoutRendererGeneration.update((generation) => generation + 1);
3581
+ }
3582
+ this.parentContextWasRestored = isRestored;
3583
+ }, ...(ngDevMode ? [{ debugName: "#resetNestedFormOnDiscard" }] : /* istanbul ignore next */ []));
3584
+ this.#ensureDefaultLabelEffect = effect(() => {
3585
+ const labels = this.labels();
3586
+ if (!this.hasLabel() || labels.length === 0) {
3587
+ return;
3588
+ }
3589
+ const value = this.valueAsObject();
3590
+ const labelId = extractValue(value.label);
3591
+ if (labelId) {
3592
+ return;
3593
+ }
3594
+ const defaulted = this.addressService.applyLabelDefaults(value, labels);
3595
+ this.setValue(this.setFormattedValue(defaulted));
3596
+ }, ...(ngDevMode ? [{ debugName: "#ensureDefaultLabelEffect" }] : /* istanbul ignore next */ []));
3597
+ this.#collapsibleHeaderEffect = effect(() => {
3598
+ const value = this.valueAsObject();
3599
+ this.formattedAddress();
3600
+ const labels = this.labels();
3601
+ const placeholder = this.placeholder();
3602
+ void this.addressService
3603
+ .resolveCollapsibleHeader(value, { labels, placeholder })
3604
+ .then((header) => {
3605
+ this.collapsibleHeaderLabel.set(header.labelText);
3606
+ this.collapsibleHeaderAddress.set(header.addressText);
3607
+ });
3608
+ }, ...(ngDevMode ? [{ debugName: "#collapsibleHeaderEffect" }] : /* istanbul ignore next */ []));
3417
3609
  this.addressMode = computed(() => extractValue(this.options()['mode']) || 'simple', ...(ngDevMode ? [{ debugName: "addressMode" }] : /* istanbul ignore next */ []));
3418
3610
  this.placeholder = computed(() => this.options()['placeholder'] || 'Enter address...', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
3419
3611
  this.labels = computed(() => this.options()['labels'] || [], ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
@@ -3438,13 +3630,10 @@ class AXPAddressWidgetEditComponent extends AXPValueWidgetComponent {
3438
3630
  addressData: this.valueAsObject(),
3439
3631
  }), ...(ngDevMode ? [{ debugName: "addressFormLayout" }] : /* istanbul ignore next */ []));
3440
3632
  /** Context for inline address form (expandable); includes showMap for map toggle. */
3441
- this.collapsibleFormContext = computed(() => {
3442
- const value = this.valueAsObject();
3443
- return {
3444
- ...value,
3445
- showMap: this.hasMap() && !!value.coordinates,
3446
- };
3447
- }, ...(ngDevMode ? [{ debugName: "collapsibleFormContext" }] : /* istanbul ignore next */ []));
3633
+ this.collapsibleFormContext = computed(() => this.addressService.buildAddressFormContext(this.valueAsObject(), {
3634
+ hasMap: this.hasMap(),
3635
+ labels: this.hasLabel() ? this.labels() : [],
3636
+ }), ...(ngDevMode ? [{ debugName: "collapsibleFormContext" }] : /* istanbul ignore next */ []));
3448
3637
  this.labelsDataSource = new AXDataSource({
3449
3638
  pageSize: 20,
3450
3639
  load: (e) => {
@@ -3459,10 +3648,18 @@ class AXPAddressWidgetEditComponent extends AXPValueWidgetComponent {
3459
3648
  },
3460
3649
  });
3461
3650
  }
3651
+ #resetNestedFormOnDiscard;
3652
+ #ensureDefaultLabelEffect;
3653
+ #collapsibleHeaderEffect;
3462
3654
  onCollapsibleFormContextChange(ctx) {
3463
3655
  if (ctx == null)
3464
3656
  return;
3465
- const normalized = this.addressService.normalizeAddressData(ctx);
3657
+ const labels = this.hasLabel() ? this.labels() : [];
3658
+ const previous = this.addressService.buildAddressFormContext(this.valueAsObject(), {
3659
+ hasMap: this.hasMap(),
3660
+ labels,
3661
+ });
3662
+ const normalized = this.addressService.normalizeAddressData(this.addressService.applyGeographicCascade(previous, ctx));
3466
3663
  normalized.full = this.addressService.formatAddress(normalized);
3467
3664
  this.setValue(this.setFormattedValue(normalized));
3468
3665
  }
@@ -3540,6 +3737,10 @@ class AXPAddressWidgetEditComponent extends AXPValueWidgetComponent {
3540
3737
  textField="name"
3541
3738
  [ngClass]="generateClasses('label')"
3542
3739
  >
3740
+ <ax-validation-rule
3741
+ rule="required"
3742
+ message="{{ '@general:validation.messages.required' | translate | async }}"
3743
+ ></ax-validation-rule>
3543
3744
  </ax-select-box>
3544
3745
  }
3545
3746
  </div>
@@ -3547,22 +3748,47 @@ class AXPAddressWidgetEditComponent extends AXPValueWidgetComponent {
3547
3748
  <div class="ax-w-full">
3548
3749
  <ax-collapse-group look="fill">
3549
3750
  <ax-collapse
3550
- [caption]="collapsibleCaption()"
3551
- icon="fa-light fa-map-marker-alt"
3751
+ [headerTemplate]="collapseHeaderTemplate"
3552
3752
  [showHeader]="true"
3553
3753
  [isCollapsed]="collapsibleCollapsed()"
3554
3754
  (isCollapsedChange)="collapsibleCollapsed.set($event)"
3555
3755
  >
3556
3756
  <div class="ax-pt-1">
3557
- <axp-layout-renderer
3558
- [layout]="addressFormLayout()"
3559
- [context]="collapsibleFormContext()"
3560
- (contextChange)="onCollapsibleFormContextChange($event)"
3561
- />
3757
+ @for (formKey of [layoutRendererGeneration()]; track formKey) {
3758
+ <axp-layout-renderer
3759
+ [layout]="addressFormLayout()"
3760
+ [context]="collapsibleFormContext()"
3761
+ (contextChange)="onCollapsibleFormContextChange($event)"
3762
+ />
3763
+ }
3562
3764
  </div>
3563
3765
  </ax-collapse>
3564
3766
  </ax-collapse-group>
3565
3767
  </div>
3768
+
3769
+ <ng-template #collapseHeaderTemplate>
3770
+ <div class="ax-collapse-header-container">
3771
+ <div class="ax-collapse-header-start">
3772
+ <span class="ax-collapse-header-icon fa-light fa-map-marker-alt"></span>
3773
+ <span>
3774
+ @if (collapsibleHeaderLabel()) {
3775
+ <span class="ax-font-semibold">{{ collapsibleHeaderLabel() }}</span>
3776
+ @if (collapsibleHeaderAddress()) {
3777
+ <span> · {{ collapsibleHeaderAddress() }}</span>
3778
+ }
3779
+ } @else {
3780
+ {{ collapsibleHeaderAddress() }}
3781
+ }
3782
+ </span>
3783
+ </div>
3784
+ <div class="ax-collapse-header-end">
3785
+ <span
3786
+ class="ax-icon ax-icon-chevron-left ax-collapse-arrow"
3787
+ [class]="collapsibleCollapsed() ? '-rotation-90' : 'rotation-90'"
3788
+ ></span>
3789
+ </div>
3790
+ </div>
3791
+ </ng-template>
3566
3792
  } @else {
3567
3793
  <div class="ax-grid ax-grid-cols-12 ax-gap-4 ax-auto-cols-max">
3568
3794
  <div class="ax-flex ax-items-center ax-gap-2 ax-col-span-12">
@@ -3629,6 +3855,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3629
3855
  textField="name"
3630
3856
  [ngClass]="generateClasses('label')"
3631
3857
  >
3858
+ <ax-validation-rule
3859
+ rule="required"
3860
+ message="{{ '@general:validation.messages.required' | translate | async }}"
3861
+ ></ax-validation-rule>
3632
3862
  </ax-select-box>
3633
3863
  }
3634
3864
  </div>
@@ -3636,22 +3866,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3636
3866
  <div class="ax-w-full">
3637
3867
  <ax-collapse-group look="fill">
3638
3868
  <ax-collapse
3639
- [caption]="collapsibleCaption()"
3640
- icon="fa-light fa-map-marker-alt"
3869
+ [headerTemplate]="collapseHeaderTemplate"
3641
3870
  [showHeader]="true"
3642
3871
  [isCollapsed]="collapsibleCollapsed()"
3643
3872
  (isCollapsedChange)="collapsibleCollapsed.set($event)"
3644
3873
  >
3645
3874
  <div class="ax-pt-1">
3646
- <axp-layout-renderer
3647
- [layout]="addressFormLayout()"
3648
- [context]="collapsibleFormContext()"
3649
- (contextChange)="onCollapsibleFormContextChange($event)"
3650
- />
3875
+ @for (formKey of [layoutRendererGeneration()]; track formKey) {
3876
+ <axp-layout-renderer
3877
+ [layout]="addressFormLayout()"
3878
+ [context]="collapsibleFormContext()"
3879
+ (contextChange)="onCollapsibleFormContextChange($event)"
3880
+ />
3881
+ }
3651
3882
  </div>
3652
3883
  </ax-collapse>
3653
3884
  </ax-collapse-group>
3654
3885
  </div>
3886
+
3887
+ <ng-template #collapseHeaderTemplate>
3888
+ <div class="ax-collapse-header-container">
3889
+ <div class="ax-collapse-header-start">
3890
+ <span class="ax-collapse-header-icon fa-light fa-map-marker-alt"></span>
3891
+ <span>
3892
+ @if (collapsibleHeaderLabel()) {
3893
+ <span class="ax-font-semibold">{{ collapsibleHeaderLabel() }}</span>
3894
+ @if (collapsibleHeaderAddress()) {
3895
+ <span> · {{ collapsibleHeaderAddress() }}</span>
3896
+ }
3897
+ } @else {
3898
+ {{ collapsibleHeaderAddress() }}
3899
+ }
3900
+ </span>
3901
+ </div>
3902
+ <div class="ax-collapse-header-end">
3903
+ <span
3904
+ class="ax-icon ax-icon-chevron-left ax-collapse-arrow"
3905
+ [class]="collapsibleCollapsed() ? '-rotation-90' : 'rotation-90'"
3906
+ ></span>
3907
+ </div>
3908
+ </div>
3909
+ </ng-template>
3655
3910
  } @else {
3656
3911
  <div class="ax-grid ax-grid-cols-12 ax-gap-4 ax-auto-cols-max">
3657
3912
  <div class="ax-flex ax-items-center ax-gap-2 ax-col-span-12">
@@ -7028,24 +7283,46 @@ class AXPSelectBoxWidgetEditComponent extends AXPDataListWidgetComponent {
7028
7283
  handleValueChange(e) {
7029
7284
  this.selectedItems.set(e.component.selectedItems);
7030
7285
  if (e.isUserInteraction) {
7286
+ AXPDebugService.instance?.trace(FORM_DIRTY_TRACE_NS, 'select-box-widget', 'handleValueChange', () => ({
7287
+ path: this.fullPath(),
7288
+ value: e.value,
7289
+ selectedItems: this.selectedItems(),
7290
+ expose: this.expose(),
7291
+ strategy: this.widgetsConfigs.selectValueStrategy,
7292
+ }));
7031
7293
  //#region ---- Expose extra values to context ----
7294
+ const widgetPath = this.fullPath();
7295
+ const savedWidgetValue = widgetPath ? this.contextService.getSavedValue(widgetPath) : undefined;
7296
+ const selectionValue = this.filterMode() ? e.value : this.widgetsConfigs.selectValueStrategy === 'valueField'
7297
+ ? e.value
7298
+ : this.multiple()
7299
+ ? this.selectedItems()
7300
+ : this.selectedItems()[0];
7301
+ const selectionMatchesSaved = this.contextService.isSavedCommitted() &&
7302
+ widgetPath != null &&
7303
+ isSelectionValueEqual(selectionValue, savedWidgetValue);
7032
7304
  if (this.expose()) {
7033
- const selections = this.selectedItems();
7034
- const exposeList = Array.isArray(this.expose()) ? this.expose() : [this.expose()];
7035
- const itemToExpose = {};
7036
- exposeList.forEach((expr) => {
7037
- if (typeof expr === 'string') {
7038
- const values = selections.map((item) => set({}, expr, get(item, expr)));
7039
- setSmart(itemToExpose, expr, this.singleOrMultiple(values));
7040
- }
7041
- else if (expr && typeof expr === 'object') {
7042
- const values = this.multiple()
7043
- ? selections.map((item) => set({}, expr.source, get(item, expr.source)))
7044
- : selections.map((item) => get(item, expr.source));
7045
- setSmart(itemToExpose, expr.target, this.singleOrMultiple(values));
7046
- }
7047
- });
7048
- this.contextService.applyObjectPaths(itemToExpose, { origin: 'user' });
7305
+ if (selectionMatchesSaved) {
7306
+ this.syncExposePathsToSavedBaseline();
7307
+ }
7308
+ else {
7309
+ const selections = this.selectedItems();
7310
+ const exposeList = Array.isArray(this.expose()) ? this.expose() : [this.expose()];
7311
+ const itemToExpose = {};
7312
+ exposeList.forEach((expr) => {
7313
+ if (typeof expr === 'string') {
7314
+ const values = selections.map((item) => set({}, expr, get(item, expr)));
7315
+ setSmart(itemToExpose, expr, this.singleOrMultiple(values));
7316
+ }
7317
+ else if (expr && typeof expr === 'object') {
7318
+ const values = this.multiple()
7319
+ ? selections.map((item) => set({}, expr.source, get(item, expr.source)))
7320
+ : selections.map((item) => get(item, expr.source));
7321
+ setSmart(itemToExpose, expr.target, this.singleOrMultiple(values));
7322
+ }
7323
+ });
7324
+ this.contextService.applyObjectPaths(itemToExpose, { origin: 'user' });
7325
+ }
7049
7326
  }
7050
7327
  //#endregion
7051
7328
  //#region ---- Set Value Based on Mode ----
@@ -7069,9 +7346,32 @@ class AXPSelectBoxWidgetEditComponent extends AXPDataListWidgetComponent {
7069
7346
  else {
7070
7347
  this.setValue(this.selectedItems()[0]);
7071
7348
  }
7349
+ if (this.expose() && selectionMatchesSaved) {
7350
+ this.syncExposePathsToSavedBaseline();
7351
+ }
7072
7352
  }
7073
7353
  //#endregion
7074
7354
  }
7355
+ /**
7356
+ * When selection matches saved baseline, align expose targets (restore or remove paths absent from baseline).
7357
+ */
7358
+ syncExposePathsToSavedBaseline() {
7359
+ const exposeValue = castArray(this.expose() ?? []);
7360
+ const parentPaths = new Set();
7361
+ exposeValue.forEach((item) => {
7362
+ const target = typeof item === 'string' ? item : item.target;
7363
+ if (target.includes('.')) {
7364
+ parentPaths.add(target.split('.').slice(0, -1).join('.'));
7365
+ }
7366
+ this.contextService.syncPathToSavedBaseline(target, { origin: 'system' });
7367
+ });
7368
+ const saved = this.contextService.saved();
7369
+ parentPaths.forEach((parentPath) => {
7370
+ if (!has(saved, parentPath)) {
7371
+ this.contextService.syncPathToSavedBaseline(parentPath, { origin: 'system' });
7372
+ }
7373
+ });
7374
+ }
7075
7375
  handleSearchValueChange(e) {
7076
7376
  if (e.isUserInteraction) {
7077
7377
  this.searchValue.set(e.value);
@@ -7430,7 +7730,7 @@ class AXPSelectionListWidgetDesignerComponent extends AXPDataListWidgetComponent
7430
7730
  }), ...(ngDevMode ? [{ debugName: "samples" }] : /* istanbul ignore next */ []));
7431
7731
  this.ef = effect(() => {
7432
7732
  this.dataSource()
7433
- .onChanged.pipe(first())
7733
+ .onChanged.pipe(first$1())
7434
7734
  .subscribe((e) => {
7435
7735
  if (e.items?.length > 0) {
7436
7736
  this.displayItems.set(e.items);
@@ -9349,52 +9649,57 @@ class AXPAvatarWidgetDesignerComponent extends AXPValueWidgetComponent {
9349
9649
  this.url = signal(null, ...(ngDevMode ? [{ debugName: "url" }] : /* istanbul ignore next */ []));
9350
9650
  this.blobUrl = signal(null, ...(ngDevMode ? [{ debugName: "blobUrl" }] : /* istanbul ignore next */ []));
9351
9651
  this.#effect1 = effect(async () => {
9352
- const value = this.getValue();
9353
- await untracked(async () => {
9354
- try {
9355
- if (value) {
9356
- this.setStatus(AXPWidgetStatus.Processing);
9357
- const source = value.source;
9358
- if (source) {
9359
- switch (source.kind) {
9360
- case 'url':
9361
- this.url.set(source.value);
9362
- break;
9363
- case 'blob': {
9364
- const prev = this.blobUrl();
9365
- if (prev) {
9366
- URL.revokeObjectURL(prev);
9652
+ this.getValue();
9653
+ this.beginHydration();
9654
+ try {
9655
+ await untracked(async () => {
9656
+ try {
9657
+ const value = this.getValue();
9658
+ if (value) {
9659
+ const source = value.source;
9660
+ if (source) {
9661
+ switch (source.kind) {
9662
+ case 'url':
9663
+ this.url.set(source.value);
9664
+ break;
9665
+ case 'blob': {
9666
+ const prev = this.blobUrl();
9667
+ if (prev) {
9668
+ URL.revokeObjectURL(prev);
9669
+ }
9670
+ const blob = source.value;
9671
+ const next = URL.createObjectURL(blob);
9672
+ this.blobUrl.set(next);
9673
+ this.url.set(next);
9674
+ break;
9367
9675
  }
9368
- const blob = source.value;
9369
- const next = URL.createObjectURL(blob);
9370
- this.blobUrl.set(next);
9371
- this.url.set(next);
9372
- break;
9373
- }
9374
- case 'fileId': {
9375
- const fileId = source.value;
9376
- const info = await this.fileStorage.getInfo(fileId);
9377
- this.url.set(info?.url ? info.url : null);
9378
- break;
9676
+ case 'fileId': {
9677
+ const fileId = source.value;
9678
+ const info = await this.fileStorage.getInfo(fileId);
9679
+ this.url.set(info?.url ? info.url : null);
9680
+ break;
9681
+ }
9682
+ default:
9683
+ this.url.set(null);
9684
+ break;
9379
9685
  }
9380
- default:
9381
- this.url.set(null);
9382
- break;
9686
+ }
9687
+ else {
9688
+ this.url.set(null);
9383
9689
  }
9384
9690
  }
9385
9691
  else {
9386
9692
  this.url.set(null);
9387
9693
  }
9388
9694
  }
9389
- else {
9390
- this.url.set(null);
9695
+ catch {
9696
+ this.setStatus(AXPWidgetStatus.Error);
9391
9697
  }
9392
- this.setStatus(AXPWidgetStatus.Rendered);
9393
- }
9394
- catch (error) {
9395
- this.setStatus(AXPWidgetStatus.Error);
9396
- }
9397
- });
9698
+ });
9699
+ }
9700
+ finally {
9701
+ this.endHydration();
9702
+ }
9398
9703
  }, ...(ngDevMode ? [{ debugName: "#effect1" }] : /* istanbul ignore next */ []));
9399
9704
  }
9400
9705
  #effect1;
@@ -9464,44 +9769,51 @@ class AXPAvatarWidgetEditComponent extends AXPValueWidgetComponent {
9464
9769
  this.isEmpty = computed(() => this.url() == null, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
9465
9770
  this.blobUrl = signal(null, ...(ngDevMode ? [{ debugName: "blobUrl" }] : /* istanbul ignore next */ []));
9466
9771
  this.#effect1 = effect(async () => {
9467
- const fileId = this.getValue();
9468
- await untracked(async () => {
9469
- try {
9470
- if (fileId) {
9471
- this.setStatus(AXPWidgetStatus.Processing);
9772
+ this.getValue();
9773
+ this.beginHydration();
9774
+ try {
9775
+ await untracked(async () => {
9776
+ try {
9472
9777
  const data = this.getValue();
9473
- if (data?.source) {
9474
- switch (data.source.kind) {
9475
- case 'url':
9476
- this.url.set(data.source.value);
9477
- break;
9478
- case 'blob': {
9479
- const blob = data.source.value;
9480
- this.blobUrl.set(URL.createObjectURL(blob));
9481
- this.url.set(this.blobUrl());
9482
- break;
9483
- }
9484
- case 'fileId': {
9485
- const fileId = data.source.value;
9486
- const info = await this.fileStorage.getInfo(fileId);
9487
- this.url.set(info?.url ? info.url : null);
9488
- break;
9778
+ if (data) {
9779
+ if (data?.source) {
9780
+ switch (data.source.kind) {
9781
+ case 'url':
9782
+ this.url.set(data.source.value);
9783
+ break;
9784
+ case 'blob': {
9785
+ const blob = data.source.value;
9786
+ this.blobUrl.set(URL.createObjectURL(blob));
9787
+ this.url.set(this.blobUrl());
9788
+ break;
9789
+ }
9790
+ case 'fileId': {
9791
+ const fileId = data.source.value;
9792
+ const info = await this.fileStorage.getInfo(fileId);
9793
+ this.url.set(info?.url ? info.url : null);
9794
+ break;
9795
+ }
9796
+ default:
9797
+ this.url.set(null);
9798
+ break;
9489
9799
  }
9490
- default:
9491
- this.url.set(null);
9492
- break;
9800
+ }
9801
+ else {
9802
+ this.url.set(null);
9493
9803
  }
9494
9804
  }
9495
9805
  else {
9496
9806
  this.url.set(null);
9497
9807
  }
9498
9808
  }
9499
- this.setStatus(AXPWidgetStatus.Rendered);
9500
- }
9501
- catch (error) {
9502
- this.setStatus(AXPWidgetStatus.Error);
9503
- }
9504
- });
9809
+ catch {
9810
+ this.setStatus(AXPWidgetStatus.Error);
9811
+ }
9812
+ });
9813
+ }
9814
+ finally {
9815
+ this.endHydration();
9816
+ }
9505
9817
  }, ...(ngDevMode ? [{ debugName: "#effect1" }] : /* istanbul ignore next */ []));
9506
9818
  }
9507
9819
  #effect1;
@@ -9641,52 +9953,57 @@ class AXPAvatarWidgetViewComponent extends AXPValueWidgetComponent {
9641
9953
  this.url = signal(null, ...(ngDevMode ? [{ debugName: "url" }] : /* istanbul ignore next */ []));
9642
9954
  this.blobUrl = signal(null, ...(ngDevMode ? [{ debugName: "blobUrl" }] : /* istanbul ignore next */ []));
9643
9955
  this.#effect1 = effect(async () => {
9644
- const value = this.getValue();
9645
- await untracked(async () => {
9646
- try {
9647
- if (value) {
9648
- this.setStatus(AXPWidgetStatus.Processing);
9649
- const source = value.source;
9650
- if (source) {
9651
- switch (source.kind) {
9652
- case 'url':
9653
- this.url.set(source.value);
9654
- break;
9655
- case 'blob': {
9656
- const prev = this.blobUrl();
9657
- if (prev) {
9658
- URL.revokeObjectURL(prev);
9956
+ this.getValue();
9957
+ this.beginHydration();
9958
+ try {
9959
+ await untracked(async () => {
9960
+ try {
9961
+ const value = this.getValue();
9962
+ if (value) {
9963
+ const source = value.source;
9964
+ if (source) {
9965
+ switch (source.kind) {
9966
+ case 'url':
9967
+ this.url.set(source.value);
9968
+ break;
9969
+ case 'blob': {
9970
+ const prev = this.blobUrl();
9971
+ if (prev) {
9972
+ URL.revokeObjectURL(prev);
9973
+ }
9974
+ const blob = source.value;
9975
+ const next = URL.createObjectURL(blob);
9976
+ this.blobUrl.set(next);
9977
+ this.url.set(next);
9978
+ break;
9659
9979
  }
9660
- const blob = source.value;
9661
- const next = URL.createObjectURL(blob);
9662
- this.blobUrl.set(next);
9663
- this.url.set(next);
9664
- break;
9665
- }
9666
- case 'fileId': {
9667
- const fileId = source.value;
9668
- const info = await this.fileStorage.getInfo(fileId);
9669
- this.url.set(info?.url ? info.url : null);
9670
- break;
9980
+ case 'fileId': {
9981
+ const fileId = source.value;
9982
+ const info = await this.fileStorage.getInfo(fileId);
9983
+ this.url.set(info?.url ? info.url : null);
9984
+ break;
9985
+ }
9986
+ default:
9987
+ this.url.set(null);
9988
+ break;
9671
9989
  }
9672
- default:
9673
- this.url.set(null);
9674
- break;
9990
+ }
9991
+ else {
9992
+ this.url.set(null);
9675
9993
  }
9676
9994
  }
9677
9995
  else {
9678
9996
  this.url.set(null);
9679
9997
  }
9680
9998
  }
9681
- else {
9682
- this.url.set(null);
9999
+ catch {
10000
+ this.setStatus(AXPWidgetStatus.Error);
9683
10001
  }
9684
- this.setStatus(AXPWidgetStatus.Rendered);
9685
- }
9686
- catch (error) {
9687
- this.setStatus(AXPWidgetStatus.Error);
9688
- }
9689
- });
10002
+ });
10003
+ }
10004
+ finally {
10005
+ this.endHydration();
10006
+ }
9690
10007
  }, ...(ngDevMode ? [{ debugName: "#effect1" }] : /* istanbul ignore next */ []));
9691
10008
  }
9692
10009
  #effect1;
@@ -10214,7 +10531,7 @@ class AXPDataListWidgetViewComponent extends AXPValueWidgetComponent {
10214
10531
  ></ax-dropdown-command-column>
10215
10532
  }
10216
10533
  </ax-data-table>
10217
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXDataTableModule }, { kind: "component", type: i1$c.AXDataTableComponent, selector: "ax-data-table", inputs: ["dataSource", "selectedRows", "parentField", "hasChildrenField", "rowDetailsTemplate", "look", "rowTemplate", "emptyTemplate", "noDataTemplate", "alternative", "showHeader", "fixedHeader", "showFooter", "fixedFooter", "itemHeight", "allowReordering", "paging", "fetchDataMode", "loading", "focusedRow"], outputs: ["selectedRowsChange", "focusedRowChange", "onRowClick", "onRowDbClick", "onColumnsOrderChanged", "onColumnSizeChanged", "onPageChanged"] }, { kind: "component", type: i1$c.AXRowIndexColumnComponent, selector: "ax-index-column", inputs: ["width", "caption", "fixed", "footerTemplate", "padZero"] }, { kind: "component", type: i1$c.AXRowSelectColumnComponent, selector: "ax-select-column", inputs: ["width", "caption", "fixed"] }, { kind: "component", type: i1$c.AXRowCommandColumnComponent, selector: "ax-command-column", inputs: ["width", "caption", "fixed", "footerTemplate", "items"], outputs: ["onItemClick"] }, { kind: "component", type: i1$c.AXRowDropdownCommandColumnComponent, selector: "ax-dropdown-command-column", inputs: ["width", "caption", "fixed", "footerTemplate", "emptyStateTemplate", "emptyStateText", "items"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1.AXPWidgetColumnRendererComponent, selector: "axp-widget-column-renderer", inputs: ["caption", "customExpandIcon", "customCollapseIcon", "customWidth", "node", "footerTemplate", "expandHandler", "sortEnabled", "headerSortDirection", "headerSortPriority", "columnFixed", "cellTemplate", "headerTemplate"], outputs: ["sortToggle"] }, { kind: "ngmodule", type: AXTranslationModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10534
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXDataTableModule }, { kind: "component", type: i1$c.AXDataTableComponent, selector: "ax-data-table", inputs: ["dataSource", "selectedRows", "parentField", "hasChildrenField", "rowDetailsTemplate", "rowColor", "rowColorField", "look", "rowTemplate", "emptyTemplate", "noDataTemplate", "alternative", "showHeader", "fixedHeader", "showFooter", "fixedFooter", "itemHeight", "allowReordering", "paging", "fetchDataMode", "loading", "focusedRow"], outputs: ["selectedRowsChange", "focusedRowChange", "onRowClick", "onRowDbClick", "onColumnsOrderChanged", "onColumnSizeChanged", "onPageChanged"] }, { kind: "component", type: i1$c.AXRowIndexColumnComponent, selector: "ax-index-column", inputs: ["width", "caption", "fixed", "footerTemplate", "padZero"] }, { kind: "component", type: i1$c.AXRowSelectColumnComponent, selector: "ax-select-column", inputs: ["width", "caption", "fixed"] }, { kind: "component", type: i1$c.AXRowCommandColumnComponent, selector: "ax-command-column", inputs: ["width", "caption", "fixed", "footerTemplate", "items"], outputs: ["onItemClick"] }, { kind: "component", type: i1$c.AXRowDropdownCommandColumnComponent, selector: "ax-dropdown-command-column", inputs: ["width", "caption", "fixed", "footerTemplate", "emptyStateTemplate", "emptyStateText", "items"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1.AXPWidgetColumnRendererComponent, selector: "axp-widget-column-renderer", inputs: ["caption", "customExpandIcon", "customCollapseIcon", "customWidth", "node", "footerTemplate", "expandHandler", "sortEnabled", "headerSortDirection", "headerSortPriority", "columnFixed", "cellTemplate", "headerTemplate"], outputs: ["sortToggle"] }, { kind: "ngmodule", type: AXTranslationModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10218
10535
  }
10219
10536
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataListWidgetViewComponent, decorators: [{
10220
10537
  type: Component,
@@ -11018,7 +11335,7 @@ class AXPGalleryWidgetViewComponent extends AXPValueWidgetComponent {
11018
11335
  </axp-state-message>
11019
11336
  }
11020
11337
  </div>
11021
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i1$e.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { 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: "component", type: i2$1.AXDecoratorFullScreenButtonComponent, selector: "ax-fullscreen-button", inputs: ["element", "isActive"], outputs: ["elementChange", "isActiveChange"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i2$2.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }, { 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: AXMediaViewerModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11338
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i1$e.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { 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: "component", type: i2$1.AXDecoratorFullScreenButtonComponent, selector: "ax-fullscreen-button", inputs: ["element", "isActive"], outputs: ["elementChange", "isActiveChange"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i2$2.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { 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: AXMediaViewerModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11022
11339
  }
11023
11340
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGalleryWidgetViewComponent, decorators: [{
11024
11341
  type: Component,
@@ -12687,9 +13004,9 @@ class AXPMapWidgetEditComponent extends AXPValueWidgetComponent {
12687
13004
  this.disabled = computed(() => this.options()['disabled'] ?? false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
12688
13005
  this.readonly = computed(() => this.options()['readonly'] ?? false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
12689
13006
  this.#af = afterNextRender(() => {
12690
- this.setStatus(AXPWidgetStatus.Rendered);
13007
+ this.endHydration();
12691
13008
  });
12692
- this.setStatus(AXPWidgetStatus.Processing);
13009
+ this.beginHydration();
12693
13010
  }
12694
13011
  #af;
12695
13012
  get __class() {
@@ -12774,9 +13091,9 @@ class AXPMapWidgetViewComponent extends AXPValueWidgetComponent {
12774
13091
  this.disabled = computed(() => this.options()['disabled'] ?? false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
12775
13092
  this.readonly = computed(() => this.options()['readonly'] ?? false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
12776
13093
  this.#af = afterNextRender(() => {
12777
- this.setStatus(AXPWidgetStatus.Rendered);
13094
+ this.endHydration();
12778
13095
  });
12779
- this.setStatus(AXPWidgetStatus.Processing);
13096
+ this.beginHydration();
12780
13097
  }
12781
13098
  #af;
12782
13099
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPMapWidgetViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
@@ -15588,8 +15905,6 @@ class AXPItemConfiguratorWidgetEditComponent extends AXPValueWidgetComponent {
15588
15905
  //#endregion
15589
15906
  //#region ---- State Signals ----
15590
15907
  this.isInitialized = signal(false, ...(ngDevMode ? [{ debugName: "isInitialized" }] : /* istanbul ignore next */ []));
15591
- /** True while syncing models from shared context (discard/load); writes use system origin. */
15592
- this.hydratingFromContext = signal(false, ...(ngDevMode ? [{ debugName: "hydratingFromContext" }] : /* istanbul ignore next */ []));
15593
15908
  //#endregion
15594
15909
  //#region ---- Models ----
15595
15910
  this.selectedItemIdModel = model('', ...(ngDevMode ? [{ debugName: "selectedItemIdModel" }] : /* istanbul ignore next */ []));
@@ -15643,29 +15958,28 @@ class AXPItemConfiguratorWidgetEditComponent extends AXPValueWidgetComponent {
15643
15958
  }, ...(ngDevMode ? [{ debugName: "loadNamedDataSourceEffect" }] : /* istanbul ignore next */ []));
15644
15959
  this.syncValueToConfigurator = effect(() => {
15645
15960
  const value = this.getValue();
15646
- this.hydratingFromContext.set(true);
15647
- if (value && value.type) {
15648
- const sid = untracked(() => this.selectedItemIdModel());
15649
- const vals = untracked(() => this.valuesModel());
15961
+ const sid = untracked(() => this.selectedItemIdModel());
15962
+ const vals = untracked(() => this.valuesModel());
15963
+ if (value?.type) {
15650
15964
  const valueOpts = (value.options ?? {});
15651
15965
  if (value.type !== sid || !isEqual(valueOpts, vals)) {
15652
15966
  untracked(() => {
15653
15967
  this.selectedItemIdModel.set(value.type);
15654
- this.valuesModel.set(valueOpts);
15968
+ this.valuesModel.set(cloneDeep(valueOpts));
15655
15969
  this.isInitialized.set(true);
15656
15970
  });
15657
15971
  }
15972
+ return;
15658
15973
  }
15659
- else {
15660
- untracked(() => {
15661
- if (!this.isInitialized()) {
15662
- this.selectedItemIdModel.set('');
15663
- this.valuesModel.set({});
15664
- this.isInitialized.set(true);
15665
- }
15666
- });
15667
- }
15668
- queueMicrotask(() => this.hydratingFromContext.set(false));
15974
+ untracked(() => {
15975
+ if (sid !== '' || !isEqual(vals, {})) {
15976
+ this.selectedItemIdModel.set('');
15977
+ this.valuesModel.set({});
15978
+ }
15979
+ if (!this.isInitialized()) {
15980
+ this.isInitialized.set(true);
15981
+ }
15982
+ });
15669
15983
  }, ...(ngDevMode ? [{ debugName: "syncValueToConfigurator" }] : /* istanbul ignore next */ []));
15670
15984
  this.syncConfiguratorToValue = effect(() => {
15671
15985
  const sid = this.selectedItemIdModel();
@@ -15674,26 +15988,34 @@ class AXPItemConfiguratorWidgetEditComponent extends AXPValueWidgetComponent {
15674
15988
  return;
15675
15989
  }
15676
15990
  const currentValue = untracked(() => this.getValue());
15677
- const next = this.buildWidgetNode(sid, vals);
15678
- if (isEqual(next, currentValue)) {
15991
+ if (!sid) {
15992
+ if (!isFormValueEqual(null, currentValue)) {
15993
+ untracked(() => {
15994
+ this.setValue(null);
15995
+ });
15996
+ }
15679
15997
  return;
15680
15998
  }
15681
- untracked(() => {
15682
- this.setValue(next);
15683
- });
15999
+ const next = this.buildWidgetNode(sid, vals);
16000
+ if (!isFormValueEqual(next, currentValue)) {
16001
+ untracked(() => {
16002
+ this.setValue(next);
16003
+ });
16004
+ }
15684
16005
  }, ...(ngDevMode ? [{ debugName: "syncConfiguratorToValue" }] : /* istanbul ignore next */ []));
15685
16006
  }
15686
16007
  //#endregion
15687
16008
  //#region ---- Utility Methods ----
15688
16009
  buildWidgetNode(sid, vals) {
16010
+ const emptyNode = { type: '', options: {} };
15689
16011
  if (!sid) {
15690
- return { type: '', options: {} };
16012
+ return emptyNode;
15691
16013
  }
15692
16014
  const currentValue = untracked(() => this.getValue());
15693
16015
  return {
15694
16016
  ...(currentValue && typeof currentValue === 'object' ? currentValue : {}),
15695
16017
  type: sid,
15696
- options: vals,
16018
+ options: cloneDeep(vals),
15697
16019
  };
15698
16020
  }
15699
16021
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPItemConfiguratorWidgetEditComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
@@ -23307,12 +23629,14 @@ class AXPImageWidgetEditComponent extends AXPValueWidgetComponent {
23307
23629
  color: 'danger',
23308
23630
  disabled: !this.imageUrl(),
23309
23631
  command: { name: 'delete-image' },
23632
+ shortcuts: ['delete'],
23310
23633
  });
23311
23634
  actions.custom({
23312
23635
  title: '@general:actions.upload.title',
23313
23636
  icon: 'fa-light fa-upload',
23314
23637
  color: 'primary',
23315
23638
  command: { name: 'upload-image' },
23639
+ shortcuts: ['ctrl+s', 'Enter'],
23316
23640
  });
23317
23641
  });
23318
23642
  })
@@ -24587,7 +24911,7 @@ class AXPTabularDataWidgetEditComponent extends AXPValueWidgetComponent {
24587
24911
  this.platform = inject(AXPlatform);
24588
24912
  }
24589
24913
  async openPopup() {
24590
- const { AXPTabularDataPopupComponent } = await import('./acorex-platform-layout-widgets-tabular-data-edit-popup.component-D4MOHEpI.mjs');
24914
+ const { AXPTabularDataPopupComponent } = await import('./acorex-platform-layout-widgets-tabular-data-edit-popup.component-CZc2zFlg.mjs');
24591
24915
  const popupData = await this.popupService.open(AXPTabularDataPopupComponent, {
24592
24916
  size: this.platform.is('Mobile') || this.platform.is('SM') ? 'full' : this.columns().length > 3 ? 'lg' : 'md',
24593
24917
  header: true,
@@ -24639,7 +24963,7 @@ class AXPTabularDataEditorWidgetViewComponent extends AXPValueWidgetComponent {
24639
24963
  this.columns = this.options()['columns'];
24640
24964
  }
24641
24965
  async openPopup() {
24642
- const { AXPTabularDataPopupComponent } = await import('./acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs');
24966
+ const { AXPTabularDataPopupComponent } = await import('./acorex-platform-layout-widgets-tabular-data-view-popup.component-XWRUVoL4.mjs');
24643
24967
  this.popupService.open(AXPTabularDataPopupComponent, {
24644
24968
  size: this.platform.is('Mobile') || this.platform.is('SM') ? 'full' : 'md',
24645
24969
  header: true,
@@ -28074,7 +28398,7 @@ class AXPTriggersWidgetEditComponent extends AXPValueWidgetComponent {
28074
28398
  </div>
28075
28399
  }
28076
28400
  </div>
28077
- `, 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.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: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }, { kind: "pipe", type: i1$4.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
28401
+ `, 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.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: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "pipe", type: i1$4.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
28078
28402
  }
28079
28403
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTriggersWidgetEditComponent, decorators: [{
28080
28404
  type: Component,
@@ -28201,12 +28525,24 @@ var triggersWidgetEditor_component = /*#__PURE__*/Object.freeze({
28201
28525
 
28202
28526
  class AXPTriggerExpressionEditorPopupComponent extends AXBasePageComponent {
28203
28527
  constructor() {
28204
- super(...arguments);
28528
+ super();
28205
28529
  //#region ---- Inputs ----
28206
28530
  this.value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
28207
28531
  //#endregion
28208
28532
  //#region ---- Properties ----
28209
28533
  this.currentValue = linkedSignal(() => this.value(), ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
28534
+ setupPopupFooterKeyboardShortcuts(() => [
28535
+ {
28536
+ title: '@general:actions.cancel.title',
28537
+ command: 'cancel',
28538
+ handler: () => this.handleCancelClick(),
28539
+ },
28540
+ {
28541
+ title: '@general:actions.save.title',
28542
+ command: 'submit',
28543
+ handler: () => this.handleSaveClick(),
28544
+ },
28545
+ ], { id: 'trigger-expression-editor-popup' });
28210
28546
  }
28211
28547
  //#endregion
28212
28548
  //#region ---- Event Handlers ----
@@ -28221,7 +28557,7 @@ class AXPTriggerExpressionEditorPopupComponent extends AXBasePageComponent {
28221
28557
  value: this.currentValue(),
28222
28558
  });
28223
28559
  }
28224
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTriggerExpressionEditorPopupComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
28560
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTriggerExpressionEditorPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
28225
28561
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: AXPTriggerExpressionEditorPopupComponent, isStandalone: true, selector: "axp-trigger-expression-editor-popup", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: `
28226
28562
  <div class="ax-flex ax-flex-col ax-h-full ax-overflow-hidden">
28227
28563
  <div class="ax-flex-1 ax-p-4 ax-overflow-hidden" style="direction: ltr;">
@@ -28288,7 +28624,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
28288
28624
  changeDetection: ChangeDetectionStrategy.OnPush,
28289
28625
  imports: [CommonModule, FormsModule, AXButtonModule, AXCodeEditorModule, AXDecoratorModule, AXTranslationModule],
28290
28626
  }]
28291
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }] } });
28627
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }] } });
28292
28628
 
28293
28629
  var triggerExpressionEditorPopup_component = /*#__PURE__*/Object.freeze({
28294
28630
  __proto__: null,