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

IgniteUI / igniteui-angular / 20960087204

13 Jan 2026 02:19PM UTC coverage: 12.713% (-78.8%) from 91.5%
20960087204

Pull #16746

github

web-flow
Merge 9afce6e5d into a967f087e
Pull Request #16746: fix(csv): export summaries - master

1008 of 16803 branches covered (6.0%)

19 of 23 new or added lines in 2 files covered. (82.61%)

24693 existing lines in 336 files now uncovered.

3985 of 31345 relevant lines covered (12.71%)

2.49 hits per line

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

1.1
/projects/igniteui-angular/query-builder/src/query-builder/query-builder-tree.component.ts
1
import { AfterViewInit, EventEmitter, LOCALE_ID, Output, TemplateRef, inject } from '@angular/core';
2
import { NgTemplateOutlet, NgClass } from '@angular/common';
3

4
import {
5
    Component, Input, ViewChild, ChangeDetectorRef, ViewChildren, QueryList, ElementRef, OnDestroy, HostBinding
6
} from '@angular/core';
7
import { FormsModule } from '@angular/forms';
8
import { Subject } from 'rxjs';
9
import { IgxChipComponent } from 'igniteui-angular/chips';
10
import {
11
    IQueryBuilderResourceStrings,
12
    QueryBuilderResourceStringsEN,
13
    PlatformUtil,
14
    trackByIdentity,
15
    GridColumnDataType,
16
    DataUtil,
17
    IgxBooleanFilteringOperand,
18
    IgxDateFilteringOperand,
19
    IgxDateTimeFilteringOperand,
20
    IgxNumberFilteringOperand,
21
    IgxStringFilteringOperand,
22
    IgxTimeFilteringOperand,
23
    FilteringLogic,
24
    IFilteringExpression,
25
    FilteringExpressionsTree,
26
    IExpressionTree,
27
    IFilteringExpressionsTree,
28
    FieldType,
29
    EntityType,
30
    HorizontalAlignment,
31
    OverlaySettings,
32
    VerticalAlignment,
33
    AbsoluteScrollStrategy,
34
    AutoPositionStrategy,
35
    CloseScrollStrategy,
36
    ConnectedPositioningStrategy,
37
    IgxPickerToggleComponent,
38
    IgxPickerClearComponent,
39
    getCurrentResourceStrings,
40
    DEFAULT_LOCALE,
41
    onResourceChangeHandle,
42
    IgxDateFormatterPipe,
43
    isTree,
44
    IgxOverlayOutletDirective
45
} from 'igniteui-angular/core';
46
import { IgxDatePickerComponent } from 'igniteui-angular/date-picker';
47

48
import {
49
    IgxButtonDirective,
50
    IgxDateTimeEditorDirective,
51
    IgxIconButtonDirective,
52
    IgxTooltipDirective,
53
    IgxTooltipTargetDirective,
54
    IgxDragIgnoreDirective,
55
    IgxDropDirective
56
} from 'igniteui-angular/directives';
57
import { IgxSelectComponent } from 'igniteui-angular/select';
58
import { IgxTimePickerComponent } from 'igniteui-angular/time-picker';
59
import { IgxInputGroupComponent, IgxInputDirective, IgxPrefixDirective } from 'igniteui-angular/input-group';
60
import { IgxSelectItemComponent } from 'igniteui-angular/select';
61
import { IgxIconComponent } from 'igniteui-angular/icon';
62
import { IComboSelectionChangingEventArgs, IgxComboComponent, IgxComboHeaderDirective } from 'igniteui-angular/combo';
63
import { IgxCheckboxComponent, IChangeCheckboxEventArgs } from 'igniteui-angular/checkbox';
64
import { IgxDialogComponent } from 'igniteui-angular/dialog';
65
import {
66
    ISelectionEventArgs,
67
    IgxDropDownComponent,
68
    IgxDropDownItemComponent,
69
    IgxDropDownItemNavigationDirective
70
} from 'igniteui-angular/drop-down';
71
import { IgxQueryBuilderSearchValueTemplateDirective } from './query-builder.directives';
72
import { IgxQueryBuilderComponent } from './query-builder.component';
73
import { IgxQueryBuilderDragService } from './query-builder-drag.service';
74
import { ExpressionGroupItem, ExpressionItem, ExpressionOperandItem, IgxFieldFormatterPipe } from './query-builder.common';
75
import { getCurrentI18n, IResourceChangeEventArgs } from 'igniteui-i18n-core';
76

77
const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate';
3✔
78
const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime';
3✔
79
const DEFAULT_PIPE_DATE_TIME_FORMAT = 'medium';
3✔
80
const DEFAULT_PIPE_DIGITS_INFO = '1.0-3';
3✔
81
const DEFAULT_CHIP_FOCUS_DELAY = 50;
3✔
82

83
/** @hidden */
84
@Component({
85
    selector: 'igx-query-builder-tree',
86
    templateUrl: './query-builder-tree.component.html',
87
    host: { 'class': 'igx-query-builder-tree' },
88
    imports: [
89
        IgxDateFormatterPipe,
90
        FormsModule,
91
        IgxButtonDirective,
92
        IgxCheckboxComponent,
93
        IgxChipComponent,
94
        IgxComboComponent,
95
        IgxComboHeaderDirective,
96
        IgxDatePickerComponent,
97
        IgxDateTimeEditorDirective,
98
        IgxDialogComponent,
99
        IgxDragIgnoreDirective,
100
        IgxDropDirective,
101
        IgxDropDownComponent,
102
        IgxDropDownItemComponent,
103
        IgxDropDownItemNavigationDirective,
104
        IgxFieldFormatterPipe,
105
        IgxIconButtonDirective,
106
        IgxIconComponent,
107
        IgxInputDirective,
108
        IgxInputGroupComponent,
109
        IgxOverlayOutletDirective,
110
        IgxPickerClearComponent,
111
        IgxPickerToggleComponent,
112
        IgxPrefixDirective,
113
        IgxSelectComponent,
114
        IgxSelectItemComponent,
115
        IgxTimePickerComponent,
116
        IgxTooltipDirective,
117
        IgxTooltipTargetDirective,
118
        NgClass,
119
        NgTemplateOutlet
120
    ],
121
    providers: [
122
        IgxQueryBuilderDragService
123
    ],
124
})
125
export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy {
3✔
UNCOV
126
    public cdr = inject(ChangeDetectorRef);
×
UNCOV
127
    public dragService = inject(IgxQueryBuilderDragService);
×
UNCOV
128
    protected platform = inject(PlatformUtil);
×
UNCOV
129
    private elRef = inject(ElementRef);
×
UNCOV
130
    protected _localeId = inject(LOCALE_ID);
×
131

132
    /**
133
     * @hidden @internal
134
     */
135
    public _expressionTree: IExpressionTree;
136

137
    /**
138
     * @hidden @internal
139
     */
140
    public _expressionTreeCopy: IExpressionTree;
141

142
    /**
143
     * @hidden @internal
144
     */
145
    @HostBinding('class') public get getClass() {
UNCOV
146
        return `igx-query-builder-tree--level-${this.level}`;
×
147
    }
148

149
    /**
150
     * Sets/gets the entities.
151
     */
152
    @Input()
153
    public entities: EntityType[];
154

155
    /**
156
     * Sets/gets the parent query builder component.
157
     */
158
    @Input()
159
    public queryBuilder: IgxQueryBuilderComponent;
160

161
    /**
162
     * Sets/gets the search value template.
163
     */
164
    @Input()
UNCOV
165
    public searchValueTemplate: TemplateRef<IgxQueryBuilderSearchValueTemplateDirective> = null;
×
166

167
    /**
168
    * Returns the parent expression operand.
169
    */
170
    @Input()
171
    public get parentExpression(): ExpressionOperandItem {
UNCOV
172
        return this._parentExpression;
×
173
    }
174

175
    /**
176
     * Sets the parent expression operand.
177
     */
178
    public set parentExpression(value: ExpressionOperandItem) {
UNCOV
179
        this._parentExpression = value;
×
180
    }
181

182
    /**
183
    * Returns the fields.
184
    */
185
    public get fields(): FieldType[] {
UNCOV
186
        if (!this._fields && this.isAdvancedFiltering()) {
×
UNCOV
187
            this._fields = this.entities[0].fields;
×
188
        }
189

UNCOV
190
        return this._fields;
×
191
    }
192

193
    /**
194
     * Sets the fields.
195
     */
196
    @Input()
197
    public set fields(fields: FieldType[]) {
UNCOV
198
        this._fields = fields;
×
199

UNCOV
200
        this._fields = this._fields?.map(f => ({...f, filters: this.getFilters(f), pipeArgs: this.getPipeArgs(f) }));
×
201

UNCOV
202
        if (!this._fields && this.isAdvancedFiltering()) {
×
UNCOV
203
            this._fields = this.entities[0].fields;
×
204
        }
205
    }
206

207
    /**
208
    * Returns the expression tree.
209
    */
210
    public get expressionTree(): IExpressionTree {
UNCOV
211
        return this._expressionTree;
×
212
    }
213

214
    /**
215
     * Sets the expression tree.
216
     */
217
    @Input()
218
    public set expressionTree(expressionTree: IExpressionTree) {
UNCOV
219
        this._expressionTree = expressionTree;
×
UNCOV
220
        if (!expressionTree) {
×
UNCOV
221
            this._selectedEntity = this.isAdvancedFiltering() && this.entities.length === 1 ? this.entities[0] : null;
×
UNCOV
222
            this._selectedReturnFields = this._selectedEntity ? this._selectedEntity.fields?.map(f => f.field) : [];
×
223
        }
224

UNCOV
225
        if (!this._preventInit) {
×
UNCOV
226
            this.init();
×
227
        }
228
    }
229

230
    /**
231
     * Gets the `locale` of the query builder.
232
     * If not set, defaults to application's locale.
233
     */
234
    @Input()
235
    public get locale(): string {
UNCOV
236
        return this._locale || this._defaultLocale;
×
237
    }
238

239
    /**
240
     * Sets the `locale` of the query builder.
241
     * Expects a valid BCP 47 language tag.
242
     */
243
    public set locale(value: string) {
UNCOV
244
        this._locale = value;
×
UNCOV
245
        this._defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN, false, this._locale);
×
246
    }
