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

atinc / ngx-tethys / cf156675-ee77-4813-934b-5d491b1be73d

11 Dec 2023 02:55AM UTC coverage: 90.452% (+0.09%) from 90.363%
cf156675-ee77-4813-934b-5d491b1be73d

Pull #2894

circleci

smile1016
Merge branch 'zxl/#INFR-10254' of github.com:atinc/ngx-tethys into zxl/#INFR-10254
Pull Request #2894: feat(cascader): add service for cascader #INFR-10254

5348 of 6574 branches covered (0.0%)

Branch coverage included in aggregate %.

299 of 314 new or added lines in 2 files covered. (95.22%)

16 existing lines in 2 files now uncovered.

13352 of 14100 relevant lines covered (94.7%)

972.56 hits per line

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

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

49✔
11
@Injectable()
49✔
12
export class ThyCascaderService {
49✔
13
    public selectionModel: SelectionModel<SelectOptionBase>;
49✔
14

49✔
15
    public columns: ThyCascaderOption[][] = [];
49✔
16

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

26
    public selectedOptions: ThyCascaderOption[] = [];
27

NEW
28
    public activatedOptions: ThyCascaderOption[] = [];
×
NEW
29

×
30
    public labelRenderText: string;
31

32
    public flattenOptions: ThyCascaderSearchOption[] = [];
33

165✔
34
    public leafNodes: ThyCascaderSearchOption[] = [];
165✔
35

36
    public isLoading = false;
37

165✔
38
    public searchResultList: ThyCascaderSearchOption[] = [];
116✔
39

40
    public defaultValue: any[];
41

49✔
42
    public value: any[];
43

44
    private prevSelectedOptions: Set<ThyCascaderOption> = new Set<ThyCascaderOption>();
45

56✔
46
    public valueChange$ = new Subject();
47

48
    public cascaderValueChange() {
39✔
49
        return this.valueChange$.pipe(
26✔
50
            map(() => {
51
                return {
13✔
52
                    value: this.getValues(),
13✔
53
                    isValueEqual: this.arrayEquals(this.value, this.getValues()),
31✔
54
                    isSelectionModelEmpty: this.selectionModel.isEmpty()
19✔
55
                };
56
            }),
57
            finalize(() => {
58
                this.defaultValue = null;
59
                this.value = this.getValues();
115✔
60
            }),
115✔
61
            debounceTime(100)
114✔
62
        );
114✔
63
    }
69✔
64

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

77
    public initSelectionModel(isMultiple?: boolean) {
78
        if (this.selectionModel) {
20!
NEW
79
            this.selectionModel.clear();
×
80
        } else {
81
            this.selectionModel = new SelectionModel(isMultiple);
20✔
82
        }
20✔
83
    }
1✔
84

85
    public initColumns(columns: ThyCascaderOption[][]) {
86
        this.columns = columns;
19✔
87
    }
88

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

101
    public initOptions(index: number) {
140✔
102
        const vs = this.defaultValue;
85✔
103
        const load = () => {
94✔
104
            this.activateOnInit(index, vs[index]);
85✔
105
            if (index < vs.length - 1) {
106
                this.initOptions(index + 1);
55✔
107
            }
3✔
108
            if (index === vs.length - 1) {
109
                this.afterWriteValue();
52!
NEW
110
            }
×
111
        };
112

140✔
113
        if (this.columns[index]?.length || !this.cascaderOptions.loadData) {
9✔
114
            load();
115
        } else {
116
            const node = this.activatedOptions[index - 1] || {};
117
            this.loadChildren(node, index - 1, load, this.afterWriteValue.bind(this));
5✔
118
        }
1!
119
    }
1✔
120

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

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

133
        if (this.cascaderOptions.isMultiple && !option.isLeaf && this.cascaderOptions.isOnlySelectLeaf && isSelect) {
134
            this.toggleAllChildren(option, index, event as boolean, selectFn);
135
        } else {
1✔
136
            this.setActiveOption(option, index, isSelect, true, selectFn);
1✔
137
        }
1!
138
    }
1✔
139

