@solcre-org/core-ui 2.12.7 → 2.12.8

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.
@@ -104,6 +104,12 @@
104
104
  "noResults": "No se encontraron resultados",
105
105
  "typeToSearch": "Escribe para buscar"
106
106
  }
107
+ },
108
+ "unsavedChanges": {
109
+ "title": "Cambios sin guardar",
110
+ "message": "Tienes cambios sin guardar. ¿Estás seguro de que quieres cerrar el modal?",
111
+ "confirm": "Cerrar sin guardar",
112
+ "cancel": "Continuar editando"
107
113
  }
108
114
  },
109
115
  "dashboard": {
@@ -4005,10 +4005,107 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImpor
4005
4005
  args: ['customTabTemplate']
4006
4006
  }] } });
4007
4007
 
4008
+ var DialogActions;
4009
+ (function (DialogActions) {
4010
+ DialogActions["CONFIRM"] = "dialog.confirm";
4011
+ DialogActions["CANCEL"] = "dialog.cancel";
4012
+ })(DialogActions || (DialogActions = {}));
4013
+
4014
+ class ConfirmationDialogService {
4015
+ isOpen = signal(false);
4016
+ isOpen$ = this.isOpen.asReadonly();
4017
+ config = signal({ title: '' });
4018
+ config$ = this.config.asReadonly();
4019
+ responseSubject = new Subject();
4020
+ response$ = this.responseSubject.asObservable();
4021
+ openDelete(title, message, validationValue) {
4022
+ return this.open({
4023
+ title,
4024
+ message,
4025
+ type: 'delete',
4026
+ icon: 'icon-delete',
4027
+ confirmButtonText: 'dialog.delete',
4028
+ cancelButtonText: 'dialog.cancel',
4029
+ confirmButtonClass: 'c-btn context:error',
4030
+ inputConfig: validationValue ? {
4031
+ label: 'dialog.confirmName',
4032
+ placeholder: 'dialog.enterName',
4033
+ validationValue
4034
+ } : undefined,
4035
+ showCloseButton: true
4036
+ });
4037
+ }
4038
+ openConfirm(config) {
4039
+ return this.open({
4040
+ ...config,
4041
+ type: 'default',
4042
+ size: config.size || 'default',
4043
+ showCloseButton: config.showCloseButton ?? true
4044
+ });
4045
+ }
4046
+ open(config) {
4047
+ this.config.set({
4048
+ ...config,
4049
+ confirmButtonText: config.confirmButtonText ?? DialogActions.CONFIRM,
4050
+ cancelButtonText: config.cancelButtonText ?? DialogActions.CANCEL,
4051
+ messageParams: config.messageParams ?? {},
4052
+ showCloseButton: config.showCloseButton ?? true
4053
+ });
4054
+ this.isOpen.set(true);
4055
+ return this.response$;
4056
+ }
4057
+ confirm(value) {
4058
+ this.responseSubject.next(value);
4059
+ this.close();
4060
+ }
4061
+ cancel() {
4062
+ this.responseSubject.next(undefined);
4063
+ this.close();
4064
+ }
4065
+ close() {
4066
+ this.isOpen.set(false);
4067
+ this.config.set({ title: '' });
4068
+ const newSubject = new Subject();
4069
+ const oldSubject = this.responseSubject;
4070
+ this.responseSubject = newSubject;
4071
+ this.response$ = this.responseSubject.asObservable();
4072
+ oldSubject.complete();
4073
+ }
4074
+ openWithOptionalInput(title, message, inputLabel, inputPlaceholder) {
4075
+ return this.open({
4076
+ title,
4077
+ message,
4078
+ type: 'default',
4079
+ confirmButtonText: 'dialog.confirm',
4080
+ cancelButtonText: 'dialog.cancel',
4081
+ inputConfig: inputLabel ? {
4082
+ label: inputLabel,
4083
+ placeholder: inputPlaceholder || '',
4084
+ } : undefined,
4085
+ showCloseButton: true
4086
+ });
4087
+ }
4088
+ updateConfig(config) {
4089
+ this.config.update(currentConfig => ({
4090
+ ...currentConfig,
4091
+ ...config
4092
+ }));
4093
+ }
4094
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: ConfirmationDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4095
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: ConfirmationDialogService, providedIn: 'root' });
4096
+ }
4097
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: ConfirmationDialogService, decorators: [{
4098
+ type: Injectable,
4099
+ args: [{
4100
+ providedIn: 'root',
4101
+ }]
4102
+ }] });
4103
+
4008
4104
  class GenericModalComponent {
4009
4105
  elementRef = inject(ElementRef);
4010
4106
  formBuilder = inject(FormBuilder);
4011
4107
  domSanitizer = inject(DomSanitizer);
4108
+ confirmationDialogService = inject(ConfirmationDialogService);
4012
4109
  ModalMode = ModalMode;
4013
4110
  FieldType = FieldType;
4014
4111
  isClosing = signal(false);
@@ -4034,6 +4131,8 @@ class GenericModalComponent {
4034
4131
  isInitialized = signal(false);
4035
4132
  form = signal(this.formBuilder.group({}));
4036
4133
  activeTabId = signal('');
4134
+ originalData = signal(null);
4135
+ hasUnsavedChanges = signal(false);
4037
4136
  hasTabs = computed(() => this.tabs().length > 0);
4038
4137
  allFields = computed(() => {
4039
4138
  if (this.hasTabs()) {
@@ -4141,6 +4240,8 @@ class GenericModalComponent {
4141
4240
  this.internalErrors.set([]);
4142
4241
  this.fieldErrors.set({});
4143
4242
  this.isInitialized.set(false);
4243
+ this.originalData.set(null);
4244
+ this.hasUnsavedChanges.set(false);
4144
4245
  return;
4145
4246
  }
4146
4247
  if (!this.isInitialized()) {
@@ -4149,6 +4250,14 @@ class GenericModalComponent {
4149
4250
  this.isInitialized.set(true);
4150
4251
  }
4151
4252
  });
4253
+ effect(() => {
4254
+ const editedData = this.editedData();
4255
+ const originalData = this.originalData();
4256
+ if (editedData && originalData && this.mode() !== ModalMode.VIEW) {
4257
+ const hasChanges = this.detectFormChanges(editedData, originalData);
4258
+ this.hasUnsavedChanges.set(hasChanges);
4259
+ }
4260
+ });
4152
4261
  }
4153
4262
  initializeData() {
4154
4263
  const data = this.data();
@@ -4222,6 +4331,11 @@ class GenericModalComponent {
4222
4331
  this.internalErrors.set([]);
4223
4332
  }, 0);
4224
4333
  this.editedData.set(newInstance);
4334
+ if (this.mode() !== ModalMode.VIEW) {
4335
+ const originalCopy = Object.create(Object.getPrototypeOf(newInstance));
4336
+ Object.assign(originalCopy, newInstance);
4337
+ this.originalData.set(originalCopy);
4338
+ }
4225
4339
  setTimeout(() => {
4226
4340
  this.modalData.emit(newInstance);
4227
4341
  }, 1);
@@ -4418,6 +4532,7 @@ class GenericModalComponent {
4418
4532
  if (data) {
4419
4533
  const filteredData = this.filterPayloadData(data);
4420
4534
  this.save.emit(filteredData);
4535
+ this.hasUnsavedChanges.set(false);
4421
4536
  }
4422
4537
  }
4423
4538
  filterPayloadData(data) {
@@ -4476,6 +4591,26 @@ class GenericModalComponent {
4476
4591
  });
4477
4592
  }
4478
4593
  onClose() {
4594
+ if (this.hasUnsavedChanges() && this.mode() !== ModalMode.VIEW) {
4595
+ this.confirmationDialogService.openConfirm({
4596
+ title: 'modal.unsavedChanges.title',
4597
+ message: 'modal.unsavedChanges.message',
4598
+ confirmButtonText: 'modal.unsavedChanges.confirm',
4599
+ cancelButtonText: 'modal.unsavedChanges.cancel',
4600
+ type: 'default',
4601
+ icon: 'icon-warning',
4602
+ showCloseButton: true
4603
+ }).subscribe((confirmed) => {
4604
+ if (confirmed) {
4605
+ this.forceClose();
4606
+ }
4607
+ });
4608
+ }
4609
+ else {
4610
+ this.forceClose();
4611
+ }
4612
+ }
4613
+ forceClose() {
4479
4614
  this.isClosing.set(true);
4480
4615
  const overlay = this.elementRef.nativeElement.querySelector('.c-modal__overlay');
4481
4616
  const onAnimationEnd = (e) => {
@@ -4485,7 +4620,15 @@ class GenericModalComponent {
4485
4620
  overlay.removeEventListener('animationend', onAnimationEnd);
4486
4621
  }
4487
4622
  };
4488
- overlay.addEventListener('animationend', onAnimationEnd);
4623
+ if (overlay) {
4624
+ overlay.addEventListener('animationend', onAnimationEnd);
4625
+ }
4626
+ else {
4627
+ setTimeout(() => {
4628
+ this.isClosing.set(false);
4629
+ this.close.emit();
4630
+ }, 300);
4631
+ }
4489
4632
  }
4490
4633
  getFieldConfig(field) {
4491
4634
  const modeConfig = field.modes?.[this.mode()];
@@ -4566,6 +4709,24 @@ class GenericModalComponent {
4566
4709
  return fieldErrors.length > 0;
4567
4710
  });
4568
4711
  }
4712
+ detectFormChanges(editedData, originalData) {
4713
+ const relevantFields = this.allFields().filter(field => {
4714
+ const modeConfig = field.modes?.[this.mode()];
4715
+ return modeConfig?.visible !== false;
4716
+ });
4717
+ return relevantFields.some(field => {
4718
+ const fieldKey = field.key;
4719
+ const editedValue = editedData[fieldKey];
4720
+ const originalValue = originalData[fieldKey];
4721
+ if (Array.isArray(editedValue) && Array.isArray(originalValue)) {
4722
+ return JSON.stringify(editedValue) !== JSON.stringify(originalValue);
4723
+ }
4724
+ if (editedValue instanceof Date && originalValue instanceof Date) {
4725
+ return editedValue.getTime() !== originalValue.getTime();
4726
+ }
4727
+ return editedValue !== originalValue;
4728
+ });
4729
+ }
4569
4730
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: GenericModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4570
4731
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: GenericModalComponent, isStandalone: true, selector: "core-generic-modal", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, isMultiple: { classPropertyName: "isMultiple", publicName: "isMultiple", isSignal: true, isRequired: false, transformFunction: null }, customTemplate: { classPropertyName: "customTemplate", publicName: "customTemplate", isSignal: true, isRequired: false, transformFunction: null }, customViewTemplate: { classPropertyName: "customViewTemplate", publicName: "customViewTemplate", isSignal: true, isRequired: false, transformFunction: null }, buttonConfig: { classPropertyName: "buttonConfig", publicName: "buttonConfig", isSignal: true, isRequired: false, transformFunction: null }, modelFactory: { classPropertyName: "modelFactory", publicName: "modelFactory", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, validators: { classPropertyName: "validators", publicName: "validators", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { save: "save", close: "close", modalData: "modalData" }, hostDirectives: [{ directive: CoreHostDirective }], ngImport: i0, template: "<div class=\"c-modal\" [class.is-visible]=\"isOpen()\" [class.is-closing]=\"isClosing()\">\n <div class=\"c-modal__overlay\" (click)=\"onClose()\"></div>\n <div class=\"c-modal__holder\">\n <div class=\"c-modal__header\">\n <p class=\"c-modal__title\">\n {{ title() | translate }}\n </p>\n <core-generic-button\n [config]=\"closeButtonConfig()\"\n (buttonClick)=\"onClose()\">\n </core-generic-button>\n </div>\n <div class=\"c-modal__body\">\n @if (editedData()) {\n @if (hasTabs()) {\n <nav core-generic-tabs\n [config]=\"genericTabsConfig()\"\n [activeTabId]=\"activeTabId()\"\n [hasTabErrors]=\"hasTabErrorsFunction\"\n (tabChange)=\"onGenericTabChange($event)\">\n </nav>\n }\n\n @if (mode() === ModalMode.VIEW) {\n <core-data-list\n [items]=\"dataListItems()\"\n [customTemplate]=\"customViewTemplate()\"\n [emptyMessage]=\"'modal.noData'\"\n ></core-data-list>\n } @else {\n <div class=\"c-entry-group\">\n @if (customTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"customTemplate()\"\n [ngTemplateOutletContext]=\"{\n $implicit: editedData(),\n mode: mode(),\n updateField: onFieldValueChange.bind(this),\n save: onSave.bind(this),\n close: onClose.bind(this),\n activeTabId: activeTabId(),\n onTabChange: onTabChange.bind(this),\n getFieldErrors: getFieldErrors.bind(this),\n validateAllFields: validateAllFields.bind(this),\n hasErrors: hasErrors()\n }\"\n ></ng-container>\n } @else {\n @for (field of (hasTabs() ? activeTabFields() : fields()); track field.key) {\n @if (getFieldConfig(field).visible) {\n <div\n coreDynamicField\n [field]=\"getFieldConfig(field)\"\n [value]=\"editedData()![field.key]\"\n [mode]=\"mode()\"\n [errors]=\"getFieldErrors(field.key)\"\n [rowData]=\"editedData()\"\n [formValue]=\"editedData()\"\n (valueChange)=\"onFieldValueChange(field.key, $event)\"\n (onBlurEvent)=\"validateAllFields()\"\n (selectionChange)=\"onSelectionChange($event)\"\n ></div>\n }\n }\n }\n </div> <!-- .c-entry-group -->\n }\n } @else {\n <p>{{ 'modal.noData' | translate }}</p>\n }\n </div>\n <div class=\"c-modal__bottom\">\n @if (buttonConfig().length > 0) {\n @for (button of buttonConfig(); track $index) {\n <core-generic-button\n [config]=\"getCustomButtonConfig(button)\"\n [data]=\"data()\"\n (buttonClick)=\"onCustomButtonClick(button)\">\n </core-generic-button>\n }\n } @else {\n <core-generic-button\n [config]=\"defaultCancelButtonConfig()\"\n (buttonClick)=\"onClose()\">\n </core-generic-button>\n @if (mode() !== ModalMode.VIEW) {\n <core-generic-button\n [config]=\"defaultSaveButtonConfig()\"\n (buttonClick)=\"onSave()\">\n </core-generic-button>\n }\n }\n </div>\n </div>\n</div>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "directive", type: DynamicFieldDirective, selector: "[coreDynamicField]", inputs: ["field", "value", "mode", "errors", "rowData", "formValue"], outputs: ["valueChange", "onBlurEvent", "onEnterEvent", "selectionChange"] }, { kind: "component", type: GenericTabsComponent, selector: "nav[core-generic-tabs]", inputs: ["config", "activeTabId", "hasTabErrors"], outputs: ["tabClick", "tabChange"] }, { kind: "component", type: GenericButtonComponent, selector: "core-generic-button", inputs: ["config", "data"], outputs: ["buttonClick"] }, { kind: "component", type: DataListComponent, selector: "core-data-list", inputs: ["items", "fields", "data", "customTemplate", "showEmptyMessage", "emptyMessage", "cssClasses"] }] });
4571
4732
  }
@@ -6217,102 +6378,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImpor
6217
6378
  type: Input
6218
6379
  }] } });
6219
6380
 
6220
- var DialogActions;
6221
- (function (DialogActions) {
6222
- DialogActions["CONFIRM"] = "dialog.confirm";
6223
- DialogActions["CANCEL"] = "dialog.cancel";
6224
- })(DialogActions || (DialogActions = {}));
6225
-
6226
- class ConfirmationDialogService {
6227
- isOpen = signal(false);
6228
- isOpen$ = this.isOpen.asReadonly();
6229
- config = signal({ title: '' });
6230
- config$ = this.config.asReadonly();
6231
- responseSubject = new Subject();
6232
- response$ = this.responseSubject.asObservable();
6233
- openDelete(title, message, validationValue) {
6234
- return this.open({
6235
- title,
6236
- message,
6237
- type: 'delete',
6238
- icon: 'icon-delete',
6239
- confirmButtonText: 'dialog.delete',
6240
- cancelButtonText: 'dialog.cancel',
6241
- confirmButtonClass: 'c-btn context:error',
6242
- inputConfig: validationValue ? {
6243
- label: 'dialog.confirmName',
6244
- placeholder: 'dialog.enterName',
6245
- validationValue
6246
- } : undefined,
6247
- showCloseButton: true
6248
- });
6249
- }
6250
- openConfirm(config) {
6251
- return this.open({
6252
- ...config,
6253
- type: 'default',
6254
- size: config.size || 'default',
6255
- showCloseButton: config.showCloseButton ?? true
6256
- });
6257
- }
6258
- open(config) {
6259
- this.config.set({
6260
- ...config,
6261
- confirmButtonText: config.confirmButtonText ?? DialogActions.CONFIRM,
6262
- cancelButtonText: config.cancelButtonText ?? DialogActions.CANCEL,
6263
- messageParams: config.messageParams ?? {},
6264
- showCloseButton: config.showCloseButton ?? true
6265
- });
6266
- this.isOpen.set(true);
6267
- return this.response$;
6268
- }
6269
- confirm(value) {
6270
- this.responseSubject.next(value);
6271
- this.close();
6272
- }
6273
- cancel() {
6274
- this.responseSubject.next(undefined);
6275
- this.close();
6276
- }
6277
- close() {
6278
- this.isOpen.set(false);
6279
- this.config.set({ title: '' });
6280
- const newSubject = new Subject();
6281
- const oldSubject = this.responseSubject;
6282
- this.responseSubject = newSubject;
6283
- this.response$ = this.responseSubject.asObservable();
6284
- oldSubject.complete();
6285
- }
6286
- openWithOptionalInput(title, message, inputLabel, inputPlaceholder) {
6287
- return this.open({
6288
- title,
6289
- message,
6290
- type: 'default',
6291
- confirmButtonText: 'dialog.confirm',
6292
- cancelButtonText: 'dialog.cancel',
6293
- inputConfig: inputLabel ? {
6294
- label: inputLabel,
6295
- placeholder: inputPlaceholder || '',
6296
- } : undefined,
6297
- showCloseButton: true
6298
- });
6299
- }
6300
- updateConfig(config) {
6301
- this.config.update(currentConfig => ({
6302
- ...currentConfig,
6303
- ...config
6304
- }));
6305
- }
6306
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: ConfirmationDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
6307
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: ConfirmationDialogService, providedIn: 'root' });
6308
- }
6309
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: ConfirmationDialogService, decorators: [{
6310
- type: Injectable,
6311
- args: [{
6312
- providedIn: 'root',
6313
- }]
6314
- }] });
6315
-
6316
6381
  class ConfirmationDialogComponent {
6317
6382
  popupElement;
6318
6383
  overlayElement;
@@ -11193,11 +11258,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImpor
11193
11258
  // Este archivo es generado automáticamente por scripts/update-version.js
11194
11259
  // No edites manualmente este archivo
11195
11260
  const VERSION = {
11196
- full: '2.12.7',
11261
+ full: '2.12.8',
11197
11262
  major: 2,
11198
11263
  minor: 12,
11199
- patch: 7,
11200
- timestamp: '2025-09-03T18:01:25.906Z',
11264
+ patch: 8,
11265
+ timestamp: '2025-09-03T18:41:20.948Z',
11201
11266
  buildDate: '3/9/2025'
11202
11267
  };
11203
11268