• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 16053471080

03 Jul 2025 02:41PM UTC coverage: 4.981% (-86.4%) from 91.409%
16053471080

Pull #16021

github

web-flow
Merge 7c49966eb into 7e40671a1
Pull Request #16021: fix(radio-group): dynamically added radio buttons do not initialize

178 of 15753 branches covered (1.13%)

13 of 14 new or added lines in 2 files covered. (92.86%)

25644 existing lines in 324 files now uncovered.

1478 of 29670 relevant lines covered (4.98%)

0.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.11
/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts
1
import {
2
    AfterViewInit,
3
    EventEmitter,
4
    LOCALE_ID,
5
    Output,
6
    TemplateRef
7
} from '@angular/core';
8
import { getLocaleFirstDayOfWeek, NgTemplateOutlet, NgClass, DatePipe } from '@angular/common';
9
import { Inject } from '@angular/core';
10
import {
11
    Component, Input, ViewChild, ChangeDetectorRef, ViewChildren, QueryList, ElementRef, OnDestroy, HostBinding
12
} from '@angular/core';
13
import { FormsModule } from '@angular/forms';
14
import { Subject } from 'rxjs';
15
import { IgxChipComponent } from '../chips/chip.component';
16
import { IQueryBuilderResourceStrings, QueryBuilderResourceStringsEN } from '../core/i18n/query-builder-resources';
17
import { PlatformUtil, trackByIdentity } from '../core/utils';
18
import { DataType, DataUtil } from '../data-operations/data-util';
19
import { IgxBooleanFilteringOperand, IgxDateFilteringOperand, IgxDateTimeFilteringOperand, IgxNumberFilteringOperand, IgxStringFilteringOperand, IgxTimeFilteringOperand } from '../data-operations/filtering-condition';
20
import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface';
21
import { FilteringExpressionsTree, IExpressionTree, IFilteringExpressionsTree } from '../data-operations/filtering-expressions-tree';
22
import { IgxDatePickerComponent } from '../date-picker/date-picker.component';
23

24
import { IgxButtonDirective } from '../directives/button/button.directive';
25
import { IgxDateTimeEditorDirective } from '../directives/date-time-editor/date-time-editor.directive';
26

27
import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive';
28
import { FieldType, EntityType } from '../grids/common/grid.interface';
29
import { IgxSelectComponent } from '../select/select.component';
30
import { HorizontalAlignment, OverlaySettings, VerticalAlignment } from '../services/overlay/utilities';
31
import { AbsoluteScrollStrategy, AutoPositionStrategy, CloseScrollStrategy, ConnectedPositioningStrategy } from '../services/public_api';
32
import { IgxTimePickerComponent } from '../time-picker/time-picker.component';
33
import { IgxPickerToggleComponent, IgxPickerClearComponent } from '../date-common/picker-icons.common';
34
import { IgxInputDirective } from '../directives/input/input.directive';
35
import { IgxInputGroupComponent } from '../input-group/input-group.component';
36
import { IgxSelectItemComponent } from '../select/select-item.component';
37
import { IgxPrefixDirective } from '../directives/prefix/prefix.directive';
38
import { IgxIconComponent } from '../icon/icon.component';
39
import { getCurrentResourceStrings } from '../core/i18n/resources';
40
import { IgxIconButtonDirective } from '../directives/button/icon-button.directive';
41
import { IComboSelectionChangingEventArgs, IgxComboComponent } from "../combo/combo.component";
42
import { IgxComboHeaderDirective } from '../combo/public_api';
43
import { IgxCheckboxComponent } from "../checkbox/checkbox.component";
44
import { IChangeCheckboxEventArgs } from '../checkbox/checkbox-base.directive';
45
import { IgxDialogComponent } from "../dialog/dialog.component";
46
import { ISelectionEventArgs } from '../drop-down/drop-down.common';
47
import { IgxTooltipDirective } from '../directives/tooltip/tooltip.directive';
48
import { IgxTooltipTargetDirective } from '../directives/tooltip/tooltip-target.directive';
49
import { IgxQueryBuilderSearchValueTemplateDirective } from './query-builder.directives';
50
import { IgxQueryBuilderComponent } from './query-builder.component';
51
import { IgxDragIgnoreDirective, IgxDropDirective } from '../directives/drag-drop/drag-drop.directive';
52
import { IgxDropDownComponent } from '../drop-down/drop-down.component';
53
import { IgxDropDownItemComponent } from '../drop-down/drop-down-item.component';
54
import { IgxDropDownItemNavigationDirective } from '../drop-down/drop-down-navigation.directive';
55
import { IgxQueryBuilderDragService } from './query-builder-drag.service';
56
import { isTree } from '../data-operations/expressions-tree-util';
57
import { ExpressionGroupItem, ExpressionItem, ExpressionOperandItem, IgxFieldFormatterPipe } from './query-builder.common';
58

59
const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate';
3✔
60
const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime';
3✔
61
const DEFAULT_PIPE_DATE_TIME_FORMAT = 'medium';
3✔
62
const DEFAULT_PIPE_DIGITS_INFO = '1.0-3';
3✔
63
const DEFAULT_CHIP_FOCUS_DELAY = 50;
3✔
64

