shirkasoft-ui-components 1.0.24 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,15 +23,16 @@ npm install shirkasoft-ui-components
23
23
 
24
24
  ### Peer Dependencies
25
25
 
26
- | Package | Minimum version |
27
- |---|---|
28
- | @angular/common | ^21.2.0 |
29
- | @angular/core | ^21.2.0 |
30
- | @angular/forms | ^21.2.0 |
31
- | rxjs | ^7.8.0 |
32
- | @jsverse/transloco | >= 8.0.0 |
33
- | @lucide/angular | ^1.17.0 |
34
- | chart.js | ^4.4.0 |
26
+ | Package | Minimum version |
27
+ | ------------------ | --------------- |
28
+ | @angular/common | ^21.2.0 |
29
+ | @angular/core | ^21.2.0 |
30
+ | @angular/forms | ^21.2.0 |
31
+ | @angular/router | ^21.2.0 |
32
+ | rxjs | ^7.8.0 |
33
+ | @jsverse/transloco | >= 8.0.0 |
34
+ | @lucide/angular | ^1.17.0 |
35
+ | chart.js | ^4.4.0 |
35
36
 
36
37
  ## Theme Setup
37
38
 
@@ -63,20 +64,22 @@ document.documentElement.classList.toggle('dark');
63
64
 
64
65
  ## Components
65
66
 
66
- | # | Component | Selector | ControlValueAccessor | Associated service |
67
- |---|---|---|---|---|
68
- | 1 | TextField | `shk-text-field` | ✅ | — |
69
- | 2 | Toggle | `shk-toggle` | ✅ | — |
70
- | 3 | Select | `shk-select` | ✅ | — |
71
- | 4 | DatePicker | `shk-date-picker` | ✅ | — |
72
- | 5 | PriceInput | `shk-price-input` | — | — |
73
- | 6 | FileUpload | `shk-file-upload` | — | — |
74
- | 7 | Table | `shk-table` | — | — |
75
- | 8 | Modal | `shk-modal` | — | `ModalService` |
76
- | 9 | Notification | `shk-notification` | — | `NotificationService` |
77
- | 10 | ConfirmDialog | `shk-confirm-dialog` | — | `ConfirmDialogService` |
78
- | 11 | Chart | `shk-chart` | — | — |
79
- | 12 | Tooltip | `shk-tooltip` | — | — |
67
+ | # | Component | Selector | ControlValueAccessor | Associated service |
68
+ | --- | ------------- | -------------------- | -------------------- | ---------------------- |
69
+ | 1 | TextField | `shk-text-field` | ✅ | — |
70
+ | 2 | Toggle | `shk-toggle` | ✅ | — |
71
+ | 3 | Select | `shk-select` | ✅ | — |
72
+ | 4 | DatePicker | `shk-date-picker` | ✅ | — |
73
+ | 5 | PriceInput | `shk-price-input` | — | — |
74
+ | 6 | FileUpload | `shk-file-upload` | — | — |
75
+ | 7 | Table | `shk-table` | — | — |
76
+ | 8 | Modal | `shk-modal` | — | `ModalService` |
77
+ | 9 | Notification | `shk-notification` | — | `NotificationService` |
78
+ | 10 | ConfirmDialog | `shk-confirm-dialog` | — | `ConfirmDialogService` |
79
+ | 11 | Chart | `shk-chart` | — | — |
80
+ | 12 | Tooltip | `shk-tooltip` | — | — |
81
+ | 13 | Sidebar | `shk-sidebar` | — | — |
82
+ | 14 | Rail Sidebar | `shk-rail-sidebar` | — | — |
80
83
 
81
84
  ---
82
85
 
@@ -98,28 +101,28 @@ Text input with support for input, textarea, password, search, credit card forma
98
101
  />
99
102
  ```
100
103
 
101
- | Input | Type | Default |
102
- |---|---|---|
103
- | `id` | `string` | `''` |
104
- | `label` | `string` | `''` |
105
- | `type` | `string` | `'text'` |
106
- | `placeholder` | `string` | `''` |
107
- | `disabled` | `boolean` | `false` |
108
- | `control` | `FormControl` | `undefined` |
109
- | `formGroup` | `FormGroup` | `undefined` |
110
- | `errorMessage` | `string` | `''` |
111
- | `errorMessages` | `{ [key: string]: string }` | `{}` |
112
- | `isTextArea` | `boolean` | `false` |
113
- | `textAreaHeight` | `string` | `'h-28'` |
114
- | `searchMode` | `boolean` | `false` |
115
- | `searchButtonClick` | `output<void>` | — |
116
- | `numbersOnly` | `boolean` | `false` |
117
- | `allowDecimals` | `boolean` | `false` |
118
- | `maxLength` | `number` | `undefined` |
119
- | `formatCard` | `boolean` | `false` |
120
- | `preventNegative` | `boolean` | `false` |
121
- | `displayValue` | `string` | `''` |
122
- | `autocomplete` | `string` | `'new-password'` |
104
+ | Input | Type | Default |
105
+ | ------------------- | --------------------------- | ---------------- |
106
+ | `id` | `string` | `''` |
107
+ | `label` | `string` | `''` |
108
+ | `type` | `string` | `'text'` |
109
+ | `placeholder` | `string` | `''` |
110
+ | `disabled` | `boolean` | `false` |
111
+ | `control` | `FormControl` | `undefined` |
112
+ | `formGroup` | `FormGroup` | `undefined` |
113
+ | `errorMessage` | `string` | `''` |
114
+ | `errorMessages` | `{ [key: string]: string }` | `{}` |
115
+ | `isTextArea` | `boolean` | `false` |
116
+ | `textAreaHeight` | `string` | `'h-28'` |
117
+ | `searchMode` | `boolean` | `false` |
118
+ | `searchButtonClick` | `output<void>` | — |
119
+ | `numbersOnly` | `boolean` | `false` |
120
+ | `allowDecimals` | `boolean` | `false` |
121
+ | `maxLength` | `number` | `undefined` |
122
+ | `formatCard` | `boolean` | `false` |
123
+ | `preventNegative` | `boolean` | `false` |
124
+ | `displayValue` | `string` | `''` |
125
+ | `autocomplete` | `string` | `'new-password'` |
123
126
 
124
127
  > Also exposes `inputElement` and `textareaElement` as `viewChild`.
125
128
 
@@ -138,14 +141,14 @@ Switch or checkbox. Implements `ControlValueAccessor`.
138
141
  />
139
142
  ```
140
143
 
141
- | Input | Type | Default |
142
- |---|---|---|
143
- | `label` | `string` | `''` |
144
- | `mode` | `'toggle' \| 'checkbox'` | `'toggle'` |
145
- | `checked` | `boolean` | `undefined` |
144
+ | Input | Type | Default |
145
+ | --------- | ------------------------ | ----------- |
146
+ | `label` | `string` | `''` |
147
+ | `mode` | `'toggle' \| 'checkbox'` | `'toggle'` |
148
+ | `checked` | `boolean` | `undefined` |
146
149
 
147
- | Output | Type |
148
- |---|---|
150
+ | Output | Type |
151
+ | --------------- | --------- |
149
152
  | `checkedChange` | `boolean` |
150
153
 
151
154
  ---
@@ -179,32 +182,32 @@ interface SelectOption {
179
182
  }