247

248
    /**
249
     * Sets the resource strings.
250
     * By default it uses EN resources.
251
     */
252
    @Input()
253
    public set resourceStrings(value: IQueryBuilderResourceStrings) {
UNCOV
254
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
×
255
    }
256

257
    /**
258
     * Returns the resource strings.
259
     */
260
    public get resourceStrings(): IQueryBuilderResourceStrings {
UNCOV
261
        return this._resourceStrings || this._defaultResourceStrings;
×
262
    }
263

264
    /**
265
     * Gets/sets the expected return field.
266
     */
UNCOV
267
    @Input() public expectedReturnField: string = null;
×
268

269
    /**
270
     * Event fired as the expression tree is changed.
271
     */
272
    @Output()
UNCOV
273
    public expressionTreeChange = new EventEmitter<IExpressionTree>();
×
274

275
    /**
276
     * Event fired if a nested query builder tree is being edited.
277
     */
278
    @Output()
UNCOV
279
    public inEditModeChange = new EventEmitter<ExpressionOperandItem>();
×
280

281
    @ViewChild('entitySelect', { read: IgxSelectComponent })
282
    protected entitySelect: IgxSelectComponent;
283

284
    @ViewChild('editingInputs', { read: ElementRef })
285
    private editingInputs: ElementRef;
286

287
    @ViewChild('returnFieldsCombo', { read: IgxComboComponent })
288
    private returnFieldsCombo: IgxComboComponent;
289

290
    @ViewChild('returnFieldSelect', { read: IgxSelectComponent })
291
    protected returnFieldSelect: IgxSelectComponent;
292

293
    @ViewChild('fieldSelect', { read: IgxSelectComponent })
294
    private fieldSelect: IgxSelectComponent;
295

296
    @ViewChild('conditionSelect', { read: IgxSelectComponent })
297
    private conditionSelect: IgxSelectComponent;
298

299
    @ViewChild('searchValueInput', { read: ElementRef })
300
    private searchValueInput: ElementRef;
301

302
    @ViewChild('picker')
303
    private picker: IgxDatePickerComponent | IgxTimePickerComponent;
304

305
    @ViewChild('addRootAndGroupButton', { read: ElementRef })
306
    private addRootAndGroupButton: ElementRef;
307

308
    @ViewChild('addConditionButton', { read: ElementRef })
309
    private addConditionButton: ElementRef;
310

311
    @ViewChild('entityChangeDialog', { read: IgxDialogComponent })
312
    private entityChangeDialog: IgxDialogComponent;
313

314
    @ViewChild('addOptionsDropDown', { read: IgxDropDownComponent })
315
    private addExpressionItemDropDown: IgxDropDownComponent;
316

317
    @ViewChild('groupContextMenuDropDown', { read: IgxDropDownComponent })
318
    private groupContextMenuDropDown: IgxDropDownComponent;
319

320
    /**
321
     * @hidden @internal
322
     */
323
    @ViewChildren(IgxChipComponent, { read: IgxChipComponent })
324
    public expressionsChips: QueryList<IgxChipComponent>;
325

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

UNCOV
335
        this._editingInputsContainer = value;
×
336
    }
337

338
    /** @hidden */
339
    protected get editingInputsContainer(): ElementRef {
340
        return this._editingInputsContainer;
×
341
    }
342

343
    @ViewChild('currentGroupButtonsContainer', { read: ElementRef })
344
    protected set currentGroupButtonsContainer(value: ElementRef) {
UNCOV
345
        if ((value && !this._currentGroupButtonsContainer) ||
×
346
            (value && this._currentGroupButtonsContainer && this._currentGroupButtonsContainer.nativeElement !== value.nativeElement)) {
UNCOV
347
            requestAnimationFrame(() => {
×
UNCOV
348
                this.scrollElementIntoView(value.nativeElement);
×
349
            });
350
        }
351

UNCOV
352
        this._currentGroupButtonsContainer = value;
×
353
    }
354

355
    /** @hidden */
356
    protected get currentGroupButtonsContainer(): ElementRef {
357
        return this._currentGroupButtonsContainer;
×
358
    }
359

360
    @ViewChild('expressionsContainer')
361
    private expressionsContainer: ElementRef;
362

363
    @ViewChild('overlayOutlet', { read: IgxOverlayOutletDirective, static: true })
364
    private overlayOutlet: IgxOverlayOutletDirective;
365

366
    @ViewChildren(IgxQueryBuilderTreeComponent)
367
    private innerQueries: QueryList<IgxQueryBuilderTreeComponent>;
368

369
    /**
370
     * @hidden @internal
371
     */
372
    public innerQueryNewExpressionTree: IExpressionTree;
373

374
    /**
375
     * @hidden @internal
376
     */
377
    public rootGroup: ExpressionGroupItem;
378

379
    /**
380
     * @hidden @internal
381
     */
UNCOV
382
    public selectedExpressions: ExpressionOperandItem[] = [];
×
383

384
    /**
385
     * @hidden @internal
386
     */
387
    public currentGroup: ExpressionGroupItem;
388

389
    /**
390
     * @hidden @internal
391
     */
392
    public contextualGroup: ExpressionGroupItem;
393

394
    /**
395
     * @hidden @internal
396
     */
397
    public filteringLogics;
398

399
    /**
400
     * @hidden @internal
401
     */
402
    public selectedCondition: string;
403

404
    /**
405
     * @hidden @internal
406
     */
UNCOV
407
    public searchValue: { value: any } = { value: null };
×
408

409
    /**
410
     * @hidden @internal
411
     */
412
    public pickerOutlet: IgxOverlayOutletDirective | ElementRef;
413

414
    /**
415
     * @hidden @internal
416
     */
417
    public prevFocusedExpression: ExpressionOperandItem;
418

419
    /**
420
     * @hidden @internal
421
     */
UNCOV
422
    public initialOperator = 0;
×
423

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

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

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

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

460
    /**
461
     * @hidden @internal
462
     */
UNCOV
463
    public addExpressionDropDownOverlaySettings: OverlaySettings = {
×
464
        scrollStrategy: new AbsoluteScrollStrategy(),
465
        modal: false,
466
        closeOnOutsideClick: true
467
    };
468

469
    /**
470
     * @hidden @internal
471
     */
UNCOV
472
    public groupContextMenuDropDownOverlaySettings: OverlaySettings = {
×
473
        scrollStrategy: new AbsoluteScrollStrategy(),
474
        modal: false,
475
        closeOnOutsideClick: true
476
    };
477

UNCOV
478
    private destroy$ = new Subject<any>();
×
479
    private _timeoutId: any;
480
    private _lastFocusedChipIndex: number;
UNCOV
481
    private _focusDelay = DEFAULT_CHIP_FOCUS_DELAY;
×
482
    private _parentExpression: ExpressionOperandItem;
483
    private _selectedEntity: EntityType;
484
    private _selectedReturnFields: string | string[];
485
    private _selectedField: FieldType;
486
    private _editingInputsContainer: ElementRef;
487
    private _currentGroupButtonsContainer: ElementRef;
488
    private _addModeExpression: ExpressionOperandItem;
489
    private _editedExpression: ExpressionOperandItem;
UNCOV
490
    private _preventInit = false;
×
491
    private _prevFocusedContainer: ElementRef;
UNCOV
492
    private _expandedExpressions: IFilteringExpression[] = [];
×
493
    private _fields: FieldType[];
494
    private _locale;
495
    private _defaultLocale;
496
    private _entityNewValue: EntityType;
UNCOV
497
    private _resourceStrings = null;
×
UNCOV
498
    private _defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN);
×
499

500
    /**
501
     * Returns if the select entity dropdown at the root level is disabled after the initial selection.
502
     */
503
    public get disableEntityChange(): boolean {
504

UNCOV
505
        return !this.parentExpression && this.selectedEntity ? this.queryBuilder.disableEntityChange : false;
×
506
    }
507

508
    /**
509
     * Returns if the fields combo at the root level is disabled.
510
     */
511
    public get disableReturnFieldsChange(): boolean {
512

UNCOV
513
        return !this.selectedEntity || this.queryBuilder.disableReturnFieldsChange;
×
514
    }
515

