@robomous/ui-core 0.1.1 → 0.2.0

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 (135) hide show
  1. package/README.md +15 -7
  2. package/dist/{primitives → components}/alert.js +1 -1
  3. package/dist/{primitives → components}/badge.js +1 -1
  4. package/dist/{primitives → components}/button.d.ts +1 -1
  5. package/dist/{primitives → components}/button.js +4 -1
  6. package/dist/{primitives → components}/card.d.ts +1 -1
  7. package/dist/{primitives → components}/card.js +2 -2
  8. package/dist/{primitives → components}/combobox.js +3 -3
  9. package/dist/{primitives → components}/dialog.js +1 -1
  10. package/dist/{primitives → components}/dropdown-menu.js +11 -6
  11. package/dist/{primitives → components}/field.js +2 -4
  12. package/dist/{primitives → components}/input-group.js +1 -1
  13. package/dist/{primitives → components}/input.js +1 -1
  14. package/dist/{primitives → components}/label.js +1 -1
  15. package/dist/components/progress.js +7 -0
  16. package/dist/{primitives → components}/select.d.ts +3 -1
  17. package/dist/{primitives → components}/select.js +6 -4
  18. package/dist/{primitives → components}/separator.js +1 -1
  19. package/dist/{primitives → components}/sheet.js +1 -1
  20. package/dist/{primitives → components}/skeleton.js +1 -1
  21. package/dist/{primitives → components}/sonner.js +17 -12
  22. package/dist/{primitives → components}/table.d.ts +1 -1
  23. package/dist/{primitives → components}/table.js +3 -3
  24. package/dist/{primitives → components}/tabs.js +1 -1
  25. package/dist/{primitives → components}/textarea.js +1 -1
  26. package/dist/{primitives → components}/tooltip.js +1 -1
  27. package/dist/gates/index.d.ts +28 -0
  28. package/dist/gates/index.js +142 -0
  29. package/dist/index.d.ts +31 -32
  30. package/dist/index.js +32 -33
  31. package/dist/{tokens.d.ts → theme/tokens.d.ts} +5 -5
  32. package/dist/{tokens.js → theme/tokens.js} +12 -12
  33. package/package.json +23 -12
  34. package/{shadcn → src/components}/alert.tsx +14 -21
  35. package/src/{primitives → components}/badge.tsx +15 -21
  36. package/src/{primitives → components}/button.tsx +13 -10
  37. package/{shadcn → src/components}/card.tsx +16 -31
  38. package/src/{primitives → components}/combobox.test.tsx +15 -2
  39. package/src/{primitives → components}/combobox.tsx +45 -74
  40. package/src/{primitives/primitives.test.tsx → components/components.test.tsx} +106 -69
  41. package/src/{primitives → components}/dialog.tsx +31 -54
  42. package/src/{primitives → components}/dropdown-menu.tsx +51 -65
  43. package/src/{primitives → components}/field.tsx +48 -62
  44. package/src/{primitives → components}/input-group.tsx +37 -49
  45. package/{shadcn → src/components}/input.tsx +5 -5
  46. package/{shadcn → src/components}/label.tsx +7 -10
  47. package/src/{primitives → components}/progress.tsx +7 -6
  48. package/{shadcn → src/components}/select.tsx +39 -41
  49. package/{shadcn → src/components}/separator.tsx +7 -7
  50. package/src/{primitives → components}/sheet.tsx +27 -44
  51. package/{shadcn → src/components}/skeleton.tsx +3 -3
  52. package/src/{primitives → components}/sonner.test.tsx +12 -4
  53. package/src/components/sonner.tsx +67 -0
  54. package/{shadcn → src/components}/table.tsx +17 -44
  55. package/{shadcn → src/components}/tabs.tsx +16 -26
  56. package/{shadcn → src/components}/textarea.tsx +5 -5
  57. package/{shadcn → src/components}/tooltip.tsx +12 -16
  58. package/src/gates/design.test.ts +188 -0
  59. package/src/gates/index.test.ts +11 -0
  60. package/src/gates/index.ts +165 -0
  61. package/src/gates/tokens.test.ts +215 -0
  62. package/src/harness.test.tsx +1 -1
  63. package/src/index.ts +63 -33
  64. package/src/theme/imports.test.ts +160 -0
  65. package/src/{statusTone.test.ts → theme/statusTone.test.ts} +5 -1
  66. package/src/{styles.css → theme/styles.css} +45 -31
  67. package/src/theme/tailwind.css +646 -0
  68. package/src/{tokens.test.ts → theme/tokens.test.ts} +30 -23
  69. package/src/{tokens.ts → theme/tokens.ts} +12 -12
  70. package/components.json +0 -13
  71. package/dist/lib/button.d.ts +0 -1
  72. package/dist/lib/button.js +0 -4
  73. package/dist/lib/cn.d.ts +0 -18
  74. package/dist/lib/cn.js +0 -21
  75. package/dist/lib/menu.d.ts +0 -1
  76. package/dist/lib/menu.js +0 -16
  77. package/dist/lib/progress.d.ts +0 -3
  78. package/dist/lib/progress.js +0 -6
  79. package/dist/lib/select.d.ts +0 -1
  80. package/dist/lib/select.js +0 -3
  81. package/dist/primitives/progress.js +0 -7
  82. package/gates/canonical.test.mjs +0 -71
  83. package/gates/extensions.test.mjs +0 -373
  84. package/gates/index.d.mts +0 -51
  85. package/gates/index.mjs +0 -514
  86. package/gates/tokens.test.mjs +0 -276
  87. package/shadcn/README.md +0 -13
  88. package/shadcn/badge.tsx +0 -49
  89. package/shadcn/button.tsx +0 -67
  90. package/shadcn/combobox.tsx +0 -299
  91. package/shadcn/dialog.tsx +0 -166
  92. package/shadcn/dropdown-menu.tsx +0 -267
  93. package/shadcn/field.tsx +0 -236
  94. package/shadcn/input-group.tsx +0 -154
  95. package/shadcn/progress.tsx +0 -29
  96. package/shadcn/sheet.tsx +0 -145
  97. package/shadcn/sonner.tsx +0 -49
  98. package/src/lib/button.ts +0 -4
  99. package/src/lib/cn.test.ts +0 -33
  100. package/src/lib/cn.ts +0 -23
  101. package/src/lib/menu.ts +0 -16
  102. package/src/lib/progress.ts +0 -6
  103. package/src/lib/select.ts +0 -4
  104. package/src/primitives/alert.tsx +0 -76
  105. package/src/primitives/card.tsx +0 -103
  106. package/src/primitives/input.tsx +0 -19
  107. package/src/primitives/label.tsx +0 -22
  108. package/src/primitives/select.tsx +0 -192
  109. package/src/primitives/separator.tsx +0 -28
  110. package/src/primitives/skeleton.tsx +0 -13
  111. package/src/primitives/sonner.tsx +0 -65
  112. package/src/primitives/table.tsx +0 -114
  113. package/src/primitives/tabs.tsx +0 -88
  114. package/src/primitives/textarea.tsx +0 -18
  115. package/src/primitives/tooltip.tsx +0 -57
  116. /package/dist/{primitives → components}/alert.d.ts +0 -0
  117. /package/dist/{primitives → components}/badge.d.ts +0 -0
  118. /package/dist/{primitives → components}/combobox.d.ts +0 -0
  119. /package/dist/{primitives → components}/dialog.d.ts +0 -0
  120. /package/dist/{primitives → components}/dropdown-menu.d.ts +0 -0
  121. /package/dist/{primitives → components}/field.d.ts +0 -0
  122. /package/dist/{primitives → components}/input-group.d.ts +0 -0
  123. /package/dist/{primitives → components}/input.d.ts +0 -0
  124. /package/dist/{primitives → components}/label.d.ts +0 -0
  125. /package/dist/{primitives → components}/progress.d.ts +0 -0
  126. /package/dist/{primitives → components}/separator.d.ts +0 -0
  127. /package/dist/{primitives → components}/sheet.d.ts +0 -0
  128. /package/dist/{primitives → components}/skeleton.d.ts +0 -0
  129. /package/dist/{primitives → components}/sonner.d.ts +0 -0
  130. /package/dist/{primitives → components}/tabs.d.ts +0 -0
  131. /package/dist/{primitives → components}/textarea.d.ts +0 -0
  132. /package/dist/{primitives → components}/tooltip.d.ts +0 -0
  133. /package/dist/{statusTone.d.ts → theme/statusTone.d.ts} +0 -0
  134. /package/dist/{statusTone.js → theme/statusTone.js} +0 -0
  135. /package/src/{statusTone.ts → theme/statusTone.ts} +0 -0
