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

atinc / ngx-tethys / e74f59f9-9b78-4a93-acf7-e35891b8e3f7

27 Dec 2023 03:40AM UTC coverage: 90.546% (+0.003%) from 90.543%
e74f59f9-9b78-4a93-acf7-e35891b8e3f7

Pull #2998

circleci

FrankWang117
fix(cascader): fix option will be auto selected when children is [] #INFR-11090
Pull Request #2998: fix(cascader): fix option will be auto selected when children is [] #INFR-11090

5408 of 6631 branches covered (0.0%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

13488 of 14238 relevant lines covered (94.73%)

981.47 hits per line

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

93.76
/src/cascader/cascader.service.ts
1
import { SelectionModel } from '@angular/cdk/collections';
2
import { Injectable } from '@angular/core';
3
import { Id } from '@tethys/cdk/immutable';
4
import { SelectOptionBase } from 'ngx-tethys/shared';
5
import { helpers, isArray, isEmpty, set } from 'ngx-tethys/util';
6
import { Subject } from 'rxjs';
7
import { debounceTime, map } from 'rxjs/operators';
45✔
8
import { ThyCascaderOption, ThyCascaderSearchOption } from './types';
9
const defaultDisplayRender = (label: any) => label.join(' / ');
10

11
/**
1✔
12
 * @internal
13
 */
50✔
14
@Injectable()
50✔
15
export class ThyCascaderService {
50✔
16
    public selectionModel: SelectionModel<SelectOptionBase>;
50✔
17

50✔
18
    public columns: ThyCascaderOption[][] = [];
50✔
19

50✔
20
    public cascaderOptions: {
50✔
21
        labelProperty?: string;
50✔
22
        valueProperty?: string;
23
        isMultiple?: boolean;
24
        isOnlySelectLeaf?: boolean;
50✔
25
        isLabelRenderTemplate?: boolean;
11✔
26
        loadData?: (node: ThyCascaderOption, index?: number) => PromiseLike<any>;
27
    };
28

29
    public selectedOptions: ThyCascaderOption[] = [];
30

11✔
31
    public activatedOptions: ThyCascaderOption[] = [];
11✔
32

11✔
33
    public labelRenderText: string;
34

35
    public flattenOptions: ThyCascaderSearchOption[] = [];
36

170✔
37
    public leafNodes: ThyCascaderSearchOption[] = [];
170✔
38

133✔
39
    public isLoading = false;
40

41
    public searchResultList: ThyCascaderSearchOption[] = [];
42

133✔
43
    public defaultValue: any[];
83✔
44

45
    public value: any[];
46

50✔
47
    private prevSelectedOptions: Set<ThyCascaderOption> = new Set<ThyCascaderOption>();
48

49
    public valueChange$ = new Subject();
50

58✔
51
    public cascaderValueChange() {
52
        return this.valueChange$.pipe(
53
            map(() => {
51✔
54
                const valueChangeOptions = {
36✔
55
                    value: this.getValues(),
56
                    isValueEqual: this.arrayEquals(this.value, this.getValues()),
15✔
57
                    isSelectionModelEmpty: this.selectionModel.isEmpty()
15✔
58
                };
33✔
59
                this.defaultValue = null;
20✔
60
                this.value = this.getValues();
61
                return valueChangeOptions;
62
            }),
63
            debounceTime(100)
64
        );
116✔
65
    }
116✔
66

115✔
67
    public setCascaderOptions(options: {
115✔
68
        labelProperty?: string;
69✔
69
        valueProperty?: string;
70
        isMultiple?: boolean;
115✔
71
        isOnlySelectLeaf?: boolean;
41✔
72
        isLabelRenderTemplate?: boolean;
73
        loadData?: (node: ThyCascaderOption, index?: number) => PromiseLike<any>;
74
    }) {
116✔
75
        this.cascaderOptions = { ...this.cascaderOptions, ...options };
114✔
76
        if (this.cascaderOptions.hasOwnProperty('isMultiple')) {
77
            this.initSelectionModel(this.cascaderOptions.isMultiple);
78
        }
2✔
79
    }
2✔
80

81
    public initSelectionModel(isMultiple?: boolean) {
82
        if (this.selectionModel) {
83
            this.selectionModel.clear();
20!
84
        } else {
×
85
            this.selectionModel = new SelectionModel(isMultiple);
86
        }
20✔
87
    }
20✔
88

1✔
89
    public initColumns(columns: ThyCascaderOption[][]) {
90
        this.columns = columns;
91
    }
19✔
92

93
    public initActivatedOptions(menuVisible: boolean) {
94
        if (isEmpty(this.selectedOptions) || !menuVisible) {
×
95
            return;
141✔
96
        }
141✔
97
        this.activatedOptions = [...this.selectedOptions];
123✔
98
        this.activatedOptions.forEach((item, index) => {
123✔
99
            if (!isEmpty(item.children) && !item.isLeaf) {
100
                this.columns[index + 1] = item.children;
3✔
101
            }
102
        });
103
    }
141✔
104

20✔
105
    public initOptions(index: number) {
106
        const vs = this.defaultValue;
141✔
107
        const load = () => {
85✔
108
            this.activateOnInit(index, vs[index]);
94✔
109
            if (index < vs.length - 1) {
85✔
110
                this.initOptions(index + 1);
111
            }
56✔
112
            if (index === vs.length - 1) {
3✔
113
                this.afterWriteValue();
114
            }
53!
115
        };
×
116

117
        if (this.columns[index]?.length || !this.cascaderOptions.loadData) {
141✔
118
            load();
9✔
119
        } else {
120
            const node = this.activatedOptions[index - 1] || {};
121
            this.loadChildren(node, index - 1, load, this.afterWriteValue.bind(this));
122
        }
5✔
123
    }
1!
124

1✔
125
    public clickOption(
126
        option: ThyCascaderOption,
4✔
127
        index: number,
4✔
128
        event: Event | boolean,
129
        selectFn?: (option: ThyCascaderOption, index: number) => void
130
    ) {
3✔
131
        if (option?.disabled && !this.cascaderOptions.isMultiple) {
3!
132
            return;
4✔
133
        }
3✔
134

135
        const isSelect = event instanceof Event ? !this.cascaderOptions.isMultiple && option.isLeaf : true;
3✔
136

1✔
137
        if (this.cascaderOptions.isMultiple && !option.isLeaf && this.cascaderOptions.isOnlySelectLeaf && isSelect) {
138
            this.toggleAllChildren(option, index, event as boolean, selectFn);
139
        } else {
140
            this.setActiveOption(option, index, isSelect, true, selectFn);
1✔
141
        }
1✔
142
    }
1!
143

1✔
144
    public setActiveOption(
145
        option: ThyCascaderOption,
146
        index: number,
147
        select: boolean,
115✔
148
        loadChildren: boolean = true,
115✔
149
        selectFn?: (option: ThyCascaderOption, index: number) => void
11✔
150
    ): void {
11!
151
        this.activatedOptions[index] = option;
152
        for (let i = index - 1; i >= 0; i--) {
153
            const originOption = this.activatedOptions[i + 1]?.parent;
11!
154
            if (
11!
155
                !this.activatedOptions[i] ||
156
                originOption?.[this.cascaderOptions.valueProperty] !== this.activatedOptions[i]?.[this.cascaderOptions.valueProperty]
157
            ) {
115✔
158
                this.activatedOptions[i] = originOption ?? this.activatedOptions[i];
115✔
159
            }
160
        }
161
        if (index < this.activatedOptions.length - 1) {
1,630✔
162
            this.activatedOptions = this.activatedOptions.slice(0, index + 1);
1,551✔
163
        }
349✔
164
        if (isArray(option.children) && option.children.length) {
165
            option.isLeaf = false;
166
            option.children.forEach(child => (child.parent = option));
1,202✔
167
            this.setColumnData(option.children, index + 1);
168
        } else if (!option.isLeaf && loadChildren) {
169
            this.loadChildren(option, index);
170
        } else if (index < this.columns.length - 1) {
79✔
171
            this.columns = this.columns.slice(0, index + 1);
79✔
172
        }
29✔
173

19✔
174
        if (select) {
19✔
175
            selectFn(option, index);
176
        }
177
    }
10✔
178

179
    private loadChildren(option: ThyCascaderOption, index: number, success?: () => void, failure?: () => void): void {
180
        if (!this.cascaderOptions?.loadData) {
79✔
181
            this.setColumnData(option.children || [], index + 1);
182
            return;
183
        }
184

1✔
185
        this.isLoading = true;
1✔
186

1✔
187
        this.cascaderOptions.loadData(option, index).then(
3✔
188
            () => this.handleLoadDataSuccess(option, index, success),
3✔
189
            () => this.handleLoadDataFailure(option, index, failure)
3✔
190
        );
191
    }
1✔
192

3✔
193
    private handleLoadDataSuccess(option: ThyCascaderOption, index: number, success?: () => void): void {
3✔
194
        option.loading = this.isLoading = false;
2✔
195

196
        if (option.children) {
197
            option.children.forEach(child => (child.parent = index < 0 ? undefined : option));
198
            this.setColumnData(option.children, index + 1);
199
        }
1✔
200

1!
201
        if (success) {
1✔
202
            success();
4✔
203
        }
4✔
204
    }
3✔
205

206
    private handleLoadDataFailure(option: ThyCascaderOption, index: number, failure?: () => void): void {
207
        option.loading = this.isLoading = false;
208
        option.isLeaf = true;
209

1!
210
        if (failure) {
×
211
            failure();
212
        }
213
    }
214

1✔
215
    private activateOnInit(index: number, value: any): void {
216
        let option = this.findOption(value, index);
217
        if (!option) {
6✔
218
            option =
6✔
219
                typeof value === 'object'
220
                    ? value
18✔
221
                    : {
38✔
222
                          [`${this.cascaderOptions.valueProperty || 'value'}`]: value,
57✔
223
                          [`${this.cascaderOptions.labelProperty || 'label'}`]: value
57✔
224
                      };
57✔
225
        }
57✔
226
        this.updatePrevSelectedOptions(option, true);
57✔
227
        this.setActiveOption(option, index, false, false);
57✔
228
    }
57✔
229

230
    public isSelectedOption(option: ThyCascaderOption, index: number): boolean {
231
        if (this.cascaderOptions?.isOnlySelectLeaf) {
232
            if (option.isLeaf) {
233
                return option.selected;
234
            } else {
235
                return this.checkSelectedStatus(option, true);
236
            }
57✔
237
        } else {
57✔
238
            const selectedOpts = this.selectionModel.selected;
32✔
239
            const appearIndex = selectedOpts.findIndex(item => {
240
                if (item.thyRawValue.value.length - 1 === index) {
241
                    const selectedItem = helpers.get(item, `thyRawValue.value.${index}`);
25✔
242
                    return helpers.shallowEqual(selectedItem, option);
243
                } else {
244
                    return false;
245
                }
246
            });
6✔
247
            return appearIndex >= 0;
6✔
248
        }
29✔
249
    }
4✔
250

251
    private toggleAllChildren(
252
        option: ThyCascaderOption,
253
        index: number,
254
        selected: boolean,
255
        selectFn?: (option: ThyCascaderOption, index: number) => void
256
    ): void {
257
        const allLeafs: {
258
            option: ThyCascaderOption;
259
            index: number;
260
        }[] = this.getAllLeafs(option, index, selected);
3,634!
261
        option.selected = selected;
×
262

263
        while (allLeafs.length) {
3,634✔
264
            const { option, index } = allLeafs.shift();
107✔
265
            option.selected = !selected;
266
            this.setActiveOption(option, index, true, null, selectFn);
3,527✔
267
        }
3,909✔
268

701✔
269
        for (let i = 0; i < this.activatedOptions.length; i++) {
270
            const option = this.activatedOptions[i];
3,208✔
271
            if (isArray(option.children) && option.children.length) {
967✔
272
                this.setColumnData(option.children, i + 1);
273
            }
274
        }
1,859✔
275
    }
276

277
    private getAllLeafs(
115✔
278
        option: ThyCascaderOption,
115✔
279
        index: number,
112!
280
        selected: boolean
151✔
281
    ): {
282
        option: ThyCascaderOption;
3✔
283
        index: number;
284
    }[] {
285
        let allLeafs: {
91✔
286
            option: ThyCascaderOption;
87✔
287
            index: number;
87✔
288
        }[] = [];
19✔
289
        if (option.children.length > 0) {
290
            for (const childOption of option.children) {
291
                childOption.parent = option;
292
                if (childOption.isLeaf && !childOption.selected === selected) {
293
                    allLeafs.push({
181✔
294
                        option: childOption,
181!
295
                        index: index + 1
314✔
296
                    });
297
                } else if (!childOption.isLeaf) {
181✔
298
                    allLeafs = allLeafs.concat(this.getAllLeafs(childOption, index + 1, selected));
299
                }
300
            }
1✔
301
        }
1✔
302
        return allLeafs;
1✔
303
    }
304

1✔
305
    public searchInLocal(searchText: string): void {
1✔
306
        this.forEachColumns();
307

1✔
308
        this.setSearchResultList(this.cascaderOptions.isOnlySelectLeaf ? this.leafNodes : this.flattenOptions, searchText);
1!
309
    }
1✔
310

311
    private forEachColumns(
1✔
312
        currentLabel?: string[],
313
        currentValue: Id[] = [],
314
        currentRowValue: ThyCascaderOption[] = [],
8✔
315
        list = this.columns[0]
8✔
316
    ) {
8✔
317
        list.forEach(item => {
318
            const curOptionLabel = this.getOptionLabel(item);
319
            const curOptionValue = this.getOptionValue(item);
1✔
320
            const label: string[] = currentLabel ? [...currentLabel, curOptionLabel] : [curOptionLabel];
1✔
321
            const valueList: Id[] = [...currentValue, curOptionValue];
1✔
322
            const rowValueList: ThyCascaderOption[] = [...currentRowValue, item];
1✔
323
            const isSelected = this.isSelectedOption(item, valueList.length - 1);
324

325
            const node = {
1✔
326
                labelList: label,
1✔
327
                valueList,
1✔
328
                selected: isSelected,
1✔
329
                thyRowValue: rowValueList,
330
                isLeaf: item.isLeaf,
331
                disabled: item.disabled
2✔
332
            };
2✔
333

4✔
334
            this.flattenOptions.push(node);
1✔
335
            if (item?.children?.length) {
336
                this.forEachColumns(label, valueList, rowValueList, item.children);
337
            } else {
338
                this.leafNodes.push(node);
339
            }
9✔
340
        });
9✔
341
    }
9✔
342

8✔
343
    private setSearchResultList(listOfOption: ThyCascaderSearchOption[], searchText: string) {
344
        this.searchResultList = [];
345
        listOfOption.forEach(item => {
1✔
346
            if (!item.disabled && item.isLeaf && item.labelList.join().toLowerCase().indexOf(searchText.toLowerCase()) !== -1) {
1✔
347
                this.searchResultList.push(item);
1!
348
            }
1✔
349
        });
1✔
350
    }
351

UNCOV
352
    /**
×
353
     * 检查所有所有子项的选择状态, 有一个不符合预期,就直接返回 false
354
     * @param option
355
     * @param trueOrFalse
1✔
356
     * @private
357
     */
9✔
358
    private checkSelectedStatus(option: ThyCascaderOption, isSelected: boolean): boolean {
359
        if (option.isLeaf) {
360
            return option.selected === isSelected;
109✔
361
        }
29✔
362
        if (helpers.isEmpty(option.children) && this.cascaderOptions?.isOnlySelectLeaf) {
87✔
363
            return false;
29✔
364
        }
29✔
365
        for (const childOption of option.children) {
366
            if (isArray(childOption.children) && childOption.children.length && !this.checkSelectedStatus(childOption, isSelected)) {
367
                return false;
29✔
368
            }
369
            if (!childOption.selected === isSelected) {
370
                return false;
371
            }
57✔
372
        }
115✔
373
        return true;
81✔
374
    }
81✔
375

81✔
376
    private findOption(option: any, index: number): ThyCascaderOption {
28✔
377
        const options: ThyCascaderOption[] = this.columns[index];
378
        if (options) {
379
            const value = typeof option === 'object' ? this.getOptionValue(option) : option;
380
            return options.find(o => value === this.getOptionValue(o));
381
        }
382
        return null;
117✔
383
    }
130✔
384

117✔
385
    private setColumnData(options: ThyCascaderOption[], index: number): void {
68✔
386
        if (!this.arrayEquals(this.columns[index], options)) {
387
            this.columns[index] = options;
388
            if (index < this.columns.length - 1) {
389
                this.columns = this.columns.slice(0, index + 1);
49✔
390
            }
4✔
391
        }
392
    }
393

45✔
394
    private getSubmitValue(originOptions: ThyCascaderOption[]): any[] {
45✔
395
        const values: any[] = [];
396
        (originOptions || []).forEach(option => {
49!
397
            values.push(this.getOptionValue(option));
49✔
398
        });
399
        return values;
400
    }
401

402
    public removeSelectedItem(item: SelectOptionBase) {
403
        const selectedItems = this.selectionModel.selected;
404
        const currentItem = selectedItems.find(i => {
405
            return helpers.shallowEqual(i.thyValue, item.thyValue);
406
        });
49✔
407
        this.deselectOption(currentItem);
408
        this.selectionModel.deselect(currentItem);
409
        // update selectedOptions
410
        const updatedSelectedItems = this.selectionModel.selected;
110!
411
        if (isArray(updatedSelectedItems) && updatedSelectedItems.length) {
×
412
            this.selectedOptions = updatedSelectedItems[updatedSelectedItems.length - 1].thyRawValue.value;
413
        }
110✔
414

79✔
415
        this.valueChange$.next();
79✔
416
    }
12✔
417

418
    public resetSearch() {
419
        this.searchResultList = [];
67✔
420
        this.leafNodes = [];
67✔
421
        this.flattenOptions = [];
67✔
422
    }
423

424
    public clearSelection() {
425
        this.labelRenderText = '';
31✔
426
        this.selectedOptions = [];
31✔
427
        this.activatedOptions = [];
31✔
428
        this.deselectAllSelected();
30✔
429
    }
30!
430

30✔
431
    private deselectAllSelected() {
432
        const selectedOptions = this.selectionModel.selected;
433
        selectedOptions.forEach(item => this.deselectOption(item));
×
434
        this.selectionModel.clear();
×
435
        this.valueChange$.next();
×
436
    }
437

438
    private deselectOption(option: SelectOptionBase) {
439
        const value: ThyCascaderOption[] = option.thyRawValue.value;
440
        value.forEach(item => {
441
            if (item.isLeaf && item.selected) {
109✔
442
                set(item, 'selected', false);
109✔
443
            }
109✔
444
        });
109✔
445
    }
446

447
    public selectOption(option: ThyCascaderOption, index: number): void {
1,554✔
448
        this.selectedOptions = this.activatedOptions;
1,537✔
449
        this.updatePrevSelectedOptions(option, false, index);
1,537✔
450
        if (option.selected) {
451
            this.buildDisplayLabel();
17✔
452
        } else {
6✔
453
            const selectedItems = this.selectionModel.selected;
454
            const currentItem = selectedItems.find(item => {
455
                if (item.thyRawValue.value.length - 1 === index) {
11✔
456
                    const selectedItem = helpers.get(item, `thyRawValue.value.${index}`);
11✔
457
                    return helpers.shallowEqual(selectedItem, option);
3✔
458
                } else {
3✔
459
                    return false;
460
                }
11✔
461
            });
462
            this.selectionModel.deselect(currentItem);
463
        }
464
        this.valueChange$.next();
1,554✔
465
    }
466

467
    private addSelectedState(selectOptions: ThyCascaderOption[]) {
468
        if (this.cascaderOptions.isMultiple && this.cascaderOptions.isOnlySelectLeaf) {
469
            selectOptions.forEach(opt => {
205✔
470
                if (opt.isLeaf) {
471
                    opt.selected = true;
1,349✔
472
                    set(opt, 'selected', true);
473
                }
474
            });
475
            this.addParentSelectedState(selectOptions);
33✔
476
        }
72✔
477
    }
33✔
478

479
    private addParentSelectedState(selectOptions: ThyCascaderOption[]) {
480
        selectOptions.forEach(opt => {
134✔
481
            if (opt?.children?.length && opt.children.every(i => i.selected)) {
115✔
482
                opt.selected = true;
483
                set(opt, 'selected', true);
484
                if (opt.parent) {
19✔
485
                    this.addParentSelectedState([opt.parent]);
4✔
486
                }
487
            }
19✔
488
        });
19✔
489
    }
10✔
490

491
    private buildDisplayLabel(): void {
492
        const selectedOptions = [...this.selectedOptions];
134✔
493
        const labels: string[] = selectedOptions.map(o => this.getOptionLabel(o));
494

495
        if (labels.length === 0) {
115✔
496
            return;
33✔
497
        }
498

499
        let labelRenderContext;
500
        let labelRenderText;
19✔
501

502
        if (this.cascaderOptions.isLabelRenderTemplate) {
503
            labelRenderContext = { labels, selectedOptions };
504
        } else {
505
            labelRenderText = defaultDisplayRender.call(this, labels, selectedOptions);
4✔
506
            this.labelRenderText = labelRenderText;
4✔
UNCOV
507
        }
×
508

509
        if (this.labelRenderText || this.cascaderOptions.isLabelRenderTemplate) {
4✔
510
            const selectedData: SelectOptionBase = {
511
                thyRawValue: {
512
                    value: selectedOptions,
187!
513
                    labelText: labelRenderText,
514
                    labelRenderContext: labelRenderContext
515
                },
522!
516
                thyValue: labels,
517
                thyLabelText: labelRenderText
518
            };
140✔
519
            this.selectionModel.select(selectedData);
82✔
520
        }
521
    }
58✔
522

6✔
523
    public writeCascaderValue(value: any): void {
524
        if (!this.selectionModel) {
525
            this.initSelectionModel(this.cascaderOptions.isMultiple);
52✔
526
        }
527
        if (!this.cascaderOptions.isMultiple) {
528
            const vs = (this.defaultValue = this.toArray(value));
529
            if (vs.length) {
102✔
530
                this.initOptions(0);
78✔
531
            } else {
532
                this.value = vs;
30✔
533
                this.activatedOptions = [];
534
                this.afterWriteValue();
535
            }
1✔
536
        } else {
537
            const values = this.toArray(value);
538
            this.selectionModel.clear();
539
            values.forEach(item => {
540
                const vs = (this.defaultValue = this.toArray(item));
541
                if (vs.length) {
542
                    this.initOptions(0);
543
                } else {
544
                    this.value = vs;
545
                    this.activatedOptions = [];
546
                    this.afterWriteValue();
547
                }
548
            });
549
        }
550
    }
551

552
    private afterWriteValue() {
553
        this.selectedOptions = this.activatedOptions;
554
        this.value = this.getSubmitValue(this.selectedOptions);
555
        this.addSelectedState(this.selectedOptions);
556
        this.buildDisplayLabel();
557
    }
558

559
    public isActivatedOption(option: ThyCascaderOption, index: number): boolean {
560
        if (!this.cascaderOptions?.isMultiple || this.cascaderOptions.isOnlySelectLeaf) {
561
            const activeOpt = this.activatedOptions[index];
562
            return activeOpt === option;
563
        } else if (option.isLeaf) {
564
            return option.selected;
565
        } else {
566
            const selectedOpts = this.selectionModel.selected;
567
            const appearIndex = selectedOpts.findIndex(item => {
568
                const selectedItem = helpers.get(item, `thyRawValue.value.${index}`);
569
                return helpers.shallowEqual(selectedItem, option);
570
            });
571
            return appearIndex >= 0;
572
        }
573
    }
574

575
    public isHalfSelectedOption(option: ThyCascaderOption, index: number): boolean {
576
        if (
577
            !option.selected &&
578
            this.cascaderOptions.isOnlySelectLeaf &&
579
            !option.isLeaf &&
580
            !this.checkSelectedStatus(option, false) &&
581
            !helpers.isEmpty(option.children)
582
        ) {
583
            return true;
584
        }
585
        return false;
586
    }
587

588
    public getValues() {
589
        let selectedItems: any[];
590
        const selected = this.selectionModel.selected;
591
        selectedItems = selected.map(item => this.getSubmitValue(item.thyRawValue.value));
592
        return this.cascaderOptions?.isMultiple ? selectedItems : selectedItems[0] ?? selectedItems;
593
    }
594

595
    private updatePrevSelectedOptions(option: ThyCascaderOption, isActivateInit: boolean, index?: number) {
596
        if (isActivateInit) {
597
            this.handleActivateInit(option);
598
        } else {
599
            if (!this.cascaderOptions.isMultiple) {
600
                this.clearPrevSelectedOptions();
601
            }
602

603
            set(option, 'selected', this.isSelected(option, index));
604

605
            if (this.cascaderOptions.isOnlySelectLeaf && this.cascaderOptions.isMultiple && option.parent) {
606
                this.updatePrevSelectedOptions(option.parent, false, index - 1);
607
            }
608
        }
609

610
        this.prevSelectedOptions.add(option);
611
    }
612

613
    private handleActivateInit(option: ThyCascaderOption): void {
614
        if (this.cascaderOptions.isOnlySelectLeaf && option.isLeaf) {
615
            set(option, 'selected', true);
616
        }
617
    }
618

619
    private isSelected(option: ThyCascaderOption, index?: number) {
620
        return this.cascaderOptions.isOnlySelectLeaf && !option.isLeaf && this.cascaderOptions.isMultiple
621
            ? this.isSelectedOption(option, index)
622
            : !this.isSelectedOption(option, index);
623
    }
624

625
    private clearPrevSelectedOptions() {
626
        const prevSelectedOptions = Array.from(this.prevSelectedOptions);
627
        while (prevSelectedOptions.length) {
628
            set(prevSelectedOptions.pop(), 'selected', false);
629
        }
630
        this.prevSelectedOptions = new Set([]);
631
    }
632

633
    private getOptionLabel(option: ThyCascaderOption): any {
634
        return option[this.cascaderOptions.labelProperty || 'label'];
635
    }
636

637
    private getOptionValue(option: ThyCascaderOption): any {
638
        return option[this.cascaderOptions.valueProperty || 'value'];
639
    }
640

641
    toArray<T>(value: T | T[]): T[] {
642
        if (value == null) {
643
            return [];
644
        } else if (!Array.isArray(value)) {
645
            return [value];
646
        } else {
647
            return value;
648
        }
649
    }
650

651
    arrayEquals<T>(array1: T[], array2: T[]): boolean {
652
        if (!Array.isArray(array1) || !Array.isArray(array2) || array1.length !== array2.length) {
653
            return false;
654
        }
655

656
        return array1.every((element, index) => element === array2[index]);
657
    }
658
}
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