@smartbit4all/ng-client 7.0.9 → 7.1.0

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, reflectComponentType, Injectable, inject, DOCUMENT, PLATFORM_ID, Optional, Inject, RendererStyleFlags2, input, computed, Component, HostListener, HostBinding, Directive, ViewContainerRef, ElementRef, Renderer2, ChangeDetectorRef, Input, output, signal, Injector, ViewChild, ViewChildren, effect, afterNextRender, NgZone, forwardRef, Pipe, ChangeDetectionStrategy, ViewEncapsulation, EnvironmentInjector, EventEmitter, Output, ApplicationRef, viewChild, makeEnvironmentProviders, importProvidersFrom } from '@angular/core';
2
+ import { InjectionToken, reflectComponentType, Injectable, inject, DOCUMENT, PLATFORM_ID, Optional, Inject, RendererStyleFlags2, input, computed, Component, HostListener, HostBinding, Directive, ViewContainerRef, ElementRef, Renderer2, ChangeDetectorRef, Input, output, signal, Injector, ViewChild, ViewChildren, effect, afterNextRender, NgZone, forwardRef, Pipe, ChangeDetectionStrategy, ViewEncapsulation, SkipSelf, NgModule, EnvironmentInjector, EventEmitter, Output, ApplicationRef, viewChild, makeEnvironmentProviders, importProvidersFrom } from '@angular/core';
3
3
  import * as i1 from '@angular/common/http';
4
4
  import { HttpHeaders, HttpContext, HttpErrorResponse, HttpParams, provideHttpClient, withInterceptors, withInterceptorsFromDi } from '@angular/common/http';
5
5
  import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
@@ -10,6 +10,7 @@ import { enUS, hu } from 'date-fns/locale';
10
10
  import { isPlatformBrowser, NgClass, NgTemplateOutlet, NgStyle, AsyncPipe, KeyValuePipe, DatePipe, Location, NgIf, NgForOf } from '@angular/common';
11
11
  import { Subject, lastValueFrom, tap, takeUntil, throwError, from, interval, startWith, map, distinctUntilChanged, catchError as catchError$1 } from 'rxjs';
12
12
  import { catchError, switchMap, takeUntil as takeUntil$1 } from 'rxjs/operators';
13
+ import { deepEqual } from 'fast-equals';
13
14
  import { MatButton, MatIconButton, MatMiniFabButton } from '@angular/material/button';
14
15
  import { MatTooltip } from '@angular/material/tooltip';
15
16
  import { MatIconRegistry, MatIcon } from '@angular/material/icon';
@@ -22,7 +23,6 @@ import { Validators, UntypedFormGroup, UntypedFormControl, NG_VALUE_ACCESSOR, Fo
22
23
  import { ENTER, COMMA } from '@angular/cdk/keycodes';
23
24
  import { MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete';
24
25
  import { MatChipGrid, MatChipRow, MatChipRemove, MatChipInput, MatChip } from '@angular/material/chips';
25
- import { deepEqual } from 'fast-equals';
26
26
  import { set } from 'date-fns';
27
27
  import { Overlay, OverlayPositionBuilder, ScrollDispatcher } from '@angular/cdk/overlay';
28
28
  import { TemplatePortal } from '@angular/cdk/portal';
@@ -43,7 +43,6 @@ import { MatMenuTrigger, MatContextMenuTrigger, MatMenu, MatMenuItem } from '@an
43
43
  import { SelectionModel } from '@angular/cdk/collections';
44
44
  import { MatDivider } from '@angular/material/divider';
45
45
  import { MatProgressSpinner } from '@angular/material/progress-spinner';
46
- import { trigger, state, style, transition, animate } from '@angular/animations';
47
46
  import { MatTable, MatColumnDef, MatHeaderCellDef, MatHeaderCell, MatCellDef, MatCell, MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow } from '@angular/material/table';
48
47
  import { NestedTreeControl, FlatTreeControl } from '@angular/cdk/tree';
49
48
  import { MatTreeNestedDataSource, MatTree, MatTreeNodeDef, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeOutlet, MatTreeFlattener, MatTreeFlatDataSource, MatTreeNode, MatTreeNodePadding } from '@angular/material/tree';
@@ -2725,6 +2724,18 @@ var MessageType;
2725
2724
  * Do not edit the class manually.
2726
2725
  */
2727
2726
 
2727
+ /**
2728
+ * View API
2729
+ * View API
2730
+ *
2731
+ * The version of the OpenAPI document: 1.0.0
2732
+ * Contact: info@it4all.hu
2733
+ *
2734
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
2735
+ * https://openapi-generator.tech
2736
+ * Do not edit the class manually.
2737
+ */
2738
+
2728
2739
  /**
2729
2740
  * View API
2730
2741
  * View API
@@ -2916,18 +2927,6 @@ var UiActionInputType;
2916
2927
  * Do not edit the class manually.
2917
2928
  */
2918
2929
 
2919
- /**
2920
- * View API
2921
- * View API
2922
- *
2923
- * The version of the OpenAPI document: 1.0.0
2924
- * Contact: info@it4all.hu
2925
- *
2926
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
2927
- * https://openapi-generator.tech
2928
- * Do not edit the class manually.
2929
- */
2930
-
2931
2930
  /**
2932
2931
  * View API
2933
2932
  * View API
@@ -3935,6 +3934,15 @@ class SmartViewContextService {
3935
3934
  this.openSmartLink = new Subject();
3936
3935
  this.isOpeningSmartLink = false;
3937
3936
  this.dialogRefs = [];
3937
+ this.modelSyncParticipants = new Map();
3938
+ /**
3939
+ * Each view's data as last synced with the backend, a copy. A view whose data is deep-equal
3940
+ * to its copy is left out of the next call; a view without a copy is always sent.
3941
+ */
3942
+ this.syncedModels = new Map();
3943
+ /** The seq of the envelope (or push) each copy came from, so a late completion cannot roll it back. */
3944
+ this.syncedSeq = new Map();
3945
+ this.modelSyncSeq = 0;
3938
3946
  this.embeddedContainers = new Map();
3939
3947
  this.embeddedViewRefs = [];
3940
3948
  this.pendingEmbeddedViews = [];
@@ -4035,33 +4043,138 @@ class SmartViewContextService {
4035
4043
  }
4036
4044
  this.embeddedViewRefs = this.embeddedViewRefs.filter((ref) => !orphaned.includes(ref));
4037
4045
  }
4038
- getCompositeModel(containerUuid) {
4039
- const result = {};
4040
- const children = this.embeddedViewRefs.filter((ref) => ref.containerUuid === containerUuid);
4041
- for (const child of children) {
4042
- result[child.uuid] = child.componentRef.instance.getModel();
4043
- Object.assign(result, this.getCompositeModel(child.uuid));
4044
- }
4045
- return result;
4046
- }
4047
- submitEmbeddedForms(containerUuid, validate) {
4046
+ /**
4047
+ * Validates the forms of the views embedded under the container, transitively, and collects
4048
+ * their invalid fields into one list for the container's snackbar.
4049
+ */
4050
+ validateEmbeddedForms(containerUuid) {
4048
4051
  const allInvalidKeys = [];
4049
4052
  const allInvalidNames = [];
4050
4053
  const children = this.embeddedViewRefs.filter((ref) => ref.containerUuid === containerUuid);
4051
4054
  for (const child of children) {
4052
4055
  const instance = child.componentRef.instance;
4053
- instance.submitForm(validate);
4054
- if (validate) {
4055
- const invalid = instance.getInvalidFields();
4056
- allInvalidKeys.push(...invalid.invalidFieldKeys);
4057
- allInvalidNames.push(...invalid.invalidFieldNames);
4058
- }
4059
- const nested = this.submitEmbeddedForms(child.uuid, validate);
4056
+ instance.submitForm(true);
4057
+ const invalid = instance.getInvalidFields();
4058
+ allInvalidKeys.push(...invalid.invalidFieldKeys);
4059
+ allInvalidNames.push(...invalid.invalidFieldNames);
4060
+ const nested = this.validateEmbeddedForms(child.uuid);
4060
4061
  allInvalidKeys.push(...nested.invalidFieldKeys);
4061
4062
  allInvalidNames.push(...nested.invalidFieldNames);
4062
4063
  }
4063
4064
  return { invalidFieldKeys: allInvalidKeys, invalidFieldNames: allInvalidNames };
4064
4065
  }
4066
+ // #region model sync
4067
+ joinModelSync(uuid, participant) {
4068
+ this.modelSyncParticipants.set(uuid, participant);
4069
+ }
4070
+ /**
4071
+ * Only the participant that is registered under the uuid leaves: a component destroyed after
4072
+ * its successor joined under the same uuid must not take the successor with it.
4073
+ */
4074
+ leaveModelSync(uuid, participant) {
4075
+ if (this.modelSyncParticipants.get(uuid) !== participant) {
4076
+ return;
4077
+ }
4078
+ this.modelSyncParticipants.delete(uuid);
4079
+ this.syncedModels.delete(uuid);
4080
+ this.syncedSeq.delete(uuid);
4081
+ }
4082
+ /**
4083
+ * Records what the backend's model just told the view as in sync. Without keys the view's
4084
+ * whole data is the backend's (initial load, a whole-model push) and becomes the copy. With
4085
+ * keys only those paths of the data were pushed: they are written into the copy, and whatever
4086
+ * else the view holds keeps its standing — an edit written into the data outside the form
4087
+ * while a call was in flight stays unsynced. A view without a copy has nothing to update.
4088
+ */
4089
+ markModelSynced(uuid, keys) {
4090
+ const participant = this.modelSyncParticipants.get(uuid);
4091
+ if (!participant) {
4092
+ return;
4093
+ }
4094
+ if (!keys) {
4095
+ this.syncedModels.set(uuid, this.cloneModel(uuid, participant.getModel()));
4096
+ }
4097
+ else {
4098
+ const copy = this.syncedModels.get(uuid);
4099
+ const model = participant.getModel();
4100
+ if (copy === undefined || copy === null || model === undefined || model === null) {
4101
+ return;
4102
+ }
4103
+ for (const key of keys) {
4104
+ setDeep(copy, key, this.cloneModel(uuid, getDeep(model, key)));
4105
+ }
4106
+ }
4107
+ this.syncedSeq.set(uuid, this.modelSyncSeq);
4108
+ }
4109
+ cloneModel(uuid, model) {
4110
+ try {
4111
+ return structuredClone(model);
4112
+ }
4113
+ catch (err) {
4114
+ const path = uncloneablePath(model);
4115
+ throw new Error(`Model sync: the data of view ${uuid} cannot be cloned` +
4116
+ (path ? ` (at ${path})` : '') +
4117
+ `: ${err.message}`);
4118
+ }
4119
+ }
4120
+ /**
4121
+ * The envelope of the next backend call: the data of every open view that differs from its
4122
+ * last synced copy. Forms are flushed into their data first, so an unsaved field value is
4123
+ * part of what is compared and sent. The envelope is always present, that is what tells the
4124
+ * backend it talks to a client that syncs; a view absent from it means "unchanged".
4125
+ *
4126
+ * The copies taken here become the synced copies when the call succeeds
4127
+ * ({@link completeModelSync}), not the data at response time: what the user types while the
4128
+ * call is in flight must count as unsynced.
4129
+ */
4130
+ beginModelSync() {
4131
+ const models = {};
4132
+ const snapshots = new Map();
4133
+ for (const [uuid, participant] of this.modelSyncParticipants) {
4134
+ participant.submitForm(false);
4135
+ const model = participant.getModel();
4136
+ if (model === undefined || model === null) {
4137
+ continue;
4138
+ }
4139
+ if (this.syncedModels.has(uuid) && deepEqual(model, this.syncedModels.get(uuid))) {
4140
+ continue;
4141
+ }
4142
+ const snapshot = this.cloneModel(uuid, model);
4143
+ models[uuid] = snapshot;
4144
+ snapshots.set(uuid, snapshot);
4145
+ }
4146
+ return { modelSync: { models }, snapshots, seq: ++this.modelSyncSeq };
4147
+ }
4148
+ /**
4149
+ * Calls run concurrently, so an envelope built earlier may complete later: its copies are
4150
+ * older than what a later envelope or push already settled, and are not written back.
4151
+ */
4152
+ completeModelSync(pending) {
4153
+ for (const [uuid, snapshot] of pending.snapshots) {
4154
+ if (!this.modelSyncParticipants.has(uuid)) {
4155
+ continue;
4156
+ }
4157
+ if ((this.syncedSeq.get(uuid) ?? -1) > pending.seq) {
4158
+ continue;
4159
+ }
4160
+ this.syncedModels.set(uuid, snapshot);
4161
+ this.syncedSeq.set(uuid, pending.seq);
4162
+ }
4163
+ }
4164
+ /**
4165
+ * Runs a backend call on a model sync carrier: a request that can bring the models (a
4166
+ * `UiActionRequest`, a `GridSelectionChange`). The envelope goes onto the carrier, the call
4167
+ * runs with it, and a successful call settles the copies the envelope was built from. What
4168
+ * happens to the response is the caller's business.
4169
+ */
4170
+ async withModelSync(carrier, call) {
4171
+ const pending = this.beginModelSync();
4172
+ carrier.modelSync = pending.modelSync;
4173
+ const viewContextChange = await call(carrier);
4174
+ this.completeModelSync(pending);
4175
+ return viewContextChange;
4176
+ }
4177
+ // #endregion
4065
4178
  unregisterEmbeddedSlot(containerUuid, containerId) {
4066
4179
  const innerMap = this.embeddedContainers.get(containerUuid);
4067
4180
  if (innerMap) {
@@ -4355,7 +4468,11 @@ class SmartViewContextService {
4355
4468
  .afterClosed()
4356
4469
  .pipe(takeUntil(this._destroy$))
4357
4470
  .subscribe(() => {
4358
- this.performAction(view.uuid, { code: DefaultUiActionCode_CLOSE, params: {} });
4471
+ // A close the backend ordered (closeView) has already left dialogRefs; only the
4472
+ // user's own close is an action the backend has yet to hear about.
4473
+ if (this.dialogRefs.some((ref) => ref.uuid === view.uuid)) {
4474
+ this.performAction(view.uuid, { code: DefaultUiActionCode_CLOSE, params: {} });
4475
+ }
4359
4476
  });
4360
4477
  }
4361
4478
  }
@@ -4424,9 +4541,9 @@ class SmartViewContextService {
4424
4541
  if (view.type === ViewType.DIALOG) {
4425
4542
  let dialogRef = this.dialogRefs.find((dialogRef) => dialogRef.uuid === view.uuid);
4426
4543
  if (dialogRef) {
4427
- dialogRef.dialogRef.close();
4428
4544
  let index = this.dialogRefs.indexOf(dialogRef);
4429
4545
  this.dialogRefs.splice(index, 1);
4546
+ dialogRef.dialogRef.close();
4430
4547
  }
4431
4548
  else {
4432
4549
  // console.log('DialogRef is not found by uuid.');
@@ -4801,47 +4918,28 @@ class SmartViewContextService {
4801
4918
  content,
4802
4919
  });
4803
4920
  }
4804
- async dataChanged(uuid, dataChange) {
4805
- return await this._dataChanged({ uuid, dataChange });
4806
- // const isCompleted = new Subject<void>();
4807
- // this.apiQueueService.addToQueue(
4808
- // {
4809
- // callback: this._dataChanged.bind(this),
4810
- // config: {
4811
- // uuid,
4812
- // dataChange,
4813
- // },
4814
- // isCompleted,
4815
- // },
4816
- // true
4817
- // );
4818
- // return await isCompleted.toPromise();
4819
- }
4820
4921
  async _performAction(config) {
4821
- return await this.handlePerformActionResponse(await this.view.performAction(config.uuid, config.uiActionRequest).toPromise());
4922
+ return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view.performAction(config.uuid, config.uiActionRequest).toPromise()));
4822
4923
  }
4823
4924
  async _performWidgetAction(config) {
4824
- return await this.handlePerformActionResponse(await this.view
4925
+ return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view
4825
4926
  .performWidgetAction(config.uuid, config.widgetId, config.nodeId, config.uiActionRequest)
4826
- .toPromise());
4927
+ .toPromise()));
4827
4928
  }
4828
4929
  async _performWidgetMainAction(config) {
4829
- return await this.handlePerformActionResponse(await this.view
4930
+ return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view
4830
4931
  .performWidgetMainAction(config.uuid, config.widgetId, config.uiActionRequest)
4831
- .toPromise());
4932
+ .toPromise()));
4832
4933
  }
4833
4934
  async _performUploadAction(config) {
4834
- return await this.handlePerformActionResponse(await this.view
4935
+ return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view
4835
4936
  .uploadAction(config.uuid, JSON.stringify(config.uiActionRequest), config.param, config.content)
4836
- .toPromise());
4937
+ .toPromise()));
4837
4938
  }
4838
4939
  async _performUploadMultipleAction(config) {
4839
- return await this.handlePerformActionResponse(await this.view
4940
+ return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view
4840
4941
  .uploadMultipleAction(config.uuid, JSON.stringify(config.uiActionRequest), config.param, config.content)
4841
- .toPromise());
4842
- }
4843
- async _dataChanged(config) {
4844
- return await this.handlePerformActionResponse(await this.view.dataChanged(config.uuid, config.dataChange).toPromise());
4942
+ .toPromise()));
4845
4943
  }
4846
4944
  async handlePerformActionResponse(viewContextChange) {
4847
4945
  this.viewContext = viewContextChange?.viewContext;
@@ -4881,6 +4979,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
4881
4979
  providedIn: 'root',
4882
4980
  }]
4883
4981
  }], ctorParameters: () => [] });