65
/** @hidden */
66
@Component({
67
    selector: 'igx-query-builder-tree',
68
    templateUrl: './query-builder-tree.component.html',
69
    host: { 'class': 'igx-query-builder-tree' },
70
    imports: [
71
        DatePipe,
72
        FormsModule,
73
        IgxButtonDirective,
74
        IgxCheckboxComponent,
75
        IgxChipComponent,
76
        IgxComboComponent,
77
        IgxComboHeaderDirective,
78
        IgxDatePickerComponent,
79
        IgxDateTimeEditorDirective,
80
        IgxDialogComponent,
81
        IgxDragIgnoreDirective,
82
        IgxDropDirective,
83
        IgxDropDownComponent,
84
        IgxDropDownItemComponent,
85
        IgxDropDownItemNavigationDirective,
86
        IgxFieldFormatterPipe,
87
        IgxIconButtonDirective,
88
        IgxIconComponent,
89
        IgxInputDirective,
90
        IgxInputGroupComponent,
91
        IgxOverlayOutletDirective,
92
        IgxPickerClearComponent,
93
        IgxPickerToggleComponent,
94
        IgxPrefixDirective,
95
        IgxSelectComponent,
96
        IgxSelectItemComponent,
97
        IgxTimePickerComponent,
98
        IgxTooltipDirective,
99
        IgxTooltipTargetDirective,
100
        NgClass,
101
        NgTemplateOutlet
102
    ],
103
    providers: [
104
        IgxQueryBuilderDragService
105
    ],
106
})
107
export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy {
3✔
108
    /**
109
     * @hidden @internal
110
     */
111
    public _expressionTree: IExpressionTree;
112

113
    /**
114
     * @hidden @internal
115
     */
116
    public _expressionTreeCopy: IExpressionTree;
117

118
    /**
119
     * @hidden @internal
120
     */
121
    @HostBinding('class') public get getClass() {
UNCOV
122
        return `igx-query-builder-tree--level-${this.level}`;
×
123
    }
124

125
    /**
126
     * Sets/gets the entities.
127
     */
128
    @Input()
129
    public entities: EntityType[];
130

131
    /**
132
     * Sets/gets the parent query builder component.
133
     */
134
    @Input()
135
    public queryBuilder: IgxQueryBuilderComponent;
136

137
    /**
138
     * Sets/gets the search value template.
139
     */
140
    @Input()
UNCOV
141
    public searchValueTemplate: TemplateRef<IgxQueryBuilderSearchValueTemplateDirective> = null;
×
142

143
    /**
144
    * Returns the parent expression operand.
145
    */
146
    @Input()
147
    public get parentExpression(): ExpressionOperandItem {
UNCOV
148
        return this._parentExpression;
×
149
    }
150

151
    /**
152
     * Sets the parent expression operand.
153
     */
154
    public set parentExpression(value: ExpressionOperandItem) {
UNCOV
155
        this._parentExpression = value;
×
156
    }
157

158
    /**
159
    * Returns the fields.
160
    */
161
    public get fields(): FieldType[] {
UNCOV
162
        if (!this._fields && this.isAdvancedFiltering()) {
×
UNCOV
163
            this._fields = this.entities[0].fields;
×
164
        }
165

UNCOV
166
        return this._fields;
×
167
    }
168

169
    /**
170
     * Sets the fields.
171
     */
172
    @Input()
173
    public set fields(fields: FieldType[]) {
UNCOV
174
        this._fields = fields;
×
175
        
UNCOV
176
        this._fields = this._fields?.map(f => ({...f, filters: this.getFilters(f), pipeArgs: this.getPipeArgs(f) }));
×
177
        
UNCOV
178
        if (!this._fields && this.isAdvancedFiltering()) {
×
UNCOV
179
            this._fields = this.entities[0].fields;
×
180
        }
181
    }
182

183
    /**
184
    * Returns the expression tree.
185
    */
186
    public get expressionTree(): IExpressionTree {
UNCOV
187
        return this._expressionTree;
×
188
    }
189

190
    /**
191
     * Sets the expression tree.
192
     */
193
    @Input()
194
    public set expressionTree(expressionTree: IExpressionTree) {
UNCOV
195
        this._expressionTree = expressionTree;
×
UNCOV
196
        if (!expressionTree) {
×
UNCOV
197
            this._selectedEntity = this.isAdvancedFiltering() && this.entities.length === 1 ? this.entities[0] : null;
×
UNCOV
198
            this._selectedReturnFields = this._selectedEntity ? this._selectedEntity.fields?.map(f => f.field) : [];
×
199
        }
200

UNCOV
201
        if (!this._preventInit) {
×
UNCOV
202
            this.init();
×
203
        }
204
    }
205

206
    /**
207
     * Gets the `locale` of the query builder.
208
     * If not set, defaults to application's locale.
209
     */
210
    @Input()
211
    public get locale(): string {
UNCOV
212
        return this._locale;
×
213
    }
214

215
    /**
216
     * Sets the `locale` of the query builder.
217
     * Expects a valid BCP 47 language tag.
218
     */
219
    public set locale(value: string) {
UNCOV
220
        this._locale = value;
×
221
        // if value is invalid, set it back to _localeId
UNCOV
222
        try {
×
UNCOV
223
            getLocaleFirstDayOfWeek(this._locale);
×
224
        } catch {
UNCOV
225
            this._locale = this._localeId;
×
226
        }
227
    }
228

229
    /**
230
     * Sets the resource strings.
231
     * By default it uses EN resources.
232
     */
233
    @Input()
234
    public set resourceStrings(value: IQueryBuilderResourceStrings) {
UNCOV
235
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
×
236
    }
237

238
    /**
239
     * Returns the resource strings.
240
     */
241
    public get resourceStrings(): IQueryBuilderResourceStrings {
UNCOV
242
        return this._resourceStrings;
×
243
    }
244

245
    /**
246
     * Gets/sets the expected return field.
247
     */
UNCOV
248
    @Input() public expectedReturnField: string = null;
×
249

250
    /**
251
     * Event fired as the expression tree is changed.
252
     */
253
    @Output()
UNCOV
254
    public expressionTreeChange = new EventEmitter<IExpressionTree>();
×
255

256
    /**
257
     * Event fired if a nested query builder tree is being edited.
258
     */
259
    @Output()
UNCOV
260
    public inEditModeChange = new EventEmitter<ExpressionOperandItem>();
×
261

262
    @ViewChild('entitySelect', { read: IgxSelectComponent })
263
    protected entitySelect: IgxSelectComponent;
264

265
    @ViewChild('editingInputs', { read: ElementRef })
266
    private editingInputs: ElementRef;
267

268
    @ViewChild('returnFieldsCombo', { read: IgxComboComponent })
269
    private returnFieldsCombo: IgxComboComponent;
270

271
    @ViewChild('returnFieldSelect', { read: IgxSelectComponent })
272
    protected returnFieldSelect: IgxSelectComponent;
273

274
    @ViewChild('fieldSelect', { read: IgxSelectComponent })
275
    private fieldSelect: IgxSelectComponent;
276

277
    @ViewChild('conditionSelect', { read: IgxSelectComponent })
278
    private conditionSelect: IgxSelectComponent;
279

280
    @ViewChild('searchValueInput', { read: ElementRef })
281
    private searchValueInput: ElementRef;
282

283
    @ViewChild('picker')
284
    private picker: IgxDatePickerComponent | IgxTimePickerComponent;
285

286
    @ViewChild('addRootAndGroupButton', { read: ElementRef })
287
    private addRootAndGroupButton: ElementRef;
288

289
    @ViewChild('addConditionButton', { read: ElementRef })
290
    private addConditionButton: ElementRef;
291

292
    @ViewChild('entityChangeDialog', { read: IgxDialogComponent })
293
    private entityChangeDialog: IgxDialogComponent;
294

295
    @ViewChild('addOptionsDropDown', { read: IgxDropDownComponent })
296
    private addExpressionItemDropDown: IgxDropDownComponent;
297

298
    @ViewChild('groupContextMenuDropDown', { read: IgxDropDownComponent })
299
    private groupContextMenuDropDown: IgxDropDownComponent;
300

301
    /**
302
     * @hidden @internal
303
     */
304
    @ViewChildren(IgxChipComponent, { read: IgxChipComponent })
305
    public expressionsChips: QueryList<IgxChipComponent>;
306

307
    @ViewChild('editingInputsContainer', { read: ElementRef })
308
    protected set editingInputsContainer(value: ElementRef) {
UNCOV
309
        if ((value && !this._editingInputsContainer) ||
×
310
            (value && this._editingInputsContainer && this._editingInputsContainer.nativeElement !== value.nativeElement)) {
UNCOV
311
            requestAnimationFrame(() => {
×
UNCOV
312
                this.scrollElementIntoView(value.nativeElement);
×
313
            });
314
        }
315

UNCOV
316
        this._editingInputsContainer = value;
×
317
    }
318

319
    /** @hidden */
320
    protected get editingInputsContainer(): ElementRef {
321
        return this._editingInputsContainer;
×
322
    }
323

324
    @ViewChild('currentGroupButtonsContainer', { read: ElementRef })
325
    protected set currentGroupButtonsContainer(value: ElementRef) {
UNCOV
326
        if ((value && !this._currentGroupButtonsContainer) ||
×
327
            (value && this._currentGroupButtonsContainer && this._currentGroupButtonsContainer.nativeElement !== value.nativeElement)) {
UNCOV
328
            requestAnimationFrame(() => {
×
UNCOV
329
                this.scrollElementIntoView(value.nativeElement);
×
330
            });
331
        }
332

UNCOV
333
        this._currentGroupButtonsContainer = value;
×
334
    }
335

336
    /** @hidden */
337
    protected get currentGroupButtonsContainer(): ElementRef {
338
        return this._currentGroupButtonsContainer;
×
339
    }
340

341
    @ViewChild('expressionsContainer')
342
    private expressionsContainer: ElementRef;
343

344
    @ViewChild('overlayOutlet', { read: IgxOverlayOutletDirective, static: true })
345
    private overlayOutlet: IgxOverlayOutletDirective;
346

347
    @ViewChildren(IgxQueryBuilderTreeComponent)
348
    private innerQueries: QueryList<IgxQueryBuilderTreeComponent>;
349

350
    /**
351
     * @hidden @internal
352
     */
353
    public innerQueryNewExpressionTree: IExpressionTree;
354

355
    /**
356
     * @hidden @internal
357
     */
358
    public rootGroup: ExpressionGroupItem;
359

360
    /**
361
     * @hidden @internal
362
     */
UNCOV
363
    public selectedExpressions: ExpressionOperandItem[] = [];
×
364

365
    /**
366
     * @hidden @internal
367
     */
368
    public currentGroup: ExpressionGroupItem;
369

370
    /**
371
     * @hidden @internal
372
     */
373
    public contextualGroup: ExpressionGroupItem;
374

375
    /**
376
     * @hidden @internal
377
     */
378
    public filteringLogics;
379

380
    /**
381
     * @hidden @internal
382
     */
383
    public selectedCondition: string;
384

385
    /**
386
     * @hidden @internal
387
     */
UNCOV
388
    public searchValue: { value: any } = { value: null };
×
389

390
    /**
391
     * @hidden @internal
392
     */
393
    public pickerOutlet: IgxOverlayOutletDirective | ElementRef;
394

395
    /**
396
     * @hidden @internal
397
     */
398
    public prevFocusedExpression: ExpressionOperandItem;
399

400
    /**
401
     * @hidden @internal
402
     */
UNCOV
403
    public initialOperator = 0;
×
404

405
    /**
406
     * @hidden @internal
407
     */
UNCOV
408
    public returnFieldSelectOverlaySettings: OverlaySettings = {
×
409
        scrollStrategy: new AbsoluteScrollStrategy(),
410
        modal: false,
411
        closeOnOutsideClick: true
412
    };
413

414
    /**
415
     * @hidden @internal
416
     */
UNCOV
417
    public entitySelectOverlaySettings: OverlaySettings = {
×
418
        scrollStrategy: new AbsoluteScrollStrategy(),
419
        modal: false,
420
        closeOnOutsideClick: true
421
    };
422

423
    /**
424
     * @hidden @internal
425
     */
UNCOV
426
    public fieldSelectOverlaySettings: OverlaySettings = {
×
427
        scrollStrategy: new AbsoluteScrollStrategy(),
428
        modal: false,
429
        closeOnOutsideClick: true
430
    };
431

432
    /**
433
     * @hidden @internal
434
     */
UNCOV
435
    public conditionSelectOverlaySettings: OverlaySettings = {
×
436
        scrollStrategy: new AbsoluteScrollStrategy(),
437
        modal: false,
438
        closeOnOutsideClick: true
439
    };
440

441
    /**
442
     * @hidden @internal
443
     */
UNCOV
444
    public addExpressionDropDownOverlaySettings: OverlaySettings = {
×
445
        scrollStrategy: new AbsoluteScrollStrategy(),
446
        modal: false,
447
        closeOnOutsideClick: true
448
    };
449

450
    /**
451
     * @hidden @internal
452
     */
UNCOV
453
    public groupContextMenuDropDownOverlaySettings: OverlaySettings = {
×
454
        scrollStrategy: new AbsoluteScrollStrategy(),
455
        modal: false,
456
        closeOnOutsideClick: true
457
    };
458

UNCOV
459
    private destroy$ = new Subject<any>();
×
460
    private _timeoutId: any;
461
    private _lastFocusedChipIndex: number;
UNCOV
462
    private _focusDelay = DEFAULT_CHIP_FOCUS_DELAY;
×
463
    private _parentExpression: ExpressionOperandItem;
464
    private _selectedEntity: EntityType;
465
    private _selectedReturnFields: string | string[];
466
    private _selectedField: FieldType;
467
    private _editingInputsContainer: ElementRef;
468
    private _currentGroupButtonsContainer: ElementRef;
469
    private _addModeExpression: ExpressionOperandItem;
470
    private _editedExpression: ExpressionOperandItem;
UNCOV
471
    private _preventInit = false;
×
472
    private _prevFocusedContainer: ElementRef;
UNCOV
473
    private _expandedExpressions: IFilteringExpression[] = [];
×
474
    private _fields: FieldType[];
475
    private _locale;
476
    private _entityNewValue: EntityType;
UNCOV
477
    private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN);