@@ -1,43 +1,39 @@
1
- "use client"
1
+ "use client";
2
2
 
3
- import * as React from "react"
4
- import { Select as SelectPrimitive } from "radix-ui"
3
+ import * as React from "react";
4
+ import { Select as SelectPrimitive } from "radix-ui";
5
5
 
6
- import { cn } from "@/lib/cn"
7
- import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
6
+ import { cn } from "cn";
7
+ import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react";
8
8
 
9
- function Select({
10
- ...props
11
- }: React.ComponentProps<typeof SelectPrimitive.Root>) {
12
- return <SelectPrimitive.Root data-slot="select" {...props} />
9
+ function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>) {
10
+ return <SelectPrimitive.Root data-slot="select" {...props} />;
13
11
  }
14
12
 
15
- function SelectGroup({
16
- className,
17
- ...props
18
- }: React.ComponentProps<typeof SelectPrimitive.Group>) {
13
+ function SelectGroup({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Group>) {
19
14
  return (
20
15
  <SelectPrimitive.Group
21
16
  data-slot="select-group"
22
17
  className={cn("scroll-my-1 p-1", className)}
23
18
  {...props}
24
19
  />
25
- )
20
+ );
26
21
  }
27
22
 
28
- function SelectValue({
29
- ...props
30
- }: React.ComponentProps<typeof SelectPrimitive.Value>) {
31
- return <SelectPrimitive.Value data-slot="select-value" {...props} />
23
+ function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>) {
24
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />;
32
25
  }
33
26
 
34
27
  function SelectTrigger({
35
28
  className,
36
29
  size = "default",
30
+ multiline = false,
37
31
  children,
38
32
  ...props
39
33
  }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
40
- size?: "sm" | "default"
34
+ size?: "sm" | "default";
35
+ /** Let the closed trigger grow to its value instead of clamping it to one line. */
36
+ multiline?: boolean;
41
37
  }) {
42
38
  return (
43
39
  <SelectPrimitive.Trigger
@@ -45,7 +41,9 @@ function SelectTrigger({
45
41
  data-size={size}
46
42
  className={cn(
47
43
  "flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
48
- className
44
+ multiline &&
45
+ "data-[size=default]:h-auto min-h-8 *:data-[slot=select-value]:line-clamp-none",
46
+ className,
49
47
  )}
50
48
  {...props}
51
49
  >
@@ -54,7 +52,7 @@ function SelectTrigger({
54
52
  <ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
55
53
  </SelectPrimitive.Icon>
56
54
  </SelectPrimitive.Trigger>
57
- )
55
+ );
58
56
  }
59
57
 
60
58
  function SelectContent({
@@ -69,7 +67,12 @@ function SelectContent({
69
67
  <SelectPrimitive.Content
70
68
  data-slot="select-content"
71
69
  data-align-trigger={position === "item-aligned"}
72
- className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
70
+ className={cn(
71
+ "relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
72
+ position === "popper" &&
73
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
74
+ className,
75
+ )}
73
76
  position={position}
74
77
  align={align}
75
78
  {...props}
@@ -79,7 +82,7 @@ function SelectContent({
79
82
  data-position={position}
80
83
  className={cn(
81
84
  "data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)",
82
- position === "popper" && ""
85
+ position === "popper" && "",
83
86
  )}
84
87
  >
85
88
  {children}
@@ -87,20 +90,17 @@ function SelectContent({
87
90
  <SelectScrollDownButton />
88
91
  </SelectPrimitive.Content>
89
92
  </SelectPrimitive.Portal>
90
- )
93
+ );
91
94
  }
92
95
 
93
- function SelectLabel({
94
- className,
95
- ...props
96
- }: React.ComponentProps<typeof SelectPrimitive.Label>) {
96
+ function SelectLabel({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Label>) {
97
97
  return (
98
98
  <SelectPrimitive.Label
99
99
  data-slot="select-label"
100
100
  className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)}
101
101
  {...props}
102
102
  />
103
- )
103
+ );
104
104
  }
105
105
 
106
106
  function SelectItem({
@@ -113,7 +113,7 @@ function SelectItem({
113
113
  data-slot="select-item"
114
114
  className={cn(
115
115
  "relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
116
- className
116
+ className,
117
117
  )}
118
118
  {...props}
119
119
  >
@@ -124,7 +124,7 @@ function SelectItem({
124
124
  </span>
125
125
  <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
126
126
  </SelectPrimitive.Item>
127
- )
127
+ );
128
128
  }
129
129
 
130
130
  function SelectSeparator({
@@ -137,7 +137,7 @@ function SelectSeparator({
137
137
  className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
138
138
  {...props}
139
139
  />
140
- )
140
+ );
141
141
  }
142
142
 
143
143
  function SelectScrollUpButton({
@@ -149,14 +149,13 @@ function SelectScrollUpButton({
149
149
  data-slot="select-scroll-up-button"
150
150
  className={cn(
151
151
  "z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
152
- className
152
+ className,
153
153
  )}
154
154
  {...props}
155
155
  >
156
- <ChevronUpIcon
157
- />
156
+ <ChevronUpIcon />
158
157
  </SelectPrimitive.ScrollUpButton>
159
- )
158
+ );
160
159
  }
161
160
 
162
161
  function SelectScrollDownButton({
@@ -168,14 +167,13 @@ function SelectScrollDownButton({
168
167
  data-slot="select-scroll-down-button"
169
168
  className={cn(
170
169
  "z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
171
- className
170
+ className,
172
171
  )}
173
172
  {...props}
174
173
  >
175
- <ChevronDownIcon
176
- />
174
+ <ChevronDownIcon />
177
175
  </SelectPrimitive.ScrollDownButton>
178
- )
176
+ );
179
177
  }
180
178
 
181
179
  export {
@@ -189,4 +187,4 @@ export {
189
187
  SelectSeparator,
190
188
  SelectTrigger,
191
189
  SelectValue,
192
- }
190
+ };
@@ -1,9 +1,9 @@
1
- "use client"
1
+ "use client";
2
2
 
3
- import * as React from "react"
4
- import { Separator as SeparatorPrimitive } from "radix-ui"
3
+ import * as React from "react";
4
+ import { Separator as SeparatorPrimitive } from "radix-ui";
5
5
 
6
- import { cn } from "@/lib/cn"
6
+ import { cn } from "cn";
7
7
 
8
8
  function Separator({
9
9
  className,
@@ -18,11 +18,11 @@ function Separator({
18
18
  orientation={orientation}
19
19
  className={cn(
20
20
  "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
21
- className
21
+ className,
22
22
  )}
23
23
  {...props}
24
24
  />
25
- )
25
+ );
26
26
  }
27
27
 
28
- export { Separator }
28
+ export { Separator };
@@ -1,30 +1,24 @@
1
- import * as React from "react"
2
- import { Dialog as SheetPrimitive } from "radix-ui"
1
+ import * as React from "react";
2
+ import { Dialog as SheetPrimitive } from "radix-ui";
3
3
 
4
- import { cn } from "../lib/cn.js"
5
- import { Button } from "./button.js"
6
- import { XIcon } from "lucide-react"
4
+ import { cn } from "cn";
5
+ import { Button } from "./button.js";
6
+ import { XIcon } from "lucide-react";
7
7
 
8
8
  function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
9
- return <SheetPrimitive.Root data-slot="sheet" {...props} />
9
+ return <SheetPrimitive.Root data-slot="sheet" {...props} />;
10
10
  }
11
11
 
12
- function SheetTrigger({
13
- ...props
14
- }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
15
- return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
12
+ function SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
13
+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
16
14
  }
17
15
 
18
- function SheetClose({
19
- ...props
20
- }: React.ComponentProps<typeof SheetPrimitive.Close>) {
21
- return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
16
+ function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>) {
17
+ return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
22
18
  }
23
19
 
24
- function SheetPortal({
25
- ...props
26
- }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
27
- return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
20
+ function SheetPortal({ ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
21
+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
28
22
  }
29
23
 
30
24
  function SheetOverlay({
@@ -36,11 +30,11 @@ function SheetOverlay({
36
30
  data-slot="sheet-overlay"
37
31
  className={cn(
38
32
  "fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
39
- className
33
+ className,
40
34
  )}
41
35
  {...props}
42
36
  />
43
- )
37
+ );
44
38
  }
45
39
 
46
40
  function SheetContent({
@@ -50,8 +44,8 @@ function SheetContent({
50
44
  showCloseButton = true,
51
45
  ...props
52
46
  }: React.ComponentProps<typeof SheetPrimitive.Content> & {
53
- side?: "top" | "right" | "bottom" | "left"
54
- showCloseButton?: boolean
47
+ side?: "top" | "right" | "bottom" | "left";
48
+ showCloseButton?: boolean;
55
49
  }) {
56
50
  return (
57
51
  <SheetPortal>
@@ -61,27 +55,22 @@ function SheetContent({
61
55
  data-side={side}
62
56
  className={cn(
63
57
  "fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-[side=bottom]:data-open:slide-in-from-bottom-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:animate-out data-closed:fade-out-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=right]:data-closed:slide-out-to-right-10 data-[side=top]:data-closed:slide-out-to-top-10",
64
- className
58
+ className,
65
59
  )}
66
60
  {...props}
67
61
  >
68
62
  {children}
69
63
  {showCloseButton && (
70
64
  <SheetPrimitive.Close data-slot="sheet-close" asChild>
71
- <Button
72
- variant="ghost"
73
- className="absolute top-3 right-3"
74
- size="icon-sm"
75
- >
76
- <XIcon
77
- />
65
+ <Button variant="ghost" className="absolute top-3 right-3" size="icon-sm">
66
+ <XIcon />
78
67
  <span className="sr-only">Close</span>
79
68
  </Button>
80
69
  </SheetPrimitive.Close>
81
70
  )}
82
71
  </SheetPrimitive.Content>
83
72
  </SheetPortal>
84
- )
73
+ );
85
74
  }
86
75
 
87
76
  function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
@@ -91,7 +80,7 @@ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
91
80
  className={cn("flex flex-col gap-0.5 p-4", className)}
92
81
  {...props}
93
82
  />
94
- )
83
+ );
95
84
  }
96
85
 
97
86
  function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
@@ -101,23 +90,17 @@ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
101
90
  className={cn("mt-auto flex flex-col gap-2 p-4", className)}
102
91
  {...props}
103
92
  />
104
- )
93
+ );
105
94
  }
106
95
 
107
- function SheetTitle({
108
- className,
109
- ...props
110
- }: React.ComponentProps<typeof SheetPrimitive.Title>) {
96
+ function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) {
111
97
  return (
112
98
  <SheetPrimitive.Title
113
99
  data-slot="sheet-title"
114
- className={cn(
115
- "font-heading text-base font-medium text-foreground",
116
- className
117
- )}
100
+ className={cn("font-heading text-base font-medium text-foreground", className)}
118
101
  {...props}
119
102
  />
120
- )
103
+ );
121
104
  }
122
105
 
123
106
  function SheetDescription({
@@ -130,7 +113,7 @@ function SheetDescription({
130
113
  className={cn("text-sm text-muted-foreground", className)}
131
114
  {...props}
132
115
  />
133
- )
116
+ );
134
117
  }
135
118
 
136
119
  export {
@@ -142,4 +125,4 @@ export {
142
125
  SheetFooter,
143
126
  SheetTitle,
144
127
  SheetDescription,
145
- }
128
+ };
@@ -1,4 +1,4 @@
1
- import { cn } from "@/lib/cn"
1
+ import { cn } from "cn";
2
2
 
3
3
  function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
4
  return (
@@ -7,7 +7,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
7
7
  className={cn("animate-pulse rounded-md bg-muted", className)}
8
8
  {...props}
9
9
  />
10
- )
10
+ );
11
11
  }
12
12
 
13
- export { Skeleton }
13
+ export { Skeleton };
@@ -16,7 +16,9 @@ describe("toaster theme", () => {
16
16
  const { container } = render(<Toaster />);
17
17
  toast("hi");
18
18
  await waitFor(() => {
19
- expect(container.querySelector("[data-sonner-toaster]")?.getAttribute("data-sonner-theme")).toBe("light");
19
+ expect(
20
+ container.querySelector("[data-sonner-toaster]")?.getAttribute("data-sonner-theme"),
21
+ ).toBe("light");
20
22
  });
21
23
  });
22
24
  it("is dark when <html> carries .dark", async () => {
@@ -24,18 +26,24 @@ describe("toaster theme", () => {
24
26
  const { container } = render(<Toaster />);
25
27
  toast("hi");
26
28
  await waitFor(() => {
27
- expect(container.querySelector("[data-sonner-toaster]")?.getAttribute("data-sonner-theme")).toBe("dark");
29
+ expect(
30
+ container.querySelector("[data-sonner-toaster]")?.getAttribute("data-sonner-theme"),
31
+ ).toBe("dark");
28
32
  });
29
33
  });
30
34
  it("follows <html> from light to dark after mount, via the MutationObserver", async () => {
31
35
  const { container } = render(<Toaster />);
32
36
  toast("hi");
33
37
  await waitFor(() => {
34
- expect(container.querySelector("[data-sonner-toaster]")?.getAttribute("data-sonner-theme")).toBe("light");
38
+ expect(
39
+ container.querySelector("[data-sonner-toaster]")?.getAttribute("data-sonner-theme"),
40
+ ).toBe("light");
35
41
  });
36
42
  document.documentElement.classList.add("dark");
37
43
  await waitFor(() => {
38
- expect(container.querySelector("[data-sonner-toaster]")?.getAttribute("data-sonner-theme")).toBe("dark");
44
+ expect(
45
+ container.querySelector("[data-sonner-toaster]")?.getAttribute("data-sonner-theme"),
46
+ ).toBe("dark");
39
47
  });
40
48
  });
41
49
  });
@@ -0,0 +1,67 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Toaster as Sonner, type ToasterProps } from "sonner";
5
+ import {
6
+ CircleCheckIcon,
7
+ InfoIcon,
8
+ TriangleAlertIcon,
9
+ OctagonXIcon,
10
+ Loader2Icon,
11
+ } from "lucide-react";
12
+
13
+ // The theme this package runs on has one source: the `dark` class on <html>,
14
+ // which is what `styles.css`'s `@custom-variant dark` keys off and therefore
15
+ // what every other component in here already follows. A toast is portalled
16
+ // outside the tree but reads the same page, so it has to answer to the same
17
+ // source — hence this hook rather than a `next-themes` `useTheme`, which asks
18
+ // for a React provider none of this runs under. Sonner needs the answer as a
19
+ // value, not a class, which is the only reason the class has to be observed
20
+ // at all.
21
+ function useTheme(): { theme: "light" | "dark" } {
22
+ const read = () =>
23
+ typeof document !== "undefined" && document.documentElement.classList.contains("dark")
24
+ ? "dark"
25
+ : "light";
26
+ const [theme, setTheme] = React.useState<"light" | "dark">(read);
27
+ React.useEffect(() => {
28
+ const observer = new MutationObserver(() => setTheme(read()));
29
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
30
+ return () => observer.disconnect();
31
+ }, []);
32
+ return { theme };
33
+ }
34
+
35
+ const Toaster = ({ ...props }: ToasterProps) => {
36
+ const { theme = "system" } = useTheme();
37
+
38
+ return (
39
+ <Sonner
40
+ theme={theme as ToasterProps["theme"]}
41
+ className="toaster group"
42
+ icons={{
43
+ success: <CircleCheckIcon className="size-4" />,
44
+ info: <InfoIcon className="size-4" />,
45
+ warning: <TriangleAlertIcon className="size-4" />,
46
+ error: <OctagonXIcon className="size-4" />,
47
+ loading: <Loader2Icon className="size-4 animate-spin" />,
48
+ }}
49
+ style={
50
+ {
51
+ "--normal-bg": "var(--popover)",
52
+ "--normal-text": "var(--popover-foreground)",
53
+ "--normal-border": "var(--border)",
54
+ "--border-radius": "var(--radius)",
55
+ } as React.CSSProperties
56
+ }
57
+ toastOptions={{
58
+ classNames: {
59
+ toast: "cn-toast",
60
+ },
61
+ }}
62
+ {...props}
63
+ />
64
+ );
65
+ };
66
+
67
+ export { Toaster };
@@ -1,30 +1,21 @@
1
- import * as React from "react"
1
+ import * as React from "react";
2
2
 
3
- import { cn } from "@/lib/cn"
3
+ import { cn } from "cn";
4
4
 
5
5
  function Table({ className, ...props }: React.ComponentProps<"table">) {
6
6
  return (
7
- <div
8
- data-slot="table-container"
9
- className="relative w-full overflow-x-auto"
10
- >
7
+ <div data-slot="table-container" className="relative w-full overflow-x-auto">
11
8
  <table
12
9
  data-slot="table"
13
10
  className={cn("w-full caption-bottom text-sm", className)}
14
11
  {...props}
15
12
  />
16
13
  </div>
17
- )
14
+ );
18
15
  }
19
16
 
20
17
  function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
21
- return (
22
- <thead
23
- data-slot="table-header"
24
- className={cn("[&_tr]:border-b", className)}
25
- {...props}
26
- />
27
- )
18
+ return <thead data-slot="table-header" className={cn("[&_tr]:border-b", className)} {...props} />;
28
19
  }
29
20
 
30
21
  function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
@@ -34,20 +25,17 @@ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
34
25
  className={cn("[&_tr:last-child]:border-0", className)}
35
26
  {...props}
36
27
  />
37
- )
28
+ );
38
29
  }
39
30
 
40
31
  function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
41
32
  return (
42
33
  <tfoot
43
34
  data-slot="table-footer"
44
- className={cn(
45
- "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
46
- className
47
- )}
35
+ className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)}
48
36
  {...props}
49
37
  />
50
- )
38
+ );
51
39
  }
52
40
 
53
41
  function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
@@ -56,11 +44,11 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
56
44
  data-slot="table-row"
57
45
  className={cn(
58
46
  "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
59
- className
47
+ className,
60
48
  )}
61
49
  {...props}
62
50
  />
63
- )
51
+ );
64
52
  }
