@hobenakicoffee/libraries 1.11.0 → 1.13.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 +25 -4
- package/package.json +84 -9
- package/src/App.tsx +28 -0
- package/src/components/turnstile-captcha.tsx +47 -0
- package/src/components/ui/alert-dialog.tsx +196 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/avatar.tsx +110 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/breadcrumb.tsx +122 -0
- package/src/components/ui/button-group.tsx +82 -0
- package/src/components/ui/button.tsx +77 -0
- package/src/components/ui/calendar.tsx +235 -0
- package/src/components/ui/card.tsx +100 -0
- package/src/components/ui/chart.tsx +364 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/dialog.tsx +162 -0
- package/src/components/ui/drawer.tsx +126 -0
- package/src/components/ui/dropdown-menu.tsx +267 -0
- package/src/components/ui/empty-minimal.tsx +20 -0
- package/src/components/ui/empty.tsx +101 -0
- package/src/components/ui/field.tsx +235 -0
- package/src/components/ui/input-group.tsx +170 -0
- package/src/components/ui/input-otp.tsx +84 -0
- package/src/components/ui/input.tsx +37 -0
- package/src/components/ui/item.tsx +196 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/popover.tsx +87 -0
- package/src/components/ui/radio-group.tsx +47 -0
- package/src/components/ui/select.tsx +205 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sheet.tsx +141 -0
- package/src/components/ui/sidebar.tsx +699 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +74 -0
- package/src/components/ui/spinner.tsx +18 -0
- package/src/components/ui/table.tsx +114 -0
- package/src/components/ui/tabs.tsx +88 -0
- package/src/components/ui/textarea.tsx +35 -0
- package/src/components/ui/toggle-group.tsx +91 -0
- package/src/components/ui/toggle.tsx +44 -0
- package/src/components/ui/tooltip.tsx +59 -0
- package/src/constants/common.test.ts +1 -1
- package/src/constants/legal.test.ts +1 -1
- package/src/constants/payment.test.ts +9 -9
- package/src/constants/platforms.test.ts +1 -1
- package/src/constants/services.test.ts +1 -1
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.css +135 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +16 -0
- package/src/moderation/datasets/bn.ts +708 -708
- package/src/moderation/normalizer.test.ts +1 -1
- package/src/moderation/normalizer.ts +16 -16
- package/src/moderation/profanity-service.test.ts +3 -3
- package/src/providers/theme-provider.tsx +73 -0
- package/src/types/supabase.ts +751 -647
- package/src/utils/check-moderation.test.ts +12 -12
- package/src/utils/format-number.test.ts +1 -1
- package/src/utils/format-plain-text.test.ts +1 -1
- package/src/utils/get-social-handle.test.ts +3 -3
- package/src/utils/get-social-link.test.ts +9 -9
- package/src/utils/get-social-link.ts +5 -3
- package/src/utils/get-user-name-initials.test.ts +1 -1
- package/src/utils/get-user-name-initials.ts +4 -4
- package/src/utils/get-user-page-link.ts +1 -1
- package/src/utils/index.ts +5 -5
- package/src/utils/open-to-new-window.ts +3 -1
- package/src/utils/post-to-facebook.test.ts +1 -1
- package/src/utils/post-to-facebook.ts +9 -3
- package/src/utils/post-to-instagram.test.ts +1 -1
- package/src/utils/post-to-linkedin.test.ts +1 -1
- package/src/utils/post-to-linkedin.ts +9 -3
- package/src/utils/post-to-x.test.ts +1 -1
- package/src/utils/post-to-x.ts +12 -4
- package/src/utils/to-human-readable.ts +6 -2
- package/src/utils/validate-phone-number.test.ts +1 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { Input } from "@/components/ui/input";
|
|
5
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
const inputGroupVariants = cva(
|
|
9
|
+
"group/input-group relative flex h-12 w-full min-w-0 items-center gap-1.5 overflow-hidden rounded-xl border border-input outline-none transition-colors has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-start]]:h-auto has-[>textarea]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:flex-col has-[textarea]:rounded-xl has-data-[align=block-end]:rounded-2xl has-data-[align=block-start]:rounded-2xl has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot=input-group-control]:focus-visible]:ring has-[[data-slot][aria-invalid=true]]:ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring has-[[data-slot][aria-invalid=true]]:ring-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 [[data-slot=combobox-content]_&]:focus-within:border-inherit [[data-slot=combobox-content]_&]:focus-within:ring-0",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "bg-input/30",
|
|
14
|
+
inverted: "bg-background",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
variant: "default",
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
function InputGroup({
|
|
24
|
+
className,
|
|
25
|
+
variant = "default",
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupVariants>) {
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
className={cn(inputGroupVariants({ variant, className }))}
|
|
31
|
+
data-slot="input-group"
|
|
32
|
+
role="group"
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const inputGroupAddonVariants = cva(
|
|
39
|
+
"flex h-auto cursor-text select-none items-center justify-center gap-2 py-2 font-medium text-muted-foreground text-sm **:data-[slot=kbd]:rounded-xl **:data-[slot=kbd]:bg-muted-foreground/10 **:data-[slot=kbd]:px-1.5 group-data-[disabled=true]/input-group:opacity-50 [&>svg:not([class*='size-'])]:size-4",
|
|
40
|
+
{
|
|
41
|
+
variants: {
|
|
42
|
+
align: {
|
|
43
|
+
"inline-start":
|
|
44
|
+
"order-first pl-3 has-[>button]:ml-[-0.25rem] has-[>kbd]:ml-[-0.15rem]",
|
|
45
|
+
"inline-end":
|
|
46
|
+
"order-last pr-3 has-[>button]:mr-[-0.25rem] has-[>kbd]:mr-[-0.15rem]",
|
|
47
|
+
"block-start":
|
|
48
|
+
"order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-3 [.border-b]:pb-3",
|
|
49
|
+
"block-end":
|
|
50
|
+
"order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-3 [.border-t]:pt-3",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
defaultVariants: {
|
|
54
|
+
align: "inline-start",
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
function InputGroupAddon({
|
|
60
|
+
className,
|
|
61
|
+
align = "inline-start",
|
|
62
|
+
...props
|
|
63
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
67
|
+
data-align={align}
|
|
68
|
+
data-slot="input-group-addon"
|
|
69
|
+
onClick={(e) => {
|
|
70
|
+
if ((e.target as HTMLElement).closest("button")) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
e.currentTarget.parentElement?.querySelector("input")?.focus();
|
|
74
|
+
}}
|
|
75
|
+
role="group"
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const inputGroupButtonVariants = cva(
|
|
82
|
+
"flex items-center gap-2 rounded-xl text-sm shadow-none",
|
|
83
|
+
{
|
|
84
|
+
variants: {
|
|
85
|
+
size: {
|
|
86
|
+
xs: "h-6 gap-1 px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
|
|
87
|
+
sm: "",
|
|
88
|
+
"icon-xs": "size-6 p-0 has-[>svg]:p-0",
|
|
89
|
+
"icon-sm": "size-8 p-0 has-[>svg]:p-0",
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
defaultVariants: {
|
|
93
|
+
size: "xs",
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
function InputGroupButton({
|
|
99
|
+
className,
|
|
100
|
+
type = "button",
|
|
101
|
+
variant = "ghost",
|
|
102
|
+
size = "xs",
|
|
103
|
+
...props
|
|
104
|
+
}: Omit<React.ComponentProps<typeof Button>, "size" | "type"> &
|
|
105
|
+
VariantProps<typeof inputGroupButtonVariants> & {
|
|
106
|
+
type?: "button" | "submit" | "reset";
|
|
107
|
+
}) {
|
|
108
|
+
return (
|
|
109
|
+
<Button
|
|
110
|
+
className={cn(inputGroupButtonVariants({ size }), className)}
|
|
111
|
+
data-size={size}
|
|
112
|
+
type={type}
|
|
113
|
+
variant={variant}
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
|
|
120
|
+
return (
|
|
121
|
+
<span
|
|
122
|
+
className={cn(
|
|
123
|
+
"flex items-center gap-2 text-muted-foreground text-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
|
|
124
|
+
className
|
|
125
|
+
)}
|
|
126
|
+
{...props}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function InputGroupInput({
|
|
132
|
+
className,
|
|
133
|
+
...props
|
|
134
|
+
}: React.ComponentProps<"input">) {
|
|
135
|
+
return (
|
|
136
|
+
<Input
|
|
137
|
+
className={cn(
|
|
138
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
|
|
139
|
+
className
|
|
140
|
+
)}
|
|
141
|
+
data-slot="input-group-control"
|
|
142
|
+
{...props}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function InputGroupTextarea({
|
|
148
|
+
className,
|
|
149
|
+
...props
|
|
150
|
+
}: React.ComponentProps<"textarea">) {
|
|
151
|
+
return (
|
|
152
|
+
<Textarea
|
|
153
|
+
className={cn(
|
|
154
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
|
|
155
|
+
className
|
|
156
|
+
)}
|
|
157
|
+
data-slot="input-group-control"
|
|
158
|
+
{...props}
|
|
159
|
+
/>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export {
|
|
164
|
+
InputGroup,
|
|
165
|
+
InputGroupAddon,
|
|
166
|
+
InputGroupButton,
|
|
167
|
+
InputGroupText,
|
|
168
|
+
InputGroupInput,
|
|
169
|
+
InputGroupTextarea,
|
|
170
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { MinusSignIcon } from "@hugeicons/core-free-icons";
|
|
2
|
+
import { HugeiconsIcon } from "@hugeicons/react";
|
|
3
|
+
import { OTPInput, OTPInputContext } from "input-otp";
|
|
4
|
+
import { type ComponentProps, useContext } from "react";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
function InputOTP({
|
|
8
|
+
className,
|
|
9
|
+
containerClassName,
|
|
10
|
+
...props
|
|
11
|
+
}: ComponentProps<typeof OTPInput> & {
|
|
12
|
+
containerClassName?: string;
|
|
13
|
+
}) {
|
|
14
|
+
return (
|
|
15
|
+
<OTPInput
|
|
16
|
+
className={cn("disabled:cursor-not-allowed", className)}
|
|
17
|
+
containerClassName={cn(
|
|
18
|
+
"cn-input-otp flex items-center has-disabled:opacity-50",
|
|
19
|
+
containerClassName
|
|
20
|
+
)}
|
|
21
|
+
data-slot="input-otp"
|
|
22
|
+
spellCheck={false}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function InputOTPGroup({ className, ...props }: ComponentProps<"div">) {
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
className={cn(
|
|
32
|
+
"flex items-center rounded-xl has-aria-invalid:border-destructive has-aria-invalid:ring has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40",
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
data-slot="input-otp-group"
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function InputOTPSlot({
|
|
42
|
+
index,
|
|
43
|
+
className,
|
|
44
|
+
...props
|
|
45
|
+
}: ComponentProps<"div"> & {
|
|
46
|
+
index: number;
|
|
47
|
+
}) {
|
|
48
|
+
const inputOTPContext = useContext(OTPInputContext);
|
|
49
|
+
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
className={cn(
|
|
54
|
+
"relative flex size-9 items-center justify-center border-input border-y border-r bg-input/10 text-foreground text-sm outline-none transition-all first:rounded-l-xl first:border-l last:rounded-r-xl aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:border-l data-[active=true]:ring data-[active=true]:ring-ring data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 sm:size-10 dark:data-[active=true]:aria-invalid:ring-destructive/40",
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
data-active={isActive}
|
|
58
|
+
data-slot="input-otp-slot"
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
{char}
|
|
62
|
+
{hasFakeCaret && (
|
|
63
|
+
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
64
|
+
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
|
|
65
|
+
</div>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function InputOTPSeparator({ ...props }: ComponentProps<"div">) {
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
className="flex items-center [&_svg:not([class*='size-'])]:size-4"
|
|
75
|
+
data-slot="input-otp-separator"
|
|
76
|
+
role="separator"
|
|
77
|
+
{...props}
|
|
78
|
+
>
|
|
79
|
+
<HugeiconsIcon icon={MinusSignIcon} strokeWidth={2} />
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
const inputVariants = cva(
|
|
6
|
+
"h-12 w-full min-w-0 rounded-xl border border-input px-3 py-1 text-base text-foreground outline-none transition-colors file:inline-flex file:h-7 file:border-0 file:bg-transparent file:font-medium file:text-foreground file:text-sm placeholder:text-muted-foreground/80 focus-visible:border-ring focus-visible:ring focus-visible:ring-ring disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring aria-invalid:ring-destructive md:text-sm dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-input/30",
|
|
11
|
+
inverted: "bg-background",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: {
|
|
15
|
+
variant: "default",
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
function Input({
|
|
21
|
+
className,
|
|
22
|
+
variant = "default",
|
|
23
|
+
type,
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<"input"> & VariantProps<typeof inputVariants>) {
|
|
26
|
+
return (
|
|
27
|
+
<input
|
|
28
|
+
className={cn(inputVariants({ variant, className }))}
|
|
29
|
+
data-slot="input"
|
|
30
|
+
data-variant={variant}
|
|
31
|
+
type={type}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { Input, inputVariants };
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import { Slot } from "radix-ui";
|
|
3
|
+
import type * as React from "react";
|
|
4
|
+
import { Separator } from "@/components/ui/separator";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
className={cn(
|
|
11
|
+
"group/item-group flex w-full flex-col gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2",
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
data-slot="item-group"
|
|
15
|
+
role="list"
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function ItemSeparator({
|
|
22
|
+
className,
|
|
23
|
+
...props
|
|
24
|
+
}: React.ComponentProps<typeof Separator>) {
|
|
25
|
+
return (
|
|
26
|
+
<Separator
|
|
27
|
+
className={cn("my-2", className)}
|
|
28
|
+
data-slot="item-separator"
|
|
29
|
+
orientation="horizontal"
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const itemVariants = cva(
|
|
36
|
+
"group/item flex w-full flex-wrap items-center rounded-xl border text-sm outline-none transition-colors duration-100 focus-visible:border-ring focus-visible:ring focus-visible:ring-ring [a]:transition-colors [a]:hover:bg-muted",
|
|
37
|
+
{
|
|
38
|
+
variants: {
|
|
39
|
+
variant: {
|
|
40
|
+
default: "border-transparent",
|
|
41
|
+
outline: "border-border",
|
|
42
|
+
muted: "border-transparent bg-muted/50",
|
|
43
|
+
},
|
|
44
|
+
size: {
|
|
45
|
+
default: "gap-3.5 px-4 py-3.5",
|
|
46
|
+
lg: "gap-3.5 px-4 py-3.5 md:px-6 md:py-6",
|
|
47
|
+
sm: "gap-2.5 px-3 py-2.5",
|
|
48
|
+
xs: "gap-2 px-2.5 py-2 [[data-slot=dropdown-menu-content]_&]:p-0",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
defaultVariants: {
|
|
52
|
+
variant: "default",
|
|
53
|
+
size: "default",
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
function Item({
|
|
59
|
+
className,
|
|
60
|
+
variant = "default",
|
|
61
|
+
size = "default",
|
|
62
|
+
asChild = false,
|
|
63
|
+
...props
|
|
64
|
+
}: React.ComponentProps<"div"> &
|
|
65
|
+
VariantProps<typeof itemVariants> & { asChild?: boolean }) {
|
|
66
|
+
const Comp = asChild ? Slot.Root : "div";
|
|
67
|
+
return (
|
|
68
|
+
<Comp
|
|
69
|
+
className={cn(itemVariants({ variant, size, className }))}
|
|
70
|
+
data-size={size}
|
|
71
|
+
data-slot="item"
|
|
72
|
+
data-variant={variant}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const itemMediaVariants = cva(
|
|
79
|
+
"flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:translate-y-0.5 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none",
|
|
80
|
+
{
|
|
81
|
+
variants: {
|
|
82
|
+
variant: {
|
|
83
|
+
default: "bg-transparent",
|
|
84
|
+
icon: "[&_svg:not([class*='size-'])]:size-4",
|
|
85
|
+
image:
|
|
86
|
+
"size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
defaultVariants: {
|
|
90
|
+
variant: "default",
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
function ItemMedia({
|
|
96
|
+
className,
|
|
97
|
+
variant = "default",
|
|
98
|
+
...props
|
|
99
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>) {
|
|
100
|
+
return (
|
|
101
|
+
<div
|
|
102
|
+
className={cn(itemMediaVariants({ variant, className }))}
|
|
103
|
+
data-slot="item-media"
|
|
104
|
+
data-variant={variant}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
111
|
+
return (
|
|
112
|
+
<div
|
|
113
|
+
className={cn(
|
|
114
|
+
"flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none",
|
|
115
|
+
className
|
|
116
|
+
)}
|
|
117
|
+
data-slot="item-content"
|
|
118
|
+
{...props}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
124
|
+
return (
|
|
125
|
+
<div
|
|
126
|
+
className={cn(
|
|
127
|
+
"line-clamp-1 flex w-fit items-center gap-2 font-medium text-sm leading-snug underline-offset-4",
|
|
128
|
+
className
|
|
129
|
+
)}
|
|
130
|
+
data-slot="item-title"
|
|
131
|
+
{...props}
|
|
132
|
+
/>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
137
|
+
return (
|
|
138
|
+
<p
|
|
139
|
+
className={cn(
|
|
140
|
+
"line-clamp-2 text-left font-normal text-muted-foreground text-sm leading-normal group-data-[size=xs]/item:text-xs [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
|
|
141
|
+
className
|
|
142
|
+
)}
|
|
143
|
+
data-slot="item-description"
|
|
144
|
+
{...props}
|
|
145
|
+
/>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
|
|
150
|
+
return (
|
|
151
|
+
<div
|
|
152
|
+
className={cn("flex items-center gap-2", className)}
|
|
153
|
+
data-slot="item-actions"
|
|
154
|
+
{...props}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
160
|
+
return (
|
|
161
|
+
<div
|
|
162
|
+
className={cn(
|
|
163
|
+
"flex basis-full items-center justify-between gap-2",
|
|
164
|
+
className
|
|
165
|
+
)}
|
|
166
|
+
data-slot="item-header"
|
|
167
|
+
{...props}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
173
|
+
return (
|
|
174
|
+
<div
|
|
175
|
+
className={cn(
|
|
176
|
+
"flex basis-full items-center justify-between gap-2",
|
|
177
|
+
className
|
|
178
|
+
)}
|
|
179
|
+
data-slot="item-footer"
|
|
180
|
+
{...props}
|
|
181
|
+
/>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export {
|
|
186
|
+
Item,
|
|
187
|
+
ItemMedia,
|
|
188
|
+
ItemContent,
|
|
189
|
+
ItemActions,
|
|
190
|
+
ItemGroup,
|
|
191
|
+
ItemSeparator,
|
|
192
|
+
ItemTitle,
|
|
193
|
+
ItemDescription,
|
|
194
|
+
ItemHeader,
|
|
195
|
+
ItemFooter,
|
|
196
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
function Label({ className, ...props }: React.ComponentProps<"label">) {
|
|
6
|
+
return (
|
|
7
|
+
// biome-ignore lint/a11y/noLabelWithoutControl: <shadcn components />
|
|
8
|
+
<label
|
|
9
|
+
className={cn(
|
|
10
|
+
"flex select-none items-center gap-2 font-medium text-muted-foreground text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
data-slot="label"
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { Label };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
function Popover({
|
|
7
|
+
...props
|
|
8
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
|
|
9
|
+
return <PopoverPrimitive.Root data-slot="popover" {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function PopoverTrigger({
|
|
13
|
+
...props
|
|
14
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
|
|
15
|
+
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function PopoverContent({
|
|
19
|
+
className,
|
|
20
|
+
align = "center",
|
|
21
|
+
sideOffset = 4,
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
|
|
24
|
+
return (
|
|
25
|
+
<PopoverPrimitive.Portal>
|
|
26
|
+
<PopoverPrimitive.Content
|
|
27
|
+
align={align}
|
|
28
|
+
className={cn(
|
|
29
|
+
"data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-4 rounded-md bg-popover p-4 text-popover-foreground text-sm shadow-md outline-hidden ring-1 ring-foreground/10 duration-100 data-closed:animate-out data-open:animate-in",
|
|
30
|
+
className
|
|
31
|
+
)}
|
|
32
|
+
data-slot="popover-content"
|
|
33
|
+
sideOffset={sideOffset}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
</PopoverPrimitive.Portal>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function PopoverAnchor({
|
|
41
|
+
...props
|
|
42
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
|
|
43
|
+
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
className={cn("flex flex-col gap-1 text-sm", className)}
|
|
50
|
+
data-slot="popover-header"
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
className={cn("font-medium", className)}
|
|
60
|
+
data-slot="popover-title"
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function PopoverDescription({
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: React.ComponentProps<"p">) {
|
|
70
|
+
return (
|
|
71
|
+
<p
|
|
72
|
+
className={cn("text-muted-foreground", className)}
|
|
73
|
+
data-slot="popover-description"
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
Popover,
|
|
81
|
+
PopoverAnchor,
|
|
82
|
+
PopoverContent,
|
|
83
|
+
PopoverDescription,
|
|
84
|
+
PopoverHeader,
|
|
85
|
+
PopoverTitle,
|
|
86
|
+
PopoverTrigger,
|
|
87
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CircleIcon } from "@hugeicons/core-free-icons";
|
|
2
|
+
import { HugeiconsIcon } from "@hugeicons/react";
|
|
3
|
+
import { RadioGroup as RadioGroupPrimitive } from "radix-ui";
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
function RadioGroup({
|
|
8
|
+
className,
|
|
9
|
+
...props
|
|
10
|
+
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
|
|
11
|
+
return (
|
|
12
|
+
<RadioGroupPrimitive.Root
|
|
13
|
+
className={cn("grid w-full gap-3", className)}
|
|
14
|
+
data-slot="radio-group"
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function RadioGroupItem({
|
|
21
|
+
className,
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
|
|
24
|
+
return (
|
|
25
|
+
<RadioGroupPrimitive.Item
|
|
26
|
+
className={cn(
|
|
27
|
+
"group/radio-group-item peer relative flex aspect-square size-5 shrink-0 rounded-full border-2 border-input text-primary shadow-xs outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[state=checked]:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
28
|
+
className
|
|
29
|
+
)}
|
|
30
|
+
data-slot="radio-group-item"
|
|
31
|
+
{...props}
|
|
32
|
+
>
|
|
33
|
+
<RadioGroupPrimitive.Indicator
|
|
34
|
+
className="flex size-5 items-center justify-center text-primary group-aria-invalid/radio-group-item:text-destructive"
|
|
35
|
+
data-slot="radio-group-indicator"
|
|
36
|
+
>
|
|
37
|
+
<HugeiconsIcon
|
|
38
|
+
className="absolute top-1/2 left-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2 fill-current"
|
|
39
|
+
icon={CircleIcon}
|
|
40
|
+
strokeWidth={2}
|
|
41
|
+
/>
|
|
42
|
+
</RadioGroupPrimitive.Indicator>
|
|
43
|
+
</RadioGroupPrimitive.Item>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { RadioGroup, RadioGroupItem };
|