180
183
  ```
181
184
 
182
- | Input | Type | Default |
183
- |---|---|---|
184
- | `options` | `SelectOption[]` | `[]` |
185
- | `label` | `string` | `undefined` |
186
- | `placeholder` | `string` | `'Select...'` |
187
- | `disabled` | `boolean` | `false` |
188
- | `control` | `FormControl` | `undefined` |
189
- | `formGroup` | `FormGroup` | `undefined` |
190
- | `multiple` | `boolean` | `false` |
191
- | `isSearchable` | `boolean` | `false` |
192
- | `usePagination` | `boolean` | `false` |
193
- | `itemsPerPage` | `number` | `10` |
194
- | `allowCustomEntries` | `boolean` | `false` |
195
- | `isLoading` | `boolean` | `false` |
196
- | `isAllDataLoaded` | `boolean` | `false` |
197
- | `preserveSearchOnLoad` | `boolean` | `false` |
198
- | `dropdownUpward` | `boolean` | `false` |
199
- | `showEmptyOption` | `boolean` | `true` |
200
- | `emptyMessageKey` | `string` | `'common.no_records'` |
201
- | `validCombinations` | `string[][]` | `undefined` |
202
- | `errorMessages` | `{ [key: string]: string }` | `{ required: 'This field is required' }` |
203
-
204
- | Output | Type |
205
- |---|---|
206
- | `selectionChange` | `any` |
207
- | `search` | `string` |
185
+ | Input | Type | Default |
186
+ | ---------------------- | --------------------------- | ---------------------------------------- |
187
+ | `options` | `SelectOption[]` | `[]` |
188
+ | `label` | `string` | `undefined` |
189
+ | `placeholder` | `string` | `'Select...'` |
190
+ | `disabled` | `boolean` | `false` |
191
+ | `control` | `FormControl` | `undefined` |
192
+ | `formGroup` | `FormGroup` | `undefined` |
193
+ | `multiple` | `boolean` | `false` |
194
+ | `isSearchable` | `boolean` | `false` |
195
+ | `usePagination` | `boolean` | `false` |
196
+ | `itemsPerPage` | `number` | `10` |
197
+ | `allowCustomEntries` | `boolean` | `false` |
198
+ | `isLoading` | `boolean` | `false` |
199
+ | `isAllDataLoaded` | `boolean` | `false` |
200
+ | `preserveSearchOnLoad` | `boolean` | `false` |
201
+ | `dropdownUpward` | `boolean` | `false` |
202
+ | `showEmptyOption` | `boolean` | `true` |
203
+ | `emptyMessageKey` | `string` | `'common.no_records'` |
204
+ | `validCombinations` | `string[][]` | `undefined` |
205
+ | `errorMessages` | `{ [key: string]: string }` | `{ required: 'This field is required' }` |
206
+
207
+ | Output | Type |
208
+ | ----------------- | -------- |
209
+ | `selectionChange` | `any` |
210
+ | `search` | `string` |
208
211
 
209
212
  ---
210
213
 
@@ -223,22 +226,23 @@ Date, month, or year picker. Implements `ControlValueAccessor`. Supports `'date'
223
226
  />
224
227
  ```
225
228
 
226
- | Input | Type | Default |
227
- |---|---|---|
228
- | `id` | `string` | `''` |
229
- | `label` | `string` | `''` |
230
- | `placeholder` | `string` | `''` |
231
- | `view` | `'date' \| 'month' \| 'year'` | `'date'` |
232
- | `control` | `FormControl` | `undefined` |
233
- | `formGroup` | `FormGroup` | `undefined` |
234
- | `disabled` | `boolean` | `false` |
235
- | `errorMessage` | `string` | `''` |
236
- | `errorMessages` | `{ [key: string]: string }` | `{}` |
237
- | `minValue` | `string` | `''` |
238
- | `maxValue` | `string` | `''` |
239
- | `locale` | `string` | `'es'` |
229
+ | Input | Type | Default |
230
+ | --------------- | ----------------------------- | ----------- |
231
+ | `id` | `string` | `''` |
232
+ | `label` | `string` | `''` |
233
+ | `placeholder` | `string` | `''` |
234
+ | `view` | `'date' \| 'month' \| 'year'` | `'date'` |
235
+ | `control` | `FormControl` | `undefined` |
236
+ | `formGroup` | `FormGroup` | `undefined` |
237
+ | `disabled` | `boolean` | `false` |
238
+ | `errorMessage` | `string` | `''` |
239
+ | `errorMessages` | `{ [key: string]: string }` | `{}` |
240
+ | `minValue` | `string` | `''` |
241
+ | `maxValue` | `string` | `''` |
242
+ | `locale` | `string` | `'es'` |
240
243
 
241
244
  **Return value** depends on `view`:
245
+
242
246
  - `'date'` → `'YYYY-MM-DD'`
243
247
  - `'month'` → `'YYYY-MM'`
244
248
  - `'year'` → `'YYYY'`
@@ -262,18 +266,18 @@ Numeric input with toggle between amount (`$`) and percentage (`%`). Uses Angula
262
266
  />
263
267
  ```
264
268
 
