chrv-components 1.12.174 → 1.12.176

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.
Binary file
@@ -1134,7 +1134,11 @@ function getSyncValidators(rules) {
1134
1134
  validators.push(minFileSize(validation.value));
1135
1135
  }
1136
1136
  if (rule == 'maxFileNameLength') {
1137
- validation.display = `Le nom du fichier (ou son nom d'affichage) est trop long !`;
1137
+ validation.display = `Le nom du fichier est trop long !`;
1138
+ validators.push(maxFileNameLength(validation.value));
1139
+ }
1140
+ if (rule == 'minFileNameLength') {
1141
+ validation.display = `Le nom du fichier est trop long !`;
1138
1142
  validators.push(maxFileNameLength(validation.value));
1139
1143
  }
1140
1144
  }
@@ -1152,7 +1156,8 @@ const BaseErrorDisplays = {
1152
1156
  maxdate: `Cette date est trop grande !`,
1153
1157
  maxfilesize: `Ce fichier est trop grand !`,
1154
1158
  minfilesize: `Ce fichier est trop petit !`,
1155
- maxfilenamelength: `Le nom du fichier (ou son nom d'affichage) est trop long !`,
1159
+ maxfilenamelength: `Le nom du fichier est trop long !`,
1160
+ minfilenamelength: `Le nom du fichier est trop court !`,
1156
1161
  };
1157
1162
 
1158
1163
  class AutofocusDirective {
@@ -4854,11 +4859,11 @@ class ChrFileInputComponent extends ChrInputComponent {
4854
4859
  return crypto.randomUUID();
4855
4860
  }
4856
4861
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4857
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ChrFileInputComponent, isStandalone: true, selector: "chr-file-input", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, base64: { classPropertyName: "base64", publicName: "base64", isSignal: true, isRequired: false, transformFunction: null }, allowChangingFileName: { classPropertyName: "allowChangingFileName", publicName: "allowChangingFileName", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "dataTemplateRef", first: true, predicate: ["data"], descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "dataTemplateControls", predicate: NgControl, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "fileNativeInput", first: true, predicate: ["fileNativeInput"], descendants: true, read: ElementRef, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"file-input-wrapper\">\n <!-- Zone de Drop & S\u00E9lection (remplace visuellement l'input texte standard) -->\n <div class=\"drop-zone\" [hidden]=\"files().length>0\" [class.dragging]=\"isDragging()\" [class.disabled]=\"isDisabled()\"\n (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n\n <input #fileNativeInput #input type=\"file\" class=\"native-file-input\" [accept]=\"accept()\" [multiple]=\"multiple()\"\n (change)=\"onFileSelected($event)\" />\n\n <div class=\"drop-zone-content\">\n <span class=\"material-symbols upload-icon\">cloud_upload</span>\n <div class=\"drop-zone-text\">\n <span class=\"primary-text\">\n @if (label()) { {{ label() }} } @else { Cliquez ou glissez un fichier ici }\n </span>\n <span class=\"secondary-text\">\n {{ multiple() ? 'S\u00E9lectionnez un ou plusieurs fichiers' : 'S\u00E9lectionnez un fichier' }}\n </span>\n </div>\n </div>\n </div>\n\n\n <!-- Liste des fichiers s\u00E9lectionn\u00E9s sous forme de cartes/puces -->\n @if (files().length > 0) {\n <div class=\"file-list\">\n @for (entry of files(); track entry.id) {\n <div class=\"file-item\" [class.error]=\"entry.error || getFileValidationError($index)\">\n <span class=\"material-symbols file-icon\">{{ entry.error || getFileValidationError($index) ? 'error' :\n 'description' }}</span>\n <div class=\"file-info\">\n <div class=\"file-data\" [class.full-width]=\"dataTemplateRef() == undefined\">\n <div class=\"file-name\">\n @if(allowChangingFileName()){\n <chr-input [value]=\"entry.file.name\"\n (valueChange)=\"entry.file.name=$event?.trim()??entry.file.file!.name.split('.')[0];onChange(this.value())\"\n type=\"text\"></chr-input>\n }@else {\n <span class=\"file-name\">{{\n entry.file.name }}\n </span>\n }\n <span>.{{entry.file.extension}}</span>\n </div>\n <div class=\"file-name\">\n <span class=\"file-size\">{{ formatFileSize(entry.file.file?.size ?? 0) }}</span>\n <span class=\"file-content\">|</span>\n <span class=\"file-content\">{{ entry.file.contentType }}</span>\n </div>\n </div>\n @if(dataTemplateRef()){\n <div class=\"file-data\">\n <ng-container *ngTemplateOutlet=\"dataTemplateRef(); context: { $implicit: entry.file}\">\n </ng-container>\n </div>\n }\n <div class=\"file-data full-width\">\n @if (entry.error) {\n <span class=\"file-error\" role=\"alert\">{{ entry.error }}</span>\n } @else if (getFileValidationError($index); as validationError) {\n <span class=\"file-error\" role=\"alert\">{{ validationError }}</span>\n }\n </div>\n </div>\n <button type=\"button\" class=\"file-remove-btn\" (click)=\"removeFile($index, $event)\"\n [attr.aria-label]=\"'Supprimer ' + entry.file.file?.name\">\n <span class=\"material-symbols\">close</span>\n </button>\n <div class=\"file-progress\" [class.error]=\"entry.error || getFileValidationError($index)\" role=\"progressbar\"\n [attr.aria-label]=\"entry.error || getFileValidationError($index) || 'Progression de la conversion'\"\n [attr.aria-valuenow]=\"entry.progress\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <div class=\"file-progress-value\" [style.width.%]=\"entry.progress\"></div>\n </div>\n </div>\n }\n @if(multiple()){\n <button class=\"file-item fixed-size add-file-button\" (click)=\"triggerFileSelect()\">\n <span class=\"material-symbols\">add</span>\n </button>\n }\n </div>\n }\n</div>", styles: [":host{display:flex;flex-direction:column;flex:1 1 auto;justify-self:stretch;align-self:center;min-width:0}.file-input-wrapper{display:flex;flex-direction:column;gap:.75rem;width:100%}.native-file-input{display:none}.drop-zone{border:2px dashed var(--neutral-color);border-radius:.5rem;padding:1.25rem 1rem;background-color:var(--background-color);cursor:pointer;transition:all .2s ease-in-out}.drop-zone:hover:not(.disabled){border-color:var(--primary-color);background-color:var(--background-neutral-color)}.drop-zone.dragging{border-color:var(--tertiary-color);background-color:var(--background-neutral-color)}.drop-zone.disabled{opacity:.6;cursor:not-allowed}.drop-zone-content{display:flex;align-items:center;justify-content:center;gap:.75rem}.upload-icon{font-size:2rem;color:color-mix(in srgb,var(--primary-color) 80%,transparent 20%)}.drop-zone-text{display:flex;flex-direction:column}.primary-text{font-size:.875rem;font-weight:500;color:var(--text-color)}.secondary-text{font-size:.75rem;color:var(--text-neutral-color)}.file-list{display:flex;flex-direction:row;flex-wrap:wrap;gap:.375rem}.file-item{position:relative;display:flex;flex:1 1 auto;align-items:center;gap:.5rem;padding:.5rem .75rem;background-color:var(--background-color);border:1px solid var(--neutral-color);border-radius:.375rem;overflow:hidden}.file-item.fixed-size{flex:0 1 auto}.file-item.error{border-color:var(--error-color)}.file-icon{font-size:1.25rem;color:var(--text-neutral-color)}.file-info{display:grid;flex:1 1 auto;grid-template-columns:1fr 1fr}.file-data{grid-column:span 1;border-left:1px solid var(--neutral-color);padding-left:.5rem}.file-data.full-width{grid-column:span 2}.file-data:not(:last-of-type){padding-right:.5rem}.file-name{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;font-size:.8125rem;font-weight:500;white-space:nowrap}.file-size,.file-content{font-size:.6875rem;color:var(--text-neutral-color)}.file-content{margin-left:.25rem}.file-error{color:var(--error-color);font-size:.6875rem}.file-remove-btn{background:transparent;border:none;color:var(--text-neutral-color);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:.25rem;border-radius:.25rem;transition:color .15s,background-color .15s}.file-remove-btn:hover{color:var(--error-color);background-color:color-mix(in srgb,var(--error-color) 10%,transparent 90%)}.file-remove-btn .material-symbols{font-size:1rem}.file-progress{position:absolute;right:0;bottom:0;left:0;height:.2rem;background-color:color-mix(in srgb,var(--neutral-color) 55%,transparent 45%)}.file-progress-value{height:100%;background-color:var(--primary-color);transition:width .15s ease-out}.file-progress.error{background-color:color-mix(in srgb,var(--error-color) 20%,transparent 80%)}.file-progress.error .file-progress-value{background-color:var(--error-color)}.add-file-button{cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ChrInputComponent, selector: "chr-input", inputs: ["label", "id", "name", "value", "displayValue", "type", "outputType", "step", "debounceTime", "isDisabled", "isFocused", "min", "max", "minLength", "maxLength", "ngControl", "tooltip", "tooltipPosition"], outputs: ["idChange", "nameChange", "valueChange", "displayValueChange", "outputTypeChange", "isDisabledChange", "isFocusedChange", "ngControlChange", "input", "debouncedInput", "focus", "enter", "blur"] }] }); }
4862
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ChrFileInputComponent, isStandalone: true, selector: "chr-file-input", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, base64: { classPropertyName: "base64", publicName: "base64", isSignal: true, isRequired: false, transformFunction: null }, allowChangingFileName: { classPropertyName: "allowChangingFileName", publicName: "allowChangingFileName", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "dataTemplateRef", first: true, predicate: ["data"], descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "dataTemplateControls", predicate: NgControl, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "fileNativeInput", first: true, predicate: ["fileNativeInput"], descendants: true, read: ElementRef, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"file-input-wrapper\">\n <!-- Zone de Drop & S\u00E9lection (remplace visuellement l'input texte standard) -->\n <div class=\"drop-zone\" [hidden]=\"files().length>0\" [class.dragging]=\"isDragging()\" [class.disabled]=\"isDisabled()\"\n (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n\n <input #fileNativeInput #input type=\"file\" class=\"native-file-input\" [accept]=\"accept()\" [multiple]=\"multiple()\"\n (change)=\"onFileSelected($event)\" />\n\n <div class=\"drop-zone-content\">\n <span class=\"material-symbols upload-icon\">cloud_upload</span>\n <div class=\"drop-zone-text\">\n <span class=\"primary-text\">\n @if (label()) { {{ label() }} } @else { Cliquez ou glissez un fichier ici }\n </span>\n <span class=\"secondary-text\">\n {{ multiple() ? 'S\u00E9lectionnez un ou plusieurs fichiers' : 'S\u00E9lectionnez un fichier' }}\n </span>\n </div>\n </div>\n </div>\n\n\n <!-- Liste des fichiers s\u00E9lectionn\u00E9s sous forme de cartes/puces -->\n @if (files().length > 0) {\n <div class=\"file-list\">\n @for (entry of files(); track entry.id) {\n <div class=\"file-item\" [class.error]=\"entry.error || getFileValidationError($index)\">\n <span class=\"material-symbols file-icon\">{{ entry.error || getFileValidationError($index) ? 'error' :\n 'description' }}</span>\n <div class=\"file-info\">\n <div class=\"file-data\" [class.full-width]=\"dataTemplateRef() == undefined\">\n <div class=\"file-name\">\n @if(allowChangingFileName()){\n <chr-input [value]=\"entry.file.name\"\n (valueChange)=\"entry.file.name=$event?.trim()??entry.file.file!.name.split('.')[0];onChange(this.value())\"\n type=\"text\"></chr-input>\n }@else {\n <span class=\"file-name\">{{\n entry.file.name }}\n </span>\n }\n <span>.{{entry.file.extension}}</span>\n </div>\n <div class=\"file-name\">\n <span class=\"file-size\">{{ formatFileSize(entry.file.file?.size ?? 0) }}</span>\n <span class=\"file-content\">|</span>\n <span class=\"file-content\">{{ entry.file.contentType }}</span>\n </div>\n </div>\n @if(dataTemplateRef()){\n <div class=\"file-data\">\n <ng-container *ngTemplateOutlet=\"dataTemplateRef(); context: { $implicit: entry.file}\">\n </ng-container>\n </div>\n }\n <div class=\"file-data full-width\">\n @if (entry.error) {\n <span class=\"file-error\" role=\"alert\">{{ entry.error }}</span>\n } @else if (getFileValidationError($index); as validationError) {\n <span class=\"file-error\" role=\"alert\">{{ validationError }}</span>\n }\n </div>\n </div>\n <button type=\"button\" class=\"file-remove-btn\" (click)=\"removeFile($index, $event)\"\n [attr.aria-label]=\"'Supprimer ' + entry.file.file?.name\">\n <span class=\"material-symbols\">close</span>\n </button>\n <div class=\"file-progress\" [class.error]=\"entry.error || getFileValidationError($index)\" role=\"progressbar\"\n [attr.aria-label]=\"entry.error || getFileValidationError($index) || 'Progression de la conversion'\"\n [attr.aria-valuenow]=\"entry.progress\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <div class=\"file-progress-value\" [style.width.%]=\"entry.progress\"></div>\n </div>\n </div>\n }\n @if(multiple()){\n <button class=\"file-item fixed-size file-add-btn\" (click)=\"triggerFileSelect()\">\n <span class=\"material-symbols\">add</span>\n </button>\n }\n </div>\n }\n</div>", styles: [":host{display:flex;flex-direction:column;flex:1 1 auto;justify-self:stretch;align-self:center;min-width:0}.file-input-wrapper{display:flex;flex-direction:column;gap:.75rem;width:100%}.native-file-input{display:none}.drop-zone{border:2px dashed var(--neutral-color);border-radius:.5rem;padding:1.25rem 1rem;background-color:var(--background-color);cursor:pointer;transition:all .2s ease-in-out}.drop-zone:hover:not(.disabled){border-color:var(--primary-color);background-color:var(--background-neutral-color)}.drop-zone.dragging{border-color:var(--tertiary-color);background-color:var(--background-neutral-color)}.drop-zone.disabled{opacity:.6;cursor:not-allowed}.drop-zone-content{display:flex;align-items:center;justify-content:center;gap:.75rem}.upload-icon{font-size:2rem;color:color-mix(in srgb,var(--primary-color) 80%,transparent 20%)}.drop-zone-text{display:flex;flex-direction:column}.primary-text{font-size:.875rem;font-weight:500;color:var(--text-color)}.secondary-text{font-size:.75rem;color:var(--text-neutral-color)}.file-list{display:flex;flex-direction:row;flex-wrap:wrap;gap:.375rem}.file-item{position:relative;display:flex;flex:1 1 auto;align-items:center;gap:.5rem;padding:.5rem .75rem;background-color:var(--background-color);border:1px solid var(--neutral-color);border-radius:.375rem;overflow:hidden}.file-item.fixed-size{flex:0 1 auto}.file-item.error{border-color:var(--error-color)}.file-icon{font-size:1.25rem;color:var(--text-neutral-color)}.file-info{display:grid;flex:1 1 auto;grid-template-columns:1fr 1fr}.file-data{grid-column:span 1;border-left:1px solid var(--neutral-color);padding-left:.5rem}.file-data.full-width{grid-column:span 2}.file-data:not(:last-of-type){padding-right:.5rem}.file-name{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;font-size:.8125rem;font-weight:500;white-space:nowrap}.file-size,.file-content{font-size:.6875rem;color:var(--text-neutral-color)}.file-content{margin-left:.25rem}.file-error{color:var(--error-color);font-size:.6875rem}.file-remove-btn{background:transparent;border:none;color:var(--text-neutral-color);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:.25rem;border-radius:.25rem;transition:color .15s,background-color .15s}.file-remove-btn:hover{color:var(--error-color);background-color:color-mix(in srgb,var(--error-color) 10%,transparent 90%)}.file-remove-btn .material-symbols{font-size:1rem}.file-add-btn{color:var(--text-neutral-color);cursor:pointer}.file-add-btn:hover{color:var(--primary-color);background-color:color-mix(in srgb,var(--primary-color) 10%,transparent 90%)}.file-progress{position:absolute;right:0;bottom:0;left:0;height:.2rem;background-color:color-mix(in srgb,var(--neutral-color) 55%,transparent 45%)}.file-progress-value{height:100%;background-color:var(--primary-color);transition:width .15s ease-out}.file-progress.error{background-color:color-mix(in srgb,var(--error-color) 20%,transparent 80%)}.file-progress.error .file-progress-value{background-color:var(--error-color)}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ChrInputComponent, selector: "chr-input", inputs: ["label", "id", "name", "value", "displayValue", "type", "outputType", "step", "debounceTime", "isDisabled", "isFocused", "min", "max", "minLength", "maxLength", "ngControl", "tooltip", "tooltipPosition"], outputs: ["idChange", "nameChange", "valueChange", "displayValueChange", "outputTypeChange", "isDisabledChange", "isFocusedChange", "ngControlChange", "input", "debouncedInput", "focus", "enter", "blur"] }] }); }
4858
4863
  }
4859
4864
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrFileInputComponent, decorators: [{
4860
4865
  type: Component,
4861
- args: [{ selector: 'chr-file-input', imports: [ReactiveFormsModule, NgTemplateOutlet, ChrInputComponent], template: "<div class=\"file-input-wrapper\">\n <!-- Zone de Drop & S\u00E9lection (remplace visuellement l'input texte standard) -->\n <div class=\"drop-zone\" [hidden]=\"files().length>0\" [class.dragging]=\"isDragging()\" [class.disabled]=\"isDisabled()\"\n (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n\n <input #fileNativeInput #input type=\"file\" class=\"native-file-input\" [accept]=\"accept()\" [multiple]=\"multiple()\"\n (change)=\"onFileSelected($event)\" />\n\n <div class=\"drop-zone-content\">\n <span class=\"material-symbols upload-icon\">cloud_upload</span>\n <div class=\"drop-zone-text\">\n <span class=\"primary-text\">\n @if (label()) { {{ label() }} } @else { Cliquez ou glissez un fichier ici }\n </span>\n <span class=\"secondary-text\">\n {{ multiple() ? 'S\u00E9lectionnez un ou plusieurs fichiers' : 'S\u00E9lectionnez un fichier' }}\n </span>\n </div>\n </div>\n </div>\n\n\n <!-- Liste des fichiers s\u00E9lectionn\u00E9s sous forme de cartes/puces -->\n @if (files().length > 0) {\n <div class=\"file-list\">\n @for (entry of files(); track entry.id) {\n <div class=\"file-item\" [class.error]=\"entry.error || getFileValidationError($index)\">\n <span class=\"material-symbols file-icon\">{{ entry.error || getFileValidationError($index) ? 'error' :\n 'description' }}</span>\n <div class=\"file-info\">\n <div class=\"file-data\" [class.full-width]=\"dataTemplateRef() == undefined\">\n <div class=\"file-name\">\n @if(allowChangingFileName()){\n <chr-input [value]=\"entry.file.name\"\n (valueChange)=\"entry.file.name=$event?.trim()??entry.file.file!.name.split('.')[0];onChange(this.value())\"\n type=\"text\"></chr-input>\n }@else {\n <span class=\"file-name\">{{\n entry.file.name }}\n </span>\n }\n <span>.{{entry.file.extension}}</span>\n </div>\n <div class=\"file-name\">\n <span class=\"file-size\">{{ formatFileSize(entry.file.file?.size ?? 0) }}</span>\n <span class=\"file-content\">|</span>\n <span class=\"file-content\">{{ entry.file.contentType }}</span>\n </div>\n </div>\n @if(dataTemplateRef()){\n <div class=\"file-data\">\n <ng-container *ngTemplateOutlet=\"dataTemplateRef(); context: { $implicit: entry.file}\">\n </ng-container>\n </div>\n }\n <div class=\"file-data full-width\">\n @if (entry.error) {\n <span class=\"file-error\" role=\"alert\">{{ entry.error }}</span>\n } @else if (getFileValidationError($index); as validationError) {\n <span class=\"file-error\" role=\"alert\">{{ validationError }}</span>\n }\n </div>\n </div>\n <button type=\"button\" class=\"file-remove-btn\" (click)=\"removeFile($index, $event)\"\n [attr.aria-label]=\"'Supprimer ' + entry.file.file?.name\">\n <span class=\"material-symbols\">close</span>\n </button>\n <div class=\"file-progress\" [class.error]=\"entry.error || getFileValidationError($index)\" role=\"progressbar\"\n [attr.aria-label]=\"entry.error || getFileValidationError($index) || 'Progression de la conversion'\"\n [attr.aria-valuenow]=\"entry.progress\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <div class=\"file-progress-value\" [style.width.%]=\"entry.progress\"></div>\n </div>\n </div>\n }\n @if(multiple()){\n <button class=\"file-item fixed-size add-file-button\" (click)=\"triggerFileSelect()\">\n <span class=\"material-symbols\">add</span>\n </button>\n }\n </div>\n }\n</div>", styles: [":host{display:flex;flex-direction:column;flex:1 1 auto;justify-self:stretch;align-self:center;min-width:0}.file-input-wrapper{display:flex;flex-direction:column;gap:.75rem;width:100%}.native-file-input{display:none}.drop-zone{border:2px dashed var(--neutral-color);border-radius:.5rem;padding:1.25rem 1rem;background-color:var(--background-color);cursor:pointer;transition:all .2s ease-in-out}.drop-zone:hover:not(.disabled){border-color:var(--primary-color);background-color:var(--background-neutral-color)}.drop-zone.dragging{border-color:var(--tertiary-color);background-color:var(--background-neutral-color)}.drop-zone.disabled{opacity:.6;cursor:not-allowed}.drop-zone-content{display:flex;align-items:center;justify-content:center;gap:.75rem}.upload-icon{font-size:2rem;color:color-mix(in srgb,var(--primary-color) 80%,transparent 20%)}.drop-zone-text{display:flex;flex-direction:column}.primary-text{font-size:.875rem;font-weight:500;color:var(--text-color)}.secondary-text{font-size:.75rem;color:var(--text-neutral-color)}.file-list{display:flex;flex-direction:row;flex-wrap:wrap;gap:.375rem}.file-item{position:relative;display:flex;flex:1 1 auto;align-items:center;gap:.5rem;padding:.5rem .75rem;background-color:var(--background-color);border:1px solid var(--neutral-color);border-radius:.375rem;overflow:hidden}.file-item.fixed-size{flex:0 1 auto}.file-item.error{border-color:var(--error-color)}.file-icon{font-size:1.25rem;color:var(--text-neutral-color)}.file-info{display:grid;flex:1 1 auto;grid-template-columns:1fr 1fr}.file-data{grid-column:span 1;border-left:1px solid var(--neutral-color);padding-left:.5rem}.file-data.full-width{grid-column:span 2}.file-data:not(:last-of-type){padding-right:.5rem}.file-name{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;font-size:.8125rem;font-weight:500;white-space:nowrap}.file-size,.file-content{font-size:.6875rem;color:var(--text-neutral-color)}.file-content{margin-left:.25rem}.file-error{color:var(--error-color);font-size:.6875rem}.file-remove-btn{background:transparent;border:none;color:var(--text-neutral-color);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:.25rem;border-radius:.25rem;transition:color .15s,background-color .15s}.file-remove-btn:hover{color:var(--error-color);background-color:color-mix(in srgb,var(--error-color) 10%,transparent 90%)}.file-remove-btn .material-symbols{font-size:1rem}.file-progress{position:absolute;right:0;bottom:0;left:0;height:.2rem;background-color:color-mix(in srgb,var(--neutral-color) 55%,transparent 45%)}.file-progress-value{height:100%;background-color:var(--primary-color);transition:width .15s ease-out}.file-progress.error{background-color:color-mix(in srgb,var(--error-color) 20%,transparent 80%)}.file-progress.error .file-progress-value{background-color:var(--error-color)}.add-file-button{cursor:pointer}\n"] }]
4866
+ args: [{ selector: 'chr-file-input', imports: [ReactiveFormsModule, NgTemplateOutlet, ChrInputComponent], template: "<div class=\"file-input-wrapper\">\n <!-- Zone de Drop & S\u00E9lection (remplace visuellement l'input texte standard) -->\n <div class=\"drop-zone\" [hidden]=\"files().length>0\" [class.dragging]=\"isDragging()\" [class.disabled]=\"isDisabled()\"\n (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n\n <input #fileNativeInput #input type=\"file\" class=\"native-file-input\" [accept]=\"accept()\" [multiple]=\"multiple()\"\n (change)=\"onFileSelected($event)\" />\n\n <div class=\"drop-zone-content\">\n <span class=\"material-symbols upload-icon\">cloud_upload</span>\n <div class=\"drop-zone-text\">\n <span class=\"primary-text\">\n @if (label()) { {{ label() }} } @else { Cliquez ou glissez un fichier ici }\n </span>\n <span class=\"secondary-text\">\n {{ multiple() ? 'S\u00E9lectionnez un ou plusieurs fichiers' : 'S\u00E9lectionnez un fichier' }}\n </span>\n </div>\n </div>\n </div>\n\n\n <!-- Liste des fichiers s\u00E9lectionn\u00E9s sous forme de cartes/puces -->\n @if (files().length > 0) {\n <div class=\"file-list\">\n @for (entry of files(); track entry.id) {\n <div class=\"file-item\" [class.error]=\"entry.error || getFileValidationError($index)\">\n <span class=\"material-symbols file-icon\">{{ entry.error || getFileValidationError($index) ? 'error' :\n 'description' }}</span>\n <div class=\"file-info\">\n <div class=\"file-data\" [class.full-width]=\"dataTemplateRef() == undefined\">\n <div class=\"file-name\">\n @if(allowChangingFileName()){\n <chr-input [value]=\"entry.file.name\"\n (valueChange)=\"entry.file.name=$event?.trim()??entry.file.file!.name.split('.')[0];onChange(this.value())\"\n type=\"text\"></chr-input>\n }@else {\n <span class=\"file-name\">{{\n entry.file.name }}\n </span>\n }\n <span>.{{entry.file.extension}}</span>\n </div>\n <div class=\"file-name\">\n <span class=\"file-size\">{{ formatFileSize(entry.file.file?.size ?? 0) }}</span>\n <span class=\"file-content\">|</span>\n <span class=\"file-content\">{{ entry.file.contentType }}</span>\n </div>\n </div>\n @if(dataTemplateRef()){\n <div class=\"file-data\">\n <ng-container *ngTemplateOutlet=\"dataTemplateRef(); context: { $implicit: entry.file}\">\n </ng-container>\n </div>\n }\n <div class=\"file-data full-width\">\n @if (entry.error) {\n <span class=\"file-error\" role=\"alert\">{{ entry.error }}</span>\n } @else if (getFileValidationError($index); as validationError) {\n <span class=\"file-error\" role=\"alert\">{{ validationError }}</span>\n }\n </div>\n </div>\n <button type=\"button\" class=\"file-remove-btn\" (click)=\"removeFile($index, $event)\"\n [attr.aria-label]=\"'Supprimer ' + entry.file.file?.name\">\n <span class=\"material-symbols\">close</span>\n </button>\n <div class=\"file-progress\" [class.error]=\"entry.error || getFileValidationError($index)\" role=\"progressbar\"\n [attr.aria-label]=\"entry.error || getFileValidationError($index) || 'Progression de la conversion'\"\n [attr.aria-valuenow]=\"entry.progress\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <div class=\"file-progress-value\" [style.width.%]=\"entry.progress\"></div>\n </div>\n </div>\n }\n @if(multiple()){\n <button class=\"file-item fixed-size file-add-btn\" (click)=\"triggerFileSelect()\">\n <span class=\"material-symbols\">add</span>\n </button>\n }\n </div>\n }\n</div>", styles: [":host{display:flex;flex-direction:column;flex:1 1 auto;justify-self:stretch;align-self:center;min-width:0}.file-input-wrapper{display:flex;flex-direction:column;gap:.75rem;width:100%}.native-file-input{display:none}.drop-zone{border:2px dashed var(--neutral-color);border-radius:.5rem;padding:1.25rem 1rem;background-color:var(--background-color);cursor:pointer;transition:all .2s ease-in-out}.drop-zone:hover:not(.disabled){border-color:var(--primary-color);background-color:var(--background-neutral-color)}.drop-zone.dragging{border-color:var(--tertiary-color);background-color:var(--background-neutral-color)}.drop-zone.disabled{opacity:.6;cursor:not-allowed}.drop-zone-content{display:flex;align-items:center;justify-content:center;gap:.75rem}.upload-icon{font-size:2rem;color:color-mix(in srgb,var(--primary-color) 80%,transparent 20%)}.drop-zone-text{display:flex;flex-direction:column}.primary-text{font-size:.875rem;font-weight:500;color:var(--text-color)}.secondary-text{font-size:.75rem;color:var(--text-neutral-color)}.file-list{display:flex;flex-direction:row;flex-wrap:wrap;gap:.375rem}.file-item{position:relative;display:flex;flex:1 1 auto;align-items:center;gap:.5rem;padding:.5rem .75rem;background-color:var(--background-color);border:1px solid var(--neutral-color);border-radius:.375rem;overflow:hidden}.file-item.fixed-size{flex:0 1 auto}.file-item.error{border-color:var(--error-color)}.file-icon{font-size:1.25rem;color:var(--text-neutral-color)}.file-info{display:grid;flex:1 1 auto;grid-template-columns:1fr 1fr}.file-data{grid-column:span 1;border-left:1px solid var(--neutral-color);padding-left:.5rem}.file-data.full-width{grid-column:span 2}.file-data:not(:last-of-type){padding-right:.5rem}.file-name{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;font-size:.8125rem;font-weight:500;white-space:nowrap}.file-size,.file-content{font-size:.6875rem;color:var(--text-neutral-color)}.file-content{margin-left:.25rem}.file-error{color:var(--error-color);font-size:.6875rem}.file-remove-btn{background:transparent;border:none;color:var(--text-neutral-color);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:.25rem;border-radius:.25rem;transition:color .15s,background-color .15s}.file-remove-btn:hover{color:var(--error-color);background-color:color-mix(in srgb,var(--error-color) 10%,transparent 90%)}.file-remove-btn .material-symbols{font-size:1rem}.file-add-btn{color:var(--text-neutral-color);cursor:pointer}.file-add-btn:hover{color:var(--primary-color);background-color:color-mix(in srgb,var(--primary-color) 10%,transparent 90%)}.file-progress{position:absolute;right:0;bottom:0;left:0;height:.2rem;background-color:color-mix(in srgb,var(--neutral-color) 55%,transparent 45%)}.file-progress-value{height:100%;background-color:var(--primary-color);transition:width .15s ease-out}.file-progress.error{background-color:color-mix(in srgb,var(--error-color) 20%,transparent 80%)}.file-progress.error .file-progress-value{background-color:var(--error-color)}\n"] }]
4862
4867
  }], ctorParameters: () => [], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], base64: [{ type: i0.Input, args: [{ isSignal: true, alias: "base64", required: false }] }], allowChangingFileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowChangingFileName", required: false }] }], fileNativeInput: [{ type: i0.ViewChild, args: ['fileNativeInput', { ...{
4863
4868
  read: (ElementRef),
4864
4869
  }, isSignal: true }] }], dataTemplateRef: [{ type: i0.ContentChild, args: ['data', { ...{ read: TemplateRef }, isSignal: true }] }], dataTemplateControls: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NgControl), { ...{
@@ -8853,6 +8858,79 @@ class AnonymousTable extends BaseTable {
8853
8858
  }
8854
8859
  }
8855
8860
 
8861
+ class MinFileNameLengthValidator {
8862
+ constructor() {
8863
+ this.max = input(0, /* @ts-ignore */
8864
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
8865
+ }
8866
+ validate(control) {
8867
+ const max = this.max();
8868
+ return max ? minFileNameLength(max)(control) : null;
8869
+ }
8870
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MinFileNameLengthValidator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8871
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: MinFileNameLengthValidator, isStandalone: true, selector: "[app-max-file-size]", inputs: { max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
8872
+ {
8873
+ provide: NG_VALIDATORS,
8874
+ useExisting: MinFileNameLengthValidator,
8875
+ multi: true,
8876
+ },
8877
+ ], ngImport: i0 }); }
8878
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MinFileNameLengthValidator, providedIn: 'root' }); }
8879
+ }
8880
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MinFileNameLengthValidator, decorators: [{
8881
+ type: Injectable,
8882
+ args: [{ providedIn: 'root' }]
8883
+ }, {
8884
+ type: Directive,
8885
+ args: [{
8886
+ selector: '[app-max-file-size]',
8887
+ providers: [
8888
+ {
8889
+ provide: NG_VALIDATORS,
8890
+ useExisting: MinFileNameLengthValidator,
8891
+ multi: true,
8892
+ },
8893
+ ],
8894
+ standalone: true,
8895
+ }]
8896
+ }], propDecorators: { max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }] } });
8897
+ function minFileNameLength(maxLength) {
8898
+ return (control) => {
8899
+ const value = control.value;
8900
+ const errors = {};
8901
+ let isSizeCompliant = true;
8902
+ let fileSize = null;
8903
+ if (value === null)
8904
+ return null;
8905
+ if (Array.isArray(value)) {
8906
+ for (let i = 0; i < value.length; i++) {
8907
+ const fileNameLength = value[i].name?.length;
8908
+ if (fileNameLength === undefined)
8909
+ continue;
8910
+ if (fileNameLength <= maxLength) {
8911
+ errors[i] = true;
8912
+ isSizeCompliant = false;
8913
+ fileSize = value[i].name.length;
8914
+ }
8915
+ }
8916
+ }
8917
+ else {
8918
+ isSizeCompliant =
8919
+ value?.name?.length === undefined || value.name.length >= maxLength;
8920
+ }
8921
+ return isSizeCompliant == false
8922
+ ? {
8923
+ minFileNameLength: {
8924
+ maxFileNameLength: maxLength,
8925
+ actual: fileSize,
8926
+ indexes: errors,
8927
+ },
8928
+ indexes: errors,
8929
+ }
8930
+ : null;
8931
+ };
8932
+ }
8933
+
8856
8934
  class AutoDataTable extends BaseTable {
8857
8935
  get columnMetadatas() {
8858
8936
  return this._columnMetadatas.sort((a, b) => a.ordinalPosition - b.ordinalPosition);
@@ -9341,5 +9419,5 @@ const provideDebounceEventPlugin = () => [
9341
9419
  * Generated bundle index. Do not edit.
9342
9420
  */
9343
9421
 
9344
- export { Aligments, AnonymousTable, AutoDataSource, AutoDataTable, AutofocusDirective, BaseErrorDisplays, BaseTable, BreadcrumbComponent, ButtonComponent, CHR_DEACTIVATION_MODAL, CHR_DEBOUNCE_DEFAULTS, CHR_MODAL_DATA, CHR_MODAL_REF, CacheService, CachingInterceptor, CarouselComponent, ChrBaseInputComponent, ChrButtonComponent, ChrButtonLegacyComponent, ChrCheckboxComponent, ChrColorInputComponent, ChrContextMenuComponent, ChrDataTable, ChrDateInputComponent, ChrDatetimeInputComponent, ChrDeactivationGuard, ChrDebounceDirective, ChrDefaultModalComponent, ChrDeleteModalComponent, ChrDropdownComponent, ChrFile, ChrFileInputComponent, ChrFormComponent, ChrHoverDirective, ChrHoverTitleComponent, ChrHoverTitleDirective, ChrInputComponent, ChrInputData, ChrInputDataOverlay, ChrInputIndicators, ChrLegacyFileInputComponent, ChrModalComponent, ChrNiceFileInputComponent, ChrPaginatorComponent, ChrPasswordInputComponent, ChrPopoverDirective, ChrPreventReloadDirective, ChrSearchSelectComponent, ChrSearchSelectInputComponent, ChrSearchbarComponent, ChrSeparatorComponent, ChrSpinnerComponent, ChrTableComponent, ChrTableHeaderCellComponent, ChrTagSelectComponent, ChrTagSelectInputComponent, ChrTextAreaInputComponent, ChrTextareaInputComponent, ChrToastComponent, ChrToggleInputComponent, Colors, ColorsVariables, ColumnFilter, ColumnGroup, ColumnMetadata, ContextMenuDirective, ControlClickDirective, CookiesService, CrossCellNavigationDirective, DEFAULTLIVEUPDATEMESSAGE, DEFAULT_TOAST_CONFIG, DGFilterMode, DGGroupAggregationEnum, DataExporterService, DataFormatterService, DataGrid, DataListComponent, DataService, DebounceDefaults, DebounceEventPlugin, DecimalValidatorDirective, DefaultLiveUpdateMessage, DropdownContent, DropdownTitle, ENABLE_CACHE, ENABLE_PROGRESS, EditableCell, FileService, HUBURL, INVALIDATE_CACHE, InlineSvgComponent, LegacyToastService, LiveUpdateService, LiveUpdateStatus, LoaderService, MaxDateValidatorDirective, MaxFileNameLengthValidator, MaxFileSizeValidator, MaxLengthValidatorDirective, MessageBanner, MinFileSizeValidator, MinLengthValidatorDirective, ModalRef, ModalService, OldSpinnerInterceptor, OutsideClickAwareDirective, ProgressInterceptor, ProgressService, RequiredValidatorDirective, SHOW_SPINNER, SKIP_SPINNER, ScrollIntoViewDirective, Spinner, SpinnerInterceptor, SpinnerService, SyncValidatorToAsync, TabComponent, TabGroupComponent, TabToEnterHandlerDirective, TabToInputHandlerDirective, TableConstraint, TableConstraintColumn, TableConstraintReference, ToastComponent, ToastDefaults, ToastRef, ToastService, TypeValidatorDirective, WaitAndStoreXsrfToken, XSRFCOOKIENAME, XSRFHEADERNAME, XsrfInterceptor, decimal, getAsyncValidators, getBackgroundColor, getBorderColor, getContrastBackgroundColor, getContrastBorderColor, getContrastTextColor, getSyncValidators, getTextColor, getValidators, getVariableColor, getVariableContrastColor, maxDate, maxFileNameLength, maxFileSize, maxLength, minFileSize, minLength, provideDebounceEventPlugin, provideXsrfHttpClient, required, type };
9422
+ export { Aligments, AnonymousTable, AutoDataSource, AutoDataTable, AutofocusDirective, BaseErrorDisplays, BaseTable, BreadcrumbComponent, ButtonComponent, CHR_DEACTIVATION_MODAL, CHR_DEBOUNCE_DEFAULTS, CHR_MODAL_DATA, CHR_MODAL_REF, CacheService, CachingInterceptor, CarouselComponent, ChrBaseInputComponent, ChrButtonComponent, ChrButtonLegacyComponent, ChrCheckboxComponent, ChrColorInputComponent, ChrContextMenuComponent, ChrDataTable, ChrDateInputComponent, ChrDatetimeInputComponent, ChrDeactivationGuard, ChrDebounceDirective, ChrDefaultModalComponent, ChrDeleteModalComponent, ChrDropdownComponent, ChrFile, ChrFileInputComponent, ChrFormComponent, ChrHoverDirective, ChrHoverTitleComponent, ChrHoverTitleDirective, ChrInputComponent, ChrInputData, ChrInputDataOverlay, ChrInputIndicators, ChrLegacyFileInputComponent, ChrModalComponent, ChrNiceFileInputComponent, ChrPaginatorComponent, ChrPasswordInputComponent, ChrPopoverDirective, ChrPreventReloadDirective, ChrSearchSelectComponent, ChrSearchSelectInputComponent, ChrSearchbarComponent, ChrSeparatorComponent, ChrSpinnerComponent, ChrTableComponent, ChrTableHeaderCellComponent, ChrTagSelectComponent, ChrTagSelectInputComponent, ChrTextAreaInputComponent, ChrTextareaInputComponent, ChrToastComponent, ChrToggleInputComponent, Colors, ColorsVariables, ColumnFilter, ColumnGroup, ColumnMetadata, ContextMenuDirective, ControlClickDirective, CookiesService, CrossCellNavigationDirective, DEFAULTLIVEUPDATEMESSAGE, DEFAULT_TOAST_CONFIG, DGFilterMode, DGGroupAggregationEnum, DataExporterService, DataFormatterService, DataGrid, DataListComponent, DataService, DebounceDefaults, DebounceEventPlugin, DecimalValidatorDirective, DefaultLiveUpdateMessage, DropdownContent, DropdownTitle, ENABLE_CACHE, ENABLE_PROGRESS, EditableCell, FileService, HUBURL, INVALIDATE_CACHE, InlineSvgComponent, LegacyToastService, LiveUpdateService, LiveUpdateStatus, LoaderService, MaxDateValidatorDirective, MaxFileNameLengthValidator, MaxFileSizeValidator, MaxLengthValidatorDirective, MessageBanner, MinFileNameLengthValidator, MinFileSizeValidator, MinLengthValidatorDirective, ModalRef, ModalService, OldSpinnerInterceptor, OutsideClickAwareDirective, ProgressInterceptor, ProgressService, RequiredValidatorDirective, SHOW_SPINNER, SKIP_SPINNER, ScrollIntoViewDirective, Spinner, SpinnerInterceptor, SpinnerService, SyncValidatorToAsync, TabComponent, TabGroupComponent, TabToEnterHandlerDirective, TabToInputHandlerDirective, TableConstraint, TableConstraintColumn, TableConstraintReference, ToastComponent, ToastDefaults, ToastRef, ToastService, TypeValidatorDirective, WaitAndStoreXsrfToken, XSRFCOOKIENAME, XSRFHEADERNAME, XsrfInterceptor, decimal, getAsyncValidators, getBackgroundColor, getBorderColor, getContrastBackgroundColor, getContrastBorderColor, getContrastTextColor, getSyncValidators, getTextColor, getValidators, getVariableColor, getVariableContrastColor, maxDate, maxFileNameLength, maxFileSize, maxLength, minFileNameLength, minFileSize, minLength, provideDebounceEventPlugin, provideXsrfHttpClient, required, type };
9345
9423
  //# sourceMappingURL=chrv-components.mjs.map