@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,329 @@
1
+ import { Component, Input, OnInit, OnDestroy, ViewEncapsulation, ChangeDetectorRef } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { cn } from '../utils';
4
+ import { SalasToastService } from './toast.service';
5
+
6
+ export type ToastType = 'default' | 'success' | 'info' | 'warning' | 'error';
7
+
8
+ export type ToastPosition =
9
+ | 'top-left'
10
+ | 'top-center'
11
+ | 'top-right'
12
+ | 'bottom-left'
13
+ | 'bottom-center'
14
+ | 'bottom-right';
15
+
16
+ export interface ToastData {
17
+ id: string;
18
+ type: ToastType;
19
+ title: string;
20
+ description?: string;
21
+ duration?: number;
22
+ dismissible?: boolean;
23
+ action?: {
24
+ label: string;
25
+ onClick: () => void;
26
+ };
27
+ }
28
+
29
+ export interface ToasterProps {
30
+ position?: ToastPosition;
31
+ duration?: number;
32
+ richColors?: boolean;
33
+ }
34
+
35
+
36
+ @Component({
37
+ selector: 'salas-toaster',
38
+ standalone: true,
39
+ imports: [CommonModule],
40
+ encapsulation: ViewEncapsulation.None,
41
+ template: `
42
+ <div [class]="containerClasses">
43
+ @for (toast of toastService.toasts; track toast.id) {
44
+ <div [class]="getToastClasses(toast)" role="alert">
45
+ <div class="salas-toast-icon-wrapper">
46
+ @switch (toast.type) {
47
+ @case ('success') {
48
+ <svg class="salas-toast-icon salas-toast-icon--success" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
49
+ }
50
+ @case ('error') {
51
+ <svg class="salas-toast-icon salas-toast-icon--error" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>
52
+ }
53
+ @case ('warning') {
54
+ <svg class="salas-toast-icon salas-toast-icon--warning" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
55
+ }
56
+ @case ('info') {
57
+ <svg class="salas-toast-icon salas-toast-icon--info" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
58
+ }
59
+ }
60
+ </div>
61
+ <div class="salas-toast-content">
62
+ <div class="salas-toast-title">{{ toast.title }}</div>
63
+ @if (toast.description) {
64
+ <div class="salas-toast-description">{{ toast.description }}</div>
65
+ }
66
+ </div>
67
+ <div class="salas-toast-actions">
68
+ @if (toast.action) {
69
+ <button class="salas-toast-action-btn" (click)="toast.action!.onClick(); dismiss(toast.id)">
70
+ {{ toast.action!.label }}
71
+ </button>
72
+ }
73
+ @if (toast.dismissible) {
74
+ <button class="salas-toast-close" (click)="dismiss(toast.id)" aria-label="Close">
75
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
76
+ </button>
77
+ }
78
+ </div>
79
+ </div>
80
+ }
81
+ </div>
82
+ `,
83
+ styles: [`
84
+ .salas-toaster {
85
+ position: fixed;
86
+ z-index: 99999;
87
+ display: flex;
88
+ flex-direction: column;
89
+ gap: 0.5rem;
90
+ pointer-events: none;
91
+ padding: 1rem;
92
+ max-width: 420px;
93
+ width: 100%;
94
+ }
95
+
96
+ .salas-toaster--top-left { top: 0; left: 0; }
97
+ .salas-toaster--top-center { top: 0; left: 50%; transform: translateX(-50%); }
98
+ .salas-toaster--top-right { top: 0; right: 0; }
99
+ .salas-toaster--bottom-left { bottom: 0; left: 0; flex-direction: column-reverse; }
100
+ .salas-toaster--bottom-center { bottom: 0; left: 50%; transform: translateX(-50%); flex-direction: column-reverse; }
101
+ .salas-toaster--bottom-right { bottom: 0; right: 0; flex-direction: column-reverse; }
102
+
103
+ .salas-toast {
104
+ pointer-events: all;
105
+ display: flex;
106
+ align-items: flex-start;
107
+ gap: 0.75rem;
108
+ width: 100%;
109
+ padding: 1rem;
110
+ border-radius: 0.5rem;
111
+ border: 1px solid var(--salas-gray-200, #e4e4e7);
112
+ background-color: white;
113
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);
114
+ animation: salas-toast-slide-in 0.3s cubic-bezier(0.21, 1.02, 0.73, 1);
115
+ font-size: 0.875rem;
116
+ line-height: 1.4;
117
+ }
118
+
119
+ @keyframes salas-toast-slide-in {
120
+ from {
121
+ opacity: 0;
122
+ transform: translateY(0.75rem);
123
+ }
124
+ to {
125
+ opacity: 1;
126
+ transform: translateY(0);
127
+ }
128
+ }
129
+
130
+ .salas-toaster--top-left .salas-toast,
131
+ .salas-toaster--top-center .salas-toast,
132
+ .salas-toaster--top-right .salas-toast {
133
+ animation-name: salas-toast-slide-in-top;
134
+ }
135
+
136
+ @keyframes salas-toast-slide-in-top {
137
+ from {
138
+ opacity: 0;
139
+ transform: translateY(-0.75rem);
140
+ }
141
+ to {
142
+ opacity: 1;
143
+ transform: translateY(0);
144
+ }
145
+ }
146
+
147
+ /* Rich color variants */
148
+ .salas-toast--success {
149
+ border-color: var(--salas-success-200, #bbf7d0);
150
+ background-color: var(--salas-success-50, #f0fdf4);
151
+ }
152
+
153
+ .salas-toast--error {
154
+ border-color: var(--salas-destructive-200, #fecaca);
155
+ background-color: var(--salas-destructive-50, #fef2f2);
156
+ }
157
+
158
+ .salas-toast--warning {
159
+ border-color: #fde68a;
160
+ background-color: #fffbeb;
161
+ }
162
+
163
+ .salas-toast--info {
164
+ border-color: #bfdbfe;
165
+ background-color: #eff6ff;
166
+ }
167
+
168
+ .salas-toast-icon-wrapper {
169
+ flex-shrink: 0;
170
+ display: flex;
171
+ align-items: center;
172
+ padding-top: 0.0625rem;
173
+ }
174
+
175
+ .salas-toast-icon--success { color: var(--salas-success-500, #22c55e); }
176
+ .salas-toast-icon--error { color: var(--salas-destructive-500, #ef4444); }
177
+ .salas-toast-icon--warning { color: #f59e0b; }
178
+ .salas-toast-icon--info { color: #3b82f6; }
179
+
180
+ .salas-toast-content {
181
+ flex: 1;
182
+ min-width: 0;
183
+ }
184
+
185
+ .salas-toast-title {
186
+ font-weight: 500;
187
+ color: var(--salas-gray-900, #18181b);
188
+ }
189
+
190
+ .salas-toast-description {
191
+ margin-top: 0.25rem;
192
+ font-size: 0.8125rem;
193
+ color: var(--salas-gray-500, #71717a);
194
+ }
195
+
196
+ .salas-toast-actions {
197
+ display: flex;
198
+ align-items: center;
199
+ gap: 0.5rem;
200
+ flex-shrink: 0;
201
+ }
202
+
203
+ .salas-toast-action-btn {
204
+ padding: 0.25rem 0.75rem;
205
+ font-size: 0.75rem;
206
+ font-weight: 500;
207
+ border-radius: 0.25rem;
208
+ border: 1px solid var(--salas-gray-200, #e4e4e7);
209
+ background-color: white;
210
+ cursor: pointer;
211
+ white-space: nowrap;
212
+ transition: background-color 0.15s;
213
+ }
214
+
215
+ .salas-toast-action-btn:hover {
216
+ background-color: var(--salas-gray-100, #f4f4f5);
217
+ }
218
+
219
+ .salas-toast-close {
220
+ display: flex;
221
+ align-items: center;
222
+ justify-content: center;
223
+ width: 1.25rem;
224
+ height: 1.25rem;
225
+ border: none;
226
+ background: none;
227
+ cursor: pointer;
228
+ color: var(--salas-gray-400, #a1a1aa);
229
+ border-radius: 0.25rem;
230
+ padding: 0;
231
+ transition: color 0.15s, background-color 0.15s;
232
+ }
233
+
234
+ .salas-toast-close:hover {
235
+ color: var(--salas-gray-600, #52525b);
236
+ background-color: var(--salas-gray-100, #f4f4f5);
237
+ }
238
+
239
+ /* Dark theme */
240
+ [data-theme='dark'] .salas-toast {
241
+ background-color: var(--salas-gray-900, #18181b);
242
+ border-color: var(--salas-gray-800, #27272a);
243
+ }
244
+
245
+ [data-theme='dark'] .salas-toast-title {
246
+ color: var(--salas-gray-100, #f4f4f5);
247
+ }
248
+
249
+ [data-theme='dark'] .salas-toast-description {
250
+ color: var(--salas-gray-400, #a1a1aa);
251
+ }
252
+
253
+ [data-theme='dark'] .salas-toast-action-btn {
254
+ background-color: var(--salas-gray-800, #27272a);
255
+ border-color: var(--salas-gray-700, #3f3f46);
256
+ color: var(--salas-gray-100, #f4f4f5);
257
+ }
258
+
259
+ [data-theme='dark'] .salas-toast-action-btn:hover {
260
+ background-color: var(--salas-gray-700, #3f3f46);
261
+ }
262
+
263
+ [data-theme='dark'] .salas-toast-close {
264
+ color: var(--salas-gray-500, #71717a);
265
+ }
266
+
267
+ [data-theme='dark'] .salas-toast-close:hover {
268
+ color: var(--salas-gray-300, #d4d4d8);
269
+ background-color: var(--salas-gray-800, #27272a);
270
+ }
271
+
272
+ [data-theme='dark'] .salas-toast--success {
273
+ background-color: rgba(34, 197, 94, 0.1);
274
+ border-color: rgba(34, 197, 94, 0.2);
275
+ }
276
+
277
+ [data-theme='dark'] .salas-toast--error {
278
+ background-color: rgba(239, 68, 68, 0.1);
279
+ border-color: rgba(239, 68, 68, 0.2);
280
+ }
281
+
282
+ [data-theme='dark'] .salas-toast--warning {
283
+ background-color: rgba(245, 158, 11, 0.1);
284
+ border-color: rgba(245, 158, 11, 0.2);
285
+ }
286
+
287
+ [data-theme='dark'] .salas-toast--info {
288
+ background-color: rgba(59, 130, 246, 0.1);
289
+ border-color: rgba(59, 130, 246, 0.2);
290
+ }
291
+ `],
292
+ })
293
+ export class SalasToasterComponent implements OnInit, OnDestroy {
294
+ @Input() position: ToastPosition = 'bottom-right';
295
+ @Input() richColors = true;
296
+
297
+ private unsubscribe?: () => void;
298
+
299
+ constructor(
300
+ public toastService: SalasToastService,
301
+ private cdr: ChangeDetectorRef,
302
+ ) {}
303
+
304
+ ngOnInit(): void {
305
+ this.toastService.position = this.position;
306
+ this.unsubscribe = this.toastService.onChange(() => {
307
+ this.cdr.detectChanges();
308
+ });
309
+ }
310
+
311
+ ngOnDestroy(): void {
312
+ this.unsubscribe?.();
313
+ }
314
+
315
+ get containerClasses(): string {
316
+ return cn('salas-toaster', `salas-toaster--${this.position}`);
317
+ }
318
+
319
+ getToastClasses(toast: ToastData): string {
320
+ return cn(
321
+ 'salas-toast',
322
+ this.richColors && toast.type !== 'default' && `salas-toast--${toast.type}`,
323
+ );
324
+ }
325
+
326
+ dismiss(id: string): void {
327
+ this.toastService.dismiss(id);
328
+ }
329
+ }
@@ -0,0 +1,43 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+ import { cn } from '../utils';
3
+
4
+ export type TooltipSide = 'top' | 'bottom' | 'left' | 'right';
5
+ export type TooltipAlign = 'start' | 'center' | 'end';
6
+
7
+ export interface TooltipProps {
8
+ content?: string;
9
+ side?: TooltipSide;
10
+ align?: TooltipAlign;
11
+ delayDuration?: number;
12
+ disabled?: boolean;
13
+ }
14
+
15
+
16
+ @Component({
17
+ selector: 'salas-tooltip-content',
18
+ standalone: true,
19
+ imports: [],
20
+ encapsulation: ViewEncapsulation.None,
21
+ template: `
22
+ @if (isVisible) {
23
+ <div [class]="popupClasses">
24
+ <ng-content></ng-content>
25
+ <span class="salas-tooltip-arrow"></span>
26
+ </div>
27
+ }
28
+ `,
29
+ })
30
+ export class SalasTooltipContentComponent {
31
+ isVisible = false;
32
+ side: TooltipSide = 'top';
33
+ align: TooltipAlign = 'center';
34
+
35
+ get popupClasses(): string {
36
+ return cn(
37
+ 'salas-tooltip-popup',
38
+ this.isVisible && 'salas-tooltip-popup--visible',
39
+ `salas-tooltip-popup--${this.side}`,
40
+ `salas-tooltip-popup--align-${this.align}`,
41
+ );
42
+ }
43
+ }
@@ -0,0 +1,13 @@
1
+ import { Component, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-tooltip-trigger',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ host: {
9
+ style: 'display: inline-block;',
10
+ },
11
+ template: `<ng-content></ng-content>`,
12
+ })
13
+ export class SalasTooltipTriggerComponent {}
@@ -0,0 +1,243 @@
1
+ import {
2
+ Component,
3
+ Input,
4
+ ViewEncapsulation,
5
+ ElementRef,
6
+ OnDestroy,
7
+ AfterContentInit,
8
+ ContentChild,
9
+ ChangeDetectorRef,
10
+ } from '@angular/core';
11
+ import { cn } from '../utils';
12
+ import { SalasTooltipTriggerComponent } from './tooltip-trigger.component';
13
+ import { SalasTooltipContentComponent } from './tooltip-content.component';
14
+
15
+ export type TooltipSide = 'top' | 'bottom' | 'left' | 'right';
16
+ export type TooltipAlign = 'start' | 'center' | 'end';
17
+
18
+ export interface TooltipProps {
19
+ content?: string;
20
+ side?: TooltipSide;
21
+ align?: TooltipAlign;
22
+ delayDuration?: number;
23
+ disabled?: boolean;
24
+ }
25
+
26
+
27
+ @Component({
28
+ selector: 'salas-tooltip',
29
+ standalone: true,
30
+ imports: [],
31
+ encapsulation: ViewEncapsulation.None,
32
+ host: {
33
+ style: 'display: inline-block; position: relative;',
34
+ '(mouseenter)': 'onMouseEnter()',
35
+ '(mouseleave)': 'onMouseLeave()',
36
+ '(focusin)': 'onFocusIn()',
37
+ '(focusout)': 'onFocusOut()',
38
+ },
39
+ template: `
40
+ <ng-content></ng-content>
41
+ `,
42
+ styles: [`
43
+ .salas-tooltip-popup {
44
+ position: absolute;
45
+ z-index: 9999;
46
+ padding: 0.375rem 0.75rem;
47
+ font-size: 0.75rem;
48
+ line-height: 1rem;
49
+ font-weight: 500;
50
+ color: var(--salas-gray-50, #fafafa);
51
+ background-color: var(--salas-gray-900, #18181b);
52
+ border-radius: 0.375rem;
53
+ pointer-events: none;
54
+ white-space: nowrap;
55
+ opacity: 0;
56
+ transform: scale(0.95);
57
+ transition: opacity 0.15s ease, transform 0.15s ease;
58
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
59
+ }
60
+
61
+ .salas-tooltip-popup--visible {
62
+ opacity: 1;
63
+ transform: scale(1);
64
+ }
65
+
66
+ .salas-tooltip-popup--top {
67
+ bottom: 100%;
68
+ margin-bottom: 0.5rem;
69
+ }
70
+
71
+ .salas-tooltip-popup--bottom {
72
+ top: 100%;
73
+ margin-top: 0.5rem;
74
+ }
75
+
76
+ .salas-tooltip-popup--left {
77
+ right: 100%;
78
+ top: 50%;
79
+ transform: translateY(-50%) scale(0.95);
80
+ margin-right: 0.5rem;
81
+ }
82
+
83
+ .salas-tooltip-popup--left.salas-tooltip-popup--visible {
84
+ transform: translateY(-50%) scale(1);
85
+ }
86
+
87
+ .salas-tooltip-popup--right {
88
+ left: 100%;
89
+ top: 50%;
90
+ transform: translateY(-50%) scale(0.95);
91
+ margin-left: 0.5rem;
92
+ }
93
+
94
+ .salas-tooltip-popup--right.salas-tooltip-popup--visible {
95
+ transform: translateY(-50%) scale(1);
96
+ }
97
+
98
+ .salas-tooltip-popup--align-start {
99
+ left: 0;
100
+ }
101
+
102
+ .salas-tooltip-popup--align-center:not(.salas-tooltip-popup--left):not(.salas-tooltip-popup--right) {
103
+ left: 50%;
104
+ transform: translateX(-50%) scale(0.95);
105
+ }
106
+
107
+ .salas-tooltip-popup--align-center.salas-tooltip-popup--visible:not(.salas-tooltip-popup--left):not(.salas-tooltip-popup--right) {
108
+ transform: translateX(-50%) scale(1);
109
+ }
110
+
111
+ .salas-tooltip-popup--align-end {
112
+ right: 0;
113
+ }
114
+
115
+ /* Arrow */
116
+ .salas-tooltip-arrow {
117
+ position: absolute;
118
+ width: 0.5rem;
119
+ height: 0.25rem;
120
+ overflow: hidden;
121
+ }
122
+
123
+ .salas-tooltip-arrow::after {
124
+ content: '';
125
+ position: absolute;
126
+ width: 0.375rem;
127
+ height: 0.375rem;
128
+ background-color: var(--salas-gray-900, #18181b);
129
+ transform: rotate(45deg);
130
+ }
131
+
132
+ .salas-tooltip-popup--top .salas-tooltip-arrow {
133
+ bottom: -0.25rem;
134
+ left: 50%;
135
+ transform: translateX(-50%);
136
+ }
137
+
138
+ .salas-tooltip-popup--top .salas-tooltip-arrow::after {
139
+ top: -0.125rem;
140
+ left: 50%;
141
+ transform: translateX(-50%) rotate(45deg);
142
+ }
143
+
144
+ .salas-tooltip-popup--bottom .salas-tooltip-arrow {
145
+ top: -0.25rem;
146
+ left: 50%;
147
+ transform: translateX(-50%);
148
+ }
149
+
150
+ .salas-tooltip-popup--bottom .salas-tooltip-arrow::after {
151
+ bottom: -0.125rem;
152
+ left: 50%;
153
+ transform: translateX(-50%) rotate(45deg);
154
+ }
155
+
156
+ /* Dark theme */
157
+ [data-theme='dark'] .salas-tooltip-popup {
158
+ background-color: var(--salas-gray-100, #f4f4f5);
159
+ color: var(--salas-gray-900, #18181b);
160
+ }
161
+
162
+ [data-theme='dark'] .salas-tooltip-arrow::after {
163
+ background-color: var(--salas-gray-100, #f4f4f5);
164
+ }
165
+ `],
166
+ })
167
+ export class SalasTooltipComponent implements AfterContentInit, OnDestroy {
168
+ @Input() side: TooltipSide = 'top';
169
+ @Input() align: TooltipAlign = 'center';
170
+ @Input() delayDuration = 200;
171
+ @Input() disabled = false;
172
+
173
+ @ContentChild(SalasTooltipContentComponent) contentChild?: SalasTooltipContentComponent;
174
+
175
+ isVisible = false;
176
+ private showTimeout: ReturnType<typeof setTimeout> | null = null;
177
+ private hideTimeout: ReturnType<typeof setTimeout> | null = null;
178
+
179
+ constructor(private cdr: ChangeDetectorRef) {}
180
+
181
+ ngAfterContentInit(): void {
182
+ this.updateContent();
183
+ }
184
+
185
+ ngOnDestroy(): void {
186
+ this.clearTimeouts();
187
+ }
188
+
189
+ onMouseEnter(): void {
190
+ if (this.disabled) return;
191
+ this.clearTimeouts();
192
+ this.showTimeout = setTimeout(() => {
193
+ this.show();
194
+ }, this.delayDuration);
195
+ }
196
+
197
+ onMouseLeave(): void {
198
+ this.clearTimeouts();
199
+ this.hideTimeout = setTimeout(() => {
200
+ this.hide();
201
+ }, 100);
202
+ }
203
+
204
+ onFocusIn(): void {
205
+ if (this.disabled) return;
206
+ this.show();
207
+ }
208
+
209
+ onFocusOut(): void {
210
+ this.hide();
211
+ }
212
+
213
+ private show(): void {
214
+ this.isVisible = true;
215
+ this.updateContent();
216
+ this.cdr.detectChanges();
217
+ }
218
+
219
+ private hide(): void {
220
+ this.isVisible = false;
221
+ this.updateContent();
222
+ this.cdr.detectChanges();
223
+ }
224
+
225
+ private updateContent(): void {
226
+ if (this.contentChild) {
227
+ this.contentChild.isVisible = this.isVisible;
228
+ this.contentChild.side = this.side;
229
+ this.contentChild.align = this.align;
230
+ }
231
+ }
232
+
233
+ private clearTimeouts(): void {
234
+ if (this.showTimeout) {
235
+ clearTimeout(this.showTimeout);
236
+ this.showTimeout = null;
237
+ }
238
+ if (this.hideTimeout) {
239
+ clearTimeout(this.hideTimeout);
240
+ this.hideTimeout = null;
241
+ }
242
+ }
243
+ }
@@ -0,0 +1,10 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { SalasTooltipComponent } from './tooltip.component';
3
+ import { SalasTooltipTriggerComponent } from './tooltip-trigger.component';
4
+ import { SalasTooltipContentComponent } from './tooltip-content.component';
5
+
6
+ @NgModule({
7
+ imports: [SalasTooltipComponent, SalasTooltipTriggerComponent, SalasTooltipContentComponent],
8
+ exports: [SalasTooltipComponent, SalasTooltipTriggerComponent, SalasTooltipContentComponent],
9
+ })
10
+ export class SalasTooltipModule {}