265
- | Input | Type | Default |
266
- |---|---|---|
267
- | `value` | `model<number>` | **required** |
268
- | `isPercentage` | `model<boolean>` | `false` |
269
- | `label` | `string` | `''` |
270
- | `inputId` | `string` | `''` |
271
- | `required` | `boolean` | `false` |
272
- | `disabled` | `boolean` | `false` |
273
- | `min` | `number` | `0` |
274
- | `max` | `number` | `Infinity` |
275
- | `showError` | `boolean` | `false` |
276
- | `errorMessage` | `string` | `''` |
269
+ | Input | Type | Default |
270
+ | -------------- | ---------------- | ------------ |
271
+ | `value` | `model<number>` | **required** |
272
+ | `isPercentage` | `model<boolean>` | `false` |
273
+ | `label` | `string` | `''` |
274
+ | `inputId` | `string` | `''` |
275
+ | `required` | `boolean` | `false` |
276
+ | `disabled` | `boolean` | `false` |
277
+ | `min` | `number` | `0` |
278
+ | `max` | `number` | `Infinity` |
279
+ | `showError` | `boolean` | `false` |
280
+ | `errorMessage` | `string` | `''` |
277
281
 
278
282
  ---
279
283
 
@@ -302,23 +306,23 @@ interface FileUploadError {
302
306
  }
303
307
  ```
304
308
 
305
- | Input | Type | Default |
306
- |---|---|---|
307
- | `label` | `string` | `'Upload file'` |
308
- | `accept` | `string` | `'image/*'` |
309
- | `maxFileSize` | `number` | `2 * 1024 * 1024` (2MB) |
310
- | `maxFiles` | `number` | `0` (unlimited) |
311
- | `multiple` | `boolean` | `false` |
312
- | `fileUploadText` | `string` | `'Select file'` |
313
- | `changeFilesText` | `string` | `'Change files'` |
314
- | `fileRecommendation` | `string` | `''` |
315
-
316
- | Output | Type |
317
- |---|---|
318
- | `fileSelected` | `File[]` |
319
- | `fileRemoved` | `void` |
320
- | `fileError` | `FileUploadError` |
321
- | `fileWarning` | `FileUploadError` |
309
+ | Input | Type | Default |
310
+ | -------------------- | --------- | ----------------------- |
311
+ | `label` | `string` | `'Upload file'` |
312
+ | `accept` | `string` | `'image/*'` |
313
+ | `maxFileSize` | `number` | `2 * 1024 * 1024` (2MB) |
314
+ | `maxFiles` | `number` | `0` (unlimited) |
315
+ | `multiple` | `boolean` | `false` |
316
+ | `fileUploadText` | `string` | `'Select file'` |
317
+ | `changeFilesText` | `string` | `'Change files'` |
318
+ | `fileRecommendation` | `string` | `''` |
319
+
320
+ | Output | Type |
321
+ | -------------- | ----------------- |
322
+ | `fileSelected` | `File[]` |
323
+ | `fileRemoved` | `void` |
324
+ | `fileError` | `FileUploadError` |
325
+ | `fileWarning` | `FileUploadError` |
322
326
 
323
327
  ---
324
328
 
@@ -391,35 +395,35 @@ interface FilterChangeEvent {
391
395
  }
392
396
  ```
393
397
 
394
- | Input | Type | Default |
395
- |---|---|---|
396
- | `data` | `any[]` | `[]` |
397
- | `columns` | `Column[]` | `[]` |
398
- | `rowsPerPage` | `number` | `10` |
399
- | `rowsPerPageOptions` | `number[]` | `[10, 25, 50]` |
400
- | `loading` | `boolean` | `false` |
401
- | `showActionRow` | `boolean` | `true` |
402
- | `headerActions` | `TableAction[]` | `[]` |
403
- | `rowActions` | `RowAction[]` | `[]` |
404
- | `hasShadow` | `boolean` | `true` |
405
- | `defaultSortField` | `string` | `''` |
406
- | `defaultSortOrder` | `number` | `1` |
407
- | `showSearch` | `boolean` | `true` |
408
- | `searchPlaceholder` | `string` | `''` |
409
- | `emptyMessage` | `string` | `''` |
410
- | `serverSide` | `boolean` | `false` |
411
- | `totalRecords` | `number` | `0` |
412
- | `filters` | `{ label: string; value: string }[]` | `[]` |
413
- | `activeFilter` | `string` | `''` |
414
- | `customTemplates` | `{ [key: string]: any }` | `{}` |
415
-
416
- | Output | Type |
417
- |---|---|
418
- | `pageChange` | `PageChangeEvent` |
398
+ | Input | Type | Default |
399
+ | -------------------- | ------------------------------------ | -------------- |
400
+ | `data` | `any[]` | `[]` |
401
+ | `columns` | `Column[]` | `[]` |
402
+ | `rowsPerPage` | `number` | `10` |
403
+ | `rowsPerPageOptions` | `number[]` | `[10, 25, 50]` |
404
+ | `loading` | `boolean` | `false` |
405
+ | `showActionRow` | `boolean` | `true` |
406
+ | `headerActions` | `TableAction[]` | `[]` |
407
+ | `rowActions` | `RowAction[]` | `[]` |
408
+ | `hasShadow` | `boolean` | `true` |
409
+ | `defaultSortField` | `string` | `''` |
410
+ | `defaultSortOrder` | `number` | `1` |
411
+ | `showSearch` | `boolean` | `true` |
412
+ | `searchPlaceholder` | `string` | `''` |
413
+ | `emptyMessage` | `string` | `''` |
414
+ | `serverSide` | `boolean` | `false` |
415
+ | `totalRecords` | `number` | `0` |
416
+ | `filters` | `{ label: string; value: string }[]` | `[]` |
417
+ | `activeFilter` | `string` | `''` |
418
+ | `customTemplates` | `{ [key: string]: any }` | `{}` |
419
+
420
+ | Output | Type |
421
+ | -------------- | ------------------- |
422
+ | `pageChange` | `PageChangeEvent` |
419
423
  | `filterChange` | `FilterChangeEvent` |
