@fanvue/ui 3.17.0 → 3.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/components/Badge/Badge.cjs +13 -11
  3. package/dist/cjs/components/Badge/Badge.cjs.map +1 -1
  4. package/dist/cjs/components/Banner/Banner.cjs +1 -1
  5. package/dist/cjs/components/Banner/Banner.cjs.map +1 -1
  6. package/dist/cjs/components/Chip/Chip.cjs +21 -1
  7. package/dist/cjs/components/Chip/Chip.cjs.map +1 -1
  8. package/dist/cjs/components/Count/Count.cjs +36 -7
  9. package/dist/cjs/components/Count/Count.cjs.map +1 -1
  10. package/dist/cjs/components/Icons/ReceiveMoneyIcon.cjs +79 -0
  11. package/dist/cjs/components/Icons/ReceiveMoneyIcon.cjs.map +1 -0
  12. package/dist/cjs/components/PhoneField/PhoneField.cjs +198 -0
  13. package/dist/cjs/components/PhoneField/PhoneField.cjs.map +1 -0
  14. package/dist/cjs/components/RatingSummary/RatingSummary.cjs +30 -23
  15. package/dist/cjs/components/RatingSummary/RatingSummary.cjs.map +1 -1
  16. package/dist/cjs/components/TextArea/TextArea.cjs +2 -2
  17. package/dist/cjs/components/TextArea/TextArea.cjs.map +1 -1
  18. package/dist/cjs/components/TextField/TextField.cjs +13 -6
  19. package/dist/cjs/components/TextField/TextField.cjs.map +1 -1
  20. package/dist/cjs/index.cjs +4 -0
  21. package/dist/cjs/index.cjs.map +1 -1
  22. package/dist/components/Badge/Badge.mjs +13 -11
  23. package/dist/components/Badge/Badge.mjs.map +1 -1
  24. package/dist/components/Banner/Banner.mjs +1 -1
  25. package/dist/components/Banner/Banner.mjs.map +1 -1
  26. package/dist/components/Chip/Chip.mjs +21 -1
  27. package/dist/components/Chip/Chip.mjs.map +1 -1
  28. package/dist/components/Count/Count.mjs +36 -7
  29. package/dist/components/Count/Count.mjs.map +1 -1
  30. package/dist/components/Icons/ReceiveMoneyIcon.mjs +62 -0
  31. package/dist/components/Icons/ReceiveMoneyIcon.mjs.map +1 -0
  32. package/dist/components/PhoneField/PhoneField.mjs +181 -0
  33. package/dist/components/PhoneField/PhoneField.mjs.map +1 -0
  34. package/dist/components/RatingSummary/RatingSummary.mjs +30 -23
  35. package/dist/components/RatingSummary/RatingSummary.mjs.map +1 -1
  36. package/dist/components/TextArea/TextArea.mjs +2 -2
  37. package/dist/components/TextArea/TextArea.mjs.map +1 -1
  38. package/dist/components/TextField/TextField.mjs +13 -6
  39. package/dist/components/TextField/TextField.mjs.map +1 -1
  40. package/dist/index.d.ts +109 -7
  41. package/dist/index.mjs +4 -0
  42. package/dist/index.mjs.map +1 -1
  43. package/package.json +1 -1