140
    public setActiveOption(
141
        option: ThyCascaderOption,
142
        index: number,
114✔
143
        select: boolean,
114✔
144
        loadChildren: boolean = true,
10✔
145
        selectFn?: (option: ThyCascaderOption, index: number) => void
10!
146
    ): void {
147
        this.activatedOptions[index] = option;
148
        for (let i = index - 1; i >= 0; i--) {
10!
149
            const originOption = this.activatedOptions[i + 1]?.parent;
10!
150
            if (
151
                !this.activatedOptions[i] ||
152
                originOption?.[this.cascaderOptions.valueProperty] !== this.activatedOptions[i]?.[this.cascaderOptions.valueProperty]
114✔
153
            ) {
114✔
154
                this.activatedOptions[i] = originOption ?? this.activatedOptions[i];
155
            }
156
        }
1,544✔
157
        if (index < this.activatedOptions.length - 1) {
1,489✔
158
            this.activatedOptions = this.activatedOptions.slice(0, index + 1);
306✔
159
        }
160
        if (isArray(option.children) && option.children.length) {
161
            option.isLeaf = false;
1,183✔
162
            option.children.forEach(child => (child.parent = option));
163
            this.setColumnData(option.children, index + 1);
164
        } else if (!option.isLeaf && loadChildren) {
165
            this.loadChildren(option, index);
55✔
166
        } else if (index < this.columns.length - 1) {
55✔
167
            this.columns = this.columns.slice(0, index + 1);
5✔
168
        }
1✔
169

1✔
170
        if (select) {
171
            selectFn(option, index);
172
        }
4✔
173
    }
174

175
    private loadChildren(option: ThyCascaderOption, index: number, success?: () => void, failure?: () => void): void {
55✔
176
        if (!this.cascaderOptions?.loadData) {
177
            this.setColumnData(option.children || [], index + 1);
178
            return;
179
        }
1✔
180

1✔
181
        this.isLoading = true;
1✔
182

3✔
183
        this.cascaderOptions.loadData(option, index).then(
3✔
184
            () => this.handleLoadDataSuccess(option, index, success),
3✔
185
            () => this.handleLoadDataFailure(option, index, failure)
186
        );
1✔
187
    }
3✔
188

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

192
        if (option.children) {
193
            option.children.forEach(child => (child.parent = index < 0 ? undefined : option));
194
            this.setColumnData(option.children, index + 1);
1✔
195
        }
1!
196

1✔
197
        if (success) {
4✔
198
            success();
4✔
199
        }
3✔
200
    }
201

202
    private handleLoadDataFailure(option: ThyCascaderOption, index: number, failure?: () => void): void {
203
        option.loading = this.isLoading = false;
204
        option.isLeaf = true;
1!
NEW
205

×
206
        if (failure) {
207
            failure();
208
        }
209
    }
1✔
210

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

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

247
    private toggleAllChildren(
248
        option: ThyCascaderOption,
249
        index: number,
250
        selected: boolean,
251
        selectFn?: (option: ThyCascaderOption, index: number) => void
252
    ): void {
253
        const allLeafs: {
254
            option: ThyCascaderOption;
255
            index: number;
3,427!
NEW
256
        }[] = this.getAllLeafs(option, index, selected);
×
257
        option.selected = selected;
258

3,427✔
259
        while (allLeafs.length) {
3,725✔
260
            const { option, index } = allLeafs.shift();
637✔
261
            option.selected = !selected;
262
            this.setActiveOption(option, index, true, null, selectFn);
3,088✔
263
        }
891✔
264

265
        for (let i = 0; i < this.activatedOptions.length; i++) {
266
            const option = this.activatedOptions[i];
1,899✔
267
            if (isArray(option.children) && option.children.length) {
268
                this.setColumnData(option.children, i + 1);
269
            }
114✔
270
        }
114✔
271
    }
111!
272

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

1!
301
    public searchInLocal(searchText: string): void {
1✔
302
        this.forEachColumns();
303

1✔
304
        this.setSearchResultList(this.cascaderOptions.isOnlySelectLeaf ? this.leafNodes : this.flattenOptions, searchText);
305
    }