×
478

479
    /**
480
     * Returns if the select entity dropdown at the root level is disabled after the initial selection.
481
     */
482
    public get disableEntityChange(): boolean {
483

UNCOV
484
        return !this.parentExpression && this.selectedEntity ? this.queryBuilder.disableEntityChange : false;
×
485
    }
486

487
    /**
488
     * Returns if the fields combo at the root level is disabled.
489
     */
490
    public get disableReturnFieldsChange(): boolean {
491

UNCOV
492
        return !this.selectedEntity || this.queryBuilder.disableReturnFieldsChange;
×
493
    }
494

495
    /**
496
     * Returns the current level.
497
     */
498
    public get level(): number {
UNCOV
499
        let parent = this.elRef.nativeElement.parentElement;
×
UNCOV
500
        let _level = 0;
×
UNCOV
501
        while (parent) {
×
UNCOV
502
            if (parent.localName === 'igx-query-builder-tree') {
×
UNCOV
503
                _level++;
×
504
            }
UNCOV
505
            parent = parent.parentElement;
×
506
        }
UNCOV
507
        return _level;
×
508
    }
509

UNCOV
510
    private _positionSettings = {
×
511
        horizontalStartPoint: HorizontalAlignment.Right,
512
        verticalStartPoint: VerticalAlignment.Top
513
    };
514

UNCOV
515
    private _overlaySettings: OverlaySettings = {
×
516
        closeOnOutsideClick: false,
517
        modal: false,
518
        positionStrategy: new ConnectedPositioningStrategy(this._positionSettings),
519
        scrollStrategy: new CloseScrollStrategy()
520
    };
521

522
    /** @hidden */
523
    protected isAdvancedFiltering(): boolean {
UNCOV
524
        return (this.entities?.length === 1 && !this.entities[0]?.name) ||
×
UNCOV
525
            this.entities?.find(e => e.childEntities?.length > 0) !== undefined ||
×
526
            this.entities !== this.queryBuilder?.entities;
527
    }
528

529
    /** @hidden */
530
    protected isHierarchicalNestedQuery(): boolean {
UNCOV
531
        return this.queryBuilder.entities !== this.entities
×
532
    }
533

534
    /** @hidden */
535
    protected isSearchValueInputDisabled(): boolean {
UNCOV
536
        return !this.selectedField ||
×
537
            !this.selectedCondition ||
538
            (this.selectedField &&
539
                (this.selectedField.filters.condition(this.selectedCondition).isUnary ||
540
                    this.selectedField.filters.condition(this.selectedCondition).isNestedQuery));
541
    }
542

UNCOV
543
    constructor(public cdr: ChangeDetectorRef,
×
UNCOV
544
        public dragService: IgxQueryBuilderDragService,
×
UNCOV
545
        protected platform: PlatformUtil,
×
UNCOV
546
        private elRef: ElementRef,
×
UNCOV
547
        @Inject(LOCALE_ID) protected _localeId: string) {
×
UNCOV
548
        this.locale = this.locale || this._localeId;
×
UNCOV
549
        this.dragService.register(this, elRef);
×
550
    }
551

552
    /**
553
     * @hidden @internal
554
     */
555
    public ngAfterViewInit(): void {
UNCOV
556
        this._overlaySettings.outlet = this.overlayOutlet;
×
UNCOV
557
        this.entitySelectOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
558
        this.fieldSelectOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
559
        this.conditionSelectOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
560
        this.returnFieldSelectOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
561
        this.addExpressionDropDownOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
562
        this.groupContextMenuDropDownOverlaySettings.outlet = this.overlayOutlet;
×
563
        
UNCOV
564
        if (this.isAdvancedFiltering() && this.entities?.length === 1) {
×
UNCOV
565
            this.selectedEntity = this.entities[0].name;
×
UNCOV
566
            if (this._selectedEntity.fields.find(f => f.field === this.expectedReturnField)) {
×
UNCOV
567
                this._selectedReturnFields = [this.expectedReturnField];
×
568
            }
569
        }
570

571
        // Trigger additional change detection cycle
UNCOV
572
        this.cdr.detectChanges();
×
573
    }
574

575
    /**
576
     * @hidden @internal
577
     */
578
    public ngOnDestroy(): void {
UNCOV
579
        this.destroy$.next(true);
×
UNCOV
580
        this.destroy$.complete();
×
581
    }
582

583
    /**
584
     * @hidden @internal
585
     */
586
    public set selectedEntity(value: string) {
UNCOV
587
        this._selectedEntity = this.entities?.find(el => el.name === value);
×
588
    }
589

590
    /**
591
     * @hidden @internal
592
     */
593
    public get selectedEntity(): EntityType {
UNCOV
594
        return this._selectedEntity;
×
595
    }
596

597
    /**
598
     * @hidden @internal
599
     */
600
    public onEntitySelectChanging(event: ISelectionEventArgs) {
UNCOV
601
        event.cancel = true;
×
UNCOV
602
        this._entityNewValue = event.newSelection.value;
×
UNCOV
603
        if (event.oldSelection.value && this.queryBuilder.showEntityChangeDialog) {
×
UNCOV
604
            this.entityChangeDialog.open();
×
605
        } else {
UNCOV
606
            this.onEntityChangeConfirm();
×
607
        }
608
    }
609

610
    /**
611
     * @hidden
612
     */
613
    public onShowEntityChangeDialogChange(eventArgs: IChangeCheckboxEventArgs) {
UNCOV
614
        this.queryBuilder.showEntityChangeDialog = !eventArgs.checked;
×
615
    }
616

617
    /**
618
     * @hidden
619
     */
620
    public onEntityChangeCancel() {
UNCOV
621
        this.entityChangeDialog.close();
×
UNCOV
622
        this.entitySelect.close();
×
UNCOV
623
        this._entityNewValue = null;
×
624
    }
625

626
    /**
627
     * @hidden
628
     */
629
    public onEntityChangeConfirm() {
UNCOV
630
        if (this._parentExpression) {
×
UNCOV
631
            this._expressionTree = this.createExpressionTreeFromGroupItem(this.createExpressionGroupItem(this._expressionTree));
×
632
        }
633

UNCOV
634
        this._selectedEntity = this._entityNewValue;
×
UNCOV
635
        if (!this._selectedEntity.fields) {
×
636
            this._selectedEntity.fields = [];
×
637
        }
UNCOV
638
        this.fields = this._entityNewValue ? this._entityNewValue.fields : [];
×
639

UNCOV
640
        if (this._selectedEntity.fields.find(f => f.field === this.expectedReturnField)) {
×
UNCOV
641
            this._selectedReturnFields = [this.expectedReturnField];
×
642
        } else {
UNCOV
643
            this._selectedReturnFields = this.parentExpression ? [] : this._entityNewValue.fields?.map(f => f.field);
×
644
        }
645

UNCOV
646
        if (this._expressionTree) {
×
UNCOV
647
            this._expressionTree.entity = this._entityNewValue.name;
×
648

UNCOV
649
            this._expressionTree.returnFields = this.fields.length === this._selectedReturnFields.length ? ['*'] : this._selectedReturnFields;
×
650

UNCOV
651
            this._expressionTree.filteringOperands = [];
×
652

UNCOV
653
            this._editedExpression = null;
×
UNCOV
654
            if (!this.parentExpression) {
×
UNCOV
655
                this.expressionTreeChange.emit(this._expressionTree);
×
656
            }
657

UNCOV
658
            this.rootGroup = null;
×
UNCOV
659
            this.currentGroup = this.rootGroup;
×
660
        }
661

UNCOV
662
        this._selectedField = null;
×
UNCOV
663
        this.selectedCondition = null;
×
UNCOV
664
        this.searchValue.value = null;
×
665

UNCOV
666
        this.entityChangeDialog.close();
×
UNCOV
667
        this.entitySelect.close();
×
668

UNCOV
669
        this._entityNewValue = null;
×
UNCOV
670
        this.innerQueryNewExpressionTree = null;
×
671

UNCOV
672
        this.initExpressionTree(this._selectedEntity.name, this.selectedReturnFields);
×
673
    }
674

675
    /**
676
     * @hidden @internal
677
     */
678
    public set selectedReturnFields(value: string[]) {
UNCOV
679
        if (this._selectedReturnFields !== value) {
×
UNCOV
680
            this._selectedReturnFields = value;
×
681

UNCOV
682
            if (this._expressionTree && !this.parentExpression) {
×
UNCOV
683
                this._expressionTree.returnFields = value.length === this.fields.length ? ['*'] : value;
×
UNCOV
684
                this.expressionTreeChange.emit(this._expressionTree);
×
685
            }
686
        }
687
    }
688