@@ -0,0 +1,198 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const jsxRuntime = require("react/jsx-runtime");
5
+ const React = require("react");
6
+ const cn = require("../../utils/cn.cjs");
7
+ const ChevronDownIcon = require("../Icons/ChevronDownIcon.cjs");
8
+ function _interopNamespaceDefault(e) {
9
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
10
+ if (e) {
11
+ for (const k in e) {
12
+ if (k !== "default") {
13
+ const d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: () => e[k]
17
+ });
18
+ }
19
+ }
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+ const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
25
+ const CONTAINER_HEIGHT = {
26
+ "48": "h-12",
27
+ "40": "h-10",
28
+ "32": "h-8"
29
+ };
30
+ const CONTAINER_PADDING_X = {
31
+ "48": "px-4",
32
+ "40": "px-4",
33
+ "32": "px-3"
34
+ };
35
+ const INPUT_TYPOGRAPHY = {
36
+ "48": "typography-body-default-16px-regular autofill-body-lg",
37
+ "40": "typography-body-default-16px-regular autofill-body-lg",
38
+ "32": "typography-body-small-14px-regular autofill-body-md"
39
+ };
40
+ const PREFIX_TYPOGRAPHY = {
41
+ "48": "typography-body-default-16px-regular",
42
+ "40": "typography-body-default-16px-regular",
43
+ "32": "typography-body-small-14px-regular"
44
+ };
45
+ function getContainerClassName(size, error, disabled) {
46
+ return cn.cn(
47
+ "relative flex items-center gap-2 overflow-hidden rounded-sm border bg-inputs-inputs-primary has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
48
+ CONTAINER_PADDING_X[size],
49
+ CONTAINER_HEIGHT[size],
50
+ error ? "border-error-content" : "border-border-primary",
51
+ !disabled && !error && "hover:border-neutral-alphas-400",
52
+ disabled && "opacity-50"
53
+ );
54
+ }
55
+ function warnMissingAccessibleName(label, ariaLabel, ariaLabelledBy) {
56
+ if (process.env.NODE_ENV !== "production") {
57
+ if (!label && !ariaLabel && !ariaLabelledBy) {
58
+ console.warn(
59
+ "PhoneField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop."
60
+ );
61
+ }
62
+ }
63
+ }
64
+ function PhoneFieldHelperText({
65
+ id,
66
+ error,
67
+ children
68
+ }) {
69
+ return /* @__PURE__ */ jsxRuntime.jsx(
70
+ "p",
71
+ {
72
+ id,
73
+ className: cn.cn(
74
+ "typography-description-12px-regular pt-2",
75
+ error ? "text-error-content" : "text-content-tertiary"
76
+ ),
77
+ children
78
+ }
79
+ );
80
+ }
81
+ const PhoneField = React__namespace.forwardRef(
82
+ ({
83
+ label,
84
+ helperText,
85
+ size = "48",
86
+ error = false,
87
+ errorMessage,
88
+ dialCode,
89
+ flag,
90
+ onCountrySelect,
91
+ countryButtonLabel = "Select country",
92
+ className,
93
+ id,
94
+ disabled,
95
+ fullWidth = false,
96
+ type = "tel",
97
+ ...props
98
+ }, ref) => {
99
+ const generatedId = React__namespace.useId();
100
+ const inputId = id || generatedId;
101
+ const helperTextId = `${inputId}-helper`;
102
+ const dialCodeId = `${inputId}-dial-code`;
103
+ const bottomText = error && errorMessage ? errorMessage : helperText;
104
+ const describedBy = [dialCode != null ? dialCodeId : null, bottomText ? helperTextId : null].filter(Boolean).join(" ") || void 0;
105
+ const innerRef = React__namespace.useRef(null);
106
+ const setRefs = React__namespace.useCallback(
107
+ (node) => {
108
+ innerRef.current = node;
109
+ if (typeof ref === "function") ref(node);
110
+ else if (ref) ref.current = node;
111
+ },
112
+ [ref]
113
+ );
114
+ const handleContainerMouseDown = (event) => {
115
+ if (disabled) return;
116
+ const target = event.target;
117
+ if (target === innerRef.current) return;
118
+ if (target.closest("[data-pf-interactive]")) return;
119
+ event.preventDefault();
120
+ innerRef.current?.focus();
121
+ };
122
+ warnMissingAccessibleName(label, props["aria-label"], props["aria-labelledby"]);
123
+ return /* @__PURE__ */ jsxRuntime.jsxs(
124
+ "div",
125
+ {
126
+ className: cn.cn("flex flex-col", fullWidth && "w-full", className),
127
+ "data-disabled": disabled ? "" : void 0,
128
+ "data-error": error ? "" : void 0,
129
+ children: [
130
+ label && /* @__PURE__ */ jsxRuntime.jsx(
131
+ "label",
132
+ {
133
+ htmlFor: inputId,
134
+ className: "typography-description-12px-semibold pb-2 text-content-primary",
135
+ children: label
136
+ }
137
+ ),
138
+ /* @__PURE__ */ jsxRuntime.jsxs(
139
+ "div",
140
+ {
141
+ className: getContainerClassName(size, error, disabled),
142
+ onMouseDown: handleContainerMouseDown,
143
+ children: [
144
+ /* @__PURE__ */ jsxRuntime.jsxs(
145
+ "button",
146
+ {
147
+ type: "button",
148
+ "data-pf-interactive": "",
149
+ onClick: onCountrySelect,
150
+ disabled,
151
+ "aria-label": countryButtonLabel,
152
+ className: "flex shrink-0 items-center gap-1 rounded-2xs focus-visible:shadow-focus-ring focus-visible:outline-none disabled:cursor-not-allowed",
153
+ children: [
154
+ flag != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-6 shrink-0 items-center justify-center overflow-hidden rounded-full", children: flag }),
155
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon.ChevronDownIcon, { size: 16, className: "shrink-0 text-content-secondary" })
156
+ ]
157
+ }
158
+ ),
159
+ dialCode != null && /* @__PURE__ */ jsxRuntime.jsx(
160
+ "span",
161
+ {
162
+ id: dialCodeId,
163
+ className: cn.cn(
164
+ "shrink-0 select-none whitespace-nowrap text-content-primary",
165
+ PREFIX_TYPOGRAPHY[size]
166
+ ),
167
+ children: dialCode
168
+ }
169
+ ),
170
+ /* @__PURE__ */ jsxRuntime.jsx(
171
+ "input",
172
+ {
173
+ ref: setRefs,
174
+ id: inputId,
175
+ type,
176
+ inputMode: "tel",
177
+ disabled,
178
+ "aria-describedby": describedBy,
179
+ "aria-invalid": error || void 0,
180
+ className: cn.cn(
181
+ "h-full min-w-0 flex-1 bg-transparent text-content-primary no-underline placeholder:text-content-tertiary focus:outline-none disabled:cursor-not-allowed",
182
+ INPUT_TYPOGRAPHY[size]
183
+ ),
184
+ ...props
185
+ }
186
+ )
187
+ ]
188
+ }
189
+ ),
190
+ bottomText && /* @__PURE__ */ jsxRuntime.jsx(PhoneFieldHelperText, { id: helperTextId, error, children: bottomText })
191
+ ]
192
+ }
193
+ );
194
+ }
195
+ );
196
+ PhoneField.displayName = "PhoneField";
197
+ exports.PhoneField = PhoneField;
198
+ //# sourceMappingURL=PhoneField.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhoneField.cjs","sources":["../../../../src/components/PhoneField/PhoneField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\n\n/** Phone field height in pixels. */\nexport type PhoneFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface PhoneFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"prefix\"> {\n /** Label text displayed above the input. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the input. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the phone field in pixels. @default \"48\" */\n size?: PhoneFieldSize;\n /** Whether the field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the input. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Dial code of the selected country, shown before the number (e.g. `\"+39\"`). */\n dialCode?: React.ReactNode;\n /** Flag of the selected country, rendered in the country selector (e.g. an image or flag icon). */\n flag?: React.ReactNode;\n /** Fired when the country selector (flag + chevron) is activated. Open the country picker here. */\n onCountrySelect?: () => void;\n /** Accessible name for the country selector button. @default \"Select country\" */\n countryButtonLabel?: string;\n /** Whether the field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n}\n\nconst CONTAINER_HEIGHT: Record<PhoneFieldSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst CONTAINER_PADDING_X: Record<PhoneFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst INPUT_TYPOGRAPHY: Record<PhoneFieldSize, string> = {\n \"48\": \"typography-body-default-16px-regular autofill-body-lg\",\n \"40\": \"typography-body-default-16px-regular autofill-body-lg\",\n \"32\": \"typography-body-small-14px-regular autofill-body-md\",\n};\n\nconst PREFIX_TYPOGRAPHY: Record<PhoneFieldSize, string> = {\n \"48\": \"typography-body-default-16px-regular\",\n \"40\": \"typography-body-default-16px-regular\",\n \"32\": \"typography-body-small-14px-regular\",\n};\n\nfunction getContainerClassName(size: PhoneFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative flex items-center gap-2 overflow-hidden rounded-sm border bg-inputs-inputs-primary has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n CONTAINER_PADDING_X[size],\n CONTAINER_HEIGHT[size],\n error ? \"border-error-content\" : \"border-border-primary\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n disabled && \"opacity-50\",\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"PhoneField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction PhoneFieldHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-description-12px-regular pt-2\",\n error ? \"text-error-content\" : \"text-content-tertiary\",\n )}\n >\n {children}\n </p>\n );\n}\n\n/**\n * A phone number input with a country selector (flag + chevron), a fixed dial\n * code prefix, and a `tel` input. The country picker itself is owned by the\n * consumer: pass the selected country's `flag` and `dialCode`, and open your\n * picker from `onCountrySelect`.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <PhoneField\n * label=\"Phone number\"\n * flag={<img src={italyFlag} alt=\"\" />}\n * dialCode=\"+39\"\n * onCountrySelect={openCountryPicker}\n * value={number}\n * onChange={(e) => setNumber(e.target.value)}\n * />\n * ```\n */\nexport const PhoneField = React.forwardRef<HTMLInputElement, PhoneFieldProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n dialCode,\n flag,\n onCountrySelect,\n countryButtonLabel = \"Select country\",\n className,\n id,\n disabled,\n fullWidth = false,\n type = \"tel\",\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const dialCodeId = `${inputId}-dial-code`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n const describedBy =\n [dialCode != null ? dialCodeId : null, bottomText ? helperTextId : null]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n const innerRef = React.useRef<HTMLInputElement>(null);\n const setRefs = React.useCallback(\n (node: HTMLInputElement | null) => {\n innerRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as React.RefObject<HTMLInputElement | null>).current = node;\n },\n [ref],\n );\n\n const handleContainerMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return;\n const target = event.target as HTMLElement;\n if (target === innerRef.current) return;\n if (target.closest(\"[data-pf-interactive]\")) return;\n event.preventDefault();\n innerRef.current?.focus();\n };\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-description-12px-semibold pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n {/* biome-ignore lint/a11y/noStaticElementInteractions: focus bridge delegates pointer clicks on the padding to the input */}\n <div\n className={getContainerClassName(size, error, disabled)}\n onMouseDown={handleContainerMouseDown}\n >\n <button\n type=\"button\"\n data-pf-interactive=\"\"\n onClick={onCountrySelect}\n disabled={disabled}\n aria-label={countryButtonLabel}\n className=\"flex shrink-0 items-center gap-1 rounded-2xs focus-visible:shadow-focus-ring focus-visible:outline-none disabled:cursor-not-allowed\"\n >\n {flag != null && (\n <span className=\"flex size-6 shrink-0 items-center justify-center overflow-hidden rounded-full\">\n {flag}\n </span>\n )}\n <ChevronDownIcon size={16} className=\"shrink-0 text-content-secondary\" />\n </button>\n\n {dialCode != null && (\n <span\n id={dialCodeId}\n className={cn(\n \"shrink-0 select-none whitespace-nowrap text-content-primary\",\n PREFIX_TYPOGRAPHY[size],\n )}\n >\n {dialCode}\n </span>\n )}\n\n <input\n ref={setRefs}\n id={inputId}\n type={type}\n inputMode=\"tel\"\n disabled={disabled}\n aria-describedby={describedBy}\n aria-invalid={error || undefined}\n className={cn(\n \"h-full min-w-0 flex-1 bg-transparent text-content-primary no-underline placeholder:text-content-tertiary focus:outline-none disabled:cursor-not-allowed\",\n INPUT_TYPOGRAPHY[size],\n )}\n {...props}\n />\n </div>\n\n {bottomText && (\n <PhoneFieldHelperText id={helperTextId} error={error}>\n {bottomText}\n </PhoneFieldHelperText>\n )}\n </div>\n );\n },\n);\n\nPhoneField.displayName = \"PhoneField\";\n"],"names":["cn","jsx","React","jsxs","ChevronDownIcon"],"mappings":"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"}
@@ -70,31 +70,38 @@ const RatingSummary = React__namespace.forwardRef(
70
70
  ] })
