@tft/crispr-forms 13.0.2 → 13.0.5
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/esm2020/lib/abstracts/autocomplete-field.abstract.mjs +3 -3
- package/esm2020/lib/form.helpers.mjs +4 -3
- package/esm2020/lib/material/autocomplete-chiplist-field/autocomplete-chiplist-field.component.mjs +7 -7
- package/esm2020/lib/material/autocomplete-field/autocomplete-field.component.mjs +7 -7
- package/esm2020/lib/material/image-upload-field/image-upload-field.component.mjs +38 -21
- package/esm2020/lib/models/image-upload-field.config.mjs +1 -1
- package/fesm2015/tft-crispr-forms.mjs +46 -27
- package/fesm2015/tft-crispr-forms.mjs.map +1 -1
- package/fesm2020/tft-crispr-forms.mjs +45 -27
- package/fesm2020/tft-crispr-forms.mjs.map +1 -1
- package/lib/material/image-upload-field/image-upload-field.component.d.ts +8 -2
- package/lib/models/image-upload-field.config.d.ts +6 -0
- package/package.json +1 -1
- package/tft-crispr-forms-13.0.5.tgz +0 -0
- package/tft-crispr-forms-13.0.2.tgz +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { of, combineLatest, from, isObservable, BehaviorSubject, Subject } from 'rxjs';
|
|
2
2
|
import * as i4$1 from '@angular/forms';
|
|
3
3
|
import { FormGroup, FormArray, FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
4
|
-
import { map, startWith, debounceTime, distinctUntilChanged, switchMap, tap
|
|
4
|
+
import { map, startWith, shareReplay, debounceTime, distinctUntilChanged, switchMap, tap } from 'rxjs/operators';
|
|
5
5
|
import * as i0 from '@angular/core';
|
|
6
6
|
import { Directive, Component, ChangeDetectionStrategy, Input, NgModule, ViewChild, EventEmitter, Output, HostListener, Optional, Self, Pipe } from '@angular/core';
|
|
7
7
|
import * as i2 from '@angular/common';
|
|
@@ -210,7 +210,7 @@ function callOptionsIfFunction(options, parentGroup, searchString) {
|
|
|
210
210
|
function observablifyOptions(options, parentGroup, searchString) {
|
|
211
211
|
const calledOptions = callOptionsIfFunction(options, parentGroup, searchString);
|
|
212
212
|
// if options are a promise
|
|
213
|
-
|
|
213
|
+
const observableOptions = calledOptions instanceof Promise
|
|
214
214
|
// convert to observable
|
|
215
215
|
? from(calledOptions)
|
|
216
216
|
// if array that isn't empty
|
|
@@ -223,6 +223,7 @@ function observablifyOptions(options, parentGroup, searchString) {
|
|
|
223
223
|
? calledOptions
|
|
224
224
|
// else an empty array
|
|
225
225
|
: of([]);
|
|
226
|
+
return observableOptions.pipe(shareReplay(1));
|
|
226
227
|
}
|
|
227
228
|
/**
|
|
228
229
|
* Determines if the field config is for a form control (not a divider, heading or other non-control field)
|
|
@@ -331,7 +332,7 @@ class AbstractAutocompleteComponent extends AbstractAutocompleteFieldMixin {
|
|
|
331
332
|
// filter options by the search string using either the default filter function or one passed in through config
|
|
332
333
|
this.options$ = this.autocompleteInputControl.valueChanges.pipe(
|
|
333
334
|
// this is needed to have the options panel to open on focus
|
|
334
|
-
startWith(''), debounceTime(this.config.typeDebounceTime),
|
|
335
|
+
startWith(''), debounceTime(this.config.typeDebounceTime), map(searchText => searchText || ''), distinctUntilChanged(), switchMap((searchText) => observablifyOptions(this.config.options, this.group, searchText)), shareReplay(1));
|
|
335
336
|
}
|
|
336
337
|
}
|
|
337
338
|
AbstractAutocompleteComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: AbstractAutocompleteComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
@@ -612,10 +613,10 @@ class AutocompleteFieldComponent extends AbstractAutocompleteComponent {
|
|
|
612
613
|
}
|
|
613
614
|
}
|
|
614
615
|
AutocompleteFieldComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: AutocompleteFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
615
|
-
AutocompleteFieldComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: AutocompleteFieldComponent, selector: "crispr-autocomplete-field", viewQueries: [{ propertyName: "autoInput", first: true, predicate: ["autoInput"], descendants: true, read: MatAutocompleteTrigger }, { propertyName: "autoInputRef", first: true, predicate: ["autoInput"], descendants: true }], usesInheritance: true, ngImport: i0, template: "\n<crispr-field-container\n [config]=\"config\"\n [group]=\"group\">\n\n <mat-form-field\n [color]=\"config.color\"\n [appearance]=\"config.appearance\">\n <mat-label *ngIf=\"config.label\">\n {{config.label}}\n </mat-label>\n <input\n matInput\n [formControl]=\"autocompleteInputControl\"\n [placeholder]=\"config.placeholder\"\n [matAutocomplete]=\"auto\"\n role=\"textbox\"\n type=\"textbox\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n #autoInput>\n\n <span *ngIf=\"config.fieldSuffix\" matSuffix>{{config.fieldSuffix}}</span>\n <crispr-info\n *ngIf=\"config.info\"\n matSuffix\n [info]=\"config.info \">\n </crispr-info>\n\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [autoActiveFirstOption]=\"config.autoActiveFirstOption\"\n (optionSelected)=\"handleSelect($event)\"\n [displayWith]=\"displayLabel(options$ | async)\">\n\n <ng-container *ngIf=\"options$ | async as options; else emptyOption\">\n <ng-container *ngIf=\"options.length > 0; else emptyOption\">\n <mat-option\n *ngFor=\"let option of options$ | async\"\n [value]=\"option.value\">\n <crispr-option [option]=\"option\"></crispr-option>\n </mat-option>\n </ng-container>\n\n </ng-container>\n <ng-template #emptyOption>\n <mat-option disabled=\"true\">\n {{config.emptyOptionsMessage}}\n </mat-option>\n </ng-template>\n\n </mat-autocomplete>\n </mat-form-field>\n</crispr-field-container>\n", styles: [""], components: [{ type: FieldContainerComponent, selector: "crispr-field-container", inputs: ["config", "group", "inlineField"] }, { type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { type: InfoComponent, selector: "crispr-info", inputs: ["info"] }, { type: i4$3.MatAutocomplete, selector: "mat-autocomplete", inputs: ["disableRipple"], exportAs: ["matAutocomplete"] }, { type: i5$1.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { type: OptionComponent, selector: "crispr-option", inputs: ["option"] }], directives: [{ type:
|
|
616
|
+
AutocompleteFieldComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: AutocompleteFieldComponent, selector: "crispr-autocomplete-field", viewQueries: [{ propertyName: "autoInput", first: true, predicate: ["autoInput"], descendants: true, read: MatAutocompleteTrigger }, { propertyName: "autoInputRef", first: true, predicate: ["autoInput"], descendants: true }], usesInheritance: true, ngImport: i0, template: "\n<crispr-field-container\n [config]=\"config\"\n [group]=\"group\">\n\n <mat-form-field\n [color]=\"config.color\"\n [appearance]=\"config.appearance\"\n [formGroup]=\"group\">\n <mat-label *ngIf=\"config.label\">\n {{config.label}}\n </mat-label>\n\n <input class=\"hidden-error-directive-anchor\" [formControlName]=\"config.controlName\" type=\"text\"/>\n\n <input\n matInput\n [formControl]=\"autocompleteInputControl\"\n [placeholder]=\"config.placeholder\"\n [matAutocomplete]=\"auto\"\n role=\"textbox\"\n type=\"textbox\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n #autoInput>\n\n <span *ngIf=\"config.fieldSuffix\" matSuffix>{{config.fieldSuffix}}</span>\n <crispr-info\n *ngIf=\"config.info\"\n matSuffix\n [info]=\"config.info \">\n </crispr-info>\n\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [autoActiveFirstOption]=\"config.autoActiveFirstOption\"\n (optionSelected)=\"handleSelect($event)\"\n [displayWith]=\"displayLabel(options$ | async)\">\n\n <ng-container *ngIf=\"options$ | async as options; else emptyOption\">\n <ng-container *ngIf=\"options.length > 0; else emptyOption\">\n <mat-option\n *ngFor=\"let option of options$ | async\"\n [value]=\"option.value\">\n <crispr-option [option]=\"option\"></crispr-option>\n </mat-option>\n </ng-container>\n\n </ng-container>\n <ng-template #emptyOption>\n <mat-option disabled=\"true\">\n {{config.emptyOptionsMessage}}\n </mat-option>\n </ng-template>\n\n </mat-autocomplete>\n </mat-form-field>\n</crispr-field-container>\n", styles: [".hidden-error-directive-anchor{display:none}\n"], components: [{ type: FieldContainerComponent, selector: "crispr-field-container", inputs: ["config", "group", "inlineField"] }, { type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { type: InfoComponent, selector: "crispr-info", inputs: ["info"] }, { type: i4$3.MatAutocomplete, selector: "mat-autocomplete", inputs: ["disableRipple"], exportAs: ["matAutocomplete"] }, { type: i5$1.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { type: OptionComponent, selector: "crispr-option", inputs: ["option"] }], directives: [{ type: i4$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i4$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i5.ControlErrorContainerDirective, selector: "[controlErrorContainer], [formGroup]" }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2$1.MatLabel, selector: "mat-label" }, { type: i4$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i4$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i5.ControlErrorsDirective, selector: "[formControl], [formControlName]" }, { type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { type: i4$3.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", exportAs: ["matAutocompleteTrigger"] }, { type: i4$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i2$1.MatSuffix, selector: "[matSuffix]" }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "async": i2.AsyncPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush, preserveWhitespaces: true });
|
|
616
617
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: AutocompleteFieldComponent, decorators: [{
|
|
617
618
|
type: Component,
|
|
618
|
-
args: [{ selector: 'crispr-autocomplete-field', changeDetection: ChangeDetectionStrategy.OnPush, template: "\n<crispr-field-container\n [config]=\"config\"\n [group]=\"group\">\n\n <mat-form-field\n [color]=\"config.color\"\n [appearance]=\"config.appearance\">\n <mat-label *ngIf=\"config.label\">\n {{config.label}}\n </mat-label>\n <input\n matInput\n [formControl]=\"autocompleteInputControl\"\n [placeholder]=\"config.placeholder\"\n [matAutocomplete]=\"auto\"\n role=\"textbox\"\n type=\"textbox\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n #autoInput>\n\n <span *ngIf=\"config.fieldSuffix\" matSuffix>{{config.fieldSuffix}}</span>\n <crispr-info\n *ngIf=\"config.info\"\n matSuffix\n [info]=\"config.info \">\n </crispr-info>\n\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [autoActiveFirstOption]=\"config.autoActiveFirstOption\"\n (optionSelected)=\"handleSelect($event)\"\n [displayWith]=\"displayLabel(options$ | async)\">\n\n <ng-container *ngIf=\"options$ | async as options; else emptyOption\">\n <ng-container *ngIf=\"options.length > 0; else emptyOption\">\n <mat-option\n *ngFor=\"let option of options$ | async\"\n [value]=\"option.value\">\n <crispr-option [option]=\"option\"></crispr-option>\n </mat-option>\n </ng-container>\n\n </ng-container>\n <ng-template #emptyOption>\n <mat-option disabled=\"true\">\n {{config.emptyOptionsMessage}}\n </mat-option>\n </ng-template>\n\n </mat-autocomplete>\n </mat-form-field>\n</crispr-field-container>\n", styles: [""] }]
|
|
619
|
+
args: [{ selector: 'crispr-autocomplete-field', changeDetection: ChangeDetectionStrategy.OnPush, template: "\n<crispr-field-container\n [config]=\"config\"\n [group]=\"group\">\n\n <mat-form-field\n [color]=\"config.color\"\n [appearance]=\"config.appearance\"\n [formGroup]=\"group\">\n <mat-label *ngIf=\"config.label\">\n {{config.label}}\n </mat-label>\n\n <input class=\"hidden-error-directive-anchor\" [formControlName]=\"config.controlName\" type=\"text\"/>\n\n <input\n matInput\n [formControl]=\"autocompleteInputControl\"\n [placeholder]=\"config.placeholder\"\n [matAutocomplete]=\"auto\"\n role=\"textbox\"\n type=\"textbox\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n #autoInput>\n\n <span *ngIf=\"config.fieldSuffix\" matSuffix>{{config.fieldSuffix}}</span>\n <crispr-info\n *ngIf=\"config.info\"\n matSuffix\n [info]=\"config.info \">\n </crispr-info>\n\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [autoActiveFirstOption]=\"config.autoActiveFirstOption\"\n (optionSelected)=\"handleSelect($event)\"\n [displayWith]=\"displayLabel(options$ | async)\">\n\n <ng-container *ngIf=\"options$ | async as options; else emptyOption\">\n <ng-container *ngIf=\"options.length > 0; else emptyOption\">\n <mat-option\n *ngFor=\"let option of options$ | async\"\n [value]=\"option.value\">\n <crispr-option [option]=\"option\"></crispr-option>\n </mat-option>\n </ng-container>\n\n </ng-container>\n <ng-template #emptyOption>\n <mat-option disabled=\"true\">\n {{config.emptyOptionsMessage}}\n </mat-option>\n </ng-template>\n\n </mat-autocomplete>\n </mat-form-field>\n</crispr-field-container>\n", styles: [".hidden-error-directive-anchor{display:none}\n"] }]
|
|
619
620
|
}], propDecorators: { autoInput: [{
|
|
620
621
|
type: ViewChild,
|
|
621
622
|
args: ['autoInput', { read: MatAutocompleteTrigger }]
|
|
@@ -703,10 +704,10 @@ class AutocompleteChiplistFieldComponent extends AbstractAutocompleteComponent {
|
|
|
703
704
|
}
|
|
704
705
|
}
|
|
705
706
|
AutocompleteChiplistFieldComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: AutocompleteChiplistFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
706
|
-
AutocompleteChiplistFieldComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: AutocompleteChiplistFieldComponent, selector: "crispr-autocomplete-chiplist-field", viewQueries: [{ propertyName: "chipInput", first: true, predicate: ["autoInput"], descendants: true, read: MatAutocompleteTrigger }, { propertyName: "chipInputRef", first: true, predicate: ["autoInput"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<crispr-field-container\n [config]=\"config\"\n [group]=\"group\">\n\n <mat-form-field\n [color]=\"config.color\"\n [appearance]=\"config.appearance\">\n <mat-label *ngIf=\"config.label\">\n {{config.label}}\n </mat-label>\n\n <mat-chip-list\n #chipList>\n <mat-chip\n class=\"refinement-chip\"\n *ngFor=\"let chip of controlValue$ | async\"\n [selectable]=\"config.chipsSelectable\"\n [removable]=\"config.areChipsRemovable\"\n (removed)=\"removeChip(chip)\">\n <span *ngIf=\"chip.imageUrl as imageUrl\" class=\"image-container\">\n <img [src]=\"imageUrl\" alt=\"image of {{chip.label}}\">\n </span>\n {{chip.label}}\n <mat-icon\n matChipRemove\n *ngIf=\"config.areChipsRemovable\">\n cancel\n </mat-icon>\n </mat-chip>\n\n <input\n matInput\n [formControl]=\"autocompleteInputControl\"\n [placeholder]=\"config.placeholder\"\n [matChipInputFor]=\"chipList\"\n [matAutocomplete]=\"auto\"\n (keydown.tab)=\"handleTab($event)\"\n [matChipInputSeparatorKeyCodes]=\"config.separatorKeyCodes\"\n (matChipInputTokenEnd)=\"handleTokenEnd($event)\"\n role=\"searchbox\"\n type=\"text\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n #autoInput/>\n <span *ngIf=\"config.fieldSuffix\" matSuffix>{{config.fieldSuffix}}</span>\n <crispr-info\n *ngIf=\"config.info\"\n matSuffix\n [info]=\"config.info \">\n </crispr-info>\n\n </mat-chip-list>\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [autoActiveFirstOption]=\"this.config.autoActiveFirstOption\"\n (optionSelected)=\"handleSelect($event)\"\n [displayWith]=\"mapToLabel\">\n <ng-container *ngIf=\"remainingOptions$ | async as remainingOptions; else emptyOption\">\n <ng-container *ngIf=\"remainingOptions.length > 0; else emptyOption\">\n <mat-option\n *ngFor=\"let option of remainingOptions$ | async\"\n [value]=\"option\">\n <crispr-option [option]=\"option\"></crispr-option>\n </mat-option>\n </ng-container>\n\n </ng-container>\n <ng-template #emptyOption>\n <mat-option disabled=\"true\">\n {{config.emptyOptionsMessage}}\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n\n </mat-form-field>\n</crispr-field-container>\n", styles: [".refinement-chip .image-container{width:26px;height:26px;border-radius:50%;overflow:hidden;margin:-2px 4px -2px -8px}.refinement-chip .image-container img{width:100%;height:100%;object-fit:cover}\n"], components: [{ type: FieldContainerComponent, selector: "crispr-field-container", inputs: ["config", "group", "inlineField"] }, { type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { type: i3$2.MatChipList, selector: "mat-chip-list", inputs: ["errorStateMatcher", "multiple", "compareWith", "value", "required", "placeholder", "disabled", "aria-orientation", "selectable", "tabIndex"], outputs: ["change", "valueChange"], exportAs: ["matChipList"] }, { type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: InfoComponent, selector: "crispr-info", inputs: ["info"] }, { type: i4$3.MatAutocomplete, selector: "mat-autocomplete", inputs: ["disableRipple"], exportAs: ["matAutocomplete"] }, { type: i5$1.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { type: OptionComponent, selector: "crispr-option", inputs: ["option"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2$1.MatLabel, selector: "mat-label" }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i3$2.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["color", "disableRipple", "tabIndex", "selected", "value", "selectable", "disabled", "removable"], outputs: ["selectionChange", "destroyed", "removed"], exportAs: ["matChip"] }, { type: i3$2.MatChipRemove, selector: "[matChipRemove]" }, { type:
|
|
707
|
+
AutocompleteChiplistFieldComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.1", type: AutocompleteChiplistFieldComponent, selector: "crispr-autocomplete-chiplist-field", viewQueries: [{ propertyName: "chipInput", first: true, predicate: ["autoInput"], descendants: true, read: MatAutocompleteTrigger }, { propertyName: "chipInputRef", first: true, predicate: ["autoInput"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<crispr-field-container\n [config]=\"config\"\n [group]=\"group\">\n\n <mat-form-field\n [color]=\"config.color\"\n [appearance]=\"config.appearance\"\n [formGroup]=\"group\">\n <mat-label *ngIf=\"config.label\">\n {{config.label}}\n </mat-label>\n\n <mat-chip-list\n #chipList>\n <mat-chip\n class=\"refinement-chip\"\n *ngFor=\"let chip of controlValue$ | async\"\n [selectable]=\"config.chipsSelectable\"\n [removable]=\"config.areChipsRemovable\"\n (removed)=\"removeChip(chip)\">\n <span *ngIf=\"chip.imageUrl as imageUrl\" class=\"image-container\">\n <img [src]=\"imageUrl\" alt=\"image of {{chip.label}}\">\n </span>\n {{chip.label}}\n <mat-icon\n matChipRemove\n *ngIf=\"config.areChipsRemovable\">\n cancel\n </mat-icon>\n </mat-chip>\n <input class=\"hidden-error-directive-anchor\" [formControlName]=\"config.controlName\" type=\"text\"/>\n\n <input\n matInput\n [formControl]=\"autocompleteInputControl\"\n [placeholder]=\"config.placeholder\"\n [matChipInputFor]=\"chipList\"\n [matAutocomplete]=\"auto\"\n (keydown.tab)=\"handleTab($event)\"\n [matChipInputSeparatorKeyCodes]=\"config.separatorKeyCodes\"\n (matChipInputTokenEnd)=\"handleTokenEnd($event)\"\n role=\"searchbox\"\n type=\"text\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n #autoInput/>\n <span *ngIf=\"config.fieldSuffix\" matSuffix>{{config.fieldSuffix}}</span>\n <crispr-info\n *ngIf=\"config.info\"\n matSuffix\n [info]=\"config.info \">\n </crispr-info>\n\n </mat-chip-list>\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [autoActiveFirstOption]=\"this.config.autoActiveFirstOption\"\n (optionSelected)=\"handleSelect($event)\"\n [displayWith]=\"mapToLabel\">\n <ng-container *ngIf=\"remainingOptions$ | async as remainingOptions; else emptyOption\">\n <ng-container *ngIf=\"remainingOptions.length > 0; else emptyOption\">\n <mat-option\n *ngFor=\"let option of remainingOptions$ | async\"\n [value]=\"option\">\n <crispr-option [option]=\"option\"></crispr-option>\n </mat-option>\n </ng-container>\n\n </ng-container>\n <ng-template #emptyOption>\n <mat-option disabled=\"true\">\n {{config.emptyOptionsMessage}}\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n\n </mat-form-field>\n</crispr-field-container>\n", styles: [".refinement-chip .image-container{width:26px;height:26px;border-radius:50%;overflow:hidden;margin:-2px 4px -2px -8px}.refinement-chip .image-container img{width:100%;height:100%;object-fit:cover}.hidden-error-directive-anchor{display:none}\n"], components: [{ type: FieldContainerComponent, selector: "crispr-field-container", inputs: ["config", "group", "inlineField"] }, { type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { type: i3$2.MatChipList, selector: "mat-chip-list", inputs: ["errorStateMatcher", "multiple", "compareWith", "value", "required", "placeholder", "disabled", "aria-orientation", "selectable", "tabIndex"], outputs: ["change", "valueChange"], exportAs: ["matChipList"] }, { type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: InfoComponent, selector: "crispr-info", inputs: ["info"] }, { type: i4$3.MatAutocomplete, selector: "mat-autocomplete", inputs: ["disableRipple"], exportAs: ["matAutocomplete"] }, { type: i5$1.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { type: OptionComponent, selector: "crispr-option", inputs: ["option"] }], directives: [{ type: i4$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i4$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i5.ControlErrorContainerDirective, selector: "[controlErrorContainer], [formGroup]" }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2$1.MatLabel, selector: "mat-label" }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i3$2.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["color", "disableRipple", "tabIndex", "selected", "value", "selectable", "disabled", "removable"], outputs: ["selectionChange", "destroyed", "removed"], exportAs: ["matChip"] }, { type: i3$2.MatChipRemove, selector: "[matChipRemove]" }, { type: i4$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i4$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i5.ControlErrorsDirective, selector: "[formControl], [formControlName]" }, { type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { type: i3$2.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { type: i4$3.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", exportAs: ["matAutocompleteTrigger"] }, { type: i4$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i2$1.MatSuffix, selector: "[matSuffix]" }], pipes: { "async": i2.AsyncPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush, preserveWhitespaces: true });
|
|
707
708
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: AutocompleteChiplistFieldComponent, decorators: [{
|
|
708
709
|
type: Component,
|
|
709
|
-
args: [{ selector: 'crispr-autocomplete-chiplist-field', changeDetection: ChangeDetectionStrategy.OnPush, template: "<crispr-field-container\n [config]=\"config\"\n [group]=\"group\">\n\n <mat-form-field\n [color]=\"config.color\"\n [appearance]=\"config.appearance\">\n <mat-label *ngIf=\"config.label\">\n {{config.label}}\n </mat-label>\n\n <mat-chip-list\n #chipList>\n <mat-chip\n class=\"refinement-chip\"\n *ngFor=\"let chip of controlValue$ | async\"\n [selectable]=\"config.chipsSelectable\"\n [removable]=\"config.areChipsRemovable\"\n (removed)=\"removeChip(chip)\">\n <span *ngIf=\"chip.imageUrl as imageUrl\" class=\"image-container\">\n <img [src]=\"imageUrl\" alt=\"image of {{chip.label}}\">\n </span>\n {{chip.label}}\n <mat-icon\n matChipRemove\n *ngIf=\"config.areChipsRemovable\">\n cancel\n </mat-icon>\n </mat-chip>\n\n <input\n matInput\n [formControl]=\"autocompleteInputControl\"\n [placeholder]=\"config.placeholder\"\n [matChipInputFor]=\"chipList\"\n [matAutocomplete]=\"auto\"\n (keydown.tab)=\"handleTab($event)\"\n [matChipInputSeparatorKeyCodes]=\"config.separatorKeyCodes\"\n (matChipInputTokenEnd)=\"handleTokenEnd($event)\"\n role=\"searchbox\"\n type=\"text\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n #autoInput/>\n <span *ngIf=\"config.fieldSuffix\" matSuffix>{{config.fieldSuffix}}</span>\n <crispr-info\n *ngIf=\"config.info\"\n matSuffix\n [info]=\"config.info \">\n </crispr-info>\n\n </mat-chip-list>\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [autoActiveFirstOption]=\"this.config.autoActiveFirstOption\"\n (optionSelected)=\"handleSelect($event)\"\n [displayWith]=\"mapToLabel\">\n <ng-container *ngIf=\"remainingOptions$ | async as remainingOptions; else emptyOption\">\n <ng-container *ngIf=\"remainingOptions.length > 0; else emptyOption\">\n <mat-option\n *ngFor=\"let option of remainingOptions$ | async\"\n [value]=\"option\">\n <crispr-option [option]=\"option\"></crispr-option>\n </mat-option>\n </ng-container>\n\n </ng-container>\n <ng-template #emptyOption>\n <mat-option disabled=\"true\">\n {{config.emptyOptionsMessage}}\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n\n </mat-form-field>\n</crispr-field-container>\n", styles: [".refinement-chip .image-container{width:26px;height:26px;border-radius:50%;overflow:hidden;margin:-2px 4px -2px -8px}.refinement-chip .image-container img{width:100%;height:100%;object-fit:cover}\n"] }]
|
|
710
|
+
args: [{ selector: 'crispr-autocomplete-chiplist-field', changeDetection: ChangeDetectionStrategy.OnPush, template: "<crispr-field-container\n [config]=\"config\"\n [group]=\"group\">\n\n <mat-form-field\n [color]=\"config.color\"\n [appearance]=\"config.appearance\"\n [formGroup]=\"group\">\n <mat-label *ngIf=\"config.label\">\n {{config.label}}\n </mat-label>\n\n <mat-chip-list\n #chipList>\n <mat-chip\n class=\"refinement-chip\"\n *ngFor=\"let chip of controlValue$ | async\"\n [selectable]=\"config.chipsSelectable\"\n [removable]=\"config.areChipsRemovable\"\n (removed)=\"removeChip(chip)\">\n <span *ngIf=\"chip.imageUrl as imageUrl\" class=\"image-container\">\n <img [src]=\"imageUrl\" alt=\"image of {{chip.label}}\">\n </span>\n {{chip.label}}\n <mat-icon\n matChipRemove\n *ngIf=\"config.areChipsRemovable\">\n cancel\n </mat-icon>\n </mat-chip>\n <input class=\"hidden-error-directive-anchor\" [formControlName]=\"config.controlName\" type=\"text\"/>\n\n <input\n matInput\n [formControl]=\"autocompleteInputControl\"\n [placeholder]=\"config.placeholder\"\n [matChipInputFor]=\"chipList\"\n [matAutocomplete]=\"auto\"\n (keydown.tab)=\"handleTab($event)\"\n [matChipInputSeparatorKeyCodes]=\"config.separatorKeyCodes\"\n (matChipInputTokenEnd)=\"handleTokenEnd($event)\"\n role=\"searchbox\"\n type=\"text\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n #autoInput/>\n <span *ngIf=\"config.fieldSuffix\" matSuffix>{{config.fieldSuffix}}</span>\n <crispr-info\n *ngIf=\"config.info\"\n matSuffix\n [info]=\"config.info \">\n </crispr-info>\n\n </mat-chip-list>\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [autoActiveFirstOption]=\"this.config.autoActiveFirstOption\"\n (optionSelected)=\"handleSelect($event)\"\n [displayWith]=\"mapToLabel\">\n <ng-container *ngIf=\"remainingOptions$ | async as remainingOptions; else emptyOption\">\n <ng-container *ngIf=\"remainingOptions.length > 0; else emptyOption\">\n <mat-option\n *ngFor=\"let option of remainingOptions$ | async\"\n [value]=\"option\">\n <crispr-option [option]=\"option\"></crispr-option>\n </mat-option>\n </ng-container>\n\n </ng-container>\n <ng-template #emptyOption>\n <mat-option disabled=\"true\">\n {{config.emptyOptionsMessage}}\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n\n </mat-form-field>\n</crispr-field-container>\n", styles: [".refinement-chip .image-container{width:26px;height:26px;border-radius:50%;overflow:hidden;margin:-2px 4px -2px -8px}.refinement-chip .image-container img{width:100%;height:100%;object-fit:cover}.hidden-error-directive-anchor{display:none}\n"] }]
|
|
710
711
|
}], propDecorators: { chipInput: [{
|
|
711
712
|
type: ViewChild,
|
|
712
713
|
args: ['autoInput', { read: MatAutocompleteTrigger }]
|
|
@@ -1127,7 +1128,6 @@ function convertBytesToMb(bytes) {
|
|
|
1127
1128
|
|
|
1128
1129
|
const ImageUploadFieldMixin = crisprControlMixin(CrisprFieldComponent);
|
|
1129
1130
|
class ImageUploadFieldComponent extends ImageUploadFieldMixin {
|
|
1130
|
-
// ngControl: NgControl = null;
|
|
1131
1131
|
constructor(ngControl, domSanitizer, cdr) {
|
|
1132
1132
|
super();
|
|
1133
1133
|
this.ngControl = ngControl;
|
|
@@ -1148,24 +1148,28 @@ class ImageUploadFieldComponent extends ImageUploadFieldMixin {
|
|
|
1148
1148
|
minCompressionThresholdMb: .6,
|
|
1149
1149
|
useWebWorker: true,
|
|
1150
1150
|
};
|
|
1151
|
+
/**
|
|
1152
|
+
* The url mapped from initial input value, consumers can us mapInputValueToUrl in config to transform their input value into a src url for the image element
|
|
1153
|
+
*/
|
|
1154
|
+
// inputValueUrl: string | undefined;
|
|
1151
1155
|
this.inputImageFileSubject = new Subject();
|
|
1152
1156
|
this.inputImageFile$ = this.inputImageFileSubject.asObservable().pipe(shareReplay(1));
|
|
1153
1157
|
this.compressedFile$ = this.inputImageFile$.pipe(switchMap(async (file) => {
|
|
1154
|
-
if (!file)
|
|
1158
|
+
if (!file || !(file instanceof File))
|
|
1155
1159
|
return null;
|
|
1156
|
-
if (typeof file === 'string')
|
|
1157
|
-
return file;
|
|
1158
1160
|
const { compressImage, targetCompressedImageFileSizeMb, minCompressionThresholdMb, maxWidthOrHeight, useWebWorker } = this.config;
|
|
1159
1161
|
const fileSizeMb = convertBytesToMb(file.size);
|
|
1160
|
-
const shouldCompress = compressImage
|
|
1162
|
+
const shouldCompress = compressImage
|
|
1163
|
+
&& targetCompressedImageFileSizeMb < fileSizeMb
|
|
1164
|
+
&& minCompressionThresholdMb < fileSizeMb;
|
|
1161
1165
|
if (shouldCompress) {
|
|
1162
1166
|
const options = {
|
|
1163
1167
|
// Use maxWidthOrHeight if it was provided, else use target compressed size
|
|
1164
1168
|
...(maxWidthOrHeight ? { maxWidthOrHeight } : { maxSizeMB: targetCompressedImageFileSizeMb }),
|
|
1165
1169
|
useWebWorker,
|
|
1166
1170
|
maxIterations: 30,
|
|
1167
|
-
onProgress: (
|
|
1168
|
-
this.progress =
|
|
1171
|
+
onProgress: (progress) => {
|
|
1172
|
+
this.progress = progress;
|
|
1169
1173
|
this.cdr.detectChanges();
|
|
1170
1174
|
}
|
|
1171
1175
|
};
|
|
@@ -1173,23 +1177,27 @@ class ImageUploadFieldComponent extends ImageUploadFieldMixin {
|
|
|
1173
1177
|
}
|
|
1174
1178
|
else
|
|
1175
1179
|
return file;
|
|
1176
|
-
}), map((
|
|
1177
|
-
if (!
|
|
1178
|
-
return
|
|
1179
|
-
const { type, name, lastModified } =
|
|
1180
|
-
return new File([
|
|
1180
|
+
}), map((compressedFile) => {
|
|
1181
|
+
if (!compressedFile)
|
|
1182
|
+
return compressedFile;
|
|
1183
|
+
const { type, name, lastModified } = compressedFile;
|
|
1184
|
+
return new File([compressedFile], this.config.fileName || name, {
|
|
1181
1185
|
type,
|
|
1182
1186
|
lastModified
|
|
1183
1187
|
});
|
|
1184
1188
|
}), tap(file => {
|
|
1185
|
-
|
|
1189
|
+
// only set the value if there is a file because resetToInitialValue sets the control value
|
|
1190
|
+
// to initial value and clears the compressed image, so setting it to null here would undo that
|
|
1191
|
+
if (file) {
|
|
1192
|
+
this.control.setValue(file);
|
|
1193
|
+
}
|
|
1186
1194
|
if (file || this.value) {
|
|
1187
1195
|
this.control.markAsTouched();
|
|
1188
1196
|
setTimeout(() => {
|
|
1189
1197
|
this.cdr.detectChanges();
|
|
1190
1198
|
}, 100);
|
|
1191
1199
|
}
|
|
1192
|
-
}),
|
|
1200
|
+
}), shareReplay(1));
|
|
1193
1201
|
this.compressedImageUrlString$ = this.compressedFile$.pipe(switchMap(async (file) => {
|
|
1194
1202
|
if (!file)
|
|
1195
1203
|
return null;
|
|
@@ -1205,11 +1213,18 @@ class ImageUploadFieldComponent extends ImageUploadFieldMixin {
|
|
|
1205
1213
|
}
|
|
1206
1214
|
ngOnInit() {
|
|
1207
1215
|
super.ngOnInit();
|
|
1216
|
+
// this.inputValueUrl = this.value && this.config.mapInputValueToUrl?.(this.value) || this.value;
|
|
1208
1217
|
this.imageSrcUrl$ = combineLatest([
|
|
1209
1218
|
this.compressedImageUrlString$.pipe(startWith(null)),
|
|
1210
|
-
this.control.valueChanges.pipe(startWith(this.value))
|
|
1219
|
+
this.control.valueChanges.pipe(startWith(this.value), map((controlValue) => {
|
|
1220
|
+
if (!controlValue)
|
|
1221
|
+
return null;
|
|
1222
|
+
if (controlValue instanceof File)
|
|
1223
|
+
return null;
|
|
1224
|
+
return this.config.mapInputValueToUrl?.(controlValue) || controlValue;
|
|
1225
|
+
}))
|
|
1211
1226
|
]).pipe(map(([compressedImageUrlString, controlUrl]) => {
|
|
1212
|
-
return compressedImageUrlString ||
|
|
1227
|
+
return compressedImageUrlString || controlUrl || null;
|
|
1213
1228
|
}));
|
|
1214
1229
|
this.disabled$ = this.config.disabledCallback
|
|
1215
1230
|
? this.config.disabledCallback(this.group, this.config)
|
|
@@ -1218,6 +1233,10 @@ class ImageUploadFieldComponent extends ImageUploadFieldMixin {
|
|
|
1218
1233
|
filesChanged(files) {
|
|
1219
1234
|
if (files && files.length > 0) {
|
|
1220
1235
|
const imageFile = files[0];
|
|
1236
|
+
// We clear the control here so that it is invalid while compressing
|
|
1237
|
+
// the input pipeline will take care of resetting it
|
|
1238
|
+
this.control.patchValue(null);
|
|
1239
|
+
// this.control.setErrors({compressing: {loadingMessage: 'Please wait while file compresses'}});
|
|
1221
1240
|
this.control.markAsTouched();
|
|
1222
1241
|
this.inputImageFileSubject.next(imageFile);
|
|
1223
1242
|
}
|
|
@@ -1230,8 +1249,8 @@ class ImageUploadFieldComponent extends ImageUploadFieldMixin {
|
|
|
1230
1249
|
this.isUploaded = false;
|
|
1231
1250
|
}
|
|
1232
1251
|
resetToInitialValue(initialValue) {
|
|
1233
|
-
this.inputImageFileSubject.next(
|
|
1234
|
-
|
|
1252
|
+
this.inputImageFileSubject.next(null);
|
|
1253
|
+
this.control.patchValue(initialValue);
|
|
1235
1254
|
this.isUploaded = false;
|
|
1236
1255
|
}
|
|
1237
1256
|
writeValue(value) { }
|
|
@@ -1310,7 +1329,6 @@ function maxFileSizeValidator(maxFileSize) {
|
|
|
1310
1329
|
const actualFileSize = +convertBytesToMb(controlValue.size).toPrecision(3);
|
|
1311
1330
|
if (maxFileSize > actualFileSize)
|
|
1312
1331
|
return null;
|
|
1313
|
-
// control.setErrors({maxFileSize: { maxFileSize, actualFileSize }} )
|
|
1314
1332
|
return { maxFileSize: { maxFileSize, actualFileSize } };
|
|
1315
1333
|
};
|
|
1316
1334
|
}
|
|
@@ -1320,7 +1338,7 @@ function allowedFileExtValidator(allowedFileExtensions, onlyAllowFiles = true) {
|
|
|
1320
1338
|
const isFile = controlValue instanceof File;
|
|
1321
1339
|
if (!onlyAllowFiles && !isFile)
|
|
1322
1340
|
return null;
|
|
1323
|
-
const actualExtension = isFile ? controlValue.name.split('.').pop() : 'Not a file';
|
|
1341
|
+
const actualExtension = isFile ? controlValue.name.split('.').pop().toLocaleLowerCase() : 'Not a file';
|
|
1324
1342
|
const isAllowedExtension = allowedFileExtensions.includes(actualExtension);
|
|
1325
1343
|
if (isAllowedExtension) {
|
|
1326
1344
|
return null;
|