689
    /**
690
     * @hidden @internal
691
     */
692
    public get selectedReturnFields(): string[] {
UNCOV
693
        if (typeof this._selectedReturnFields == 'string') {
×
694
            return [this._selectedReturnFields];
×
695
        }
UNCOV
696
        return this._selectedReturnFields;
×
697
    }
698

699
    /**
700
     * @hidden @internal
701
     */
702
    public set selectedField(value: FieldType) {
UNCOV
703
        const oldValue = this._selectedField;
×
704

UNCOV
705
        if (this._selectedField !== value) {
×
UNCOV
706
            this._selectedField = value;
×
UNCOV
707
            if (this._selectedField && !this._selectedField.dataType) {
×
708
                this._selectedField.filters = this.getFilters(this._selectedField);
×
709
            }
710

UNCOV
711
            this.selectDefaultCondition();
×
UNCOV
712
            if (oldValue && this._selectedField && this._selectedField.dataType !== oldValue.dataType) {
×
UNCOV
713
                this.searchValue.value = null;
×
UNCOV
714
                this.cdr.detectChanges();
×
715
            }
716
        }
717
    }
718

719
    /**
720
     * @hidden @internal
721
     */
722
    public get selectedField(): FieldType {
UNCOV
723
        return this._selectedField;
×
724
    }
725

726
    /**
727
     * @hidden @internal
728
     *
729
     * used by the grid
730
     */
731
    public setPickerOutlet(outlet?: IgxOverlayOutletDirective | ElementRef) {
UNCOV
732
        this.pickerOutlet = outlet;
×
733
    }
734

735
    /**
736
     * @hidden @internal
737
     *
738
     * used by the grid
739
     */
740
    public get isContextMenuVisible(): boolean {
741
        return !this.groupContextMenuDropDown.collapsed;
×
742
    }
743

744
    /**
745
     * @hidden @internal
746
     */
747
    public get hasEditedExpression(): boolean {
UNCOV
748
        return this._editedExpression !== undefined && this._editedExpression !== null;
×
749
    }
750

751
    /**
752
     * @hidden @internal
753
     */
754
    public addCondition(parent: ExpressionGroupItem, afterExpression?: ExpressionOperandItem, isUIInteraction?: boolean) {
UNCOV
755
        this.cancelOperandAdd();
×
756

UNCOV
757
        const operandItem = new ExpressionOperandItem({
×
758
            fieldName: null,
759
            condition: null,
760
            conditionName: null,
761
            ignoreCase: true,
762
            searchVal: null
763
        }, parent);
764

UNCOV
765
        const groupItem = new ExpressionGroupItem(this.getOperator(null) ?? FilteringLogic.And, parent);
×
UNCOV
766
        this.contextualGroup = groupItem;
×
UNCOV
767
        this.initialOperator = null;
×
768

UNCOV
769
        this._lastFocusedChipIndex = this._lastFocusedChipIndex === undefined ? -1 : this._lastFocusedChipIndex;
×
770

UNCOV
771
        if (parent) {
×
UNCOV
772
            if (afterExpression) {
×
UNCOV
773
                const index = parent.children.indexOf(afterExpression);
×
UNCOV
774
                parent.children.splice(index + 1, 0, operandItem);
×
775
            } else {
UNCOV
776
                parent.children.push(operandItem);
×
777
            }
UNCOV
778
            this._lastFocusedChipIndex++;
×
779
        } else {
UNCOV
780
            this.rootGroup = groupItem;
×
UNCOV
781
            operandItem.parent = groupItem;
×
UNCOV
782
            this.rootGroup.children.push(operandItem);
×
UNCOV
783
            this._lastFocusedChipIndex = 0;
×
784
        }
785

UNCOV
786
        this._focusDelay = 250;
×
787

UNCOV
788
        if (isUIInteraction && !afterExpression) {
×
UNCOV
789
            this._lastFocusedChipIndex = this.expressionsChips.length;
×
UNCOV
790
            this._focusDelay = DEFAULT_CHIP_FOCUS_DELAY;
×
791
        }
792

UNCOV
793
        this.enterExpressionEdit(operandItem);
×
794
    }
795

796
    /**
797
     * @hidden @internal
798
     */
799
    public addReverseGroup(parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) {
UNCOV
800
        parent = parent ?? this.rootGroup;
×
801

UNCOV
802
        if (parent.operator === FilteringLogic.And) {
×
UNCOV
803
            this.addGroup(FilteringLogic.Or, parent, afterExpression);
×
804
        } else {
UNCOV
805
            this.addGroup(FilteringLogic.And, parent, afterExpression);
×
806
        }
807
    }
808

809
    /**
810
     * @hidden @internal
811
     */
812
    public endGroup(groupItem: ExpressionGroupItem) {
813
        this.currentGroup = groupItem.parent;
×
814
    }
815

816
    /**
817
     * @hidden @internal
818
     */
819
    public commitExpression() {
UNCOV
820
        this.commitOperandEdit();
×
UNCOV
821
        this.focusEditedExpressionChip();
×
822
    }
823

824
    /**
825
     * @hidden @internal
826
     */
827
    public discardExpression(expressionItem?: ExpressionOperandItem) {
UNCOV
828
        this.cancelOperandEdit();
×
UNCOV
829
        if (expressionItem && expressionItem.expression.fieldName) {
×
UNCOV
830
            this.focusEditedExpressionChip();
×
831
        }
832
    }
833

834
    /**
835
     * @hidden @internal
836
     */
837
    public commitOperandEdit() {
UNCOV
838
        const actualSearchValue = this.searchValue.value;
×
UNCOV
839
        if (this._editedExpression) {
×
UNCOV
840
            this._editedExpression.expression.fieldName = this.selectedField.field;
×
UNCOV
841
            this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition);
×
UNCOV
842
            this._editedExpression.expression.conditionName = this.selectedCondition;
×
UNCOV
843
            this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, actualSearchValue) || actualSearchValue;
×
UNCOV
844
            this._editedExpression.fieldLabel = this.selectedField.label
×
845
                ? this.selectedField.label
846
                : this.selectedField.header
×
847
                    ? this.selectedField.header
848
                    : this.selectedField.field;
849

UNCOV
850
            const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]
×
UNCOV
851
            if (innerQuery && this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery) {
×
UNCOV
852
                innerQuery.exitEditAddMode();
×
UNCOV
853
                this._editedExpression.expression.searchTree = this.getExpressionTreeCopy(innerQuery.expressionTree);
×
UNCOV
854
                const returnFields = innerQuery.selectedReturnFields.length > 0 ?
×
855
                                        innerQuery.selectedReturnFields :
856
                                        [innerQuery.fields[0].field];
UNCOV
857
                this._editedExpression.expression.searchTree.returnFields = returnFields;
×
858
            } else {
UNCOV
859
                this._editedExpression.expression.searchTree = null;
×
860
            }
UNCOV
861
            this.innerQueryNewExpressionTree = null;
×
862

UNCOV
863
            if (this.selectedField.filters.condition(this.selectedCondition)?.isUnary || this.selectedField.filters.condition(this.selectedCondition)?.isNestedQuery) {
×
UNCOV
864
                this._editedExpression.expression.searchVal = null;
×
865
            }
866

UNCOV
867
            this._editedExpression.inEditMode = false;
×
UNCOV
868
            this._editedExpression = null;
×
869
        }
870

UNCOV
871
        if (this.selectedReturnFields.length === 0) {
×
872
            this.selectedReturnFields = this.fields.map(f => f.field);
×
873
        }
874

UNCOV
875
        this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup, this.selectedEntity?.name, this.selectedReturnFields);
×
UNCOV
876
        if (!this.parentExpression) {
×
UNCOV
877
            this.expressionTreeChange.emit(this._expressionTree);
×
878
        }
879
    }
880

881
    /**
882
     * @hidden @internal
883
     */
884
    public cancelOperandAdd() {
UNCOV
885
        if (this._addModeExpression) {
×
886
            this._addModeExpression.inAddMode = false;
×
887
            this._addModeExpression = null;
×
888
        }
889
    }
890

891
    /**
892
     * @hidden @internal
893
     */
UNCOV
894
    public deleteItem = (expressionItem: ExpressionItem, skipEmit: boolean = false) => {
×
UNCOV
895
        if (!expressionItem.parent) {
×
UNCOV
896
            this.rootGroup = null;
×
UNCOV
897
            this.currentGroup = null;
×
898
            //this._expressionTree = null;
UNCOV
899
            return;
×
900
        }
901

UNCOV
902
        if (expressionItem === this.currentGroup) {
×
903
            this.currentGroup = this.currentGroup.parent;
×
904
        }
905

UNCOV
906
        const children = expressionItem.parent.children;
×
UNCOV
907
        const index = children.indexOf(expressionItem);
×
UNCOV
908
        children.splice(index, 1);
×
UNCOV
909
        const entity = this.expressionTree ? this.expressionTree.entity : null;
×
UNCOV
910
        const returnFields = this.expressionTree ? this.expressionTree.returnFields : null;
×
UNCOV
911
        this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup, entity, returnFields); // TODO: don't recreate if not necessary
×
912

UNCOV
913
        if (!children.length) {
×
UNCOV
914
            this.deleteItem(expressionItem.parent, true);
×
915
        }
916

UNCOV
917
        if (!this.parentExpression && !skipEmit) {
×
UNCOV
918
            this.expressionTreeChange.emit(this._expressionTree);
×
919
        }
920
    }
921

922
    /**
923
     * @hidden @internal
924
     */
925
    public cancelOperandEdit() {
UNCOV
926
        if (this.innerQueries) {
×
UNCOV
927
            const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0];
×
UNCOV
928
            if (innerQuery) {
×
UNCOV
929
                if (innerQuery._editedExpression) {
×
UNCOV
930
                    innerQuery.cancelOperandEdit();
×
931
                }
932

UNCOV
933
                innerQuery.expressionTree = this.getExpressionTreeCopy(this._editedExpression.expression.searchTree);
×
UNCOV
934
                this.innerQueryNewExpressionTree = null;
×
935
            }
936
        }
937

UNCOV
938
        if (this._editedExpression) {
×
UNCOV
939
            this._editedExpression.inEditMode = false;
×
940

UNCOV
941
            if (!this._editedExpression.expression.fieldName) {
×
UNCOV
942
                this.deleteItem(this._editedExpression);
×
943
            }
944

UNCOV
945
            this._editedExpression = null;
×
946
        }
947

UNCOV
948
        if (!this.expressionTree && this.contextualGroup) {
×
UNCOV
949
            this.initialOperator = this.contextualGroup.operator;
×
950
        }
951
    }
952

953
    /**
954
     * @hidden @internal
955
     */
956
    public operandCanBeCommitted(): boolean {
UNCOV
957
        const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0];
×
UNCOV
958
        return this.selectedField && this.selectedCondition &&
×
959
            (
960
                (
961
                    ((!Array.isArray(this.searchValue.value) && !!this.searchValue.value) || (Array.isArray(this.searchValue.value) && this.searchValue.value.length !== 0)) &&
962
                    !(this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery)
963
                ) ||
964
                (
965
                    this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery && innerQuery && !!innerQuery.expressionTree && innerQuery.selectedReturnFields?.length > 0
966
                ) ||
967
                this.selectedField.filters.condition(this.selectedCondition)?.isUnary
968
            );
969
    }
970
    
971
    /**
972
     * @hidden @internal
973
     */
974
    public canCommitCurrentState(): boolean {
UNCOV
975
        const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0];
×
UNCOV
976
        if (innerQuery) {
×
UNCOV
977
            return this.selectedReturnFields?.length > 0 && innerQuery.canCommitCurrentState();
×
978
        } else {
UNCOV
979
            return this.selectedReturnFields?.length > 0 &&
×
980
                (
981
                    (!this._editedExpression) || // no edited expr
982
                    (this._editedExpression && !this.selectedField) || // empty edited expr
983
                    (this._editedExpression && this.operandCanBeCommitted() === true) // valid edited expr
984
                );
985
        }
986
    }
987

988
    /**
989
     * @hidden @internal
990
     */
991
    public commitCurrentState(): void {
UNCOV
992
        const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0];
×
UNCOV
993
        if (innerQuery) {
×
UNCOV
994
            innerQuery.commitCurrentState();
×
995
        }
996

UNCOV
997
        if (this._editedExpression) {
×
UNCOV
998
            if (this.selectedField) {
×
UNCOV
999
                this.commitOperandEdit();
×
1000
            } else {
1001
                this.deleteItem(this._editedExpression);
×
1002
                this._editedExpression = null;
×
1003
            }
1004
        }
1005
    }
1006

1007
    /**
1008
     * @hidden @internal
1009
     */
1010
    public exitEditAddMode(shouldPreventInit = false) {
×
UNCOV
1011
        if (!this._editedExpression) {
×
UNCOV
1012
            return;
×
1013
        }
1014

UNCOV
1015
        this.exitOperandEdit();
×
UNCOV
1016
        this.cancelOperandAdd();
×
1017

UNCOV
1018
        if (shouldPreventInit) {
×
UNCOV
1019
            this._preventInit = true;
×
1020
        }
1021
    }
1022

1023
    /**
1024
     * @hidden @internal
1025
     *
1026
     * used by the grid
1027
     */
1028
    public exitOperandEdit() {
UNCOV
1029
        if (!this._editedExpression) {
×
UNCOV
1030
            return;
×
1031
        }
1032

UNCOV
1033
        if (this.operandCanBeCommitted()) {
×
UNCOV
1034
            this.commitOperandEdit();
×
1035
        } else {
UNCOV
1036
            this.cancelOperandEdit();
×
1037
        }
1038
    }
1039

1040
    /**
1041
     * @hidden @internal
1042
     */
1043
    public isExpressionGroup(expression: ExpressionItem): boolean {
UNCOV
1044
        return expression instanceof ExpressionGroupItem;
×
1045
    }
1046

1047
    /**
1048
     * @hidden @internal
1049
     */
1050
    public onExpressionFocus(expressionItem: ExpressionOperandItem) {
UNCOV
1051
        if (this.prevFocusedExpression) {
×
UNCOV
1052
            this.prevFocusedExpression.focused = false;
×
1053
        }
UNCOV
1054
        expressionItem.focused = true;
×
UNCOV
1055
        this.prevFocusedExpression = expressionItem;
×
1056
    }
1057

1058
    /**
1059
     * @hidden @internal
1060
     */
1061
    public onExpressionBlur(event, expressionItem: ExpressionOperandItem) {
UNCOV
1062
        if (this._prevFocusedContainer && this._prevFocusedContainer !== event.target.closest('.igx-filter-tree__expression-item')) {
×
1063
            expressionItem.focused = false;
×
1064
        }
UNCOV
1065
        this._prevFocusedContainer = event.target.closest('.igx-filter-tree__expression-item');
×
1066
    }
1067

1068
    /**
1069
     * @hidden @internal
1070
     */
1071
    public onChipRemove(expressionItem: ExpressionItem) {
UNCOV
1072
        this.exitEditAddMode();
×
UNCOV
1073
        this.deleteItem(expressionItem);
×
1074
    }
1075

1076
    /**
1077
     * @hidden @internal
1078
     */
UNCOV
1079
    public focusChipAfterDrag = (index: number) => {
×
UNCOV
1080
        this._lastFocusedChipIndex = index;
×
UNCOV
1081
        this.focusEditedExpressionChip();
×
1082
    }
1083
    /**
1084
     * @hidden @internal
1085
     */
1086
    public addExpressionBlur() {
1087
        if (this.prevFocusedExpression) {
×
1088
            this.prevFocusedExpression.focused = false;
×
1089
        }
1090
        if (this.addExpressionItemDropDown && !this.addExpressionItemDropDown.collapsed) {
×
1091
            this.addExpressionItemDropDown.close();
×
1092
        }
1093
    }
1094

1095
    /**
1096
     * @hidden @internal
1097
     */
1098
    public onChipClick(expressionItem: ExpressionOperandItem, chip: IgxChipComponent) {
UNCOV
1099
        this.enterExpressionEdit(expressionItem, chip);
×
1100
    }
1101

1102
    /**
1103
     * @hidden @internal
1104
     */
1105
    public enterExpressionEdit(expressionItem: ExpressionOperandItem, chip?: IgxChipComponent) {
UNCOV
1106
        this.exitEditAddMode(true);
×
UNCOV
1107
        this.cdr.detectChanges();
×
UNCOV
1108
        this._lastFocusedChipIndex = chip ? this.expressionsChips.toArray().findIndex(expr => expr === chip) : this._lastFocusedChipIndex;
×
UNCOV
1109
        this.enterEditMode(expressionItem);
×
1110
    }
1111

1112

1113
    /**
1114
     * @hidden @internal
1115
     */
1116
    public clickExpressionAdd(targetButton: HTMLElement, chip: IgxChipComponent) {
UNCOV
1117
        this.exitEditAddMode(true);
×
UNCOV
1118
        this.cdr.detectChanges();
×
UNCOV
1119
        this._lastFocusedChipIndex = this.expressionsChips.toArray().findIndex(expr => expr === chip);
×
UNCOV
1120
        this.openExpressionAddDialog(targetButton);
×
1121
    }
1122

1123
    /**
1124
     * @hidden @internal
1125
     */
1126
    public openExpressionAddDialog(targetButton: HTMLElement) {
UNCOV
1127
        this.addExpressionDropDownOverlaySettings.target = targetButton;
×
UNCOV
1128
        this.addExpressionDropDownOverlaySettings.positionStrategy = new ConnectedPositioningStrategy({
×
1129
            horizontalDirection: HorizontalAlignment.Right,
1130
            horizontalStartPoint: HorizontalAlignment.Left,
1131
            verticalStartPoint: VerticalAlignment.Bottom
1132
        });
1133

UNCOV
1134
        this.addExpressionItemDropDown.open(this.addExpressionDropDownOverlaySettings);
×
1135
    }
1136

1137
    /**
1138
     * @hidden @internal
1139
     */
1140
    public enterExpressionAdd(event: ISelectionEventArgs, expressionItem: ExpressionOperandItem) {
UNCOV
1141
        if (this._addModeExpression) {
×
1142
            this._addModeExpression.inAddMode = false;
×
1143
        }
1144

UNCOV
1145
        if (this.parentExpression) {
×
1146
            this.inEditModeChange.emit(this.parentExpression);
×
1147
        }
1148

UNCOV
1149
        const parent = expressionItem.parent ?? this.rootGroup;
×
UNCOV
1150
        requestAnimationFrame(() => {
×
UNCOV
1151
            if (event.newSelection.value === 'addCondition') {
×
UNCOV
1152
                this.addCondition(parent, expressionItem);
×
1153
            } else if (event.newSelection.value === 'addGroup') {
×
1154
                this.addReverseGroup(parent, expressionItem);
×
1155
            }
UNCOV
1156
            expressionItem.inAddMode = true;
×
UNCOV
1157
            this._addModeExpression = expressionItem;
×
1158
        })
1159
    }
