11import {
22 booleanAttribute ,
33 Component ,
4- EventEmitter ,
5- HostBinding ,
4+ computed ,
5+ effect ,
66 inject ,
7- Input ,
7+ input ,
8+ linkedSignal ,
89 OnChanges ,
910 OnDestroy ,
1011 OnInit ,
11- Output ,
12+ output ,
1213 Renderer2 ,
14+ signal ,
1315 SimpleChanges
1416} from '@angular/core' ;
1517import { DOCUMENT } from '@angular/common' ;
@@ -23,7 +25,11 @@ import { SidebarBackdropService } from '../sidebar-backdrop/sidebar-backdrop.ser
2325 selector : 'c-sidebar' ,
2426 exportAs : 'cSidebar' ,
2527 template : '<ng-content />' ,
26- host : { class : 'sidebar' }
28+ host : {
29+ class : 'sidebar' ,
30+ '[class]' : 'hostClasses()' ,
31+ '[attr.inert]' : '!this.sidebarState.visible || null'
32+ }
2733} )
2834export class SidebarComponent implements OnChanges , OnDestroy , OnInit {
2935 readonly #document = inject < Document > ( DOCUMENT ) ;
@@ -32,14 +38,13 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
3238 readonly #sidebarService = inject ( SidebarService ) ;
3339 readonly #backdropService = inject ( SidebarBackdropService ) ;
3440
35- #visible = false ;
3641 #onMobile = false ;
3742 #layoutChangeSubscription! : Subscription ;
3843 #stateToggleSubscription! : Subscription ;
3944
40- state : ISidebarAction = {
45+ readonly state = signal < ISidebarAction > ( {
4146 sidebar : this
42- } ;
47+ } ) ;
4348
4449 #stateInitial = {
4550 narrow : false ,
@@ -48,105 +53,120 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
4853 } ;
4954
5055 /**
51- * Sets if the color of text should be colored for a light or dark background. [docs]
52- *
53- * @type 'dark' | 'light'
56+ * Sets if the color of text should be colored for a light or dark background.
57+ * @return 'dark' | 'light'
5458 */
55- @ Input ( ) colorScheme ?: 'dark' | 'light' ;
59+ readonly colorScheme = input < 'dark' | 'light' > ( ) ;
5660
5761 /**
58- * Sets html attribute id. [docs]
59- *
60- * @type string
62+ * Sets html attribute id.
63+ * @return string
6164 */
62- @ Input ( ) id ?: string ;
65+ readonly id = input < string > ( ) ;
6366
6467 /**
65- * Make sidebar narrow. [docs]
66- * @type boolean
68+ * Make sidebar narrow.
69+ * @return boolean
6770 * @default false
6871 */
69- @Input ( { transform : booleanAttribute } ) narrow : boolean = false ;
72+ readonly narrowInput = input ( false , { transform : booleanAttribute , alias : 'narrow' } ) ;
73+
74+ readonly #narrow = linkedSignal ( this . narrowInput ) ;
75+
76+ set narrow ( value ) {
77+ this . #narrow. set ( value ) ;
78+ }
79+
80+ get narrow ( ) {
81+ return this . #narrow( ) ;
82+ }
7083
7184 /**
7285 * Set sidebar to overlaid variant.
73- * @type boolean
86+ * @return boolean
7487 * @default false
7588 */
76- @ Input ( { transform : booleanAttribute } ) overlaid : boolean = false ;
89+ readonly overlaid = input ( false , { transform : booleanAttribute } ) ;
7790
7891 /**
79- * Components placement, there’s no default placement. [docs]
80- * @type 'start' | 'end'
92+ * Components placement, there’s no default placement.
93+ * @return 'start' | 'end'
8194 */
82- @ Input ( ) placement ?: 'start' | 'end' ;
95+ readonly placement = input < 'start' | 'end' > ( ) ;
8396
8497 /**
85- * Place sidebar in non-static positions. [docs]
98+ * Place sidebar in non-static positions.
99+ * @return 'fixed' | 'sticky'
86100 * @default 'fixed'
87101 */
88- @ Input ( ) position : 'fixed' | 'sticky' = 'fixed' ;
102+ readonly position = input < 'fixed' | 'sticky' > ( 'fixed' ) ;
89103
90104 /**
91- * Size the component small, large, or extra large. [docs]
105+ * Size the component small, large, or extra large.
106+ * @return 'sm' | 'lg' | 'xl'
92107 */
93- @ Input ( ) size ?: 'sm' | 'lg' | 'xl' ;
108+ readonly size = input < 'sm' | 'lg' | 'xl' > ( ) ;
94109
95110 /**
96- * Expand narrowed sidebar on hover. [docs]
111+ * Expand narrowed sidebar on hover.
97112 * @type boolean
98113 * @default false
99114 */
100- @Input ( { transform : booleanAttribute } ) unfoldable : boolean = false ;
115+ readonly unfoldableInput = input ( false , { transform : booleanAttribute , alias : 'unfoldable' } ) ;
116+
117+ readonly unfoldable = linkedSignal ( {
118+ source : this . unfoldableInput ,
119+ computation : ( value ) => value
120+ } ) ;
101121
102122 /**
103- * Toggle the visibility of sidebar component. [docs]
123+ * Toggle the visibility of sidebar component.
104124 * @type boolean
105125 * @default false
106126 */
107- @Input ( { transform : booleanAttribute } )
127+ readonly visibleInput = input ( false , { transform : booleanAttribute , alias : 'visible' } ) ;
128+
129+ readonly #visible = linkedSignal ( this . visibleInput ) ;
130+
131+ readonly #visibleEffect = effect ( ( ) => {
132+ this . visibleChange . emit ( this . #visible( ) ) ;
133+ } ) ;
134+
108135 set visible ( value : boolean ) {
109- const visible = value ;
110- if ( this . #visible !== visible ) {
111- this . #visible = visible ;
112- this . visibleChange . emit ( this . #visible) ;
113- }
136+ this . #visible. set ( value ) ;
114137 }
115138
116139 get visible ( ) {
117- return this . #visible;
140+ return this . #visible( ) ;
118141 }
119142
120143 /**
121- * Event emitted on visibility change. [docs]
122- * @type boolean
144+ * Event emitted on visibility change.
145+ * @return boolean
123146 */
124- @ Output ( ) visibleChange = new EventEmitter < boolean > ( ) ;
147+ readonly visibleChange = output < boolean > ( ) ;
125148
126149 set sidebarState ( value : ISidebarAction ) {
127150 const newState = value ;
128151 if ( 'toggle' in newState ) {
129152 if ( newState . toggle === 'visible' ) {
130- newState . visible = ! this . state . visible ;
131- this . visible = newState . visible ;
153+ newState . visible = ! this . state ( ) . visible ;
154+ this . # visible. set ( newState . visible ) ;
132155 } else if ( newState . toggle === 'unfoldable' ) {
133- newState . unfoldable = ! this . state . unfoldable ;
134- this . unfoldable = newState . unfoldable ;
156+ newState . unfoldable = ! this . state ( ) . unfoldable ;
157+ this . unfoldable . set ( newState . unfoldable ) ;
135158 }
136159 } else {
137- this . visible = ( newState . visible ?? this . visible ) && ! this . overlaid ;
160+ this . # visible. update ( ( visible ) => ( newState . visible ?? visible ) && ! this . overlaid ( ) ) ;
138161 }
139- this . state = {
140- ...this . state ,
141- ...newState
142- } ;
143- this . state . mobile && this . state . visible
162+ this . state . update ( ( state ) => ( { ...state , ...newState } ) ) ;
163+ this . state ( ) . mobile && this . state ( ) . visible
144164 ? this . #backdropService. setBackdrop ( this )
145165 : this . #backdropService. clearBackdrop ( ) ;
146166 }
147167
148168 get sidebarState ( ) : ISidebarAction {
149- return this . state ;
169+ return { ... this . state ( ) } ;
150170 }
151171
152172 get getMobileBreakpoint ( ) : string {
@@ -164,23 +184,26 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
164184 this . #backdropService. renderer = this . #renderer;
165185 }
166186
167- @HostBinding ( 'class' )
168- get getClasses ( ) : any {
169- const { mobile, visible } = this . sidebarState ;
187+ readonly hostClasses = computed ( ( ) => {
188+ const { mobile, visible } = { ...this . sidebarState } ;
189+ const unfoldable = this . unfoldable ( ) ;
190+ const placement = this . placement ( ) ;
191+ const colorScheme = this . colorScheme ( ) ;
192+ const size = this . size ( ) ;
170193 return {
171194 sidebar : true ,
172- 'sidebar-fixed' : this . position === 'fixed' && ! mobile ,
173- 'sidebar-narrow' : this . narrow && ! this . unfoldable ,
174- 'sidebar-narrow-unfoldable' : this . unfoldable ,
175- 'sidebar-overlaid' : this . overlaid ,
176- [ `sidebar-${ this . placement } ` ] : ! ! this . placement ,
177- [ `sidebar-${ this . colorScheme } ` ] : ! ! this . colorScheme ,
178- [ `sidebar-${ this . size } ` ] : ! ! this . size ,
195+ 'sidebar-fixed' : this . position ( ) === 'fixed' && ! mobile ,
196+ 'sidebar-narrow' : this . # narrow( ) && ! unfoldable ,
197+ 'sidebar-narrow-unfoldable' : unfoldable ,
198+ 'sidebar-overlaid' : this . overlaid ( ) ,
199+ [ `sidebar-${ placement } ` ] : ! ! placement ,
200+ [ `sidebar-${ colorScheme } ` ] : ! ! colorScheme ,
201+ [ `sidebar-${ size } ` ] : ! ! size ,
179202 show : visible ,
180203 // show: visible && this.#onMobile, //todo: check
181204 hide : ! visible
182205 } ;
183- }
206+ } ) ;
184207
185208 ngOnInit ( ) : void {
186209 this . setInitialState ( ) ;
@@ -194,7 +217,7 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
194217 }
195218
196219 ngOnChanges ( changes : SimpleChanges ) : void {
197- const oldStateMap = new Map ( Object . entries ( this . state ) ) ;
220+ const oldStateMap = new Map ( Object . entries ( this . state ( ) ) ) ;
198221 const newStateMap = new Map ( ) ;
199222 newStateMap . set ( 'sidebar' , this ) ;
200223
@@ -219,9 +242,9 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
219242
220243 setInitialState ( ) : void {
221244 this . #stateInitial = {
222- narrow : this . narrow ,
223- visible : this . visible ,
224- unfoldable : this . unfoldable
245+ narrow : this . # narrow( ) ,
246+ visible : this . # visible( ) ,
247+ unfoldable : this . unfoldable ( )
225248 } ;
226249 this . #sidebarService. toggle ( {
227250 ...this . #stateInitial,
@@ -232,8 +255,8 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
232255 private stateToggleSubscribe ( subscribe : boolean = true ) : void {
233256 if ( subscribe ) {
234257 this . #stateToggleSubscription = this . #sidebarService. sidebarState$ . subscribe ( ( state ) => {
235- if ( this === state . sidebar || this . id === state . id ) {
236- this . sidebarState = state ;
258+ if ( this === state . sidebar || this . id ( ) === state . id ) {
259+ this . sidebarState = { ... state } ;
237260 }
238261 } ) ;
239262 } else {
@@ -249,7 +272,7 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
249272
250273 this . #layoutChangeSubscription = layoutChanges . subscribe ( ( result : BreakpointState ) => {
251274 const isOnMobile = result . breakpoints [ onMobile ] ;
252- const isUnfoldable = isOnMobile ? false : this . unfoldable ;
275+ const isUnfoldable = isOnMobile ? false : this . unfoldable ( ) ;
253276 if ( this . #onMobile !== isOnMobile ) {
254277 this . #onMobile = isOnMobile ;
255278 this . #sidebarService. toggle ( {
0 commit comments