untitledui 0.1.5 → 0.1.8

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 (101) hide show
  1. package/config/postcss.config.mjs +6 -0
  2. package/{templates/default/src → config}/styles/globals.css +2 -33
  3. package/{templates/default/src → config}/styles/theme.css +31 -33
  4. package/{templates/default/src → config}/styles/typography.css +24 -24
  5. package/dist/index.mjs +54 -15
  6. package/package.json +4 -3
  7. package/templates/default/.prettierrc +0 -10
  8. package/templates/default/README.md +0 -36
  9. package/templates/default/eslint.config.mjs +0 -58
  10. package/templates/default/next.config.ts +0 -6
  11. package/templates/default/package.json +0 -56
  12. package/templates/default/postcss.config.js +0 -5
  13. package/templates/default/public/favicon.ico +0 -0
  14. package/templates/default/public/marketing/smiling-girl.png +0 -0
  15. package/templates/default/public/marketing/spirals.webp +0 -0
  16. package/templates/default/src/app/home-screen.tsx +0 -108
  17. package/templates/default/src/app/layout.tsx +0 -34
  18. package/templates/default/src/app/not-found.tsx +0 -40
  19. package/templates/default/src/app/page.tsx +0 -3
  20. package/templates/default/src/components/foundations/dot-icon.tsx +0 -27
  21. package/templates/default/src/components/foundations/featured-icon/featured-icons.tsx +0 -153
  22. package/templates/default/src/components/foundations/logo/UntitledLogo.tsx +0 -63
  23. package/templates/default/src/components/foundations/logo/UntitledLogoMinimal.tsx +0 -164
  24. package/templates/default/src/components/foundations/payment-icons/amex-icon.tsx +0 -19
  25. package/templates/default/src/components/foundations/payment-icons/apple-pay-icon.tsx +0 -27
  26. package/templates/default/src/components/foundations/payment-icons/discover-icon.tsx +0 -34
  27. package/templates/default/src/components/foundations/payment-icons/index.tsx +0 -10
  28. package/templates/default/src/components/foundations/payment-icons/mastercard-icon.tsx +0 -39
  29. package/templates/default/src/components/foundations/payment-icons/paypal-icon.tsx +0 -45
  30. package/templates/default/src/components/foundations/payment-icons/stripe-icon.tsx +0 -27
  31. package/templates/default/src/components/foundations/payment-icons/union-pay-icon.tsx +0 -37
  32. package/templates/default/src/components/foundations/payment-icons/visa-icon.tsx +0 -27
  33. package/templates/default/src/components/marketing/header-navigation/base-components/nav-menu-item.tsx +0 -41
  34. package/templates/default/src/components/marketing/header-navigation/components/header.tsx +0 -245
  35. package/templates/default/src/components/marketing/header-navigation/dropdown-header-navigation.tsx +0 -53
  36. package/templates/default/src/components/shared/avatar/avatar-label-group.tsx +0 -32
  37. package/templates/default/src/components/shared/avatar/avatar-profile-photo.tsx +0 -84
  38. package/templates/default/src/components/shared/avatar/avatar.tsx +0 -131
  39. package/templates/default/src/components/shared/avatar/base-components/avatar-add-button.tsx +0 -33
  40. package/templates/default/src/components/shared/avatar/base-components/avatar-company-icon.tsx +0 -26
  41. package/templates/default/src/components/shared/avatar/base-components/avatar-online-indicator.tsx +0 -31
  42. package/templates/default/src/components/shared/avatar/base-components/index.ts +0 -4
  43. package/templates/default/src/components/shared/avatar/base-components/verified-tick.tsx +0 -34
  44. package/templates/default/src/components/shared/avatar/utils.ts +0 -12
  45. package/templates/default/src/components/shared/badges/badge-groups.tsx +0 -176
  46. package/templates/default/src/components/shared/badges/badge-types.ts +0 -264
  47. package/templates/default/src/components/shared/badges/badges.tsx +0 -479
  48. package/templates/default/src/components/shared/button-group/button-group.tsx +0 -97
  49. package/templates/default/src/components/shared/buttons/app-store-buttons-outline.tsx +0 -454
  50. package/templates/default/src/components/shared/buttons/app-store-buttons.tsx +0 -806
  51. package/templates/default/src/components/shared/buttons/button-utility.tsx +0 -87
  52. package/templates/default/src/components/shared/buttons/button.tsx +0 -285
  53. package/templates/default/src/components/shared/buttons/close-button.tsx +0 -39
  54. package/templates/default/src/components/shared/buttons/social-button.tsx +0 -135
  55. package/templates/default/src/components/shared/buttons/social-logos.tsx +0 -115
  56. package/templates/default/src/components/shared/checkbox/checkbox.tsx +0 -120
  57. package/templates/default/src/components/shared/dropdown/dropdown.tsx +0 -147
  58. package/templates/default/src/components/shared/file-upload-trigger/file-upload-trigger.tsx +0 -74
  59. package/templates/default/src/components/shared/form/form.tsx +0 -10
  60. package/templates/default/src/components/shared/form/hook-form.tsx +0 -75
  61. package/templates/default/src/components/shared/input/hint-text.tsx +0 -34
  62. package/templates/default/src/components/shared/input/index.tsx +0 -189
  63. package/templates/default/src/components/shared/input/input-payment.tsx +0 -134
  64. package/templates/default/src/components/shared/input/input-with-button.tsx +0 -69
  65. package/templates/default/src/components/shared/input/input-with-dropdown.tsx +0 -178
  66. package/templates/default/src/components/shared/input/input-with-prefix.tsx +0 -74
  67. package/templates/default/src/components/shared/input/label.tsx +0 -46
  68. package/templates/default/src/components/shared/progress-indicators/progress-circles.tsx +0 -176
  69. package/templates/default/src/components/shared/progress-indicators/progress-indicators.tsx +0 -86
  70. package/templates/default/src/components/shared/progress-indicators/simple-circle.tsx +0 -29
  71. package/templates/default/src/components/shared/radio-buttons/radio-buttons.tsx +0 -125
  72. package/templates/default/src/components/shared/radio-groups/radio-group-avatar.tsx +0 -62
  73. package/templates/default/src/components/shared/radio-groups/radio-group-checkbox.tsx +0 -72
  74. package/templates/default/src/components/shared/radio-groups/radio-group-icon-card.tsx +0 -95
  75. package/templates/default/src/components/shared/radio-groups/radio-group-icon-simple.tsx +0 -70
  76. package/templates/default/src/components/shared/radio-groups/radio-group-payment-icon.tsx +0 -71
  77. package/templates/default/src/components/shared/radio-groups/radio-group-radio-button.tsx +0 -76
  78. package/templates/default/src/components/shared/radio-groups/radio-groups.tsx +0 -8
  79. package/templates/default/src/components/shared/select/combobox.tsx +0 -161
  80. package/templates/default/src/components/shared/select/multi-select.tsx +0 -373
  81. package/templates/default/src/components/shared/select/popover.tsx +0 -36
  82. package/templates/default/src/components/shared/select/select-item.tsx +0 -70
  83. package/templates/default/src/components/shared/select/select-native.tsx +0 -63
  84. package/templates/default/src/components/shared/select/select.tsx +0 -143
  85. package/templates/default/src/components/shared/slider/slider.tsx +0 -76
  86. package/templates/default/src/components/shared/tags/base-components/tag-checkbox.tsx +0 -47
  87. package/templates/default/src/components/shared/tags/base-components/tag-close-x.tsx +0 -34
  88. package/templates/default/src/components/shared/tags/tags.tsx +0 -162
  89. package/templates/default/src/components/shared/textarea/textarea.tsx +0 -82
  90. package/templates/default/src/components/shared/toggle/toggle.tsx +0 -140
  91. package/templates/default/src/components/shared/tooltips/tooltips.tsx +0 -140
  92. package/templates/default/src/components/utils/index.ts +0 -48
  93. package/templates/default/src/components/utils/isDeepEqual.ts +0 -31
  94. package/templates/default/src/components/utils/isReactComponent.ts +0 -22
  95. package/templates/default/src/components/utils/mergeRefs.ts +0 -19
  96. package/templates/default/src/components/utils/useBreakpoint.ts +0 -36
  97. package/templates/default/src/components/utils/uuid.ts +0 -9
  98. package/templates/default/src/hooks/use-resize-observer.tsx +0 -55
  99. package/templates/default/src/providers/theme.tsx +0 -11
  100. package/templates/default/src/styles/text-styles.css +0 -177
  101. package/templates/default/tsconfig.json +0 -27
