cooterlabs 1.0.2 → 2.0.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 +55 -11
- package/bin/index.js +327 -22
- package/package.json +12 -4
- package/templates/action-sheet/action-sheet.tsx +113 -0
- package/templates/alert/alert.tsx +96 -0
- package/templates/avatar/avatar.tsx +21 -4
- package/templates/badge/badge.tsx +106 -15
- package/templates/breadcrumb/breadcrumb.tsx +113 -0
- package/templates/button/button.tsx +40 -2
- package/templates/button-group/button-group.tsx +93 -0
- package/templates/card/card.tsx +31 -19
- package/templates/checkbox/checkbox.tsx +4 -3
- package/templates/context-menu/context-menu.tsx +177 -0
- package/templates/dialog/dialog.tsx +127 -0
- package/templates/dropdown/dropdown.tsx +180 -0
- package/templates/input/input.tsx +30 -3
- package/templates/label/label.tsx +2 -2
- package/templates/list/list.tsx +96 -0
- package/templates/loader/loader.tsx +38 -0
- package/templates/navbar-bottom/navbar-bottom.tsx +76 -0
- package/templates/navbar-top/navbar-top.tsx +84 -0
- package/templates/page-control/page-control.tsx +55 -0
- package/templates/pagination/pagination.tsx +109 -0
- package/templates/progress/progress.tsx +69 -0
- package/templates/radio/radio.tsx +41 -0
- package/templates/stepper/stepper.tsx +93 -0
- package/templates/tabs/tabs.tsx +57 -0
- package/templates/textarea/textarea.tsx +1 -1
- package/templates/toggle/toggle.tsx +27 -0
- package/templates/tooltip/tooltip.tsx +37 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { clsx, type ClassValue } from "clsx"
|
|
3
|
+
import { twMerge } from "tailwind-merge"
|
|
4
|
+
|
|
5
|
+
function cn(...inputs: ClassValue[]) {
|
|
6
|
+
return twMerge(clsx(inputs))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const NavbarBottom = React.forwardRef<
|
|
10
|
+
HTMLElement,
|
|
11
|
+
React.HTMLAttributes<HTMLElement>
|
|
12
|
+
>(({ className, ...props }, ref) => (
|
|
13
|
+
<nav
|
|
14
|
+
ref={ref}
|
|
15
|
+
// box-content keeps the tab row a full h-12 above the safe-area padding
|
|
16
|
+
className={cn(
|
|
17
|
+
"box-content flex h-12 items-stretch border-t border-grey-100 bg-background pb-[env(safe-area-inset-bottom)]",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
))
|
|
23
|
+
NavbarBottom.displayName = "NavbarBottom"
|
|
24
|
+
|
|
25
|
+
export interface NavbarBottomItemProps
|
|
26
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
27
|
+
label?: string
|
|
28
|
+
active?: boolean
|
|
29
|
+
badge?: boolean
|
|
30
|
+
// Figma Style="Line Selector + Icon": active top indicator line, no label
|
|
31
|
+
lineSelector?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const NavbarBottomItem = React.forwardRef<
|
|
35
|
+
HTMLButtonElement,
|
|
36
|
+
NavbarBottomItemProps
|
|
37
|
+
>(
|
|
38
|
+
(
|
|
39
|
+
{
|
|
40
|
+
className,
|
|
41
|
+
label,
|
|
42
|
+
active = false,
|
|
43
|
+
badge = false,
|
|
44
|
+
lineSelector = false,
|
|
45
|
+
children,
|
|
46
|
+
...props
|
|
47
|
+
},
|
|
48
|
+
ref
|
|
49
|
+
) => (
|
|
50
|
+
<button
|
|
51
|
+
ref={ref}
|
|
52
|
+
type="button"
|
|
53
|
+
aria-current={active ? "page" : undefined}
|
|
54
|
+
className={cn(
|
|
55
|
+
"relative flex flex-1 flex-col items-center justify-center gap-1 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:text-grey-400",
|
|
56
|
+
active ? "text-primary" : "text-grey-400 hover:bg-secondary",
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
{lineSelector && active && (
|
|
62
|
+
<span className="absolute inset-x-3 top-0 h-0.5 rounded-full bg-primary" />
|
|
63
|
+
)}
|
|
64
|
+
<span className="relative [&_svg]:size-6 [&_svg]:shrink-0">
|
|
65
|
+
{children}
|
|
66
|
+
{badge && (
|
|
67
|
+
<span className="absolute -right-1 -top-1 size-2 rounded-full bg-error-500" />
|
|
68
|
+
)}
|
|
69
|
+
</span>
|
|
70
|
+
{label && !lineSelector && <span className="text-c3">{label}</span>}
|
|
71
|
+
</button>
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
NavbarBottomItem.displayName = "NavbarBottomItem"
|
|
75
|
+
|
|
76
|
+
export { NavbarBottom, NavbarBottomItem }
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { ChevronLeft } from "lucide-react"
|
|
3
|
+
import { clsx, type ClassValue } from "clsx"
|
|
4
|
+
import { twMerge } from "tailwind-merge"
|
|
5
|
+
|
|
6
|
+
function cn(...inputs: ClassValue[]) {
|
|
7
|
+
return twMerge(clsx(inputs))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface NavbarTopProps extends React.HTMLAttributes<HTMLElement> {
|
|
11
|
+
bordered?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const NavbarTop = React.forwardRef<HTMLElement, NavbarTopProps>(
|
|
15
|
+
({ className, bordered = false, ...props }, ref) => (
|
|
16
|
+
<header
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={cn(
|
|
19
|
+
"relative flex h-11 items-center justify-between gap-2 bg-background px-4",
|
|
20
|
+
bordered && "border-b border-grey-100",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
NavbarTop.displayName = "NavbarTop"
|
|
28
|
+
|
|
29
|
+
const NavbarTopLeft = React.forwardRef<
|
|
30
|
+
HTMLButtonElement,
|
|
31
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
32
|
+
>(({ className, children, ...props }, ref) => (
|
|
33
|
+
<button
|
|
34
|
+
ref={ref}
|
|
35
|
+
type="button"
|
|
36
|
+
aria-label="Back"
|
|
37
|
+
className={cn(
|
|
38
|
+
"-ml-2 inline-flex items-center gap-1 rounded-xs p-2 text-b2 text-foreground transition-colors hover:bg-secondary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:text-grey-400 [&_svg]:size-6 [&_svg]:shrink-0",
|
|
39
|
+
className
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
{children ?? <ChevronLeft />}
|
|
44
|
+
</button>
|
|
45
|
+
))
|
|
46
|
+
NavbarTopLeft.displayName = "NavbarTopLeft"
|
|
47
|
+
|
|
48
|
+
export interface NavbarTopTitleProps
|
|
49
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
50
|
+
subtitle?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const NavbarTopTitle = React.forwardRef<HTMLDivElement, NavbarTopTitleProps>(
|
|
54
|
+
({ className, subtitle, children, ...props }, ref) => (
|
|
55
|
+
<div
|
|
56
|
+
ref={ref}
|
|
57
|
+
className={cn(
|
|
58
|
+
"pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-center text-s2 text-foreground",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
{subtitle && (
|
|
65
|
+
<div className="text-c1 text-muted-foreground">{subtitle}</div>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
NavbarTopTitle.displayName = "NavbarTopTitle"
|
|
71
|
+
|
|
72
|
+
const NavbarTopRight = React.forwardRef<
|
|
73
|
+
HTMLDivElement,
|
|
74
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
75
|
+
>(({ className, ...props }, ref) => (
|
|
76
|
+
<div
|
|
77
|
+
ref={ref}
|
|
78
|
+
className={cn("ml-auto flex items-center gap-2", className)}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
))
|
|
82
|
+
NavbarTopRight.displayName = "NavbarTopRight"
|
|
83
|
+
|
|
84
|
+
export { NavbarTop, NavbarTopLeft, NavbarTopTitle, NavbarTopRight }
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { clsx, type ClassValue } from "clsx"
|
|
3
|
+
import { twMerge } from "tailwind-merge"
|
|
4
|
+
|
|
5
|
+
export function cn(...inputs: ClassValue[]) {
|
|
6
|
+
return twMerge(clsx(inputs))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PageControlProps
|
|
10
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
11
|
+
count: number
|
|
12
|
+
index: number
|
|
13
|
+
onChange?: (index: number) => void
|
|
14
|
+
orientation?: "horizontal" | "vertical"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const PageControl = React.forwardRef<HTMLDivElement, PageControlProps>(
|
|
18
|
+
(
|
|
19
|
+
{ className, count, index, onChange, orientation = "horizontal", ...props },
|
|
20
|
+
ref
|
|
21
|
+
) => (
|
|
22
|
+
<div
|
|
23
|
+
ref={ref}
|
|
24
|
+
role="tablist"
|
|
25
|
+
aria-label="Page control"
|
|
26
|
+
aria-orientation={orientation}
|
|
27
|
+
className={cn(
|
|
28
|
+
"flex items-center gap-2 p-2",
|
|
29
|
+
orientation === "vertical" && "flex-col",
|
|
30
|
+
className
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
{Array.from({ length: count }, (_, i) => (
|
|
35
|
+
<button
|
|
36
|
+
key={i}
|
|
37
|
+
type="button"
|
|
38
|
+
role="tab"
|
|
39
|
+
aria-label={`Page ${i + 1}`}
|
|
40
|
+
aria-selected={i === index}
|
|
41
|
+
tabIndex={i === index ? 0 : -1}
|
|
42
|
+
disabled={!onChange}
|
|
43
|
+
onClick={() => onChange?.(i)}
|
|
44
|
+
className={cn(
|
|
45
|
+
"size-2 rounded-full ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none",
|
|
46
|
+
i === index ? "bg-primary" : "bg-grey-200 hover:bg-grey-300"
|
|
47
|
+
)}
|
|
48
|
+
/>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
PageControl.displayName = "PageControl"
|
|
54
|
+
|
|
55
|
+
export { PageControl }
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
|
|
3
|
+
import { clsx, type ClassValue } from "clsx"
|
|
4
|
+
import { twMerge } from "tailwind-merge"
|
|
5
|
+
|
|
6
|
+
export function cn(...inputs: ClassValue[]) {
|
|
7
|
+
return twMerge(clsx(inputs))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Pagination = ({
|
|
11
|
+
className,
|
|
12
|
+
...props
|
|
13
|
+
}: React.HTMLAttributes<HTMLElement>) => (
|
|
14
|
+
<nav
|
|
15
|
+
role="navigation"
|
|
16
|
+
aria-label="pagination"
|
|
17
|
+
className={cn("mx-auto flex w-full justify-center", className)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
Pagination.displayName = "Pagination"
|
|
22
|
+
|
|
23
|
+
const PaginationContent = React.forwardRef<
|
|
24
|
+
HTMLUListElement,
|
|
25
|
+
React.HTMLAttributes<HTMLUListElement>
|
|
26
|
+
>(({ className, ...props }, ref) => (
|
|
27
|
+
<ul
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn("flex flex-row items-center gap-1", className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
))
|
|
33
|
+
PaginationContent.displayName = "PaginationContent"
|
|
34
|
+
|
|
35
|
+
const PaginationItem = React.forwardRef<
|
|
36
|
+
HTMLLIElement,
|
|
37
|
+
React.LiHTMLAttributes<HTMLLIElement>
|
|
38
|
+
>(({ ...props }, ref) => <li ref={ref} {...props} />)
|
|
39
|
+
PaginationItem.displayName = "PaginationItem"
|
|
40
|
+
|
|
41
|
+
export interface PaginationLinkProps
|
|
42
|
+
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
43
|
+
isActive?: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const PaginationLink = React.forwardRef<HTMLAnchorElement, PaginationLinkProps>(
|
|
47
|
+
({ className, isActive = false, ...props }, ref) => (
|
|
48
|
+
<a
|
|
49
|
+
ref={ref}
|
|
50
|
+
aria-current={isActive ? "page" : undefined}
|
|
51
|
+
className={cn(
|
|
52
|
+
"inline-flex size-12 items-center justify-center whitespace-nowrap rounded-xs text-b4 ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 aria-disabled:pointer-events-none aria-disabled:text-grey-400 [&_svg]:size-4",
|
|
53
|
+
isActive
|
|
54
|
+
? "bg-primary text-primary-foreground"
|
|
55
|
+
: "text-foreground hover:bg-secondary",
|
|
56
|
+
className
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
/>
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
PaginationLink.displayName = "PaginationLink"
|
|
63
|
+
|
|
64
|
+
const PaginationPrevious = React.forwardRef<
|
|
65
|
+
HTMLAnchorElement,
|
|
66
|
+
PaginationLinkProps
|
|
67
|
+
>(({ ...props }, ref) => (
|
|
68
|
+
<PaginationLink ref={ref} aria-label="Go to previous page" {...props}>
|
|
69
|
+
<ChevronLeft />
|
|
70
|
+
</PaginationLink>
|
|
71
|
+
))
|
|
72
|
+
PaginationPrevious.displayName = "PaginationPrevious"
|
|
73
|
+
|
|
74
|
+
const PaginationNext = React.forwardRef<HTMLAnchorElement, PaginationLinkProps>(
|
|
75
|
+
({ ...props }, ref) => (
|
|
76
|
+
<PaginationLink ref={ref} aria-label="Go to next page" {...props}>
|
|
77
|
+
<ChevronRight />
|
|
78
|
+
</PaginationLink>
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
PaginationNext.displayName = "PaginationNext"
|
|
82
|
+
|
|
83
|
+
const PaginationEllipsis = ({
|
|
84
|
+
className,
|
|
85
|
+
...props
|
|
86
|
+
}: React.HTMLAttributes<HTMLSpanElement>) => (
|
|
87
|
+
<span
|
|
88
|
+
aria-hidden="true"
|
|
89
|
+
className={cn(
|
|
90
|
+
"flex size-12 items-center justify-center text-muted-foreground",
|
|
91
|
+
className
|
|
92
|
+
)}
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
<MoreHorizontal className="size-4" />
|
|
96
|
+
<span className="sr-only">More pages</span>
|
|
97
|
+
</span>
|
|
98
|
+
)
|
|
99
|
+
PaginationEllipsis.displayName = "PaginationEllipsis"
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
Pagination,
|
|
103
|
+
PaginationContent,
|
|
104
|
+
PaginationItem,
|
|
105
|
+
PaginationLink,
|
|
106
|
+
PaginationPrevious,
|
|
107
|
+
PaginationNext,
|
|
108
|
+
PaginationEllipsis,
|
|
109
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
|
3
|
+
import { clsx, type ClassValue } from "clsx"
|
|
4
|
+
import { twMerge } from "tailwind-merge"
|
|
5
|
+
|
|
6
|
+
export function cn(...inputs: ClassValue[]) {
|
|
7
|
+
return twMerge(clsx(inputs))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ProgressProps
|
|
11
|
+
extends React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> {
|
|
12
|
+
label?: "left" | "right"
|
|
13
|
+
disabled?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const Progress = React.forwardRef<
|
|
17
|
+
React.ElementRef<typeof ProgressPrimitive.Root>,
|
|
18
|
+
ProgressProps
|
|
19
|
+
>(({ className, value, label, disabled = false, ...props }, ref) => {
|
|
20
|
+
const percent = value ?? 0
|
|
21
|
+
const bar = (
|
|
22
|
+
<ProgressPrimitive.Root
|
|
23
|
+
ref={ref}
|
|
24
|
+
className={cn(
|
|
25
|
+
"relative h-2 w-full overflow-hidden rounded-full bg-grey-100",
|
|
26
|
+
!label && className
|
|
27
|
+
)}
|
|
28
|
+
value={value}
|
|
29
|
+
{...props}
|
|
30
|
+
>
|
|
31
|
+
<ProgressPrimitive.Indicator
|
|
32
|
+
className={cn(
|
|
33
|
+
"h-full w-full flex-1 rounded-full transition-all",
|
|
34
|
+
disabled ? "bg-grey-200" : "bg-primary"
|
|
35
|
+
)}
|
|
36
|
+
style={{ transform: `translateX(-${100 - percent}%)` }}
|
|
37
|
+
/>
|
|
38
|
+
</ProgressPrimitive.Root>
|
|
39
|
+
)
|
|
40
|
+
if (!label) return bar
|
|
41
|
+
return (
|
|
42
|
+
<div className={cn("flex items-center gap-3", className)}>
|
|
43
|
+
{label === "left" && (
|
|
44
|
+
<span
|
|
45
|
+
className={cn(
|
|
46
|
+
"text-c2 font-medium",
|
|
47
|
+
disabled ? "text-grey-400" : "text-muted-foreground"
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
{percent}%
|
|
51
|
+
</span>
|
|
52
|
+
)}
|
|
53
|
+
{bar}
|
|
54
|
+
{label === "right" && (
|
|
55
|
+
<span
|
|
56
|
+
className={cn(
|
|
57
|
+
"text-c2 font-medium",
|
|
58
|
+
disabled ? "text-grey-400" : "text-muted-foreground"
|
|
59
|
+
)}
|
|
60
|
+
>
|
|
61
|
+
{percent}%
|
|
62
|
+
</span>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
)
|
|
66
|
+
})
|
|
67
|
+
Progress.displayName = ProgressPrimitive.Root.displayName
|
|
68
|
+
|
|
69
|
+
export { Progress }
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
|
3
|
+
import { clsx, type ClassValue } from "clsx"
|
|
4
|
+
import { twMerge } from "tailwind-merge"
|
|
5
|
+
|
|
6
|
+
export function cn(...inputs: ClassValue[]) {
|
|
7
|
+
return twMerge(clsx(inputs))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const RadioGroup = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
|
12
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
|
|
13
|
+
>(({ className, ...props }, ref) => (
|
|
14
|
+
<RadioGroupPrimitive.Root
|
|
15
|
+
ref={ref}
|
|
16
|
+
className={cn("grid gap-2", className)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
))
|
|
20
|
+
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
|
|
21
|
+
|
|
22
|
+
const RadioGroupItem = React.forwardRef<
|
|
23
|
+
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
|
24
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
|
25
|
+
>(({ className, ...props }, ref) => (
|
|
26
|
+
<RadioGroupPrimitive.Item
|
|
27
|
+
ref={ref}
|
|
28
|
+
className={cn(
|
|
29
|
+
"aspect-square size-5 shrink-0 rounded-full border border-grey-300 bg-background ring-offset-background transition-colors hover:border-grey-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:border-grey-200 disabled:bg-muted data-[state=checked]:border-primary",
|
|
30
|
+
className
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
|
35
|
+
<span className="block size-2.5 rounded-full bg-primary" />
|
|
36
|
+
</RadioGroupPrimitive.Indicator>
|
|
37
|
+
</RadioGroupPrimitive.Item>
|
|
38
|
+
))
|
|
39
|
+
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
|
|
40
|
+
|
|
41
|
+
export { RadioGroup, RadioGroupItem }
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Check } from "lucide-react"
|
|
3
|
+
import { clsx, type ClassValue } from "clsx"
|
|
4
|
+
import { twMerge } from "tailwind-merge"
|
|
5
|
+
|
|
6
|
+
export function cn(...inputs: ClassValue[]) {
|
|
7
|
+
return twMerge(clsx(inputs))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const stepperCircleStates = {
|
|
11
|
+
completed: "bg-primary text-primary-foreground",
|
|
12
|
+
current: "border border-primary bg-background text-primary",
|
|
13
|
+
upcoming: "border border-grey-200 bg-background text-grey-400",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const stepperLabelStates = {
|
|
17
|
+
completed: "text-foreground",
|
|
18
|
+
current: "text-primary",
|
|
19
|
+
upcoming: "text-grey-400",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const Stepper = React.forwardRef<
|
|
23
|
+
HTMLOListElement,
|
|
24
|
+
React.OlHTMLAttributes<HTMLOListElement>
|
|
25
|
+
>(({ className, ...props }, ref) => (
|
|
26
|
+
<ol
|
|
27
|
+
ref={ref}
|
|
28
|
+
className={cn("flex w-full items-center", className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
))
|
|
32
|
+
Stepper.displayName = "Stepper"
|
|
33
|
+
|
|
34
|
+
export interface StepperItemProps
|
|
35
|
+
extends React.LiHTMLAttributes<HTMLLIElement> {
|
|
36
|
+
index: number
|
|
37
|
+
state?: keyof typeof stepperCircleStates
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const StepperItem = React.forwardRef<HTMLLIElement, StepperItemProps>(
|
|
41
|
+
({ className, index, state = "upcoming", children, ...props }, ref) => (
|
|
42
|
+
<li
|
|
43
|
+
ref={ref}
|
|
44
|
+
aria-current={state === "current" ? "step" : undefined}
|
|
45
|
+
className={cn("flex items-center gap-2", className)}
|
|
46
|
+
{...props}
|
|
47
|
+
>
|
|
48
|
+
<span
|
|
49
|
+
className={cn(
|
|
50
|
+
"flex size-8 shrink-0 items-center justify-center rounded-full text-c2 transition-colors",
|
|
51
|
+
stepperCircleStates[state]
|
|
52
|
+
)}
|
|
53
|
+
>
|
|
54
|
+
{state === "completed" ? (
|
|
55
|
+
<Check className="size-4" aria-hidden="true" />
|
|
56
|
+
) : (
|
|
57
|
+
index
|
|
58
|
+
)}
|
|
59
|
+
</span>
|
|
60
|
+
{children != null && (
|
|
61
|
+
<span className={cn("text-c1", stepperLabelStates[state])}>
|
|
62
|
+
{children}
|
|
63
|
+
</span>
|
|
64
|
+
)}
|
|
65
|
+
</li>
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
StepperItem.displayName = "StepperItem"
|
|
69
|
+
|
|
70
|
+
export interface StepperSeparatorProps
|
|
71
|
+
extends React.HTMLAttributes<HTMLLIElement> {
|
|
72
|
+
completed?: boolean
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const StepperSeparator = ({
|
|
76
|
+
className,
|
|
77
|
+
completed = false,
|
|
78
|
+
...props
|
|
79
|
+
}: StepperSeparatorProps) => (
|
|
80
|
+
<li
|
|
81
|
+
role="presentation"
|
|
82
|
+
aria-hidden="true"
|
|
83
|
+
className={cn(
|
|
84
|
+
"mx-2 h-px flex-1",
|
|
85
|
+
completed ? "bg-primary" : "bg-grey-200",
|
|
86
|
+
className
|
|
87
|
+
)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
StepperSeparator.displayName = "StepperSeparator"
|
|
92
|
+
|
|
93
|
+
export { Stepper, StepperItem, StepperSeparator }
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
|
3
|
+
import { clsx, type ClassValue } from "clsx"
|
|
4
|
+
import { twMerge } from "tailwind-merge"
|
|
5
|
+
|
|
6
|
+
export function cn(...inputs: ClassValue[]) {
|
|
7
|
+
return twMerge(clsx(inputs))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Tabs = TabsPrimitive.Root
|
|
11
|
+
|
|
12
|
+
const TabsList = React.forwardRef<
|
|
13
|
+
React.ComponentRef<typeof TabsPrimitive.List>,
|
|
14
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
|
15
|
+
>(({ className, ...props }, ref) => (
|
|
16
|
+
<TabsPrimitive.List
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={cn(
|
|
19
|
+
"inline-flex items-center justify-center gap-1 rounded-xs bg-secondary p-1",
|
|
20
|
+
className
|
|
21
|
+
)}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
))
|
|
25
|
+
TabsList.displayName = TabsPrimitive.List.displayName
|
|
26
|
+
|
|
27
|
+
const TabsTrigger = React.forwardRef<
|
|
28
|
+
React.ComponentRef<typeof TabsPrimitive.Trigger>,
|
|
29
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
|
30
|
+
>(({ className, ...props }, ref) => (
|
|
31
|
+
<TabsPrimitive.Trigger
|
|
32
|
+
ref={ref}
|
|
33
|
+
className={cn(
|
|
34
|
+
"inline-flex h-10 items-center justify-center whitespace-nowrap rounded-xxs px-4 text-b4 text-muted-foreground ring-offset-background transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:text-grey-400 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-100",
|
|
35
|
+
className
|
|
36
|
+
)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
))
|
|
40
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
|
41
|
+
|
|
42
|
+
const TabsContent = React.forwardRef<
|
|
43
|
+
React.ComponentRef<typeof TabsPrimitive.Content>,
|
|
44
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
45
|
+
>(({ className, ...props }, ref) => (
|
|
46
|
+
<TabsPrimitive.Content
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cn(
|
|
49
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
50
|
+
className
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
))
|
|
55
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName
|
|
56
|
+
|
|
57
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
|
@@ -14,7 +14,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
14
14
|
return (
|
|
15
15
|
<textarea
|
|
16
16
|
className={cn(
|
|
17
|
-
"flex min-h-
|
|
17
|
+
"flex min-h-24 w-full rounded-xs border border-input bg-background px-4 py-3 text-b1 text-foreground ring-offset-background transition-colors placeholder:text-muted-foreground hover:border-grey-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 aria-invalid:border-error disabled:pointer-events-none disabled:border-grey-200 disabled:bg-secondary disabled:text-grey-400",
|
|
18
18
|
className
|
|
19
19
|
)}
|
|
20
20
|
ref={ref}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as SwitchPrimitive from "@radix-ui/react-switch"
|
|
3
|
+
import { clsx, type ClassValue } from "clsx"
|
|
4
|
+
import { twMerge } from "tailwind-merge"
|
|
5
|
+
|
|
6
|
+
export function cn(...inputs: ClassValue[]) {
|
|
7
|
+
return twMerge(clsx(inputs))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Toggle = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof SwitchPrimitive.Root>,
|
|
12
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>
|
|
13
|
+
>(({ className, ...props }, ref) => (
|
|
14
|
+
<SwitchPrimitive.Root
|
|
15
|
+
ref={ref}
|
|
16
|
+
className={cn(
|
|
17
|
+
"peer inline-flex h-6 w-11 shrink-0 items-center rounded-full bg-grey-200 px-0.5 ring-offset-background transition-colors hover:bg-grey-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:hover:bg-primary-700",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
>
|
|
22
|
+
<SwitchPrimitive.Thumb className="pointer-events-none block size-5 rounded-full bg-background shadow-100 transition-transform data-[state=checked]:translate-x-5" />
|
|
23
|
+
</SwitchPrimitive.Root>
|
|
24
|
+
))
|
|
25
|
+
Toggle.displayName = "Toggle"
|
|
26
|
+
|
|
27
|
+
export { Toggle }
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
|
3
|
+
import { clsx, type ClassValue } from "clsx"
|
|
4
|
+
import { twMerge } from "tailwind-merge"
|
|
5
|
+
|
|
6
|
+
export function cn(...inputs: ClassValue[]) {
|
|
7
|
+
return twMerge(clsx(inputs))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const TooltipProvider = TooltipPrimitive.Provider
|
|
11
|
+
|
|
12
|
+
const Tooltip = TooltipPrimitive.Root
|
|
13
|
+
|
|
14
|
+
const TooltipTrigger = TooltipPrimitive.Trigger
|
|
15
|
+
|
|
16
|
+
const TooltipContent = React.forwardRef<
|
|
17
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
18
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
19
|
+
>(({ className, sideOffset = 4, children, ...props }, ref) => (
|
|
20
|
+
<TooltipPrimitive.Portal>
|
|
21
|
+
<TooltipPrimitive.Content
|
|
22
|
+
ref={ref}
|
|
23
|
+
sideOffset={sideOffset}
|
|
24
|
+
className={cn(
|
|
25
|
+
"z-50 overflow-hidden rounded-xxs bg-grey-900 px-3 py-1.5 text-c2 font-medium text-white shadow-300",
|
|
26
|
+
className
|
|
27
|
+
)}
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
<TooltipPrimitive.Arrow className="fill-grey-900" />
|
|
32
|
+
</TooltipPrimitive.Content>
|
|
33
|
+
</TooltipPrimitive.Portal>
|
|
34
|
+
))
|
|
35
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
|
36
|
+
|
|
37
|
+
export { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent }
|