1160

1161
    /**
1162
     * @hidden @internal
1163
     */
1164
    public enterEditMode(expressionItem: ExpressionOperandItem) {
UNCOV
1165
        if (this._editedExpression) {
×
1166
            this._editedExpression.inEditMode = false;
×
1167
        }
1168

UNCOV
1169
        if (this.parentExpression) {
×
UNCOV
1170
            this.inEditModeChange.emit(this.parentExpression);
×
1171
        }
1172

UNCOV
1173
        expressionItem.hovered = false;
×
UNCOV
1174
        this.fields = this.selectedEntity ? this.selectedEntity.fields : null;
×
UNCOV
1175
        this.selectedField =
×
1176
            expressionItem.expression.fieldName ?
×
UNCOV
1177
                this.fields?.find(field => field.field === expressionItem.expression.fieldName)
×
1178
                : null;
UNCOV
1179
        this.selectedCondition =
×
1180
            expressionItem.expression.condition ?
×
1181
                expressionItem.expression.condition.name :
1182
                null;
UNCOV
1183
        this.searchValue.value = expressionItem.expression.searchVal instanceof Set ?
×
1184
            Array.from(expressionItem.expression.searchVal) :
1185
            expressionItem.expression.searchVal;
1186

UNCOV
1187
        expressionItem.inEditMode = true;
×
UNCOV
1188
        this._editedExpression = expressionItem;
×
UNCOV
1189
        this.cdr.detectChanges();
×
1190

UNCOV
1191
        this.entitySelectOverlaySettings.target = this.entitySelect.element;
×
UNCOV
1192
        this.entitySelectOverlaySettings.excludeFromOutsideClick = [this.entitySelect.element as HTMLElement];
×
UNCOV
1193
        this.entitySelectOverlaySettings.positionStrategy = new AutoPositionStrategy();
×
1194

UNCOV
1195
        if (this.returnFieldSelect) {
×
UNCOV
1196
            this.returnFieldSelectOverlaySettings.target = this.returnFieldSelect.element;
×
UNCOV
1197
            this.returnFieldSelectOverlaySettings.excludeFromOutsideClick = [this.returnFieldSelect.element as HTMLElement];
×
UNCOV
1198
            this.returnFieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy();
×
1199
        }
UNCOV
1200
        if (this.fieldSelect) {
×
UNCOV
1201
            this.fieldSelectOverlaySettings.target = this.fieldSelect.element;
×
UNCOV
1202
            this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.element as HTMLElement];
×
UNCOV
1203
            this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy();
×
1204
        }
UNCOV
1205
        if (this.conditionSelect) {
×
UNCOV
1206
            this.conditionSelectOverlaySettings.target = this.conditionSelect.element;
×
UNCOV
1207
            this.conditionSelectOverlaySettings.excludeFromOutsideClick = [this.conditionSelect.element as HTMLElement];
×
UNCOV
1208
            this.conditionSelectOverlaySettings.positionStrategy = new AutoPositionStrategy();
×
1209
        }
1210

UNCOV
1211
        if (!this.selectedField) {
×
UNCOV
1212
            this.fieldSelect.input.nativeElement.focus();
×
UNCOV
1213
        } else if (this.selectedField.filters.condition(this.selectedCondition)?.isUnary) {
×
UNCOV
1214
            this.conditionSelect?.input.nativeElement.focus();
×
1215
        } else {
UNCOV
1216
            const input = this.searchValueInput?.nativeElement || this.picker?.getEditElement();
×
UNCOV
1217
            input?.focus();
×
1218
        }
1219

UNCOV
1220
        (this.editingInputs?.nativeElement.parentElement as HTMLElement)?.scrollIntoView({ block: "nearest", inline: "nearest" });
×
1221
    }
1222

1223
    /**
1224
     * @hidden @internal
1225
     */
1226
    public onConditionSelectChanging(event: ISelectionEventArgs) {
UNCOV
1227
        event.cancel = true;
×
UNCOV
1228
        this.selectedCondition = event.newSelection.value;
×
UNCOV
1229
        this.conditionSelect.close();
×
UNCOV
1230
        this.cdr.detectChanges();
×
1231
    }
1232

1233
    /**
1234
     * @hidden @internal
1235
     */
1236
    public onKeyDown(eventArgs: KeyboardEvent) {
1237
        eventArgs.stopPropagation();
×
1238
    }
1239

1240
    /**
1241
     * @hidden @internal
1242
     */
1243
    public onGroupClick(groupContextMenuDropDown: any, targetButton: HTMLButtonElement, groupItem: ExpressionGroupItem) {
UNCOV
1244
        this.exitEditAddMode();
×
UNCOV
1245
        this.cdr.detectChanges();
×
1246

UNCOV
1247
        this.groupContextMenuDropDown = groupContextMenuDropDown;
×
UNCOV
1248
        this.groupContextMenuDropDownOverlaySettings.target = targetButton;
×
UNCOV
1249
        this.groupContextMenuDropDownOverlaySettings.positionStrategy = new ConnectedPositioningStrategy({
×
1250
            horizontalDirection: HorizontalAlignment.Right,
1251
            horizontalStartPoint: HorizontalAlignment.Left,
1252
            verticalStartPoint: VerticalAlignment.Bottom
1253
        });
1254

UNCOV
1255
        if (groupContextMenuDropDown.collapsed) {
×
UNCOV
1256
            this.contextualGroup = groupItem;
×
UNCOV
1257
            groupContextMenuDropDown.open(this.groupContextMenuDropDownOverlaySettings);
×
1258
        } else {
UNCOV
1259
            groupContextMenuDropDown.close();
×
1260
        }
1261
    }
1262

1263
    /**
1264
     * @hidden @internal
1265
     */
1266
    public getOperator(expressionItem: any) {
1267
        // if (!expressionItem && !this.expressionTree && !this.initialOperator) {
1268
        //     this.initialOperator = 0;
1269
        // }
1270

UNCOV
1271
        const operator = expressionItem ?
×
1272
            expressionItem.operator :
1273
            this.expressionTree ?
×
1274
                this.expressionTree.operator :
1275
                this.initialOperator;
UNCOV
1276
        return operator;
×
1277
    }
1278

1279
    /**
1280
     * @hidden @internal
1281
     */
1282
    public getSwitchGroupText(expressionItem: any) {
UNCOV
1283
        const operator = this.getOperator(expressionItem);
×
UNCOV
1284
        const condition = operator === FilteringLogic.Or ? this.resourceStrings.igx_query_builder_and_label : this.resourceStrings.igx_query_builder_or_label
×
UNCOV
1285
        return this.resourceStrings.igx_query_builder_switch_group.replace('{0}', condition.toUpperCase());
×
1286
    }
1287

1288
    /**
1289
     * @hidden @internal
1290
     */
1291
    public onGroupContextMenuDropDownSelectionChanging(event: ISelectionEventArgs) {
UNCOV
1292
        event.cancel = true;
×
1293

UNCOV
1294
        if (event.newSelection.value === 'switchCondition') {
×
UNCOV
1295
            const newOperator = (!this.expressionTree ? this.initialOperator : (this.contextualGroup ?? this._expressionTree).operator) === 0 ? 1 : 0;
×
UNCOV
1296
            this.selectFilteringLogic(newOperator);
×
UNCOV
1297
        } else if (event.newSelection.value === 'ungroup') {
×
UNCOV
1298
            this.ungroup();
×
1299
        }
1300

UNCOV
1301
        this.groupContextMenuDropDown.close();
×
1302
    }
1303

1304
    /**
1305
     * @hidden @internal
1306
     */
1307
    public ungroup() {
UNCOV
1308
        const selectedGroup = this.contextualGroup;
×
UNCOV
1309
        const parent = selectedGroup.parent;
×
UNCOV
1310
        if (parent) {
×
UNCOV
1311
            const index = parent.children.indexOf(selectedGroup);
×
UNCOV
1312
            parent.children.splice(index, 1, ...selectedGroup.children);
×
1313

UNCOV
1314
            for (const expr of selectedGroup.children) {
×
UNCOV
1315
                expr.parent = parent;
×
1316
            }
1317
        }
UNCOV
1318
        this.commitOperandEdit();
×
1319
    }
1320

1321
    /**
1322
     * @hidden @internal
1323
     */
1324
    public selectFilteringLogic(index: number) {
UNCOV
1325
        if (!this.expressionTree) {
×
1326
            this.initialOperator = index;
×
1327
            return;
×
1328
        }
1329

UNCOV
1330
        if (this.contextualGroup) {
×
UNCOV
1331
            this.contextualGroup.operator = index as FilteringLogic;
×
UNCOV
1332
            this.commitOperandEdit();
×
UNCOV
1333
        } else if (this.expressionTree) {
×
UNCOV
1334
            this._expressionTree.operator = index as FilteringLogic;
×
1335
        }
1336

UNCOV
1337
        this.initialOperator = null;
×
1338
    }
1339

1340
    /**
1341
     * @hidden @internal
1342
     */