65
53
 
66
54
  function TableHead({ className, ...props }: React.ComponentProps<"th">) {
@@ -69,46 +57,31 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
69
57
  data-slot="table-head"
70
58
  className={cn(
71
59
  "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
72
- className
60
+ className,
73
61
  )}
74
62
  {...props}
75
63
  />
76
- )
64
+ );
77
65
  }
78
66
 
79
67
  function TableCell({ className, ...props }: React.ComponentProps<"td">) {
80
68
  return (
81
69
  <td
82
70
  data-slot="table-cell"
83
- className={cn(
84
- "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
85
- className
86
- )}
71
+ className={cn("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0", className)}
87
72
  {...props}
88
73
  />
89
- )
74
+ );
90
75
  }
91
76
 
92
- function TableCaption({
93
- className,
94
- ...props
95
- }: React.ComponentProps<"caption">) {
77
+ function TableCaption({ className, ...props }: React.ComponentProps<"caption">) {
96
78
  return (
97
79
  <caption
98
80
  data-slot="table-caption"
99
81
  className={cn("mt-4 text-sm text-muted-foreground", className)}
100
82
  {...props}
101
83
  />
102
- )
84
+ );
103
85
  }
104
86
 
105
- export {
106
- Table,
107
- TableHeader,
108
- TableBody,
109
- TableFooter,
110
- TableHead,
111
- TableRow,
112
- TableCell,
113
- TableCaption,
114
- }
87
+ export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };