iss-ctrl 0.0.21 → 0.0.22
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.
- package/README.md +33 -0
- package/fesm2022/iss-ctrl-attachment.viewer-DVUL39xd.mjs +110 -0
- package/fesm2022/iss-ctrl-attachment.viewer-DVUL39xd.mjs.map +1 -0
- package/fesm2022/iss-ctrl-base.mjs +663 -0
- package/fesm2022/iss-ctrl-base.mjs.map +1 -0
- package/fesm2022/iss-ctrl-checkbox.field-Dj5EGLUr.mjs +92 -0
- package/fesm2022/iss-ctrl-checkbox.field-Dj5EGLUr.mjs.map +1 -0
- package/fesm2022/iss-ctrl-checkbox.mjs +143 -0
- package/fesm2022/iss-ctrl-checkbox.mjs.map +1 -0
- package/fesm2022/iss-ctrl-checkbox.viewer-C3SEUszR.mjs +31 -0
- package/fesm2022/iss-ctrl-checkbox.viewer-C3SEUszR.mjs.map +1 -0
- package/fesm2022/iss-ctrl-chips.field-BLLAjBbP.mjs +76 -0
- package/fesm2022/iss-ctrl-chips.field-BLLAjBbP.mjs.map +1 -0
- package/fesm2022/iss-ctrl-chips.mjs +168 -0
- package/fesm2022/iss-ctrl-chips.mjs.map +1 -0
- package/fesm2022/iss-ctrl-chips.viewer-BcKT2hcq.mjs +38 -0
- package/fesm2022/iss-ctrl-chips.viewer-BcKT2hcq.mjs.map +1 -0
- package/fesm2022/iss-ctrl-date-picker.field-BSpXukpO.mjs +98 -0
- package/fesm2022/iss-ctrl-date-picker.field-BSpXukpO.mjs.map +1 -0
- package/fesm2022/iss-ctrl-date-picker.mjs +200 -0
- package/fesm2022/iss-ctrl-date-picker.mjs.map +1 -0
- package/fesm2022/iss-ctrl-date-picker.viewer-Dsmsf91t.mjs +40 -0
- package/fesm2022/iss-ctrl-date-picker.viewer-Dsmsf91t.mjs.map +1 -0
- package/fesm2022/iss-ctrl-date-range-picker.field-CjNHU1RW.mjs +97 -0
- package/fesm2022/iss-ctrl-date-range-picker.field-CjNHU1RW.mjs.map +1 -0
- package/fesm2022/iss-ctrl-date-range-picker.mjs +307 -0
- package/fesm2022/iss-ctrl-date-range-picker.mjs.map +1 -0
- package/fesm2022/iss-ctrl-date-range-picker.viewer-tmTQGPFh.mjs +41 -0
- package/fesm2022/iss-ctrl-date-range-picker.viewer-tmTQGPFh.mjs.map +1 -0
- package/fesm2022/iss-ctrl-datetime-picker.field-DGMY9PaU.mjs +98 -0
- package/fesm2022/iss-ctrl-datetime-picker.field-DGMY9PaU.mjs.map +1 -0
- package/fesm2022/iss-ctrl-datetime-picker.mjs +226 -0
- package/fesm2022/iss-ctrl-datetime-picker.mjs.map +1 -0
- package/fesm2022/iss-ctrl-datetime-picker.viewer-BeUyEBKo.mjs +42 -0
- package/fesm2022/iss-ctrl-datetime-picker.viewer-BeUyEBKo.mjs.map +1 -0
- package/fesm2022/iss-ctrl-file-picker.field-CbepZaX6.mjs +210 -0
- package/fesm2022/iss-ctrl-file-picker.field-CbepZaX6.mjs.map +1 -0
- package/fesm2022/iss-ctrl-file-picker.mjs +272 -0
- package/fesm2022/iss-ctrl-file-picker.mjs.map +1 -0
- package/fesm2022/iss-ctrl-input.field-Ctlf3OCN.mjs +104 -0
- package/fesm2022/iss-ctrl-input.field-Ctlf3OCN.mjs.map +1 -0
- package/fesm2022/iss-ctrl-input.mjs +204 -0
- package/fesm2022/iss-ctrl-input.mjs.map +1 -0
- package/fesm2022/iss-ctrl-input.viewer-BOZnW0_e.mjs +22 -0
- package/fesm2022/iss-ctrl-input.viewer-BOZnW0_e.mjs.map +1 -0
- package/fesm2022/iss-ctrl-iss-ctrl-DvnkwSMG.mjs +1789 -0
- package/fesm2022/iss-ctrl-iss-ctrl-DvnkwSMG.mjs.map +1 -0
- package/fesm2022/{iss-ctrl-load-event-bOt6IlIo.mjs → iss-ctrl-load-event-CuZ7pMjk.mjs} +4 -3
- package/fesm2022/iss-ctrl-load-event-CuZ7pMjk.mjs.map +1 -0
- package/fesm2022/{iss-ctrl-load-options-event-CxyAyNMe.mjs → iss-ctrl-load-options-event-B10rhdqh.mjs} +3 -3
- package/fesm2022/{iss-ctrl-load-options-event-CxyAyNMe.mjs.map → iss-ctrl-load-options-event-B10rhdqh.mjs.map} +1 -1
- package/fesm2022/{iss-ctrl-load-value-event-DJKJ5S3h.mjs → iss-ctrl-load-value-event-C-aRgKLx.mjs} +3 -3
- package/fesm2022/{iss-ctrl-load-value-event-DJKJ5S3h.mjs.map → iss-ctrl-load-value-event-C-aRgKLx.mjs.map} +1 -1
- package/fesm2022/iss-ctrl-mobile.field-DXVYT3q-.mjs +77 -0
- package/fesm2022/iss-ctrl-mobile.field-DXVYT3q-.mjs.map +1 -0
- package/fesm2022/iss-ctrl-mobile.mjs +2465 -0
- package/fesm2022/iss-ctrl-mobile.mjs.map +1 -0
- package/fesm2022/iss-ctrl-mobile.viewer-DQgxAcR-.mjs +22 -0
- package/fesm2022/iss-ctrl-mobile.viewer-DQgxAcR-.mjs.map +1 -0
- package/fesm2022/{iss-ctrl-note.field-B_Lbp28X.mjs → iss-ctrl-note.field-C8X9-mgD.mjs} +2 -2
- package/fesm2022/{iss-ctrl-note.field-B_Lbp28X.mjs.map → iss-ctrl-note.field-C8X9-mgD.mjs.map} +1 -1
- package/fesm2022/iss-ctrl-plate.field-aI81tAOV.mjs +92 -0
- package/fesm2022/iss-ctrl-plate.field-aI81tAOV.mjs.map +1 -0
- package/fesm2022/iss-ctrl-plate.mjs +491 -0
- package/fesm2022/iss-ctrl-plate.mjs.map +1 -0
- package/fesm2022/iss-ctrl-plate.viewer-B8PUBLH8.mjs +179 -0
- package/fesm2022/iss-ctrl-plate.viewer-B8PUBLH8.mjs.map +1 -0
- package/fesm2022/iss-ctrl-radio.field-B_JjPIlb.mjs +96 -0
- package/fesm2022/iss-ctrl-radio.field-B_JjPIlb.mjs.map +1 -0
- package/fesm2022/iss-ctrl-radio.mjs +101 -0
- package/fesm2022/iss-ctrl-radio.mjs.map +1 -0
- package/fesm2022/iss-ctrl-radio.viewer-EiDliZro.mjs +30 -0
- package/fesm2022/iss-ctrl-radio.viewer-EiDliZro.mjs.map +1 -0
- package/fesm2022/{iss-ctrl-replace-api-keys-C0XYQW4i.mjs → iss-ctrl-replace-api-keys-DLnagkOr.mjs} +2 -2
- package/fesm2022/{iss-ctrl-replace-api-keys-C0XYQW4i.mjs.map → iss-ctrl-replace-api-keys-DLnagkOr.mjs.map} +1 -1
- package/fesm2022/iss-ctrl-select.field-DoU4G_8t.mjs +87 -0
- package/fesm2022/iss-ctrl-select.field-DoU4G_8t.mjs.map +1 -0
- package/fesm2022/iss-ctrl-select.mjs +387 -0
- package/fesm2022/iss-ctrl-select.mjs.map +1 -0
- package/fesm2022/iss-ctrl-select.viewer-Dv9M-lG-.mjs +30 -0
- package/fesm2022/iss-ctrl-select.viewer-Dv9M-lG-.mjs.map +1 -0
- package/fesm2022/{iss-ctrl-set-value-event-CgXhi0Bv.mjs → iss-ctrl-set-value-event-Dhw0Vj5J.mjs} +2 -2
- package/fesm2022/{iss-ctrl-set-value-event-CgXhi0Bv.mjs.map → iss-ctrl-set-value-event-Dhw0Vj5J.mjs.map} +1 -1
- package/fesm2022/iss-ctrl-slide-toggle.field-TVVbAr6p.mjs +85 -0
- package/fesm2022/iss-ctrl-slide-toggle.field-TVVbAr6p.mjs.map +1 -0
- package/fesm2022/iss-ctrl-slide-toggle.mjs +72 -0
- package/fesm2022/iss-ctrl-slide-toggle.mjs.map +1 -0
- package/fesm2022/iss-ctrl-slide-toggle.viewer-hsIHwDj3.mjs +34 -0
- package/fesm2022/iss-ctrl-slide-toggle.viewer-hsIHwDj3.mjs.map +1 -0
- package/fesm2022/{iss-ctrl-table.field-B5FxF6hn.mjs → iss-ctrl-table.field-E0r9w0os.mjs} +142 -140
- package/fesm2022/iss-ctrl-table.field-E0r9w0os.mjs.map +1 -0
- package/fesm2022/iss-ctrl-textarea.field-CmHPDpk-.mjs +82 -0
- package/fesm2022/iss-ctrl-textarea.field-CmHPDpk-.mjs.map +1 -0
- package/fesm2022/iss-ctrl-textarea.mjs +115 -0
- package/fesm2022/iss-ctrl-textarea.mjs.map +1 -0
- package/fesm2022/iss-ctrl-textarea.viewer-CpmLauik.mjs +22 -0
- package/fesm2022/iss-ctrl-textarea.viewer-CpmLauik.mjs.map +1 -0
- package/fesm2022/iss-ctrl-time-picker.field-Cb0I5Ut1.mjs +77 -0
- package/fesm2022/iss-ctrl-time-picker.field-Cb0I5Ut1.mjs.map +1 -0
- package/fesm2022/iss-ctrl-time-picker.mjs +170 -0
- package/fesm2022/iss-ctrl-time-picker.mjs.map +1 -0
- package/fesm2022/iss-ctrl-time-picker.viewer-CJeFLrdI.mjs +22 -0
- package/fesm2022/iss-ctrl-time-picker.viewer-CJeFLrdI.mjs.map +1 -0
- package/fesm2022/iss-ctrl-toggle-button.field-Bj8WrkB-.mjs +105 -0
- package/fesm2022/iss-ctrl-toggle-button.field-Bj8WrkB-.mjs.map +1 -0
- package/fesm2022/iss-ctrl-toggle-button.mjs +156 -0
- package/fesm2022/iss-ctrl-toggle-button.mjs.map +1 -0
- package/fesm2022/iss-ctrl-toggle-button.viewer-CC-8_Dy4.mjs +30 -0
- package/fesm2022/iss-ctrl-toggle-button.viewer-CC-8_Dy4.mjs.map +1 -0
- package/fesm2022/iss-ctrl-tokens.mjs +25 -0
- package/fesm2022/iss-ctrl-tokens.mjs.map +1 -0
- package/fesm2022/{iss-ctrl-trigger-based-on-json-value-2v1kLVHZ.mjs → iss-ctrl-trigger-based-on-json-value-D9rTXVD1.mjs} +2 -2
- package/fesm2022/{iss-ctrl-trigger-based-on-json-value-2v1kLVHZ.mjs.map → iss-ctrl-trigger-based-on-json-value-D9rTXVD1.mjs.map} +1 -1
- package/fesm2022/{iss-ctrl-user-picker.field-BH63sZ-X.mjs → iss-ctrl-user-picker.field-BIzB5ZX6.mjs} +151 -150
- package/fesm2022/iss-ctrl-user-picker.field-BIzB5ZX6.mjs.map +1 -0
- package/fesm2022/iss-ctrl.mjs +3 -1
- package/fesm2022/iss-ctrl.mjs.map +1 -1
- package/package.json +69 -1
- package/types/iss-ctrl-base.d.ts +306 -0
- package/types/iss-ctrl-checkbox.d.ts +45 -0
- package/types/iss-ctrl-chips.d.ts +41 -0
- package/types/iss-ctrl-date-picker.d.ts +75 -0
- package/types/iss-ctrl-date-range-picker.d.ts +95 -0
- package/types/iss-ctrl-datetime-picker.d.ts +77 -0
- package/types/iss-ctrl-file-picker.d.ts +95 -0
- package/types/iss-ctrl-input.d.ts +76 -0
- package/types/iss-ctrl-mobile.d.ts +91 -0
- package/types/iss-ctrl-plate.d.ts +54 -0
- package/types/iss-ctrl-radio.d.ts +34 -0
- package/types/iss-ctrl-select.d.ts +75 -0
- package/types/iss-ctrl-slide-toggle.d.ts +26 -0
- package/types/iss-ctrl-textarea.d.ts +30 -0
- package/types/iss-ctrl-time-picker.d.ts +48 -0
- package/types/iss-ctrl-toggle-button.d.ts +41 -0
- package/types/iss-ctrl-tokens.d.ts +21 -0
- package/types/iss-ctrl.d.ts +120 -1810
- package/fesm2022/iss-ctrl-iss-ctrl-BKu6a5HF.mjs +0 -9683
- package/fesm2022/iss-ctrl-iss-ctrl-BKu6a5HF.mjs.map +0 -1
- package/fesm2022/iss-ctrl-load-event-bOt6IlIo.mjs.map +0 -1
- package/fesm2022/iss-ctrl-table.field-B5FxF6hn.mjs.map +0 -1
- package/fesm2022/iss-ctrl-user-picker.field-BH63sZ-X.mjs.map +0 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, Component } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/forms';
|
|
4
|
+
import { FormGroup, FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
|
|
5
|
+
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
6
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
7
|
+
import { MatInputModule } from '@angular/material/input';
|
|
8
|
+
import { MatTimepickerModule } from '@angular/material/timepicker';
|
|
9
|
+
import { format } from 'date-fns';
|
|
10
|
+
import { ControlBase } from 'iss-ctrl/base';
|
|
11
|
+
import { DatePickerControl } from 'iss-ctrl/date-picker';
|
|
12
|
+
import { TimePickerControl } from 'iss-ctrl/time-picker';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `<iss-ctrl-datetime-picker>` — a date and a time in a single field.
|
|
16
|
+
*
|
|
17
|
+
* Combines the calendar of the date picker with the time list of the time picker; both parts
|
|
18
|
+
* must be set before a value is emitted.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```html
|
|
22
|
+
* <iss-ctrl-datetime-picker formControlName="scheduledAt" [minDate]="today" minTime="08:00" maxTime="17:00">
|
|
23
|
+
* <label>Scheduled at</label>
|
|
24
|
+
* </iss-ctrl-datetime-picker>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* ### Own inputs
|
|
28
|
+
* | Property | Type | Default | Description |
|
|
29
|
+
* | --- | --- | --- | --- |
|
|
30
|
+
* | `minDate` | `Date \| null` | `null` | Earliest selectable date. |
|
|
31
|
+
* | `maxDate` | `Date \| null` | `null` | Latest selectable date. |
|
|
32
|
+
* | `minTime` | `string \| null` | `null` | Earliest selectable time, `HH:mm`. |
|
|
33
|
+
* | `maxTime` | `string \| null` | `null` | Latest selectable time, `HH:mm`. |
|
|
34
|
+
* | `disabledDays` | `number[]` | `[]` | Week days to block, `0` = Sunday … `6` = Saturday. |
|
|
35
|
+
* | `hijriCalendar` | `boolean` | `false` | Shows an Umm al-Qura Hijri calendar; the value stays Gregorian. |
|
|
36
|
+
* | `emitHijri` | `boolean` | `false` | Emits the date half as a Hijri date. Implies `hijriCalendar`. |
|
|
37
|
+
*
|
|
38
|
+
* ### Hijri dates
|
|
39
|
+
* Both flags are handed straight to the inner date picker; the time half is untouched, so a
|
|
40
|
+
* Hijri value reads `1447-03-15T09:30:00`.
|
|
41
|
+
*
|
|
42
|
+
* ### Inherited from {@link ControlBase}
|
|
43
|
+
* `formControlName` · `readonly` · `className` · `(onChange)`
|
|
44
|
+
*
|
|
45
|
+
* Projects `<label>` into the floating label; omit it to render the control without one.
|
|
46
|
+
*/
|
|
47
|
+
class DatetimePickerControl extends ControlBase {
|
|
48
|
+
datetimeForm = new FormGroup({
|
|
49
|
+
date: new FormControl(null),
|
|
50
|
+
time: new FormControl(null),
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Week days that cannot be selected — `0` is Sunday through `6` for Saturday.
|
|
54
|
+
*/
|
|
55
|
+
disabledDays = input([], ...(ngDevMode ? [{ debugName: "disabledDays" }] : /* istanbul ignore next */ []));
|
|
56
|
+
/**
|
|
57
|
+
* Earliest selectable date.
|
|
58
|
+
*/
|
|
59
|
+
minDate = input(null, ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
|
|
60
|
+
/**
|
|
61
|
+
* Latest selectable date.
|
|
62
|
+
*/
|
|
63
|
+
maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
|
|
64
|
+
/**
|
|
65
|
+
* Earliest selectable time of day, as `HH:mm`.
|
|
66
|
+
*/
|
|
67
|
+
minTime = input(null, ...(ngDevMode ? [{ debugName: "minTime" }] : /* istanbul ignore next */ []));
|
|
68
|
+
/**
|
|
69
|
+
* Latest selectable time of day, as `HH:mm`.
|
|
70
|
+
*/
|
|
71
|
+
maxTime = input(null, ...(ngDevMode ? [{ debugName: "maxTime" }] : /* istanbul ignore next */ []));
|
|
72
|
+
/**
|
|
73
|
+
* Renders the calendar as an Umm al-Qura Hijri calendar, leaving the emitted date Gregorian.
|
|
74
|
+
*/
|
|
75
|
+
hijriCalendar = input(false, ...(ngDevMode ? [{ debugName: "hijriCalendar" }] : /* istanbul ignore next */ []));
|
|
76
|
+
/**
|
|
77
|
+
* Emits the date half of the value as a Hijri date. Implies {@link hijriCalendar}.
|
|
78
|
+
*/
|
|
79
|
+
emitHijri = input(false, ...(ngDevMode ? [{ debugName: "emitHijri" }] : /* istanbul ignore next */ []));
|
|
80
|
+
setDisabledState(isDisabled) {
|
|
81
|
+
super.setDisabledState(isDisabled);
|
|
82
|
+
// the template binds `datetimeForm`, disabling ctrl alone leaves the pickers live
|
|
83
|
+
if (isDisabled) {
|
|
84
|
+
this.datetimeForm.disable({ emitEvent: false });
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
this.datetimeForm.enable({ emitEvent: false });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
emit() {
|
|
91
|
+
if (this.readonly()) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const datetimeStr = this.#stringifyDateTime(this.datetimeForm.getRawValue());
|
|
95
|
+
this.propagateValue(datetimeStr);
|
|
96
|
+
this.emitOnChangeEvent(datetimeStr);
|
|
97
|
+
}
|
|
98
|
+
writeValue(value) {
|
|
99
|
+
this.datetimeForm.patchValue({
|
|
100
|
+
date: this.#stringifyDate(value),
|
|
101
|
+
time: this.#stringifyTime(value),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
#stringifyDateTime(datetime) {
|
|
105
|
+
if (!datetime) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
if (this.#stringifyDate(datetime) && this.#stringifyTime(datetime)) {
|
|
109
|
+
return `${this.#stringifyDate(datetime)}T${this.#stringifyTime(datetime)}`;
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
#stringifyDate(datetime) {
|
|
114
|
+
if (!datetime) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
if (datetime.date && typeof datetime.date === "string") {
|
|
118
|
+
return `${datetime.date.split("T")[0]}`;
|
|
119
|
+
}
|
|
120
|
+
const date = new Date(datetime);
|
|
121
|
+
if (!Number.isNaN(date.getTime())) {
|
|
122
|
+
return format(datetime, "yyyy-MM-dd");
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
#stringifyTime(datetime) {
|
|
127
|
+
if (!datetime) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
if (datetime.time && typeof datetime.time === "string") {
|
|
131
|
+
return `${datetime.time}`;
|
|
132
|
+
}
|
|
133
|
+
const date = new Date(datetime);
|
|
134
|
+
if (!Number.isNaN(date.getTime())) {
|
|
135
|
+
return format(datetime, "HH:mm:ss");
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatetimePickerControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
140
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: DatetimePickerControl, isStandalone: true, selector: "iss-ctrl-datetime-picker", inputs: { disabledDays: { classPropertyName: "disabledDays", publicName: "disabledDays", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, minTime: { classPropertyName: "minTime", publicName: "minTime", isSignal: true, isRequired: false, transformFunction: null }, maxTime: { classPropertyName: "maxTime", publicName: "maxTime", isSignal: true, isRequired: false, transformFunction: null }, hijriCalendar: { classPropertyName: "hijriCalendar", publicName: "hijriCalendar", isSignal: true, isRequired: false, transformFunction: null }, emitHijri: { classPropertyName: "emitHijri", publicName: "emitHijri", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
141
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: DatetimePickerControl, multi: true },
|
|
142
|
+
{ provide: NG_VALIDATORS, useExisting: DatetimePickerControl, multi: true },
|
|
143
|
+
], usesInheritance: true, ngImport: i0, template: `
|
|
144
|
+
<form [formGroup]="datetimeForm" class="d-flex flex-row gap-1">
|
|
145
|
+
<iss-ctrl-date-picker
|
|
146
|
+
(focusout)="markAsTouched()"
|
|
147
|
+
[disabledDays]="disabledDays()"
|
|
148
|
+
[maxDate]="maxDate()"
|
|
149
|
+
[minDate]="minDate()"
|
|
150
|
+
[readonly]="readonly()"
|
|
151
|
+
[hijriCalendar]="hijriCalendar()"
|
|
152
|
+
[emitHijri]="emitHijri()"
|
|
153
|
+
formControlName="date"
|
|
154
|
+
(onChange)="emit()"
|
|
155
|
+
>
|
|
156
|
+
<!-- Re-projected with ngProjectAs so it still matches the date picker's own select="label";
|
|
157
|
+
wrapping it in a second <label> instead would nest one inside the other and defeat the label rule in upgrade.scss. -->
|
|
158
|
+
<ng-content select="label" ngProjectAs="label"></ng-content>
|
|
159
|
+
</iss-ctrl-date-picker>
|
|
160
|
+
<iss-ctrl-time-picker
|
|
161
|
+
(focusout)="markAsTouched()"
|
|
162
|
+
[maxTime]="maxTime()"
|
|
163
|
+
[minTime]="minTime()"
|
|
164
|
+
[readonly]="readonly()"
|
|
165
|
+
formControlName="time"
|
|
166
|
+
(onChange)="emit()"
|
|
167
|
+
/>
|
|
168
|
+
</form>
|
|
169
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatTimepickerModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: DatePickerControl, selector: "iss-ctrl-date-picker", inputs: ["disabledDays", "minDate", "maxDate", "hijriCalendar", "emitHijri"] }, { kind: "component", type: TimePickerControl, selector: "iss-ctrl-time-picker", inputs: ["interval", "minTime", "maxTime"] }] });
|
|
170
|
+
}
|
|
171
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatetimePickerControl, decorators: [{
|
|
172
|
+
type: Component,
|
|
173
|
+
args: [{
|
|
174
|
+
selector: "iss-ctrl-datetime-picker",
|
|
175
|
+
imports: [
|
|
176
|
+
MatFormFieldModule,
|
|
177
|
+
MatInputModule,
|
|
178
|
+
MatTimepickerModule,
|
|
179
|
+
MatDatepickerModule,
|
|
180
|
+
ReactiveFormsModule,
|
|
181
|
+
DatePickerControl,
|
|
182
|
+
TimePickerControl,
|
|
183
|
+
],
|
|
184
|
+
providers: [
|
|
185
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: DatetimePickerControl, multi: true },
|
|
186
|
+
{ provide: NG_VALIDATORS, useExisting: DatetimePickerControl, multi: true },
|
|
187
|
+
],
|
|
188
|
+
template: `
|
|
189
|
+
<form [formGroup]="datetimeForm" class="d-flex flex-row gap-1">
|
|
190
|
+
<iss-ctrl-date-picker
|
|
191
|
+
(focusout)="markAsTouched()"
|
|
192
|
+
[disabledDays]="disabledDays()"
|
|
193
|
+
[maxDate]="maxDate()"
|
|
194
|
+
[minDate]="minDate()"
|
|
195
|
+
[readonly]="readonly()"
|
|
196
|
+
[hijriCalendar]="hijriCalendar()"
|
|
197
|
+
[emitHijri]="emitHijri()"
|
|
198
|
+
formControlName="date"
|
|
199
|
+
(onChange)="emit()"
|
|
200
|
+
>
|
|
201
|
+
<!-- Re-projected with ngProjectAs so it still matches the date picker's own select="label";
|
|
202
|
+
wrapping it in a second <label> instead would nest one inside the other and defeat the label rule in upgrade.scss. -->
|
|
203
|
+
<ng-content select="label" ngProjectAs="label"></ng-content>
|
|
204
|
+
</iss-ctrl-date-picker>
|
|
205
|
+
<iss-ctrl-time-picker
|
|
206
|
+
(focusout)="markAsTouched()"
|
|
207
|
+
[maxTime]="maxTime()"
|
|
208
|
+
[minTime]="minTime()"
|
|
209
|
+
[readonly]="readonly()"
|
|
210
|
+
formControlName="time"
|
|
211
|
+
(onChange)="emit()"
|
|
212
|
+
/>
|
|
213
|
+
</form>
|
|
214
|
+
`
|
|
215
|
+
}]
|
|
216
|
+
}], propDecorators: { disabledDays: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDays", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], minTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "minTime", required: false }] }], maxTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxTime", required: false }] }], hijriCalendar: [{ type: i0.Input, args: [{ isSignal: true, alias: "hijriCalendar", required: false }] }], emitHijri: [{ type: i0.Input, args: [{ isSignal: true, alias: "emitHijri", required: false }] }] } });
|
|
217
|
+
|
|
218
|
+
// The datetime-picker control. Its field and viewer stay in the primary entry point and reach it
|
|
219
|
+
// from there, so the control ships only to an app that renders it or imports it directly.
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Generated bundle index. Do not edit.
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
export { DatetimePickerControl };
|
|
226
|
+
//# sourceMappingURL=iss-ctrl-datetime-picker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iss-ctrl-datetime-picker.mjs","sources":["../../../projects/iss/datetime-picker/src/datetime-picker.control.ts","../../../projects/iss/datetime-picker/src/public-api.ts","../../../projects/iss/datetime-picker/src/iss-ctrl-datetime-picker.ts"],"sourcesContent":["import { Component, input, InputSignal } from \"@angular/core\";\nimport { FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, ReactiveFormsModule } from \"@angular/forms\";\nimport { MatDatepickerModule } from \"@angular/material/datepicker\";\nimport { MatFormFieldModule } from \"@angular/material/form-field\";\nimport { MatInputModule } from \"@angular/material/input\";\nimport { MatTimepickerModule } from \"@angular/material/timepicker\";\nimport { format } from \"date-fns\";\nimport { ControlBase } from \"iss-ctrl/base\";\nimport { DatePickerControl } from \"iss-ctrl/date-picker\";\nimport { TimePickerControl } from \"iss-ctrl/time-picker\";\n\n/**\n * `<iss-ctrl-datetime-picker>` — a date and a time in a single field.\n *\n * Combines the calendar of the date picker with the time list of the time picker; both parts\n * must be set before a value is emitted.\n *\n * @example\n * ```html\n * <iss-ctrl-datetime-picker formControlName=\"scheduledAt\" [minDate]=\"today\" minTime=\"08:00\" maxTime=\"17:00\">\n * <label>Scheduled at</label>\n * </iss-ctrl-datetime-picker>\n * ```\n *\n * ### Own inputs\n * | Property | Type | Default | Description |\n * | --- | --- | --- | --- |\n * | `minDate` | `Date \\| null` | `null` | Earliest selectable date. |\n * | `maxDate` | `Date \\| null` | `null` | Latest selectable date. |\n * | `minTime` | `string \\| null` | `null` | Earliest selectable time, `HH:mm`. |\n * | `maxTime` | `string \\| null` | `null` | Latest selectable time, `HH:mm`. |\n * | `disabledDays` | `number[]` | `[]` | Week days to block, `0` = Sunday … `6` = Saturday. |\n * | `hijriCalendar` | `boolean` | `false` | Shows an Umm al-Qura Hijri calendar; the value stays Gregorian. |\n * | `emitHijri` | `boolean` | `false` | Emits the date half as a Hijri date. Implies `hijriCalendar`. |\n *\n * ### Hijri dates\n * Both flags are handed straight to the inner date picker; the time half is untouched, so a\n * Hijri value reads `1447-03-15T09:30:00`.\n *\n * ### Inherited from {@link ControlBase}\n * `formControlName` · `readonly` · `className` · `(onChange)`\n *\n * Projects `<label>` into the floating label; omit it to render the control without one.\n */\n@Component({\n selector: \"iss-ctrl-datetime-picker\",\n imports: [\n MatFormFieldModule,\n MatInputModule,\n MatTimepickerModule,\n MatDatepickerModule,\n ReactiveFormsModule,\n DatePickerControl,\n TimePickerControl,\n ],\n providers: [\n { provide: NG_VALUE_ACCESSOR, useExisting: DatetimePickerControl, multi: true },\n { provide: NG_VALIDATORS, useExisting: DatetimePickerControl, multi: true },\n ],\n template: `\n <form [formGroup]=\"datetimeForm\" class=\"d-flex flex-row gap-1\">\n <iss-ctrl-date-picker\n (focusout)=\"markAsTouched()\"\n [disabledDays]=\"disabledDays()\"\n [maxDate]=\"maxDate()\"\n [minDate]=\"minDate()\"\n [readonly]=\"readonly()\"\n [hijriCalendar]=\"hijriCalendar()\"\n [emitHijri]=\"emitHijri()\"\n formControlName=\"date\"\n (onChange)=\"emit()\"\n >\n <!-- Re-projected with ngProjectAs so it still matches the date picker's own select=\"label\";\n wrapping it in a second <label> instead would nest one inside the other and defeat the label rule in upgrade.scss. -->\n <ng-content select=\"label\" ngProjectAs=\"label\"></ng-content>\n </iss-ctrl-date-picker>\n <iss-ctrl-time-picker\n (focusout)=\"markAsTouched()\"\n [maxTime]=\"maxTime()\"\n [minTime]=\"minTime()\"\n [readonly]=\"readonly()\"\n formControlName=\"time\"\n (onChange)=\"emit()\"\n />\n </form>\n `\n})\nexport class DatetimePickerControl extends ControlBase {\n datetimeForm: FormGroup = new FormGroup({\n date: new FormControl(null),\n time: new FormControl(null),\n });\n /**\n * Week days that cannot be selected — `0` is Sunday through `6` for Saturday.\n */\n disabledDays: InputSignal<number[]> = input<number[]>([]);\n /**\n * Earliest selectable date.\n */\n minDate: InputSignal<Date | null> = input<Date | null>(null);\n /**\n * Latest selectable date.\n */\n maxDate: InputSignal<Date | null> = input<Date | null>(null);\n /**\n * Earliest selectable time of day, as `HH:mm`.\n */\n minTime: InputSignal<string | null> = input<string | null>(null);\n /**\n * Latest selectable time of day, as `HH:mm`.\n */\n maxTime: InputSignal<string | null> = input<string | null>(null);\n /**\n * Renders the calendar as an Umm al-Qura Hijri calendar, leaving the emitted date Gregorian.\n */\n hijriCalendar: InputSignal<boolean> = input(false);\n /**\n * Emits the date half of the value as a Hijri date. Implies {@link hijriCalendar}.\n */\n emitHijri: InputSignal<boolean> = input(false);\n\n override setDisabledState(isDisabled: boolean): void {\n super.setDisabledState(isDisabled);\n // the template binds `datetimeForm`, disabling ctrl alone leaves the pickers live\n if (isDisabled) {\n this.datetimeForm.disable({ emitEvent: false });\n } else {\n this.datetimeForm.enable({ emitEvent: false });\n }\n }\n\n emit() {\n if (this.readonly()) {\n return;\n }\n const datetimeStr = this.#stringifyDateTime(this.datetimeForm.getRawValue());\n this.propagateValue(datetimeStr);\n this.emitOnChangeEvent(datetimeStr);\n }\n\n override writeValue(value: string | Date | null) {\n this.datetimeForm.patchValue({\n date: this.#stringifyDate(value),\n time: this.#stringifyTime(value),\n });\n }\n\n #stringifyDateTime(datetime: any): string | undefined {\n if (!datetime) {\n return undefined;\n }\n if (this.#stringifyDate(datetime) && this.#stringifyTime(datetime)) {\n return `${this.#stringifyDate(datetime)}T${this.#stringifyTime(datetime)}`;\n }\n return undefined;\n }\n\n #stringifyDate(datetime: any) {\n if (!datetime) {\n return undefined;\n }\n\n if (datetime.date && typeof datetime.date === \"string\") {\n return `${datetime.date.split(\"T\")[0]}`;\n }\n\n const date = new Date(datetime);\n if (!Number.isNaN(date.getTime())) {\n return format(datetime, \"yyyy-MM-dd\");\n }\n return undefined;\n }\n\n #stringifyTime(datetime: any) {\n if (!datetime) {\n return undefined;\n }\n\n if (datetime.time && typeof datetime.time === \"string\") {\n return `${datetime.time}`;\n }\n\n const date = new Date(datetime);\n if (!Number.isNaN(date.getTime())) {\n return format(datetime, \"HH:mm:ss\");\n }\n return undefined;\n }\n}\n","// The datetime-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 \"./datetime-picker.control\";\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { DatePipe } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { computed, Component } from '@angular/core';
|
|
4
|
+
import { ViewerBase, DATETIME_FORMAT, toHijriDisplay } from 'iss-ctrl/base';
|
|
5
|
+
|
|
6
|
+
class DatePickerTimeViewer extends ViewerBase {
|
|
7
|
+
DATETIME_FORMAT = DATETIME_FORMAT;
|
|
8
|
+
// a field picked on the Hijri calendar reads back as a Hijri date, whichever calendar its
|
|
9
|
+
// stored value is in
|
|
10
|
+
hijriDatetime = computed(() => toHijriDisplay(this.field().ctrlValue(), {
|
|
11
|
+
hijriCalendar: this.field().config?.hijriCalendar(),
|
|
12
|
+
emitHijri: this.field().config?.emitHijri(),
|
|
13
|
+
withTime: true,
|
|
14
|
+
}), ...(ngDevMode ? [{ debugName: "hijriDatetime" }] : /* istanbul ignore next */ []));
|
|
15
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatePickerTimeViewer, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
16
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DatePickerTimeViewer, isStandalone: true, selector: "iss-viewer-datetime-picker", usesInheritance: true, ngImport: i0, template: `
|
|
17
|
+
@if (hijriDatetime(); as hijri) {
|
|
18
|
+
{{ hijri }}
|
|
19
|
+
} @else {
|
|
20
|
+
{{ field().ctrlValue() | date: DATETIME_FORMAT }}
|
|
21
|
+
}
|
|
22
|
+
`, isInline: true, dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }] });
|
|
23
|
+
}
|
|
24
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatePickerTimeViewer, decorators: [{
|
|
25
|
+
type: Component,
|
|
26
|
+
args: [{
|
|
27
|
+
selector: "iss-viewer-datetime-picker",
|
|
28
|
+
imports: [
|
|
29
|
+
DatePipe
|
|
30
|
+
],
|
|
31
|
+
template: `
|
|
32
|
+
@if (hijriDatetime(); as hijri) {
|
|
33
|
+
{{ hijri }}
|
|
34
|
+
} @else {
|
|
35
|
+
{{ field().ctrlValue() | date: DATETIME_FORMAT }}
|
|
36
|
+
}
|
|
37
|
+
`
|
|
38
|
+
}]
|
|
39
|
+
}] });
|
|
40
|
+
|
|
41
|
+
export { DatePickerTimeViewer };
|
|
42
|
+
//# sourceMappingURL=iss-ctrl-datetime-picker.viewer-BeUyEBKo.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iss-ctrl-datetime-picker.viewer-BeUyEBKo.mjs","sources":["../../../projects/iss/src/components/controls/datetime-picker/datetime-picker.viewer.ts"],"sourcesContent":["import { DatePipe } from \"@angular/common\";\nimport { Component, computed, Signal } from \"@angular/core\";\nimport { DATETIME_FORMAT } from \"iss-ctrl/base\";\nimport { toHijriDisplay } from \"iss-ctrl/base\";\nimport { ViewerBase } from \"iss-ctrl/base\";\n\n@Component({\n selector: \"iss-viewer-datetime-picker\",\n imports: [\n DatePipe\n ],\n template: `\n @if (hijriDatetime(); as hijri) {\n {{ hijri }}\n } @else {\n {{ field().ctrlValue() | date: DATETIME_FORMAT }}\n }\n `\n})\nexport class DatePickerTimeViewer extends ViewerBase {\n protected readonly DATETIME_FORMAT = DATETIME_FORMAT;\n // a field picked on the Hijri calendar reads back as a Hijri date, whichever calendar its\n // stored value is in\n protected hijriDatetime: Signal<string | null> = computed(() => toHijriDisplay(this.field().ctrlValue(), {\n hijriCalendar: this.field().config?.hijriCalendar(),\n emitHijri: this.field().config?.emitHijri(),\n withTime: true,\n }));\n}\n"],"names":[],"mappings":";;;;;AAmBM,MAAO,oBAAqB,SAAQ,UAAU,CAAA;IAC/B,eAAe,GAAG,eAAe;;;AAG1C,IAAA,aAAa,GAA0B,QAAQ,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,EAAE;QACvG,aAAa,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE;QACnD,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE;AAC3C,QAAA,QAAQ,EAAE,IAAI;AACf,KAAA,CAAC,oFAAC;uGARQ,oBAAoB,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAApB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,oBAAoB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,4BAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EARrB;;;;;;AAMT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EARC,QAAQ,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA;;2FAUC,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBAbhC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,4BAA4B;AACtC,oBAAA,OAAO,EAAE;wBACP;AACD,qBAAA;AACD,oBAAA,QAAQ,EAAE;;;;;;AAMT,EAAA;AACF,iBAAA;;;;;"}
|
|
@@ -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-DvnkwSMG.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-CbepZaX6.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iss-ctrl-file-picker.field-CbepZaX6.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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"}
|