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

atinc / ngx-tethys / 3a07857c-fe40-47f4-bc02-6b91359dbc8f

30 Aug 2023 09:02AM UTC coverage: 90.195% (+0.08%) from 90.118%
3a07857c-fe40-47f4-bc02-6b91359dbc8f

Pull #2821

circleci

why520crazy
docs: add display block
Pull Request #2821: feat(grid): add thyFlex and thyFlexItem #INFR-9429

5167 of 6387 branches covered (0.0%)

Branch coverage included in aggregate %.

62 of 62 new or added lines in 5 files covered. (100.0%)

13009 of 13765 relevant lines covered (94.51%)

972.1 hits per line

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

94.17
/src/layout/sidebar.component.ts
1
import { NgClass, NgIf, NgStyle, NgTemplateOutlet } from '@angular/common';
2
import {
3
    Component,
4
    ElementRef,
5
    EventEmitter,
6
    Host,
7
    HostBinding,
8
    HostListener,
9
    Input,
10
    OnDestroy,
11
    OnInit,
12
    Optional,
1✔
13
    Output,
1✔
14
    TemplateRef
15
} from '@angular/core';
16
import { ThyHotkeyDispatcher } from '@tethys/cdk/hotkey';
17
import { isMacPlatform } from '@tethys/cdk/is';
18
import { InputBoolean, InputNumber } from 'ngx-tethys/core';
19
import { ThyIconComponent } from 'ngx-tethys/icon';
1✔
20
import { ThyResizableDirective, ThyResizeEvent, ThyResizeHandleComponent } from 'ngx-tethys/resizable';
21
import { ThyTooltipDirective } from 'ngx-tethys/tooltip';
122✔
22
import { coerceBooleanProperty } from 'ngx-tethys/util';
6✔
23
import { Subscription } from 'rxjs';
24
import { ThyLayoutComponent } from './layout.component';
25

116✔
26
const LG_WIDTH = 300;
27
const SIDEBAR_DEFAULT_WIDTH = 240;
28

29
export type ThySidebarTheme = 'white' | 'light' | 'dark';
2✔
30

31
export type ThySidebarDirection = 'left' | 'right';
32

2✔
33
/**
34
 * 布局侧边栏组件
35
 * @name thy-sidebar
29✔
36
 * @order 20
1✔
37
 */