516
    /**
517
     * Returns the current level.
518
     */
519
    public get level(): number {
UNCOV
520
        let parent = this.elRef.nativeElement.parentElement;
×
UNCOV
521
        let _level = 0;
×
UNCOV
522
        while (parent) {
×
UNCOV
523
            if (parent.localName === 'igx-query-builder-tree') {
×
UNCOV
524
                _level++;
×
525
            }
UNCOV
526
            parent = parent.parentElement;
×
527
        }
UNCOV
528
        return _level;
×
529
    }
530

UNCOV
531
    private _positionSettings = {
×
532
        horizontalStartPoint: HorizontalAlignment.Right,
533
        verticalStartPoint: VerticalAlignment.Top
534
    };
535

UNCOV
536
    private _overlaySettings: OverlaySettings = {
×
537
        closeOnOutsideClick: false,
538
        modal: false,
539
        positionStrategy: new ConnectedPositioningStrategy(this._positionSettings),
540
        scrollStrategy: new CloseScrollStrategy()
541
    };
542

543
    /** @hidden */
544
    protected isAdvancedFiltering(): boolean {
UNCOV
545
        return (this.entities?.length === 1 && !this.entities[0]?.name) ||
×
UNCOV
546
            this.entities?.find(e => e.childEntities?.length > 0) !== undefined ||
×
547
            (this.entities?.length > 0 && this.queryBuilder?.entities?.length > 0 && this.entities !== this.queryBuilder?.entities);
548
    }
549

550
    /** @hidden */
551
    protected isHierarchicalNestedQuery(): boolean {
UNCOV
552
        return this.queryBuilder.entities !== this.entities
×
553
    }
554

555
    /** @hidden */
556
    protected isSearchValueInputDisabled(): boolean {
UNCOV
557
        return !this.selectedField ||
×
558
            !this.selectedCondition ||
559
            (this.selectedField &&
560
                (this.selectedField.filters.condition(this.selectedCondition).isUnary ||
561
                    this.selectedField.filters.condition(this.selectedCondition).isNestedQuery));
562
    }
563

564
    constructor() {
UNCOV
565
        this.initLocale();
×
UNCOV
566
        this.dragService.register(this, this.elRef);
×
567
    }
568

569
    /**
570
     * @hidden @internal
571
     */
572
    public ngAfterViewInit(): void {
UNCOV
573
        this._overlaySettings.outlet = this.overlayOutlet;
×
UNCOV
574
        this.entitySelectOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
575
        this.fieldSelectOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
576
        this.conditionSelectOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
577
        this.returnFieldSelectOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
578
        this.addExpressionDropDownOverlaySettings.outlet = this.overlayOutlet;
×
UNCOV
579
        this.groupContextMenuDropDownOverlaySettings.outlet = this.overlayOutlet;
×
580

UNCOV
581
        if (this.isAdvancedFiltering() && this.entities?.length === 1) {
×
UNCOV
582
            this.selectedEntity = this.entities[0].name;
×
UNCOV
583
            if (this._selectedEntity.fields.find(f => f.field === this.expectedReturnField)) {
×
UNCOV
584
                this._selectedReturnFields = [this.expectedReturnField];
×
585
            }
586
        }
587

588
        // Trigger additional change detection cycle
UNCOV
589
        this.cdr.detectChanges();
×
590
    }
591

592
    /**
593
     * @hidden @internal
594
     */
595
    public ngOnDestroy(): void {
UNCOV
596
        this.destroy$.next(true);
×
UNCOV
597
        this.destroy$.complete();
×
598
    }
599

600
    /**
601
     * @hidden @internal
602
     */
603
    public set selectedEntity(value: string) {
UNCOV
604
        this._selectedEntity = this.entities?.find(el => el.name === value);
×
605
    }
606

607
    /**
608
     * @hidden @internal
609
     */
610
    public get selectedEntity(): EntityType {
UNCOV
611
        return this._selectedEntity;
×
612
    }
613

614
    /**
615
     * @hidden @internal
616
     */
617
    public onEntitySelectChanging(event: ISelectionEventArgs) {
UNCOV
618
        event.cancel = true;
×
UNCOV
619
        this._entityNewValue = event.newSelection.value;
×
UNCOV
620
        if (event.oldSelection.value && this.queryBuilder.showEntityChangeDialog) {
×
UNCOV
621
            this.entityChangeDialog.open();
×
622
        } else {
UNCOV
623
            this.onEntityChangeConfirm();
×
624
        }
625
    }
626

627
    /**
628
     * @hidden
629
     */
630
    public onShowEntityChangeDialogChange(eventArgs: IChangeCheckboxEventArgs) {
UNCOV
631
        this.queryBuilder.showEntityChangeDialog = !eventArgs.checked;
×
632
    }
633

634
    /**
635
     * @hidden
636
     */
637
    public onEntityChangeCancel() {
UNCOV
638
        this.entityChangeDialog.close();
×
UNCOV
639
        this.entitySelect.close();
×
UNCOV
640
        this._entityNewValue = null;
×
641
    }
642

643
    /**
644
     * @hidden
645
     */
646
    public onEntityChangeConfirm() {
UNCOV
647
        if (this._parentExpression) {
×
UNCOV
648
            this._expressionTree = this.createExpressionTreeFromGroupItem(this.createExpressionGroupItem(this._expressionTree));
×
649
        }
650

UNCOV
651
        this._selectedEntity = this._entityNewValue;
×
UNCOV
652
        if (!this._selectedEntity.fields) {
×
653
            this._selectedEntity.fields = [];
×
654
        }
UNCOV
655
        this.fields = this._entityNewValue ? this._entityNewValue.fields : [];
×
656

UNCOV
657
        if (this._selectedEntity.fields.find(f => f.field === this.expectedReturnField)) {
×
UNCOV
658
            this._selectedReturnFields = [this.expectedReturnField];
×
659
        } else {
UNCOV
660
            this._selectedReturnFields = this.parentExpression ? [] : this._entityNewValue.fields?.map(f => f.field);
×
661
        }
662

UNCOV
663
        if (this._expressionTree) {
×
UNCOV
664
            this._expressionTree.entity = this._entityNewValue.name;
×
665

UNCOV
666
            const returnFields = Array.isArray(this._selectedReturnFields) ? this._selectedReturnFields : [this._selectedReturnFields];
×
UNCOV
667
            this._expressionTree.returnFields = this.fields.length === returnFields.length ? ['*'] : returnFields;
×
668

UNCOV
669
            this._expressionTree.filteringOperands = [];
×
670

UNCOV
671
            this._editedExpression = null;
×
UNCOV
672
            if (!this.parentExpression) {
×
UNCOV
673
                this.expressionTreeChange.emit(this._expressionTree);
×
674
            }
675

UNCOV
676
            this.rootGroup = null;
×
UNCOV
677
            this.currentGroup = this.rootGroup;
×
678
        }
679

UNCOV
680
        this._selectedField = null;
×
UNCOV
681
        this.selectedCondition = null;
×
UNCOV
682
        this.searchValue.value = null;
×
683

UNCOV
684
        this.entityChangeDialog.close();
×
UNCOV
685
        this.entitySelect.close();
×
686

UNCOV
687
        this._entityNewValue = null;
×
UNCOV
688
        this.innerQueryNewExpressionTree = null;
×
689

UNCOV
690
        this.initExpressionTree(this._selectedEntity.name, this.selectedReturnFields);
×
691
    }
692

693
    /**
694
     * @hidden @internal
695
     */
696
    public set selectedReturnFields(value: string[]) {
UNCOV
697
        if (this._selectedReturnFields !== value) {
×
UNCOV
698
            this._selectedReturnFields = value;
×
699

UNCOV
700
            if (this._expressionTree && !this.parentExpression) {
×
UNCOV
701
                this._expressionTree.returnFields = value.length === this.fields.length ? ['*'] : value;
×
UNCOV
702
                this.expressionTreeChange.emit(this._expressionTree);
×
703
            }
704
        }
705
    }
706

707
    /**
708
     * @hidden @internal
709
     */
710
    public get selectedReturnFields(): string[] {
UNCOV
711
        if (typeof this._selectedReturnFields == 'string') {
×
712
            return [this._selectedReturnFields];
×
713
        }
UNCOV
714
        return this._selectedReturnFields;
×
715
    }
716

717
    /**
718
     * @hidden @internal
719
     */
720
    public set selectedField(value: FieldType) {
UNCOV
721
        const oldValue = this._selectedField;
×
722

UNCOV
723
        if (this._selectedField !== value) {
×
UNCOV
724
            this._selectedField = value;
×
UNCOV
725
            if (this._selectedField && !this._selectedField.dataType) {
×
726
                this._selectedField.filters = this.getFilters(this._selectedField);
×
727
            }
728

UNCOV
729
            this.selectDefaultCondition();
×
UNCOV
730
            if (oldValue && this._selectedField && this._selectedField.dataType !== oldValue.dataType) {
×
UNCOV
731
                this.searchValue.value = null;
×
UNCOV
732
                this.cdr.detectChanges();
×
733
            }
734
        }
735
    }
736

737
    /**
738
     * @hidden @internal
739
     */
740
    public get selectedField(): FieldType {
UNCOV
741
        return this._selectedField;
×
742
    }
743

