@srcroot/ui 0.0.48 → 0.0.52

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.
package/dist/index.js CHANGED
@@ -27,7 +27,8 @@ var THEME_METADATA = {
27
27
  rose: { name: "Rose", description: "Soft and elegant pinkish-red" },
28
28
  blue: { name: "Blue", description: "Trustworthy corporate blue" },
29
29
  green: { name: "Green", description: "Nature and success green" },
30
- orange: { name: "Orange", description: "Energetic and creative orange" }
30
+ orange: { name: "Orange", description: "Energetic and creative orange" },
31
+ glass: { name: "Glass", description: "Modern glassmorphic aesthetic" }
31
32
  };
32
33
  var ThemeService = class {
33
34
  registryThemesPath;
@@ -336,7 +337,7 @@ export function cn(...inputs: ClassValue[]) {
336
337
  }
337
338
  async installDependencies() {
338
339
  const cfg = this.config;
339
- const spinner = ora("Installing dependencies...").start();
340
+ const spinner = ora("Checking dependencies...").start();
340
341
  const deps = [
341
342
  "clsx",
342
343
  "tailwind-merge",
@@ -347,15 +348,25 @@ export function cn(...inputs: ClassValue[]) {
347
348
  deps.push("tailwindcss-animate");
348
349
  }
349
350
  try {
350
- await execa(cfg.packageManager, [cfg.installCmd, ...deps], {
351
+ const packageJsonPath = path3.join(cfg.cwd, "package.json");
352
+ const pkg = await fs3.readJson(packageJsonPath);
353
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
354
+ const missingDeps = deps.filter((dep) => !allDeps[dep]);
355
+ if (missingDeps.length === 0) {
356
+ spinner.succeed("Dependencies already installed");
357
+ return;
358
+ }
359
+ spinner.text = "Installing dependencies...";
360
+ await execa(cfg.packageManager, [cfg.installCmd, ...missingDeps], {
351
361
  cwd: cfg.cwd,
352
362
  stdio: "pipe"
353
363
  });
354
- spinner.succeed(`Installed ${deps.length} dependencies`);
364
+ spinner.succeed(`Installed ${missingDeps.length} dependencies`);
355
365
  } catch (error) {
356
366
  spinner.fail("Failed to install dependencies");
367
+ const missingDeps = deps;
357
368
  logger.warn(`
358
- Manually run: ${cfg.packageManager} ${cfg.installCmd} ${deps.join(" ")}`);
369
+ Manually run: ${cfg.packageManager} ${cfg.installCmd} ${missingDeps.join(" ")}`);
359
370
  }
360
371
  }
361
372
  printSuccess() {
@@ -387,7 +398,7 @@ var REGISTRY = {
387
398
  file: "ui/button.tsx",
388
399
  description: "Polymorphic button with variants",
389
400
  category: "Core",
390
- dependencies: []
401
+ dependencies: ["slot"]
391
402
  },
392
403
  badge: {
393
404
  file: "ui/badge.tsx",
@@ -426,7 +437,7 @@ var REGISTRY = {
426
437
  description: "Text input field",
427
438
  category: "Forms",
428
439
  dependencies: [],
429
- registryDependencies: ["react-icons"]
440
+ registryDependencies: ["react-icons^5.5.0"]
430
441
  },
431
442
  textarea: {
432
443
  file: "ui/textarea.tsx",
@@ -519,37 +530,37 @@ var REGISTRY = {
519
530
  file: "ui/dialog.tsx",
520
531
  description: "Modal dialog",
521
532
  category: "Overlay / Feedback",
522
- dependencies: []
533
+ dependencies: ["slot"]
523
534
  },
524
535
  "alert-dialog": {
525
536
  file: "ui/alert-dialog.tsx",
526
537
  description: "Confirmation dialog",
527
538
  category: "Overlay / Feedback",
528
- dependencies: ["dialog"]
539
+ dependencies: ["dialog", "slot"]
529
540
  },
530
541
  sheet: {
531
542
  file: "ui/sheet.tsx",
532
543
  description: "Slide-in panel",
533
544
  category: "Overlay / Feedback",
534
- dependencies: []
545
+ dependencies: ["slot"]
535
546
  },
536
547
  popover: {
537
548
  file: "ui/popover.tsx",
538
549
  description: "Floating content",
539
550
  category: "Overlay / Feedback",
540
- dependencies: []
551
+ dependencies: ["slot"]
541
552
  },
542
553
  tooltip: {
543
554
  file: "ui/tooltip.tsx",
544
555
  description: "Hover tooltip",
545
556
  category: "Overlay / Feedback",
546
- dependencies: []
557
+ dependencies: ["slot"]
547
558
  },
548
559
  "dropdown-menu": {
549
560
  file: "ui/dropdown-menu.tsx",
550
561
  description: "Action dropdown",
551
562
  category: "Overlay / Feedback",
552
- dependencies: []
563
+ dependencies: ["slot"]
553
564
  },
554
565
  toast: {
555
566
  file: "ui/toast.tsx",
@@ -568,7 +579,7 @@ var REGISTRY = {
568
579
  file: "ui/breadcrumb.tsx",
569
580
  description: "Breadcrumb navigation",
570
581
  category: "Navigation",
571
- dependencies: []
582
+ dependencies: ["slot"]
572
583
  },
573
584
  pagination: {
574
585
  file: "ui/pagination.tsx",
@@ -617,7 +628,7 @@ var REGISTRY = {
617
628
  file: "ui/collapsible.tsx",
618
629
  description: "Expandable section",
619
630
  category: "Data Display",
620
- dependencies: []
631
+ dependencies: ["slot"]
621
632
  },
622
633
  carousel: {
623
634
  file: "ui/carousel.tsx",
@@ -635,7 +646,7 @@ var REGISTRY = {
635
646
  file: "ui/sidebar.tsx",
636
647
  description: "Responsive sidebar with mobile drawer",
637
648
  category: "Layout",
638
- dependencies: ["sheet", "button"]
649
+ dependencies: ["sheet", "button", "slot"]
639
650
  },
640
651
  // Added Components
641
652
  combobox: {
@@ -672,13 +683,14 @@ var REGISTRY = {
672
683
  file: "ui/date-picker.tsx",
673
684
  description: "Date picker with calendar",
674
685
  category: "Forms",
675
- dependencies: ["calendar", "popover", "button"]
686
+ dependencies: ["calendar", "popover", "button"],
687
+ registryDependencies: ["date-fns^4.1.0"]
676
688
  },
677
689
  drawer: {
678
690
  file: "ui/drawer.tsx",
679
691
  description: "Bottom/top sheet drawer",
680
692
  category: "Overlay / Feedback",
681
- dependencies: []
693
+ dependencies: ["slot"]
682
694
  },
683
695
  "file-upload": {
684
696
  file: "ui/file-upload.tsx",
@@ -690,7 +702,7 @@ var REGISTRY = {
690
702
  file: "ui/hover-card.tsx",
691
703
  description: "Hover-triggered popover",
692
704
  category: "Overlay / Feedback",
693
- dependencies: []
705
+ dependencies: ["slot"]
694
706
  },
695
707
  kbd: {
696
708
  file: "ui/kbd.tsx",
@@ -775,7 +787,13 @@ var REGISTRY = {
775
787
  description: "Charts using Recharts",
776
788
  category: "Data Display",
777
789
  dependencies: [],
778
- registryDependencies: ["recharts"]
790
+ registryDependencies: ["recharts^2.15.4"]
791
+ },
792
+ slot: {
793
+ file: "ui/slot.tsx",
794
+ description: "Slot utility for polymorphic components",
795
+ category: "Core",
796
+ dependencies: []
779
797
  }
780
798
  };
781
799
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srcroot/ui",
3
- "version": "0.0.48",
3
+ "version": "0.0.52",
4
4
  "description": "A UI library with polymorphic, accessible React components",
5
5
  "author": "Shifaul Islam",
6
6
  "license": "MIT",
@@ -36,6 +36,7 @@
36
36
  "@types/fs-extra": "^11.0.4",
37
37
  "@types/node": "^22.10.1",
38
38
  "@types/prompts": "^2.4.9",
39
+ "@types/react": "^19.2.7",
39
40
  "class-variance-authority": "^0.7.1",
40
41
  "clsx": "^2.1.1",
41
42
  "cmdk": "^1.0.0",
@@ -0,0 +1,154 @@
1
+ /**
2
+ * @srcroot/ui - Glass Theme (Tailwind 3)
3
+ * Glassmorphic transparent aesthetic
4
+ */
5
+
6
+ @tailwind base;
7
+ @tailwind components;
8
+ @tailwind utilities;
9
+
10
+ @layer base {
11
+ :root {
12
+ --background: 0 0% 100%;
13
+ --foreground: 240 10% 3.9%;
14
+
15
+ --card: 0 0% 100% / 0.7;
16
+ --card-foreground: 240 10% 3.9%;
17
+
18
+ --popover: 0 0% 100% / 0.7;
19
+ --popover-foreground: 240 10% 3.9%;
20
+
21
+ --primary: 240 5.9% 10%;
22
+ --primary-foreground: 0 0% 98%;
23
+
24
+ --secondary: 240 4.8% 95.9% / 0.7;
25
+ --secondary-foreground: 240 5.9% 10%;
26
+
27
+ --muted: 240 4.8% 95.9% / 0.7;
28
+ --muted-foreground: 240 3.8% 46.1%;
29
+
30
+ --accent: 240 4.8% 95.9% / 0.7;
31
+ --accent-foreground: 240 5.9% 10%;
32
+
33
+ --destructive: 0 84.2% 60.2%;
34
+ --destructive-foreground: 0 0% 98%;
35
+
36
+ --success: 142.1 76.2% 36.3%;
37
+ --success-foreground: 0 0% 100%;
38
+
39
+ --warning: 45.4 93.4% 47.5%;
40
+ --warning-foreground: 240 10% 3.9%;
41
+
42
+ --info: 201.3 96.3% 32.2%;
43
+ --info-foreground: 0 0% 100%;
44
+
45
+ --border: 240 5.9% 90% / 0.5;
46
+ --input: 240 5.9% 90% / 0.5;
47
+ --ring: 240 10% 3.9%;
48
+
49
+ --radius: 0.5rem;
50
+
51
+ --sidebar-width: 16rem;
52
+ --sidebar-width-mobile: 18rem;
53
+ --sidebar-width-collapsed: 3rem;
54
+ --sidebar-width-icon: 3rem;
55
+ --header-height: 3.5rem;
56
+
57
+ --sidebar-background: 0 0% 98% / 0.7;
58
+ --sidebar-foreground: 240 5.3% 26.1%;
59
+ --sidebar-primary: 240 5.9% 10%;
60
+ --sidebar-primary-foreground: 0 0% 98%;
61
+ --sidebar-accent: 240 4.8% 95.9%;
62
+ --sidebar-accent-foreground: 240 5.9% 10%;
63
+ --sidebar-border: 220 13% 91% / 0.5;
64
+ --sidebar-ring: 217.2 91.2% 59.8%;
65
+ }
66
+
67
+ .dark {
68
+ --background: 240 10% 3.9%;
69
+ --foreground: 0 0% 98%;
70
+
71
+ --card: 240 10% 3.9% / 0.7;
72
+ --card-foreground: 0 0% 98%;
73
+
74
+ --popover: 240 10% 3.9% / 0.7;
75
+ --popover-foreground: 0 0% 98%;
76
+
77
+ --primary: 0 0% 98%;
78
+ --primary-foreground: 240 5.9% 10%;
79
+
80
+ --secondary: 240 3.7% 15.9% / 0.7;
81
+ --secondary-foreground: 0 0% 98%;
82
+
83
+ --muted: 240 3.7% 15.9% / 0.7;
84
+ --muted-foreground: 240 5% 64.9%;
85
+
86
+ --accent: 240 3.7% 15.9% / 0.7;
87
+ --accent-foreground: 0 0% 98%;
88
+
89
+ --destructive: 0 62.8% 30.6%;
90
+ --destructive-foreground: 0 0% 98%;
91
+
92
+ --success: 142.1 70.6% 45.3%;
93
+ --success-foreground: 0 0% 100%;
94
+
95
+ --warning: 48 96.5% 53.1%;
96
+ --warning-foreground: 0 0% 98%;
97
+
98
+ --info: 199.4 95.5% 53.8%;
99
+ --info-foreground: 0 0% 98%;
100
+
101
+ --border: 240 3.7% 15.9% / 0.5;
102
+ --input: 240 3.7% 15.9% / 0.5;
103
+ --ring: 240 4.9% 83.9%;
104
+
105
+ --sidebar-background: 240 10% 3.9% / 0.7;
106
+ --sidebar-foreground: 240 4.8% 95.9%;
107
+ --sidebar-primary: 224.3 76.3% 48%;
108
+ --sidebar-primary-foreground: 0 0% 100%;
109
+ --sidebar-accent: 240 3.7% 15.9%;
110
+ --sidebar-accent-foreground: 240 4.8% 95.9%;
111
+ --sidebar-border: 240 3.7% 15.9% / 0.5;
112
+ --sidebar-ring: 217.2 91.2% 59.8%;
113
+ }
114
+ }
115
+
116
+ @layer base {
117
+ * {
118
+ @apply border-border;
119
+ }
120
+
121
+ body {
122
+ @apply bg-background text-foreground;
123
+ }
124
+ }
125
+
126
+ @layer utilities {
127
+ @keyframes accordion-down {
128
+ from {
129
+ height: 0;
130
+ }
131
+
132
+ to {
133
+ height: var(--radix-accordion-content-height);
134
+ }
135
+ }
136
+
137
+ @keyframes accordion-up {
138
+ from {
139
+ height: var(--radix-accordion-content-height);
140
+ }
141
+
142
+ to {
143
+ height: 0;
144
+ }
145
+ }
146
+
147
+ .animate-accordion-down {
148
+ animation: accordion-down 0.2s ease-out;
149
+ }
150
+
151
+ .animate-accordion-up {
152
+ animation: accordion-up 0.2s ease-out;
153
+ }
154
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * @srcroot/ui - Glass Theme (Tailwind 4)
3
+ * Glassmorphic transparent aesthetic
4
+ */
5
+
6
+ @import "tailwindcss";
7
+
8
+ :root {
9
+ --background: hsl(0 0% 100%);
10
+ --foreground: hsl(240 10% 3.9%);
11
+
12
+ --card: hsl(0 0% 100% / 0.7);
13
+ --card-foreground: hsl(240 10% 3.9%);
14
+
15
+ --popover: hsl(0 0% 100% / 0.7);
16
+ --popover-foreground: hsl(240 10% 3.9%);
17
+
18
+ --primary: hsl(240 5.9% 10%);
19
+ --primary-foreground: hsl(0 0% 98%);
20
+
21
+ --secondary: hsl(240 4.8% 95.9% / 0.7);
22
+ --secondary-foreground: hsl(240 5.9% 10%);
23
+
24
+ --muted: hsl(240 4.8% 95.9% / 0.7);
25
+ --muted-foreground: hsl(240 3.8% 46.1%);
26
+
27
+ --accent: hsl(240 4.8% 95.9% / 0.7);
28
+ --accent-foreground: hsl(240 5.9% 10%);
29
+
30
+ --destructive: hsl(0 84.2% 60.2%);
31
+ --destructive-foreground: hsl(0 0% 98%);
32
+
33
+ --success: hsl(142.1 76.2% 36.3%);
34
+ --success-foreground: hsl(0 0% 100%);
35
+
36
+ --warning: hsl(45.4 93.4% 47.5%);
37
+ --warning-foreground: hsl(240 10% 3.9%);
38
+
39
+ --info: hsl(201.3 96.3% 32.2%);
40
+ --info-foreground: hsl(0 0% 100%);
41
+
42
+ --border: hsl(240 5.9% 90% / 0.5);
43
+ --input: hsl(240 5.9% 90% / 0.5);
44
+ --ring: hsl(240 10% 3.9%);
45
+
46
+ --radius: 0.5rem;
47
+
48
+ --sidebar-width: 16rem;
49
+ --sidebar-width-mobile: 18rem;
50
+ --sidebar-width-collapsed: 3rem;
51
+ --sidebar-width-icon: 3rem;
52
+ --header-height: 3.5rem;
53
+
54
+ --sidebar-background: hsl(0 0% 98% / 0.7);
55
+ --sidebar-foreground: hsl(240 5.3% 26.1%);
56
+ --sidebar-primary: hsl(240 5.9% 10%);
57
+ --sidebar-primary-foreground: hsl(0 0% 98%);
58
+ --sidebar-accent: hsl(240 4.8% 95.9%);
59
+ --sidebar-accent-foreground: hsl(240 5.9% 10%);
60
+ --sidebar-border: hsl(220 13% 91% / 0.5);
61
+ --sidebar-ring: hsl(217.2 91.2% 59.8%);
62
+ }
63
+
64
+ @theme inline {
65
+ --color-border: var(--border);
66
+ --color-input: var(--input);
67
+ --color-ring: var(--ring);
68
+ --color-background: var(--background);
69
+ --color-foreground: var(--foreground);
70
+
71
+ --color-primary: var(--primary);
72
+ --color-primary-foreground: var(--primary-foreground);
73
+
74
+ --color-secondary: var(--secondary);
75
+ --color-secondary-foreground: var(--secondary-foreground);
76
+
77
+ --color-destructive: var(--destructive);
78
+ --color-destructive-foreground: var(--destructive-foreground);
79
+
80
+ --color-muted: var(--muted);
81
+ --color-muted-foreground: var(--muted-foreground);
82
+
83
+ --color-accent: var(--accent);
84
+ --color-accent-foreground: var(--accent-foreground);
85
+
86
+ --color-popover: var(--popover);
87
+ --color-popover-foreground: var(--popover-foreground);
88
+
89
+ --color-card: var(--card);
90
+ --color-card-foreground: var(--card-foreground);
91
+
92
+ --color-sidebar: var(--sidebar-background);
93
+ --color-sidebar-foreground: var(--sidebar-foreground);
94
+ --color-sidebar-primary: var(--sidebar-primary);
95
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
96
+ --color-sidebar-accent: var(--sidebar-accent);
97
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
98
+ --color-sidebar-border: var(--sidebar-border);
99
+ --color-sidebar-ring: var(--sidebar-ring);
100
+
101
+ --radius-lg: var(--radius);
102
+ --radius-md: calc(var(--radius) - 2px);
103
+ --radius-sm: calc(var(--radius) - 4px);
104
+
105
+ --animate-accordion-down: accordion-down 0.2s ease-out;
106
+ --animate-accordion-up: accordion-up 0.2s ease-out;
107
+
108
+ @keyframes accordion-down {
109
+ from {
110
+ height: 0;
111
+ }
112
+
113
+ to {
114
+ height: var(--radix-accordion-content-height);
115
+ }
116
+ }
117
+
118
+ @keyframes accordion-up {
119
+ from {
120
+ height: var(--radix-accordion-content-height);
121
+ }
122
+
123
+ to {
124
+ height: 0;
125
+ }
126
+ }
127
+ }
128
+
129
+ .dark {
130
+ --background: hsl(240 10% 3.9%);
131
+ --foreground: hsl(0 0% 98%);
132
+
133
+ --card: hsl(240 10% 3.9% / 0.7);
134
+ --card-foreground: hsl(0 0% 98%);
135
+
136
+ --popover: hsl(240 10% 3.9% / 0.7);
137
+ --popover-foreground: hsl(0 0% 98%);
138
+
139
+ --primary: hsl(0 0% 98%);
140
+ --primary-foreground: hsl(240 5.9% 10%);
141
+
142
+ --secondary: hsl(240 3.7% 15.9% / 0.7);
143
+ --secondary-foreground: hsl(0 0% 98%);
144
+
145
+ --muted: hsl(240 3.7% 15.9% / 0.7);
146
+ --muted-foreground: hsl(240 5% 64.9%);
147
+
148
+ --accent: hsl(240 3.7% 15.9% / 0.7);
149
+ --accent-foreground: hsl(0 0% 98%);
150
+
151
+ --destructive: hsl(0 62.8% 30.6%);
152
+ --destructive-foreground: hsl(0 0% 98%);
153
+
154
+ --success: hsl(142.1 70.6% 45.3%);
155
+ --success-foreground: hsl(0 0% 100%);
156
+
157
+ --warning: hsl(48 96.5% 53.1%);
158
+ --warning-foreground: hsl(0 0% 98%);
159
+
160
+ --info: hsl(199.4 95.5% 53.8%);
161
+ --info-foreground: hsl(0 0% 98%);
162
+
163
+ --border: hsl(240 3.7% 15.9% / 0.5);
164
+ --input: hsl(240 3.7% 15.9% / 0.5);
165
+ --ring: hsl(240 4.9% 83.9%);
166
+
167
+ --sidebar-background: hsl(240 10% 3.9% / 0.7);
168
+ --sidebar-foreground: hsl(240 4.8% 95.9%);
169
+ --sidebar-primary: hsl(224.3 76.3% 48%);
170
+ --sidebar-primary-foreground: hsl(0 0% 100%);
171
+ --sidebar-accent: hsl(240 3.7% 15.9%);
172
+ --sidebar-accent-foreground: hsl(240 4.8% 95.9%);
173
+ --sidebar-border: hsl(240 3.7% 15.9% / 0.5);
174
+ --sidebar-ring: hsl(217.2 91.2% 59.8%);
175
+ }
176
+
177
+ body {
178
+ background: var(--background);
179
+ color: var(--foreground);
180
+ font-family: Arial, Helvetica, sans-serif;
181
+ }
@@ -1,5 +1,7 @@
1
1
  import * as React from "react"
2
+ import { createPortal } from "react-dom"
2
3
  import { cn } from "@/lib/utils"
4
+ import { Slot } from "@/components/ui/slot"
3
5
 
4
6
  interface AlertDialogContextValue {
5
7
  open: boolean
@@ -63,17 +65,12 @@ const AlertDialogTrigger = React.forwardRef<HTMLButtonElement, AlertDialogTrigge
63
65
  context.onOpenChange(true)
64
66
  }
65
67
 
66
- if (asChild && React.isValidElement(children)) {
67
- return React.cloneElement(children as React.ReactElement<any>, {
68
- onClick: handleClick,
69
- ref,
70
- })
71
- }
68
+ const Comp = asChild ? Slot : "button"
72
69
 
73
70
  return (
74
- <button ref={ref} onClick={handleClick} {...props}>
71
+ <Comp ref={ref} onClick={handleClick} {...props}>
75
72
  {children}
76
- </button>
73
+ </Comp>
77
74
  )
78
75
  }
79
76
  )
@@ -95,9 +92,16 @@ const AlertDialogContent = React.forwardRef<
95
92
  }
96
93
  }, [context.open])
97
94
 
95
+ const [mounted, setMounted] = React.useState(false)
96
+
97
+ React.useEffect(() => {
98
+ setMounted(true)
99
+ }, [])
100
+
98
101
  if (!context.open) return null
102
+ if (!mounted) return null
99
103
 
100
- return (
104
+ return createPortal(
101
105
  <>
102
106
  <div className="fixed inset-0 z-50 bg-black/80" />
103
107
  <div
@@ -112,7 +116,8 @@ const AlertDialogContent = React.forwardRef<
112
116
  >
113
117
  {children}
114
118
  </div>
115
- </>
119
+ </>,
120
+ document.body
116
121
  )
117
122
  })
118
123
  AlertDialogContent.displayName = "AlertDialogContent"
@@ -1,5 +1,6 @@
1
1
  import * as React from "react"
2
2
  import { cn } from "@/lib/utils"
3
+ import { Slot } from "@/components/ui/slot"
3
4
 
4
5
  /**
5
6
  * Breadcrumb navigation component
@@ -59,21 +60,16 @@ interface BreadcrumbLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorEleme
59
60
 
60
61
  const BreadcrumbLink = React.forwardRef<HTMLAnchorElement, BreadcrumbLinkProps>(
61
62
  ({ asChild, className, children, ...props }, ref) => {
62
- if (asChild && React.isValidElement(children)) {
63
- return React.cloneElement(children as React.ReactElement<any>, {
64
- ref,
65
- className: cn("transition-colors hover:text-foreground", className),
66
- })
67
- }
63
+ const Comp = asChild ? Slot : "a"
68
64
 
69
65
  return (
70
- <a
66
+ <Comp
71
67
  ref={ref}
72
68
  className={cn("transition-colors hover:text-foreground", className)}
73
69
  {...props}
74
70
  >
75
71
  {children}
76
- </a>
72
+ </Comp>
77
73
  )
78
74
  }
79
75
  )
@@ -50,24 +50,17 @@ interface ButtonProps
50
50
  * <LoadingSpinner /> Processing...
51
51
  * </Button>
52
52
  */
53
- const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
54
- ({ className, variant, size, asChild = false, children, ...props }, ref) => {
55
- if (asChild && React.isValidElement(children)) {
56
- return React.cloneElement(children as React.ReactElement<any>, {
57
- ref,
58
- className: cn(buttonVariants({ variant, size }), className),
59
- ...props,
60
- })
61
- }
53
+ import { Slot } from "@/components/ui/slot"
62
54
 
55
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
56
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
57
+ const Comp = asChild ? Slot : "button"
63
58
  return (
64
- <button
65
- ref={ref}
59
+ <Comp
66
60
  className={cn(buttonVariants({ variant, size }), className)}
61
+ ref={ref}
67
62
  {...props}
68
- >
69
- {children}
70
- </button>
63
+ />
71
64
  )
72
65
  }
73
66
  )
@@ -1,5 +1,6 @@
1
1
  import * as React from "react"
2
2
  import { cn } from "@/lib/utils"
3
+ import { Slot } from "@/components/ui/slot"
3
4
 
4
5
  interface CollapsibleContextValue {
5
6
  open: boolean
@@ -59,16 +60,10 @@ const CollapsibleTrigger = React.forwardRef<HTMLButtonElement, CollapsibleTrigge
59
60
  context.onOpenChange(!context.open)
60
61
  }
61
62
 
62
- if (asChild && React.isValidElement(children)) {
63
- return React.cloneElement(children as React.ReactElement<any>, {
64
- onClick: handleClick,
65
- "aria-expanded": context.open,
66
- ref,
67
- })
68
- }
63
+ const Comp = asChild ? Slot : "button"
69
64
 
70
65
  return (
71
- <button
66
+ <Comp
72
67
  ref={ref}
73
68
  type="button"
74
69
  aria-expanded={context.open}
@@ -77,7 +72,7 @@ const CollapsibleTrigger = React.forwardRef<HTMLButtonElement, CollapsibleTrigge
77
72
  {...props}
78
73
  >
79
74
  {children}
80
- </button>
75
+ </Comp>
81
76
  )
82
77
  }
83
78
  )