4982
+ /** Reads the value at a dotted path; a missing segment yields undefined. */
4983
+ function getDeep(model, key) {
4984
+ let value = model;
4985
+ for (const segment of key.split('.')) {
4986
+ if (value === undefined || value === null) {
4987
+ return undefined;
4988
+ }
4989
+ value = value[segment];
4990
+ }
4991
+ return value;
4992
+ }
4993
+ /** Writes the value at a dotted path, creating the objects on the way. */
4994
+ function setDeep(model, key, value) {
4995
+ const segments = key.split('.');
4996
+ let target = model;
4997
+ for (const segment of segments.slice(0, -1)) {
4998
+ if (target[segment] === undefined || target[segment] === null) {
4999
+ target[segment] = {};
5000
+ }
5001
+ target = target[segment];
5002
+ }
5003
+ target[segments[segments.length - 1]] = value;
5004
+ }
5005
+ /** The first path in the value that structuredClone refuses (a function, a DOM node), if any. */
5006
+ function uncloneablePath(value, path = '') {
5007
+ if (typeof value === 'function' || typeof value === 'symbol') {
5008
+ return path || '<root>';
5009
+ }
5010
+ if (typeof Node !== 'undefined' && value instanceof Node) {
5011
+ return path || '<root>';
5012
+ }
5013
+ if (value !== null && typeof value === 'object' && !(value instanceof Date)) {
5014
+ for (const [k, v] of Object.entries(value)) {
5015
+ const found = uncloneablePath(v, path ? `${path}.${k}` : k);
5016
+ if (found) {
5017
+ return found;
5018
+ }
5019
+ }
5020
+ }
5021
+ return undefined;
5022
+ }
4884
5023
  const IS_ASYNC_PARAM_NAME = 'isAsync';
