11import {
2- AfterViewInit ,
32 booleanAttribute ,
43 computed ,
54 Directive ,
6- DoCheck ,
5+ effect ,
76 ElementRef ,
8- Input ,
7+ inject ,
98 input ,
10- OnChanges ,
119 OnDestroy ,
1210 output ,
1311 Renderer2 ,
14- SimpleChanges
12+ signal
1513} from '@angular/core' ;
1614import { AnimationBuilder , AnimationPlayer , useAnimation } from '@angular/animations' ;
1715
@@ -22,19 +20,31 @@ import {
2220 expandHorizontalAnimation
2321} from './collapse.animations' ;
2422
25- // todo
2623@Directive ( {
2724 selector : '[cCollapse]' ,
2825 exportAs : 'cCollapse' ,
2926 standalone : true ,
30- host : { '[class]' : 'hostClasses()' }
27+ host : { '[class]' : 'hostClasses()' , '[style]' : '{display: "none"}' }
3128} )
32- export class CollapseDirective implements OnDestroy , AfterViewInit , DoCheck , OnChanges {
29+ export class CollapseDirective implements OnDestroy {
30+ readonly #hostElement = inject ( ElementRef ) ;
31+ readonly #renderer = inject ( Renderer2 ) ;
32+ readonly #animationBuilder = inject ( AnimationBuilder ) ;
33+ #player: AnimationPlayer | undefined = undefined ;
34+
3335 /**
3436 * @ignore
35- * todo: 'animate' input signal for navbar
3637 */
37- @Input ( { transform : booleanAttribute } ) animate : boolean = true ;
38+ readonly animateInput = input ( true , { transform : booleanAttribute , alias : 'animate' } ) ;
39+
40+ readonly animate = signal ( true ) ;
41+
42+ readonly animateInputEffect = effect (
43+ ( ) => {
44+ this . animate . set ( this . animateInput ( ) ) ;
45+ } ,
46+ { allowSignalWrites : true }
47+ ) ;
3848
3949 /**
4050 * Set horizontal collapsing to transition the width instead of height.
@@ -47,18 +57,31 @@ export class CollapseDirective implements OnDestroy, AfterViewInit, DoCheck, OnC
4757 * Toggle the visibility of collapsible element.
4858 * @type boolean
4959 * @default false
50- * todo: 'visible' input signal
5160 */
52- @Input ( { transform : booleanAttribute } )
53- set visible ( value ) {
54- this . _visible = value ;
55- }
61+ readonly visibleInput = input ( false , { transform : booleanAttribute , alias : 'visible' } ) ;
5662
57- get visible ( ) : boolean {
58- return this . _visible ;
59- }
63+ readonly visibleChange = output < boolean > ( ) ;
64+
65+ readonly visibleInputEffect = effect (
66+ ( ) => {
67+ this . visible . set ( this . visibleInput ( ) ) ;
68+ } ,
69+ { allowSignalWrites : true }
70+ ) ;
71+
72+ readonly visible = signal < boolean > ( false ) ;
6073
61- private _visible : boolean = false ;
74+ #init = false ;
75+
76+ readonly visibleEffect = effect (
77+ ( ) => {
78+ const visible = this . visible ( ) ;
79+
80+ ( this . #init || visible ) && this . createPlayer ( visible ) ;
81+ this . #init = true ;
82+ } ,
83+ { allowSignalWrites : true }
84+ ) ;
6285
6386 /**
6487 * Add `navbar` prop for grouping and hiding navbar contents by a parent breakpoint.
@@ -83,71 +106,38 @@ export class CollapseDirective implements OnDestroy, AfterViewInit, DoCheck, OnC
83106 */
84107 readonly collapseChange = output < string > ( ) ;
85108
86- private player ! : AnimationPlayer ;
87- private readonly host : HTMLElement ;
88- // private scrollHeight!: number;
89- private scrollWidth ! : number ;
90- private collapsing : boolean = false ;
91-
92- constructor (
93- private readonly hostElement : ElementRef ,
94- private readonly renderer : Renderer2 ,
95- private readonly animationBuilder : AnimationBuilder
96- ) {
97- this . host = this . hostElement . nativeElement ;
98- this . renderer . setStyle ( this . host , 'display' , 'none' ) ;
99- }
100-
101109 readonly hostClasses = computed ( ( ) => {
102110 return {
103111 'navbar-collapse' : this . navbar ( ) ,
104112 'collapse-horizontal' : this . horizontal ( )
105113 } as Record < string , boolean > ;
106114 } ) ;
107115
108- ngAfterViewInit ( ) : void {
109- if ( this . visible ) {
110- this . toggle ( ) ;
111- }
112- }
113-
114116 ngOnDestroy ( ) : void {
115117 this . destroyPlayer ( ) ;
116118 }
117119
118- ngOnChanges ( changes : SimpleChanges ) : void {
119- if ( changes [ 'visible' ] ) {
120- if ( ! changes [ 'visible' ] . firstChange || ! changes [ 'visible' ] . currentValue ) {
121- this . toggle ( changes [ 'visible' ] . currentValue ) ;
122- }
123- }
124- }
125-
126- ngDoCheck ( ) : void {
127- if ( this . _visible !== this . visible ) {
128- this . toggle ( ) ;
129- }
130- }
131-
132- toggle ( visible = this . visible ) : void {
133- this . createPlayer ( visible ) ;
134- this . player ?. play ( ) ;
120+ toggle ( visible = ! this . visible ( ) ) : void {
121+ this . visible . set ( visible ) ;
135122 }
136123
137124 destroyPlayer ( ) : void {
138- this . player ?. destroy ( ) ;
125+ this . #player?. destroy ( ) ;
126+ this . #player = undefined ;
139127 }
140128
141- createPlayer ( visible : boolean = this . visible ) : void {
142- if ( this . player ?. hasStarted ( ) ) {
129+ createPlayer ( visible : boolean = this . visible ( ) ) : void {
130+ if ( this . # player?. hasStarted ( ) ) {
143131 this . destroyPlayer ( ) ;
144132 }
145133
134+ const host : HTMLElement = this . #hostElement. nativeElement ;
135+
146136 if ( visible ) {
147- this . renderer . removeStyle ( this . host , 'display' ) ;
137+ this . # renderer. removeStyle ( host , 'display' ) ;
148138 }
149139
150- const duration = this . animate ? this . duration ( ) : '0ms' ;
140+ const duration = this . animate ( ) ? this . duration ( ) : '0ms' ;
151141
152142 const expand = this . horizontal ( ) ? expandHorizontalAnimation : expandAnimation ;
153143 const collapse = this . horizontal ( ) ? collapseHorizontalAnimation : collapseAnimation ;
@@ -156,53 +146,48 @@ export class CollapseDirective implements OnDestroy, AfterViewInit, DoCheck, OnC
156146 const capitalizedDimension = dimension [ 0 ] . toUpperCase ( ) + dimension . slice ( 1 ) ;
157147 const scrollSize = `scroll${ capitalizedDimension } ` ;
158148
159- const animationFactory = this . animationBuilder ?. build (
149+ const animationFactory = this . # animationBuilder?. build (
160150 useAnimation ( visible ? expand : collapse , { params : { time : duration , easing : this . transition ( ) } } )
161151 ) ;
162152
163- this . player = animationFactory . create ( this . host ) ;
153+ this . # player = animationFactory . create ( host ) ;
164154
165- this . renderer . setStyle ( this . host , dimension , visible ? 0 : ` ${ this . host . getBoundingClientRect ( ) [ dimension ] } px` ) ;
155+ ! visible && host . offsetHeight && host . style [ dimension ] && host . scrollHeight ;
166156
167- ! visible && this . host . offsetHeight ;
157+ this . #renderer . setStyle ( host , dimension , visible ? 0 : ` ${ host . getBoundingClientRect ( ) [ dimension ] } px` ) ;
168158
169- this . player . onStart ( ( ) => {
159+ this . # player. onStart ( ( ) => {
170160 this . setMaxSize ( ) ;
171- this . renderer . removeClass ( this . host , 'collapse' ) ;
172- this . renderer . addClass ( this . host , 'collapsing' ) ;
173- this . renderer . removeClass ( this . host , 'show' ) ;
174- this . collapsing = true ;
175- if ( visible ) {
176- this . renderer . setStyle ( this . host , dimension , `${ this . hostElement . nativeElement [ scrollSize ] } px` ) ;
177- } else {
178- this . renderer . setStyle ( this . host , dimension , '' ) ;
179- }
180- this . collapseChange . emit ( visible ? 'opening' : 'collapsing' ) ;
161+ this . #renderer. removeClass ( host , 'collapse' ) ;
162+ this . #renderer. addClass ( host , 'collapsing' ) ;
163+ this . #renderer. removeClass ( host , 'show' ) ;
164+ this . #renderer. setStyle ( host , dimension , visible ? `${ ( host as any ) [ scrollSize ] } px` : '' ) ;
165+ this . collapseChange ?. emit ( visible ? 'opening' : 'collapsing' ) ;
181166 } ) ;
182- this . player . onDone ( ( ) => {
183- this . visible = visible ;
184- this . collapsing = false ;
185- this . renderer . removeClass ( this . host , 'collapsing' ) ;
186- this . renderer . addClass ( this . host , 'collapse' ) ;
167+
168+ this . #player. onDone ( ( ) => {
169+ this . #renderer. removeClass ( host , 'collapsing' ) ;
170+ this . #renderer. addClass ( host , 'collapse' ) ;
187171 if ( visible ) {
188- this . renderer . addClass ( this . host , 'show' ) ;
189- this . renderer . setStyle ( this . host , dimension , '' ) ;
172+ this . # renderer. addClass ( host , 'show' ) ;
173+ this . # renderer. setStyle ( host , dimension , '' ) ;
190174 } else {
191- this . renderer . removeClass ( this . host , 'show' ) ;
175+ this . # renderer. removeClass ( host , 'show' ) ;
192176 }
193- this . collapseChange . emit ( visible ? 'open' : 'collapsed' ) ;
177+ this . collapseChange ?. emit ( visible ? 'open' : 'collapsed' ) ;
178+ this . destroyPlayer ( ) ;
179+ this . visibleChange . emit ( visible ) ;
194180 } ) ;
181+
182+ this . #player?. play ( ) ;
195183 }
196184
197185 setMaxSize ( ) {
198- // setTimeout(() => {
186+ const host = this . #hostElement . nativeElement ;
199187 if ( this . horizontal ( ) ) {
200- this . scrollWidth = this . host . scrollWidth ;
201- this . scrollWidth > 0 && this . renderer . setStyle ( this . host , 'maxWidth' , `${ this . scrollWidth } px` ) ;
188+ host . scrollWidth > 0 && this . #renderer. setStyle ( host , 'maxWidth' , `${ host . scrollWidth } px` ) ;
202189 // } else {
203- // this.scrollHeight = this.host.scrollHeight;
204- // this.scrollHeight > 0 && this.renderer.setStyle(this.host, 'maxHeight', `${this.scrollHeight}px`);
190+ // host.scrollHeight > 0 && this.#renderer.setStyle(host, 'maxHeight', `${host.scrollHeight}px`);
205191 }
206- // });
207192 }
208193}
0 commit comments