306

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

1✔
321
            const node = {
322
                labelList: label,
323
                valueList,
2✔
324
                selected: isSelected,
2✔
325
                thyRowValue: rowValueList,
4✔
326
                isLeaf: item.isLeaf,
1✔
327
                disabled: item.disabled
328
            };
329

330
            this.flattenOptions.push(node);
331
            if (item?.children?.length) {
9✔
332
                this.forEachColumns(label, valueList, rowValueList, item.children);
9✔
333
            } else {
9✔
334
                this.leafNodes.push(node);
8✔
335
            }
336
        });
337
    }
1✔
338

1✔
339
    private setSearchResultList(listOfOption: ThyCascaderSearchOption[], searchText: string) {
1!
340
        this.searchResultList = [];
1✔
341
        listOfOption.forEach(item => {
1✔
342
            if (!item.disabled && item.isLeaf && item.labelList.join().toLowerCase().indexOf(searchText.toLowerCase()) !== -1) {
343
                this.searchResultList.push(item);
NEW
344
            }
×
345
        });
346
    }
347

1✔
348
    /**
349
     * 检查所有所有子项的选择状态, 有一个不符合预期,就直接返回 false
9✔
350
     * @param option
351
     * @param trueOrFalse
352
     * @private
108✔
353
     */
29✔
354
    private checkSelectedStatus(option: ThyCascaderOption, isSelected: boolean): boolean {
87✔
355
        if (option.isLeaf) {
29✔
356
            return option.selected === isSelected;
29✔
357
        }
358
        for (const childOption of option.children) {
359
            if (isArray(childOption.children) && childOption.children.length && !this.checkSelectedStatus(childOption, isSelected)) {
29✔
360
                return false;
361
            }
362
            if (!childOption.selected === isSelected) {
363
                return false;
57✔
364
            }
115✔
365
        }
84✔
366
        return true;
84✔
367
    }
84✔
368

28✔
369
    private findOption(option: any, index: number): ThyCascaderOption {
370
        const options: ThyCascaderOption[] = this.columns[index];
371
        if (options) {
372
            const value = typeof option === 'object' ? this.getOptionValue(option) : option;
373
            return options.find(o => value === this.getOptionValue(o));
374
        }
116✔
375
        return null;
130✔
376
    }
116✔
377

67✔
378
    private setColumnData(options: ThyCascaderOption[], index: number): void {
379
        if (!this.arrayEquals(this.columns[index], options)) {
380
            this.columns[index] = options;
381
            if (index < this.columns.length - 1) {
49✔
382
                this.columns = this.columns.slice(0, index + 1);
4✔
383
            }
384
        }
385
    }
45✔
386

45✔
387
    private getSubmitValue(originOptions: ThyCascaderOption[]): any[] {
388
        const values: any[] = [];
49!
389
        (originOptions || []).forEach(option => {
49✔
390
            values.push(this.getOptionValue(option));
391
        });
392
        return values;
393
    }
394

395
    public removeSelectedItem(item: SelectOptionBase) {
396
        const selectedItems = this.selectionModel.selected;
397
        const currentItem = selectedItems.find(i => {
398
            return helpers.shallowEqual(i.thyValue, item.thyValue);
49✔
399
        });
400
        this.deselectOption(currentItem);
401
        this.selectionModel.deselect(currentItem);
402
        // update selectedOptions
108!
NEW
403
        const updatedSelectedItems = this.selectionModel.selected;
×
404
        if (isArray(updatedSelectedItems) && updatedSelectedItems.length) {
405
            this.selectedOptions = updatedSelectedItems[updatedSelectedItems.length - 1].thyRawValue.value;
108✔
406
        }
78✔
407

78✔
408
        this.valueChange$.next();
12✔
409
    }
410

411
    public resetSearch() {
66✔
412
        this.searchResultList = [];
66✔
413
        this.leafNodes = [];
66✔
414
        this.flattenOptions = [];
415
    }
416

