@bearmenu/ui 0.1.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 (207) hide show
  1. package/dist/chunk-2CUFS5LF.js +80 -0
  2. package/dist/chunk-3GWWZKX7.js +33 -0
  3. package/dist/chunk-3VMDCCCX.js +10 -0
  4. package/dist/chunk-4AVS6CV4.js +89 -0
  5. package/dist/chunk-4JOV7FXW.js +17 -0
  6. package/dist/chunk-4KOMHDZD.js +89 -0
  7. package/dist/chunk-4UO73SKC.js +26 -0
  8. package/dist/chunk-7H54PTVH.js +116 -0
  9. package/dist/chunk-CW24PLDW.js +24 -0
  10. package/dist/chunk-EBAMCZDW.js +112 -0
  11. package/dist/chunk-ERMIA7NP.js +106 -0
  12. package/dist/chunk-ESO2EM3L.js +46 -0
  13. package/dist/chunk-EUHEIWHZ.js +32 -0
  14. package/dist/chunk-FEK5XGZW.js +8 -0
  15. package/dist/chunk-NKHH23YK.js +31 -0
  16. package/dist/chunk-R47NEOSI.js +78 -0
  17. package/dist/chunk-UXAYXRFN.js +69 -0
  18. package/dist/chunk-X6GATUI2.js +19 -0
  19. package/dist/components/accordion.d.ts +9 -0
  20. package/dist/components/accordion.js +53 -0
  21. package/dist/components/alert-dialog.d.ts +23 -0
  22. package/dist/components/alert-dialog.js +98 -0
  23. package/dist/components/alert.d.ts +11 -0
  24. package/dist/components/alert.js +52 -0
  25. package/dist/components/aspect-ratio.d.ts +6 -0
  26. package/dist/components/aspect-ratio.js +2 -0
  27. package/dist/components/avatar.d.ts +8 -0
  28. package/dist/components/avatar.js +47 -0
  29. package/dist/components/badge.d.ts +13 -0
  30. package/dist/components/badge.js +3 -0
  31. package/dist/components/breadcrumb.d.ts +14 -0
  32. package/dist/components/breadcrumb.js +96 -0
  33. package/dist/components/button.d.ts +16 -0
  34. package/dist/components/button.js +4 -0
  35. package/dist/components/calendar.d.ts +11 -0
  36. package/dist/components/calendar.js +56 -0
  37. package/dist/components/card.d.ts +13 -0
  38. package/dist/components/card.js +4 -0
  39. package/dist/components/carousel.d.ts +24 -0
  40. package/dist/components/carousel.js +198 -0
  41. package/dist/components/checkbox.d.ts +6 -0
  42. package/dist/components/checkbox.js +25 -0
  43. package/dist/components/collapsible.d.ts +8 -0
  44. package/dist/components/collapsible.js +26 -0
  45. package/dist/components/command.d.ts +84 -0
  46. package/dist/components/command.js +4 -0
  47. package/dist/components/currency-input.d.ts +17 -0
  48. package/dist/components/currency-input.js +121 -0
  49. package/dist/components/dialog.d.ts +22 -0
  50. package/dist/components/dialog.js +3 -0
  51. package/dist/components/drawer.d.ts +27 -0
  52. package/dist/components/drawer.js +85 -0
  53. package/dist/components/dropdown-menu.d.ts +28 -0
  54. package/dist/components/dropdown-menu.js +211 -0
  55. package/dist/components/empty-state.d.ts +22 -0
  56. package/dist/components/empty-state.js +81 -0
  57. package/dist/components/form.d.ts +28 -0
  58. package/dist/components/form.js +97 -0
  59. package/dist/components/haptic-button.d.ts +12 -0
  60. package/dist/components/haptic-button.js +17 -0
  61. package/dist/components/input-otp.d.ts +14 -0
  62. package/dist/components/input-otp.js +62 -0
  63. package/dist/components/input.d.ts +6 -0
  64. package/dist/components/input.js +3 -0
  65. package/dist/components/label.d.ts +8 -0
  66. package/dist/components/label.js +3 -0
  67. package/dist/components/lazy-markdown.d.ts +11 -0
  68. package/dist/components/lazy-markdown.js +13 -0
  69. package/dist/components/link-button.d.ts +30 -0
  70. package/dist/components/link-button.js +17 -0
  71. package/dist/components/markdown-renderer.d.ts +11 -0
  72. package/dist/components/markdown-renderer.js +10 -0
  73. package/dist/components/multi-select-combobox.d.ts +23 -0
  74. package/dist/components/multi-select-combobox.js +114 -0
  75. package/dist/components/pagination.d.ts +18 -0
  76. package/dist/components/pagination.js +101 -0
  77. package/dist/components/popover.d.ts +8 -0
  78. package/dist/components/popover.js +3 -0
  79. package/dist/components/progress.d.ts +6 -0
  80. package/dist/components/progress.js +30 -0
  81. package/dist/components/radio-group.d.ts +7 -0
  82. package/dist/components/radio-group.js +30 -0
  83. package/dist/components/rating.d.ts +15 -0
  84. package/dist/components/rating.js +120 -0
  85. package/dist/components/scroll-area.d.ts +7 -0
  86. package/dist/components/scroll-area.js +44 -0
  87. package/dist/components/search-bar.d.ts +17 -0
  88. package/dist/components/search-bar.js +92 -0
  89. package/dist/components/searchable-select.d.ts +22 -0
  90. package/dist/components/searchable-select.js +104 -0
  91. package/dist/components/select.d.ts +15 -0
  92. package/dist/components/select.js +129 -0
  93. package/dist/components/separator.d.ts +6 -0
  94. package/dist/components/separator.js +25 -0
  95. package/dist/components/sheet.d.ts +29 -0
  96. package/dist/components/sheet.js +96 -0
  97. package/dist/components/skeleton-card.d.ts +13 -0
  98. package/dist/components/skeleton-card.js +154 -0
  99. package/dist/components/skeleton.d.ts +6 -0
  100. package/dist/components/skeleton.js +3 -0
  101. package/dist/components/slider.d.ts +6 -0
  102. package/dist/components/slider.js +24 -0
  103. package/dist/components/sliding-panels.d.ts +19 -0
  104. package/dist/components/sliding-panels.js +75 -0
  105. package/dist/components/sonner.d.ts +6 -0
  106. package/dist/components/sonner.js +23 -0
  107. package/dist/components/sticky-container.d.ts +44 -0
  108. package/dist/components/sticky-container.js +39 -0
  109. package/dist/components/switch.d.ts +6 -0
  110. package/dist/components/switch.js +31 -0
  111. package/dist/components/table.d.ts +12 -0
  112. package/dist/components/table.js +81 -0
  113. package/dist/components/tabs.d.ts +12 -0
  114. package/dist/components/tabs.js +55 -0
  115. package/dist/components/text.d.ts +31 -0
  116. package/dist/components/text.js +3 -0
  117. package/dist/components/textarea.d.ts +6 -0
  118. package/dist/components/textarea.js +23 -0
  119. package/dist/components/toast.d.ts +18 -0
  120. package/dist/components/toast.js +3 -0
  121. package/dist/components/toaster.d.ts +5 -0
  122. package/dist/components/toaster.js +25 -0
  123. package/dist/components/toggle-group.d.ts +12 -0
  124. package/dist/components/toggle-group.js +72 -0
  125. package/dist/components/toggle.d.ts +13 -0
  126. package/dist/components/toggle.js +3 -0
  127. package/dist/components/tooltip.d.ts +9 -0
  128. package/dist/components/tooltip.js +26 -0
  129. package/dist/components/use-mobile.d.ts +3 -0
  130. package/dist/components/use-mobile.js +19 -0
  131. package/dist/components/use-toast.d.ts +42 -0
  132. package/dist/components/use-toast.js +2 -0
  133. package/dist/hooks/use-haptic.d.ts +22 -0
  134. package/dist/hooks/use-haptic.js +2 -0
  135. package/dist/hooks/use-mobile.d.ts +3 -0
  136. package/dist/hooks/use-mobile.js +19 -0
  137. package/dist/hooks/use-mounted-theme.d.ts +6 -0
  138. package/dist/hooks/use-mounted-theme.js +18 -0
  139. package/dist/hooks/use-sticky.d.ts +18 -0
  140. package/dist/hooks/use-sticky.js +2 -0
  141. package/dist/lib/utils.d.ts +5 -0
  142. package/dist/lib/utils.js +2 -0
  143. package/package.json +203 -0
  144. package/src/components/accordion.tsx +57 -0
  145. package/src/components/alert-dialog.tsx +116 -0
  146. package/src/components/alert.tsx +57 -0
  147. package/src/components/aspect-ratio.tsx +9 -0
  148. package/src/components/avatar.tsx +49 -0
  149. package/src/components/badge.tsx +38 -0
  150. package/src/components/breadcrumb.tsx +102 -0
  151. package/src/components/button.tsx +94 -0
  152. package/src/components/calendar.tsx +60 -0
  153. package/src/components/card.tsx +67 -0
  154. package/src/components/carousel.tsx +230 -0
  155. package/src/components/checkbox.tsx +27 -0
  156. package/src/components/collapsible.tsx +28 -0
  157. package/src/components/command.tsx +148 -0
  158. package/src/components/currency-input.tsx +158 -0
  159. package/src/components/dialog.tsx +103 -0
  160. package/src/components/drawer.tsx +99 -0
  161. package/src/components/dropdown-menu.tsx +228 -0
  162. package/src/components/empty-state.tsx +118 -0
  163. package/src/components/form.tsx +152 -0
  164. package/src/components/haptic-button.tsx +19 -0
  165. package/src/components/input-otp.tsx +70 -0
  166. package/src/components/input.tsx +23 -0
  167. package/src/components/label.tsx +20 -0
  168. package/src/components/lazy-markdown.tsx +20 -0
  169. package/src/components/link-button.tsx +40 -0
  170. package/src/components/markdown-renderer.tsx +20 -0
  171. package/src/components/multi-select-combobox.tsx +148 -0
  172. package/src/components/pagination.tsx +106 -0
  173. package/src/components/popover.tsx +30 -0
  174. package/src/components/progress.tsx +27 -0
  175. package/src/components/radio-group.tsx +37 -0
  176. package/src/components/rating.tsx +136 -0
  177. package/src/components/scroll-area.tsx +45 -0
  178. package/src/components/search-bar.tsx +113 -0
  179. package/src/components/searchable-select.tsx +134 -0
  180. package/src/components/select.tsx +152 -0
  181. package/src/components/separator.tsx +25 -0
  182. package/src/components/sheet.tsx +121 -0
  183. package/src/components/skeleton-card.tsx +282 -0
  184. package/src/components/skeleton.tsx +17 -0
  185. package/src/components/slider.tsx +24 -0
  186. package/src/components/sliding-panels.tsx +99 -0
  187. package/src/components/sonner.tsx +25 -0
  188. package/src/components/sticky-container.tsx +88 -0
  189. package/src/components/switch.tsx +28 -0
  190. package/src/components/table.tsx +90 -0
  191. package/src/components/tabs.tsx +72 -0
  192. package/src/components/text.test.tsx +192 -0
  193. package/src/components/text.tsx +114 -0
  194. package/src/components/textarea.tsx +22 -0
  195. package/src/components/toast.tsx +129 -0
  196. package/src/components/toaster.tsx +31 -0
  197. package/src/components/toggle-group.tsx +69 -0
  198. package/src/components/toggle.tsx +46 -0
  199. package/src/components/tooltip.tsx +29 -0
  200. package/src/components/use-mobile.tsx +19 -0
  201. package/src/components/use-toast.ts +180 -0
  202. package/src/globals.css +409 -0
  203. package/src/hooks/use-haptic.ts +118 -0
  204. package/src/hooks/use-mobile.ts +19 -0
  205. package/src/hooks/use-mounted-theme.ts +20 -0
  206. package/src/hooks/use-sticky.ts +47 -0
  207. package/src/lib/utils.ts +6 -0