1343
    public getConditionFriendlyName(name: string): string {
1344
        // As we have an 'In' condition already used in ESF to search in a Set, we add the 'Query' suffix to the newly introduced nested query condition names.
1345
        // So instead of in/notIn we end up with 'inQuery'/'notInQuery', hence removing the suffix from the friendly name.
UNCOV
1346
        return this.resourceStrings[`igx_query_builder_filter_${name?.replace('Query', '')}`] || name;
×
1347
    }
1348

1349
    /**
1350
     * @hidden @internal
1351
     */
1352
    public isDate(value: any) {
UNCOV
1353
        return value instanceof Date;
×
1354
    }
1355

1356
    /**
1357
     * @hidden @internal
1358
     */
1359
    public invokeClick(eventArgs: KeyboardEvent) {
UNCOV
1360
        if (!this.dragService.dropGhostExpression && this.platform.isActivationKey(eventArgs)) {
×
UNCOV
1361
            eventArgs.preventDefault();
×
UNCOV
1362
            (eventArgs.currentTarget as HTMLElement).click();
×
1363
        }
1364
    }
1365

1366
    /**
1367
     * @hidden @internal
1368
     */
1369
    public openPicker(args: KeyboardEvent) {
1370
        if (this.platform.isActivationKey(args)) {
×
1371
            args.preventDefault();
×
1372
            this.picker.open();
×
1373
        }
1374
    }
1375

1376
    /**
1377
     * @hidden @internal
1378
     */
1379
    public onOutletPointerDown(event) {
1380
        // This prevents closing the select's dropdown when clicking the scroll
UNCOV
1381
        event.preventDefault();
×
1382
    }
1383

1384
    /**
1385
     * @hidden @internal
1386
     */
1387
    public getConditionList(): string[] {
UNCOV
1388
        if (!this.selectedField) return [];
×
1389

UNCOV
1390
        if (!this.selectedField.filters) {
×
1391
            this.selectedField.filters = this.getFilters(this.selectedField);
×
1392
        }
1393

UNCOV
1394
        if ((this.isAdvancedFiltering() && !this.entities[0].childEntities) ||
×
1395
            (this.isHierarchicalNestedQuery() && this.selectedEntity.name && !this.selectedEntity.childEntities)) {
UNCOV
1396
            return this.selectedField.filters.conditionList();
×
1397
        }
1398

UNCOV
1399
        return this.selectedField.filters.extendedConditionList();
×
1400
    }
1401

1402
    /**
1403
     * @hidden @internal
1404
     */
1405
    public getFormatter(field: string) {
UNCOV
1406
        return this.fields?.find(el => el.field === field)?.formatter;
×
1407
    }
1408

1409
    /**
1410
     * @hidden @internal
1411
     */
1412
    public getFormat(field: string) {
UNCOV
1413
        return this.fields?.find(el => el.field === field).pipeArgs.format;
×
1414
    }
1415

1416
    /**
1417
     * @hidden @internal
1418
     *
1419
     * used by the grid
1420
     */
1421
    public setAddButtonFocus() {
UNCOV
1422
        if (this.addRootAndGroupButton) {
×
1423
            this.addRootAndGroupButton.nativeElement.focus();
×
UNCOV
1424
        } else if (this.addConditionButton) {
×
UNCOV
1425
            this.addConditionButton.nativeElement.focus();
×
1426
        }
1427
    }
1428

1429
    /**
1430
     * @hidden @internal
1431
     */
1432
    public context(expression: ExpressionItem, afterExpression?: ExpressionItem) {
UNCOV
1433
        return {
×
1434
            $implicit: expression,
1435
            afterExpression
1436
        };
1437
    }
1438

1439
    public formatReturnFields(innerTree: IFilteringExpressionsTree) {
UNCOV
1440
        const returnFields = innerTree.returnFields;
×
UNCOV
1441
        let text = returnFields.join(', ');
×
UNCOV
1442
        const innerTreeEntity = this.entities?.find(el => el.name === innerTree.entity);
×
UNCOV
1443
        if (returnFields.length === innerTreeEntity?.fields.length) {
×
1444
            text = this.resourceStrings.igx_query_builder_all_fields;
×
1445
        } else {
UNCOV
1446
            text = returnFields.join(', ');
×
UNCOV
1447
            text = text.length > 25 ? text.substring(0, 25) + ' ...' : text;
×
1448
        }
UNCOV
1449
        return text;
×
1450
    }
1451

1452
    public isInEditMode(): boolean {
UNCOV
1453
        return !this.parentExpression || (this.parentExpression && this.parentExpression.inEditMode);
×
1454
    }
1455

1456
    public onInEditModeChanged(expressionItem: ExpressionOperandItem) {
UNCOV
1457
        if (!expressionItem.inEditMode) {
×
1458
            this.enterExpressionEdit(expressionItem);
×
1459
        }
1460
    }
1461

1462
    public getExpressionTreeCopy(expressionTree: IExpressionTree, shouldAssignInnerQueryExprTree?: boolean): IExpressionTree {
UNCOV
1463
        if (!expressionTree) {
×
UNCOV
1464
            return null;
×
1465
        }
1466

UNCOV
1467
        const exprTreeCopy = new FilteringExpressionsTree(expressionTree.operator, expressionTree.fieldName, expressionTree.entity, expressionTree.returnFields);
×
UNCOV
1468
        exprTreeCopy.filteringOperands = [];
×
1469

UNCOV
1470
        expressionTree.filteringOperands.forEach(o => isTree(o) ? exprTreeCopy.filteringOperands.push(this.getExpressionTreeCopy(o)) : exprTreeCopy.filteringOperands.push(o));
×
1471

UNCOV
1472
        if (!this.innerQueryNewExpressionTree && shouldAssignInnerQueryExprTree) {
×
UNCOV
1473
            this.innerQueryNewExpressionTree = exprTreeCopy;
×
1474
        }
1475

UNCOV
1476
        return exprTreeCopy;
×
1477
    }
1478

1479
    public onSelectAllClicked() {
UNCOV
1480
        if (
×
1481
            (this._selectedReturnFields.length > 0 && this._selectedReturnFields.length < this._selectedEntity.fields.length) ||
×
1482
            this._selectedReturnFields.length == this._selectedEntity.fields.length
1483
        ) {
UNCOV
1484
            this.returnFieldsCombo.deselectAllItems();
×
1485
        } else {
UNCOV
1486
            this.returnFieldsCombo.selectAllItems();
×
1487
        }
1488
    }
1489

1490
    public onReturnFieldSelectChanging(event: IComboSelectionChangingEventArgs | ISelectionEventArgs) {
UNCOV
1491
        let newSelection = [];
×
UNCOV
1492
        if (Array.isArray(event.newSelection)) {
×
UNCOV
1493
            newSelection = event.newSelection.map(item => item.field)
×
1494
        } else {
UNCOV
1495
            newSelection.push(event.newSelection.value);
×
UNCOV
1496
            this._selectedReturnFields = newSelection;
×
1497
        }
1498

UNCOV
1499
        this.initExpressionTree(this.selectedEntity.name, newSelection);
×
1500
    }
1501

1502
    public initExpressionTree(selectedEntityName: string, selectedReturnFields: string[]) {
UNCOV
1503
        if (!this._expressionTree) {
×
UNCOV
1504
            this._expressionTree = this.createExpressionTreeFromGroupItem(new ExpressionGroupItem(FilteringLogic.And, this.rootGroup), selectedEntityName, selectedReturnFields);
×
1505
        }
1506

UNCOV
1507
        if (!this.parentExpression) {
×
UNCOV
1508
            this.expressionTreeChange.emit(this._expressionTree);
×
1509
        }
1510
    }
1511

1512
    public getSearchValueTemplateContext(defaultSearchValueTemplate): any {
UNCOV
1513
        const ctx = {
×
1514
            $implicit: this.searchValue,
1515
            selectedField: this.selectedField,
1516
            selectedCondition: this.selectedCondition,
1517
            defaultSearchValueTemplate: defaultSearchValueTemplate
1518
        };
UNCOV
1519
        return ctx;
×
1520
    }
1521

1522
    private getPipeArgs(field: FieldType) {
UNCOV
1523
        let pipeArgs = {...field.pipeArgs};
×
UNCOV
1524
        if (!pipeArgs) {
×
1525
            pipeArgs = { digitsInfo: DEFAULT_PIPE_DIGITS_INFO };
×
1526
        }
1527

UNCOV
1528
        if (!pipeArgs.format) {
×
UNCOV
1529
            pipeArgs.format = field.dataType === DataType.Time ?
×
1530
                DEFAULT_PIPE_TIME_FORMAT : field.dataType === DataType.DateTime ?
×
1531
                    DEFAULT_PIPE_DATE_TIME_FORMAT : DEFAULT_PIPE_DATE_FORMAT;
1532
        }
1533
        
UNCOV
1534
        return pipeArgs;
×
1535
    }
1536

1537
    private selectDefaultCondition() {
UNCOV
1538
        if (this.selectedField && this.selectedField.filters) {
×
UNCOV
1539
            this.selectedCondition = this.selectedField.filters.conditionList().indexOf('equals') >= 0 ? 'equals' : this.selectedField.filters.conditionList()[0];
×
1540
        }
1541
    }
1542

