@ng-cn/core 1.0.17 → 1.0.18

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 (81) hide show
  1. package/package.json +6 -5
  2. package/src/app/lib/components/ui/alert-dialog/alert-dialog-content.component.ts +21 -20
  3. package/src/app/lib/components/ui/avatar/ui-avatar.component.ts +4 -0
  4. package/src/app/lib/components/ui/calendar/calendar.component.ts +5 -1
  5. package/src/app/lib/components/ui/carousel/carousel-content.component.ts +1 -0
  6. package/src/app/lib/components/ui/carousel/carousel-item.component.ts +1 -0
  7. package/src/app/lib/components/ui/carousel/carousel-next.component.ts +1 -0
  8. package/src/app/lib/components/ui/carousel/carousel-previous.component.ts +1 -0
  9. package/src/app/lib/components/ui/carousel/carousel.component.ts +1 -0
  10. package/src/app/lib/components/ui/chart/chart-container.component.ts +1 -0
  11. package/src/app/lib/components/ui/chart/chart-legend-content.component.ts +1 -0
  12. package/src/app/lib/components/ui/chart/chart-legend.component.ts +5 -5
  13. package/src/app/lib/components/ui/chart/chart-tooltip-content.component.ts +5 -5
  14. package/src/app/lib/components/ui/chart/chart-tooltip.component.ts +5 -5
  15. package/src/app/lib/components/ui/chart/chart.component.ts +1 -0
  16. package/src/app/lib/components/ui/checkbox/checkbox.component.ts +1 -1
  17. package/src/app/lib/components/ui/collapsible/collapsible-content.component.ts +2 -1
  18. package/src/app/lib/components/ui/collapsible/collapsible-context.ts +1 -0
  19. package/src/app/lib/components/ui/collapsible/collapsible-trigger.component.ts +1 -0
  20. package/src/app/lib/components/ui/collapsible/collapsible.component.ts +3 -0
  21. package/src/app/lib/components/ui/context-menu/context-menu-content.component.ts +48 -17
  22. package/src/app/lib/components/ui/context-menu/context-menu-sub-content.component.ts +2 -0
  23. package/src/app/lib/components/ui/context-menu/context-menu-sub-trigger.component.ts +30 -1
  24. package/src/app/lib/components/ui/context-menu/context-menu-sub.component.ts +3 -0
  25. package/src/app/lib/components/ui/date-picker/date-picker.component.ts +1 -0
  26. package/src/app/lib/components/ui/dialog/dialog-content.component.ts +26 -19
  27. package/src/app/lib/components/ui/direction/direction-context.ts +9 -0
  28. package/src/app/lib/components/ui/direction/direction.component.ts +48 -0
  29. package/src/app/lib/components/ui/direction/index.ts +2 -0
  30. package/src/app/lib/components/ui/drawer/drawer-content.component.ts +44 -0
  31. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-content.component.ts +2 -2
  32. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.component.ts +1 -0
  33. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.component.ts +2 -0
  34. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.component.ts +28 -2
  35. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-sub.component.ts +3 -0
  36. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-trigger.component.ts +25 -0
  37. package/src/app/lib/components/ui/empty/empty-action.component.ts +1 -0
  38. package/src/app/lib/components/ui/empty/empty-description.component.ts +1 -0
  39. package/src/app/lib/components/ui/empty/empty-icon.component.ts +2 -1
  40. package/src/app/lib/components/ui/empty/empty-title.component.ts +1 -0
  41. package/src/app/lib/components/ui/empty/empty.component.ts +1 -0
  42. package/src/app/lib/components/ui/form/form-description.component.ts +2 -2
  43. package/src/app/lib/components/ui/hover-card/hover-card-content.component.ts +108 -60
  44. package/src/app/lib/components/ui/hover-card/hover-card-context.ts +4 -2
  45. package/src/app/lib/components/ui/hover-card/hover-card-trigger.component.ts +5 -3
  46. package/src/app/lib/components/ui/hover-card/hover-card.component.ts +8 -3
  47. package/src/app/lib/components/ui/input-group/input-group-addon.component.ts +1 -0
  48. package/src/app/lib/components/ui/input-group/input-group-input.component.ts +1 -0
  49. package/src/app/lib/components/ui/input-group/input-group.component.ts +1 -0
  50. package/src/app/lib/components/ui/menubar/menubar-content.component.ts +1 -1
  51. package/src/app/lib/components/ui/navigation-menu/navigation-menu-content.component.ts +7 -1
  52. package/src/app/lib/components/ui/navigation-menu/navigation-menu-context.ts +14 -0
  53. package/src/app/lib/components/ui/navigation-menu/navigation-menu-item.component.ts +9 -4
  54. package/src/app/lib/components/ui/navigation-menu/navigation-menu-trigger.component.ts +69 -2
  55. package/src/app/lib/components/ui/navigation-menu/navigation-menu.component.ts +32 -4
  56. package/src/app/lib/components/ui/pagination/pagination.component.ts +3 -1
  57. package/src/app/lib/components/ui/popover/popover-content.component.ts +11 -0
  58. package/src/app/lib/components/ui/popover/popover-context.ts +2 -0
  59. package/src/app/lib/components/ui/popover/popover.component.ts +4 -0
  60. package/src/app/lib/components/ui/progress/progress.component.ts +1 -2
  61. package/src/app/lib/components/ui/scroll-area/scroll-area.component.ts +7 -6
  62. package/src/app/lib/components/ui/segmented/segmented-item.component.ts +1 -0
  63. package/src/app/lib/components/ui/segmented/segmented.component.ts +1 -0
  64. package/src/app/lib/components/ui/select/select-content.component.ts +35 -15
  65. package/src/app/lib/components/ui/select/select-context.ts +10 -0
  66. package/src/app/lib/components/ui/select/select-item.component.ts +25 -7
  67. package/src/app/lib/components/ui/select/select-trigger.component.ts +6 -13
  68. package/src/app/lib/components/ui/select/select.component.ts +46 -0
  69. package/src/app/lib/components/ui/sheet/sheet-content.component.ts +22 -5
  70. package/src/app/lib/components/ui/slider/slider.component.ts +2 -2
  71. package/src/app/lib/components/ui/sonner/index.ts +2 -0
  72. package/src/app/lib/components/ui/sonner/sonner.component.ts +70 -0
  73. package/src/app/lib/components/ui/switch/switch.component.ts +1 -14
  74. package/src/app/lib/components/ui/tabs/tabs-list.component.ts +18 -0
  75. package/src/app/lib/components/ui/tabs/tabs-trigger.component.ts +0 -1
  76. package/src/app/lib/components/ui/toggle/toggle.component.ts +12 -6
  77. package/src/app/lib/components/ui/tooltip/tooltip-content.component.ts +141 -17
  78. package/src/app/lib/components/ui/tooltip/tooltip-context.ts +3 -1
  79. package/src/app/lib/components/ui/tooltip/tooltip-provider.component.ts +1 -1
  80. package/src/app/lib/components/ui/tooltip/tooltip-trigger.component.ts +5 -2
  81. package/src/app/lib/components/ui/tooltip/tooltip.component.ts +3 -1