71
71
  ] })
72
72
  ] }),
73
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col gap-4 p-0", "aria-label": "Rating breakdown", children: rows.map(({ rating, count }) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
74
- "div",
73
+ /* @__PURE__ */ jsxRuntime.jsx(
74
+ "ul",
75
75
  {
76
- className: "flex items-center gap-4",
77
- role: "img",
78
- "aria-label": formatRatingLabel ? formatRatingLabel(rating, count) : `${rating} ${rating === 1 ? "star" : "stars"}, ${formatCount(count)}`,
79
- children: [
80
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-4 flex-1 overflow-hidden rounded-full bg-neutral-alphas-50", children: /* @__PURE__ */ jsxRuntime.jsx(
81
- "div",
82
- {
83
- className: "absolute inset-y-0 left-0 rounded-full bg-brand-primary-default transition-[width] duration-300 ease-in-out",
84
- style: { width: maxCount === 0 ? "0%" : `${count / maxCount * 100}%` }
85
- }
86
- ) }),
87
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex shrink-0 items-center gap-1.5", children: [
88
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "typography-body-small-14px-semibold text-content-primary", children: rating }),
89
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "typography-body-small-14px-regular text-content-tertiary", children: [
90
- "(",
91
- formatCount(count),
92
- ")"
93
- ] })
94
- ] })
95
- ]
76
+ className: "m-0 grid list-none grid-cols-[1fr_auto] gap-4 p-0",
77
+ "aria-label": "Rating breakdown",
78
+ children: rows.map(({ rating, count }) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "col-span-2 grid grid-cols-subgrid", children: /* @__PURE__ */ jsxRuntime.jsxs(
79
+ "div",
80
+ {
81
+ className: "col-span-2 grid grid-cols-subgrid items-center",
82
+ role: "img",
83
+ "aria-label": formatRatingLabel ? formatRatingLabel(rating, count) : `${rating} ${rating === 1 ? "star" : "stars"}, ${formatCount(count)}`,
84
+ children: [
85
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-4 overflow-hidden rounded-full bg-neutral-alphas-50", children: /* @__PURE__ */ jsxRuntime.jsx(
86
+ "div",
87
+ {
88
+ className: "absolute inset-y-0 left-0 rounded-full bg-brand-primary-default transition-[width] duration-300 ease-in-out",
89
+ style: { width: maxCount === 0 ? "0%" : `${count / maxCount * 100}%` }
90
+ }
91
+ ) }),
92
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5", children: [
93
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "typography-body-small-14px-semibold text-content-primary", children: rating }),
94
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "typography-body-small-14px-regular text-content-tertiary", children: [
95
+ "(",
96
+ formatCount(count),
97
+ ")"
98
+ ] })
99
+ ] })
100
+ ]
101
+ }
102
+ ) }, rating))
96
103
  }
97
- ) }, rating)) })
104
+ )
98
105
  ] });
99
106
  }
100
107
  );