744
    /**
745
     * @hidden @internal
746
     *
747
     * used by the grid
748
     */
749
    public setPickerOutlet(outlet?: IgxOverlayOutletDirective | ElementRef) {
UNCOV
750
        this.pickerOutlet = outlet;
×
751
    }
752

753
    /**
754
     * @hidden @internal
755
     *
756
     * used by the grid
757
     */
758
    public get isContextMenuVisible(): boolean {
759
        return !this.groupContextMenuDropDown.collapsed;
×
760
    }
761

762
    /**
763
     * @hidden @internal
764
     */
765
    public get hasEditedExpression(): boolean {
UNCOV
766
        return this._editedExpression !== undefined && this._editedExpression !== null;
×
767
    }
768

769
    /**
770
     * @hidden @internal
771
     */
772
    public addCondition(parent: ExpressionGroupItem, afterExpression?: ExpressionOperandItem, isUIInteraction?: boolean) {
UNCOV
773
        this.cancelOperandAdd();
×
774

UNCOV
775
        const operandItem = new ExpressionOperandItem({
×
776
            fieldName: null,
777
            condition: null,
778
            conditionName: null,
779
            ignoreCase: true,
780
            searchVal: null
781
        }, parent);
782

UNCOV
783
        const groupItem = new ExpressionGroupItem(this.getOperator(null) ?? FilteringLogic.And, parent);
×
UNCOV
784
        this.contextualGroup = groupItem;
×
UNCOV
785
        this.initialOperator = null;
×
786

UNCOV
787
        this._lastFocusedChipIndex = this._lastFocusedChipIndex === undefined ? -1 : this._lastFocusedChipIndex;
×
788

UNCOV
789
        if (parent) {
×
UNCOV
790
            if (afterExpression) {
×
UNCOV
791
                const index = parent.children.indexOf(afterExpression);
×
UNCOV
792
                parent.children.splice(index + 1, 0, operandItem);
×
793
            } else {
UNCOV
794
                parent.children.push(operandItem);
×
795
            }
UNCOV
796
            this._lastFocusedChipIndex++;
×
797
        } else {
UNCOV
798
            this.rootGroup = groupItem;
×
UNCOV
799
            operandItem.parent = groupItem;
×
UNCOV
800
            this.rootGroup.children.push(operandItem);
×
UNCOV
801
            this._lastFocusedChipIndex = 0;
×
802
        }
803

UNCOV
804
        this._focusDelay = 250;
×
805

UNCOV
806
        if (isUIInteraction && !afterExpression) {
×
UNCOV
807
            this._lastFocusedChipIndex = this.expressionsChips.length;
×
UNCOV
808
            this._focusDelay = DEFAULT_CHIP_FOCUS_DELAY;
×
809
        }
810

UNCOV
811
        this.enterExpressionEdit(operandItem);
×
812
    }
813

814
    /**
815
     * @hidden @internal
816
     */
817
    public addReverseGroup(parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) {
UNCOV
818
        parent = parent ?? this.rootGroup;
×
819

UNCOV
820
        if (parent.operator === FilteringLogic.And) {
×
UNCOV
821
            this.addGroup(FilteringLogic.Or, parent, afterExpression);
×
822
        } else {
UNCOV
823
            this.addGroup(FilteringLogic.And, parent, afterExpression);
×
824
        }
825
    }
826

827
    /**
828
     * @hidden @internal
829
     */
830
    public endGroup(groupItem: ExpressionGroupItem) {
831
        this.currentGroup = groupItem.parent;
×
832
    }
833

834
    /**
835
     * @hidden @internal
836
     */
837
    public commitExpression() {
UNCOV
838
        this.commitOperandEdit();
×
UNCOV
839
        this.focusEditedExpressionChip();
×
840
    }
841

842
    /**
843
     * @hidden @internal
844
     */
845
    public discardExpression(expressionItem?: ExpressionOperandItem) {
UNCOV
846
        this.cancelOperandEdit();
×
UNCOV
847
        if (expressionItem && expressionItem.expression.fieldName) {
×
UNCOV
848
            this.focusEditedExpressionChip();
×
849
        }
850
    }
851

852
    /**
853
     * @hidden @internal
854
     */
855
    public commitOperandEdit() {
UNCOV
856
        const actualSearchValue = this.searchValue.value;
×
UNCOV
857
        if (this._editedExpression) {
×
UNCOV
858
            this._editedExpression.expression.fieldName = this.selectedField.field;
×
UNCOV
859
            this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition);
×
UNCOV
860
            this._editedExpression.expression.conditionName = this.selectedCondition;
×
UNCOV
861
            this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, actualSearchValue) || actualSearchValue;
×
UNCOV
862
            this._editedExpression.fieldLabel = this.selectedField.label
×
863
                ? this.selectedField.label
864
                : this.selectedField.header
×
865
                    ? this.selectedField.header
866
                    : this.selectedField.field;
867

UNCOV
868
            const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]
×
UNCOV
869
            if (innerQuery && this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery) {
×
UNCOV
870
                innerQuery.exitEditAddMode();
×
UNCOV
871
                this._editedExpression.expression.searchTree = this.getExpressionTreeCopy(innerQuery.expressionTree);
×
UNCOV
872
                const returnFields = innerQuery.selectedReturnFields.length > 0 ?
×
873
                                        innerQuery.selectedReturnFields :
874
                                        [innerQuery.fields[0].field];
UNCOV
875
                this._editedExpression.expression.searchTree.returnFields = returnFields;
×
876
            } else {
UNCOV
877
                this._editedExpression.expression.searchTree = null;
×
878
            }
UNCOV
879
            this.innerQueryNewExpressionTree = null;
×
880

UNCOV
881
            if (this.selectedField.filters.condition(this.selectedCondition)?.isUnary || this.selectedField.filters.condition(this.selectedCondition)?.isNestedQuery) {
×
UNCOV
882
                this._editedExpression.expression.searchVal = null;
×
883
            }
884

UNCOV
885
            this._editedExpression.inEditMode = false;
×
UNCOV
886
            this._editedExpression = null;
×
887
        }
888

UNCOV
889
        if (this.selectedReturnFields.length === 0) {
×
890
            this.selectedReturnFields = this.fields.map(f => f.field);
×
891
        }
892

UNCOV
893
        this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup, this.selectedEntity?.name, this.selectedReturnFields);
×
UNCOV
894
        if (!this.parentExpression) {
×
UNCOV
895
            this.expressionTreeChange.emit(this._expressionTree);
×
896
        }
897
    }
898

899
    /**
900
     * @hidden @internal
901
     */
902
    public cancelOperandAdd() {
UNCOV
903
        if (this._addModeExpression) {
×
904
            this._addModeExpression.inAddMode = false;
×
905
            this._addModeExpression = null;
×
906
        }
907
    }
908

909
    /**
910
     * @hidden @internal
911
     */
UNCOV
912
    public deleteItem = (expressionItem: ExpressionItem, skipEmit: boolean = false) => {
×
UNCOV
913
        if (!expressionItem.parent) {
×
UNCOV
914
            this.rootGroup = null;
×
UNCOV
915
            this.currentGroup = null;
×
916
            //this._expressionTree = null;
UNCOV
917
            return;
×
918
        }
919

UNCOV
920
        if (expressionItem === this.currentGroup) {
×
921
            this.currentGroup = this.currentGroup.parent;
×
922
        }
923

UNCOV
924
        const children = expressionItem.parent.children;
×
UNCOV
925
        const index = children.indexOf(expressionItem);
×
UNCOV
926
        children.splice(index, 1);
×
UNCOV
927
        const entity = this.expressionTree ? this.expressionTree.entity : null;
×
UNCOV
928
        const returnFields = this.expressionTree ? this.expressionTree.returnFields : null;
×
UNCOV
929
        this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup, entity, returnFields); // TODO: don't recreate if not necessary
×
930

UNCOV
931
        if (!children.length) {
×
UNCOV
932
            this.deleteItem(expressionItem.parent, true);
×
933
        }
934

UNCOV
935
        if (!this.parentExpression && !skipEmit) {
×
UNCOV
936
            this.expressionTreeChange.emit(this._expressionTree);
×
937
        }
938
    }
939

940
    /**
941
     * @hidden @internal
942
     */
943
    public cancelOperandEdit() {
UNCOV
944
        if (this.innerQueries) {
×
UNCOV
945
            const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0];
×
UNCOV
946
            if (innerQuery) {
×
UNCOV
947
                if (innerQuery._editedExpression) {
×
UNCOV
948
                    innerQuery.cancelOperandEdit();
×
949
                }
950

UNCOV
951
                innerQuery.expressionTree = this.getExpressionTreeCopy(this._editedExpression.expression.searchTree);
×
UNCOV
952
                this.innerQueryNewExpressionTree = null;
×
953
            }
954
        }
955

UNCOV
956
        if (this._editedExpression) {
×
UNCOV
957
            this._editedExpression.inEditMode = false;
×
958

UNCOV
959
            if (!this._editedExpression.expression.fieldName) {
×
UNCOV
960
                this.deleteItem(this._editedExpression);
×
961
            }
962

UNCOV
963
            this._editedExpression = null;
×
964
        }
965

