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,70 +0,0 @@
1
- "use client";
2
-
3
- import { useContext } from "react";
4
- import { Check } from "@untitledui/icons";
5
- import type { ListBoxItemProps as AriaListBoxItemProps } from "react-aria-components";
6
- import { ListBoxItem as AriaListBoxItem, Text } from "react-aria-components";
7
- import Avatar from "@/components/shared/avatar/avatar";
8
- import { cx } from "@/components/utils/cx";
9
- import type { SelectItemType } from "./select";
10
- import { SelectContext } from "./select";
11
-
12
- const sizes = {
13
- sm: "p-2 pr-2.5",
14
- md: "p-2.5 pl-2",
15
- };
16
-
17
- interface SelectItemProps extends Omit<AriaListBoxItemProps<SelectItemType>, "id">, SelectItemType {}
18
-
19
- export const SelectItem = ({ label, id, value, avatarUrl, supportingText, isDisabled, icon: Icon, className, children, ...props }: SelectItemProps) => {
20
- const { size } = useContext(SelectContext);
21
-
22
- const textValue = supportingText ? label + " " + supportingText : label;
23
-
24
- return (
25
- <AriaListBoxItem
26
- id={id}
27
- value={value as unknown as object}
28
- textValue={textValue}
29
- isDisabled={isDisabled}
30
- {...props}
31
- className={(state) => cx("w-full px-1.5 py-px outline-hidden", typeof className === "function" ? className(state) : className)}
32
- >
33
- {(state) => (
34
- <div
35
- className={cx(
36
- "flex cursor-pointer items-center gap-2 rounded-md outline-hidden select-none",
37
- state.isSelected && "bg-active",
38
- state.isDisabled && "cursor-not-allowed",
39
- state.isFocused && "bg-primary_hover",
40
- state.isFocusVisible && "ring-2 ring-focus-ring ring-inset",
41
- sizes[size],
42
- )}
43
- >
44
- {avatarUrl ? (
45
- <Avatar aria-hidden="true" size="xs" src={avatarUrl} alt={label} />
46
- ) : Icon ? (
47
- <Icon aria-hidden="true" className={cx("size-5 shrink-0 text-fg-quaternary", state.isDisabled && "text-fg-disabled")} />
48
- ) : null}
49
-
50
- <section className="flex w-full min-w-0 flex-1 flex-wrap gap-x-2">
51
- <Text slot="label" className={cx("truncate tt-md-md whitespace-nowrap text-primary", state.isDisabled && "text-disabled")}>
52
- {label || (typeof children === "function" ? children(state) : children)}
53
- </Text>
54
-
55
- {supportingText && (
56
- <Text slot="description" className={cx("tt-md whitespace-nowrap text-tertiary", state.isDisabled && "text-disabled")}>
57
- {supportingText}
58
- </Text>
59
- )}
60
- </section>
61
- {state.isSelected && (
62
- <Check className={cx("ml-auto size-5 text-fg-brand-primary", state.isDisabled && "text-fg-disabled")} aria-hidden="true" />
63
- )}
64
- </div>
65
- )}
66
- </AriaListBoxItem>
67
- );
68
- };
69
-
70
- export default SelectItem;
@@ -1,63 +0,0 @@
1
- "use client";
2
-
3
- import { type ChangeEventHandler, useId } from "react";
4
- import { ChevronDown } from "@untitledui/icons";
5
- import HintText from "../input/hint-text";
6
- import Label from "../input/label";
7
-
8
- export const NativeSelect = (props: {
9
- label?: string;
10
- hint?: string;
11
- value?: string;
12
- options: { label: string; value: string; disabled?: boolean }[];
13
- onChange?: ChangeEventHandler<HTMLSelectElement>;
14
- }) => {
15
- const id = useId();
16
- const selectId = `select-native-${id}`;
17
- const hintId = `select-native-hint-${id}`;
18
-
19
- return (
20
- <div className="w-full">
21
- {props.label && (
22
- <Label htmlFor={selectId} id={selectId} className="mb-1.5">
23
- {props.label}
24
- </Label>
25
- )}
26
-
27
- <div className="relative grid w-full items-center">
28
- <select
29
- id={selectId}
30
- value={props.value}
31
- defaultValue="default"
32
- onChange={props.onChange}
33
- aria-describedby={hintId}
34
- aria-labelledby={selectId}
35
- className="appearance-none rounded-lg bg-primary px-3.5 py-2.5 tt-md-md text-primary shadow-xs ring-1 ring-border-primary outline-hidden transition duration-200 ease-in-out ring-inset placeholder:text-fg-quaternary focus:ring-2 focus:ring-border-brand disabled:cursor-not-allowed disabled:bg-disabled_subtle disabled:text-disabled"
36
- >
37
- <option value="default" disabled defaultChecked>
38
- Select team member
39
- </option>
40
- {props.options.map((opt) => (
41
- <option key={opt.value} value={opt.value}>
42
- {opt.label}
43
- </option>
44
- ))}
45
- </select>
46
- <span className="absolute right-3.5">
47
- <ChevronDown aria-hidden="true" size={20} className="text-fg-quaternary" />
48
- </span>
49
- </div>
50
-
51
- {props.hint && (
52
- <HintText className="mt-2" id={hintId}>
53
- {props.hint}
54
- </HintText>
55
- )}
56
- </div>
57
- );
58
- };
59
-
60
- NativeSelect.args = {
61
- label: "Team member",
62
- hint: "This is a hint text to help user.",
63
- };
@@ -1,143 +0,0 @@
1
- "use client";
2
-
3
- import type { ReactNode, Ref, RefAttributes } from "react";
4
- import { createContext } from "react";
5
- import { ChevronDown, User01 } from "@untitledui/icons";
6
- import type { SelectProps as AriaSelectProps } from "react-aria-components";
7
- import { Button as AriaButton, ListBox as AriaListBox, Select as AriaSelect, SelectValue as AriaSelectValue } from "react-aria-components";
8
- import Avatar from "@/components/shared/avatar/avatar";
9
- import type { IconComponentType } from "@/components/shared/badges/badge-types";
10
- import HintText from "@/components/shared/input/hint-text";
11
- import Label from "@/components/shared/input/label";
12
- import { cx } from "@/components/utils/cx";
13
- import { ComboBox } from "./combobox";
14
- import { Popover } from "./popover";
15
- import Item from "./select-item";
16
-
17
- export type SelectItemType = {
18
- id: string;
19
- label?: string;
20
- avatarUrl?: string;
21
- isDisabled?: boolean;
22
- supportingText?: string;
23
- icon?: IconComponentType;
24
- };
25
-
26
- type Types = "default" | "iconLeading" | "avatarLeading" | "search" | "tags";
27
- type SelectTypes = "default" | "iconLeading" | "avatarLeading";
28
-
29
- export interface CommonProps {
30
- hint?: string;
31
- label?: string;
32
- tooltip?: string;
33
- size?: "sm" | "md";
34
- placeholder?: string;
35
- }
36
-
37
- interface SelectProps extends Omit<AriaSelectProps<SelectItemType>, "children" | "items">, RefAttributes<HTMLDivElement>, CommonProps {
38
- type?: SelectTypes;
39
- items?: SelectItemType[];
40
- popoverClassName?: string;
41
- placeholderIcon?: IconComponentType;
42
- children: ReactNode | ((item: SelectItemType) => ReactNode);
43
- }
44
-
45
- interface SelectValueProps {
46
- isOpen: boolean;
47
- size: "sm" | "md";
48
- type: SelectTypes;
49
- isFocused: boolean;
50
- isDisabled: boolean;
51
- placeholder?: string;
52
- ref?: Ref<HTMLButtonElement>;
53
- placeholderIcon?: IconComponentType;
54
- }
55
-
56
- export const sizes = {
57
- sm: { root: "py-2 px-3", shortcut: "pr-2.5" },
58
- md: { root: "py-2.5 px-3.5", shortcut: "pr-3" },
59
- };
60
-
61
- const SelectValue = (props: SelectValueProps) => {
62
- return (
63
- <AriaButton
64
- className={cx(
65
- "relative flex w-full cursor-pointer items-center rounded-lg bg-primary shadow-xs ring-1 ring-border-primary outline-hidden transition duration-100 ease-linear ring-inset",
66
- (props.isFocused || props.isOpen) && "ring-2 ring-border-brand",
67
- props.isDisabled && "cursor-not-allowed bg-disabled_subtle text-disabled",
68
- )}
69
- >
70
- <AriaSelectValue<SelectItemType>
71
- className={cx("flex h-max w-full items-center justify-start gap-2 truncate text-left align-middle", sizes[props.size].root)}
72
- >
73
- {(state) => {
74
- const Icon = state?.selectedItem?.icon || props.placeholderIcon;
75
-
76
- return (
77
- <>
78
- {state?.selectedItem?.avatarUrl ? (
79
- <Avatar size="xs" src={state.selectedItem.avatarUrl} alt={state.selectedItem.label} />
80
- ) : Icon ? (
81
- <Icon aria-hidden="true" className={cx("size-5 shrink-0 text-fg-quaternary", props.isDisabled && "text-fg-disabled")} />
82
- ) : null}
83
-
84
- {state.selectedItem ? (
85
- <section className="flex w-full gap-2 truncate">
86
- <p className="truncate tt-md-md text-primary">{state.selectedItem?.label}</p>
87
- {state.selectedItem?.supportingText && <p className="tt-md text-tertiary">{state.selectedItem?.supportingText}</p>}
88
- </section>
89
- ) : (
90
- <p className={cx("tt-md text-placeholder", props.isDisabled && "text-disabled")}>{props.placeholder}</p>
91
- )}
92
-
93
- <ChevronDown size={20} aria-hidden="true" className="ml-auto shrink-0 text-fg-quaternary" />
94
- </>
95
- );
96
- }}
97
- </AriaSelectValue>
98
- </AriaButton>
99
- );
100
- };
101
-
102
- export const SelectContext = createContext<{ type: Types; size: "sm" | "md" }>({ type: "default", size: "sm" });
103
-
104
- const Select = ({ type = "default", placeholder = "Select", placeholderIcon, size = "sm", children, items, label, hint, tooltip, ...rest }: SelectProps) => {
105
- return (
106
- <SelectContext.Provider value={{ type, size }}>
107
- <AriaSelect {...rest}>
108
- {(state) => (
109
- <div className="flex flex-col gap-1.5">
110
- {label && (
111
- <Label isRequired={state.isRequired} tooltip={tooltip}>
112
- {label}
113
- </Label>
114
- )}
115
-
116
- <SelectValue
117
- {...state}
118
- {...{ type, size, placeholder }}
119
- placeholderIcon={type === "avatarLeading" || type === "iconLeading" ? placeholderIcon || User01 : undefined}
120
- />
121
-
122
- <Popover size={size} className={rest.popoverClassName}>
123
- <AriaListBox items={items} className="size-full outline-hidden">
124
- {children}
125
- </AriaListBox>
126
- </Popover>
127
-
128
- {hint && <HintText isInvalid={state.isInvalid}>{hint}</HintText>}
129
- </div>
130
- )}
131
- </AriaSelect>
132
- </SelectContext.Provider>
133
- );
134
- };
135
-
136
- const _Select = Select as typeof Select & {
137
- ComboBox: typeof ComboBox;
138
- Item: typeof Item;
139
- };
140
- _Select.ComboBox = ComboBox;
141
- _Select.Item = Item;
142
-
143
- export { _Select as Select };
@@ -1,76 +0,0 @@
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/cx";
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
- };
@@ -1,47 +0,0 @@
1
- "use client";
2
-
3
- import { cx } from "@/components/utils/cx";
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;
@@ -1,34 +0,0 @@
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/cx";
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
- };
@@ -1,162 +0,0 @@
1
- "use client";
2
-
3
- import { type PropsWithChildren, type RefAttributes, createContext, useContext } from "react";
4
- import {
5
- Tag as AriaTag,
6
- TagGroup as AriaTagGroup,
7
- type TagGroupProps as AriaTagGroupProps,
8
- TagList as AriaTagList,
9
- type TagProps as AriaTagProps,
10
- } from "react-aria-components";
11
- import Dot from "@/components/foundations/dot-icon";
12
- import { cx } from "@/components/utils/cx";
13
- import Avatar from "../avatar/avatar";
14
- import TagCheckbox from "./base-components/tag-checkbox";
15
- import { TagCloseX } from "./base-components/tag-close-x";
16
-
17
- export interface TagItem {
18
- id: string;
19
- label: string;
20
- count?: number;
21
- avatarSrc?: string;
22
- avatarContrastBorder?: boolean;
23
- dot?: boolean;
24
- dotClassName?: string;
25
- isDisabled?: boolean;
26
- onClose?: (id: string) => void;
27
- }
28
-
29
- const TagGroupContext = createContext<{
30
- selectionMode: "none" | "single" | "multiple";
31
- size: "sm" | "md" | "lg";
32
- }>({
33
- selectionMode: "none",
34
- size: "sm",
35
- });
36
-
37
- interface TagGroupProps extends AriaTagGroupProps, RefAttributes<HTMLDivElement> {
38
- label: string;
39
- size?: "sm" | "md" | "lg";
40
- }
41
-
42
- export const TagGroup = ({ label, selectionMode = "none", size = "sm", children, ...otherProps }: TagGroupProps) => {
43
- return (
44
- <TagGroupContext.Provider value={{ selectionMode, size }}>
45
- <AriaTagGroup aria-label={label} selectionMode={selectionMode} disallowEmptySelection={selectionMode === "single"} {...otherProps}>
46
- {children}
47
- </AriaTagGroup>
48
- </TagGroupContext.Provider>
49
- );
50
- };
51
-
52
- export const TagList = AriaTagList;
53
-
54
- const styles = {
55
- sm: {
56
- root: {
57
- base: "px-2 py-[3px] tt-xs-md",
58
- withCheckbox: "pl-[5px]",
59
- withAvatar: "pl-1",
60
- withDot: "pl-1.5",
61
- withCount: "pr-1",
62
- withClose: "pr-1",
63
- },
64
- content: "gap-1",
65
- count: "px-1 tt-xs-md",
66
- },
67
- md: {
68
- root: {
69
- base: "px-[9px] py-[2px] tt-sm-md",
70
- withCheckbox: "pl-1",
71
- withAvatar: "pl-[5px]",
72
- withDot: "pl-[7px]",
73
- withCount: "pr-[3px]",
74
- withClose: "pr-1",
75
- },
76
- content: "gap-[5px]",
77
- count: "px-[5px] tt-xs-md",
78
- },
79
- lg: {
80
- root: {
81
- base: "px-2.5 py-[4px] tt-sm-md",
82
- withCheckbox: "pl-[5px]",
83
- withAvatar: "pl-[7px]",
84
- withDot: "pl-[9px]",
85
- withCount: "pr-1",
86
- withClose: "pr-1",
87
- },
88
- content: "gap-1.5",
89
- count: "px-1.5 tt-sm-md",
90
- },
91
- };
92
-
93
- interface TagProps extends AriaTagProps, RefAttributes<object>, Omit<TagItem, "label" | "id"> {}
94
-
95
- export const Tag = ({
96
- id,
97
- avatarSrc,
98
- avatarContrastBorder,
99
- dot,
100
- dotClassName,
101
- isDisabled,
102
- count,
103
- className,
104
- children,
105
- onClose,
106
- }: PropsWithChildren<TagProps>) => {
107
- const context = useContext(TagGroupContext);
108
-
109
- const leadingContent = avatarSrc ? (
110
- <Avatar size="xxs" src={avatarSrc} alt="Avatar" contrastBorder={avatarContrastBorder} />
111
- ) : dot ? (
112
- <Dot className={cx("text-fg-success-secondary", dotClassName)} size="sm" />
113
- ) : null;
114
-
115
- return (
116
- <AriaTag
117
- id={id}
118
- isDisabled={isDisabled}
119
- className={(state) =>
120
- cx(
121
- "flex cursor-default items-center gap-[3px] rounded-md bg-primary text-secondary ring-1 ring-border-primary ring-inset focus:outline-hidden focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring",
122
- styles[context.size].root.base,
123
-
124
- // With avatar
125
- avatarSrc && styles[context.size].root.withAvatar,
126
- // With X button
127
- (onClose || state.allowsRemoving) && styles[context.size].root.withClose,
128
- // With dot
129
- dot && styles[context.size].root.withDot,
130
- // With count
131
- typeof count === "number" && styles[context.size].root.withCount,
132
- // With checkbox
133
- context.selectionMode !== "none" && styles[context.size].root.withCheckbox,
134
- // Disabled
135
- isDisabled && "cursor-not-allowed",
136
-
137
- typeof className === "function" ? className(state) : className,
138
- )
139
- }
140
- >
141
- {({ isSelected, isDisabled, allowsRemoving }) => (
142
- <>
143
- <div className={cx("flex items-center gap-1", styles[context.size].content)}>
144
- {context.selectionMode !== "none" && <TagCheckbox size={context.size} isSelected={isSelected} isDisabled={isDisabled} />}
145
-
146
- {leadingContent}
147
-
148
- {children}
149
-
150
- {typeof count === "number" && (
151
- <span className={cx("flex items-center justify-center rounded-[3px] bg-tertiary text-center", styles[context.size].count)}>
152
- {count}
153
- </span>
154
- )}
155
- </div>
156
-
157
- {(onClose || allowsRemoving) && <TagCloseX size={context.size} onPress={() => id && onClose?.(id.toString())} />}
158
- </>
159
- )}
160
- </AriaTag>
161
- );
162
- };
@@ -1,82 +0,0 @@
1
- "use client";
2
-
3
- import type { ReactNode, Ref } from "react";
4
- import React from "react";
5
- import type { TextAreaProps as AriaTextAreaProps, TextFieldProps as AriaTextFieldProps } from "react-aria-components";
6
- import { TextArea as AriaTextArea } from "react-aria-components";
7
- import HintText from "@/components/shared/input/hint-text";
8
- import Label from "@/components/shared/input/label";
9
- import { cx } from "@/components/utils/cx";
10
- import { TextField } from "../input";
11
-
12
- interface TextAreaBaseProps extends AriaTextAreaProps {
13
- ref?: Ref<HTMLTextAreaElement>;
14
- }
15
-
16
- export const TextAreaBase = ({ className, ...props }: TextAreaBaseProps) => {
17
- return (
18
- <AriaTextArea
19
- {...props}
20
- className={(state) =>
21
- cx(
22
- "w-full scroll-py-3 rounded-lg bg-primary px-3.5 py-3 tt-md text-primary shadow-xs ring-1 ring-border-primary transition duration-100 ease-linear ring-inset placeholder:text-placeholder autofill:rounded-lg autofill:text-primary focus:outline-hidden",
23
-
24
- // Resize handle
25
- "[&::-webkit-resizer]:bg-[url(/shared/textarea-resize-handle-light-mode.svg)] [&::-webkit-resizer]:bg-contain dark:[&::-webkit-resizer]:bg-[url(/shared/textarea-resize-handle-dark-mode.svg)]",
26
-
27
- state.isFocused && !state.isDisabled && "ring-2 ring-border-brand",
28
- state.isDisabled && "cursor-not-allowed bg-disabled_subtle text-disabled ring-border-disabled",
29
- state.isInvalid && "ring-border-error_subtle",
30
- state.isInvalid && state.isFocused && "ring-2 ring-border-error",
31
-
32
- typeof className === "function" ? className(state) : className,
33
- )
34
- }
35
- />
36
- );
37
- };
38
-
39
- TextAreaBase.displayName = "TextAreaBase";
40
-
41
- interface TextFieldProps extends AriaTextAreaProps {
42
- label?: string;
43
- hint?: ReactNode;
44
- tooltip?: string;
45
- ref?: Ref<HTMLDivElement>;
46
- isInvalid?: AriaTextFieldProps["isInvalid"];
47
- isDisabled?: AriaTextFieldProps["isDisabled"];
48
- isRequired?: AriaTextFieldProps["isRequired"];
49
- isReadOnly?: AriaTextFieldProps["isReadOnly"];
50
- wrapperClassName?: AriaTextFieldProps["className"];
51
- }
52
-
53
- export const TextArea = ({
54
- label,
55
- hint,
56
- wrapperClassName,
57
- isDisabled,
58
- isInvalid,
59
- isRequired,
60
- isReadOnly,
61
- tooltip,
62
- value,
63
- defaultValue,
64
- ref,
65
- ...textAreaProps
66
- }: TextFieldProps) => {
67
- return (
68
- <TextField
69
- {...{ ref, isDisabled, isInvalid, isReadOnly, isRequired, className: wrapperClassName }}
70
- value={value as string}
71
- defaultValue={defaultValue as string}
72
- >
73
- {label && <Label tooltip={tooltip}>{label}</Label>}
74
-
75
- <TextAreaBase {...textAreaProps} />
76
-
77
- {hint && <HintText>{hint}</HintText>}
78
- </TextField>
79
- );
80
- };
81
-
82
- TextArea.displayName = "TextArea";