417
    public clearSelection() {
30✔
418
        this.labelRenderText = '';
30✔
419
        this.selectedOptions = [];
30✔
420
        this.activatedOptions = [];
30✔
421
        this.deselectAllSelected();
30!
422
    }
30✔
423

424
    private deselectAllSelected() {
NEW
425
        const selectedOptions = this.selectionModel.selected;
×
NEW
426
        selectedOptions.forEach(item => this.deselectOption(item));
×
NEW
427
        this.selectionModel.clear();
×
428
        this.valueChange$.next();
429
    }
430

431
    private deselectOption(option: SelectOptionBase) {
432
        const value: ThyCascaderOption[] = option.thyRawValue.value;
433
        value.forEach(item => {
108✔
434
            if (item.isLeaf && item.selected) {
108✔
435
                set(item, 'selected', false);
108✔
436
            }
108✔
437
        });
438
    }
439

1,381✔
440
    public selectOption(option: ThyCascaderOption, index: number): void {
1,364✔
441
        this.selectedOptions = this.activatedOptions;
1,364✔
442
        this.updatePrevSelectedOptions(option, false, index);
443
        if (option.selected) {
17✔
444
            this.buildDisplayLabel();
6✔
445
        } else {
446
            const selectedItems = this.selectionModel.selected;
447
            const currentItem = selectedItems.find(item => {
11✔
448
                if (item.thyRawValue.value.length - 1 === index) {
11✔
449
                    const selectedItem = helpers.get(item, `thyRawValue.value.${index}`);
3✔
450
                    return helpers.shallowEqual(selectedItem, option);
3✔
451
                } else {
452
                    return false;
11✔
453
                }
454
            });
455
            this.selectionModel.deselect(currentItem);
456
        }
1,381✔
457
        this.valueChange$.next();
183✔
458
    }
459

1,198✔
460
    private addSelectedState(selectOptions: ThyCascaderOption[]) {
461
        if (this.cascaderOptions.isMultiple && this.cascaderOptions.isOnlySelectLeaf) {
462
            selectOptions.forEach(opt => {
463
                if (opt.isLeaf) {
22✔
464
                    opt.selected = true;
48✔
465
                    set(opt, 'selected', true);
22✔
466
                }
467
            });
468
            this.addParentSelectedState(selectOptions);
220✔
469
        }
220✔
470
    }
29✔
471

472
    private addParentSelectedState(selectOptions: ThyCascaderOption[]) {
220✔
473
        selectOptions.forEach(opt => {
97✔
474
            if (opt?.children?.length && opt.children.every(i => i.selected)) {
475
                opt.selected = true;
220✔
476
                set(opt, 'selected', true);
477
                if (opt.parent) {
478
                    this.addParentSelectedState([opt.parent]);
220✔
479
                }
33✔
480
            }
481
        });
187✔
482
    }
106✔
483

484
    private buildDisplayLabel(): void {
485
        const selectedOptions = [...this.selectedOptions];
486
        const labels: string[] = selectedOptions.map(o => this.getOptionLabel(o));
487

488
        if (labels.length === 0) {
29✔
489
            return;
29✔
490
        }
13✔
491

492
        let labelRenderContext;
29✔
493
        let labelRenderText;
494

495
        if (this.cascaderOptions.isLabelRenderTemplate) {
187!
496
            labelRenderContext = { labels, selectedOptions };
497
        } else {
498
            labelRenderText = defaultDisplayRender.call(this, labels, selectedOptions);
452!
499
            this.labelRenderText = labelRenderText;
500
        }
501

138✔
502
        if (this.labelRenderText || this.cascaderOptions.isLabelRenderTemplate) {
80✔
503
            const selectedData: SelectOptionBase = {
504
                thyRawValue: {
58✔
505
                    value: selectedOptions,
6✔
506
                    labelText: labelRenderText,
507
                    labelRenderContext: labelRenderContext
508
                },
52✔
509
                thyValue: labels,
510
                thyLabelText: labelRenderText
511
            };
512
            this.selectionModel.select(selectedData);
102✔
513
        }
78✔
514
    }
515

