11import {
22booleanAttribute ,
33Component ,
4- EventEmitter ,
5- HostBinding ,
4+ computed ,
5+ effect ,
66inject ,
7- Input ,
7+ input ,
8+ linkedSignal ,
89OnChanges ,
910OnDestroy ,
1011OnInit ,
11- Output ,
12+ output ,
1213Renderer2 ,
14+ signal ,
1315SimpleChanges
1416} from '@angular/core' ;
1517import { DOCUMENT } from '@angular/common' ;
@@ -23,7 +25,11 @@ import { SidebarBackdropService } from '../sidebar-backdrop/sidebar-backdrop.ser
2325selector :'c-sidebar' ,
2426exportAs :'cSidebar' ,
2527template :'<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 {
2935readonly #document= inject < Document > ( DOCUMENT ) ;
@@ -32,14 +38,13 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
3238readonly #sidebarService= inject ( SidebarService ) ;
3339readonly #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 > ( {
4146sidebar :this
42- } ;
47+ } ) ;
4348
4449 #stateInitial= {
4550narrow :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+
108135set 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
116139get 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
126149set sidebarState ( value :ISidebarAction ) {
127150const newState = value ;
128151if ( 'toggle' in newState ) {
129152if ( 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
148168get sidebarState ( ) :ISidebarAction {
149- return this . state ;
169+ return { ... this . state ( ) } ;
150170}
151171
152172get getMobileBreakpoint ( ) :string {
@@ -164,23 +184,26 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
164184this . #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 ( ) ;
170193return {
171194sidebar :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 ,
179202show :visible ,
180203// show: visible && this.#onMobile, //todo: check
181204hide :! visible
182205} ;
183- }
206+ } ) ;
184207
185208ngOnInit ( ) :void {
186209this . setInitialState ( ) ;
@@ -194,7 +217,7 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
194217}
195218
196219ngOnChanges ( changes :SimpleChanges ) :void {
197- const oldStateMap = new Map ( Object . entries ( this . state ) ) ;
220+ const oldStateMap = new Map ( Object . entries ( this . state ( ) ) ) ;
198221const newStateMap = new Map ( ) ;
199222newStateMap . set ( 'sidebar' , this ) ;
200223
@@ -219,9 +242,9 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
219242
220243setInitialState ( ) :void {
221244this . #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} ;
226249this . #sidebarService. toggle ( {
227250 ...this . #stateInitial,
@@ -232,8 +255,8 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
232255private stateToggleSubscribe ( subscribe :boolean = true ) :void {
233256if ( subscribe ) {
234257this . #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
250273this . #layoutChangeSubscription= layoutChanges . subscribe ( ( result :BreakpointState ) => {
251274const isOnMobile = result . breakpoints [ onMobile ] ;
252- const isUnfoldable = isOnMobile ?false :this . unfoldable ;
275+ const isUnfoldable = isOnMobile ?false :this . unfoldable ( ) ;
253276if ( this . #onMobile!== isOnMobile ) {
254277this . #onMobile= isOnMobile ;
255278this . #sidebarService. toggle ( {