UNPKG

novo-elements

Version:

1 lines 186 kB
{"version":3,"file":"novo-elements-elements-query-builder.mjs","sources":["../../../projects/novo-elements/src/elements/query-builder/query-builder.types.ts","../../../projects/novo-elements/src/elements/query-builder/query-builder.service.ts","../../../projects/novo-elements/src/elements/query-builder/query-builder.tokens.ts","../../../projects/novo-elements/src/elements/query-builder/query-builder.directives.ts","../../../projects/novo-elements/src/elements/query-builder/condition-definitions/abstract-condition.definition.ts","../../../projects/novo-elements/src/elements/query-builder/condition-definitions/address-condition.definition.ts","../../../projects/novo-elements/src/elements/query-builder/condition-definitions/boolean-condition.definition.ts","../../../projects/novo-elements/src/elements/query-builder/condition-definitions/date-condition.definition.ts","../../../projects/novo-elements/src/elements/query-builder/condition-definitions/date-time-condition.definition.ts","../../../projects/novo-elements/src/elements/query-builder/condition-definitions/string-condition.definition.ts","../../../projects/novo-elements/src/elements/query-builder/condition-definitions/number-condition.definition.ts","../../../projects/novo-elements/src/elements/query-builder/condition-definitions/id-condition.definition.ts","../../../projects/novo-elements/src/elements/query-builder/condition-definitions/picker-condition.definition.ts","../../../projects/novo-elements/src/elements/query-builder/condition-templates/condition-templates.component.ts","../../../projects/novo-elements/src/elements/query-builder/condition-templates/condition-templates.component.html","../../../projects/novo-elements/src/elements/query-builder/condition-builder/condition-builder.component.ts","../../../projects/novo-elements/src/elements/query-builder/condition-builder/condition-builder.component.html","../../../projects/novo-elements/src/elements/query-builder/condition-group/condition-group.component.ts","../../../projects/novo-elements/src/elements/query-builder/condition-group/condition-group.component.html","../../../projects/novo-elements/src/elements/query-builder/criteria-builder/criteria-builder.component.ts","../../../projects/novo-elements/src/elements/query-builder/criteria-builder/criteria-builder.component.html","../../../projects/novo-elements/src/elements/query-builder/query-builder.module.ts","../../../projects/novo-elements/src/elements/query-builder/novo-elements-elements-query-builder.ts"],"sourcesContent":["import { ViewContainerRef } from '@angular/core';\nimport { Day } from 'date-fns';\n\nexport enum Conjunction {\n AND = 'and',\n OR = 'or',\n NOT = 'not',\n}\n\nexport type ConditionType = '$and' | '$or' | '$not';\n\nexport type ConditionGroup = {\n [K in Conjunction as `$${K}`]?: Condition[];\n};\n\nexport type NestedConditionGroup = {\n [K in Conjunction as `$${K}`]?: ConditionOrConditionGroup[];\n};\n\nexport type ConditionOrConditionGroup = Condition | NestedConditionGroup;\n\nexport enum Operator {\n after = 'after',\n before = 'before',\n beginsWith = 'beginsWith',\n between = 'between',\n equalTo = 'equalTo',\n exclude = 'exclude',\n excludeAny = 'excludeAny',\n greaterThan = 'greaterThan',\n include = 'include',\n includeAll = 'includeAll',\n includeAny = 'includeAny',\n insideRadius = 'insideRadius',\n isEmpty = 'isEmpty',\n isNull = 'isNull',\n lessThan = 'lessThan',\n outsideRadius = 'outsideRadius',\n radius = 'radius',\n within = 'within',\n}\n\nexport type OperatorName = keyof typeof Operator;\n\nexport interface Condition {\n conditionType?: ConditionType;\n field: string;\n operator: OperatorName | string;\n scope?: string;\n value: any;\n supportingValue?: any;\n entity?: string;\n warnOnDelete?: () => Promise<boolean>;\n}\n\nexport interface Criteria {\n criteria: ConditionGroup[];\n}\n\nexport interface NestedCriteria {\n criteria: NestedConditionGroup[];\n}\n\nexport interface BaseFieldDef {\n name: string;\n label?: string;\n type: string;\n dataSpecialization?: string;\n optional?: boolean;\n multiValue?: boolean;\n inputType?: string;\n options?: { value: string | number; label: string; readOnly?: boolean }[];\n optionsUrl?: string;\n optionsType?: string;\n dataType?: string;\n icon?: string;\n}\n\nexport interface FieldConfig<T extends BaseFieldDef> {\n value: string;\n label: string;\n options: T[];\n search: (term: string) => T[];\n find: (name: string) => T;\n}\n\nexport interface AddressData {\n address_components: AddressComponent[];\n formatted_address: string;\n geometry: AddressGeometry;\n name?: string;\n place_id: string;\n radius?: AddressRadius;\n postal_codes?: string[];\n types?: string[];\n}\n\nexport interface AddressRadius {\n value: number;\n units: AddressRadiusUnitsName;\n operator?: string;\n}\n\nexport interface AddressComponent {\n long_name: string;\n short_name: string;\n types: string[];\n}\n\nexport interface AddressGeometry {\n location: AddressGeometryLocation;\n viewport: AddressGeometryViewport;\n}\n\nexport interface AddressGeometryLocation {\n lat: number;\n lng: number;\n}\n\nexport interface AddressGeometryViewport {\n north: number;\n south: number;\n east: number;\n west: number;\n}\n\nexport enum RadiusUnits {\n miles = 'miles',\n km = 'km',\n}\n\nexport type AddressRadiusUnitsName = keyof typeof RadiusUnits;\n\n/** All options that can be used to override the defaults for the address criteria */\nexport type AddressCriteriaConfig = {\n radiusEnabled?: boolean;\n radiusUnits?: AddressRadiusUnitsName;\n}\n\n/** All options that can be used to configure date pickers */\nexport type DateCriteriaConfig = {\n weekStart?: Day;\n}\n\n/** Interface used to provide an outlet for rows to be inserted into. */\nexport interface QueryFilterOutlet {\n viewContainer: ViewContainerRef;\n}\n","import { computed, Injectable, signal } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { NovoLabelService } from 'novo-elements/services';\nimport { BaseConditionFieldDef } from './query-builder.directives';\nimport { BaseFieldDef, Conjunction, FieldConfig } from './query-builder.types';\n\nexport const defaultEditTypeFn = (field: BaseFieldDef) => {\n return field.inputType || field.dataType || field.type;\n};\n\nexport interface QueryBuilderConfig {\n fields: FieldConfig<BaseFieldDef>[];\n staticFieldSelection?: string;\n}\n\n@Injectable()\nexport class QueryBuilderService {\n private _customFieldDefs = new Set<BaseConditionFieldDef>();\n private _fieldDefsByName = new Map<string, BaseConditionFieldDef>();\n public scopes = signal([]);\n public hasMultipleScopes = computed(() => this.scopes()?.length > 1);\n /**\n * Will dispatch when properties changes, subscribe to this if component should\n * re-render when props are updated\n */\n readonly stateChanges: Subject<void> = new Subject<void>();\n\n /**\n * Function to determine operator and input templates for a field. Value passed\n * through the criteria builder Input.\n */\n public get editTypeFn(): (field: BaseFieldDef) => string {\n return this._editTypeFn;\n }\n public set editTypeFn(value: (field: BaseFieldDef) => string) {\n this._editTypeFn = value ?? defaultEditTypeFn;\n this.stateChanges.next();\n }\n private _editTypeFn: (field: BaseFieldDef) => string = defaultEditTypeFn;\n\n /**\n * The field configuration to control which types of fields are available to select\n * within the Condition Builder.\n */\n public get config(): QueryBuilderConfig {\n return this._config;\n }\n public set config(value: QueryBuilderConfig) {\n this._config = value;\n this.stateChanges.next();\n }\n private _config: QueryBuilderConfig = {\n fields: [],\n staticFieldSelection: null,\n };\n\n /**\n * The configuration to control which types of conjuntions can be used in the query builder.\n * Value passed through the criteria builder Input\n * eg. and, or, not\n */\n public get allowedGroupings(): Conjunction[] {\n return this._allowedGroupings;\n }\n public set allowedGroupings(value: Conjunction[]) {\n this._allowedGroupings = value;\n this.stateChanges.next();\n }\n private _allowedGroupings: Conjunction[];\n\n public componentHost: any;\n\n constructor(private labels: NovoLabelService) {}\n\n /** Adds a field definition that was not included as part of the content children. */\n registerFieldDef(fieldDef: BaseConditionFieldDef) {\n this._customFieldDefs.add(fieldDef);\n this._fieldDefsByName.set(fieldDef.name, fieldDef);\n }\n\n /** Removes a field definition that was not included as part of the content children. */\n unregisterFieldDef(fieldDef: BaseConditionFieldDef) {\n this._customFieldDefs.delete(fieldDef);\n this._fieldDefsByName.delete(fieldDef.name);\n }\n\n getFieldDefsByName() {\n return this._fieldDefsByName;\n }\n\n getConjunctionLabel(conjunction: string) {\n switch (conjunction.replace('$', '').toLowerCase()) {\n case Conjunction.OR:\n return this.labels.or;\n case Conjunction.NOT:\n return this.labels.not;\n case Conjunction.AND:\n default:\n return this.labels.and;\n }\n }\n}\n","import { InjectionToken } from '@angular/core';\n\nexport const NOVO_QUERY_BUILDER = new InjectionToken<any>('NOVO_QUERY_BUILDER');\nexport const NOVO_CRITERIA_BUILDER = new InjectionToken<any>('NOVO_CRITERIA_BUILDER');\nexport const NOVO_CONDITION_BUILDER = new InjectionToken<any>('NOVO_CONDITION_BUILDER');\n","import { ContentChild, Directive, Input, TemplateRef } from '@angular/core';\nimport { QueryBuilderService } from './query-builder.service';\n\n/** Base interface for a condidation template directives. */\nexport interface ConditionDef {\n template: TemplateRef<any>;\n}\n\n/**\n * Contained within a novoConditionField definition describing what input should be\n * used to capture the compare value of the Condtion\n */\n@Directive({\n selector: '[novoConditionInputDef]',\n standalone: false,\n})\nexport class NovoConditionInputDef implements ConditionDef {\n constructor(/** @docs-private */ public template: TemplateRef<any>) {}\n}\n\n/**\n * Contained within a novoConditionField definition describing what operators should be available.\n */\n@Directive({\n selector: '[novoConditionOperatorsDef]',\n standalone: false,\n})\nexport class NovoConditionOperatorsDef implements ConditionDef {\n constructor(/** @docs-private */ public template: TemplateRef<any>) {}\n}\n\n/**\n * Field Field definition for the QueryBuilder.\n * Defines the inputType and operators to use for the query builder.\n */\n@Directive()\nexport class BaseConditionFieldDef {\n /** Unique name for this field. */\n @Input('novoFilterFieldDef')\n get name(): string {\n return this._name;\n }\n set name(name: string) {\n this._setNameInput(name);\n }\n protected _name: string;\n\n @ContentChild(NovoConditionInputDef) fieldInput: NovoConditionInputDef;\n @ContentChild(NovoConditionOperatorsDef) fieldOperators: NovoConditionOperatorsDef;\n\n /**\n * Transformed version of the column name that can be used as part of a CSS classname. Excludes\n * all non-alphanumeric characters and the special characters '-' and '_'. Any characters that\n * do not match are replaced by the '-' character.\n */\n cssClassFriendlyName: string;\n _fieldCssClassName: string[];\n\n defaultOperator: string;\n\n constructor() {}\n\n /**\n * Overridable method that sets the css classes that will be added to every cell in this\n * column.\n * In the future, columnCssClassName will change from type string[] to string and this\n * will set a single string value.\n * @docs-private\n */\n protected _updateFieldCssClassName() {\n this._fieldCssClassName = [`novo-filter-field-${this.cssClassFriendlyName}`];\n }\n\n protected _setNameInput(value: string) {\n // If the directive is set without a name (updated programmatically), then this setter will\n // trigger with an empty string and should not overwrite the programmatically set value.\n if (value) {\n this._name = value;\n this.cssClassFriendlyName = value.replace(/[^a-z0-9_-]/gi, '-');\n this._updateFieldCssClassName();\n }\n }\n}\n\n@Directive({\n selector: '[novoConditionFieldDef]',\n standalone: false,\n})\nexport class NovoConditionFieldDef extends BaseConditionFieldDef {\n constructor(private qbs: QueryBuilderService) {\n super();\n }\n register() {\n this.qbs.registerFieldDef(this);\n }\n unregister() {\n this.qbs.unregisterFieldDef(this);\n }\n}\n","import { AfterViewInit, Directive, Input, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';\nimport { FormControlName, UntypedFormGroup } from '@angular/forms';\nimport { NovoLabelService } from 'novo-elements/services';\nimport { NovoConditionFieldDef } from '../query-builder.directives';\nimport { Operator } from '../query-builder.types';\n\n@Directive()\nexport abstract class AbstractConditionFieldDef implements OnDestroy, OnInit, AfterViewInit {\n /** Column name that should be used to reference this column. */\n @Input()\n get name(): string {\n return this._name;\n }\n set name(name: string) {\n this._name = name;\n // With Ivy, inputs can be initialized before static query results are\n // available. In that case, we defer the synchronization until \"ngOnInit\" fires.\n this._syncFieldDefName();\n }\n _name: string;\n\n defaultOperator: Operator | string;\n protected _previousOperatorValue: Operator;\n\n protected operatorEditGroups: Set<Operator>[] = [];\n\n @ViewChild(NovoConditionFieldDef, { static: true }) fieldDef: NovoConditionFieldDef;\n @ViewChildren(FormControlName) formControlsByName: QueryList<FormControlName>;\n\n constructor(public labels: NovoLabelService) {}\n\n ngOnInit() {\n this._syncFieldDefName();\n this._syncFieldDefOperatorValue();\n this._previousOperatorValue = this.defaultOperator as Operator;\n // Need to add self to FilterBuilder because \"ContentChildren won't find it\"\n this.fieldDef?.register();\n }\n\n ngAfterViewInit() {\n setTimeout(() => {\n this.frameAfterViewInit();\n });\n }\n\n frameAfterViewInit() {\n const operatorField = this.formControlsByName.find(formControlDirective => formControlDirective.name === 'operator')?.control;\n if (operatorField) {\n this._previousOperatorValue = operatorField.value;\n }\n }\n\n ngOnDestroy() {\n this.fieldDef?.unregister();\n }\n\n /**\n * Define an edit group of operators. Once defined, if the user switches from one of these operators to another,\n * then the condition value will not be cleared. This makes sense if both operators use the same UI controls for editing.\n * @param operators The set of Operator values intended to share UI controls.\n */\n protected defineOperatorEditGroup(...operators: Operator[]): void {\n this.operatorEditGroups.push(new Set(operators));\n }\n\n onOperatorSelect(formGroup: UntypedFormGroup): void {\n let clearVal = true;\n if (this._previousOperatorValue && this.operatorEditGroups?.length) {\n const previousOperatorGroupIndex = this.operatorEditGroups.findIndex(grp => grp.has(this._previousOperatorValue));\n const newOperatorValue = formGroup.get('operator').getRawValue();\n const newOperatorGroupIndex = this.operatorEditGroups.findIndex(grp => grp.has(newOperatorValue));\n if (previousOperatorGroupIndex !== -1 && newOperatorGroupIndex !== -1 && previousOperatorGroupIndex === newOperatorGroupIndex) {\n clearVal = false;\n }\n }\n this._previousOperatorValue = formGroup.get('operator').value;\n if (clearVal) {\n formGroup.get('value').setValue(null);\n }\n }\n\n /** Synchronizes the column definition name with the text column name. */\n private _syncFieldDefName() {\n if (this.fieldDef) {\n this.fieldDef.name = this.name;\n }\n }\n\n private _syncFieldDefOperatorValue() {\n if (this.fieldDef) {\n this.fieldDef.defaultOperator = this.defaultOperator;\n }\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n inject,\n input,\n InputSignal,\n OnDestroy,\n QueryList,\n Signal,\n ViewChild,\n ViewChildren,\n ViewEncapsulation,\n} from '@angular/core';\nimport { AbstractControl, UntypedFormGroup } from '@angular/forms';\nimport { NovoPickerToggleElement } from 'novo-elements/elements/field';\nimport { PlacesListComponent } from 'novo-elements/elements/places';\nimport { NovoLabelService } from 'novo-elements/services';\nimport { Key } from 'novo-elements/utils';\nimport { Subscription } from 'rxjs';\nimport {\n AddressCriteriaConfig,\n AddressData,\n AddressRadius,\n AddressRadiusUnitsName,\n Operator,\n RadiusUnits,\n} from '../query-builder.types';\nimport { AbstractConditionFieldDef } from './abstract-condition.definition';\nimport { NovoSelectElement } from 'novo-elements/elements/select';\n\n/**\n * Handle selection of field values when a list of options is provided.\n */\n@Component({\n selector: 'novo-address-condition-def',\n template: `\n <ng-container novoConditionFieldDef>\n <novo-field *novoConditionOperatorsDef=\"let formGroup\" [formGroup]=\"formGroup\">\n <novo-select [placeholder]=\"labels.operator\" formControlName=\"operator\" (onSelect)=\"onOperatorSelect(formGroup)\">\n <novo-option value=\"includeAny\">{{ labels.includeAny }}</novo-option>\n <novo-option value=\"excludeAny\">{{ labels.exclude }}</novo-option>\n <novo-option value=\"insideRadius\" *ngIf=\"radiusEnabled()\">{{ labels.insideRadius }}</novo-option>\n <novo-option value=\"outsideRadius\" *ngIf=\"radiusEnabled()\">{{ labels.outsideRadius }}</novo-option>\n </novo-select>\n </novo-field>\n <ng-container *novoConditionInputDef=\"let formGroup; viewIndex as viewIndex; fieldMeta as meta\" [formGroup]=\"formGroup\">\n <novo-flex justify=\"space-between\" align=\"end\">\n <novo-field #input *ngIf=\"['radius', 'insideRadius', 'outsideRadius'].includes(formGroup.value.operator)\" class=\"address-radius\">\n <input\n novoInput\n paddingLeft=\"3px\"\n type=\"number\"\n min=\"1\"\n max=\"9999\"\n step=\"1\"\n formControlName=\"supportingValue\"\n #distanceInput\n (input)=\"onRadiusSelect(formGroup, $event)\"\n />\n <span marginLeft=\"2px\" marginRight=\"4px\" paddingTop=\"3px\">{{ unitsLabel() }}</span>\n </novo-field>\n <novo-field #novoField class=\"address-location\">\n <novo-chip-list [(ngModel)]=\"chipListModel\" [ngModelOptions]=\"{ standalone: true }\" (click)=\"openPlacesList(viewIndex)\">\n <novo-chip *ngFor=\"let item of formGroup.get('value').value\" (removed)=\"remove(item, formGroup, viewIndex)\">\n <novo-text ellipsis [tooltip]=\"item.formatted_address\" tooltipOnOverflow>{{ item.formatted_address }}</novo-text>\n <novo-icon novoChipRemove>close</novo-icon>\n </novo-chip>\n <input\n novoChipInput\n [id]=\"viewIndex\"\n [placeholder]=\"labels.location\"\n (keyup)=\"onKeyup($event, viewIndex)\"\n (keydown)=\"onKeydown($event, viewIndex)\"\n [picker]=\"placesPicker\"\n #addressInput/>\n </novo-chip-list>\n <novo-picker-toggle [overlayId]=\"viewIndex\" icon=\"location\" novoSuffix>\n <google-places-list\n [term]=\"term\"\n (select)=\"selectPlace($event, formGroup, viewIndex)\"\n formControlName=\"value\"\n #placesPicker/>\n </novo-picker-toggle>\n </novo-field>\n </novo-flex>\n </ng-container>\n </ng-container>\n `,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.Default,\n standalone: false,\n})\nexport class NovoDefaultAddressConditionDef extends AbstractConditionFieldDef implements OnDestroy {\n @ViewChildren(NovoPickerToggleElement) overlayChildren: QueryList<NovoPickerToggleElement>;\n @ViewChildren('addressInput') inputChildren: QueryList<ElementRef>;\n @ViewChild('placesPicker') placesPicker: PlacesListComponent;\n @ViewChildren(NovoSelectElement) addressSideTest: any;\n\n // Overridable defaults\n defaults: AddressCriteriaConfig = {\n radiusEnabled: false,\n radiusUnits: 'miles',\n };\n config: InputSignal<AddressCriteriaConfig> = input();\n radiusUnits: Signal<AddressRadiusUnitsName> = computed(() =>\n this.config()?.radiusUnits || this.defaults.radiusUnits,\n );\n radiusEnabled: Signal<boolean> = computed(() =>\n this.config()?.radiusEnabled || this.defaults.radiusEnabled,\n );\n unitsLabel: Signal<string> = computed(() =>\n this.radiusUnits() === RadiusUnits.miles ? this.labels.miles : this.labels.km,\n );\n\n defaultOperator = Operator.includeAny;\n chipListModel: any = '';\n term: string = '';\n\n private _addressChangesSubscription: Subscription = Subscription.EMPTY;\n\n public element = inject(ElementRef);\n\n constructor(labelService: NovoLabelService) {\n super(labelService);\n this.defineOperatorEditGroup(Operator.includeAny, Operator.excludeAny, Operator.insideRadius, Operator.outsideRadius);\n }\n\n ngOnDestroy() {\n this._addressChangesSubscription.unsubscribe();\n }\n\n onKeyup(event, viewIndex) {\n if (![Key.Escape, Key.Enter].includes(event.key)) {\n this.openPlacesList(viewIndex);\n }\n this.term = event.target.value;\n }\n\n onKeydown(event, viewIndex) {\n if (!this.placesPicker.dropdownOpen) {\n this.openPlacesList(viewIndex);\n this.placesPicker.dropdownOpen = true;\n }\n if ([Key.Escape, Key.Tab].includes(event.key)) {\n this.closePlacesList(viewIndex);\n } else {\n this.placesPicker.onKeyDown(event);\n }\n }\n\n getValue(formGroup: AbstractControl): AddressData[] {\n return formGroup.value.value || [];\n }\n\n getCurrentOverlay(viewIndex: string): NovoPickerToggleElement {\n return this.overlayChildren?.find(item => item.overlayId === viewIndex);\n }\n\n getCurrentInput(viewIndex: string): ElementRef {\n return this.inputChildren?.find(item => (item as any).nativeElement.id === viewIndex);\n }\n\n openPlacesList(viewIndex) {\n this.getCurrentOverlay(viewIndex)?.openPanel();\n }\n\n closePlacesList(viewIndex) {\n this.getCurrentOverlay(viewIndex)?.closePanel();\n }\n\n selectPlace(event: any, formGroup: AbstractControl, viewIndex: string): void {\n const valueToAdd: AddressData = {\n address_components: event.address_components,\n formatted_address: event.formatted_address,\n geometry: event.geometry,\n name: event.name,\n postal_codes: event.postal_codes,\n place_id: event.place_id,\n types: event.types,\n };\n const current: AddressData | AddressData[] = this.getValue(formGroup);\n const updated: AddressData[] = Array.isArray(current) ? [...current, valueToAdd] : [valueToAdd];\n formGroup.get('value').setValue(this.updateRadiusInValues(formGroup, updated));\n\n this.inputChildren.forEach(inputChild => {\n inputChild.nativeElement.value = '';\n })\n this.getCurrentInput(viewIndex)?.nativeElement.focus();\n this.closePlacesList(viewIndex);\n }\n\n remove(valueToRemove: AddressData, formGroup: AbstractControl, viewIndex: string): void {\n const current = this.getValue(formGroup);\n const index = current.indexOf(valueToRemove);\n if (index >= 0) {\n const oldValue = [...current]\n oldValue.splice(index, 1);\n formGroup.get('value').setValue(oldValue);\n }\n this.closePlacesList(viewIndex);\n }\n\n // Override abstract behavior - allow moving location from includeAny to radius, but when moving the opposite direction,\n // trim out radius information from the value\n onOperatorSelect(formGroup: UntypedFormGroup): void {\n const previousOperator = this._previousOperatorValue;\n super.onOperatorSelect(formGroup);\n if ([previousOperator, formGroup.get('operator').getRawValue()].indexOf(Operator.insideRadius) !== -1 &&\n formGroup.get('value').getRawValue() != null) {\n formGroup.get('value').setValue(this.updateRadiusInValues(formGroup, this.getValue(formGroup)));\n }\n }\n\n onRadiusSelect(formGroup: AbstractControl, event): void {\n const maxLengthRadius = event.target.value.slice(0, 4);\n event.target.value = maxLengthRadius;\n formGroup.get('supportingValue').setValue(maxLengthRadius);\n // We must dirty the form explicitly to show up as a user modification when it was done programmatically\n formGroup.get('value').setValue(this.updateRadiusInValues(formGroup, this.getValue(formGroup)));\n formGroup.markAsDirty();\n }\n\n private updateRadiusInValues(formGroup: AbstractControl, values: AddressData[]): AddressData[] {\n return values.map(val => ({\n ...val,\n radius: this.isRadiusOperatorSelected(formGroup) ? this.getRadiusData(formGroup) : undefined,\n }));\n }\n\n private getRadiusData(formGroup: AbstractControl): AddressRadius {\n return {\n value: formGroup.value.supportingValue,\n units: this.radiusUnits(),\n operator: formGroup.value.operator,\n };\n }\n\n private isRadiusOperatorSelected(formGroup: AbstractControl): boolean {\n return ['insideRadius', 'outsideRadius'].includes(formGroup.get('operator')?.value) && formGroup.value?.supportingValue !== null;\n }\n}\n","import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';\nimport { AbstractConditionFieldDef } from './abstract-condition.definition';\nimport { Operator } from '../query-builder.types';\nimport { NovoLabelService } from 'novo-elements/services';\n\n/**\n * When constructing a query using a field that is a boolean with only true/false as possible values.\n */\n@Component({\n selector: 'novo-boolean-condition-def',\n template: `\n <ng-container novoConditionFieldDef>\n <novo-field *novoConditionOperatorsDef=\"let formGroup; fieldMeta as meta\" [formGroup]=\"formGroup\">\n <novo-select [placeholder]=\"labels.operator\" formControlName=\"operator\" (onSelect)=\"onOperatorSelect(formGroup)\">\n <novo-option value=\"include\">{{ labels.equals }}</novo-option>\n <novo-option value=\"exclude\">{{ labels.doesNotEqual }}</novo-option>\n <novo-option value=\"isNull\" *ngIf=\"!meta?.removeIsEmpty\">{{ labels.isEmpty }}</novo-option>\n </novo-select>\n </novo-field>\n <novo-field *novoConditionInputDef=\"let formGroup; fieldMeta as meta\" [style.maxWidth.px]=\"250\" [formGroup]=\"formGroup\">\n @let isNull = formGroup.value.operator === 'isNull';\n @let useYesNo = isNull || meta.dataType === 'Boolean';\n @let customOptions = !isNull && meta.options?.length === 2;\n @if (customOptions) {\n <novo-radio-group formControlName=\"value\">\n <novo-radio *ngFor=\"let opt of meta.options; trackBy: optIdentify\" [value]=\"opt.value\">{{ opt.label }}</novo-radio>\n </novo-radio-group>\n } @else {\n <novo-radio-group formControlName=\"value\">\n <novo-radio [value]=\"true\">{{ useYesNo ? labels.yes : labels.true }}</novo-radio>\n <novo-radio [value]=\"false\">{{ useYesNo ? labels.no : labels.false }}</novo-radio>\n </novo-radio-group>\n }\n </novo-field>\n </ng-container>\n `,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.Default,\n standalone: false,\n})\nexport class NovoDefaultBooleanConditionDef extends AbstractConditionFieldDef {\n defaultOperator = Operator.include;\n\n constructor(labelService: NovoLabelService) {\n super(labelService);\n this.defineOperatorEditGroup(Operator.include, Operator.exclude, Operator.isNull);\n }\n\n optIdentify(option) {\n return option.label;\n }\n}\n","import { ChangeDetectionStrategy, Component, input, InputSignal, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core';\nimport { NovoPickerToggleElement } from 'novo-elements/elements/field';\nimport { NovoLabelService } from 'novo-elements/services';\nimport { DateCriteriaConfig, Operator } from '../query-builder.types';\nimport { AbstractConditionFieldDef } from './abstract-condition.definition';\n\n/**\n * Most complicated of the default conditions defs, a date needs to provide a different\n * input type depending on the operator selected.\n */\n@Component({\n selector: 'novo-date-condition-def',\n template: `\n <ng-container novoConditionFieldDef=\"DATE\">\n <novo-field *novoConditionOperatorsDef=\"let formGroup; fieldMeta as meta\" [formGroup]=\"formGroup\">\n <novo-select [placeholder]=\"labels.operator\" formControlName=\"operator\" (onSelect)=\"onOperatorSelect(formGroup)\">\n <novo-option value=\"before\">{{ labels.before }}</novo-option>\n <novo-option value=\"after\">{{ labels.after }}</novo-option>\n <novo-option value=\"equalTo\">{{ labels.equals }}</novo-option>\n <novo-option value=\"within\">{{ labels.within }}</novo-option>\n <novo-option value=\"between\">{{ labels.between }}</novo-option>\n <novo-option value=\"isNull\" *ngIf=\"!meta?.removeIsEmpty\">{{ labels.isEmpty }}</novo-option>\n </novo-select>\n </novo-field>\n <ng-container *novoConditionInputDef=\"let formGroup; viewIndex as viewIndex\" [ngSwitch]=\"formGroup.value.operator\" [formGroup]=\"formGroup\">\n <novo-field *novoSwitchCases=\"['before', 'after', 'equalTo']\">\n <input novoInput dateFormat=\"yyyy-mm-dd\" [picker]=\"datepicker\" formControlName=\"value\"/>\n <novo-picker-toggle triggerOnFocus [overlayId]=\"viewIndex\" novoSuffix icon=\"calendar\">\n <novo-date-picker #datepicker (onSelect)=\"closePanel($event, viewIndex)\" [weekStart]=\"config()?.weekStart\"></novo-date-picker>\n </novo-picker-toggle>\n </novo-field>\n <novo-field *novoSwitchCases=\"['between']\">\n <input novoInput dateRangeFormat=\"date\" [picker]=\"daterangepicker\" formControlName=\"value\"/>\n <novo-picker-toggle [for]=\"daterangepicker\" triggerOnFocus [overlayId]=\"viewIndex\" novoSuffix icon=\"calendar\">\n <novo-date-picker #daterangepicker (onSelect)=\"closePanel($event, viewIndex)\" [weekStart]=\"config()?.weekStart\" mode=\"range\"\n numberOfMonths=\"2\"></novo-date-picker>\n </novo-picker-toggle>\n </novo-field>\n <novo-field *novoSwitchCases=\"['within']\">\n <novo-select [placeholder]=\"labels.selectDateRange\" formControlName=\"value\">\n <novo-option value=\"future\">{{ labels.future }}</novo-option>\n <novo-option value=\"-1\">{{ labels.past1Day }}</novo-option>\n <novo-option value=\"-7\">{{ labels.past7Days }}</novo-option>\n <novo-option value=\"-14\">{{ labels.past14Days }}</novo-option>\n <novo-option value=\"-21\">{{ labels.past21Days }}</novo-option>\n <novo-option value=\"-30\">{{ labels.past30Days }}</novo-option>\n <novo-option value=\"-60\">{{ labels.past60Days }}</novo-option>\n <novo-option value=\"-90\">{{ labels.past90Days }}</novo-option>\n <novo-option value=\"-180\">{{ labels.past180Days }}</novo-option>\n <novo-option value=\"-270\">{{ labels.past270Days }}</novo-option>\n <novo-option value=\"-365\">{{ labels.past1Year }}</novo-option>\n <novo-option value=\"1\">{{ labels.next1Day }}</novo-option>\n <novo-option value=\"7\">{{ labels.next7Days }}</novo-option>\n <novo-option value=\"14\">{{ labels.next14Days }}</novo-option>\n <novo-option value=\"21\">{{ labels.next21Days }}</novo-option>\n <novo-option value=\"30\">{{ labels.next30Days }}</novo-option>\n <novo-option value=\"60\">{{ labels.next60Days }}</novo-option>\n <novo-option value=\"90\">{{ labels.next90Days }}</novo-option>\n <novo-option value=\"180\">{{ labels.next180Days }}</novo-option>\n <novo-option value=\"270\">{{ labels.next270Days }}</novo-option>\n <novo-option value=\"365\">{{ labels.next1Year }}</novo-option>\n </novo-select>\n </novo-field>\n <novo-field *novoSwitchCases=\"['isNull']\">\n <novo-radio-group formControlName=\"value\">\n <novo-radio [value]=\"true\">{{ labels.yes }}</novo-radio>\n <novo-radio [value]=\"false\">{{ labels.no }}</novo-radio>\n </novo-radio-group>\n </novo-field>\n </ng-container>\n </ng-container>\n `,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.Default,\n standalone: false,\n})\nexport class NovoDefaultDateConditionDef extends AbstractConditionFieldDef {\n @ViewChildren(NovoPickerToggleElement)\n overlayChildren: QueryList<NovoPickerToggleElement>;\n\n defaultOperator = Operator.within;\n\n config: InputSignal<DateCriteriaConfig> = input();\n\n constructor(labelService: NovoLabelService) {\n super(labelService);\n this.defineOperatorEditGroup(Operator.before, Operator.after, Operator.equalTo);\n }\n\n closePanel(event, viewIndex): void {\n const overlay = this.overlayChildren.find(item => item.overlayId === viewIndex);\n overlay.closePanel(event);\n }\n}\n","import { ChangeDetectionStrategy, Component, input, InputSignal, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core';\nimport { NovoPickerToggleElement } from 'novo-elements/elements/field';\nimport { AbstractConditionFieldDef } from './abstract-condition.definition';\nimport { DateCriteriaConfig, Operator } from '../query-builder.types';\nimport { NovoLabelService } from 'novo-elements/services';\n\n/**\n * Most complicated of the default conditions defs, a date needs to provide a different\n * input type depending on the operator selected.\n */\n@Component({\n selector: 'novo-date-time-condition-def',\n template: `\n <ng-container novoConditionFieldDef=\"DATE\">\n <novo-field *novoConditionOperatorsDef=\"let formGroup; fieldMeta as meta\" [formGroup]=\"formGroup\">\n <novo-select [placeholder]=\"labels.operator\" formControlName=\"operator\" (onSelect)=\"onOperatorSelect(formGroup)\">\n <novo-option value=\"before\">{{ labels.before }}</novo-option>\n <novo-option value=\"after\">{{ labels.after }}</novo-option>\n <novo-option value=\"equalTo\">{{ labels.equals }}</novo-option>\n <novo-option value=\"within\">{{ labels.within }}</novo-option>\n <novo-option value=\"between\">{{ labels.between }}</novo-option>\n <novo-option value=\"isNull\" *ngIf=\"!meta?.removeIsEmpty\">{{ labels.isEmpty }}</novo-option>\n </novo-select>\n </novo-field>\n <ng-container *novoConditionInputDef=\"let formGroup; viewIndex as viewIndex\" [ngSwitch]=\"formGroup.value.operator\" [formGroup]=\"formGroup\">\n <novo-field *novoSwitchCases=\"['after']\">\n <input novoInput dateTimeFormat=\"iso8601\" [picker]=\"datetimepicker\" formControlName=\"value\"/>\n <novo-picker-toggle triggerOnFocus [width]=\"-1\" [overlayId]=\"viewIndex\" novoSuffix icon=\"calendar\">\n <novo-date-time-picker defaultTime=\"end\" (onSelect)=\"closePanel($event, viewIndex)\" #datetimepicker\n [weekStart]=\"config()?.weekStart\"></novo-date-time-picker>\n </novo-picker-toggle>\n </novo-field>\n <novo-field *novoSwitchCases=\"['before']\">\n <input novoInput dateTimeFormat=\"iso8601\" [picker]=\"datetimepickerbefore\" formControlName=\"value\"/>\n <novo-picker-toggle triggerOnFocus [width]=\"-1\" [overlayId]=\"viewIndex\" novoSuffix icon=\"calendar\">\n <novo-date-time-picker defaultTime=\"start\" (onSelect)=\"closePanel($event, viewIndex)\" #datetimepickerbefore\n [weekStart]=\"config()?.weekStart\"></novo-date-time-picker>\n </novo-picker-toggle>\n </novo-field>\n <novo-field *novoSwitchCases=\"['equalTo']\">\n <input novoInput dateFormat=\"yyyy-mm-dd\" [picker]=\"datepicker\" formControlName=\"value\"/>\n <novo-picker-toggle triggerOnFocus [overlayId]=\"viewIndex\" novoSuffix icon=\"calendar\">\n <novo-date-picker (onSelect)=\"closePanel($event, viewIndex)\" #datepicker [weekStart]=\"config()?.weekStart\"></novo-date-picker>\n </novo-picker-toggle>\n </novo-field>\n <novo-field *novoSwitchCases=\"['between']\">\n <input novoInput dateRangeFormat=\"date\" [picker]=\"daterangepicker\" formControlName=\"value\"/>\n <novo-picker-toggle [for]=\"daterangepicker\" triggerOnFocus [overlayId]=\"viewIndex\" novoSuffix icon=\"calendar\">\n <novo-date-picker #daterangepicker (onSelect)=\"closePanel($event, viewIndex)\" mode=\"range\" numberOfMonths=\"2\"\n [weekStart]=\"config()?.weekStart\"></novo-date-picker>\n </novo-picker-toggle>\n </novo-field>\n <novo-field *novoSwitchCases=\"['within']\">\n <novo-select [placeholder]=\"labels.selectDateRange\" formControlName=\"value\">\n <novo-option value=\"future\">{{ labels.future }}</novo-option>\n <novo-option value=\"-1\">{{ labels.past1Day }}</novo-option>\n <novo-option value=\"-7\">{{ labels.past7Days }}</novo-option>\n <novo-option value=\"-14\">{{ labels.past14Days }}</novo-option>\n <novo-option value=\"-21\">{{ labels.past21Days }}</novo-option>\n <novo-option value=\"-30\">{{ labels.past30Days }}</novo-option>\n <novo-option value=\"-60\">{{ labels.past60Days }}</novo-option>\n <novo-option value=\"-90\">{{ labels.past90Days }}</novo-option>\n <novo-option value=\"-180\">{{ labels.past180Days }}</novo-option>\n <novo-option value=\"-270\">{{ labels.past270Days }}</novo-option>\n <novo-option value=\"-365\">{{ labels.past1Year }}</novo-option>\n <novo-option value=\"1\">{{ labels.next1Day }}</novo-option>\n <novo-option value=\"7\">{{ labels.next7Days }}</novo-option>\n <novo-option value=\"14\">{{ labels.next14Days }}</novo-option>\n <novo-option value=\"21\">{{ labels.next21Days }}</novo-option>\n <novo-option value=\"30\">{{ labels.next30Days }}</novo-option>\n <novo-option value=\"60\">{{ labels.next60Days }}</novo-option>\n <novo-option value=\"90\">{{ labels.next90Days }}</novo-option>\n <novo-option value=\"180\">{{ labels.next180Days }}</novo-option>\n <novo-option value=\"270\">{{ labels.next270Days }}</novo-option>\n <novo-option value=\"365\">{{ labels.next1Year }}</novo-option>\n </novo-select>\n </novo-field>\n <novo-field *novoSwitchCases=\"['isNull']\">\n <novo-radio-group formControlName=\"value\">\n <novo-radio [value]=\"true\">{{ labels.yes }}</novo-radio>\n <novo-radio [value]=\"false\">{{ labels.no }}</novo-radio>\n </novo-radio-group>\n </novo-field>\n </ng-container>\n </ng-container>\n `,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.Default,\n standalone: false,\n})\nexport class NovoDefaultDateTimeConditionDef extends AbstractConditionFieldDef {\n @ViewChildren(NovoPickerToggleElement)\n overlayChildren: QueryList<NovoPickerToggleElement>;\n\n defaultOperator = Operator.within;\n\n config: InputSignal<DateCriteriaConfig> = input();\n\n constructor(labelService: NovoLabelService) {\n super(labelService);\n this.defineOperatorEditGroup(Operator.before, Operator.after);\n }\n\n closePanel(event, viewIndex): void {\n const overlay = this.overlayChildren.find(item => item.overlayId === viewIndex);\n overlay.closePanel(event);\n }\n}\n","import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';\nimport { AbstractControl } from '@angular/forms';\nimport { NovoLabelService } from 'novo-elements/services';\nimport { Operator } from '../query-builder.types';\nimport { AbstractConditionFieldDef } from './abstract-condition.definition';\n\n/**\n * Constructing filters against String fields can be complex. Each \"chip\" added to the\n * condition can be independently used to query a database. Not all systems support\n * querying within a text column, ie sql unless LIKE is enabled. This could result in a\n * performance penalty.\n */\n@Component({\n selector: 'novo-string-condition-def',\n template: `\n <!-- fieldTypes should be UPPERCASE -->\n <ng-container novoConditionFieldDef=\"STRING\">\n <novo-field *novoConditionOperatorsDef=\"let formGroup; fieldMeta as meta\" [formGroup]=\"formGroup\">\n <novo-select [placeholder]=\"labels.operator\" formControlName=\"operator\" (onSelect)=\"onOperatorSelect(formGroup)\">\n <novo-option value=\"includeAny\">{{ labels.includeAny }}</novo-option>\n <novo-option value=\"includeAll\" *ngIf=\"!meta?.removeIncludeAll\">{{ labels.includeAll }}</novo-option>\n <novo-option value=\"excludeAny\">{{ labels.exclude }}</novo-option>\n <novo-option value=\"beginsWith\" *ngIf=\"meta?.hasBeginsWith\">{{ labels.beginsWith }}</novo-option>\n <novo-option value=\"isEmpty\" *ngIf=\"!meta?.removeIsEmpty\">{{ labels.isEmpty }}</novo-option>\n </novo-select>\n </novo-field>\n <ng-container *novoConditionInputDef=\"let formGroup\" [ngSwitch]=\"formGroup.value.operator\" [formGroup]=\"formGroup\">\n <novo-field *novoSwitchCases=\"['includeAny', 'includeAll', 'excludeAny', 'beginsWith']\">\n <novo-chip-list #chipList aria-label=\"filter value\" multiple=\"true\" [chipsToggleable]=\"false\" formControlName=\"value\">\n <novo-chip *ngFor=\"let chip of formGroup.value?.value || []\" [value]=\"chip\" (removed)=\"remove(chip, formGroup)\">\n <novo-text ellipsis [tooltip]=\"chip\" tooltipOnOverflow>{{ chip }}</novo-text>\n <novo-icon novoChipRemove>close</novo-icon>\n </novo-chip>\n <input\n novoChipInput\n [placeholder]=\"labels.typeToAddChips\"\n autocomplete=\"off\"\n (novoChipInputTokenEnd)=\"add($event, formGroup)\"\n />\n </novo-chip-list>\n <novo-autocomplete></novo-autocomplete>\n </novo-field>\n <novo-field *novoSwitchCases=\"['isEmpty']\">\n <novo-radio-group formControlName=\"value\">\n <novo-radio [value]=\"true\">{{ labels.yes }}</novo-radio>\n <novo-radio [value]=\"false\">{{ labels.no }}</novo-radio>\n </novo-radio-group>\n </novo-field>\n </ng-container>\n </ng-container>\n `,\n encapsulation: ViewEncapsulation.None,\n // Change detection is intentionally not set to OnPush. This component's template will be provided\n // to the table to be inserted into its view. This is problematic when change detection runs since\n // the bindings in this template will be evaluated _after_ the table's view is evaluated, which\n // means the template in the table's view will not have the updated value (and in fact will cause\n // an ExpressionChangedAfterItHasBeenCheckedError).\n changeDetection: ChangeDetectionStrategy.Default,\n standalone: false,\n})\nexport class NovoDefaultStringConditionDef extends AbstractConditionFieldDef {\n defaultOperator = Operator.includeAny;\n\n constructor(labelService: NovoLabelService) {\n super(labelService);\n this.defineOperatorEditGroup(Operator.includeAny, Operator.includeAll, Operator.excludeAny, Operator.beginsWith);\n }\n\n getValue(formGroup: AbstractControl): any[] {\n return formGroup.value?.value || [];\n }\n\n add(event: any, formGroup: AbstractControl): void {\n const input = event.input;\n input.value = '';\n const valueToAdd = event.value;\n if (valueToAdd !== '') {\n const current: any[] = this.getValue(formGroup);\n const newValue: any[] = Array.isArray(current) ? [...current, valueToAdd] : [valueToAdd];\n this.setFormValue(formGroup, newValue);\n }\n }\n\n remove(valueToRemove: string, formGroup: AbstractControl): void {\n const current = this.getValue(formGroup);\n const index = current.indexOf(valueToRemove);\n if (index >= 0) {\n const value = [...current]\n value.splice(index, 1);\n this.setFormValue(formGroup, value);\n }\n }\n\n private setFormValue(formGroup: AbstractControl, newValue: any[]) {\n formGroup.get('value').setValue(newValue);\n formGroup.markAsDirty();\n }\n}\n","import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';\nimport { AbstractConditionFieldDef } from './abstract-condition.definition';\nimport { Operator } from '../query-builder.types';\nimport { NovoLabelService } from 'novo-elements/services';\n\n/**\n * When constructing a query using a field that is an Int, Double, Number ...etc.\n * TODO: Do we implement currency formation here potentially?\n */\n@Component({\n selector: 'novo-number-condition-def',\n template: `\n <ng-container novoConditionFieldDef>\n <novo-field *novoConditionOperatorsDef=\"let formGroup\" [formGroup]=\"formGroup\">\n <novo-select [placeholder]=\"labels.operator\" formControlName=\"operator\" (onSelect)=\"onOperatorSelect(formGroup)\">\n <novo-option value=\"greaterThan\">{{ labels.greaterThan }}</novo-option>\n <novo-option value=\"lessThan\">{{ labels.lessThan }}</novo-option>\n <novo-option value=\"equalTo\">{{ labels.equalTo }}</novo-option>\n <novo-option value=\"between\">{{ labels.between }}</novo-option>\n </novo-select>\n </novo-field>\n <ng-container *novoConditionInputDef=\"let formGroup\" [ngSwitch]=\"formGroup.value.operator\" [formGroup]=\"formGroup\">\n <novo-field *novoSwitchCases=\"['greaterThan', 'lessThan', 'equalTo']\">\n <input novoInput type=\"number\" formControlName=\"value\"/>\n </novo-field>\n <novo-field *novoSwitchCases=\"['isNull']\">\n <novo-radio-group formControlName=\"value\">\n <novo-radio [value]=\"true\">{{ labels.yes }}</novo-radio>\n <novo-radio [value]=\"false\">{{ labels.no }}</novo-radio>\n </novo-radio-group>\n </novo-field>\n <ng-container *novoSwitchCases=\"['between']\">\n <novo-number-range formControlName=\"value\"/>\n </ng-container>\n </ng-container>\n </ng-container>\n `,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.Default,\n standalone: false,\n})\nexport class NovoDefaultNumberConditionDef extends AbstractConditionFieldDef {\n defaultOperator = Operator.equalTo;\n\n constructor(labelService: NovoLabelService) {\n super(labelService);\n this.defineOperatorEditGroup(Operator.greaterThan, Operator.lessThan, Operator.equalTo);\n }\n}\n","import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';\nimport { AbstractConditionFieldDef } from './abstract-condition.definition';\nimport { Operator } from '../query-builder.types';\n\n/**\n * Any condition that has a type of ID usually only is queried by ID.\n */\n@Component({\n selector: 'novo-id-condition-def',\n template: `\n <ng-container novoConditionFieldDef>\n <novo-field *novoConditionOperatorsDef=\"let formGroup\" [formGroup]=\"formGroup\">\n <novo-select [placeholder]=\"labels.operator\" formControlName=\"operator\">\n <novo-option value=\"equalTo\">{{ labels.equalTo }}</novo-option>\n <ng-content></ng-content>\n </novo-select>\n </novo-field>\n <novo-field *novoConditionInputDef=\"let formGroup\" [formGroup]=\"formGroup\">\n <input novoInput type=\"number\" min=\"1\" step=\"1\" formControlName=\"value\" />\n </novo-field>\n </ng-container>\n `,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.Default,\n standalone: false,\n})\nexport class NovoDefaultIdConditionDef extends AbstractConditionFieldDef {\n defaultOperator = Operator.equalTo;\n}\n","import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';\nimport { UntypedFormGroup } from '@angular/forms';\nimport { NovoLabelService } from 'novo-elements/services';\nimport { BaseFieldDef, Operator } from '../query-builder.types';\nimport { AbstractConditionFieldDef } from './abstract-condition.definition';\nimport { NovoSelectElement } from 'novo-elements/elements/select';\n\ntype FieldOption = BaseFieldDef['options'][number];\n/**\n * Handle selection of field values when a list of options is provided.\n */\n@Component({\n selector: 'novo-picker-condition-def',\n template: `\n <ng-container novoConditionFieldDef>\n <novo-field *novoConditionOperatorsDef=\"let formGroup; fieldMeta as meta\" [formGroup]=\"formGroup\">\n <novo-select [placeho