@jm-7c3/common-lib 22.2.0 → 22.3.0

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, signal, effect, ChangeDetectionStrategy, Component, Injectable, inject, Inject, ChangeDetectorRef, viewChild, ViewContainerRef, Service, Directive, contentChild, output, InjectionToken, computed, ViewEncapsulation, NgModule } from '@angular/core';
2
+ import { input, signal, effect, ChangeDetectionStrategy, Component, Injectable, inject, Inject, ChangeDetectorRef, viewChild, ViewContainerRef, Service, Directive, contentChild, output, InjectionToken, computed, ViewEncapsulation, NgModule, model } from '@angular/core';
3
3
  import * as i1 from 'primeng/button';
4
4
  import { ButtonModule } from 'primeng/button';
5
5
  import * as i2 from 'primeng/api';
@@ -26,6 +26,7 @@ import * as i1$6 from 'primeng/toast';
26
26
  import { ToastModule } from 'primeng/toast';
27
27
  import { cloneDeep } from 'lodash-es';
28
28
  import { v4 } from 'uuid';
29
+ import { validate, form } from '@angular/forms/signals';
29
30
  import * as i2$3 from '@angular/material/icon';
30
31
  import { MatIconModule } from '@angular/material/icon';
31
32
 
@@ -578,13 +579,13 @@ class PanelComponent {
578
579
  title = input('', /* @ts-ignore */
579
580
  ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
580
581
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
581
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: PanelComponent, isStandalone: true, selector: "cl-panel", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "contentTemplate", first: true, predicate: ["contentTemplate"], descendants: true, isSignal: true }, { propertyName: "footerTemplate", first: true, predicate: ["footerTemplate"], descendants: true, isSignal: true }, { propertyName: "headerTemplate", first: true, predicate: ["headerTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"border-1 border-gray-300 p-5 rounded-lg\">\n @if (headerTemplate() || title()) {\n <div>\n @if (title()) {\n <div class=\"font-medium mb-5 text-xl\">\n {{ title() }}\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\" />\n }\n </div>\n }\n <div>\n <ng-container [ngTemplateOutlet]=\"contentTemplate()\" />\n </div>\n @if (footerTemplate()) {\n <div>\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\" />\n </div>\n }\n</div>\n\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
582
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: PanelComponent, isStandalone: true, selector: "cl-panel", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "contentTemplate", first: true, predicate: ["contentTemplate"], descendants: true, isSignal: true }, { propertyName: "footerTemplate", first: true, predicate: ["footerTemplate"], descendants: true, isSignal: true }, { propertyName: "headerTemplate", first: true, predicate: ["headerTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"border-1 border-gray-300 p-5 rounded-lg\">\n <!-- TODO - Use flex and gap instead of margins -->\n @if (headerTemplate() || title()) {\n <div>\n @if (title()) {\n <div class=\"font-medium mb-5 text-xl\">\n {{ title() }}\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\" />\n }\n </div>\n }\n <div>\n <ng-container [ngTemplateOutlet]=\"contentTemplate()\" />\n </div>\n @if (footerTemplate()) {\n <div class=\"mt-5\">\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\" />\n </div>\n }\n</div>\n\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
582
583
  }
583
584
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PanelComponent, decorators: [{
584
585
  type: Component,
585
586
  args: [{ selector: 'cl-panel', imports: [
586
587
  CommonModule,
587
- ], template: "<div class=\"border-1 border-gray-300 p-5 rounded-lg\">\n @if (headerTemplate() || title()) {\n <div>\n @if (title()) {\n <div class=\"font-medium mb-5 text-xl\">\n {{ title() }}\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\" />\n }\n </div>\n }\n <div>\n <ng-container [ngTemplateOutlet]=\"contentTemplate()\" />\n </div>\n @if (footerTemplate()) {\n <div>\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\" />\n </div>\n }\n</div>\n\n" }]
588
+ ], template: "<div class=\"border-1 border-gray-300 p-5 rounded-lg\">\n <!-- TODO - Use flex and gap instead of margins -->\n @if (headerTemplate() || title()) {\n <div>\n @if (title()) {\n <div class=\"font-medium mb-5 text-xl\">\n {{ title() }}\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\" />\n }\n </div>\n }\n <div>\n <ng-container [ngTemplateOutlet]=\"contentTemplate()\" />\n </div>\n @if (footerTemplate()) {\n <div class=\"mt-5\">\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\" />\n </div>\n }\n</div>\n\n" }]
588
589
  }], propDecorators: { contentTemplate: [{ type: i0.ContentChild, args: ['contentTemplate', { isSignal: true }] }], footerTemplate: [{ type: i0.ContentChild, args: ['footerTemplate', { isSignal: true }] }], headerTemplate: [{ type: i0.ContentChild, args: ['headerTemplate', { isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }] } });