4885
5024
  class ApiQueueService {
4886
5025
  constructor() {
@@ -5133,6 +5272,10 @@ const UI_ACTION_CONFIRM_DIALOG_SERVICE = new InjectionToken('UI_ACTION_CONFIRM_D
5133
5272
  const UI_ACTION_INPUT_DIALOG_SERVICE = new InjectionToken('UI_ACTION_INPUT_DIALOG_SERVICE');
5134
5273
  const UI_ACTION_FILE_UPLOAD_DIALOG_SERVICE = new InjectionToken('UI_ACTION_FILE_UPLOAD_DIALOG_SERVICE');
5135
5274
 
5275
+ /** A view's own client, as opposed to an executor standing in for another API. */
5276
+ function isViewClient(executor) {
5277
+ return typeof executor.uuid === 'string';
5278
+ }
5136
5279
  class UiActionService {
5137
5280
  constructor() {
5138
5281
  this.confirmDialogService = inject(UI_ACTION_CONFIRM_DIALOG_SERVICE);
@@ -5168,14 +5311,14 @@ class UiActionService {
5168
5311
  identifier: uiAction.identifier,
5169
5312
  params: uiAction.params ?? {},
5170
5313
  };
5171
- if (uiAction.submit || uiAction.model) {
5172
- // submit: with validation, model: without validation. both can be undefined
5173
- const validate = uiAction.submit || !uiAction.model;
5174
- executor.submitForm(validate);
5314
+ // submit: validate before sending, the embedded views' forms included. The model itself
5315
+ // travels by model sync whatever the flags say; the deprecated `model` flag only keeps
5316
+ // flushing the form of an executor that is not a view.
5317
+ if (uiAction.submit) {
5318
+ executor.submitForm(true);
5175
5319
  let invalidFields = executor.getInvalidFields();
5176
- const containerUuid = executor.uuid;
5177
- if (uiAction.composite && containerUuid) {
5178
- const embeddedInvalid = this.viewContext.submitEmbeddedForms(containerUuid, validate);
5320
+ if (isViewClient(executor)) {
5321
+ const embeddedInvalid = this.viewContext.validateEmbeddedForms(executor.uuid);
5179
5322
  invalidFields = {
5180
5323
  invalidFieldKeys: [...invalidFields.invalidFieldKeys, ...embeddedInvalid.invalidFieldKeys],
5181
5324
  invalidFieldNames: [
@@ -5184,11 +5327,14 @@ class UiActionService {
5184
5327
  ],
5185
5328
  };
5186
5329
  }
5187
- if (validate && invalidFields.invalidFieldKeys.length) {
5330
+ if (invalidFields.invalidFieldKeys.length) {
5188
5331
  this.showInvalidFieldsSnackbar(invalidFields);
5189
5332
  return;
5190
5333
  }
5191
5334
  }
5335
+ else if (uiAction.model && !isViewClient(executor)) {
5336
+ executor.submitForm(false);
5337
+ }
5192
5338
  if (uiAction.confirm) {
5193
5339
  this.confirmDialogService.action = uiAction;
5194
5340
  this.confirmDialogService.openDialog();
@@ -5293,26 +5439,10 @@ class UiActionService {
5293
5439
  return uiActionRequest;
5294
5440
  }
5295
5441
  async performAction(uiAction, uiActionRequest, uiActionModel, executor, options) {
5296
- if (!uiActionRequest.params ||
5297
- (uiActionRequest.params && !Object.keys(uiActionRequest.params).length)) {
5298
- uiActionRequest.params = {
5299
- model: executor.getModel(),
5300
- };
5301
- }
5302
- else if (uiAction.submit || uiAction.model) {
5303
- if (options?.widgetId && options?.nodeId) {
5304
- uiActionRequest.params['clientPageModel'] = { data: executor.getModel() };
5305
- }
5306
- else {
5307
- uiActionRequest.params['model'] = executor.getModel();
5308
- }
5309
- }
5310
- if (uiAction.composite) {
5311
- const containerUuid = executor.uuid;
5312
- if (containerUuid) {
5313
- uiActionRequest.params['compositeModel'] =
5314
- this.viewContext.getCompositeModel(containerUuid);
5315
- }
5442
+ // A view's model goes by model sync (SmartViewContextService). An executor that is not a
5443
+ // view (tree, filter editor) has no view to sync, so its model still rides on the request.
5444
+ if (!isViewClient(executor) && (uiAction.submit || uiAction.model)) {
5445
+ uiActionRequest.params['model'] = executor.getModel();
5316
5446
  }
5317
5447
  try {
5318
5448
  await executor.performUiActionRequest(uiActionRequest, options?.widgetId, options?.nodeId);
@@ -6418,6 +6548,14 @@ class SmartFormService {
6418
6548
  this.revision = this.revisionState.asReadonly();
6419
6549
  this.emptyKey = 'e#m%p@t*y';
6420
6550
  this.group = {};
6551
+ /**
6552
+ * The model value each control last agreed with, by control key: what the control was built
6553
+ * or refreshed from, or what it last wrote into the data. A control whose value still equals
6554
+ * it holds no edit of the user's, and a data value that no longer equals it was changed by
6555
+ * the backend. The form's own dirty flag is not this: it lives until the form is rebuilt and
6556
+ * says nothing about what the data holds.
6557
+ */
6558
+ this.exchanged = new Map();
6421
6559
  this.regexRegexChars = new RegExp(/[\.\-\[\]\/\{\}\(\)\+\^\$\\\|]/g);
6422
6560
  this.regexAsteriskInTheMiddle = new RegExp(/([^\*])\*([^\*])/g);
6423
6561
  this.regexAsteriskInTheEnd = new RegExp(/([^\*])\*$/g);
@@ -6432,6 +6570,10 @@ class SmartFormService {
6432
6570
  widgetsChanged() {
6433
6571
  this.revisionState.update((revision) => revision + 1);
6434
6572
  }
6573
+ /** Forgets the agreed values; the form is being rebuilt from the model. */
6574
+ resetExchangedValues() {
6575
+ this.exchanged.clear();
6576
+ }
6435
6577
  getSmartFormWidgetByKey(key) {
6436
6578
  return this.findSmartFormWidgetByKey(this.smartForm?.widgets, key);
6437
6579
  }
@@ -6556,6 +6698,12 @@ class SmartFormService {
6556
6698
  widget = this.findSmartFormWidgetByKey(widgets, key);
6557
6699
  if (widget) {
6558
6700
  let value = this.getValueDeeply(data, key);
6701
+ // A data value the control already agreed with was not pushed: the control keeps
6702
+ // what it shows, which may be the user's edit.
6703
+ if (this.exchanged.has(key) && deepEqual(value, this.exchanged.get(key))) {
6704
+ continue;
6705
+ }
6706
+ this.exchanged.set(key, structuredClone(value));
6559
6707
  if (widget.type === SmartFormWidgetType.CHECK_BOX && typeof value !== 'boolean') {
6560
6708
  value = value === 'true';
6561
6709
  }
@@ -6575,6 +6723,8 @@ class SmartFormService {
6575
6723
  if (widget.showLabel === undefined && smartForm.showLabel) {
6576
6724
  widget.showLabel = true;
6577
6725
  }
6726
+ // The backend changed this field: its value reaches the control even over an edit.
6727
+ this.group[key]?.setValue(value ?? '', { emitEvent: false });
6578
6728
  }
6579
6729
  this.copyFromValueSetToValuesList(widget, smartForm.componentModel.valueSets);
6580
6730
  }
@@ -6734,7 +6884,7 @@ class SmartFormService {
6734
6884
  formControl = new UntypedFormControl();
6735
6885
  this.group[key] = formControl;
6736
6886
  }
6737
- formControl.setValue(value || '', { emitEvent: false });
6887
+ formControl.setValue(value ?? '', { emitEvent: false });
6738
6888
  formControl.clearValidators();
6739
6889
  validators?.map((validator) => {
6740
6890
  formControl.addValidators(validator.validator);
@@ -6800,15 +6950,29 @@ class SmartFormService {
6800
6950
  }
6801
6951
  });
6802
6952
  }
6953
+ /**
6954
+ * Copies the user's edits from the controls into the data: only a control whose value differs
6955
+ * from what it last agreed with writes, typed as the data holds the field. Nothing is
6956
+ * submitted; a control the user never touched leaves its field as the backend sent it.
6957
+ */
6803
6958
  toSmartForm(group) {
6804
6959
  if (this.smartForm) {
6805
6960
  this.copyValueFromFormToWidgetBeans(this.smartForm.widgets, group);
6806
6961
  const data = this.smartForm.componentModel?.data;
6807
6962
  if (data) {
6808
6963
  for (const key of this.getPropertyNamesDeeply(data)) {
6809
- if (group.controls[key]) {
6810
- this.setValueDeeply(data, key, group.controls[key].value);
6964
+ if (!group.controls[key]) {
6965
+ continue;
6966
+ }
6967
+ const agreed = this.exchanged.has(key)
6968
+ ? this.exchanged.get(key)
6969
+ : this.getValueDeeply(data, key);
6970
+ const value = coerceToTypeOf(agreed, group.controls[key].value);
6971
+ if (this.exchanged.has(key) && deepEqual(value, agreed)) {
6972
+ continue;
6811
6973
  }
6974
+ this.setValueDeeply(data, key, value);
6975
+ this.exchanged.set(key, structuredClone(this.getValueDeeply(data, key)));
6812
6976
  }
6813
6977
  }
6814
6978
  }
@@ -6820,12 +6984,14 @@ class SmartFormService {
6820
6984
  }
6821
6985
  let originalModel = this.smartForm.componentModel.data;
6822
6986
  let copyOfModel = { ...originalModel };
6987
+ const exchangedBefore = new Map(this.exchanged);
6823
6988
  // copyOfModel should be the current model
6824
6989
  this.smartForm.componentModel.data = copyOfModel;
6825
6990
  // copy value from FormGroup into model
6826
6991
  this.toSmartForm(group);
6827
6992
  let newModel = this.smartForm.componentModel.data;
6828
6993
  this.smartForm.componentModel.data = originalModel;
6994
+ this.exchanged = exchangedBefore;
6829
6995
  return newModel;
6830
6996
  }
6831
6997
  applyComponentConstraints(widgets, constraints) {
@@ -7002,6 +7168,32 @@ function extractTime(widgetInstance) {
7002
7168
  }
7003
7169
  return time;
7004
7170
  }
7171
+ /**
7172
+ * A control's value in the type its field has in the data. The controls of a boolean or a
7173
+ * number shown as text hold strings; the data keeps the field's type across a round trip.
7174
+ */
7175
+ function coerceToTypeOf(reference, value) {
7176
+ if (typeof reference === 'boolean') {
7177
+ if (value === 'true') {
7178
+ return true;
7179
+ }
7180
+ if (value === 'false') {
7181
+ return false;
7182
+ }
7183
+ if (value === '') {
7184
+ return null;
7185
+ }
7186
+ }
7187
+ else if (typeof reference === 'number') {
7188
+ if (value === '') {
7189
+ return null;
7190
+ }
7191
+ if (typeof value === 'string' && value.trim() !== '' && !isNaN(Number(value))) {
7192
+ return Number(value);
7193
+ }
7194
+ }
7195
+ return value;
7196
+ }
7005
7197
 
7006
7198
  /*
7007
7199
  * Public API Surface of component-factory-service
@@ -10761,6 +10953,7 @@ class SmartformComponent {
10761
10953
  if (!this.originalWidgets || this.originalWidgets.length === 0) {
10762
10954
  this.originalWidgets = this.smartForm.widgets;
10763
10955
  }
10956
+ this.service.resetExchangedValues();
10764
10957
  this.service.setValuesFromModel(this.originalWidgets);
10765
10958
  this.service.applyConstraints(this.originalWidgets);
10766
10959
  this.smartForm.widgets = this.service.filterWidgetsByVisibility(this.originalWidgets);
@@ -11243,6 +11436,8 @@ class SmartGridToolbarActionsUtil {
11243
11436
  }
11244
11437
  }
11245
11438
 
11439
+ /** Id of the detail row's collapse animation, so a second leave call can find the running one. */
11440
+ const DETAIL_COLLAPSE = 'smart-table-detail-collapse';
11246
11441
  class Table {
11247
11442
  constructor() {
11248
11443
  this.cfService = inject(ComponentFactoryService);
@@ -11252,6 +11447,8 @@ class Table {
11252
11447
  this.smartTableButtonType = SmartTableButtonType;
11253
11448
  this.highlightedRows = [];
11254
11449
  this.sortEvent = new Subject();
11450
+ /** The `when` predicate of the detail row definition. */
11451
+ this.isExpandedRow = (_index, row) => row === this.expandedElement;
11255
11452
  // Cells to UiAction models, used for toolbars in cells
11256
11453
  this.cellToActionMap = {};
11257
11454
  this.onSelectionChanged = new Subject();
@@ -11260,15 +11457,45 @@ class Table {
11260
11457
  // ---- Toolbar ----
11261
11458
  this.defaultActionToolbarId = SmartGridToolbarActionsUtil.defaultActionToolbarId;
11262
11459
  this.rowMenuActionModelArrayCache = new Map();
11460
+ // ------------- TREE_GRID -------------
11461
+ this.expandedRowIds = new Set();
11462
+ this.visibleRows = [];
11463
+ this.rowsById = new Map();
11464
+ this.isTreeGrid = false;
11465
+ this.treeInitialized = false;
11263
11466
  }
11264
11467
  /** The address of the toolbar in this grid's header; `undefined` while it has no id. */
11265
11468
  get headerToolbarId() {
11266
11469
  return headerToolbarAddress(this.smartTable?.getGridId());
11267
11470
  }
11471
+ /**
11472
+ * `animate.leave` of the detail row: collapses the detail the way it expanded, then lets
11473
+ * the row go. A class on the detail itself would not do — the row is what leaves, and
11474
+ * Angular does not look for leave animations behind the `mat-row` component boundary.
11475
+ * Angular calls this once for detaching and once for destroying the row; both wait for
11476
+ * the one animation.
11477
+ */
11478
+ onDetailRowLeave(event) {
11479
+ const detail = event.target.querySelector('.smart-table-detail');
11480
+ if (!detail) {
11481
+ event.animationComplete();
11482
+ return;
11483
+ }
11484
+ const collapse = detail.getAnimations().find((animation) => animation.id === DETAIL_COLLAPSE) ??
11485
+ detail.animate([{ gridTemplateRows: '1fr' }, { gridTemplateRows: '0fr' }], {
11486
+ id: DETAIL_COLLAPSE,
11487
+ duration: 225,
11488
+ easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
11489
+ fill: 'forwards',
11490
+ });
11491
+ const done = () => event.animationComplete();
11492
+ collapse.finished.then(done, done);
11493
+ }
11268
11494
  ngOnInit() {
11269
11495
  if (this.smartTable.sortable)
11270
11496
  this.sortEvent = new Subject();
11271
11497
  this.createCellToActionMap();
11498
+ this.calcTree();
11272
11499
  }
11273
11500
  ngOnDestroy() {
11274
11501
  this._destroy$.next();
@@ -11298,6 +11525,7 @@ class Table {
11298
11525
  this.smartTable = smartTable;
11299
11526
  // Pushed in by the grid rather than read from it, so nothing else marks this view. The
11300
11527
  // pull the rest of the library moved to (ADR-0005) is the 7.1 grid/table merge.
11528
+ this.calcTree();
11301
11529
  this.changeDetector.markForCheck();
11302
11530
  this.renderRows();
11303
11531
  }
@@ -11621,24 +11849,27 @@ class Table {
11621
11849
  if (event) {
11622
11850
  event?.stopPropagation();
11623
11851
  }
11624
- let oldIndex = this.smartTable.tableRows.findIndex((element) => element === this.expandedElement);
11625
- if (oldIndex !== -1) {
11626
- let ref = this.vcRef.filter((element, index) => index === oldIndex);
11627
- ref[0].clear();
11628
- }
11629
11852
  this.expandedElement = this.expandedElement === element ? null : element;
11630
- let newIndex = this.smartTable.tableRows.findIndex((element) => element === this.expandedElement);
11853
+ // The row definitions are re-evaluated on render only: this drops the old detail row
11854
+ // (and the component in it) and creates the new one, then the query picks it up.
11855
+ this.myTableChild.renderRows();
11856
+ this.changeDetector.detectChanges();
11857
+ if (!this.expandedElement) {
11858
+ return;
11859
+ }
11860
+ const container = this.vcRef.first;
11631
11861
  let rowDataResponse;
11632
- if (newIndex !== -1) {
11633
- if (this.smartTable.asyncOnExpand) {
11634
- this.componentRef = this.cfService.createComponent(this.vcRef?.filter((element, index) => index === newIndex)[0], LoadingComponent, new Map());
11635
- rowDataResponse = await this.smartTable.rowExpander?.expandAsync(newIndex);
11636
- let ref = this.vcRef.filter((element, index) => index === newIndex)[0];
11637
- ref.clear();
11862
+ if (this.smartTable.asyncOnExpand) {
11863
+ this.componentRef = this.cfService.createComponent(container, LoadingComponent, new Map());
11864
+ rowDataResponse = await this.smartTable.rowExpander?.expandAsync(this.smartTable.tableRows.indexOf(element));
11865
+ // Another toggle while loading has already replaced this row's detail row.
11866
+ if (this.expandedElement !== element) {
11867
+ return;
11638
11868
  }
11639
- this.componentRef = this.cfService.createComponent(this.vcRef?.filter((element, index) => index === newIndex)[0], this.smartTable.expandedComponent, new Map([['rowData', rowDataResponse]]));
11640
- this.element = undefined;
11869
+ container.clear();
11641
11870
  }
11871
+ this.componentRef = this.cfService.createComponent(container, this.smartTable.expandedComponent, new Map([['rowData', rowDataResponse]]));
11872
+ this.element = undefined;
11642
11873
  }
11643
11874
  setSelection(element) {
11644
11875
  if (this.smartTable.maxNumOfSelectableRows &&
@@ -11785,6 +12016,143 @@ class Table {
11785
12016
  shouldShowMenuButton(row) {
11786
12017
  return SmartGridToolbarActionsUtil.showMenu(row, this.smartTable.tableHeaders);
11787
12018
  }
12019
+ initializeExpandedRows(rows) {
12020
+ if (this.treeInitialized) {
12021
+ return;
12022
+ }
12023
+ rows.forEach(row => {
12024
+ if (row.id && row.children?.length) {
12025
+ this.expandedRowIds.add(row.id);
12026
+ }
12027
+ });
12028
+ this.treeInitialized = true;
12029
+ }
12030
+ calcTree() {
12031
+ const rows = this.smartTable.tableRows ?? [];
12032
+ this.isTreeGrid = rows.some(row => !!row?.children?.length || !!row?.parent);
12033
+ if (!this.isTreeGrid) {
12034
+ this.visibleRows = rows;
12035
+ return;
12036
+ }
12037
+ // Open all rows on first tree initialization.
12038
+ this.initializeExpandedRows(rows);
12039
+ this.rowsById.clear();
12040
+ rows.forEach(row => {
12041
+ if (row.id) {
12042
+ this.rowsById.set(row.id, row);
12043
+ }
12044
+ });
12045
+ const roots = rows.filter(row => !row.parent);
12046
+ const result = [];
12047
+ const walk = (row) => {
12048
+ result.push(row);
12049
+ if (row.id &&
12050
+ row.children?.length &&
12051
+ this.expandedRowIds.has(row.id)) {
12052
+ row.children
12053
+ .map(childId => this.rowsById.get(childId))
12054
+ .filter((child) => !!child)
12055
+ .forEach(child => walk(child));
12056
+ }
12057
+ };
12058
+ roots.forEach(root => walk(root));
12059
+ this.visibleRows = result;
12060
+ }
12061
+ hasChildren(row) {
12062
+ return !!row?.children?.length;
12063
+ }
12064
+ isRowExpanded(row) {
12065
+ return !!row?.id && this.expandedRowIds.has(row.id);
12066
+ }
12067
+ toggleRowExpansion(row, event) {
12068
+ event?.stopPropagation();
12069
+ if (!row?.id) {
12070
+ return;
12071
+ }
12072
+ if (this.expandedRowIds.has(row.id)) {
12073
+ this.expandedRowIds.delete(row.id);
12074
+ }
12075
+ else {
12076
+ this.expandedRowIds.add(row.id);
12077
+ }
12078
+ this.calcTree();
12079
+ }
12080
+ canExpand() {
12081
+ return this.visibleRows.some(row => this.hasChildren(row) && !this.isRowExpanded(row));
12082
+ }
12083
+ canCollapse() {
12084
+ return this.expandedRowIds.size > 0;
12085
+ }
12086
+ expandOneLevel() {
12087
+ let changed = false;
12088
+ for (const row of this.visibleRows) {
12089
+ if (row.id && this.hasChildren(row) && !this.expandedRowIds.has(row.id)) {
12090
+ this.expandedRowIds.add(row.id);
12091
+ changed = true;
12092
+ }
12093
+ }
12094
+ if (changed) {
12095
+ this.calcTree();
12096
+ }
12097
+ }
12098
+ collapseOneLevel() {
12099
+ let maxLevel = -1;
12100
+ for (const row of this.visibleRows) {
12101
+ if (row.id && this.expandedRowIds.has(row.id)) {
12102
+ maxLevel = Math.max(maxLevel, row.level ?? 0);
12103
+ }
12104
+ }
12105
+ if (maxLevel < 0) {
12106
+ return;
12107
+ }
12108
+ let changed = false;
12109
+ for (const row of this.visibleRows) {
12110
+ if (row.id && this.expandedRowIds.has(row.id) && (row.level ?? 0) === maxLevel) {
12111
+ this.expandedRowIds.delete(row.id);
12112
+ changed = true;
12113
+ }
12114
+ }
12115
+ if (changed) {
12116
+ this.calcTree();
12117
+ }
12118
+ }
12119
+ expandAll() {
12120
+ let changed = false;
12121
+ (this.smartTable.tableRows ?? []).forEach(row => {
12122
+ if (row.id && this.hasChildren(row) && !this.expandedRowIds.has(row.id)) {
12123
+ this.expandedRowIds.add(row.id);
12124
+ changed = true;
12125
+ }
12126
+ });
12127
+ if (changed) {
12128
+ this.calcTree();
12129
+ }
12130
+ }
12131
+ collapseAll() {
12132
+ if (this.expandedRowIds.size === 0) {
12133
+ return;
12134
+ }
12135
+ this.expandedRowIds.clear();
12136
+ this.calcTree();
12137
+ }
12138
+ get expandTreeGridLevel() {
12139
+ return {
12140
+ title: '',
12141
+ color: 'primary',
12142
+ type: UiActionButtonType.MINI_FAB,
12143
+ icon: 'add',
12144
+ iconPosition: IconPosition.PRE,
12145
+ };
12146
+ }
12147
+ get closeTreeGridLevel() {
12148
+ return {
12149
+ title: '',
12150
+ color: 'primary',
12151
+ type: UiActionButtonType.MINI_FAB,
12152
+ icon: 'remove',
12153
+ iconPosition: IconPosition.PRE,
12154
+ };
12155
+ }
11788
12156
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: Table, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11789
12157
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: Table, isStandalone: true, selector: "ng-component", inputs: { smartTable: "smartTable" }, viewQueries: [{ propertyName: "myTableChild", first: true, predicate: ["myTable"], descendants: true }, { propertyName: "vcRef", predicate: ["expandedArea"], descendants: true, read: ViewContainerRef }, { propertyName: "defaultActionMenuComponents", predicate: DefaultActionsPopupComponent, descendants: true }, { propertyName: "triggers", predicate: MatMenuTrigger, descendants: true }], ngImport: i0, template: '', isInline: true }); }
11790
12158
  }
@@ -11845,23 +12213,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
11845
12213
 
11846
12214
  class MaterialTableComponent extends Table {
11847
12215
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MaterialTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
11848
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: MaterialTableComponent, isStandalone: true, selector: "lib-material-table", usesInheritance: true, ngImport: i0, template: "<table\n #myTable\n mat-table\n [dataSource]=\"smartTable.tableRows\"\n class=\"full-width\"\n multiTemplateDataRows\n >\n <!-- Column Descriptor -->\n @if (smartTable.title) {\n <caption class=\"captionTitle\">\n {{ smartTable.title }}\n </caption>\n }\n @for (header of smartTable.tableHeaders; track header; let i = $index) {\n <ng-container\n matColumnDef=\"{{ header }}\"\n >\n <!-- my_menu is the implicit action column present on all tables: -->\n @if ('my_menu' === header) {\n <th\n mat-header-cell\n *matHeaderCellDef\n [ngClass]=\"getColumnClasses(smartTable.customSmartTableHeaders![i])\"\n [ngStyle]=\"getColumnStyles(smartTable.customSmartTableHeaders![i])\"\n [attr.data-testid]=\"smartTable.customSmartTableHeaders![i].propertyName\"\n >\n <smart-ui-action-toolbar [id]=\"headerToolbarId\"></smart-ui-action-toolbar>\n </th>\n } @else {\n <th\n mat-header-cell\n *matHeaderCellDef\n [ngClass]=\"getColumnClasses(smartTable.customSmartTableHeaders![i])\"\n [ngStyle]=\"getColumnStyles(smartTable.customSmartTableHeaders![i])\"\n [attr.data-testid]=\"smartTable.customSmartTableHeaders![i].propertyName\"\n >\n @if (\n header === 'icon' || header === 'img' || header === 'options' || header === 'button'\n ) {\n <div\n ></div>\n }\n @if (header === 'select') {\n <div>\n @if (smartTable.customSmartTableHeaders![i].showCheckboxInHeader) {\n <mat-checkbox\n (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"smartTable.selection!.hasValue() && isAllSelected()\"\n [indeterminate]=\"smartTable.selection!.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\"\n >\n </mat-checkbox>\n }\n @if (!smartTable.customSmartTableHeaders![i].showCheckboxInHeader) {\n <div>\n {{ smartTable.customTableHeaders[i] }}\n </div>\n }\n </div>\n }\n @if (\n header !== 'icon' &&\n header !== 'img' &&\n header !== 'options' &&\n header !== 'button' &&\n header !== 'select' &&\n header !== 'expand' &&\n header !== 'actions'\n ) {\n <div\n >\n @if (smartTable.sortable) {\n @if (smartTable.sortable && isSortable(smartTable.customSmartTableHeaders![i])) {\n <button\n (click)=\"sortButtonClicked($event, smartTable.customSmartTableHeaders![i])\"\n mat-button\n class=\"sortableHeaderButton\"\n >\n {{ smartTable.customTableHeaders[i] }}\n @if (getSortIcon(header)) {\n <smart-icon\n class=\"sortableHeaderButtonIcon\"\n title=\"sort\"\n [icon]=\"getSortIcon(header)!\"\n ></smart-icon>\n }\n @if (hasSortNumIcon(header)) {\n <smart-icon\n class=\"sortableHeaderButtonIcon\"\n title=\"sort\"\n [icon]=\"getSortNumIcon(header)\"\n ></smart-icon>\n }\n </button>\n }\n } @else {\n {{ smartTable.customTableHeaders[i] }}\n }\n </div>\n }\n </th>\n }\n <td mat-cell *matCellDef=\"let element\" [ngClass]=\"isDisabled(element) ? 'disabledRow' : ''\">\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].propertyName === 'select' &&\n !isDisabled(element)\n ) {\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"\n $event\n ? setSelection(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n : null\n \"\n [disabled]=\"isDisabled(element)\"\n [checked]=\"\n smartTable.selection!.isSelected(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n \"\n [aria-label]=\"\n checkboxLabel(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n \"\n >\n </mat-checkbox>\n }\n @if (\n smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].properties\n ) {\n <div\n >\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().DATETIME) {\n <div>\n {{\n getValue(element, header)\n | smartDateTime: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().DATE) {\n <div>\n {{\n getValue(element, header)\n | smartDate: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().TIME) {\n <div>\n {{\n getValue(element, header)\n | smartTime: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().CHECKBOX) {\n <div>\n <mat-checkbox [disabled]=\"true\" [checked]=\"getValue(element, header)\"></mat-checkbox>\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders[i].properties?.type === type().ICON &&\n smartTable.customSmartTableHeaders[i].properties?.icons?.length\n ) {\n <div\n >\n <smart-icon\n [smartTooltip]=\"getToolTip(element, i)\"\n [icon]=\"getIcon(getValue(element, header), i)!\"\n [color]=\"getColor(getValue(element, header), i)\"\n >\n </smart-icon>\n </div>\n }\n </div>\n }\n <div class=\"smart-table-icon-container\">\n @for (ir of getImageResourceIcons(element, header); track ir) {\n <div>\n <smart-icon [imageResource]=\"ir\"> </smart-icon>\n </div>\n }\n </div>\n @if (smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].buttons) {\n <div\n class=\"smart-table-buttons-col\"\n >\n @for (button of smartTable.customSmartTableHeaders[i].buttons; track button) {\n <div>\n @if (showButton(button, element)) {\n <div>\n @switch (button.type) {\n @case (smartTableButtonType.ICON) {\n <button\n (click)=\"customButtonClicked($event, button, element)\"\n mat-icon-button\n color=\"{{ button.color }}\"\n >\n <smart-icon title=\"{{ button.label }}\" [icon]=\"button.icon!\"></smart-icon>\n </button>\n }\n @case (smartTableButtonType.NORMAL) {\n <button\n (click)=\"customButtonClicked($event, button, element)\"\n mat-button\n color=\"{{ button.color }}\"\n >\n @if (button.icon) {\n <smart-icon [icon]=\"button.icon\"></smart-icon>\n }\n {{ button.label ?? (button.translator ? button.translator(element).title : '') }}\n </button>\n }\n @case (smartTableButtonType.RAISED) {\n <button\n (click)=\"customButtonClicked($event, button, element)\"\n mat-raised-button\n color=\"{{ button.color }}\"\n >\n @if (button.icon) {\n <smart-icon [icon]=\"button.icon\"></smart-icon>\n }\n {{ button.label ?? (button.translator ? button.translator(element).title : '') }}\n </button>\n }\n <!------ MENU ------>\n @case (smartTableButtonType.MENU) {\n <div class=\"menu-button\">\n <!------ DEFAULT_ACTION_COLUMN ID TOOLBAR ------>\n <smart-ui-action-toolbar\n [uiActionModels]=\"getRowColumnAction(element, defaultActionToolbarId)\"\n [widgetId]=\"smartTable.getGridId()\"\n [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"\n ></smart-ui-action-toolbar>\n <!------ GENERIC HAMBURGER ------>\n <!------ TOOLBAR ------>\n @if (shouldShowMenuButton(element)) {\n <smart-ui-action-toolbar\n [uiActionModels]=\"getMenuActions(element)\"\n [widgetId]=\"smartTable.getGridId()\"\n [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"\n ></smart-ui-action-toolbar>\n }\n </div>\n }\n <!------ MENU ------>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n @if (smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].icon) {\n <div>\n @if (smartTable.customSmartTableHeaders[i].icon?.icon) {\n <smart-icon\n [ngClass]=\"smartTable.customSmartTableHeaders[i].icon?.cssClass ?? ''\"\n [color]=\"$safeNavigationMigration(smartTable.customSmartTableHeaders[i].icon?.color)\"\n [icon]=\"smartTable.customSmartTableHeaders[i].icon!.icon\"\n >\n </smart-icon>\n }\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].translator !== undefined\n ) {\n <div\n >\n {{ smartTable.customSmartTableHeaders[i].translator!(getValue(element, header)) }}\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].propertyName === 'expand'\n ) {\n <button\n mat-icon-button\n aria-label=\"expand row\"\n (click)=\"onToggle(element, $event)\"\n >\n @if (expandedElement !== element) {\n <smart-icon [icon]=\"'keyboard_arrow_down'\"></smart-icon>\n }\n @if (expandedElement === element) {\n <smart-icon [icon]=\"'keyboard_arrow_up'\"></smart-icon>\n }\n </button>\n }\n @if (\n !smartTable.customSmartTableHeaders ||\n (smartTable.customSmartTableHeaders &&\n !smartTable.customSmartTableHeaders[i].properties &&\n !smartTable.customSmartTableHeaders[i].icon &&\n !smartTable.customSmartTableHeaders[i].buttons &&\n !smartTable.customSmartTableHeaders[i].translator &&\n !(smartTable.customSmartTableHeaders[i].propertyName === 'select') &&\n !(smartTable.customSmartTableHeaders[i].propertyName === 'expand'))\n ) {\n <div\n >\n @if (header === 'icon') {\n <smart-icon [icon]=\"getValue(element, header)!\"> </smart-icon>\n }\n @if (header === 'img') {\n <img\n [src]=\"getValue(element, header)\"\n alt=\"\"\n class=\"smarttableImg\"\n />\n }\n <!------ TOOLBAR ------>\n @if (showCellToolbar(element, header)) {\n <smart-ui-action-toolbar\n [uiActionModels]=\"getRowColumnAction(element, header)\"\n [widgetId]=\"smartTable.getGridId()\"\n [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"\n ></smart-ui-action-toolbar>\n }\n <!------ TOOLBAR ------>\n @if (\n header !== 'icon' &&\n header !== 'img' &&\n header !== 'option' &&\n header !== 'button' &&\n !isImageResource(element, header)\n ) {\n <div\n [innerHtml]=\"getValue(element, header)\"\n ></div>\n }\n </div>\n }\n </td>\n </ng-container>\n }\n\n <!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->\n <ng-container matColumnDef=\"expandedDetail\">\n <td mat-cell *matCellDef=\"let element\" [attr.colspan]=\"smartTable.tableHeaders.length\">\n <div\n class=\"example-element-detail\"\n [@detailExpand]=\"element == expandedElement ? 'expanded' : 'collapsed'\"\n >\n <ng-template #expandedArea></ng-template>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"smartTable.tableHeaders; sticky: true\"></tr>\n <ng-container *matRowDef=\"let element; columns: smartTable.tableHeaders\">\n <tr\n mat-row\n class=\"example-element-row\"\n [class.example-expanded-row]=\"expandedElement === element\"\n [ngClass]=\"getRowClasses(element)\"\n [ngStyle]=\"getRowStyles(element)\"\n (click)=\"handleOnRowClick(element)\"\n (dblclick)=\"handleOnRowDoubleClick($event, element)\"\n [attr.data-testid]=\"element?.id ?? null\"\n ></tr>\n @if (smartTable.defaultActionCodes && smartTable.defaultActionCodes.length > 0) {\n <lib-default-actions-popup\n #defaultActionMenu\n [buttons]=\"getDefaultActionsForRow(element)!\"\n [row]=\"element\"\n ></lib-default-actions-popup>\n }\n </ng-container>\n <tr mat-row *matRowDef=\"let row; columns: ['expandedDetail']\" class=\"example-detail-row\"></tr>\n</table>\n", styles: [".full-width{width:100%}.smarttableImg{width:25px}.smartTableRowHover:hover{cursor:pointer}tr.example-detail-row{height:0}tr.example-element-row:not(.example-expanded-row):hover{background:#f5f5f5}tr.example-element-row:not(.example-expanded-row):active{background:#efefef}.example-element-row td{border-bottom-width:0}.example-element-detail{overflow:hidden;display:flex;flex-direction:column}.example-element-diagram{min-width:80px;border:2px solid black;padding:8px;font-weight:lighter;margin:8px 0;height:104px}.example-element-symbol{font-weight:700;font-size:40px;line-height:normal}.example-element-description{padding:16px}.example-element-description-attribution{opacity:.5}.disabledRow{color:var(--disabled)}.disabledRow:hover{cursor:default}.smart-table-buttons-col{display:flex;flex-direction:row;justify-content:flex-end}.sortableHeaderButton{margin:0!important;padding:0!important;text-align:left!important}.selected{background-color:var(--primary-light-color)}.smart-table-icon-container{display:flex;flex-direction:row;justify-content:space-between;white-space:initial}.reversed{flex-direction:row-reverse;gap:1rem}:host ::ng-deep .mat-mdc-menu-item{line-height:normal!important}.mat-mdc-menu-item[disabled]{cursor:default!important}.menu-button{display:flex;flex-direction:row;justify-content:flex-end;text-align:-webkit-right;align-items:center}.captionTitle{font-size:1.5rem;align-content:center;padding:.5rem .75rem;text-align:start;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);background:#f5f5f5}\n"], dependencies: [{ kind: "component", type: MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"] }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: SmartIconComponent, selector: "smart-icon", inputs: ["icon", "color", "imageResource"] }, { kind: "directive", type: MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: SmartTooltipDirective, selector: "[smartTooltip]", inputs: ["smartTooltip"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "component", type: MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "directive", type: MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "component", type: MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: DefaultActionsPopupComponent, selector: "lib-default-actions-popup", inputs: ["buttons", "row", "colIdx"] }, { kind: "pipe", type: SmartDateTimePipe, name: "smartDateTime" }, { kind: "pipe", type: SmartDatePipe, name: "smartDate" }, { kind: "pipe", type: SmartTimePipe, name: "smartTime" }], animations: [
11849
- trigger('detailExpand', [
11850
- state('collapsed', style({ height: '0px', minHeight: '0' })),
11851
- state('expanded', style({ height: '*' })),
11852
- transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
11853
- ]),
11854
- ] }); }
12216
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: MaterialTableComponent, isStandalone: true, selector: "lib-material-table", usesInheritance: true, ngImport: i0, template: "<table #myTable mat-table [dataSource]=\"isTreeGrid ? visibleRows : smartTable.tableRows\" class=\"full-width\"\n multiTemplateDataRows>\n <!-- Column Descriptor -->\n @if (smartTable.title) {\n <caption class=\"captionTitle\">\n {{ smartTable.title }}\n </caption>\n }\n\n @for (header of smartTable.tableHeaders; track header; let i = $index) {\n\n <ng-container matColumnDef=\"{{ header }}\">\n <!-- my_menu is the implicit action column present on all tables: -->\n @if ('my_menu' === header) {\n <th mat-header-cell *matHeaderCellDef [ngClass]=\"getColumnClasses(smartTable.customSmartTableHeaders![i])\"\n [ngStyle]=\"getColumnStyles(smartTable.customSmartTableHeaders![i])\"\n [attr.data-testid]=\"smartTable.customSmartTableHeaders![i].propertyName\">\n\n <!-- ==================== TREE ACTIONS ==================== -->\n @if ( isTreeGrid) {\n <div class=\"tree-grid-actions\">\n\n <ui-action-button (actionClick)=\"expandOneLevel()\" [descriptor]=\"expandTreeGridLevel\">\n </ui-action-button>\n\n\n <ui-action-button (actionClick)=\"collapseOneLevel()\" [descriptor]=\"closeTreeGridLevel\">\n </ui-action-button>\n </div>\n }\n <!-- ==================== TREE ACTIONS ==================== -->\n\n <smart-ui-action-toolbar [id]=\"headerToolbarId\"></smart-ui-action-toolbar>\n </th>\n } @else {\n <th mat-header-cell *matHeaderCellDef [ngClass]=\"getColumnClasses(smartTable.customSmartTableHeaders![i])\"\n [ngStyle]=\"getColumnStyles(smartTable.customSmartTableHeaders![i])\"\n [attr.data-testid]=\"smartTable.customSmartTableHeaders![i].propertyName\">\n @if (\n header === 'icon' || header === 'img' || header === 'options' || header === 'button'\n ) {\n <div></div>\n }\n @if (header === 'select') {\n <div>\n @if (smartTable.customSmartTableHeaders![i].showCheckboxInHeader) {\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"smartTable.selection!.hasValue() && isAllSelected()\"\n [indeterminate]=\"smartTable.selection!.hasValue() && !isAllSelected()\" [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n }\n @if (!smartTable.customSmartTableHeaders![i].showCheckboxInHeader) {\n <div>\n {{ smartTable.customTableHeaders[i] }}\n </div>\n }\n </div>\n }\n @if (\n header !== 'icon' &&\n header !== 'img' &&\n header !== 'options' &&\n header !== 'button' &&\n header !== 'select' &&\n header !== 'expand' &&\n header !== 'actions'\n ) {\n <div>\n @if (smartTable.sortable) {\n @if (smartTable.sortable && isSortable(smartTable.customSmartTableHeaders![i])) {\n <button (click)=\"sortButtonClicked($event, smartTable.customSmartTableHeaders![i])\" mat-button\n class=\"sortableHeaderButton\">\n {{ smartTable.customTableHeaders[i] }}\n @if (getSortIcon(header)) {\n <smart-icon class=\"sortableHeaderButtonIcon\" title=\"sort\" [icon]=\"getSortIcon(header)!\"></smart-icon>\n }\n @if (hasSortNumIcon(header)) {\n <smart-icon class=\"sortableHeaderButtonIcon\" title=\"sort\" [icon]=\"getSortNumIcon(header)\"></smart-icon>\n }\n </button>\n }\n } @else {\n {{ smartTable.customTableHeaders[i] }}\n }\n </div>\n }\n </th>\n }\n\n <td mat-cell *matCellDef=\"let element\" [ngClass]=\"isDisabled(element) ? 'disabledRow' : ''\">\n <div class=\"mat-cell-content\">\n\n\n <!-- ==================== TREE TOGGLE ==================== -->\n @if (isTreeGrid && i === 0) {\n <div class=\"tree-grid-first-column\" [style.padding-left.px]=\"(element.level ?? 0) * 24\">\n @if (hasChildren(element)) {\n <button mat-icon-button type=\"button\" (click)=\"toggleRowExpansion(element, $event)\">\n <smart-icon [icon]=\"\n isRowExpanded(element)\n ? 'keyboard_arrow_down'\n : 'keyboard_arrow_right'\n \"></smart-icon>\n </button>\n } @else {\n <span class=\"tree-grid-spacer\"></span>\n }\n\n </div>\n }\n <!-- ==================== TREE TOGGLE ==================== -->\n\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].propertyName === 'select' &&\n !isDisabled(element)\n ) {\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"\n $event\n ? setSelection(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n : null\n \" [disabled]=\"isDisabled(element)\" [checked]=\"\n smartTable.selection!.isSelected(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n \" [aria-label]=\"\n checkboxLabel(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n \">\n </mat-checkbox>\n }\n @if (\n smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].properties\n ) {\n <div>\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().DATETIME) {\n <div>\n {{\n getValue(element, header)\n | smartDateTime: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().DATE) {\n <div>\n {{\n getValue(element, header)\n | smartDate: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().TIME) {\n <div>\n {{\n getValue(element, header)\n | smartTime: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().CHECKBOX) {\n <div>\n <mat-checkbox [disabled]=\"true\" [checked]=\"getValue(element, header)\"></mat-checkbox>\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders[i].properties?.type === type().ICON &&\n smartTable.customSmartTableHeaders[i].properties?.icons?.length\n ) {\n <div>\n <smart-icon [smartTooltip]=\"getToolTip(element, i)\" [icon]=\"getIcon(getValue(element, header), i)!\"\n [color]=\"getColor(getValue(element, header), i)\">\n </smart-icon>\n </div>\n }\n </div>\n }\n <div class=\"smart-table-icon-container\">\n @for (ir of getImageResourceIcons(element, header); track ir) {\n <div>\n <smart-icon [imageResource]=\"ir\"> </smart-icon>\n </div>\n }\n </div>\n @if (smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].buttons) {\n <div class=\"smart-table-buttons-col\">\n @for (button of smartTable.customSmartTableHeaders[i].buttons; track button) {\n <div>\n @if (showButton(button, element)) {\n <div>\n @switch (button.type) {\n @case (smartTableButtonType.ICON) {\n <button (click)=\"customButtonClicked($event, button, element)\" mat-icon-button color=\"{{ button.color }}\">\n <smart-icon title=\"{{ button.label }}\" [icon]=\"button.icon!\"></smart-icon>\n </button>\n }\n @case (smartTableButtonType.NORMAL) {\n <button (click)=\"customButtonClicked($event, button, element)\" mat-button color=\"{{ button.color }}\">\n @if (button.icon) {\n <smart-icon [icon]=\"button.icon\"></smart-icon>\n }\n {{ button.label ?? (button.translator ? button.translator(element).title : '') }}\n </button>\n }\n @case (smartTableButtonType.RAISED) {\n <button (click)=\"customButtonClicked($event, button, element)\" mat-raised-button\n color=\"{{ button.color }}\">\n @if (button.icon) {\n <smart-icon [icon]=\"button.icon\"></smart-icon>\n }\n {{ button.label ?? (button.translator ? button.translator(element).title : '') }}\n </button>\n }\n <!------ MENU ------>\n @case (smartTableButtonType.MENU) {\n <div class=\"menu-button\">\n <!------ DEFAULT_ACTION_COLUMN ID TOOLBAR ------>\n <smart-ui-action-toolbar [uiActionModels]=\"getRowColumnAction(element, defaultActionToolbarId)\"\n [widgetId]=\"smartTable.getGridId()\" [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"></smart-ui-action-toolbar>\n <!------ GENERIC HAMBURGER ------>\n <!------ TOOLBAR ------>\n @if (shouldShowMenuButton(element)) {\n <smart-ui-action-toolbar [uiActionModels]=\"getMenuActions(element)\" [widgetId]=\"smartTable.getGridId()\"\n [nodeId]=\"element.id\" [actionParams]=\"{ model: element }\"></smart-ui-action-toolbar>\n }\n </div>\n }\n <!------ MENU ------>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n @if (smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].icon) {\n <div>\n @if (smartTable.customSmartTableHeaders[i].icon?.icon) {\n <smart-icon [ngClass]=\"smartTable.customSmartTableHeaders[i].icon?.cssClass ?? ''\"\n [color]=\"$safeNavigationMigration(smartTable.customSmartTableHeaders[i].icon?.color)\"\n [icon]=\"smartTable.customSmartTableHeaders[i].icon!.icon\">\n </smart-icon>\n }\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].translator !== undefined\n ) {\n <div>\n {{ smartTable.customSmartTableHeaders[i].translator!(getValue(element, header)) }}\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].propertyName === 'expand'\n ) {\n <button mat-icon-button aria-label=\"expand row\" (click)=\"onToggle(element, $event)\">\n @if (expandedElement !== element) {\n <smart-icon [icon]=\"'keyboard_arrow_down'\"></smart-icon>\n }\n @if (expandedElement === element) {\n <smart-icon [icon]=\"'keyboard_arrow_up'\"></smart-icon>\n }\n </button>\n }\n @if (\n !smartTable.customSmartTableHeaders ||\n (smartTable.customSmartTableHeaders &&\n !smartTable.customSmartTableHeaders[i].properties &&\n !smartTable.customSmartTableHeaders[i].icon &&\n !smartTable.customSmartTableHeaders[i].buttons &&\n !smartTable.customSmartTableHeaders[i].translator &&\n !(smartTable.customSmartTableHeaders[i].propertyName === 'select') &&\n !(smartTable.customSmartTableHeaders[i].propertyName === 'expand'))\n ) {\n <div>\n @if (header === 'icon') {\n <smart-icon [icon]=\"getValue(element, header)!\"> </smart-icon>\n }\n @if (header === 'img') {\n <img [src]=\"getValue(element, header)\" alt=\"\" class=\"smarttableImg\" />\n }\n <!------ TOOLBAR ------>\n @if (showCellToolbar(element, header)) {\n <smart-ui-action-toolbar [uiActionModels]=\"getRowColumnAction(element, header)\"\n [widgetId]=\"smartTable.getGridId()\" [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"></smart-ui-action-toolbar>\n }\n <!------ TOOLBAR ------>\n @if (\n header !== 'icon' &&\n header !== 'img' &&\n header !== 'option' &&\n header !== 'button' &&\n !isImageResource(element, header)\n ) {\n <div [innerHtml]=\"getValue(element, header)\"></div>\n }\n </div>\n }\n\n </div>\n </td>\n </ng-container>\n }\n\n <!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->\n @if (smartTable.expandable) {\n <ng-container matColumnDef=\"expandedDetail\">\n <td mat-cell *matCellDef=\"let element\" [attr.colspan]=\"smartTable.tableHeaders.length\">\n <div class=\"smart-table-detail\" animate.enter=\"smart-table-detail-enter\">\n <div class=\"smart-table-detail-content\">\n <ng-template #expandedArea></ng-template>\n </div>\n </div>\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"smartTable.tableHeaders; sticky: true\"></tr>\n <ng-container *matRowDef=\"let element; columns: smartTable.tableHeaders\">\n <tr mat-row class=\"smart-table-row\" [class.smart-table-row-expanded]=\"expandedElement === element\"\n [ngClass]=\"getRowClasses(element)\" [ngStyle]=\"getRowStyles(element)\" (click)=\"handleOnRowClick(element)\"\n (dblclick)=\"handleOnRowDoubleClick($event, element)\" [attr.data-testid]=\"element?.id ?? null\"></tr>\n\n @if (smartTable.defaultActionCodes && smartTable.defaultActionCodes.length > 0) {\n <lib-default-actions-popup #defaultActionMenu [buttons]=\"getDefaultActionsForRow(element)!\"\n [row]=\"element\"></lib-default-actions-popup>\n }\n </ng-container>\n <!-- Only the expanded row has a detail row; no host style can give a collapsed one a height. -->\n @if (smartTable.expandable) {\n <tr mat-row *matRowDef=\"let row; columns: ['expandedDetail']; when: isExpandedRow\" class=\"smart-table-detail-row\"\n (animate.leave)=\"onDetailRowLeave($event)\"></tr>\n }\n</table>", styles: [".full-width{width:100%}.smarttableImg{width:25px}.smartTableRowHover:hover{cursor:pointer}tr.smart-table-row:not(.smart-table-row-expanded):hover{background:#f5f5f5}tr.smart-table-row:not(.smart-table-row-expanded):active{background:#efefef}tr.smart-table-row-expanded td{border-bottom-width:0}tr.smart-table-detail-row{height:0}.smart-table-detail{display:grid;grid-template-rows:1fr}.smart-table-detail-enter{animation:smart-table-detail-expand 225ms cubic-bezier(.4,0,.2,1)}.smart-table-detail-content{min-height:0;overflow:hidden}@keyframes smart-table-detail-expand{0%{grid-template-rows:0fr}to{grid-template-rows:1fr}}.disabledRow{color:var(--disabled)}.disabledRow:hover{cursor:default}.smart-table-buttons-col{display:flex;flex-direction:row;justify-content:flex-end}.sortableHeaderButton{margin:0!important;padding:0!important;text-align:left!important}.selected{background-color:var(--primary-light-color)}.smart-table-icon-container{display:flex;flex-direction:row;justify-content:space-between;white-space:initial}.reversed{flex-direction:row-reverse;gap:1rem}:host ::ng-deep .mat-mdc-menu-item{line-height:normal!important}.mat-mdc-menu-item[disabled]{cursor:default!important}.menu-button{display:flex;flex-direction:row;justify-content:flex-end;text-align:-webkit-right;align-items:center}.captionTitle{font-size:1.5rem;align-content:center;padding:.5rem .75rem;text-align:start;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);background:#f5f5f5}.tree-grid-actions,.mat-cell-content{display:flex;flex-direction:row;align-items:center}.mat-column-my_menu,.tree-grid-column{width:0}.tree-grid-cell{display:flex;align-items:center;min-height:48px}.tree-grid-expand-button{flex:0 0 auto}.tree-grid-spacer{display:inline-block;width:40px;flex:0 0 40px}.tree-grid-label{min-width:0}\n"], dependencies: [{ kind: "component", type: MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"] }, { kind: "component", type: UiActionButtonComponent, selector: "ui-action-button", inputs: ["disabled", "descriptor", "code", "identifier", "viewActivated", "addedCssClass", "addBasicClasses", "iconPlaceholder"], outputs: ["actionClick", "actionDoubleClick"] }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: SmartIconComponent, selector: "smart-icon", inputs: ["icon", "color", "imageResource"] }, { kind: "directive", type: MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: SmartTooltipDirective, selector: "[smartTooltip]", inputs: ["smartTooltip"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "component", type: MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "directive", type: MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "component", type: MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: DefaultActionsPopupComponent, selector: "lib-default-actions-popup", inputs: ["buttons", "row", "colIdx"] }, { kind: "pipe", type: SmartDateTimePipe, name: "smartDateTime" }, { kind: "pipe", type: SmartDatePipe, name: "smartDate" }, { kind: "pipe", type: SmartTimePipe, name: "smartTime" }] }); }
11855
12217
  }
11856
12218
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MaterialTableComponent, decorators: [{
11857
12219
  type: Component,
11858
- args: [{ selector: 'lib-material-table', animations: [
11859
- trigger('detailExpand', [
11860
- state('collapsed', style({ height: '0px', minHeight: '0' })),
11861
- state('expanded', style({ height: '*' })),
11862
- transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
11863
- ]),
11864
- ], imports: [MatTable, MatColumnDef, MatHeaderCellDef, MatHeaderCell, NgClass, NgStyle, UiActionToolbarComponent, MatCheckbox, MatButton, SmartIconComponent, MatCellDef, MatCell, SmartTooltipDirective, MatIconButton, MatMenuTrigger, MatMenu, MatMenuItem, MatDivider, MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow, DefaultActionsPopupComponent, SmartDateTimePipe, SmartDatePipe, SmartTimePipe], template: "<table\n #myTable\n mat-table\n [dataSource]=\"smartTable.tableRows\"\n class=\"full-width\"\n multiTemplateDataRows\n >\n <!-- Column Descriptor -->\n @if (smartTable.title) {\n <caption class=\"captionTitle\">\n {{ smartTable.title }}\n </caption>\n }\n @for (header of smartTable.tableHeaders; track header; let i = $index) {\n <ng-container\n matColumnDef=\"{{ header }}\"\n >\n <!-- my_menu is the implicit action column present on all tables: -->\n @if ('my_menu' === header) {\n <th\n mat-header-cell\n *matHeaderCellDef\n [ngClass]=\"getColumnClasses(smartTable.customSmartTableHeaders![i])\"\n [ngStyle]=\"getColumnStyles(smartTable.customSmartTableHeaders![i])\"\n [attr.data-testid]=\"smartTable.customSmartTableHeaders![i].propertyName\"\n >\n <smart-ui-action-toolbar [id]=\"headerToolbarId\"></smart-ui-action-toolbar>\n </th>\n } @else {\n <th\n mat-header-cell\n *matHeaderCellDef\n [ngClass]=\"getColumnClasses(smartTable.customSmartTableHeaders![i])\"\n [ngStyle]=\"getColumnStyles(smartTable.customSmartTableHeaders![i])\"\n [attr.data-testid]=\"smartTable.customSmartTableHeaders![i].propertyName\"\n >\n @if (\n header === 'icon' || header === 'img' || header === 'options' || header === 'button'\n ) {\n <div\n ></div>\n }\n @if (header === 'select') {\n <div>\n @if (smartTable.customSmartTableHeaders![i].showCheckboxInHeader) {\n <mat-checkbox\n (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"smartTable.selection!.hasValue() && isAllSelected()\"\n [indeterminate]=\"smartTable.selection!.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\"\n >\n </mat-checkbox>\n }\n @if (!smartTable.customSmartTableHeaders![i].showCheckboxInHeader) {\n <div>\n {{ smartTable.customTableHeaders[i] }}\n </div>\n }\n </div>\n }\n @if (\n header !== 'icon' &&\n header !== 'img' &&\n header !== 'options' &&\n header !== 'button' &&\n header !== 'select' &&\n header !== 'expand' &&\n header !== 'actions'\n ) {\n <div\n >\n @if (smartTable.sortable) {\n @if (smartTable.sortable && isSortable(smartTable.customSmartTableHeaders![i])) {\n <button\n (click)=\"sortButtonClicked($event, smartTable.customSmartTableHeaders![i])\"\n mat-button\n class=\"sortableHeaderButton\"\n >\n {{ smartTable.customTableHeaders[i] }}\n @if (getSortIcon(header)) {\n <smart-icon\n class=\"sortableHeaderButtonIcon\"\n title=\"sort\"\n [icon]=\"getSortIcon(header)!\"\n ></smart-icon>\n }\n @if (hasSortNumIcon(header)) {\n <smart-icon\n class=\"sortableHeaderButtonIcon\"\n title=\"sort\"\n [icon]=\"getSortNumIcon(header)\"\n ></smart-icon>\n }\n </button>\n }\n } @else {\n {{ smartTable.customTableHeaders[i] }}\n }\n </div>\n }\n </th>\n }\n <td mat-cell *matCellDef=\"let element\" [ngClass]=\"isDisabled(element) ? 'disabledRow' : ''\">\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].propertyName === 'select' &&\n !isDisabled(element)\n ) {\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"\n $event\n ? setSelection(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n : null\n \"\n [disabled]=\"isDisabled(element)\"\n [checked]=\"\n smartTable.selection!.isSelected(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n \"\n [aria-label]=\"\n checkboxLabel(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n \"\n >\n </mat-checkbox>\n }\n @if (\n smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].properties\n ) {\n <div\n >\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().DATETIME) {\n <div>\n {{\n getValue(element, header)\n | smartDateTime: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().DATE) {\n <div>\n {{\n getValue(element, header)\n | smartDate: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().TIME) {\n <div>\n {{\n getValue(element, header)\n | smartTime: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().CHECKBOX) {\n <div>\n <mat-checkbox [disabled]=\"true\" [checked]=\"getValue(element, header)\"></mat-checkbox>\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders[i].properties?.type === type().ICON &&\n smartTable.customSmartTableHeaders[i].properties?.icons?.length\n ) {\n <div\n >\n <smart-icon\n [smartTooltip]=\"getToolTip(element, i)\"\n [icon]=\"getIcon(getValue(element, header), i)!\"\n [color]=\"getColor(getValue(element, header), i)\"\n >\n </smart-icon>\n </div>\n }\n </div>\n }\n <div class=\"smart-table-icon-container\">\n @for (ir of getImageResourceIcons(element, header); track ir) {\n <div>\n <smart-icon [imageResource]=\"ir\"> </smart-icon>\n </div>\n }\n </div>\n @if (smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].buttons) {\n <div\n class=\"smart-table-buttons-col\"\n >\n @for (button of smartTable.customSmartTableHeaders[i].buttons; track button) {\n <div>\n @if (showButton(button, element)) {\n <div>\n @switch (button.type) {\n @case (smartTableButtonType.ICON) {\n <button\n (click)=\"customButtonClicked($event, button, element)\"\n mat-icon-button\n color=\"{{ button.color }}\"\n >\n <smart-icon title=\"{{ button.label }}\" [icon]=\"button.icon!\"></smart-icon>\n </button>\n }\n @case (smartTableButtonType.NORMAL) {\n <button\n (click)=\"customButtonClicked($event, button, element)\"\n mat-button\n color=\"{{ button.color }}\"\n >\n @if (button.icon) {\n <smart-icon [icon]=\"button.icon\"></smart-icon>\n }\n {{ button.label ?? (button.translator ? button.translator(element).title : '') }}\n </button>\n }\n @case (smartTableButtonType.RAISED) {\n <button\n (click)=\"customButtonClicked($event, button, element)\"\n mat-raised-button\n color=\"{{ button.color }}\"\n >\n @if (button.icon) {\n <smart-icon [icon]=\"button.icon\"></smart-icon>\n }\n {{ button.label ?? (button.translator ? button.translator(element).title : '') }}\n </button>\n }\n <!------ MENU ------>\n @case (smartTableButtonType.MENU) {\n <div class=\"menu-button\">\n <!------ DEFAULT_ACTION_COLUMN ID TOOLBAR ------>\n <smart-ui-action-toolbar\n [uiActionModels]=\"getRowColumnAction(element, defaultActionToolbarId)\"\n [widgetId]=\"smartTable.getGridId()\"\n [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"\n ></smart-ui-action-toolbar>\n <!------ GENERIC HAMBURGER ------>\n <!------ TOOLBAR ------>\n @if (shouldShowMenuButton(element)) {\n <smart-ui-action-toolbar\n [uiActionModels]=\"getMenuActions(element)\"\n [widgetId]=\"smartTable.getGridId()\"\n [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"\n ></smart-ui-action-toolbar>\n }\n </div>\n }\n <!------ MENU ------>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n @if (smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].icon) {\n <div>\n @if (smartTable.customSmartTableHeaders[i].icon?.icon) {\n <smart-icon\n [ngClass]=\"smartTable.customSmartTableHeaders[i].icon?.cssClass ?? ''\"\n [color]=\"$safeNavigationMigration(smartTable.customSmartTableHeaders[i].icon?.color)\"\n [icon]=\"smartTable.customSmartTableHeaders[i].icon!.icon\"\n >\n </smart-icon>\n }\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].translator !== undefined\n ) {\n <div\n >\n {{ smartTable.customSmartTableHeaders[i].translator!(getValue(element, header)) }}\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].propertyName === 'expand'\n ) {\n <button\n mat-icon-button\n aria-label=\"expand row\"\n (click)=\"onToggle(element, $event)\"\n >\n @if (expandedElement !== element) {\n <smart-icon [icon]=\"'keyboard_arrow_down'\"></smart-icon>\n }\n @if (expandedElement === element) {\n <smart-icon [icon]=\"'keyboard_arrow_up'\"></smart-icon>\n }\n </button>\n }\n @if (\n !smartTable.customSmartTableHeaders ||\n (smartTable.customSmartTableHeaders &&\n !smartTable.customSmartTableHeaders[i].properties &&\n !smartTable.customSmartTableHeaders[i].icon &&\n !smartTable.customSmartTableHeaders[i].buttons &&\n !smartTable.customSmartTableHeaders[i].translator &&\n !(smartTable.customSmartTableHeaders[i].propertyName === 'select') &&\n !(smartTable.customSmartTableHeaders[i].propertyName === 'expand'))\n ) {\n <div\n >\n @if (header === 'icon') {\n <smart-icon [icon]=\"getValue(element, header)!\"> </smart-icon>\n }\n @if (header === 'img') {\n <img\n [src]=\"getValue(element, header)\"\n alt=\"\"\n class=\"smarttableImg\"\n />\n }\n <!------ TOOLBAR ------>\n @if (showCellToolbar(element, header)) {\n <smart-ui-action-toolbar\n [uiActionModels]=\"getRowColumnAction(element, header)\"\n [widgetId]=\"smartTable.getGridId()\"\n [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"\n ></smart-ui-action-toolbar>\n }\n <!------ TOOLBAR ------>\n @if (\n header !== 'icon' &&\n header !== 'img' &&\n header !== 'option' &&\n header !== 'button' &&\n !isImageResource(element, header)\n ) {\n <div\n [innerHtml]=\"getValue(element, header)\"\n ></div>\n }\n </div>\n }\n </td>\n </ng-container>\n }\n\n <!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->\n <ng-container matColumnDef=\"expandedDetail\">\n <td mat-cell *matCellDef=\"let element\" [attr.colspan]=\"smartTable.tableHeaders.length\">\n <div\n class=\"example-element-detail\"\n [@detailExpand]=\"element == expandedElement ? 'expanded' : 'collapsed'\"\n >\n <ng-template #expandedArea></ng-template>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"smartTable.tableHeaders; sticky: true\"></tr>\n <ng-container *matRowDef=\"let element; columns: smartTable.tableHeaders\">\n <tr\n mat-row\n class=\"example-element-row\"\n [class.example-expanded-row]=\"expandedElement === element\"\n [ngClass]=\"getRowClasses(element)\"\n [ngStyle]=\"getRowStyles(element)\"\n (click)=\"handleOnRowClick(element)\"\n (dblclick)=\"handleOnRowDoubleClick($event, element)\"\n [attr.data-testid]=\"element?.id ?? null\"\n ></tr>\n @if (smartTable.defaultActionCodes && smartTable.defaultActionCodes.length > 0) {\n <lib-default-actions-popup\n #defaultActionMenu\n [buttons]=\"getDefaultActionsForRow(element)!\"\n [row]=\"element\"\n ></lib-default-actions-popup>\n }\n </ng-container>\n <tr mat-row *matRowDef=\"let row; columns: ['expandedDetail']\" class=\"example-detail-row\"></tr>\n</table>\n", styles: [".full-width{width:100%}.smarttableImg{width:25px}.smartTableRowHover:hover{cursor:pointer}tr.example-detail-row{height:0}tr.example-element-row:not(.example-expanded-row):hover{background:#f5f5f5}tr.example-element-row:not(.example-expanded-row):active{background:#efefef}.example-element-row td{border-bottom-width:0}.example-element-detail{overflow:hidden;display:flex;flex-direction:column}.example-element-diagram{min-width:80px;border:2px solid black;padding:8px;font-weight:lighter;margin:8px 0;height:104px}.example-element-symbol{font-weight:700;font-size:40px;line-height:normal}.example-element-description{padding:16px}.example-element-description-attribution{opacity:.5}.disabledRow{color:var(--disabled)}.disabledRow:hover{cursor:default}.smart-table-buttons-col{display:flex;flex-direction:row;justify-content:flex-end}.sortableHeaderButton{margin:0!important;padding:0!important;text-align:left!important}.selected{background-color:var(--primary-light-color)}.smart-table-icon-container{display:flex;flex-direction:row;justify-content:space-between;white-space:initial}.reversed{flex-direction:row-reverse;gap:1rem}:host ::ng-deep .mat-mdc-menu-item{line-height:normal!important}.mat-mdc-menu-item[disabled]{cursor:default!important}.menu-button{display:flex;flex-direction:row;justify-content:flex-end;text-align:-webkit-right;align-items:center}.captionTitle{font-size:1.5rem;align-content:center;padding:.5rem .75rem;text-align:start;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);background:#f5f5f5}\n"] }]
12220
+ args: [{ selector: 'lib-material-table', imports: [MatTable, MatColumnDef, MatHeaderCellDef, MatHeaderCell, NgClass, NgStyle, UiActionToolbarComponent, UiActionButtonComponent, MatCheckbox, MatButton, SmartIconComponent, MatCellDef, MatCell, SmartTooltipDirective, MatIconButton, MatMenuTrigger, MatMenu, MatMenuItem, MatDivider, MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow, DefaultActionsPopupComponent, SmartDateTimePipe, SmartDatePipe, SmartTimePipe], template: "<table #myTable mat-table [dataSource]=\"isTreeGrid ? visibleRows : smartTable.tableRows\" class=\"full-width\"\n multiTemplateDataRows>\n <!-- Column Descriptor -->\n @if (smartTable.title) {\n <caption class=\"captionTitle\">\n {{ smartTable.title }}\n </caption>\n }\n\n @for (header of smartTable.tableHeaders; track header; let i = $index) {\n\n <ng-container matColumnDef=\"{{ header }}\">\n <!-- my_menu is the implicit action column present on all tables: -->\n @if ('my_menu' === header) {\n <th mat-header-cell *matHeaderCellDef [ngClass]=\"getColumnClasses(smartTable.customSmartTableHeaders![i])\"\n [ngStyle]=\"getColumnStyles(smartTable.customSmartTableHeaders![i])\"\n [attr.data-testid]=\"smartTable.customSmartTableHeaders![i].propertyName\">\n\n <!-- ==================== TREE ACTIONS ==================== -->\n @if ( isTreeGrid) {\n <div class=\"tree-grid-actions\">\n\n <ui-action-button (actionClick)=\"expandOneLevel()\" [descriptor]=\"expandTreeGridLevel\">\n </ui-action-button>\n\n\n <ui-action-button (actionClick)=\"collapseOneLevel()\" [descriptor]=\"closeTreeGridLevel\">\n </ui-action-button>\n </div>\n }\n <!-- ==================== TREE ACTIONS ==================== -->\n\n <smart-ui-action-toolbar [id]=\"headerToolbarId\"></smart-ui-action-toolbar>\n </th>\n } @else {\n <th mat-header-cell *matHeaderCellDef [ngClass]=\"getColumnClasses(smartTable.customSmartTableHeaders![i])\"\n [ngStyle]=\"getColumnStyles(smartTable.customSmartTableHeaders![i])\"\n [attr.data-testid]=\"smartTable.customSmartTableHeaders![i].propertyName\">\n @if (\n header === 'icon' || header === 'img' || header === 'options' || header === 'button'\n ) {\n <div></div>\n }\n @if (header === 'select') {\n <div>\n @if (smartTable.customSmartTableHeaders![i].showCheckboxInHeader) {\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"smartTable.selection!.hasValue() && isAllSelected()\"\n [indeterminate]=\"smartTable.selection!.hasValue() && !isAllSelected()\" [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n }\n @if (!smartTable.customSmartTableHeaders![i].showCheckboxInHeader) {\n <div>\n {{ smartTable.customTableHeaders[i] }}\n </div>\n }\n </div>\n }\n @if (\n header !== 'icon' &&\n header !== 'img' &&\n header !== 'options' &&\n header !== 'button' &&\n header !== 'select' &&\n header !== 'expand' &&\n header !== 'actions'\n ) {\n <div>\n @if (smartTable.sortable) {\n @if (smartTable.sortable && isSortable(smartTable.customSmartTableHeaders![i])) {\n <button (click)=\"sortButtonClicked($event, smartTable.customSmartTableHeaders![i])\" mat-button\n class=\"sortableHeaderButton\">\n {{ smartTable.customTableHeaders[i] }}\n @if (getSortIcon(header)) {\n <smart-icon class=\"sortableHeaderButtonIcon\" title=\"sort\" [icon]=\"getSortIcon(header)!\"></smart-icon>\n }\n @if (hasSortNumIcon(header)) {\n <smart-icon class=\"sortableHeaderButtonIcon\" title=\"sort\" [icon]=\"getSortNumIcon(header)\"></smart-icon>\n }\n </button>\n }\n } @else {\n {{ smartTable.customTableHeaders[i] }}\n }\n </div>\n }\n </th>\n }\n\n <td mat-cell *matCellDef=\"let element\" [ngClass]=\"isDisabled(element) ? 'disabledRow' : ''\">\n <div class=\"mat-cell-content\">\n\n\n <!-- ==================== TREE TOGGLE ==================== -->\n @if (isTreeGrid && i === 0) {\n <div class=\"tree-grid-first-column\" [style.padding-left.px]=\"(element.level ?? 0) * 24\">\n @if (hasChildren(element)) {\n <button mat-icon-button type=\"button\" (click)=\"toggleRowExpansion(element, $event)\">\n <smart-icon [icon]=\"\n isRowExpanded(element)\n ? 'keyboard_arrow_down'\n : 'keyboard_arrow_right'\n \"></smart-icon>\n </button>\n } @else {\n <span class=\"tree-grid-spacer\"></span>\n }\n\n </div>\n }\n <!-- ==================== TREE TOGGLE ==================== -->\n\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].propertyName === 'select' &&\n !isDisabled(element)\n ) {\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"\n $event\n ? setSelection(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n : null\n \" [disabled]=\"isDisabled(element)\" [checked]=\"\n smartTable.selection!.isSelected(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n \" [aria-label]=\"\n checkboxLabel(\n smartTable.selectionProperty\n ? smartTable.getValueDeeply(element, smartTable.selectionProperty)\n : element\n )\n \">\n </mat-checkbox>\n }\n @if (\n smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].properties\n ) {\n <div>\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().DATETIME) {\n <div>\n {{\n getValue(element, header)\n | smartDateTime: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().DATE) {\n <div>\n {{\n getValue(element, header)\n | smartDate: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().TIME) {\n <div>\n {{\n getValue(element, header)\n | smartTime: $safeNavigationMigration(smartTable.customSmartTableHeaders[i].properties?.dateFormat)\n }}\n </div>\n }\n @if (smartTable.customSmartTableHeaders[i].properties?.type === type().CHECKBOX) {\n <div>\n <mat-checkbox [disabled]=\"true\" [checked]=\"getValue(element, header)\"></mat-checkbox>\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders[i].properties?.type === type().ICON &&\n smartTable.customSmartTableHeaders[i].properties?.icons?.length\n ) {\n <div>\n <smart-icon [smartTooltip]=\"getToolTip(element, i)\" [icon]=\"getIcon(getValue(element, header), i)!\"\n [color]=\"getColor(getValue(element, header), i)\">\n </smart-icon>\n </div>\n }\n </div>\n }\n <div class=\"smart-table-icon-container\">\n @for (ir of getImageResourceIcons(element, header); track ir) {\n <div>\n <smart-icon [imageResource]=\"ir\"> </smart-icon>\n </div>\n }\n </div>\n @if (smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].buttons) {\n <div class=\"smart-table-buttons-col\">\n @for (button of smartTable.customSmartTableHeaders[i].buttons; track button) {\n <div>\n @if (showButton(button, element)) {\n <div>\n @switch (button.type) {\n @case (smartTableButtonType.ICON) {\n <button (click)=\"customButtonClicked($event, button, element)\" mat-icon-button color=\"{{ button.color }}\">\n <smart-icon title=\"{{ button.label }}\" [icon]=\"button.icon!\"></smart-icon>\n </button>\n }\n @case (smartTableButtonType.NORMAL) {\n <button (click)=\"customButtonClicked($event, button, element)\" mat-button color=\"{{ button.color }}\">\n @if (button.icon) {\n <smart-icon [icon]=\"button.icon\"></smart-icon>\n }\n {{ button.label ?? (button.translator ? button.translator(element).title : '') }}\n </button>\n }\n @case (smartTableButtonType.RAISED) {\n <button (click)=\"customButtonClicked($event, button, element)\" mat-raised-button\n color=\"{{ button.color }}\">\n @if (button.icon) {\n <smart-icon [icon]=\"button.icon\"></smart-icon>\n }\n {{ button.label ?? (button.translator ? button.translator(element).title : '') }}\n </button>\n }\n <!------ MENU ------>\n @case (smartTableButtonType.MENU) {\n <div class=\"menu-button\">\n <!------ DEFAULT_ACTION_COLUMN ID TOOLBAR ------>\n <smart-ui-action-toolbar [uiActionModels]=\"getRowColumnAction(element, defaultActionToolbarId)\"\n [widgetId]=\"smartTable.getGridId()\" [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"></smart-ui-action-toolbar>\n <!------ GENERIC HAMBURGER ------>\n <!------ TOOLBAR ------>\n @if (shouldShowMenuButton(element)) {\n <smart-ui-action-toolbar [uiActionModels]=\"getMenuActions(element)\" [widgetId]=\"smartTable.getGridId()\"\n [nodeId]=\"element.id\" [actionParams]=\"{ model: element }\"></smart-ui-action-toolbar>\n }\n </div>\n }\n <!------ MENU ------>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n @if (smartTable.customSmartTableHeaders && smartTable.customSmartTableHeaders[i].icon) {\n <div>\n @if (smartTable.customSmartTableHeaders[i].icon?.icon) {\n <smart-icon [ngClass]=\"smartTable.customSmartTableHeaders[i].icon?.cssClass ?? ''\"\n [color]=\"$safeNavigationMigration(smartTable.customSmartTableHeaders[i].icon?.color)\"\n [icon]=\"smartTable.customSmartTableHeaders[i].icon!.icon\">\n </smart-icon>\n }\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].translator !== undefined\n ) {\n <div>\n {{ smartTable.customSmartTableHeaders[i].translator!(getValue(element, header)) }}\n </div>\n }\n @if (\n smartTable.customSmartTableHeaders &&\n smartTable.customSmartTableHeaders[i].propertyName === 'expand'\n ) {\n <button mat-icon-button aria-label=\"expand row\" (click)=\"onToggle(element, $event)\">\n @if (expandedElement !== element) {\n <smart-icon [icon]=\"'keyboard_arrow_down'\"></smart-icon>\n }\n @if (expandedElement === element) {\n <smart-icon [icon]=\"'keyboard_arrow_up'\"></smart-icon>\n }\n </button>\n }\n @if (\n !smartTable.customSmartTableHeaders ||\n (smartTable.customSmartTableHeaders &&\n !smartTable.customSmartTableHeaders[i].properties &&\n !smartTable.customSmartTableHeaders[i].icon &&\n !smartTable.customSmartTableHeaders[i].buttons &&\n !smartTable.customSmartTableHeaders[i].translator &&\n !(smartTable.customSmartTableHeaders[i].propertyName === 'select') &&\n !(smartTable.customSmartTableHeaders[i].propertyName === 'expand'))\n ) {\n <div>\n @if (header === 'icon') {\n <smart-icon [icon]=\"getValue(element, header)!\"> </smart-icon>\n }\n @if (header === 'img') {\n <img [src]=\"getValue(element, header)\" alt=\"\" class=\"smarttableImg\" />\n }\n <!------ TOOLBAR ------>\n @if (showCellToolbar(element, header)) {\n <smart-ui-action-toolbar [uiActionModels]=\"getRowColumnAction(element, header)\"\n [widgetId]=\"smartTable.getGridId()\" [nodeId]=\"element.id\"\n [actionParams]=\"{ model: element }\"></smart-ui-action-toolbar>\n }\n <!------ TOOLBAR ------>\n @if (\n header !== 'icon' &&\n header !== 'img' &&\n header !== 'option' &&\n header !== 'button' &&\n !isImageResource(element, header)\n ) {\n <div [innerHtml]=\"getValue(element, header)\"></div>\n }\n </div>\n }\n\n </div>\n </td>\n </ng-container>\n }\n\n <!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->\n @if (smartTable.expandable) {\n <ng-container matColumnDef=\"expandedDetail\">\n <td mat-cell *matCellDef=\"let element\" [attr.colspan]=\"smartTable.tableHeaders.length\">\n <div class=\"smart-table-detail\" animate.enter=\"smart-table-detail-enter\">\n <div class=\"smart-table-detail-content\">\n <ng-template #expandedArea></ng-template>\n </div>\n </div>\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"smartTable.tableHeaders; sticky: true\"></tr>\n <ng-container *matRowDef=\"let element; columns: smartTable.tableHeaders\">\n <tr mat-row class=\"smart-table-row\" [class.smart-table-row-expanded]=\"expandedElement === element\"\n [ngClass]=\"getRowClasses(element)\" [ngStyle]=\"getRowStyles(element)\" (click)=\"handleOnRowClick(element)\"\n (dblclick)=\"handleOnRowDoubleClick($event, element)\" [attr.data-testid]=\"element?.id ?? null\"></tr>\n\n @if (smartTable.defaultActionCodes && smartTable.defaultActionCodes.length > 0) {\n <lib-default-actions-popup #defaultActionMenu [buttons]=\"getDefaultActionsForRow(element)!\"\n [row]=\"element\"></lib-default-actions-popup>\n }\n </ng-container>\n <!-- Only the expanded row has a detail row; no host style can give a collapsed one a height. -->\n @if (smartTable.expandable) {\n <tr mat-row *matRowDef=\"let row; columns: ['expandedDetail']; when: isExpandedRow\" class=\"smart-table-detail-row\"\n (animate.leave)=\"onDetailRowLeave($event)\"></tr>\n }\n</table>", styles: [".full-width{width:100%}.smarttableImg{width:25px}.smartTableRowHover:hover{cursor:pointer}tr.smart-table-row:not(.smart-table-row-expanded):hover{background:#f5f5f5}tr.smart-table-row:not(.smart-table-row-expanded):active{background:#efefef}tr.smart-table-row-expanded td{border-bottom-width:0}tr.smart-table-detail-row{height:0}.smart-table-detail{display:grid;grid-template-rows:1fr}.smart-table-detail-enter{animation:smart-table-detail-expand 225ms cubic-bezier(.4,0,.2,1)}.smart-table-detail-content{min-height:0;overflow:hidden}@keyframes smart-table-detail-expand{0%{grid-template-rows:0fr}to{grid-template-rows:1fr}}.disabledRow{color:var(--disabled)}.disabledRow:hover{cursor:default}.smart-table-buttons-col{display:flex;flex-direction:row;justify-content:flex-end}.sortableHeaderButton{margin:0!important;padding:0!important;text-align:left!important}.selected{background-color:var(--primary-light-color)}.smart-table-icon-container{display:flex;flex-direction:row;justify-content:space-between;white-space:initial}.reversed{flex-direction:row-reverse;gap:1rem}:host ::ng-deep .mat-mdc-menu-item{line-height:normal!important}.mat-mdc-menu-item[disabled]{cursor:default!important}.menu-button{display:flex;flex-direction:row;justify-content:flex-end;text-align:-webkit-right;align-items:center}.captionTitle{font-size:1.5rem;align-content:center;padding:.5rem .75rem;text-align:start;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);background:#f5f5f5}.tree-grid-actions,.mat-cell-content{display:flex;flex-direction:row;align-items:center}.mat-column-my_menu,.tree-grid-column{width:0}.tree-grid-cell{display:flex;align-items:center;min-height:48px}.tree-grid-expand-button{flex:0 0 auto}.tree-grid-spacer{display:inline-block;width:40px;flex:0 0 40px}.tree-grid-label{min-width:0}\n"] }]
11865
12221
  }] });
11866
12222
 
11867
12223
  class MobileTableComponent extends Table {
@@ -15589,11 +15945,11 @@ class SmarttreeGenericService extends SmarttreeService {
15589
15945
  let nodeId = uiActionRequest.params['nodeUuid'];
15590
15946
  delete uiActionRequest.params['nodeUuid'];
15591
15947
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
15592
- this.treeFromBackend = await this.viewContext.handlePerformActionResponse(await this.service.performAction2(viewUuid, this.treeId, nodeId, uiActionRequest).toPromise());
15948
+ this.treeFromBackend = await this.viewContext.handlePerformActionResponse(await this.viewContext.withModelSync(uiActionRequest, (request) => this.service.performAction2(viewUuid, this.treeId, nodeId, request).toPromise()));
15593
15949
  }
15594
15950
  async performMainUiActionRequest(uiActionRequest) {
15595
15951
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
15596
- this.treeFromBackend = await this.viewContext.handlePerformActionResponse(await this.service.performMainAction2(viewUuid, this.treeId, uiActionRequest).toPromise());
15952
+ this.treeFromBackend = await this.viewContext.handlePerformActionResponse(await this.viewContext.withModelSync(uiActionRequest, (request) => this.service.performMainAction2(viewUuid, this.treeId, request).toPromise()));
15597
15953
  }
15598
15954
  handleSpecificDemandsAsynchronously(uiAction, uiActionRequest) {
15599
15955
  throw new Error('Method not implemented.');
@@ -15635,11 +15991,13 @@ class SmartComponentApiClient {
15635
15991
  }
15636
15992
  if (this.uuid) {
15637
15993
  this.viewContext.unfollow(this.uuid);
15994
+ this.viewContext.leaveModelSync(this.uuid, this);
15638
15995
  }
15639
15996
  this.uuidState.set(uuid);
15640
15997
  // Widgets read `widgetUuid` themselves; nothing is pushed into them from here. (ADR-0005)
15641
15998
  if (uuid) {
15642
15999
  this.viewContext.follow(this.uuid, this.detectChange);
16000
+ this.viewContext.joinModelSync(this.uuid, this);
15643
16001
  this.onUuidResolved(uuid);
15644
16002
  }
15645
16003
  }
@@ -15729,7 +16087,6 @@ class SmartComponentApiClient {
15729
16087
  */
15730
16088
  this.commands = new Subject();
15731
16089
  this.dataChanged = new Subject();
15732
- this.sendModelOnWidgetAction = false;
15733
16090
  this.inmediateDataChangeKeys = [];
15734
16091
  this.blurDataChangeKeys = [];
15735
16092
  // check and calculate pageName
@@ -15766,6 +16123,7 @@ class SmartComponentApiClient {
15766
16123
  }
15767
16124
  _ngOnDestroy() {
15768
16125
  this.viewContext.unfollow(this.uuid);
16126
+ this.viewContext.leaveModelSync(this.uuid, this);
15769
16127
  this._destroy$.next();
15770
16128
  this._destroy$.complete();
15771
16129
  }
@@ -15851,6 +16209,7 @@ class SmartComponentApiClient {
15851
16209
  // grids are already loaded in setUuid()
15852
16210
  this.handleChangeWidgets(this.model.widgets, true);
15853
16211
  }
16212
+ this.viewContext.markModelSynced(uuid);
15854
16213
  }
15855
16214
  async executeUiAction(uiAction, options) {
15856
16215
  const uiActionModel = {
@@ -15877,7 +16236,7 @@ class SmartComponentApiClient {
15877
16236
  }
15878
16237
  let blurIdx = this.blurDataChangeKeys.indexOf(key);
15879
16238
  if (blurIdx >= 0) {
15880
- this.inmediateDataChangeKeys.splice(blurIdx, 1);
16239
+ this.blurDataChangeKeys.splice(blurIdx, 1);
15881
16240
  }
15882
16241
  }
15883
16242
  /**
@@ -15889,7 +16248,6 @@ class SmartComponentApiClient {
15889
16248
  if (changeKeys.includes(event.key)) {
15890
16249
  this.executeUiAction({
15891
16250
  code: event.key,
15892
- model: true,
15893
16251
  params: {
15894
16252
  item: event.value,
15895
16253
  },
@@ -15935,7 +16293,6 @@ class SmartComponentApiClient {
15935
16293
  delete uiActionRequest.params['_files'];
15936
16294
  delete uiActionRequest.params['_uploadDescriptor'];
15937
16295
  delete uiActionRequest.params['isMultiple'];
15938
- // await this.sendModelToServer();
15939
16296
  if (!isMultiple) {
15940
16297
  await this.viewContext.performUploadAction(this.uuid, uiActionRequest, param, files[0]);
15941
16298
  }
@@ -15975,38 +16332,15 @@ class SmartComponentApiClient {
15975
16332
  }
15976
16333
  }
15977
16334
  else if (widgetId && nodeId) {
15978
- // await this.sendModelToServer();
15979
- this.addModelToWidgetAction(uiActionRequest);
15980
16335
  await this.viewContext.performWidgetAction(this.uuid, widgetId, nodeId, uiActionRequest);
15981
16336
  }
15982
16337
  else if (widgetId) {
15983
- // await this.sendModelToServer();
15984
- this.addModelToWidgetAction(uiActionRequest);
15985
16338
  await this.viewContext.performWidgetMainAction(this.uuid, widgetId, uiActionRequest);
15986
16339
  }
15987
16340
  else {
15988
16341
  await this.viewContext.performAction(this.uuid, uiActionRequest);
15989
16342
  }
15990
16343
  }
15991
- addModelToWidgetAction(uiActionRequest) {
15992
- this.addModelToWidgetParams(uiActionRequest.params);
15993
- }
15994
- addModelToWidgetParams(params) {
15995
- if (this.sendModelOnWidgetAction) {
15996
- this.submitForm(false);
15997
- // mimic ComponentModel
15998
- params['clientPageModel'] = { data: this.getModel() };
15999
- }
16000
- }
16001
- // private async sendModelToServer() {
16002
- // this.submitForms(false);
16003
- // let dataChange = {
16004
- // kind: DataChangeKind.Widget,
16005
- // source: '',
16006
- // values: [['', this.getModel()]],
16007
- // };
16008
- // await this.viewContext.dataChanged(this.uuid!, dataChange);
16009
- // }
16010
16344
  handleSpecificDemandsAsynchronously(uiAction, uiActionRequest) {
16011
16345
  throw new Error('handleSpecificDemandsAsynchronously method not implemented.');
16012
16346
  }
@@ -16056,27 +16390,10 @@ class SmartComponentApiClient {
16056
16390
  return matData?.uuid;
16057
16391
  }
16058
16392
  // #region handle changes
16059
- handleValueChanges(valueChange) {
16060
- let dataChange;
16061
- if (valueChange.key.includes('.')) {
16062
- const source = valueChange.key.split('.')[0];
16063
- const valueKey = valueChange.key.split('.')[1];
16064
- dataChange = {
16065
- kind: valueChange.kind === 'basic' ? DataChangeKind.WIDGET : DataChangeKind.SELECTION,
16066
- source,
16067
- values: [[valueKey, valueChange.value]],
16068
- };
16069
- }
16070
- else {
16071
- dataChange = {
16072
- kind: valueChange.kind === 'basic' ? DataChangeKind.WIDGET : DataChangeKind.SELECTION,
16073
- source: valueChange.key,
16074
- values: [[valueChange.key, valueChange.value]],
16075
- };
16076
- }
16077
- this.viewContext.dataChanged(this.uuid, dataChange);
16078
- }
16079
16393
  handleChanges(change) {
16394
+ // What of the data the push wrote: the paths under `data`, or everything on a whole-model
16395
+ // replacement. Tells the view context how much of the data to take as the backend's.
16396
+ let pushedKeys = [];
16080
16397
  try {
16081
16398
  if (!this.componentModelLoaded) {
16082
16399
  // initial load
@@ -16091,6 +16408,7 @@ class SmartComponentApiClient {
16091
16408
  Object.keys(changes).map((changeKey) => {
16092
16409
  if (changeKey.startsWith('data.')) {
16093
16410
  this.handleDataChange(changes, changeKey);
16411
+ pushedKeys?.push(changeKey.substring('data.'.length));
16094
16412
  }
16095
16413
  else if (changeKey.startsWith('layouts.') && this.model?.layouts) {
16096
16414
  this.handleLayoutChange(changes, changeKey);
@@ -16110,6 +16428,7 @@ class SmartComponentApiClient {
16110
16428
  else if (changeKey === '') {
16111
16429
  this.model = changes[''];
16112
16430
  dataChangeHasBeenNexted = true;
16431
+ pushedKeys = undefined;
16113
16432
  }
16114
16433
  });
16115
16434
  if (!dataChangeHasBeenNexted) {
@@ -16120,9 +16439,14 @@ class SmartComponentApiClient {
16120
16439
  catch (error) {
16121
16440
  console.error(error);
16122
16441
  this.model = change.value;
16442
+ pushedKeys = undefined;
16123
16443
  }
16124
16444
  if (!change.changedWidgets && !change.changes && change.value) {
16125
16445
  this.model = change.value;
16446
+ pushedKeys = undefined;
16447
+ }
16448
+ if (pushedKeys === undefined || pushedKeys.length) {
16449
+ this.viewContext.markModelSynced(this.uuid, pushedKeys);
16126
16450
  }
16127
16451
  }
16128
16452
  /**
@@ -16276,6 +16600,7 @@ var SmartLayoutDef;
16276
16600
  SmartLayoutDef["EXPANDABLE"] = "EXPANDABLE";
16277
16601
  SmartLayoutDef["CARD"] = "CARD";
16278
16602
  SmartLayoutDef["TREE"] = "TREE";
16603
+ SmartLayoutDef["TREE_GRID"] = "TREE_GRID";
16279
16604
  })(SmartLayoutDef || (SmartLayoutDef = {}));
16280
16605
  var SmartGridButtonType;
16281
16606
  (function (SmartGridButtonType) {
@@ -16979,18 +17304,6 @@ var GridDataAccessConfigKindEnum;
16979
17304
  })(GridDataAccessConfigKindEnum || (GridDataAccessConfigKindEnum = {}));
16980
17305
  ;
16981
17306
 
16982
- /**
16983
- * Grid api
16984
- * The grid api is resposible for the grid components that shows a list of item.
16985
- *
16986
- * The version of the OpenAPI document: 1.0.0
16987
- * Contact: info@it4all.hu
16988
- *
16989
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
16990
- * https://openapi-generator.tech
16991
- * Do not edit the class manually.
16992
- */
16993
-
16994
17307
  /**
16995
17308
  * Grid api
16996
17309
  * The grid api is resposible for the grid components that shows a list of item.
@@ -17031,9 +17344,46 @@ var GridViewDescriptorKindEnum;
17031
17344
  GridViewDescriptorKindEnum["TABLE"] = "TABLE";
17032
17345
  GridViewDescriptorKindEnum["CARDS"] = "CARDS";
17033
17346
  GridViewDescriptorKindEnum["TREE"] = "TREE";
17347
+ GridViewDescriptorKindEnum["TREE_GRID"] = "TREE_GRID";
17034
17348
  })(GridViewDescriptorKindEnum || (GridViewDescriptorKindEnum = {}));
17035
17349
  ;
17036
17350
 
17351
+ class ApiModule {
17352
+ static forRoot(configurationFactory) {
17353
+ return {
17354
+ ngModule: ApiModule,
17355
+ providers: [{ provide: Configuration$2, useFactory: configurationFactory }]
17356
+ };
17357
+ }
17358
+ constructor(parentModule, http) {
17359
+ if (parentModule) {
17360
+ throw new Error('ApiModule is already loaded. Import in your base AppModule only.');
17361
+ }
17362
+ if (!http) {
17363
+ throw new Error('You need to import the HttpClientModule in your AppModule! \n' +
17364
+ 'See also https://github.com/angular/angular/issues/20575');
17365
+ }
17366
+ }
17367
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ApiModule, deps: [{ token: ApiModule, optional: true, skipSelf: true }, { token: i1.HttpClient, optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
17368
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: ApiModule }); }
17369
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ApiModule }); }
17370
+ }
17371
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ApiModule, decorators: [{
17372
+ type: NgModule,
17373
+ args: [{
17374
+ imports: [],
17375
+ declarations: [],
17376
+ exports: [],
17377
+ providers: []
17378
+ }]
17379
+ }], ctorParameters: () => [{ type: ApiModule, decorators: [{
17380
+ type: Optional
17381
+ }, {
17382
+ type: SkipSelf
17383
+ }] }, { type: i1.HttpClient, decorators: [{
17384
+ type: Optional
17385
+ }] }] });
17386
+
17037
17387
  class TableLayoutDefinerComponent {
17038
17388
  constructor() {
17039
17389
  this.data = inject(MAT_DIALOG_DATA);
@@ -17219,6 +17569,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17219
17569
  args: ['forms']
17220
17570
  }] } });
17221
17571
 
17572
+ /** The model sync carrier of a selection call; the view context puts the envelope on it. */
17573
+ function selection(selected, unselected) {
17574
+ return { selected, unselected, params: {} };
17575
+ }
17222
17576
  class SmartGridService {
17223
17577
  constructor() {
17224
17578
  this.api = inject(GridService);
@@ -17256,27 +17610,19 @@ class SmartGridService {
17256
17610
  }
17257
17611
  async select(smartGrid, gridRow) {
17258
17612
  gridRow.selected = !gridRow.selected;
17259
- const gridSelectionChange = this.buildGridSelectionChange(smartGrid, [], []);
17260
- return await this.api
17613
+ return await smartGrid.viewContextService.withModelSync(selection([], []), (gridSelectionChange) => this.api
17261
17614
  .select(smartGrid.gridModel.viewUuid, smartGrid.gridIdentifier, gridRow.id, gridRow.selected, gridSelectionChange)
17262
- .toPromise();
17615
+ .toPromise());
17263
17616
  }
17264
17617
  async selectRows(smartGrid, selected, unselected) {
17265
- const gridSelectionChange = this.buildGridSelectionChange(smartGrid, selected, unselected);
17266
- return await this.api
17618
+ return await smartGrid.viewContextService.withModelSync(selection(selected, unselected), (gridSelectionChange) => this.api
17267
17619
  .selectRows(smartGrid.gridModel.viewUuid, smartGrid.gridIdentifier, gridSelectionChange)
17268
- .toPromise();
17620
+ .toPromise());
17269
17621
  }
17270
17622
  async selectAll(smartGrid, selected) {
17271
- const gridSelectionChange = this.buildGridSelectionChange(smartGrid, [], []);
17272
- return await this.api
17623
+ return await smartGrid.viewContextService.withModelSync(selection([], []), (gridSelectionChange) => this.api
17273
17624
  .selectAll(smartGrid.gridModel.viewUuid, smartGrid.gridIdentifier, selected, gridSelectionChange)
17274
- .toPromise();
17275
- }
17276
- buildGridSelectionChange(smartGrid, selected, unselected) {
17277
- const gridSelectionChange = { selected, unselected, params: {} };
17278
- smartGrid.serviceToUse?.addModelToWidgetParams?.(gridSelectionChange.params);
17279
- return gridSelectionChange;
17625
+ .toPromise());
17280
17626
  }
17281
17627
  async update(uuid, identifier, updateData) {
17282
17628
  if (!updateData.pageSize) {
@@ -17821,6 +18167,9 @@ class SmartGridComponent {
17821
18167
  if (kind === GridViewDescriptorKindEnum.TABLE) {
17822
18168
  layoutDef = SmartLayoutDef.TABLE;
17823
18169
  }
18170
+ else if (kind === GridViewDescriptorKindEnum.TREE_GRID) {
18171
+ layoutDef = SmartLayoutDef.TREE_GRID;
18172
+ }
17824
18173
  else if (kind === GridViewDescriptorKindEnum.TREE) {
17825
18174
  layoutDef = SmartLayoutDef.TREE;
17826
18175
  }
@@ -17846,6 +18195,9 @@ class SmartGridComponent {
17846
18195
  if (this.smartGrid.layoutDef === SmartLayoutDef.TABLE) {
17847
18196
  this.renderTable();
17848
18197
  }
18198
+ else if (this.smartGrid.layoutDef === SmartLayoutDef.TREE_GRID) {
18199
+ this.renderTable();
18200
+ }
17849
18201
  else if (this.smartGrid.layoutDef === SmartLayoutDef.EXPANDABLE) {
17850
18202
  this.renderExpandableSections();
17851
18203
  }
@@ -17883,7 +18235,7 @@ class SmartGridComponent {
17883
18235
  else {
17884
18236
  this.tableType = SmartTableType.INHERITED;
17885
18237
  }
17886
- if (this.smartGrid.layoutDef === SmartLayoutDef.TABLE) {
18238
+ if (this.smartGrid.layoutDef === SmartLayoutDef.TABLE || this.smartGrid.layoutDef === SmartLayoutDef.TREE_GRID) {
17887
18239
  let tableConfig = {
17888
18240
  ...this.smartGrid.tableConfig,
17889
18241
  title: this.smartGrid?.gridModel.title,
@@ -18413,12 +18765,21 @@ class SmartGridComponent {
18413
18765
  };
18414
18766
  this.dialog.open(TableLayoutDefinerComponent, { data: dialogData });
18415
18767
  }
18768
+ get showEditColumns() {
18769
+ return {
18770
+ title: '',
18771
+ color: 'primary',
18772
+ type: UiActionButtonType.MINI_FAB,
18773
+ icon: 'view_columns',
18774
+ iconPosition: IconPosition.PRE,
18775
+ };
18776
+ }
18416
18777
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
18417
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SmartGridComponent, isStandalone: true, selector: "smart-grid", inputs: { smartGrid: { classPropertyName: "smartGrid", publicName: "smartGrid", isSignal: false, isRequired: false, transformFunction: null }, uuid: { classPropertyName: "uuid", publicName: "uuid", isSignal: false, isRequired: false, transformFunction: null }, parentLayoutComponent: { classPropertyName: "parentLayoutComponent", publicName: "parentLayoutComponent", isSignal: true, isRequired: false, transformFunction: null }, dev: { classPropertyName: "dev", publicName: "dev", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-testid": "this.testId" } }, providers: [SmartGridService], viewQueries: [{ propertyName: "vcRefTable", first: true, predicate: ["table"], descendants: true, read: ViewContainerRef }, { propertyName: "expandedRowsComps", predicate: ExpandedRowRendererComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (smartGrid) {\n <div class=\"smartGrid-container\">\n <div class=\"smart-grid-toolbar\">\n <smart-ui-action-toolbar [id]=\"toolbarId\"></smart-ui-action-toolbar>\n @if (smartGrid.showEditColumns) {\n <button\n (click)=\"editColumns()\"\n mat-mini-fab\n color=\"text-primary\"\n >\n <mat-icon aria-hidden=\"false\" aria-label=\"Columns\" class=\"smart-grid-edit-icon\"\n >view_columns</mat-icon\n >\n </button>\n }\n </div>\n <!-- <div [ngClass]=\"isBlocked ? 'blocked' : ''\"></div> -->\n <div class=\"smartGridContent\">\n <div>\n @if (smartGrid.showResultCount) {\n <div>\n <div class=\"smartGrid-data-number\">\n {{ smartGrid.gridModel.totalRowCount }}\n </div>\n </div>\n }\n </div>\n <div class=\"smart-grid-table-container\">\n <ng-template #table></ng-template>\n </div>\n @if (smartGrid.layoutDef === layoutDef().EXPANDABLE) {\n <div>\n @for (expandableSection of expandableSections; track expandableSection) {\n <smart-expandable-section\n #gridExpandableSection\n [data]=\"expandableSection\"\n ></smart-expandable-section>\n }\n </div>\n }\n @if (smartGrid.layoutDef === layoutDef().CARD && smartGrid.dataLayoutDef) {\n <div\n class=\"cards-container\"\n [ngStyle]=\"{\n 'grid-template-columns':\n smartGrid.numberOfColumn !== undefined\n ? 'repeat(' + smartGrid.numberOfColumn + ', 1fr)'\n : '',\n }\"\n >\n @for (row of smartGrid.gridModel.page.rows; track row.id) {\n <app-smart-grid-card\n class=\"smartGrid-card-container\"\n [class.clickable-row]=\"row.layoutDescriptor\"\n (click)=\"row.layoutDescriptor && onClick(row)\"\n [item]=\"row\"\n [smartGrid]=\"smartGrid\"\n [parentLayoutComponent]=\"parentLayoutComponent()\"\n ></app-smart-grid-card>\n }\n </div>\n }\n @if (smartGrid.layoutDef === layoutDef().TREE && treeControl) {\n <div\n class=\"tree-container\"\n [ngStyle]=\"{\n 'grid-template-columns':\n smartGrid.numberOfColumn !== undefined\n ? 'repeat(' + smartGrid.numberOfColumn + ', 1fr)'\n : '',\n }\"\n >\n <mat-tree [dataSource]=\"treeDataSource!\" [treeControl]=\"treeControl!\">\n <mat-tree-node\n *matTreeNodeDef=\"let node\"\n matTreeNodeToggle\n matTreeNodePadding\n matTreeNodePaddingIndent=\"24\"\n >\n <button mat-icon-button disabled class=\"tree-button\"></button>\n <mat-checkbox\n class=\"checklist-leaf-node\"\n [checked]=\"$safeNavigationMigration(treeChecklistSelection?.isSelected(node))\"\n (change)=\"treeNodeSelectionToggle(node)\"\n >{{ getTreeItem(node) }}</mat-checkbox\n >\n </mat-tree-node>\n <mat-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n matTreeNodePadding\n matTreeNodePaddingIndent=\"24\"\n >\n <button\n mat-icon-button\n matTreeNodeToggle\n class=\"tree-button\"\n [attr.aria-label]=\"'Toggle ' + node.item\"\n >\n <mat-icon class=\"mat-icon-rtl-mirror\">\n {{ treeControl!.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <mat-checkbox\n [checked]=\"$safeNavigationMigration(treeChecklistSelection?.isSelected(node))\"\n [indeterminate]=\"descendantsPartiallySelected(node)\"\n (change)=\"treeNodeSelectionToggle(node)\"\n >{{ getTreeItem(node) }}</mat-checkbox\n >\n </mat-tree-node>\n </mat-tree>\n </div>\n }\n </div>\n @if (smartGrid.paginator) {\n <div class=\"paginator-row-material\">\n @if (smartGrid.gridModel.refreshEnabled) {\n <button mat-icon-button type=\"button\" matTooltip=\"Friss\u00EDt\u00E9s\" (click)=\"refreshPartialResults()\">\n <mat-icon>refresh</mat-icon>\n </button>\n }\n @if (smartGrid.paginator && !treeControl) {\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageIndex]=\"pageIndex\"\n [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onChangePage($event)\"\n ></mat-paginator>\n }\n </div>\n }\n </div>\n }\n", styles: [".smartGrid-container{position:relative;padding:1.5rem;display:flex;flex-direction:column;gap:1rem}.smartGrid-data-number{padding-bottom:1rem}.cards-container{display:grid;gap:1rem;padding:1rem}.smartGrid-container ::ng-deep .mat-form-field-appearance-legacy .mat-form-field-underline{bottom:0}.smartGridContent{width:100%;flex:1;overflow:auto}.blocked{position:absolute;width:calc(100% - 3rem);height:calc(100% - 3rem);z-index:110;background-color:#0003}.smart-grid-table-container{display:flex;flex-direction:column;gap:1rem}.smart-grid-toolbar{display:flex;flex-direction:row;justify-content:flex-end;align-items:center}.smart-grid-toolbar smart-ui-action-toolbar{width:100%}.smart-grid-edit-icon{color:var(--light-gray)}.tree-button{width:24px!important;height:24px!important}.paginator-row-material{display:flex;align-items:center;align-self:flex-end;gap:.25rem}.clickable-row{cursor:pointer}\n"], dependencies: [{ kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"] }, { kind: "component", type: MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ExpandableSectionComponent, selector: "smart-expandable-section", inputs: ["data", "index"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: SmartGridCardComponent, selector: "app-smart-grid-card", inputs: ["item", "smartGrid", "parentLayoutComponent"] }, { kind: "component", type: MatTree, selector: "mat-tree", exportAs: ["matTree"] }, { kind: "directive", type: MatTreeNodeDef, selector: "[matTreeNodeDef]", inputs: ["matTreeNodeDefWhen", "matTreeNode"] }, { kind: "directive", type: MatTreeNode, selector: "mat-tree-node", inputs: ["tabIndex", "disabled"], outputs: ["activation", "expandedChange"], exportAs: ["matTreeNode"] }, { kind: "directive", type: MatTreeNodeToggle, selector: "[matTreeNodeToggle]", inputs: ["matTreeNodeToggleRecursive"] }, { kind: "directive", type: MatTreeNodePadding, selector: "[matTreeNodePadding]", inputs: ["matTreeNodePadding", "matTreeNodePaddingIndent"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }] }); }
18778
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SmartGridComponent, isStandalone: true, selector: "smart-grid", inputs: { smartGrid: { classPropertyName: "smartGrid", publicName: "smartGrid", isSignal: false, isRequired: false, transformFunction: null }, uuid: { classPropertyName: "uuid", publicName: "uuid", isSignal: false, isRequired: false, transformFunction: null }, parentLayoutComponent: { classPropertyName: "parentLayoutComponent", publicName: "parentLayoutComponent", isSignal: true, isRequired: false, transformFunction: null }, dev: { classPropertyName: "dev", publicName: "dev", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-testid": "this.testId" } }, providers: [SmartGridService], viewQueries: [{ propertyName: "vcRefTable", first: true, predicate: ["table"], descendants: true, read: ViewContainerRef }, { propertyName: "expandedRowsComps", predicate: ExpandedRowRendererComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (smartGrid) {\n<div class=\"smartGrid-container\">\n <div class=\"smart-grid-toolbar\">\n <smart-ui-action-toolbar [id]=\"toolbarId\"></smart-ui-action-toolbar>\n @if (smartGrid.showEditColumns) {\n <ui-action-button (actionClick)=\"editColumns()\" [descriptor]=\"showEditColumns\">\n </ui-action-button>\n }\n </div>\n <!-- <div [ngClass]=\"isBlocked ? 'blocked' : ''\"></div> -->\n <div class=\"smartGridContent\">\n <div>\n @if (smartGrid.showResultCount) {\n <div>\n <div class=\"smartGrid-data-number\">\n {{ smartGrid.gridModel.totalRowCount }}\n </div>\n </div>\n }\n </div>\n <div class=\"smart-grid-table-container\">\n <ng-template #table></ng-template>\n </div>\n @if (smartGrid.layoutDef === layoutDef().EXPANDABLE) {\n <div>\n @for (expandableSection of expandableSections; track expandableSection) {\n <smart-expandable-section #gridExpandableSection [data]=\"expandableSection\"></smart-expandable-section>\n }\n </div>\n }\n @if (smartGrid.layoutDef === layoutDef().CARD && smartGrid.dataLayoutDef) {\n <div class=\"cards-container\" [ngStyle]=\"{\n 'grid-template-columns':\n smartGrid.numberOfColumn !== undefined\n ? 'repeat(' + smartGrid.numberOfColumn + ', 1fr)'\n : '',\n }\">\n @for (row of smartGrid.gridModel.page.rows; track row.id) {\n <app-smart-grid-card class=\"smartGrid-card-container\" [class.clickable-row]=\"row.layoutDescriptor\"\n (click)=\"row.layoutDescriptor && onClick(row)\" [item]=\"row\" [smartGrid]=\"smartGrid\"\n [parentLayoutComponent]=\"parentLayoutComponent()\"></app-smart-grid-card>\n }\n </div>\n }\n @if (smartGrid.layoutDef === layoutDef().TREE && treeControl) {\n <div class=\"tree-container\" [ngStyle]=\"{\n 'grid-template-columns':\n smartGrid.numberOfColumn !== undefined\n ? 'repeat(' + smartGrid.numberOfColumn + ', 1fr)'\n : '',\n }\">\n <mat-tree [dataSource]=\"treeDataSource!\" [treeControl]=\"treeControl!\">\n <mat-tree-node *matTreeNodeDef=\"let node\" matTreeNodeToggle matTreeNodePadding matTreeNodePaddingIndent=\"24\">\n <button mat-icon-button disabled class=\"tree-button\"></button>\n <mat-checkbox class=\"checklist-leaf-node\"\n [checked]=\"$safeNavigationMigration(treeChecklistSelection?.isSelected(node))\"\n (change)=\"treeNodeSelectionToggle(node)\">{{ getTreeItem(node) }}</mat-checkbox>\n </mat-tree-node>\n <mat-tree-node *matTreeNodeDef=\"let node; when: hasChild\" matTreeNodePadding matTreeNodePaddingIndent=\"24\">\n <button mat-icon-button matTreeNodeToggle class=\"tree-button\" [attr.aria-label]=\"'Toggle ' + node.item\">\n <mat-icon class=\"mat-icon-rtl-mirror\">\n {{ treeControl!.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <mat-checkbox [checked]=\"$safeNavigationMigration(treeChecklistSelection?.isSelected(node))\"\n [indeterminate]=\"descendantsPartiallySelected(node)\" (change)=\"treeNodeSelectionToggle(node)\">{{\n getTreeItem(node) }}</mat-checkbox>\n </mat-tree-node>\n </mat-tree>\n </div>\n }\n </div>\n @if (smartGrid.paginator) {\n <div class=\"paginator-row-material\">\n @if (smartGrid.gridModel.refreshEnabled) {\n <button mat-icon-button type=\"button\" matTooltip=\"Friss\u00EDt\u00E9s\" (click)=\"refreshPartialResults()\">\n <mat-icon>refresh</mat-icon>\n </button>\n }\n @if (smartGrid.paginator && !treeControl) {\n <mat-paginator #paginator [length]=\"length\" [pageIndex]=\"pageIndex\" [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\" (page)=\"onChangePage($event)\"></mat-paginator>\n }\n </div>\n }\n</div>\n}", styles: [".smartGrid-container{position:relative;padding:1.5rem;display:flex;flex-direction:column;gap:1rem}.smartGrid-data-number{padding-bottom:1rem}.cards-container{display:grid;gap:1rem;padding:1rem}.smartGrid-container ::ng-deep .mat-form-field-appearance-legacy .mat-form-field-underline{bottom:0}.smartGridContent{width:100%;flex:1;overflow:auto}.blocked{position:absolute;width:calc(100% - 3rem);height:calc(100% - 3rem);z-index:110;background-color:#0003}.smart-grid-table-container{display:flex;flex-direction:column;gap:1rem}.smart-grid-toolbar{display:flex;flex-direction:row;justify-content:flex-end;align-items:center}.smart-grid-toolbar smart-ui-action-toolbar{width:100%}.smart-grid-edit-icon{color:var(--light-gray)}.tree-button{width:24px!important;height:24px!important}.paginator-row-material{display:flex;align-items:center;align-self:flex-end;gap:.25rem}.clickable-row{cursor:pointer}\n"], dependencies: [{ kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"] }, { kind: "component", type: UiActionButtonComponent, selector: "ui-action-button", inputs: ["disabled", "descriptor", "code", "identifier", "viewActivated", "addedCssClass", "addBasicClasses", "iconPlaceholder"], outputs: ["actionClick", "actionDoubleClick"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ExpandableSectionComponent, selector: "smart-expandable-section", inputs: ["data", "index"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: SmartGridCardComponent, selector: "app-smart-grid-card", inputs: ["item", "smartGrid", "parentLayoutComponent"] }, { kind: "component", type: MatTree, selector: "mat-tree", exportAs: ["matTree"] }, { kind: "directive", type: MatTreeNodeDef, selector: "[matTreeNodeDef]", inputs: ["matTreeNodeDefWhen", "matTreeNode"] }, { kind: "directive", type: MatTreeNode, selector: "mat-tree-node", inputs: ["tabIndex", "disabled"], outputs: ["activation", "expandedChange"], exportAs: ["matTreeNode"] }, { kind: "directive", type: MatTreeNodeToggle, selector: "[matTreeNodeToggle]", inputs: ["matTreeNodeToggleRecursive"] }, { kind: "directive", type: MatTreeNodePadding, selector: "[matTreeNodePadding]", inputs: ["matTreeNodePadding", "matTreeNodePaddingIndent"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }] }); }
18418
18779
  }
18419
18780
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartGridComponent, decorators: [{
18420
18781
  type: Component,
18421
- args: [{ selector: 'smart-grid', providers: [SmartGridService], imports: [UiActionToolbarComponent, MatMiniFabButton, MatIcon, ExpandableSectionComponent, NgStyle, SmartGridCardComponent, MatTree, MatTreeNodeDef, MatTreeNode, MatTreeNodeToggle, MatTreeNodePadding, MatIconButton, MatCheckbox, MatTooltip, MatPaginator], template: "@if (smartGrid) {\n <div class=\"smartGrid-container\">\n <div class=\"smart-grid-toolbar\">\n <smart-ui-action-toolbar [id]=\"toolbarId\"></smart-ui-action-toolbar>\n @if (smartGrid.showEditColumns) {\n <button\n (click)=\"editColumns()\"\n mat-mini-fab\n color=\"text-primary\"\n >\n <mat-icon aria-hidden=\"false\" aria-label=\"Columns\" class=\"smart-grid-edit-icon\"\n >view_columns</mat-icon\n >\n </button>\n }\n </div>\n <!-- <div [ngClass]=\"isBlocked ? 'blocked' : ''\"></div> -->\n <div class=\"smartGridContent\">\n <div>\n @if (smartGrid.showResultCount) {\n <div>\n <div class=\"smartGrid-data-number\">\n {{ smartGrid.gridModel.totalRowCount }}\n </div>\n </div>\n }\n </div>\n <div class=\"smart-grid-table-container\">\n <ng-template #table></ng-template>\n </div>\n @if (smartGrid.layoutDef === layoutDef().EXPANDABLE) {\n <div>\n @for (expandableSection of expandableSections; track expandableSection) {\n <smart-expandable-section\n #gridExpandableSection\n [data]=\"expandableSection\"\n ></smart-expandable-section>\n }\n </div>\n }\n @if (smartGrid.layoutDef === layoutDef().CARD && smartGrid.dataLayoutDef) {\n <div\n class=\"cards-container\"\n [ngStyle]=\"{\n 'grid-template-columns':\n smartGrid.numberOfColumn !== undefined\n ? 'repeat(' + smartGrid.numberOfColumn + ', 1fr)'\n : '',\n }\"\n >\n @for (row of smartGrid.gridModel.page.rows; track row.id) {\n <app-smart-grid-card\n class=\"smartGrid-card-container\"\n [class.clickable-row]=\"row.layoutDescriptor\"\n (click)=\"row.layoutDescriptor && onClick(row)\"\n [item]=\"row\"\n [smartGrid]=\"smartGrid\"\n [parentLayoutComponent]=\"parentLayoutComponent()\"\n ></app-smart-grid-card>\n }\n </div>\n }\n @if (smartGrid.layoutDef === layoutDef().TREE && treeControl) {\n <div\n class=\"tree-container\"\n [ngStyle]=\"{\n 'grid-template-columns':\n smartGrid.numberOfColumn !== undefined\n ? 'repeat(' + smartGrid.numberOfColumn + ', 1fr)'\n : '',\n }\"\n >\n <mat-tree [dataSource]=\"treeDataSource!\" [treeControl]=\"treeControl!\">\n <mat-tree-node\n *matTreeNodeDef=\"let node\"\n matTreeNodeToggle\n matTreeNodePadding\n matTreeNodePaddingIndent=\"24\"\n >\n <button mat-icon-button disabled class=\"tree-button\"></button>\n <mat-checkbox\n class=\"checklist-leaf-node\"\n [checked]=\"$safeNavigationMigration(treeChecklistSelection?.isSelected(node))\"\n (change)=\"treeNodeSelectionToggle(node)\"\n >{{ getTreeItem(node) }}</mat-checkbox\n >\n </mat-tree-node>\n <mat-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n matTreeNodePadding\n matTreeNodePaddingIndent=\"24\"\n >\n <button\n mat-icon-button\n matTreeNodeToggle\n class=\"tree-button\"\n [attr.aria-label]=\"'Toggle ' + node.item\"\n >\n <mat-icon class=\"mat-icon-rtl-mirror\">\n {{ treeControl!.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <mat-checkbox\n [checked]=\"$safeNavigationMigration(treeChecklistSelection?.isSelected(node))\"\n [indeterminate]=\"descendantsPartiallySelected(node)\"\n (change)=\"treeNodeSelectionToggle(node)\"\n >{{ getTreeItem(node) }}</mat-checkbox\n >\n </mat-tree-node>\n </mat-tree>\n </div>\n }\n </div>\n @if (smartGrid.paginator) {\n <div class=\"paginator-row-material\">\n @if (smartGrid.gridModel.refreshEnabled) {\n <button mat-icon-button type=\"button\" matTooltip=\"Friss\u00EDt\u00E9s\" (click)=\"refreshPartialResults()\">\n <mat-icon>refresh</mat-icon>\n </button>\n }\n @if (smartGrid.paginator && !treeControl) {\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageIndex]=\"pageIndex\"\n [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onChangePage($event)\"\n ></mat-paginator>\n }\n </div>\n }\n </div>\n }\n", styles: [".smartGrid-container{position:relative;padding:1.5rem;display:flex;flex-direction:column;gap:1rem}.smartGrid-data-number{padding-bottom:1rem}.cards-container{display:grid;gap:1rem;padding:1rem}.smartGrid-container ::ng-deep .mat-form-field-appearance-legacy .mat-form-field-underline{bottom:0}.smartGridContent{width:100%;flex:1;overflow:auto}.blocked{position:absolute;width:calc(100% - 3rem);height:calc(100% - 3rem);z-index:110;background-color:#0003}.smart-grid-table-container{display:flex;flex-direction:column;gap:1rem}.smart-grid-toolbar{display:flex;flex-direction:row;justify-content:flex-end;align-items:center}.smart-grid-toolbar smart-ui-action-toolbar{width:100%}.smart-grid-edit-icon{color:var(--light-gray)}.tree-button{width:24px!important;height:24px!important}.paginator-row-material{display:flex;align-items:center;align-self:flex-end;gap:.25rem}.clickable-row{cursor:pointer}\n"] }]
18782
+ args: [{ selector: 'smart-grid', providers: [SmartGridService], imports: [UiActionToolbarComponent, UiActionButtonComponent, MatMiniFabButton, MatIcon, ExpandableSectionComponent, NgStyle, SmartGridCardComponent, MatTree, MatTreeNodeDef, MatTreeNode, MatTreeNodeToggle, MatTreeNodePadding, MatIconButton, MatCheckbox, MatTooltip, MatPaginator], template: "@if (smartGrid) {\n<div class=\"smartGrid-container\">\n <div class=\"smart-grid-toolbar\">\n <smart-ui-action-toolbar [id]=\"toolbarId\"></smart-ui-action-toolbar>\n @if (smartGrid.showEditColumns) {\n <ui-action-button (actionClick)=\"editColumns()\" [descriptor]=\"showEditColumns\">\n </ui-action-button>\n }\n </div>\n <!-- <div [ngClass]=\"isBlocked ? 'blocked' : ''\"></div> -->\n <div class=\"smartGridContent\">\n <div>\n @if (smartGrid.showResultCount) {\n <div>\n <div class=\"smartGrid-data-number\">\n {{ smartGrid.gridModel.totalRowCount }}\n </div>\n </div>\n }\n </div>\n <div class=\"smart-grid-table-container\">\n <ng-template #table></ng-template>\n </div>\n @if (smartGrid.layoutDef === layoutDef().EXPANDABLE) {\n <div>\n @for (expandableSection of expandableSections; track expandableSection) {\n <smart-expandable-section #gridExpandableSection [data]=\"expandableSection\"></smart-expandable-section>\n }\n </div>\n }\n @if (smartGrid.layoutDef === layoutDef().CARD && smartGrid.dataLayoutDef) {\n <div class=\"cards-container\" [ngStyle]=\"{\n 'grid-template-columns':\n smartGrid.numberOfColumn !== undefined\n ? 'repeat(' + smartGrid.numberOfColumn + ', 1fr)'\n : '',\n }\">\n @for (row of smartGrid.gridModel.page.rows; track row.id) {\n <app-smart-grid-card class=\"smartGrid-card-container\" [class.clickable-row]=\"row.layoutDescriptor\"\n (click)=\"row.layoutDescriptor && onClick(row)\" [item]=\"row\" [smartGrid]=\"smartGrid\"\n [parentLayoutComponent]=\"parentLayoutComponent()\"></app-smart-grid-card>\n }\n </div>\n }\n @if (smartGrid.layoutDef === layoutDef().TREE && treeControl) {\n <div class=\"tree-container\" [ngStyle]=\"{\n 'grid-template-columns':\n smartGrid.numberOfColumn !== undefined\n ? 'repeat(' + smartGrid.numberOfColumn + ', 1fr)'\n : '',\n }\">\n <mat-tree [dataSource]=\"treeDataSource!\" [treeControl]=\"treeControl!\">\n <mat-tree-node *matTreeNodeDef=\"let node\" matTreeNodeToggle matTreeNodePadding matTreeNodePaddingIndent=\"24\">\n <button mat-icon-button disabled class=\"tree-button\"></button>\n <mat-checkbox class=\"checklist-leaf-node\"\n [checked]=\"$safeNavigationMigration(treeChecklistSelection?.isSelected(node))\"\n (change)=\"treeNodeSelectionToggle(node)\">{{ getTreeItem(node) }}</mat-checkbox>\n </mat-tree-node>\n <mat-tree-node *matTreeNodeDef=\"let node; when: hasChild\" matTreeNodePadding matTreeNodePaddingIndent=\"24\">\n <button mat-icon-button matTreeNodeToggle class=\"tree-button\" [attr.aria-label]=\"'Toggle ' + node.item\">\n <mat-icon class=\"mat-icon-rtl-mirror\">\n {{ treeControl!.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <mat-checkbox [checked]=\"$safeNavigationMigration(treeChecklistSelection?.isSelected(node))\"\n [indeterminate]=\"descendantsPartiallySelected(node)\" (change)=\"treeNodeSelectionToggle(node)\">{{\n getTreeItem(node) }}</mat-checkbox>\n </mat-tree-node>\n </mat-tree>\n </div>\n }\n </div>\n @if (smartGrid.paginator) {\n <div class=\"paginator-row-material\">\n @if (smartGrid.gridModel.refreshEnabled) {\n <button mat-icon-button type=\"button\" matTooltip=\"Friss\u00EDt\u00E9s\" (click)=\"refreshPartialResults()\">\n <mat-icon>refresh</mat-icon>\n </button>\n }\n @if (smartGrid.paginator && !treeControl) {\n <mat-paginator #paginator [length]=\"length\" [pageIndex]=\"pageIndex\" [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\" (page)=\"onChangePage($event)\"></mat-paginator>\n }\n </div>\n }\n</div>\n}", styles: [".smartGrid-container{position:relative;padding:1.5rem;display:flex;flex-direction:column;gap:1rem}.smartGrid-data-number{padding-bottom:1rem}.cards-container{display:grid;gap:1rem;padding:1rem}.smartGrid-container ::ng-deep .mat-form-field-appearance-legacy .mat-form-field-underline{bottom:0}.smartGridContent{width:100%;flex:1;overflow:auto}.blocked{position:absolute;width:calc(100% - 3rem);height:calc(100% - 3rem);z-index:110;background-color:#0003}.smart-grid-table-container{display:flex;flex-direction:column;gap:1rem}.smart-grid-toolbar{display:flex;flex-direction:row;justify-content:flex-end;align-items:center}.smart-grid-toolbar smart-ui-action-toolbar{width:100%}.smart-grid-edit-icon{color:var(--light-gray)}.tree-button{width:24px!important;height:24px!important}.paginator-row-material{display:flex;align-items:center;align-self:flex-end;gap:.25rem}.clickable-row{cursor:pointer}\n"] }]
18422
18783
  }], ctorParameters: () => [], propDecorators: { smartGrid: [{
18423
18784
  type: Input
18424
18785
  }], uuid: [{
@@ -22374,7 +22735,6 @@ class GenericPageComponent extends SmartComponent {
22374
22735
  /** Written from `dataChanged`, so a signal — the page has no template event that would. */
22375
22736
  this.componentLayoutState = signal(undefined, /* @ts-ignore */
22376
22737
  ...(ngDevMode ? [{ debugName: "componentLayoutState" }] : /* istanbul ignore next */ []));
22377
- this.sendModelOnWidgetAction = true;
22378
22738
  this.dataChanged.pipe(takeUntil(this._destroy$)).subscribe(() => {
22379
22739
  if (this.model?.componentLayouts) {
22380
22740
  this.setUpComponentLayout();
@@ -22436,7 +22796,6 @@ class SearchPageComponent extends SmartComponent {
22436
22796
  this.layoutComponent = viewChild(SmartComponentLayoutComponent, /* @ts-ignore */
22437
22797
  ...(ngDevMode ? [{ debugName: "layoutComponent" }] : /* istanbul ignore next */ []));
22438
22798
  this.setupGrid();
22439
- this.sendModelOnWidgetAction = true;
22440
22799
  // uuid is set in onUuidResolved — it is not known at construction for
22441
22800
  // dynamic default-component views. (#28870)
22442
22801
  this.filterService.config = {
@@ -23357,7 +23716,6 @@ class NoPermissionPageComponent extends SmartComponent {
23357
23716
  /** Written from `dataChanged`, so a signal. */
23358
23717
  this.componentLayoutState = signal(undefined, /* @ts-ignore */
23359
23718
  ...(ngDevMode ? [{ debugName: "componentLayoutState" }] : /* istanbul ignore next */ []));
23360
- this.sendModelOnWidgetAction = true;
23361
23719
  this.dataChanged.pipe(takeUntil(this._destroy$)).subscribe(() => {
23362
23720
  if (this.model?.componentLayouts) {
23363
23721
  this.setUpComponentLayout();
@@ -23399,7 +23757,6 @@ class InvalidSmartlinkComponent extends SmartComponent {
23399
23757
  /** Written from `dataChanged`, so a signal. */
23400
23758
  this.componentLayoutState = signal(undefined, /* @ts-ignore */
23401
23759
  ...(ngDevMode ? [{ debugName: "componentLayoutState" }] : /* istanbul ignore next */ []));
23402
- this.sendModelOnWidgetAction = true;
23403
23760
  this.dataChanged.pipe(takeUntil(this._destroy$)).subscribe(() => {
23404
23761
  if (this.model?.componentLayouts) {
23405
23762
  this.setUpComponentLayout();