Upload 3 files
Browse files- components/ui/button.tsx +58 -0
- components/ui/select.tsx +181 -0
- components/ui/slider.tsx +63 -0
components/ui/button.tsx
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react"
|
| 2 |
+
import { Slot } from "@radix-ui/react-slot"
|
| 3 |
+
import { cva, type VariantProps } from "class-variance-authority"
|
| 4 |
+
|
| 5 |
+
import { cn } from "@/lib/utils"
|
| 6 |
+
|
| 7 |
+
const buttonVariants = cva(
|
| 8 |
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
| 9 |
+
{
|
| 10 |
+
variants: {
|
| 11 |
+
variant: {
|
| 12 |
+
default:
|
| 13 |
+
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
| 14 |
+
destructive:
|
| 15 |
+
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
|
| 16 |
+
outline:
|
| 17 |
+
"border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
|
| 18 |
+
secondary:
|
| 19 |
+
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
| 20 |
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
| 21 |
+
link: "text-primary underline-offset-4 hover:underline",
|
| 22 |
+
},
|
| 23 |
+
size: {
|
| 24 |
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
| 25 |
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
| 26 |
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
| 27 |
+
icon: "size-9",
|
| 28 |
+
},
|
| 29 |
+
},
|
| 30 |
+
defaultVariants: {
|
| 31 |
+
variant: "default",
|
| 32 |
+
size: "default",
|
| 33 |
+
},
|
| 34 |
+
}
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
function Button({
|
| 38 |
+
className,
|
| 39 |
+
variant,
|
| 40 |
+
size,
|
| 41 |
+
asChild = false,
|
| 42 |
+
...props
|
| 43 |
+
}: React.ComponentProps<"button"> &
|
| 44 |
+
VariantProps<typeof buttonVariants> & {
|
| 45 |
+
asChild?: boolean
|
| 46 |
+
}) {
|
| 47 |
+
const Comp = asChild ? Slot : "button"
|
| 48 |
+
|
| 49 |
+
return (
|
| 50 |
+
<Comp
|
| 51 |
+
data-slot="button"
|
| 52 |
+
className={cn(buttonVariants({ variant, size, className }))}
|
| 53 |
+
{...props}
|
| 54 |
+
/>
|
| 55 |
+
)
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
export { Button, buttonVariants }
|
components/ui/select.tsx
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client"
|
| 2 |
+
|
| 3 |
+
import * as React from "react"
|
| 4 |
+
import * as SelectPrimitive from "@radix-ui/react-select"
|
| 5 |
+
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
| 6 |
+
|
| 7 |
+
import { cn } from "@/lib/utils"
|
| 8 |
+
|
| 9 |
+
function Select({
|
| 10 |
+
...props
|
| 11 |
+
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
| 12 |
+
return <SelectPrimitive.Root data-slot="select" {...props} />
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
function SelectGroup({
|
| 16 |
+
...props
|
| 17 |
+
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
| 18 |
+
return <SelectPrimitive.Group data-slot="select-group" {...props} />
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
function SelectValue({
|
| 22 |
+
...props
|
| 23 |
+
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
| 24 |
+
return <SelectPrimitive.Value data-slot="select-value" {...props} />
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
function SelectTrigger({
|
| 28 |
+
className,
|
| 29 |
+
children,
|
| 30 |
+
...props
|
| 31 |
+
}: React.ComponentProps<typeof SelectPrimitive.Trigger>) {
|
| 32 |
+
return (
|
| 33 |
+
<SelectPrimitive.Trigger
|
| 34 |
+
data-slot="select-trigger"
|
| 35 |
+
className={cn(
|
| 36 |
+
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex h-9 w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
| 37 |
+
className
|
| 38 |
+
)}
|
| 39 |
+
{...props}
|
| 40 |
+
>
|
| 41 |
+
{children}
|
| 42 |
+
<SelectPrimitive.Icon asChild>
|
| 43 |
+
<ChevronDownIcon className="size-4 opacity-50" />
|
| 44 |
+
</SelectPrimitive.Icon>
|
| 45 |
+
</SelectPrimitive.Trigger>
|
| 46 |
+
)
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
function SelectContent({
|
| 50 |
+
className,
|
| 51 |
+
children,
|
| 52 |
+
position = "popper",
|
| 53 |
+
...props
|
| 54 |
+
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
| 55 |
+
return (
|
| 56 |
+
<SelectPrimitive.Portal>
|
| 57 |
+
<SelectPrimitive.Content
|
| 58 |
+
data-slot="select-content"
|
| 59 |
+
className={cn(
|
| 60 |
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
| 61 |
+
position === "popper" &&
|
| 62 |
+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
| 63 |
+
className
|
| 64 |
+
)}
|
| 65 |
+
position={position}
|
| 66 |
+
{...props}
|
| 67 |
+
>
|
| 68 |
+
<SelectScrollUpButton />
|
| 69 |
+
<SelectPrimitive.Viewport
|
| 70 |
+
className={cn(
|
| 71 |
+
"p-1",
|
| 72 |
+
position === "popper" &&
|
| 73 |
+
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
| 74 |
+
)}
|
| 75 |
+
>
|
| 76 |
+
{children}
|
| 77 |
+
</SelectPrimitive.Viewport>
|
| 78 |
+
<SelectScrollDownButton />
|
| 79 |
+
</SelectPrimitive.Content>
|
| 80 |
+
</SelectPrimitive.Portal>
|
| 81 |
+
)
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
function SelectLabel({
|
| 85 |
+
className,
|
| 86 |
+
...props
|
| 87 |
+
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
|
| 88 |
+
return (
|
| 89 |
+
<SelectPrimitive.Label
|
| 90 |
+
data-slot="select-label"
|
| 91 |
+
className={cn("px-2 py-1.5 text-sm font-medium", className)}
|
| 92 |
+
{...props}
|
| 93 |
+
/>
|
| 94 |
+
)
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
function SelectItem({
|
| 98 |
+
className,
|
| 99 |
+
children,
|
| 100 |
+
...props
|
| 101 |
+
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
| 102 |
+
return (
|
| 103 |
+
<SelectPrimitive.Item
|
| 104 |
+
data-slot="select-item"
|
| 105 |
+
className={cn(
|
| 106 |
+
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
| 107 |
+
className
|
| 108 |
+
)}
|
| 109 |
+
{...props}
|
| 110 |
+
>
|
| 111 |
+
<span className="absolute right-2 flex size-3.5 items-center justify-center">
|
| 112 |
+
<SelectPrimitive.ItemIndicator>
|
| 113 |
+
<CheckIcon className="size-4" />
|
| 114 |
+
</SelectPrimitive.ItemIndicator>
|
| 115 |
+
</span>
|
| 116 |
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
| 117 |
+
</SelectPrimitive.Item>
|
| 118 |
+
)
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
function SelectSeparator({
|
| 122 |
+
className,
|
| 123 |
+
...props
|
| 124 |
+
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
|
| 125 |
+
return (
|
| 126 |
+
<SelectPrimitive.Separator
|
| 127 |
+
data-slot="select-separator"
|
| 128 |
+
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
|
| 129 |
+
{...props}
|
| 130 |
+
/>
|
| 131 |
+
)
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
function SelectScrollUpButton({
|
| 135 |
+
className,
|
| 136 |
+
...props
|
| 137 |
+
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
| 138 |
+
return (
|
| 139 |
+
<SelectPrimitive.ScrollUpButton
|
| 140 |
+
data-slot="select-scroll-up-button"
|
| 141 |
+
className={cn(
|
| 142 |
+
"flex cursor-default items-center justify-center py-1",
|
| 143 |
+
className
|
| 144 |
+
)}
|
| 145 |
+
{...props}
|
| 146 |
+
>
|
| 147 |
+
<ChevronUpIcon className="size-4" />
|
| 148 |
+
</SelectPrimitive.ScrollUpButton>
|
| 149 |
+
)
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
function SelectScrollDownButton({
|
| 153 |
+
className,
|
| 154 |
+
...props
|
| 155 |
+
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
| 156 |
+
return (
|
| 157 |
+
<SelectPrimitive.ScrollDownButton
|
| 158 |
+
data-slot="select-scroll-down-button"
|
| 159 |
+
className={cn(
|
| 160 |
+
"flex cursor-default items-center justify-center py-1",
|
| 161 |
+
className
|
| 162 |
+
)}
|
| 163 |
+
{...props}
|
| 164 |
+
>
|
| 165 |
+
<ChevronDownIcon className="size-4" />
|
| 166 |
+
</SelectPrimitive.ScrollDownButton>
|
| 167 |
+
)
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
export {
|
| 171 |
+
Select,
|
| 172 |
+
SelectContent,
|
| 173 |
+
SelectGroup,
|
| 174 |
+
SelectItem,
|
| 175 |
+
SelectLabel,
|
| 176 |
+
SelectScrollDownButton,
|
| 177 |
+
SelectScrollUpButton,
|
| 178 |
+
SelectSeparator,
|
| 179 |
+
SelectTrigger,
|
| 180 |
+
SelectValue,
|
| 181 |
+
}
|
components/ui/slider.tsx
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client"
|
| 2 |
+
|
| 3 |
+
import * as React from "react"
|
| 4 |
+
import * as SliderPrimitive from "@radix-ui/react-slider"
|
| 5 |
+
|
| 6 |
+
import { cn } from "@/lib/utils"
|
| 7 |
+
|
| 8 |
+
function Slider({
|
| 9 |
+
className,
|
| 10 |
+
defaultValue,
|
| 11 |
+
value,
|
| 12 |
+
min = 0,
|
| 13 |
+
max = 100,
|
| 14 |
+
...props
|
| 15 |
+
}: React.ComponentProps<typeof SliderPrimitive.Root>) {
|
| 16 |
+
const _values = React.useMemo(
|
| 17 |
+
() =>
|
| 18 |
+
Array.isArray(value)
|
| 19 |
+
? value
|
| 20 |
+
: Array.isArray(defaultValue)
|
| 21 |
+
? defaultValue
|
| 22 |
+
: [min, max],
|
| 23 |
+
[value, defaultValue, min, max]
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
return (
|
| 27 |
+
<SliderPrimitive.Root
|
| 28 |
+
data-slot="slider"
|
| 29 |
+
defaultValue={defaultValue}
|
| 30 |
+
value={value}
|
| 31 |
+
min={min}
|
| 32 |
+
max={max}
|
| 33 |
+
className={cn(
|
| 34 |
+
"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
| 35 |
+
className
|
| 36 |
+
)}
|
| 37 |
+
{...props}
|
| 38 |
+
>
|
| 39 |
+
<SliderPrimitive.Track
|
| 40 |
+
data-slot="slider-track"
|
| 41 |
+
className={cn(
|
| 42 |
+
"bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
|
| 43 |
+
)}
|
| 44 |
+
>
|
| 45 |
+
<SliderPrimitive.Range
|
| 46 |
+
data-slot="slider-range"
|
| 47 |
+
className={cn(
|
| 48 |
+
"bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
|
| 49 |
+
)}
|
| 50 |
+
/>
|
| 51 |
+
</SliderPrimitive.Track>
|
| 52 |
+
{Array.from({ length: _values.length }, (_, index) => (
|
| 53 |
+
<SliderPrimitive.Thumb
|
| 54 |
+
data-slot="slider-thumb"
|
| 55 |
+
key={index}
|
| 56 |
+
className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
| 57 |
+
/>
|
| 58 |
+
))}
|
| 59 |
+
</SliderPrimitive.Root>
|
| 60 |
+
)
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
export { Slider }
|