@@ -0,0 +1,72 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
5
+ import { cn } from "../lib/utils";
6
+
7
+ type TabsVariant = "pill" | "underline";
8
+
9
+ const TabsVariantContext = React.createContext<TabsVariant>("pill");
10
+
11
+ const Tabs = TabsPrimitive.Root;
12
+
13
+ const TabsList = React.forwardRef<
14
+ React.ElementRef<typeof TabsPrimitive.List>,
15
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {
16
+ variant?: TabsVariant;
17
+ }
18
+ >(({ className, variant = "pill", children, ...props }, ref) => (
19
+ <TabsVariantContext.Provider value={variant}>
20
+ <TabsPrimitive.List
21
+ ref={ref}
22
+ className={cn(
23
+ variant === "pill"
24
+ ? "inline-flex h-12 items-center justify-center gap-2 rounded-3xl glass p-1 text-muted-foreground border border-border/50 shadow-lg"
25
+ : "inline-flex h-auto items-center gap-0 rounded-none bg-transparent border-b border-border p-0 shadow-none text-muted-foreground",
26
+ className
27
+ )}
28
+ {...props}
29
+ >
30
+ {children}
31
+ </TabsPrimitive.List>
32
+ </TabsVariantContext.Provider>
33
+ ));
34
+ TabsList.displayName = TabsPrimitive.List.displayName;
35
+
36
+ const TabsTrigger = React.forwardRef<
37
+ React.ElementRef<typeof TabsPrimitive.Trigger>,
38
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
39
+ >(({ className, ...props }, ref) => {
40
+ const variant = React.useContext(TabsVariantContext);
41
+
42
+ return (
43
+ <TabsPrimitive.Trigger
44
+ ref={ref}
45
+ className={cn(
46
+ variant === "pill"
47
+ ? "inline-flex items-center justify-center whitespace-nowrap rounded-2xl px-4 py-2 text-sm font-medium ring-offset-background transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-primary/90 data-[state=active]:text-primary-foreground data-[state=active]:shadow-lg data-[state=active]:backdrop-blur-md data-[state=active]:border data-[state=active]:border-border/50 hover:bg-accent/50 hover:text-accent-foreground [@media(pointer:coarse)]:active:scale-95"
48
+ : "relative inline-flex items-center justify-center whitespace-nowrap rounded-none px-4 h-12 text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:text-foreground data-[state=active]:bg-transparent data-[state=active]:shadow-none text-muted-foreground hover:text-foreground/80 cursor-pointer",
49
+ className
50
+ )}
51
+ {...props}
52
+ />
53
+ );
54
+ });
55
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
56
+
57
+ const TabsContent = React.forwardRef<
58
+ React.ElementRef<typeof TabsPrimitive.Content>,
59
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
60
+ >(({ className, ...props }, ref) => (
61
+ <TabsPrimitive.Content
62
+ ref={ref}
63
+ className={cn(
64
+ "mt-4 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
65
+ className
66
+ )}
67
+ {...props}
68
+ />
69
+ ));
70
+ TabsContent.displayName = TabsPrimitive.Content.displayName;
71
+
72
+ export { Tabs, TabsList, TabsTrigger, TabsContent };
@@ -0,0 +1,192 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { Text, textVariants } from "./text";
4
+
5
+ // ─── textVariants utility ────────────────────────────────────────────────────
6
+
7
+ describe("textVariants", () => {
8
+ it("returns empty string with no arguments (color:inherit default)", () => {
9
+ const result = textVariants({});
10
+ // color:inherit maps to "" so the full output should be empty/whitespace
11
+ expect(result.trim()).toBe("");
12
+ });
13
+
14
+ it("maps color tokens to correct classes", () => {
15
+ expect(textVariants({ color: "default" })).toContain("text-foreground");
16
+ expect(textVariants({ color: "muted" })).toContain("text-muted-foreground");
17
+ expect(textVariants({ color: "primary" })).toContain("text-primary");
18
+ expect(textVariants({ color: "destructive" })).toContain("text-destructive");
19
+ expect(textVariants({ color: "inherit" }).trim()).toBe("");
20
+ });
21
+
22
+ it("maps variant to correct class bundles", () => {
23
+ expect(textVariants({ variant: "display" })).toContain("text-5xl");
24
+ expect(textVariants({ variant: "h1" })).toContain("text-3xl");
25
+ expect(textVariants({ variant: "h2" })).toContain("text-2xl");
26
+ expect(textVariants({ variant: "h3" })).toContain("text-xl");
27
+ expect(textVariants({ variant: "h4" })).toContain("text-lg");
28
+ expect(textVariants({ variant: "body-lg" })).toContain("text-base");
29
+ expect(textVariants({ variant: "body" })).toContain("text-sm");
30
+ expect(textVariants({ variant: "body-sm" })).toContain("text-sm");
31
+ expect(textVariants({ variant: "caption" })).toContain("text-xs");
32
+ expect(textVariants({ variant: "label" })).toContain("font-medium");
33
+ expect(textVariants({ variant: "overline" })).toContain("uppercase");
34
+ });
35
+
36
+ it("maps weight props", () => {
37
+ expect(textVariants({ weight: "normal" })).toContain("font-normal");
38
+ expect(textVariants({ weight: "medium" })).toContain("font-medium");
39
+ expect(textVariants({ weight: "semibold" })).toContain("font-semibold");
40
+ expect(textVariants({ weight: "bold" })).toContain("font-bold");
41
+ });
42
+
43
+ it("maps leading props", () => {
44
+ expect(textVariants({ leading: "none" })).toContain("leading-none");
45
+ expect(textVariants({ leading: "tight" })).toContain("leading-tight");
46
+ expect(textVariants({ leading: "snug" })).toContain("leading-snug");
47
+ expect(textVariants({ leading: "normal" })).toContain("leading-normal");
48
+ expect(textVariants({ leading: "relaxed" })).toContain("leading-relaxed");
49
+ expect(textVariants({ leading: "loose" })).toContain("leading-loose");
50
+ });
51
+
52
+ it("maps tracking props", () => {
53
+ expect(textVariants({ tracking: "tight" })).toContain("tracking-tight");
54
+ expect(textVariants({ tracking: "normal" })).toContain("tracking-normal");
55
+ expect(textVariants({ tracking: "wide" })).toContain("tracking-wide");
56
+ expect(textVariants({ tracking: "wider" })).toContain("tracking-wider");
57
+ });
58
+
59
+ it("maps wrap props", () => {
60
+ expect(textVariants({ wrap: "balance" })).toContain("text-balance");
61
+ expect(textVariants({ wrap: "pretty" })).toContain("text-pretty");
62
+ expect(textVariants({ wrap: "nowrap" })).toContain("whitespace-nowrap");
63
+ });
64
+
65
+ it("combines multiple props without conflict", () => {
66
+ const result = textVariants({
67
+ weight: "semibold",
68
+ leading: "none",
69
+ tracking: "tight",
70
+ wrap: "balance",
71
+ });
72
+ expect(result).toContain("font-semibold");
73
+ expect(result).toContain("leading-none");
74
+ expect(result).toContain("tracking-tight");
75
+ expect(result).toContain("text-balance");
76
+ });
77
+ });
78
+
79
+ // ─── <Text> component ─────────────────────────────────────────────────────────
80
+
81
+ describe("Text component", () => {
82
+ // Happy path — renders with correct element and content
83
+
84
+ it("renders as <p> by default", () => {
85
+ render(<Text>hello</Text>);
86
+ const el = screen.getByText("hello");
87
+ expect(el.tagName).toBe("P");
88
+ });
89
+
90
+ it("renders the correct tag when as= is specified", () => {
91
+ render(<Text as="h1">Heading</Text>);
92
+ expect(screen.getByText("Heading").tagName).toBe("H1");
93
+ });
94
+
95
+ it("renders as span", () => {
96
+ render(<Text as="span">inline</Text>);
97
+ expect(screen.getByText("inline").tagName).toBe("SPAN");
98
+ });
99
+
100
+ it("renders as label with htmlFor", () => {
101
+ render(
102
+ <Text as="label" htmlFor="my-input">
103
+ Label
104
+ </Text>
105
+ );
106
+ const el = screen.getByText("Label");
107
+ expect(el.tagName).toBe("LABEL");
108
+ expect(el.getAttribute("for")).toBe("my-input");
109
+ });
110
+
111
+ it("renders children correctly", () => {
112
+ render(<Text>test content</Text>);
113
+ expect(screen.getByText("test content")).toBeDefined();
114
+ });
115
+
116
+ // Class output
117
+
118
+ it("applies color class from color prop", () => {
119
+ render(<Text color="muted">muted text</Text>);
120
+ expect(screen.getByText("muted text").className).toContain("text-muted-foreground");
121
+ });
122
+
123
+ it("applies weight class from weight prop", () => {
124
+ render(<Text weight="bold">bold text</Text>);
125
+ expect(screen.getByText("bold text").className).toContain("font-bold");
126
+ });
127
+
128
+ it("applies leading class from leading prop", () => {
129
+ render(<Text leading="relaxed">relaxed</Text>);
130
+ expect(screen.getByText("relaxed").className).toContain("leading-relaxed");
131
+ });
132
+
133
+ it("applies wrap class from wrap prop", () => {
134
+ render(<Text wrap="balance">balanced</Text>);
135
+ expect(screen.getByText("balanced").className).toContain("text-balance");
136
+ });
137
+
138
+ it("applies variant class bundle", () => {
139
+ render(<Text variant="h4">heading</Text>);
140
+ const el = screen.getByText("heading");
141
+ expect(el.className).toContain("text-lg");
142
+ expect(el.className).toContain("font-semibold");
143
+ expect(el.className).toContain("leading-none");
144
+ expect(el.className).toContain("tracking-tight");
145
+ });
146
+
147
+ it("passes through className and merges via twMerge", () => {
148
+ render(
149
+ <Text color="muted" className="mb-4 text-red-500">
150
+ override
151
+ </Text>
152
+ );
153
+ const el = screen.getByText("override");
154
+ // twMerge: text-red-500 overrides text-muted-foreground
155
+ expect(el.className).toContain("text-red-500");
156
+ expect(el.className).not.toContain("text-muted-foreground");
157
+ expect(el.className).toContain("mb-4");
158
+ });
159
+
160
+ it("passes through arbitrary HTML attributes", () => {
161
+ render(
162
+ <Text id="my-id" data-testid="text-el">
163
+ content
164
+ </Text>
165
+ );
166
+ const el = screen.getByTestId("text-el");
167
+ expect(el.id).toBe("my-id");
168
+ });
169
+
170
+ // Edge cases
171
+
172
+ it("renders with no props (no classes, pure element)", () => {
173
+ render(<Text>bare</Text>);
174
+ const el = screen.getByText("bare");
175
+ // color:inherit emits empty string, no variant = no variant classes
176
+ expect(el.className.trim()).toBe("");
177
+ });
178
+
179
+ it("renders children including nested elements", () => {
180
+ render(
181
+ <Text as="p">
182
+ Text with <strong>bold</strong> inside
183
+ </Text>
184
+ );
185
+ expect(screen.getByText("bold").tagName).toBe("STRONG");
186
+ });
187
+
188
+ it("renders div correctly", () => {
189
+ render(<Text as="div">block</Text>);
190
+ expect(screen.getByText("block").tagName).toBe("DIV");
191
+ });
192
+ });
@@ -0,0 +1,114 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "../lib/utils";
4
+
5
+ const textVariants = cva("", {
6
+ variants: {
7
+ variant: {
8
+ display: "text-5xl font-bold tracking-tight",
9
+ h1: "text-3xl font-bold tracking-tight",
10
+ h2: "text-2xl font-semibold tracking-tight",
11
+ h3: "text-xl font-semibold",
12
+ h4: "text-lg font-semibold leading-none tracking-tight",
13
+ "body-lg": "text-base leading-relaxed",
14
+ body: "text-sm leading-normal",
15
+ "body-sm": "text-sm leading-relaxed",
16
+ caption: "text-xs leading-normal",
17
+ label: "text-sm font-medium",
18
+ overline: "text-xs font-semibold uppercase tracking-wider",
19
+ },
20
+ color: {
21
+ default: "text-foreground",
22
+ muted: "text-muted-foreground",
23
+ primary: "text-primary",
24
+ destructive: "text-destructive",
25
+ "primary-foreground": "text-primary-foreground",
26
+ inherit: "",
27
+ },
28
+ weight: {
29
+ normal: "font-normal",
30
+ medium: "font-medium",
31
+ semibold: "font-semibold",
32
+ bold: "font-bold",
33
+ },
34
+ leading: {
35
+ none: "leading-none",
36
+ tight: "leading-tight",
37
+ snug: "leading-snug",
38
+ normal: "leading-normal",
39
+ relaxed: "leading-relaxed",
40
+ loose: "leading-loose",
41
+ },
42
+ tracking: {
43
+ tight: "tracking-tight",
44
+ normal: "tracking-normal",
45
+ wide: "tracking-wide",
46
+ wider: "tracking-wider",
47
+ },
48
+ wrap: {
49
+ balance: "text-balance",
50
+ pretty: "text-pretty",
51
+ nowrap: "whitespace-nowrap",
52
+ },
53
+ },
54
+ defaultVariants: {
55
+ color: "inherit",
56
+ },
57
+ });
58
+
59
+ export type TextTag =
60
+ | "h1"
61
+ | "h2"
62
+ | "h3"
63
+ | "h4"
64
+ | "h5"
65
+ | "h6"
66
+ | "p"
67
+ | "span"
68
+ | "div"
69
+ | "label"
70
+ | "strong"
71
+ | "em"
72
+ | "small"
73
+ | "li"
74
+ | "dt"
75
+ | "dd";
76
+
77
+ type TextOwnProps<T extends TextTag = "p"> = {
78
+ as?: T;
79
+ variant?: VariantProps<typeof textVariants>["variant"];
80
+ color?: VariantProps<typeof textVariants>["color"];
81
+ weight?: VariantProps<typeof textVariants>["weight"];
82
+ leading?: VariantProps<typeof textVariants>["leading"];
83
+ tracking?: VariantProps<typeof textVariants>["tracking"];
84
+ wrap?: VariantProps<typeof textVariants>["wrap"];
85
+ className?: string;
86
+ };
87
+
88
+ // Omit HTML's deprecated `color` attribute so it doesn't conflict with our color prop
89
+ type TextProps<T extends TextTag = "p"> = TextOwnProps<T> &
90
+ Omit<React.ComponentPropsWithoutRef<T>, keyof TextOwnProps<T> | "color">;
91
+
92
+ function Text<T extends TextTag = "p">({
93
+ as,
94
+ variant,
95
+ color,
96
+ weight,
97
+ leading,
98
+ tracking,
99
+ wrap,
100
+ className,
101
+ ...props
102
+ }: TextProps<T>) {
103
+ const Comp = (as ?? "p") as React.ElementType;
104
+ return (
105
+ <Comp
106
+ className={cn(textVariants({ variant, color, weight, leading, tracking, wrap }), className)}
107
+ {...(props as React.HTMLAttributes<HTMLElement>)}
108
+ />
109
+ );
110
+ }
111
+
112
+ Text.displayName = "Text";
113
+
114
+ export { Text, textVariants };
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ import { cn } from "../lib/utils";
3
+
4
+ export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
5
+
6
+ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
7
+ ({ className, ...props }, ref) => {
8
+ return (
9
+ <textarea
10
+ className={cn(
11
+ "flex min-h-[80px] w-full rounded-xl border border-border bg-input/50 backdrop-blur-md px-4 py-3 text-base transition-all duration-300 placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:bg-input focus-visible:border-accent-foreground/30 focus-visible:shadow-lg focus-visible:shadow-accent/10 disabled:cursor-not-allowed disabled:opacity-50 resize-none",
12
+ className
13
+ )}
14
+ ref={ref}
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+ );
20
+ Textarea.displayName = "Textarea";
21
+
22
+ export { Textarea };
@@ -0,0 +1,129 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ToastPrimitives from "@radix-ui/react-toast";
5
+ import { cva, type VariantProps } from "class-variance-authority";
6
+ import { X } from "lucide-react";
7
+ import { cn } from "../lib/utils";
8
+
9
+ const ToastProvider = ToastPrimitives.Provider;
10
+
11
+ const ToastViewport = React.forwardRef<
12
+ React.ElementRef<typeof ToastPrimitives.Viewport>,
13
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
14
+ >(({ className, ...props }, ref) => (
15
+ <ToastPrimitives.Viewport
16
+ ref={ref}
17
+ className={cn(
18
+ "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
19
+ className
20
+ )}
21
+ {...props}
22
+ />
23
+ ));
24
+ ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
25
+
26
+ const toastVariants = cva(
27
+ "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-xl glass p-6 pr-8 shadow-2xl transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full border border-border/50",
28
+ {
29
+ variants: {
30
+ variant: {
31
+ default: "border bg-background text-foreground",
32
+ destructive: "destructive border-destructive/50 bg-destructive text-destructive-foreground",
33
+ },
34
+ },
35
+ defaultVariants: {
36
+ variant: "default",
37
+ },
38
+ }
39
+ );
40
+
41
+ const Toast = React.forwardRef<
42
+ React.ElementRef<typeof ToastPrimitives.Root>,
43
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants>
44
+ >(({ className, variant, ...props }, ref) => {
45
+ return (
46
+ <ToastPrimitives.Root
47
+ ref={ref}
48
+ className={cn(toastVariants({ variant }), className)}
49
+ {...props}
50
+ />
51
+ );
52
+ });
53
+ Toast.displayName = ToastPrimitives.Root.displayName;
54
+
55
+ const ToastAction = React.forwardRef<
56
+ React.ElementRef<typeof ToastPrimitives.Action>,
57
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
58
+ >(({ className, ...props }, ref) => (
59
+ <ToastPrimitives.Action
60
+ ref={ref}
61
+ className={cn(
62
+ "inline-flex h-8 shrink-0 items-center justify-center rounded-lg border border-muted bg-transparent px-3 text-xs font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
63
+ className
64
+ )}
65
+ {...props}
66
+ />
67
+ ));
68
+ ToastAction.displayName = ToastPrimitives.Action.displayName;
69
+
70
+ const ToastClose = React.forwardRef<
71
+ React.ElementRef<typeof ToastPrimitives.Close>,
72
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
73
+ >(({ className, ...props }, ref) => (
74
+ <ToastPrimitives.Close
75
+ ref={ref}
76
+ className={cn(
77
+ "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-white group-[.destructive]:opacity-100 group-[.destructive]:hover:text-white group-[.destructive]:focus:ring-white/50 group-[.destructive]:focus:ring-offset-destructive",
78
+ className
79
+ )}
80
+ toast-close=""
81
+ {...props}
82
+ >
83
+ <X className="h-4 w-4" />
84
+ </ToastPrimitives.Close>
85
+ ));
86
+ ToastClose.displayName = ToastPrimitives.Close.displayName;
87
+
88
+ const ToastTitle = React.forwardRef<
89
+ React.ElementRef<typeof ToastPrimitives.Title>,
90
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
91
+ >(({ className, ...props }, ref) => (
92
+ <ToastPrimitives.Title
93
+ ref={ref}
94
+ className={cn(
95
+ "text-sm font-semibold group-[.destructive]:text-destructive-foreground",
96
+ className
97
+ )}
98
+ {...props}
99
+ />
100
+ ));
101
+ ToastTitle.displayName = ToastPrimitives.Title.displayName;
102
+
103
+ const ToastDescription = React.forwardRef<
104
+ React.ElementRef<typeof ToastPrimitives.Description>,
105
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
106
+ >(({ className, ...props }, ref) => (
107
+ <ToastPrimitives.Description
108
+ ref={ref}
109
+ className={cn("text-sm opacity-90 group-[.destructive]:text-white", className)}
110
+ {...props}
111
+ />
112
+ ));
113
+ ToastDescription.displayName = ToastPrimitives.Description.displayName;
114
+
115
+ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
116
+
117
+ type ToastActionElement = React.ReactElement<typeof ToastAction>;
118
+
119
+ export {
120
+ type ToastProps,
121
+ type ToastActionElement,
122
+ ToastProvider,
123
+ ToastViewport,
124
+ Toast,
125
+ ToastTitle,
126
+ ToastDescription,
127
+ ToastClose,
128
+ ToastAction,
129
+ };
@@ -0,0 +1,31 @@
1
+ "use client";
2
+
3
+ import {
4
+ Toast,
5
+ ToastClose,
6
+ ToastDescription,
7
+ ToastProvider,
8
+ ToastTitle,
9
+ ToastViewport,
10
+ } from "./toast";
11
+ import { useToast } from "./use-toast";
12
+
13
+ export function Toaster() {
14
+ const { toasts } = useToast();
15
+
16
+ return (
17
+ <ToastProvider>
18
+ {toasts.map(({ id, title, description, action, ...props }) => (
19
+ <Toast key={id} {...props}>
20
+ <div className="grid gap-1">
21
+ {title && <ToastTitle>{title}</ToastTitle>}
22
+ {description && <ToastDescription>{description}</ToastDescription>}
23
+ </div>
24
+ {action}
25
+ <ToastClose />
26
+ </Toast>
27
+ ))}
28
+ <ToastViewport />
29
+ </ToastProvider>
30
+ );
31
+ }
@@ -0,0 +1,69 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
5
+ import { type VariantProps } from "class-variance-authority";
6
+
7
+ import { cn } from "../lib/utils";
8
+ import { toggleVariants } from "./toggle";
9
+
10
+ const ToggleGroupContext = React.createContext<VariantProps<typeof toggleVariants>>({
11
+ size: "default",
12
+ variant: "default",
13
+ });
14
+
15
+ function ToggleGroup({
16
+ className,
17
+ variant,
18
+ size,
19
+ children,
20
+ ...props
21
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> & VariantProps<typeof toggleVariants>) {
22
+ return (
23
+ <ToggleGroupPrimitive.Root
24
+ data-slot="toggle-group"
25
+ data-variant={variant}
26
+ data-size={size}
27
+ className={cn(
28
+ "group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
29
+ className
30
+ )}
31
+ {...props}
32
+ >
33
+ <ToggleGroupContext.Provider value={{ variant, size }}>
34
+ {children}
35
+ </ToggleGroupContext.Provider>
36
+ </ToggleGroupPrimitive.Root>
37
+ );
38
+ }
39
+
40
+ function ToggleGroupItem({
41
+ className,
42
+ children,
43
+ variant,
44
+ size,
45
+ ...props
46
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> & VariantProps<typeof toggleVariants>) {
47
+ const context = React.useContext(ToggleGroupContext);
48
+
49
+ return (
50
+ <ToggleGroupPrimitive.Item
51
+ data-slot="toggle-group-item"
52
+ data-variant={context.variant || variant}
53
+ data-size={context.size || size}
54
+ className={cn(
55
+ toggleVariants({
56
+ variant: context.variant || variant,
57
+ size: context.size || size,
58
+ }),
59
+ "min-w-0 flex-1 shrink-0 rounded-none shadow-none first:rounded-l-md last:rounded-r-md focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
60
+ className
61
+ )}
62
+ {...props}
63
+ >
64
+ {children}
65
+ </ToggleGroupPrimitive.Item>
66
+ );
67
+ }
68
+
69
+ export { ToggleGroup, ToggleGroupItem };
@@ -0,0 +1,46 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as TogglePrimitive from "@radix-ui/react-toggle";
5
+ import { cva, type VariantProps } from "class-variance-authority";
6
+
7
+ import { cn } from "../lib/utils";
8
+
9
+ const toggleVariants = cva(
10
+ "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
11
+ {
12
+ variants: {
13
+ variant: {
14
+ default: "bg-transparent",
15
+ outline:
16
+ "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
17
+ },
18
+ size: {
19
+ default: "h-9 px-2 min-w-9",
20
+ sm: "h-8 px-1.5 min-w-8",
21
+ lg: "h-10 px-2.5 min-w-10",
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: "default",
26
+ size: "default",
27
+ },
28
+ }
29
+ );
30
+
31
+ function Toggle({
32
+ className,
33
+ variant,
34
+ size,
35
+ ...props
36
+ }: React.ComponentProps<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>) {
37
+ return (
38
+ <TogglePrimitive.Root
39
+ data-slot="toggle"
40
+ className={cn(toggleVariants({ variant, size, className }))}
41
+ {...props}
42
+ />
43
+ );
44
+ }
45
+
46
+ export { Toggle, toggleVariants };