Skip to content

Commit 24ed302

Browse files
committed
feat(collapse): horizontal prop
1 parent d4b56e7 commit 24ed302

File tree

2 files changed

+86
-21
lines changed

2 files changed

+86
-21
lines changed

projects/coreui-angular/src/lib/collapse/collapse.animations.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,18 @@ export const collapseAnimation = animation([
1414
style({ height: 0, visibility: 'hidden', opacity: 0, overflow: 'hidden', paddingTop: 0, paddingBottom: 0, minHeight: 0 })
1515
)
1616
]);
17+
18+
export const expandHorizontalAnimation = animation([
19+
style({ width: 0, visibility: 'hidden', paddingLeft: 0, paddingRight: 0 }),
20+
animate('{{ time }} {{ easing }}', style({ width: '*', visibility: 'visible', paddingLeft: '*', paddingRight: '*', minWidth: '*' })
21+
),
22+
animate('{{ time }}', style({opacity: '*'})),
23+
]);
24+
25+
export const collapseHorizontalAnimation = animation([
26+
style({ width: '*', visibility: 'visible', paddingLeft: '*', paddingRight: '*', minWidth: '*' }),
27+
animate(
28+
'{{ time }} {{ easing }}',
29+
style({ width: 0, visibility: 'hidden', opacity: 0, paddingLeft: 0, paddingRight: 0, minWidth: 0 })
30+
)
31+
]);

projects/coreui-angular/src/lib/collapse/collapse.directive.ts

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,15 @@ import {
1414
} from '@angular/core';
1515
import { AnimationBuilder, AnimationPlayer, useAnimation } from '@angular/animations';
1616

17-
import { collapseAnimation, expandAnimation } from './collapse.animations';
1817
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
1918

19+
import {
20+
collapseAnimation,
21+
collapseHorizontalAnimation,
22+
expandAnimation,
23+
expandHorizontalAnimation
24+
} from './collapse.animations';
25+
2026
// todo
2127
// tslint:disable-next-line:no-conflicting-lifecycle
2228
@Directive({
@@ -25,6 +31,7 @@ import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
2531
})
2632
export class CollapseDirective implements OnChanges, OnDestroy, DoCheck, AfterViewInit {
2733

34+
static ngAcceptInputType_horizontal: BooleanInput;
2835
static ngAcceptInputType_navbar: BooleanInput;
2936

3037
private _animate = true;
@@ -39,6 +46,18 @@ export class CollapseDirective implements OnChanges, OnDestroy, DoCheck, AfterVi
3946
return this._animate;
4047
}
4148

