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.
@@ -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-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
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 }