1
1
import {
2
2
booleanAttribute ,
3
3
Component ,
4
- EventEmitter ,
5
- HostBinding ,
4
+ computed ,
5
+ effect ,
6
6
inject ,
7
- Input ,
7
+ input ,
8
+ linkedSignal ,
8
9
OnChanges ,
9
10
OnDestroy ,
10
11
OnInit ,
11
- Output ,
12
+ output ,
12
13
Renderer2 ,
14
+ signal ,
13
15
SimpleChanges
14
16
} from '@angular/core' ;
15
17
import { DOCUMENT } from '@angular/common' ;
@@ -23,7 +25,11 @@ import { SidebarBackdropService } from '../sidebar-backdrop/sidebar-backdrop.ser
23
25
selector : 'c-sidebar' ,
24
26
exportAs : 'cSidebar' ,
25
27
template : '<ng-content />' ,
26
- host : { class : 'sidebar' }
28
+ host : {
29
+ class : 'sidebar' ,
30
+ '[class]' : 'hostClasses()' ,
31
+ '[attr.inert]' : '!this.sidebarState.visible || null'
32
+ }
27
33
} )
28
34
export class SidebarComponent implements OnChanges , OnDestroy , OnInit {
29
35
readonly #document = inject < Document > ( DOCUMENT ) ;
@@ -32,14 +38,13 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
32
38
readonly #sidebarService = inject ( SidebarService ) ;
33
39
readonly #backdropService = inject ( SidebarBackdropService ) ;
34
40
35
- #visible = false ;
36
41
#onMobile = false ;
37
42
#layoutChangeSubscription! : Subscription ;
38
43
#stateToggleSubscription! : Subscription ;
39
44
40
- state : ISidebarAction = {
45
+ readonly state = signal < ISidebarAction > ( {
41
46
sidebar : this
42
- } ;
47
+ } ) ;
43
48
44
49
#stateInitial = {
45
50
narrow : false ,
@@ -48,105 +53,120 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
48
53
} ;
49
54
50
55
/**
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'
54
58
*/
55
- @ Input ( ) colorScheme ?: 'dark' | 'light' ;
59
+ readonly colorScheme = input < 'dark' | 'light' > ( ) ;
56
60
57
61
/**
58
- * Sets html attribute id. [docs]
59
- *
60
- * @type string
62
+ * Sets html attribute id.
63
+ * @return string
61
64
*/
62
- @ Input ( ) id ?: string ;
65
+ readonly id = input < string > ( ) ;
63
66
64
67
/**
65
- * Make sidebar narrow. [docs]
66
- * @type boolean
68
+ * Make sidebar narrow.
69
+ * @return boolean
67
70
* @default false
68
71
*/
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
+ }
70
83
71
84
/**
72
85
* Set sidebar to overlaid variant.
73
- * @type boolean
86
+ * @return boolean
74
87
* @default false
75
88
*/
76
- @ Input ( { transform : booleanAttribute } ) overlaid : boolean = false ;
89
+ readonly overlaid = input ( false , { transform : booleanAttribute } ) ;
77
90
78
91
/**
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'
81
94
*/
82
- @ Input ( ) placement ?: 'start' | 'end' ;
95
+ readonly placement = input < 'start' | 'end' > ( ) ;
83
96
84
97
/**
85
- * Place sidebar in non-static positions. [docs]
98
+ * Place sidebar in non-static positions.
99
+ * @return 'fixed' | 'sticky'
86
100
* @default 'fixed'
87
101
*/
88
- @ Input ( ) position : 'fixed' | 'sticky' = 'fixed' ;
102
+ readonly position = input < 'fixed' | 'sticky' > ( 'fixed' ) ;
89
103
90
104
/**
91
- * Size the component small, large, or extra large. [docs]
105
+ * Size the component small, large, or extra large.
106
+ * @return 'sm' | 'lg' | 'xl'
92
107
*/
93
- @ Input ( ) size ?: 'sm' | 'lg' | 'xl' ;
108
+ readonly size = input < 'sm' | 'lg' | 'xl' > ( ) ;
94
109
95
110
/**
96
- * Expand narrowed sidebar on hover. [docs]
111
+ * Expand narrowed sidebar on hover.
97
112
* @type boolean
98
113
* @default false
99
114
*/
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
+ } ) ;
101
121
102
122
/**
103
- * Toggle the visibility of sidebar component. [docs]
123
+ * Toggle the visibility of sidebar component.
104
124
* @type boolean
105
125
* @default false
106
126
*/
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
+
108
135
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 ) ;
114
137
}
115
138
116
139
get visible ( ) {
117
- return this . #visible;
140
+ return this . #visible( ) ;
118
141
}
119
142
120
143
/**
121
- * Event emitted on visibility change. [docs]
122
- * @type boolean
144
+ * Event emitted on visibility change.
145
+ * @return boolean
123
146
*/
124
- @ Output ( ) visibleChange = new EventEmitter < boolean > ( ) ;
147
+ readonly visibleChange = output < boolean > ( ) ;
125
148
126
149
set sidebarState ( value : ISidebarAction ) {
127
150
const newState = value ;
128
151
if ( 'toggle' in newState ) {
129
152
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 ) ;
132
155
} 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 ) ;
135
158
}
136
159
} else {
137
- this . visible = ( newState . visible ?? this . visible ) && ! this . overlaid ;
160
+ this . # visible. update ( ( visible ) => ( newState . visible ?? visible ) && ! this . overlaid ( ) ) ;
138
161
}
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
144
164
? this . #backdropService. setBackdrop ( this )
145
165
: this . #backdropService. clearBackdrop ( ) ;
146
166
}
147
167
148
168
get sidebarState ( ) : ISidebarAction {
149
- return this . state ;
169
+ return { ... this . state ( ) } ;
150
170
}
151
171
152
172
get getMobileBreakpoint ( ) : string {
@@ -164,23 +184,26 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
164
184
this . #backdropService. renderer = this . #renderer;
165
185
}
166
186
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 ( ) ;
170
193
return {
171
194
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 ,
179
202
show : visible ,
180
203
// show: visible && this.#onMobile, //todo: check
181
204
hide : ! visible
182
205
} ;
183
- }
206
+ } ) ;
184
207
185
208
ngOnInit ( ) : void {
186
209
this . setInitialState ( ) ;
@@ -194,7 +217,7 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
194
217
}
195
218
196
219
ngOnChanges ( changes : SimpleChanges ) : void {
197
- const oldStateMap = new Map ( Object . entries ( this . state ) ) ;
220
+ const oldStateMap = new Map ( Object . entries ( this . state ( ) ) ) ;
198
221
const newStateMap = new Map ( ) ;
199
222
newStateMap . set ( 'sidebar' , this ) ;
200
223
@@ -219,9 +242,9 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
219
242
220
243
setInitialState ( ) : void {
221
244
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 ( )
225
248
} ;
226
249
this . #sidebarService. toggle ( {
227
250
...this . #stateInitial,
@@ -232,8 +255,8 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
232
255
private stateToggleSubscribe ( subscribe : boolean = true ) : void {
233
256
if ( subscribe ) {
234
257
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 } ;
237
260
}
238
261
} ) ;
239
262
} else {
@@ -249,7 +272,7 @@ export class SidebarComponent implements OnChanges, OnDestroy, OnInit {
249
272
250
273
this . #layoutChangeSubscription = layoutChanges . subscribe ( ( result : BreakpointState ) => {
251
274
const isOnMobile = result . breakpoints [ onMobile ] ;
252
- const isUnfoldable = isOnMobile ? false : this . unfoldable ;
275
+ const isUnfoldable = isOnMobile ? false : this . unfoldable ( ) ;
253
276
if ( this . #onMobile !== isOnMobile ) {
254
277
this . #onMobile = isOnMobile ;
255
278
this . #sidebarService. toggle ( {
0 commit comments