49+
/**
50+
* Set horizontal collapsing to transition the width instead of height.
51+
*/
52+
@Input()
53+
set horizontal(value: boolean) {
54+
this._horizontal = coerceBooleanProperty(value);
55+
}
56+
get horizontal(): boolean {
57+
return this._horizontal;
58+
}
59+
private _horizontal: boolean = false;
60+
4261
private _visible = false;
4362
/**
4463
* Toggle the visibility of collapsible element.
@@ -66,11 +85,11 @@ export class CollapseDirective implements OnChanges, OnDestroy, DoCheck, AfterVi
6685
/**
6786
* @ignore
6887
*/
69-
@Input() duration = '400ms';
88+
@Input() duration = '350ms';
7089
/**
7190
* @ignore
7291
*/
73-
@Input() transition = 'ease-in-out';
92+
@Input() transition = 'ease';
7493
/**
7594
* Event emitted on visibility change. [docs]
7695
* @type string
@@ -79,32 +98,45 @@ export class CollapseDirective implements OnChanges, OnDestroy, DoCheck, AfterVi
7998

8099
private player!: AnimationPlayer;
81100
private readonly host: HTMLElement;
101+
private scrollHeight!: number;
102+
private scrollWidth!: number;
103+
private collapsing: boolean = false;
82104

83105
constructor(
84106
private hostElement: ElementRef,
85107
private renderer: Renderer2,
86108
private animationBuilder: AnimationBuilder
87109
) {
88110
this.host = this.hostElement.nativeElement;
89-
this.renderer.setStyle(this.host, 'display', 'none');
111+
this.setDisplay(false);
90112
}
91113

92114
@HostBinding('class')
93115
get hostClasses(): any {
94116
return {
95117
'navbar-collapse': this.navbar,
96-
show: this.visible
118+
show: this.visible,
119+
'collapse-horizontal': this.horizontal,
120+
collapsing: this.collapsing
121+
// collapse: !this.collapsing
97122
};
98123
}
99124

125+
ngAfterViewInit(): void {
126+
if (this.visible) {
127+
this.toggle();
128+
}
129+
}
130+
100131
ngOnDestroy(): void {
101132
this.destroyPlayer();
102133
}
103134

104135
ngOnChanges(changes: SimpleChanges): void {
105136
if (changes['visible']) {
106-
// tslint:disable-next-line:no-unused-expression
107-
(!changes['visible'].firstChange || !changes['visible'].currentValue) && this.toggle(changes['visible'].currentValue);
137+
if (!changes['visible'].firstChange || !changes['visible'].currentValue) {
138+
this.toggle(changes['visible'].currentValue);
139+
}
108140
}
109141
}
110142

@@ -115,39 +147,57 @@ export class CollapseDirective implements OnChanges, OnDestroy, DoCheck, AfterVi
115147
}
116148

117149
toggle(visible = this.visible): void {
118-
if (visible) {
119-
this.renderer.removeStyle(this.host, 'display');
120-
}
121-
this.visible = visible;
150+
this.setDisplay(true);
122151
this.createPlayer(visible);
123-
this.player.play();
152+
this.visible = visible;
153+
this.player?.play();
124154
}
125155

126156
destroyPlayer(): void {
127-
if (this.player) {
128-
this.player.destroy();
129-
}
157+
this.player?.destroy();
130158
}
131159

132160
createPlayer(visible: boolean = this.visible): void {
133-
if (this.player) {
161+
if (this.player?.hasStarted()) {
134162
this.destroyPlayer();
135163
}
136164

137165
let animationFactory;
138166

139167
const duration = this.animate ? this.duration : '0ms';
140168

169+
const expand = this.horizontal ? expandHorizontalAnimation : expandAnimation;
170+
const collapse = this.horizontal ? collapseHorizontalAnimation : collapseAnimation;
171+
141172
animationFactory = this.animationBuilder.build(
142-
useAnimation(visible ? expandAnimation : collapseAnimation, { params: { time: duration, easing: this.transition } })
173+
useAnimation(visible ? expand : collapse, { params: { time: duration, easing: this.transition } })
143174
);
144175

145176
this.player = animationFactory.create(this.host);
146-
this.player.onStart(() => {this.collapseChange.emit(visible ? 'opening' : 'collapsing'); });
147-
this.player.onDone(() => {this.collapseChange.emit(visible ? 'open' : 'collapsed'); });
177+
this.player.onStart(() => {
178+
this.setMaxSize();
179+
this.collapsing = true;
180+
this.collapseChange.emit(visible ? 'opening' : 'collapsing');
181+
});
182+
this.player.onDone(() => {
183+
this.collapsing = false;
184+
this.collapseChange.emit(visible ? 'open' : 'collapsed');
185+
});
148186
}
149187

150-
ngAfterViewInit(): void {
151-
this.toggle();
188+
setMaxSize() {
189+
setTimeout(() => {
190+
if (this.horizontal) {
191+
this.scrollWidth = this.host.scrollWidth;
192+
this.scrollWidth > 0 && this.renderer.setStyle(this.host, 'maxWidth', `${this.scrollWidth}px`);
193+
// } else {
194+
// this.scrollHeight = this.host.scrollHeight;
195+
// this.scrollHeight > 0 && this.renderer.setStyle(this.host, 'maxHeight', `${this.scrollHeight}px`);
196+
}
197+
});
198+
}
199+
200+
setDisplay(display: boolean) {
201+
display ? this.renderer.removeStyle(this.host, 'display') : this.renderer.setStyle(this.host, 'display', 'none');
152202
}
153203
}

0 commit comments

Comments
 (0)