@salas-ds/cli 0.1.0 → 0.2.1

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.
Files changed (92) hide show
  1. package/dist/index.js +296 -94
  2. package/package.json +4 -5
  3. package/templates/angular/accordion/accordion-content.component.ts +9 -0
  4. package/templates/angular/accordion/accordion-item.component.ts +138 -0
  5. package/templates/angular/accordion/accordion-trigger.component.ts +9 -0
  6. package/templates/angular/accordion/accordion.component.ts +120 -0
  7. package/templates/angular/accordion/accordion.module.ts +21 -0
  8. package/templates/angular/autocomplete/autocomplete.component.ts +707 -0
  9. package/templates/angular/autocomplete/autocomplete.module.ts +8 -0
  10. package/templates/angular/avatar/avatar-badge.component.ts +18 -0
  11. package/templates/angular/avatar/avatar-fallback.component.ts +39 -0
  12. package/templates/angular/avatar/avatar-group-count.component.ts +46 -0
  13. package/templates/angular/avatar/avatar-group.component.ts +33 -0
  14. package/templates/angular/avatar/avatar-image.component.ts +57 -0
  15. package/templates/angular/avatar/avatar.component.ts +73 -0
  16. package/templates/angular/avatar/avatar.module.ts +27 -0
  17. package/templates/angular/badge/badge.component.ts +84 -0
  18. package/templates/angular/badge/badge.module.ts +9 -0
  19. package/templates/angular/button/button.component.ts +24 -4
  20. package/templates/angular/card/card.component.ts +100 -0
  21. package/templates/angular/card/card.module.ts +8 -0
  22. package/templates/angular/checkbox/checkbox.component.ts +172 -0
  23. package/templates/angular/checkbox/checkbox.module.ts +8 -0
  24. package/templates/angular/datepicker/datepicker.component.ts +660 -0
  25. package/templates/angular/datepicker/datepicker.module.ts +8 -0
  26. package/templates/angular/dialog/dialog-content.component.ts +9 -0
  27. package/templates/angular/dialog/dialog-description.component.ts +17 -0
  28. package/templates/angular/dialog/dialog-footer.component.ts +17 -0
  29. package/templates/angular/dialog/dialog-header.component.ts +14 -0
  30. package/templates/angular/dialog/dialog-title.component.ts +18 -0
  31. package/templates/angular/dialog/dialog-trigger.component.ts +9 -0
  32. package/templates/angular/dialog/dialog.component.ts +212 -0
  33. package/templates/angular/dialog/dialog.module.ts +31 -0
  34. package/templates/angular/input/input.component.ts +229 -0
  35. package/templates/angular/input/input.module.ts +8 -0
  36. package/templates/angular/scroll-area/scroll-area.component.ts +72 -0
  37. package/templates/angular/scroll-area/scroll-area.module.ts +9 -0
  38. package/templates/angular/scroll-area/scroll-bar.component.ts +15 -0
  39. package/templates/angular/select/select.component.ts +292 -0
  40. package/templates/angular/select/select.module.ts +8 -0
  41. package/templates/angular/separator/separator.component.ts +63 -0
  42. package/templates/angular/separator/separator.module.ts +9 -0
  43. package/templates/angular/sheet/sheet-content.component.ts +13 -0
  44. package/templates/angular/sheet/sheet-description.component.ts +29 -0
  45. package/templates/angular/sheet/sheet-footer.component.ts +27 -0
  46. package/templates/angular/sheet/sheet-header.component.ts +26 -0
  47. package/templates/angular/sheet/sheet-title.component.ts +31 -0
  48. package/templates/angular/sheet/sheet-trigger.component.ts +11 -0
  49. package/templates/angular/sheet/sheet.component.ts +251 -0
  50. package/templates/angular/sheet/sheet.module.ts +30 -0
  51. package/templates/angular/sidebar/sidebar-content.component.ts +25 -0
  52. package/templates/angular/sidebar/sidebar-footer.component.ts +20 -0
  53. package/templates/angular/sidebar/sidebar-group-content.component.ts +16 -0
  54. package/templates/angular/sidebar/sidebar-group-label.component.ts +20 -0
  55. package/templates/angular/sidebar/sidebar-group.component.ts +14 -0
  56. package/templates/angular/sidebar/sidebar-header.component.ts +25 -0
  57. package/templates/angular/sidebar/sidebar-inset.component.ts +85 -0
  58. package/templates/angular/sidebar/sidebar-menu-button.component.ts +75 -0
  59. package/templates/angular/sidebar/sidebar-menu-item.component.ts +14 -0
  60. package/templates/angular/sidebar/sidebar-menu.component.ts +19 -0
  61. package/templates/angular/sidebar/sidebar-provider.component.ts +77 -0
  62. package/templates/angular/sidebar/sidebar-trigger.component.ts +58 -0
  63. package/templates/angular/sidebar/sidebar.component.ts +228 -0
  64. package/templates/angular/sidebar/sidebar.module.ts +48 -0
  65. package/templates/angular/sidebar/sidebar.service.ts +93 -0
  66. package/templates/angular/skeleton/skeleton.component.ts +44 -0
  67. package/templates/angular/skeleton/skeleton.module.ts +8 -0
  68. package/templates/angular/spinner/spinner.component.ts +75 -0
  69. package/templates/angular/spinner/spinner.module.ts +8 -0
  70. package/templates/angular/table/table-body.component.ts +23 -0
  71. package/templates/angular/table/table-caption.component.ts +29 -0
  72. package/templates/angular/table/table-cell.component.ts +49 -0
  73. package/templates/angular/table/table-footer.component.ts +32 -0
  74. package/templates/angular/table/table-head.component.ts +48 -0
  75. package/templates/angular/table/table-header.component.ts +28 -0
  76. package/templates/angular/table/table-row.component.ts +36 -0
  77. package/templates/angular/table/table.component.ts +35 -0
  78. package/templates/angular/table/table.module.ts +33 -0
  79. package/templates/angular/tabs/tabs-content.component.ts +71 -0
  80. package/templates/angular/tabs/tabs-list.component.ts +70 -0
  81. package/templates/angular/tabs/tabs-trigger.component.ts +149 -0
  82. package/templates/angular/tabs/tabs.component.ts +155 -0
  83. package/templates/angular/tabs/tabs.module.ts +21 -0
  84. package/templates/angular/textarea/textarea.component.ts +268 -0
  85. package/templates/angular/textarea/textarea.module.ts +8 -0
  86. package/templates/angular/toast/toast.module.ts +8 -0
  87. package/templates/angular/toast/toast.service.ts +104 -0
  88. package/templates/angular/toast/toaster.component.ts +329 -0
  89. package/templates/angular/tooltip/tooltip-content.component.ts +43 -0
  90. package/templates/angular/tooltip/tooltip-trigger.component.ts +13 -0
  91. package/templates/angular/tooltip/tooltip.component.ts +243 -0
  92. package/templates/angular/tooltip/tooltip.module.ts +10 -0
