@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,292 @@
1
+ import { Component, Input, Output, EventEmitter, forwardRef, OnInit, OnChanges, SimpleChanges } from '@angular/core';
2
+ import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
3
+ import { cn } from '../utils';
4
+ import { LucideAngularModule } from 'lucide-angular';
5
+ import { CommonModule } from '@angular/common';
6
+
7
+ /**
8
+ * Select component types
9
+ */
10
+
11
+ export type SelectSize = 'sm' | 'md' | 'lg';
12
+ export type SelectVariant = 'default' | 'error' | 'success';
13
+
14
+ export interface SelectOption<T = string> {
15
+ label: string;
16
+ value: T;
17
+ disabled?: boolean;
18
+ group?: string;
19
+ }
20
+
21
+ export interface SelectProps<T = string> {
22
+ size?: SelectSize;
23
+ variant?: SelectVariant;
24
+ disabled?: boolean;
25
+ required?: boolean;
26
+ placeholder?: string;
27
+ options: SelectOption<T>[];
28
+ value?: T;
29
+ name?: string;
30
+ id?: string;
31
+ multiple?: boolean;
32
+ }
33
+
34
+
35
+ interface GroupedOption<T> {
36
+ name: string | null;
37
+ options: SelectOption<T>[];
38
+ }
39
+
40
+ @Component({
41
+ selector: 'salas-select',
42
+ standalone: true,
43
+ imports: [FormsModule, CommonModule, LucideAngularModule],
44
+ template: `
45
+ <div [class]="wrapperClasses">
46
+ <select
47
+ [id]="id"
48
+ [name]="name"
49
+ [disabled]="disabled"
50
+ [required]="required"
51
+ [multiple]="multiple"
52
+ [class]="selectClasses"
53
+ [value]="value"
54
+ (change)="onChange($event)"
55
+ (blur)="onBlur()"
56
+ (focus)="onFocus()"
57
+ [attr.aria-invalid]="variant === 'error'"
58
+ >
59
+ @if (placeholder) {
60
+ <option value="" [disabled]="required">{{ placeholder }}</option>
61
+ }
62
+ @for (group of groupedOptions; track group.name) {
63
+ @if (group.name) {
64
+ <optgroup [label]="group.name">
65
+ @for (option of group.options; track option.value) {
66
+ <option [value]="option.value" [disabled]="option.disabled">
67
+ {{ option.label }}
68
+ </option>
69
+ }
70
+ </optgroup>
71
+ } @else {
72
+ @for (option of group.options; track option.value) {
73
+ <option [value]="option.value" [disabled]="option.disabled">
74
+ {{ option.label }}
75
+ </option>
76
+ }
77
+ }
78
+ }
79
+ </select>
80
+ @if (!multiple) {
81
+ <lucide-icon name="chevron-down" [size]="iconSize" class="salas-select-icon" />
82
+ }
83
+ </div>
84
+ `,
85
+ styles: [`
86
+ .salas-select-wrapper {
87
+ position: relative;
88
+ display: inline-block;
89
+ width: 100%;
90
+ }
91
+
92
+ .salas-select {
93
+ width: 100%;
94
+ border-radius: 0.375rem;
95
+ border: 1px solid var(--salas-gray-300);
96
+ background-color: white;
97
+ padding: 0 0.75rem;
98
+ font-size: 0.875rem;
99
+ transition: all 0.2s;
100
+ outline: none;
101
+ appearance: none;
102
+ cursor: pointer;
103
+ }
104
+
105
+ .salas-select:focus {
106
+ border-color: var(--salas-primary-500);
107
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
108
+ }
109
+
110
+ .salas-select:disabled {
111
+ background-color: var(--salas-gray-50);
112
+ cursor: not-allowed;
113
+ opacity: 0.6;
114
+ }
115
+
116
+ .salas-select option {
117
+ padding: 0.5rem;
118
+ }
119
+
120
+ /* Variants */
121
+ .salas-select--error {
122
+ border-color: var(--salas-destructive-500);
123
+ }
124
+
125
+ .salas-select--error:focus {
126
+ border-color: var(--salas-destructive-500);
127
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
128
+ }
129
+
130
+ .salas-select--success {
131
+ border-color: var(--salas-success-500);
132
+ }
133
+
134
+ .salas-select--success:focus {
135
+ border-color: var(--salas-success-500);
136
+ box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.1);
137
+ }
138
+
139
+ /* Sizes */
140
+ .salas-select--sm {
141
+ height: 2rem;
142
+ padding: 0 0.625rem;
143
+ font-size: 0.8125rem;
144
+ }
145
+
146
+ .salas-select--md {
147
+ height: 2.5rem;
148
+ }
149
+
150
+ .salas-select--lg {
151
+ height: 3rem;
152
+ padding: 0 1rem;
153
+ font-size: 1rem;
154
+ }
155
+
156
+ .salas-select-icon {
157
+ position: absolute;
158
+ right: 0.75rem;
159
+ top: 50%;
160
+ transform: translateY(-50%);
161
+ pointer-events: none;
162
+ color: var(--salas-gray-500);
163
+ }
164
+ `],
165
+ providers: [
166
+ {
167
+ provide: NG_VALUE_ACCESSOR,
168
+ useExisting: forwardRef(() => SalasSelectComponent),
169
+ multi: true,
170
+ },
171
+ ],
172
+ })
173
+ export class SalasSelectComponent<T = string> implements ControlValueAccessor, OnInit, OnChanges, SelectProps<T> {
174
+ @Input() size: SelectSize = 'md';
175
+ @Input() variant: SelectVariant = 'default';
176
+ @Input() disabled = false;
177
+ @Input() required = false;
178
+ @Input() placeholder = '';
179
+ @Input() options: SelectOption<T>[] = [];
180
+ @Input() value?: T;
181
+ @Input() name = '';
182
+ @Input() id = '';
183
+ @Input() multiple = false;
184
+ @Output() valueChange = new EventEmitter<T | T[]>();
185
+
186
+ private onValueChange = (value: T | T[]) => {};
187
+ private onTouched = () => {};
188
+ groupedOptions: GroupedOption<T>[] = [];
189
+
190
+ ngOnInit(): void {
191
+ this.processGroups();
192
+ }
193
+
194
+ ngOnChanges(changes: SimpleChanges): void {
195
+ if (changes['options']) {
196
+ this.processGroups();
197
+ }
198
+ }
199
+
200
+ get wrapperClasses(): string {
201
+ return cn('salas-select-wrapper');
202
+ }
203
+
204
+ get selectClasses(): string {
205
+ return cn(
206
+ 'salas-select',
207
+ `salas-select--${this.size}`,
208
+ this.variant !== 'default' && `salas-select--${this.variant}`
209
+ );
210
+ }
211
+
212
+ get iconSize(): number {
213
+ return this.size === 'sm' ? 16 : this.size === 'lg' ? 20 : 18;
214
+ }
215
+
216
+ processGroups(): void {
217
+ const groupsMap = new Map<string | null, SelectOption<T>[]>();
218
+
219
+ // Separar opções com e sem grupo
220
+ this.options.forEach(option => {
221
+ const groupName = option.group || null;
222
+ if (!groupsMap.has(groupName)) {
223
+ groupsMap.set(groupName, []);
224
+ }
225
+ groupsMap.get(groupName)!.push(option);
226
+ });
227
+
228
+ // Converter para array ordenado: primeiro grupos, depois opções sem grupo
229
+ this.groupedOptions = [];
230
+
231
+ // Adicionar grupos (ordenados alfabeticamente)
232
+ const groups = Array.from(groupsMap.keys())
233
+ .filter(name => name !== null)
234
+ .sort() as string[];
235
+
236
+ groups.forEach(groupName => {
237
+ this.groupedOptions.push({
238
+ name: groupName,
239
+ options: groupsMap.get(groupName)!
240
+ });
241
+ });
242
+
243
+ // Adicionar opções sem grupo no final
244
+ if (groupsMap.has(null)) {
245
+ this.groupedOptions.push({
246
+ name: null,
247
+ options: groupsMap.get(null)!
248
+ });
249
+ }
250
+ }
251
+
252
+ onChange(event: Event): void {
253
+ const target = event.target as HTMLSelectElement;
254
+ if (this.multiple) {
255
+ const selectedValues = Array.from(target.selectedOptions).map(
256
+ option => option.value as T
257
+ );
258
+ this.value = selectedValues as any;
259
+ this.onChange(selectedValues as any);
260
+ this.valueChange.emit(selectedValues as any);
261
+ } else {
262
+ this.value = target.value as T;
263
+ this.onValueChange(this.value);
264
+ this.valueChange.emit(this.value);
265
+ }
266
+ }
267
+
268
+ onBlur(): void {
269
+ this.onTouched();
270
+ }
271
+
272
+ onFocus(): void {
273
+ // Can be extended for focus handling
274
+ }
275
+
276
+ // ControlValueAccessor implementation
277
+ writeValue(value: T | T[]): void {
278
+ this.value = value as T;
279
+ }
280
+
281
+ registerOnChange(fn: (value: T | T[]) => void): void {
282
+ this.onValueChange = fn;
283
+ }
284
+
285
+ registerOnTouched(fn: () => void): void {
286
+ this.onTouched = fn;
287
+ }
288
+
289
+ setDisabledState(isDisabled: boolean): void {
290
+ this.disabled = isDisabled;
291
+ }
292
+ }
@@ -0,0 +1,8 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { SalasSelectComponent } from './select.component';
3
+
4
+ @NgModule({
5
+ imports: [SalasSelectComponent],
6
+ exports: [SalasSelectComponent],
7
+ })
8
+ export class SalasSelectModule {}
@@ -0,0 +1,63 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import { cn } from '../utils';
3
+
4
+ export type SeparatorOrientation = 'horizontal' | 'vertical';
5
+
6
+ export interface SeparatorProps {
7
+ orientation?: SeparatorOrientation;
8
+ /**
9
+ * When true, hides the separator from assistive technologies.
10
+ */
11
+ decorative?: boolean;
12
+ }
13
+
14
+
15
+ @Component({
16
+ selector: 'salas-separator',
17
+ standalone: true,
18
+ imports: [],
19
+ template: `
20
+ <div
21
+ [class]="separatorClasses"
22
+ [attr.role]="decorative ? 'none' : 'separator'"
23
+ [attr.aria-orientation]="orientation === 'vertical' ? 'vertical' : 'horizontal'"
24
+ [attr.aria-hidden]="decorative ? 'true' : null"
25
+ ></div>
26
+ `,
27
+ styles: [`
28
+ :host {
29
+ display: block;
30
+ }
31
+
32
+ .salas-separator {
33
+ box-sizing: border-box;
34
+ background-color: var(--salas-gray-200);
35
+ border-radius: 9999px;
36
+ width: 100%;
37
+ height: 1px;
38
+ }
39
+
40
+ .salas-separator--vertical {
41
+ width: 1px;
42
+ height: 100%;
43
+ align-self: stretch;
44
+ }
45
+
46
+ /* Dark mode: slightly lighter line for contrast */
47
+ :host-context([data-theme="dark"]) .salas-separator {
48
+ background-color: var(--salas-gray-200);
49
+ }
50
+ `],
51
+ })
52
+ export class SalasSeparatorComponent implements SeparatorProps {
53
+ @Input() orientation: SeparatorOrientation = 'horizontal';
54
+ @Input() decorative = false;
55
+
56
+ get separatorClasses(): string {
57
+ return cn(
58
+ 'salas-separator',
59
+ this.orientation === 'vertical' && 'salas-separator--vertical',
60
+ );
61
+ }
62
+ }
63
+
@@ -0,0 +1,9 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { SalasSeparatorComponent } from './separator.component';
3
+
4
+ @NgModule({
5
+ imports: [SalasSeparatorComponent],
6
+ exports: [SalasSeparatorComponent],
7
+ })
8
+ export class SalasSeparatorModule {}
9
+
@@ -0,0 +1,13 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sheet-content',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ host: {
9
+ style: 'display: flex; flex-direction: column; flex: 1; padding: 1.5rem;',
10
+ },
11
+ template: `<ng-content></ng-content>`,
12
+ })
13
+ export class SalasSheetContentComponent {}
@@ -0,0 +1,29 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sheet-description',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ template: `
9
+ <p class="salas-sheet-description">
10
+ <ng-content></ng-content>
11
+ </p>
12
+ `,
13
+ styles: [`
14
+ salas-sheet-description {
15
+ display: block;
16
+ }
17
+
18
+ .salas-sheet-description {
19
+ margin: 0;
20
+ font-size: 0.875rem;
21
+ color: var(--salas-gray-500, #71717a);
22
+ }
23
+
24
+ [data-theme='dark'] .salas-sheet-description {
25
+ color: var(--salas-gray-400, #a1a1aa);
26
+ }
27
+ `],
28
+ })
29
+ export class SalasSheetDescriptionComponent {}
@@ -0,0 +1,27 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sheet-footer',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ template: `
9
+ <div class="salas-sheet-footer">
10
+ <ng-content></ng-content>
11
+ </div>
12
+ `,
13
+ styles: [`
14
+ salas-sheet-footer {
15
+ display: block;
16
+ margin-top: auto;
17
+ }
18
+
19
+ .salas-sheet-footer {
20
+ display: flex;
21
+ justify-content: flex-end;
22
+ gap: 0.5rem;
23
+ padding-top: 1rem;
24
+ }
25
+ `],
26
+ })
27
+ export class SalasSheetFooterComponent {}
@@ -0,0 +1,26 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sheet-header',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ template: `
9
+ <div class="salas-sheet-header">
10
+ <ng-content></ng-content>
11
+ </div>
12
+ `,
13
+ styles: [`
14
+ salas-sheet-header {
15
+ display: block;
16
+ }
17
+
18
+ .salas-sheet-header {
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: 0.375rem;
22
+ margin-bottom: 1.5rem;
23
+ }
24
+ `],
25
+ })
26
+ export class SalasSheetHeaderComponent {}
@@ -0,0 +1,31 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sheet-title',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ template: `
9
+ <h3 class="salas-sheet-title">
10
+ <ng-content></ng-content>
11
+ </h3>
12
+ `,
13
+ styles: [`
14
+ salas-sheet-title {
15
+ display: block;
16
+ }
17
+
18
+ .salas-sheet-title {
19
+ margin: 0;
20
+ font-size: 1.125rem;
21
+ font-weight: 600;
22
+ line-height: 1;
23
+ color: var(--salas-text, #111827);
24
+ }
25
+
26
+ [data-theme='dark'] .salas-sheet-title {
27
+ color: var(--salas-gray-100, #f4f4f5);
28
+ }
29
+ `],
30
+ })
31
+ export class SalasSheetTitleComponent {}
@@ -0,0 +1,11 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-sheet-trigger',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ host: { style: 'display: inline-block;' },
9
+ template: `<ng-content></ng-content>`,
10
+ })
11
+ export class SalasSheetTriggerComponent {}