create-better-t-stack 3.8.3-pr730.1784e47 → 3.9.0-pr730.0ee9844

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 (60) hide show
  1. package/README.md +17 -16
  2. package/dist/cli.mjs +1 -1
  3. package/dist/index.mjs +1 -1
  4. package/dist/{src-GZpht_dQ.mjs → src-DW15ZRe9.mjs} +70 -89
  5. package/package.json +39 -40
  6. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +21 -19
  7. package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +6 -2
  8. package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +11 -6
  9. package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +2 -2
  10. package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +23 -20
  11. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +13 -5
  12. package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +25 -22
  13. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +23 -24
  14. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +4 -4
  15. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +0 -1
  16. package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +24 -21
  17. package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +24 -21
  18. package/templates/auth/better-auth/web/react/{tanstack-start/src/components/user-menu.tsx → tanstack-router/src/components/user-menu.tsx.hbs} +26 -23
  19. package/templates/auth/better-auth/web/react/{tanstack-router/src/components/user-menu.tsx → tanstack-start/src/components/user-menu.tsx.hbs} +26 -23
  20. package/templates/frontend/react/next/package.json.hbs +8 -7
  21. package/templates/frontend/react/next/src/app/layout.tsx.hbs +28 -1
  22. package/templates/frontend/react/next/src/components/providers.tsx.hbs +14 -4
  23. package/templates/frontend/react/react-router/package.json.hbs +2 -1
  24. package/templates/frontend/react/{tanstack-router/src/components/mode-toggle.tsx → react-router/src/components/mode-toggle.tsx.hbs} +1 -1
  25. package/templates/frontend/react/tanstack-router/package.json.hbs +2 -1
  26. package/templates/frontend/react/{react-router/src/components/mode-toggle.tsx → tanstack-router/src/components/mode-toggle.tsx.hbs} +1 -1
  27. package/templates/frontend/react/tanstack-start/package.json.hbs +2 -1
  28. package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +6 -0
  29. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +13 -14
  30. package/templates/frontend/react/tanstack-start/vite.config.ts.hbs +5 -0
  31. package/templates/frontend/react/web-base/components.json +5 -2
  32. package/templates/frontend/react/web-base/src/components/ui/button.tsx.hbs +57 -0
  33. package/templates/frontend/react/web-base/src/components/ui/card.tsx.hbs +103 -0
  34. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx.hbs +26 -0
  35. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx.hbs +262 -0
  36. package/templates/frontend/react/web-base/src/components/ui/input.tsx.hbs +20 -0
  37. package/templates/frontend/react/web-base/src/components/ui/label.tsx.hbs +20 -0
  38. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +13 -0
  39. package/templates/frontend/react/web-base/src/components/ui/sonner.tsx.hbs +44 -0
  40. package/templates/frontend/react/web-base/src/index.css.hbs +57 -63
  41. package/templates/frontend/react/web-base/src/components/ui/button.tsx +0 -56
  42. package/templates/frontend/react/web-base/src/components/ui/card.tsx +0 -75
  43. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx +0 -27
  44. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx +0 -228
  45. package/templates/frontend/react/web-base/src/components/ui/input.tsx +0 -21
  46. package/templates/frontend/react/web-base/src/components/ui/label.tsx +0 -19
  47. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx +0 -13
  48. package/templates/frontend/react/web-base/src/components/ui/sonner.tsx +0 -25
  49. /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  50. /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  51. /package/templates/auth/better-auth/web/react/tanstack-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  52. /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  53. /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  54. /package/templates/auth/better-auth/web/react/tanstack-start/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  55. /package/templates/auth/better-auth/web/solid/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  56. /package/templates/auth/better-auth/web/solid/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  57. /package/templates/auth/better-auth/web/solid/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  58. /package/templates/frontend/react/react-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  59. /package/templates/frontend/react/tanstack-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  60. /package/templates/frontend/react/web-base/src/lib/{utils.ts → utils.ts.hbs} +0 -0
@@ -6,7 +6,7 @@ import { useAuth } from "@clerk/nextjs";
6
6
  import { ConvexReactClient } from "convex/react";
7
7
  import { ConvexProviderWithClerk } from "convex/react-clerk";
8
8
  {{else if (eq auth "better-auth")}}