@@ -0,0 +1,251 @@
1
+ import {
2
+ Component,
3
+ Input,
4
+ Output,
5
+ EventEmitter,
6
+ ViewEncapsulation,
7
+ HostListener,
8
+ } from '@angular/core';
9
+ import { cn } from '../utils';
10
+
11
+ export type SheetSide = 'top' | 'right' | 'bottom' | 'left';
12
+
13
+ export interface SheetProps {
14
+ side?: SheetSide;
15
+ showCloseButton?: boolean;
16
+ }
17
+
18
+
19
+ @Component({
20
+ selector: 'salas-sheet',
21
+ standalone: true,
22
+ imports: [],
23
+ encapsulation: ViewEncapsulation.None,
24
+ template: `
25
+ <div class="salas-sheet-trigger-wrapper" (click)="openSheet()">
26
+ <ng-content select="salas-sheet-trigger"></ng-content>
27
+ </div>
28
+
29
+ @if (isOpen) {
30
+ <div class="salas-sheet-overlay" (click)="onOverlayClick()"></div>
31
+ <div [class]="panelClasses" role="dialog" aria-modal="true">
32
+ @if (showCloseButton) {
33
+ <button
34
+ type="button"
35
+ class="salas-sheet-close"
36
+ aria-label="Close"
37
+ (click)="closeSheet()"
38
+ >
39
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
40
+ </button>
41
+ }
42
+ <ng-content select="salas-sheet-content"></ng-content>
43
+ </div>
44
+ }
45
+ `,
46
+ styles: [`
47
+ .salas-sheet-trigger-wrapper {
48
+ display: inline-block;
49
+ }
50
+
51
+ .salas-sheet-overlay {
52
+ position: fixed;
53
+ inset: 0;
54
+ background-color: rgba(0, 0, 0, 0.5);
55
+ z-index: 50;
56
+ animation: salas-sheet-fade-in 0.2s ease;
57
+ }
58
+
59
+ @keyframes salas-sheet-fade-in {
60
+ from { opacity: 0; }
61
+ to { opacity: 1; }
62
+ }
63
+
64
+ /* Panel base */
65
+ .salas-sheet-panel {
66
+ position: fixed;
67
+ z-index: 51;
68
+ display: flex;
69
+ flex-direction: column;
70
+ background-color: var(--salas-bg-elevated, #fff);
71
+ color: var(--salas-text, #111827);
72
+ border: 1px solid var(--salas-gray-200, #e5e7eb);
73
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
74
+ overflow: auto;
75
+ }
76
+
77
+ /* Side: right (default) */
78
+ .salas-sheet-panel--right {
79
+ top: 0;
80
+ right: 0;
81
+ height: 100%;
82
+ width: 100%;
83
+ max-width: 24rem;
84
+ border-left: 1px solid var(--salas-gray-200, #e5e7eb);
85
+ border-top: none;
86
+ border-bottom: none;
87
+ border-right: none;
88
+ animation: salas-sheet-slide-right 0.3s cubic-bezier(0.16, 1, 0.3, 1);
89
+ }
90
+
91
+ @keyframes salas-sheet-slide-right {
92
+ from { transform: translateX(100%); }
93
+ to { transform: translateX(0); }
94
+ }
95
+
96
+ /* Side: left */
97
+ .salas-sheet-panel--left {
98
+ top: 0;
99
+ left: 0;
100
+ height: 100%;
101
+ width: 100%;
102
+ max-width: 24rem;
103
+ border-right: 1px solid var(--salas-gray-200, #e5e7eb);
104
+ border-top: none;
105
+ border-bottom: none;
106
+ border-left: none;
107
+ animation: salas-sheet-slide-left 0.3s cubic-bezier(0.16, 1, 0.3, 1);
108
+ }
109
+
110
+ @keyframes salas-sheet-slide-left {
111
+ from { transform: translateX(-100%); }
112
+ to { transform: translateX(0); }
113
+ }
114
+
115
+ /* Side: top */
116
+ .salas-sheet-panel--top {
117
+ top: 0;
118
+ left: 0;
119
+ right: 0;
120
+ width: 100%;
121
+ max-height: 24rem;
122
+ border-bottom: 1px solid var(--salas-gray-200, #e5e7eb);
123
+ border-top: none;
124
+ border-left: none;
125
+ border-right: none;
126
+ animation: salas-sheet-slide-top 0.3s cubic-bezier(0.16, 1, 0.3, 1);
127
+ }
128
+
129
+ @keyframes salas-sheet-slide-top {
130
+ from { transform: translateY(-100%); }
131
+ to { transform: translateY(0); }
132
+ }
133
+
134
+ /* Side: bottom */
135
+ .salas-sheet-panel--bottom {
136
+ bottom: 0;
137
+ left: 0;
138
+ right: 0;
139
+ width: 100%;
140
+ max-height: 24rem;
141
+ border-top: 1px solid var(--salas-gray-200, #e5e7eb);
142
+ border-bottom: none;
143
+ border-left: none;
144
+ border-right: none;
145
+ animation: salas-sheet-slide-bottom 0.3s cubic-bezier(0.16, 1, 0.3, 1);
146
+ }
147
+
148
+ @keyframes salas-sheet-slide-bottom {
149
+ from { transform: translateY(100%); }
150
+ to { transform: translateY(0); }
151
+ }
152
+
153
+ /* Close button */
154
+ .salas-sheet-close {
155
+ position: absolute;
156
+ top: 1rem;
157
+ right: 1rem;
158
+ display: flex;
159
+ align-items: center;
160
+ justify-content: center;
161
+ width: 1.5rem;
162
+ height: 1.5rem;
163
+ border: none;
164
+ background: transparent;
165
+ color: var(--salas-text-muted, #6b7280);
166
+ cursor: pointer;
167
+ border-radius: 0.25rem;
168
+ padding: 0;
169
+ z-index: 1;
170
+ transition: color 0.15s, background-color 0.15s;
171
+ }
172
+
173
+ .salas-sheet-close:hover {
174
+ background-color: var(--salas-gray-100, #f3f4f6);
175
+ color: var(--salas-text, #111827);
176
+ }
177
+
178
+ /* Dark theme */
179
+ [data-theme='dark'] .salas-sheet-panel {
180
+ background-color: var(--salas-bg-elevated, #18181b);
181
+ border-color: var(--salas-gray-800, #27272a);
182
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
183
+ }
184
+
185
+ [data-theme='dark'] .salas-sheet-panel--right {
186
+ border-left-color: var(--salas-gray-800, #27272a);
187
+ }
188
+
189
+ [data-theme='dark'] .salas-sheet-panel--left {
190
+ border-right-color: var(--salas-gray-800, #27272a);
191
+ }
192
+
193
+ [data-theme='dark'] .salas-sheet-panel--top {
194
+ border-bottom-color: var(--salas-gray-800, #27272a);
195
+ }
196
+
197
+ [data-theme='dark'] .salas-sheet-panel--bottom {
198
+ border-top-color: var(--salas-gray-800, #27272a);
199
+ }
200
+
201
+ [data-theme='dark'] .salas-sheet-close:hover {
202
+ background-color: var(--salas-gray-800, #27272a);
203
+ color: var(--salas-gray-100, #f4f4f5);
204
+ }
205
+ `],
206
+ })
207
+ export class SalasSheetComponent {
208
+ @Input() side: SheetSide = 'right';
209
+ @Input() open?: boolean;
210
+ @Input() showCloseButton = true;
211
+ @Input() closeOnOverlayClick = true;
212
+
213
+ @Output() openChange = new EventEmitter<boolean>();
214
+
215
+ internalOpen = false;
216
+
217
+ get isOpen(): boolean {
218
+ return this.open ?? this.internalOpen;
219
+ }
220
+
221
+ get panelClasses(): string {
222
+ return cn('salas-sheet-panel', `salas-sheet-panel--${this.side}`);
223
+ }
224
+
225
+ openSheet(): void {
226
+ if (this.open === undefined) {
227
+ this.internalOpen = true;
228
+ }
229
+ this.openChange.emit(true);
230
+ }
231
+
232
+ closeSheet(): void {
233
+ if (this.open === undefined) {
234
+ this.internalOpen = false;
235
+ }
236
+ this.openChange.emit(false);
237
+ }
238
+
239
+ onOverlayClick(): void {
240
+ if (this.closeOnOverlayClick) {
241
+ this.closeSheet();
242
+ }
243
+ }
244
+
245
+ @HostListener('document:keydown.escape')
246
+ onEscapeKey(): void {
247
+ if (this.isOpen) {
248
+ this.closeSheet();
249
+ }
250
+ }
251
+ }
@@ -0,0 +1,30 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { SalasSheetComponent } from './sheet.component';
3
+ import { SalasSheetTriggerComponent } from './sheet-trigger.component';
4
+ import { SalasSheetContentComponent } from './sheet-content.component';
5
+ import { SalasSheetHeaderComponent } from './sheet-header.component';
6
+ import { SalasSheetTitleComponent } from './sheet-title.component';
7
+ import { SalasSheetDescriptionComponent } from './sheet-description.component';
8
+ import { SalasSheetFooterComponent } from './sheet-footer.component';
9
+
10
+ @NgModule({
11
+ imports: [
12
+ SalasSheetComponent,
13
+ SalasSheetTriggerComponent,
14
+ SalasSheetContentComponent,
15
+ SalasSheetHeaderComponent,
16
+ SalasSheetTitleComponent,
17
+ SalasSheetDescriptionComponent,
18
+ SalasSheetFooterComponent,
19
+ ],
20
+ exports: [
21
+ SalasSheetComponent,
22
+ SalasSheetTriggerComponent,
23
+ SalasSheetContentComponent,
24
+ SalasSheetHeaderComponent,
25
+ SalasSheetTitleComponent,
26
+ SalasSheetDescriptionComponent,
27
+ SalasSheetFooterComponent,
28
+ ],
29
+ })
30
+ export class SalasSheetModule {}
@@ -0,0 +1,25 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sidebar-content',
5
+ standalone: true,
6
+ imports: [],
7
+ template: `<div class="salas-sidebar-content"><ng-content></ng-content></div>`,
8
+ styles: [`
9
+ :host {
10
+ display: flex;
11
+ flex: 1;
12
+ min-height: 0;
13
+ overflow: hidden;
14
+ }
15
+ .salas-sidebar-content {
16
+ flex: 1;
17
+ min-height: 0;
18
+ overflow-y: auto;
19
+ overflow-x: hidden;
20
+ padding: 0.5rem;
21
+ color: var(--salas-text);
22
+ }
23
+ `],
24
+ })
25
+ export class SalasSidebarContentComponent {}
@@ -0,0 +1,20 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sidebar-footer',
5
+ standalone: true,
6
+ imports: [],
7
+ template: `<footer class="salas-sidebar-footer"><ng-content></ng-content></footer>`,
8
+ styles: [`
9
+ .salas-sidebar-footer {
10
+ flex-shrink: 0;
11
+ padding: 1rem 1.25rem;
12
+ border-top: 1px solid var(--salas-gray-200);
13
+ color: var(--salas-text);
14
+ overflow: hidden;
15
+ white-space: nowrap;
16
+ transition: padding 0.2s ease, height 0.2s ease, opacity 0.2s ease;
17
+ }
18
+ `],
19
+ })
20
+ export class SalasSidebarFooterComponent {}
@@ -0,0 +1,16 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sidebar-group-content',
5
+ standalone: true,
6
+ imports: [],
7
+ template: `<div class="salas-sidebar-group-content"><ng-content></ng-content></div>`,
8
+ styles: [`
9
+ .salas-sidebar-group-content {
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: 0.125rem;
13
+ }
14
+ `],
15
+ })
16
+ export class SalasSidebarGroupContentComponent {}
@@ -0,0 +1,20 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sidebar-group-label',
5
+ standalone: true,
6
+ imports: [],
7
+ template: `<div class="salas-sidebar-group-label"><ng-content></ng-content></div>`,
8
+ styles: [`
9
+ .salas-sidebar-group-label {
10
+ padding: 0.5rem 0.75rem;
11
+ font-size: 0.75rem;
12
+ font-weight: 600;
13
+ color: var(--salas-text-muted);
14
+ text-transform: uppercase;
15
+ letter-spacing: 0.05em;
16
+ transition: padding 0.2s ease, height 0.2s ease, opacity 0.2s ease;
17
+ }
18
+ `],
19
+ })
20
+ export class SalasSidebarGroupLabelComponent {}
@@ -0,0 +1,14 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sidebar-group',
5
+ standalone: true,
6
+ imports: [],
7
+ template: `<div class="salas-sidebar-group"><ng-content></ng-content></div>`,
8
+ styles: [`
9
+ .salas-sidebar-group {
10
+ margin-bottom: 0.5rem;
11
+ }
12
+ `],
13
+ })
14
+ export class SalasSidebarGroupComponent {}
@@ -0,0 +1,25 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sidebar-header',
5
+ standalone: true,
6
+ imports: [],
7
+ template: `<header class="salas-sidebar-header"><ng-content></ng-content></header>`,
8
+ styles: [`
9
+ :host {
10
+ display: block;
11
+ }
12
+ .salas-sidebar-header {
13
+ flex-shrink: 0;
14
+ padding: 1rem 1.25rem;
15
+ border-bottom: 1px solid var(--salas-gray-200);
16
+ color: var(--salas-text);
17
+ overflow: hidden;
18
+ white-space: nowrap;
19
+ transition: padding 0.2s ease, height 0.2s ease, opacity 0.2s ease;
20
+ font-weight: 600;
21
+ font-size: 1rem;
22
+ }
23
+ `],
24
+ })
25
+ export class SalasSidebarHeaderComponent {}
@@ -0,0 +1,85 @@
1
+ import { Component, Input, Optional } from '@angular/core';
2
+ import { SidebarService } from './sidebar.service';
3
+
4
+ /**
5
+ * Sidebar component types (shadcn-style)
6
+ */
7
+
8
+ export type SidebarSide = 'left' | 'right';
9
+ export type SidebarVariant = 'sidebar' | 'floating' | 'inset';
10
+ export type SidebarCollapsible = 'offcanvas' | 'icon' | 'none';
11
+
12
+ export interface SidebarProps {
13
+ side?: SidebarSide;
14
+ variant?: SidebarVariant;
15
+ collapsible?: SidebarCollapsible;
16
+ }
17
+
18
+ export interface SidebarProviderProps {
19
+ defaultOpen?: boolean;
20
+ open?: boolean;
21
+ }
22
+
23
+
24
+ const SIDEBAR_WIDTH = '16rem';
25
+ const SIDEBAR_WIDTH_ICON = '3rem';
26
+
27
+ @Component({
28
+ selector: 'salas-sidebar-inset',
29
+ standalone: true,
30
+ imports: [],
31
+ template: `<main class="salas-sidebar-inset" [class.salas-sidebar-inset--side-right]="effectiveSide === 'right'" [style.margin-left]="marginLeft" [style.margin-right]="marginRight"><ng-content></ng-content></main>`,
32
+ styles: [`
33
+ .salas-sidebar-inset {
34
+ flex: 1;
35
+ min-width: 0;
36
+ min-height: 100vh;
37
+ padding: 1rem;
38
+ background-color: var(--salas-bg);
39
+ color: var(--salas-text);
40
+ transition: margin 0.2s ease, align-items 0.2s ease, text-align 0.2s ease;
41
+ text-align: left;
42
+ display: flex;
43
+ flex-direction: column;
44
+ align-items: flex-start;
45
+ }
46
+
47
+ .salas-sidebar-inset--side-right {
48
+ align-items: flex-end;
49
+ text-align: right;
50
+ }
51
+ `],
52
+ })
53
+ export class SalasSidebarInsetComponent {
54
+ @Input() side?: SidebarSide;
55
+
56
+ constructor(@Optional() public sidebar: SidebarService | null) {}
57
+
58
+ get effectiveSide(): SidebarSide {
59
+ return this.side ?? this.sidebar?.side ?? 'left';
60
+ }
61
+
62
+ get marginLeft(): string | null {
63
+ if (this.effectiveSide !== 'left') return null;
64
+ if (this.sidebar?.isMobile && this.sidebar.collapsible === 'offcanvas') {
65
+ return '0';
66
+ }
67
+ if (this.sidebar?.collapsible === 'offcanvas' && !this.sidebar.open) return '0';
68
+ if (this.sidebar?.collapsible === 'icon' && this.sidebar.state === 'collapsed') {
69
+ return SIDEBAR_WIDTH_ICON;
70
+ }
71
+ return SIDEBAR_WIDTH;
72
+ }
73
+
74
+ get marginRight(): string | null {
75
+ if (this.effectiveSide !== 'right') return null;
76
+ if (this.sidebar?.isMobile && this.sidebar.collapsible === 'offcanvas') {
77
+ return '0';
78
+ }
79
+ if (this.sidebar?.collapsible === 'offcanvas' && !this.sidebar.open) return '0';
80
+ if (this.sidebar?.collapsible === 'icon' && this.sidebar.state === 'collapsed') {
81
+ return SIDEBAR_WIDTH_ICON;
82
+ }
83
+ return SIDEBAR_WIDTH;
84
+ }
85
+ }
@@ -0,0 +1,75 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import { cn } from '../utils';
3
+
4
+ @Component({
5
+ selector: 'salas-sidebar-menu-button',
6
+ standalone: true,
7
+ imports: [],
8
+ template: `
9
+ @if (href) {
10
+ <a
11
+ [class]="buttonClasses"
12
+ [href]="href"
13
+ [attr.aria-current]="isActive ? 'page' : null"
14
+ [attr.title]="tooltipLabel"
15
+ >
16
+ <ng-content></ng-content>
17
+ </a>
18
+ } @else {
19
+ <button
20
+ type="button"
21
+ [class]="buttonClasses"
22
+ [attr.aria-current]="isActive ? 'page' : null"
23
+ [attr.title]="tooltipLabel"
24
+ >
25
+ <ng-content></ng-content>
26
+ </button>
27
+ }
28
+ `,
29
+ styles: [`
30
+ :host {
31
+ display: block;
32
+ }
33
+
34
+ .salas-sidebar-menu-button {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 0.75rem;
38
+ width: 100%;
39
+ padding: 0.5rem 0.75rem;
40
+ border-radius: 0.375rem;
41
+ font-size: 0.875rem;
42
+ color: var(--salas-text);
43
+ text-decoration: none;
44
+ transition: background-color 0.2s, color 0.2s, padding 0.2s, gap 0.2s;
45
+ cursor: pointer;
46
+ border: none;
47
+ background: none;
48
+ font-family: inherit;
49
+ overflow: hidden;
50
+ }
51
+
52
+ .salas-sidebar-menu-button:hover {
53
+ background-color: var(--salas-gray-100);
54
+ color: var(--salas-text);
55
+ }
56
+
57
+ .salas-sidebar-menu-button--active {
58
+ background-color: var(--salas-gray-100);
59
+ color: var(--salas-text);
60
+ font-weight: 500;
61
+ }
62
+ `],
63
+ })
64
+ export class SalasSidebarMenuButtonComponent {
65
+ @Input() isActive = false;
66
+ @Input() href?: string;
67
+ @Input() tooltipLabel?: string;
68
+
69
+ get buttonClasses(): string {
70
+ return cn(
71
+ 'salas-sidebar-menu-button',
72
+ this.isActive && 'salas-sidebar-menu-button--active'
73
+ );
74
+ }
75
+ }
@@ -0,0 +1,14 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sidebar-menu-item',
5
+ standalone: true,
6
+ imports: [],
7
+ template: `<li class="salas-sidebar-menu-item" role="none"><ng-content></ng-content></li>`,
8
+ styles: [`
9
+ .salas-sidebar-menu-item {
10
+ display: block;
11
+ }
12
+ `],
13
+ })
14
+ export class SalasSidebarMenuItemComponent {}
@@ -0,0 +1,19 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sidebar-menu',
5
+ standalone: true,
6
+ imports: [],
7
+ template: `<ul class="salas-sidebar-menu" role="menu"><ng-content></ng-content></ul>`,
8
+ styles: [`
9
+ .salas-sidebar-menu {
10
+ list-style: none;
11
+ margin: 0;
12
+ padding: 0;
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: 0.125rem;
16
+ }
17
+ `],
18
+ })
19
+ export class SalasSidebarMenuComponent {}
@@ -0,0 +1,77 @@
1
+ import {
2
+ Component,
3
+ Input,
4
+ Output,
5
+ EventEmitter,
6
+ OnInit,
7
+ OnDestroy,
8
+ OnChanges,
9
+ SimpleChanges,
10
+ Inject,
11
+ PLATFORM_ID,
12
+ } from '@angular/core';
13
+ import { isPlatformBrowser } from '@angular/common';
14
+ import { SidebarService } from './sidebar.service';
15
+
16
+ const MOBILE_BREAKPOINT = 768;
17
+
18
+ @Component({
19
+ selector: 'salas-sidebar-provider',
20
+ standalone: true,
21
+ imports: [],
22
+ providers: [SidebarService],
23
+ template: `<div class="salas-sidebar-provider"><ng-content></ng-content></div>`,
24
+ styles: [`
25
+ :host {
26
+ display: block;
27
+ height: 100%;
28
+ min-height: 100vh;
29
+ overflow: visible;
30
+ }
31
+ .salas-sidebar-provider {
32
+ display: flex;
33
+ flex-direction: row;
34
+ min-height: 100vh;
35
+ height: 100%;
36
+ width: 100%;
37
+ position: relative;
38
+ overflow: visible;
39
+ }
40
+ `],
41
+ })
42
+ export class SalasSidebarProviderComponent implements OnInit, OnDestroy, OnChanges {
43
+ @Input() defaultOpen = true;
44
+ @Input() open?: boolean;
45
+ @Output() openChange = new EventEmitter<boolean>();
46
+
47
+ private mediaQuery?: MediaQueryList;
48
+ private listener = () => this.updateMobile();
49
+
50
+ constructor(
51
+ public sidebar: SidebarService,
52
+ @Inject(PLATFORM_ID) private platformId: object
53
+ ) {}
54
+
55
+ public ngOnChanges(changes: SimpleChanges): void {
56
+ if (changes['open'] && !changes['open'].firstChange && this.open !== undefined) {
57
+ this.sidebar.setOpen(this.open);
58
+ }
59
+ }
60
+
61
+ ngOnInit(): void {
62
+ this.sidebar.init(this.defaultOpen, this.open, (open) => this.openChange.emit(open));
63
+ if (isPlatformBrowser(this.platformId) && typeof window !== 'undefined' && window.matchMedia) {
64
+ this.mediaQuery = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
65
+ this.sidebar.isMobile = this.mediaQuery.matches;
66
+ this.mediaQuery.addEventListener('change', this.listener);
67
+ }
68
+ }
69
+
70
+ ngOnDestroy(): void {
71
+ this.mediaQuery?.removeEventListener('change', this.listener);
72
+ }
73
+
74
+ private updateMobile(): void {
75
+ this.sidebar.isMobile = this.mediaQuery?.matches ?? false;
76
+ }
77
+ }