@@ -1,4 +1,4 @@
1
- import { cn } from '@/lib/utils';
1
+ import { cn, Presence } from '@/lib/utils';
2
2
  import { FocusTrapDirective } from '@/lib/utils/accessibility';
3
3
  import {
4
4
  ChangeDetectionStrategy,
@@ -10,22 +10,25 @@ import {
10
10
  HostListener,
11
11
  inject,
12
12
  input,
13
- signal,
14
13
  } from '@angular/core';
15
14
  import { DIALOG_CONTEXT } from './dialog-context';
16
15
 
17
- /** Animation duration in ms — must match Tailwind's duration-200 */
18
- const EXIT_ANIMATION_MS = 200;
19
-
20
16
  /**
21
17
  * DialogContent component - the content of the dialog.
22
18
  * Matches shadcn/ui React DialogContent exactly.
19
+ *
20
+ * Features:
21
+ * - Escape key closes the dialog
22
+ * - Overlay click closes the dialog
23
+ * - Focus is trapped within the dialog
24
+ * - Exit animations handled by Presence component (no setTimeout needed)
25
+ * - Focus restored on any close path (overlay click, close button, Escape, programmatic)
23
26
  */
24
27
  @Component({
25
28
  selector: 'DialogContent',
26
- imports: [FocusTrapDirective],
29
+ imports: [FocusTrapDirective, Presence],
27
30
  template: `
28
- @if (shouldRender()) {
31
+ <Presence [present]="context.isOpen()">
29
32
  <!-- Overlay -->
30
33
  <div
31
34
  class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-200"
@@ -38,7 +41,7 @@ const EXIT_ANIMATION_MS = 200;
38
41
  hlmFocusTrap
39
42
  [trapFocus]="context.isOpen()"
40
43
  [autoFocus]="true"
41
- [restoreFocus]="true"
44
+ [restoreFocus]="false"
42
45
  [initialFocus]="initialFocus()"
43
46
  [class]="computedClass()"
44
47
  [attr.data-state]="context.isOpen() ? 'open' : 'closed'"
@@ -76,7 +79,7 @@ const EXIT_ANIMATION_MS = 200;
76
79
  </button>
77
80
  }
78
81
  </div>
79
- }
82
+ </Presence>
80
83
  `,
81
84
  host: {
82
85
  'attr.data-slot': '"dialog-content"',
@@ -86,22 +89,22 @@ const EXIT_ANIMATION_MS = 200;
86
89
  })
87
90
  export class DialogContent {
88
91
  constructor() {
92
+ let wasOpen = false;
93
+
89
94
  effect(() => {
90
95
  const isOpen = this.context.isOpen();
91
96
  this._cdr.markForCheck();
92
97
 
93
98
  if (isOpen) {
94
- this.shouldRender.set(true);
99
+ wasOpen = true;
95
100
  this.lockBodyScroll();
96
101
  } else {
97
102
  this.unlockBodyScroll();
98
- if (this.shouldRender()) {
99
- // Keep DOM alive for the exit animation, then unmount
100
- setTimeout(() => {
101
- this.shouldRender.set(false);
102
- this._cdr.markForCheck();
103
- }, EXIT_ANIMATION_MS);
103
+ // Restore focus on any close path (overlay click, close button, Escape, programmatic)
104
+ if (wasOpen) {
105
+ this.restoreFocus();
104
106
  }
107
+ wasOpen = false;
105
108
  }
106
109
  });
107
110
 
@@ -121,8 +124,6 @@ export class DialogContent {
121
124
 
122
125
  readonly context = inject(DIALOG_CONTEXT);
123
126
 
124
- protected readonly shouldRender = signal(false);
125
-
126
127
  protected readonly computedClass = computed(() =>
127
128
  cn(
128
129
  'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background text-foreground p-6 shadow-lg duration-200',
@@ -137,6 +138,7 @@ export class DialogContent {
137
138
  );
138
139
 
139
140
  private previousBodyOverflow = '';
141
+ private previousBodyPaddingRight = '';
140
142
 
141
143
  @HostListener('document:keydown.escape')
142
144
  onEscapeKey(): void {
@@ -154,17 +156,22 @@ export class DialogContent {
154
156
 
155
157
  private lockBodyScroll(): void {
156
158
  if (typeof document !== 'undefined') {
159
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
157
160
  this.previousBodyOverflow = document.body.style.overflow;
161
+ this.previousBodyPaddingRight = document.body.style.paddingRight;
158
162
  document.body.style.overflow = 'hidden';
163
+ if (scrollbarWidth > 0) {
164
+ document.body.style.paddingRight = scrollbarWidth + 'px';
165
+ }
159
166
  }
160
167
  }
161
168
  private unlockBodyScroll(): void {
162
169
  if (typeof document !== 'undefined') {
163
170
  document.body.style.overflow = this.previousBodyOverflow;
171
+ document.body.style.paddingRight = this.previousBodyPaddingRight;
164
172
  }
165
173
  }
166
174
  private close(): void {
167
- this.restoreFocus();
168
175
  this.context.setOpen(false);
169
176
  }
170
177
  private restoreFocus(): void {
@@ -0,0 +1,9 @@
1
+ import { InjectionToken, signal } from '@angular/core';
2
+
3
+ export type Direction = 'ltr' | 'rtl';
4
+
5
+ export interface DirectionContext {
6
+ dir: ReturnType<typeof signal<Direction>>;
7
+ }
8
+
9
+ export const DIRECTION_CONTEXT = new InjectionToken<DirectionContext>('DirectionContext');
@@ -0,0 +1,48 @@
1
+ import { ChangeDetectionStrategy, Component, effect, forwardRef, input, signal } from '@angular/core';
2
+ import {
3
+ DIRECTION_CONTEXT,
4
+ type Direction as DirectionType,
5
+ type DirectionContext,
6
+ } from './direction-context';
7
+
8
+ /**
9
+ * Direction component - provides RTL/LTR direction context to descendants.
10
+ * Equivalent to Radix UI's DirectionProvider.
11
+ *
12
+ * @example
13
+ * <Direction dir="rtl">
14
+ * <!-- All components inside will be RTL -->
15
+ * <Select>...</Select>
16
+ * </Direction>
17
+ */
18
+ @Component({
19
+ selector: 'Direction',
20
+ template: `<ng-content />`,
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ host: {
23
+ 'attr.data-slot': '"direction"',
24
+ '[attr.dir]': 'dir()',
25
+ style: 'display: contents',
26
+ },
27
+ providers: [
28
+ {
29
+ provide: DIRECTION_CONTEXT,
30
+ useFactory: (component: Direction) => component.context,
31
+ deps: [forwardRef(() => Direction)],
32
+ },
33
+ ],
34
+ })
35
+ export class Direction {
36
+ /** Text direction */
37
+ readonly dir = input<DirectionType>('ltr');
38
+
39
+ readonly context: DirectionContext = {
40
+ dir: signal<DirectionType>(this.dir()),
41
+ };
42
+
43
+ constructor() {
44
+ effect(() => {
45
+ this.context.dir.set(this.dir());
46
+ });
47
+ }
48
+ }
@@ -0,0 +1,2 @@
1
+ export { Direction } from './direction.component';
2
+ export { DIRECTION_CONTEXT, type Direction as DirectionType, type DirectionContext } from './direction-context';
@@ -11,6 +11,7 @@ import {
11
11
  Injector,
12
12
  input,
13
13
  OnDestroy,
14
+ signal,
14
15
  viewChild,
15
16
  } from '@angular/core';
16
17
  import { DRAWER_CONTEXT } from './drawer-context';
@@ -35,6 +36,7 @@ import { DRAWER_CONTEXT } from './drawer-context';
35
36
  <div
36
37
  #contentEl
37
38
  [class]="computedClass()"
39
+ [style]="swipeStyle()"
38
40
  [attr.data-state]="context.open() ? 'open' : 'closed'"
39
41
  role="dialog"
40
42
  aria-modal="true"
@@ -44,6 +46,9 @@ import { DRAWER_CONTEXT } from './drawer-context';
44
46
  hlmFocusTrap
45
47
  [trapFocus]="context.open()"
46
48
  (keydown.escape)="onEscapeKey()"
49
+ (touchstart)="onTouchStart($event)"
50
+ (touchmove)="onTouchMove($event)"
51
+ (touchend)="onTouchEnd()"
47
52
  >
48
53
  <!-- Handle -->
49
54
  <div class="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted"></div>
@@ -105,6 +110,17 @@ export class DrawerContent implements OnDestroy {
105
110
  );
106
111
  });
107
112
 
113
+ protected readonly swipeDelta = signal(0);
114
+ private touchStartCoord = 0;
115
+
116
+ protected readonly swipeStyle = computed(() => {
117
+ const delta = this.swipeDelta();
118
+ if (delta === 0) return '';
119
+ const dir = this.context.direction;
120
+ if (dir === 'bottom' || dir === 'top') return { transform: `translateY(${delta}px)` };
121
+ return { transform: `translateX(${delta}px)` };
122
+ });
123
+
108
124
  /** Previous body overflow style for restoration */
109
125
  private previousBodyOverflow = '';
110
126
 
@@ -119,6 +135,34 @@ export class DrawerContent implements OnDestroy {
119
135
  onEscapeKey(): void {
120
136
  this.context.setOpen(false);
121
137
  }
138
+ onTouchStart(event: TouchEvent): void {
139
+ const touch = event.touches[0];
140
+ const dir = this.context.direction;
141
+ this.touchStartCoord = dir === 'bottom' || dir === 'top' ? touch.clientY : touch.clientX;
142
+ }
143
+ onTouchMove(event: TouchEvent): void {
144
+ const touch = event.touches[0];
145
+ const dir = this.context.direction;
146
+ const coord = dir === 'bottom' || dir === 'top' ? touch.clientY : touch.clientX;
147
+ const rawDelta = coord - this.touchStartCoord;
148
+ // Only allow dragging in the "away" direction
149
+ const isAway =
150
+ (dir === 'bottom' && rawDelta > 0) ||
151
+ (dir === 'top' && rawDelta < 0) ||
152
+ (dir === 'right' && rawDelta > 0) ||
153
+ (dir === 'left' && rawDelta < 0);
154
+ this.swipeDelta.set(isAway ? rawDelta : 0);
155
+ }
156
+ onTouchEnd(): void {
157
+ const threshold = 80;
158
+ if (Math.abs(this.swipeDelta()) >= threshold) {
159
+ this.swipeDelta.set(0);
160
+ this.context.setOpen(false);
161
+ } else {
162
+ this.swipeDelta.set(0);
163
+ }
164
+ this.touchStartCoord = 0;
165
+ }
122
166
 
123
167
  private focusFirstElement(): void {
124
168
  setTimeout(() => {
@@ -111,7 +111,7 @@ export class DropdownMenuContent implements OnDestroy {
111
111
  /** Additional CSS classes */
112
112
  readonly class = input<string>('');
113
113
  /** Positioning strategy: 'absolute' stays within parent, 'fixed' escapes overflow containers */
114
- readonly strategy = input<'absolute' | 'fixed'>('absolute');
114
+ readonly strategy = input<'absolute' | 'fixed'>('fixed');
115
115
 
116
116
  private readonly _elementRef = inject(ElementRef);
117
117
 
@@ -198,7 +198,7 @@ export class DropdownMenuContent implements OnDestroy {
198
198
  if (content) {
199
199
  this.menuItems = Array.from(
200
200
  content.querySelectorAll(
201
- '[role="menuitem"]:not([aria-disabled="true"]):not([data-disabled])',
201
+ '[role="menuitem"]:not([aria-disabled="true"]):not([data-disabled=""])',
202
202
  ),
203
203
  );
204
204
  }
@@ -67,5 +67,6 @@ export class DropdownMenuRadioItem {
67
67
  }
68
68
  this._radioGroupContext.setValue(this.value());
69
69
  this.onSelect.emit();
70
+ this._context.open.set(false);
70
71
  }
71
72
  }
@@ -43,9 +43,11 @@ export class DropdownMenuSubContent {
43
43
  );
44
44
 
45
45
  protected onMouseEnter(): void {
46
+ this.subContext.isMouseInSubContent.set(true);
46
47
  this.subContext.open.set(true);
47
48
  }
48
49
  protected onMouseLeave(): void {
50
+ this.subContext.isMouseInSubContent.set(false);
49
51
  this.subContext.open.set(false);
50
52
  }
51
53
  }
@@ -19,7 +19,12 @@ import { DROPDOWN_MENU_SUB_CONTEXT } from './dropdown-menu-sub.component';
19
19
  '[class]': 'computedClass()',
20
20
  '(mouseenter)': 'onMouseEnter()',
21
21
  '(mouseleave)': 'onMouseLeave()',
22
+ '(keydown)': 'onKeyDown($event)',
22
23
  '[attr.data-state]': 'subContext.open() ? "open" : "closed"',
24
+ 'role': 'menuitem',
25
+ '[attr.aria-haspopup]': '"menu"',
26
+ '[attr.aria-expanded]': 'subContext.open()',
27
+ '[attr.tabindex]': '"-1"',
23
28
  },
24
29
  changeDetection: ChangeDetectionStrategy.OnPush,
25
30
  })
@@ -46,9 +51,30 @@ export class DropdownMenuSubTrigger {
46
51
  this.subContext.open.set(true);
47
52
  }
48
53
  protected onMouseLeave(): void {
49
- // Delay closing to allow mouse to move to sub-content
50
54
  setTimeout(() => {
51
- // Check if mouse is still outside both trigger and content
55
+ if (!this.subContext.isMouseInSubContent()) {
56
+ this.subContext.open.set(false);
57
+ }
52
58
  }, 100);
53
59
  }
60
+ protected onKeyDown(event: KeyboardEvent): void {
61
+ if (event.key === 'ArrowRight' || event.key === 'Enter' || event.key === ' ') {
62
+ event.preventDefault();
63
+ event.stopPropagation();
64
+ this.subContext.open.set(true);
65
+ // Focus first focusable item in sub-content after it renders
66
+ setTimeout(() => {
67
+ const subContent = (event.target as HTMLElement)
68
+ .closest('DropdownMenuSub')
69
+ ?.querySelector<HTMLElement>('[role="menu"] [role="menuitem"]:not([aria-disabled="true"]):not([data-disabled=""])');
70
+ if (subContent) {
71
+ subContent.focus();
72
+ }
73
+ }, 10);
74
+ }
75
+ if (event.key === 'ArrowLeft') {
76
+ event.preventDefault();
77
+ this.subContext.open.set(false);
78
+ }
79
+ }
54
80
  }
@@ -8,6 +8,8 @@ import {
8
8
 
9
9
  export interface DropdownMenuSubContext {
10
10
  open: WritableSignal<boolean>;
11
+ /** True while the mouse is hovering over the sub-content panel */
12
+ isMouseInSubContent: WritableSignal<boolean>;
11
13
  }
12
14
 
13
15
  export const DROPDOWN_MENU_SUB_CONTEXT = new InjectionToken<DropdownMenuSubContext>(
@@ -26,6 +28,7 @@ export const DROPDOWN_MENU_SUB_CONTEXT = new InjectionToken<DropdownMenuSubConte
26
28
  provide: DROPDOWN_MENU_SUB_CONTEXT,
27
29
  useFactory: (): DropdownMenuSubContext => ({
28
30
  open: signal(false),
31
+ isMouseInSubContent: signal(false),
29
32
  }),
30
33
  },
31
34
  ],
@@ -36,6 +36,31 @@ export class DropdownMenuTrigger {
36
36
  this.context.open.set(true);
37
37
  this.context.focusedIndex.set(0);
38
38
  break;
39
+ case 'ArrowUp':
40
+ event.preventDefault();
41
+ this.context.triggerElement.set(this._elementRef.nativeElement);
42
+ this.context.open.set(true);
43
+ // Set focusedIndex to -1 so the content effect opens normally,
44
+ // then after it renders and focuses the first item we move to last.
45
+ this.context.focusedIndex.set(-1);
46
+ setTimeout(() => {
47
+ // After the content's own setTimeout(0) has run and focused item[0],
48
+ // query the menu items and focus the last one.
49
+ const menu = document.querySelector('[data-slot="dropdown-menu-content"] [role="menu"]');
50
+ if (menu) {
51
+ const items = Array.from(
52
+ menu.querySelectorAll<HTMLElement>(
53
+ '[role="menuitem"]:not([aria-disabled="true"]):not([data-disabled=""])',
54
+ ),
55
+ );
56
+ if (items.length > 0) {
57
+ const lastIndex = items.length - 1;
58
+ items[lastIndex].focus();
59
+ this.context.focusedIndex.set(lastIndex);
60
+ }
61
+ }
62
+ }, 10);
63
+ break;
39
64
  case 'Enter':
40
65
  case ' ':
41
66
  event.preventDefault();
@@ -8,6 +8,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
8
8
  selector: 'EmptyAction',
9
9
  template: `<ng-content />`,
10
10
  host: {
11
+ 'attr.data-slot': '"empty-action"',
11
12
  '[class]': 'computedClass()',
12
13
  },
13
14
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -8,6 +8,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
8
8
  selector: 'EmptyDescription',
9
9
  template: `<ng-content />`,
10
10
  host: {
11
+ 'attr.data-slot': '"empty-description"',
11
12
  '[class]': 'computedClass()',
12
13
  },
13
14
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -8,6 +8,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
8
8
  selector: 'EmptyIcon',
9
9
  template: `<ng-content />`,
10
10
  host: {
11
+ 'attr.data-slot': '"empty-icon"',
11
12
  '[class]': 'computedClass()',
12
13
  },
13
14
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -19,7 +20,7 @@ export class EmptyIcon {
19
20
  /** Computed class combining base styles and custom classes */
20
21
  protected readonly computedClass = computed(() =>
21
22
  cn(
22
- 'mx-auto flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground [&>svg]:size-6',
23
+ 'mx-auto flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground [&>svg]:size-6 [&>lucide-icon]:size-6',
23
24
  this.class(),
24
25
  ),
25
26
  );
@@ -8,6 +8,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
8
8
  selector: 'EmptyTitle',
9
9
  template: `<ng-content />`,
10
10
  host: {
11
+ 'attr.data-slot': '"empty-title"',
11
12
  '[class]': 'computedClass()',
12
13
  },
13
14
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -30,6 +30,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
30
30
  selector: 'Empty',
31
31
  template: `<ng-content />`,
32
32
  host: {
33
+ 'attr.data-slot': '"empty"',
33
34
  '[class]': 'computedClass()',
34
35
  },
35
36
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -14,7 +14,7 @@ import { FORM_FIELD_CONTEXT } from './form-context';
14
14
  host: {
15
15
  '[class]': 'computedClass()',
16
16
  '[attr.id]': 'fieldContext?.formDescriptionId()',
17
- 'data-slot': 'form-description',
17
+ 'attr.data-slot': '"form-description"',
18
18
  },
19
19
  changeDetection: ChangeDetectionStrategy.OnPush,
20
20
  })
@@ -26,6 +26,6 @@ export class FormDescription {
26
26
 
27
27
  /** Computed class combining base styles and custom classes */
28
28
  protected readonly computedClass = computed(() =>
29
- cn('text-muted-foreground text-[0.8rem]', this.class()),
29
+ cn('text-muted-foreground text-[length:var(--font-size-description)]', this.class()),
30
30
  );
31
31
  }