@gunjo/ui 0.0.1-alpha.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.
- package/README.md +129 -0
- package/design/atoms-metadata.json +82 -0
- package/design/molecules-metadata.json +130 -0
- package/design/organisms-metadata.json +38 -0
- package/design/templates-metadata.json +38 -0
- package/package.json +158 -0
- package/src/components/atoms/Alert.tsx +63 -0
- package/src/components/atoms/Avatar.tsx +57 -0
- package/src/components/atoms/Badge.tsx +30 -0
- package/src/components/atoms/Button.tsx +29 -0
- package/src/components/atoms/ButtonVariants.ts +37 -0
- package/src/components/atoms/Checkbox.tsx +52 -0
- package/src/components/atoms/Img.tsx +102 -0
- package/src/components/atoms/Input.tsx +37 -0
- package/src/components/atoms/Kbd.tsx +22 -0
- package/src/components/atoms/Label.tsx +22 -0
- package/src/components/atoms/Progress.tsx +38 -0
- package/src/components/atoms/RadioGroup.tsx +86 -0
- package/src/components/atoms/Select.tsx +28 -0
- package/src/components/atoms/Separator.tsx +33 -0
- package/src/components/atoms/Skeleton.tsx +36 -0
- package/src/components/atoms/Slider.tsx +26 -0
- package/src/components/atoms/Spinner.tsx +34 -0
- package/src/components/atoms/Switch.tsx +47 -0
- package/src/components/atoms/Textarea.tsx +34 -0
- package/src/components/atoms/ToggleGroup.tsx +60 -0
- package/src/components/atoms/ToolPill.tsx +77 -0
- package/src/components/atoms/generated/default-variant-keys.ts +36 -0
- package/src/components/atoms/generated/variant-keys.ts +61 -0
- package/src/components/generated/component-manifest.ts +741 -0
- package/src/components/generated/component-style-hints.ts +1262 -0
- package/src/components/molecules/AIChatInput.tsx +140 -0
- package/src/components/molecules/AIChatMessage.tsx +109 -0
- package/src/components/molecules/Accordion.tsx +99 -0
- package/src/components/molecules/Breadcrumb.tsx +115 -0
- package/src/components/molecules/Calendar.tsx +60 -0
- package/src/components/molecules/Card.tsx +78 -0
- package/src/components/molecules/Carousel.tsx +261 -0
- package/src/components/molecules/Command.tsx +152 -0
- package/src/components/molecules/ContextMenu.tsx +200 -0
- package/src/components/molecules/Dialog.tsx +122 -0
- package/src/components/molecules/DropdownMenu.tsx +200 -0
- package/src/components/molecules/FilterButton.tsx +133 -0
- package/src/components/molecules/Form.tsx +90 -0
- package/src/components/molecules/HoverCard.tsx +29 -0
- package/src/components/molecules/List.tsx +120 -0
- package/src/components/molecules/Menubar.tsx +231 -0
- package/src/components/molecules/Modal.tsx +66 -0
- package/src/components/molecules/NotificationCenter.tsx +118 -0
- package/src/components/molecules/Pagination.tsx +118 -0
- package/src/components/molecules/Popover.tsx +31 -0
- package/src/components/molecules/ProgressWidget.tsx +40 -0
- package/src/components/molecules/Resizable.tsx +47 -0
- package/src/components/molecules/ScrollArea.tsx +48 -0
- package/src/components/molecules/Sheet.tsx +140 -0
- package/src/components/molecules/SidebarItem.tsx +134 -0
- package/src/components/molecules/SortButton.tsx +56 -0
- package/src/components/molecules/StatusBar.tsx +41 -0
- package/src/components/molecules/Stepper.tsx +108 -0
- package/src/components/molecules/Table.tsx +117 -0
- package/src/components/molecules/Tabs.tsx +64 -0
- package/src/components/molecules/Toast.tsx +57 -0
- package/src/components/molecules/Tooltip.tsx +30 -0
- package/src/components/molecules/generated/default-variant-keys.ts +22 -0
- package/src/components/molecules/generated/variant-keys.ts +33 -0
- package/src/components/organisms/AppRail.tsx +28 -0
- package/src/components/organisms/CommandPalette.tsx +58 -0
- package/src/components/organisms/FileUploader.tsx +151 -0
- package/src/components/organisms/FloatingPanel.tsx +46 -0
- package/src/components/organisms/InspectorPanel.tsx +65 -0
- package/src/components/organisms/RightRail.tsx +29 -0
- package/src/components/organisms/ShareModal.tsx +182 -0
- package/src/components/organisms/SpatialCanvas.tsx +36 -0
- package/src/components/organisms/ToastProvider.tsx +49 -0
- package/src/components/templates/AuthTemplate.tsx +58 -0
- package/src/components/templates/BannalyzeTemplate.tsx +55 -0
- package/src/components/templates/ChatTemplate.tsx +55 -0
- package/src/components/templates/DashboardTemplate.tsx +34 -0
- package/src/components/templates/EditorTemplate.tsx +46 -0
- package/src/components/templates/KanbanTemplate.tsx +38 -0
- package/src/components/templates/LandingTemplate.tsx +53 -0
- package/src/components/templates/MediaLibraryTemplate.tsx +55 -0
- package/src/components/templates/SettingsTemplate.tsx +48 -0
- package/src/globals.css +108 -0
- package/src/index.ts +89 -0
- package/src/lib/utils.ts +6 -0
- package/tailwind-preset.js +11 -0
- package/tailwind-theme-extend.cjs +86 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
import type { AvatarVariantKey } from "./generated/variant-keys"
|
|
8
|
+
import { avatarDefaultVariantKey } from "./generated/default-variant-keys"
|
|
9
|
+
|
|
10
|
+
const avatarSlotClasses: Record<AvatarVariantKey, string> = {
|
|
11
|
+
fallback: "flex h-full w-full items-center justify-center rounded-full bg-secondary text-sm font-medium text-muted-foreground",
|
|
12
|
+
image: "aspect-square h-full w-full",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Avatar = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof AvatarPrimitive.Root>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
|
18
|
+
>(({ className, ...props }, ref) => (
|
|
19
|
+
<AvatarPrimitive.Root
|
|
20
|
+
ref={ref}
|
|
21
|
+
className={cn(
|
|
22
|
+
"relative inline-flex h-10 w-10 shrink-0 items-center overflow-hidden rounded-full",
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
))
|
|
28
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName
|
|
29
|
+
|
|
30
|
+
const AvatarImage = React.forwardRef<
|
|
31
|
+
React.ElementRef<typeof AvatarPrimitive.Image>,
|
|
32
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
33
|
+
>(({ className, ...props }, ref) => (
|
|
34
|
+
<AvatarPrimitive.Image
|
|
35
|
+
ref={ref}
|
|
36
|
+
className={cn(avatarSlotClasses.image, className)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
))
|
|
40
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName
|
|
41
|
+
|
|
42
|
+
const AvatarFallback = React.forwardRef<
|
|
43
|
+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
44
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
45
|
+
>(({ className, ...props }, ref) => (
|
|
46
|
+
<AvatarPrimitive.Fallback
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cn(
|
|
49
|
+
avatarSlotClasses[avatarDefaultVariantKey],
|
|
50
|
+
className
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
))
|
|
55
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
|
56
|
+
|
|
57
|
+
export { Avatar, AvatarImage, AvatarFallback }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "../../lib/utils"
|
|
3
|
+
import type { BadgeVariantKey } from "./generated/variant-keys"
|
|
4
|
+
import { badgeDefaultVariantKey } from "./generated/default-variant-keys"
|
|
5
|
+
|
|
6
|
+
const badgeVariantClasses: Record<BadgeVariantKey, string> = {
|
|
7
|
+
default: "bg-foreground text-primary-foreground hover:bg-foreground/80",
|
|
8
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
9
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
10
|
+
outline: "border-border bg-transparent text-foreground",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
14
|
+
variant?: BadgeVariantKey
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function Badge({ className, variant = badgeDefaultVariantKey, ...props }: BadgeProps) {
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
className={cn(
|
|
21
|
+
"inline-flex items-center w-fit h-5 rounded-full border border-transparent px-2.5 py-1 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
22
|
+
badgeVariantClasses[variant],
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { Badge }
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
5
|
+
import { type VariantProps } from "class-variance-authority"
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
import { buttonVariants } from "./ButtonVariants"
|
|
8
|
+
|
|
9
|
+
export interface ButtonProps
|
|
10
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
11
|
+
VariantProps<typeof buttonVariants> {
|
|
12
|
+
asChild?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
16
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
17
|
+
const Comp = asChild ? Slot : "button"
|
|
18
|
+
return (
|
|
19
|
+
<Comp
|
|
20
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
21
|
+
ref={ref}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
Button.displayName = "Button"
|
|
28
|
+
|
|
29
|
+
export { Button }
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
+
import type { ButtonVariantKey } from "./generated/variant-keys"
|
|
3
|
+
import { buttonDefaultVariantKey } from "./generated/default-variant-keys"
|
|
4
|
+
|
|
5
|
+
const buttonVariantClasses: Record<ButtonVariantKey, string> = {
|
|
6
|
+
default:
|
|
7
|
+
"bg-foreground text-primary-foreground shadow hover:bg-foreground/90",
|
|
8
|
+
destructive:
|
|
9
|
+
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
10
|
+
outline:
|
|
11
|
+
"border border-border bg-transparent shadow-sm hover:bg-muted hover:text-foreground",
|
|
12
|
+
secondary:
|
|
13
|
+
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
14
|
+
ghost: "bg-transparent hover:bg-muted hover:text-foreground",
|
|
15
|
+
link: "bg-transparent text-foreground underline underline-offset-4 hover:underline",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const buttonVariants = cva(
|
|
19
|
+
"inline-flex items-center justify-center w-fit gap-2 rounded-lg rounded-[var(--radius)] text-sm font-medium transition-colors duration-150 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
20
|
+
{
|
|
21
|
+
variants: {
|
|
22
|
+
variant: buttonVariantClasses,
|
|
23
|
+
size: {
|
|
24
|
+
default: "h-9 px-4 py-2",
|
|
25
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
26
|
+
lg: "h-10 rounded-md px-8",
|
|
27
|
+
icon: "h-9 w-9",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: buttonDefaultVariantKey,
|
|
32
|
+
size: "default",
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
export type ButtonVariantsProps = VariantProps<typeof buttonVariants>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Check } from "lucide-react"
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
import type { CheckboxVariantKey } from "./generated/variant-keys"
|
|
7
|
+
import { checkboxDefaultVariantKey } from "./generated/default-variant-keys"
|
|
8
|
+
|
|
9
|
+
const checkboxStateClasses: Record<CheckboxVariantKey, string> = {
|
|
10
|
+
checked: "border-transparent bg-foreground text-primary-foreground text-xs font-semibold",
|
|
11
|
+
disabled: "bg-transparent disabled:bg-muted disabled:opacity-50",
|
|
12
|
+
unchecked: "bg-transparent",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface CheckboxProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
16
|
+
checked?: boolean
|
|
17
|
+
onCheckedChange?: (checked: boolean) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(
|
|
21
|
+
({ className, checked, onCheckedChange, disabled, ...props }, ref) => {
|
|
22
|
+
const checkboxState: CheckboxVariantKey = disabled
|
|
23
|
+
? "disabled"
|
|
24
|
+
: checked
|
|
25
|
+
? "checked"
|
|
26
|
+
: checkboxDefaultVariantKey
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
role="checkbox"
|
|
32
|
+
aria-checked={checked}
|
|
33
|
+
onClick={() => onCheckedChange?.(!checked)}
|
|
34
|
+
ref={ref}
|
|
35
|
+
className={cn(
|
|
36
|
+
"peer inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-[4px] border border-input bg-transparent ring-offset-background focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed",
|
|
37
|
+
checkboxStateClasses[checkboxState],
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
disabled={disabled}
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
{checked && (
|
|
44
|
+
<Check className="h-3 w-3" strokeWidth={3} />
|
|
45
|
+
)}
|
|
46
|
+
</button>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
Checkbox.displayName = "Checkbox"
|
|
51
|
+
|
|
52
|
+
export { Checkbox }
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
import { Image as ImageIcon } from "lucide-react"
|
|
6
|
+
import type { ImgVariantKey } from "./generated/variant-keys"
|
|
7
|
+
import { imgDefaultVariantKey } from "./generated/default-variant-keys"
|
|
8
|
+
|
|
9
|
+
const imgOpacityClasses: Record<ImgVariantKey, string> = {
|
|
10
|
+
error: "opacity-100",
|
|
11
|
+
loaded: "opacity-100",
|
|
12
|
+
loading: "opacity-0",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const imgVariantClasses: Record<ImgVariantKey, string> = {
|
|
16
|
+
error: "inline-flex items-center w-[256px] h-[256px] gap-2 rounded-lg text-xs font-normal",
|
|
17
|
+
loaded: "w-[256px] h-[256px] rounded-lg",
|
|
18
|
+
loading: "w-[256px] h-[256px] rounded-lg",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ImgProps extends React.ImgHTMLAttributes<HTMLImageElement> {
|
|
22
|
+
fallback?: React.ReactNode
|
|
23
|
+
aspectRatio?: "square" | "video" | "auto" | "portrait" | "wide"
|
|
24
|
+
objectFit?: "cover" | "contain" | "fill" | "none" | "scale-down"
|
|
25
|
+
showSkeleton?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const Img = React.forwardRef<HTMLImageElement, ImgProps>(
|
|
29
|
+
({ className, src, alt, fallback, aspectRatio = "auto", objectFit = "cover", showSkeleton = true, onLoad, onError, ...props }, ref) => {
|
|
30
|
+
const [status, setStatus] = React.useState<ImgVariantKey>(imgDefaultVariantKey)
|
|
31
|
+
|
|
32
|
+
const handleLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {
|
|
33
|
+
setStatus("loaded")
|
|
34
|
+
onLoad?.(e)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handleError = (e: React.SyntheticEvent<HTMLImageElement>) => {
|
|
38
|
+
setStatus("error")
|
|
39
|
+
onError?.(e)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Reset status if src changes
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
setStatus(imgDefaultVariantKey)
|
|
45
|
+
}, [src])
|
|
46
|
+
|
|
47
|
+
const aspectRatioClass = {
|
|
48
|
+
"square": "aspect-square",
|
|
49
|
+
"video": "aspect-video",
|
|
50
|
+
"portrait": "aspect-[3/4]",
|
|
51
|
+
"wide": "aspect-[21/9]",
|
|
52
|
+
"auto": "aspect-auto"
|
|
53
|
+
}[aspectRatio]
|
|
54
|
+
|
|
55
|
+
const objectFitClass = {
|
|
56
|
+
"cover": "object-cover",
|
|
57
|
+
"contain": "object-contain",
|
|
58
|
+
"fill": "object-fill",
|
|
59
|
+
"none": "object-none",
|
|
60
|
+
"scale-down": "object-scale-down"
|
|
61
|
+
}[objectFit]
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className={cn("relative overflow-hidden bg-secondary", imgVariantClasses[status], aspectRatioClass, className)}>
|
|
65
|
+
{showSkeleton && status === "loading" && (
|
|
66
|
+
<div className="absolute inset-0 z-10 flex animate-pulse items-center justify-center bg-secondary">
|
|
67
|
+
{/* Optional spinner or just pulse */}
|
|
68
|
+
{/* <Loader2 className="w-6 h-6 text-muted-foreground animate-spin" /> */}
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
|
|
72
|
+
{status === "error" ? (
|
|
73
|
+
<div className="absolute inset-0 inline-flex flex-col items-center justify-center bg-secondary p-4 text-center text-muted-foreground">
|
|
74
|
+
{fallback || (
|
|
75
|
+
<>
|
|
76
|
+
<ImageIcon className="mb-2 h-8 w-8 opacity-80" />
|
|
77
|
+
<span className="text-xs">Image not found</span>
|
|
78
|
+
</>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
) : (
|
|
82
|
+
<img
|
|
83
|
+
ref={ref}
|
|
84
|
+
src={src}
|
|
85
|
+
alt={alt}
|
|
86
|
+
onLoad={handleLoad}
|
|
87
|
+
onError={handleError}
|
|
88
|
+
className={cn(
|
|
89
|
+
"w-full h-full transition-opacity duration-300",
|
|
90
|
+
objectFitClass,
|
|
91
|
+
imgOpacityClasses[status]
|
|
92
|
+
)}
|
|
93
|
+
{...props}
|
|
94
|
+
/>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
Img.displayName = "Img"
|
|
101
|
+
|
|
102
|
+
export { Img }
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "../../lib/utils"
|
|
3
|
+
import type { InputVariantKey } from "./generated/variant-keys"
|
|
4
|
+
import { inputDefaultVariantKey } from "./generated/default-variant-keys"
|
|
5
|
+
|
|
6
|
+
const inputVariantClasses: Record<InputVariantKey, string> = {
|
|
7
|
+
default: "",
|
|
8
|
+
disabled: "disabled:bg-muted disabled:text-muted-foreground disabled:opacity-50",
|
|
9
|
+
placeholder: "placeholder:text-muted-foreground",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { }
|
|
13
|
+
|
|
14
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
15
|
+
({ className, type, disabled, ...props }, ref) => {
|
|
16
|
+
const inputState: InputVariantKey = disabled ? "disabled" : inputDefaultVariantKey
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<input
|
|
20
|
+
type={type}
|
|
21
|
+
className={cn(
|
|
22
|
+
"inline-flex h-9 w-[280px] max-w-full items-center rounded-lg border border-input bg-transparent px-3 py-2 text-sm font-normal shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed",
|
|
23
|
+
inputVariantClasses[inputState],
|
|
24
|
+
inputVariantClasses.placeholder,
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
ref={ref}
|
|
28
|
+
suppressHydrationWarning
|
|
29
|
+
disabled={disabled}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
Input.displayName = "Input"
|
|
36
|
+
|
|
37
|
+
export { Input }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "../../lib/utils"
|
|
3
|
+
|
|
4
|
+
export interface KbdProps extends React.HTMLAttributes<HTMLSpanElement> { }
|
|
5
|
+
|
|
6
|
+
const Kbd = React.forwardRef<HTMLSpanElement, KbdProps>(
|
|
7
|
+
({ className, ...props }, ref) => {
|
|
8
|
+
return (
|
|
9
|
+
<kbd
|
|
10
|
+
className={cn(
|
|
11
|
+
"pointer-events-none inline-flex h-5 w-fit select-none items-center gap-1 rounded-[4px] border border-input bg-secondary py-1 px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100",
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
ref={ref}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
Kbd.displayName = "Kbd"
|
|
21
|
+
|
|
22
|
+
export { Kbd }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
|
|
6
|
+
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> { }
|
|
7
|
+
|
|
8
|
+
const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
|
|
9
|
+
({ className, ...props }, ref) => (
|
|
10
|
+
<label
|
|
11
|
+
ref={ref}
|
|
12
|
+
className={cn(
|
|
13
|
+
"text-sm font-medium leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
)
|
|
20
|
+
Label.displayName = "Label"
|
|
21
|
+
|
|
22
|
+
export { Label }
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
|
|
6
|
+
export interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
value?: number
|
|
8
|
+
max?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
|
|
12
|
+
({ className, value = 0, max = 100, ...props }, ref) => {
|
|
13
|
+
const percentage = Math.min(100, Math.max(0, ((value || 0) / max) * 100))
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div
|
|
17
|
+
ref={ref}
|
|
18
|
+
role="progressbar"
|
|
19
|
+
aria-valuemin={0}
|
|
20
|
+
aria-valuemax={max}
|
|
21
|
+
aria-valuenow={value}
|
|
22
|
+
className={cn(
|
|
23
|
+
"relative h-4 w-[200px] overflow-hidden rounded-full bg-secondary",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
<div
|
|
29
|
+
className="h-full w-full flex-1 bg-foreground transition-all"
|
|
30
|
+
style={{ transform: `translateX(-${100 - percentage}%)` }}
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
Progress.displayName = "Progress"
|
|
37
|
+
|
|
38
|
+
export { Progress }
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
import type { RadioGroupVariantKey } from "./generated/variant-keys"
|
|
6
|
+
import { radioGroupDefaultVariantKey } from "./generated/default-variant-keys"
|
|
7
|
+
|
|
8
|
+
// Simple Context-less Radio for batch 1, or Context-based?
|
|
9
|
+
// Let's go Simple but composable.
|
|
10
|
+
// Actually standard standard is RadioGroup + RadioGroupItem.
|
|
11
|
+
|
|
12
|
+
const RadioGroupContext = React.createContext<{
|
|
13
|
+
value?: string,
|
|
14
|
+
onValueChange?: (value: string) => void,
|
|
15
|
+
name?: string
|
|
16
|
+
} | undefined>(undefined);
|
|
17
|
+
|
|
18
|
+
const radioGroupItemStateClasses: Record<RadioGroupVariantKey, string> = {
|
|
19
|
+
checked: "border-foreground bg-transparent",
|
|
20
|
+
unchecked: "border-input bg-transparent",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RadioGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
24
|
+
value?: string
|
|
25
|
+
defaultValue?: string
|
|
26
|
+
onValueChange?: (value: string) => void
|
|
27
|
+
name?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
|
|
31
|
+
({ className, value, onValueChange, defaultValue, name, children, ...props }, ref) => {
|
|
32
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue)
|
|
33
|
+
const activeValue = value !== undefined ? value : internalValue
|
|
34
|
+
|
|
35
|
+
const handleValueChange = (v: string) => {
|
|
36
|
+
if (value === undefined) setInternalValue(v);
|
|
37
|
+
onValueChange?.(v);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<RadioGroupContext.Provider value={{ value: activeValue, onValueChange: handleValueChange, name }}>
|
|
42
|
+
<div className={cn("grid gap-2", className)} ref={ref} {...props}>
|
|
43
|
+
{children}
|
|
44
|
+
</div>
|
|
45
|
+
</RadioGroupContext.Provider>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
RadioGroup.displayName = "RadioGroup"
|
|
50
|
+
|
|
51
|
+
export interface RadioGroupItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
52
|
+
value: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const RadioGroupItem = React.forwardRef<HTMLButtonElement, RadioGroupItemProps>(
|
|
56
|
+
({ className, value, ...props }, ref) => {
|
|
57
|
+
const context = React.useContext(RadioGroupContext)
|
|
58
|
+
const checked = context?.value === value
|
|
59
|
+
const itemState: RadioGroupVariantKey = checked ? "checked" : radioGroupDefaultVariantKey
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<button
|
|
63
|
+
type="button"
|
|
64
|
+
role="radio"
|
|
65
|
+
aria-checked={checked}
|
|
66
|
+
onClick={() => context?.onValueChange?.(value)}
|
|
67
|
+
ref={ref}
|
|
68
|
+
className={cn(
|
|
69
|
+
"aspect-square h-4 w-4 rounded-lg border text-foreground ring-offset-background focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
70
|
+
radioGroupItemStateClasses[itemState],
|
|
71
|
+
className
|
|
72
|
+
)}
|
|
73
|
+
{...props}
|
|
74
|
+
>
|
|
75
|
+
{checked && (
|
|
76
|
+
<div className="flex items-center justify-center">
|
|
77
|
+
<div className="h-2.5 w-2.5 rounded-full bg-foreground" />
|
|
78
|
+
</div>
|
|
79
|
+
)}
|
|
80
|
+
</button>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
RadioGroupItem.displayName = "RadioGroupItem"
|
|
85
|
+
|
|
86
|
+
export { RadioGroup, RadioGroupItem }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "../../lib/utils"
|
|
3
|
+
import { ChevronDown } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> { }
|
|
6
|
+
|
|
7
|
+
const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
|
|
8
|
+
({ className, children, ...props }, ref) => {
|
|
9
|
+
return (
|
|
10
|
+
<div className="relative">
|
|
11
|
+
<select
|
|
12
|
+
className={cn(
|
|
13
|
+
"inline-flex h-10 w-[200px] appearance-none items-center justify-between rounded-lg border border-input bg-transparent px-3 py-2 text-sm font-normal text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground disabled:opacity-50",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
ref={ref}
|
|
17
|
+
{...props}
|
|
18
|
+
>
|
|
19
|
+
{children}
|
|
20
|
+
</select>
|
|
21
|
+
<ChevronDown className="pointer-events-none absolute right-3 top-3 h-4 w-4 text-muted-foreground" />
|
|
22
|
+
</div>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
Select.displayName = "Select"
|
|
27
|
+
|
|
28
|
+
export { Select }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
import type { SeparatorVariantKey } from "./generated/variant-keys"
|
|
6
|
+
import { separatorDefaultVariantKey } from "./generated/default-variant-keys"
|
|
7
|
+
|
|
8
|
+
const separatorOrientationClasses: Record<SeparatorVariantKey, string> = {
|
|
9
|
+
horizontal: "w-[200px] h-[1px]",
|
|
10
|
+
vertical: "w-[1px] h-6",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
14
|
+
orientation?: SeparatorVariantKey
|
|
15
|
+
className?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(
|
|
19
|
+
({ className, orientation = separatorDefaultVariantKey, ...props }, ref) => (
|
|
20
|
+
<div
|
|
21
|
+
ref={ref}
|
|
22
|
+
className={cn(
|
|
23
|
+
"shrink-0 bg-border",
|
|
24
|
+
separatorOrientationClasses[orientation],
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
Separator.displayName = "Separator"
|
|
32
|
+
|
|
33
|
+
export { Separator }
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "../../lib/utils"
|
|
4
|
+
|
|
5
|
+
const skeletonVariants = cva(
|
|
6
|
+
"animate-pulse rounded-md bg-muted",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
shape: {
|
|
10
|
+
rectangle: "h-4 w-[200px]",
|
|
11
|
+
circle: "h-10 w-10 rounded-full",
|
|
12
|
+
text: "h-3.5 w-[160px] rounded-full",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
shape: "rectangle",
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
export interface SkeletonProps
|
|
22
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
23
|
+
VariantProps<typeof skeletonVariants> {}
|
|
24
|
+
|
|
25
|
+
const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(
|
|
26
|
+
({ className, shape, ...props }, ref) => (
|
|
27
|
+
<div
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn(skeletonVariants({ shape }), className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
Skeleton.displayName = "Skeleton"
|
|
35
|
+
|
|
36
|
+
export { Skeleton, skeletonVariants }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
|
|
6
|
+
export interface SliderProps extends React.InputHTMLAttributes<HTMLInputElement> { }
|
|
7
|
+
|
|
8
|
+
const Slider = React.forwardRef<HTMLInputElement, SliderProps>(
|
|
9
|
+
({ className, ...props }, ref) => (
|
|
10
|
+
<div className="relative flex w-[200px] touch-none select-none items-center">
|
|
11
|
+
<input
|
|
12
|
+
type="range"
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn(
|
|
15
|
+
"h-5 w-full cursor-pointer appearance-none rounded-[10px] bg-input disabled:cursor-not-allowed disabled:opacity-50",
|
|
16
|
+
"[&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-foreground [&::-webkit-slider-thumb]:bg-background [&::-webkit-slider-thumb]:transition-colors [&::-webkit-slider-thumb]:focus-visible:outline-none [&::-webkit-slider-thumb]:focus-visible:ring-1 [&::-webkit-slider-thumb]:focus-visible:ring-ring [&::-webkit-slider-thumb]:focus-visible:ring-offset-1 [&::-webkit-slider-thumb]:focus-visible:ring-offset-background [&::-webkit-slider-thumb]:disabled:pointer-events-none [&::-webkit-slider-thumb]:disabled:opacity-50",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
Slider.displayName = "Slider"
|
|
25
|
+
|
|
26
|
+
export { Slider }
|