@solcre-org/core-ui 2.11.42 → 2.12.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.
@@ -989,6 +989,7 @@ class FileFieldComponent extends BaseFieldComponent {
989
989
  selectedFiles = signal([]);
990
990
  existingFiles = signal([]);
991
991
  previewUrls = signal([]);
992
+ newFilesPreviews = signal([]);
992
993
  previewBlobs = signal([]);
993
994
  previewFileIds = signal(new Map());
994
995
  errorMessage = signal('');
@@ -1005,10 +1006,18 @@ class FileFieldComponent extends BaseFieldComponent {
1005
1006
  const existing = this.existingFiles();
1006
1007
  const selected = this.selectedFiles();
1007
1008
  const blobs = this.previewBlobs();
1008
- if (blobs.length > 0) {
1009
- return [...selected, ...blobs];
1009
+ const hasPreviewUrls = this.getPreviewUrls().length > 0;
1010
+ if (hasPreviewUrls) {
1011
+ return [...blobs, ...selected];
1012
+ }
1013
+ else {
1014
+ return [...existing, ...selected];
1010
1015
  }
1011
- return [...existing, ...selected];
1016
+ });
1017
+ allPreviewUrls = computed(() => {
1018
+ const remoteUrls = this.previewUrls();
1019
+ const newFileUrls = this.newFilesPreviews();
1020
+ return [...remoteUrls, ...newFileUrls];
1012
1021
  });
1013
1022
  previewUrlsEffect = effect(() => {
1014
1023
  const previewUrls = this.getPreviewUrls();
@@ -1018,17 +1027,8 @@ class FileFieldComponent extends BaseFieldComponent {
1018
1027
  });
1019
1028
  valueChangeEffect = effect(() => {
1020
1029
  const currentValue = this.value();
1021
- const field = this.field();
1022
- const syncEnabled = field.syncWithValue !== false;
1023
- if (syncEnabled) {
1024
- this.reinitializeFilesFromValue(currentValue, field);
1025
- }
1026
- });
1027
- refreshTriggerEffect = effect(() => {
1028
- const field = this.field();
1029
- if (field.refreshTrigger !== undefined) {
1030
- const currentValue = this.value();
1031
- this.reinitializeFilesFromValue(currentValue, field);
1030
+ if (currentValue) {
1031
+ setTimeout(() => this.regeneratePreviewsIfNeeded(), 0);
1032
1032
  }
1033
1033
  });
1034
1034
  getPreviewUrls() {
@@ -1098,6 +1098,10 @@ class FileFieldComponent extends BaseFieldComponent {
1098
1098
  ngOnInit() {
1099
1099
  super.ngOnInit();
1100
1100
  this.initializeFiles();
1101
+ setTimeout(() => this.regeneratePreviewsIfNeeded(), 100);
1102
+ }
1103
+ ngAfterViewInit() {
1104
+ setTimeout(() => this.regeneratePreviewsIfNeeded(), 200);
1101
1105
  }
1102
1106
  initializeFormControl() {
1103
1107
  const modeConfig = this.field().modes?.[this.mode()];
@@ -1151,53 +1155,71 @@ class FileFieldComponent extends BaseFieldComponent {
1151
1155
  this.existingFiles.set([]);
1152
1156
  }
1153
1157
  }
1154
- reinitializeFilesFromValue(currentValue, field) {
1155
- this.selectedFiles.set([]);
1156
- this.existingFiles.set([]);
1157
- this.previewUrls.set([]);
1158
- this.previewBlobs.set([]);
1159
- this.previewFileIds.set(new Map());
1160
- this.clearLocalError();
1158
+ regeneratePreviewsIfNeeded() {
1159
+ const currentValue = this.value();
1160
+ const field = this.field();
1161
1161
  const hasPreviewUrls = field.previewUrls && ((Array.isArray(field.previewUrls) && field.previewUrls.length > 0) ||
1162
1162
  (typeof field.previewUrls === 'function'));
1163
- if (currentValue && !hasPreviewUrls) {
1163
+ if (currentValue) {
1164
+ const currentNewPreviews = this.newFilesPreviews();
1165
+ const currentSelected = this.selectedFiles();
1164
1166
  if (currentValue instanceof File) {
1165
- this.selectedFiles.set([currentValue]);
1166
- this.generatePreviews([currentValue]);
1167
+ if (!currentSelected.includes(currentValue) || currentNewPreviews.length === 0) {
1168
+ this.selectedFiles.set([currentValue]);
1169
+ this.generatePreviews([currentValue]);
1170
+ }
1167
1171
  }
1168
1172
  else if (Array.isArray(currentValue)) {
1169
1173
  const files = currentValue.filter(v => v instanceof File);
1170
1174
  const serverFiles = currentValue.filter(v => this.isServerFile(v));
1171
- this.selectedFiles.set(files);
1172
- this.existingFiles.set(serverFiles);
1173
- this.generatePreviews(files);
1174
- }
1175
- else if (this.isServerFile(currentValue)) {
1176
- this.existingFiles.set([currentValue]);
1175
+ const filesMatch = files.length === currentSelected.length &&
1176
+ files.every(file => currentSelected.includes(file));
1177
+ if (!filesMatch || (files.length > 0 && currentNewPreviews.length === 0)) {
1178
+ this.selectedFiles.set(files);
1179
+ if (!hasPreviewUrls) {
1180
+ this.existingFiles.set(serverFiles);
1181
+ }
1182
+ this.generatePreviews(files);
1183
+ }
1184
+ else if (files.length === 0 && serverFiles.length > 0 && currentSelected.length > 0) {
1185
+ this.selectedFiles.set([]);
1186
+ if (!hasPreviewUrls) {
1187
+ this.existingFiles.set(serverFiles);
1188
+ }
1189
+ this.newFilesPreviews.set([]);
1190
+ }
1177
1191
  }
1178
1192
  }
1179
- else if (hasPreviewUrls) {
1180
- this.selectedFiles.set([]);
1181
- this.existingFiles.set([]);
1182
- }
1183
1193
  }
1184
1194
  isServerFile(value) {
1185
1195
  return value && typeof value === 'object' && 'id' in value && 'filename' in value;
1186
1196
  }
1187
- forceRefresh() {
1188
- const currentValue = this.value();
1189
- const field = this.field();
1190
- this.reinitializeFilesFromValue(currentValue, field);
1191
- }
1192
1197
  generatePreviews(files) {
1198
+ if (!files.length)
1199
+ return;
1193
1200
  const urls = [];
1194
- files.forEach(file => {
1201
+ let processedCount = 0;
1202
+ const totalFiles = files.filter(file => file.type.startsWith('image/')).length;
1203
+ if (totalFiles === 0)
1204
+ return;
1205
+ files.forEach((file, index) => {
1195
1206
  if (file.type.startsWith('image/')) {
1196
1207
  const reader = new FileReader();
1197
1208
  reader.onload = (e) => {
1198
1209
  if (e.target?.result) {
1199
- urls.push(e.target.result);
1200
- this.previewUrls.set([...urls]);
1210
+ urls[index] = e.target.result;
1211
+ processedCount++;
1212
+ if (processedCount === totalFiles) {
1213
+ const validUrls = urls.filter(url => url);
1214
+ this.newFilesPreviews.set(validUrls);
1215
+ }
1216
+ }
1217
+ };
1218
+ reader.onerror = () => {
1219
+ processedCount++;
1220
+ if (processedCount === totalFiles) {
1221
+ const validUrls = urls.filter(url => url);
1222
+ this.newFilesPreviews.set(validUrls);
1201
1223
  }
1202
1224
  };
1203
1225
  reader.readAsDataURL(file);
@@ -1412,6 +1434,31 @@ class FileFieldComponent extends BaseFieldComponent {
1412
1434
  return -1;
1413
1435
  return this.selectedFiles().indexOf(file);
1414
1436
  }
1437
+ getPreviewUrlForFile(file) {
1438
+ if (this.isPreviewFile(file)) {
1439
+ return this.getOriginalUrl(file);
1440
+ }
1441
+ if (!this.isServerFile(file)) {
1442
+ const fileIndex = this.selectedFiles().indexOf(file);
1443
+ if (fileIndex >= 0) {
1444
+ const newFilePreviews = this.newFilesPreviews();
1445
+ return newFilePreviews[fileIndex] || null;
1446
+ }
1447
+ }
1448
+ return null;
1449
+ }
1450
+ getPreviewUrl(file) {
1451
+ if (this.isServerFile(file))
1452
+ return null;
1453
+ if (this.isPreviewFile(file))
1454
+ return this.getOriginalUrl(file);
1455
+ const fileIndex = this.selectedFiles().indexOf(file);
1456
+ if (fileIndex >= 0) {
1457
+ const newFilePreviews = this.newFilesPreviews();
1458
+ return newFilePreviews[fileIndex] || null;
1459
+ }
1460
+ return null;
1461
+ }
1415
1462
  isPreviewFile(file) {
1416
1463
  return !this.isServerFile(file) && file.__isPreviewFile === true;
1417
1464
  }
@@ -1422,11 +1469,11 @@ class FileFieldComponent extends BaseFieldComponent {
1422
1469
  this.onBlur();
1423
1470
  }
1424
1471
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: FileFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1425
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: FileFieldComponent, isStandalone: true, selector: "core-file-field", usesInheritance: true, hostDirectives: [{ directive: CoreHostDirective }], ngImport: i0, template: "<label class=\"c-entry-item\" [class.c-entry-item--inline]=\"field().inline\">\n @if (field().label) {\n <span class=\"c-entry-text\">\n {{ field().label | translate }}\n @if (hasRequiredValidators()) {\n <span class=\"c-required\">*</span>\n }\n </span>\n }\n <div class=\"c-entry-file\" [class.has-error]=\"hasError()\">\n @if(fieldConfig().multiple || isEditMode() || allFiles().length === 0) {\n <label class=\"c-entry-file__label\">\n <span class=\"icon-upload c-entry-file__icon\"></span>\n <input\n type=\"file\"\n class=\"c-entry-file__input\"\n (change)=\"onFileSelected($event)\"\n [accept]=\"fieldConfig().accept\"\n [multiple]=\"fieldConfig().multiple\"\n (blur)=\"onBlurInput()\"\n [readonly]=\"isReadonly()\"\n >\n {{ fieldConfig().placeholder ?? fieldConfig().label | translate }}\n @if(fieldConfig().acceptString) {\n <br>\n <span class=\"c-entry-file__filename\" id=\"file-name\">{{ fieldConfig().acceptString }}</span>\n }\n </label>\n }\n\n @if (allFiles().length > 0) {\n <div class=\"c-attachments\">\n <p class=\"c-entry-text\">{{ 'files.attachedFiles' | translate }}</p>\n \n <ul class=\"c-attachments__list\">\n @for (file of allFiles(); track $index; let i = $index) {\n <li class=\"c-attachments__item\">\n <div class=\"c-bulleted-text\">\n @if(!isServerFile(file)) {\n <time>{{ getCurrentDate() }}</time>\n <span>{{ getCurrentUser() }}</span>\n }\n </div>\n \n <div class=\"c-attachments__holder\">\n @if(!isServerFile(file) && file.type.startsWith('image/') && fieldConfig().showPreview !== false) {\n <div class=\"c-attachments__content\">\n <div class=\"c-attachments__pic\">\n @if(isPreviewFile(file) && getOriginalUrl(file)) {\n <core-image-preview\n [src]=\"getOriginalUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n } @else if(getPreviewIndex(file) >= 0 && previewUrls()[getPreviewIndex(file)]) {\n <core-image-preview\n [src]=\"previewUrls()[getPreviewIndex(file)]\"\n [alt]=\"file.name\"\n ></core-image-preview>\n }\n </div>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n </div>\n } @else {\n <a class=\"c-attachments__content\">\n @if(isServerFile(file)) {\n <span class=\"icon-file\"></span>\n {{ file.filename }}\n } @else {\n <span [class]=\"getFileIcon(file)\"></span>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n }\n </a>\n }\n\n <div class=\"c-attachments__actions u-flex u-flex--wrap\">\n <button \n type=\"button\" \n class=\"c-link context:error\" \n (click)=\"removeFile(i)\"\n [title]=\"'files.remove' | translate\"\n >\n <span class=\"icon-delete\"></span>\n {{ 'files.remove' | translate }}\n </button>\n\n @if(fieldConfig().customActions) {\n @for(action of fieldConfig().customActions; track action.id) {\n <button \n type=\"button\"\n class=\"c-link c-link--underlined\" \n (click)=\"action.action(file)\"\n [title]=\"action.label | translate\"\n >\n {{ action.label | translate }}\n <span [ngClass]=\"action.icon | coreIconCompat\"></span>\n </button>\n }\n }\n </div>\n </div>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n <core-field-errors [errors]=\"errors()\" />\n @if(displayErrorMessage()) {\n <span class=\"c-entry-error\">{{ displayErrorMessage()!.key | translate:displayErrorMessage()!.params }}</span>\n }\n</label>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: FieldErrorsComponent, selector: "core-field-errors", inputs: ["errors"] }, { kind: "pipe", type: IconCompatPipe, name: "coreIconCompat" }, { kind: "component", type: ImagePreviewComponent, selector: "core-image-preview", inputs: ["src", "alt", "title", "width", "height", "objectFit", "borderRadius", "cursor", "loading"] }] });
1472
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: FileFieldComponent, isStandalone: true, selector: "core-file-field", usesInheritance: true, hostDirectives: [{ directive: CoreHostDirective }], ngImport: i0, template: "<label class=\"c-entry-item\" [class.c-entry-item--inline]=\"field().inline\">\n @if (field().label) {\n <span class=\"c-entry-text\">\n {{ field().label | translate }}\n @if (hasRequiredValidators()) {\n <span class=\"c-required\">*</span>\n }\n </span>\n }\n <div class=\"c-entry-file\" [class.has-error]=\"hasError()\">\n @if(fieldConfig().multiple || isEditMode() || allFiles().length === 0) {\n <label class=\"c-entry-file__label\">\n <span class=\"icon-upload c-entry-file__icon\"></span>\n <input\n type=\"file\"\n class=\"c-entry-file__input\"\n (change)=\"onFileSelected($event)\"\n [accept]=\"fieldConfig().accept\"\n [multiple]=\"fieldConfig().multiple\"\n (blur)=\"onBlurInput()\"\n [readonly]=\"isReadonly()\"\n >\n {{ fieldConfig().placeholder ?? fieldConfig().label | translate }}\n @if(fieldConfig().acceptString) {\n <br>\n <span class=\"c-entry-file__filename\" id=\"file-name\">{{ fieldConfig().acceptString }}</span>\n }\n </label>\n }\n\n @if (allFiles().length > 0) {\n <div class=\"c-attachments\">\n <p class=\"c-entry-text\">{{ 'files.attachedFiles' | translate }}</p>\n \n <ul class=\"c-attachments__list\">\n @for (file of allFiles(); track $index; let i = $index) {\n <li class=\"c-attachments__item\">\n <div class=\"c-bulleted-text\">\n @if(!isServerFile(file)) {\n <time>{{ getCurrentDate() }}</time>\n <span>{{ getCurrentUser() }}</span>\n }\n </div>\n \n <div class=\"c-attachments__holder\">\n @if(!isServerFile(file) && file.type.startsWith('image/') && fieldConfig().showPreview !== false) {\n <div class=\"c-attachments__content\">\n <div class=\"c-attachments__pic\">\n @if(isPreviewFile(file) && getOriginalUrl(file)) {\n <core-image-preview\n [src]=\"getOriginalUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n } @else if(getPreviewUrl(file)) {\n <core-image-preview\n [src]=\"getPreviewUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n }\n </div>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n </div>\n } @else {\n <a class=\"c-attachments__content\">\n @if(isServerFile(file)) {\n <span class=\"icon-file\"></span>\n {{ file.filename }}\n } @else {\n <span [class]=\"getFileIcon(file)\"></span>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n }\n </a>\n }\n\n <div class=\"c-attachments__actions u-flex u-flex--wrap\">\n <button \n type=\"button\" \n class=\"c-link context:error\" \n (click)=\"removeFile(i)\"\n [title]=\"'files.remove' | translate\"\n >\n <span class=\"icon-delete\"></span>\n {{ 'files.remove' | translate }}\n </button>\n\n @if(fieldConfig().customActions) {\n @for(action of fieldConfig().customActions; track action.id) {\n <button \n type=\"button\"\n class=\"c-link c-link--underlined\" \n (click)=\"action.action(file)\"\n [title]=\"action.label | translate\"\n >\n {{ action.label | translate }}\n <span [ngClass]=\"action.icon | coreIconCompat\"></span>\n </button>\n }\n }\n </div>\n </div>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n <core-field-errors [errors]=\"errors()\" />\n @if(displayErrorMessage()) {\n <span class=\"c-entry-error\">{{ displayErrorMessage()!.key | translate:displayErrorMessage()!.params }}</span>\n }\n</label>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: FieldErrorsComponent, selector: "core-field-errors", inputs: ["errors"] }, { kind: "pipe", type: IconCompatPipe, name: "coreIconCompat" }, { kind: "component", type: ImagePreviewComponent, selector: "core-image-preview", inputs: ["src", "alt", "title", "width", "height", "objectFit", "borderRadius", "cursor", "loading"] }] });
1426
1473
  }
1427
1474
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: FileFieldComponent, decorators: [{
1428
1475
  type: Component,
1429
- args: [{ selector: 'core-file-field', standalone: true, imports: [CommonModule, FormsModule, TranslateModule, ReactiveFormsModule, FieldErrorsComponent, IconCompatPipe, ImagePreviewComponent], hostDirectives: [CoreHostDirective], template: "<label class=\"c-entry-item\" [class.c-entry-item--inline]=\"field().inline\">\n @if (field().label) {\n <span class=\"c-entry-text\">\n {{ field().label | translate }}\n @if (hasRequiredValidators()) {\n <span class=\"c-required\">*</span>\n }\n </span>\n }\n <div class=\"c-entry-file\" [class.has-error]=\"hasError()\">\n @if(fieldConfig().multiple || isEditMode() || allFiles().length === 0) {\n <label class=\"c-entry-file__label\">\n <span class=\"icon-upload c-entry-file__icon\"></span>\n <input\n type=\"file\"\n class=\"c-entry-file__input\"\n (change)=\"onFileSelected($event)\"\n [accept]=\"fieldConfig().accept\"\n [multiple]=\"fieldConfig().multiple\"\n (blur)=\"onBlurInput()\"\n [readonly]=\"isReadonly()\"\n >\n {{ fieldConfig().placeholder ?? fieldConfig().label | translate }}\n @if(fieldConfig().acceptString) {\n <br>\n <span class=\"c-entry-file__filename\" id=\"file-name\">{{ fieldConfig().acceptString }}</span>\n }\n </label>\n }\n\n @if (allFiles().length > 0) {\n <div class=\"c-attachments\">\n <p class=\"c-entry-text\">{{ 'files.attachedFiles' | translate }}</p>\n \n <ul class=\"c-attachments__list\">\n @for (file of allFiles(); track $index; let i = $index) {\n <li class=\"c-attachments__item\">\n <div class=\"c-bulleted-text\">\n @if(!isServerFile(file)) {\n <time>{{ getCurrentDate() }}</time>\n <span>{{ getCurrentUser() }}</span>\n }\n </div>\n \n <div class=\"c-attachments__holder\">\n @if(!isServerFile(file) && file.type.startsWith('image/') && fieldConfig().showPreview !== false) {\n <div class=\"c-attachments__content\">\n <div class=\"c-attachments__pic\">\n @if(isPreviewFile(file) && getOriginalUrl(file)) {\n <core-image-preview\n [src]=\"getOriginalUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n } @else if(getPreviewIndex(file) >= 0 && previewUrls()[getPreviewIndex(file)]) {\n <core-image-preview\n [src]=\"previewUrls()[getPreviewIndex(file)]\"\n [alt]=\"file.name\"\n ></core-image-preview>\n }\n </div>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n </div>\n } @else {\n <a class=\"c-attachments__content\">\n @if(isServerFile(file)) {\n <span class=\"icon-file\"></span>\n {{ file.filename }}\n } @else {\n <span [class]=\"getFileIcon(file)\"></span>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n }\n </a>\n }\n\n <div class=\"c-attachments__actions u-flex u-flex--wrap\">\n <button \n type=\"button\" \n class=\"c-link context:error\" \n (click)=\"removeFile(i)\"\n [title]=\"'files.remove' | translate\"\n >\n <span class=\"icon-delete\"></span>\n {{ 'files.remove' | translate }}\n </button>\n\n @if(fieldConfig().customActions) {\n @for(action of fieldConfig().customActions; track action.id) {\n <button \n type=\"button\"\n class=\"c-link c-link--underlined\" \n (click)=\"action.action(file)\"\n [title]=\"action.label | translate\"\n >\n {{ action.label | translate }}\n <span [ngClass]=\"action.icon | coreIconCompat\"></span>\n </button>\n }\n }\n </div>\n </div>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n <core-field-errors [errors]=\"errors()\" />\n @if(displayErrorMessage()) {\n <span class=\"c-entry-error\">{{ displayErrorMessage()!.key | translate:displayErrorMessage()!.params }}</span>\n }\n</label>\n" }]
1476
+ args: [{ selector: 'core-file-field', standalone: true, imports: [CommonModule, FormsModule, TranslateModule, ReactiveFormsModule, FieldErrorsComponent, IconCompatPipe, ImagePreviewComponent], hostDirectives: [CoreHostDirective], template: "<label class=\"c-entry-item\" [class.c-entry-item--inline]=\"field().inline\">\n @if (field().label) {\n <span class=\"c-entry-text\">\n {{ field().label | translate }}\n @if (hasRequiredValidators()) {\n <span class=\"c-required\">*</span>\n }\n </span>\n }\n <div class=\"c-entry-file\" [class.has-error]=\"hasError()\">\n @if(fieldConfig().multiple || isEditMode() || allFiles().length === 0) {\n <label class=\"c-entry-file__label\">\n <span class=\"icon-upload c-entry-file__icon\"></span>\n <input\n type=\"file\"\n class=\"c-entry-file__input\"\n (change)=\"onFileSelected($event)\"\n [accept]=\"fieldConfig().accept\"\n [multiple]=\"fieldConfig().multiple\"\n (blur)=\"onBlurInput()\"\n [readonly]=\"isReadonly()\"\n >\n {{ fieldConfig().placeholder ?? fieldConfig().label | translate }}\n @if(fieldConfig().acceptString) {\n <br>\n <span class=\"c-entry-file__filename\" id=\"file-name\">{{ fieldConfig().acceptString }}</span>\n }\n </label>\n }\n\n @if (allFiles().length > 0) {\n <div class=\"c-attachments\">\n <p class=\"c-entry-text\">{{ 'files.attachedFiles' | translate }}</p>\n \n <ul class=\"c-attachments__list\">\n @for (file of allFiles(); track $index; let i = $index) {\n <li class=\"c-attachments__item\">\n <div class=\"c-bulleted-text\">\n @if(!isServerFile(file)) {\n <time>{{ getCurrentDate() }}</time>\n <span>{{ getCurrentUser() }}</span>\n }\n </div>\n \n <div class=\"c-attachments__holder\">\n @if(!isServerFile(file) && file.type.startsWith('image/') && fieldConfig().showPreview !== false) {\n <div class=\"c-attachments__content\">\n <div class=\"c-attachments__pic\">\n @if(isPreviewFile(file) && getOriginalUrl(file)) {\n <core-image-preview\n [src]=\"getOriginalUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n } @else if(getPreviewUrl(file)) {\n <core-image-preview\n [src]=\"getPreviewUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n }\n </div>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n </div>\n } @else {\n <a class=\"c-attachments__content\">\n @if(isServerFile(file)) {\n <span class=\"icon-file\"></span>\n {{ file.filename }}\n } @else {\n <span [class]=\"getFileIcon(file)\"></span>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n }\n </a>\n }\n\n <div class=\"c-attachments__actions u-flex u-flex--wrap\">\n <button \n type=\"button\" \n class=\"c-link context:error\" \n (click)=\"removeFile(i)\"\n [title]=\"'files.remove' | translate\"\n >\n <span class=\"icon-delete\"></span>\n {{ 'files.remove' | translate }}\n </button>\n\n @if(fieldConfig().customActions) {\n @for(action of fieldConfig().customActions; track action.id) {\n <button \n type=\"button\"\n class=\"c-link c-link--underlined\" \n (click)=\"action.action(file)\"\n [title]=\"action.label | translate\"\n >\n {{ action.label | translate }}\n <span [ngClass]=\"action.icon | coreIconCompat\"></span>\n </button>\n }\n }\n </div>\n </div>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n <core-field-errors [errors]=\"errors()\" />\n @if(displayErrorMessage()) {\n <span class=\"c-entry-error\">{{ displayErrorMessage()!.key | translate:displayErrorMessage()!.params }}</span>\n }\n</label>\n" }]
1430
1477
  }] });
1431
1478
 
1432
1479
  var NumberFieldConfigType;
@@ -10783,11 +10830,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImpor
10783
10830
  // Este archivo es generado automáticamente por scripts/update-version.js
10784
10831
  // No edites manualmente este archivo
10785
10832
  const VERSION = {
10786
- full: '2.11.42',
10833
+ full: '2.12.0',
10787
10834
  major: 2,
10788
- minor: 11,
10789
- patch: 42,
10790
- timestamp: '2025-09-02T17:49:00.511Z',
10835
+ minor: 12,
10836
+ patch: 0,
10837
+ timestamp: '2025-09-02T20:07:18.238Z',
10791
10838
  buildDate: '2/9/2025'
10792
10839
  };
10793
10840
 
@@ -12685,6 +12732,171 @@ var SliderNavigationType;
12685
12732
  SliderNavigationType["COUNTER"] = "counter";
12686
12733
  })(SliderNavigationType || (SliderNavigationType = {}));
12687
12734
 
12735
+ class CarouselComponent {
12736
+ images = input([]);
12737
+ config = input({});
12738
+ carouselHolder = viewChild('carouselHolder');
12739
+ carouselViewport = viewChild('carouselViewport');
12740
+ currentIndex = signal(0);
12741
+ timer = signal(null);
12742
+ autoplay = computed(() => this.config().autoplay ?? false);
12743
+ interval = computed(() => this.config().interval ?? 3000);
12744
+ perView = computed(() => Math.max(1, this.config().perView ?? 1));
12745
+ step = computed(() => Math.max(1, this.config().step ?? this.perView()));
12746
+ showDots = computed(() => this.config().showDots ?? true);
12747
+ showArrows = computed(() => this.config().showArrows ?? true);
12748
+ arrowsOutside = computed(() => this.config().arrowsOutside ?? false);
12749
+ itemsGap = computed(() => this.config().itemsGap ?? '0px');
12750
+ controlsOffset = computed(() => this.config().controlsOffset ?? '2rem');
12751
+ controlsSize = computed(() => this.config().controlsSize ?? '2.5rem');
12752
+ ariaLabel = computed(() => this.config().ariaLabel ?? 'Galería de imágenes');
12753
+ maxIndex = computed(() => Math.max(0, this.images().length - this.perView()));
12754
+ pagesLength = computed(() => Math.max(1, Math.ceil(this.images().length / this.perView())));
12755
+ dots = computed(() => Array.from({ length: this.pagesLength() }, (_, i) => i));
12756
+ currentPage = computed(() => Math.min(this.pagesLength() - 1, Math.floor(this.currentIndex() / this.perView())));
12757
+ carouselClasses = computed(() => {
12758
+ const classes = ['c-img-carousel', 'js-img-carousel'];
12759
+ if (this.arrowsOutside()) {
12760
+ classes.push('c-img-carousel--arrows-out');
12761
+ }
12762
+ return classes;
12763
+ });
12764
+ constructor() {
12765
+ effect(() => {
12766
+ const viewport = this.carouselViewport();
12767
+ if (viewport) {
12768
+ const element = viewport.nativeElement;
12769
+ element.style.setProperty('--per-view', this.perView().toString());
12770
+ element.style.setProperty('--items-gap', this.itemsGap());
12771
+ element.style.setProperty('--controls-offset', this.controlsOffset());
12772
+ element.style.setProperty('--controls-size', this.controlsSize());
12773
+ }
12774
+ });
12775
+ effect(() => {
12776
+ const shouldAutoplay = this.autoplay() && this.images().length > 1;
12777
+ const currentTimer = this.timer();
12778
+ if (shouldAutoplay && !currentTimer) {
12779
+ this.startAutoplay();
12780
+ }
12781
+ else if (!shouldAutoplay && currentTimer) {
12782
+ this.stopAutoplay();
12783
+ }
12784
+ });
12785
+ effect(() => {
12786
+ const holder = this.carouselHolder();
12787
+ const index = this.currentIndex();
12788
+ if (holder) {
12789
+ const element = holder.nativeElement;
12790
+ const percentage = -(index * (100 / this.perView()));
12791
+ element.style.transform = `translateX(${percentage}%)`;
12792
+ }
12793
+ });
12794
+ }
12795
+ ngAfterViewInit() {
12796
+ this.goToSlide(0, false);
12797
+ }
12798
+ ngOnDestroy() {
12799
+ this.stopAutoplay();
12800
+ }
12801
+ goToSlide(index, animate = true, fromAutoplay = false) {
12802
+ if (index < 0) {
12803
+ index = this.maxIndex();
12804
+ }
12805
+ else if (index > this.maxIndex()) {
12806
+ index = 0;
12807
+ }
12808
+ const holder = this.carouselHolder();
12809
+ if (holder) {
12810
+ holder.nativeElement.style.transition = animate ? 'transform 350ms ease' : 'none';
12811
+ }
12812
+ this.currentIndex.set(index);
12813
+ if (!fromAutoplay && this.autoplay()) {
12814
+ this.restartAutoplay();
12815
+ }
12816
+ }
12817
+ goToPage(pageIndex) {
12818
+ const targetSlide = Math.min(this.maxIndex(), Math.max(0, pageIndex * this.perView()));
12819
+ this.goToSlide(targetSlide, true, false);
12820
+ }
12821
+ goToNextSlide(fromAutoplay = false) {
12822
+ this.goToSlide(this.currentIndex() + this.step(), true, fromAutoplay);
12823
+ }
12824
+ goToPrevSlide() {
12825
+ this.goToSlide(this.currentIndex() - this.step(), true, false);
12826
+ }
12827
+ isDotActive(dotIndex) {
12828
+ return dotIndex === this.currentPage();
12829
+ }
12830
+ startAutoplay() {
12831
+ const currentTimer = this.timer();
12832
+ if (currentTimer) {
12833
+ clearInterval(currentTimer);
12834
+ }
12835
+ const intervalId = setInterval(() => {
12836
+ this.goToNextSlide(true);
12837
+ }, this.interval());
12838
+ this.timer.set(intervalId);
12839
+ }
12840
+ stopAutoplay() {
12841
+ const timerId = this.timer();
12842
+ if (timerId) {
12843
+ clearInterval(timerId);
12844
+ this.timer.set(null);
12845
+ }
12846
+ }
12847
+ restartAutoplay() {
12848
+ if (this.autoplay()) {
12849
+ this.startAutoplay();
12850
+ }
12851
+ }
12852
+ onResize() {
12853
+ this.goToSlide(this.currentIndex(), false);
12854
+ }
12855
+ onKeyDown(event) {
12856
+ if (event.key === 'ArrowLeft') {
12857
+ this.goToPrevSlide();
12858
+ }
12859
+ else if (event.key === 'ArrowRight') {
12860
+ this.goToNextSlide();
12861
+ }
12862
+ }
12863
+ getSlideStyle(index) {
12864
+ return {
12865
+ 'flex': `0 0 ${100 / this.perView()}%`
12866
+ };
12867
+ }
12868
+ getImages() {
12869
+ return this.images();
12870
+ }
12871
+ getCarouselClasses() {
12872
+ return this.carouselClasses();
12873
+ }
12874
+ getShowArrows() {
12875
+ return this.showArrows() && this.images().length > this.perView();
12876
+ }
12877
+ getShowDots() {
12878
+ return this.showDots() && this.pagesLength() > 1;
12879
+ }
12880
+ getDots() {
12881
+ return this.dots();
12882
+ }
12883
+ getAriaLabel() {
12884
+ return this.ariaLabel();
12885
+ }
12886
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12887
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.0.6", type: CarouselComponent, isStandalone: true, selector: "core-carousel", inputs: { images: { classPropertyName: "images", publicName: "images", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "onResize()", "keydown": "onKeyDown($event)" } }, viewQueries: [{ propertyName: "carouselHolder", first: true, predicate: ["carouselHolder"], descendants: true, isSignal: true }, { propertyName: "carouselViewport", first: true, predicate: ["carouselViewport"], descendants: true, isSignal: true }], ngImport: i0, template: "<div \n [ngClass]=\"getCarouselClasses()\"\n [attr.aria-label]=\"getAriaLabel()\"\n tabindex=\"0\"\n #carouselViewport>\n \n <div class=\"c-img-carousel__viewport\">\n <div class=\"c-img-carousel__holder js-img-carousel-holder\" #carouselHolder>\n <div \n *ngFor=\"let image of getImages(); let i = index\"\n class=\"c-img-carousel__slide js-img-carousel-slide\"\n [ngStyle]=\"getSlideStyle(i)\">\n <div class=\"c-img-carousel__slide-inner\">\n <img \n [src]=\"image.url\" \n [alt]=\"image.alt || 'Imagen ' + (i + 1)\"\n loading=\"lazy\">\n </div>\n </div>\n </div>\n \n <!-- Botones de navegaci\u00F3n -->\n <button \n *ngIf=\"getShowArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--prev icon-arrow-left\"\n type=\"button\"\n (click)=\"goToPrevSlide()\"\n [attr.aria-label]=\"'Anterior'\"\n data-control=\"prevBtn\">\n </button>\n \n <button \n *ngIf=\"getShowArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--next icon-arrow-right\"\n type=\"button\"\n (click)=\"goToNextSlide()\"\n [attr.aria-label]=\"'Siguiente'\"\n data-control=\"nextBtn\">\n </button>\n \n <!-- Indicadores de navegaci\u00F3n (dots) -->\n <div \n *ngIf=\"getShowDots()\"\n class=\"c-img-carousel__dots js-img-carousel-nav\"\n aria-label=\"Navegaci\u00F3n\">\n <button\n *ngFor=\"let dot of getDots(); let i = index\"\n class=\"c-img-carousel__dot js-img-carousel-dot\"\n type=\"button\"\n (click)=\"goToPage(i)\"\n [attr.aria-label]=\"'Ir a p\u00E1gina ' + (i + 1)\"\n [attr.aria-current]=\"isDotActive(i) ? 'true' : 'false'\">\n </button>\n </div>\n </div>\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
12888
+ }
12889
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: CarouselComponent, decorators: [{
12890
+ type: Component,
12891
+ args: [{ selector: 'core-carousel', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div \n [ngClass]=\"getCarouselClasses()\"\n [attr.aria-label]=\"getAriaLabel()\"\n tabindex=\"0\"\n #carouselViewport>\n \n <div class=\"c-img-carousel__viewport\">\n <div class=\"c-img-carousel__holder js-img-carousel-holder\" #carouselHolder>\n <div \n *ngFor=\"let image of getImages(); let i = index\"\n class=\"c-img-carousel__slide js-img-carousel-slide\"\n [ngStyle]=\"getSlideStyle(i)\">\n <div class=\"c-img-carousel__slide-inner\">\n <img \n [src]=\"image.url\" \n [alt]=\"image.alt || 'Imagen ' + (i + 1)\"\n loading=\"lazy\">\n </div>\n </div>\n </div>\n \n <!-- Botones de navegaci\u00F3n -->\n <button \n *ngIf=\"getShowArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--prev icon-arrow-left\"\n type=\"button\"\n (click)=\"goToPrevSlide()\"\n [attr.aria-label]=\"'Anterior'\"\n data-control=\"prevBtn\">\n </button>\n \n <button \n *ngIf=\"getShowArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--next icon-arrow-right\"\n type=\"button\"\n (click)=\"goToNextSlide()\"\n [attr.aria-label]=\"'Siguiente'\"\n data-control=\"nextBtn\">\n </button>\n \n <!-- Indicadores de navegaci\u00F3n (dots) -->\n <div \n *ngIf=\"getShowDots()\"\n class=\"c-img-carousel__dots js-img-carousel-nav\"\n aria-label=\"Navegaci\u00F3n\">\n <button\n *ngFor=\"let dot of getDots(); let i = index\"\n class=\"c-img-carousel__dot js-img-carousel-dot\"\n type=\"button\"\n (click)=\"goToPage(i)\"\n [attr.aria-label]=\"'Ir a p\u00E1gina ' + (i + 1)\"\n [attr.aria-current]=\"isDotActive(i) ? 'true' : 'false'\">\n </button>\n </div>\n </div>\n</div>" }]
12892
+ }], ctorParameters: () => [], propDecorators: { onResize: [{
12893
+ type: HostListener,
12894
+ args: ['window:resize']
12895
+ }], onKeyDown: [{
12896
+ type: HostListener,
12897
+ args: ['keydown', ['$event']]
12898
+ }] } });
12899
+
12688
12900
  class CacheBustingInterceptor {
12689
12901
  intercept(req, next) {
12690
12902
  if (req.url.includes('/assets/i18n/') && req.url.endsWith('.json')) {
@@ -13012,5 +13224,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImpor
13012
13224
  * Generated bundle index. Do not edit.
13013
13225
  */
13014
13226
 
13015
- export { ActiveFiltersComponent, AlertComponent, AlertContainerComponent, AlertService, AlertType, ApiConfigurationProvider, BaseFieldComponent, ButtonContext, ButtonSize, ButtonType, CacheBustingInterceptor, CardComponent, CheckboxFieldComponent, ConfigurationModel, ConfirmationDialogComponent, ConfirmationDialogService, CoreHostDirective, CoreUiHttpLoaderFactory, CoreUiTranslateLoader, CoreUiTranslateService, DataListComponent, DataListItemComponent, DateFieldComponent, DateUtility, DatetimeFieldComponent, DialogActions, DocumentAction, DocumentDisplayMode, DropdownComponent, DropdownDirection, DropdownService, DynamicFieldDirective, FieldErrorsComponent, FieldType, FileFieldComponent, FileModel, FileTemplateModel, FileTemplateType, FileType, FileTypeModel, FileUploadService, FilterModalComponent, FilterService, FilterType, GenericButtonComponent, GenericDocumentationComponent, GenericModalComponent, GenericPaginationComponent, GenericRatingComponent, GenericSidebarComponent, GenericSliderComponent, GenericStepsComponent, GenericTableComponent, GenericTabsComponent, GenericTimelineComponent, GlobalApiConfigService, HeaderComponent, HeaderConfigurationService, HeaderElementType, HeaderService, HttpLoaderFactory, ImageModalComponent, ImageModalService, ImagePreviewComponent, LayoutAuth, LayoutBreakpoint, LayoutComponent, LayoutService, LayoutStateService, LayoutType, LoaderComponent, LoaderService, MainNavComponent, MainNavService, ModalMode, ModelApiService, MultiEntryFieldComponent, MultiEntryOutputFormat, NumberFieldComponent, NumberFieldConfigType, NumberFieldType, NumberRange, PERMISSION_ACTIONS_PROVIDER, PERMISSION_PROVIDER, PERMISSION_RESOURCES_PROVIDER, PaginationService, PasswordFieldComponent, PermissionEnumsService, PermissionModel, PermissionService, PermissionWrapperService, PermissionsActions, PermissionsInterceptor, PermissionsResources, ProgressBarComponent, ProgressBarSize, RatingService, RatingSize, RatingType, ResetPasswordModel, RoleModel, SelectFieldComponent, ServerSelectFieldComponent, ServerSelectService, SidebarCustomModalComponent, SidebarCustomModalService, SidebarHeight, SidebarMobileModalService, SidebarMobileType, SidebarPosition, SidebarService, SidebarState, SidebarTemplateRegistryService, SidebarVisibility, SidebarWidth, SliderActionType, SliderDirection, SliderNavigationType, SliderService, SliderTransition, SmartFieldComponent, StepSize, StepStatus, StepType, StepsService, SwitchFieldComponent, TableAction, TableActionService, TableDataService, TextAreaFieldComponent, TextFieldComponent, TimeFieldComponent, TimeInterval, TimelineService, TimelineStatus, TimelineType, TranslationMergeService, UsersModel, VERSION, equalToValidator, isSameDate, provideCoreUiTranslateLoader, providePermissionActions, providePermissionEnums, providePermissionResources, providePermissionService, providePermissionServiceFactory, provideTranslateLoader };
13227
+ export { ActiveFiltersComponent, AlertComponent, AlertContainerComponent, AlertService, AlertType, ApiConfigurationProvider, BaseFieldComponent, ButtonContext, ButtonSize, ButtonType, CacheBustingInterceptor, CardComponent, CarouselComponent, CheckboxFieldComponent, ConfigurationModel, ConfirmationDialogComponent, ConfirmationDialogService, CoreHostDirective, CoreUiHttpLoaderFactory, CoreUiTranslateLoader, CoreUiTranslateService, DataListComponent, DataListItemComponent, DateFieldComponent, DateUtility, DatetimeFieldComponent, DialogActions, DocumentAction, DocumentDisplayMode, DropdownComponent, DropdownDirection, DropdownService, DynamicFieldDirective, FieldErrorsComponent, FieldType, FileFieldComponent, FileModel, FileTemplateModel, FileTemplateType, FileType, FileTypeModel, FileUploadService, FilterModalComponent, FilterService, FilterType, GenericButtonComponent, GenericDocumentationComponent, GenericModalComponent, GenericPaginationComponent, GenericRatingComponent, GenericSidebarComponent, GenericSliderComponent, GenericStepsComponent, GenericTableComponent, GenericTabsComponent, GenericTimelineComponent, GlobalApiConfigService, HeaderComponent, HeaderConfigurationService, HeaderElementType, HeaderService, HttpLoaderFactory, ImageModalComponent, ImageModalService, ImagePreviewComponent, LayoutAuth, LayoutBreakpoint, LayoutComponent, LayoutService, LayoutStateService, LayoutType, LoaderComponent, LoaderService, MainNavComponent, MainNavService, ModalMode, ModelApiService, MultiEntryFieldComponent, MultiEntryOutputFormat, NumberFieldComponent, NumberFieldConfigType, NumberFieldType, NumberRange, PERMISSION_ACTIONS_PROVIDER, PERMISSION_PROVIDER, PERMISSION_RESOURCES_PROVIDER, PaginationService, PasswordFieldComponent, PermissionEnumsService, PermissionModel, PermissionService, PermissionWrapperService, PermissionsActions, PermissionsInterceptor, PermissionsResources, ProgressBarComponent, ProgressBarSize, RatingService, RatingSize, RatingType, ResetPasswordModel, RoleModel, SelectFieldComponent, ServerSelectFieldComponent, ServerSelectService, SidebarCustomModalComponent, SidebarCustomModalService, SidebarHeight, SidebarMobileModalService, SidebarMobileType, SidebarPosition, SidebarService, SidebarState, SidebarTemplateRegistryService, SidebarVisibility, SidebarWidth, SliderActionType, SliderDirection, SliderNavigationType, SliderService, SliderTransition, SmartFieldComponent, StepSize, StepStatus, StepType, StepsService, SwitchFieldComponent, TableAction, TableActionService, TableDataService, TextAreaFieldComponent, TextFieldComponent, TimeFieldComponent, TimeInterval, TimelineService, TimelineStatus, TimelineType, TranslationMergeService, UsersModel, VERSION, equalToValidator, isSameDate, provideCoreUiTranslateLoader, providePermissionActions, providePermissionEnums, providePermissionResources, providePermissionService, providePermissionServiceFactory, provideTranslateLoader };
13016
13228
  //# sourceMappingURL=solcre-org-core-ui.mjs.map