420
- | `searchChange` | `string` |
421
- | `filterClick` | `string` |
422
- | `refresh` | `void` |
424
+ | `searchChange` | `string` |
425
+ | `filterClick` | `string` |
426
+ | `refresh` | `void` |
423
427
 
424
428
  ---
425
429
 
@@ -446,6 +450,7 @@ interface ModalConfig {
446
450
  ```
447
451
 
448
452
  The injected component **must** expose:
453
+
449
454
  - `form?: FormGroup` (optional, for validation)
450
455
  - `onSubmit()` → calls `submitSuccess` or `submitError`
451
456
  - `submitSuccess?: EventEmitter<void>` (optional)
@@ -473,12 +478,12 @@ closeModal() {
473
478
 
474
479
  **Service methods:**
475
480
 
476
- | Method | Description |
477
- |---|---|
478
- | `open(config: ModalConfig)` | Opens a new modal |
479
- | `close()` | Closes the current modal |
480
- | `accept()` | Closes the modal (alias for close) |
481
- | `clear()` | Closes all modals |
481
+ | Method | Description |
482
+ | --------------------------- | ---------------------------------- |
483
+ | `open(config: ModalConfig)` | Opens a new modal |
484
+ | `close()` | Closes the current modal |
485
+ | `accept()` | Closes the modal (alias for close) |
486
+ | `clear()` | Closes all modals |
482
487
 
483
488
  ---
484
489
 
@@ -517,19 +522,19 @@ updateProgress(id: string, progress: number) {
517
522
  }
518
523
  ```
519
524
 
520
- | Input | Type | Default |
521
- |---|---|---|
525
+ | Input | Type | Default |
526
+ | ---------- | ------------------------------------------- | -------------- |
522
527
  | `position` | `'center-top' \| 'right-top' \| 'left-top'` | `'center-top'` |
523
528
 
524
529
  **Service methods:**
525
530
 
526
- | Method | Description |
527
- |---|---|
528
- | `addNotification(msg, type?, showProgress?, duration?)` | Adds a notification |
529
- | `updateProgress(id, progress)` | Updates the progress bar |
530
- | `removeNotification(notification)` | Removes a notification |
531
- | `removeNotificationById(id)` | Removes by ID |
532
- | `clearAll()` | Removes all notifications |
531
+ | Method | Description |
532
+ | ------------------------------------------------------- | ------------------------- |
533
+ | `addNotification(msg, type?, showProgress?, duration?)` | Adds a notification |
534
+ | `updateProgress(id, progress)` | Updates the progress bar |
535
+ | `removeNotification(notification)` | Removes a notification |
536
+ | `removeNotificationById(id)` | Removes by ID |
537
+ | `clearAll()` | Removes all notifications |
533
538
 
534
539
  ---
535
540
 
@@ -571,19 +576,19 @@ async deleteItem() {
571
576
  }
572
577
  ```
573
578
 
574
- | Input | Type | Default |
575
- |---|---|---|
576
- | `title` | `string` | `'Confirm action'` |
577
- | `message` | `string` | `'Are you sure you want to proceed?'` |
578
- | `confirmLabel` | `string` | `'Confirm'` |
579
- | `cancelLabel` | `string` | `'Cancel'` |
580
- | `loadingText` | `string` | `'Processing…'` |
581
- | `type` | `'danger' \| 'info' \| 'warning'` | `'danger'` |
582
- | `loading` | `boolean` | `false` |
583
- | `showCancel` | `boolean` | `true` |
584
-
585
- | Output | Type |
586
- |---|---|
579
+ | Input | Type | Default |
580
+ | -------------- | --------------------------------- | ------------------------------------- |
581
+ | `title` | `string` | `'Confirm action'` |
582
+ | `message` | `string` | `'Are you sure you want to proceed?'` |
583
+ | `confirmLabel` | `string` | `'Confirm'` |
584
+ | `cancelLabel` | `string` | `'Cancel'` |
585
+ | `loadingText` | `string` | `'Processing…'` |
586
+ | `type` | `'danger' \| 'info' \| 'warning'` | `'danger'` |
587
+ | `loading` | `boolean` | `false` |
588
+ | `showCancel` | `boolean` | `true` |
589
+
590
+ | Output | Type |
591
+ | -------- | ------ |
587
592
  | `closed` | `void` |
588
593
 
589
594
  ---
@@ -593,11 +598,7 @@ async deleteItem() {
593
598
  Chart.js wrapper supporting the main chart types.
594
599
 
595
600
  ```html
596
- <shk-chart
597
- [type]="'bar'"
598
- [data]="chartData"
599
- [options]="chartOptions"
600
- />
601
+ <shk-chart [type]="'bar'" [data]="chartData" [options]="chartOptions" />
601
602
  ```
602
603
 
603
604
  ```typescript
@@ -606,17 +607,17 @@ import { COLOR_PALETTE, readThemeColors } from 'shirkasoft-ui-components';
606
607
  const colors = readThemeColors(); // current theme colors
607
608
  ```
608
609
 
609
- | Input | Type | Default |
610
- |---|---|---|
611
- | `type` | `'line' \| 'bar' \| 'pie' \| 'doughnut' \| 'polarArea'` | `'bar'` |
612
- | `data` | `any` | `{ datasets: [] }` |
613
- | `options` | `any` | `{}` |
610
+ | Input | Type | Default |
611
+ | --------- | ------------------------------------------------------- | ------------------ |
612
+ | `type` | `'line' \| 'bar' \| 'pie' \| 'doughnut' \| 'polarArea'` | `'bar'` |
613
+ | `data` | `any` | `{ datasets: [] }` |
614
+ | `options` | `any` | `{}` |
614
615
 
615
616
  **Exported utils:**
616
617
 
617
- | Export | Description |
618
- |---|---|
619
- | `COLOR_PALETTE` | Predefined color array (bg + border) |
618
+ | Export | Description |
619
+ | ------------------- | ---------------------------------------------- |
620
+ | `COLOR_PALETTE` | Predefined color array (bg + border) |
620
621
  | `readThemeColors()` | Reads current theme colors (`--shk-surface-*`) |
621
622
 
622
623
  ---
@@ -631,12 +632,160 @@ Tooltip with two modes: `hover` (CSS positioning) and `fixed` (JS-calculated pos
631
632
  </shk-tooltip>
632
633
  ```
633
634
 
634
- | Input | Type | Default |
635
- |---|---|---|
636
- | `text` | `string` | `''` |
635
+ | Input | Type | Default |
636
+ | ---------- | ------------------------------ | ---------- |
637
+ | `text` | `string` | `''` |
637
638
  | `position` | `'top' \| 'bottom' \| 'right'` | `'bottom'` |
638
- | `mode` | `'hover' \| 'fixed'` | `'hover'` |
639
- | `offset` | `number` | `8` |
639
+ | `mode` | `'hover' \| 'fixed'` | `'hover'` |
640
+ | `offset` | `number` | `8` |
641
+
642
+ ---
643
+
644
+ ### Sidebar (`shk-sidebar`)
645
+
646
+ Brand/logo header, round collapse toggle with tooltip, expandable groups with nested children, standalone items, Lucide icons, badges, separators, disabled items, active-item highlighting (`primary`-600 with white text), projected `header`/`content`/`footer` slots, tooltips on collapsed items, and a responsive off-canvas mode below 1024px. Uses the same theme tokens (`--shk-*`) as the rest of the library.
647
+
648
+ ```html
649
+ <shk-sidebar
650
+ [items]="items"
651
+ [(collapsed)]="collapsed"
652
+ [activeItemId]="activeId"
653
+ title="WS Admin"
654
+ subtitle="Panel de control"
655
+ logo="/logo.svg"
656
+ [defaultExpanded]="['products']"
657
+ (itemClick)="onItemClick($event)"
658
+ >
659
+ <div sidebarFooter>
660
+ <button (click)="logout()">Cerrar sesión</button>
661
+ </div>
662
+ </shk-sidebar>
663
+ ```
664
+
665
+ ```typescript
666
+ interface SidebarItem {
667
+ id: string;
668
+ label: string;
669
+ icon?: string; // Lucide icon name
670
+ badge?: string | number;
671
+ disabled?: boolean;
672
+ separator?: boolean; // renders a divider before the item
673
+ route?: string; // navigation target (optional)
674
+ roles?: string[]; // when `userRole` is set, items are filtered
675
+ children?: SidebarItem[]; // renders an expandable group with nested items
676
+ }
677
+
678
+ interface SidebarGroup {
679
+ id?: string;
680
+ label?: string;
681
+ items: SidebarItem[];
682
+ }
683
+ ```
684
+
685
+ | Input | Type | Default |
686
+ | ----------------- | --------------------------- | --------------------------------------------- |
687
+ | `items` | `SidebarItem[]` | `[]` |
688
+ | `groups` | `SidebarGroup[]` | `[]` (when set, overrides `items`) |
689
+ | `title` | `string` | `''` |
690
+ | `subtitle` | `string` | `''` |
691
+ | `logo` | `string` | `''` (image URL shown in the brand header) |
692
+ | `collapsed` | `model<boolean>` | `false` |
693
+ | `mobileOpen` | `model<boolean>` | `false` (off-canvas state on mobile) |
694
+ | `collapsible` | `boolean` | `true` |
695
+ | `showToggle` | `boolean` | `true` |
696
+ | `responsive` | `boolean` | `true` (off-canvas below 1024px) |
697
+ | `fixed` | `boolean` | `false` (uses `position: fixed` + `h-screen`) |
698
+ | `hasShadow` | `boolean` | `true` |
699
+ | `expandedWidth` | `string` | `'16rem'` |
700
+ | `collapsedWidth` | `string` | `'5rem'` |
701
+ | `activeItemId` | `string` | `''` |
702
+ | `userRole` | `string` | `''` (filters items by `roles`) |
703
+ | `defaultExpanded` | `string[]` | `[]` (groups expanded on init) |
704
+ | `labelPipe` | `(label: string) => string` | identity (e.g. pass a Transloco translate fn) |
705
+ | `expandTooltip` | `string` | `'Expandir'` |
706
+ | `collapseTooltip` | `string` | `'Colapsar'` |
707
+
708
+ | Output | Type |
709
+ | ----------- | ------------- |
710
+ | `itemClick` | `SidebarItem` |
711
+ | `toggle` | `boolean` |
712
+
713
+ **Projected slots:**
714
+
715
+ | Slot | Description |
716
+ | ---------------- | --------------------------------------------------- |
717
+ | `sidebarHeader` | Brand/logo area (rendered before the title) |
718
+ | `sidebarContent` | Custom content between the header and the nav |
719
+ | `sidebarFooter` | Footer area (e.g. logout button); hidden when empty |
720
+
721
+ > Icons are resolved by name via a Lucide map with a `Menu` fallback. Use `[fixed]="true"` for a full-screen sidebar (as in ws-admin) or leave it embedded in a container for previews.
722
+
723
+ ### Rail Sidebar (`shk-rail-sidebar`)
724
+
725
+ Sidebar estilo rail + nav, una columna vertical de iconos (una por sección) y un panel con el menú de la sección activa, con subgrupos expandibles, toggle de idioma, botón de logout y colapso.
726
+
727
+ ```html
728
+ <shk-rail-sidebar
729
+ [sections]="sections"
730
+ [activeUrl]="router.url"
731
+ [(activeSectionKey)]="activeSectionKey"
732
+ [(collapsed)]="collapsed"
733
+ [activeLang]="transloco.getActiveLang()"
734
+ appName="WAC Bookings"
735
+ logo="/logo.png"
736
+ (sectionSelected)="onSectionSelected($event)"
737
+ (itemClick)="onItemClick($event)"
738
+ (setLang)="onSetLanguage($event)"
739
+ (logoutRequested)="requestLogout()"
740
+ />
741
+ ```
742
+
743
+ ```ts
744
+ interface RailSidebarSection {
745
+ key: string;
746
+ label: string;
747
+ icon?: string; // nombre del icono Lucide
748
+ routerLink?: string; // ruta de la sección (selección automática por URL)
749
+ matchPrefixes?: string[]; // prefijos de URL que activan la sección
750
+ items?: RailSidebarItem[]; // menú del panel cuando la sección está activa
751
+ }
752
+
753
+ interface RailSidebarItem {
754
+ id: string;
755
+ label: string;
756
+ icon?: string;
757
+ routerLink?: string;
758
+ children?: RailSidebarItem[]; // subgrupo expandible
759
+ roles?: string[]; // filtro por rol (userRole)
760
+ badge?: string | number;
761
+ disabled?: boolean;
762
+ separator?: boolean;
763
+ }
764
+ ```
765
+
766
+ | Input | Type | Default |
767
+ | ------------------ | ---------------------- | --------------------------------------------------------- |
768
+ | `sections` | `RailSidebarSection[]` | `[]` |
769
+ | `activeUrl` | `string` | `''` — activa la sección por `matchPrefixes`/`routerLink` |
770
+ | `activeSectionKey` | `string` (model) | `''` |
771
+ | `collapsed` | `boolean` (model) | `false` |
772
+ | `activeLang` | `string` | `'es'` |
773
+ | `langs` | `string[]` | `['es', 'en']` — idiomas al togglear |
774
+ | `logo` | `string` | `''` — imagen del workspace |
775
+ | `appName` | `string` | `''` — título del panel |
776
+ | `userRole` | `string` | `''` — filtra `items.roles` |
777
+ | `labelPipe` | `(label) => string` | identidad (para transloco) |
778
+ | `railWidth` | `string` | `'4.5rem'` |
779
+ | `expandedWidth` | `string` | `'16.5rem'` |
780
+
781
+ | Output | Type |
782
+ | ----------------- | ---------------------- |
783
+ | `sectionSelected` | `{ key, routerLink? }` |
784
+ | `itemClick` | `RailSidebarItem` |
785
+ | `setLang` | `string` |
786
+ | `logoutRequested` | `void` |
787
+ | `toggleSidebar` | `void` |
788
+ | `workspaceClick` | `void` |
640
789
 
641
790
  ---
642
791
 
@@ -659,20 +808,35 @@ pnpm watch
659
808
  ### Versioning
660
809
 
661
810
  ```bash
662
- pnpm version:patch # 1.0.22 → 1.0.23
663
- pnpm version:minor # 1.0.22 → 1.1.0
664
- pnpm version:major # 1.0.22 → 2.0.0
811
+ pnpm version:patch # 1.0.23 → 1.0.24 (creates commit + tag locally)
812
+ pnpm version:minor # 1.0.23 → 1.1.0
813
+ pnpm version:major # 1.0.23 → 2.0.0
814
+ ```
815
+
816
+ ### Push code (without publishing)
817
+
818
+ ```bash
819
+ git push origin main
820
+ ```
821
+
822
+ ### Push code + publish to npm
823
+
824
+ ```bash
825
+ git push origin main --tags
665
826
  ```
666
827
 
667
828
  ---
668
829
 
669
830
  ## Publishing
670
831
 
671
- CI/CD via GitHub Actions:
672
- - Pushing a `v*` tag automatically publishes to npm.
673
- - Pushing to `main`/`master` deploys the showcase to GitHub Pages.
832
+ ### CI/CD via GitHub Actions
833
+
834
+ | Workflow | Trigger | Description |
835
+ | ------------------- | --------------------------------------------- | ------------------------------------------------------------------------- |
836
+ | **Publish to npm** | Manual (`workflow_dispatch`) or push `v*` tag | Builds library, publishes to npm, builds showcase, deploys to public repo |
837
+ | **Deploy Showcase** | Push to `main`/`master` or manual | Builds library + showcase, deploys to GitHub Pages public repo |
674
838
 
675
- Manual:
839
+ ### Manual publish (without GitHub Actions)
676
840
 
677
841
  ```bash
678
842
  bash scripts/publish-lib.sh