30✔
516
    public writeCascaderValue(value: any): void {
517
        if (!this.selectionModel) {
518
            this.initSelectionModel(this.cascaderOptions.isMultiple);
1✔
519
        }
520
        if (!this.cascaderOptions.isMultiple) {
521
            const vs = (this.defaultValue = this.toArray(value));
522
            if (vs.length) {
523
                this.initOptions(0);
524
            } else {
525
                this.value = vs;
526
                this.activatedOptions = [];
527
                this.afterWriteValue();
528
            }
529
        } else {
530
            const values = this.toArray(value);
531
            this.selectionModel.clear();
532
            values.forEach(item => {
533
                const vs = (this.defaultValue = this.toArray(item));
534
                if (vs.length) {
535
                    this.initOptions(0);
536
                } else {
537
                    this.value = vs;
538
                    this.activatedOptions = [];
539
                    this.afterWriteValue();
540
                }
541
            });
542
        }
543
    }
544

545
    private afterWriteValue() {
546
        this.selectedOptions = this.activatedOptions;
547
        this.value = this.getSubmitValue(this.selectedOptions);
548
        this.addSelectedState(this.selectedOptions);
549
        this.buildDisplayLabel();
550
    }
551

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

568
    public isHalfSelectedOption(option: ThyCascaderOption, index: number): boolean {
569
        if (!option.selected && this.cascaderOptions.isOnlySelectLeaf && !option.isLeaf && !this.checkSelectedStatus(option, false)) {
570
            return true;
571
        }
572
        return false;
573
    }
574

575
    public getValues() {
576
        let selectedItems: any[];
577
        const selected = this.selectionModel.selected;
578
        selectedItems = selected.map(item => this.getSubmitValue(item.thyRawValue.value));
579
        return this.cascaderOptions?.isMultiple ? selectedItems : selectedItems[0] ?? selectedItems;
580
    }
581

582
    private updatePrevSelectedOptions(option: ThyCascaderOption, isActivateInit: boolean, index?: number) {
583
        set(option, 'selected', this.isSelected(option, isActivateInit, index));
584
        if (!this.cascaderOptions.isMultiple) {
585
            this.clearPrevSelectedOptions();
586
        }
587

588
        if (this.cascaderOptions.isOnlySelectLeaf && this.cascaderOptions.isMultiple && option.parent) {
589
            this.updatePrevSelectedOptions(option.parent, false, index - 1);
590
        }
591

592
        this.prevSelectedOptions.add(option);
593
    }
594

595
    private isSelected(option: ThyCascaderOption, isActivateInit: boolean, index?: number) {
596
        if (isActivateInit && this.cascaderOptions.isOnlySelectLeaf && option.isLeaf) {
597
            return true;
598
        } else if (!isActivateInit) {
599
            return this.cascaderOptions.isOnlySelectLeaf && !option.isLeaf && this.cascaderOptions.isMultiple
600
                ? this.isSelectedOption(option, index)
601
                : !this.isSelectedOption(option, index);
602
        }
603
    }
604

605
    private clearPrevSelectedOptions() {
606
        const prevSelectedOptions = Array.from(this.prevSelectedOptions);
607
        while (prevSelectedOptions.length) {
608
            set(prevSelectedOptions.pop(), 'selected', false);
609
        }
610
        this.prevSelectedOptions.clear();
611
    }
612

613
    private getOptionLabel(option: ThyCascaderOption): any {
614
        return option[this.cascaderOptions.labelProperty || 'label'];
615
    }
616

617
    private getOptionValue(option: ThyCascaderOption): any {
618
        return option[this.cascaderOptions.valueProperty || 'value'];
619
    }
620

621
    toArray<T>(value: T | T[]): T[] {
622
        if (value == null) {
623
            return [];
624
        } else if (!Array.isArray(value)) {
625
            return [value];
626
        } else {
627
            return value;
628
        }
629
    }
630

631
    arrayEquals<T>(array1: T[], array2: T[]): boolean {
632
        if (!Array.isArray(array1) || !Array.isArray(array2) || array1.length !== array2.length) {
633
            return false;
634
        }
635

636
        return array1.every((element, index) => element === array2[index]);
637
    }
638
}
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