589
590
 
590
591
  class ProgressBarDialogComponent {
@@ -1125,62 +1126,61 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
1125
1126
  }]
1126
1127
  }] });
1127
1128
 
1129
+ function matFileInputRequired(message = 'This field is required') {
1130
+ return (path) => {
1131
+ return validate(path, ({ value }) => {
1132
+ const firstValue = (value() || []).at(-1);
1133
+ if (!value() || (!firstValue?.isNew && firstValue?.toDelete)) {
1134
+ return {
1135
+ kind: 'mat-file-input-required',
1136
+ message,
1137
+ };
1138
+ }
1139
+ return null;
1140
+ });
1141
+ };
1142
+ }
1128
1143
  class MatFileInputComponent {
1144
+ // #region Inputs, outputs and viewChild
1129
1145
  directory = input('', /* @ts-ignore */
1130
1146
  ...(ngDevMode ? [{ debugName: "directory" }] : /* istanbul ignore next */ []));
1147
+ disabled = input(false, /* @ts-ignore */
1148
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1131
1149
  icon = input('add', /* @ts-ignore */
1132
1150
  ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
1133
1151
  label = input('Browse', /* @ts-ignore */
1134
1152
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1135
1153
  readonly = input(false, /* @ts-ignore */
1136
1154
  ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
1137
- onDownloadEmitter = output({ alias: 'onDownload' });
1155
+ onDownloadEmitter = output({
1156
+ alias: 'onDownload',
1157
+ });
1138
1158
  fileInput = viewChild('fileInput', /* @ts-ignore */
1139
1159
  ...(ngDevMode ? [{ debugName: "fileInput" }] : /* istanbul ignore next */ []));
1140
- fileName = signal('', /* @ts-ignore */
1141
- ...(ngDevMode ? [{ debugName: "fileName" }] : /* istanbul ignore next */ []));
1142
- isDisabled = signal(false, /* @ts-ignore */
1143
- ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1144
- isFileSelected = signal(false, /* @ts-ignore */
1145
- ...(ngDevMode ? [{ debugName: "isFileSelected" }] : /* istanbul ignore next */ []));
1146
- value = signal(null, /* @ts-ignore */
1160
+ // #endregion
1161
+ // #region Properties
1162
+ value = model(null, /* @ts-ignore */
1147
1163
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
1148
- // ControlValueAccessor interface methods
1149
- registerOnChange(fn) {
1150
- this.onChange = fn;
1151
- }
1152
- registerOnTouched(fn) {
1153
- this.onTouched = fn;
1154
- }
1155
- setDisabledState(isDisabled) {
1156
- setTimeout(() => {
1157
- this.isDisabled.set(isDisabled);
1158
- });
1159
- }
1160
- writeValue(value = null) {
1161
- let newValue = [];
1162
- if (Array.isArray(value)) {
1163
- newValue = cloneDeep(value);
1164
- this.selectFile(newValue[0]);
1165
- }
1166
- else if (typeof value === 'string') {
1167
- const hash = this.getHashFromFileName(value);
1168
- const file = {
1169
- isNew: !value,
1170
- hash,
1171
- name: value,
1172
- path: this.directory(),
1173
- toDelete: false,
1174
- };
1175
- newValue.push(file);
1176
- this.selectFile(file);
1177
- }
1178
- this.setValue(newValue);
1164
+ form = form(this.value, (path) => {
1165
+ matFileInputRequired()(path);
1166
+ });
1167
+ // #endregion
1168
+ // #region Computed properties
1169
+ isFileSelected = computed(() => this.value()?.filter((file) => !file?.toDelete).length, /* @ts-ignore */
1170
+ ...(ngDevMode ? [{ debugName: "isFileSelected" }] : /* istanbul ignore next */ []));
1171
+ fileName = computed(() => {
1172
+ const file = this.getLastFile(this.value());
1173
+ return this.getSafeFileName(file);
1174
+ }, /* @ts-ignore */
1175
+ ...(ngDevMode ? [{ debugName: "fileName" }] : /* istanbul ignore next */ []));
1176
+ ngOnInit() {
1177
+ this.writeValue(this.value());
1179
1178
  }
1180
- // Custom public methods
1181
- onClick() {
1179
+ // #endregion
1180
+ // #region Protected methods
1181
+ onClick(event) {
1182
+ event.preventDefault();
1182
1183
  this.fileInput()?.nativeElement.click();
1183
- this.onTouched();
1184
1184
  }
1185
1185
  onFileSelected(event) {
1186
1186
  const file = event.target.files[0];
@@ -1197,8 +1197,7 @@ class MatFileInputComponent {
1197
1197
  };
1198
1198
  const newValue = [...(this.value() ?? [])];
1199
1199
  newValue.push(newFile);
1200
- this.selectFile(newFile);
1201
- this.setValue(newValue);
1200
+ this.value.set(newValue);
1202
1201
  }
1203
1202
  }
1204
1203
  onDelete() {
@@ -1211,15 +1210,18 @@ class MatFileInputComponent {
1211
1210
  else {
1212
1211
  file.toDelete = true;
1213
1212
  }
1214
- this.fileName.set('');
1215
- this.isFileSelected.set(false);
1216
- this.setValue(newValue);
1213
+ if (newValue.length === 0) {
1214
+ this.value.set(null);
1215
+ }
1216
+ else {
1217
+ this.value.set(newValue);
1218
+ }
1217
1219
  }
1218
1220
  }
1219
1221
  onDownload() {
1220
1222
  this.onDownloadEmitter.emit(this.value());
1221
1223
  }
1222
- // Custom private methods
1224
+ // #region Private methods
1223
1225
  getFileNameParts(fileName) {
1224
1226
  const lastDotIndex = fileName.lastIndexOf('.');
1225
1227
  if (lastDotIndex === -1) {
@@ -1236,148 +1238,128 @@ class MatFileInputComponent {
1236
1238
  return parts.length > 2 ? parts[parts.length - 2] : '';
1237
1239
  }
1238
1240
  getLastFile(value) {
1239
- return value.length > 0 ? value[value.length - 1] : undefined;
1241
+ const response = (value ?? []).at(-1);
1242
+ return response ?? null;
1240
1243
  }
1241
1244
  getSafeFileName(file) {
1245
+ if (!file) {
1246
+ return '';
1247
+ }
1242
1248
  const { name, extension } = this.getFileNameParts(file.name);
1243
1249
  const hash = this.getHashFromFileName(file.name);
1244
1250
  const fileName = name.replace(`.${hash}`, '');
1245
1251
  return `${decodeURIComponent(fileName)}.${extension}`;
1246
1252
  }
1247
- onChange = (value) => { };
1248
- onTouched = () => { };
1249
- selectFile(file) {
1250
- const safeFileName = this.getSafeFileName(file);
1251
- this.fileName.set(safeFileName);
1252
- this.isFileSelected.set(true);
1253
- }
1254
- setValue(value) {
1255
- this.value.set(value);
1256
- this.onChange(value);
1257
- this.onTouched();
1253
+ writeValue(value = null) {
1254
+ if (Array.isArray(value)) {
1255
+ this.value.set(cloneDeep(value));
1256
+ }
1257
+ else if (typeof value === 'string') {
1258
+ const hash = this.getHashFromFileName(value);
1259
+ const file = {
1260
+ isNew: !value,
1261
+ hash,
1262
+ name: value,
1263
+ path: this.directory(),
1264
+ toDelete: false,
1265
+ };
1266
+ this.value.set([file]);
1267
+ }
1258
1268
  }
1259
1269
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MatFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1260
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: MatFileInputComponent, isStandalone: true, selector: "cl-mat-file-input", inputs: { directory: { classPropertyName: "directory", publicName: "directory", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onDownloadEmitter: "onDownload" }, providers: [
1261
- {
1262
- multi: true,
1263
- provide: NG_VALUE_ACCESSOR,
1264
- useExisting: MatFileInputComponent,
1265
- },
1266
- ], viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"file-input-container\">\n <input #fileInput (change)=\"onFileSelected($event)\" [disabled]=\"isDisabled()\" class=\"file-input\" hidden type=\"file\" />\n\n @if (isFileSelected()) {\n <div class=\"file-details\">\n <span class=\"file-name\" (click)=\"onDownload()\">{{ fileName() }}</span>\n @if (!readonly()) {\n <button mat-icon-button (click)=\"onDelete()\" [disabled]=\"isDisabled()\">\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n } @else {\n <button mat-raised-button (click)=\"onClick()\" [disabled]=\"isDisabled() || readonly()\">\n <mat-icon>{{ icon() }}</mat-icon>\n <span>{{ label() }}</span>\n </button>\n }\n</div>", styles: [":host .file-input-container{display:flex;align-items:center}:host .file-details{display:flex;align-items:center;gap:8px}:host .file-name{cursor:pointer;text-decoration:underline;color:#00f}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
1270
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: MatFileInputComponent, isStandalone: true, selector: "cl-mat-file-input", inputs: { directory: { classPropertyName: "directory", publicName: "directory", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onDownloadEmitter: "onDownload", value: "valueChange" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"file-input-container\">\n <input\n (change)=\"onFileSelected($event)\"\n class=\"file-input\"\n [disabled]=\"disabled()\"\n #fileInput\n hidden\n type=\"file\"\n />\n\n @if (isFileSelected()) {\n <div class=\"file-details\">\n <span class=\"file-name\" (click)=\"onDownload()\">{{ fileName() }}</span>\n @if (!readonly()) {\n <button\n (click)=\"onDelete()\"\n [disabled]=\"disabled()\"\n mat-icon-button\n type=\"button\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n } @else {\n <button\n (click)=\"onClick($event)\"\n [disabled]=\"disabled() || readonly()\"\n mat-raised-button\n type=\"button\"\n >\n <mat-icon>{{ icon() }}</mat-icon>\n <span>{{ label() }}</span>\n </button>\n }\n</div>\n", styles: [":host .file-input-container{display:flex;align-items:center}:host .file-details{display:flex;align-items:center;gap:8px}:host .file-name{cursor:pointer;text-decoration:underline;color:#00f}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
1267
1271
  }
1268
1272
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MatFileInputComponent, decorators: [{
1269
1273
  type: Component,
1270
- args: [{ selector: 'cl-mat-file-input', standalone: true, imports: [FormsModule, MatButtonModule, MatIconModule], providers: [
1271
- {
1272
- multi: true,
1273
- provide: NG_VALUE_ACCESSOR,
1274
- useExisting: MatFileInputComponent,
1275
- },
1276
- ], template: "<div class=\"file-input-container\">\n <input #fileInput (change)=\"onFileSelected($event)\" [disabled]=\"isDisabled()\" class=\"file-input\" hidden type=\"file\" />\n\n @if (isFileSelected()) {\n <div class=\"file-details\">\n <span class=\"file-name\" (click)=\"onDownload()\">{{ fileName() }}</span>\n @if (!readonly()) {\n <button mat-icon-button (click)=\"onDelete()\" [disabled]=\"isDisabled()\">\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n } @else {\n <button mat-raised-button (click)=\"onClick()\" [disabled]=\"isDisabled() || readonly()\">\n <mat-icon>{{ icon() }}</mat-icon>\n <span>{{ label() }}</span>\n </button>\n }\n</div>", styles: [":host .file-input-container{display:flex;align-items:center}:host .file-details{display:flex;align-items:center;gap:8px}:host .file-name{cursor:pointer;text-decoration:underline;color:#00f}\n"] }]
1277
- }], propDecorators: { directory: [{ type: i0.Input, args: [{ isSignal: true, alias: "directory", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], onDownloadEmitter: [{ type: i0.Output, args: ["onDownload"] }], fileInput: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }] } });
1274
+ args: [{ selector: 'cl-mat-file-input', standalone: true, imports: [MatButtonModule, MatIconModule], template: "<div class=\"file-input-container\">\n <input\n (change)=\"onFileSelected($event)\"\n class=\"file-input\"\n [disabled]=\"disabled()\"\n #fileInput\n hidden\n type=\"file\"\n />\n\n @if (isFileSelected()) {\n <div class=\"file-details\">\n <span class=\"file-name\" (click)=\"onDownload()\">{{ fileName() }}</span>\n @if (!readonly()) {\n <button\n (click)=\"onDelete()\"\n [disabled]=\"disabled()\"\n mat-icon-button\n type=\"button\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n } @else {\n <button\n (click)=\"onClick($event)\"\n [disabled]=\"disabled() || readonly()\"\n mat-raised-button\n type=\"button\"\n >\n <mat-icon>{{ icon() }}</mat-icon>\n <span>{{ label() }}</span>\n </button>\n }\n</div>\n", styles: [":host .file-input-container{display:flex;align-items:center}:host .file-details{display:flex;align-items:center;gap:8px}:host .file-name{cursor:pointer;text-decoration:underline;color:#00f}\n"] }]
1275
+ }], propDecorators: { directory: [{ type: i0.Input, args: [{ isSignal: true, alias: "directory", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], onDownloadEmitter: [{ type: i0.Output, args: ["onDownload"] }], fileInput: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
1278
1276
 
1279
- class MatFileInputsComponent extends BaseFormComponent {
1280
- cdRef = inject(ChangeDetectorRef);
1277
+ function matFileInputsRequired(message = 'This field is required') {
1278
+ return (path) => {
1279
+ return validate(path, ({ value }) => {
1280
+ if (value()?.some((file) => file === null || file.at(-1)?.toDelete)) {
1281
+ return {
1282
+ kind: 'mat-file-inputs-required',
1283
+ message,
1284
+ };
1285
+ }
1286
+ return null;
1287
+ });
1288
+ };
1289
+ }
1290
+ class MatFileInputsComponent {
1291
+ // #region Inputs and outputs
1281
1292
  directory = input('', /* @ts-ignore */
1282
1293
  ...(ngDevMode ? [{ debugName: "directory" }] : /* istanbul ignore next */ []));
1294
+ disabled = input(false, /* @ts-ignore */
1295
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1283
1296
  icon = input('add', /* @ts-ignore */
1284
1297
  ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
1285
1298
  label = input('Browse', /* @ts-ignore */
1286
1299
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1287
- max = input(5, /* @ts-ignore */
1288
- ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
1300
+ maxFiles = input(5, /* @ts-ignore */
1301
+ ...(ngDevMode ? [{ debugName: "maxFiles" }] : /* istanbul ignore next */ []));
1289
1302
  readonly = input(false, /* @ts-ignore */
1290
1303
  ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
1291
1304
  onDownloadEmitter = output({ alias: 'onDownload' });
1305
+ // #endregion
1306
+ // #region Properties
1307
+ value = model(null, /* @ts-ignore */
1308
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
1309
+ form = form(this.value);
1310
+ // #endregion
1311
+ // #region NG Hooks
1312
+ ngOnInit() {
1313
+ this.writeValue(this.value());
1314
+ }
1315
+ // #endregion
1316
+ // #region Protected methods
1292
1317
  onAdd() {
1293
- this.formArray.push(new FormControl());
1294
- this.cdRef.markForCheck();
1318
+ this.value.update((value) => [...(value ?? []), null]);
1295
1319
  }
1296
1320
  onDelete(index) {
1297
- this.formArray.removeAt(index);
1298
- this.cdRef.markForCheck();
1321
+ this.value.update((currentValue) => {
1322
+ if (!currentValue) {
1323
+ return null;
1324
+ }
1325
+ // Return a new array with the item removed
1326
+ return currentValue.filter((_, i) => i !== index);
1327
+ });
1299
1328
  }
1300
1329
  onDownload(file) {
1301
- this.onDownloadEmitter.emit(file);
1302
- }
1303
- setDisabledState(isDisabled) {
1304
- for (const control of this.formArray.controls) {
1305
- if (isDisabled) {
1306
- control.disable();
1307
- }
1308
- else {
1309
- control.enable();
1310
- }
1330
+ if (!file) {
1331
+ return;
1311
1332
  }
1333
+ this.onDownloadEmitter.emit(file);
1312
1334
  }
1313
- validate(control) {
1314
- if (control?.hasValidator(Validators.required) && Array.isArray(control?.value)) {
1315
- const first = control.value[0];
1316
- if (!(typeof first === 'string' ||
1317
- (Array.isArray(first) && first.some(({ toDelete }) => toDelete === false)))) {
1318
- return {
1319
- required: true
1320
- };
1321
- }
1322
- }
1323
- return null;
1335
+ onValueChange(file, index) {
1336
+ this.value.update((value) => {
1337
+ const newValue = [...(value ?? [])];
1338
+ newValue[index] = file;
1339
+ return newValue;
1340
+ });
1324
1341
  }
1342
+ // #region Private methods
1325
1343
  writeValue(value) {
1326
1344
  if (Array.isArray(value)) {
1327
- setTimeout(() => {
1328
- if (value.length === 0) {
1329
- this.onAdd();
1330
- }
1331
- else {
1332
- for (let i = 0, j = value.length; i < j; i++) {
1333
- this.onAdd();
1334
- }
1335
- this.setValue(value);
1336
- }
1337
- });
1345
+ if (value.length === 0) {
1346
+ this.onAdd();
1347
+ }
1348
+ else {
1349
+ this.value.set(value);
1350
+ }
1338
1351
  }
1339
1352
  else if (!this.readonly()) {
1340
1353
  this.onAdd();
1341
1354
  }
1342
1355
  }
1343
- getForm() {
1344
- return new FormArray([]);
1345
- }
1346
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MatFileInputsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1347
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: MatFileInputsComponent, isStandalone: true, selector: "cl-mat-file-inputs", inputs: { directory: { classPropertyName: "directory", publicName: "directory", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onDownloadEmitter: "onDownload" }, providers: [
1348
- {
1349
- multi: true,
1350
- provide: NG_VALIDATORS,
1351
- useExisting: MatFileInputsComponent
1352
- },
1353
- {
1354
- multi: true,
1355
- provide: NG_VALUE_ACCESSOR,
1356
- useExisting: MatFileInputsComponent
1357
- }
1358
- ], usesInheritance: true, ngImport: i0, template: "<ng-container [formGroup]=\"formGroup\">\n @for (item of formArray.value; track index; let index = $index) {\n <div class=\"flex mb-3\">\n <cl-mat-file-input [formControlName]=\"index\" [directory]=\"directory()\" [icon]=\"icon()\" [label]=\"label()\"\n (onDownload)=\"onDownload($event)\" [readonly]=\"readonly()\" />\n @if (!readonly()) {\n @if (index === 0) {\n <button (click)=\"onAdd()\" [disabled]=\"formArray.length >= max()\" mat-icon-button type=\"button\">\n <mat-icon>{{ icon() }}</mat-icon>\n </button>\n } @else if (index > 0) {\n <button (click)=\"onDelete(index)\" mat-icon-button type=\"button\">\n <mat-icon>remove</mat-icon>\n </button>\n }\n }\n </div>\n }\n</ng-container>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatFileInputComponent, selector: "cl-mat-file-input", inputs: ["directory", "icon", "label", "readonly"], outputs: ["onDownload"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
1356
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MatFileInputsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1357
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: MatFileInputsComponent, isStandalone: true, selector: "cl-mat-file-inputs", inputs: { directory: { classPropertyName: "directory", publicName: "directory", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onDownloadEmitter: "onDownload", value: "valueChange" }, ngImport: i0, template: "@for (item of value(); track index; let index = $index) {\n <div class=\"flex mb-3\">\n <cl-mat-file-input\n [directory]=\"directory()\"\n [icon]=\"icon()\"\n [label]=\"label()\"\n (onDownload)=\"onDownload($event)\"\n [value]=\"item\"\n (valueChange)=\"onValueChange($event, index)\"\n />\n @if (!readonly()) {\n @if (index === 0) {\n @let allowMoreFiles = (value()?.length ?? 0) < maxFiles();\n <button\n (click)=\"onAdd()\"\n [disabled]=\"disabled() || !allowMoreFiles\"\n mat-icon-button\n type=\"button\"\n >\n <mat-icon>{{ icon() }}</mat-icon>\n </button>\n } @else if (index > 0) {\n <button (click)=\"onDelete(index)\" mat-icon-button type=\"button\">\n <mat-icon>remove</mat-icon>\n </button>\n }\n }\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatFileInputComponent, selector: "cl-mat-file-input", inputs: ["directory", "disabled", "icon", "label", "readonly", "value"], outputs: ["onDownload", "valueChange"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
1359
1358
  }
1360
1359
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MatFileInputsComponent, decorators: [{
1361
1360
  type: Component,
1362
- args: [{ selector: 'cl-mat-file-inputs', imports: [
1363
- MatButtonModule,
1364
- MatFileInputComponent,
1365
- MatIconModule,
1366
- FormsModule,
1367
- ReactiveFormsModule,
1368
- ], providers: [
1369
- {
1370
- multi: true,
1371
- provide: NG_VALIDATORS,
1372
- useExisting: MatFileInputsComponent
1373
- },
1374
- {
1375
- multi: true,
1376
- provide: NG_VALUE_ACCESSOR,
1377
- useExisting: MatFileInputsComponent
1378
- }
1379
- ], template: "<ng-container [formGroup]=\"formGroup\">\n @for (item of formArray.value; track index; let index = $index) {\n <div class=\"flex mb-3\">\n <cl-mat-file-input [formControlName]=\"index\" [directory]=\"directory()\" [icon]=\"icon()\" [label]=\"label()\"\n (onDownload)=\"onDownload($event)\" [readonly]=\"readonly()\" />\n @if (!readonly()) {\n @if (index === 0) {\n <button (click)=\"onAdd()\" [disabled]=\"formArray.length >= max()\" mat-icon-button type=\"button\">\n <mat-icon>{{ icon() }}</mat-icon>\n </button>\n } @else if (index > 0) {\n <button (click)=\"onDelete(index)\" mat-icon-button type=\"button\">\n <mat-icon>remove</mat-icon>\n </button>\n }\n }\n </div>\n }\n</ng-container>\n" }]
1380
- }], propDecorators: { directory: [{ type: i0.Input, args: [{ isSignal: true, alias: "directory", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], onDownloadEmitter: [{ type: i0.Output, args: ["onDownload"] }] } });
1361
+ args: [{ selector: 'cl-mat-file-inputs', imports: [MatButtonModule, MatFileInputComponent, MatIconModule], template: "@for (item of value(); track index; let index = $index) {\n <div class=\"flex mb-3\">\n <cl-mat-file-input\n [directory]=\"directory()\"\n [icon]=\"icon()\"\n [label]=\"label()\"\n (onDownload)=\"onDownload($event)\"\n [value]=\"item\"\n (valueChange)=\"onValueChange($event, index)\"\n />\n @if (!readonly()) {\n @if (index === 0) {\n @let allowMoreFiles = (value()?.length ?? 0) < maxFiles();\n <button\n (click)=\"onAdd()\"\n [disabled]=\"disabled() || !allowMoreFiles\"\n mat-icon-button\n type=\"button\"\n >\n <mat-icon>{{ icon() }}</mat-icon>\n </button>\n } @else if (index > 0) {\n <button (click)=\"onDelete(index)\" mat-icon-button type=\"button\">\n <mat-icon>remove</mat-icon>\n </button>\n }\n }\n </div>\n}\n" }]
1362
+ }], propDecorators: { directory: [{ type: i0.Input, args: [{ isSignal: true, alias: "directory", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], onDownloadEmitter: [{ type: i0.Output, args: ["onDownload"] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
1381
1363
 
1382
1364
  ;
1383
1365
 
@@ -1416,5 +1398,5 @@ function concatWithToArray(values) {
1416
1398
  * Generated bundle index. Do not edit.
1417
1399
  */
1418
1400
 
1419
- export { BaseFormComponent, ButtonComponent, ClStateService, CommonLibModule, ConfirmationComponent, ConfirmationService, ContentDialogComponent, ContentDialogService, ERROR_DIRECTIVE, ErrorDirective, FORM_FIELD, FileInputComponent, FileInputsComponent, FormDialogComponent, FormDialogService, FormFieldComponent, FormFieldModule, IconButtonComponent, LABEL_DIRECTIVE, LabelDirective, MatConfirmationComponent, MatConfirmationService, MatFileInputComponent, MatFileInputsComponent, MatFormDialogComponent, MatFormDialogService, MatFormHostableComponent, PanelComponent, ProgressBarDialogComponent, ProgressBarDialogService, ReadonlyValueComponent, ToastsComponent, ToastsService, concatWithToArray };
1401
+ export { BaseFormComponent, ButtonComponent, ClStateService, CommonLibModule, ConfirmationComponent, ConfirmationService, ContentDialogComponent, ContentDialogService, ERROR_DIRECTIVE, ErrorDirective, FORM_FIELD, FileInputComponent, FileInputsComponent, FormDialogComponent, FormDialogService, FormFieldComponent, FormFieldModule, IconButtonComponent, LABEL_DIRECTIVE, LabelDirective, MatConfirmationComponent, MatConfirmationService, MatFileInputComponent, MatFileInputsComponent, MatFormDialogComponent, MatFormDialogService, MatFormHostableComponent, PanelComponent, ProgressBarDialogComponent, ProgressBarDialogService, ReadonlyValueComponent, ToastsComponent, ToastsService, concatWithToArray, matFileInputRequired, matFileInputsRequired };
1420
1402
  //# sourceMappingURL=jm-7c3-common-lib.mjs.map