@@ -1 +1 @@
1
- {"version":3,"file":"RatingSummary.cjs","sources":["../../../../src/components/RatingSummary/RatingSummary.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { StarIcon } from \"../Icons/StarIcon\";\n\n/** A single rating value paired with the number of reviews that gave it. */\nexport interface RatingCount {\n /** The rating value this entry represents (e.g. `5`). */\n rating: number;\n /** Number of reviews that gave this rating. */\n count: number;\n}\n\nexport interface RatingSummaryProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /**\n * Per-rating review counts. Order is not significant — rows always render from\n * `maxRating` down to `1`, and any rating without an entry renders as zero.\n * Entries are expected to use whole-number ratings within `1`–`maxRating`;\n * any outside that range are ignored, including in the derived total and average.\n */\n distribution: RatingCount[];\n /**\n * Average rating shown in the header. Defaults to the count-weighted mean of\n * `distribution`; provide it to display a server-computed average instead.\n */\n averageRating?: number;\n /** Highest possible rating. Sets how many histogram rows render. @default 5 */\n maxRating?: number;\n /**\n * Formats a review count into its label, without surrounding parentheses.\n * Used for the visible text and as the count portion of the default\n * accessible labels.\n * @default ``(count) => `${count.toLocaleString()} review(s)` ``\n */\n formatCount?: (count: number) => string;\n /**\n * Builds the accessible label for the header. Override to localise the\n * screen-reader text. `average` is the already-formatted average string.\n * @default ``(average, max, total) => `Average rating ${average} out of ${max}, ${formatCount(total)}` ``\n */\n formatAverageLabel?: (average: string, maxRating: number, total: number) => string;\n /**\n * Builds the accessible label for each histogram row. Override to localise the\n * screen-reader text.\n * @default ``(rating, count) => `${rating} star(s), ${formatCount(count)}` ``\n */\n formatRatingLabel?: (rating: number, count: number) => string;\n}\n\nfunction defaultFormatCount(count: number): string {\n return `${count.toLocaleString()} ${count === 1 ? \"review\" : \"reviews\"}`;\n}\n\nfunction getWeightedAverage(distribution: RatingCount[]): number {\n let total = 0;\n let weighted = 0;\n for (const { rating, count } of distribution) {\n total += count;\n weighted += rating * count;\n }\n return total === 0 ? 0 : weighted / total;\n}\n\n/**\n * An aggregated rating overview: a headline average with the total review count,\n * followed by a per-rating histogram. Bar lengths are scaled relative to the\n * most-reviewed rating, so the busiest rating always fills the track.\n *\n * The brand star is decorative; the header and each histogram row expose\n * `role=\"img\"` with an accessible label (override via `formatAverageLabel` /\n * `formatRatingLabel`) so the single-star-plus-number reads clearly.\n *\n * @example\n * ```tsx\n * <RatingSummary\n * distribution={[\n * { rating: 5, count: 300 },\n * { rating: 4, count: 20 },\n * { rating: 3, count: 20 },\n * { rating: 2, count: 10 },\n * { rating: 1, count: 0 },\n * ]}\n * />\n * ```\n */\nexport const RatingSummary = React.forwardRef<HTMLDivElement, RatingSummaryProps>(\n (\n {\n distribution,\n averageRating,\n maxRating = 5,\n formatCount = defaultFormatCount,\n formatAverageLabel,\n formatRatingLabel,\n className,\n ...props\n },\n ref,\n ) => {\n const countByRating = new Map<number, number>();\n for (const { rating, count } of distribution) {\n countByRating.set(rating, (countByRating.get(rating) ?? 0) + count);\n }\n\n const rows = Array.from({ length: maxRating }, (_, index) => {\n const rating = maxRating - index;\n return { rating, count: countByRating.get(rating) ?? 0 };\n });\n\n const totalCount = rows.reduce((sum, row) => sum + row.count, 0);\n const maxCount = rows.reduce((max, row) => Math.max(max, row.count), 0);\n const average = averageRating ?? getWeightedAverage(rows);\n const averageLabel = average.toFixed(1);\n\n const headerLabel = formatAverageLabel\n ? formatAverageLabel(averageLabel, maxRating, totalCount)\n : `Average rating ${averageLabel} out of ${maxRating}, ${formatCount(totalCount)}`;\n\n return (\n <div ref={ref} className={cn(\"flex flex-col gap-4\", className)} {...props}>\n <div className=\"flex items-center gap-1\" role=\"img\" aria-label={headerLabel}>\n <StarIcon size={24} filled className=\"shrink-0 text-brand-primary-default\" />\n <span className=\"flex items-center gap-1.5\">\n <span className=\"typography-body-small-14px-semibold text-content-primary\">\n {averageLabel}\n </span>\n <span className=\"typography-body-small-14px-regular text-content-tertiary\">\n ({formatCount(totalCount)})\n </span>\n </span>\n </div>\n\n <ul className=\"m-0 flex list-none flex-col gap-4 p-0\" aria-label=\"Rating breakdown\">\n {rows.map(({ rating, count }) => (\n <li key={rating}>\n <div\n className=\"flex items-center gap-4\"\n role=\"img\"\n aria-label={\n formatRatingLabel\n ? formatRatingLabel(rating, count)\n : `${rating} ${rating === 1 ? \"star\" : \"stars\"}, ${formatCount(count)}`\n }\n >\n <div className=\"relative h-4 flex-1 overflow-hidden rounded-full bg-neutral-alphas-50\">\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-brand-primary-default transition-[width] duration-300 ease-in-out\"\n style={{ width: maxCount === 0 ? \"0%\" : `${(count / maxCount) * 100}%` }}\n />\n </div>\n <span className=\"flex shrink-0 items-center gap-1.5\">\n <span className=\"typography-body-small-14px-semibold text-content-primary\">\n {rating}\n </span>\n <span className=\"typography-body-small-14px-regular text-content-tertiary\">\n ({formatCount(count)})\n </span>\n </span>\n </div>\n </li>\n ))}\n </ul>\n </div>\n );\n },\n);\n\nRatingSummary.displayName = \"RatingSummary\";\n"],"names":["React","jsxs","cn","jsx","StarIcon"],"mappings":"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"}
1
+ {"version":3,"file":"RatingSummary.cjs","sources":["../../../../src/components/RatingSummary/RatingSummary.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { StarIcon } from \"../Icons/StarIcon\";\n\n/** A single rating value paired with the number of reviews that gave it. */\nexport interface RatingCount {\n /** The rating value this entry represents (e.g. `5`). */\n rating: number;\n /** Number of reviews that gave this rating. */\n count: number;\n}\n\nexport interface RatingSummaryProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /**\n * Per-rating review counts. Order is not significant — rows always render from\n * `maxRating` down to `1`, and any rating without an entry renders as zero.\n * Entries are expected to use whole-number ratings within `1`–`maxRating`;\n * any outside that range are ignored, including in the derived total and average.\n */\n distribution: RatingCount[];\n /**\n * Average rating shown in the header. Defaults to the count-weighted mean of\n * `distribution`; provide it to display a server-computed average instead.\n */\n averageRating?: number;\n /** Highest possible rating. Sets how many histogram rows render. @default 5 */\n maxRating?: number;\n /**\n * Formats a review count into its label, without surrounding parentheses.\n * Used for the visible text and as the count portion of the default\n * accessible labels.\n * @default ``(count) => `${count.toLocaleString()} review(s)` ``\n */\n formatCount?: (count: number) => string;\n /**\n * Builds the accessible label for the header. Override to localise the\n * screen-reader text. `average` is the already-formatted average string.\n * @default ``(average, max, total) => `Average rating ${average} out of ${max}, ${formatCount(total)}` ``\n */\n formatAverageLabel?: (average: string, maxRating: number, total: number) => string;\n /**\n * Builds the accessible label for each histogram row. Override to localise the\n * screen-reader text.\n * @default ``(rating, count) => `${rating} star(s), ${formatCount(count)}` ``\n */\n formatRatingLabel?: (rating: number, count: number) => string;\n}\n\nfunction defaultFormatCount(count: number): string {\n return `${count.toLocaleString()} ${count === 1 ? \"review\" : \"reviews\"}`;\n}\n\nfunction getWeightedAverage(distribution: RatingCount[]): number {\n let total = 0;\n let weighted = 0;\n for (const { rating, count } of distribution) {\n total += count;\n weighted += rating * count;\n }\n return total === 0 ? 0 : weighted / total;\n}\n\n/**\n * An aggregated rating overview: a headline average with the total review count,\n * followed by a per-rating histogram. Bar lengths are scaled relative to the\n * most-reviewed rating, so the busiest rating always fills the track.\n *\n * The histogram is one shared grid (with subgrid rows) rather than per-row\n * flex: the label column is sized by the widest label across all rows, so a\n * width difference between labels (e.g. \"1 review\" vs \"3 reviews\") never\n * changes a row's track length — every bar's right edge stays aligned.\n *\n * The brand star is decorative; the header and each histogram row expose\n * `role=\"img\"` with an accessible label (override via `formatAverageLabel` /\n * `formatRatingLabel`) so the single-star-plus-number reads clearly.\n *\n * @example\n * ```tsx\n * <RatingSummary\n * distribution={[\n * { rating: 5, count: 300 },\n * { rating: 4, count: 20 },\n * { rating: 3, count: 20 },\n * { rating: 2, count: 10 },\n * { rating: 1, count: 0 },\n * ]}\n * />\n * ```\n */\nexport const RatingSummary = React.forwardRef<HTMLDivElement, RatingSummaryProps>(\n (\n {\n distribution,\n averageRating,\n maxRating = 5,\n formatCount = defaultFormatCount,\n formatAverageLabel,\n formatRatingLabel,\n className,\n ...props\n },\n ref,\n ) => {\n const countByRating = new Map<number, number>();\n for (const { rating, count } of distribution) {\n countByRating.set(rating, (countByRating.get(rating) ?? 0) + count);\n }\n\n const rows = Array.from({ length: maxRating }, (_, index) => {\n const rating = maxRating - index;\n return { rating, count: countByRating.get(rating) ?? 0 };\n });\n\n const totalCount = rows.reduce((sum, row) => sum + row.count, 0);\n const maxCount = rows.reduce((max, row) => Math.max(max, row.count), 0);\n const average = averageRating ?? getWeightedAverage(rows);\n const averageLabel = average.toFixed(1);\n\n const headerLabel = formatAverageLabel\n ? formatAverageLabel(averageLabel, maxRating, totalCount)\n : `Average rating ${averageLabel} out of ${maxRating}, ${formatCount(totalCount)}`;\n\n return (\n <div ref={ref} className={cn(\"flex flex-col gap-4\", className)} {...props}>\n <div className=\"flex items-center gap-1\" role=\"img\" aria-label={headerLabel}>\n <StarIcon size={24} filled className=\"shrink-0 text-brand-primary-default\" />\n <span className=\"flex items-center gap-1.5\">\n <span className=\"typography-body-small-14px-semibold text-content-primary\">\n {averageLabel}\n </span>\n <span className=\"typography-body-small-14px-regular text-content-tertiary\">\n ({formatCount(totalCount)})\n </span>\n </span>\n </div>\n\n <ul\n className=\"m-0 grid list-none grid-cols-[1fr_auto] gap-4 p-0\"\n aria-label=\"Rating breakdown\"\n >\n {rows.map(({ rating, count }) => (\n <li key={rating} className=\"col-span-2 grid grid-cols-subgrid\">\n <div\n className=\"col-span-2 grid grid-cols-subgrid items-center\"\n role=\"img\"\n aria-label={\n formatRatingLabel\n ? formatRatingLabel(rating, count)\n : `${rating} ${rating === 1 ? \"star\" : \"stars\"}, ${formatCount(count)}`\n }\n >\n <div className=\"relative h-4 overflow-hidden rounded-full bg-neutral-alphas-50\">\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-brand-primary-default transition-[width] duration-300 ease-in-out\"\n style={{ width: maxCount === 0 ? \"0%\" : `${(count / maxCount) * 100}%` }}\n />\n </div>\n <span className=\"flex items-center gap-1.5\">\n <span className=\"typography-body-small-14px-semibold text-content-primary\">\n {rating}\n </span>\n <span className=\"typography-body-small-14px-regular text-content-tertiary\">\n ({formatCount(count)})\n </span>\n </span>\n </div>\n </li>\n ))}\n </ul>\n </div>\n );\n },\n);\n\nRatingSummary.displayName = \"RatingSummary\";\n"],"names":["React","jsxs","cn","jsx","StarIcon"],"mappings":"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"}
@@ -78,8 +78,8 @@ function TextAreaHelperText({
78
78
  {
79
79
  id,
80
80
  className: cn.cn(
81
- "typography-description-12px-regular px-2 pt-1 pb-0.5",
82
- error ? "text-error-content" : "text-content-secondary"
81
+ "typography-description-12px-regular pt-2",
82
+ error ? "text-error-content" : "text-content-tertiary"
83
83
  ),
84
84
  children
85
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea.cjs","sources":["../../../../src/components/TextArea/TextArea.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CheckOutlineIcon } from \"../Icons/CheckOutlineIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Text area height in pixels. */\nexport type TextAreaSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n /** Label text displayed above the textarea. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the textarea. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Minimum height of the text area in pixels. @default \"48\" */\n size?: TextAreaSize;\n /** Whether the text area is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the textarea. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text area is validated. @default false */\n validated?: boolean;\n /** Whether the text area stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Whether to show a clear button when text is present. @default false */\n showClearButton?: boolean;\n /** Callback fired when the clear button is clicked. Note: `onChange` is also called with an empty value when clearing. */\n onClear?: () => void;\n /** Minimum number of rows (lines) for the textarea. */\n minRows?: number;\n /** Maximum number of rows (lines) for the textarea. */\n maxRows?: number;\n /** Whether the textarea can be resized by the user. @default true */\n resizable?: boolean;\n}\n\nconst CONTAINER_MIN_HEIGHT: Record<TextAreaSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst TEXTAREA_SIZE_CLASSES: Record<TextAreaSize, string> = {\n \"48\": \"py-3 typography-body-default-16px-regular autofill-body-lg\",\n \"40\": \"py-2 typography-body-default-16px-regular autofill-body-lg\",\n \"32\": \"py-2 typography-body-small-14px-regular autofill-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextAreaSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst PADDING_RIGHT_WITH_CLEAR: Record<TextAreaSize, string> = {\n \"48\": \"pr-11\",\n \"40\": \"pr-11\",\n \"32\": \"pr-10\",\n};\n\nconst CLEAR_BUTTON_RIGHT: Record<TextAreaSize, string> = {\n \"48\": \"right-4 top-3\",\n \"40\": \"right-4 top-2\",\n \"32\": \"right-3 top-2\",\n};\n\nfunction getContainerClassName(size: TextAreaSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative rounded-sm border bg-inputs-inputs-primary has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-border-primary\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_MIN_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getTextareaClassName(\n size: TextAreaSize,\n hasClearButton: boolean,\n hasMinRows: boolean,\n resizable: boolean,\n) {\n return cn(\n \"h-full w-full bg-transparent text-content-primary no-underline placeholder:text-content-tertiary focus:outline-none disabled:cursor-not-allowed\",\n resizable ? \"resize-y\" : \"resize-none\",\n !hasMinRows && \"min-h-[80px]\",\n TEXTAREA_SIZE_CLASSES[size],\n PADDING_HORIZONTAL[size],\n hasClearButton ? PADDING_RIGHT_WITH_CLEAR[size] : \"\",\n );\n}\n\nfunction TextAreaHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-description-12px-regular px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextArea: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction calculateMaxHeight(size: TextAreaSize, maxRows?: number): string | undefined {\n if (!maxRows) return undefined;\n\n // Line height is 24px for body-1 (sizes 48 and 40) and 20px for body-2 (size 32)\n const lineHeight = size === \"32\" ? 20 : 24;\n // py-2 = 8px, py-3 = 12px\n const verticalPadding = size === \"32\" ? 8 : size === \"40\" ? 8 : 12;\n\n return `${lineHeight * maxRows + verticalPadding * 2}px`;\n}\n\nfunction useTextAreaValue(\n value: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"value\"],\n defaultValue: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"defaultValue\"],\n showClearButton: boolean,\n onChange?: React.ChangeEventHandler<HTMLTextAreaElement>,\n onClear?: () => void,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n) {\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const resolvedValue = value !== undefined ? value : internalValue;\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(e.target.value);\n onChange?.(e);\n };\n\n const handleClear = () => {\n setInternalValue(\"\");\n\n if (onChange && textareaRef?.current) {\n const syntheticEvent = {\n target: { ...textareaRef.current, value: \"\" },\n currentTarget: { ...textareaRef.current, value: \"\" },\n } as React.ChangeEvent<HTMLTextAreaElement>;\n onChange(syntheticEvent);\n }\n\n onClear?.();\n };\n\n return {\n resolvedValue,\n displayValue: showClearButton ? resolvedValue : value,\n resolvedDefaultValue: showClearButton ? undefined : defaultValue,\n handleChange,\n handleClear,\n };\n}\n\n/**\n * A multi-line text input with optional label, helper/error text, and clear button.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextArea\n * label=\"Description\"\n * placeholder=\"Enter your description...\"\n * showClearButton\n * error={!!descError}\n * errorMessage={descError}\n * />\n * ```\n */\nexport const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n className,\n id,\n disabled,\n fullWidth = false,\n showClearButton = false,\n onClear,\n value,\n defaultValue,\n onChange,\n minRows,\n maxRows,\n resizable = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n const maxHeight = calculateMaxHeight(size, maxRows);\n\n const internalRef = React.useRef<HTMLTextAreaElement>(null);\n\n const { resolvedValue, displayValue, resolvedDefaultValue, handleChange, handleClear } =\n useTextAreaValue(value, defaultValue, showClearButton, onChange, onClear, internalRef);\n\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n internalRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const showClear = showClearButton && resolvedValue !== \"\" && !disabled;\n const showValidated = validated && !showClear;\n const ariaDescribedBy = bottomText ? helperTextId : undefined;\n const textareaStyle = maxHeight ? { maxHeight } : undefined;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-description-12px-semibold pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <textarea\n ref={mergedRef}\n id={inputId}\n disabled={disabled}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error || undefined}\n className={getTextareaClassName(size, showClear, !!minRows, resizable)}\n value={displayValue}\n defaultValue={resolvedDefaultValue}\n onChange={handleChange}\n rows={minRows}\n style={textareaStyle}\n {...props}\n />\n\n {showClear && (\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n icon={<CloseIcon />}\n aria-label=\"Clear text\"\n onClick={handleClear}\n className={cn(\n \"absolute flex size-5 items-center justify-center self-end\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n />\n )}\n {showValidated && (\n <div\n className={cn(\n \"pointer-events-none absolute flex size-5 items-center justify-center\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n >\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextAreaHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextAreaHelperText>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n"],"names":["cn","jsx","React","jsxs","IconButton","CloseIcon","CheckOutlineIcon"],"mappings":"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"}
1
+ {"version":3,"file":"TextArea.cjs","sources":["../../../../src/components/TextArea/TextArea.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CheckOutlineIcon } from \"../Icons/CheckOutlineIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Text area height in pixels. */\nexport type TextAreaSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n /** Label text displayed above the textarea. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the textarea. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Minimum height of the text area in pixels. @default \"48\" */\n size?: TextAreaSize;\n /** Whether the text area is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the textarea. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text area is validated. @default false */\n validated?: boolean;\n /** Whether the text area stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Whether to show a clear button when text is present. @default false */\n showClearButton?: boolean;\n /** Callback fired when the clear button is clicked. Note: `onChange` is also called with an empty value when clearing. */\n onClear?: () => void;\n /** Minimum number of rows (lines) for the textarea. */\n minRows?: number;\n /** Maximum number of rows (lines) for the textarea. */\n maxRows?: number;\n /** Whether the textarea can be resized by the user. @default true */\n resizable?: boolean;\n}\n\nconst CONTAINER_MIN_HEIGHT: Record<TextAreaSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst TEXTAREA_SIZE_CLASSES: Record<TextAreaSize, string> = {\n \"48\": \"py-3 typography-body-default-16px-regular autofill-body-lg\",\n \"40\": \"py-2 typography-body-default-16px-regular autofill-body-lg\",\n \"32\": \"py-2 typography-body-small-14px-regular autofill-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextAreaSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst PADDING_RIGHT_WITH_CLEAR: Record<TextAreaSize, string> = {\n \"48\": \"pr-11\",\n \"40\": \"pr-11\",\n \"32\": \"pr-10\",\n};\n\nconst CLEAR_BUTTON_RIGHT: Record<TextAreaSize, string> = {\n \"48\": \"right-4 top-3\",\n \"40\": \"right-4 top-2\",\n \"32\": \"right-3 top-2\",\n};\n\nfunction getContainerClassName(size: TextAreaSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative rounded-sm border bg-inputs-inputs-primary has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-border-primary\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_MIN_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getTextareaClassName(\n size: TextAreaSize,\n hasClearButton: boolean,\n hasMinRows: boolean,\n resizable: boolean,\n) {\n return cn(\n \"h-full w-full bg-transparent text-content-primary no-underline placeholder:text-content-tertiary focus:outline-none disabled:cursor-not-allowed\",\n resizable ? \"resize-y\" : \"resize-none\",\n !hasMinRows && \"min-h-[80px]\",\n TEXTAREA_SIZE_CLASSES[size],\n PADDING_HORIZONTAL[size],\n hasClearButton ? PADDING_RIGHT_WITH_CLEAR[size] : \"\",\n );\n}\n\nfunction TextAreaHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-description-12px-regular pt-2\",\n error ? \"text-error-content\" : \"text-content-tertiary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextArea: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction calculateMaxHeight(size: TextAreaSize, maxRows?: number): string | undefined {\n if (!maxRows) return undefined;\n\n // Line height is 24px for body-1 (sizes 48 and 40) and 20px for body-2 (size 32)\n const lineHeight = size === \"32\" ? 20 : 24;\n // py-2 = 8px, py-3 = 12px\n const verticalPadding = size === \"32\" ? 8 : size === \"40\" ? 8 : 12;\n\n return `${lineHeight * maxRows + verticalPadding * 2}px`;\n}\n\nfunction useTextAreaValue(\n value: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"value\"],\n defaultValue: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"defaultValue\"],\n showClearButton: boolean,\n onChange?: React.ChangeEventHandler<HTMLTextAreaElement>,\n onClear?: () => void,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n) {\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const resolvedValue = value !== undefined ? value : internalValue;\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(e.target.value);\n onChange?.(e);\n };\n\n const handleClear = () => {\n setInternalValue(\"\");\n\n if (onChange && textareaRef?.current) {\n const syntheticEvent = {\n target: { ...textareaRef.current, value: \"\" },\n currentTarget: { ...textareaRef.current, value: \"\" },\n } as React.ChangeEvent<HTMLTextAreaElement>;\n onChange(syntheticEvent);\n }\n\n onClear?.();\n };\n\n return {\n resolvedValue,\n displayValue: showClearButton ? resolvedValue : value,\n resolvedDefaultValue: showClearButton ? undefined : defaultValue,\n handleChange,\n handleClear,\n };\n}\n\n/**\n * A multi-line text input with optional label, helper/error text, and clear button.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextArea\n * label=\"Description\"\n * placeholder=\"Enter your description...\"\n * showClearButton\n * error={!!descError}\n * errorMessage={descError}\n * />\n * ```\n */\nexport const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n className,\n id,\n disabled,\n fullWidth = false,\n showClearButton = false,\n onClear,\n value,\n defaultValue,\n onChange,\n minRows,\n maxRows,\n resizable = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n const maxHeight = calculateMaxHeight(size, maxRows);\n\n const internalRef = React.useRef<HTMLTextAreaElement>(null);\n\n const { resolvedValue, displayValue, resolvedDefaultValue, handleChange, handleClear } =\n useTextAreaValue(value, defaultValue, showClearButton, onChange, onClear, internalRef);\n\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n internalRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const showClear = showClearButton && resolvedValue !== \"\" && !disabled;\n const showValidated = validated && !showClear;\n const ariaDescribedBy = bottomText ? helperTextId : undefined;\n const textareaStyle = maxHeight ? { maxHeight } : undefined;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-description-12px-semibold pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <textarea\n ref={mergedRef}\n id={inputId}\n disabled={disabled}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error || undefined}\n className={getTextareaClassName(size, showClear, !!minRows, resizable)}\n value={displayValue}\n defaultValue={resolvedDefaultValue}\n onChange={handleChange}\n rows={minRows}\n style={textareaStyle}\n {...props}\n />\n\n {showClear && (\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n icon={<CloseIcon />}\n aria-label=\"Clear text\"\n onClick={handleClear}\n className={cn(\n \"absolute flex size-5 items-center justify-center self-end\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n />\n )}\n {showValidated && (\n <div\n className={cn(\n \"pointer-events-none absolute flex size-5 items-center justify-center\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n >\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextAreaHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextAreaHelperText>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n"],"names":["cn","jsx","React","jsxs","IconButton","CloseIcon","CheckOutlineIcon"],"mappings":"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"}
@@ -33,6 +33,11 @@ const CONTAINER_PADDING_X = {
33
33
  "40": "px-4",
34
34
  "32": "px-3"
35
35
  };
36
+ const CONTAINER_PADDING_X_WITH_ACTION = {
37
+ "48": "pl-4 pr-2",
38
+ "40": "pl-4 pr-2",
39
+ "32": "pl-3 pr-2"
40
+ };
36
41
  const CONTAINER_GAP = {
37
42
  "48": "gap-2.5",
38
43
  "40": "gap-2.5",
@@ -48,10 +53,10 @@ const SIDE_LABEL_TYPOGRAPHY = {
48
53
  "40": "typography-body-default-16px-regular",
49
54
  "32": "typography-body-small-14px-regular"
50
55
  };
51
- function getContainerClassName(size, error, disabled) {
56
+ function getContainerClassName(size, error, disabled, hasAction) {
52
57
  return cn.cn(
53
58
  "relative flex items-center overflow-hidden rounded-sm border bg-inputs-inputs-primary has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
54
- CONTAINER_PADDING_X[size],
59
+ hasAction ? CONTAINER_PADDING_X_WITH_ACTION[size] : CONTAINER_PADDING_X[size],
55
60
  CONTAINER_GAP[size],
56
61
  error ? "border-error-content" : "border-border-primary",
57
62
  !disabled && !error && "hover:border-neutral-alphas-400",
@@ -103,8 +108,8 @@ function TextFieldHelperText({
103
108
  {
104
109
  id,
105
110
  className: cn.cn(
106
- "typography-description-12px-regular px-2 pt-2 pb-0.5",
107
- error ? "text-error-content" : "text-content-secondary"
111
+ "typography-description-12px-regular pt-2",
112
+ error ? "text-error-content" : "text-content-tertiary"
108
113
  ),
109
114
  children
110
115
  }
@@ -131,6 +136,7 @@ const TextField = React__namespace.forwardRef(
131
136
  rightIcon,
132
137
  leftLabel,
133
138
  rightLabel,
139
+ action,
134
140
  className,
135
141
  id,
136
142
  disabled,
@@ -184,7 +190,7 @@ const TextField = React__namespace.forwardRef(
184
190
  /* @__PURE__ */ jsxRuntime.jsxs(
185
191
  "div",
186
192
  {
187
- className: getContainerClassName(size, error, disabled),
193
+ className: getContainerClassName(size, error, disabled, action != null),
188
194
  onMouseDown: handleContainerMouseDown,
189
195
  children: [
190
196
  /* @__PURE__ */ jsxRuntime.jsx(LeadingIcon, { children: leftIcon }),
@@ -206,7 +212,8 @@ const TextField = React__namespace.forwardRef(
206
212
  }
207
213
  ),
208
214
  /* @__PURE__ */ jsxRuntime.jsx(SideLabel, { id: rightLabelId, size, align: "right", children: rightLabel }),
209
- /* @__PURE__ */ jsxRuntime.jsx(TrailingAdornment, { rightIcon, validated })
215
+ /* @__PURE__ */ jsxRuntime.jsx(TrailingAdornment, { rightIcon, validated }),
216
+ action != null && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-tf-interactive": "", className: "flex shrink-0 items-center", children: action })
210
217
  ]
211
218
  }
212
219
  ),
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.cjs","sources":["../../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CheckOutlineIcon } from \"@/index\";\nimport { cn } from \"../../utils/cn\";\n\n/** Text field height in pixels. */\nexport type TextFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"prefix\"> {\n /** Label text displayed above the input. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the input. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the text field in pixels. @default \"48\" */\n size?: TextFieldSize;\n /** Whether the text field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the input. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text field is validated. @default false */\n validated?: boolean;\n /** Icon element displayed at the left side of the input. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed at the right side of the input. */\n rightIcon?: React.ReactNode;\n /** Fixed, non-editable label pinned inside the left edge of the field — for a prefix such as a currency symbol or country code. */\n leftLabel?: React.ReactNode;\n /** Fixed, non-editable label pinned inside the right edge of the field — for a unit or suffix such as a currency code or domain. */\n rightLabel?: React.ReactNode;\n /** Whether the text field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n}\n\nconst CONTAINER_HEIGHT: Record<TextFieldSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst CONTAINER_PADDING_X: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst CONTAINER_GAP: Record<TextFieldSize, string> = {\n \"48\": \"gap-2.5\",\n \"40\": \"gap-2.5\",\n \"32\": \"gap-2\",\n};\n\nconst INPUT_TYPOGRAPHY: Record<TextFieldSize, string> = {\n \"48\": \"typography-body-default-16px-regular autofill-body-lg\",\n \"40\": \"typography-body-default-16px-regular autofill-body-lg\",\n \"32\": \"typography-body-small-14px-regular autofill-body-md\",\n};\n\nconst SIDE_LABEL_TYPOGRAPHY: Record<TextFieldSize, string> = {\n \"48\": \"typography-body-default-16px-regular\",\n \"40\": \"typography-body-default-16px-regular\",\n \"32\": \"typography-body-small-14px-regular\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative flex items-center overflow-hidden rounded-sm border bg-inputs-inputs-primary has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n CONTAINER_PADDING_X[size],\n CONTAINER_GAP[size],\n error ? \"border-error-content\" : \"border-border-primary\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction LeadingIcon({ children }: { children?: React.ReactNode }) {\n if (!children) return null;\n return (\n <span className=\"pointer-events-none flex size-4 shrink-0 items-center justify-center text-content-secondary\">\n {children}\n </span>\n );\n}\n\nfunction SideLabel({\n id,\n size,\n align,\n children,\n}: {\n id?: string;\n size: TextFieldSize;\n align: \"left\" | \"right\";\n children?: React.ReactNode;\n}) {\n if (!children) return null;\n return (\n <span\n id={id}\n className={cn(\n \"shrink-0 select-none whitespace-nowrap text-content-tertiary\",\n align === \"right\" && \"text-right\",\n SIDE_LABEL_TYPOGRAPHY[size],\n )}\n >\n {children}\n </span>\n );\n}\n\nfunction TrailingAdornment({\n rightIcon,\n validated,\n}: {\n rightIcon?: React.ReactNode;\n validated: boolean;\n}) {\n if (!rightIcon && !validated) return null;\n return (\n <span className=\"flex shrink-0 items-center gap-2 text-content-secondary\">\n {rightIcon && (\n <span data-tf-interactive=\"\" className=\"flex size-4 shrink-0 items-center justify-center\">\n {rightIcon}\n </span>\n )}\n {validated && (\n <span className=\"pointer-events-none flex size-4 shrink-0 items-center justify-center\">\n <CheckOutlineIcon className=\"text-success-content\" />\n </span>\n )}\n </span>\n );\n}\n\nfunction TextFieldHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-description-12px-regular px-2 pt-2 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A text input field with optional label, helper/error text, icon slots, and side labels.\n *\n * Use `leftLabel` / `rightLabel` for fixed unit or prefix affordances (currency symbol,\n * country code, domain suffix). Provide at least one of `label`, `aria-label`, or\n * `aria-labelledby` for accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextField\n * label=\"Email\"\n * placeholder=\"you@example.com\"\n * error={!!emailError}\n * errorMessage={emailError}\n * />\n * ```\n *\n * @example\n * ```tsx\n * <TextField label=\"Price\" leftLabel=\"$\" rightLabel=\"USD\" placeholder=\"0.00\" />\n * ```\n */\nexport const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n leftIcon,\n rightIcon,\n leftLabel,\n rightLabel,\n className,\n id,\n disabled,\n fullWidth = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const leftLabelId = `${inputId}-left-label`;\n const rightLabelId = `${inputId}-right-label`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n const describedBy =\n [\n leftLabel != null ? leftLabelId : null,\n rightLabel != null ? rightLabelId : null,\n bottomText ? helperTextId : null,\n ]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n const innerRef = React.useRef<HTMLInputElement>(null);\n const setRefs = React.useCallback(\n (node: HTMLInputElement | null) => {\n innerRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = node;\n },\n [ref],\n );\n\n // Keep clicks on the non-interactive adornments (icons, side labels, padding)\n // focusing the input, matching the old absolute/pointer-events-none layout.\n const handleContainerMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return;\n const target = event.target as HTMLElement;\n if (target === innerRef.current) return;\n if (target.closest(\"[data-tf-interactive]\")) return;\n event.preventDefault();\n innerRef.current?.focus();\n };\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-description-12px-semibold pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n {/* biome-ignore lint/a11y/noStaticElementInteractions: focus bridge delegates pointer clicks on adornments to the input */}\n <div\n className={getContainerClassName(size, error, disabled)}\n onMouseDown={handleContainerMouseDown}\n >\n <LeadingIcon>{leftIcon}</LeadingIcon>\n <SideLabel id={leftLabelId} size={size} align=\"left\">\n {leftLabel}\n </SideLabel>\n\n <input\n ref={setRefs}\n id={inputId}\n disabled={disabled}\n aria-describedby={describedBy}\n aria-invalid={error || undefined}\n className={cn(\n \"h-full min-w-0 flex-1 bg-transparent text-content-primary no-underline placeholder:text-content-tertiary focus:outline-none disabled:cursor-not-allowed\",\n INPUT_TYPOGRAPHY[size],\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n <SideLabel id={rightLabelId} size={size} align=\"right\">\n {rightLabel}\n </SideLabel>\n <TrailingAdornment rightIcon={rightIcon} validated={validated} />\n </div>\n\n {bottomText && (\n <TextFieldHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextFieldHelperText>\n )}\n </div>\n );\n },\n);\n\nTextField.displayName = \"TextField\";\n"],"names":["cn","jsx","jsxs","CheckOutlineIcon","React"],"mappings":"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"}
1
+ {"version":3,"file":"TextField.cjs","sources":["../../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CheckOutlineIcon } from \"@/index\";\nimport { cn } from \"../../utils/cn\";\n\n/** Text field height in pixels. */\nexport type TextFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"prefix\"> {\n /** Label text displayed above the input. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the input. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the text field in pixels. @default \"48\" */\n size?: TextFieldSize;\n /** Whether the text field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the input. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text field is validated. @default false */\n validated?: boolean;\n /** Icon element displayed at the left side of the input. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed at the right side of the input. */\n rightIcon?: React.ReactNode;\n /** Fixed, non-editable label pinned inside the left edge of the field — for a prefix such as a currency symbol or country code. */\n leftLabel?: React.ReactNode;\n /** Fixed, non-editable label pinned inside the right edge of the field — for a unit or suffix such as a currency code or domain. */\n rightLabel?: React.ReactNode;\n /**\n * Trailing interactive element pinned to the right edge — typically a `Chip`\n * or `Button` (the \"with button\" field type). Reduces the right padding so the\n * control sits flush, and clicks on it do not steal focus from the input.\n */\n action?: React.ReactNode;\n /** Whether the text field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n}\n\nconst CONTAINER_HEIGHT: Record<TextFieldSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst CONTAINER_PADDING_X: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst CONTAINER_PADDING_X_WITH_ACTION: Record<TextFieldSize, string> = {\n \"48\": \"pl-4 pr-2\",\n \"40\": \"pl-4 pr-2\",\n \"32\": \"pl-3 pr-2\",\n};\n\nconst CONTAINER_GAP: Record<TextFieldSize, string> = {\n \"48\": \"gap-2.5\",\n \"40\": \"gap-2.5\",\n \"32\": \"gap-2\",\n};\n\nconst INPUT_TYPOGRAPHY: Record<TextFieldSize, string> = {\n \"48\": \"typography-body-default-16px-regular autofill-body-lg\",\n \"40\": \"typography-body-default-16px-regular autofill-body-lg\",\n \"32\": \"typography-body-small-14px-regular autofill-body-md\",\n};\n\nconst SIDE_LABEL_TYPOGRAPHY: Record<TextFieldSize, string> = {\n \"48\": \"typography-body-default-16px-regular\",\n \"40\": \"typography-body-default-16px-regular\",\n \"32\": \"typography-body-small-14px-regular\",\n};\n\nfunction getContainerClassName(\n size: TextFieldSize,\n error: boolean,\n disabled?: boolean,\n hasAction?: boolean,\n) {\n return cn(\n \"relative flex items-center overflow-hidden rounded-sm border bg-inputs-inputs-primary has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n hasAction ? CONTAINER_PADDING_X_WITH_ACTION[size] : CONTAINER_PADDING_X[size],\n CONTAINER_GAP[size],\n error ? \"border-error-content\" : \"border-border-primary\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction LeadingIcon({ children }: { children?: React.ReactNode }) {\n if (!children) return null;\n return (\n <span className=\"pointer-events-none flex size-4 shrink-0 items-center justify-center text-content-secondary\">\n {children}\n </span>\n );\n}\n\nfunction SideLabel({\n id,\n size,\n align,\n children,\n}: {\n id?: string;\n size: TextFieldSize;\n align: \"left\" | \"right\";\n children?: React.ReactNode;\n}) {\n if (!children) return null;\n return (\n <span\n id={id}\n className={cn(\n \"shrink-0 select-none whitespace-nowrap text-content-tertiary\",\n align === \"right\" && \"text-right\",\n SIDE_LABEL_TYPOGRAPHY[size],\n )}\n >\n {children}\n </span>\n );\n}\n\nfunction TrailingAdornment({\n rightIcon,\n validated,\n}: {\n rightIcon?: React.ReactNode;\n validated: boolean;\n}) {\n if (!rightIcon && !validated) return null;\n return (\n <span className=\"flex shrink-0 items-center gap-2 text-content-secondary\">\n {rightIcon && (\n <span data-tf-interactive=\"\" className=\"flex size-4 shrink-0 items-center justify-center\">\n {rightIcon}\n </span>\n )}\n {validated && (\n <span className=\"pointer-events-none flex size-4 shrink-0 items-center justify-center\">\n <CheckOutlineIcon className=\"text-success-content\" />\n </span>\n )}\n </span>\n );\n}\n\nfunction TextFieldHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-description-12px-regular pt-2\",\n error ? \"text-error-content\" : \"text-content-tertiary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A text input field with optional label, helper/error text, icon slots, and side labels.\n *\n * Use `leftLabel` / `rightLabel` for fixed unit or prefix affordances (currency symbol,\n * country code, domain suffix). Provide at least one of `label`, `aria-label`, or\n * `aria-labelledby` for accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextField\n * label=\"Email\"\n * placeholder=\"you@example.com\"\n * error={!!emailError}\n * errorMessage={emailError}\n * />\n * ```\n *\n * @example\n * ```tsx\n * <TextField label=\"Price\" leftLabel=\"$\" rightLabel=\"USD\" placeholder=\"0.00\" />\n * ```\n *\n * @example\n * ```tsx\n * <TextField\n * label=\"Promo code\"\n * placeholder=\"Enter code\"\n * action={<Chip size=\"32\" onClick={apply}>Apply</Chip>}\n * />\n * ```\n */\nexport const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n leftIcon,\n rightIcon,\n leftLabel,\n rightLabel,\n action,\n className,\n id,\n disabled,\n fullWidth = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const leftLabelId = `${inputId}-left-label`;\n const rightLabelId = `${inputId}-right-label`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n const describedBy =\n [\n leftLabel != null ? leftLabelId : null,\n rightLabel != null ? rightLabelId : null,\n bottomText ? helperTextId : null,\n ]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n const innerRef = React.useRef<HTMLInputElement>(null);\n const setRefs = React.useCallback(\n (node: HTMLInputElement | null) => {\n innerRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = node;\n },\n [ref],\n );\n\n // Keep clicks on the non-interactive adornments (icons, side labels, padding)\n // focusing the input, matching the old absolute/pointer-events-none layout.\n const handleContainerMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return;\n const target = event.target as HTMLElement;\n if (target === innerRef.current) return;\n if (target.closest(\"[data-tf-interactive]\")) return;\n event.preventDefault();\n innerRef.current?.focus();\n };\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-description-12px-semibold pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n {/* biome-ignore lint/a11y/noStaticElementInteractions: focus bridge delegates pointer clicks on adornments to the input */}\n <div\n className={getContainerClassName(size, error, disabled, action != null)}\n onMouseDown={handleContainerMouseDown}\n >\n <LeadingIcon>{leftIcon}</LeadingIcon>\n <SideLabel id={leftLabelId} size={size} align=\"left\">\n {leftLabel}\n </SideLabel>\n\n <input\n ref={setRefs}\n id={inputId}\n disabled={disabled}\n aria-describedby={describedBy}\n aria-invalid={error || undefined}\n className={cn(\n \"h-full min-w-0 flex-1 bg-transparent text-content-primary no-underline placeholder:text-content-tertiary focus:outline-none disabled:cursor-not-allowed\",\n INPUT_TYPOGRAPHY[size],\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n <SideLabel id={rightLabelId} size={size} align=\"right\">\n {rightLabel}\n </SideLabel>\n <TrailingAdornment rightIcon={rightIcon} validated={validated} />\n {action != null && (\n <span data-tf-interactive=\"\" className=\"flex shrink-0 items-center\">\n {action}\n </span>\n )}\n </div>\n\n {bottomText && (\n <TextFieldHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextFieldHelperText>\n )}\n </div>\n );\n },\n);\n\nTextField.displayName = \"TextField\";\n"],"names":["cn","jsx","jsxs","CheckOutlineIcon","React"],"mappings":"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"}
@@ -153,6 +153,7 @@ const PlayIcon = require("./components/Icons/PlayIcon.cjs");
153
153
  const PlusIcon = require("./components/Icons/PlusIcon.cjs");
154
154
  const PrivacyIcon = require("./components/Icons/PrivacyIcon.cjs");
155
155
  const QueueIcon = require("./components/Icons/QueueIcon.cjs");
156
+ const ReceiveMoneyIcon = require("./components/Icons/ReceiveMoneyIcon.cjs");
156
157
  const RepeatIcon = require("./components/Icons/RepeatIcon.cjs");
157
158
  const Reply2Icon = require("./components/Icons/Reply2Icon.cjs");
158
159
  const ReplyIcon = require("./components/Icons/ReplyIcon.cjs");
@@ -215,6 +216,7 @@ const OnlineBlinkingIcon = require("./components/OnlineBlinkingIcon/OnlineBlinki
215
216
  const PageSelector = require("./components/PageSelector/PageSelector.cjs");
216
217
  const Pagination = require("./components/Pagination/Pagination.cjs");
217
218
  const PasswordField = require("./components/PasswordField/PasswordField.cjs");
219
+ const PhoneField = require("./components/PhoneField/PhoneField.cjs");
218
220
  const Pill = require("./components/Pill/Pill.cjs");
219
221
  const ProfileOnlineStatus = require("./components/ProfileOnlineStatus/ProfileOnlineStatus.cjs");
220
222
  const ProfileStatus = require("./components/ProfileStatus/ProfileStatus.cjs");
@@ -452,6 +454,7 @@ exports.PlayIcon = PlayIcon.PlayIcon;
452
454
  exports.PlusIcon = PlusIcon.PlusIcon;
453
455
  exports.PrivacyIcon = PrivacyIcon.PrivacyIcon;
454
456
  exports.QueueIcon = QueueIcon.QueueIcon;
457
+ exports.ReceiveMoneyIcon = ReceiveMoneyIcon.ReceiveMoneyIcon;
455
458
  exports.RepeatIcon = RepeatIcon.RepeatIcon;
456
459
  exports.Reply2Icon = Reply2Icon.Reply2Icon;
457
460
  exports.ReplyIcon = ReplyIcon.ReplyIcon;
@@ -516,6 +519,7 @@ exports.OnlineBlinkingIcon = OnlineBlinkingIcon.OnlineBlinkingIcon;
516
519
  exports.PageSelector = PageSelector.PageSelector;
517
520
  exports.Pagination = Pagination.Pagination;
518
521
  exports.PasswordField = PasswordField.PasswordField;
522
+ exports.PhoneField = PhoneField.PhoneField;
519
523
  exports.Pill = Pill.Pill;
520
524
  exports.ProfileOnlineStatus = ProfileOnlineStatus.ProfileOnlineStatus;
521
525
  exports.ProfileStatus = ProfileStatus.ProfileStatus;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}