9
- import { ConvexProvider, ConvexReactClient } from "convex/react";
9
+ import { ConvexReactClient } from "convex/react";
10
10
  import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
11
11
  import { authClient } from "@/lib/auth-client";
12
12
  {{else}}
@@ -32,9 +32,15 @@ const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
32
32
  {{/if}}
33
33
 
34
34
  export default function Providers({
35
- children
35
+ children,
36
+ {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
37
+ initialToken,
38
+ {{/if}}
36
39
  }: {
37
- children: React.ReactNode
40
+ children: React.ReactNode;
41
+ {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
42
+ initialToken?: string | null;
43
+ {{/if}}
38
44
  }) {
39
45
  return (
40
46
  <ThemeProvider
@@ -49,7 +55,11 @@ export default function Providers({
49
55
  {children}
50
56
  </ConvexProviderWithClerk>
51
57
  {{else if (eq auth "better-auth")}}
52
- <ConvexBetterAuthProvider client={convex} authClient={authClient}>
58
+ <ConvexBetterAuthProvider
59
+ client={convex}
60
+ authClient={authClient}
61
+ initialToken={initialToken}
62
+ >
53
63
  {children}
54
64
  </ConvexBetterAuthProvider>
55
65
  {{else}}
@@ -9,7 +9,8 @@
9
9
  "typecheck": "react-router typegen && tsc"
10
10
  },
11
11
  "dependencies": {
12
- "radix-ui": "^1.4.2",
12
+ "@base-ui/react": "^1.0.0",
13
+ "shadcn": "^3.6.2",
13
14
  "@react-router/fs-routes": "^7.10.1",
14
15
  "@react-router/node": "^7.10.1",
15
16
  "@react-router/serve": "^7.10.1",
@@ -14,7 +14,7 @@ export function ModeToggle() {
14
14
 
15
15
  return (
16
16
  <DropdownMenu>
17
- <DropdownMenuTrigger asChild>
17
+ <DropdownMenuTrigger>
18
18
  <Button variant="outline" size="icon">
19
19
  <Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
20
20
  <Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@hookform/resolvers": "^5.1.1",
15
- "radix-ui": "^1.4.2",
15
+ "@base-ui/react": "^1.0.0",
16
+ "shadcn": "^3.6.2",
16
17
  "@tanstack/react-form": "^1.12.3",
17
18
  "@tailwindcss/vite": "^4.0.15",
18
19
  "@tanstack/react-router": "^1.141.1",
@@ -14,7 +14,7 @@ export function ModeToggle() {
14
14
 
15
15
  return (
16
16
  <DropdownMenu>
17
- <DropdownMenuTrigger asChild>
17
+ <DropdownMenuTrigger>
18
18
  <Button variant="outline" size="icon">
19
19
  <Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
20
20
  <Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
@@ -8,7 +8,8 @@
8
8
  "dev": "vite dev"
9
9
  },
10
10
  "dependencies": {
11
- "radix-ui": "^1.4.2",
11
+ "@base-ui/react": "^1.0.0",
12
+ "shadcn": "^3.6.2",
12
13
  "@tanstack/react-form": "^1.23.5",
13
14
  "@tailwindcss/vite": "^4.1.8",
14
15
  "@tanstack/react-query": "^5.80.6",
@@ -35,7 +35,13 @@ export function getRouter() {
35
35
  unsavedChangesWarning: false,
36
36
  });
37
37
 
38
+ {{#if (eq auth "better-auth")}}
39
+ const convexQueryClient = new ConvexQueryClient(convex, {
40
+ expectAuth: true,
41
+ });
42
+ {{else}}
38
43
  const convexQueryClient = new ConvexQueryClient(convex);
44
+ {{/if}}
39
45
 
40
46
  const queryClient: QueryClient = new QueryClient({
41
47
  defaultOptions: {
@@ -37,20 +37,12 @@ const fetchClerkAuth = createServerFn({ method: "GET" }).handler(async () => {
37
37
  });
38
38
  {{else if (and (eq backend "convex") (eq auth "better-auth"))}}
39
39
  import { createServerFn } from "@tanstack/react-start";
40
- import { getRequest, getCookie } from "@tanstack/react-start/server";
41
40
  import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
42
- import { fetchSession, getCookieName } from "@convex-dev/better-auth/react-start";
43
41
  import { authClient } from "@/lib/auth-client";
44
- import { createAuth } from "@{{projectName}}/backend/convex/auth";
42
+ import { getToken } from "@/lib/auth-server";
45
43
 
46
- const fetchAuth = createServerFn({ method: "GET" }).handler(async () => {
47
- const { session } = await fetchSession(getRequest());
48
- const sessionCookieName = getCookieName(createAuth);
49
- const token = getCookie(sessionCookieName);
50
- return {
51
- userId: session?.user.id,
52
- token,
53
- };
44
+ const getAuth = createServerFn({ method: "GET" }).handler(async () => {
45
+ return await getToken();
54
46
  });
55
47
  {{/if}}
56
48
 
@@ -113,11 +105,14 @@ export const Route = createRootRouteWithContext<RouterAppContext>()({
113
105
  },
114
106
  {{else if (and (eq backend "convex") (eq auth "better-auth"))}}
115
107
  beforeLoad: async (ctx) => {
116
- const { userId, token } = await fetchAuth();
108
+ const token = await getAuth();
117
109
  if (token) {
118
110
  ctx.context.convexQueryClient.serverHttpClient?.setAuth(token);
119
111
  }
120
- return { userId, token };
112
+ return {
113
+ isAuthenticated: !!token,
114
+ token,
115
+ };
121
116
  },
122
117
  {{/if}}
123
118
  });
@@ -148,7 +143,11 @@ function RootDocument() {
148
143
  {{else if (and (eq backend "convex") (eq auth "better-auth"))}}
149
144
  const context = useRouteContext({ from: Route.id });
150
145
  return (
151
- <ConvexBetterAuthProvider client={context.convexClient} authClient={authClient}>
146
+ <ConvexBetterAuthProvider
147
+ client={context.convexClient}
148
+ authClient={authClient}
149
+ initialToken={context.token}
150
+ >
152
151
  <html lang="en" className="dark">
153
152
  <head>
154
153
  <HeadContent />
@@ -14,4 +14,9 @@ export default defineConfig({
14
14
  server: {
15
15
  port: 3001,
16
16
  },
17
+ {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
18
+ ssr: {
19
+ noExternal: ["@convex-dev/better-auth"],
20
+ },
21
+ {{/if}}
17
22
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "new-york",
3
+ "style": "base-lyra",
4
4
  "rsc": false,
5
5
  "tsx": true,
6
6
  "tailwind": {
@@ -10,6 +10,7 @@
10
10
  "cssVariables": true,
11
11
  "prefix": ""
12
12
  },
13
+ "iconLibrary": "lucide",
13
14
  "aliases": {
14
15
  "components": "@/components",
15
16
  "utils": "@/lib/utils",
@@ -17,5 +18,7 @@
17
18
  "lib": "@/lib",
18
19
  "hooks": "@/hooks"
19
20
  },
20
- "iconLibrary": "lucide"
21
+ "menuColor": "default",
22
+ "menuAccent": "subtle",
23
+ "registries": {}
21
24
  }
@@ -0,0 +1,57 @@
1
+ import { Button as ButtonPrimitive } from '@base-ui/react/button'
2
+ import { cva } from 'class-variance-authority'
3
+ import type {VariantProps} from 'class-variance-authority';
4
+
5
+ import { cn } from '@/lib/utils'
6
+
7
+ const buttonVariants = cva(
8
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-none border border-transparent bg-clip-padding text-xs font-medium focus-visible:ring-1 aria-invalid:ring-1 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
13
+ outline:
14
+ 'border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground',
15
+ secondary:
16
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
17
+ ghost:
18
+ 'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground',
19
+ destructive:
20
+ 'bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30',
21
+ link: 'text-primary underline-offset-4 hover:underline',
22
+ },
23
+ size: {
24
+ default:
25
+ 'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
26
+ xs: "h-6 gap-1 rounded-none px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
27
+ sm: "h-7 gap-1 rounded-none px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
28
+ lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3',
29
+ icon: 'size-8',
30
+ 'icon-xs': "size-6 rounded-none [&_svg:not([class*='size-'])]:size-3",
31
+ 'icon-sm': 'size-7 rounded-none',
32
+ 'icon-lg': 'size-9',
33
+ },
34
+ },
35
+ defaultVariants: {
36
+ variant: 'default',
37
+ size: 'default',
38
+ },
39
+ },
40
+ )
41
+
42
+ function Button({
43
+ className,
44
+ variant = 'default',
45
+ size = 'default',
46
+ ...props
47
+ }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
48
+ return (
49
+ <ButtonPrimitive
50
+ data-slot="button"
51
+ className={cn(buttonVariants({ variant, size, className }))}
52
+ {...props}
53
+ />
54
+ )
55
+ }
56
+
57
+ export { Button, buttonVariants }
@@ -0,0 +1,103 @@
1
+ import * as React from 'react'
2
+
3
+ import { cn } from '@/lib/utils'
4
+
5
+ function Card({
6
+ className,
7
+ size = 'default',
8
+ ...props
9
+ }: React.ComponentProps<'div'> & { size?: 'default' | 'sm' }) {
10
+ return (
11
+ <div
12
+ data-slot="card"
13
+ data-size={size}
14
+ className={cn(
15
+ 'ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-none py-4 text-xs/relaxed ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-2 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-none *:[img:last-child]:rounded-none group/card flex flex-col',
16
+ className,
17
+ )}
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+
23
+ function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
24
+ return (
25
+ <div
26
+ data-slot="card-header"
27
+ className={cn(
28
+ 'gap-1 rounded-none px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]',
29
+ className,
30
+ )}
31
+ {...props}
32
+ />
33
+ )
34
+ }
35
+
36
+ function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
37
+ return (
38
+ <div
39
+ data-slot="card-title"
40
+ className={cn(
41
+ 'text-sm font-medium group-data-[size=sm]/card:text-sm',
42
+ className,
43
+ )}
44
+ {...props}
45
+ />
46
+ )
47
+ }
48
+
49
+ function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
50
+ return (
51
+ <div
52
+ data-slot="card-description"
53
+ className={cn('text-muted-foreground text-xs/relaxed', className)}
54
+ {...props}
55
+ />
56
+ )
57
+ }
58
+
59
+ function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
60
+ return (
61
+ <div
62
+ data-slot="card-action"
63
+ className={cn(
64
+ 'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
65
+ className,
66
+ )}
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
73
+ return (
74
+ <div
75
+ data-slot="card-content"
76
+ className={cn('px-4 group-data-[size=sm]/card:px-3', className)}
77
+ {...props}
78
+ />
79
+ )
80
+ }
81
+
82
+ function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
83
+ return (
84
+ <div
85
+ data-slot="card-footer"
86
+ className={cn(
87
+ 'rounded-none border-t p-4 group-data-[size=sm]/card:p-3 flex items-center',
88
+ className,
89
+ )}
90
+ {...props}
91
+ />
92
+ )
93
+ }
94
+
95
+ export {
96
+ Card,
97
+ CardHeader,
98
+ CardFooter,
99
+ CardTitle,
100
+ CardAction,
101
+ CardDescription,
102
+ CardContent,
103
+ }
@@ -0,0 +1,26 @@
1
+ import { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox'
2
+
3
+ import { CheckIcon } from 'lucide-react'
4
+ import { cn } from '@/lib/utils'
5
+
6
+ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
7
+ return (
8
+ <CheckboxPrimitive.Root
9
+ data-slot="checkbox"
10
+ className={cn(
11
+ 'border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-none border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-1 aria-invalid:ring-1 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50',
12
+ className,
13
+ )}
14
+ {...props}
15
+ >
16
+ <CheckboxPrimitive.Indicator
17
+ data-slot="checkbox-indicator"
18
+ className="[&>svg]:size-3.5 grid place-content-center text-current transition-none"
19
+ >
20
+ <CheckIcon />
21
+ </CheckboxPrimitive.Indicator>
22
+ </CheckboxPrimitive.Root>
23
+ )
24
+ }
25
+
26
+ export { Checkbox }
@@ -0,0 +1,262 @@
1
+ import * as React from 'react'
2
+ import { Menu as MenuPrimitive } from '@base-ui/react/menu'
3
+
4
+ import { CheckIcon, ChevronRightIcon } from 'lucide-react'
5
+ import { cn } from '@/lib/utils'
6
+
7
+ function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
8
+ return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
9
+ }
10
+
11
+ function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
12
+ return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
13
+ }
14
+
15
+ function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
16
+ return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
17
+ }
18
+
19
+ function DropdownMenuContent({
20
+ align = 'start',
21
+ alignOffset = 0,
22
+ side = 'bottom',
23
+ sideOffset = 4,
24
+ className,
25
+ ...props
26
+ }: MenuPrimitive.Popup.Props &
27
+ Pick<
28
+ MenuPrimitive.Positioner.Props,
29
+ 'align' | 'alignOffset' | 'side' | 'sideOffset'
30
+ >) {
31
+ return (
32
+ <MenuPrimitive.Portal>
33
+ <MenuPrimitive.Positioner
34
+ className="isolate z-50 outline-none"
35
+ align={align}
36
+ alignOffset={alignOffset}
37
+ side={side}
38
+ sideOffset={sideOffset}
39
+ >
40
+ <MenuPrimitive.Popup
41
+ data-slot="dropdown-menu-content"
42
+ className={cn(
43
+ 'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 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 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-none shadow-md ring-1 duration-100 z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden',
44
+ className,
45
+ )}
46
+ {...props}
47
+ />
48
+ </MenuPrimitive.Positioner>
49
+ </MenuPrimitive.Portal>
50
+ )
51
+ }
52
+
53
+ function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
54
+ return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
55
+ }
56
+
57
+ function DropdownMenuLabel({
58
+ className,
59
+ inset,
60
+ ...props
61
+ }: MenuPrimitive.GroupLabel.Props & {
62
+ inset?: boolean
63
+ }) {
64
+ return (
65
+ <MenuPrimitive.GroupLabel
66
+ data-slot="dropdown-menu-label"
67
+ data-inset={inset}
68
+ className={cn(
69
+ 'text-muted-foreground px-2 py-2 text-xs data-[inset]:pl-8',
70
+ className,
71
+ )}
72
+ {...props}
73
+ />
74
+ )
75
+ }
76
+
77
+ function DropdownMenuItem({
78
+ className,
79
+ inset,
80
+ variant = 'default',
81
+ ...props
82
+ }: MenuPrimitive.Item.Props & {
83
+ inset?: boolean
84
+ variant?: 'default' | 'destructive'
85
+ }) {
86
+ return (
87
+ <MenuPrimitive.Item
88
+ data-slot="dropdown-menu-item"
89
+ data-inset={inset}
90
+ data-variant={variant}
91
+ className={cn(
92
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-none px-2 py-2 text-xs [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
93
+ className,
94
+ )}
95
+ {...props}
96
+ />
97
+ )
98
+ }
99
+
100
+ function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
101
+ return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
102
+ }
103
+
104
+ function DropdownMenuSubTrigger({
105
+ className,
106
+ inset,
107
+ children,
108
+ ...props
109
+ }: MenuPrimitive.SubmenuTrigger.Props & {
110
+ inset?: boolean
111
+ }) {
112
+ return (
113
+ <MenuPrimitive.SubmenuTrigger
114
+ data-slot="dropdown-menu-sub-trigger"
115
+ data-inset={inset}
116
+ className={cn(
117
+ "focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-none px-2 py-2 text-xs [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
118
+ className,
119
+ )}
120
+ {...props}
121
+ >
122
+ {children}
123
+ <ChevronRightIcon className="ml-auto" />
124
+ </MenuPrimitive.SubmenuTrigger>
125
+ )
126
+ }
127
+
128
+ function DropdownMenuSubContent({
129
+ align = 'start',
130
+ alignOffset = -3,
131
+ side = 'right',
132
+ sideOffset = 0,
133
+ className,
134
+ ...props
135
+ }: React.ComponentProps<typeof DropdownMenuContent>) {
136
+ return (
137
+ <DropdownMenuContent
138
+ data-slot="dropdown-menu-sub-content"
139
+ className={cn(
140
+ 'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 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 ring-foreground/10 bg-popover text-popover-foreground min-w-[96px] rounded-none shadow-lg ring-1 duration-100 w-auto',
141
+ className,
142
+ )}
143
+ align={align}
144
+ alignOffset={alignOffset}
145
+ side={side}
146
+ sideOffset={sideOffset}
147
+ {...props}
148
+ />
149
+ )
150
+ }
151
+
152
+ function DropdownMenuCheckboxItem({
153
+ className,
154
+ children,
155
+ checked,
156
+ ...props
157
+ }: MenuPrimitive.CheckboxItem.Props) {
158
+ return (
159
+ <MenuPrimitive.CheckboxItem
160
+ data-slot="dropdown-menu-checkbox-item"
161
+ className={cn(
162
+ "focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2 rounded-none py-2 pr-8 pl-2 text-xs [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
163
+ className,
164
+ )}
165
+ checked={checked}
166
+ {...props}
167
+ >
168
+ <span
169
+ className="pointer-events-none absolute right-2 flex items-center justify-center pointer-events-none"
170
+ data-slot="dropdown-menu-checkbox-item-indicator"
171
+ >
172
+ <MenuPrimitive.CheckboxItemIndicator>
173
+ <CheckIcon />
174
+ </MenuPrimitive.CheckboxItemIndicator>
175
+ </span>
176
+ {children}
177
+ </MenuPrimitive.CheckboxItem>
178
+ )
179
+ }
180
+
181
+ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
182
+ return (
183
+ <MenuPrimitive.RadioGroup
184
+ data-slot="dropdown-menu-radio-group"
185
+ {...props}
186
+ />
187
+ )
188
+ }
189
+
190
+ function DropdownMenuRadioItem({
191
+ className,
192
+ children,
193
+ ...props
194
+ }: MenuPrimitive.RadioItem.Props) {
195
+ return (
196
+ <MenuPrimitive.RadioItem
197
+ data-slot="dropdown-menu-radio-item"
198
+ className={cn(
199
+ "focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2 rounded-none py-2 pr-8 pl-2 text-xs [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
200
+ className,
201
+ )}
202
+ {...props}
203
+ >
204
+ <span
205
+ className="pointer-events-none absolute right-2 flex items-center justify-center pointer-events-none"
206
+ data-slot="dropdown-menu-radio-item-indicator"
207
+ >
208
+ <MenuPrimitive.RadioItemIndicator>
209
+ <CheckIcon />
210
+ </MenuPrimitive.RadioItemIndicator>
211
+ </span>
212
+ {children}
213
+ </MenuPrimitive.RadioItem>
214
+ )
215
+ }
216
+
217
+ function DropdownMenuSeparator({
218
+ className,
219
+ ...props
220
+ }: MenuPrimitive.Separator.Props) {
221
+ return (
222
+ <MenuPrimitive.Separator
223
+ data-slot="dropdown-menu-separator"
224
+ className={cn('bg-border -mx-1 h-px', className)}
225
+ {...props}
226
+ />
227
+ )
228
+ }
229
+
230
+ function DropdownMenuShortcut({
231
+ className,
232
+ ...props
233
+ }: React.ComponentProps<'span'>) {
234
+ return (
235
+ <span
236
+ data-slot="dropdown-menu-shortcut"
237
+ className={cn(
238
+ 'text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest',
239
+ className,
240
+ )}
241
+ {...props}
242
+ />
243
+ )
244
+ }
245
+
246
+ export {
247
+ DropdownMenu,
248
+ DropdownMenuPortal,
249
+ DropdownMenuTrigger,
250
+ DropdownMenuContent,
251
+ DropdownMenuGroup,
252
+ DropdownMenuLabel,
253
+ DropdownMenuItem,
254
+ DropdownMenuCheckboxItem,
255
+ DropdownMenuRadioGroup,
256
+ DropdownMenuRadioItem,
257
+ DropdownMenuSeparator,
258
+ DropdownMenuShortcut,
259
+ DropdownMenuSub,
260
+ DropdownMenuSubTrigger,
261
+ DropdownMenuSubContent,
262
+ }
@@ -0,0 +1,20 @@
1
+ import * as React from 'react'
2
+ import { Input as InputPrimitive } from '@base-ui/react/input'
3
+
4
+ import { cn } from '@/lib/utils'
5
+
6
+ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
7
+ return (
8
+ <InputPrimitive
9
+ type={type}
10
+ data-slot="input"
11
+ className={cn(
12
+ 'dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-none border bg-transparent px-2.5 py-1 text-xs transition-colors file:h-6 file:text-xs file:font-medium focus-visible:ring-1 aria-invalid:ring-1 md:text-xs file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
13
+ className,
14
+ )}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ export { Input }