@telcomdev/ui 0.1.17 → 0.1.18

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.
@@ -3091,6 +3091,384 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
3091
3091
  args: [{ providedIn: 'root' }]
3092
3092
  }] });
3093
3093
 
3094
+ let fileUploadSequence = 0;
3095
+ class TdFileUpload {
3096
+ cdr = inject(ChangeDetectorRef);
3097
+ viewer = inject(TdFileViewerService);
3098
+ generatedId = `td-file-upload-${++fileUploadSequence}`;
3099
+ nativeInput;
3100
+ id = '';
3101
+ label = 'Seleccionar archivos';
3102
+ placeholder = 'Ningún archivo seleccionado';
3103
+ description = 'Arrastra tus archivos aquí o selecciónalos desde tu dispositivo.';
3104
+ hint = '';
3105
+ accept = '';
3106
+ multiple = false;
3107
+ maxFiles = 0;
3108
+ maxSizeMb = 0;
3109
+ variant = 'dropzone';
3110
+ showPreview = true;
3111
+ clearable = true;
3112
+ disabled = false;
3113
+ readonly = false;
3114
+ required = false;
3115
+ error = '';
3116
+ dark = false;
3117
+ valueChange = new EventEmitter();
3118
+ filesChange = new EventEmitter();
3119
+ rejected = new EventEmitter();
3120
+ preview = new EventEmitter();
3121
+ removed = new EventEmitter();
3122
+ items = [];
3123
+ dragging = false;
3124
+ touched = false;
3125
+ rejectedItems = [];
3126
+ onChange = () => undefined;
3127
+ onTouched = () => undefined;
3128
+ get inputId() {
3129
+ return this.id || this.generatedId;
3130
+ }
3131
+ get isDisabled() {
3132
+ return this.disabled;
3133
+ }
3134
+ get selectedText() {
3135
+ if (!this.items.length)
3136
+ return this.placeholder;
3137
+ if (this.items.length === 1)
3138
+ return this.items[0]?.name ?? this.placeholder;
3139
+ return `${this.items.length} archivos seleccionados`;
3140
+ }
3141
+ get acceptText() {
3142
+ return this.accept || 'Cualquier archivo';
3143
+ }
3144
+ get hasError() {
3145
+ return !!this.error || !!this.rejectedItems.length;
3146
+ }
3147
+ get message() {
3148
+ return this.error || this.rejectedItems[0]?.message || this.hint;
3149
+ }
3150
+ writeValue(value) {
3151
+ this.revokePreviewUrls();
3152
+ const files = value ? (Array.isArray(value) ? value : [value]) : [];
3153
+ this.items = files.map((file) => this.createItem(file));
3154
+ this.rejectedItems = [];
3155
+ this.cdr.markForCheck();
3156
+ }
3157
+ registerOnChange(fn) {
3158
+ this.onChange = fn;
3159
+ }
3160
+ registerOnTouched(fn) {
3161
+ this.onTouched = fn;
3162
+ }
3163
+ setDisabledState(disabled) {
3164
+ this.disabled = disabled;
3165
+ this.cdr.markForCheck();
3166
+ }
3167
+ browse() {
3168
+ if (this.isDisabled || this.readonly)
3169
+ return;
3170
+ this.nativeInput?.nativeElement.click();
3171
+ }
3172
+ handleInput(event) {
3173
+ const input = event.target;
3174
+ this.addFiles(Array.from(input.files ?? []));
3175
+ input.value = '';
3176
+ }
3177
+ handleDragOver(event) {
3178
+ if (this.isDisabled || this.readonly)
3179
+ return;
3180
+ event.preventDefault();
3181
+ this.dragging = true;
3182
+ }
3183
+ handleDragLeave(event) {
3184
+ if (!this.isLeavingHost(event))
3185
+ return;
3186
+ this.dragging = false;
3187
+ }
3188
+ handleDrop(event) {
3189
+ if (this.isDisabled || this.readonly)
3190
+ return;
3191
+ event.preventDefault();
3192
+ this.dragging = false;
3193
+ this.addFiles(Array.from(event.dataTransfer?.files ?? []));
3194
+ }
3195
+ remove(item, event) {
3196
+ event?.stopPropagation();
3197
+ if (this.isDisabled || this.readonly)
3198
+ return;
3199
+ this.items = this.items.filter((current) => current.id !== item.id);
3200
+ this.revokePreviewUrl(item);
3201
+ this.removed.emit(item);
3202
+ this.emitValue();
3203
+ }
3204
+ clear(event) {
3205
+ event?.stopPropagation();
3206
+ if (this.isDisabled || this.readonly)
3207
+ return;
3208
+ this.revokePreviewUrls();
3209
+ this.items = [];
3210
+ this.rejectedItems = [];
3211
+ this.emitValue();
3212
+ }
3213
+ markTouched() {
3214
+ if (this.touched)
3215
+ return;
3216
+ this.touched = true;
3217
+ this.onTouched();
3218
+ }
3219
+ async openPreview(item, event) {
3220
+ event?.stopPropagation();
3221
+ this.preview.emit(item);
3222
+ if (item.previewKind === 'file')
3223
+ return;
3224
+ const file = await this.fileToDataUrl(item.file);
3225
+ this.viewer.open({
3226
+ file,
3227
+ fileName: item.name,
3228
+ type: item.type,
3229
+ size: item.size,
3230
+ description: item.sizeLabel,
3231
+ }, { titulo: item.name, descripcion: 'Vista previa del archivo seleccionado.' });
3232
+ }
3233
+ iconFor(item) {
3234
+ if (item.previewKind === 'image')
3235
+ return 'image';
3236
+ if (item.previewKind === 'pdf')
3237
+ return 'pdf';
3238
+ if (item.extension === 'xlsx' || item.extension === 'xls')
3239
+ return 'excel';
3240
+ if (item.extension === 'xml')
3241
+ return 'xml';
3242
+ if (item.extension === 'zip' || item.extension === 'rar' || item.extension === '7z')
3243
+ return 'zip';
3244
+ return 'file';
3245
+ }
3246
+ addFiles(files) {
3247
+ this.markTouched();
3248
+ if (!files.length)
3249
+ return;
3250
+ const accepted = [];
3251
+ const rejected = [];
3252
+ const availableSlots = this.resolveAvailableSlots();
3253
+ for (const file of files) {
3254
+ if (availableSlots !== null && accepted.length >= availableSlots) {
3255
+ rejected.push({
3256
+ file,
3257
+ reason: 'maxFiles',
3258
+ message: `Solo puedes seleccionar ${this.maxFiles} archivo(s).`,
3259
+ });
3260
+ continue;
3261
+ }
3262
+ const typeError = this.validateType(file);
3263
+ if (typeError) {
3264
+ rejected.push(typeError);
3265
+ continue;
3266
+ }
3267
+ const sizeError = this.validateSize(file);
3268
+ if (sizeError) {
3269
+ rejected.push(sizeError);
3270
+ continue;
3271
+ }
3272
+ accepted.push(file);
3273
+ }
3274
+ this.rejectedItems = rejected;
3275
+ if (rejected.length)
3276
+ this.rejected.emit(rejected);
3277
+ if (accepted.length) {
3278
+ const nextItems = accepted.map((file) => this.createItem(file));
3279
+ if (this.multiple) {
3280
+ this.items = [...this.items, ...nextItems];
3281
+ }
3282
+ else {
3283
+ this.revokePreviewUrls();
3284
+ this.items = nextItems.slice(0, 1);
3285
+ }
3286
+ this.emitValue();
3287
+ }
3288
+ this.cdr.markForCheck();
3289
+ }
3290
+ resolveAvailableSlots() {
3291
+ if (!this.multiple)
3292
+ return 1;
3293
+ if (this.maxFiles <= 0)
3294
+ return null;
3295
+ return Math.max(this.maxFiles - this.items.length, 0);
3296
+ }
3297
+ validateType(file) {
3298
+ const rules = this.accept
3299
+ .split(',')
3300
+ .map((rule) => rule.trim().toLowerCase())
3301
+ .filter(Boolean);
3302
+ if (!rules.length)
3303
+ return null;
3304
+ const extension = this.extensionOf(file.name);
3305
+ const fileType = file.type.toLowerCase();
3306
+ const valid = rules.some((rule) => {
3307
+ if (rule.startsWith('.'))
3308
+ return rule.slice(1) === extension;
3309
+ if (rule.endsWith('/*'))
3310
+ return fileType.startsWith(rule.slice(0, -1));
3311
+ return fileType === rule;
3312
+ });
3313
+ return valid
3314
+ ? null
3315
+ : {
3316
+ file,
3317
+ reason: 'type',
3318
+ message: `El archivo "${file.name}" no tiene un formato permitido.`,
3319
+ };
3320
+ }
3321
+ validateSize(file) {
3322
+ if (this.maxSizeMb <= 0)
3323
+ return null;
3324
+ const maxBytes = this.maxSizeMb * 1024 * 1024;
3325
+ return file.size <= maxBytes
3326
+ ? null
3327
+ : {
3328
+ file,
3329
+ reason: 'size',
3330
+ message: `El archivo "${file.name}" supera el máximo de ${this.maxSizeMb} MB.`,
3331
+ };
3332
+ }
3333
+ emitValue() {
3334
+ const files = this.items.map((item) => item.file);
3335
+ const value = this.multiple ? files : files[0] ?? null;
3336
+ this.onChange(value);
3337
+ this.valueChange.emit(value);
3338
+ this.filesChange.emit(files);
3339
+ this.cdr.markForCheck();
3340
+ }
3341
+ createItem(file) {
3342
+ const extension = this.extensionOf(file.name);
3343
+ const previewKind = file.type.startsWith('image/')
3344
+ ? 'image'
3345
+ : file.type === 'application/pdf' || extension === 'pdf'
3346
+ ? 'pdf'
3347
+ : 'file';
3348
+ return {
3349
+ id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
3350
+ file,
3351
+ name: file.name,
3352
+ size: file.size,
3353
+ sizeLabel: this.formatSize(file.size),
3354
+ type: file.type || this.typeFromExtension(extension),
3355
+ extension,
3356
+ previewKind,
3357
+ previewUrl: previewKind === 'image' ? URL.createObjectURL(file) : undefined,
3358
+ };
3359
+ }
3360
+ extensionOf(name) {
3361
+ return name.includes('.') ? name.split('.').pop()?.toLowerCase() || '' : '';
3362
+ }
3363
+ typeFromExtension(extension) {
3364
+ if (extension === 'pdf')
3365
+ return 'application/pdf';
3366
+ if (extension === 'xml')
3367
+ return 'application/xml';
3368
+ if (extension === 'xlsx')
3369
+ return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
3370
+ if (extension === 'xls')
3371
+ return 'application/vnd.ms-excel';
3372
+ if (extension === 'zip')
3373
+ return 'application/zip';
3374
+ return 'application/octet-stream';
3375
+ }
3376
+ formatSize(bytes) {
3377
+ if (bytes < 1024)
3378
+ return `${bytes} B`;
3379
+ const kb = bytes / 1024;
3380
+ if (kb < 1024)
3381
+ return `${kb.toFixed(kb >= 100 ? 0 : 1)} KB`;
3382
+ const mb = kb / 1024;
3383
+ return `${mb.toFixed(mb >= 100 ? 0 : 1)} MB`;
3384
+ }
3385
+ fileToDataUrl(file) {
3386
+ return new Promise((resolve, reject) => {
3387
+ const reader = new FileReader();
3388
+ reader.onload = () => resolve(String(reader.result ?? ''));
3389
+ reader.onerror = () => reject(reader.error);
3390
+ reader.readAsDataURL(file);
3391
+ });
3392
+ }
3393
+ isLeavingHost(event) {
3394
+ const currentTarget = event.currentTarget;
3395
+ const relatedTarget = event.relatedTarget;
3396
+ return currentTarget instanceof Node && !currentTarget.contains(relatedTarget);
3397
+ }
3398
+ revokePreviewUrls() {
3399
+ this.items.forEach((item) => this.revokePreviewUrl(item));
3400
+ }
3401
+ revokePreviewUrl(item) {
3402
+ if (item.previewUrl)
3403
+ URL.revokeObjectURL(item.previewUrl);
3404
+ }
3405
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: TdFileUpload, deps: [], target: i0.ɵɵFactoryTarget.Component });
3406
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: TdFileUpload, isStandalone: true, selector: "td-file-upload", inputs: { id: "id", label: "label", placeholder: "placeholder", description: "description", hint: "hint", accept: "accept", multiple: "multiple", maxFiles: "maxFiles", maxSizeMb: "maxSizeMb", variant: "variant", showPreview: "showPreview", clearable: "clearable", disabled: "disabled", readonly: "readonly", required: "required", error: "error", dark: "dark" }, outputs: { valueChange: "valueChange", filesChange: "filesChange", rejected: "rejected", preview: "preview", removed: "removed" }, providers: [
3407
+ {
3408
+ provide: NG_VALUE_ACCESSOR,
3409
+ useExisting: forwardRef(() => TdFileUpload),
3410
+ multi: true,
3411
+ },
3412
+ ], viewQueries: [{ propertyName: "nativeInput", first: true, predicate: ["nativeInput"], descendants: true }], ngImport: i0, template: "<div\n class=\"td-file-upload\"\n [class.td-file-upload--input]=\"variant === 'input'\"\n [class.td-file-upload--dropzone]=\"variant === 'dropzone'\"\n [class.td-file-upload--dragging]=\"dragging\"\n [class.td-file-upload--disabled]=\"isDisabled\"\n [class.td-file-upload--readonly]=\"readonly\"\n [class.td-file-upload--error]=\"hasError\"\n [class.td-file-upload--dark]=\"dark\"\n (dragover)=\"handleDragOver($event)\"\n (dragleave)=\"handleDragLeave($event)\"\n (drop)=\"handleDrop($event)\"\n (click)=\"variant === 'dropzone' ? browse() : null\"\n (blur)=\"markTouched()\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-disabled]=\"isDisabled || readonly\"\n [attr.aria-describedby]=\"message ? inputId + '-message' : null\"\n>\n <input\n #nativeInput\n class=\"td-file-upload__native\"\n [id]=\"inputId\"\n type=\"file\"\n [accept]=\"accept\"\n [multiple]=\"multiple\"\n [disabled]=\"isDisabled\"\n [required]=\"required\"\n (change)=\"handleInput($event)\"\n (blur)=\"markTouched()\"\n />\n\n @if (variant === 'input') {\n <label class=\"td-file-upload__label\" [for]=\"inputId\">{{ label }}</label>\n <div class=\"td-file-upload__input-shell\">\n <div class=\"td-file-upload__input-text\">\n <td-icon nombre=\"upload\" />\n <span>{{ selectedText }}</span>\n </div>\n <button\n type=\"button\"\n class=\"td-file-upload__button\"\n [disabled]=\"isDisabled || readonly\"\n (click)=\"browse(); $event.stopPropagation()\"\n >\n Seleccionar\n </button>\n </div>\n } @else {\n <div class=\"td-file-upload__hero\">\n <span class=\"td-file-upload__icon\">\n <td-icon nombre=\"cloud\" />\n </span>\n <div>\n <strong>{{ label }}</strong>\n <p>{{ description }}</p>\n </div>\n <button\n type=\"button\"\n class=\"td-file-upload__button\"\n [disabled]=\"isDisabled || readonly\"\n (click)=\"browse(); $event.stopPropagation()\"\n >\n Buscar archivo\n </button>\n </div>\n <div class=\"td-file-upload__meta\">\n <span>Formatos: {{ acceptText }}</span>\n @if (maxSizeMb > 0) {\n <span>M\u00E1x. {{ maxSizeMb }} MB</span>\n }\n @if (multiple && maxFiles > 0) {\n <span>Hasta {{ maxFiles }} archivos</span>\n }\n </div>\n }\n</div>\n\n@if (message) {\n <p\n class=\"td-file-upload__message\"\n [class.td-file-upload__message--error]=\"hasError\"\n [id]=\"inputId + '-message'\"\n >\n {{ message }}\n </p>\n}\n\n@if (showPreview && items.length) {\n <div\n class=\"td-file-upload__list\"\n [class.td-file-upload__list--gallery]=\"variant === 'dropzone'\"\n [class.td-file-upload__list--compact]=\"variant === 'input'\"\n [class.td-file-upload__list--dark]=\"dark\"\n >\n @for (item of items; track item.id) {\n <article class=\"td-file-upload__item\">\n <button\n type=\"button\"\n class=\"td-file-upload__thumb\"\n [class.td-file-upload__thumb--clickable]=\"item.previewKind !== 'file'\"\n [title]=\"item.previewKind !== 'file' ? 'Previsualizar' : item.name\"\n (click)=\"openPreview(item, $event)\"\n >\n @if (item.previewKind === 'image' && item.previewUrl) {\n <img [src]=\"item.previewUrl\" [alt]=\"item.name\" />\n } @else {\n <td-icon [nombre]=\"iconFor(item)\" />\n }\n </button>\n\n <div class=\"td-file-upload__item-info\">\n <strong>{{ item.name }}</strong>\n <span>{{ item.sizeLabel }} \u00B7 {{ item.type || item.extension || 'archivo' }}</span>\n </div>\n\n <div class=\"td-file-upload__actions\">\n @if (item.previewKind !== 'file') {\n <button type=\"button\" title=\"Previsualizar\" (click)=\"openPreview(item, $event)\">\n <td-icon nombre=\"visibility\" />\n </button>\n }\n @if (clearable) {\n <button type=\"button\" title=\"Quitar\" (click)=\"remove(item, $event)\">\n <td-icon nombre=\"close\" />\n </button>\n }\n </div>\n </article>\n }\n\n @if (clearable && items.length > 1) {\n <button type=\"button\" class=\"td-file-upload__clear-all\" (click)=\"clear($event)\">\n Limpiar todo\n </button>\n }\n </div>\n}\n", styles: [":host{display:block;font-family:var(--td-font-family, Inter, ui-sans-serif, system-ui, sans-serif)}.td-file-upload{position:relative;color:var(--td-color-text, #202b3d)}.td-file-upload__native{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}.td-file-upload__label{display:inline-flex;margin:0 0 .45rem .85rem;color:var(--td-color-primary, #5746d8);font-size:.72rem;font-weight:800}.td-file-upload__input-shell,.td-file-upload__hero{border:1px solid var(--td-color-border, #dce3ef);border-radius:var(--td-radius-lg, 1rem);background:var(--td-color-surface, #ffffff);box-shadow:var(--td-shadow-xs, 0 8px 24px rgba(38, 50, 72, .06));transition:border-color .18s ease,box-shadow .18s ease,background .18s ease}.td-file-upload__input-shell{display:flex;align-items:center;justify-content:space-between;gap:.75rem;min-height:3.25rem;padding:.45rem .5rem .45rem .9rem}.td-file-upload__input-text{display:inline-flex;min-width:0;align-items:center;gap:.65rem;color:var(--td-color-text-muted, #63708a);font-size:.86rem;font-weight:650}.td-file-upload__input-text span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.td-file-upload__hero{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:1rem;padding:1.25rem;cursor:pointer}.td-file-upload__hero strong{display:block;color:var(--td-color-text, #202b3d);font-size:.96rem;font-weight:850}.td-file-upload__hero p{margin:.25rem 0 0;color:var(--td-color-text-muted, #6c7890);font-size:.78rem;line-height:1.45}.td-file-upload__icon{display:inline-grid;width:3rem;height:3rem;place-items:center;border-radius:var(--td-radius-lg, 1rem);color:var(--td-color-primary, #5746d8);background:var(--td-color-primary-soft, #f0edff)}.td-file-upload__icon td-icon{width:1.45rem;height:1.45rem}.td-file-upload__button{display:inline-flex;align-items:center;justify-content:center;min-height:2.35rem;border:0;border-radius:var(--td-radius-md, .8rem);padding:0 .9rem;color:#fff;background:var(--td-color-primary, #315eb8);font-size:.78rem;font-weight:850;cursor:pointer;transition:transform .18s ease,background .18s ease}.td-file-upload__button:hover:not(:disabled){transform:translateY(-1px);background:var(--td-color-primary-hover, #284f9e)}.td-file-upload__button:disabled{cursor:not-allowed;opacity:.55}.td-file-upload__meta{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.65rem}.td-file-upload__meta span{border:1px solid var(--td-color-border, #e1e7f1);border-radius:999px;padding:.25rem .55rem;color:var(--td-color-text-muted, #6c7890);background:var(--td-color-surface-soft, #f8faff);font-size:.68rem;font-weight:750}.td-file-upload--dragging .td-file-upload__hero,.td-file-upload:focus-within .td-file-upload__input-shell,.td-file-upload:focus .td-file-upload__hero{border-color:var(--td-color-primary, #5746d8);box-shadow:var(--td-focus-ring, 0 0 0 4px rgba(87, 70, 216, .16))}.td-file-upload--error .td-file-upload__input-shell,.td-file-upload--error .td-file-upload__hero{border-color:var(--td-color-danger, #ef4444)}.td-file-upload--disabled,.td-file-upload--readonly{cursor:not-allowed;opacity:.7}.td-file-upload__message{margin:.45rem 0 0;color:var(--td-color-text-muted, #6c7890);font-size:.72rem;font-weight:650}.td-file-upload__message--error{color:var(--td-color-danger, #dc2626)}.td-file-upload__list{display:grid;gap:.55rem;margin-top:.75rem}.td-file-upload__list--gallery{grid-template-columns:repeat(auto-fill,minmax(138px,1fr));gap:.75rem}.td-file-upload__item{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.75rem;border:1px solid var(--td-color-border, #e2e8f0);border-radius:var(--td-radius-lg, 1rem);padding:.55rem;background:var(--td-color-surface, #ffffff)}.td-file-upload__list--gallery .td-file-upload__item{position:relative;display:flex;min-height:150px;overflow:hidden;align-items:stretch;gap:0;aspect-ratio:1/1;border-radius:var(--td-radius-xl, 1.15rem);padding:0;background:linear-gradient(135deg,#5746d814,#315eb80a),var(--td-color-surface-soft, #f7f9fc);box-shadow:var(--td-shadow-xs, 0 10px 26px rgba(38, 50, 72, .08))}.td-file-upload__list--gallery .td-file-upload__item:hover{transform:translateY(-2px);box-shadow:var(--td-shadow-md, 0 18px 40px rgba(38, 50, 72, .14))}.td-file-upload__thumb{display:inline-grid;width:3rem;height:3rem;place-items:center;overflow:hidden;border:1px solid var(--td-color-border, #dce3ef);border-radius:var(--td-radius-md, .8rem);color:var(--td-color-primary, #315eb8);background:var(--td-color-surface-soft, #f7f9fc)}.td-file-upload__list--gallery .td-file-upload__thumb{position:absolute;inset:0;width:100%;height:100%;border:0;border-radius:inherit;color:var(--td-color-primary, #315eb8);background:radial-gradient(circle at 30% 18%,rgba(255,255,255,.72),transparent 24%),linear-gradient(135deg,var(--td-color-primary-soft, #eef2ff),var(--td-color-surface-soft, #f7f9fc))}.td-file-upload__thumb--clickable{cursor:pointer}.td-file-upload__thumb img{width:100%;height:100%;object-fit:cover}.td-file-upload__list--gallery .td-file-upload__thumb img{transform:scale(1.01)}.td-file-upload__list--gallery .td-file-upload__thumb td-icon{width:2.6rem;height:2.6rem;filter:drop-shadow(0 10px 18px rgba(49,94,184,.16))}.td-file-upload__item-info{display:grid;min-width:0;gap:.2rem}.td-file-upload__list--gallery .td-file-upload__item-info{position:absolute;right:0;bottom:0;left:0;z-index:1;gap:.1rem;padding:1.65rem .7rem .65rem;background:linear-gradient(to top,#0b1220c7,#0b122000)}.td-file-upload__item-info strong{overflow:hidden;color:var(--td-color-text, #202b3d);font-size:.82rem;font-weight:850;text-overflow:ellipsis;white-space:nowrap}.td-file-upload__list--gallery .td-file-upload__item-info strong{color:#fff;font-size:.75rem;text-shadow:0 1px 2px rgba(0,0,0,.35)}.td-file-upload__item-info span{overflow:hidden;color:var(--td-color-text-muted, #72809a);font-size:.7rem;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.td-file-upload__list--gallery .td-file-upload__item-info span{color:#ffffffd1;font-size:.66rem}.td-file-upload__actions{display:inline-flex;gap:.3rem}.td-file-upload__list--gallery .td-file-upload__actions{position:absolute;top:.5rem;right:.5rem;z-index:2;opacity:0;transform:translateY(-3px);transition:opacity .18s ease,transform .18s ease}.td-file-upload__list--gallery .td-file-upload__item:hover .td-file-upload__actions,.td-file-upload__list--gallery .td-file-upload__item:focus-within .td-file-upload__actions{opacity:1;transform:translateY(0)}.td-file-upload__actions button,.td-file-upload__clear-all{display:inline-grid;min-width:2rem;height:2rem;place-items:center;border:1px solid var(--td-color-border, #dce3ef);border-radius:var(--td-radius-md, .75rem);color:var(--td-color-text-muted, #62708a);background:var(--td-color-surface, #ffffff);cursor:pointer}.td-file-upload__list--gallery .td-file-upload__actions button{width:2rem;min-width:2rem;height:2rem;border-color:#ffffff3d;color:#fff;background:#0f172a8a;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.td-file-upload__list--gallery .td-file-upload__actions button:hover{color:#fff;border-color:#ffffff85;background:#0f172ac7}.td-file-upload__actions button:hover,.td-file-upload__clear-all:hover{color:var(--td-color-primary, #315eb8);border-color:var(--td-color-primary, #315eb8)}.td-file-upload__clear-all{justify-self:end;grid-column:1/-1;width:auto;padding:0 .75rem;font-size:.72rem;font-weight:800}:host-context(.docs-shell--oscuro) .td-file-upload__input-shell,:host-context(.docs-shell--oscuro) .td-file-upload__hero,:host-context(.docs-shell--oscuro) .td-file-upload__item,.td-file-upload--dark .td-file-upload__input-shell,.td-file-upload--dark .td-file-upload__hero,.td-file-upload__list--dark .td-file-upload__item{border-color:var(--td-color-border, #30384a);background:var(--td-color-surface, #171923)}@media(max-width:700px){.td-file-upload__hero{grid-template-columns:1fr}.td-file-upload__button{width:100%}}\n"], dependencies: [{ kind: "component", type: TdIcon, selector: "td-icon", inputs: ["nombre", "titulo", "tamano"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3413
+ }
3414
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: TdFileUpload, decorators: [{
3415
+ type: Component,
3416
+ args: [{ selector: 'td-file-upload', imports: [TdIcon], providers: [
3417
+ {
3418
+ provide: NG_VALUE_ACCESSOR,
3419
+ useExisting: forwardRef(() => TdFileUpload),
3420
+ multi: true,
3421
+ },
3422
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"td-file-upload\"\n [class.td-file-upload--input]=\"variant === 'input'\"\n [class.td-file-upload--dropzone]=\"variant === 'dropzone'\"\n [class.td-file-upload--dragging]=\"dragging\"\n [class.td-file-upload--disabled]=\"isDisabled\"\n [class.td-file-upload--readonly]=\"readonly\"\n [class.td-file-upload--error]=\"hasError\"\n [class.td-file-upload--dark]=\"dark\"\n (dragover)=\"handleDragOver($event)\"\n (dragleave)=\"handleDragLeave($event)\"\n (drop)=\"handleDrop($event)\"\n (click)=\"variant === 'dropzone' ? browse() : null\"\n (blur)=\"markTouched()\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-disabled]=\"isDisabled || readonly\"\n [attr.aria-describedby]=\"message ? inputId + '-message' : null\"\n>\n <input\n #nativeInput\n class=\"td-file-upload__native\"\n [id]=\"inputId\"\n type=\"file\"\n [accept]=\"accept\"\n [multiple]=\"multiple\"\n [disabled]=\"isDisabled\"\n [required]=\"required\"\n (change)=\"handleInput($event)\"\n (blur)=\"markTouched()\"\n />\n\n @if (variant === 'input') {\n <label class=\"td-file-upload__label\" [for]=\"inputId\">{{ label }}</label>\n <div class=\"td-file-upload__input-shell\">\n <div class=\"td-file-upload__input-text\">\n <td-icon nombre=\"upload\" />\n <span>{{ selectedText }}</span>\n </div>\n <button\n type=\"button\"\n class=\"td-file-upload__button\"\n [disabled]=\"isDisabled || readonly\"\n (click)=\"browse(); $event.stopPropagation()\"\n >\n Seleccionar\n </button>\n </div>\n } @else {\n <div class=\"td-file-upload__hero\">\n <span class=\"td-file-upload__icon\">\n <td-icon nombre=\"cloud\" />\n </span>\n <div>\n <strong>{{ label }}</strong>\n <p>{{ description }}</p>\n </div>\n <button\n type=\"button\"\n class=\"td-file-upload__button\"\n [disabled]=\"isDisabled || readonly\"\n (click)=\"browse(); $event.stopPropagation()\"\n >\n Buscar archivo\n </button>\n </div>\n <div class=\"td-file-upload__meta\">\n <span>Formatos: {{ acceptText }}</span>\n @if (maxSizeMb > 0) {\n <span>M\u00E1x. {{ maxSizeMb }} MB</span>\n }\n @if (multiple && maxFiles > 0) {\n <span>Hasta {{ maxFiles }} archivos</span>\n }\n </div>\n }\n</div>\n\n@if (message) {\n <p\n class=\"td-file-upload__message\"\n [class.td-file-upload__message--error]=\"hasError\"\n [id]=\"inputId + '-message'\"\n >\n {{ message }}\n </p>\n}\n\n@if (showPreview && items.length) {\n <div\n class=\"td-file-upload__list\"\n [class.td-file-upload__list--gallery]=\"variant === 'dropzone'\"\n [class.td-file-upload__list--compact]=\"variant === 'input'\"\n [class.td-file-upload__list--dark]=\"dark\"\n >\n @for (item of items; track item.id) {\n <article class=\"td-file-upload__item\">\n <button\n type=\"button\"\n class=\"td-file-upload__thumb\"\n [class.td-file-upload__thumb--clickable]=\"item.previewKind !== 'file'\"\n [title]=\"item.previewKind !== 'file' ? 'Previsualizar' : item.name\"\n (click)=\"openPreview(item, $event)\"\n >\n @if (item.previewKind === 'image' && item.previewUrl) {\n <img [src]=\"item.previewUrl\" [alt]=\"item.name\" />\n } @else {\n <td-icon [nombre]=\"iconFor(item)\" />\n }\n </button>\n\n <div class=\"td-file-upload__item-info\">\n <strong>{{ item.name }}</strong>\n <span>{{ item.sizeLabel }} \u00B7 {{ item.type || item.extension || 'archivo' }}</span>\n </div>\n\n <div class=\"td-file-upload__actions\">\n @if (item.previewKind !== 'file') {\n <button type=\"button\" title=\"Previsualizar\" (click)=\"openPreview(item, $event)\">\n <td-icon nombre=\"visibility\" />\n </button>\n }\n @if (clearable) {\n <button type=\"button\" title=\"Quitar\" (click)=\"remove(item, $event)\">\n <td-icon nombre=\"close\" />\n </button>\n }\n </div>\n </article>\n }\n\n @if (clearable && items.length > 1) {\n <button type=\"button\" class=\"td-file-upload__clear-all\" (click)=\"clear($event)\">\n Limpiar todo\n </button>\n }\n </div>\n}\n", styles: [":host{display:block;font-family:var(--td-font-family, Inter, ui-sans-serif, system-ui, sans-serif)}.td-file-upload{position:relative;color:var(--td-color-text, #202b3d)}.td-file-upload__native{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}.td-file-upload__label{display:inline-flex;margin:0 0 .45rem .85rem;color:var(--td-color-primary, #5746d8);font-size:.72rem;font-weight:800}.td-file-upload__input-shell,.td-file-upload__hero{border:1px solid var(--td-color-border, #dce3ef);border-radius:var(--td-radius-lg, 1rem);background:var(--td-color-surface, #ffffff);box-shadow:var(--td-shadow-xs, 0 8px 24px rgba(38, 50, 72, .06));transition:border-color .18s ease,box-shadow .18s ease,background .18s ease}.td-file-upload__input-shell{display:flex;align-items:center;justify-content:space-between;gap:.75rem;min-height:3.25rem;padding:.45rem .5rem .45rem .9rem}.td-file-upload__input-text{display:inline-flex;min-width:0;align-items:center;gap:.65rem;color:var(--td-color-text-muted, #63708a);font-size:.86rem;font-weight:650}.td-file-upload__input-text span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.td-file-upload__hero{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:1rem;padding:1.25rem;cursor:pointer}.td-file-upload__hero strong{display:block;color:var(--td-color-text, #202b3d);font-size:.96rem;font-weight:850}.td-file-upload__hero p{margin:.25rem 0 0;color:var(--td-color-text-muted, #6c7890);font-size:.78rem;line-height:1.45}.td-file-upload__icon{display:inline-grid;width:3rem;height:3rem;place-items:center;border-radius:var(--td-radius-lg, 1rem);color:var(--td-color-primary, #5746d8);background:var(--td-color-primary-soft, #f0edff)}.td-file-upload__icon td-icon{width:1.45rem;height:1.45rem}.td-file-upload__button{display:inline-flex;align-items:center;justify-content:center;min-height:2.35rem;border:0;border-radius:var(--td-radius-md, .8rem);padding:0 .9rem;color:#fff;background:var(--td-color-primary, #315eb8);font-size:.78rem;font-weight:850;cursor:pointer;transition:transform .18s ease,background .18s ease}.td-file-upload__button:hover:not(:disabled){transform:translateY(-1px);background:var(--td-color-primary-hover, #284f9e)}.td-file-upload__button:disabled{cursor:not-allowed;opacity:.55}.td-file-upload__meta{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.65rem}.td-file-upload__meta span{border:1px solid var(--td-color-border, #e1e7f1);border-radius:999px;padding:.25rem .55rem;color:var(--td-color-text-muted, #6c7890);background:var(--td-color-surface-soft, #f8faff);font-size:.68rem;font-weight:750}.td-file-upload--dragging .td-file-upload__hero,.td-file-upload:focus-within .td-file-upload__input-shell,.td-file-upload:focus .td-file-upload__hero{border-color:var(--td-color-primary, #5746d8);box-shadow:var(--td-focus-ring, 0 0 0 4px rgba(87, 70, 216, .16))}.td-file-upload--error .td-file-upload__input-shell,.td-file-upload--error .td-file-upload__hero{border-color:var(--td-color-danger, #ef4444)}.td-file-upload--disabled,.td-file-upload--readonly{cursor:not-allowed;opacity:.7}.td-file-upload__message{margin:.45rem 0 0;color:var(--td-color-text-muted, #6c7890);font-size:.72rem;font-weight:650}.td-file-upload__message--error{color:var(--td-color-danger, #dc2626)}.td-file-upload__list{display:grid;gap:.55rem;margin-top:.75rem}.td-file-upload__list--gallery{grid-template-columns:repeat(auto-fill,minmax(138px,1fr));gap:.75rem}.td-file-upload__item{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.75rem;border:1px solid var(--td-color-border, #e2e8f0);border-radius:var(--td-radius-lg, 1rem);padding:.55rem;background:var(--td-color-surface, #ffffff)}.td-file-upload__list--gallery .td-file-upload__item{position:relative;display:flex;min-height:150px;overflow:hidden;align-items:stretch;gap:0;aspect-ratio:1/1;border-radius:var(--td-radius-xl, 1.15rem);padding:0;background:linear-gradient(135deg,#5746d814,#315eb80a),var(--td-color-surface-soft, #f7f9fc);box-shadow:var(--td-shadow-xs, 0 10px 26px rgba(38, 50, 72, .08))}.td-file-upload__list--gallery .td-file-upload__item:hover{transform:translateY(-2px);box-shadow:var(--td-shadow-md, 0 18px 40px rgba(38, 50, 72, .14))}.td-file-upload__thumb{display:inline-grid;width:3rem;height:3rem;place-items:center;overflow:hidden;border:1px solid var(--td-color-border, #dce3ef);border-radius:var(--td-radius-md, .8rem);color:var(--td-color-primary, #315eb8);background:var(--td-color-surface-soft, #f7f9fc)}.td-file-upload__list--gallery .td-file-upload__thumb{position:absolute;inset:0;width:100%;height:100%;border:0;border-radius:inherit;color:var(--td-color-primary, #315eb8);background:radial-gradient(circle at 30% 18%,rgba(255,255,255,.72),transparent 24%),linear-gradient(135deg,var(--td-color-primary-soft, #eef2ff),var(--td-color-surface-soft, #f7f9fc))}.td-file-upload__thumb--clickable{cursor:pointer}.td-file-upload__thumb img{width:100%;height:100%;object-fit:cover}.td-file-upload__list--gallery .td-file-upload__thumb img{transform:scale(1.01)}.td-file-upload__list--gallery .td-file-upload__thumb td-icon{width:2.6rem;height:2.6rem;filter:drop-shadow(0 10px 18px rgba(49,94,184,.16))}.td-file-upload__item-info{display:grid;min-width:0;gap:.2rem}.td-file-upload__list--gallery .td-file-upload__item-info{position:absolute;right:0;bottom:0;left:0;z-index:1;gap:.1rem;padding:1.65rem .7rem .65rem;background:linear-gradient(to top,#0b1220c7,#0b122000)}.td-file-upload__item-info strong{overflow:hidden;color:var(--td-color-text, #202b3d);font-size:.82rem;font-weight:850;text-overflow:ellipsis;white-space:nowrap}.td-file-upload__list--gallery .td-file-upload__item-info strong{color:#fff;font-size:.75rem;text-shadow:0 1px 2px rgba(0,0,0,.35)}.td-file-upload__item-info span{overflow:hidden;color:var(--td-color-text-muted, #72809a);font-size:.7rem;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.td-file-upload__list--gallery .td-file-upload__item-info span{color:#ffffffd1;font-size:.66rem}.td-file-upload__actions{display:inline-flex;gap:.3rem}.td-file-upload__list--gallery .td-file-upload__actions{position:absolute;top:.5rem;right:.5rem;z-index:2;opacity:0;transform:translateY(-3px);transition:opacity .18s ease,transform .18s ease}.td-file-upload__list--gallery .td-file-upload__item:hover .td-file-upload__actions,.td-file-upload__list--gallery .td-file-upload__item:focus-within .td-file-upload__actions{opacity:1;transform:translateY(0)}.td-file-upload__actions button,.td-file-upload__clear-all{display:inline-grid;min-width:2rem;height:2rem;place-items:center;border:1px solid var(--td-color-border, #dce3ef);border-radius:var(--td-radius-md, .75rem);color:var(--td-color-text-muted, #62708a);background:var(--td-color-surface, #ffffff);cursor:pointer}.td-file-upload__list--gallery .td-file-upload__actions button{width:2rem;min-width:2rem;height:2rem;border-color:#ffffff3d;color:#fff;background:#0f172a8a;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.td-file-upload__list--gallery .td-file-upload__actions button:hover{color:#fff;border-color:#ffffff85;background:#0f172ac7}.td-file-upload__actions button:hover,.td-file-upload__clear-all:hover{color:var(--td-color-primary, #315eb8);border-color:var(--td-color-primary, #315eb8)}.td-file-upload__clear-all{justify-self:end;grid-column:1/-1;width:auto;padding:0 .75rem;font-size:.72rem;font-weight:800}:host-context(.docs-shell--oscuro) .td-file-upload__input-shell,:host-context(.docs-shell--oscuro) .td-file-upload__hero,:host-context(.docs-shell--oscuro) .td-file-upload__item,.td-file-upload--dark .td-file-upload__input-shell,.td-file-upload--dark .td-file-upload__hero,.td-file-upload__list--dark .td-file-upload__item{border-color:var(--td-color-border, #30384a);background:var(--td-color-surface, #171923)}@media(max-width:700px){.td-file-upload__hero{grid-template-columns:1fr}.td-file-upload__button{width:100%}}\n"] }]
3423
+ }], propDecorators: { nativeInput: [{
3424
+ type: ViewChild,
3425
+ args: ['nativeInput']
3426
+ }], id: [{
3427
+ type: Input
3428
+ }], label: [{
3429
+ type: Input
3430
+ }], placeholder: [{
3431
+ type: Input
3432
+ }], description: [{
3433
+ type: Input
3434
+ }], hint: [{
3435
+ type: Input
3436
+ }], accept: [{
3437
+ type: Input
3438
+ }], multiple: [{
3439
+ type: Input
3440
+ }], maxFiles: [{
3441
+ type: Input
3442
+ }], maxSizeMb: [{
3443
+ type: Input
3444
+ }], variant: [{
3445
+ type: Input
3446
+ }], showPreview: [{
3447
+ type: Input
3448
+ }], clearable: [{
3449
+ type: Input
3450
+ }], disabled: [{
3451
+ type: Input
3452
+ }], readonly: [{
3453
+ type: Input
3454
+ }], required: [{
3455
+ type: Input
3456
+ }], error: [{
3457
+ type: Input
3458
+ }], dark: [{
3459
+ type: Input
3460
+ }], valueChange: [{
3461
+ type: Output
3462
+ }], filesChange: [{
3463
+ type: Output
3464
+ }], rejected: [{
3465
+ type: Output
3466
+ }], preview: [{
3467
+ type: Output
3468
+ }], removed: [{
3469
+ type: Output
3470
+ }] } });
3471
+
3094
3472
  class TdHeader {
3095
3473
  menuAbierto = true;
3096
3474
  mostrarMenu = true;
@@ -4149,6 +4527,167 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
4149
4527
  type: Input
4150
4528
  }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
4151
4529
 
4530
+ let radioSequence = 0;
4531
+ class TdRadioGroup {
4532
+ cdr = inject(ChangeDetectorRef);
4533
+ formBridge = createTdFormControlBridge();
4534
+ generatedName = `td-radio-${++radioSequence}`;
4535
+ options = [];
4536
+ label = '';
4537
+ hint = '';
4538
+ dark = false;
4539
+ direction = 'vertical';
4540
+ variant = 'default';
4541
+ compareWith = (first, second) => Object.is(first, second);
4542
+ name = input('', /* @ts-ignore */
4543
+ ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
4544
+ disabled = input(false, /* @ts-ignore */
4545
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4546
+ disabledReasons = input([], /* @ts-ignore */
4547
+ ...(ngDevMode ? [{ debugName: "disabledReasons" }] : /* istanbul ignore next */ []));
4548
+ readonly = input(false, /* @ts-ignore */
4549
+ ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
4550
+ hidden = input(false, /* @ts-ignore */
4551
+ ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
4552
+ invalid = input(false, /* @ts-ignore */
4553
+ ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
4554
+ pending = input(false, /* @ts-ignore */
4555
+ ...(ngDevMode ? [{ debugName: "pending" }] : /* istanbul ignore next */ []));
4556
+ dirty = input(false, /* @ts-ignore */
4557
+ ...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
4558
+ required = input(false, /* @ts-ignore */
4559
+ ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
4560
+ errors = input([], /* @ts-ignore */
4561
+ ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
4562
+ touched = model(false, /* @ts-ignore */
4563
+ ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
4564
+ touch = output();
4565
+ error = input('', /* @ts-ignore */
4566
+ ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
4567
+ errorDisplay = input('auto', /* @ts-ignore */
4568
+ ...(ngDevMode ? [{ debugName: "errorDisplay" }] : /* istanbul ignore next */ []));
4569
+ value = model(null, /* @ts-ignore */
4570
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4571
+ selectionChange = new EventEmitter();
4572
+ isDisabled = computed(() => this.formBridge.isDisabled(this.disabled()), /* @ts-ignore */
4573
+ ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
4574
+ fieldError = computed(() => this.errors()[0]?.message || '', /* @ts-ignore */
4575
+ ...(ngDevMode ? [{ debugName: "fieldError" }] : /* istanbul ignore next */ []));
4576
+ shouldDisplayFieldError = computed(() => this.formBridge.shouldDisplayError(this.errorDisplay(), this.invalid(), !!this.fieldError(), this.touched(), this.dirty()), /* @ts-ignore */
4577
+ ...(ngDevMode ? [{ debugName: "shouldDisplayFieldError" }] : /* istanbul ignore next */ []));
4578
+ displayError = computed(() => this.error() || (this.shouldDisplayFieldError() ? this.fieldError() : ''), /* @ts-ignore */
4579
+ ...(ngDevMode ? [{ debugName: "displayError" }] : /* istanbul ignore next */ []));
4580
+ hasError = computed(() => !!this.error() || this.shouldDisplayFieldError(), /* @ts-ignore */
4581
+ ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
4582
+ onChange = () => undefined;
4583
+ get groupName() {
4584
+ return this.name() || this.generatedName;
4585
+ }
4586
+ get messageId() {
4587
+ return this.displayError() || this.hint ? `${this.groupName}-message` : null;
4588
+ }
4589
+ writeValue(value) {
4590
+ this.value.set(value ?? null);
4591
+ this.cdr.markForCheck();
4592
+ }
4593
+ registerOnChange(fn) {
4594
+ this.onChange = fn;
4595
+ }
4596
+ registerOnTouched(fn) {
4597
+ this.formBridge.registerOnTouched(fn);
4598
+ }
4599
+ setDisabledState(disabled) {
4600
+ this.formBridge.setDisabledState(disabled);
4601
+ }
4602
+ isSelected(option) {
4603
+ return this.compareWith(option.value, this.value());
4604
+ }
4605
+ optionId(index) {
4606
+ return `${this.groupName}-${index}`;
4607
+ }
4608
+ select(option) {
4609
+ if (this.isDisabled() || this.readonly() || option.disabled)
4610
+ return;
4611
+ if (this.isSelected(option)) {
4612
+ this.markTouched();
4613
+ return;
4614
+ }
4615
+ this.value.set(option.value);
4616
+ this.onChange(option.value);
4617
+ this.selectionChange.emit(option.value);
4618
+ this.markTouched();
4619
+ this.cdr.markForCheck();
4620
+ }
4621
+ handleKeydown(event, index) {
4622
+ if (!['ArrowDown', 'ArrowRight', 'ArrowUp', 'ArrowLeft', 'Home', 'End'].includes(event.key)) {
4623
+ return;
4624
+ }
4625
+ event.preventDefault();
4626
+ const enabled = this.options
4627
+ .map((option, optionIndex) => ({ option, optionIndex }))
4628
+ .filter((item) => !item.option.disabled);
4629
+ if (!enabled.length)
4630
+ return;
4631
+ const currentPosition = enabled.findIndex((item) => item.optionIndex === index);
4632
+ let nextPosition = currentPosition;
4633
+ if (event.key === 'Home')
4634
+ nextPosition = 0;
4635
+ if (event.key === 'End')
4636
+ nextPosition = enabled.length - 1;
4637
+ if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {
4638
+ nextPosition = (currentPosition + 1 + enabled.length) % enabled.length;
4639
+ }
4640
+ if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {
4641
+ nextPosition = (currentPosition - 1 + enabled.length) % enabled.length;
4642
+ }
4643
+ const next = enabled[nextPosition];
4644
+ if (!next)
4645
+ return;
4646
+ this.select(next.option);
4647
+ queueMicrotask(() => document.getElementById(this.optionId(next.optionIndex))?.focus());
4648
+ }
4649
+ markTouched() {
4650
+ this.formBridge.markTouched(this.touched, this.touch);
4651
+ }
4652
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: TdRadioGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
4653
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: TdRadioGroup, isStandalone: true, selector: "td-radio-group", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: false, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: false, isRequired: false, transformFunction: null }, dark: { classPropertyName: "dark", publicName: "dark", isSignal: false, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: false, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: false, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "compareWith", isSignal: false, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, disabledReasons: { classPropertyName: "disabledReasons", publicName: "disabledReasons", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, pending: { classPropertyName: "pending", publicName: "pending", isSignal: true, isRequired: false, transformFunction: null }, dirty: { classPropertyName: "dirty", publicName: "dirty", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, errorDisplay: { classPropertyName: "errorDisplay", publicName: "errorDisplay", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { touched: "touchedChange", touch: "touch", value: "valueChange", selectionChange: "selectionChange" }, host: { properties: { "hidden": "hidden()", "attr.aria-busy": "pending() ? \"true\" : null" } }, providers: [
4654
+ {
4655
+ provide: NG_VALUE_ACCESSOR,
4656
+ useExisting: forwardRef(() => TdRadioGroup),
4657
+ multi: true,
4658
+ },
4659
+ ], ngImport: i0, template: "<div\n class=\"td-radio\"\n [class.td-radio--horizontal]=\"direction === 'horizontal'\"\n [class.td-radio--card]=\"variant === 'card'\"\n [class.td-radio--disabled]=\"isDisabled()\"\n [class.td-radio--readonly]=\"readonly()\"\n [class.td-radio--error]=\"hasError()\"\n [class.td-radio--dark]=\"dark\"\n role=\"radiogroup\"\n [attr.aria-label]=\"label || null\"\n [attr.aria-required]=\"required()\"\n [attr.aria-invalid]=\"hasError()\"\n [attr.aria-describedby]=\"messageId\"\n>\n @if (label) {\n <span class=\"td-radio__legend\">{{ label }}</span>\n }\n\n <div class=\"td-radio__options\">\n @for (option of options; track option.value; let index = $index) {\n <label\n class=\"td-radio__option\"\n [class.td-radio__option--selected]=\"isSelected(option)\"\n [class.td-radio__option--disabled]=\"option.disabled || isDisabled()\"\n [for]=\"optionId(index)\"\n >\n <input\n [id]=\"optionId(index)\"\n type=\"radio\"\n [name]=\"groupName\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"isDisabled() || option.disabled\"\n [required]=\"required()\"\n [attr.aria-readonly]=\"readonly()\"\n [attr.aria-invalid]=\"hasError()\"\n (change)=\"select(option)\"\n (blur)=\"markTouched()\"\n (keydown)=\"handleKeydown($event, index)\"\n />\n\n <span class=\"td-radio__control\" aria-hidden=\"true\"></span>\n\n @if (option.icon) {\n <span class=\"td-radio__icon\">\n <td-icon [nombre]=\"option.icon\" />\n </span>\n }\n\n <span class=\"td-radio__content\">\n <span class=\"td-radio__label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"td-radio__description\">{{ option.description }}</span>\n }\n </span>\n </label>\n }\n </div>\n</div>\n\n@if (displayError() || hint) {\n <p\n class=\"td-radio__message\"\n [class.td-radio__message--error]=\"hasError()\"\n [id]=\"messageId\"\n >\n {{ displayError() || hint }}\n </p>\n}\n", styles: [":host{display:block;min-width:0;font-family:var(--td-font-family, Inter, ui-sans-serif, system-ui, sans-serif)}*{box-sizing:border-box}.td-radio{--td-radio-bg: var(--td-color-surface, #ffffff);--td-radio-border: var(--td-color-border-strong, #cfd5e1);--td-radio-text: var(--td-color-text, #263147);--td-radio-muted: var(--td-color-text-muted, #748096);color:var(--td-radio-text)}.td-radio--dark{--td-radio-bg: var(--td-color-surface, #19191f);--td-radio-border: var(--td-color-border-strong, #555563);--td-radio-text: var(--td-color-text, #ededf0);--td-radio-muted: var(--td-color-text-muted, #a1a1aa);color-scheme:dark}.td-radio__legend{display:inline-flex;margin-bottom:.65rem;color:var(--td-color-primary, #5746d8);font-size:.72rem;font-weight:850}.td-radio__options{display:grid;gap:.65rem}.td-radio--horizontal .td-radio__options{display:flex;flex-wrap:wrap}.td-radio__option{position:relative;display:inline-flex;max-width:100%;align-items:flex-start;gap:.72rem;color:var(--td-radio-text);cursor:pointer;-webkit-user-select:none;user-select:none}.td-radio--card .td-radio__option{align-items:center;border:1px solid var(--td-color-border, #dce3ef);border-radius:var(--td-radius-lg, 1rem);padding:.85rem;background:var(--td-radio-bg);box-shadow:var(--td-shadow-xs, 0 8px 24px rgba(38, 50, 72, .06));transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease,background .16s ease}.td-radio--card .td-radio__option:hover:not(.td-radio__option--disabled){transform:translateY(-1px);border-color:var(--td-color-primary, #5746d8);box-shadow:var(--td-shadow-md, 0 14px 34px rgba(38, 50, 72, .1))}input{position:absolute;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none}.td-radio__control{display:grid;width:1.22rem;height:1.22rem;flex:0 0 auto;place-items:center;margin-top:.05rem;border:1.6px solid var(--td-radio-border);border-radius:999px;background:var(--td-radio-bg);transition:border-color .14s ease,box-shadow .14s ease,background .14s ease,transform .1s ease}.td-radio__control:after{width:.5rem;height:.5rem;border-radius:999px;background:var(--td-color-on-primary, #ffffff);content:\"\";opacity:0;transform:scale(.35);transition:opacity .14s ease,transform .14s ease}.td-radio__option:hover:not(.td-radio__option--disabled) .td-radio__control{border-color:var(--td-color-primary, #5746d8)}.td-radio__option--selected .td-radio__control{border-color:var(--td-color-primary, #5746d8);background:var(--td-color-primary, #5746d8)}.td-radio__option--selected .td-radio__control:after{opacity:1;transform:scale(1)}input:focus-visible+.td-radio__control{border-color:var(--td-color-primary, #5746d8);box-shadow:var(--td-focus-ring, 0 0 0 3px rgba(87, 70, 216, .16))}input:active+.td-radio__control{transform:scale(.94)}.td-radio--error .td-radio__control{border-color:var(--td-color-danger, #d33f3f);box-shadow:var(--td-danger-ring, 0 0 0 3px color-mix(in srgb, var(--td-color-danger, #d33f3f) 16%, transparent))}.td-radio__icon{display:inline-grid;width:2rem;height:2rem;flex:0 0 auto;place-items:center;border-radius:var(--td-radius-md, .75rem);color:var(--td-color-primary, #5746d8);background:var(--td-color-primary-soft, #f0edff)}.td-radio__content{display:grid;min-width:0;gap:.15rem}.td-radio__label{color:var(--td-radio-text);font-size:.78rem;font-weight:780;line-height:1.35}.td-radio__description{color:var(--td-radio-muted);font-size:.68rem;line-height:1.4}.td-radio--disabled,.td-radio__option--disabled{cursor:not-allowed;opacity:.56}.td-radio--readonly .td-radio__option{cursor:default}.td-radio__message{margin:.45rem 0 0;color:var(--td-color-text-muted, #748096);font-size:.66rem;line-height:1.4}.td-radio__message--error{color:var(--td-color-danger, #c43636)}@media(max-width:700px){.td-radio--horizontal .td-radio__options{display:grid}}\n"], dependencies: [{ kind: "component", type: TdIcon, selector: "td-icon", inputs: ["nombre", "titulo", "tamano"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4660
+ }
4661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: TdRadioGroup, decorators: [{
4662
+ type: Component,
4663
+ args: [{ selector: 'td-radio-group', imports: [TdIcon], providers: [
4664
+ {
4665
+ provide: NG_VALUE_ACCESSOR,
4666
+ useExisting: forwardRef(() => TdRadioGroup),
4667
+ multi: true,
4668
+ },
4669
+ ], host: {
4670
+ '[hidden]': 'hidden()',
4671
+ '[attr.aria-busy]': 'pending() ? "true" : null',
4672
+ }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"td-radio\"\n [class.td-radio--horizontal]=\"direction === 'horizontal'\"\n [class.td-radio--card]=\"variant === 'card'\"\n [class.td-radio--disabled]=\"isDisabled()\"\n [class.td-radio--readonly]=\"readonly()\"\n [class.td-radio--error]=\"hasError()\"\n [class.td-radio--dark]=\"dark\"\n role=\"radiogroup\"\n [attr.aria-label]=\"label || null\"\n [attr.aria-required]=\"required()\"\n [attr.aria-invalid]=\"hasError()\"\n [attr.aria-describedby]=\"messageId\"\n>\n @if (label) {\n <span class=\"td-radio__legend\">{{ label }}</span>\n }\n\n <div class=\"td-radio__options\">\n @for (option of options; track option.value; let index = $index) {\n <label\n class=\"td-radio__option\"\n [class.td-radio__option--selected]=\"isSelected(option)\"\n [class.td-radio__option--disabled]=\"option.disabled || isDisabled()\"\n [for]=\"optionId(index)\"\n >\n <input\n [id]=\"optionId(index)\"\n type=\"radio\"\n [name]=\"groupName\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"isDisabled() || option.disabled\"\n [required]=\"required()\"\n [attr.aria-readonly]=\"readonly()\"\n [attr.aria-invalid]=\"hasError()\"\n (change)=\"select(option)\"\n (blur)=\"markTouched()\"\n (keydown)=\"handleKeydown($event, index)\"\n />\n\n <span class=\"td-radio__control\" aria-hidden=\"true\"></span>\n\n @if (option.icon) {\n <span class=\"td-radio__icon\">\n <td-icon [nombre]=\"option.icon\" />\n </span>\n }\n\n <span class=\"td-radio__content\">\n <span class=\"td-radio__label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"td-radio__description\">{{ option.description }}</span>\n }\n </span>\n </label>\n }\n </div>\n</div>\n\n@if (displayError() || hint) {\n <p\n class=\"td-radio__message\"\n [class.td-radio__message--error]=\"hasError()\"\n [id]=\"messageId\"\n >\n {{ displayError() || hint }}\n </p>\n}\n", styles: [":host{display:block;min-width:0;font-family:var(--td-font-family, Inter, ui-sans-serif, system-ui, sans-serif)}*{box-sizing:border-box}.td-radio{--td-radio-bg: var(--td-color-surface, #ffffff);--td-radio-border: var(--td-color-border-strong, #cfd5e1);--td-radio-text: var(--td-color-text, #263147);--td-radio-muted: var(--td-color-text-muted, #748096);color:var(--td-radio-text)}.td-radio--dark{--td-radio-bg: var(--td-color-surface, #19191f);--td-radio-border: var(--td-color-border-strong, #555563);--td-radio-text: var(--td-color-text, #ededf0);--td-radio-muted: var(--td-color-text-muted, #a1a1aa);color-scheme:dark}.td-radio__legend{display:inline-flex;margin-bottom:.65rem;color:var(--td-color-primary, #5746d8);font-size:.72rem;font-weight:850}.td-radio__options{display:grid;gap:.65rem}.td-radio--horizontal .td-radio__options{display:flex;flex-wrap:wrap}.td-radio__option{position:relative;display:inline-flex;max-width:100%;align-items:flex-start;gap:.72rem;color:var(--td-radio-text);cursor:pointer;-webkit-user-select:none;user-select:none}.td-radio--card .td-radio__option{align-items:center;border:1px solid var(--td-color-border, #dce3ef);border-radius:var(--td-radius-lg, 1rem);padding:.85rem;background:var(--td-radio-bg);box-shadow:var(--td-shadow-xs, 0 8px 24px rgba(38, 50, 72, .06));transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease,background .16s ease}.td-radio--card .td-radio__option:hover:not(.td-radio__option--disabled){transform:translateY(-1px);border-color:var(--td-color-primary, #5746d8);box-shadow:var(--td-shadow-md, 0 14px 34px rgba(38, 50, 72, .1))}input{position:absolute;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none}.td-radio__control{display:grid;width:1.22rem;height:1.22rem;flex:0 0 auto;place-items:center;margin-top:.05rem;border:1.6px solid var(--td-radio-border);border-radius:999px;background:var(--td-radio-bg);transition:border-color .14s ease,box-shadow .14s ease,background .14s ease,transform .1s ease}.td-radio__control:after{width:.5rem;height:.5rem;border-radius:999px;background:var(--td-color-on-primary, #ffffff);content:\"\";opacity:0;transform:scale(.35);transition:opacity .14s ease,transform .14s ease}.td-radio__option:hover:not(.td-radio__option--disabled) .td-radio__control{border-color:var(--td-color-primary, #5746d8)}.td-radio__option--selected .td-radio__control{border-color:var(--td-color-primary, #5746d8);background:var(--td-color-primary, #5746d8)}.td-radio__option--selected .td-radio__control:after{opacity:1;transform:scale(1)}input:focus-visible+.td-radio__control{border-color:var(--td-color-primary, #5746d8);box-shadow:var(--td-focus-ring, 0 0 0 3px rgba(87, 70, 216, .16))}input:active+.td-radio__control{transform:scale(.94)}.td-radio--error .td-radio__control{border-color:var(--td-color-danger, #d33f3f);box-shadow:var(--td-danger-ring, 0 0 0 3px color-mix(in srgb, var(--td-color-danger, #d33f3f) 16%, transparent))}.td-radio__icon{display:inline-grid;width:2rem;height:2rem;flex:0 0 auto;place-items:center;border-radius:var(--td-radius-md, .75rem);color:var(--td-color-primary, #5746d8);background:var(--td-color-primary-soft, #f0edff)}.td-radio__content{display:grid;min-width:0;gap:.15rem}.td-radio__label{color:var(--td-radio-text);font-size:.78rem;font-weight:780;line-height:1.35}.td-radio__description{color:var(--td-radio-muted);font-size:.68rem;line-height:1.4}.td-radio--disabled,.td-radio__option--disabled{cursor:not-allowed;opacity:.56}.td-radio--readonly .td-radio__option{cursor:default}.td-radio__message{margin:.45rem 0 0;color:var(--td-color-text-muted, #748096);font-size:.66rem;line-height:1.4}.td-radio__message--error{color:var(--td-color-danger, #c43636)}@media(max-width:700px){.td-radio--horizontal .td-radio__options{display:grid}}\n"] }]
4673
+ }], propDecorators: { options: [{
4674
+ type: Input
4675
+ }], label: [{
4676
+ type: Input
4677
+ }], hint: [{
4678
+ type: Input
4679
+ }], dark: [{
4680
+ type: Input
4681
+ }], direction: [{
4682
+ type: Input
4683
+ }], variant: [{
4684
+ type: Input
4685
+ }], compareWith: [{
4686
+ type: Input
4687
+ }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], disabledReasons: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledReasons", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], pending: [{ type: i0.Input, args: [{ isSignal: true, alias: "pending", required: false }] }], dirty: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirty", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], errorDisplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorDisplay", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], selectionChange: [{
4688
+ type: Output
4689
+ }] } });
4690
+
4152
4691
  let autocompleteSequence = 0;
4153
4692
  class TdAutocompleteSelect {
4154
4693
  cdr = inject(ChangeDetectorRef);
@@ -5973,5 +6512,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
5973
6512
  * Generated bundle index. Do not edit.
5974
6513
  */
5975
6514
 
5976
- export { TD_DIALOG_CONFIG, TD_DIALOG_DATA, TD_ICONOS, TD_ICONOS_NOMBRES, TD_THEME_DARK_COLORS, TD_THEME_LIGHT_COLORS, TdAlerta, TdAutocompleteSelect, TdButton, TdCheckbox, TdDataTable, TdDatePicker, TdDialog, TdDialogActions, TdDialogClose, TdDialogPrimary, TdDialogRef, TdFileViewer, TdFileViewerService, TdFooter, TdHeader, TdIcon, TdIconoRegistry, TdInput, TdMenu, TdSelect, TdSidebar, TdTab, TdTabs, TdTextarea, TdThemeService, TelcomdevUi, createTdFormControlBridge };
6515
+ export { TD_DIALOG_CONFIG, TD_DIALOG_DATA, TD_ICONOS, TD_ICONOS_NOMBRES, TD_THEME_DARK_COLORS, TD_THEME_LIGHT_COLORS, TdAlerta, TdAutocompleteSelect, TdButton, TdCheckbox, TdDataTable, TdDatePicker, TdDialog, TdDialogActions, TdDialogClose, TdDialogPrimary, TdDialogRef, TdFileUpload, TdFileViewer, TdFileViewerService, TdFooter, TdHeader, TdIcon, TdIconoRegistry, TdInput, TdMenu, TdRadioGroup, TdSelect, TdSidebar, TdTab, TdTabs, TdTextarea, TdThemeService, TelcomdevUi, createTdFormControlBridge };
5977
6516
  //# sourceMappingURL=telcomdev-ui.mjs.map