@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,58 @@
1
+ import { Component, Optional } from '@angular/core';
2
+ import { SidebarService } from './sidebar.service';
3
+
4
+ @Component({
5
+ selector: 'salas-sidebar-trigger',
6
+ standalone: true,
7
+ imports: [],
8
+ template: `
9
+ <button type="button" class="salas-sidebar-trigger" [class.salas-sidebar-trigger--right]="isRight" (click)="onClick()" aria-label="Toggle sidebar">
10
+ @if (isRight) {
11
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
12
+ <rect width="18" height="18" x="3" y="3" rx="2"/>
13
+ <path d="M15 3v18"/>
14
+ </svg>
15
+ } @else {
16
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
17
+ <rect width="18" height="18" x="3" y="3" rx="2"/>
18
+ <path d="M9 3v18"/>
19
+ </svg>
20
+ }
21
+ </button>
22
+ `,
23
+ styles: [`
24
+ .salas-sidebar-trigger {
25
+ display: inline-flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ width: 2.5rem;
29
+ height: 2.5rem;
30
+ border-radius: 0.375rem;
31
+ border: none;
32
+ background-color: transparent;
33
+ color: var(--salas-text);
34
+ cursor: pointer;
35
+ transition: background-color 0.2s, color 0.2s;
36
+ }
37
+
38
+ .salas-sidebar-trigger:hover {
39
+ background-color: var(--salas-gray-100);
40
+ color: var(--salas-text);
41
+ }
42
+
43
+ .salas-sidebar-trigger svg {
44
+ display: block;
45
+ }
46
+ `],
47
+ })
48
+ export class SalasSidebarTriggerComponent {
49
+ constructor(@Optional() private sidebar: SidebarService | null) {}
50
+
51
+ get isRight(): boolean {
52
+ return this.sidebar?.side === 'right';
53
+ }
54
+
55
+ onClick(): void {
56
+ this.sidebar?.toggleSidebar();
57
+ }
58
+ }
@@ -0,0 +1,228 @@
1
+ import { Component, Input, Optional, OnInit, ViewEncapsulation } from '@angular/core';
2
+ import { cn } from '../utils';
3
+ import { SidebarService } from './sidebar.service';
4
+
5
+ /**
6
+ * Sidebar component types (shadcn-style)
7
+ */
8
+
9
+ export type SidebarSide = 'left' | 'right';
10
+ export type SidebarVariant = 'sidebar' | 'floating' | 'inset';
11
+ export type SidebarCollapsible = 'offcanvas' | 'icon' | 'none';
12
+
13
+ export interface SidebarProps {
14
+ side?: SidebarSide;
15
+ variant?: SidebarVariant;
16
+ collapsible?: SidebarCollapsible;
17
+ }
18
+
19
+ export interface SidebarProviderProps {
20
+ defaultOpen?: boolean;
21
+ open?: boolean;
22
+ }
23
+
24
+
25
+ @Component({
26
+ selector: 'salas-sidebar',
27
+ standalone: true,
28
+ imports: [],
29
+ encapsulation: ViewEncapsulation.None,
30
+ template: `
31
+ <aside
32
+ [class]="sidebarClasses"
33
+ [attr.data-side]="side"
34
+ [attr.data-state]="effectiveState"
35
+ [attr.data-collapsible]="collapsible"
36
+ [attr.data-variant]="variant"
37
+ [attr.data-mobile]="sidebar?.isMobile ?? false"
38
+ >
39
+ <ng-content></ng-content>
40
+ </aside>
41
+ `,
42
+ styles: [`
43
+ :host {
44
+ display: block;
45
+ flex-shrink: 0;
46
+ width: 0;
47
+ min-width: 0;
48
+ }
49
+
50
+ .salas-sidebar {
51
+ --salas-sidebar-width: 16rem;
52
+ --salas-sidebar-width-icon: 3rem;
53
+ box-sizing: border-box;
54
+ background-color: var(--salas-bg-elevated);
55
+ color: var(--salas-text);
56
+ border-right: 1px solid var(--salas-gray-200);
57
+ display: flex;
58
+ flex-direction: column;
59
+ height: 100vh;
60
+ min-height: 100vh;
61
+ position: fixed;
62
+ top: 0;
63
+ z-index: 10;
64
+ transition: width 0.2s ease, transform 0.2s ease, left 0.2s ease, right 0.2s ease;
65
+ width: var(--salas-sidebar-width);
66
+ }
67
+
68
+ .salas-sidebar > salas-sidebar-header {
69
+ flex-shrink: 0;
70
+ display: block;
71
+ }
72
+ .salas-sidebar > salas-sidebar-content {
73
+ flex: 1 1 0;
74
+ min-height: 0;
75
+ display: flex;
76
+ overflow: hidden;
77
+ }
78
+ .salas-sidebar > salas-sidebar-content > .salas-sidebar-content {
79
+ flex: 1;
80
+ min-height: 0;
81
+ display: block;
82
+ overflow-y: auto;
83
+ overflow-x: hidden;
84
+ width: 100%;
85
+ padding: 0.5rem;
86
+ }
87
+ .salas-sidebar salas-sidebar-menu {
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: 0.125rem;
91
+ }
92
+ .salas-sidebar salas-sidebar-menu-item {
93
+ display: block;
94
+ }
95
+
96
+ .salas-sidebar[data-side="left"] {
97
+ left: 0;
98
+ }
99
+
100
+ .salas-sidebar[data-side="right"] {
101
+ right: 0;
102
+ border-right: none;
103
+ border-left: 1px solid var(--salas-gray-200);
104
+ }
105
+
106
+ .salas-sidebar[data-collapsible="offcanvas"][data-mobile="true"][data-state="collapsed"] {
107
+ transform: translateX(-100%);
108
+ }
109
+
110
+ .salas-sidebar[data-side="right"][data-collapsible="offcanvas"][data-mobile="true"][data-state="collapsed"] {
111
+ transform: translateX(100%);
112
+ }
113
+
114
+ .salas-sidebar[data-collapsible="offcanvas"][data-mobile="false"][data-state="collapsed"] {
115
+ transform: translateX(-100%);
116
+ }
117
+
118
+ .salas-sidebar[data-side="right"][data-collapsible="offcanvas"][data-mobile="false"][data-state="collapsed"] {
119
+ transform: translateX(100%);
120
+ }
121
+
122
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] {
123
+ width: var(--salas-sidebar-width-icon);
124
+ overflow: hidden;
125
+ }
126
+
127
+ .salas-sidebar[data-variant="floating"] {
128
+ margin: 0.5rem;
129
+ border-radius: 0.5rem;
130
+ height: calc(100vh - 1rem);
131
+ border: 1px solid var(--salas-gray-200);
132
+ }
133
+
134
+ .salas-sidebar[data-variant="inset"] {
135
+ position: relative;
136
+ height: 100%;
137
+ border-radius: 0.5rem;
138
+ }
139
+
140
+ /* Collapsed state: hide everything except icons */
141
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] salas-sidebar-header {
142
+ display: none !important;
143
+ }
144
+
145
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] salas-sidebar-footer {
146
+ display: none !important;
147
+ }
148
+
149
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] salas-sidebar-group-label {
150
+ display: none !important;
151
+ }
152
+
153
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] .salas-sidebar-content {
154
+ padding: 0.25rem 0 !important;
155
+ }
156
+
157
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] .salas-sidebar-group {
158
+ margin-bottom: 0 !important;
159
+ }
160
+
161
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] .salas-sidebar-group-content {
162
+ gap: 0.25rem !important;
163
+ }
164
+
165
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] .salas-sidebar-menu {
166
+ padding: 0 !important;
167
+ align-items: center !important;
168
+ }
169
+
170
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] salas-sidebar-menu-button {
171
+ display: flex !important;
172
+ justify-content: center !important;
173
+ }
174
+
175
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] .salas-sidebar-menu-button {
176
+ justify-content: center !important;
177
+ padding: 0.5rem !important;
178
+ gap: 0 !important;
179
+ overflow: hidden !important;
180
+ width: auto !important;
181
+ }
182
+
183
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] .salas-sidebar-menu-button > *:not(.salas-sidebar-menu-icon) {
184
+ display: none !important;
185
+ width: 0 !important;
186
+ height: 0 !important;
187
+ overflow: hidden !important;
188
+ }
189
+
190
+ .salas-sidebar[data-collapsible="icon"][data-state="collapsed"] .salas-sidebar-menu-icon {
191
+ flex-shrink: 0 !important;
192
+ }
193
+ `],
194
+ })
195
+ export class SalasSidebarComponent implements SidebarProps, OnInit {
196
+ @Input() side: SidebarSide = 'left';
197
+ @Input() variant: SidebarVariant = 'sidebar';
198
+ @Input() collapsible: SidebarCollapsible = 'icon';
199
+
200
+ constructor(@Optional() public sidebar: SidebarService | null) {}
201
+
202
+ ngOnInit(): void {
203
+ if (this.sidebar) {
204
+ this.sidebar.collapsible = this.collapsible;
205
+ this.sidebar.side = this.side;
206
+ }
207
+ }
208
+
209
+ get effectiveState(): string {
210
+ if (!this.sidebar) return 'expanded';
211
+ if (this.sidebar.isMobile) {
212
+ return this.sidebar.openMobile ? 'expanded' : 'collapsed';
213
+ }
214
+ if (this.collapsible === 'offcanvas') {
215
+ return this.sidebar.open ? 'expanded' : 'collapsed';
216
+ }
217
+ return this.sidebar.state;
218
+ }
219
+
220
+ get sidebarClasses(): string {
221
+ return cn(
222
+ 'salas-sidebar',
223
+ `salas-sidebar--${this.side}`,
224
+ this.variant !== 'sidebar' && `salas-sidebar--${this.variant}`,
225
+ `salas-sidebar--collapsible-${this.collapsible}`
226
+ );
227
+ }
228
+ }
@@ -0,0 +1,48 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { SalasSidebarProviderComponent } from './sidebar-provider.component';
3
+ import { SalasSidebarComponent } from './sidebar.component';
4
+ import { SalasSidebarHeaderComponent } from './sidebar-header.component';
5
+ import { SalasSidebarFooterComponent } from './sidebar-footer.component';
6
+ import { SalasSidebarContentComponent } from './sidebar-content.component';
7
+ import { SalasSidebarGroupComponent } from './sidebar-group.component';
8
+ import { SalasSidebarGroupLabelComponent } from './sidebar-group-label.component';
9
+ import { SalasSidebarGroupContentComponent } from './sidebar-group-content.component';
10
+ import { SalasSidebarTriggerComponent } from './sidebar-trigger.component';
11
+ import { SalasSidebarMenuComponent } from './sidebar-menu.component';
12
+ import { SalasSidebarMenuItemComponent } from './sidebar-menu-item.component';
13
+ import { SalasSidebarMenuButtonComponent } from './sidebar-menu-button.component';
14
+ import { SalasSidebarInsetComponent } from './sidebar-inset.component';
15
+
16
+ @NgModule({
17
+ imports: [
18
+ SalasSidebarProviderComponent,
19
+ SalasSidebarComponent,
20
+ SalasSidebarHeaderComponent,
21
+ SalasSidebarFooterComponent,
22
+ SalasSidebarContentComponent,
23
+ SalasSidebarGroupComponent,
24
+ SalasSidebarGroupLabelComponent,
25
+ SalasSidebarGroupContentComponent,
26
+ SalasSidebarTriggerComponent,
27
+ SalasSidebarMenuComponent,
28
+ SalasSidebarMenuItemComponent,
29
+ SalasSidebarMenuButtonComponent,
30
+ SalasSidebarInsetComponent,
31
+ ],
32
+ exports: [
33
+ SalasSidebarProviderComponent,
34
+ SalasSidebarComponent,
35
+ SalasSidebarHeaderComponent,
36
+ SalasSidebarFooterComponent,
37
+ SalasSidebarContentComponent,
38
+ SalasSidebarGroupComponent,
39
+ SalasSidebarGroupLabelComponent,
40
+ SalasSidebarGroupContentComponent,
41
+ SalasSidebarTriggerComponent,
42
+ SalasSidebarMenuComponent,
43
+ SalasSidebarMenuItemComponent,
44
+ SalasSidebarMenuButtonComponent,
45
+ SalasSidebarInsetComponent,
46
+ ],
47
+ })
48
+ export class SalasSidebarModule {}
@@ -0,0 +1,93 @@
1
+ import { Injectable } from '@angular/core';
2
+
3
+ export type SidebarState = 'expanded' | 'collapsed';
4
+
5
+ @Injectable()
6
+ export class SidebarService {
7
+ private _open = true;
8
+ private _openMobile = false;
9
+ private _isMobile = false;
10
+ private _state: SidebarState = 'expanded';
11
+ private _collapsible: 'offcanvas' | 'icon' | 'none' = 'icon';
12
+ private _side: 'left' | 'right' = 'left';
13
+ private onOpenChangeCallback: ((open: boolean) => void) | null = null;
14
+
15
+ get open(): boolean {
16
+ return this._open;
17
+ }
18
+ set open(value: boolean) {
19
+ this._open = value;
20
+ this.onOpenChangeCallback?.(value);
21
+ }
22
+
23
+ get openMobile(): boolean {
24
+ return this._openMobile;
25
+ }
26
+ set openMobile(value: boolean) {
27
+ this._openMobile = value;
28
+ }
29
+
30
+ get isMobile(): boolean {
31
+ return this._isMobile;
32
+ }
33
+ set isMobile(value: boolean) {
34
+ this._isMobile = value;
35
+ }
36
+
37
+ get state(): SidebarState {
38
+ return this._state;
39
+ }
40
+
41
+ get collapsible(): 'offcanvas' | 'icon' | 'none' {
42
+ return this._collapsible;
43
+ }
44
+ set collapsible(value: 'offcanvas' | 'icon' | 'none') {
45
+ this._collapsible = value;
46
+ }
47
+
48
+ get side(): 'left' | 'right' {
49
+ return this._side;
50
+ }
51
+ set side(value: 'left' | 'right') {
52
+ this._side = value;
53
+ }
54
+
55
+ setOpen(value: boolean): void {
56
+ this.open = value;
57
+ }
58
+
59
+ setOpenMobile(value: boolean): void {
60
+ this._openMobile = value;
61
+ }
62
+
63
+ setState(state: SidebarState): void {
64
+ this._state = state;
65
+ }
66
+
67
+ toggleSidebar(): void {
68
+ if (this._isMobile) {
69
+ this._openMobile = !this._openMobile;
70
+ } else {
71
+ if (this._collapsible === 'icon') {
72
+ this._state = this._state === 'expanded' ? 'collapsed' : 'expanded';
73
+ } else {
74
+ this.open = !this.open;
75
+ }
76
+ }
77
+ }
78
+
79
+ setOpenChange(callback: (open: boolean) => void): void {
80
+ this.onOpenChangeCallback = callback;
81
+ }
82
+
83
+ init(defaultOpen: boolean, open?: boolean, onOpenChange?: (open: boolean) => void): void {
84
+ if (open !== undefined) {
85
+ this._open = open;
86
+ } else {
87
+ this._open = defaultOpen;
88
+ }
89
+ if (onOpenChange) {
90
+ this.onOpenChangeCallback = onOpenChange;
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,44 @@
1
+ import { Component, Input, ViewEncapsulation } from '@angular/core';
2
+ import { cn } from '../utils';
3
+
4
+ @Component({
5
+ selector: 'salas-skeleton',
6
+ standalone: true,
7
+ imports: [],
8
+ encapsulation: ViewEncapsulation.None,
9
+ host: {
10
+ '[class]': 'hostClasses',
11
+ },
12
+ template: ``,
13
+ styles: [`
14
+ @keyframes salas-skeleton-pulse {
15
+ 0%, 100% { opacity: 1; }
16
+ 50% { opacity: 0.5; }
17
+ }
18
+
19
+ .salas-skeleton {
20
+ display: block;
21
+ border-radius: 0.375rem;
22
+ background-color: var(--salas-gray-200);
23
+ animation: salas-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
24
+ }
25
+
26
+ .salas-skeleton--circle {
27
+ border-radius: 9999px;
28
+ }
29
+
30
+ [data-theme='dark'] .salas-skeleton {
31
+ background-color: var(--salas-gray-800);
32
+ }
33
+ `],
34
+ })
35
+ export class SalasSkeletonComponent {
36
+ @Input() circle = false;
37
+
38
+ get hostClasses(): string {
39
+ return cn(
40
+ 'salas-skeleton',
41
+ this.circle && 'salas-skeleton--circle',
42
+ );
43
+ }
44
+ }
@@ -0,0 +1,8 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { SalasSkeletonComponent } from './skeleton.component';
3
+
4
+ @NgModule({
5
+ imports: [SalasSkeletonComponent],
6
+ exports: [SalasSkeletonComponent],
7
+ })
8
+ export class SalasSkeletonModule {}
@@ -0,0 +1,75 @@
1
+ import { Component, Input, ViewEncapsulation } from '@angular/core';
2
+ import { cn } from '../utils';
3
+
4
+ export type SpinnerSize = 'sm' | 'default' | 'lg';
5
+
6
+ export interface SpinnerProps {
7
+ size?: SpinnerSize;
8
+ }
9
+
10
+
11
+ @Component({
12
+ selector: 'salas-spinner',
13
+ standalone: true,
14
+ imports: [],
15
+ encapsulation: ViewEncapsulation.None,
16
+ template: `
17
+ <svg
18
+ [class]="spinnerClasses"
19
+ xmlns="http://www.w3.org/2000/svg"
20
+ viewBox="0 0 24 24"
21
+ fill="none"
22
+ stroke="currentColor"
23
+ stroke-width="2"
24
+ stroke-linecap="round"
25
+ stroke-linejoin="round"
26
+ role="status"
27
+ [attr.aria-label]="ariaLabel"
28
+ >
29
+ <path d="M21 12a9 9 0 1 1-6.219-8.56" />
30
+ </svg>
31
+ `,
32
+ styles: [`
33
+ .salas-spinner {
34
+ display: inline-block;
35
+ animation: spin 1s linear infinite;
36
+ color: var(--salas-primary-500);
37
+ }
38
+
39
+ .salas-spinner--sm {
40
+ width: 1rem;
41
+ height: 1rem;
42
+ }
43
+
44
+ .salas-spinner--default {
45
+ width: 1.25rem;
46
+ height: 1.25rem;
47
+ }
48
+
49
+ .salas-spinner--lg {
50
+ width: 1.5rem;
51
+ height: 1.5rem;
52
+ }
53
+
54
+ @keyframes spin {
55
+ from {
56
+ transform: rotate(0deg);
57
+ }
58
+ to {
59
+ transform: rotate(360deg);
60
+ }
61
+ }
62
+
63
+ [data-theme='dark'] .salas-spinner {
64
+ color: var(--salas-primary-400);
65
+ }
66
+ `],
67
+ })
68
+ export class SalasSpinnerComponent implements SpinnerProps {
69
+ @Input() size: SpinnerSize = 'default';
70
+ @Input() ariaLabel = 'Loading';
71
+
72
+ get spinnerClasses(): string {
73
+ return cn('salas-spinner', `salas-spinner--${this.size}`);
74
+ }
75
+ }
@@ -0,0 +1,8 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { SalasSpinnerComponent } from './spinner.component';
3
+
4
+ @NgModule({
5
+ imports: [SalasSpinnerComponent],
6
+ exports: [SalasSpinnerComponent],
7
+ })
8
+ export class SalasSpinnerModule {}
@@ -0,0 +1,23 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-table-body',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ template: `
9
+ <tbody class="salas-table-body">
10
+ <ng-content></ng-content>
11
+ </tbody>
12
+ `,
13
+ styles: [`
14
+ salas-table-body {
15
+ display: contents;
16
+ }
17
+
18
+ .salas-table-body [data-slot="table-row"]:last-child {
19
+ border-bottom: 0;
20
+ }
21
+ `],
22
+ })
23
+ export class SalasTableBodyComponent {}
@@ -0,0 +1,29 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-table-caption',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ template: `
9
+ <caption class="salas-table-caption">
10
+ <ng-content></ng-content>
11
+ </caption>
12
+ `,
13
+ styles: [`
14
+ salas-table-caption {
15
+ display: contents;
16
+ }
17
+
18
+ .salas-table-caption {
19
+ margin-top: 1rem;
20
+ font-size: 0.875rem;
21
+ color: var(--salas-gray-500, #71717a);
22
+ }
23
+
24
+ [data-theme='dark'] .salas-table-caption {
25
+ color: var(--salas-gray-400, #a1a1aa);
26
+ }
27
+ `],
28
+ })
29
+ export class SalasTableCaptionComponent {}
@@ -0,0 +1,49 @@
1
+ import { Component, Input, ViewEncapsulation } from '@angular/core';
2
+ import { cn } from '../utils';
3
+
4
+ @Component({
5
+ selector: 'salas-table-cell',
6
+ standalone: true,
7
+ imports: [],
8
+ encapsulation: ViewEncapsulation.None,
9
+ template: `
10
+ <td [class]="cellClasses" data-slot="table-cell">
11
+ <ng-content></ng-content>
12
+ </td>
13
+ `,
14
+ styles: [`
15
+ salas-table-cell {
16
+ display: contents;
17
+ }
18
+
19
+ .salas-table-cell {
20
+ padding: 0.75rem 1rem;
21
+ vertical-align: middle;
22
+ }
23
+
24
+ .salas-table-cell--right {
25
+ text-align: right;
26
+ }
27
+
28
+ .salas-table-cell--center {
29
+ text-align: center;
30
+ }
31
+
32
+ .salas-table-cell--font-medium {
33
+ font-weight: 500;
34
+ }
35
+ `],
36
+ })
37
+ export class SalasTableCellComponent {
38
+ @Input() align: 'left' | 'right' | 'center' = 'left';
39
+ @Input() fontWeight: 'normal' | 'medium' = 'normal';
40
+
41
+ get cellClasses(): string {
42
+ return cn(
43
+ 'salas-table-cell',
44
+ this.align === 'right' && 'salas-table-cell--right',
45
+ this.align === 'center' && 'salas-table-cell--center',
46
+ this.fontWeight === 'medium' && 'salas-table-cell--font-medium',
47
+ );
48
+ }
49
+ }
@@ -0,0 +1,32 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-table-footer',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ template: `
9
+ <tfoot class="salas-table-footer">
10
+ <ng-content></ng-content>
11
+ </tfoot>
12
+ `,
13
+ styles: [`
14
+ salas-table-footer {
15
+ display: contents;
16
+ }
17
+
18
+ .salas-table-footer {
19
+ border-top: 1px solid var(--salas-gray-200, #e4e4e7);
20
+ font-weight: 500;
21
+ }
22
+
23
+ .salas-table-footer [data-slot="table-row"]:last-child {
24
+ border-bottom: 0;
25
+ }
26
+
27
+ [data-theme='dark'] .salas-table-footer {
28
+ border-top-color: var(--salas-gray-800, #27272a);
29
+ }
30
+ `],
31
+ })
32
+ export class SalasTableFooterComponent {}