UNCOV
966
        if (!this.expressionTree && this.contextualGroup) {
×
UNCOV
967
            this.initialOperator = this.contextualGroup.operator;
×
968
        }
969
    }
970

971
    /**
972
     * @hidden @internal
973
     */
974
    public operandCanBeCommitted(): boolean {
UNCOV
975
        const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0];
×
UNCOV
976
        return this.selectedField && this.selectedCondition &&
×
977
            (
978
                (
979
                    ((!Array.isArray(this.searchValue.value) && !!this.searchValue.value) || (Array.isArray(this.searchValue.value) && this.searchValue.value.length !== 0)) &&
980
                    !(this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery)
981
                ) ||
982
                (
983
                    this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery && innerQuery && !!innerQuery.expressionTree && innerQuery.selectedReturnFields?.length > 0
984
                ) ||
985
                this.selectedField.filters.condition(this.selectedCondition)?.isUnary
986
            );
987
    }
988

989
    /**
990
     * @hidden @internal
991
     */
992
    public canCommitCurrentState(): boolean {
UNCOV
993
        const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0];
×
UNCOV
994
        if (innerQuery) {
×
UNCOV
995
            return this.selectedReturnFields?.length > 0 && innerQuery.canCommitCurrentState();
×
996
        } else {
UNCOV
997
            return this.selectedReturnFields?.length > 0 &&
×
998
                (
999
                    (!this._editedExpression) || // no edited expr
1000
                    (this._editedExpression && !this.selectedField) || // empty edited expr
1001
                    (this._editedExpression && this.operandCanBeCommitted() === true) // valid edited expr
1002
                );
1003
        }
1004
    }
1005

1006
    /**
1007
     * @hidden @internal
1008
     */
1009
    public commitCurrentState(): void {
UNCOV
1010
        const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0];
×
UNCOV
1011
        if (innerQuery) {
×
UNCOV
1012
            innerQuery.commitCurrentState();
×
1013
        }
1014

UNCOV
1015
        if (this._editedExpression) {
×
UNCOV
1016
            if (this.selectedField) {
×
UNCOV
1017
                this.commitOperandEdit();
×
1018
            } else {
1019
                this.deleteItem(this._editedExpression);
×
1020
                this._editedExpression = null;
×
1021
            }
1022
        }
1023
    }
1024

1025
    /**
1026
     * @hidden @internal
1027
     */
1028
    public exitEditAddMode(shouldPreventInit = false) {
×
UNCOV
1029
        if (!this._editedExpression) {
×
UNCOV
1030
            return;
×
1031
        }
1032

UNCOV
1033
        this.exitOperandEdit();
×
UNCOV
1034
        this.cancelOperandAdd();
×
1035

UNCOV
1036
        if (shouldPreventInit) {
×
UNCOV
1037
            this._preventInit = true;
×
1038
        }
1039
    }
1040

1041
    /**
1042
     * @hidden @internal
1043
     *
1044
     * used by the grid
1045
     */
1046
    public exitOperandEdit() {
UNCOV
1047
        if (!this._editedExpression) {
×
UNCOV
1048
            return;
×
1049
        }
1050

UNCOV
1051
        if (this.operandCanBeCommitted()) {
×
UNCOV
1052
            this.commitOperandEdit();
×
1053
        } else {
UNCOV
1054
            this.cancelOperandEdit();
×
1055
        }
1056
    }
1057

1058
    /**
1059
     * @hidden @internal
1060
     */
1061
    public isExpressionGroup(expression: ExpressionItem): boolean {
UNCOV
1062
        return expression instanceof ExpressionGroupItem;
×
1063
    }
1064

1065
    /**
1066
     * @hidden @internal
1067
     */
1068
    public onExpressionFocus(expressionItem: ExpressionOperandItem) {
UNCOV
1069
        if (this.prevFocusedExpression) {
×
UNCOV
1070
            this.prevFocusedExpression.focused = false;
×
1071
        }
UNCOV
1072
        expressionItem.focused = true;
×
UNCOV
1073
        this.prevFocusedExpression = expressionItem;
×
1074
    }
1075

1076
    /**
1077
     * @hidden @internal
1078
     */
1079
    public onExpressionBlur(event, expressionItem: ExpressionOperandItem) {
UNCOV
1080
        if (this._prevFocusedContainer && this._prevFocusedContainer !== event.target.closest('.igx-filter-tree__expression-item')) {
×
1081
            expressionItem.focused = false;
×
1082
        }
UNCOV
1083
        this._prevFocusedContainer = event.target.closest('.igx-filter-tree__expression-item');
×
1084
    }
1085

1086
    /**
1087
     * @hidden @internal
1088
     */
1089
    public onChipRemove(expressionItem: ExpressionItem) {
UNCOV
1090
        this.exitEditAddMode();
×
UNCOV
1091
        this.deleteItem(expressionItem);
×
1092
    }
1093

1094
    /**
1095
     * @hidden @internal
1096
     */
UNCOV
1097
    public focusChipAfterDrag = (index: number) => {
×
UNCOV
1098
        this._lastFocusedChipIndex = index;
×
UNCOV
1099
        this.focusEditedExpressionChip();
×
1100
    }
1101
    /**
1102
     * @hidden @internal
1103
     */
1104
    public addExpressionBlur() {
1105
        if (this.prevFocusedExpression) {
×
1106
            this.prevFocusedExpression.focused = false;
×
1107
        }
1108
        if (this.addExpressionItemDropDown && !this.addExpressionItemDropDown.collapsed) {
×
1109
            this.addExpressionItemDropDown.close();
×
1110
        }
1111
    }
1112

1113
    /**
1114
     * @hidden @internal
1115
     */
1116
    public onChipClick(expressionItem: ExpressionOperandItem, chip: IgxChipComponent) {
UNCOV
1117
        this.enterExpressionEdit(expressionItem, chip);
×
1118
    }
1119

1120
    /**
1121
     * @hidden @internal
1122
     */
1123
    public enterExpressionEdit(expressionItem: ExpressionOperandItem, chip?: IgxChipComponent) {
UNCOV
1124
        this.exitEditAddMode(true);
×
UNCOV
1125
        this.cdr.detectChanges();
×
UNCOV
1126
        this._lastFocusedChipIndex = chip ? this.expressionsChips.toArray().findIndex(expr => expr === chip) : this._lastFocusedChipIndex;
×
UNCOV
1127
        this.enterEditMode(expressionItem);
×
1128
    }
1129

1130

1131
    /**
1132
     * @hidden @internal
1133
     */
1134
    public clickExpressionAdd(targetButton: HTMLElement, chip: IgxChipComponent) {
UNCOV
1135
        this.exitEditAddMode(true);
×
UNCOV
1136
        this.cdr.detectChanges();
×
UNCOV
1137
        this._lastFocusedChipIndex = this.expressionsChips.toArray().findIndex(expr => expr === chip);
×
UNCOV
1138
        this.openExpressionAddDialog(targetButton);
×
1139
    }
1140

1141
    /**
1142
     * @hidden @internal
1143
     */
1144
    public openExpressionAddDialog(targetButton: HTMLElement) {
UNCOV
1145
        this.addExpressionDropDownOverlaySettings.target = targetButton;
×
UNCOV
1146
        this.addExpressionDropDownOverlaySettings.positionStrategy = new ConnectedPositioningStrategy({
×
1147
            horizontalDirection: HorizontalAlignment.Right,
1148
            horizontalStartPoint: HorizontalAlignment.Left,
1149
            verticalStartPoint: VerticalAlignment.Bottom
1150
        });
1151

UNCOV
1152
        this.addExpressionItemDropDown.open(this.addExpressionDropDownOverlaySettings);
×
1153
    }
1154

1155
    /**
1156
     * @hidden @internal
1157
     */
1158
    public enterExpressionAdd(event: ISelectionEventArgs, expressionItem: ExpressionOperandItem) {
UNCOV
1159
        if (this._addModeExpression) {
×
1160
            this._addModeExpression.inAddMode = false;
×
1161
        }
1162

UNCOV
1163
        if (this.parentExpression) {
×
1164
            this.inEditModeChange.emit(this.parentExpression);
×
1165
        }
1166

UNCOV
1167
        const parent = expressionItem.parent ?? this.rootGroup;
×
UNCOV
1168
        requestAnimationFrame(() => {
×
UNCOV
1169
            if (event.newSelection.value === 'addCondition') {
×
UNCOV
1170
                this.addCondition(parent, expressionItem);
×
1171
            } else if (event.newSelection.value === 'addGroup') {
×
1172
                this.addReverseGroup(parent, expressionItem);
×
1173
            }
UNCOV
1174
            expressionItem.inAddMode = true;
×
UNCOV
1175
            this._addModeExpression = expressionItem;
×
1176
        })
1177
    }
1178

1179
    /**
1180
     * @hidden @internal
1181
     */
1182
    public enterEditMode(expressionItem: ExpressionOperandItem) {
UNCOV
1183
        if (this._editedExpression) {
×
1184
            this._editedExpression.inEditMode = false;
×
1185
        }
1186

UNCOV
1187
        if (this.parentExpression) {
×
UNCOV
1188
            this.inEditModeChange.emit(this.parentExpression);
×
1189
        }
1190

UNCOV
1191
        expressionItem.hovered = false;
×
UNCOV
1192
        this.fields = this.selectedEntity ? this.selectedEntity.fields : null;
×
UNCOV
1193
        this.selectedField =
×
1194
            expressionItem.expression.fieldName ?
×
UNCOV
1195
                this.fields?.find(field => field.field === expressionItem.expression.fieldName)
×
1196
                : null;
UNCOV
1197
        this.selectedCondition =
×
1198
            expressionItem.expression.condition ?
×
1199
                expressionItem.expression.condition.name :
1200
                null;
UNCOV
1201
        this.searchValue.value = expressionItem.expression.searchVal instanceof Set ?
×
1202
            Array.from(expressionItem.expression.searchVal) :
1203
            expressionItem.expression.searchVal;
1204

UNCOV
1205
        expressionItem.inEditMode = true;
×
UNCOV
1206
        this._editedExpression = expressionItem;
×
UNCOV
1207
        this.cdr.detectChanges();
×
1208

UNCOV
1209
        this.entitySelectOverlaySettings.target = this.entitySelect.getEditElement();
×
UNCOV
1210
        this.entitySelectOverlaySettings.excludeFromOutsideClick = [this.entitySelect.getEditElement() as HTMLElement];
×
UNCOV
1211
        this.entitySelectOverlaySettings.positionStrategy = new AutoPositionStrategy();
×
1212

UNCOV
1213
        if (this.returnFieldSelect) {
×
UNCOV
1214
            this.returnFieldSelectOverlaySettings.target = this.returnFieldSelect.getEditElement();
×
UNCOV
1215
            this.returnFieldSelectOverlaySettings.excludeFromOutsideClick = [this.returnFieldSelect.getEditElement() as HTMLElement];
×
UNCOV
1216
            this.returnFieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy();
×
1217
        }
UNCOV
1218
        if (this.fieldSelect) {
×
UNCOV
1219
            this.fieldSelectOverlaySettings.target = this.fieldSelect.getEditElement();
×
UNCOV
1220
            this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.getEditElement() as HTMLElement];
×
UNCOV
1221
            this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy();
×
1222
        }
UNCOV
1223
        if (this.conditionSelect) {
×
UNCOV
1224
            this.conditionSelectOverlaySettings.target = this.conditionSelect.getEditElement();
×
UNCOV
1225
            this.conditionSelectOverlaySettings.excludeFromOutsideClick = [this.conditionSelect.getEditElement() as HTMLElement];
×
UNCOV
1226
            this.conditionSelectOverlaySettings.positionStrategy = new AutoPositionStrategy();
×
1227
        }
1228

UNCOV
1229
        if (!this.selectedField) {
×
UNCOV
1230
            this.fieldSelect.input.nativeElement.focus();
×
UNCOV
1231
        } else if (this.selectedField.filters.condition(this.selectedCondition)?.isUnary) {
×
UNCOV
1232
            this.conditionSelect?.input.nativeElement.focus();
×
1233
        } else {
UNCOV
1234
            const input = this.searchValueInput?.nativeElement || this.picker?.getEditElement();
×
UNCOV
1235
            input?.focus();
×
1236
        }
1237

UNCOV
1238
        (this.editingInputs?.nativeElement.parentElement as HTMLElement)?.scrollIntoView({ block: "nearest", inline: "nearest" });
×
1239
    }
1240

1241
    /**
1242
     * @hidden @internal
1243
     */
1244
    public onConditionSelectChanging(event: ISelectionEventArgs) {
UNCOV
1245
        event.cancel = true;
×
UNCOV
1246
        this.selectedCondition = event.newSelection.value;
×
UNCOV
1247
        this.conditionSelect.close();
×
UNCOV
1248
        this.cdr.detectChanges();
×
1249
    }
1250

1251
    /**
1252
     * @hidden @internal
1253
     */
1254
    public onKeyDown(eventArgs: KeyboardEvent) {
1255
        eventArgs.stopPropagation();
×
1256
    }
1257

1258
    /**
1259
     * @hidden @internal
1260
     */
1261
    public onGroupClick(groupContextMenuDropDown: any, targetButton: HTMLButtonElement, groupItem: ExpressionGroupItem) {
UNCOV
1262
        this.exitEditAddMode();
×
UNCOV
1263
        this.cdr.detectChanges();
×
1264

UNCOV
1265
        this.groupContextMenuDropDown = groupContextMenuDropDown;
×
UNCOV
1266
        this.groupContextMenuDropDownOverlaySettings.target = targetButton;
×
UNCOV
1267
        this.groupContextMenuDropDownOverlaySettings.positionStrategy = new ConnectedPositioningStrategy({
×
1268
            horizontalDirection: HorizontalAlignment.Right,
1269
            horizontalStartPoint: HorizontalAlignment.Left,
1270
            verticalStartPoint: VerticalAlignment.Bottom
1271
        });
1272

UNCOV
1273
        if (groupContextMenuDropDown.collapsed) {
×
UNCOV
1274
            this.contextualGroup = groupItem;
×
UNCOV
1275
            groupContextMenuDropDown.open(this.groupContextMenuDropDownOverlaySettings);
×
1276
        } else {
UNCOV
1277
            groupContextMenuDropDown.close();
×
1278
        }
1279
    }
1280

1281
    /**
1282
     * @hidden @internal
1283
     */
1284
    public getOperator(expressionItem: any) {
1285
        // if (!expressionItem && !this.expressionTree && !this.initialOperator) {
1286
        //     this.initialOperator = 0;
1287
        // }
1288

UNCOV
1289
        const operator = expressionItem ?
×
1290
            expressionItem.operator :
1291
            this.expressionTree ?
×
1292
                this.expressionTree.operator :
1293
                this.initialOperator;
UNCOV
1294
        return operator;
×
1295
    }
1296

1297
    /**
1298
     * @hidden @internal
1299
     */
1300
    public getSwitchGroupText(expressionItem: any) {
UNCOV
1301
        const operator = this.getOperator(expressionItem);
×
UNCOV
1302
        const condition = operator === FilteringLogic.Or ? this.resourceStrings.igx_query_builder_and_label : this.resourceStrings.igx_query_builder_or_label
×
UNCOV
1303
        return this.resourceStrings.igx_query_builder_switch_group.replace('{0}', condition.toUpperCase());
×
1304
    }
1305

1306
    /**
1307
     * @hidden @internal
1308
     */
1309
    public onGroupContextMenuDropDownSelectionChanging(event: ISelectionEventArgs) {
UNCOV
1310
        event.cancel = true;
×
1311

UNCOV
1312
        if (event.newSelection.value === 'switchCondition') {
×
UNCOV
1313
            const newOperator = (!this.expressionTree ? this.initialOperator : (this.contextualGroup ?? this._expressionTree).operator) === 0 ? 1 : 0;
×
UNCOV
1314
            this.selectFilteringLogic(newOperator);
×
UNCOV
1315
        } else if (event.newSelection.value === 'ungroup') {
×
UNCOV
1316
            this.ungroup();
×
1317
        }
1318

UNCOV
1319
        this.groupContextMenuDropDown.close();
×
1320
    }
1321

1322
    /**
1323
     * @hidden @internal
1324
     */
1325
    public ungroup() {
UNCOV
1326
        const selectedGroup = this.contextualGroup;
×
UNCOV
1327
        const parent = selectedGroup.parent;
×
UNCOV
1328
        if (parent) {
×
UNCOV
1329
            const index = parent.children.indexOf(selectedGroup);
×
UNCOV
1330
            parent.children.splice(index, 1, ...selectedGroup.children);
×
1331

UNCOV
1332
            for (const expr of selectedGroup.children) {
×
UNCOV
1333
                expr.parent = parent;
×
1334
            }
1335
        }
UNCOV
1336
        this.commitOperandEdit();
×
1337
    }
1338

1339
    /**
1340
     * @hidden @internal
1341
     */
1342
    public selectFilteringLogic(index: number) {
UNCOV
1343
        if (!this.expressionTree) {
×
1344
            this.initialOperator = index;
×
1345
            return;
×
1346
        }
1347

UNCOV
1348
        if (this.contextualGroup) {
×
UNCOV
1349
            this.contextualGroup.operator = index as FilteringLogic;
×
UNCOV
1350
            this.commitOperandEdit();
×
UNCOV
1351
        } else if (this.expressionTree) {
×
UNCOV
1352
            this._expressionTree.operator = index as FilteringLogic;
×
1353
        }
1354

UNCOV
1355
        this.initialOperator = null;
×
1356
    }
1357

1358
    /**
1359
     * @hidden @internal
1360
     */
1361
    public getConditionFriendlyName(name: string): string {
1362
        // 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.
1363
        // So instead of in/notIn we end up with 'inQuery'/'notInQuery', hence removing the suffix from the friendly name.
UNCOV
1364
        return this.resourceStrings[`igx_query_builder_filter_${name?.replace('Query', '')}`] || name;
×
1365
    }
1366

1367
    /**
1368
     * @hidden @internal
1369
     */
1370
    public isDate(value: any) {
UNCOV
1371
        return value instanceof Date;
×
1372
    }
1373

1374
    /**
1375
     * @hidden @internal
1376
     */
