chrv-components 1.12.161 → 1.12.162
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
|
|
@@ -4501,9 +4501,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
4501
4501
|
args: [{ selector: 'chr-input-data-overlay', template: '<ng-content></ng-content>', styles: [":host{display:flex;flex-direction:column;align-items:start;justify-content:center}\n"] }]
|
|
4502
4502
|
}] });
|
|
4503
4503
|
|
|
4504
|
+
/**
|
|
4505
|
+
* Custom File Input. Support multiple files, base64 encoding and per-file validation.
|
|
4506
|
+
* Disclaimer: this is an AI created input.
|
|
4507
|
+
*/
|
|
4504
4508
|
class ChrFileInputComponent extends ChrInputComponent {
|
|
4505
4509
|
constructor() {
|
|
4506
4510
|
super();
|
|
4511
|
+
this.fileService = inject(FileService);
|
|
4512
|
+
this.nextFileId = 0;
|
|
4507
4513
|
/**
|
|
4508
4514
|
* Types de fichiers autorisés (ex: "image/*,.pdf")
|
|
4509
4515
|
*/
|
|
@@ -4514,24 +4520,31 @@ class ChrFileInputComponent extends ChrInputComponent {
|
|
|
4514
4520
|
*/
|
|
4515
4521
|
this.multiple = input(false, /* @ts-ignore */
|
|
4516
4522
|
...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
|
|
4523
|
+
/**
|
|
4524
|
+
* Convert selected files to data URLs before updating the form value
|
|
4525
|
+
*/
|
|
4526
|
+
this.base64 = input(false, /* @ts-ignore */
|
|
4527
|
+
...(ngDevMode ? [{ debugName: "base64" }] : /* istanbul ignore next */ []));
|
|
4517
4528
|
/**
|
|
4518
4529
|
* État du glisser-déposer (Drag & Drop)
|
|
4519
4530
|
*/
|
|
4520
4531
|
this.isDragging = signal(false, /* @ts-ignore */
|
|
4521
4532
|
...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
|
|
4533
|
+
this.fileEntries = signal([], /* @ts-ignore */
|
|
4534
|
+
...(ngDevMode ? [{ debugName: "fileEntries" }] : /* istanbul ignore next */ []));
|
|
4535
|
+
this.validationRevision = signal(0, /* @ts-ignore */
|
|
4536
|
+
...(ngDevMode ? [{ debugName: "validationRevision" }] : /* istanbul ignore next */ []));
|
|
4522
4537
|
/**
|
|
4523
4538
|
* Signal calculé pour garantir la manipulation d'un tableau de fichiers
|
|
4524
4539
|
*/
|
|
4525
|
-
this.files = computed(() =>
|
|
4526
|
-
const val = this.value();
|
|
4527
|
-
if (!val)
|
|
4528
|
-
return [];
|
|
4529
|
-
return Array.isArray(val) ? val : [val];
|
|
4530
|
-
}, /* @ts-ignore */
|
|
4540
|
+
this.files = computed(() => this.fileEntries(), /* @ts-ignore */
|
|
4531
4541
|
...(ngDevMode ? [{ debugName: "files" }] : /* istanbul ignore next */ []));
|
|
4532
4542
|
}
|
|
4533
4543
|
ngOnInit() {
|
|
4534
4544
|
super.ngOnInit();
|
|
4545
|
+
this.ngControl()?.control?.statusChanges.subscribe(() => {
|
|
4546
|
+
this.validationRevision.update((revision) => revision + 1);
|
|
4547
|
+
});
|
|
4535
4548
|
// Initialisation du modèle selon le mode unique/multiple
|
|
4536
4549
|
if (this.multiple() && !Array.isArray(this.value())) {
|
|
4537
4550
|
this.value.set([]);
|
|
@@ -4585,18 +4598,65 @@ class ChrFileInputComponent extends ChrInputComponent {
|
|
|
4585
4598
|
* Ajoute les fichiers et met à jour le Form Value
|
|
4586
4599
|
*/
|
|
4587
4600
|
addFiles(newFiles) {
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
this.
|
|
4601
|
+
const currentEntries = this.files();
|
|
4602
|
+
const filteredNewFiles = this.multiple()
|
|
4603
|
+
? newFiles.filter((newFile) => !currentEntries.some((entry) => entry.file.file?.name === newFile.name &&
|
|
4604
|
+
entry.file.file?.size === newFile.size))
|
|
4605
|
+
: newFiles.slice(0, 1);
|
|
4606
|
+
const newEntries = filteredNewFiles.map((file) => ({
|
|
4607
|
+
id: this.nextFileId++,
|
|
4608
|
+
file: new ChrFile({ file }),
|
|
4609
|
+
progress: this.isAcceptedFile(file) && this.base64() ? 0 : 100,
|
|
4610
|
+
error: this.isAcceptedFile(file)
|
|
4611
|
+
? undefined
|
|
4612
|
+
: 'Type de fichier non autorisé',
|
|
4613
|
+
}));
|
|
4614
|
+
const updatedEntries = this.multiple()
|
|
4615
|
+
? [...currentEntries, ...newEntries]
|
|
4616
|
+
: newEntries;
|
|
4617
|
+
this.fileEntries.set(updatedEntries);
|
|
4618
|
+
const filesToConvert = newEntries.filter((entry) => !entry.error);
|
|
4619
|
+
if (!this.base64() || filesToConvert.length === 0) {
|
|
4620
|
+
this.updateValue(updatedEntries);
|
|
4621
|
+
return;
|
|
4595
4622
|
}
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
this.
|
|
4623
|
+
let completedConversions = 0;
|
|
4624
|
+
let conversionFailed = false;
|
|
4625
|
+
for (const entry of filesToConvert) {
|
|
4626
|
+
this.fileService.toBase64WithProgress(entry.file).subscribe({
|
|
4627
|
+
next: (result) => {
|
|
4628
|
+
const updatedEntries = this.files().map((currentEntry) => currentEntry.id === entry.id
|
|
4629
|
+
? {
|
|
4630
|
+
...currentEntry,
|
|
4631
|
+
file: result.file ?? currentEntry.file,
|
|
4632
|
+
progress: result.progress,
|
|
4633
|
+
}
|
|
4634
|
+
: currentEntry);
|
|
4635
|
+
this.fileEntries.set(updatedEntries);
|
|
4636
|
+
},
|
|
4637
|
+
error: () => {
|
|
4638
|
+
conversionFailed = true;
|
|
4639
|
+
this.fileEntries.update((entries) => entries.map((currentEntry) => currentEntry.id === entry.id
|
|
4640
|
+
? {
|
|
4641
|
+
...currentEntry,
|
|
4642
|
+
progress: 0,
|
|
4643
|
+
error: 'La conversion du fichier a échoué',
|
|
4644
|
+
}
|
|
4645
|
+
: currentEntry));
|
|
4646
|
+
completedConversions++;
|
|
4647
|
+
if (completedConversions === filesToConvert.length &&
|
|
4648
|
+
!conversionFailed) {
|
|
4649
|
+
this.updateValue(this.files());
|
|
4650
|
+
}
|
|
4651
|
+
},
|
|
4652
|
+
complete: () => {
|
|
4653
|
+
completedConversions++;
|
|
4654
|
+
if (completedConversions === filesToConvert.length &&
|
|
4655
|
+
!conversionFailed) {
|
|
4656
|
+
this.updateValue(this.files());
|
|
4657
|
+
}
|
|
4658
|
+
},
|
|
4659
|
+
});
|
|
4600
4660
|
}
|
|
4601
4661
|
}
|
|
4602
4662
|
/**
|
|
@@ -4607,14 +4667,58 @@ class ChrFileInputComponent extends ChrInputComponent {
|
|
|
4607
4667
|
if (this.multiple()) {
|
|
4608
4668
|
const updatedList = [...this.files()];
|
|
4609
4669
|
updatedList.splice(index, 1);
|
|
4610
|
-
this.
|
|
4611
|
-
this.
|
|
4670
|
+
this.fileEntries.set(updatedList);
|
|
4671
|
+
this.updateValue(updatedList);
|
|
4612
4672
|
}
|
|
4613
4673
|
else {
|
|
4674
|
+
this.fileEntries.set([]);
|
|
4614
4675
|
this.value.set(null);
|
|
4615
4676
|
this.onChange(null);
|
|
4616
4677
|
}
|
|
4617
4678
|
}
|
|
4679
|
+
updateValue(entries) {
|
|
4680
|
+
const value = entries
|
|
4681
|
+
.filter((entry) => !entry.error)
|
|
4682
|
+
.map((entry) => entry.file);
|
|
4683
|
+
const nextValue = this.multiple() ? value : (value[0] ?? null);
|
|
4684
|
+
this.value.set(nextValue);
|
|
4685
|
+
this.onChange(nextValue);
|
|
4686
|
+
}
|
|
4687
|
+
isAcceptedFile(file) {
|
|
4688
|
+
const acceptedTypes = this.accept()
|
|
4689
|
+
.split(',')
|
|
4690
|
+
.map((type) => type.trim().toLowerCase())
|
|
4691
|
+
.filter(Boolean);
|
|
4692
|
+
if (acceptedTypes.length === 0 || acceptedTypes.includes('*')) {
|
|
4693
|
+
return true;
|
|
4694
|
+
}
|
|
4695
|
+
const fileName = file.name.toLowerCase();
|
|
4696
|
+
const fileType = file.type.toLowerCase();
|
|
4697
|
+
return acceptedTypes.some((acceptedType) => {
|
|
4698
|
+
if (acceptedType.startsWith('.')) {
|
|
4699
|
+
return fileName.endsWith(acceptedType);
|
|
4700
|
+
}
|
|
4701
|
+
if (acceptedType.endsWith('/*')) {
|
|
4702
|
+
return fileType.startsWith(`${acceptedType.slice(0, -1)}`);
|
|
4703
|
+
}
|
|
4704
|
+
return fileType === acceptedType;
|
|
4705
|
+
});
|
|
4706
|
+
}
|
|
4707
|
+
getFileValidationError(index) {
|
|
4708
|
+
this.validationRevision();
|
|
4709
|
+
const errors = this.ngControl()?.control?.errors;
|
|
4710
|
+
if (!errors)
|
|
4711
|
+
return null;
|
|
4712
|
+
const indexedErrors = errors['indexes'];
|
|
4713
|
+
const isSingleFile = !this.multiple() && index === 0;
|
|
4714
|
+
if (!isSingleFile && !indexedErrors?.[index])
|
|
4715
|
+
return null;
|
|
4716
|
+
if (errors['maxfilesize'])
|
|
4717
|
+
return 'Ce fichier est trop grand !';
|
|
4718
|
+
if (errors['minfilesize'])
|
|
4719
|
+
return 'Ce fichier est trop petit !';
|
|
4720
|
+
return null;
|
|
4721
|
+
}
|
|
4618
4722
|
/**
|
|
4619
4723
|
* Utilitaire pour afficher la taille en Ko/Mo
|
|
4620
4724
|
*/
|
|
@@ -4627,12 +4731,12 @@ class ChrFileInputComponent extends ChrInputComponent {
|
|
|
4627
4731
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
4628
4732
|
}
|
|
4629
4733
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4630
|
-
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 } }, viewQueries: [{ propertyName: "fileNativeInput", first: true, predicate: ["fileNativeInput"], descendants: 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\" [class.dragging]=\"isDragging()\" [class.disabled]=\"isDisabled()\"
|
|
4734
|
+
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 } }, viewQueries: [{ propertyName: "fileNativeInput", first: true, predicate: ["fileNativeInput"], descendants: 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>\n <span class=\"file-name\" [contentEditable]=\"true\"\n (focusout)=\"entry.file.name = $any($event.target).innerText.trim()\">{{\n entry.file.name }}\n </span>\n <span class=\"file-name\">.{{entry.file.extension}}</span>\n </div>\n <span class=\"file-size\">{{ formatFileSize(entry.file.file?.size ?? 0) }}</span>\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 <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\" (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(--primary-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(--tertiary-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:flex;flex-direction:column;flex:1;overflow:hidden}.file-name{font-size:.8125rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:.6875rem;color:var(--text-neutral-color)}.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)}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }] }); }
|
|
4631
4735
|
}
|
|
4632
4736
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrFileInputComponent, decorators: [{
|
|
4633
4737
|
type: Component,
|
|
4634
|
-
args: [{ selector: 'chr-file-input', imports: [ReactiveFormsModule], template: "<div class=\"file-input-wrapper\">\n <!-- Zone de Drop & S\u00E9lection (remplace visuellement l'input texte standard) -->\n <div class=\"drop-zone\" [class.dragging]=\"isDragging()\" [class.disabled]=\"isDisabled()\"
|
|
4635
|
-
}], ctorParameters: () => [], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], fileNativeInput: [{
|
|
4738
|
+
args: [{ selector: 'chr-file-input', imports: [ReactiveFormsModule], 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>\n <span class=\"file-name\" [contentEditable]=\"true\"\n (focusout)=\"entry.file.name = $any($event.target).innerText.trim()\">{{\n entry.file.name }}\n </span>\n <span class=\"file-name\">.{{entry.file.extension}}</span>\n </div>\n <span class=\"file-size\">{{ formatFileSize(entry.file.file?.size ?? 0) }}</span>\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 <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\" (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(--primary-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(--tertiary-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:flex;flex-direction:column;flex:1;overflow:hidden}.file-name{font-size:.8125rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:.6875rem;color:var(--text-neutral-color)}.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)}\n"] }]
|
|
4739
|
+
}], 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 }] }], fileNativeInput: [{
|
|
4636
4740
|
type: ViewChild,
|
|
4637
4741
|
args: ['fileNativeInput']
|
|
4638
4742
|
}] } });
|
|
@@ -4692,7 +4796,7 @@ class ChrSearchSelectInputComponent extends ChrInputComponent {
|
|
|
4692
4796
|
super.ngOnInit();
|
|
4693
4797
|
}
|
|
4694
4798
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrSearchSelectInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4695
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ChrSearchSelectInputComponent, isStandalone: true, selector: "chr-search-select-input", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: "<div class=\"search-select-input-wrapper\">\n <chr-input [id]=\"id()\" [name]=\"name()\" type=\"text\" [label]=\"label()\" [displayValue]=\"displayValue()\"\n [value]=\"displayValue()\" (input)=\"dl.open();onInput($event);\">\n <chr-input-indicators>\n <ng-content select=\"chr-input-indicators\"></ng-content>\n <!-- @if(isFocused()){ -->\n @if(value() == null){\n <span class=\"material-symbols selection-status-indicator error\">close</span>\n }@else {\n <span class=\"material-symbols selection-status-indicator success\">check</span>\n }\n <span class=\"selection-status-indicator\" style=\"width: 1rem; \"></span>\n <!-- } -->\n </chr-input-indicators>\n <chr-input-data-overlay>\n <chr-data-list #dl [for]=\"''+name()\" [suggestions]=\"data()\" [display]=\"display()\" [selectOnFullMatch]=\"true\"\n (optionSelected)=\"selectOption($event.value)\"></chr-data-list>\n </chr-input-data-overlay>\n </chr-input>\n</div>\n\n<div class=\"data-overlay\">\n @if(isTouched()){\n @let error = getLastError();\n @if(error != null){\n <div class=\"error card glass\">\n {{getLastError() ?? null}}\n </div>\n }\n }\n <ng-content select=\"chr-input-data-overlay\">\n </ng-content>\n</div>\n<div class=\"data\">\n <ng-content select=\"chr-input-data\">\n <!-- Fallback to default projection-->\n <ng-content></ng-content>\n </ng-content>\n</div>", styles: [":host{display:flex;flex-direction:column;flex:1 1 auto;min-width:0}.search-select-input-wrapper{position:relative;min-height:1.5rem;display:flex;flex:1 1 auto;min-width:0;flex-direction:column}.selection-status-indicator.success{color:var(--success-color)}.selection-status-indicator.error{color:var(--error-color)}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { 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"] }, { kind: "component", type: ChrInputIndicators, selector: "chr-input-indicators" }, { kind: "component", type: DataListComponent, selector: "chr-data-list", inputs: ["suggestions", "for", "allowStringify", "display", "targetElement", "blurOnSelect", "selectOnFullMatch"], outputs: ["optionSelected"] }, { kind: "component", type: ChrInputDataOverlay, selector: "chr-input-data-overlay" }] }); }
|
|
4799
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ChrSearchSelectInputComponent, isStandalone: true, selector: "chr-search-select-input", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: "<div class=\"search-select-input-wrapper\">\n <chr-input [id]=\"id()\" [name]=\"name()\" type=\"text\" [label]=\"label()\" [displayValue]=\"displayValue()\"\n [value]=\"displayValue()\" (input)=\"dl.open();onInput($event);\">\n <chr-input-indicators>\n <ng-content select=\"chr-input-indicators\"></ng-content>\n <!-- @if(isFocused()){ -->\n @if(value() == null){\n <span class=\"material-symbols selection-status-indicator error\">close</span>\n }@else {\n <span class=\"material-symbols selection-status-indicator success\">check</span>\n }\n <span class=\"selection-status-indicator\" style=\"width: 1rem; \"></span>\n <!-- } -->\n </chr-input-indicators>\n <chr-input-data-overlay>\n <chr-data-list #dl [for]=\"''+name()\" [suggestions]=\"data()\" [display]=\"display()\" [selectOnFullMatch]=\"true\"\n (optionSelected)=\"selectOption($event.value)\"></chr-data-list>\n </chr-input-data-overlay>\n </chr-input>\n</div>\n\n<div class=\"data-overlay\">\n @if(isTouched()){\n @let error = getLastError();\n @if(error != null){\n <div class=\"error card glass\">\n {{getLastError() ?? null}}\n </div>\n }\n }\n <ng-content select=\"chr-input-data-overlay\">\n </ng-content>\n</div>\n<div class=\"data\">\n <ng-content select=\"chr-input-data\">\n <!-- Fallback to default projection-->\n <ng-content></ng-content>\n </ng-content>\n</div>", styles: [":host{display:flex;flex-direction:column;flex:1 1 auto;min-width:0}.search-select-input-wrapper{position:relative;min-height:1.5rem;max-height:1.5rem;display:flex;flex:1 1 auto;min-width:0;flex-direction:column}.selection-status-indicator.success{color:var(--success-color)}.selection-status-indicator.error{color:var(--error-color)}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { 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"] }, { kind: "component", type: ChrInputIndicators, selector: "chr-input-indicators" }, { kind: "component", type: DataListComponent, selector: "chr-data-list", inputs: ["suggestions", "for", "allowStringify", "display", "targetElement", "blurOnSelect", "selectOnFullMatch"], outputs: ["optionSelected"] }, { kind: "component", type: ChrInputDataOverlay, selector: "chr-input-data-overlay" }] }); }
|
|
4696
4800
|
}
|
|
4697
4801
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrSearchSelectInputComponent, decorators: [{
|
|
4698
4802
|
type: Component,
|
|
@@ -4702,7 +4806,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
4702
4806
|
ChrInputIndicators,
|
|
4703
4807
|
DataListComponent,
|
|
4704
4808
|
ChrInputDataOverlay,
|
|
4705
|
-
], template: "<div class=\"search-select-input-wrapper\">\n <chr-input [id]=\"id()\" [name]=\"name()\" type=\"text\" [label]=\"label()\" [displayValue]=\"displayValue()\"\n [value]=\"displayValue()\" (input)=\"dl.open();onInput($event);\">\n <chr-input-indicators>\n <ng-content select=\"chr-input-indicators\"></ng-content>\n <!-- @if(isFocused()){ -->\n @if(value() == null){\n <span class=\"material-symbols selection-status-indicator error\">close</span>\n }@else {\n <span class=\"material-symbols selection-status-indicator success\">check</span>\n }\n <span class=\"selection-status-indicator\" style=\"width: 1rem; \"></span>\n <!-- } -->\n </chr-input-indicators>\n <chr-input-data-overlay>\n <chr-data-list #dl [for]=\"''+name()\" [suggestions]=\"data()\" [display]=\"display()\" [selectOnFullMatch]=\"true\"\n (optionSelected)=\"selectOption($event.value)\"></chr-data-list>\n </chr-input-data-overlay>\n </chr-input>\n</div>\n\n<div class=\"data-overlay\">\n @if(isTouched()){\n @let error = getLastError();\n @if(error != null){\n <div class=\"error card glass\">\n {{getLastError() ?? null}}\n </div>\n }\n }\n <ng-content select=\"chr-input-data-overlay\">\n </ng-content>\n</div>\n<div class=\"data\">\n <ng-content select=\"chr-input-data\">\n <!-- Fallback to default projection-->\n <ng-content></ng-content>\n </ng-content>\n</div>", styles: [":host{display:flex;flex-direction:column;flex:1 1 auto;min-width:0}.search-select-input-wrapper{position:relative;min-height:1.5rem;display:flex;flex:1 1 auto;min-width:0;flex-direction:column}.selection-status-indicator.success{color:var(--success-color)}.selection-status-indicator.error{color:var(--error-color)}\n"] }]
|
|
4809
|
+
], template: "<div class=\"search-select-input-wrapper\">\n <chr-input [id]=\"id()\" [name]=\"name()\" type=\"text\" [label]=\"label()\" [displayValue]=\"displayValue()\"\n [value]=\"displayValue()\" (input)=\"dl.open();onInput($event);\">\n <chr-input-indicators>\n <ng-content select=\"chr-input-indicators\"></ng-content>\n <!-- @if(isFocused()){ -->\n @if(value() == null){\n <span class=\"material-symbols selection-status-indicator error\">close</span>\n }@else {\n <span class=\"material-symbols selection-status-indicator success\">check</span>\n }\n <span class=\"selection-status-indicator\" style=\"width: 1rem; \"></span>\n <!-- } -->\n </chr-input-indicators>\n <chr-input-data-overlay>\n <chr-data-list #dl [for]=\"''+name()\" [suggestions]=\"data()\" [display]=\"display()\" [selectOnFullMatch]=\"true\"\n (optionSelected)=\"selectOption($event.value)\"></chr-data-list>\n </chr-input-data-overlay>\n </chr-input>\n</div>\n\n<div class=\"data-overlay\">\n @if(isTouched()){\n @let error = getLastError();\n @if(error != null){\n <div class=\"error card glass\">\n {{getLastError() ?? null}}\n </div>\n }\n }\n <ng-content select=\"chr-input-data-overlay\">\n </ng-content>\n</div>\n<div class=\"data\">\n <ng-content select=\"chr-input-data\">\n <!-- Fallback to default projection-->\n <ng-content></ng-content>\n </ng-content>\n</div>", styles: [":host{display:flex;flex-direction:column;flex:1 1 auto;min-width:0}.search-select-input-wrapper{position:relative;min-height:1.5rem;max-height:1.5rem;display:flex;flex:1 1 auto;min-width:0;flex-direction:column}.selection-status-indicator.success{color:var(--success-color)}.selection-status-indicator.error{color:var(--error-color)}\n"] }]
|
|
4706
4810
|
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }] } });
|
|
4707
4811
|
|
|
4708
4812
|
class ChrTagSelectInputComponent extends ChrInputComponent {
|