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

atinc / ngx-tethys / #55

30 Jul 2025 07:08AM UTC coverage: 9.866% (-80.4%) from 90.297%
#55

push

why520crazy
feat(empty): add setMessage for update display text #TINFR-2616

92 of 6794 branches covered (1.35%)

Branch coverage included in aggregate %.

2014 of 14552 relevant lines covered (13.84%)

6.15 hits per line

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

3.23
/src/slide/slide-container.component.ts
1
import { ThyAbstractOverlayContainer, ThyPortalOutlet } from 'ngx-tethys/core';
2
import { helpers } from 'ngx-tethys/util';
3
import { Observable, Subject } from 'rxjs';
4
import { filter, startWith, takeUntil } from 'rxjs/operators';
5

6
import { AnimationEvent } from '@angular/animations';
7
import { ViewportRuler } from '@angular/cdk/overlay';
8
import { PortalModule } from '@angular/cdk/portal';
9
import { DOCUMENT } from '@angular/common';
10
import { ChangeDetectorRef, Component, ElementRef, NgZone, OnDestroy, Renderer2, ViewChild, inject } from '@angular/core';
11
import { useHostRenderer } from '@tethys/cdk/dom';
12

13
import { thySlideAnimations } from './slide-animations';
14
import { slideAbstractOverlayOptions, ThySlideConfig, ThySlideFromTypes } from './slide.config';
15

16
/**
1✔
17
 * @private
18
 */