1377
    public invokeClick(eventArgs: KeyboardEvent) {
UNCOV
1378
        if (!this.dragService.dropGhostExpression && this.platform.isActivationKey(eventArgs)) {
×
UNCOV
1379
            eventArgs.preventDefault();
×
UNCOV
1380
            (eventArgs.currentTarget as HTMLElement).click();
×
1381
        }
1382
    }
1383

1384
    /**
1385
     * @hidden @internal
1386
     */
1387
    public openPicker(args: KeyboardEvent) {
1388
        if (this.platform.isActivationKey(args)) {
×
1389
            args.preventDefault();
×
1390
            this.picker.open();
×
1391
        }
1392
    }
1393

1394
    /**
1395
     * @hidden @internal
1396
     */
1397
    public onOutletPointerDown(event) {
1398
        // This prevents closing the select's dropdown when clicking the scroll
UNCOV
1399
        event.preventDefault();
×
1400
    }
1401

1402
    /**
1403
     * @hidden @internal
1404
     */
1405
    public getConditionList(): string[] {
UNCOV
1406
        if (!this.selectedField) return [];
×
1407

UNCOV
1408
        if (!this.selectedField.filters) {
×
1409
            this.selectedField.filters = this.getFilters(this.selectedField);
×
1410
        }
1411

UNCOV
1412
        if ((this.isAdvancedFiltering() && !this.entities[0].childEntities) ||
×
1413
            (this.isHierarchicalNestedQuery() && this.selectedEntity.name && !this.selectedEntity.childEntities)) {
UNCOV
1414
            return this.selectedField.filters.conditionList();
×
1415
        }
1416

UNCOV
1417
        return this.selectedField.filters.extendedConditionList();
×
1418
    }
1419

1420
    /**
1421
     * @hidden @internal
1422
     */
1423
    public getFormatter(field: string) {
UNCOV
1424
        return this.fields?.find(el => el.field === field)?.formatter;
×
1425
    }
1426

1427
    /**
1428
     * @hidden @internal
1429
     */
1430
    public getFormat(field: string) {
UNCOV
1431
        return this.fields?.find(el => el.field === field).pipeArgs.format;
×
1432
    }
1433

1434
    /**
1435
     * @hidden @internal
1436
     *
1437
     * used by the grid
1438
     */
1439
    public setAddButtonFocus() {
UNCOV
1440
        if (this.addRootAndGroupButton) {
×
1441
            this.addRootAndGroupButton.nativeElement.focus();
×
UNCOV
1442
        } else if (this.addConditionButton) {
×
UNCOV
1443
            this.addConditionButton.nativeElement.focus();
×
1444
        }
1445
    }
1446

1447
    /**
1448
     * @hidden @internal
1449
     */
1450
    public context(expression: ExpressionItem, afterExpression?: ExpressionItem) {
UNCOV
1451
        return {
×
1452
            $implicit: expression,
1453
            afterExpression
1454
        };
1455
    }
1456

1457
    public formatReturnFields(innerTree: IFilteringExpressionsTree) {
UNCOV
1458
        const returnFields = innerTree.returnFields;
×
UNCOV
1459
        let text = returnFields.join(', ');
×
UNCOV
1460
        const innerTreeEntity = this.entities?.find(el => el.name === innerTree.entity);
×
UNCOV
1461
        if (returnFields.length === innerTreeEntity?.fields.length) {
×
1462
            text = this.resourceStrings.igx_query_builder_all_fields;
×
1463
        } else {
UNCOV
1464
            text = returnFields.join(', ');
×
UNCOV
1465
            text = text.length > 25 ? text.substring(0, 25) + ' ...' : text;
×
1466
        }
UNCOV
1467
        return text;
×
1468
    }
1469

1470
    public isInEditMode(): boolean {
UNCOV
1471
        return !this.parentExpression || (this.parentExpression && this.parentExpression.inEditMode);
×
1472
    }
1473

1474
    public onInEditModeChanged(expressionItem: ExpressionOperandItem) {
UNCOV
1475
        if (!expressionItem.inEditMode) {
×
1476
            this.enterExpressionEdit(expressionItem);
×
1477
        }
1478
    }
1479

1480
    public getExpressionTreeCopy(expressionTree: IExpressionTree, shouldAssignInnerQueryExprTree?: boolean): IExpressionTree {
UNCOV
1481
        if (!expressionTree) {
×
UNCOV
1482
            return null;
×
1483
        }
1484

UNCOV
1485
        const exprTreeCopy = new FilteringExpressionsTree(expressionTree.operator, expressionTree.fieldName, expressionTree.entity, expressionTree.returnFields);
×
UNCOV
1486
        exprTreeCopy.filteringOperands = [];
×
1487

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

UNCOV
1490
        if (!this.innerQueryNewExpressionTree && shouldAssignInnerQueryExprTree) {
×
UNCOV
1491
            this.innerQueryNewExpressionTree = exprTreeCopy;
×
1492
        }
1493

UNCOV
1494
        return exprTreeCopy;
×
1495
    }
1496

1497
    public onSelectAllClicked() {
UNCOV
1498
        if (
×
1499
            (this._selectedReturnFields.length > 0 && this._selectedReturnFields.length < this._selectedEntity.fields.length) ||
×
1500
            this._selectedReturnFields.length == this._selectedEntity.fields.length
1501
        ) {
UNCOV
1502
            this.returnFieldsCombo.deselectAllItems();
×
1503
        } else {
UNCOV
1504
            this.returnFieldsCombo.selectAllItems();
×
1505
        }
1506
    }
1507

1508
    public onReturnFieldSelectChanging(event: IComboSelectionChangingEventArgs | ISelectionEventArgs) {
UNCOV
1509
        let newSelection = [];
×
UNCOV
1510
        if (Array.isArray(event.newSelection)) {
×
UNCOV
1511
            newSelection = event.newSelection.map(item => item.field)
×
1512
        } else {
UNCOV
1513
            newSelection.push(event.newSelection.value);
×
UNCOV
1514
            this._selectedReturnFields = newSelection;
×
1515
        }
1516

UNCOV
1517
        this.initExpressionTree(this.selectedEntity.name, newSelection);
×
1518
    }
1519

1520
    public initExpressionTree(selectedEntityName: string, selectedReturnFields: string[]) {
UNCOV
1521
        if (!this._expressionTree) {
×
UNCOV
1522
            this._expressionTree = this.createExpressionTreeFromGroupItem(new ExpressionGroupItem(FilteringLogic.And, this.rootGroup), selectedEntityName, selectedReturnFields);
×
1523
        }
1524

UNCOV
1525
        if (!this.parentExpression) {
×
UNCOV
1526
            this.expressionTreeChange.emit(this._expressionTree);
×
1527
        }
1528
    }
1529

1530
    public getSearchValueTemplateContext(defaultSearchValueTemplate): any {
UNCOV
1531
        const ctx = {
×
1532
            $implicit: this.searchValue,
1533
            selectedField: this.selectedField,
1534
            selectedCondition: this.selectedCondition,
1535
            defaultSearchValueTemplate: defaultSearchValueTemplate
1536
        };
UNCOV
1537
        return ctx;
×
1538
    }
1539

1540
    private getPipeArgs(field: FieldType) {
UNCOV
1541
        let pipeArgs = {...field.pipeArgs};
×
UNCOV
1542
        if (!pipeArgs) {
×
1543
            pipeArgs = { digitsInfo: DEFAULT_PIPE_DIGITS_INFO };
×
1544
        }
1545

UNCOV
1546
        if (!pipeArgs.format) {
×
UNCOV
1547
            pipeArgs.format = field.dataType === GridColumnDataType.Time ?
×
1548
                DEFAULT_PIPE_TIME_FORMAT : field.dataType === GridColumnDataType.DateTime ?
×
1549
                    DEFAULT_PIPE_DATE_TIME_FORMAT : DEFAULT_PIPE_DATE_FORMAT;
1550
        }
1551

UNCOV
1552
        return pipeArgs;
×
1553
    }
1554

1555
    private selectDefaultCondition() {
UNCOV
1556
        if (this.selectedField && this.selectedField.filters) {
×
UNCOV
1557
            this.selectedCondition = this.selectedField.filters.conditionList().indexOf('equals') >= 0 ? 'equals' : this.selectedField.filters.conditionList()[0];
×
1558
        }
1559
    }
1560

1561
    private getFilters(field: FieldType) {
UNCOV
1562
        if (!field.filters) {
×
UNCOV
1563
            switch (field.dataType) {
×
1564
                case GridColumnDataType.Boolean:
UNCOV
1565
                    return IgxBooleanFilteringOperand.instance();
×
1566
                case GridColumnDataType.Number:
1567
                case GridColumnDataType.Currency:
1568
                case GridColumnDataType.Percent:
UNCOV
1569
                    return IgxNumberFilteringOperand.instance();
×
1570
                case GridColumnDataType.Date:
UNCOV
1571
                    return IgxDateFilteringOperand.instance();
×
1572
                case GridColumnDataType.Time:
1573
                    return IgxTimeFilteringOperand.instance();
×
1574
                case GridColumnDataType.DateTime:
1575
                    return IgxDateTimeFilteringOperand.instance();
×
1576
                case GridColumnDataType.String:
1577
                default:
UNCOV
1578
                    return IgxStringFilteringOperand.instance();
×
1579
            }
1580
        } else {
UNCOV
1581
            return field.filters;
×
1582
        }
1583
    }
1584

1585

1586
    private addGroup(operator: FilteringLogic, parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) {
UNCOV
1587
        this.cancelOperandAdd();
×
1588

UNCOV
1589
        const groupItem = new ExpressionGroupItem(operator, parent);
×
1590

UNCOV
1591
        if (parent) {
×
UNCOV
1592
            if (afterExpression) {
×
1593
                const index = parent.children.indexOf(afterExpression);
×
1594
                parent.children.splice(index + 1, 0, groupItem);
×
1595
            } else {
UNCOV
1596
                parent.children.push(groupItem);
×
1597
            }
1598
        } else {
1599
            this.rootGroup = groupItem;
×
1600
        }
1601

UNCOV
1602
        this.addCondition(groupItem);
×
UNCOV
1603
        this.currentGroup = groupItem;
×
1604
    }
1605

1606
    private createExpressionGroupItem(expressionTree: IExpressionTree, parent?: ExpressionGroupItem, entityName?: string): ExpressionGroupItem {
1607
        let groupItem: ExpressionGroupItem;
UNCOV
1608
        if (expressionTree) {
×
UNCOV
1609
            groupItem = new ExpressionGroupItem(expressionTree.operator, parent);
×
UNCOV
1610
            if (!expressionTree.filteringOperands) {
×
1611
                return groupItem;
×
1612
            }
1613

UNCOV
1614
            for (let i = 0; i < expressionTree.filteringOperands.length; i++) {
×
UNCOV
1615
                const expr = expressionTree.filteringOperands[i];
×
1616

UNCOV
1617
                if (isTree(expr)) {
×
UNCOV
1618
                    groupItem.children.push(this.createExpressionGroupItem(expr, groupItem, expressionTree.entity));
×
1619
                } else {
UNCOV
1620
                    const filteringExpr = expr as IFilteringExpression;
×
UNCOV
1621
                    const exprCopy: IFilteringExpression = {
×
1622
                        fieldName: filteringExpr.fieldName,
1623
                        condition: filteringExpr.condition,
1624
                        conditionName: filteringExpr.condition?.name || filteringExpr.conditionName,
×
1625
                        searchVal: filteringExpr.searchVal,
1626
                        searchTree: filteringExpr.searchTree,
1627
                        ignoreCase: filteringExpr.ignoreCase
1628
                    };
UNCOV
1629
                    const operandItem = new ExpressionOperandItem(exprCopy, groupItem);
×
UNCOV
1630
                    const field = this.fields?.find(el => el.field === filteringExpr.fieldName);
×
UNCOV
1631
                    operandItem.fieldLabel = field?.label || field?.header || field?.field;
×
UNCOV
1632
                    if (this._expandedExpressions.filter(e => e.searchTree == operandItem.expression.searchTree).length > 0) {
×
1633
                        operandItem.expanded = true;
×
1634
                    }
UNCOV
1635
                    groupItem.children.push(operandItem);
×
1636
                }
1637
            }
1638

1639

UNCOV
1640
            if (expressionTree.entity) {
×
UNCOV
1641
                entityName = expressionTree.entity;
×
1642
            }
UNCOV
1643
            const entity = this.entities?.find(el => el.name === entityName);
×
UNCOV
1644
            if (entity) {
×
UNCOV
1645
                this.fields = entity.fields;
×
1646
            }
1647

UNCOV
1648
            this._selectedEntity = this.entities?.find(el => el.name === entityName);
×
UNCOV
1649
            this._selectedReturnFields =
×
1650
                !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') || expressionTree.returnFields.length === 0
×
UNCOV
1651
                    ? this.fields?.map(f => f.field)
×
UNCOV
1652
                    : this.fields?.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field);
×
1653
        }
UNCOV
1654
        return groupItem;
×
1655
    }
1656

1657
    private createExpressionTreeFromGroupItem(groupItem: ExpressionGroupItem, entity?: string, returnFields?: string[]): FilteringExpressionsTree {
UNCOV
1658
        if (!groupItem) {
×
UNCOV
1659
            return null;
×
1660
        }
1661

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

UNCOV
1664
        for (let i = 0; i < groupItem.children.length; i++) {
×
UNCOV
1665
            const item = groupItem.children[i];
×
1666

UNCOV
1667
            if (item instanceof ExpressionGroupItem) {
×
UNCOV
1668
                const subTree = this.createExpressionTreeFromGroupItem((item as ExpressionGroupItem), entity, returnFields);
×
UNCOV
1669
                expressionTree.filteringOperands.push(subTree);
×
1670
            } else {
UNCOV
1671
                expressionTree.filteringOperands.push((item as ExpressionOperandItem).expression);
×
1672
            }
1673
        }
1674

UNCOV
1675
        return expressionTree;
×
1676
    }
1677

1678
    private scrollElementIntoView(target: HTMLElement) {
UNCOV
1679
        const container = this.expressionsContainer.nativeElement;
×
UNCOV
1680
        const targetOffset = target.offsetTop - container.offsetTop;
×
UNCOV
1681
        const delta = 10;
×
1682

UNCOV
1683
        if (container.scrollTop + delta > targetOffset) {
×
UNCOV
1684
            container.scrollTop = targetOffset - delta;
×
UNCOV
1685
        } else if (container.scrollTop + container.clientHeight < targetOffset + target.offsetHeight + delta) {
×
UNCOV
1686
            container.scrollTop = targetOffset + target.offsetHeight + delta - container.clientHeight;
×
1687
        }
1688
    }
1689

1690
    private focusEditedExpressionChip() {
UNCOV
1691
        if (this._timeoutId) {
×
UNCOV
1692
            clearTimeout(this._timeoutId);
×
1693
        }
1694

UNCOV
1695
        this._timeoutId = setTimeout(() => {
×
UNCOV
1696
            if (this._lastFocusedChipIndex != -1) {
×
1697
                //Sort the expression chip list.
1698
                //If there was a recent drag&drop and the tree hasn't rerendered(child query), they will be unordered
UNCOV
1699
                const sortedChips = this.expressionsChips.toArray().sort(function (a, b) {
×
UNCOV
1700
                    if (a === b) return 0;
×
UNCOV
1701
                    if (a.chipArea.nativeElement.compareDocumentPosition(b.chipArea.nativeElement) & 2) {
×
1702
                        // b comes before a
UNCOV
1703
                        return 1;
×
1704
                    }
UNCOV
1705
                    return -1;
×
1706
                });
UNCOV
1707
                const chipElement = sortedChips[this._lastFocusedChipIndex]?.nativeElement;
×
UNCOV
1708
                if (chipElement) {
×
UNCOV
1709
                    chipElement.focus();
×
1710
                }
UNCOV
1711
                this._lastFocusedChipIndex = -1;
×
UNCOV
1712
                this._focusDelay = DEFAULT_CHIP_FOCUS_DELAY;
×
1713
            }
1714
        }, this._focusDelay);
1715
    }
1716

1717
    private init() {
UNCOV
1718
        this.cancelOperandAdd();
×
UNCOV
1719
        this.cancelOperandEdit();
×
1720

1721
        // Ignore values of certain properties for the comparison
UNCOV
1722
        const propsToIgnore = ['parent', 'hovered', 'ignoreCase', 'inEditMode', 'inAddMode'];
×
UNCOV
1723
        const propsReplacer = function replacer(key, value) {
×
UNCOV
1724
            if (propsToIgnore.indexOf(key) >= 0) {
×
UNCOV
1725
                return undefined;
×
1726
            } else {
UNCOV
1727
                return value;
×
1728
            }
1729
        };
1730

1731
        // Skip root being recreated if the same
UNCOV
1732
        const newRootGroup = this.createExpressionGroupItem(this.expressionTree);
×
UNCOV
1733
        if (JSON.stringify(this.rootGroup, propsReplacer) !== JSON.stringify(newRootGroup, propsReplacer)) {
×
UNCOV
1734
            this.rootGroup = this.createExpressionGroupItem(this.expressionTree);
×
UNCOV
1735
            this.currentGroup = this.rootGroup;
×
1736
        }
1737

UNCOV
1738
        if (this.rootGroup?.children?.length == 0) {
×
UNCOV
1739
            this.rootGroup = null;
×
UNCOV
1740
            this.currentGroup = null;
×
1741
        }
1742
    }
1743

1744
    private initLocale() {
UNCOV
1745
        this._defaultLocale = getCurrentI18n();
×
UNCOV
1746
        this._locale = this._localeId !== DEFAULT_LOCALE ? this._localeId : this._locale;
×
UNCOV
1747
        onResourceChangeHandle(this.destroy$, this.onResourceChange, this);
×
1748
    }
1749

1750
    private onResourceChange(args: CustomEvent<IResourceChangeEventArgs>) {
UNCOV
1751
        this._defaultLocale = args.detail.newLocale;
×
UNCOV
1752
        if (!this._locale) {
×
1753
            this._defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN, false);
×
1754
        }
1755
    }
1756

1757
    /** rootGroup is recreated after clicking Apply, which sets new expressionTree and calls init()*/
UNCOV
1758
    protected trackExpressionItem = trackByIdentity;
×
1759
}
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

© 2026 Coveralls, Inc