Skip to content

Commit 8c15b75

Browse files
committed
refactor(offcanvas): animation classes, scrollbar behavior, cleanup
1 parent 8bdf397 commit 8c15b75

File tree

2 files changed

+64
-41
lines changed

2 files changed

+64
-41
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:host {
2+
display: none;
3+
}

projects/coreui-angular/src/lib/offcanvas/offcanvas/offcanvas.component.ts

Lines changed: 61 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,19 @@ import {
66
HostListener,
77
Inject,
88
Input,
9-
OnChanges,
109
OnDestroy,
1110
OnInit,
1211
Output,
1312
PLATFORM_ID,
14-
Renderer2,
15-
SimpleChanges
13+
Renderer2
1614
} from '@angular/core';
17-
import { animate, state, style, transition, trigger } from '@angular/animations';
15+
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
16+
import { animate, AnimationEvent, state, style, transition, trigger } from '@angular/animations';
1817
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
1918
import { Subscription } from 'rxjs';
2019

21-
import { OffcanvasService } from '../offcanvas.service';
2220
import { BackdropService } from '../../backdrop/backdrop.service';
23-
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
21+
import { OffcanvasService } from '../offcanvas.service';
2422

2523
let nextId = 0;
2624

@@ -29,29 +27,30 @@ let nextId = 0;
2927
animations: [
3028
trigger('showHide', [
3129
state(
32-
'true',
30+
'visible',
3331
style({
34-
visibility: 'visible'
32+
// visibility: 'visible'
3533
})
3634
),
3735
state(
38-
'false',
36+
'hidden',
3937
style({
40-
visibility: 'hidden'
38+
// visibility: 'hidden'
4139
})
4240
),
43-
transition('true => false', [animate('300ms')])
41+
transition('visible <=> *', [animate('300ms')])
4442
])
4543
],
4644
templateUrl: './offcanvas.component.html',
4745
styleUrls: ['./offcanvas.component.scss'],
4846
exportAs: 'cOffcanvas'
4947
})
50-
export class OffcanvasComponent implements OnChanges, OnInit, OnDestroy {
48+
export class OffcanvasComponent implements OnInit, OnDestroy {
49+
5150
static ngAcceptInputType_scroll: BooleanInput;
5251

5352
constructor(
54-
@Inject(DOCUMENT) private document: any,
53+
@Inject(DOCUMENT) private document: Document,
5554
@Inject(PLATFORM_ID) private platformId: any,
5655
private renderer: Renderer2,
5756
private hostElement: ElementRef,
@@ -96,6 +95,7 @@ export class OffcanvasComponent implements OnChanges, OnInit, OnDestroy {
9695
private _scroll = false;
9796

9897
@Input() id = `offcanvas-${this.placement}-${nextId++}`;
98+
9999
/**
100100
* Default role for offcanvas. [docs]
101101
* @type string
@@ -113,32 +113,32 @@ export class OffcanvasComponent implements OnChanges, OnInit, OnDestroy {
113113
/**
114114
* Toggle the visibility of offcanvas component.
115115
* @type boolean
116+
* @default false
116117
*/
117118
@Input()
118119
set visible(value: boolean) {
119120
this._visible = coerceBooleanProperty(value);
120-
if (value) {
121+
if (this._visible) {
121122
this.setBackdrop(this.backdrop);
122123
this.setFocus();
123124
} else {
124125
this.setBackdrop(false);
125126
}
126-
this.setScroll();
127127
this.visibleChange.emit(value);
128128
}
129129

130130
get visible(): boolean {
131131
return this._visible;
132132
}
133133

134-
private _visible!: boolean;
134+
private _visible: boolean = false;
135135

136136
/**
137137
* Event triggered on visible change.
138138
*/
139139
@Output() visibleChange = new EventEmitter<boolean>();
140140

141-
private activeBackdrop!: any;
141+
private activeBackdrop!: HTMLDivElement;
142142
private scrollbarWidth!: string;
143143

144144
private stateToggleSubscription!: Subscription;
@@ -149,7 +149,7 @@ export class OffcanvasComponent implements OnChanges, OnInit, OnDestroy {
149149
return {
150150
offcanvas: true,
151151
[`offcanvas-${this.placement}`]: !!this.placement,
152-
show: this.visible
152+
show: this.show
153153
};
154154
}
155155

@@ -164,8 +164,47 @@ export class OffcanvasComponent implements OnChanges, OnInit, OnDestroy {
164164
}
165165

166166
@HostBinding('@showHide')
167-
get animateType(): boolean {
168-
return this.visible;
167+
get animateTrigger(): string {
168+
return this.visible ? 'visible' : 'hidden';
169+
}
170+
171+
get show(): boolean {
172+
return this.visible && this._show;
173+
}
174+
175+
set show(value: boolean) {
176+
this._show = value;
177+
}
178+
179+
private _show = false;
180+
181+
@HostListener('@showHide.start', ['$event'])
182+
animateStart(event: AnimationEvent) {
183+
const scrollbarWidth = this.scrollbarWidth;
184+
if (event.toState === 'visible') {
185+
if (!this.scroll) {
186+
this.renderer.setStyle(this.document.body, 'overflow', 'hidden');
187+
this.renderer.setStyle(this.document.body, 'padding-right', scrollbarWidth);
188+
}
189+
this.renderer.addClass(this.hostElement.nativeElement, 'showing');
190+
} else {
191+
this.renderer.addClass(this.hostElement.nativeElement, 'hiding');
192+
}
193+
}
194+
195+
@HostListener('@showHide.done', ['$event'])
196+
animateDone(event: AnimationEvent) {
197+
setTimeout(() => {
198+
if (event.toState === 'visible') {
199+
this.renderer.removeClass(this.hostElement.nativeElement, 'showing');
200+
}
201+
if (event.toState === 'hidden') {
202+
this.renderer.removeClass(this.hostElement.nativeElement, 'hiding');
203+
this.renderer.removeStyle(this.document.body, 'overflow');
204+
this.renderer.removeStyle(this.document.body, 'paddingRight');
205+
}
206+
});
207+
this.show = this.visible;
169208
}
170209

171210
@HostListener('document:keydown', ['$event'])
@@ -181,22 +220,17 @@ export class OffcanvasComponent implements OnChanges, OnInit, OnDestroy {
181220
}
182221

183222
ngOnInit(): void {
184-
this.setScroll();
185223
this.scrollbarWidth = this.backdropService.scrollbarWidth;
186224
this.stateToggleSubscribe();
225+
// hotfix to avoid end offcanvas flicker on first render
226+
this.renderer.setStyle(this.hostElement.nativeElement, 'display', 'flex');
187227
}
188228

189229
ngOnDestroy(): void {
190230
this.offcanvasService.toggle({ show: false, id: this.id });
191231
this.stateToggleSubscribe(false);
192232
}
193233

194-
ngOnChanges(changes: SimpleChanges): void {
195-
if (changes['scroll']) {
196-
this.setScroll();
197-
}
198-
}
199-
200234
private stateToggleSubscribe(subscribe: boolean = true): void {
201235
if (subscribe) {
202236
this.stateToggleSubscription =
@@ -237,18 +271,4 @@ export class OffcanvasComponent implements OnChanges, OnInit, OnDestroy {
237271
setTimeout(() => this.hostElement.nativeElement.focus());
238272
}
239273
}
240-
241-
setScroll() {
242-
if (this.visible) {
243-
if (!this.scroll) {
244-
this.renderer.setStyle(this.document.body, 'overflow', 'hidden');
245-
this.renderer.setStyle(this.document.body, 'paddingRight.px', '0');
246-
}
247-
return;
248-
}
249-
if (!this.scroll) {
250-
this.renderer.removeStyle(this.document.body, 'overflow');
251-
this.renderer.removeStyle(this.document.body, 'paddingRight');
252-
}
253-
}
254274
}

0 commit comments

Comments
 (0)