valtech-components 2.0.951 → 2.0.953

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.
@@ -54,7 +54,7 @@ import 'prismjs/components/prism-json';
54
54
  * Current version of valtech-components.
55
55
  * This is automatically updated during the publish process.
56
56
  */
57
- const VERSION = '2.0.951';
57
+ const VERSION = '2.0.953';
58
58
 
59
59
  // Control de estado de refresco (singleton a nivel de módulo)
60
60
  let isRefreshing = false;
@@ -18414,7 +18414,7 @@ class SearchSelectorComponent {
18414
18414
  <ion-select-option [value]="o.id">{{ o.name }}</ion-select-option>
18415
18415
  }
18416
18416
  </ion-select>
18417
- `, isInline: true, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}ion-select{border:.0625rem solid var(--ion-color-medium);border-radius:1.5rem;margin-top:.375rem;padding-top:.5rem!important;padding-bottom:.5rem!important;padding-inline-start:1rem!important;padding-inline-end:.625rem!important}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$7.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }] }); }
18417
+ `, isInline: true, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}ion-select{border:.0625rem solid var(--ion-color-medium);border-radius:1.5rem;margin-top:.375rem;padding-top:.375rem!important;padding-bottom:.375rem!important;padding-inline-start:1rem!important;padding-inline-end:.625rem!important}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$7.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }] }); }
18418
18418
  }
18419
18419
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SearchSelectorComponent, decorators: [{
18420
18420
  type: Component,
@@ -18432,7 +18432,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
18432
18432
  <ion-select-option [value]="o.id">{{ o.name }}</ion-select-option>
18433
18433
  }
18434
18434
  </ion-select>
18435
- `, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}ion-select{border:.0625rem solid var(--ion-color-medium);border-radius:1.5rem;margin-top:.375rem;padding-top:.5rem!important;padding-bottom:.5rem!important;padding-inline-start:1rem!important;padding-inline-end:.625rem!important}\n"] }]
18435
+ `, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}ion-select{border:.0625rem solid var(--ion-color-medium);border-radius:1.5rem;margin-top:.375rem;padding-top:.375rem!important;padding-bottom:.375rem!important;padding-inline-start:1rem!important;padding-inline-end:.625rem!important}\n"] }]
18436
18436
  }], propDecorators: { preset: [{
18437
18437
  type: Input
18438
18438
  }], props: [{
@@ -29666,11 +29666,11 @@ class AttachmentUploaderComponent {
29666
29666
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
29667
29667
  }
29668
29668
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AttachmentUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
29669
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AttachmentUploaderComponent, isStandalone: true, selector: "val-attachment-uploader", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { attachmentsChange: "attachmentsChange" }, ngImport: i0, template: "<div class=\"attachment-uploader\">\n <input #filePicker type=\"file\" [accept]=\"accept()\" multiple class=\"hidden-input\" (change)=\"onFilesSelected($event)\" />\n\n <div class=\"attachment-actions\">\n <ion-button fill=\"outline\" color=\"dark\" size=\"small\" [disabled]=\"isDisabled()\" (click)=\"filePicker.click()\">\n <ion-icon slot=\"start\" name=\"attach-outline\"></ion-icon>\n {{ i18n.t('attachAdd') }}\n </ion-button>\n <!-- label trick: native browser gesture forwarding \u2192 iOS/PWA no bloquea el picker -->\n <label class=\"camera-label\" [class.camera-label--disabled]=\"isDisabled()\">\n <input\n type=\"file\"\n accept=\"image/*\"\n capture=\"environment\"\n class=\"hidden-input\"\n [disabled]=\"isDisabled()\"\n (change)=\"onFilesSelected($event)\"\n />\n <ion-icon name=\"camera-outline\"></ion-icon>\n {{ i18n.t('attachCamera') }}\n </label>\n </div>\n\n @if (maxReached()) {\n <ion-note color=\"warning\" class=\"max-note\">\n {{ i18n.t('attachMaxCount', '_global', { count: maxFiles().toString() }) }}\n </ion-note>\n } @if (attachments().length > 0) {\n <div class=\"attachment-list\">\n @for (item of attachments(); track item.id) {\n <div class=\"attachment-item\" [class]=\"'status-' + item.status\">\n <ion-icon\n class=\"file-icon\"\n [name]=\"item.file.type.startsWith('image/') ? 'image-outline' : 'document-outline'\"\n ></ion-icon>\n <div class=\"file-info\">\n <span class=\"file-name\">{{ item.file.name }}</span>\n <span class=\"file-size\">{{ formatSize(item.file.size) }}</span>\n </div>\n\n @if (item.status === 'uploading') {\n <ion-spinner class=\"status-icon\" name=\"circular\"></ion-spinner>\n } @else if (item.status === 'ready') {\n <ion-icon class=\"status-icon\" name=\"checkmark-circle-outline\" color=\"success\"></ion-icon>\n } @else if (item.status === 'error') {\n <ion-icon class=\"status-icon\" name=\"close-circle-outline\" color=\"danger\"></ion-icon>\n } @if (item.status !== 'uploading') {\n <ion-button fill=\"clear\" size=\"small\" color=\"dark\" (click)=\"remove(item.id)\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n } @if (item.status === 'error' && item.error) {\n <ion-note color=\"danger\" class=\"error-note\">{{ item.error }}</ion-note>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [".hidden-input{display:none}.camera-label{display:inline-flex;align-items:center;gap:6px;padding:0 12px;height:32px;font-size:14px;font-weight:500;border-radius:4px;border:1px solid currentColor;color:var(--ion-color-dark);background:transparent;cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .15s}.camera-label ion-icon{font-size:16px;flex-shrink:0}.camera-label--disabled{opacity:.4;pointer-events:none}.attachment-uploader{display:flex;flex-direction:column;gap:8px}.attachment-actions{display:flex;gap:8px;flex-wrap:wrap}.attachment-list{display:flex;flex-direction:column;gap:4px}.attachment-item{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;background:var(--ion-background-color, #fff);border:1px solid var(--ion-color-light-shade, rgba(0, 0, 0, .08));flex-wrap:wrap}.attachment-item.status-error{border-color:var(--ion-color-danger)}.file-icon{font-size:22px;color:var(--ion-text-color);flex-shrink:0}.file-info{flex:1;min-width:0;display:flex;flex-direction:column}.file-name{font-size:13px;color:var(--ion-text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:11px;color:var(--ion-color-medium)}:host-context(.dark) .attachment-item,:host-context(.ion-palette-dark) .attachment-item,:host-context(html.ion-palette-dark) .attachment-item,:host-context(body.dark) .attachment-item,:host-context([data-theme=dark]) .attachment-item{background:#ffffff0a;border-color:#ffffff1f}:host-context(.dark) .attachment-item.status-error,:host-context(.ion-palette-dark) .attachment-item.status-error,:host-context(html.ion-palette-dark) .attachment-item.status-error,:host-context(body.dark) .attachment-item.status-error,:host-context([data-theme=dark]) .attachment-item.status-error{border-color:var(--ion-color-danger)}.status-icon{font-size:20px;flex-shrink:0}.error-note{font-size:11px;width:100%}.max-note{font-size:12px}\n"], dependencies: [{ kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }] }); }
29669
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AttachmentUploaderComponent, isStandalone: true, selector: "val-attachment-uploader", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { attachmentsChange: "attachmentsChange" }, ngImport: i0, template: "<div class=\"attachment-uploader\">\n @if (props().title) {\n <val-title class=\"attachment-title\" [props]=\"{ content: props().title, size: 'small', color: 'dark', bold: false }\" />\n }\n\n <input #filePicker type=\"file\" [accept]=\"accept()\" multiple class=\"hidden-input\" (change)=\"onFilesSelected($event)\" />\n\n <div class=\"attachment-actions\">\n <ion-button fill=\"outline\" color=\"dark\" size=\"small\" [disabled]=\"isDisabled()\" (click)=\"filePicker.click()\">\n <ion-icon slot=\"start\" name=\"attach-outline\"></ion-icon>\n {{ i18n.t('attachAdd') }}\n </ion-button>\n <!-- label trick: native browser gesture forwarding \u2192 iOS/PWA no bloquea el picker -->\n <label class=\"camera-label\" [class.camera-label--disabled]=\"isDisabled()\">\n <input\n type=\"file\"\n accept=\"image/*\"\n capture=\"environment\"\n class=\"hidden-input\"\n [disabled]=\"isDisabled()\"\n (change)=\"onFilesSelected($event)\"\n />\n <ion-icon name=\"camera-outline\"></ion-icon>\n {{ i18n.t('attachCamera') }}\n </label>\n </div>\n\n @if (maxReached()) {\n <ion-note color=\"warning\" class=\"max-note\">\n {{ i18n.t('attachMaxCount', '_global', { count: maxFiles().toString() }) }}\n </ion-note>\n } @if (attachments().length > 0) {\n <div class=\"attachment-list\">\n @for (item of attachments(); track item.id) {\n <div class=\"attachment-item\" [class]=\"'status-' + item.status\">\n <ion-icon\n class=\"file-icon\"\n [name]=\"item.file.type.startsWith('image/') ? 'image-outline' : 'document-outline'\"\n ></ion-icon>\n <div class=\"file-info\">\n <span class=\"file-name\">{{ item.file.name }}</span>\n <span class=\"file-size\">{{ formatSize(item.file.size) }}</span>\n </div>\n\n @if (item.status === 'uploading') {\n <ion-spinner class=\"status-icon\" name=\"circular\"></ion-spinner>\n } @else if (item.status === 'ready') {\n <ion-icon class=\"status-icon\" name=\"checkmark-circle-outline\" color=\"success\"></ion-icon>\n } @else if (item.status === 'error') {\n <ion-icon class=\"status-icon\" name=\"close-circle-outline\" color=\"danger\"></ion-icon>\n } @if (item.status !== 'uploading') {\n <ion-button fill=\"clear\" size=\"small\" color=\"dark\" (click)=\"remove(item.id)\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n } @if (item.status === 'error' && item.error) {\n <ion-note color=\"danger\" class=\"error-note\">{{ item.error }}</ion-note>\n }\n </div>\n }\n </div>\n } @if (props().hint) {\n <p class=\"attachment-hint\">{{ props().hint }}</p>\n }\n</div>\n", styles: [".hidden-input{display:none}.camera-label{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 14px;height:32px;box-sizing:border-box;font-size:13px;font-weight:500;letter-spacing:.01em;border-radius:4px;border:1px solid currentColor;color:var(--ion-color-dark);background:transparent;cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .15s}.camera-label ion-icon{font-size:18px;flex-shrink:0}.camera-label--disabled{opacity:.4;pointer-events:none}.attachment-uploader{display:flex;flex-direction:column;gap:8px}.attachment-title{display:block;margin-bottom:2px}.attachment-hint{margin:2px 0 0;text-align:right;font-size:12px;line-height:1.4;color:var(--ion-color-dark)}.attachment-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.attachment-actions ion-button{margin:0;height:32px}.attachment-list{display:flex;flex-direction:column;gap:4px}.attachment-item{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;background:var(--ion-background-color, #fff);border:1px solid var(--ion-color-light-shade, rgba(0, 0, 0, .08));flex-wrap:wrap}.attachment-item.status-error{border-color:var(--ion-color-danger)}.file-icon{font-size:22px;color:var(--ion-text-color);flex-shrink:0}.file-info{flex:1;min-width:0;display:flex;flex-direction:column}.file-name{font-size:13px;color:var(--ion-text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:11px;color:var(--ion-color-medium)}:host-context(.dark) .attachment-item,:host-context(.ion-palette-dark) .attachment-item,:host-context(html.ion-palette-dark) .attachment-item,:host-context(body.dark) .attachment-item,:host-context([data-theme=dark]) .attachment-item{background:#ffffff0a;border-color:#ffffff1f}:host-context(.dark) .attachment-item.status-error,:host-context(.ion-palette-dark) .attachment-item.status-error,:host-context(html.ion-palette-dark) .attachment-item.status-error,:host-context(body.dark) .attachment-item.status-error,:host-context([data-theme=dark]) .attachment-item.status-error{border-color:var(--ion-color-danger)}.status-icon{font-size:20px;flex-shrink:0}.error-note{font-size:11px;width:100%}.max-note{font-size:12px}\n"], dependencies: [{ kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
29670
29670
  }
29671
29671
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AttachmentUploaderComponent, decorators: [{
29672
29672
  type: Component,
29673
- args: [{ selector: 'val-attachment-uploader', standalone: true, imports: [IonButton, IonIcon, IonNote, IonSpinner], template: "<div class=\"attachment-uploader\">\n <input #filePicker type=\"file\" [accept]=\"accept()\" multiple class=\"hidden-input\" (change)=\"onFilesSelected($event)\" />\n\n <div class=\"attachment-actions\">\n <ion-button fill=\"outline\" color=\"dark\" size=\"small\" [disabled]=\"isDisabled()\" (click)=\"filePicker.click()\">\n <ion-icon slot=\"start\" name=\"attach-outline\"></ion-icon>\n {{ i18n.t('attachAdd') }}\n </ion-button>\n <!-- label trick: native browser gesture forwarding \u2192 iOS/PWA no bloquea el picker -->\n <label class=\"camera-label\" [class.camera-label--disabled]=\"isDisabled()\">\n <input\n type=\"file\"\n accept=\"image/*\"\n capture=\"environment\"\n class=\"hidden-input\"\n [disabled]=\"isDisabled()\"\n (change)=\"onFilesSelected($event)\"\n />\n <ion-icon name=\"camera-outline\"></ion-icon>\n {{ i18n.t('attachCamera') }}\n </label>\n </div>\n\n @if (maxReached()) {\n <ion-note color=\"warning\" class=\"max-note\">\n {{ i18n.t('attachMaxCount', '_global', { count: maxFiles().toString() }) }}\n </ion-note>\n } @if (attachments().length > 0) {\n <div class=\"attachment-list\">\n @for (item of attachments(); track item.id) {\n <div class=\"attachment-item\" [class]=\"'status-' + item.status\">\n <ion-icon\n class=\"file-icon\"\n [name]=\"item.file.type.startsWith('image/') ? 'image-outline' : 'document-outline'\"\n ></ion-icon>\n <div class=\"file-info\">\n <span class=\"file-name\">{{ item.file.name }}</span>\n <span class=\"file-size\">{{ formatSize(item.file.size) }}</span>\n </div>\n\n @if (item.status === 'uploading') {\n <ion-spinner class=\"status-icon\" name=\"circular\"></ion-spinner>\n } @else if (item.status === 'ready') {\n <ion-icon class=\"status-icon\" name=\"checkmark-circle-outline\" color=\"success\"></ion-icon>\n } @else if (item.status === 'error') {\n <ion-icon class=\"status-icon\" name=\"close-circle-outline\" color=\"danger\"></ion-icon>\n } @if (item.status !== 'uploading') {\n <ion-button fill=\"clear\" size=\"small\" color=\"dark\" (click)=\"remove(item.id)\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n } @if (item.status === 'error' && item.error) {\n <ion-note color=\"danger\" class=\"error-note\">{{ item.error }}</ion-note>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [".hidden-input{display:none}.camera-label{display:inline-flex;align-items:center;gap:6px;padding:0 12px;height:32px;font-size:14px;font-weight:500;border-radius:4px;border:1px solid currentColor;color:var(--ion-color-dark);background:transparent;cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .15s}.camera-label ion-icon{font-size:16px;flex-shrink:0}.camera-label--disabled{opacity:.4;pointer-events:none}.attachment-uploader{display:flex;flex-direction:column;gap:8px}.attachment-actions{display:flex;gap:8px;flex-wrap:wrap}.attachment-list{display:flex;flex-direction:column;gap:4px}.attachment-item{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;background:var(--ion-background-color, #fff);border:1px solid var(--ion-color-light-shade, rgba(0, 0, 0, .08));flex-wrap:wrap}.attachment-item.status-error{border-color:var(--ion-color-danger)}.file-icon{font-size:22px;color:var(--ion-text-color);flex-shrink:0}.file-info{flex:1;min-width:0;display:flex;flex-direction:column}.file-name{font-size:13px;color:var(--ion-text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:11px;color:var(--ion-color-medium)}:host-context(.dark) .attachment-item,:host-context(.ion-palette-dark) .attachment-item,:host-context(html.ion-palette-dark) .attachment-item,:host-context(body.dark) .attachment-item,:host-context([data-theme=dark]) .attachment-item{background:#ffffff0a;border-color:#ffffff1f}:host-context(.dark) .attachment-item.status-error,:host-context(.ion-palette-dark) .attachment-item.status-error,:host-context(html.ion-palette-dark) .attachment-item.status-error,:host-context(body.dark) .attachment-item.status-error,:host-context([data-theme=dark]) .attachment-item.status-error{border-color:var(--ion-color-danger)}.status-icon{font-size:20px;flex-shrink:0}.error-note{font-size:11px;width:100%}.max-note{font-size:12px}\n"] }]
29673
+ args: [{ selector: 'val-attachment-uploader', standalone: true, imports: [IonButton, IonIcon, IonNote, IonSpinner, TitleComponent], template: "<div class=\"attachment-uploader\">\n @if (props().title) {\n <val-title class=\"attachment-title\" [props]=\"{ content: props().title, size: 'small', color: 'dark', bold: false }\" />\n }\n\n <input #filePicker type=\"file\" [accept]=\"accept()\" multiple class=\"hidden-input\" (change)=\"onFilesSelected($event)\" />\n\n <div class=\"attachment-actions\">\n <ion-button fill=\"outline\" color=\"dark\" size=\"small\" [disabled]=\"isDisabled()\" (click)=\"filePicker.click()\">\n <ion-icon slot=\"start\" name=\"attach-outline\"></ion-icon>\n {{ i18n.t('attachAdd') }}\n </ion-button>\n <!-- label trick: native browser gesture forwarding \u2192 iOS/PWA no bloquea el picker -->\n <label class=\"camera-label\" [class.camera-label--disabled]=\"isDisabled()\">\n <input\n type=\"file\"\n accept=\"image/*\"\n capture=\"environment\"\n class=\"hidden-input\"\n [disabled]=\"isDisabled()\"\n (change)=\"onFilesSelected($event)\"\n />\n <ion-icon name=\"camera-outline\"></ion-icon>\n {{ i18n.t('attachCamera') }}\n </label>\n </div>\n\n @if (maxReached()) {\n <ion-note color=\"warning\" class=\"max-note\">\n {{ i18n.t('attachMaxCount', '_global', { count: maxFiles().toString() }) }}\n </ion-note>\n } @if (attachments().length > 0) {\n <div class=\"attachment-list\">\n @for (item of attachments(); track item.id) {\n <div class=\"attachment-item\" [class]=\"'status-' + item.status\">\n <ion-icon\n class=\"file-icon\"\n [name]=\"item.file.type.startsWith('image/') ? 'image-outline' : 'document-outline'\"\n ></ion-icon>\n <div class=\"file-info\">\n <span class=\"file-name\">{{ item.file.name }}</span>\n <span class=\"file-size\">{{ formatSize(item.file.size) }}</span>\n </div>\n\n @if (item.status === 'uploading') {\n <ion-spinner class=\"status-icon\" name=\"circular\"></ion-spinner>\n } @else if (item.status === 'ready') {\n <ion-icon class=\"status-icon\" name=\"checkmark-circle-outline\" color=\"success\"></ion-icon>\n } @else if (item.status === 'error') {\n <ion-icon class=\"status-icon\" name=\"close-circle-outline\" color=\"danger\"></ion-icon>\n } @if (item.status !== 'uploading') {\n <ion-button fill=\"clear\" size=\"small\" color=\"dark\" (click)=\"remove(item.id)\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n } @if (item.status === 'error' && item.error) {\n <ion-note color=\"danger\" class=\"error-note\">{{ item.error }}</ion-note>\n }\n </div>\n }\n </div>\n } @if (props().hint) {\n <p class=\"attachment-hint\">{{ props().hint }}</p>\n }\n</div>\n", styles: [".hidden-input{display:none}.camera-label{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 14px;height:32px;box-sizing:border-box;font-size:13px;font-weight:500;letter-spacing:.01em;border-radius:4px;border:1px solid currentColor;color:var(--ion-color-dark);background:transparent;cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .15s}.camera-label ion-icon{font-size:18px;flex-shrink:0}.camera-label--disabled{opacity:.4;pointer-events:none}.attachment-uploader{display:flex;flex-direction:column;gap:8px}.attachment-title{display:block;margin-bottom:2px}.attachment-hint{margin:2px 0 0;text-align:right;font-size:12px;line-height:1.4;color:var(--ion-color-dark)}.attachment-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.attachment-actions ion-button{margin:0;height:32px}.attachment-list{display:flex;flex-direction:column;gap:4px}.attachment-item{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;background:var(--ion-background-color, #fff);border:1px solid var(--ion-color-light-shade, rgba(0, 0, 0, .08));flex-wrap:wrap}.attachment-item.status-error{border-color:var(--ion-color-danger)}.file-icon{font-size:22px;color:var(--ion-text-color);flex-shrink:0}.file-info{flex:1;min-width:0;display:flex;flex-direction:column}.file-name{font-size:13px;color:var(--ion-text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:11px;color:var(--ion-color-medium)}:host-context(.dark) .attachment-item,:host-context(.ion-palette-dark) .attachment-item,:host-context(html.ion-palette-dark) .attachment-item,:host-context(body.dark) .attachment-item,:host-context([data-theme=dark]) .attachment-item{background:#ffffff0a;border-color:#ffffff1f}:host-context(.dark) .attachment-item.status-error,:host-context(.ion-palette-dark) .attachment-item.status-error,:host-context(html.ion-palette-dark) .attachment-item.status-error,:host-context(body.dark) .attachment-item.status-error,:host-context([data-theme=dark]) .attachment-item.status-error{border-color:var(--ion-color-danger)}.status-icon{font-size:20px;flex-shrink:0}.error-note{font-size:11px;width:100%}.max-note{font-size:12px}\n"] }]
29674
29674
  }], ctorParameters: () => [] });
29675
29675
 
29676
29676
  /**
@@ -30586,7 +30586,8 @@ class FormComponent {
30586
30586
  this.textareaPropMemo = new WeakMap();
30587
30587
  this.selectPropMemo = new WeakMap();
30588
30588
  this.previousUploadingCount = 0;
30589
- // Campos ATTACHMENT con uploads en curso — bloquea submit + pone botón en WORKING
30589
+ // Campos ATTACHMENT con uploads en curso — bloquea submit + DESHABILITA el
30590
+ // botón (no loading: el spinner se reserva para el submit real del form).
30590
30591
  this.uploadingFields = new Set();
30591
30592
  }
30592
30593
  ngOnInit() {
@@ -30683,10 +30684,14 @@ class FormComponent {
30683
30684
  */
30684
30685
  updateActionsCache() {
30685
30686
  let actionState = this.props.actions.state;
30686
- if (this.props.state === ComponentStates.WORKING || this.uploadingFields.size > 0) {
30687
+ if (this.props.state === ComponentStates.WORKING) {
30688
+ // Submit real del form en curso → botón en loading (spinner).
30687
30689
  actionState = ComponentStates.WORKING;
30688
30690
  }
30689
- else if (this.props.state === ComponentStates.DISABLED) {
30691
+ else if (this.props.state === ComponentStates.DISABLED || this.uploadingFields.size > 0) {
30692
+ // Subida de adjunto en curso → DESHABILITAR el submit, NO loading: todavía
30693
+ // no se está enviando el form, solo subiendo un archivo. Un spinner acá
30694
+ // confunde (parece que el form se está enviando).
30690
30695
  actionState = ComponentStates.DISABLED;
30691
30696
  }
30692
30697
  else if (this.props.state === ComponentStates.ENABLED || this.form?.valid) {
@@ -30917,6 +30922,8 @@ class FormComponent {
30917
30922
  @case (types.ATTACHMENT) {
30918
30923
  <val-attachment-uploader
30919
30924
  [props]="{
30925
+ title: f.label,
30926
+ hint: f.hint,
30920
30927
  uploadFn: f.uploadFn,
30921
30928
  disabled: f.state === states.DISABLED || f.state === states.WORKING,
30922
30929
  }"
@@ -31057,6 +31064,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
31057
31064
  @case (types.ATTACHMENT) {
31058
31065
  <val-attachment-uploader
31059
31066
  [props]="{
31067
+ title: f.label,
31068
+ hint: f.hint,
31060
31069
  uploadFn: f.uploadFn,
31061
31070
  disabled: f.state === states.DISABLED || f.state === states.WORKING,
31062
31071
  }"