38
@Component({
29✔
39
    selector: 'thy-sidebar',
40
    preserveWhitespaces: false,
41
    template: `
82✔
42
        <ng-content></ng-content>
43
        <div
44
            thyResizable
27✔
45
            class="sidebar-drag"
46
            *ngIf="thyDraggable"
47
            thyBounds="window"
27✔
48
            [thyMaxWidth]="thyDragMaxWidth"
49
            [thyMinWidth]="thyCollapsedWidth"
50
            (thyResize)="resizeHandler($event)"
27✔
51
            (thyResizeStart)="resizeStart()"
52
            (thyResizeEnd)="resizeEnd()"
53
            [style.display]="!isResizable ? 'contents' : null">
35✔
54
            <thy-resize-handle
35✔
55
                *ngIf="!thyCollapsed"
9✔
56
                [thyDirection]="thyDirection === 'right' ? 'left' : 'right'"
57
                class="sidebar-resize-handle"
58
                thyLine="true"
26✔
59
                (mouseenter)="toggleResizable($event, 'enter')"
60
                (mouseleave)="toggleResizable($event, 'leave')"
61
                (dblclick)="restoreToDefaultWidth()">
62
            </thy-resize-handle>
375✔
63
        </div>
64
        <div *ngIf="thyCollapsible" class="sidebar-collapse-line"></div>
65
        <div
37✔
66
            *ngIf="thyCollapsible && thyTrigger !== null"
67
            class="sidebar-collapse"
68
            [ngClass]="{ 'collapse-visible': collapseVisible, 'collapse-hidden': collapseHidden }"
230✔
69
            (click)="toggleCollapse($event)"
70
            [thyTooltip]="!thyTrigger && collapseTip">
71
            <ng-template [ngTemplateOutlet]="thyTrigger || defaultTrigger"></ng-template>
122✔
72
            <ng-template #defaultTrigger>
73
                <thy-icon class="sidebar-collapse-icon" [thyIconName]="this.thyCollapsed ? 'indent' : 'outdent'"></thy-icon>
74
            </ng-template>
122✔
75
        </div>
76
    `,
77
    host: {
28✔
78
        class: 'thy-layout-sidebar',
28✔
79
        '[class.thy-layout-sidebar-right]': 'thyDirection === "right"',
28✔
80
        '[class.thy-layout-sidebar--clear-border-right]': 'thyDirection === "left" && !isDivided',
28✔
81
        '[class.thy-layout-sidebar--clear-border-left]': 'thyDirection === "right" && !isDivided',
28✔
82
        '[class.sidebar-theme-light]': 'thyTheme === "light"',
28✔
83
        '[class.sidebar-theme-dark]': 'thyTheme === "dark"',
28✔
84
        '[class.thy-layout-sidebar-isolated]': 'sidebarIsolated'
28✔
85
    },
28✔
86
    standalone: true,
28✔
87
    imports: [
28✔
88
        NgTemplateOutlet,
28✔
89
        NgIf,
28✔
90
        ThyResizeHandleComponent,
91
        ThyResizableDirective,
92
        ThyIconComponent,
28!
93
        ThyTooltipDirective,
28✔
94
        NgClass,
95
        NgStyle
28✔
96
    ]
2✔
97
})
98
export class ThySidebarComponent implements OnInit, OnDestroy {
28✔
99
    thyLayoutSidebarWidth: number;
100

101
    isDivided = true;
9✔
102

2✔
103
    sidebarIsolated = false;
104

105
    @HostBinding('style.width.px') get sidebarWidth() {
106
        if (this.thyCollapsible && this.thyCollapsed) {
35✔
107
            return this.thyCollapsedWidth;
35!
108
        } else {
109
            return this.thyLayoutSidebarWidth;
110
        }
5✔
111
    }
1✔
112

113
    @HostListener('mouseenter', ['$event'])
4!
114
    mouseenter($event: MouseEvent) {
×
115
        this.resizeHandleHover($event, 'enter');
116
    }
4✔
117

2✔
118
    @HostListener('mouseleave', ['$event'])
2✔
119
    mouseleave($event: MouseEvent) {
2✔
120
        this.resizeHandleHover($event, 'leave');
2✔
121
    }
2✔
122

2✔
123
    /**
124
     * 宽度,默认是 240px,传入 `lg` 大小时宽度是300px
2✔
125
     * @default 240px
2✔
126
     */
127
    @Input('thyWidth')
128
    set thyWidth(value: any) {
3✔
129
        if (value === 'lg') {
3✔
130
            value = LG_WIDTH;
3✔
131
        }
132
        this.thyLayoutSidebarWidth = value || SIDEBAR_DEFAULT_WIDTH;
133
    }
2✔
134

2✔
135
    /**
136
     * sidebar 位置,默认在左侧
137
     */
4✔
138
    @Input() thyDirection: ThySidebarDirection = 'left';
139

140
    /**
5✔
141
     * sidebar 是否有分割线。当`thyDirection`值为`left`时,控制右侧是否有分割线;当`thyDirection`值为`right`时,控制左侧是否有分割线。
5✔
142
     * @default true
5✔
143
     */
144
    @Input('thyDivided')
145
    set thyDivided(value: string) {
2✔
146
        this.isDivided = coerceBooleanProperty(value);
147
    }
148

1!
149
    /**
×
150
     * 右侧是否有边框,已废弃,请使用 thyDivided
151
     * @deprecated please use thyDivided
1!
152
     * @default true
1✔
153
     */
154
    @Input('thyHasBorderRight')
155
    set thyHasBorderRight(value: string) {
28✔
156
        this.thyDivided = value;
157
    }
1✔
158

159
    /**
160
     * 左侧是否有边框,已废弃,请使用 thyDivided
161
     * @deprecated please use thyDivided
162
     * @default true
1✔
163
     */
164
    @Input('thyHasBorderLeft')
165
    set thyHasBorderLeft(value: string) {
166
        this.thyDivided = value;
167
    }
168

169
    /**
170
     * 是否和右侧 /左侧隔离,当为 true 时距右侧 /左侧会有 margin,同时边框会去掉
171
     * @default false
172
     */
173
    @Input('thyIsolated')
174
    set thyIsolated(value: string) {
175
        this.sidebarIsolated = coerceBooleanProperty(value);
176
    }
177

178
    /**
179
     * 宽度是否可以拖拽
180
     * @default false
181
     */
182
    @Input() @InputBoolean() thyDraggable: boolean = false;
183

184
    /**
185
     * 拖拽的最大宽度
186
     */
1✔
187
    @Input() @InputNumber() thyDragMaxWidth: number;
188

189
    /**
190
     * 展示收起的触发器自定义模板,默认显示展开收起的圆形图标,设置为 null 表示不展示触发元素,手动控制展开收起状态
1✔
191
     * @type null | undefined | TemplateRef<any>
192
     * @default undefined
193
     */
194
    @Input() thyTrigger: null | undefined | TemplateRef<any> = undefined;
1✔
195

196
    /**
197
     * 收起状态改变后的事件
198
     */
199
    @Output()
1✔
200
    thyCollapsedChange = new EventEmitter<boolean>();
201

202
    /**
203
     * 拖拽宽度的修改事件
204
     */
1✔
205
    @Output()
206
    thyDragWidthChange = new EventEmitter<number>();
207

208
    /**
1✔
209
     * 开启收起/展开功能
210
     * @default false
211
     */
212
    @Input() @InputBoolean() set thyCollapsible(collapsible: boolean) {
213
        this.collapsible = collapsible;
214
        if (this.collapsible) {
215
            this.subscribeHotkeyEvent();
216
        } else {
217
            this.hotkeySubscription?.unsubscribe();
218
        }
219
    }
220

221
    get thyCollapsible() {
222
        return this.collapsible;
223
    }
224

225
    /**
226
     * 是否是收起
227
     * @default false
228
     */
229
    @Input() @InputBoolean() set thyCollapsed(value: boolean) {
230
        this.isCollapsed = value;
231
    }
232

233
    get thyCollapsed() {
234
        return this.isCollapsed;
235
    }
236

237
    /**
238
     * 收起后的宽度
239
     */
240
    @Input() @InputNumber() thyCollapsedWidth = 20;
241

242
    /**
243
     * 主题
244
     * @type white | light | dark
245
     * @default white
246
     */
247
    @Input() thyTheme: ThySidebarTheme;
248

249
    /**
250
     * 默认宽度,双击后可恢复到此宽度,默认是 240px,传入 lg 大小时宽度是300px
251
     */
252
    @Input() thyDefaultWidth: string | number;
253

254
    @HostBinding('class.sidebar-collapse-show')
255
    get collapseVisibility() {
256
        return this.thyCollapsed;
257
    }
258

259
    @HostBinding('class.remove-transition')
260
    get removeTransition() {
261
        return this.isRemoveTransition;
262
    }
263

264
    collapseTip: string;
265

266
    collapsible: boolean;
267

268
    isCollapsed = false;
269

270
    originWidth: number = SIDEBAR_DEFAULT_WIDTH;
271

272
    collapseVisible: boolean;
273

274
    collapseHidden: boolean;
275

276
    isRemoveTransition: boolean;
277

278
    isResizable: boolean;
279

280
    private hotkeySubscription: Subscription;
281

282
    constructor(
283
        @Optional() @Host() private thyLayoutComponent: ThyLayoutComponent,
284
        public elementRef: ElementRef,
285
        private hotkeyDispatcher: ThyHotkeyDispatcher
286
    ) {}
287

288
    ngOnInit() {
289
        if (this.thyLayoutComponent) {
290
            this.thyLayoutComponent.hasSidebar = true;
291
        }
292
        if (this.thyDirection === 'right') {
293
            this.thyLayoutComponent.isSidebarRight = true;
294
        }
295
        this.updateCollapseTip();
296
    }
297

298
    private subscribeHotkeyEvent() {
299
        this.hotkeySubscription = this.hotkeyDispatcher.keydown(['Control+/', 'Meta+/']).subscribe(() => {
300
            this.toggleCollapse();
301
        });
302
    }
303

304
    private updateCollapseTip() {
305
        this.collapseTip = this.thyCollapsed ? '展开' : '收起';
306
        this.collapseTip = this.collapseTip + (isMacPlatform() ? `(⌘ + /)` : `(Ctrl + /)`);
307
    }
308

309
    resizeHandler({ width }: ThyResizeEvent) {
310
        if (width === this.thyLayoutSidebarWidth) {
311
            return;
312
        }
313
        if (this.thyCollapsible && width < this.thyCollapsedWidth) {
314
            return;
315
        }
316
        if (this.thyCollapsible && width === this.thyCollapsedWidth) {
317
            this.thyCollapsed = true;
318
            setTimeout(() => this.updateCollapseTip(), 200);
319
            this.thyCollapsedChange.emit(this.isCollapsed);
320
            this.thyLayoutSidebarWidth = this.originWidth;
321
            this.collapseVisible = false;
322
            return;
323
        }
324
        this.thyLayoutSidebarWidth = width;
325
        this.thyDragWidthChange.emit(width);
326
    }
327

328
    resizeStart() {
329
        this.originWidth = this.thyLayoutSidebarWidth;
330
        this.collapseHidden = true;
331
        this.isRemoveTransition = true;
332
    }
333

334
    resizeEnd() {
335
        this.collapseHidden = false;
336
        this.isRemoveTransition = false;
337
    }
338

339
    resizeHandleHover(event: MouseEvent, type: 'enter' | 'leave') {
340
        this.collapseVisible = type === 'enter' && !this.thyCollapsed ? true : false;
341
    }
342

343
    toggleCollapse(event?: MouseEvent) {
344
        this.thyCollapsed = !this.thyCollapsed;
345
        setTimeout(() => this.updateCollapseTip(), 200);
346
        this.thyCollapsedChange.emit(this.isCollapsed);
347
    }
348

349
    public toggleResizable(event: MouseEvent, type: 'enter' | 'leave') {
350
        this.isResizable = type === 'enter' ? true : false;
351
    }
352

353
    restoreToDefaultWidth() {
354
        if (this.thyDefaultWidth === 'lg') {
355
            this.thyDefaultWidth = LG_WIDTH;
356
        }
357
        this.thyLayoutSidebarWidth = (this.thyDefaultWidth as number) || SIDEBAR_DEFAULT_WIDTH;
358
        this.thyDragWidthChange.emit(this.thyLayoutSidebarWidth);
359
    }
360

361
    ngOnDestroy(): void {
362
        this.hotkeySubscription?.unsubscribe();
363
    }
364
}
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