iss-ctrl 0.0.21 → 0.0.23

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.
Files changed (141) hide show
  1. package/README.md +33 -0
  2. package/fesm2022/iss-ctrl-attachment.viewer-DVUL39xd.mjs +110 -0
  3. package/fesm2022/iss-ctrl-attachment.viewer-DVUL39xd.mjs.map +1 -0
  4. package/fesm2022/iss-ctrl-base.mjs +682 -0
  5. package/fesm2022/iss-ctrl-base.mjs.map +1 -0
  6. package/fesm2022/iss-ctrl-checkbox.field-CILXADas.mjs +88 -0
  7. package/fesm2022/iss-ctrl-checkbox.field-CILXADas.mjs.map +1 -0
  8. package/fesm2022/iss-ctrl-checkbox.mjs +146 -0
  9. package/fesm2022/iss-ctrl-checkbox.mjs.map +1 -0
  10. package/fesm2022/iss-ctrl-checkbox.viewer-C3SEUszR.mjs +31 -0
  11. package/fesm2022/iss-ctrl-checkbox.viewer-C3SEUszR.mjs.map +1 -0
  12. package/fesm2022/iss-ctrl-chips.field-445Ds3V7.mjs +76 -0
  13. package/fesm2022/iss-ctrl-chips.field-445Ds3V7.mjs.map +1 -0
  14. package/fesm2022/iss-ctrl-chips.mjs +168 -0
  15. package/fesm2022/iss-ctrl-chips.mjs.map +1 -0
  16. package/fesm2022/iss-ctrl-chips.viewer-BcKT2hcq.mjs +38 -0
  17. package/fesm2022/iss-ctrl-chips.viewer-BcKT2hcq.mjs.map +1 -0
  18. package/fesm2022/iss-ctrl-date-picker.field-Djhh1FsR.mjs +98 -0
  19. package/fesm2022/iss-ctrl-date-picker.field-Djhh1FsR.mjs.map +1 -0
  20. package/fesm2022/iss-ctrl-date-picker.mjs +200 -0
  21. package/fesm2022/iss-ctrl-date-picker.mjs.map +1 -0
  22. package/fesm2022/iss-ctrl-date-picker.viewer-Dsmsf91t.mjs +40 -0
  23. package/fesm2022/iss-ctrl-date-picker.viewer-Dsmsf91t.mjs.map +1 -0
  24. package/fesm2022/iss-ctrl-date-range-picker.field-Dv636SAm.mjs +97 -0
  25. package/fesm2022/iss-ctrl-date-range-picker.field-Dv636SAm.mjs.map +1 -0
  26. package/fesm2022/iss-ctrl-date-range-picker.mjs +307 -0
  27. package/fesm2022/iss-ctrl-date-range-picker.mjs.map +1 -0
  28. package/fesm2022/iss-ctrl-date-range-picker.viewer-tmTQGPFh.mjs +41 -0
  29. package/fesm2022/iss-ctrl-date-range-picker.viewer-tmTQGPFh.mjs.map +1 -0
  30. package/fesm2022/iss-ctrl-datetime-picker.field-DldY8vke.mjs +98 -0
  31. package/fesm2022/iss-ctrl-datetime-picker.field-DldY8vke.mjs.map +1 -0
  32. package/fesm2022/iss-ctrl-datetime-picker.mjs +226 -0
  33. package/fesm2022/iss-ctrl-datetime-picker.mjs.map +1 -0
  34. package/fesm2022/iss-ctrl-datetime-picker.viewer-BeUyEBKo.mjs +42 -0
  35. package/fesm2022/iss-ctrl-datetime-picker.viewer-BeUyEBKo.mjs.map +1 -0
  36. package/fesm2022/iss-ctrl-dropdownlist.field-DikBAtc_.mjs +87 -0
  37. package/fesm2022/iss-ctrl-dropdownlist.field-DikBAtc_.mjs.map +1 -0
  38. package/fesm2022/iss-ctrl-dropdownlist.mjs +398 -0
  39. package/fesm2022/iss-ctrl-dropdownlist.mjs.map +1 -0
  40. package/fesm2022/iss-ctrl-dropdownlist.viewer-OWJxDpr8.mjs +30 -0
  41. package/fesm2022/iss-ctrl-dropdownlist.viewer-OWJxDpr8.mjs.map +1 -0
  42. package/fesm2022/iss-ctrl-file-picker.field-BMZaJxvj.mjs +210 -0
  43. package/fesm2022/iss-ctrl-file-picker.field-BMZaJxvj.mjs.map +1 -0
  44. package/fesm2022/iss-ctrl-file-picker.mjs +274 -0
  45. package/fesm2022/iss-ctrl-file-picker.mjs.map +1 -0
  46. package/fesm2022/iss-ctrl-input.field-BiwBFYx3.mjs +104 -0
  47. package/fesm2022/iss-ctrl-input.field-BiwBFYx3.mjs.map +1 -0
  48. package/fesm2022/iss-ctrl-input.mjs +204 -0
  49. package/fesm2022/iss-ctrl-input.mjs.map +1 -0
  50. package/fesm2022/iss-ctrl-input.viewer-BOZnW0_e.mjs +22 -0
  51. package/fesm2022/iss-ctrl-input.viewer-BOZnW0_e.mjs.map +1 -0
  52. package/fesm2022/iss-ctrl-iss-ctrl-BG-QWWdq.mjs +1770 -0
  53. package/fesm2022/iss-ctrl-iss-ctrl-BG-QWWdq.mjs.map +1 -0
  54. package/fesm2022/{iss-ctrl-load-event-bOt6IlIo.mjs → iss-ctrl-load-event-T4L3k9XO.mjs} +4 -3
  55. package/fesm2022/iss-ctrl-load-event-T4L3k9XO.mjs.map +1 -0
  56. package/fesm2022/{iss-ctrl-load-options-event-CxyAyNMe.mjs → iss-ctrl-load-options-event-tG258cbP.mjs} +3 -3
  57. package/fesm2022/{iss-ctrl-load-options-event-CxyAyNMe.mjs.map → iss-ctrl-load-options-event-tG258cbP.mjs.map} +1 -1
  58. package/fesm2022/{iss-ctrl-load-value-event-DJKJ5S3h.mjs → iss-ctrl-load-value-event--7zt90Dl.mjs} +3 -3
  59. package/fesm2022/{iss-ctrl-load-value-event-DJKJ5S3h.mjs.map → iss-ctrl-load-value-event--7zt90Dl.mjs.map} +1 -1
  60. package/fesm2022/iss-ctrl-mobile.field-DLccnWKX.mjs +77 -0
  61. package/fesm2022/iss-ctrl-mobile.field-DLccnWKX.mjs.map +1 -0
  62. package/fesm2022/iss-ctrl-mobile.mjs +2465 -0
  63. package/fesm2022/iss-ctrl-mobile.mjs.map +1 -0
  64. package/fesm2022/iss-ctrl-mobile.viewer-DQgxAcR-.mjs +22 -0
  65. package/fesm2022/iss-ctrl-mobile.viewer-DQgxAcR-.mjs.map +1 -0
  66. package/fesm2022/{iss-ctrl-note.field-B_Lbp28X.mjs → iss-ctrl-note.field-CT1wVmfF.mjs} +2 -2
  67. package/fesm2022/{iss-ctrl-note.field-B_Lbp28X.mjs.map → iss-ctrl-note.field-CT1wVmfF.mjs.map} +1 -1
  68. package/fesm2022/iss-ctrl-plate.field-CQQY8rnE.mjs +92 -0
  69. package/fesm2022/iss-ctrl-plate.field-CQQY8rnE.mjs.map +1 -0
  70. package/fesm2022/iss-ctrl-plate.mjs +493 -0
  71. package/fesm2022/iss-ctrl-plate.mjs.map +1 -0
  72. package/fesm2022/iss-ctrl-plate.viewer-B8PUBLH8.mjs +179 -0
  73. package/fesm2022/iss-ctrl-plate.viewer-B8PUBLH8.mjs.map +1 -0
  74. package/fesm2022/iss-ctrl-radio.field-Dp5D-AXQ.mjs +88 -0
  75. package/fesm2022/iss-ctrl-radio.field-Dp5D-AXQ.mjs.map +1 -0
  76. package/fesm2022/iss-ctrl-radio.mjs +104 -0
  77. package/fesm2022/iss-ctrl-radio.mjs.map +1 -0
  78. package/fesm2022/iss-ctrl-radio.viewer-EiDliZro.mjs +30 -0
  79. package/fesm2022/iss-ctrl-radio.viewer-EiDliZro.mjs.map +1 -0
  80. package/fesm2022/{iss-ctrl-replace-api-keys-C0XYQW4i.mjs → iss-ctrl-replace-api-keys-COUv4Kin.mjs} +2 -2
  81. package/fesm2022/{iss-ctrl-replace-api-keys-C0XYQW4i.mjs.map → iss-ctrl-replace-api-keys-COUv4Kin.mjs.map} +1 -1
  82. package/fesm2022/{iss-ctrl-set-value-event-CgXhi0Bv.mjs → iss-ctrl-set-value-event-D0-_qIyS.mjs} +2 -2
  83. package/fesm2022/{iss-ctrl-set-value-event-CgXhi0Bv.mjs.map → iss-ctrl-set-value-event-D0-_qIyS.mjs.map} +1 -1
  84. package/fesm2022/iss-ctrl-slide-toggle.field-vCJHOiSG.mjs +85 -0
  85. package/fesm2022/iss-ctrl-slide-toggle.field-vCJHOiSG.mjs.map +1 -0
  86. package/fesm2022/iss-ctrl-slide-toggle.mjs +66 -0
  87. package/fesm2022/iss-ctrl-slide-toggle.mjs.map +1 -0
  88. package/fesm2022/iss-ctrl-slide-toggle.viewer-hsIHwDj3.mjs +34 -0
  89. package/fesm2022/iss-ctrl-slide-toggle.viewer-hsIHwDj3.mjs.map +1 -0
  90. package/fesm2022/{iss-ctrl-table.field-B5FxF6hn.mjs → iss-ctrl-table.field-DdFjH-D8.mjs} +142 -140
  91. package/fesm2022/iss-ctrl-table.field-DdFjH-D8.mjs.map +1 -0
  92. package/fesm2022/iss-ctrl-textarea.field-Dqhhu2m4.mjs +82 -0
  93. package/fesm2022/iss-ctrl-textarea.field-Dqhhu2m4.mjs.map +1 -0
  94. package/fesm2022/iss-ctrl-textarea.mjs +115 -0
  95. package/fesm2022/iss-ctrl-textarea.mjs.map +1 -0
  96. package/fesm2022/iss-ctrl-textarea.viewer-CpmLauik.mjs +22 -0
  97. package/fesm2022/iss-ctrl-textarea.viewer-CpmLauik.mjs.map +1 -0
  98. package/fesm2022/iss-ctrl-time-picker.field-CdmxRiH8.mjs +77 -0
  99. package/fesm2022/iss-ctrl-time-picker.field-CdmxRiH8.mjs.map +1 -0
  100. package/fesm2022/iss-ctrl-time-picker.mjs +170 -0
  101. package/fesm2022/iss-ctrl-time-picker.mjs.map +1 -0
  102. package/fesm2022/iss-ctrl-time-picker.viewer-CJeFLrdI.mjs +22 -0
  103. package/fesm2022/iss-ctrl-time-picker.viewer-CJeFLrdI.mjs.map +1 -0
  104. package/fesm2022/iss-ctrl-toggle-button.field-rAT7dVFC.mjs +101 -0
  105. package/fesm2022/iss-ctrl-toggle-button.field-rAT7dVFC.mjs.map +1 -0
  106. package/fesm2022/iss-ctrl-toggle-button.mjs +158 -0
  107. package/fesm2022/iss-ctrl-toggle-button.mjs.map +1 -0
  108. package/fesm2022/iss-ctrl-toggle-button.viewer-CC-8_Dy4.mjs +30 -0
  109. package/fesm2022/iss-ctrl-toggle-button.viewer-CC-8_Dy4.mjs.map +1 -0
  110. package/fesm2022/iss-ctrl-tokens.mjs +25 -0
  111. package/fesm2022/iss-ctrl-tokens.mjs.map +1 -0
  112. package/fesm2022/{iss-ctrl-trigger-based-on-json-value-2v1kLVHZ.mjs → iss-ctrl-trigger-based-on-json-value-I92SNsv9.mjs} +2 -2
  113. package/fesm2022/{iss-ctrl-trigger-based-on-json-value-2v1kLVHZ.mjs.map → iss-ctrl-trigger-based-on-json-value-I92SNsv9.mjs.map} +1 -1
  114. package/fesm2022/{iss-ctrl-user-picker.field-BH63sZ-X.mjs → iss-ctrl-user-picker.field-DpmYCfpD.mjs} +154 -153
  115. package/fesm2022/iss-ctrl-user-picker.field-DpmYCfpD.mjs.map +1 -0
  116. package/fesm2022/iss-ctrl.mjs +3 -1
  117. package/fesm2022/iss-ctrl.mjs.map +1 -1
  118. package/package.json +69 -1
  119. package/types/iss-ctrl-base.d.ts +318 -0
  120. package/types/iss-ctrl-checkbox.d.ts +46 -0
  121. package/types/iss-ctrl-chips.d.ts +41 -0
  122. package/types/iss-ctrl-date-picker.d.ts +75 -0
  123. package/types/iss-ctrl-date-range-picker.d.ts +95 -0
  124. package/types/iss-ctrl-datetime-picker.d.ts +77 -0
  125. package/types/iss-ctrl-dropdownlist.d.ts +78 -0
  126. package/types/iss-ctrl-file-picker.d.ts +95 -0
  127. package/types/iss-ctrl-input.d.ts +76 -0
  128. package/types/iss-ctrl-mobile.d.ts +91 -0
  129. package/types/iss-ctrl-plate.d.ts +54 -0
  130. package/types/iss-ctrl-radio.d.ts +34 -0
  131. package/types/iss-ctrl-slide-toggle.d.ts +26 -0
  132. package/types/iss-ctrl-textarea.d.ts +30 -0
  133. package/types/iss-ctrl-time-picker.d.ts +48 -0
  134. package/types/iss-ctrl-toggle-button.d.ts +41 -0
  135. package/types/iss-ctrl-tokens.d.ts +21 -0
  136. package/types/iss-ctrl.d.ts +303 -2005
  137. package/fesm2022/iss-ctrl-iss-ctrl-BKu6a5HF.mjs +0 -9683
  138. package/fesm2022/iss-ctrl-iss-ctrl-BKu6a5HF.mjs.map +0 -1
  139. package/fesm2022/iss-ctrl-load-event-bOt6IlIo.mjs.map +0 -1
  140. package/fesm2022/iss-ctrl-table.field-B5FxF6hn.mjs.map +0 -1
  141. package/fesm2022/iss-ctrl-user-picker.field-BH63sZ-X.mjs.map +0 -1
@@ -0,0 +1,210 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, signal, computed, effect, Component } from '@angular/core';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import * as i1 from '@angular/forms';
5
+ import { FormControl, ReactiveFormsModule } from '@angular/forms';
6
+ import { MatLabel } from '@angular/material/input';
7
+ import { catchError, EMPTY } from 'rxjs';
8
+ import { F as FieldBase, c as FileHandler, V as VIEW_PROVIDER, a as ValidationMessage, b as FieldViewer } from './iss-ctrl-iss-ctrl-BG-QWWdq.mjs';
9
+ import { AttachmentViewer } from './iss-ctrl-attachment.viewer-DVUL39xd.mjs';
10
+ import { FilePickerControl } from 'iss-ctrl/file-picker';
11
+
12
+ /**
13
+ * `<iss-field-file-picker>` — attachment upload.
14
+ *
15
+ * Rendered by the outlet for fields of type `attachment`. In `edit` mode it renders
16
+ * {@link FilePickerControl} plus the validation message; in `view` mode the matching viewer.
17
+ *
18
+ * ### Supported `config` keys
19
+ * | Key | Type | Description |
20
+ * | --- | --- | --- |
21
+ * | `multiple` | `boolean` | Allows attaching more than one file. |
22
+ * | `readonly` | `boolean` | Shows the value but blocks interaction. |
23
+ *
24
+ * ### Other field members used
25
+ * `label()` · `value` · `validation` · `ctrlJsonValue()`
26
+ *
27
+ * Every field also honours `visible`, `mode`, `validation`, `asyncValidation` and
28
+ * `events` — see {@link IssField} for the full model and {@link FieldConfig} for all keys.
29
+ *
30
+ * The field uploads the picked files itself, stores the returned ids as the control value
31
+ * and keeps the resolved attachments in `field.ctrlJsonValue`.
32
+ *
33
+ * Accepted extensions come from the `fileType` **validation** rule, not from `config`; the
34
+ * `allowedFileSize`, `allowedFileTypes`, `minFilesCount`, `maxFilesCount`, `templateId` and
35
+ * `templateUrl` config keys exist on {@link FieldConfig} but are not read here yet.
36
+ *
37
+ * @example
38
+ * ```json
39
+ * { "name": "attachments", "type": "attachment", "label": "Attachments", "config": { "multiple": true }, "validation": { "fileType": ["pdf", "png"] } }
40
+ * ```
41
+ */
42
+ class FilePickerField extends FieldBase {
43
+ picker = new FormControl();
44
+ fileHandlerService = inject(FileHandler);
45
+ uploading = signal(false, ...(ngDevMode ? [{ debugName: "uploading" }] : /* istanbul ignore next */ []));
46
+ viewField = computed(() => this.#generateViewField(this.field.ctrlJsonValue()), ...(ngDevMode ? [{ debugName: "viewField" }] : /* istanbul ignore next */ []));
47
+ attachments = signal([], ...(ngDevMode ? [{ debugName: "attachments" }] : /* istanbul ignore next */ []));
48
+ uploadingError = signal(undefined, ...(ngDevMode ? [{ debugName: "uploadingError" }] : /* istanbul ignore next */ []));
49
+ accept = this.#acceptFactory();
50
+ // the ids the picker already holds files for; without it the effect would re-download
51
+ // the very files captureFile just uploaded, since it writes them back into ctrl
52
+ #hydratedValue = null;
53
+ #valueEffect = effect(() => {
54
+ const value = this.ctrlValueChange();
55
+ if (!value || !this.field.value || value === this.#hydratedValue) {
56
+ return;
57
+ }
58
+ this.#hydratedValue = value;
59
+ this.fileHandlerService.fetchFiles(value)
60
+ .pipe(takeUntilDestroyed(this.destroyRef))
61
+ .subscribe((res) => {
62
+ const filesOnly = res.map((attachment) => attachment.file);
63
+ const formDataValue = this.fileHandlerService.formDataFactory(filesOnly, !!this.field.config.multiple());
64
+ this.attachments.set(res);
65
+ this.picker.setValue(formDataValue);
66
+ this.field.ctrlJsonValue.set(res);
67
+ });
68
+ }, ...(ngDevMode ? [{ debugName: "#valueEffect" }] : /* istanbul ignore next */ []));
69
+ captureFile(formData) {
70
+ if (!formData) {
71
+ return;
72
+ }
73
+ this.uploading.set(true);
74
+ const notUploadedFiles = this.#getNotUploadedFiles(formData);
75
+ if (this.fileHandlerService.formDataHasValue(notUploadedFiles)) {
76
+ this.fileHandlerService.upload(notUploadedFiles).pipe(catchError((err) => {
77
+ this.uploading.set(false);
78
+ this.uploadingError.set(err?.message ?? "Uploading Failed!");
79
+ console.error(err);
80
+ return EMPTY;
81
+ })).subscribe((res) => {
82
+ this.attachments.update((attachments) => {
83
+ if (!this.field.config.multiple()) {
84
+ return res;
85
+ }
86
+ return [...attachments, ...res];
87
+ });
88
+ const stringifiedValue = this.stringifyIssAttachment(this.attachments());
89
+ this.#hydratedValue = stringifiedValue;
90
+ this.ctrl.setValue(stringifiedValue);
91
+ this.field.ctrlJsonValue.set(this.attachments());
92
+ this.uploading.set(false);
93
+ });
94
+ }
95
+ else {
96
+ this.uploading.set(false);
97
+ }
98
+ }
99
+ stringifyIssAttachment(attachments) {
100
+ return attachments.map((attachment) => attachment.id).join(",");
101
+ }
102
+ ngOnDestroy() {
103
+ super.ngOnDestroy();
104
+ this.#valueEffect.destroy();
105
+ }
106
+ // The accepted extensions are the `fileType` rule's own value, the same one its validator is
107
+ // built from — a list, or the comma-separated string an older definition wrote it as.
108
+ #acceptFactory() {
109
+ const extensions = this.field.validation?.["fileType"];
110
+ if (!extensions) {
111
+ return "";
112
+ }
113
+ const list = Array.isArray(extensions) ? extensions : String(extensions).split(",");
114
+ return list
115
+ .map((extension) => extension.trim())
116
+ .filter((extension) => !!extension)
117
+ .map((extension) => extension.startsWith(".") ? extension : `.${extension}`)
118
+ .join(",");
119
+ }
120
+ #generateViewField(value) {
121
+ return {
122
+ config: {
123
+ showDownloadButton: true,
124
+ showDeleteButton: false
125
+ },
126
+ ctrlJsonValue: signal(value ?? [])
127
+ };
128
+ }
129
+ #getNotUploadedFiles(formData) {
130
+ const fd = new FormData();
131
+ if (formData) {
132
+ for (const pair of formData.entries()) {
133
+ const file = pair[1];
134
+ if (!file.id) {
135
+ fd.append(pair[0], pair[1]);
136
+ }
137
+ }
138
+ }
139
+ return fd;
140
+ }
141
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FilePickerField, deps: null, target: i0.ɵɵFactoryTarget.Component });
142
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FilePickerField, isStandalone: true, selector: "iss-field-file-picker", usesInheritance: true, ngImport: i0, template: `
143
+ @if (field.visible()) {
144
+ @if (field.mode() === "edit") {
145
+ <mat-label>{{ field.label() }}</mat-label>
146
+ <input type="hidden" [formControlName]="field.name">
147
+ <!-- [showDeleteButton]="field.config.showDeleteButton" [showDownloadButton]="field.config.showDownloadButton"-->
148
+ <iss-ctrl-file-picker
149
+ [formControl]="picker"
150
+ [accept]="accept"
151
+ (onChange)="captureFile($event)"
152
+ [multiple]="field.config.multiple()"
153
+ [readonly]="field.config.readonly() ?? false"
154
+ [uploading]="uploading()"
155
+ [uploadedFiles]="attachments()"
156
+ [uploadingError]="uploadingError()"
157
+ />
158
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="ctrl.touched"/>
159
+ } @else {
160
+ <iss-field-viewer>
161
+ <label>{{ field.label() }}</label>
162
+ <iss-viewer-attachment [uploading]="uploading()" [field]="viewField()"/>
163
+ </iss-field-viewer>
164
+ }
165
+ }
166
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: FilePickerControl, selector: "iss-ctrl-file-picker", inputs: ["accept", "showDownloadButton", "showDeleteButton", "uploading", "uploadedFiles", "uploadingError"] }, { kind: "component", type: ValidationMessage, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: AttachmentViewer, selector: "iss-viewer-attachment", inputs: ["uploading", "uploadingError"], outputs: ["deleteOut"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: FieldViewer, selector: "iss-field-viewer" }], viewProviders: [VIEW_PROVIDER] });
167
+ }
168
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FilePickerField, decorators: [{
169
+ type: Component,
170
+ args: [{
171
+ selector: "iss-field-file-picker",
172
+ viewProviders: [VIEW_PROVIDER],
173
+ imports: [
174
+ ReactiveFormsModule,
175
+ FilePickerControl,
176
+ ValidationMessage,
177
+ AttachmentViewer,
178
+ MatLabel,
179
+ FieldViewer
180
+ ],
181
+ template: `
182
+ @if (field.visible()) {
183
+ @if (field.mode() === "edit") {
184
+ <mat-label>{{ field.label() }}</mat-label>
185
+ <input type="hidden" [formControlName]="field.name">
186
+ <!-- [showDeleteButton]="field.config.showDeleteButton" [showDownloadButton]="field.config.showDownloadButton"-->
187
+ <iss-ctrl-file-picker
188
+ [formControl]="picker"
189
+ [accept]="accept"
190
+ (onChange)="captureFile($event)"
191
+ [multiple]="field.config.multiple()"
192
+ [readonly]="field.config.readonly() ?? false"
193
+ [uploading]="uploading()"
194
+ [uploadedFiles]="attachments()"
195
+ [uploadingError]="uploadingError()"
196
+ />
197
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="ctrl.touched"/>
198
+ } @else {
199
+ <iss-field-viewer>
200
+ <label>{{ field.label() }}</label>
201
+ <iss-viewer-attachment [uploading]="uploading()" [field]="viewField()"/>
202
+ </iss-field-viewer>
203
+ }
204
+ }
205
+ `
206
+ }]
207
+ }] });
208
+
209
+ export { FilePickerField };
210
+ //# sourceMappingURL=iss-ctrl-file-picker.field-BMZaJxvj.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iss-ctrl-file-picker.field-BMZaJxvj.mjs","sources":["../../../projects/iss/src/components/controls/file-picker/file-picker.field.ts"],"sourcesContent":["import { Component, computed, effect, inject, OnDestroy, Signal, signal, WritableSignal, } from \"@angular/core\";\nimport { takeUntilDestroyed } from \"@angular/core/rxjs-interop\";\nimport { FormControl, ReactiveFormsModule } from \"@angular/forms\";\nimport { MatLabel } from \"@angular/material/input\";\nimport { catchError, EMPTY } from \"rxjs\";\nimport { VIEW_PROVIDER } from \"../../../shared/constants/view-provider.const\";\nimport { FieldViewer } from \"../../shared/field-viewer\";\nimport { ValidationMessage } from \"../../shared/validation-message\";\nimport { FieldBase } from \"../base/field-base\";\nimport { IssAttachment } from \"iss-ctrl/tokens\";\nimport { AttachmentViewer } from \"./attachment.viewer\";\nimport { IssFile } from \"iss-ctrl/file-picker\";\nimport { FileHandler } from \"./file-handler\";\nimport { FilePickerControl } from \"iss-ctrl/file-picker\";\n\n/**\n * `<iss-field-file-picker>` — attachment upload.\n *\n * Rendered by the outlet for fields of type `attachment`. In `edit` mode it renders\n * {@link FilePickerControl} plus the validation message; in `view` mode the matching viewer.\n *\n * ### Supported `config` keys\n * | Key | Type | Description |\n * | --- | --- | --- |\n * | `multiple` | `boolean` | Allows attaching more than one file. |\n * | `readonly` | `boolean` | Shows the value but blocks interaction. |\n *\n * ### Other field members used\n * `label()` · `value` · `validation` · `ctrlJsonValue()`\n *\n * Every field also honours `visible`, `mode`, `validation`, `asyncValidation` and\n * `events` — see {@link IssField} for the full model and {@link FieldConfig} for all keys.\n *\n * The field uploads the picked files itself, stores the returned ids as the control value\n * and keeps the resolved attachments in `field.ctrlJsonValue`.\n *\n * Accepted extensions come from the `fileType` **validation** rule, not from `config`; the\n * `allowedFileSize`, `allowedFileTypes`, `minFilesCount`, `maxFilesCount`, `templateId` and\n * `templateUrl` config keys exist on {@link FieldConfig} but are not read here yet.\n *\n * @example\n * ```json\n * { \"name\": \"attachments\", \"type\": \"attachment\", \"label\": \"Attachments\", \"config\": { \"multiple\": true }, \"validation\": { \"fileType\": [\"pdf\", \"png\"] } }\n * ```\n */\n@Component({\n selector: \"iss-field-file-picker\",\n viewProviders: [VIEW_PROVIDER],\n imports: [\n ReactiveFormsModule,\n FilePickerControl,\n ValidationMessage,\n AttachmentViewer,\n MatLabel,\n FieldViewer\n ],\n template: `\n @if (field.visible()) {\n @if (field.mode() === \"edit\") {\n <mat-label>{{ field.label() }}</mat-label>\n <input type=\"hidden\" [formControlName]=\"field.name\">\n <!-- [showDeleteButton]=\"field.config.showDeleteButton\" [showDownloadButton]=\"field.config.showDownloadButton\"-->\n <iss-ctrl-file-picker\n [formControl]=\"picker\"\n [accept]=\"accept\"\n (onChange)=\"captureFile($event)\"\n [multiple]=\"field.config.multiple()\"\n [readonly]=\"field.config.readonly() ?? false\"\n [uploading]=\"uploading()\"\n [uploadedFiles]=\"attachments()\"\n [uploadingError]=\"uploadingError()\"\n />\n <iss-validation-message-viewer [errors]=\"ctrl.errors\" [showMessage]=\"ctrl.touched\"/>\n } @else {\n <iss-field-viewer>\n <label>{{ field.label() }}</label>\n <iss-viewer-attachment [uploading]=\"uploading()\" [field]=\"viewField()\"/>\n </iss-field-viewer>\n }\n }\n `\n})\nexport class FilePickerField extends FieldBase implements OnDestroy {\n picker = new FormControl();\n fileHandlerService = inject(FileHandler);\n uploading: WritableSignal<boolean> = signal(false);\n viewField: Signal<any> = computed(() => this.#generateViewField(this.field.ctrlJsonValue()));\n attachments: WritableSignal<IssAttachment[]> = signal([]);\n uploadingError = signal<string | undefined>(undefined);\n accept: string = this.#acceptFactory();\n // the ids the picker already holds files for; without it the effect would re-download\n // the very files captureFile just uploaded, since it writes them back into ctrl\n #hydratedValue: string | null = null;\n #valueEffect = effect(() => {\n const value = this.ctrlValueChange();\n if (!value || !this.field.value || value === this.#hydratedValue) {\n return;\n }\n\n this.#hydratedValue = value;\n this.fileHandlerService.fetchFiles(value)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe((res: any) => {\n const filesOnly = res.map((attachment: any) => attachment.file);\n const formDataValue: FormData | null = this.fileHandlerService.formDataFactory(filesOnly, !!this.field.config.multiple());\n this.attachments.set(res);\n this.picker.setValue(formDataValue);\n this.field.ctrlJsonValue.set(res);\n });\n });\n\n captureFile(formData: FormData | null) {\n if (!formData) {\n return;\n }\n\n this.uploading.set(true);\n const notUploadedFiles: FormData = this.#getNotUploadedFiles(formData);\n if (this.fileHandlerService.formDataHasValue(notUploadedFiles)) {\n this.fileHandlerService.upload(notUploadedFiles).pipe(\n catchError((err) => {\n this.uploading.set(false);\n this.uploadingError.set(err?.message ?? \"Uploading Failed!\");\n console.error(err);\n return EMPTY;\n }),\n ).subscribe((res: any) => {\n this.attachments.update((attachments: IssAttachment[]): IssAttachment[] => {\n if (!this.field.config.multiple()) {\n return res;\n }\n return [...attachments, ...res];\n });\n const stringifiedValue = this.stringifyIssAttachment(this.attachments());\n this.#hydratedValue = stringifiedValue;\n this.ctrl.setValue(stringifiedValue);\n this.field.ctrlJsonValue.set(this.attachments());\n this.uploading.set(false);\n });\n } else {\n this.uploading.set(false);\n }\n }\n\n stringifyIssAttachment(attachments: IssAttachment[]): string {\n return attachments.map((attachment: IssAttachment) => attachment.id).join(\",\");\n }\n\n override ngOnDestroy() {\n super.ngOnDestroy();\n this.#valueEffect.destroy();\n }\n\n // The accepted extensions are the `fileType` rule's own value, the same one its validator is\n // built from — a list, or the comma-separated string an older definition wrote it as.\n #acceptFactory(): string {\n const extensions: any = this.field.validation?.[\"fileType\"];\n if (!extensions) {\n return \"\";\n }\n const list: string[] = Array.isArray(extensions) ? extensions : String(extensions).split(\",\");\n return list\n .map((extension: string) => extension.trim())\n .filter((extension: string) => !!extension)\n .map((extension: string) => extension.startsWith(\".\") ? extension : `.${extension}`)\n .join(\",\");\n }\n\n #generateViewField(value: IssFile[]) {\n return {\n config: {\n showDownloadButton: true,\n showDeleteButton: false\n },\n ctrlJsonValue: signal(value ?? [])\n };\n }\n\n #getNotUploadedFiles(formData: FormData): FormData {\n const fd: FormData = new FormData();\n if (formData) {\n for (const pair of formData.entries()) {\n const file: IssFile = pair[1] as unknown as IssFile;\n if (!file.id) {\n fd.append(pair[0], pair[1]);\n }\n }\n }\n return fd;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,274 @@
1
+ import * as i0 from '@angular/core';
2
+ import { output, HostListener, Directive, input, computed, signal, effect, CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
3
+ import * as i1 from '@angular/forms';
4
+ import { ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
5
+ import { ControlBase, Hints } from 'iss-ctrl/base';
6
+
7
+ class Droppable {
8
+ onDrop = output();
9
+ onDragOver(evt) {
10
+ evt.preventDefault();
11
+ evt.stopPropagation();
12
+ }
13
+ onDragLeave(evt) {
14
+ evt.preventDefault();
15
+ evt.stopPropagation();
16
+ }
17
+ onDropped(evt) {
18
+ evt.preventDefault();
19
+ evt.stopPropagation();
20
+ this.onDrop.emit(evt.dataTransfer.files);
21
+ }
22
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: Droppable, deps: [], target: i0.ɵɵFactoryTarget.Directive });
23
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: Droppable, isStandalone: true, selector: "[droppable]", outputs: { onDrop: "onDrop" }, host: { listeners: { "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)", "drop": "onDropped($event)" } }, ngImport: i0 });
24
+ }
25
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: Droppable, decorators: [{
26
+ type: Directive,
27
+ args: [{ selector: "[droppable]" }]
28
+ }], propDecorators: { onDrop: [{ type: i0.Output, args: ["onDrop"] }], onDragOver: [{
29
+ type: HostListener,
30
+ args: ["dragover", ["$event"]]
31
+ }], onDragLeave: [{
32
+ type: HostListener,
33
+ args: ["dragleave", ["$event"]]
34
+ }], onDropped: [{
35
+ type: HostListener,
36
+ args: ["drop", ["$event"]]
37
+ }] } });
38
+
39
+ /**
40
+ * `<iss-ctrl-file-picker>` — attachment upload with a per-file progress and error state.
41
+ *
42
+ * Uploading is done by the host: the control emits the picked files and renders whatever is
43
+ * fed back through `uploadedFiles`, `uploading` and `uploadingError`.
44
+ *
45
+ * @example
46
+ * ```html
47
+ * <iss-ctrl-file-picker
48
+ * formControlName="attachments"
49
+ * [multiple]="true"
50
+ * accept=".pdf,.png"
51
+ * [uploading]="uploading()"
52
+ * [uploadedFiles]="uploaded()"
53
+ * />
54
+ * ```
55
+ *
56
+ * ### Own inputs
57
+ * | Property | Type | Default | Description |
58
+ * | --- | --- | --- | --- |
59
+ * | `accept` | `string \| undefined` | `undefined` | Accepted extensions or MIME types for the native picker. |
60
+ * | `showDownloadButton` | `boolean` | `true` | Shows the download action on each attachment. |
61
+ * | `showDeleteButton` | `boolean` | `true` | Shows the remove action on each attachment. |
62
+ * | `uploading` | `boolean` | `false` | Renders the in-progress state. |
63
+ * | `uploadedFiles` | `IssAttachment[]` | `[]` | Files already stored on the server. |
64
+ * | `uploadingError` | `string \| undefined` | `undefined` | Error message attached to the failed upload. |
65
+ *
66
+ * ### Inherited from {@link ControlBase}
67
+ * `formControlName` · `readonly` · `className` · `(onChange)` · `multiple` · `hints`
68
+ *
69
+ * Projects a `<label>` above the control — the wrapping `iss-field-*` passes the field label in that way.
70
+ */
71
+ class FilePickerControl extends ControlBase {
72
+ /**
73
+ * Accepted extensions or MIME types, passed straight to the native file input.
74
+ */
75
+ accept = input(...(ngDevMode ? [undefined, { debugName: "accept" }] : /* istanbul ignore next */ []));
76
+ /**
77
+ * Shows the download action on each listed attachment.
78
+ */
79
+ showDownloadButton = input(true, ...(ngDevMode ? [{ debugName: "showDownloadButton" }] : /* istanbul ignore next */ []));
80
+ /**
81
+ * Shows the remove action on each listed attachment.
82
+ */
83
+ showDeleteButton = input(true, ...(ngDevMode ? [{ debugName: "showDeleteButton" }] : /* istanbul ignore next */ []));
84
+ /**
85
+ * Renders the upload-in-progress state; set by the host that performs the upload.
86
+ */
87
+ uploading = input(false, ...(ngDevMode ? [{ debugName: "uploading" }] : /* istanbul ignore next */ []));
88
+ /**
89
+ * Attachments already stored on the server, merged into the rendered list.
90
+ */
91
+ uploadedFiles = input([], ...(ngDevMode ? [{ debugName: "uploadedFiles" }] : /* istanbul ignore next */ []));
92
+ /**
93
+ * Error message shown on the attachment whose upload failed.
94
+ */
95
+ uploadingError = input(undefined, ...(ngDevMode ? [{ debugName: "uploadingError" }] : /* istanbul ignore next */ []));
96
+ filesValue = computed(() => this.#returnAttachmentViewObject(), ...(ngDevMode ? [{ debugName: "filesValue" }] : /* istanbul ignore next */ []));
97
+ attachments = signal([], ...(ngDevMode ? [{ debugName: "attachments" }] : /* istanbul ignore next */ []));
98
+ #uploadedFilesEffectRef = effect(() => {
99
+ this.#markUploadedFiles();
100
+ }, ...(ngDevMode ? [{ debugName: "#uploadedFilesEffectRef" }] : /* istanbul ignore next */ []));
101
+ /*todo needs review and refactor*/
102
+ ngOnDestroy() {
103
+ this.#uploadedFilesEffectRef.destroy();
104
+ }
105
+ writeValue(val) {
106
+ if (typeof val === "string") {
107
+ return;
108
+ }
109
+ this.#reset();
110
+ if (val) {
111
+ this.attachments.set([...this.attachments(), ...this.#convertFormDataToFiles(val)]);
112
+ }
113
+ }
114
+ onDelete(files) {
115
+ this.attachments.set(files);
116
+ this.emitValue(this.#formDataFactory(this.attachments()));
117
+ }
118
+ captureFile($event) {
119
+ if (this.readonly() || this.disabled()) {
120
+ return;
121
+ }
122
+ if (!$event) {
123
+ return;
124
+ }
125
+ if (!$event.length) {
126
+ return;
127
+ }
128
+ this.markAsTouched();
129
+ const files = Array.from($event);
130
+ const issFiles = files.map((file) => {
131
+ return {
132
+ name: file.name,
133
+ size: file.size,
134
+ type: file.type,
135
+ selected: Date.now(),
136
+ file: file,
137
+ };
138
+ });
139
+ this.#updateAttachments(issFiles);
140
+ // The field component uploads this output. Emit only the files selected in
141
+ // this interaction: `attachments` also contains files selected earlier, so
142
+ // serialising it would upload those files again whenever more are added.
143
+ this.emitValue(this.#formDataFactory(issFiles));
144
+ }
145
+ openFilePicker(input) {
146
+ if (this.readonly() || this.disabled()) {
147
+ return;
148
+ }
149
+ input.click();
150
+ this.markAsTouched();
151
+ }
152
+ #updateAttachments(selectedFiles) {
153
+ if (!this.multiple()) {
154
+ this.attachments.set([selectedFiles[0]]);
155
+ return;
156
+ }
157
+ this.attachments.update((files) => {
158
+ files = [...files, ...selectedFiles];
159
+ return files;
160
+ });
161
+ }
162
+ #formDataFactory(files) {
163
+ if (files.length === 0) {
164
+ return null;
165
+ }
166
+ const fd = new FormData();
167
+ let formDataKey = "file";
168
+ if (this.multiple()) {
169
+ formDataKey = "files";
170
+ }
171
+ for (let i = 0; i < files.length; i++) {
172
+ fd.append(formDataKey, files[i].file);
173
+ }
174
+ return fd;
175
+ }
176
+ #reset() {
177
+ this.attachments.set([]);
178
+ }
179
+ #convertFormDataToFiles(val) {
180
+ if (!val || !(val instanceof FormData)) {
181
+ return [];
182
+ }
183
+ const files = [];
184
+ for (const pair of val.entries()) {
185
+ if (pair[1] instanceof File) {
186
+ files.push(pair[1]);
187
+ }
188
+ }
189
+ return files;
190
+ }
191
+ #markUploadedFiles() {
192
+ this.attachments.update((attachments) => {
193
+ return attachments.map((attachment) => {
194
+ // const uploadedFile = this.uploadedFiles().find((f) => f.selected === attachment.selected && f.name === attachment.name);
195
+ if (this.uploadingError()) {
196
+ attachment.errorMsg = this.uploadingError();
197
+ }
198
+ // if (uploadedFile) {
199
+ // attachment.id = uploadedFile.id;
200
+ // }
201
+ return attachment;
202
+ });
203
+ });
204
+ }
205
+ #returnAttachmentViewObject() {
206
+ return {
207
+ config: {
208
+ showDownloadButton: this.showDownloadButton(),
209
+ showDeleteButton: this.showDeleteButton()
210
+ },
211
+ ctrlJsonValue: signal(this.attachments())
212
+ };
213
+ }
214
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FilePickerControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
215
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: FilePickerControl, isStandalone: true, selector: "iss-ctrl-file-picker", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, showDownloadButton: { classPropertyName: "showDownloadButton", publicName: "showDownloadButton", isSignal: true, isRequired: false, transformFunction: null }, showDeleteButton: { classPropertyName: "showDeleteButton", publicName: "showDeleteButton", isSignal: true, isRequired: false, transformFunction: null }, uploading: { classPropertyName: "uploading", publicName: "uploading", isSignal: true, isRequired: false, transformFunction: null }, uploadedFiles: { classPropertyName: "uploadedFiles", publicName: "uploadedFiles", isSignal: true, isRequired: false, transformFunction: null }, uploadingError: { classPropertyName: "uploadingError", publicName: "uploadingError", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
216
+ { provide: NG_VALUE_ACCESSOR, useExisting: FilePickerControl, multi: true }
217
+ ], usesInheritance: true, ngImport: i0, template: `
218
+ <ng-content select="label"></ng-content>
219
+ <ds-file-picker (click)="openFilePicker(inputFile)" (onDrop)="captureFile($event)" droppable>
220
+ <input
221
+ #inputFile
222
+ (change)="captureFile($any($event.target).files)"
223
+ (click)="$event.stopPropagation()"
224
+ [accept]="accept()"
225
+ [formControl]="ctrl"
226
+ [multiple]="multiple()"
227
+ [disabled]="readonly()"
228
+ [style.display]="'none'"
229
+ type="file"
230
+ />
231
+ </ds-file-picker>
232
+
233
+ <iss-ctrl-hints [hints]="hints()" [touched]="touched()"/>
234
+ <!--<iss-viewer-attachment (deleteOut)="onDelete($event)" [field]="filesValue()" [uploading]="uploading()"/>-->
235
+ `, isInline: true, styles: [".file-uploader{--uploader-height: 150px;--uploader-width: 100%;--uploader-bg: var(--off-white);--uploader-border: 1px dashed var(--dark-gray);--uploader-radius: var(--box-radius);--uploader-padding: 1rem;height:var(--uploader-height);background-color:var(--uploader-bg);border:var(--uploader-border);border-radius:var(--uploader-radius);padding:var(--uploader-padding);text-align:center;cursor:pointer}.file-uploader --input{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:.75rem}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: Droppable, selector: "[droppable]", outputs: ["onDrop"] }, { kind: "component", type: Hints, selector: "iss-ctrl-hints", inputs: ["hints", "touched"] }] });
236
+ }
237
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FilePickerControl, decorators: [{
238
+ type: Component,
239
+ args: [{ selector: "iss-ctrl-file-picker", schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [
240
+ ReactiveFormsModule,
241
+ Droppable,
242
+ Hints,
243
+ ], providers: [
244
+ { provide: NG_VALUE_ACCESSOR, useExisting: FilePickerControl, multi: true }
245
+ ], template: `
246
+ <ng-content select="label"></ng-content>
247
+ <ds-file-picker (click)="openFilePicker(inputFile)" (onDrop)="captureFile($event)" droppable>
248
+ <input
249
+ #inputFile
250
+ (change)="captureFile($any($event.target).files)"
251
+ (click)="$event.stopPropagation()"
252
+ [accept]="accept()"
253
+ [formControl]="ctrl"
254
+ [multiple]="multiple()"
255
+ [disabled]="readonly()"
256
+ [style.display]="'none'"
257
+ type="file"
258
+ />
259
+ </ds-file-picker>
260
+
261
+ <iss-ctrl-hints [hints]="hints()" [touched]="touched()"/>
262
+ <!--<iss-viewer-attachment (deleteOut)="onDelete($event)" [field]="filesValue()" [uploading]="uploading()"/>-->
263
+ `, styles: [".file-uploader{--uploader-height: 150px;--uploader-width: 100%;--uploader-bg: var(--off-white);--uploader-border: 1px dashed var(--dark-gray);--uploader-radius: var(--box-radius);--uploader-padding: 1rem;height:var(--uploader-height);background-color:var(--uploader-bg);border:var(--uploader-border);border-radius:var(--uploader-radius);padding:var(--uploader-padding);text-align:center;cursor:pointer}.file-uploader --input{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:.75rem}\n"] }]
264
+ }], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], showDownloadButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDownloadButton", required: false }] }], showDeleteButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDeleteButton", required: false }] }], uploading: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploading", required: false }] }], uploadedFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadedFiles", required: false }] }], uploadingError: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadingError", required: false }] }] } });
265
+
266
+ // The file-picker control. Its field and viewer stay in the primary entry point and reach it
267
+ // from there, so the control ships only to an app that renders it or imports it directly.
268
+
269
+ /**
270
+ * Generated bundle index. Do not edit.
271
+ */
272
+
273
+ export { Droppable, FilePickerControl };
274
+ //# sourceMappingURL=iss-ctrl-file-picker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iss-ctrl-file-picker.mjs","sources":["../../../projects/iss/file-picker/src/droppable.ts","../../../projects/iss/file-picker/src/file-picker.control.ts","../../../projects/iss/file-picker/src/public-api.ts","../../../projects/iss/file-picker/src/iss-ctrl-file-picker.ts"],"sourcesContent":["import { Directive, HostListener, output, OutputEmitterRef } from \"@angular/core\";\r\n\r\n@Directive({selector: \"[droppable]\"})\r\nexport class Droppable {\r\n onDrop: OutputEmitterRef<FileList> = output();\r\n\r\n @HostListener(\"dragover\", [\"$event\"])\r\n onDragOver(evt: any) {\r\n evt.preventDefault();\r\n evt.stopPropagation();\r\n }\r\n\r\n @HostListener(\"dragleave\", [\"$event\"])\r\n onDragLeave(evt: any) {\r\n evt.preventDefault();\r\n evt.stopPropagation();\r\n }\r\n\r\n @HostListener(\"drop\", [\"$event\"])\r\n onDropped(evt: any) {\r\n evt.preventDefault();\r\n evt.stopPropagation();\r\n this.onDrop.emit(evt.dataTransfer.files);\r\n }\r\n}\r\n","import { Component, computed, CUSTOM_ELEMENTS_SCHEMA, effect, input, InputSignal, OnDestroy, Signal, signal, WritableSignal } from \"@angular/core\";\nimport { NG_VALUE_ACCESSOR, ReactiveFormsModule } from \"@angular/forms\";\nimport { Hints } from \"iss-ctrl/base\";\nimport { ControlBase } from \"iss-ctrl/base\";\nimport { IssAttachment } from \"iss-ctrl/tokens\";\nimport { Droppable } from \"./droppable\";\nimport { IssFile } from \"./file\";\n\n/**\n * `<iss-ctrl-file-picker>` — attachment upload with a per-file progress and error state.\n *\n * Uploading is done by the host: the control emits the picked files and renders whatever is\n * fed back through `uploadedFiles`, `uploading` and `uploadingError`.\n *\n * @example\n * ```html\n * <iss-ctrl-file-picker\n * formControlName=\"attachments\"\n * [multiple]=\"true\"\n * accept=\".pdf,.png\"\n * [uploading]=\"uploading()\"\n * [uploadedFiles]=\"uploaded()\"\n * />\n * ```\n *\n * ### Own inputs\n * | Property | Type | Default | Description |\n * | --- | --- | --- | --- |\n * | `accept` | `string \\| undefined` | `undefined` | Accepted extensions or MIME types for the native picker. |\n * | `showDownloadButton` | `boolean` | `true` | Shows the download action on each attachment. |\n * | `showDeleteButton` | `boolean` | `true` | Shows the remove action on each attachment. |\n * | `uploading` | `boolean` | `false` | Renders the in-progress state. |\n * | `uploadedFiles` | `IssAttachment[]` | `[]` | Files already stored on the server. |\n * | `uploadingError` | `string \\| undefined` | `undefined` | Error message attached to the failed upload. |\n *\n * ### Inherited from {@link ControlBase}\n * `formControlName` · `readonly` · `className` · `(onChange)` · `multiple` · `hints`\n *\n * Projects a `<label>` above the control — the wrapping `iss-field-*` passes the field label in that way.\n */\n@Component({\n selector: \"iss-ctrl-file-picker\",\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n imports: [\n ReactiveFormsModule,\n Droppable,\n Hints,\n ],\n providers: [\n { provide: NG_VALUE_ACCESSOR, useExisting: FilePickerControl, multi: true }\n ],\n template: `\n <ng-content select=\"label\"></ng-content>\n <ds-file-picker (click)=\"openFilePicker(inputFile)\" (onDrop)=\"captureFile($event)\" droppable>\n <input\n #inputFile\n (change)=\"captureFile($any($event.target).files)\"\n (click)=\"$event.stopPropagation()\"\n [accept]=\"accept()\"\n [formControl]=\"ctrl\"\n [multiple]=\"multiple()\"\n [disabled]=\"readonly()\"\n [style.display]=\"'none'\"\n type=\"file\"\n />\n </ds-file-picker>\n\n <iss-ctrl-hints [hints]=\"hints()\" [touched]=\"touched()\"/>\n <!--<iss-viewer-attachment (deleteOut)=\"onDelete($event)\" [field]=\"filesValue()\" [uploading]=\"uploading()\"/>-->\n `,\n styles: `\n .file-uploader {\n --uploader-height: 150px;\n --uploader-width: 100%;\n --uploader-bg: var(--off-white);\n --uploader-border: 1px dashed var(--dark-gray);\n --uploader-radius: var(--box-radius);\n --uploader-padding: 1rem;\n\n height: var(--uploader-height);\n background-color: var(--uploader-bg);\n border: var(--uploader-border);\n border-radius: var(--uploader-radius);\n padding: var(--uploader-padding);\n text-align: center;\n cursor: pointer;\n\n & --input {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 0.75rem;\n }\n }`\n})\nexport class FilePickerControl extends ControlBase implements OnDestroy {\n /**\n * Accepted extensions or MIME types, passed straight to the native file input.\n */\n accept: InputSignal<string | undefined> = input();\n /**\n * Shows the download action on each listed attachment.\n */\n showDownloadButton: InputSignal<boolean> = input(true);\n /**\n * Shows the remove action on each listed attachment.\n */\n showDeleteButton: InputSignal<boolean> = input(true);\n /**\n * Renders the upload-in-progress state; set by the host that performs the upload.\n */\n uploading: InputSignal<boolean> = input(false);\n /**\n * Attachments already stored on the server, merged into the rendered list.\n */\n uploadedFiles: InputSignal<IssAttachment[]> = input<IssAttachment[]>([]);\n /**\n * Error message shown on the attachment whose upload failed.\n */\n uploadingError: InputSignal<string | undefined> = input<string | undefined>(undefined);\n readonly filesValue: Signal<any> = computed(() => this.#returnAttachmentViewObject());\n protected attachments: WritableSignal<IssFile[]> = signal([]);\n #uploadedFilesEffectRef = effect(() => {\n this.#markUploadedFiles();\n });\n\n /*todo needs review and refactor*/\n\n ngOnDestroy(): void {\n this.#uploadedFilesEffectRef.destroy();\n }\n\n override writeValue(val: FormData | string | null): void {\n if (typeof val === \"string\") {\n return;\n }\n this.#reset();\n if (val) {\n this.attachments.set([...this.attachments(), ...this.#convertFormDataToFiles(val)]);\n }\n }\n\n protected onDelete(files: IssFile[]): void {\n this.attachments.set(files);\n this.emitValue(this.#formDataFactory(this.attachments()));\n }\n\n protected captureFile($event: FileList) {\n if (this.readonly() || this.disabled()) {\n return;\n }\n if (!$event) {\n return;\n }\n if (!$event.length) {\n return;\n }\n this.markAsTouched();\n const files: File[] = Array.from($event);\n const issFiles: IssFile[] = files.map((file: File): IssFile => {\n return {\n name: file.name,\n size: file.size,\n type: file.type,\n selected: Date.now(),\n file: file,\n };\n });\n this.#updateAttachments(issFiles);\n // The field component uploads this output. Emit only the files selected in\n // this interaction: `attachments` also contains files selected earlier, so\n // serialising it would upload those files again whenever more are added.\n this.emitValue(this.#formDataFactory(issFiles));\n }\n\n protected openFilePicker(input: HTMLInputElement): void {\n if (this.readonly() || this.disabled()) {\n return;\n }\n input.click();\n this.markAsTouched();\n }\n\n #updateAttachments(selectedFiles: IssFile[]) {\n if (!this.multiple()) {\n this.attachments.set([selectedFiles[0]]);\n return;\n }\n this.attachments.update((files: IssFile[]) => {\n files = [...files, ...selectedFiles];\n return files;\n });\n }\n\n #formDataFactory(files: IssFile[]): FormData | null {\n if (files.length === 0) {\n return null;\n }\n const fd: FormData = new FormData();\n let formDataKey = \"file\";\n if (this.multiple()) {\n formDataKey = \"files\";\n }\n for (let i = 0; i < files.length; i++) {\n fd.append(formDataKey, (files[i].file as File));\n }\n return fd;\n }\n\n #reset() {\n this.attachments.set([]);\n }\n\n #convertFormDataToFiles(val: FormData): IssFile[] {\n if (!val || !(val instanceof FormData)) {\n return [];\n }\n const files: File[] = [];\n for (const pair of val.entries()) {\n if (pair[1] instanceof File) {\n files.push(pair[1]);\n }\n }\n return files;\n }\n\n #markUploadedFiles() {\n this.attachments.update((attachments: IssFile[]) => {\n return attachments.map((attachment: IssFile) => {\n // const uploadedFile = this.uploadedFiles().find((f) => f.selected === attachment.selected && f.name === attachment.name);\n if (this.uploadingError()) {\n attachment.errorMsg = this.uploadingError();\n }\n // if (uploadedFile) {\n // attachment.id = uploadedFile.id;\n // }\n return attachment;\n });\n });\n }\n\n #returnAttachmentViewObject() {\n return {\n config: {\n showDownloadButton: this.showDownloadButton(),\n showDeleteButton: this.showDeleteButton()\n },\n ctrlJsonValue: signal(this.attachments())\n };\n }\n}\n","// The file-picker control. Its field and viewer stay in the primary entry point and reach it\n// from there, so the control ships only to an app that renders it or imports it directly.\n\nexport * from \"./droppable\";\nexport * from \"./file-picker.control\";\nexport * from \"./file\";\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}