×
19
@Component({
20
    selector: 'thy-slide-container',
21
    template: ` <ng-template thyPortalOutlet></ng-template> `,
×
22
    animations: [thySlideAnimations.slideContainer],
23
    host: {
24
        class: 'thy-slide-container',
×
25
        '[class.thy-slide-push]': 'isPush',
26
        '[class.thy-slide-side]': 'isSide',
27
        '[class.thy-slide-over]': '!isPush && !isSide',
×
28
        tabindex: '-1',
×
29
        '[attr.role]': `'slide'`,
×
30
        '[@slideContainer]': 'animationState',
31
        '(@slideContainer.start)': 'onAnimationStart($event)',
32
        '(@slideContainer.done)': 'onAnimationDone($event)',
×
33
        '[style.width.px]': 'slideContainerStyles.width',
34
        '[style.height.px]': 'slideContainerStyles.height',
×
35
        '[style.max-height.px]': 'slideContainerStyles.height'
36
    },
37
    imports: [PortalModule, ThyPortalOutlet]
×
38
})
39
export class ThySlideContainer extends ThyAbstractOverlayContainer implements OnDestroy {
×
40
    private elementRef = inject(ElementRef);
41
    private document = inject(DOCUMENT);
×
42
    config = inject(ThySlideConfig);
43
    private renderer = inject(Renderer2);
×
44
    private viewportRuler = inject(ViewportRuler);
45
    private readonly ngZone = inject(NgZone);
×
46

47
    @ViewChild(ThyPortalOutlet, { static: true })
48
    portalOutlet: ThyPortalOutlet;
49

×
50
    animationOpeningDone: Observable<AnimationEvent>;
51

52
    animationClosingDone: Observable<AnimationEvent>;
×
53

×
54
    animationState: ThySlideFromTypes = 'void';
×
55

×
56
    slideContainerStyles: { width?: number; height?: number } = {};
×
57

×
58
    private drawerContainerElement: HTMLElement;
×
59

×
60
    private ngUnsubscribe$ = new Subject<void>();
×
61

×
62
    private hostRenderer = useHostRenderer();
×
63

×
64
    get isPush() {
×
65
        return this.config.mode === 'push' && !!this.drawerContainerElement;
×
66
    }
67

×
68
    get isSide() {
×
69
        return this.config.mode === 'side' && !!this.drawerContainerElement;
70
    }
×
71

×
72
    private get isLeftOrRight(): boolean {
×
73
        return this.config.from === 'left' || this.config.from === 'right';
74
    }
75

×
76
    private get hostOffset() {
×
77
        let offset = 0;
78
        if (this.isLeftOrRight) {
×
79
            offset = this.elementRef.nativeElement.clientWidth + this.config.offset || 0;
×
80
        } else {
81
            offset = this.elementRef.nativeElement.clientHeight + this.config.offset || 0;
×
82
        }
×
83
        return offset;
84
    }
85

86
    private get transform() {
87
        switch (this.config.from) {
×
88
            case 'left':
×
89
                return `translateX(${this.hostOffset}px)`;
×
90
            case 'right':
×
91
                return `translateX(-${this.hostOffset}px)`;
×
92
            case 'top':
93
                return `translateY(${this.hostOffset}px)`;
94
            case 'bottom':
×
95
                return `translateY(${this.hostOffset}px)`;
×
96
        }
×
97
    }
98

×
99
    private get drawerContainerElementClass() {
100
        return `thy-slide-${this.config.mode}-drawer-container`;
101
    }
102

103
    constructor() {
104
        const changeDetectorRef = inject(ChangeDetectorRef);
×
105

106
        super(slideAbstractOverlayOptions, changeDetectorRef);
107
        this.animationOpeningDone = this.animationStateChanged.pipe(
108
            filter((event: AnimationEvent) => {
×
109
                return event.phaseName === 'done' && event.toState === this.animationState;
110
            })
111
        );
112
        this.animationClosingDone = this.animationStateChanged.pipe(
×
113
            filter((event: AnimationEvent) => {
×
114
                return event.phaseName === 'done' && event.toState === 'exit';
115
            })
116
        );
117
        this.setDrawerContainerElement();
×
118
        this.checkContainerWithinViewport();
×
119
        this.addDrawerContainerElementClass();
120
    }
121

122
    private setDrawerContainerElement() {
×
123
        if (typeof this.config.drawerContainer === 'string') {
×
124
            this.drawerContainerElement = this.config.drawerContainer && document.querySelector(this.config.drawerContainer);
125
        }
×
126
        if (this.config.drawerContainer instanceof ElementRef) {
×
127
            this.drawerContainerElement = this.config.drawerContainer.nativeElement;
128
        }
129
        if (this.config.drawerContainer instanceof HTMLElement) {
130
            this.drawerContainerElement = this.config.drawerContainer as HTMLElement;
×
131
        }
×
132
    }
133

×
134
    private setSlideContainerStyles() {
×
135
        let width, height, top, left;
136
        const drawerContainerElementRect = (this.drawerContainerElement || document.body).getBoundingClientRect();
137
        if (this.isLeftOrRight) {
138
            height = drawerContainerElementRect.height;
×
139
            top = drawerContainerElementRect.top;
×
140
            this.hostRenderer.setStyle('top', `${top}px`);
×
141
        } else {
142
            width = drawerContainerElementRect.width;
143
            left = drawerContainerElementRect.left;
×
144
            this.hostRenderer.setStyle('left', `${left}px`);
145
        }
×
146
        this.slideContainerStyles = {
147
            width: width,
148
            height: height
×
149
        };
150
    }
151

×
152
    private checkContainerWithinViewport() {
153
        this.viewportRuler
154
            .change(100)
×
155
            .pipe(startWith(null), takeUntil(this.ngUnsubscribe$))
156
            .subscribe(() => {
157
                this.ngZone.run(() => this.setSlideContainerStyles());
×
158
            });
×
159
    }
×
160

×
161
    private addDrawerContainerElementClass() {
162
        if (this.drawerContainerElement) {
1✔
163
            this.renderer.addClass(this.drawerContainerElement, this.drawerContainerElementClass);
1✔
164
        }
165
    }
166

167
    private removeDrawerContainerElementClass() {
1✔
168
        if (this.drawerContainerElement) {
169
            this.renderer.removeClass(this.drawerContainerElement, this.drawerContainerElementClass);
170
        }
171
    }
172

173
    private setDrawerContainerElementStyle() {
174
        if (this.isSide) {
175
            this.renderer.setStyle(this.drawerContainerElement, `margin-${this.config.from}`, `${this.hostOffset}px`);
176
        } else if (this.isPush) {
177
            this.renderer.setStyle(this.drawerContainerElement, `transform`, this.transform);
178
        }
179
    }
180

181
    private removeDrawerContainerElementStyle() {
182
        if (this.isSide) {
183
            this.renderer.removeStyle(this.drawerContainerElement, `margin-${this.config.from}`);
184
        } else if (this.isPush) {
185
            this.renderer.removeStyle(this.drawerContainerElement, `transform`);
186
        }
187
    }
188

189
    beforeAttachPortal(): void {
190
        if (this.config.offset) {
191
            this.hostRenderer.setStyle(this.config.from, `${this.config.offset}px`);
192
            this.animationState = helpers.camelCase(['offset', this.config.from]) as ThySlideFromTypes;
193
        } else {
194
            this.animationState = this.config.from;
195
        }
196
        this.setDrawerContainerElementStyle();
197
    }
198

199
    beforeDetachPortal(): void {
200
        this.removeDrawerContainerElementStyle();
201
    }
202

203
    onAnimationDone(event: AnimationEvent) {
204
        this.animationStateChanged.emit(event);
205
    }
206

207
    onAnimationStart(event: AnimationEvent) {
208
        this.animationStateChanged.emit(event);
209
    }
210

211
    ngOnDestroy() {
212
        super.destroy();
213
        this.removeDrawerContainerElementClass();
214
        this.ngUnsubscribe$.next();
215
        this.ngUnsubscribe$.complete();
216
    }
217
}
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