@tft/crispr-forms 21.0.16 → 21.0.17
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 +47 -0
- package/fesm2022/{tft-crispr-forms-autocomplete-field.component-D5Hx6_m-.mjs → tft-crispr-forms-autocomplete-field.component-BgQZYqB0.mjs} +3 -3
- package/fesm2022/{tft-crispr-forms-autocomplete-field.component-D5Hx6_m-.mjs.map → tft-crispr-forms-autocomplete-field.component-BgQZYqB0.mjs.map} +1 -1
- package/fesm2022/{tft-crispr-forms-index-CLbh6FgL.mjs → tft-crispr-forms-index-3aw152gK.mjs} +2 -2
- package/fesm2022/{tft-crispr-forms-index-CLbh6FgL.mjs.map → tft-crispr-forms-index-3aw152gK.mjs.map} +1 -1
- package/fesm2022/{tft-crispr-forms-index-BD0TGTEL.mjs → tft-crispr-forms-index-BFzx83fR.mjs} +2 -2
- package/fesm2022/{tft-crispr-forms-index-BD0TGTEL.mjs.map → tft-crispr-forms-index-BFzx83fR.mjs.map} +1 -1
- package/fesm2022/{tft-crispr-forms-index-BuSXb4-S.mjs → tft-crispr-forms-index-BMZQakFs.mjs} +3 -3
- package/fesm2022/{tft-crispr-forms-index-BuSXb4-S.mjs.map → tft-crispr-forms-index-BMZQakFs.mjs.map} +1 -1
- package/fesm2022/tft-crispr-forms-index-B_lhA83w.mjs +2 -0
- package/fesm2022/tft-crispr-forms-index-B_lhA83w.mjs.map +1 -0
- package/fesm2022/{tft-crispr-forms-index-WIb0_UHp.mjs → tft-crispr-forms-index-BawhjTMp.mjs} +3 -3
- package/fesm2022/{tft-crispr-forms-index-WIb0_UHp.mjs.map → tft-crispr-forms-index-BawhjTMp.mjs.map} +1 -1
- package/fesm2022/{tft-crispr-forms-index-CcgQXC4a.mjs → tft-crispr-forms-index-CXXD0vdZ.mjs} +12 -22
- package/fesm2022/tft-crispr-forms-index-CXXD0vdZ.mjs.map +1 -0
- package/fesm2022/{tft-crispr-forms-index-BDIWv-PS.mjs → tft-crispr-forms-index-CmVahfkJ.mjs} +2 -2
- package/fesm2022/{tft-crispr-forms-index-BDIWv-PS.mjs.map → tft-crispr-forms-index-CmVahfkJ.mjs.map} +1 -1
- package/fesm2022/tft-crispr-forms-index-CoUwTugQ.mjs +309 -0
- package/fesm2022/tft-crispr-forms-index-CoUwTugQ.mjs.map +1 -0
- package/fesm2022/{tft-crispr-forms-index-CTtb80yI.mjs → tft-crispr-forms-index-D99OHDPc.mjs} +2 -2
- package/fesm2022/{tft-crispr-forms-index-CTtb80yI.mjs.map → tft-crispr-forms-index-D99OHDPc.mjs.map} +1 -1
- package/fesm2022/{tft-crispr-forms-index-DDmbulkK.mjs → tft-crispr-forms-index-DYWkX7D3.mjs} +3 -3
- package/fesm2022/{tft-crispr-forms-index-DDmbulkK.mjs.map → tft-crispr-forms-index-DYWkX7D3.mjs.map} +1 -1
- package/fesm2022/{tft-crispr-forms-index-D-ZwydH8.mjs → tft-crispr-forms-index-DysioRya.mjs} +2 -2
- package/fesm2022/{tft-crispr-forms-index-D-ZwydH8.mjs.map → tft-crispr-forms-index-DysioRya.mjs.map} +1 -1
- package/fesm2022/{tft-crispr-forms-index-z95NpwT_.mjs → tft-crispr-forms-index-GlYQbbbv.mjs} +2 -2
- package/fesm2022/{tft-crispr-forms-index-z95NpwT_.mjs.map → tft-crispr-forms-index-GlYQbbbv.mjs.map} +1 -1
- package/fesm2022/{tft-crispr-forms-index-D9XpYXkk.mjs → tft-crispr-forms-index-UX-Wdyk1.mjs} +2 -2
- package/fesm2022/{tft-crispr-forms-index-D9XpYXkk.mjs.map → tft-crispr-forms-index-UX-Wdyk1.mjs.map} +1 -1
- package/fesm2022/{tft-crispr-forms-index-BOV5FHpK.mjs → tft-crispr-forms-index-bTdYHJPk.mjs} +2 -2
- package/fesm2022/{tft-crispr-forms-index-BOV5FHpK.mjs.map → tft-crispr-forms-index-bTdYHJPk.mjs.map} +1 -1
- package/fesm2022/tft-crispr-forms-index-kah2HT_N.mjs +54 -0
- package/fesm2022/tft-crispr-forms-index-kah2HT_N.mjs.map +1 -0
- package/fesm2022/{tft-crispr-forms-tft-crispr-forms-C0bqWzUB.mjs → tft-crispr-forms-tft-crispr-forms-8liD7eof.mjs} +42 -25
- package/fesm2022/tft-crispr-forms-tft-crispr-forms-8liD7eof.mjs.map +1 -0
- package/fesm2022/tft-crispr-forms.mjs +1 -1
- package/package.json +6 -1
- package/types/tft-crispr-forms.d.ts +81 -17
- package/fesm2022/tft-crispr-forms-index-CcgQXC4a.mjs.map +0 -1
- package/fesm2022/tft-crispr-forms-index-DWW8uD4i.mjs +0 -2
- package/fesm2022/tft-crispr-forms-index-DWW8uD4i.mjs.map +0 -1
- package/fesm2022/tft-crispr-forms-index-IyuNneu2.mjs +0 -177
- package/fesm2022/tft-crispr-forms-index-IyuNneu2.mjs.map +0 -1
- package/fesm2022/tft-crispr-forms-index-K7Ecsl-N.mjs +0 -54
- package/fesm2022/tft-crispr-forms-index-K7Ecsl-N.mjs.map +0 -1
- package/fesm2022/tft-crispr-forms-tft-crispr-forms-C0bqWzUB.mjs.map +0 -1
package/fesm2022/{tft-crispr-forms-index-BDIWv-PS.mjs.map → tft-crispr-forms-index-CmVahfkJ.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tft-crispr-forms-index-
|
|
1
|
+
{"version":3,"file":"tft-crispr-forms-index-CmVahfkJ.mjs","sources":["../../../../libs/crispr-forms/src/lib/ui/divider/divider.component.ts","../../../../libs/crispr-forms/src/lib/ui/divider/divider.component.html"],"sourcesContent":["import { Component, OnInit } from '@angular/core';\nimport { MatDividerModule } from '@angular/material/divider';\nimport { CommonModule } from '@angular/common';\n// eslint-disable-next-line @nx/enforce-module-boundaries\nimport { CrisprFieldComponent, DividerConfig } from '../../utils';\n\n@Component({\n selector: 'crispr-divider',\n templateUrl: './divider.component.html',\n styleUrls: ['./divider.component.scss'],\n imports: [CommonModule, MatDividerModule],\n standalone: true\n})\nexport class DividerComponent\n extends CrisprFieldComponent<DividerConfig>\n{\n // TODO: pull in type after configs are better organized\n defaultConfig = {};\n}\n","<mat-divider [ngClass]=\"config().classes\"></mat-divider>"],"names":[],"mappings":";;;;;;;;AAaM,MAAO,gBACX,SAAQ,oBAAmC,CAAA;AAR7C,IAAA,WAAA,GAAA;;;QAWE,IAAA,CAAA,aAAa,GAAG,EAAE;AACnB,IAAA;8GALY,gBAAgB,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAhB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,gBAAgB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECb7B,4DAAwD,EAAA,MAAA,EAAA,CAAA,sGAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDU1C,YAAY,4HAAE,gBAAgB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,UAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,CAAA,UAAA,EAAA,OAAA,CAAA,EAAA,CAAA,EAAA,mBAAA,EAAA,IAAA,EAAA,CAAA,CAAA;;2FAG/B,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAP5B,SAAS;AACI,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,gBAAgB,WAGjB,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAC7B,IAAI,EAAA,QAAA,EAAA,4DAAA,EAAA,MAAA,EAAA,CAAA,sGAAA,CAAA,EAAA;;;;;"}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import * as i1$2 from '@angular/common';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { inject, ChangeDetectorRef, signal, untracked, ViewChild, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
5
|
+
import * as i1 from '@angular/forms';
|
|
6
|
+
import { NgControl, ReactiveFormsModule } from '@angular/forms';
|
|
7
|
+
import * as i1$1 from '@angular/material/button';
|
|
8
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
9
|
+
import * as i2$1 from '@angular/material/form-field';
|
|
10
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
11
|
+
import * as i2 from '@angular/material/icon';
|
|
12
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
13
|
+
import * as i3 from '@angular/material/input';
|
|
14
|
+
import { MatInputModule } from '@angular/material/input';
|
|
15
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
16
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
17
|
+
import imageCompression from 'browser-image-compression';
|
|
18
|
+
import { Subject, Observable, concat, of, from, combineLatest, defer } from 'rxjs';
|
|
19
|
+
import { shareReplay, switchMap, tap, map, startWith } from 'rxjs/operators';
|
|
20
|
+
import * as i6 from '@tft/form-validation-handler';
|
|
21
|
+
import { FormValidationHandlerModule } from '@tft/form-validation-handler';
|
|
22
|
+
import { a as CrisprControlComponent, e as convertBytesToMb, F as FieldContainerComponent, S as SelectedFileComponent, f as FileDropzoneDirective } from './tft-crispr-forms-tft-crispr-forms-8liD7eof.mjs';
|
|
23
|
+
|
|
24
|
+
/* eslint-disable @nx/enforce-module-boundaries */
|
|
25
|
+
class ImageUploadFieldComponent extends CrisprControlComponent {
|
|
26
|
+
/** Compresses (when configured) and wraps a file. Never rejects, failures are returned. */
|
|
27
|
+
async processFile(file, signal) {
|
|
28
|
+
if (!file || !(file instanceof File))
|
|
29
|
+
return { file: null };
|
|
30
|
+
try {
|
|
31
|
+
const { compressImage, targetCompressedImageFileSizeMb, minCompressionThresholdMb, maxWidthOrHeight, useWebWorker, } = this.config();
|
|
32
|
+
const fileSizeMb = convertBytesToMb(file.size);
|
|
33
|
+
const shouldCompress = compressImage &&
|
|
34
|
+
targetCompressedImageFileSizeMb < fileSizeMb &&
|
|
35
|
+
minCompressionThresholdMb < fileSizeMb;
|
|
36
|
+
let processed = file;
|
|
37
|
+
if (shouldCompress) {
|
|
38
|
+
const options = {
|
|
39
|
+
// Use maxWidthOrHeight if it was provided, else use target compressed size
|
|
40
|
+
...(maxWidthOrHeight
|
|
41
|
+
? { maxWidthOrHeight }
|
|
42
|
+
: { maxSizeMB: targetCompressedImageFileSizeMb }),
|
|
43
|
+
useWebWorker,
|
|
44
|
+
maxIterations: 30,
|
|
45
|
+
signal,
|
|
46
|
+
onProgress: (progress) => {
|
|
47
|
+
// ignore progress from a compression that a newer selection has superseded
|
|
48
|
+
if (this.destroyed || this.latestFile !== file)
|
|
49
|
+
return;
|
|
50
|
+
this.progress.set(progress);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
processed = await imageCompression(file, options);
|
|
54
|
+
}
|
|
55
|
+
const { type, name, lastModified } = processed;
|
|
56
|
+
return {
|
|
57
|
+
file: new File([processed], this.config().fileName || name, {
|
|
58
|
+
type,
|
|
59
|
+
lastModified,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return { file: null, failed: file };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async toImageUrl(file) {
|
|
68
|
+
try {
|
|
69
|
+
const compressedStringImage = await imageCompression.getDataUrlFromFile(file);
|
|
70
|
+
return this.domSanitizer.bypassSecurityTrustResourceUrl(compressedStringImage);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
constructor() {
|
|
77
|
+
super();
|
|
78
|
+
this.ngControl = inject(NgControl, { optional: true, self: true });
|
|
79
|
+
this.domSanitizer = inject(DomSanitizer);
|
|
80
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
81
|
+
this.defaultConfig = {
|
|
82
|
+
// allowMultipleFiles: false,
|
|
83
|
+
acceptedTypes: 'image/*',
|
|
84
|
+
dropZoneText: 'DROP FILE HERE',
|
|
85
|
+
selectFilesButtonType: 'button',
|
|
86
|
+
selectButtonText: 'SELECT',
|
|
87
|
+
showClearFilesButton: true,
|
|
88
|
+
clearFilesButtonText: 'CLEAR',
|
|
89
|
+
resetFilesButtonText: 'RESET',
|
|
90
|
+
imagePreviewHeight: '250px',
|
|
91
|
+
compressImage: false,
|
|
92
|
+
targetCompressedImageFileSizeMb: 0.4,
|
|
93
|
+
minCompressionThresholdMb: 0.6,
|
|
94
|
+
useWebWorker: true,
|
|
95
|
+
};
|
|
96
|
+
this.inputImageFileSubject = new Subject();
|
|
97
|
+
this.inputImageFile$ = this.inputImageFileSubject
|
|
98
|
+
.asObservable()
|
|
99
|
+
.pipe(shareReplay(1));
|
|
100
|
+
/**
|
|
101
|
+
* The compression progress (0-100), undefined when nothing is compressing
|
|
102
|
+
*/
|
|
103
|
+
this.progress = signal(undefined, ...(ngDevMode ? [{ debugName: "progress" }] : []));
|
|
104
|
+
/**
|
|
105
|
+
* Whether the field is idle, processing (compressing) a selected image, or failed to process it.
|
|
106
|
+
* While `processing` or `failed` the control is invalid.
|
|
107
|
+
*/
|
|
108
|
+
this.processingState = signal('idle', ...(ngDevMode ? [{ debugName: "processingState" }] : []));
|
|
109
|
+
/** The message describing why processing failed, null unless `processingState` is `failed` */
|
|
110
|
+
this.failureMessage = signal(null, ...(ngDevMode ? [{ debugName: "failureMessage" }] : []));
|
|
111
|
+
/** The name of the file currently being processed */
|
|
112
|
+
this.processingFileName = signal('', ...(ngDevMode ? [{ debugName: "processingFileName" }] : []));
|
|
113
|
+
/**
|
|
114
|
+
* The most recently selected file (null once cleared/reset). Used to ignore progress
|
|
115
|
+
* reports from compressions that were superseded by a newer selection.
|
|
116
|
+
*/
|
|
117
|
+
this.latestFile = null;
|
|
118
|
+
/** Set on destroy so that an in-flight compression can no longer touch the control or call back */
|
|
119
|
+
this.destroyed = false;
|
|
120
|
+
/**
|
|
121
|
+
* Makes the control invalid while an image is processing or after processing failed.
|
|
122
|
+
* Implemented as a validator because Angular clears manually set errors whenever
|
|
123
|
+
* the control's validity is recalculated.
|
|
124
|
+
*/
|
|
125
|
+
this.imageProcessingValidator = () =>
|
|
126
|
+
// untracked: this runs inside effects (e.g. setControlValue) which must not depend on the state
|
|
127
|
+
untracked(() => {
|
|
128
|
+
switch (this.processingState()) {
|
|
129
|
+
case 'processing':
|
|
130
|
+
return { imageProcessing: true };
|
|
131
|
+
case 'failed':
|
|
132
|
+
return {
|
|
133
|
+
imageProcessingFailed: {
|
|
134
|
+
message: this.failureMessage() ||
|
|
135
|
+
'The image could not be processed. Please try a different image.',
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
default:
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
/**
|
|
143
|
+
* Compresses (when configured) and wraps each selected file. Failures are caught inside the
|
|
144
|
+
* switchMap so that one bad file never errors out, and so kills, the stream.
|
|
145
|
+
*/
|
|
146
|
+
this.compressedFile$ = this.inputImageFile$.pipe(
|
|
147
|
+
// Each selection gets its own AbortController. switchMap unsubscribing (new selection, clear,
|
|
148
|
+
// reset) runs the teardown below which aborts the superseded compression, and an aborted
|
|
149
|
+
// compression never emits so it can not produce a `failed` state.
|
|
150
|
+
switchMap((file) => new Observable((subscriber) => {
|
|
151
|
+
const controller = new AbortController();
|
|
152
|
+
let finished = false;
|
|
153
|
+
this.processFile(file, controller.signal).then((result) => {
|
|
154
|
+
if (controller.signal.aborted)
|
|
155
|
+
return;
|
|
156
|
+
finished = true;
|
|
157
|
+
subscriber.next(result);
|
|
158
|
+
subscriber.complete();
|
|
159
|
+
});
|
|
160
|
+
return () => {
|
|
161
|
+
if (!finished)
|
|
162
|
+
controller.abort();
|
|
163
|
+
};
|
|
164
|
+
})),
|
|
165
|
+
// unsubscribing from the switchMap on destroy aborts any in-flight compression
|
|
166
|
+
takeUntilDestroyed(),
|
|
167
|
+
// switchMap has already discarded results of superseded selections, so state
|
|
168
|
+
// transitions made here always belong to the latest selection
|
|
169
|
+
tap(({ file, failed }) => {
|
|
170
|
+
if (this.destroyed)
|
|
171
|
+
return;
|
|
172
|
+
const control = this.control();
|
|
173
|
+
if (failed) {
|
|
174
|
+
this.progress.set(undefined);
|
|
175
|
+
this.setProcessingState('failed', `Unable to process "${failed.name}". Please try a different image.`, () => control.updateValueAndValidity());
|
|
176
|
+
}
|
|
177
|
+
else if (file) {
|
|
178
|
+
this.progress.set(undefined);
|
|
179
|
+
// only set the value if there is a file because resetToInitialValue sets the control value
|
|
180
|
+
// to initial value and clears the compressed image, so setting it to null here would undo that
|
|
181
|
+
this.setProcessingState('idle', null, () => control.setValue(file));
|
|
182
|
+
this.config().filesChanged?.(this.group(), file);
|
|
183
|
+
}
|
|
184
|
+
if (file || this.value()) {
|
|
185
|
+
control.markAsTouched();
|
|
186
|
+
this.cdr.markForCheck();
|
|
187
|
+
}
|
|
188
|
+
}), map(({ file }) => file), shareReplay(1));
|
|
189
|
+
this.compressedImageUrlString$ = this.compressedFile$.pipe(
|
|
190
|
+
// null is emitted synchronously whenever a file starts (or no file is left) so that a
|
|
191
|
+
// resubscribing template (shareReplay replays the latest value) can never see the url of a
|
|
192
|
+
// previous file, or of one that has since been cleared or failed, while the new url is pending
|
|
193
|
+
switchMap((file) => file ? concat(of(null), from(this.toImageUrl(file))) : of(null)), shareReplay(1));
|
|
194
|
+
const ngControl = this.ngControl;
|
|
195
|
+
if (this.ngControl != null) {
|
|
196
|
+
// Setting the value accessor directly (instead of using
|
|
197
|
+
// the providers) to avoid running into a circular import.
|
|
198
|
+
ngControl.valueAccessor = this;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
ngOnInit() {
|
|
202
|
+
super.ngOnInit();
|
|
203
|
+
const control = this.control();
|
|
204
|
+
control.addValidators(this.imageProcessingValidator);
|
|
205
|
+
this.destroyRef.onDestroy(() => {
|
|
206
|
+
this.destroyed = true;
|
|
207
|
+
this.inputImageFileSubject.complete();
|
|
208
|
+
control.removeValidators(this.imageProcessingValidator);
|
|
209
|
+
// a destroyed field must not leave the form invalid because of a processing/failed state
|
|
210
|
+
control.updateValueAndValidity();
|
|
211
|
+
if (this.processingState() !== 'idle') {
|
|
212
|
+
this.processingState.set('idle');
|
|
213
|
+
this.failureMessage.set(null);
|
|
214
|
+
this.config().processingStateChange?.('idle', this.group());
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
this.imageSrcUrl$ = combineLatest([
|
|
218
|
+
this.compressedImageUrlString$.pipe(startWith(null)),
|
|
219
|
+
// defer: start with the control's value at subscribe time, the template resubscribes after
|
|
220
|
+
// every processing phase and must not replay the value the control had at init
|
|
221
|
+
defer(() => this.control().valueChanges.pipe(startWith(this.control().value), map((controlValue) => {
|
|
222
|
+
if (!controlValue)
|
|
223
|
+
return null;
|
|
224
|
+
if (controlValue instanceof File)
|
|
225
|
+
return null;
|
|
226
|
+
return (this.config().mapInputValueToUrl?.(controlValue) ||
|
|
227
|
+
controlValue);
|
|
228
|
+
}))),
|
|
229
|
+
]).pipe(map(([compressedImageUrlString, controlUrl]) => {
|
|
230
|
+
return compressedImageUrlString || controlUrl || null;
|
|
231
|
+
}));
|
|
232
|
+
this.disabled$ = this.config().disabledCallback
|
|
233
|
+
? this.config().disabledCallback(this.group())
|
|
234
|
+
: of(false);
|
|
235
|
+
}
|
|
236
|
+
filesChanged(files) {
|
|
237
|
+
if (files && files.length > 0) {
|
|
238
|
+
const imageFile = files[0];
|
|
239
|
+
this.latestFile = imageFile;
|
|
240
|
+
this.progress.set(undefined);
|
|
241
|
+
this.processingFileName.set(imageFile.name);
|
|
242
|
+
// We clear the control here and flag the field as processing so that the control is invalid
|
|
243
|
+
// while compressing, the input pipeline will take care of setting the processed value
|
|
244
|
+
this.setProcessingState('processing', null, () => {
|
|
245
|
+
this.control().patchValue(null);
|
|
246
|
+
this.control().markAsTouched();
|
|
247
|
+
});
|
|
248
|
+
this.inputImageFileSubject.next(imageFile);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
clearFileInput() {
|
|
252
|
+
this.latestFile = null;
|
|
253
|
+
this.inputImageFileSubject.next(null);
|
|
254
|
+
this.fileInputRef.nativeElement.value = '';
|
|
255
|
+
this.progress.set(undefined);
|
|
256
|
+
this.setProcessingState('idle', null, () => {
|
|
257
|
+
this.control().patchValue(null);
|
|
258
|
+
this.control().markAsTouched();
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
resetToInitialValue(initialValue) {
|
|
262
|
+
this.latestFile = null;
|
|
263
|
+
this.inputImageFileSubject.next(null);
|
|
264
|
+
this.progress.set(undefined);
|
|
265
|
+
this.setProcessingState('idle', null, () => this.control().patchValue(initialValue));
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Transitions the processing state. `applyValue` updates the control (which revalidates it),
|
|
269
|
+
* so validity is current before `processingStateChange` is called. The callback only fires
|
|
270
|
+
* when the state actually changes.
|
|
271
|
+
*/
|
|
272
|
+
setProcessingState(state, message, applyValue) {
|
|
273
|
+
const changed = this.processingState() !== state || this.failureMessage() !== message;
|
|
274
|
+
this.processingState.set(state);
|
|
275
|
+
this.failureMessage.set(message);
|
|
276
|
+
applyValue();
|
|
277
|
+
if (changed) {
|
|
278
|
+
this.config().processingStateChange?.(state, this.group());
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
writeValue(value) { }
|
|
282
|
+
registerOnChange(fn) {
|
|
283
|
+
this.onChange = fn;
|
|
284
|
+
}
|
|
285
|
+
registerOnTouched(fn) { }
|
|
286
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ImageUploadFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
287
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: ImageUploadFieldComponent, isStandalone: true, selector: "crispr-image-upload-field", viewQueries: [{ propertyName: "fileInputRef", first: true, predicate: ["fileInput"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<crispr-field-container\n [config]=\"config()\"\n [group]=\"group()\">\n\n <mat-form-field floatLabel=\"always\"\n [attr.data-testid]=\"testId()\"\n [color]=\"config().color\"\n [appearance]=\"config().appearance\"\n [formGroup]=\"group()\">\n <mat-label>{{config().label}}</mat-label>\n <input [formControlName]=\"config().controlName\" matInput type=\"text\"/>\n <input\n #fileInput\n type=\"file\"\n [accept]=\"config().acceptedTypes\"\n (change)=\"filesChanged($event.target.files)\"/>\n <div class=\"ui-container\">\n\n <div class=\"image-preview\" [style.height]=\"config().imagePreviewHeight\">\n @if (processingState() === 'processing') {\n <crispr-selected-file\n data-testid=\"image-processing\"\n [color]=\"config().color === 'primary' ? 'accent' : 'primary'\"\n [fileName]=\"(config().compressImage ? 'Compressing: ' : 'Processing: ') + processingFileName()\"\n [showProgress]=\"true\"\n [progress]=\"progress()\">\n </crispr-selected-file>\n } @else {\n <!-- <img *ngIf=\"(initialUrlString$ | async) as initialUrlString;\" [src]=\"initialUrlString\" alt=\"\"> -->\n @if (imageSrcUrl$ | async; as srcUrl) {\n <img [src]=\"srcUrl\" alt=\"\">\n } @else {\n <div class=\"drop-container\"\n [crisprFileDropzone]=\"!(disabled$ | async)\"\n (fileDrop)=\"filesChanged($event)\">\n <mat-icon class=\"drop-icon\" inline=\"true\">cloud_upload</mat-icon>\n <h2 class=\"mat-h2\">{{config().dropZoneText}}</h2>\n </div>\n }\n @if (processingState() === 'failed') {\n <p class=\"processing-failed mat-body-2\" role=\"alert\" data-testid=\"image-processing-failed\">\n <mat-icon inline=\"true\">error</mat-icon>\n {{failureMessage()}}\n </p>\n }\n }\n\n </div>\n\n\n\n <!-- </ng-template> -->\n @if (true) {\n <div class=\"button-container\">\n <button\n mat-button\n (click)=\"clearFileInput()\"\n type=\"button\"\n [disabled]=\"(disabled$ | async)\"\n aria-label=\"Remove Selected File(s)\">\n <mat-icon>delete</mat-icon>\n {{config().clearFilesButtonText}}\n </button>\n @if (value()) {\n <button\n mat-button\n (click)=\"resetToInitialValue(value())\"\n type=\"button\"\n [disabled]=\"(disabled$ | async)\"\n aria-label=\"Remove Selected File(s)\">\n <mat-icon>replay</mat-icon>\n {{config().resetFilesButtonText}}\n </button>\n }\n <button\n mat-button\n type=\"button\"\n color=\"primary\"\n [disabled]=\"(disabled$ | async)\"\n (click)=\"fileInput.click()\"\n [attr.aria-label]=\"config().controlName\">\n <mat-icon>\n camera\n </mat-icon>\n SELECT\n </button>\n </div>\n }\n </div>\n\n @if (config().hint && (control().valid || !control().touched)) {<mat-hint>{{config().hint}}</mat-hint>}\n\n</mat-form-field>\n\n</crispr-field-container>\n", styles: [":host ::ng-deep .mat-mdc-form-field .mat-mdc-form-field-infix{padding-top:0}:host ::ng-deep .mat-mdc-form-field .mat-button-wrapper{display:flex;flex-direction:column;align-items:center;padding-top:8px}:host .ui-container{display:flex;flex-direction:column}:host .ui-container .image-preview{display:flex;flex-direction:column;align-content:space-between;justify-content:center;align-items:center;margin:8px 0;flex-grow:1}:host .ui-container .image-preview crispr-selected-file{width:100%}:host .ui-container .image-preview .drop-container{display:flex;box-sizing:border-box;flex-direction:column;align-items:center;border:3px dashed gray;height:100%;width:100%;align-content:center;justify-content:center;border-radius:8px}:host .ui-container .image-preview .drop-container .drop-icon{height:32px;width:32px;font-size:36px}:host .ui-container .image-preview .drop-container .mat-h2{margin:0}:host .ui-container .image-preview .processing-failed{display:flex;align-items:center;gap:4px;margin:8px 0 0;color:var(--mat-sys-error, #b3261e)}:host .ui-container .image-preview img{height:inherit;max-width:100%}:host .ui-container .button-container{display:flex;flex-direction:var(--image-upload-button-container-direction);justify-content:flex-end}:host .ui-container .button-container button{margin-left:8px}:host input{position:absolute;max-height:0;visibility:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: FieldContainerComponent, selector: "crispr-field-container", inputs: ["config", "group", "inlineField"] }, { 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.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: SelectedFileComponent, selector: "crispr-selected-file", inputs: ["fileName", "progress", "showProgress", "color"] }, { kind: "directive", type: FileDropzoneDirective, selector: "[crisprFileDropzone]", inputs: ["crisprFileDropzone"], outputs: ["fileDrop"] }, { kind: "ngmodule", type: FormValidationHandlerModule }, { kind: "directive", type: i6.ControlErrorsDirective, selector: "[formControl], [formControlName]" }, { kind: "directive", type: i6.ControlErrorContainerDirective, selector: "[controlErrorContainer], [formGroup]" }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, preserveWhitespaces: true }); }
|
|
288
|
+
}
|
|
289
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ImageUploadFieldComponent, decorators: [{
|
|
290
|
+
type: Component,
|
|
291
|
+
args: [{ selector: 'crispr-image-upload-field', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
292
|
+
CommonModule,
|
|
293
|
+
FieldContainerComponent,
|
|
294
|
+
ReactiveFormsModule,
|
|
295
|
+
MatIconModule,
|
|
296
|
+
MatButtonModule,
|
|
297
|
+
MatFormFieldModule,
|
|
298
|
+
MatInputModule,
|
|
299
|
+
SelectedFileComponent,
|
|
300
|
+
FileDropzoneDirective,
|
|
301
|
+
FormValidationHandlerModule,
|
|
302
|
+
], standalone: true, template: "<crispr-field-container\n [config]=\"config()\"\n [group]=\"group()\">\n\n <mat-form-field floatLabel=\"always\"\n [attr.data-testid]=\"testId()\"\n [color]=\"config().color\"\n [appearance]=\"config().appearance\"\n [formGroup]=\"group()\">\n <mat-label>{{config().label}}</mat-label>\n <input [formControlName]=\"config().controlName\" matInput type=\"text\"/>\n <input\n #fileInput\n type=\"file\"\n [accept]=\"config().acceptedTypes\"\n (change)=\"filesChanged($event.target.files)\"/>\n <div class=\"ui-container\">\n\n <div class=\"image-preview\" [style.height]=\"config().imagePreviewHeight\">\n @if (processingState() === 'processing') {\n <crispr-selected-file\n data-testid=\"image-processing\"\n [color]=\"config().color === 'primary' ? 'accent' : 'primary'\"\n [fileName]=\"(config().compressImage ? 'Compressing: ' : 'Processing: ') + processingFileName()\"\n [showProgress]=\"true\"\n [progress]=\"progress()\">\n </crispr-selected-file>\n } @else {\n <!-- <img *ngIf=\"(initialUrlString$ | async) as initialUrlString;\" [src]=\"initialUrlString\" alt=\"\"> -->\n @if (imageSrcUrl$ | async; as srcUrl) {\n <img [src]=\"srcUrl\" alt=\"\">\n } @else {\n <div class=\"drop-container\"\n [crisprFileDropzone]=\"!(disabled$ | async)\"\n (fileDrop)=\"filesChanged($event)\">\n <mat-icon class=\"drop-icon\" inline=\"true\">cloud_upload</mat-icon>\n <h2 class=\"mat-h2\">{{config().dropZoneText}}</h2>\n </div>\n }\n @if (processingState() === 'failed') {\n <p class=\"processing-failed mat-body-2\" role=\"alert\" data-testid=\"image-processing-failed\">\n <mat-icon inline=\"true\">error</mat-icon>\n {{failureMessage()}}\n </p>\n }\n }\n\n </div>\n\n\n\n <!-- </ng-template> -->\n @if (true) {\n <div class=\"button-container\">\n <button\n mat-button\n (click)=\"clearFileInput()\"\n type=\"button\"\n [disabled]=\"(disabled$ | async)\"\n aria-label=\"Remove Selected File(s)\">\n <mat-icon>delete</mat-icon>\n {{config().clearFilesButtonText}}\n </button>\n @if (value()) {\n <button\n mat-button\n (click)=\"resetToInitialValue(value())\"\n type=\"button\"\n [disabled]=\"(disabled$ | async)\"\n aria-label=\"Remove Selected File(s)\">\n <mat-icon>replay</mat-icon>\n {{config().resetFilesButtonText}}\n </button>\n }\n <button\n mat-button\n type=\"button\"\n color=\"primary\"\n [disabled]=\"(disabled$ | async)\"\n (click)=\"fileInput.click()\"\n [attr.aria-label]=\"config().controlName\">\n <mat-icon>\n camera\n </mat-icon>\n SELECT\n </button>\n </div>\n }\n </div>\n\n @if (config().hint && (control().valid || !control().touched)) {<mat-hint>{{config().hint}}</mat-hint>}\n\n</mat-form-field>\n\n</crispr-field-container>\n", styles: [":host ::ng-deep .mat-mdc-form-field .mat-mdc-form-field-infix{padding-top:0}:host ::ng-deep .mat-mdc-form-field .mat-button-wrapper{display:flex;flex-direction:column;align-items:center;padding-top:8px}:host .ui-container{display:flex;flex-direction:column}:host .ui-container .image-preview{display:flex;flex-direction:column;align-content:space-between;justify-content:center;align-items:center;margin:8px 0;flex-grow:1}:host .ui-container .image-preview crispr-selected-file{width:100%}:host .ui-container .image-preview .drop-container{display:flex;box-sizing:border-box;flex-direction:column;align-items:center;border:3px dashed gray;height:100%;width:100%;align-content:center;justify-content:center;border-radius:8px}:host .ui-container .image-preview .drop-container .drop-icon{height:32px;width:32px;font-size:36px}:host .ui-container .image-preview .drop-container .mat-h2{margin:0}:host .ui-container .image-preview .processing-failed{display:flex;align-items:center;gap:4px;margin:8px 0 0;color:var(--mat-sys-error, #b3261e)}:host .ui-container .image-preview img{height:inherit;max-width:100%}:host .ui-container .button-container{display:flex;flex-direction:var(--image-upload-button-container-direction);justify-content:flex-end}:host .ui-container .button-container button{margin-left:8px}:host input{position:absolute;max-height:0;visibility:hidden}\n"] }]
|
|
303
|
+
}], ctorParameters: () => [], propDecorators: { fileInputRef: [{
|
|
304
|
+
type: ViewChild,
|
|
305
|
+
args: ['fileInput']
|
|
306
|
+
}] } });
|
|
307
|
+
|
|
308
|
+
export { ImageUploadFieldComponent };
|
|
309
|
+
//# sourceMappingURL=tft-crispr-forms-index-CoUwTugQ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tft-crispr-forms-index-CoUwTugQ.mjs","sources":["../../../../libs/crispr-forms/src/lib/ui/image-upload-field/image-upload-field.component.ts","../../../../libs/crispr-forms/src/lib/ui/image-upload-field/image-upload-field.component.html"],"sourcesContent":["/* eslint-disable @nx/enforce-module-boundaries */\nimport { CommonModule } from '@angular/common';\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, inject, OnInit, signal, untracked, ViewChild } from '@angular/core';\nimport {\n ControlValueAccessor,\n NgControl,\n ReactiveFormsModule,\n ValidationErrors,\n ValidatorFn,\n} from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatInputModule } from '@angular/material/input';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport imageCompression from 'browser-image-compression';\nimport { combineLatest, concat, defer, from, Observable, of, Subject } from 'rxjs';\nimport { map, shareReplay, startWith, switchMap, tap } from 'rxjs/operators';\nimport { FormValidationHandlerModule } from '@tft/form-validation-handler';\nimport { FileDropzoneDirective } from '../file-dropzone';\nimport { SelectedFileComponent } from '../selected-file';\nimport { FieldContainerComponent } from '../field-container';\nimport {\n convertBytesToMb,\n ImageProcessingState,\n ImageUploadFieldConfig,\n} from '../../utils';\nimport { CrisprControlComponent } from '../../utils/abstracts/crispr-control.abstract';\n\n/** The outcome of processing one selected file */\ninterface ProcessedImage {\n /** The processed file, null when there is no file or processing failed */\n file: File | null;\n /** The selected file that could not be processed */\n failed?: File;\n}\n\n@Component({\n selector: 'crispr-image-upload-field',\n templateUrl: './image-upload-field.component.html',\n styleUrls: ['./image-upload-field.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n CommonModule,\n FieldContainerComponent,\n ReactiveFormsModule,\n MatIconModule,\n MatButtonModule,\n MatFormFieldModule,\n MatInputModule,\n SelectedFileComponent,\n FileDropzoneDirective,\n FormValidationHandlerModule,\n ],\n standalone: true\n})\nexport class ImageUploadFieldComponent\n extends CrisprControlComponent<ImageUploadFieldConfig>\n implements OnInit, ControlValueAccessor\n{\n ngControl = inject(NgControl, { optional: true, self: true });\n private domSanitizer = inject(DomSanitizer);\n private cdr = inject(ChangeDetectorRef);\n\n onChange: () => void;\n\n defaultConfig: Partial<ImageUploadFieldConfig> = {\n // allowMultipleFiles: false,\n acceptedTypes: 'image/*',\n dropZoneText: 'DROP FILE HERE',\n selectFilesButtonType: 'button',\n selectButtonText: 'SELECT',\n showClearFilesButton: true,\n clearFilesButtonText: 'CLEAR',\n resetFilesButtonText: 'RESET',\n imagePreviewHeight: '250px',\n compressImage: false,\n targetCompressedImageFileSizeMb: 0.4,\n minCompressionThresholdMb: 0.6,\n useWebWorker: true,\n };\n\n private inputImageFileSubject = new Subject<File | null>();\n inputImageFile$ = this.inputImageFileSubject\n .asObservable()\n .pipe(shareReplay(1));\n\n /**\n * The compression progress (0-100), undefined when nothing is compressing\n */\n progress = signal<number | undefined>(undefined);\n\n /**\n * Whether the field is idle, processing (compressing) a selected image, or failed to process it.\n * While `processing` or `failed` the control is invalid.\n */\n processingState = signal<ImageProcessingState>('idle');\n\n /** The message describing why processing failed, null unless `processingState` is `failed` */\n failureMessage = signal<string | null>(null);\n\n /** The name of the file currently being processed */\n processingFileName = signal<string>('');\n\n /**\n * The most recently selected file (null once cleared/reset). Used to ignore progress\n * reports from compressions that were superseded by a newer selection.\n */\n private latestFile: File | null = null;\n\n /** Set on destroy so that an in-flight compression can no longer touch the control or call back */\n private destroyed = false;\n\n /**\n * Makes the control invalid while an image is processing or after processing failed.\n * Implemented as a validator because Angular clears manually set errors whenever\n * the control's validity is recalculated.\n */\n private imageProcessingValidator: ValidatorFn = (): ValidationErrors | null =>\n // untracked: this runs inside effects (e.g. setControlValue) which must not depend on the state\n untracked(() => {\n switch (this.processingState()) {\n case 'processing':\n return { imageProcessing: true };\n case 'failed':\n return {\n imageProcessingFailed: {\n message:\n this.failureMessage() ||\n 'The image could not be processed. Please try a different image.',\n },\n };\n default:\n return null;\n }\n });\n\n /**\n * Compresses (when configured) and wraps each selected file. Failures are caught inside the\n * switchMap so that one bad file never errors out, and so kills, the stream.\n */\n compressedFile$: Observable<File | null> = this.inputImageFile$.pipe(\n // Each selection gets its own AbortController. switchMap unsubscribing (new selection, clear,\n // reset) runs the teardown below which aborts the superseded compression, and an aborted\n // compression never emits so it can not produce a `failed` state.\n switchMap((file) =>\n new Observable<ProcessedImage>((subscriber) => {\n const controller = new AbortController();\n let finished = false;\n this.processFile(file, controller.signal).then((result) => {\n if (controller.signal.aborted) return;\n finished = true;\n subscriber.next(result);\n subscriber.complete();\n });\n return () => {\n if (!finished) controller.abort();\n };\n })\n ),\n // unsubscribing from the switchMap on destroy aborts any in-flight compression\n takeUntilDestroyed(),\n // switchMap has already discarded results of superseded selections, so state\n // transitions made here always belong to the latest selection\n tap(({ file, failed }) => {\n if (this.destroyed) return;\n const control = this.control();\n if (failed) {\n this.progress.set(undefined);\n this.setProcessingState(\n 'failed',\n `Unable to process \"${failed.name}\". Please try a different image.`,\n () => control.updateValueAndValidity()\n );\n } else if (file) {\n this.progress.set(undefined);\n // only set the value if there is a file because resetToInitialValue sets the control value\n // to initial value and clears the compressed image, so setting it to null here would undo that\n this.setProcessingState('idle', null, () => control.setValue(file));\n this.config().filesChanged?.(this.group(), file);\n }\n if (file || this.value()) {\n control.markAsTouched();\n this.cdr.markForCheck();\n }\n }),\n map(({ file }) => file),\n shareReplay(1)\n );\n\n compressedImageUrlString$: Observable<string | null> =\n this.compressedFile$.pipe(\n // null is emitted synchronously whenever a file starts (or no file is left) so that a\n // resubscribing template (shareReplay replays the latest value) can never see the url of a\n // previous file, or of one that has since been cleared or failed, while the new url is pending\n switchMap((file) =>\n file ? concat(of(null), from(this.toImageUrl(file))) : of(null)\n ),\n shareReplay(1)\n );\n\n /** Compresses (when configured) and wraps a file. Never rejects, failures are returned. */\n private async processFile(\n file: File | null,\n signal: AbortSignal\n ): Promise<ProcessedImage> {\n if (!file || !(file instanceof File)) return { file: null };\n try {\n const {\n compressImage,\n targetCompressedImageFileSizeMb,\n minCompressionThresholdMb,\n maxWidthOrHeight,\n useWebWorker,\n } = this.config();\n const fileSizeMb = convertBytesToMb(file.size);\n const shouldCompress =\n compressImage &&\n targetCompressedImageFileSizeMb < fileSizeMb &&\n minCompressionThresholdMb < fileSizeMb;\n let processed: File = file;\n if (shouldCompress) {\n const options = {\n // Use maxWidthOrHeight if it was provided, else use target compressed size\n ...(maxWidthOrHeight\n ? { maxWidthOrHeight }\n : { maxSizeMB: targetCompressedImageFileSizeMb }),\n useWebWorker,\n maxIterations: 30,\n signal,\n onProgress: (progress: number) => {\n // ignore progress from a compression that a newer selection has superseded\n if (this.destroyed || this.latestFile !== file) return;\n this.progress.set(progress);\n },\n };\n processed = await imageCompression(file, options);\n }\n const { type, name, lastModified } = processed;\n return {\n file: new File([processed], this.config().fileName || name, {\n type,\n lastModified,\n }),\n };\n } catch {\n return { file: null, failed: file };\n }\n }\n\n private async toImageUrl(file: File): Promise<string | null> {\n try {\n const compressedStringImage = await imageCompression.getDataUrlFromFile(\n file\n );\n return this.domSanitizer.bypassSecurityTrustResourceUrl(\n compressedStringImage\n ) as string;\n } catch {\n return null;\n }\n }\n\n // This is what is displayed by the image preview\n imageSrcUrl$: Observable<string | null>;\n\n @ViewChild('fileInput') fileInputRef: ElementRef;\n\n disabled$: Observable<boolean>;\n\n constructor() {\n super();\n const ngControl = this.ngControl;\n\n if (this.ngControl != null) {\n // Setting the value accessor directly (instead of using\n // the providers) to avoid running into a circular import.\n ngControl.valueAccessor = this;\n }\n }\n\n ngOnInit() {\n super.ngOnInit();\n const control = this.control();\n control.addValidators(this.imageProcessingValidator);\n this.destroyRef.onDestroy(() => {\n this.destroyed = true;\n this.inputImageFileSubject.complete();\n control.removeValidators(this.imageProcessingValidator);\n // a destroyed field must not leave the form invalid because of a processing/failed state\n control.updateValueAndValidity();\n if (this.processingState() !== 'idle') {\n this.processingState.set('idle');\n this.failureMessage.set(null);\n this.config().processingStateChange?.('idle', this.group());\n }\n });\n this.imageSrcUrl$ = combineLatest([\n this.compressedImageUrlString$.pipe(startWith(null)),\n // defer: start with the control's value at subscribe time, the template resubscribes after\n // every processing phase and must not replay the value the control had at init\n defer(() => this.control().valueChanges.pipe(\n startWith(this.control().value),\n map((controlValue): string => {\n if (!controlValue) return null;\n if (controlValue instanceof File) return null;\n return (\n this.config().mapInputValueToUrl?.(controlValue) ||\n (controlValue as string)\n );\n })\n )),\n ]).pipe(\n map(([compressedImageUrlString, controlUrl]) => {\n return compressedImageUrlString || controlUrl || null;\n })\n );\n this.disabled$ = this.config().disabledCallback\n ? this.config().disabledCallback(this.group())\n : of(false);\n }\n\n filesChanged(files: FileList): void {\n if (files && files.length > 0) {\n const imageFile = files[0];\n this.latestFile = imageFile;\n this.progress.set(undefined);\n this.processingFileName.set(imageFile.name);\n // We clear the control here and flag the field as processing so that the control is invalid\n // while compressing, the input pipeline will take care of setting the processed value\n this.setProcessingState('processing', null, () => {\n this.control().patchValue(null);\n this.control().markAsTouched();\n });\n this.inputImageFileSubject.next(imageFile);\n }\n }\n\n clearFileInput(): void {\n this.latestFile = null;\n this.inputImageFileSubject.next(null);\n this.fileInputRef.nativeElement.value = '';\n this.progress.set(undefined);\n this.setProcessingState('idle', null, () => {\n this.control().patchValue(null);\n this.control().markAsTouched();\n });\n }\n\n resetToInitialValue(initialValue: string): void {\n this.latestFile = null;\n this.inputImageFileSubject.next(null);\n this.progress.set(undefined);\n this.setProcessingState('idle', null, () =>\n this.control().patchValue(initialValue)\n );\n }\n\n /**\n * Transitions the processing state. `applyValue` updates the control (which revalidates it),\n * so validity is current before `processingStateChange` is called. The callback only fires\n * when the state actually changes.\n */\n private setProcessingState(\n state: ImageProcessingState,\n message: string | null,\n applyValue: () => void\n ): void {\n const changed =\n this.processingState() !== state || this.failureMessage() !== message;\n this.processingState.set(state);\n this.failureMessage.set(message);\n applyValue();\n if (changed) {\n this.config().processingStateChange?.(state, this.group());\n }\n }\n\n writeValue(value: null) {}\n\n registerOnChange(fn: () => void) {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void) {}\n}\n","<crispr-field-container\n [config]=\"config()\"\n [group]=\"group()\">\n\n <mat-form-field floatLabel=\"always\"\n [attr.data-testid]=\"testId()\"\n [color]=\"config().color\"\n [appearance]=\"config().appearance\"\n [formGroup]=\"group()\">\n <mat-label>{{config().label}}</mat-label>\n <input [formControlName]=\"config().controlName\" matInput type=\"text\"/>\n <input\n #fileInput\n type=\"file\"\n [accept]=\"config().acceptedTypes\"\n (change)=\"filesChanged($event.target.files)\"/>\n <div class=\"ui-container\">\n\n <div class=\"image-preview\" [style.height]=\"config().imagePreviewHeight\">\n @if (processingState() === 'processing') {\n <crispr-selected-file\n data-testid=\"image-processing\"\n [color]=\"config().color === 'primary' ? 'accent' : 'primary'\"\n [fileName]=\"(config().compressImage ? 'Compressing: ' : 'Processing: ') + processingFileName()\"\n [showProgress]=\"true\"\n [progress]=\"progress()\">\n </crispr-selected-file>\n } @else {\n <!-- <img *ngIf=\"(initialUrlString$ | async) as initialUrlString;\" [src]=\"initialUrlString\" alt=\"\"> -->\n @if (imageSrcUrl$ | async; as srcUrl) {\n <img [src]=\"srcUrl\" alt=\"\">\n } @else {\n <div class=\"drop-container\"\n [crisprFileDropzone]=\"!(disabled$ | async)\"\n (fileDrop)=\"filesChanged($event)\">\n <mat-icon class=\"drop-icon\" inline=\"true\">cloud_upload</mat-icon>\n <h2 class=\"mat-h2\">{{config().dropZoneText}}</h2>\n </div>\n }\n @if (processingState() === 'failed') {\n <p class=\"processing-failed mat-body-2\" role=\"alert\" data-testid=\"image-processing-failed\">\n <mat-icon inline=\"true\">error</mat-icon>\n {{failureMessage()}}\n </p>\n }\n }\n\n </div>\n\n\n\n <!-- </ng-template> -->\n @if (true) {\n <div class=\"button-container\">\n <button\n mat-button\n (click)=\"clearFileInput()\"\n type=\"button\"\n [disabled]=\"(disabled$ | async)\"\n aria-label=\"Remove Selected File(s)\">\n <mat-icon>delete</mat-icon>\n {{config().clearFilesButtonText}}\n </button>\n @if (value()) {\n <button\n mat-button\n (click)=\"resetToInitialValue(value())\"\n type=\"button\"\n [disabled]=\"(disabled$ | async)\"\n aria-label=\"Remove Selected File(s)\">\n <mat-icon>replay</mat-icon>\n {{config().resetFilesButtonText}}\n </button>\n }\n <button\n mat-button\n type=\"button\"\n color=\"primary\"\n [disabled]=\"(disabled$ | async)\"\n (click)=\"fileInput.click()\"\n [attr.aria-label]=\"config().controlName\">\n <mat-icon>\n camera\n </mat-icon>\n SELECT\n </button>\n </div>\n }\n </div>\n\n @if (config().hint && (control().valid || !control().touched)) {<mat-hint>{{config().hint}}</mat-hint>}\n\n</mat-form-field>\n\n</crispr-field-container>\n"],"names":["i3","i4","i7"],"mappings":"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"}
|
package/fesm2022/{tft-crispr-forms-index-CTtb80yI.mjs → tft-crispr-forms-index-D99OHDPc.mjs}
RENAMED
|
@@ -8,7 +8,7 @@ import * as i1$1 from '@angular/common';
|
|
|
8
8
|
import { CommonModule } from '@angular/common';
|
|
9
9
|
import * as i6 from '@tft/form-validation-handler';
|
|
10
10
|
import { FormValidationHandlerModule } from '@tft/form-validation-handler';
|
|
11
|
-
import { a as CrisprControlComponent, o as observablifyOptions, F as FieldContainerComponent } from './tft-crispr-forms-tft-crispr-forms-
|
|
11
|
+
import { a as CrisprControlComponent, o as observablifyOptions, F as FieldContainerComponent } from './tft-crispr-forms-tft-crispr-forms-8liD7eof.mjs';
|
|
12
12
|
|
|
13
13
|
/* eslint-disable @nx/enforce-module-boundaries */
|
|
14
14
|
class RadioFieldComponent extends CrisprControlComponent {
|
|
@@ -37,4 +37,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
37
37
|
}] });
|
|
38
38
|
|
|
39
39
|
export { RadioFieldComponent };
|
|
40
|
-
//# sourceMappingURL=tft-crispr-forms-index-
|
|
40
|
+
//# sourceMappingURL=tft-crispr-forms-index-D99OHDPc.mjs.map
|
package/fesm2022/{tft-crispr-forms-index-CTtb80yI.mjs.map → tft-crispr-forms-index-D99OHDPc.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tft-crispr-forms-index-
|
|
1
|
+
{"version":3,"file":"tft-crispr-forms-index-D99OHDPc.mjs","sources":["../../../../libs/crispr-forms/src/lib/ui/radio-field/radio-field.component.ts","../../../../libs/crispr-forms/src/lib/ui/radio-field/radio-field.component.html"],"sourcesContent":["/* eslint-disable @nx/enforce-module-boundaries */\nimport { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';\nimport { Observable } from 'rxjs';\n\nimport { MatRadioModule } from '@angular/material/radio';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { CommonModule } from '@angular/common';\nimport { FormValidationHandlerModule } from '@tft/form-validation-handler';\nimport { FieldContainerComponent } from '../field-container';\nimport { RadioFieldConfig, SelectOption, observablifyOptions } from '../../utils';\nimport { CrisprControlComponent } from '../../utils/abstracts/crispr-control.abstract';\n\n@Component({\n selector: 'crispr-radio-field',\n templateUrl: './radio-field.component.html',\n styleUrls: ['./radio-field.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n CommonModule,\n ReactiveFormsModule,\n FieldContainerComponent,\n MatRadioModule,\n FormValidationHandlerModule,\n ],\n standalone: true\n})\nexport class RadioFieldComponent extends CrisprControlComponent<RadioFieldConfig> implements OnInit {\n defaultConfig = {};\n options$: Observable<SelectOption[]>;\n ngOnInit() {\n super.ngOnInit();\n // options$ can be passed as an array, promise that resolves array, or observable that resolves array\n // this functions accounts for all possibilities and converts to observable that resolves array\n this.options$ = observablifyOptions(this.config().options, this.group());\n }\n}\n","<crispr-field-container\n [config]=\"config()\"\n [group]=\"group()\">\n\n <mat-radio-group\n [attr.data-testid]=\"testId()\"\n [formControl]=\"control()\"\n [color]=\"config().color\">\n @for (option of options$ | async; track option) {\n <mat-radio-button class=\"example-radio-button\" [value]=\"option.value\">\n {{option.label}}\n </mat-radio-button>\n }\n </mat-radio-group>\n</crispr-field-container>\n"],"names":["i3","i4"],"mappings":"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"}
|
package/fesm2022/{tft-crispr-forms-index-DDmbulkK.mjs → tft-crispr-forms-index-DYWkX7D3.mjs}
RENAMED
|
@@ -11,10 +11,10 @@ import * as i1$1 from '@angular/forms';
|
|
|
11
11
|
import { ReactiveFormsModule } from '@angular/forms';
|
|
12
12
|
import * as i2 from '@angular/material/form-field';
|
|
13
13
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
14
|
-
import { O as OptionComponent } from './tft-crispr-forms-index-
|
|
14
|
+
import { O as OptionComponent } from './tft-crispr-forms-index-3aw152gK.mjs';
|
|
15
15
|
import * as i6 from '@tft/form-validation-handler';
|
|
16
16
|
import { FormValidationHandlerModule } from '@tft/form-validation-handler';
|
|
17
|
-
import { a as CrisprControlComponent, o as observablifyOptions, I as InfoComponent, F as FieldContainerComponent } from './tft-crispr-forms-tft-crispr-forms-
|
|
17
|
+
import { a as CrisprControlComponent, o as observablifyOptions, I as InfoComponent, F as FieldContainerComponent } from './tft-crispr-forms-tft-crispr-forms-8liD7eof.mjs';
|
|
18
18
|
import * as i2$1 from '@angular/material/autocomplete';
|
|
19
19
|
|
|
20
20
|
/* eslint-disable @nx/enforce-module-boundaries */
|
|
@@ -80,4 +80,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
80
80
|
}] } });
|
|
81
81
|
|
|
82
82
|
export { SelectFieldComponent };
|
|
83
|
-
//# sourceMappingURL=tft-crispr-forms-index-
|
|
83
|
+
//# sourceMappingURL=tft-crispr-forms-index-DYWkX7D3.mjs.map
|
package/fesm2022/{tft-crispr-forms-index-DDmbulkK.mjs.map → tft-crispr-forms-index-DYWkX7D3.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tft-crispr-forms-index-
|
|
1
|
+
{"version":3,"file":"tft-crispr-forms-index-DYWkX7D3.mjs","sources":["../../../../libs/crispr-forms/src/lib/ui/select-field/select-field.component.ts","../../../../libs/crispr-forms/src/lib/ui/select-field/select-field.component.html"],"sourcesContent":["/* eslint-disable @nx/enforce-module-boundaries */\nimport {\n Component,\n OnInit,\n ChangeDetectionStrategy,\n ViewChild,\n AfterViewInit,\n} from '@angular/core';\nimport { Observable } from 'rxjs';\nimport { tap } from 'rxjs/operators';\n\nimport { MatSelect, MatSelectModule } from '@angular/material/select';\nimport { ThemePalette } from '@angular/material/core';\nimport { CommonModule } from '@angular/common';\nimport { MatCheckboxModule } from '@angular/material/checkbox';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { OptionComponent } from '../option';\nimport { FormValidationHandlerModule } from '@tft/form-validation-handler';\nimport { InfoComponent } from '../info';\nimport { FieldContainerComponent } from '../field-container';\nimport {\n observablifyOptions,\n SelectFieldConfig,\n SelectOption,\n} from '../../utils';\nimport { CrisprControlComponent } from '../../utils/abstracts/crispr-control.abstract';\n\n@Component({\n selector: 'crispr-select-field',\n templateUrl: './select-field.component.html',\n styleUrls: ['./select-field.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n CommonModule,\n InfoComponent,\n OptionComponent,\n MatCheckboxModule,\n MatSelectModule,\n ReactiveFormsModule,\n FormValidationHandlerModule,\n FieldContainerComponent,\n MatFormFieldModule,\n ],\n standalone: true\n})\nexport class SelectFieldComponent\n extends CrisprControlComponent<SelectFieldConfig>\n implements OnInit, AfterViewInit\n{\n defaultConfig = {\n enableToggleAll: false,\n color: 'primary' as ThemePalette,\n };\n options$: Observable<SelectOption[]>;\n allSelected = false;\n\n @ViewChild('selectField') selectField: MatSelect;\n\n ngOnInit() {\n super.ngOnInit();\n // options$ can be passed as an array, promise that resolves array, or observable that resolves array\n // this functions accounts for all possibilities and converts to observable that resolves array\n this.options$ = observablifyOptions(this.config().options, this.group()).pipe(\n tap(() => {\n // mat-select.writeValue() runs before async options render, so _setSelectionByValue\n // finds no matches. Re-apply the value after options are in the DOM.\n setTimeout(() => {\n const ctrl = this.control();\n if (ctrl?.value != null) {\n ctrl.setValue(ctrl.value, { emitEvent: false });\n }\n });\n })\n );\n }\n\n ngAfterViewInit() {\n if (\n this.config().multiple &&\n this.config().enableToggleAll &&\n this.selectField.options?.length === (this.value() as any[])?.length\n ) {\n this.allSelected = true;\n }\n }\n\n toggleAll(isSelected: boolean) {\n if (isSelected) {\n this.selectField.options.forEach((option) => option.deselect());\n } else {\n this.selectField.options.forEach((option) => option.select());\n }\n this.allSelected = !this.allSelected;\n }\n}\n","<crispr-field-container\n [config]=\"config()\"\n [group]=\"group()\">\n\n <mat-form-field\n [attr.data-testid]=\"testId()\"\n [color]=\"config().color\"\n [appearance]=\"config().appearance\"\n [formGroup]=\"group()\">\n @if (config().label) {<mat-label>{{config().label}}</mat-label>}\n <mat-select\n #selectField\n [formControlName]=\"config().controlName\"\n [placeholder]=\"config().placeholder\"\n [multiple]=\"config().multiple\">\n\n @if (config()?.multiple && config().enableToggleAll) {\n <mat-checkbox\n [color]=\"config().color\"\n [checked]=\"allSelected\"\n (click)=\"toggleAll(allSelected)\">\n Select All\n </mat-checkbox>\n }\n @for (option of options$ | async; track option) {\n <mat-option\n [value]=\"option.value\">\n <crispr-option [option]=\"option\">\n </crispr-option>\n </mat-option>\n }\n\n </mat-select>\n\n @if (config().fieldSuffix) {<span matSuffix>{{config().fieldSuffix}}</span>}\n @if (config().info) {<crispr-info matSuffix [info]=\"config().info\"></crispr-info>}\n @if (config().hint && (control().valid || !control().touched)) {<mat-hint>{{config().hint}}</mat-hint>}\n\n </mat-form-field>\n\n</crispr-field-container>\n"],"names":["i5","i7"],"mappings":"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"}
|
package/fesm2022/{tft-crispr-forms-index-D-ZwydH8.mjs → tft-crispr-forms-index-DysioRya.mjs}
RENAMED
|
@@ -8,7 +8,7 @@ import * as i3 from '@angular/material/input';
|
|
|
8
8
|
import { MatInputModule } from '@angular/material/input';
|
|
9
9
|
import * as i6 from '@tft/form-validation-handler';
|
|
10
10
|
import { FormValidationHandlerModule } from '@tft/form-validation-handler';
|
|
11
|
-
import { a as CrisprControlComponent, I as InfoComponent, F as FieldContainerComponent } from './tft-crispr-forms-tft-crispr-forms-
|
|
11
|
+
import { a as CrisprControlComponent, I as InfoComponent, F as FieldContainerComponent } from './tft-crispr-forms-tft-crispr-forms-8liD7eof.mjs';
|
|
12
12
|
|
|
13
13
|
/* eslint-disable @nx/enforce-module-boundaries */
|
|
14
14
|
const defaultConfig = { rows: 5 };
|
|
@@ -36,4 +36,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
36
36
|
}] });
|
|
37
37
|
|
|
38
38
|
export { TextareaFieldComponent };
|
|
39
|
-
//# sourceMappingURL=tft-crispr-forms-index-
|
|
39
|
+
//# sourceMappingURL=tft-crispr-forms-index-DysioRya.mjs.map
|
package/fesm2022/{tft-crispr-forms-index-D-ZwydH8.mjs.map → tft-crispr-forms-index-DysioRya.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tft-crispr-forms-index-
|
|
1
|
+
{"version":3,"file":"tft-crispr-forms-index-DysioRya.mjs","sources":["../../../../libs/crispr-forms/src/lib/ui/textarea-field/textarea-field.component.ts","../../../../libs/crispr-forms/src/lib/ui/textarea-field/textarea-field.component.html"],"sourcesContent":["/* eslint-disable @nx/enforce-module-boundaries */\nimport { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatInputModule } from '@angular/material/input';\nimport { FormValidationHandlerModule } from '@tft/form-validation-handler';\nimport { FieldContainerComponent } from '../field-container';\nimport { InfoComponent } from '../info';\nimport {\n TextareaFieldConfig,\n} from '../../utils';\nimport { CrisprControlComponent } from '../../utils/abstracts/crispr-control.abstract';\n\nconst defaultConfig: Partial<TextareaFieldConfig> = { rows: 5 };\n\n@Component({\n selector: 'crispr-textarea-field',\n templateUrl: './textarea-field.component.html',\n styleUrls: ['./textarea-field.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n InfoComponent,\n ReactiveFormsModule,\n FieldContainerComponent,\n MatFormFieldModule,\n MatInputModule,\n FormValidationHandlerModule\n ],\n standalone: true\n})\nexport class TextareaFieldComponent\n extends CrisprControlComponent<TextareaFieldConfig>\n implements OnInit\n{\n defaultConfig = defaultConfig;\n\n ngOnInit() {\n super.ngOnInit();\n }\n}\n","<crispr-field-container [config]=\"config()\" [group]=\"group()\">\n <mat-form-field [attr.data-testid]=\"testId()\" [color]=\"config().color\" [appearance]=\"config().appearance\"\n [formGroup]=\"group()\">\n @if (config().label) {<mat-label>{{config().label}}</mat-label>}\n <textarea [rows]=\"config().rows\" matInput [placeholder]=\"config().placeholder\"\n [formControlName]=\"config().controlName\">\n </textarea>\n @if (config().info) {<crispr-info [info]=\"config().info\"></crispr-info>}\n @if (config().fieldSuffix) {<span matSuffix>{{config().fieldSuffix}}</span>}\n @if (config().hint && control().valid) {<mat-hint>{{config().hint}}</mat-hint>}\n\n </mat-form-field>\n\n</crispr-field-container>"],"names":["i4"],"mappings":"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"}
|
package/fesm2022/{tft-crispr-forms-index-z95NpwT_.mjs → tft-crispr-forms-index-GlYQbbbv.mjs}
RENAMED
|
@@ -4,7 +4,7 @@ import * as i2 from '@angular/material/slider';
|
|
|
4
4
|
import { MatSliderModule } from '@angular/material/slider';
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
6
6
|
import { ReactiveFormsModule } from '@angular/forms';
|
|
7
|
-
import { a as CrisprControlComponent, I as InfoComponent, F as FieldContainerComponent } from './tft-crispr-forms-tft-crispr-forms-
|
|
7
|
+
import { a as CrisprControlComponent, I as InfoComponent, F as FieldContainerComponent } from './tft-crispr-forms-tft-crispr-forms-8liD7eof.mjs';
|
|
8
8
|
|
|
9
9
|
/* eslint-disable @nx/enforce-module-boundaries */
|
|
10
10
|
const defaultConfig = {
|
|
@@ -33,4 +33,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
|
|
|
33
33
|
}], ctorParameters: () => [] });
|
|
34
34
|
|
|
35
35
|
export { SliderFieldComponent };
|
|
36
|
-
//# sourceMappingURL=tft-crispr-forms-index-
|
|
36
|
+
//# sourceMappingURL=tft-crispr-forms-index-GlYQbbbv.mjs.map
|
package/fesm2022/{tft-crispr-forms-index-z95NpwT_.mjs.map → tft-crispr-forms-index-GlYQbbbv.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tft-crispr-forms-index-
|
|
1
|
+
{"version":3,"file":"tft-crispr-forms-index-GlYQbbbv.mjs","sources":["../../../../libs/crispr-forms/src/lib/ui/slider-field/slider-field.component.ts","../../../../libs/crispr-forms/src/lib/ui/slider-field/slider-field.component.html"],"sourcesContent":["/* eslint-disable @nx/enforce-module-boundaries */\nimport { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';\nimport { MatSliderModule } from '@angular/material/slider';\n\nimport { ReactiveFormsModule } from '@angular/forms';\n// import { FormValidationHandlerModule } from '@tft/form-validation-handler';\nimport { FieldContainerComponent } from '../field-container';\nimport { InfoComponent } from '../info';\nimport {\n SliderFieldConfig\n} from '../../utils';\nimport { CrisprControlComponent } from '../../utils/abstracts/crispr-control.abstract';\n\nconst defaultConfig: Partial<SliderFieldConfig> = { \n displayLimits: true,\n displayWith: (val) => val,\n};\n\n\n@Component({\n selector: 'crispr-slider-field',\n templateUrl: './slider-field.component.html',\n styleUrls: ['./slider-field.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n InfoComponent,\n FieldContainerComponent,\n ReactiveFormsModule,\n MatSliderModule\n ]\n})\nexport class SliderFieldComponent extends CrisprControlComponent<SliderFieldConfig> implements OnInit {\n defaultConfig = defaultConfig;\n constructor() {\n super();\n }\n\n ngOnInit() {\n super.ngOnInit();\n }\n}\n","<crispr-field-container [config]=\"config()\" [group]=\"group()\">\n @if (config().label) {\n <label>\n {{config().label}}\n @if (config().info) {\n <crispr-info [info]=\"config().info\">\n </crispr-info>\n }\n </label>\n }\n <div class=\"slider-container\">\n @if (config().displayLimits) {\n <span>\n {{config().min}}\n </span>\n }\n <mat-slider [attr.data-testid]=\"testId()\" [discrete]=\"config().discrete\"\n [displayWith]=\"config().displayWith\" class=\"example-margin\" [formGroup]=\"group()\" [max]=\"config().max\"\n [min]=\"config().min\" [step]=\"config().step\">\n <input matSliderThumb [formControlName]=\"config().controlName\">\n </mat-slider>\n @if (config().displayLimits) {\n <span>\n {{config().max}}\n </span>\n }\n </div>\n\n</crispr-field-container>"],"names":[],"mappings":"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"}
|