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.
Files changed (121) hide show
  1. package/dist/commands/add.js +339 -0
  2. package/dist/commands/init.js +436 -0
  3. package/dist/helper/download-tar-api.js +129 -0
  4. package/dist/helper/download-tar.js +81 -0
  5. package/dist/helper/find-css-file.js +19 -0
  6. package/dist/helper/formatText.js +37 -0
  7. package/dist/helper/get-components-api.js +47 -0
  8. package/dist/helper/get-components-list.js +62 -0
  9. package/dist/helper/get-components.js +19 -0
  10. package/dist/helper/get-config.js +163 -0
  11. package/dist/helper/get-package-info.js +99 -0
  12. package/dist/helper/get-pkg-manager.js +16 -0
  13. package/dist/helper/get-project.js +176 -0
  14. package/dist/helper/install-template.js +29 -0
  15. package/dist/helper/match-color-css.js +82 -0
  16. package/dist/helper/update-color-css.js +134 -0
  17. package/dist/index.js +25 -0
  18. package/dist/package.json +50 -0
  19. package/dist/res/components.json +520 -0
  20. package/dist/res/config.json +3 -0
  21. package/package.json +61 -0
  22. package/templates/default/.prettierrc +10 -0
  23. package/templates/default/README.md +36 -0
  24. package/templates/default/eslint.config.mjs +58 -0
  25. package/templates/default/next.config.ts +6 -0
  26. package/templates/default/package.json +57 -0
  27. package/templates/default/postcss.config.js +5 -0
  28. package/templates/default/public/favicon.ico +0 -0
  29. package/templates/default/public/marketing/smiling-girl.png +0 -0
  30. package/templates/default/public/marketing/spirals.webp +0 -0
  31. package/templates/default/src/app/home-screen.tsx +109 -0
  32. package/templates/default/src/app/layout.tsx +42 -0
  33. package/templates/default/src/app/not-found.tsx +40 -0
  34. package/templates/default/src/app/page.tsx +3 -0
  35. package/templates/default/src/components/foundations/dot-icon.tsx +27 -0
  36. package/templates/default/src/components/foundations/featured-icon/featured-icons.tsx +153 -0
  37. package/templates/default/src/components/foundations/logo/UntitledLogo.tsx +63 -0
  38. package/templates/default/src/components/foundations/logo/UntitledLogoMinimal.tsx +164 -0
  39. package/templates/default/src/components/foundations/payment-icons/amex-icon.tsx +19 -0
  40. package/templates/default/src/components/foundations/payment-icons/apple-pay-icon.tsx +27 -0
  41. package/templates/default/src/components/foundations/payment-icons/discover-icon.tsx +34 -0
  42. package/templates/default/src/components/foundations/payment-icons/index.tsx +10 -0
  43. package/templates/default/src/components/foundations/payment-icons/mastercard-icon.tsx +39 -0
  44. package/templates/default/src/components/foundations/payment-icons/paypal-icon.tsx +45 -0
  45. package/templates/default/src/components/foundations/payment-icons/stripe-icon.tsx +27 -0
  46. package/templates/default/src/components/foundations/payment-icons/union-pay-icon.tsx +37 -0
  47. package/templates/default/src/components/foundations/payment-icons/visa-icon.tsx +27 -0
  48. package/templates/default/src/components/marketing/header-navigation/base-components/nav-menu-item.tsx +41 -0
  49. package/templates/default/src/components/marketing/header-navigation/components/header.tsx +245 -0
  50. package/templates/default/src/components/marketing/header-navigation/dropdown-header-navigation.tsx +53 -0
  51. package/templates/default/src/components/shared/avatar/avatar-label-group.tsx +32 -0
  52. package/templates/default/src/components/shared/avatar/avatar-profile-photo.tsx +84 -0
  53. package/templates/default/src/components/shared/avatar/avatar.tsx +131 -0
  54. package/templates/default/src/components/shared/avatar/base-components/avatar-add-button.tsx +33 -0
  55. package/templates/default/src/components/shared/avatar/base-components/avatar-company-icon.tsx +26 -0
  56. package/templates/default/src/components/shared/avatar/base-components/avatar-online-indicator.tsx +31 -0
  57. package/templates/default/src/components/shared/avatar/base-components/index.ts +4 -0
  58. package/templates/default/src/components/shared/avatar/base-components/verified-tick.tsx +34 -0
  59. package/templates/default/src/components/shared/avatar/utils.ts +12 -0
  60. package/templates/default/src/components/shared/badges/badge-groups.tsx +176 -0
  61. package/templates/default/src/components/shared/badges/badge-types.ts +264 -0
  62. package/templates/default/src/components/shared/badges/badges.tsx +479 -0
  63. package/templates/default/src/components/shared/button-group/button-group.tsx +97 -0
  64. package/templates/default/src/components/shared/buttons/app-store-buttons-outline.tsx +454 -0
  65. package/templates/default/src/components/shared/buttons/app-store-buttons.tsx +806 -0
  66. package/templates/default/src/components/shared/buttons/button-utility.tsx +87 -0
  67. package/templates/default/src/components/shared/buttons/button.tsx +284 -0
  68. package/templates/default/src/components/shared/buttons/close-button.tsx +39 -0
  69. package/templates/default/src/components/shared/buttons/social-button.tsx +135 -0
  70. package/templates/default/src/components/shared/buttons/social-logos.tsx +115 -0
  71. package/templates/default/src/components/shared/checkbox/checkbox.tsx +120 -0
  72. package/templates/default/src/components/shared/dropdown/dropdown.tsx +138 -0
  73. package/templates/default/src/components/shared/input-dropdown/combobox.tsx +161 -0
  74. package/templates/default/src/components/shared/input-dropdown/dropdown-item.tsx +98 -0
  75. package/templates/default/src/components/shared/input-dropdown/input-dropdown.tsx +172 -0
  76. package/templates/default/src/components/shared/input-dropdown/multi-select.tsx +373 -0
  77. package/templates/default/src/components/shared/input-dropdown/popover.tsx +36 -0
  78. package/templates/default/src/components/shared/input-dropdown/select.tsx +63 -0
  79. package/templates/default/src/components/shared/inputs/file-upload-trigger.tsx +74 -0
  80. package/templates/default/src/components/shared/inputs/form/form.tsx +10 -0
  81. package/templates/default/src/components/shared/inputs/hint-text.tsx +34 -0
  82. package/templates/default/src/components/shared/inputs/input/index.tsx +189 -0
  83. package/templates/default/src/components/shared/inputs/input/input-payment.tsx +134 -0
  84. package/templates/default/src/components/shared/inputs/input/input-with-button.tsx +69 -0
  85. package/templates/default/src/components/shared/inputs/input/input-with-dropdown.tsx +178 -0
  86. package/templates/default/src/components/shared/inputs/input/input-with-prefix.tsx +74 -0
  87. package/templates/default/src/components/shared/inputs/label.tsx +46 -0
  88. package/templates/default/src/components/shared/inputs/textarea/textarea.tsx +82 -0
  89. package/templates/default/src/components/shared/progress-indicators/progress-circles.tsx +176 -0
  90. package/templates/default/src/components/shared/progress-indicators/progress-indicators.tsx +86 -0
  91. package/templates/default/src/components/shared/progress-indicators/simple-circle.tsx +29 -0
  92. package/templates/default/src/components/shared/radio-buttons/radio-buttons.tsx +125 -0
  93. package/templates/default/src/components/shared/radio-groups/radio-group-avatar.tsx +62 -0
  94. package/templates/default/src/components/shared/radio-groups/radio-group-checkbox.tsx +72 -0
  95. package/templates/default/src/components/shared/radio-groups/radio-group-icon-card.tsx +95 -0
  96. package/templates/default/src/components/shared/radio-groups/radio-group-icon-simple.tsx +70 -0
  97. package/templates/default/src/components/shared/radio-groups/radio-group-payment-icon.tsx +71 -0
  98. package/templates/default/src/components/shared/radio-groups/radio-group-radio-button.tsx +76 -0
  99. package/templates/default/src/components/shared/radio-groups/radio-groups.tsx +8 -0
  100. package/templates/default/src/components/shared/slider/slider.tsx +76 -0
  101. package/templates/default/src/components/shared/tags/base-components/tag-checkbox.tsx +47 -0
  102. package/templates/default/src/components/shared/tags/base-components/tag-close-x.tsx +34 -0
  103. package/templates/default/src/components/shared/tags/tags.tsx +162 -0
  104. package/templates/default/src/components/shared/toggle/toggle.tsx +140 -0
  105. package/templates/default/src/components/shared/tooltips/tooltips.tsx +140 -0
  106. package/templates/default/src/components/utils/index.ts +48 -0
  107. package/templates/default/src/components/utils/isDeepEqual.ts +31 -0
  108. package/templates/default/src/components/utils/isReactComponent.ts +22 -0
  109. package/templates/default/src/components/utils/mergeRefs.ts +19 -0
  110. package/templates/default/src/components/utils/useBreakpoint.ts +36 -0
  111. package/templates/default/src/components/utils/uuid.ts +9 -0
  112. package/templates/default/src/fonts/GeistMonoVF.woff +0 -0
  113. package/templates/default/src/fonts/GeistVF.woff +0 -0
  114. package/templates/default/src/hooks/use-resize-observer.tsx +55 -0
  115. package/templates/default/src/providers/theme.tsx +11 -0
  116. package/templates/default/src/styles/colors.css +805 -0
  117. package/templates/default/src/styles/globals.css +86 -0
  118. package/templates/default/src/styles/text-styles.css +177 -0
  119. package/templates/default/src/styles/theme.css +1310 -0
  120. package/templates/default/src/styles/typography.css +428 -0
  121. 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
+ };