[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5',
+ className,
+ )}
+ {...props}
+ />
+ );
+}
+
+const inputGroupAddonVariants = cva(
+ "text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4 flex cursor-text items-center justify-center select-none",
+ {
+ variants: {
+ align: {
+ 'inline-start':
+ 'pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem] order-first',
+ 'inline-end':
+ 'pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem] order-last',
+ 'block-start':
+ 'px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2 order-first w-full justify-start',
+ 'block-end':
+ 'px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2 order-last w-full justify-start',
+ },
+ },
+ defaultVariants: {
+ align: 'inline-start',
+ },
+ },
+);
+
+function InputGroupAddon({
+ className,
+ align = 'inline-start',
+ ...props
+}: React.ComponentProps<'div'> & VariantProps
) {
+ return (
+ {
+ if ((e.target as HTMLElement).closest('button')) {
+ return;
+ }
+ e.currentTarget.parentElement?.querySelector('input')?.focus();
+ }}
+ {...props}
+ />
+ );
+}
+
+const inputGroupButtonVariants = cva(
+ 'gap-2 text-sm shadow-none flex items-center',
+ {
+ variants: {
+ size: {
+ xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
+ sm: '',
+ 'icon-xs':
+ 'size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0',
+ 'icon-sm': 'size-8 p-0 has-[>svg]:p-0',
+ },
+ },
+ defaultVariants: {
+ size: 'xs',
+ },
+ },
+);
+
+function InputGroupButton({
+ className,
+ type = 'button',
+ variant = 'ghost',
+ size = 'xs',
+ ...props
+}: Omit, 'size' | 'type'> &
+ VariantProps & {
+ type?: 'button' | 'submit' | 'reset';
+ }) {
+ return (
+
+ );
+}
+
+function InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {
+ return (
+
+ );
+}
+
+function InputGroupInput({
+ className,
+ ...props
+}: React.ComponentProps<'input'>) {
+ return (
+
+ );
+}
+
+function InputGroupTextarea({
+ className,
+ ...props
+}: React.ComponentProps<'textarea'>) {
+ return (
+
+ );
+}
+
+export {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupText,
+ InputGroupInput,
+ InputGroupTextarea,
+};
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
new file mode 100644
index 0000000..fe8561c
--- /dev/null
+++ b/src/components/ui/input.tsx
@@ -0,0 +1,20 @@
+import * as React from 'react';
+import { Input as InputPrimitive } from '@base-ui/react/input';
+
+import { cn } from '@/lib/utils';
+
+function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
+ return (
+
+ );
+}
+
+export { Input };
diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx
new file mode 100644
index 0000000..6d13a34
--- /dev/null
+++ b/src/components/ui/label.tsx
@@ -0,0 +1,20 @@
+'use client';
+
+import * as React from 'react';
+
+import { cn } from '@/lib/utils';
+
+function Label({ className, ...props }: React.ComponentProps<'label'>) {
+ return (
+
+ );
+}
+
+export { Label };
diff --git a/components/ui/select.tsx b/src/components/ui/select.tsx
similarity index 52%
rename from components/ui/select.tsx
rename to src/components/ui/select.tsx
index 396b930..7de21e7 100644
--- a/components/ui/select.tsx
+++ b/src/components/ui/select.tsx
@@ -1,74 +1,74 @@
-"use client"
+'use client';
-import * as React from "react"
-import { Select as SelectPrimitive } from "@base-ui/react/select"
+import * as React from 'react';
+import { Select as SelectPrimitive } from '@base-ui/react/select';
-import { cn } from "@/lib/utils"
-import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
+import { cn } from '@/lib/utils';
+import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from 'lucide-react';
-const Select = SelectPrimitive.Root
+const Select = SelectPrimitive.Root;
function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
return (
- )
+ );
}
function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
return (
- )
+ );
}
function SelectTrigger({
className,
- size = "default",
+ size = 'default',
children,
...props
}: SelectPrimitive.Trigger.Props & {
- size?: "sm" | "default"
+ size?: 'sm' | 'default';
}) {
return (
{children}
+
}
/>
- )
+ );
}
function SelectContent({
className,
children,
- side = "bottom",
+ side = 'bottom',
sideOffset = 4,
- align = "center",
+ align = 'center',
alignOffset = 0,
alignItemWithTrigger = true,
...props
}: SelectPrimitive.Popup.Props &
Pick<
SelectPrimitive.Positioner.Props,
- "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger"
+ 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'
>) {
return (
@@ -83,7 +83,10 @@ function SelectContent({
@@ -92,7 +95,7 @@ function SelectContent({
- )
+ );
}
function SelectLabel({
@@ -102,10 +105,10 @@ function SelectLabel({
return (
- )
+ );
}
function SelectItem({
@@ -117,21 +120,23 @@ function SelectItem({
-
+
{children}
}
+ render={
+
+ }
>
- )
+ );
}
function SelectSeparator({
@@ -141,10 +146,10 @@ function SelectSeparator({
return (
- )
+ );
}
function SelectScrollUpButton({
@@ -154,13 +159,15 @@ function SelectScrollUpButton({
return (
-
+
- )
+ );
}
function SelectScrollDownButton({
@@ -170,13 +177,15 @@ function SelectScrollDownButton({
return (
-
+
- )
+ );
}
export {
@@ -190,4 +199,4 @@ export {
SelectSeparator,
SelectTrigger,
SelectValue,
-}
+};
diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx
new file mode 100644
index 0000000..eb86fcc
--- /dev/null
+++ b/src/components/ui/separator.tsx
@@ -0,0 +1,25 @@
+'use client';
+
+import { Separator as SeparatorPrimitive } from '@base-ui/react/separator';
+
+import { cn } from '@/lib/utils';
+
+function Separator({
+ className,
+ orientation = 'horizontal',
+ ...props
+}: SeparatorPrimitive.Props) {
+ return (
+
+ );
+}
+
+export { Separator };
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..35314cd
--- /dev/null
+++ b/src/components/ui/textarea.tsx
@@ -0,0 +1,18 @@
+import * as React from 'react';
+
+import { cn } from '@/lib/utils';
+
+function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
+ return (
+
+ );
+}
+
+export { Textarea };
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000..2819a83
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/tsconfig.json b/tsconfig.json
index 3a13f90..678d64d 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -19,7 +19,10 @@
}
],
"paths": {
- "@/*": ["./*"]
+ "@/*": ["./src/*"],
+ "@components/*": ["./src/components/*"],
+ "@ui/*": ["./src/components/ui/*"],
+ "@lib/*": ["./src/lib/*"]
}
},
"include": [