@@ -1,134 +0,0 @@
1
- "use client";
2
-
3
- import React, { useState } from "react";
4
- import { TextField } from "react-aria-components";
5
- import { AmexIcon, DiscoverIcon, MastercardIcon, UnionPayIcon, VisaIcon } from "@/components/foundations/payment-icons";
6
- import type { InputBaseProps } from "@/components/shared/input";
7
- import { InputBase } from "@/components/shared/input";
8
- import HintText from "@/components/shared/input/hint-text";
9
- import Label from "@/components/shared/input/label";
10
- import { cx } from "@/components/utils/cx";
11
-
12
- const cardTypes = [
13
- {
14
- name: "Visa",
15
- pattern: /^4[0-9]{3,}$/, // Visa card numbers start with 4 and are 13 or 16 digits long
16
- card: "visa",
17
- icon: VisaIcon,
18
- },
19
- {
20
- name: "MasterCard",
21
- pattern: /^5[1-5][0-9]{2,}$/, // MasterCard numbers start with 51-55 and are 16 digits long
22
- card: "mastercard",
23
- icon: MastercardIcon,
24
- },
25
- {
26
- name: "American Express",
27
- pattern: /^3[47][0-9]{2,}$/, // American Express numbers start with 34 or 37 and are 15 digits long
28
- card: "amex",
29
- icon: AmexIcon,
30
- },
31
- {
32
- name: "Discover",
33
- pattern: /^6(?:011|5[0-9]{2}|4[4-9][0-9])[0-9]{12}$/, // Discover card numbers start with 6011 or 65 and are 16 digits long
34
- card: "discover",
35
- icon: DiscoverIcon,
36
- },
37
- {
38
- name: "UnionPay",
39
- pattern: /^(62|88)[0-9]{14,17}$/, // UnionPay card numbers start with 62 or 88 and are between 15-19 digits long
40
- card: "unionpay",
41
- icon: UnionPayIcon,
42
- },
43
- {
44
- name: "Unknown",
45
- pattern: /.*/, // Fallback pattern for unknown cards
46
- card: "unknown",
47
- icon: MastercardIcon,
48
- },
49
- ];
50
-
51
- /**
52
- * Detect the card type based on the card number.
53
- * @param number The card number to detect the type for.
54
- * @returns The matching card type object.
55
- */
56
- const detectCardType = (number: string) => {
57
- // Remove all spaces
58
- const sanitizedNumber = number.replace(/\D/g, "");
59
-
60
- // Find the matching card type
61
- const card = cardTypes.find((cardType) => cardType.pattern.test(sanitizedNumber));
62
-
63
- return card || cardTypes[cardTypes.length - 1];
64
- };
65
-
66
- /**
67
- * Format the card number in groups of 4 digits (i.e. 1234 5678 9012 3456).
68
- */
69
- export const formatCardNumber = (number: string) => {
70
- // Remove non-numeric characters
71
- const cleaned = number.replace(/\D/g, "");
72
-
73
- // Format the card number in groups of 4 digits
74
- const match = cleaned.match(/\d{1,4}/g);
75
-
76
- if (match) {
77
- return match.join(" ");
78
- }
79
-
80
- return cleaned;
81
- };
82
-
83
- interface PaymentInputProps extends Omit<InputBaseProps, "icon"> {}
84
-
85
- export const PaymentInput = ({ onChange, className, maxLength = 19, label, hint, ...props }: PaymentInputProps) => {
86
- const [cardNumber, setCardNumber] = useState(formatCardNumber(props.value || props.defaultValue || ""));
87
-
88
- const handleCardNumberChange = (value: string) => {
89
- // Remove all non-numeric characters
90
- value = value.replace(/\D/g, "");
91
-
92
- // Return if the value is empty
93
- if (!value) {
94
- setCardNumber("");
95
- onChange?.("");
96
- return "";
97
- }
98
-
99
- // Format the card number in groups of 4 digits
100
- const formatted = formatCardNumber(value);
101
-
102
- setCardNumber(formatted);
103
- onChange?.(value);
104
- return value;
105
- };
106
-
107
- const card = detectCardType(cardNumber);
108
-
109
- return (
110
- <TextField
111
- aria-label={!label ? props?.placeholder : undefined}
112
- {...props}
113
- value={cardNumber}
114
- inputMode="numeric"
115
- maxLength={maxLength}
116
- onChange={handleCardNumberChange}
117
- className={(state) =>
118
- cx("flex h-max w-full flex-col items-start justify-start gap-1.5", typeof className === "function" ? className(state) : className)
119
- }
120
- >
121
- {({ isDisabled, isInvalid, isRequired }) => (
122
- <>
123
- {label && <Label {...{ isRequired }}>{label}</Label>}
124
-
125
- <InputBase {...props} {...{ isDisabled, isInvalid }} icon={card.icon} inputClassName="pl-13" iconClassName="left-2.5 size-max" />
126
-
127
- {hint && <HintText {...{ isInvalid }}>{hint}</HintText>}
128
- </>
129
- )}
130
- </TextField>
131
- );
132
- };
133
-
134
- PaymentInput.displayName = "PaymentInput";
@@ -1,69 +0,0 @@
1
- "use client";
2
-
3
- import type { ReactNode } from "react";
4
- import React from "react";
5
- import { Copy01 } from "@untitledui/icons";
6
- import { TextField } from "react-aria-components";
7
- import type { CommonProps } from "@/components/shared/buttons/button";
8
- import Button from "@/components/shared/buttons/button";
9
- import type { InputBaseProps } from "@/components/shared/input";
10
- import { InputBase } from "@/components/shared/input";
11
- import HintText from "@/components/shared/input/hint-text";
12
- import Label from "@/components/shared/input/label";
13
- import { cx } from "@/components/utils/cx";
14
-
15
- interface InputWithButtonProps extends Omit<InputBaseProps, "icon"> {
16
- buttonText: string;
17
- onClick?: () => void;
18
- buttonColor?: CommonProps["color"];
19
- iconLeading?: CommonProps["iconLeading"];
20
- iconTrailing?: CommonProps["iconTrailing"];
21
- hint?: ReactNode;
22
- }
23
-
24
- export const InputWithButton = ({
25
- size = "sm",
26
- buttonColor = "secondary",
27
- iconLeading = Copy01,
28
- onClick,
29
- className,
30
- buttonText,
31
- label,
32
- hint,
33
- ...props
34
- }: InputWithButtonProps) => {
35
- return (
36
- <TextField
37
- aria-label={!label ? props?.placeholder : undefined}
38
- {...props}
39
- className={(state) =>
40
- cx("flex h-max w-full flex-col items-start justify-start gap-1.5", typeof className === "function" ? className(state) : className)
41
- }
42
- >
43
- {({ isDisabled, isInvalid, isRequired }) => (
44
- <>
45
- {label && <Label {...{ isRequired }}>{label}</Label>}
46
-
47
- <div className="flex h-max w-full flex-row justify-center">
48
- <InputBase {...props} {...{ isDisabled, isInvalid }} wrapperClassName="rounded-r-none z-10" />
49
-
50
- {/* TODO: Take this button out of here and move it to be a prop so the user can fully control the button. */}
51
- <Button
52
- onClick={onClick}
53
- color={buttonColor}
54
- iconLeading={iconLeading}
55
- size={size === "sm" ? "md" : "lg"}
56
- className="-ml-px rounded-l-none shadow-xs! ring-1 ring-border-primary ring-inset focus:z-10"
57
- >
58
- {buttonText}
59
- </Button>
60
- </div>
61
-
62
- {hint && <HintText {...{ isInvalid }}>{hint}</HintText>}
63
- </>
64
- )}
65
- </TextField>
66
- );
67
- };
68
-
69
- InputWithButton.displayName = "InputWithButton";
@@ -1,178 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { ChevronDown } from "@untitledui/icons";
5
- import type { Key } from "react-aria-components";
6
- import { TextField } from "react-aria-components";
7
- import type { InputBaseProps } from "@/components/shared/input";
8
- import { InputBase } from "@/components/shared/input";
9
- import HintText from "@/components/shared/input/hint-text";
10
- import Label from "@/components/shared/input/label";
11
- import { cx, sortCx } from "@/components/utils/cx";
12
-
13
- interface SelectorComponentProps {
14
- size: "sm" | "md";
15
- options: Option[];
16
- selectedKey?: Key;
17
- className?: string;
18
- isInvalid?: boolean;
19
- selectName?: string;
20
- isDisabled?: boolean;
21
- iconClassName?: string;
22
- onSelectionChange?: (value: Key) => void;
23
- }
24
-
25
- const SelectorComponent = (props: SelectorComponentProps) => {
26
- return (
27
- <div className="relative inline-flex h-full items-center">
28
- <select
29
- name={props.selectName}
30
- value={props.selectedKey}
31
- disabled={props.isDisabled}
32
- autoComplete="input-dropdown"
33
- onChange={(e) => props.onSelectionChange?.(e.target.value)}
34
- className={cx(
35
- "flex h-full w-min appearance-none items-center gap-1 bg-inherit px-3 py-2 tt-md text-tertiary outline-hidden ring-inset focus:ring-2 focus:ring-border-brand disabled:cursor-not-allowed",
36
- props.isInvalid && "focus:ring-2 focus:ring-border-error",
37
- props.isDisabled && "text-disabled",
38
- props.className,
39
- )}
40
- >
41
- {props.options.map((option) => (
42
- <option key={option.value} value={option.value}>
43
- {option.label}
44
- </option>
45
- ))}
46
- </select>
47
- <ChevronDown className={cx("pointer-events-none absolute right-0 size-4 stroke-[2.625px] text-fg-quaternary", props.iconClassName)} />
48
- </div>
49
- );
50
- };
51
-
52
- type Option = {
53
- label: string;
54
- value: string | number;
55
- };
56
-
57
- interface InputWithDropdownProps extends Omit<InputBaseProps, "icon"> {
58
- selectedKey?: Key;
59
- selectName?: string;
60
- leadingText?: string;
61
- leadingOptions?: Option[];
62
- trailingOptions?: Option[];
63
- onSelectionChange?: (value: Key) => void;
64
- }
65
-
66
- export const InputWithDropdown = ({
67
- size = "sm",
68
- leadingOptions,
69
- trailingOptions,
70
- leadingText,
71
- className,
72
- selectedKey,
73
- label,
74
- hint,
75
- onChange,
76
- onSelectionChange,
77
- ...props
78
- }: InputWithDropdownProps) => {
79
- const hasLeadingDropdown = !!leadingOptions?.length;
80
- const hasTrailingDropdown = !!trailingOptions?.length;
81
-
82
- const paddings = sortCx({
83
- sm: {
84
- input: cx(hasLeadingDropdown && "px-2.5 pl-2.5", hasTrailingDropdown && (leadingText ? "pr-6! pl-0" : "pr-6! pl-3")),
85
- leadingText: "pl-3",
86
- dropdownLeading: "rounded-l-lg py-2 pr-4.5 pl-3",
87
- dropdownTrailing: "rounded-r-lg py-2 pr-9 pl-3",
88
- dropdownTrailingIcon: "right-3",
89
- },
90
- md: {
91
- input: cx(hasLeadingDropdown && "px-3 pl-3", hasTrailingDropdown && (leadingText ? "pr-6! pl-0!" : "pr-6! pl-3!")),
92
- leadingText: "pl-3.5",
93
- dropdownLeading: "rounded-l-lg py-2.5 pr-4.5 pl-3.5",
94
- dropdownTrailing: "rounded-r-lg py-2.5 pr-9.5 pl-3.5",
95
- dropdownTrailingIcon: "right-3.5",
96
- },
97
- });
98
-
99
- return (
100
- <TextField
101
- aria-label={!label ? props?.placeholder : undefined}
102
- {...props}
103
- className={(state) =>
104
- cx("flex h-max w-full flex-col items-start justify-start gap-1.5", typeof className === "function" ? className(state) : className)
105
- }
106
- >
107
- {({ isDisabled, isInvalid, isRequired }) => (
108
- <>
109
- {label && <Label {...{ isRequired }}>{label}</Label>}
110
-
111
- <div
112
- className={cx(
113
- "relative flex h-max w-full flex-row justify-start rounded-lg bg-primary shadow-xs ring-1 ring-border-primary transition-all duration-100 ease-linear ring-inset",
114
-
115
- // Only apply focus ring when child input is focused
116
- "has-[input:focus]:ring-2 has-[input:focus]:ring-border-brand",
117
-
118
- isDisabled && "cursor-not-allowed bg-disabled_subtle ring-border-disabled",
119
- isInvalid && "ring-border-error_subtle has-[input:focus]:ring-border-error",
120
- )}
121
- >
122
- {/* leading dropdown with padding style */}
123
- {hasLeadingDropdown && (
124
- <SelectorComponent
125
- {...{
126
- size,
127
- isInvalid,
128
- isDisabled,
129
- selectedKey,
130
- onSelectionChange,
131
- options: leadingOptions,
132
- className: paddings[size].dropdownLeading,
133
- name: props.selectName,
134
- }}
135
- />
136
- )}
137
-
138
- {/* leading text if trailing dropdown is applied */}
139
- {leadingText && (
140
- <span className={cx("my-auto grow pr-2", paddings[size].leadingText)}>
141
- <p className={cx("tt-md text-tertiary", isDisabled && "text-disabled")}>{leadingText}</p>
142
- </span>
143
- )}
144
-
145
- <InputBase
146
- {...props}
147
- {...{ isDisabled, isInvalid }}
148
- wrapperClassName={cx("bg-transparent shadow-none! ring-0 focus-within:ring-0")}
149
- inputClassName={cx(paddings[size].input)}
150
- tooltipClassName={cx(hasTrailingDropdown && "right-0")}
151
- />
152
-
153
- {/* trailing dropdown with padding style */}
154
- {hasTrailingDropdown && (
155
- <SelectorComponent
156
- {...{
157
- size,
158
- isInvalid,
159
- isDisabled,
160
- selectedKey,
161
- onSelectionChange,
162
- options: trailingOptions,
163
- iconClassName: paddings[size].dropdownTrailingIcon,
164
- className: paddings[size].dropdownTrailing,
165
- name: props.selectName,
166
- }}
167
- />
168
- )}
169
- </div>
170
-
171
- {hint && <HintText {...{ isInvalid }}>{hint}</HintText>}
172
- </>
173
- )}
174
- </TextField>
175
- );
176
- };
177
-
178
- InputWithDropdown.displayName = "InputWithDropdown";
@@ -1,74 +0,0 @@
1
- "use client";
2
-
3
- import type { HTMLAttributes } from "react";
4
- import type { InputBaseProps } from "@/components/shared/input";
5
- import { InputBase, TextField } from "@/components/shared/input";
6
- import HintText from "@/components/shared/input/hint-text";
7
- import Label from "@/components/shared/input/label";
8
- import { cx } from "@/components/utils/cx";
9
-
10
- interface InputPrefixProps extends HTMLAttributes<HTMLDivElement> {
11
- position?: "leading" | "trailing";
12
- size?: "sm" | "md";
13
- isDisabled?: boolean;
14
- }
15
-
16
- export const InputPrefix = ({ position = "leading", size = "sm", isDisabled, children, ...props }: InputPrefixProps) => {
17
- const styles = {
18
- sm: "px-3 py-2",
19
- md: "py-2.5 pl-3.5 pr-3",
20
- };
21
-
22
- return (
23
- <div
24
- {...props}
25
- className={cx(
26
- "flex tt-md text-tertiary shadow-xs ring-1 ring-border-primary ring-inset",
27
- styles[size],
28
- position === "leading" && "-mr-px rounded-l-lg",
29
- position === "trailing" && "-ml-px rounded-r-lg",
30
-
31
- // Disabled state
32
- isDisabled && "border-disabled bg-disabled_subtle text-tertiary",
33
- "group-disabled:border-disabled group-disabled:bg-disabled_subtle group-disabled:text-tertiary",
34
-
35
- props.className,
36
- )}
37
- >
38
- {children}
39
- </div>
40
- );
41
- };
42
-
43
- interface InputWithPrefixProps extends Omit<InputBaseProps, "icon"> {
44
- leadingText?: string;
45
- trailingText?: string;
46
- }
47
-
48
- export const InputWithPrefix = ({ size = "sm", placeholder, leadingText, trailingText, className, label, hint, ...props }: InputWithPrefixProps) => {
49
- return (
50
- <TextField aria-label={!label ? placeholder : undefined} {...props} className={className}>
51
- {label && <Label>{label}</Label>}
52
-
53
- <div className="flex w-full">
54
- {leadingText && (
55
- <InputPrefix position="leading" size={size}>
56
- {leadingText}
57
- </InputPrefix>
58
- )}
59
-
60
- <InputBase {...props} {...{ size, placeholder }} wrapperClassName={cx(trailingText && "rounded-r-none", leadingText && "rounded-l-none")} />
61
-
62
- {trailingText && (
63
- <InputPrefix position="trailing" size={size}>
64
- {trailingText}
65
- </InputPrefix>
66
- )}
67
- </div>
68
-
69
- {hint && <HintText>{hint}</HintText>}
70
- </TextField>
71
- );
72
- };
73
-
74
- InputWithPrefix.displayName = "InputWithPrefix";
@@ -1,46 +0,0 @@
1
- "use client";
2
-
3
- import type { ReactNode, Ref } from "react";
4
- import { HelpCircle } from "@untitledui/icons";
5
- import type { LabelProps as AriaLabelProps } from "react-aria-components";
6
- import { Label as AriaLabel } from "react-aria-components";
7
- import { Tooltip, TooltipTrigger } from "@/components/shared/tooltips/tooltips";
8
- import { cx } from "@/components/utils/cx";
9
-
10
- interface LabelProps extends AriaLabelProps {
11
- children: ReactNode;
12
- isRequired?: boolean;
13
- tooltip?: string;
14
- tooltipDescription?: string;
15
- ref?: Ref<HTMLLabelElement>;
16
- }
17
-
18
- const Label = ({ isRequired, tooltip, tooltipDescription, className, ...props }: LabelProps) => {
19
- return (
20
- <AriaLabel
21
- // Used for conditionally hiding/showing the label element via CSS:
22
- // <Input label="Visible only on mobile" className="lg:**:data-label:hidden" />
23
- // or
24
- // <Input label="Visible only on mobile" className="lg:label:hidden" />
25
- data-label="true"
26
- {...props}
27
- className={cx("flex w-full cursor-default items-center gap-0.5 tt-sm-md text-secondary", className)}
28
- >
29
- {props.children}
30
-
31
- <span className={cx("hidden text-brand-tertiary", isRequired && "block", typeof isRequired === "undefined" && "group-required:block")}>*</span>
32
-
33
- {tooltip && (
34
- <Tooltip title={tooltip} description={tooltipDescription} placement="top">
35
- <TooltipTrigger className="cursor-pointer text-fg-quaternary transition duration-200 hover:text-fg-quaternary_hover focus:text-fg-quaternary_hover">
36
- <HelpCircle className="size-4" />
37
- </TooltipTrigger>
38
- </Tooltip>
39
- )}
40
- </AriaLabel>
41
- );
42
- };
43
-
44
- Label.displayName = "Label";
45
-
46
- export default Label;
@@ -1,176 +0,0 @@
1
- "use client";
2
-
3
- import { cx as clx, sortCx } from "@/components/utils/cx";
4
-
5
- interface ProgressBarProps {
6
- value: number;
7
- min?: number;
8
- max?: number;
9
- size: "xxs" | "xs" | "sm" | "md" | "lg";
10
- label?: string;
11
- valueFormatter?: (value: number, valueInPercentage: number) => string | number;
12
- }
13
-
14
- const sizes = sortCx({
15
- xxs: {
16
- strokeWidth: 6,
17
- radius: 29,
18
- valueClass: "tt-sm-semi text-primary",
19
- labelClass: "tt-xs-md text-tertiary",
20
- halfCircleTextPosition: "absolute bottom-0.5 text-center",
21
- },
22
- xs: {
23
- strokeWidth: 16,
24
- radius: 72,
25
- valueClass: "td-xs-semi text-primary",
26
- labelClass: "tt-xs-md text-tertiary",
27
- halfCircleTextPosition: "absolute bottom-0.5 text-center",
28
- },
29
- sm: {
30
- strokeWidth: 20,
31
- radius: 90,
32
- valueClass: "td-sm-semi text-primary",
33
- labelClass: "tt-xs-md text-tertiary",
34
- halfCircleTextPosition: "absolute bottom-1 text-center",
35
- },
36
- md: {
37
- strokeWidth: 24,
38
- radius: 108,
39
- valueClass: "td-md-semi text-primary",
40
- labelClass: "tt-sm-md text-tertiary",
41
- halfCircleTextPosition: "absolute bottom-1 text-center",
42
- },
43
- lg: {
44
- strokeWidth: 28,
45
- radius: 126,
46
- valueClass: "td-lg-semi text-primary",
47
- labelClass: "tt-sm-md text-tertiary",
48
- halfCircleTextPosition: "absolute bottom-0 text-center",
49
- },
50
- });
51
-
52
- export const ProgressBarCircle = ({ value, min = 0, max = 100, size, label, valueFormatter }: ProgressBarProps) => {
53
- const percentage = Math.round(((value - min) * 100) / (max - min));
54
-
55
- const sizeConfig = sizes[size];
56
-
57
- const { strokeWidth, radius, valueClass, labelClass } = sizeConfig;
58
-
59
- const diameter = 2 * (radius + strokeWidth / 2);
60
- const width = diameter;
61
- const height = diameter;
62
- const viewBox = `0 0 ${width} ${height}`;
63
- const cx = diameter / 2;
64
- const cy = diameter / 2;
65
-
66
- const textPosition = label ? "absolute text-center" : "absolute text-primary";
67
- const strokeDashoffset = 100 - percentage;
68
-
69
- return (
70
- <div className="flex flex-col items-center gap-0.5">
71
- <div role="progressbar" aria-valuenow={value} aria-valuemin={min} aria-valuemax={max} className="relative flex w-max items-center justify-center">
72
- <svg className="-rotate-90" width={width} height={height} viewBox={viewBox}>
73
- {/* Background circle */}
74
- <circle
75
- className="stroke-bg-quaternary"
76
- cx={cx}
77
- cy={cy}
78
- r={radius}
79
- fill="none"
80
- strokeWidth={strokeWidth}
81
- pathLength="100"
82
- strokeDasharray="100"
83
- strokeLinecap="round"
84
- />
85
-
86
- {/* Foreground circle */}
87
- <circle
88
- className="stroke-fg-brand-primary"
89
- cx={cx}
90
- cy={cy}
91
- r={radius}
92
- fill="none"
93
- strokeWidth={strokeWidth}
94
- pathLength="100"
95
- strokeDasharray="100"
96
- strokeLinecap="round"
97
- strokeDashoffset={strokeDashoffset}
98
- />
99
- </svg>
100
- {label && size !== "xxs" ? (
101
- <div className="absolute text-center">
102
- <div className={labelClass}>{label}</div>
103
- <div className={valueClass}>{valueFormatter ? valueFormatter(value, percentage) : `${percentage}%`}</div>
104
- </div>
105
- ) : (
106
- <span className={clx(textPosition, valueClass)}>{valueFormatter ? valueFormatter(value, percentage) : `${percentage}%`}</span>
107
- )}
108
- </div>
109
-
110
- {label && size === "xxs" && <div className={labelClass}>{label}</div>}
111
- </div>
112
- );
113
- };
114
-
115
- export const ProgressBarHalfCircle = ({ value, min = 0, max = 100, size, label, valueFormatter }: ProgressBarProps) => {
116
- const percentage = Math.round(((value - min) * 100) / (max - min));
117
-
118
- const sizeConfig = sizes[size];
119
-
120
- const { strokeWidth, radius, valueClass, labelClass, halfCircleTextPosition } = sizeConfig;
121
-
122
- const width = 2 * (radius + strokeWidth / 2);
123
- const height = radius + strokeWidth;
124
- const viewBox = `0 0 ${width} ${height}`;
125
- const cx = "50%";
126
- const cy = radius + strokeWidth / 2;
127
-
128
- const strokeDashoffset = -50 - (100 - percentage) / 2;
129
-
130
- return (
131
- <div className="flex flex-col items-center gap-0.5">
132
- <div role="progressbar" aria-valuenow={value} aria-valuemin={min} aria-valuemax={max} className="relative flex w-max items-center justify-center">
133
- <svg width={width} height={height} viewBox={viewBox}>
134
- {/* Background half-circle */}
135
- <circle
136
- className="stroke-bg-quaternary"
137
- cx={cx}
138
- cy={cy}
139
- r={radius}
140
- fill="none"
141
- strokeWidth={strokeWidth}
142
- pathLength="100"
143
- strokeDasharray="100"
144
- strokeDashoffset="-50"
145
- strokeLinecap="round"
146
- />
147
-
148
- {/* Foreground half-circle */}
149
- <circle
150
- className="origin-center -scale-x-100 stroke-fg-brand-primary"
151
- cx={cx}
152
- cy={cy}
153
- r={radius}
154
- fill="none"
155
- strokeWidth={strokeWidth}
156
- pathLength="100"
157
- strokeDasharray="100"
158
- strokeDashoffset={strokeDashoffset}
159
- strokeLinecap="round"
160
- />
161
- </svg>
162
-
163
- {label && size !== "xxs" ? (
164
- <div className={halfCircleTextPosition}>
165
- <div className={labelClass}>{label}</div>
166
- <div className={valueClass}>{valueFormatter ? valueFormatter(value, percentage) : `${percentage}%`}</div>
167
- </div>
168
- ) : (
169
- <span className={clx(halfCircleTextPosition, valueClass)}>{valueFormatter ? valueFormatter(value, percentage) : `${percentage}%`}</span>
170
- )}
171
- </div>
172
-
173
- {label && size === "xxs" && <div className={labelClass}>{label}</div>}
174
- </div>
175
- );
176
- };