untitledui 0.1.1
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/dist/commands/add.js +339 -0
- package/dist/commands/init.js +436 -0
- package/dist/helper/download-tar-api.js +129 -0
- package/dist/helper/download-tar.js +81 -0
- package/dist/helper/find-css-file.js +19 -0
- package/dist/helper/formatText.js +37 -0
- package/dist/helper/get-components-api.js +47 -0
- package/dist/helper/get-components-list.js +62 -0
- package/dist/helper/get-components.js +19 -0
- package/dist/helper/get-config.js +163 -0
- package/dist/helper/get-package-info.js +99 -0
- package/dist/helper/get-pkg-manager.js +16 -0
- package/dist/helper/get-project.js +176 -0
- package/dist/helper/install-template.js +29 -0
- package/dist/helper/match-color-css.js +82 -0
- package/dist/helper/update-color-css.js +134 -0
- package/dist/index.js +25 -0
- package/dist/package.json +50 -0
- package/dist/res/components.json +520 -0
- package/dist/res/config.json +3 -0
- package/package.json +61 -0
- package/templates/default/.prettierrc +10 -0
- package/templates/default/README.md +36 -0
- package/templates/default/eslint.config.mjs +58 -0
- package/templates/default/next.config.ts +6 -0
- package/templates/default/package.json +57 -0
- package/templates/default/postcss.config.js +5 -0
- package/templates/default/public/favicon.ico +0 -0
- package/templates/default/public/marketing/smiling-girl.png +0 -0
- package/templates/default/public/marketing/spirals.webp +0 -0
- package/templates/default/src/app/home-screen.tsx +109 -0
- package/templates/default/src/app/layout.tsx +42 -0
- package/templates/default/src/app/not-found.tsx +40 -0
- package/templates/default/src/app/page.tsx +3 -0
- package/templates/default/src/components/foundations/dot-icon.tsx +27 -0
- package/templates/default/src/components/foundations/featured-icon/featured-icons.tsx +153 -0
- package/templates/default/src/components/foundations/logo/UntitledLogo.tsx +63 -0
- package/templates/default/src/components/foundations/logo/UntitledLogoMinimal.tsx +164 -0
- package/templates/default/src/components/foundations/payment-icons/amex-icon.tsx +19 -0
- package/templates/default/src/components/foundations/payment-icons/apple-pay-icon.tsx +27 -0
- package/templates/default/src/components/foundations/payment-icons/discover-icon.tsx +34 -0
- package/templates/default/src/components/foundations/payment-icons/index.tsx +10 -0
- package/templates/default/src/components/foundations/payment-icons/mastercard-icon.tsx +39 -0
- package/templates/default/src/components/foundations/payment-icons/paypal-icon.tsx +45 -0
- package/templates/default/src/components/foundations/payment-icons/stripe-icon.tsx +27 -0
- package/templates/default/src/components/foundations/payment-icons/union-pay-icon.tsx +37 -0
- package/templates/default/src/components/foundations/payment-icons/visa-icon.tsx +27 -0
- package/templates/default/src/components/marketing/header-navigation/base-components/nav-menu-item.tsx +41 -0
- package/templates/default/src/components/marketing/header-navigation/components/header.tsx +245 -0
- package/templates/default/src/components/marketing/header-navigation/dropdown-header-navigation.tsx +53 -0
- package/templates/default/src/components/shared/avatar/avatar-label-group.tsx +32 -0
- package/templates/default/src/components/shared/avatar/avatar-profile-photo.tsx +84 -0
- package/templates/default/src/components/shared/avatar/avatar.tsx +131 -0
- package/templates/default/src/components/shared/avatar/base-components/avatar-add-button.tsx +33 -0
- package/templates/default/src/components/shared/avatar/base-components/avatar-company-icon.tsx +26 -0
- package/templates/default/src/components/shared/avatar/base-components/avatar-online-indicator.tsx +31 -0
- package/templates/default/src/components/shared/avatar/base-components/index.ts +4 -0
- package/templates/default/src/components/shared/avatar/base-components/verified-tick.tsx +34 -0
- package/templates/default/src/components/shared/avatar/utils.ts +12 -0
- package/templates/default/src/components/shared/badges/badge-groups.tsx +176 -0
- package/templates/default/src/components/shared/badges/badge-types.ts +264 -0
- package/templates/default/src/components/shared/badges/badges.tsx +479 -0
- package/templates/default/src/components/shared/button-group/button-group.tsx +97 -0
- package/templates/default/src/components/shared/buttons/app-store-buttons-outline.tsx +454 -0
- package/templates/default/src/components/shared/buttons/app-store-buttons.tsx +806 -0
- package/templates/default/src/components/shared/buttons/button-utility.tsx +87 -0
- package/templates/default/src/components/shared/buttons/button.tsx +284 -0
- package/templates/default/src/components/shared/buttons/close-button.tsx +39 -0
- package/templates/default/src/components/shared/buttons/social-button.tsx +135 -0
- package/templates/default/src/components/shared/buttons/social-logos.tsx +115 -0
- package/templates/default/src/components/shared/checkbox/checkbox.tsx +120 -0
- package/templates/default/src/components/shared/dropdown/dropdown.tsx +138 -0
- package/templates/default/src/components/shared/input-dropdown/combobox.tsx +161 -0
- package/templates/default/src/components/shared/input-dropdown/dropdown-item.tsx +98 -0
- package/templates/default/src/components/shared/input-dropdown/input-dropdown.tsx +172 -0
- package/templates/default/src/components/shared/input-dropdown/multi-select.tsx +373 -0
- package/templates/default/src/components/shared/input-dropdown/popover.tsx +36 -0
- package/templates/default/src/components/shared/input-dropdown/select.tsx +63 -0
- package/templates/default/src/components/shared/inputs/file-upload-trigger.tsx +74 -0
- package/templates/default/src/components/shared/inputs/form/form.tsx +10 -0
- package/templates/default/src/components/shared/inputs/hint-text.tsx +34 -0
- package/templates/default/src/components/shared/inputs/input/index.tsx +189 -0
- package/templates/default/src/components/shared/inputs/input/input-payment.tsx +134 -0
- package/templates/default/src/components/shared/inputs/input/input-with-button.tsx +69 -0
- package/templates/default/src/components/shared/inputs/input/input-with-dropdown.tsx +178 -0
- package/templates/default/src/components/shared/inputs/input/input-with-prefix.tsx +74 -0
- package/templates/default/src/components/shared/inputs/label.tsx +46 -0
- package/templates/default/src/components/shared/inputs/textarea/textarea.tsx +82 -0
- package/templates/default/src/components/shared/progress-indicators/progress-circles.tsx +176 -0
- package/templates/default/src/components/shared/progress-indicators/progress-indicators.tsx +86 -0
- package/templates/default/src/components/shared/progress-indicators/simple-circle.tsx +29 -0
- package/templates/default/src/components/shared/radio-buttons/radio-buttons.tsx +125 -0
- package/templates/default/src/components/shared/radio-groups/radio-group-avatar.tsx +62 -0
- package/templates/default/src/components/shared/radio-groups/radio-group-checkbox.tsx +72 -0
- package/templates/default/src/components/shared/radio-groups/radio-group-icon-card.tsx +95 -0
- package/templates/default/src/components/shared/radio-groups/radio-group-icon-simple.tsx +70 -0
- package/templates/default/src/components/shared/radio-groups/radio-group-payment-icon.tsx +71 -0
- package/templates/default/src/components/shared/radio-groups/radio-group-radio-button.tsx +76 -0
- package/templates/default/src/components/shared/radio-groups/radio-groups.tsx +8 -0
- package/templates/default/src/components/shared/slider/slider.tsx +76 -0
- package/templates/default/src/components/shared/tags/base-components/tag-checkbox.tsx +47 -0
- package/templates/default/src/components/shared/tags/base-components/tag-close-x.tsx +34 -0
- package/templates/default/src/components/shared/tags/tags.tsx +162 -0
- package/templates/default/src/components/shared/toggle/toggle.tsx +140 -0
- package/templates/default/src/components/shared/tooltips/tooltips.tsx +140 -0
- package/templates/default/src/components/utils/index.ts +48 -0
- package/templates/default/src/components/utils/isDeepEqual.ts +31 -0
- package/templates/default/src/components/utils/isReactComponent.ts +22 -0
- package/templates/default/src/components/utils/mergeRefs.ts +19 -0
- package/templates/default/src/components/utils/useBreakpoint.ts +36 -0
- package/templates/default/src/components/utils/uuid.ts +9 -0
- package/templates/default/src/fonts/GeistMonoVF.woff +0 -0
- package/templates/default/src/fonts/GeistVF.woff +0 -0
- package/templates/default/src/hooks/use-resize-observer.tsx +55 -0
- package/templates/default/src/providers/theme.tsx +11 -0
- package/templates/default/src/styles/colors.css +805 -0
- package/templates/default/src/styles/globals.css +86 -0
- package/templates/default/src/styles/text-styles.css +177 -0
- package/templates/default/src/styles/theme.css +1310 -0
- package/templates/default/src/styles/typography.css +428 -0
- package/templates/default/tsconfig.json +27 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { RadioGroupProps } from "react-aria-components";
|
|
4
|
+
import { Label, Radio, RadioGroup, Text } from "react-aria-components";
|
|
5
|
+
import AvatarComponent from "@/components/shared/avatar/avatar";
|
|
6
|
+
import { CheckboxBase } from "@/components/shared/checkbox/checkbox";
|
|
7
|
+
import { cx } from "@/components/utils";
|
|
8
|
+
|
|
9
|
+
interface AvatarItemType {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
username: string;
|
|
13
|
+
title: string;
|
|
14
|
+
avatarUrl: string;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface RadioGroupAvatarProps extends RadioGroupProps {
|
|
19
|
+
size?: "sm" | "md";
|
|
20
|
+
items: AvatarItemType[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const RadioGroupAvatar = ({ items, size = "sm", className, ...props }: RadioGroupAvatarProps) => {
|
|
24
|
+
return (
|
|
25
|
+
<RadioGroup {...props} className={(states) => cx("flex flex-col gap-3", typeof className === "function" ? className(states) : className)}>
|
|
26
|
+
{items.map((person) => (
|
|
27
|
+
<Radio
|
|
28
|
+
isDisabled={person.disabled}
|
|
29
|
+
key={person.id}
|
|
30
|
+
value={person.id}
|
|
31
|
+
className={({ isDisabled, isSelected, isFocusVisible }) =>
|
|
32
|
+
cx(
|
|
33
|
+
"relative flex cursor-pointer items-start gap-1 rounded-xl bg-primary p-4 transition duration-100 ring-inset",
|
|
34
|
+
isSelected ? "ring-2 ring-border-brand" : "ring-1 ring-border-secondary",
|
|
35
|
+
isDisabled && "cursor-not-allowed bg-disabled_subtle ring-border-disabled_subtle",
|
|
36
|
+
isFocusVisible && "outline-2 outline-offset-2 outline-focus-ring",
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
{({ isDisabled, isSelected, isFocusVisible }) => (
|
|
41
|
+
<>
|
|
42
|
+
<div className={cx("flex flex-1", size === "md" ? "gap-3 md:gap-4" : "gap-3")}>
|
|
43
|
+
<AvatarComponent alt={person.name} src={person.avatarUrl} size={size === "md" ? "md" : "sm"} />
|
|
44
|
+
|
|
45
|
+
<div className={cx("flex flex-col", size === "md" ? "gap-0.5" : "")}>
|
|
46
|
+
<Label className={cx("pointer-events-none flex", size === "md" ? "gap-1.5" : "gap-1")}>
|
|
47
|
+
<span className={cx("text-secondary", size === "md" ? "tt-md-md" : "tt-sm-md")}>{person.name}</span>
|
|
48
|
+
<span className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>{person.username}</span>
|
|
49
|
+
</Label>
|
|
50
|
+
<Text slot="description" className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>
|
|
51
|
+
{person.title}
|
|
52
|
+
</Text>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<CheckboxBase size={size === "md" ? "md" : "sm"} isDisabled={isDisabled} isSelected={isSelected} isFocused={isFocusVisible} />
|
|
56
|
+
</>
|
|
57
|
+
)}
|
|
58
|
+
</Radio>
|
|
59
|
+
))}
|
|
60
|
+
</RadioGroup>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { FC } from "react";
|
|
4
|
+
import type { RadioGroupProps } from "react-aria-components";
|
|
5
|
+
import { Label, Radio, RadioGroup, Text } from "react-aria-components";
|
|
6
|
+
import { CheckboxBase } from "@/components/shared/checkbox/checkbox";
|
|
7
|
+
import { cx } from "@/components/utils";
|
|
8
|
+
|
|
9
|
+
type RadioGroupItemType = {
|
|
10
|
+
value: string;
|
|
11
|
+
title: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
description: string;
|
|
14
|
+
secondaryTitle: string;
|
|
15
|
+
icon: FC<{ className?: string }>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
interface RadioGroupCheckboxProps extends RadioGroupProps {
|
|
19
|
+
size?: "sm" | "md";
|
|
20
|
+
items: RadioGroupItemType[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const RadioGroupCheckbox = ({ items, size = "sm", className, ...props }: RadioGroupCheckboxProps) => {
|
|
24
|
+
return (
|
|
25
|
+
<RadioGroup {...props} className={(states) => cx("flex flex-col gap-3", typeof className === "function" ? className(states) : className)}>
|
|
26
|
+
{items.map((plan) => (
|
|
27
|
+
<Radio
|
|
28
|
+
isDisabled={plan.disabled}
|
|
29
|
+
key={plan.value}
|
|
30
|
+
value={plan.value}
|
|
31
|
+
className={({ isDisabled, isFocusVisible, isSelected }) =>
|
|
32
|
+
cx(
|
|
33
|
+
"relative flex cursor-pointer items-start gap-1 rounded-xl bg-primary p-4 transition duration-100 ring-inset",
|
|
34
|
+
size === "md" ? "gap-3" : "gap-2",
|
|
35
|
+
isSelected ? "ring-2 ring-border-brand" : "ring-1 ring-border-secondary",
|
|
36
|
+
isDisabled && "cursor-not-allowed bg-disabled_subtle ring-border-disabled_subtle",
|
|
37
|
+
isFocusVisible && "outline-2 outline-offset-2 outline-focus-ring",
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
>
|
|
41
|
+
{({ isDisabled, isSelected, isFocusVisible }) => (
|
|
42
|
+
<>
|
|
43
|
+
<CheckboxBase
|
|
44
|
+
size={size === "md" ? "md" : "sm"}
|
|
45
|
+
isDisabled={isDisabled}
|
|
46
|
+
isSelected={isSelected}
|
|
47
|
+
isFocused={isFocusVisible}
|
|
48
|
+
className="mt-0.5"
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
<div
|
|
52
|
+
className={cx(
|
|
53
|
+
"flex flex-col",
|
|
54
|
+
|
|
55
|
+
size === "md" ? "gap-0.5" : "",
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
<Label className={cx("pointer-events-none flex", size === "md" ? "gap-1.5" : "gap-1")}>
|
|
59
|
+
<span className={cx("text-secondary", size === "md" ? "tt-md-md" : "tt-sm-md")}>{plan.title}</span>
|
|
60
|
+
<span className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>{plan.secondaryTitle}</span>
|
|
61
|
+
</Label>
|
|
62
|
+
<Text slot="description" className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>
|
|
63
|
+
{plan.description}
|
|
64
|
+
</Text>
|
|
65
|
+
</div>
|
|
66
|
+
</>
|
|
67
|
+
)}
|
|
68
|
+
</Radio>
|
|
69
|
+
))}
|
|
70
|
+
</RadioGroup>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { FC, ReactNode } from "react";
|
|
4
|
+
import type { RadioGroupProps } from "react-aria-components";
|
|
5
|
+
import { Radio, RadioGroup } from "react-aria-components";
|
|
6
|
+
import { FeaturedIcon } from "@/components/foundations/featured-icon/featured-icons";
|
|
7
|
+
import { BadgeWithDot } from "@/components/shared/badges/badges";
|
|
8
|
+
import { CheckboxBase } from "@/components/shared/checkbox/checkbox";
|
|
9
|
+
import { cx } from "@/components/utils";
|
|
10
|
+
|
|
11
|
+
type IconCardItemType = {
|
|
12
|
+
value: string;
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
secondaryTitle?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
price?: string;
|
|
18
|
+
badge?: ReactNode;
|
|
19
|
+
icon: FC<{ className?: string }>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
interface RadioGroupIconCardProps extends RadioGroupProps {
|
|
23
|
+
size?: "sm" | "md";
|
|
24
|
+
items: IconCardItemType[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const RadioGroupIconCard = ({ items, size = "sm", className, ...props }: RadioGroupIconCardProps) => {
|
|
28
|
+
return (
|
|
29
|
+
<RadioGroup {...props} className={(states) => cx("flex flex-col gap-3", typeof className === "function" ? className(states) : className)}>
|
|
30
|
+
{items.map((plan) => (
|
|
31
|
+
<Radio
|
|
32
|
+
isDisabled={plan.disabled}
|
|
33
|
+
key={plan.value}
|
|
34
|
+
value={plan.value}
|
|
35
|
+
className={({ isDisabled, isSelected, isFocusVisible }) =>
|
|
36
|
+
cx(
|
|
37
|
+
"relative block cursor-pointer rounded-xl bg-primary transition duration-100 ring-inset",
|
|
38
|
+
isSelected ? "ring-2 ring-border-brand" : "ring-1 ring-border-secondary",
|
|
39
|
+
isDisabled && "cursor-not-allowed bg-disabled_subtle ring-border-disabled",
|
|
40
|
+
isSelected && isDisabled && "ring-border-disabled_subtle",
|
|
41
|
+
isFocusVisible && "outline-2 outline-offset-2 outline-focus-ring",
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
>
|
|
45
|
+
{({ isDisabled, isSelected, isFocusVisible }) => (
|
|
46
|
+
<>
|
|
47
|
+
<div
|
|
48
|
+
className={cx(
|
|
49
|
+
"flex items-center gap-3 rounded-t-xl p-3 pr-5 transition-inherit-all ring-inset",
|
|
50
|
+
isSelected ? "ring-2 ring-border-brand" : "ring-1 ring-border-secondary",
|
|
51
|
+
isDisabled && "ring-border-disabled",
|
|
52
|
+
isSelected && isDisabled && "ring-border-disabled_subtle",
|
|
53
|
+
isFocusVisible && "outline-hidden",
|
|
54
|
+
)}
|
|
55
|
+
>
|
|
56
|
+
<FeaturedIcon
|
|
57
|
+
size={size === "md" ? "md" : "sm"}
|
|
58
|
+
icon={plan.icon}
|
|
59
|
+
color="gray"
|
|
60
|
+
theme="modern"
|
|
61
|
+
className={cx(isDisabled && "bg-disabled text-fg-disabled")}
|
|
62
|
+
/>
|
|
63
|
+
|
|
64
|
+
<span className={cx("mr-1 text-secondary", size === "md" ? "tt-lg-semi" : "tt-md-semi")}>{plan.title}</span>
|
|
65
|
+
|
|
66
|
+
<CheckboxBase
|
|
67
|
+
size={size === "md" ? "md" : "sm"}
|
|
68
|
+
className="ml-auto"
|
|
69
|
+
isDisabled={isDisabled}
|
|
70
|
+
isSelected={isSelected}
|
|
71
|
+
isFocused={isFocusVisible}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div className="flex flex-col-reverse items-start justify-between gap-4 rounded-b-lg p-4 sm:flex-row sm:gap-1">
|
|
76
|
+
<div className={cx("flex flex-col", size === "md" ? "gap-2" : "gap-1")}>
|
|
77
|
+
<p className="flex items-baseline gap-1">
|
|
78
|
+
<span className={cx("text-secondary", size === "md" ? "td-md-semi" : "td-sm-semi")}>{plan.price}</span>
|
|
79
|
+
<span className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>{plan.secondaryTitle}</span>
|
|
80
|
+
</p>
|
|
81
|
+
<p className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>{plan.description}</p>
|
|
82
|
+
</div>
|
|
83
|
+
{plan.badge && (
|
|
84
|
+
<BadgeWithDot size="sm" type="modern" color="success">
|
|
85
|
+
{plan.badge}
|
|
86
|
+
</BadgeWithDot>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
</>
|
|
90
|
+
)}
|
|
91
|
+
</Radio>
|
|
92
|
+
))}
|
|
93
|
+
</RadioGroup>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { FC } from "react";
|
|
4
|
+
import type { RadioGroupProps } from "react-aria-components";
|
|
5
|
+
import { Label, Radio, RadioGroup, Text } from "react-aria-components";
|
|
6
|
+
import { FeaturedIcon } from "@/components/foundations/featured-icon/featured-icons";
|
|
7
|
+
import { CheckboxBase } from "@/components/shared/checkbox/checkbox";
|
|
8
|
+
import { cx } from "@/components/utils";
|
|
9
|
+
|
|
10
|
+
type RadioGroupItemType = {
|
|
11
|
+
value: string;
|
|
12
|
+
title: string;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
description: string;
|
|
15
|
+
secondaryTitle: string;
|
|
16
|
+
icon: FC<{ className?: string }>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
interface RadioGroupIconSimpleProps extends RadioGroupProps {
|
|
20
|
+
size?: "sm" | "md";
|
|
21
|
+
items: RadioGroupItemType[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const RadioGroupIconSimple = ({ items, size = "sm", className, ...props }: RadioGroupIconSimpleProps) => {
|
|
25
|
+
return (
|
|
26
|
+
<RadioGroup {...props} className={(states) => cx("flex flex-col gap-3", typeof className === "function" ? className(states) : className)}>
|
|
27
|
+
{items.map((plan) => (
|
|
28
|
+
<Radio
|
|
29
|
+
isDisabled={plan.disabled}
|
|
30
|
+
key={plan.value}
|
|
31
|
+
value={plan.value}
|
|
32
|
+
className={({ isDisabled, isSelected, isFocusVisible }) =>
|
|
33
|
+
cx(
|
|
34
|
+
"relative flex cursor-pointer items-start gap-1 rounded-xl bg-primary p-4 transition duration-100 ring-inset",
|
|
35
|
+
isSelected ? "ring-2 ring-border-brand" : "ring-1 ring-border-secondary",
|
|
36
|
+
isDisabled && "cursor-not-allowed bg-disabled_subtle ring-border-disabled",
|
|
37
|
+
isFocusVisible && "outline-2 outline-offset-2 outline-focus-ring",
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
>
|
|
41
|
+
{({ isDisabled, isSelected, isFocusVisible }) => (
|
|
42
|
+
<>
|
|
43
|
+
<div className="flex flex-1 gap-3">
|
|
44
|
+
<FeaturedIcon
|
|
45
|
+
icon={plan.icon}
|
|
46
|
+
size={size === "md" ? "md" : "sm"}
|
|
47
|
+
color="gray"
|
|
48
|
+
theme="modern"
|
|
49
|
+
className={cx(isDisabled && "bg-disabled text-fg-disabled")}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<div className={cx("flex flex-col", size === "md" ? "gap-0.5" : "")}>
|
|
53
|
+
<Label className={cx("pointer-events-none flex", size === "md" ? "gap-1.5" : "gap-1")}>
|
|
54
|
+
<span className={cx("text-secondary", size === "md" ? "tt-md-md" : "tt-sm-md")}>{plan.title}</span>
|
|
55
|
+
<span className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>{plan.secondaryTitle}</span>
|
|
56
|
+
</Label>
|
|
57
|
+
<Text slot="description" className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>
|
|
58
|
+
{plan.description}
|
|
59
|
+
</Text>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<CheckboxBase size={size === "md" ? "md" : "sm"} isDisabled={isDisabled} isSelected={isSelected} isFocused={isFocusVisible} />
|
|
64
|
+
</>
|
|
65
|
+
)}
|
|
66
|
+
</Radio>
|
|
67
|
+
))}
|
|
68
|
+
</RadioGroup>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type { RadioGroupProps } from "react-aria-components";
|
|
5
|
+
import { Label, Radio, RadioGroup, Text } from "react-aria-components";
|
|
6
|
+
import Button from "@/components/shared/buttons/button";
|
|
7
|
+
import { CheckboxBase } from "@/components/shared/checkbox/checkbox";
|
|
8
|
+
import { cx } from "@/components/utils";
|
|
9
|
+
|
|
10
|
+
interface PaymentCardItemType {
|
|
11
|
+
value: string;
|
|
12
|
+
title: string;
|
|
13
|
+
description: string;
|
|
14
|
+
logo: ReactNode;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface RadioGroupPaymentIconProps extends RadioGroupProps {
|
|
19
|
+
size?: "sm" | "md";
|
|
20
|
+
items: PaymentCardItemType[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const RadioGroupPaymentIcon = ({ items, size = "sm", className, ...props }: RadioGroupPaymentIconProps) => {
|
|
24
|
+
return (
|
|
25
|
+
<RadioGroup {...props} className={(states) => cx("flex flex-col gap-3", typeof className === "function" ? className(states) : className)}>
|
|
26
|
+
{items.map((card) => (
|
|
27
|
+
<Radio
|
|
28
|
+
isDisabled={card.disabled}
|
|
29
|
+
key={card.value}
|
|
30
|
+
value={card.value}
|
|
31
|
+
className={({ isDisabled, isSelected, isFocusVisible }) =>
|
|
32
|
+
cx(
|
|
33
|
+
"relative flex cursor-pointer items-start gap-1 rounded-xl bg-primary p-4 transition duration-100 ring-inset",
|
|
34
|
+
isSelected ? "ring-2 ring-border-brand" : "ring-1 ring-border-secondary",
|
|
35
|
+
isDisabled && "cursor-not-allowed bg-disabled_subtle ring-border-disabled_subtle",
|
|
36
|
+
isFocusVisible && "outline-2 outline-offset-2 outline-focus-ring",
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
{({ isDisabled, isSelected, isFocusVisible }) => (
|
|
41
|
+
<>
|
|
42
|
+
<div className={cx("flex flex-1", size === "md" ? "gap-3 md:gap-4" : "gap-3")}>
|
|
43
|
+
<span className="shrink-0">{card.logo}</span>
|
|
44
|
+
<div>
|
|
45
|
+
<div className={cx("flex flex-col", size === "md" ? "gap-0.5" : "")}>
|
|
46
|
+
<Label className={cx("pointer-events-none text-secondary", size === "md" ? "tt-md-md" : "tt-sm-md")}>
|
|
47
|
+
{card.title}
|
|
48
|
+
</Label>
|
|
49
|
+
<Text slot="description" className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>
|
|
50
|
+
{card.description}
|
|
51
|
+
</Text>
|
|
52
|
+
</div>
|
|
53
|
+
<div className={cx("flex gap-3", size === "md" ? "mt-3" : "mt-2")}>
|
|
54
|
+
<Button color="link-gray" size={size === "md" ? "md" : "sm"} isDisabled={isDisabled}>
|
|
55
|
+
Set as default
|
|
56
|
+
</Button>
|
|
57
|
+
<Button color="link-color" size={size === "md" ? "md" : "sm"} isDisabled={isDisabled}>
|
|
58
|
+
Edit
|
|
59
|
+
</Button>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<CheckboxBase size={size === "md" ? "md" : "sm"} isDisabled={isDisabled} isSelected={isSelected} isFocused={isFocusVisible} />
|
|
65
|
+
</>
|
|
66
|
+
)}
|
|
67
|
+
</Radio>
|
|
68
|
+
))}
|
|
69
|
+
</RadioGroup>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { FC } from "react";
|
|
4
|
+
import type { RadioGroupProps } from "react-aria-components";
|
|
5
|
+
import { Label, Radio, RadioGroup, Text } from "react-aria-components";
|
|
6
|
+
import { cx } from "@/components/utils";
|
|
7
|
+
|
|
8
|
+
type RadioGroupItemType = {
|
|
9
|
+
value: string;
|
|
10
|
+
title: string;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
description: string;
|
|
13
|
+
secondaryTitle: string;
|
|
14
|
+
icon: FC<{ className?: string }>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
interface RadioGroupRadioButtonProps extends RadioGroupProps {
|
|
18
|
+
size?: "sm" | "md";
|
|
19
|
+
items: RadioGroupItemType[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const RadioGroupRadioButton = ({ items, size = "sm", className, ...props }: RadioGroupRadioButtonProps) => {
|
|
23
|
+
return (
|
|
24
|
+
<RadioGroup {...props} className={(states) => cx("flex flex-col gap-3", typeof className === "function" ? className(states) : className)}>
|
|
25
|
+
{items.map((plan) => (
|
|
26
|
+
<Radio
|
|
27
|
+
isDisabled={plan.disabled}
|
|
28
|
+
key={plan.value}
|
|
29
|
+
value={plan.value}
|
|
30
|
+
className={({ isDisabled, isSelected, isFocusVisible }) =>
|
|
31
|
+
cx(
|
|
32
|
+
"relative flex cursor-pointer rounded-xl bg-primary p-4 transition duration-100 ring-inset",
|
|
33
|
+
size === "md" ? "gap-3" : "gap-2",
|
|
34
|
+
isSelected ? "ring-2 ring-border-brand" : "ring-1 ring-border-secondary",
|
|
35
|
+
isDisabled && "cursor-not-allowed bg-disabled_subtle ring-border-disabled_subtle",
|
|
36
|
+
isFocusVisible && "outline-2 outline-offset-2 outline-focus-ring",
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
{({ isSelected, isDisabled, isFocusVisible }) => (
|
|
41
|
+
<>
|
|
42
|
+
<div
|
|
43
|
+
className={cx(
|
|
44
|
+
"relative mt-0.5 inline-flex shrink-0 items-center justify-center rounded-full transition-inherit-all ring-inset",
|
|
45
|
+
size === "md" ? "size-5" : "size-4",
|
|
46
|
+
isSelected ? "bg-brand-solid" : "ring-1 ring-border-primary",
|
|
47
|
+
isDisabled && "bg-disabled_subtle ring-1 ring-border-disabled",
|
|
48
|
+
isFocusVisible && "outline-2 outline-offset-2 outline-focus-ring",
|
|
49
|
+
)}
|
|
50
|
+
>
|
|
51
|
+
<div
|
|
52
|
+
className={cx(
|
|
53
|
+
"absolute rounded-full bg-fg-white opacity-0 transition-inherit-all",
|
|
54
|
+
size === "md" ? "size-2" : "size-1.5",
|
|
55
|
+
isSelected ? "opacity-100" : "opacity-0",
|
|
56
|
+
isDisabled && "bg-fg-disabled_subtle",
|
|
57
|
+
)}
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div className={cx("flex flex-col", size === "md" ? "gap-0.5" : "")}>
|
|
62
|
+
<Label className={cx("pointer-events-none flex", size === "md" ? "gap-1.5" : "gap-1")}>
|
|
63
|
+
<span className={cx("tt-sm-md text-secondary", size === "md" ? "tt-md-md" : "tt-sm-md")}>{plan.title}</span>
|
|
64
|
+
<span className={cx("text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>{plan.secondaryTitle}</span>
|
|
65
|
+
</Label>
|
|
66
|
+
<Text slot="description" className={cx("tt-sm text-tertiary", size === "md" ? "tt-md" : "tt-sm")}>
|
|
67
|
+
{plan.description}
|
|
68
|
+
</Text>
|
|
69
|
+
</div>
|
|
70
|
+
</>
|
|
71
|
+
)}
|
|
72
|
+
</Radio>
|
|
73
|
+
))}
|
|
74
|
+
</RadioGroup>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export { RadioGroupIconSimple as IconSimple } from "./radio-group-icon-simple";
|
|
4
|
+
export { RadioGroupIconCard as IconCard } from "./radio-group-icon-card";
|
|
5
|
+
export { RadioGroupAvatar as Avatar } from "./radio-group-avatar";
|
|
6
|
+
export { RadioGroupPaymentIcon as PaymentIcon } from "./radio-group-payment-icon";
|
|
7
|
+
export { RadioGroupRadioButton as RadioButton } from "./radio-group-radio-button";
|
|
8
|
+
export { RadioGroupCheckbox as Checkbox } from "./radio-group-checkbox";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { SliderProps as AriaSliderProps } from "react-aria-components";
|
|
4
|
+
import { Slider as AriaSlider, Label, SliderOutput, SliderThumb, SliderTrack } from "react-aria-components";
|
|
5
|
+
import { cx, sortCx } from "@/components/utils";
|
|
6
|
+
|
|
7
|
+
const styles = sortCx({
|
|
8
|
+
default: "hidden",
|
|
9
|
+
bottom: "absolute top-2 left-1/2 -translate-x-1/2 translate-y-full text-md leading-md font-medium text-primary",
|
|
10
|
+
"top-floating":
|
|
11
|
+
"absolute -top-2 left-1/2 -translate-x-1/2 -translate-y-full rounded-lg bg-primary px-3 py-2 text-xs leading-xs font-semibold text-secondary shadow-lg ring-1 ring-border-secondary_alt",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
interface SliderProps extends AriaSliderProps {
|
|
15
|
+
labelPosition?: keyof typeof styles;
|
|
16
|
+
labelFormatter?: (value: number) => string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const Slider = ({
|
|
20
|
+
labelPosition = "default",
|
|
21
|
+
minValue = 0,
|
|
22
|
+
maxValue = 100,
|
|
23
|
+
defaultValue = [minValue, maxValue],
|
|
24
|
+
labelFormatter,
|
|
25
|
+
formatOptions,
|
|
26
|
+
...rest
|
|
27
|
+
}: SliderProps) => {
|
|
28
|
+
// Format thumb value as percentage by default.
|
|
29
|
+
const defaultFormatOptions: Intl.NumberFormatOptions = {
|
|
30
|
+
style: "percent",
|
|
31
|
+
maximumFractionDigits: 0,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<AriaSlider {...rest} {...{ minValue, maxValue, defaultValue }} formatOptions={formatOptions ?? defaultFormatOptions}>
|
|
36
|
+
<Label />
|
|
37
|
+
<SliderTrack className="relative h-2 w-full rounded-full bg-quaternary">
|
|
38
|
+
{({ state: { values, getThumbValue, getThumbPercent, getFormattedValue, getThumbValueLabel } }) => {
|
|
39
|
+
const left = getThumbPercent(0);
|
|
40
|
+
const width = getThumbPercent(1) - left;
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<span
|
|
45
|
+
className="absolute z-10 h-2 w-full rounded-full bg-brand-solid"
|
|
46
|
+
style={{
|
|
47
|
+
left: `${left * 100}%`,
|
|
48
|
+
width: `${width * 100}%`,
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
{values.map((_, index) => {
|
|
52
|
+
return (
|
|
53
|
+
<SliderThumb
|
|
54
|
+
key={index}
|
|
55
|
+
index={index}
|
|
56
|
+
className={({ isFocusVisible, isDragging }) =>
|
|
57
|
+
cx(
|
|
58
|
+
"absolute top-1/2 z-50 box-border size-6 cursor-grab rounded-full bg-slider-handle-bg shadow-md ring-2 ring-slider-handle-border ring-inset",
|
|
59
|
+
isFocusVisible && "outline-2 outline-offset-2 outline-focus-ring",
|
|
60
|
+
isDragging && "cursor-grabbing",
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
>
|
|
64
|
+
<SliderOutput className={cx("whitespace-nowrap", styles[labelPosition])}>
|
|
65
|
+
{labelFormatter ? labelFormatter(getThumbValue(index)) : getFormattedValue(getThumbValue(index) / 100)}
|
|
66
|
+
</SliderOutput>
|
|
67
|
+
</SliderThumb>
|
|
68
|
+
);
|
|
69
|
+
})}
|
|
70
|
+
</>
|
|
71
|
+
);
|
|
72
|
+
}}
|
|
73
|
+
</SliderTrack>
|
|
74
|
+
</AriaSlider>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cx } from "@/components/utils";
|
|
4
|
+
|
|
5
|
+
interface TagCheckboxProps {
|
|
6
|
+
size?: "sm" | "md" | "lg";
|
|
7
|
+
className?: string;
|
|
8
|
+
isFocused?: boolean;
|
|
9
|
+
isSelected?: boolean;
|
|
10
|
+
isDisabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const TagCheckbox = ({ className, isFocused, isSelected, isDisabled, size = "sm" }: TagCheckboxProps) => {
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
className={cx(
|
|
17
|
+
"flex cursor-pointer appearance-none items-center justify-center rounded bg-primary ring-1 ring-border-primary ring-inset",
|
|
18
|
+
size === "sm" && "size-3.5",
|
|
19
|
+
size === "md" && "size-4",
|
|
20
|
+
size === "lg" && "size-4.5",
|
|
21
|
+
isSelected && "bg-brand-solid ring-bg-brand-solid",
|
|
22
|
+
isDisabled && "cursor-not-allowed bg-disabled_subtle ring-border-disabled",
|
|
23
|
+
isFocused && "outline-2 outline-offset-2 outline-focus-ring",
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
>
|
|
27
|
+
<svg
|
|
28
|
+
aria-hidden="true"
|
|
29
|
+
viewBox="0 0 14 14"
|
|
30
|
+
fill="none"
|
|
31
|
+
className={cx(
|
|
32
|
+
"pointer-events-none absolute text-fg-white opacity-0 transition-inherit-all",
|
|
33
|
+
size === "sm" && "size-2.5",
|
|
34
|
+
size === "md" && "size-3",
|
|
35
|
+
size === "lg" && "size-3.5",
|
|
36
|
+
isSelected && "opacity-100",
|
|
37
|
+
isDisabled && "text-fg-disabled_subtle",
|
|
38
|
+
)}
|
|
39
|
+
>
|
|
40
|
+
<path d="M11.6666 3.5L5.24992 9.91667L2.33325 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
|
41
|
+
</svg>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
TagCheckbox.displayName = "TagCheckbox";
|
|
46
|
+
|
|
47
|
+
export default TagCheckbox;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { RefAttributes } from "react";
|
|
4
|
+
import { XClose } from "@untitledui/icons";
|
|
5
|
+
import { Button as AriaButton, type ButtonProps } from "react-aria-components";
|
|
6
|
+
import { cx } from "@/components/utils";
|
|
7
|
+
|
|
8
|
+
interface TagCloseXProps extends ButtonProps, RefAttributes<HTMLButtonElement> {
|
|
9
|
+
size?: "sm" | "md" | "lg";
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const styles = {
|
|
14
|
+
sm: { root: "p-0.5", icon: "size-2.5" },
|
|
15
|
+
md: { root: "p-0.5", icon: "size-3" },
|
|
16
|
+
lg: { root: "p-[3px]", icon: "size-3.5" },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const TagCloseX = ({ size = "md", className, isDisabled, ...otherProps }: TagCloseXProps) => {
|
|
20
|
+
return (
|
|
21
|
+
<AriaButton
|
|
22
|
+
slot="remove"
|
|
23
|
+
aria-label="Remove this tag"
|
|
24
|
+
className={cx(
|
|
25
|
+
"flex cursor-pointer rounded-[3px] text-fg-quaternary outline-focus-ring transition duration-100 ease-linear hover:bg-primary_hover hover:text-fg-quaternary_hover focus:outline-2 focus:outline-offset-2 disabled:cursor-not-allowed",
|
|
26
|
+
styles[size].root,
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
29
|
+
{...otherProps}
|
|
30
|
+
>
|
|
31
|
+
<XClose className={cx("transition-inherit-all", styles[size].icon)} strokeWidth="3" />
|
|
32
|
+
</AriaButton>
|
|
33
|
+
);
|
|
34
|
+
};
|