1543
    private getFilters(field: FieldType) {
UNCOV
1544
        if (!field.filters) {
×
UNCOV
1545
            switch (field.dataType) {
×
1546
                case DataType.Boolean:
UNCOV
1547
                    return IgxBooleanFilteringOperand.instance();
×
1548
                case DataType.Number:
1549
                case DataType.Currency:
1550
                case DataType.Percent:
UNCOV
1551
                    return IgxNumberFilteringOperand.instance();
×
1552
                case DataType.Date:
UNCOV
1553
                    return IgxDateFilteringOperand.instance();
×
1554
                case DataType.Time:
1555
                    return IgxTimeFilteringOperand.instance();
×
1556
                case DataType.DateTime:
1557
                    return IgxDateTimeFilteringOperand.instance();
×
1558
                case DataType.String:
1559
                default:
UNCOV
1560
                    return IgxStringFilteringOperand.instance();
×
1561
            }
1562
        } else {
UNCOV
1563
            return field.filters;
×
1564
        }
1565
    }
1566

1567

1568
    private addGroup(operator: FilteringLogic, parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) {
UNCOV
1569
        this.cancelOperandAdd();
×
1570

UNCOV
1571
        const groupItem = new ExpressionGroupItem(operator, parent);
×
1572

UNCOV
1573
        if (parent) {
×
UNCOV
1574
            if (afterExpression) {
×
1575
                const index = parent.children.indexOf(afterExpression);
×
1576
                parent.children.splice(index + 1, 0, groupItem);
×
1577
            } else {
UNCOV
1578
                parent.children.push(groupItem);
×
1579
            }
1580
        } else {
1581
            this.rootGroup = groupItem;
×
1582
        }
1583

UNCOV
1584
        this.addCondition(groupItem);
×
UNCOV
1585
        this.currentGroup = groupItem;
×
1586
    }
1587

1588
    private createExpressionGroupItem(expressionTree: IExpressionTree, parent?: ExpressionGroupItem, entityName?: string): ExpressionGroupItem {
1589
        let groupItem: ExpressionGroupItem;
UNCOV
1590
        if (expressionTree) {
×
UNCOV
1591
            groupItem = new ExpressionGroupItem(expressionTree.operator, parent);
×
UNCOV
1592
            if (!expressionTree.filteringOperands) {
×
1593
                return groupItem;
×
1594
            }
1595

UNCOV
1596
            for (let i = 0; i < expressionTree.filteringOperands.length; i++) {
×
UNCOV
1597
                const expr = expressionTree.filteringOperands[i];
×
1598

UNCOV
1599
                if (isTree(expr)) {
×
UNCOV
1600
                    groupItem.children.push(this.createExpressionGroupItem(expr, groupItem, expressionTree.entity));
×
1601
                } else {
UNCOV
1602
                    const filteringExpr = expr as IFilteringExpression;
×
UNCOV
1603
                    const exprCopy: IFilteringExpression = {
×
1604
                        fieldName: filteringExpr.fieldName,
1605
                        condition: filteringExpr.condition,
1606
                        conditionName: filteringExpr.condition?.name || filteringExpr.conditionName,
×
1607
                        searchVal: filteringExpr.searchVal,
1608
                        searchTree: filteringExpr.searchTree,
1609
                        ignoreCase: filteringExpr.ignoreCase
1610
                    };
UNCOV
1611
                    const operandItem = new ExpressionOperandItem(exprCopy, groupItem);
×
UNCOV
1612
                    const field = this.fields?.find(el => el.field === filteringExpr.fieldName);
×
UNCOV
1613
                    operandItem.fieldLabel = field?.label || field?.header || field?.field;
×
UNCOV
1614
                    if (this._expandedExpressions.filter(e => e.searchTree == operandItem.expression.searchTree).length > 0) {
×
1615
                        operandItem.expanded = true;
×
1616
                    }
UNCOV
1617
                    groupItem.children.push(operandItem);
×
1618
                }
1619
            }
1620

1621

UNCOV
1622
            if (expressionTree.entity) {
×
UNCOV
1623
                entityName = expressionTree.entity;
×
1624
            }
UNCOV
1625
            const entity = this.entities?.find(el => el.name === entityName);
×
UNCOV
1626
            if (entity) {
×
UNCOV
1627
                this.fields = entity.fields;
×
1628
            }
1629

UNCOV
1630
            this._selectedEntity = this.entities?.find(el => el.name === entityName);
×
UNCOV
1631
            this._selectedReturnFields =
×
1632
                !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') || expressionTree.returnFields.length === 0
×
UNCOV
1633
                    ? this.fields?.map(f => f.field)
×
UNCOV
1634
                    : this.fields?.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field);
×
1635
        }
UNCOV
1636
        return groupItem;
×
1637
    }
1638

1639
    private createExpressionTreeFromGroupItem(groupItem: ExpressionGroupItem, entity?: string, returnFields?: string[]): FilteringExpressionsTree {
UNCOV
1640
        if (!groupItem) {
×
UNCOV
1641
            return null;
×
1642
        }
1643

UNCOV
1644
        const expressionTree = new FilteringExpressionsTree(groupItem.operator, undefined, entity, returnFields);
×
1645

UNCOV
1646
        for (let i = 0; i < groupItem.children.length; i++) {
×
UNCOV
1647
            const item = groupItem.children[i];
×
1648

UNCOV
1649
            if (item instanceof ExpressionGroupItem) {
×
UNCOV
1650
                const subTree = this.createExpressionTreeFromGroupItem((item as ExpressionGroupItem), entity, returnFields);
×
UNCOV
1651
                expressionTree.filteringOperands.push(subTree);
×
1652
            } else {
UNCOV
1653
                expressionTree.filteringOperands.push((item as ExpressionOperandItem).expression);
×
1654
            }
1655
        }
1656

UNCOV
1657
        return expressionTree;
×
1658
    }
1659

1660
    private scrollElementIntoView(target: HTMLElement) {
UNCOV
1661
        const container = this.expressionsContainer.nativeElement;
×
UNCOV
1662
        const targetOffset = target.offsetTop - container.offsetTop;
×
UNCOV
1663
        const delta = 10;
×
1664

UNCOV
1665
        if (container.scrollTop + delta > targetOffset) {
×
UNCOV
1666
            container.scrollTop = targetOffset - delta;
×
UNCOV
1667
        } else if (container.scrollTop + container.clientHeight < targetOffset + target.offsetHeight + delta) {
×
UNCOV
1668
            container.scrollTop = targetOffset + target.offsetHeight + delta - container.clientHeight;
×
1669
        }
1670
    }
1671

1672
    private focusEditedExpressionChip() {
UNCOV
1673
        if (this._timeoutId) {
×
UNCOV
1674
            clearTimeout(this._timeoutId);
×
1675
        }
1676

UNCOV
1677
        this._timeoutId = setTimeout(() => {
×
UNCOV
1678
            if (this._lastFocusedChipIndex != -1) {
×
1679
                //Sort the expression chip list.
1680
                //If there was a recent drag&drop and the tree hasn't rerendered(child query), they will be unordered
UNCOV
1681
                const sortedChips = this.expressionsChips.toArray().sort(function (a, b) {
×
UNCOV
1682
                    if (a === b) return 0;
×
UNCOV
1683
                    if (a.chipArea.nativeElement.compareDocumentPosition(b.chipArea.nativeElement) & 2) {
×
1684
                        // b comes before a
UNCOV
1685
                        return 1;
×
1686
                    }
UNCOV
1687
                    return -1;
×
1688
                });
UNCOV
1689
                const chipElement = sortedChips[this._lastFocusedChipIndex]?.nativeElement;
×
UNCOV
1690
                if (chipElement) {
×
UNCOV
1691
                    chipElement.focus();
×
1692
                }
UNCOV
1693
                this._lastFocusedChipIndex = -1;
×
UNCOV
1694
                this._focusDelay = DEFAULT_CHIP_FOCUS_DELAY;
×
1695
            }
1696
        }, this._focusDelay);
1697
    }
1698

1699
    private init() {
UNCOV
1700
        this.cancelOperandAdd();
×
UNCOV
1701
        this.cancelOperandEdit();
×
1702

1703
        // Ignore values of certain properties for the comparison
UNCOV
1704
        const propsToIgnore = ['parent', 'hovered', 'ignoreCase', 'inEditMode', 'inAddMode'];
×
UNCOV
1705
        const propsReplacer = function replacer(key, value) {
×
UNCOV
1706
            if (propsToIgnore.indexOf(key) >= 0) {
×
UNCOV
1707
                return undefined;
×
1708
            } else {
UNCOV
1709
                return value;
×
1710
            }
1711
        };
1712

1713
        // Skip root being recreated if the same
UNCOV
1714
        const newRootGroup = this.createExpressionGroupItem(this.expressionTree);
×
UNCOV
1715
        if (JSON.stringify(this.rootGroup, propsReplacer) !== JSON.stringify(newRootGroup, propsReplacer)) {
×
UNCOV
1716
            this.rootGroup = this.createExpressionGroupItem(this.expressionTree);
×
UNCOV
1717
            this.currentGroup = this.rootGroup;
×
1718
        }
1719

UNCOV
1720
        if (this.rootGroup?.children?.length == 0) {
×
UNCOV
1721
            this.rootGroup = null;
×
UNCOV
1722
            this.currentGroup = null;
×
1723
        }
1724
    }
1725

1726
    /** rootGroup is recreated after clicking Apply, which sets new expressionTree and calls init()*/
UNCOV
1727
    protected trackExpressionItem = trackByIdentity;
×
1728
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc