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

atinc / ngx-tethys / 5ba92dd9-5587-4696-bca7-7a8d7d5fe2d3

16 May 2025 06:19AM UTC coverage: 90.329% (+0.001%) from 90.328%
5ba92dd9-5587-4696-bca7-7a8d7d5fe2d3

push

circleci

web-flow
refactor(breadcrumb): migrate to signal for breadcrumb #TINFR-1451 (#3398)

5591 of 6851 branches covered (81.61%)

Branch coverage included in aggregate %.

13 of 14 new or added lines in 1 file covered. (92.86%)

13500 of 14284 relevant lines covered (94.51%)

914.34 hits per line

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

96.0
/src/breadcrumb/breadcrumb.component.ts
1
import { Component, ChangeDetectionStrategy, TemplateRef, numberAttribute, input, contentChild, Signal, computed } from '@angular/core';
2
import { ThyIcon } from 'ngx-tethys/icon';
3
import { NgClass, NgTemplateOutlet } from '@angular/common';
4
import { ThyBreadcrumbItem } from './breadcrumb-item.component';
5
import { SafeAny } from 'ngx-tethys/types';
6
import { ThyDropdownDirective, ThyDropdownMenuComponent, ThyDropdownMenuItemDirective } from 'ngx-tethys/dropdown';
7
import { ThyAction } from 'ngx-tethys/action';
8
import { coerceBooleanProperty } from 'ngx-tethys/util';
9

10
const THY_BREADCRUMB_ITEM_ELLIPSIS_ID = 'THY_BREADCRUMB_ITEM_ELLIPSIS_ID';
11

1✔
12
const ELLIPSIS_ITEM = { _id: THY_BREADCRUMB_ITEM_ELLIPSIS_ID };
1✔
13

14
/**
15
 * 面包屑组件
16
 * @name thy-breadcrumb
17
 * @order 10
18
 */
1✔
19
@Component({
20
    selector: 'thy-breadcrumb',
15✔
21
    templateUrl: './breadcrumb.component.html',
15✔
22
    exportAs: 'ThyBreadcrumb',
11✔
23
    changeDetection: ChangeDetectionStrategy.OnPush,
11✔
24
    host: {
1✔
25
        class: 'thy-breadcrumb',
1!
NEW
26
        '[class.thy-breadcrumb-separator]': '!!thySeparator()',
×
27
        '[class.thy-breadcrumb-separator-slash]': 'thySeparator() === "slash"',
28
        '[class.thy-breadcrumb-separator-backslash]': 'thySeparator() === "backslash"',
1✔
29
        '[class.thy-breadcrumb-separator-vertical-line]': 'thySeparator() === "vertical-line"'
30
    },
31
    imports: [
10✔
32
        ThyIcon,
33
        NgClass,
34
        ThyBreadcrumbItem,
15✔
35
        NgTemplateOutlet,
15✔
36
        ThyAction,
15✔
37
        ThyDropdownDirective,
4✔
38
        ThyDropdownMenuItemDirective,
39
        ThyDropdownMenuComponent,
40
        ThyIcon
11✔
41
    ]
42
})
43
export class ThyBreadcrumb {
15✔
44
    /**
15✔
45
     * 面包屑的前缀 展示图标,如 folder-fill
15✔
46
     */
15✔
47
    readonly thyIcon = input<string>();
15✔
48

15✔
49
    iconClasses: Signal<string[]> = computed(() => {
15✔
50
        const icon = this.thyIcon();
17✔
51
        if (icon && icon.includes('wtf')) {
17✔
52
            const classes = icon.split(' ');
11✔
53
            if (classes.length === 1) {
54
                classes.unshift('wtf');
6✔
55
            }
6✔
56
            return classes;
4✔
57
        } else {
4✔
58
            return null;
59
        }
60
    });
61

62
    svgIconName: Signal<string> = computed(() => {
63
        const icon = this.thyIcon();
2✔
64
        if (icon && !icon.includes('wtf')) {
65
            return icon;
66
        } else {
67
            return null;
68
        }
69
    });
70

1✔
71
    /**
72
     * 面包屑的分隔符,不传值默认为 ">"
73
     * @type slash | backslash | vertical-line
74
     */
75
    readonly thySeparator = input<'slash' | 'backslash' | 'vertical-line'>();
76

77
    /**
78
     * 面包屑的每一项数据
79
     */
1✔
80
    readonly items = input<SafeAny[]>(undefined, { alias: 'thyItems' });
81

82
    /**
83
     * 最大显示数量,超出此数量后,面包屑会被省略, 0 表示不省略(仅当传入 thyItems 时有效)
84
     */
85
    readonly thyMaxCount = input(4, { transform: numberAttribute });
86

87
    /**
88
     * 是否可点击弹出已被省略的面包屑项(仅当传入 thyItems 时有效)
89
     */
90
    readonly thyExpandable = input(true, { transform: coerceBooleanProperty });
91

92
    /**
93
     * 面包屑的每一项模板(仅当传入 thyItems 时有效)
94
     */
95
    readonly itemTemplate = contentChild<TemplateRef<SafeAny>>('item');
96

97
    public ellipsisItemId = THY_BREADCRUMB_ITEM_ELLIPSIS_ID;
98

99
    public readonly processedItems: Signal<{
100
        ellipsisItems: SafeAny[];
101
        showItems: SafeAny[];
102
    }> = computed(() => {
103
        const items = this.items();
104
        if (!items?.length) {
105
            return;
106
        }
107
        const thyMaxCount = this.thyMaxCount();
108
        if (thyMaxCount && items.length > thyMaxCount) {
109
            const ellipsisIndex = items.length - thyMaxCount + 2;
110
            return {
111
                ellipsisItems: items.slice(1, ellipsisIndex),
112
                showItems: [items[0], ELLIPSIS_ITEM, ...items.slice(ellipsisIndex)]
113
            };
114
        } else {
115
            return {
116
                ellipsisItems: [],
117
                showItems: [...items]
118
            };
119
        }
120
    });
121
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc