novo-elements
Version:
770 lines (762 loc) • 181 kB
JavaScript
import * as i0 from '@angular/core';
import { signal, computed, Injectable, InjectionToken, Directive, ContentChild, Input, ViewChildren, ViewChild, input, inject, ElementRef, ChangeDetectionStrategy, ViewEncapsulation, Component, SkipSelf, Optional, HostBinding, forwardRef, viewChild, viewChildren, ContentChildren, NgModule } from '@angular/core';
import * as i3 from '@angular/forms';
import { FormControlName, FormControl, Validators, NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i1 from 'novo-elements/services';
import { NovoLabelService } from 'novo-elements/services';
import { Subject, Subscription, merge, interval } from 'rxjs';
import { debounceTime, distinctUntilChanged, takeUntil, startWith, filter, debounce } from 'rxjs/operators';
import { Helpers, BooleanInput } from 'novo-elements/utils';
import * as i2 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i5 from 'novo-elements/elements/common';
import { NovoCommonModule, NovoOptionModule, NovoOverlayModule } from 'novo-elements/elements/common';
import * as i5$1 from 'novo-elements/elements/select';
import { NovoSelectElement, NovoSelectModule } from 'novo-elements/elements/select';
import * as i6 from 'novo-elements/elements/field';
import { NovoPickerToggleElement, NovoFieldModule } from 'novo-elements/elements/field';
import * as i8 from 'novo-elements/elements/flex';
import { NovoFlexModule } from 'novo-elements/elements/flex';
import * as i9$3 from 'novo-elements/elements/loading';
import { NovoLoadingModule } from 'novo-elements/elements/loading';
import * as i9$2 from 'novo-elements/elements/select-search';
import { NovoSelectSearchModule } from 'novo-elements/elements/select-search';
import * as i4 from 'novo-elements/elements/places';
import { GooglePlacesModule } from 'novo-elements/elements/places';
import * as i9 from 'novo-elements/elements/icon';
import { NovoIconModule } from 'novo-elements/elements/icon';
import * as i10 from 'novo-elements/elements/chips';
import { NovoChipsModule } from 'novo-elements/elements/chips';
import * as i11 from 'novo-elements/elements/tooltip';
import { NovoTooltipModule } from 'novo-elements/elements/tooltip';
import * as i9$1 from 'novo-elements/elements/radio';
import { NovoRadioModule } from 'novo-elements/elements/radio';
import * as i7 from 'novo-elements/elements/date-picker';
import { NovoDatePickerModule } from 'novo-elements/elements/date-picker';
import * as i8$1 from 'novo-elements/elements/date-time-picker';
import { NovoDateTimePickerModule } from 'novo-elements/elements/date-time-picker';
import * as i4$1 from 'novo-elements/elements/autocomplete';
import { NovoAutoCompleteModule } from 'novo-elements/elements/autocomplete';
import * as i8$2 from 'novo-elements/elements/form';
import { NovoFormModule, NovoFormExtrasModule } from 'novo-elements/elements/form';
import * as i7$1 from 'novo-elements/elements/tabbed-group-picker';
import { NovoTabbedGroupPickerElement, NovoTabbedGroupPickerModule } from 'novo-elements/elements/tabbed-group-picker';
import * as i5$2 from 'novo-elements/elements/button';
import { NovoButtonModule } from 'novo-elements/elements/button';
import * as i8$3 from 'novo-elements/elements/dropdown';
import { NovoDropdownModule } from 'novo-elements/elements/dropdown';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { CdkTableModule } from '@angular/cdk/table';
import { NovoCardModule } from 'novo-elements/elements/card';
import { NovoNonIdealStateModule } from 'novo-elements/elements/non-ideal-state';
import { NovoSearchBoxModule } from 'novo-elements/elements/search';
import { NovoSwitchModule } from 'novo-elements/elements/switch';
import { NovoTabModule } from 'novo-elements/elements/tabs';
var Conjunction;
(function (Conjunction) {
Conjunction["AND"] = "and";
Conjunction["OR"] = "or";
Conjunction["NOT"] = "not";
})(Conjunction || (Conjunction = {}));
var Operator;
(function (Operator) {
Operator["after"] = "after";
Operator["before"] = "before";
Operator["beginsWith"] = "beginsWith";
Operator["between"] = "between";
Operator["equalTo"] = "equalTo";
Operator["exclude"] = "exclude";
Operator["excludeAny"] = "excludeAny";
Operator["greaterThan"] = "greaterThan";
Operator["include"] = "include";
Operator["includeAll"] = "includeAll";
Operator["includeAny"] = "includeAny";
Operator["insideRadius"] = "insideRadius";
Operator["isEmpty"] = "isEmpty";
Operator["isNull"] = "isNull";
Operator["lessThan"] = "lessThan";
Operator["outsideRadius"] = "outsideRadius";
Operator["radius"] = "radius";
Operator["within"] = "within";
})(Operator || (Operator = {}));
var RadiusUnits;
(function (RadiusUnits) {
RadiusUnits["miles"] = "miles";
RadiusUnits["km"] = "km";
})(RadiusUnits || (RadiusUnits = {}));
const defaultEditTypeFn = (field) => {
return field.inputType || field.dataType || field.type;
};
class QueryBuilderService {
/**
* Function to determine operator and input templates for a field. Value passed
* through the criteria builder Input.
*/
get editTypeFn() {
return this._editTypeFn;
}
set editTypeFn(value) {
this._editTypeFn = value ?? defaultEditTypeFn;
this.stateChanges.next();
}
/**
* The field configuration to control which types of fields are available to select
* within the Condition Builder.
*/
get config() {
return this._config;
}
set config(value) {
this._config = value;
this.stateChanges.next();
}
/**
* The configuration to control which types of conjuntions can be used in the query builder.
* Value passed through the criteria builder Input
* eg. and, or, not
*/
get allowedGroupings() {
return this._allowedGroupings;
}
set allowedGroupings(value) {
this._allowedGroupings = value;
this.stateChanges.next();
}
constructor(labels) {
this.labels = labels;
this._customFieldDefs = new Set();
this._fieldDefsByName = new Map();
this.scopes = signal([], ...(ngDevMode ? [{ debugName: "scopes" }] : []));
this.hasMultipleScopes = computed(() => this.scopes()?.length > 1, ...(ngDevMode ? [{ debugName: "hasMultipleScopes" }] : []));
/**
* Will dispatch when properties changes, subscribe to this if component should
* re-render when props are updated
*/
this.stateChanges = new Subject();
this._editTypeFn = defaultEditTypeFn;
this._config = {
fields: [],
staticFieldSelection: null,
};
}
/** Adds a field definition that was not included as part of the content children. */
registerFieldDef(fieldDef) {
this._customFieldDefs.add(fieldDef);
this._fieldDefsByName.set(fieldDef.name, fieldDef);
}
/** Removes a field definition that was not included as part of the content children. */
unregisterFieldDef(fieldDef) {
this._customFieldDefs.delete(fieldDef);
this._fieldDefsByName.delete(fieldDef.name);
}
getFieldDefsByName() {
return this._fieldDefsByName;
}
getConjunctionLabel(conjunction) {
switch (conjunction.replace('$', '').toLowerCase()) {
case Conjunction.OR:
return this.labels.or;
case Conjunction.NOT:
return this.labels.not;
case Conjunction.AND:
default:
return this.labels.and;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: QueryBuilderService, deps: [{ token: i1.NovoLabelService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: QueryBuilderService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: QueryBuilderService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1.NovoLabelService }] });
const NOVO_QUERY_BUILDER = new InjectionToken('NOVO_QUERY_BUILDER');
const NOVO_CRITERIA_BUILDER = new InjectionToken('NOVO_CRITERIA_BUILDER');
const NOVO_CONDITION_BUILDER = new InjectionToken('NOVO_CONDITION_BUILDER');
/**
* Contained within a novoConditionField definition describing what input should be
* used to capture the compare value of the Condtion
*/
class NovoConditionInputDef {
constructor(/** @docs-private */ template) {
this.template = template;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoConditionInputDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NovoConditionInputDef, isStandalone: false, selector: "[novoConditionInputDef]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoConditionInputDef, decorators: [{
type: Directive,
args: [{
selector: '[novoConditionInputDef]',
standalone: false,
}]
}], ctorParameters: () => [{ type: i0.TemplateRef }] });
/**
* Contained within a novoConditionField definition describing what operators should be available.
*/
class NovoConditionOperatorsDef {
constructor(/** @docs-private */ template) {
this.template = template;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoConditionOperatorsDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NovoConditionOperatorsDef, isStandalone: false, selector: "[novoConditionOperatorsDef]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoConditionOperatorsDef, decorators: [{
type: Directive,
args: [{
selector: '[novoConditionOperatorsDef]',
standalone: false,
}]
}], ctorParameters: () => [{ type: i0.TemplateRef }] });
/**
* Field Field definition for the QueryBuilder.
* Defines the inputType and operators to use for the query builder.
*/
class BaseConditionFieldDef {
/** Unique name for this field. */
get name() {
return this._name;
}
set name(name) {
this._setNameInput(name);
}
constructor() { }
/**
* Overridable method that sets the css classes that will be added to every cell in this
* column.
* In the future, columnCssClassName will change from type string[] to string and this
* will set a single string value.
* @docs-private
*/
_updateFieldCssClassName() {
this._fieldCssClassName = [`novo-filter-field-${this.cssClassFriendlyName}`];
}
_setNameInput(value) {
// If the directive is set without a name (updated programmatically), then this setter will
// trigger with an empty string and should not overwrite the programmatically set value.
if (value) {
this._name = value;
this.cssClassFriendlyName = value.replace(/[^a-z0-9_-]/gi, '-');
this._updateFieldCssClassName();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BaseConditionFieldDef, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: BaseConditionFieldDef, isStandalone: true, inputs: { name: ["novoFilterFieldDef", "name"] }, queries: [{ propertyName: "fieldInput", first: true, predicate: NovoConditionInputDef, descendants: true }, { propertyName: "fieldOperators", first: true, predicate: NovoConditionOperatorsDef, descendants: true }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BaseConditionFieldDef, decorators: [{
type: Directive
}], ctorParameters: () => [], propDecorators: { name: [{
type: Input,
args: ['novoFilterFieldDef']
}], fieldInput: [{
type: ContentChild,
args: [NovoConditionInputDef]
}], fieldOperators: [{
type: ContentChild,
args: [NovoConditionOperatorsDef]
}] } });
class NovoConditionFieldDef extends BaseConditionFieldDef {
constructor(qbs) {
super();
this.qbs = qbs;
}
register() {
this.qbs.registerFieldDef(this);
}
unregister() {
this.qbs.unregisterFieldDef(this);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoConditionFieldDef, deps: [{ token: QueryBuilderService }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NovoConditionFieldDef, isStandalone: false, selector: "[novoConditionFieldDef]", usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoConditionFieldDef, decorators: [{
type: Directive,
args: [{
selector: '[novoConditionFieldDef]',
standalone: false,
}]
}], ctorParameters: () => [{ type: QueryBuilderService }] });
class AbstractConditionFieldDef {
/** Column name that should be used to reference this column. */
get name() {
return this._name;
}
set name(name) {
this._name = name;
// With Ivy, inputs can be initialized before static query results are
// available. In that case, we defer the synchronization until "ngOnInit" fires.
this._syncFieldDefName();
}
constructor(labels) {
this.labels = labels;
this.operatorEditGroups = [];
}
ngOnInit() {
this._syncFieldDefName();
this._syncFieldDefOperatorValue();
this._previousOperatorValue = this.defaultOperator;
// Need to add self to FilterBuilder because "ContentChildren won't find it"
this.fieldDef?.register();
}
ngAfterViewInit() {
setTimeout(() => {
this.frameAfterViewInit();
});
}
frameAfterViewInit() {
const operatorField = this.formControlsByName.find(formControlDirective => formControlDirective.name === 'operator')?.control;
if (operatorField) {
this._previousOperatorValue = operatorField.value;
}
}
ngOnDestroy() {
this.fieldDef?.unregister();
}
/**
* Define an edit group of operators. Once defined, if the user switches from one of these operators to another,
* then the condition value will not be cleared. This makes sense if both operators use the same UI controls for editing.
* @param operators The set of Operator values intended to share UI controls.
*/
defineOperatorEditGroup(...operators) {
this.operatorEditGroups.push(new Set(operators));
}
onOperatorSelect(formGroup) {
let clearVal = true;
if (this._previousOperatorValue && this.operatorEditGroups?.length) {
const previousOperatorGroupIndex = this.operatorEditGroups.findIndex(grp => grp.has(this._previousOperatorValue));
const newOperatorValue = formGroup.get('operator').getRawValue();
const newOperatorGroupIndex = this.operatorEditGroups.findIndex(grp => grp.has(newOperatorValue));
if (previousOperatorGroupIndex !== -1 && newOperatorGroupIndex !== -1 && previousOperatorGroupIndex === newOperatorGroupIndex) {
clearVal = false;
}
}
this._previousOperatorValue = formGroup.get('operator').value;
if (clearVal) {
formGroup.get('value').setValue(null);
}
}
/** Synchronizes the column definition name with the text column name. */
_syncFieldDefName() {
if (this.fieldDef) {
this.fieldDef.name = this.name;
}
}
_syncFieldDefOperatorValue() {
if (this.fieldDef) {
this.fieldDef.defaultOperator = this.defaultOperator;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AbstractConditionFieldDef, deps: [{ token: i1.NovoLabelService }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: AbstractConditionFieldDef, isStandalone: true, inputs: { name: "name" }, viewQueries: [{ propertyName: "fieldDef", first: true, predicate: NovoConditionFieldDef, descendants: true, static: true }, { propertyName: "formControlsByName", predicate: FormControlName, descendants: true }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AbstractConditionFieldDef, decorators: [{
type: Directive
}], ctorParameters: () => [{ type: i1.NovoLabelService }], propDecorators: { name: [{
type: Input
}], fieldDef: [{
type: ViewChild,
args: [NovoConditionFieldDef, { static: true }]
}], formControlsByName: [{
type: ViewChildren,
args: [FormControlName]
}] } });
/**
* Handle selection of field values when a list of options is provided.
*/
class NovoDefaultAddressConditionDef extends AbstractConditionFieldDef {
constructor(labelService) {
super(labelService);
// Overridable defaults
this.defaults = {
radiusEnabled: false,
radiusUnits: 'miles',
};
this.config = input(...(ngDevMode ? [undefined, { debugName: "config" }] : []));
this.radiusUnits = computed(() => this.config()?.radiusUnits || this.defaults.radiusUnits, ...(ngDevMode ? [{ debugName: "radiusUnits" }] : []));
this.radiusEnabled = computed(() => this.config()?.radiusEnabled || this.defaults.radiusEnabled, ...(ngDevMode ? [{ debugName: "radiusEnabled" }] : []));
this.unitsLabel = computed(() => this.radiusUnits() === RadiusUnits.miles ? this.labels.miles : this.labels.km, ...(ngDevMode ? [{ debugName: "unitsLabel" }] : []));
this.defaultOperator = Operator.includeAny;
this.chipListModel = '';
this.term = '';
this._addressChangesSubscription = Subscription.EMPTY;
this.element = inject(ElementRef);
this.defineOperatorEditGroup(Operator.includeAny, Operator.excludeAny, Operator.insideRadius, Operator.outsideRadius);
}
ngOnDestroy() {
this._addressChangesSubscription.unsubscribe();
}
onKeyup(event, viewIndex) {
if (!["Escape" /* Key.Escape */, "Enter" /* Key.Enter */].includes(event.key)) {
this.openPlacesList(viewIndex);
}
this.term = event.target.value;
}
onKeydown(event, viewIndex) {
if (!this.placesPicker.dropdownOpen) {
this.openPlacesList(viewIndex);
this.placesPicker.dropdownOpen = true;
}
if (["Escape" /* Key.Escape */, "Tab" /* Key.Tab */].includes(event.key)) {
this.closePlacesList(viewIndex);
}
else {
this.placesPicker.onKeyDown(event);
}
}
getValue(formGroup) {
return formGroup.value.value || [];
}
getCurrentOverlay(viewIndex) {
return this.overlayChildren?.find(item => item.overlayId === viewIndex);
}
getCurrentInput(viewIndex) {
return this.inputChildren?.find(item => item.nativeElement.id === viewIndex);
}
openPlacesList(viewIndex) {
this.getCurrentOverlay(viewIndex)?.openPanel();
}
closePlacesList(viewIndex) {
this.getCurrentOverlay(viewIndex)?.closePanel();
}
selectPlace(event, formGroup, viewIndex) {
const valueToAdd = {
address_components: event.address_components,
formatted_address: event.formatted_address,
geometry: event.geometry,
name: event.name,
postal_codes: event.postal_codes,
place_id: event.place_id,
types: event.types,
};
const current = this.getValue(formGroup);
const updated = Array.isArray(current) ? [...current, valueToAdd] : [valueToAdd];
formGroup.get('value').setValue(this.updateRadiusInValues(formGroup, updated));
this.inputChildren.forEach(inputChild => {
inputChild.nativeElement.value = '';
});
this.getCurrentInput(viewIndex)?.nativeElement.focus();
this.closePlacesList(viewIndex);
}
remove(valueToRemove, formGroup, viewIndex) {
const current = this.getValue(formGroup);
const index = current.indexOf(valueToRemove);
if (index >= 0) {
const oldValue = [...current];
oldValue.splice(index, 1);
formGroup.get('value').setValue(oldValue);
}
this.closePlacesList(viewIndex);
}
// Override abstract behavior - allow moving location from includeAny to radius, but when moving the opposite direction,
// trim out radius information from the value
onOperatorSelect(formGroup) {
const previousOperator = this._previousOperatorValue;
super.onOperatorSelect(formGroup);
if ([previousOperator, formGroup.get('operator').getRawValue()].indexOf(Operator.insideRadius) !== -1 &&
formGroup.get('value').getRawValue() != null) {
formGroup.get('value').setValue(this.updateRadiusInValues(formGroup, this.getValue(formGroup)));
}
}
onRadiusSelect(formGroup, event) {
const maxLengthRadius = event.target.value.slice(0, 4);
event.target.value = maxLengthRadius;
formGroup.get('supportingValue').setValue(maxLengthRadius);
// We must dirty the form explicitly to show up as a user modification when it was done programmatically
formGroup.get('value').setValue(this.updateRadiusInValues(formGroup, this.getValue(formGroup)));
formGroup.markAsDirty();
}
updateRadiusInValues(formGroup, values) {
return values.map(val => ({
...val,
radius: this.isRadiusOperatorSelected(formGroup) ? this.getRadiusData(formGroup) : undefined,
}));
}
getRadiusData(formGroup) {
return {
value: formGroup.value.supportingValue,
units: this.radiusUnits(),
operator: formGroup.value.operator,
};
}
isRadiusOperatorSelected(formGroup) {
return ['insideRadius', 'outsideRadius'].includes(formGroup.get('operator')?.value) && formGroup.value?.supportingValue !== null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDefaultAddressConditionDef, deps: [{ token: i1.NovoLabelService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.19", type: NovoDefaultAddressConditionDef, isStandalone: false, selector: "novo-address-condition-def", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "placesPicker", first: true, predicate: ["placesPicker"], descendants: true }, { propertyName: "overlayChildren", predicate: NovoPickerToggleElement, descendants: true }, { propertyName: "inputChildren", predicate: ["addressInput"], descendants: true }, { propertyName: "addressSideTest", predicate: NovoSelectElement, descendants: true }], usesInheritance: true, ngImport: i0, template: `
<ng-container novoConditionFieldDef>
<novo-field *novoConditionOperatorsDef="let formGroup" [formGroup]="formGroup">
<novo-select [placeholder]="labels.operator" formControlName="operator" (onSelect)="onOperatorSelect(formGroup)">
<novo-option value="includeAny">{{ labels.includeAny }}</novo-option>
<novo-option value="excludeAny">{{ labels.exclude }}</novo-option>
<novo-option value="insideRadius" *ngIf="radiusEnabled()">{{ labels.insideRadius }}</novo-option>
<novo-option value="outsideRadius" *ngIf="radiusEnabled()">{{ labels.outsideRadius }}</novo-option>
</novo-select>
</novo-field>
<ng-container *novoConditionInputDef="let formGroup; viewIndex as viewIndex; fieldMeta as meta" [formGroup]="formGroup">
<novo-flex justify="space-between" align="end">
<novo-field #input *ngIf="['radius', 'insideRadius', 'outsideRadius'].includes(formGroup.value.operator)" class="address-radius">
<input
novoInput
paddingLeft="3px"
type="number"
min="1"
max="9999"
step="1"
formControlName="supportingValue"
#distanceInput
(input)="onRadiusSelect(formGroup, $event)"
/>
<span marginLeft="2px" marginRight="4px" paddingTop="3px">{{ unitsLabel() }}</span>
</novo-field>
<novo-field #novoField class="address-location">
<novo-chip-list [(ngModel)]="chipListModel" [ngModelOptions]="{ standalone: true }" (click)="openPlacesList(viewIndex)">
<novo-chip *ngFor="let item of formGroup.get('value').value" (removed)="remove(item, formGroup, viewIndex)">
<novo-text ellipsis [tooltip]="item.formatted_address" tooltipOnOverflow>{{ item.formatted_address }}</novo-text>
<novo-icon novoChipRemove>close</novo-icon>
</novo-chip>
<input
novoChipInput
[id]="viewIndex"
[placeholder]="labels.location"
(keyup)="onKeyup($event, viewIndex)"
(keydown)="onKeydown($event, viewIndex)"
[picker]="placesPicker"
#addressInput/>
</novo-chip-list>
<novo-picker-toggle [overlayId]="viewIndex" icon="location" novoSuffix>
<google-places-list
[term]="term"
(select)="selectPlace($event, formGroup, viewIndex)"
formControlName="value"
#placesPicker/>
</novo-picker-toggle>
</novo-field>
</novo-flex>
</ng-container>
</ng-container>
`, isInline: true, dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.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: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i4.PlacesListComponent, selector: "google-places-list", inputs: ["userSettings"], outputs: ["termChange", "select", "matchesUpdated"] }, { kind: "component", type: i5.NovoText, selector: "novo-text,[novo-text]", inputs: ["block"] }, { kind: "directive", type: i5.MarginDirective, selector: "[m],[margin],[marginTop],[marginRight],[marginBottom],[marginLeft],[marginX],[marginY],[mt],[mr],[mb],[ml],[mx],[my]", inputs: ["margin", "m", "marginLeft", "ml", "marginRight", "mr", "marginTop", "mt", "marginBottom", "mb", "marginX", "mx", "marginY", "my"] }, { kind: "directive", type: i5.PaddingDirective, selector: "[p],[padding],[paddingTop],[paddingRight],[paddingBottom],[paddingLeft],[paddingX],[paddingY],[pt],[pr],[pb],[pl],[px],[py]", inputs: ["padding", "p", "paddingLeft", "pl", "paddingRight", "pr", "paddingTop", "pt", "paddingBottom", "pb", "paddingX", "px", "paddingY", "py"] }, { kind: "component", type: i5$1.NovoSelectElement, selector: "novo-select", inputs: ["disabled", "required", "tabIndex", "id", "name", "placeholder", "readonly", "headerConfig", "position", "overlayWidth", "overlayHeight", "displayIcon", "displayWith", "compareWith", "hideLegacyOptions", "value", "multiple", "options"], outputs: ["onSelect", "selectionChange", "valueChange", "openedChange", "opened", "closed"] }, { kind: "component", type: i6.NovoFieldElement, selector: "novo-field", inputs: ["layout", "appearance", "customOverlayOrigin", "width"], outputs: ["valueChanges", "stateChanges"] }, { kind: "directive", type: i6.NovoInput, selector: "input[novoInput], textarea[novoInput], select[novoInput]", inputs: ["disabled", "id", "placeholder", "required", "type", "value", "readonly"], outputs: ["onSelect"] }, { kind: "directive", type: i6.NovoFieldSuffixDirective, selector: "[novoSuffix]" }, { kind: "component", type: i6.NovoPickerToggleElement, selector: "novo-picker-toggle", inputs: ["for", "icon", "tabIndex", "aria-label", "triggerOnFocus", "overlayId", "width", "disabled"], exportAs: ["novoPickerToggle"] }, { kind: "directive", type: i6.NovoPickerDirective, selector: "input[picker]", inputs: ["picker", "autocomplete"] }, { kind: "component", type: i5.NovoOption, selector: "novo-option", inputs: ["selected", "keepOpen", "novoInert", "value", "disabled"], exportAs: ["novoOption"] }, { kind: "component", type: i8.NovoFlexElement, selector: "novo-flex,novo-row", inputs: ["direction", "align", "justify", "wrap", "gap"] }, { kind: "component", type: i9.NovoIconComponent, selector: "novo-icon", inputs: ["raised", "theme", "shape", "color", "size", "smaller", "larger", "alt", "name"] }, { kind: "component", type: i10.NovoChipElement, selector: "novo-chip, [novo-chip]", inputs: ["color", "tabIndex", "size", "type", "selected", "value", "selectable", "disabled", "removable"], outputs: ["selectionChange", "destroyed", "removed"] }, { kind: "directive", type: i10.NovoChipRemove, selector: "[novoChipRemove]" }, { kind: "directive", type: i10.NovoChipInput, selector: "input[novoChipInput]", inputs: ["novoChipInputAddOnBlur", "novoChipInputSeparatorKeyCodes", "placeholder", "id", "disabled"], outputs: ["novoChipInputTokenEnd"], exportAs: ["novoChipInput", "novoChipInputFor"] }, { kind: "component", type: i10.NovoChipList, selector: "novo-chip-list", inputs: ["errorStateMatcher", "multiple", "chipsToggleable", "stacked", "compareWith", "value", "required", "placeholder", "disabled", "aria-orientation", "selectable", "tabIndex"], outputs: ["change", "valueChange"], exportAs: ["novoChipList"] }, { kind: "directive", type: i11.TooltipDirective, selector: "[tooltip]", inputs: ["tooltip", "tooltipPosition", "tooltipType", "tooltipSize", "tooltipBounce", "tooltipNoAnimate", "tooltipRounded", "tooltipAlways", "tooltipPreline", "removeTooltipArrow", "tooltipAutoPosition", "tooltipIsHTML", "tooltipCloseOnClick", "tooltipOnOverflow", "tooltipActive"] }, { kind: "directive", type: NovoConditionOperatorsDef, selector: "[novoConditionOperatorsDef]" }, { kind: "directive", type: NovoConditionInputDef, selector: "[novoConditionInputDef]" }, { kind: "directive", type: NovoConditionFieldDef, selector: "[novoConditionFieldDef]" }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDefaultAddressConditionDef, decorators: [{
type: Component,
args: [{
selector: 'novo-address-condition-def',
template: `
<ng-container novoConditionFieldDef>
<novo-field *novoConditionOperatorsDef="let formGroup" [formGroup]="formGroup">
<novo-select [placeholder]="labels.operator" formControlName="operator" (onSelect)="onOperatorSelect(formGroup)">
<novo-option value="includeAny">{{ labels.includeAny }}</novo-option>
<novo-option value="excludeAny">{{ labels.exclude }}</novo-option>
<novo-option value="insideRadius" *ngIf="radiusEnabled()">{{ labels.insideRadius }}</novo-option>
<novo-option value="outsideRadius" *ngIf="radiusEnabled()">{{ labels.outsideRadius }}</novo-option>
</novo-select>
</novo-field>
<ng-container *novoConditionInputDef="let formGroup; viewIndex as viewIndex; fieldMeta as meta" [formGroup]="formGroup">
<novo-flex justify="space-between" align="end">
<novo-field #input *ngIf="['radius', 'insideRadius', 'outsideRadius'].includes(formGroup.value.operator)" class="address-radius">
<input
novoInput
paddingLeft="3px"
type="number"
min="1"
max="9999"
step="1"
formControlName="supportingValue"
#distanceInput
(input)="onRadiusSelect(formGroup, $event)"
/>
<span marginLeft="2px" marginRight="4px" paddingTop="3px">{{ unitsLabel() }}</span>
</novo-field>
<novo-field #novoField class="address-location">
<novo-chip-list [(ngModel)]="chipListModel" [ngModelOptions]="{ standalone: true }" (click)="openPlacesList(viewIndex)">
<novo-chip *ngFor="let item of formGroup.get('value').value" (removed)="remove(item, formGroup, viewIndex)">
<novo-text ellipsis [tooltip]="item.formatted_address" tooltipOnOverflow>{{ item.formatted_address }}</novo-text>
<novo-icon novoChipRemove>close</novo-icon>
</novo-chip>
<input
novoChipInput
[id]="viewIndex"
[placeholder]="labels.location"
(keyup)="onKeyup($event, viewIndex)"
(keydown)="onKeydown($event, viewIndex)"
[picker]="placesPicker"
#addressInput/>
</novo-chip-list>
<novo-picker-toggle [overlayId]="viewIndex" icon="location" novoSuffix>
<google-places-list
[term]="term"
(select)="selectPlace($event, formGroup, viewIndex)"
formControlName="value"
#placesPicker/>
</novo-picker-toggle>
</novo-field>
</novo-flex>
</ng-container>
</ng-container>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.Default,
standalone: false,
}]
}], ctorParameters: () => [{ type: i1.NovoLabelService }], propDecorators: { overlayChildren: [{
type: ViewChildren,
args: [NovoPickerToggleElement]
}], inputChildren: [{
type: ViewChildren,
args: ['addressInput']
}], placesPicker: [{
type: ViewChild,
args: ['placesPicker']
}], addressSideTest: [{
type: ViewChildren,
args: [NovoSelectElement]
}], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }] } });
/**
* When constructing a query using a field that is a boolean with only true/false as possible values.
*/
class NovoDefaultBooleanConditionDef extends AbstractConditionFieldDef {
constructor(labelService) {
super(labelService);
this.defaultOperator = Operator.include;
this.defineOperatorEditGroup(Operator.include, Operator.exclude, Operator.isNull);
}
optIdentify(option) {
return option.label;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDefaultBooleanConditionDef, deps: [{ token: i1.NovoLabelService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NovoDefaultBooleanConditionDef, isStandalone: false, selector: "novo-boolean-condition-def", usesInheritance: true, ngImport: i0, template: `
<ng-container novoConditionFieldDef>
<novo-field *novoConditionOperatorsDef="let formGroup; fieldMeta as meta" [formGroup]="formGroup">
<novo-select [placeholder]="labels.operator" formControlName="operator" (onSelect)="onOperatorSelect(formGroup)">
<novo-option value="include">{{ labels.equals }}</novo-option>
<novo-option value="exclude">{{ labels.doesNotEqual }}</novo-option>
<novo-option value="isNull" *ngIf="!meta?.removeIsEmpty">{{ labels.isEmpty }}</novo-option>
</novo-select>
</novo-field>
<novo-field *novoConditionInputDef="let formGroup; fieldMeta as meta" [style.maxWidth.px]="250" [formGroup]="formGroup">
@let isNull = formGroup.value.operator === 'isNull';
@let useYesNo = isNull || meta.dataType === 'Boolean';
@let customOptions = !isNull && meta.options?.length === 2;
@if (customOptions) {
<novo-radio-group formControlName="value">
<novo-radio *ngFor="let opt of meta.options; trackBy: optIdentify" [value]="opt.value">{{ opt.label }}</novo-radio>
</novo-radio-group>
} @else {
<novo-radio-group formControlName="value">
<novo-radio [value]="true">{{ useYesNo ? labels.yes : labels.true }}</novo-radio>
<novo-radio [value]="false">{{ useYesNo ? labels.no : labels.false }}</novo-radio>
</novo-radio-group>
}
</novo-field>
</ng-container>
`, isInline: true, dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i5$1.NovoSelectElement, selector: "novo-select", inputs: ["disabled", "required", "tabIndex", "id", "name", "placeholder", "readonly", "headerConfig", "position", "overlayWidth", "overlayHeight", "displayIcon", "displayWith", "compareWith", "hideLegacyOptions", "value", "multiple", "options"], outputs: ["onSelect", "selectionChange", "valueChange", "openedChange", "opened", "closed"] }, { kind: "component", type: i6.NovoFieldElement, selector: "novo-field", inputs: ["layout", "appearance", "customOverlayOrigin", "width"], outputs: ["valueChanges", "stateChanges"] }, { kind: "component", type: i5.NovoOption, selector: "novo-option", inputs: ["selected", "keepOpen", "novoInert", "value", "disabled"], exportAs: ["novoOption"] }, { kind: "component", type: i9$1.NovoRadioElement, selector: "novo-radio", inputs: ["id", "name", "tabindex", "vertical", "label", "button", "theme", "size", "icon", "color", "disabled", "checked", "value"], outputs: ["change", "blur", "focus"] }, { kind: "component", type: i9$1.NovoRadioGroup, selector: "novo-radio-group", inputs: ["id", "tabindex", "errorStateMatcher", "appearance", "value", "name", "disabled", "required", "placeholder"], outputs: ["change", "blur"] }, { kind: "directive", type: NovoConditionOperatorsDef, selector: "[novoConditionOperatorsDef]" }, { kind: "directive", type: NovoConditionInputDef, selector: "[novoConditionInputDef]" }, { kind: "directive", type: NovoConditionFieldDef, selector: "[novoConditionFieldDef]" }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDefaultBooleanConditionDef, decorators: [{
type: Component,
args: [{
selector: 'novo-boolean-condition-def',
template: `
<ng-container novoConditionFieldDef>
<novo-field *novoConditionOperatorsDef="let formGroup; fieldMeta as meta" [formGroup]="formGroup">
<novo-select [placeholder]="labels.operator" formControlName="operator" (onSelect)="onOperatorSelect(formGroup)">
<novo-option value="include">{{ labels.equals }}</novo-option>
<novo-option value="exclude">{{ labels.doesNotEqual }}</novo-option>
<novo-option value="isNull" *ngIf="!meta?.removeIsEmpty">{{ labels.isEmpty }}</novo-option>
</novo-select>
</novo-field>
<novo-field *novoConditionInputDef="let formGroup; fieldMeta as meta" [style.maxWidth.px]="250" [formGroup]="formGroup">
@let isNull = formGroup.value.operator === 'isNull';
@let useYesNo = isNull || meta.dataType === 'Boolean';
@let customOptions = !isNull && meta.options?.length === 2;
@if (customOptions) {
<novo-radio-group formControlName="value">
<novo-radio *ngFor="let opt of meta.options; trackBy: optIdentify" [value]="opt.value">{{ opt.label }}</novo-radio>
</novo-radio-group>
} @else {
<novo-radio-group formControlName="value">
<novo-radio [value]="true">{{ useYesNo ? labels.yes : labels.true }}</novo-radio>
<novo-radio [value]="false">{{ useYesNo ? labels.no : labels.false }}</novo-radio>
</novo-radio-group>
}
</novo-field>
</ng-container>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.Default,
standalone: false,
}]
}], ctorParameters: () => [{ type: i1.NovoLabelService }] });
/**
* Most complicated of the default conditions defs, a date needs to provide a different
* input type depending on the operator selected.
*/
class NovoDefaultDateConditionDef extends AbstractConditionFieldDef {
constructor(labelService) {
super(labelService);
this.defaultOperator = Operator.within;
this.config = input(...(ngDevMode ? [undefined, { debugName: "config" }] : []));
this.defineOperatorEditGroup(Operator.before, Operator.after, Operator.equalTo);
}
closePanel(event, viewIndex) {
const overlay = this.overlayChildren.find(item => item.overlayId === viewIndex);
overlay.closePanel(event);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDefaultDateConditionDef, deps: [{ token: i1.NovoLabelService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.19", type: NovoDefaultDateConditionDef, isStandalone: false, selector: "novo-date-condition-def", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "overlayChildren", predicate: NovoPickerToggleElement, descendants: true }], usesInheritance: true, ngImport: i0, template: `
<ng-container novoConditionFieldDef="DATE">
<novo-field *novoConditionOperatorsDef="let formGroup; fieldMeta as meta" [formGroup]="formGroup">
<novo-select [placeholder]="labels.operator" formControlName="operator" (onSelect)="onOperatorSelect(formGroup)">
<novo-option value="before">{{ labels.before }}</novo-option>
<novo-option value="after">{{ labels.after }}</novo-option>
<novo-option value="equalTo">{{ labels.equals }}</novo-option>
<novo-option value="within">{{ labels.within }}</novo-option>
<novo-option value="between">{{ labels.between }}</novo-option>
<novo-option value="isNull" *ngIf="!meta?.removeIsEmpty">{{ labels.isEmpty }}</novo-option>
</novo-select>
</novo-field>
<ng-container *novoConditionInputDef="let formGroup; viewIndex as viewIndex" [ngSwitch]="formGroup.value.operator" [formGroup]="formGroup">
<novo-field *novoSwitchCases="['before', 'after', 'equalTo']">
<input novoInput dateFormat="yyyy-mm-dd" [picker]="datepicker" formControlName="value"/>
<novo-picker-toggle triggerOnFocus [overlayId]="viewIndex" novoSuffix icon="calendar">
<novo-date-picker #datepicker (onSelect)="closePanel($event, viewIndex)" [weekStart]="config()?.weekStart"></novo-date-picker>
</novo-picker-toggle>
</novo-field>
<novo-field *novoSwitchCases="['between']">
<input novoInput dateRangeFormat="date" [picker]="daterangepicker" formControlName="value"/>
<novo-picker-toggle [for]="daterangepicker" triggerOnFocus [overlayId]="viewIndex" novoSuffix icon="calendar">
<novo-date-picker #daterangepicker (onSelect)="closePanel($event, viewIndex)" [weekStart]="config()?.weekStart" mode="range"
numberOfMonths="2"></novo-date-picker>
</novo-picker-toggle>
</novo-field>
<novo-field *novoSwitchCases="['within']">
<novo-select [placeholder]="labels.selectDateRange" formControlName="value">
<novo-option value="future">{{ labels.future }}</novo-option>
<novo-option value="-1">{{ labels.past1Day }}</novo-option>
<novo-option value="-7">{{ labels.past7Days }}</novo-option>
<novo-option value="-14">{{ labels.past14Days }}</novo-option>
<novo-option value="-21">{{ labels.past21Days }}</novo-option>
<novo-option value="-30">{{ labels.past30Days }}</novo-option>
<novo-option value="-60">{{ labels.past60Days }}</novo-option>
<novo-option value="-90">{{ labels.past90Days }}</novo-option>
<novo-option value="-180">{{ labels.past180Days }}</novo-option>
<novo-option value="-270">{{ labels.past270Days }}</novo-option>
<novo-option value="-365">{{ labels.past1Year }}</novo-option>
<novo-option value="1">{{ labels.next1Day }}</novo-option>
<novo-option value="7">{{ labels.next7Days }}</novo-option>
<novo-option value="14">{{ labels.next14Days }}</novo-option>
<novo-option value="21">{{ labels.next21Days }}</novo-option>
<novo-option value="30">{{ labels.next30Days }}</novo-option>
<novo-option value="60">{{ labels.next60Days }}</novo-option>
<novo-option value="90">{{ labels.next90Days }}</novo-option>
<novo-option value="180">{{ labels.next180Days }}</novo-option>
<novo-option value="270">{{ labels.next270Days }}</novo-option>
<novo-option value="365">{{ labels.next1Year }}</novo-option>
</novo-select>
</novo-field>
<novo-field *novoSwitchCases="['isNull']">
<novo-radio-group formControlName="value">
<novo-radio [value]="true">{{ labels.yes }}</novo-radio>
<novo-radio [value]="false">{{ labels.no }}</novo-radio>
</novo-radio-group>
</novo-field>
</ng-container>
</ng-container>
`, isInline: true, dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: