@fanvue/ui 3.12.0 → 3.13.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 (46) hide show
  1. package/dist/cjs/components/Accordion/AccordionTrigger.cjs +11 -2
  2. package/dist/cjs/components/Accordion/AccordionTrigger.cjs.map +1 -1
  3. package/dist/cjs/components/Avatar/Avatar.cjs +2 -2
  4. package/dist/cjs/components/Avatar/Avatar.cjs.map +1 -1
  5. package/dist/cjs/components/Dialog/Dialog.cjs +1 -1
  6. package/dist/cjs/components/Dialog/Dialog.cjs.map +1 -1
  7. package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs +25 -2
  8. package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  9. package/dist/cjs/components/Icons/GifIcon.cjs +12 -36
  10. package/dist/cjs/components/Icons/GifIcon.cjs.map +1 -1
  11. package/dist/cjs/components/ProgressBar/ProgressBar.cjs +30 -5
  12. package/dist/cjs/components/ProgressBar/ProgressBar.cjs.map +1 -1
  13. package/dist/cjs/components/ProgressBar/ProgressBarItem.cjs +49 -0
  14. package/dist/cjs/components/ProgressBar/ProgressBarItem.cjs.map +1 -0
  15. package/dist/cjs/components/ProgressBar/ProgressBarSteps.cjs +66 -0
  16. package/dist/cjs/components/ProgressBar/ProgressBarSteps.cjs.map +1 -0
  17. package/dist/cjs/components/TextArea/TextArea.cjs +4 -4
  18. package/dist/cjs/components/TextArea/TextArea.cjs.map +1 -1
  19. package/dist/cjs/components/TextField/TextField.cjs +105 -62
  20. package/dist/cjs/components/TextField/TextField.cjs.map +1 -1
  21. package/dist/cjs/index.cjs +4 -0
  22. package/dist/cjs/index.cjs.map +1 -1
  23. package/dist/components/Accordion/AccordionTrigger.mjs +11 -2
  24. package/dist/components/Accordion/AccordionTrigger.mjs.map +1 -1
  25. package/dist/components/Avatar/Avatar.mjs +2 -2
  26. package/dist/components/Avatar/Avatar.mjs.map +1 -1
  27. package/dist/components/Dialog/Dialog.mjs +1 -1
  28. package/dist/components/Dialog/Dialog.mjs.map +1 -1
  29. package/dist/components/DropdownMenu/DropdownMenu.mjs +25 -2
  30. package/dist/components/DropdownMenu/DropdownMenu.mjs.map +1 -1
  31. package/dist/components/Icons/GifIcon.mjs +12 -36
  32. package/dist/components/Icons/GifIcon.mjs.map +1 -1
  33. package/dist/components/ProgressBar/ProgressBar.mjs +30 -5
  34. package/dist/components/ProgressBar/ProgressBar.mjs.map +1 -1
  35. package/dist/components/ProgressBar/ProgressBarItem.mjs +32 -0
  36. package/dist/components/ProgressBar/ProgressBarItem.mjs.map +1 -0
  37. package/dist/components/ProgressBar/ProgressBarSteps.mjs +49 -0
  38. package/dist/components/ProgressBar/ProgressBarSteps.mjs.map +1 -0
  39. package/dist/components/TextArea/TextArea.mjs +4 -4
  40. package/dist/components/TextArea/TextArea.mjs.map +1 -1
  41. package/dist/components/TextField/TextField.mjs +105 -62
  42. package/dist/components/TextField/TextField.mjs.map +1 -1
  43. package/dist/index.d.ts +103 -9
  44. package/dist/index.mjs +4 -0
  45. package/dist/index.mjs.map +1 -1
  46. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea.mjs","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-neutral-alphas-50 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\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-secondary 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 px-1 pt-1 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":[],"mappings":"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"}
1
+ {"version":3,"file":"TextArea.mjs","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":[],"mappings":"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"}
@@ -9,43 +9,71 @@ const CONTAINER_HEIGHT = {
9
9
  "40": "h-10",
10
10
  "32": "h-8"
11
11
  };
12
- const INPUT_SIZE_CLASSES = {
13
- "48": "py-3 typography-body-default-16px-regular autofill-body-lg",
14
- "40": "py-2 typography-body-default-16px-regular autofill-body-lg",
15
- "32": "py-2 typography-body-small-14px-regular autofill-body-md"
16
- };
17
- const INPUT_PL = {
18
- "48": { default: "pl-4", withIcon: "pl-10" },
19
- "40": { default: "pl-4", withIcon: "pl-10" },
20
- "32": { default: "pl-3", withIcon: "pl-9" }
21
- };
22
- const INPUT_PR = {
23
- "48": { default: "pr-4", withIcon: "pr-10" },
24
- "40": { default: "pr-4", withIcon: "pr-10" },
25
- "32": { default: "pr-3", withIcon: "pr-9" }
26
- };
27
- const ICON_INSET = {
12
+ const CONTAINER_PADDING_X = {
28
13
  "48": "px-4",
29
14
  "40": "px-4",
30
15
  "32": "px-3"
31
16
  };
17
+ const CONTAINER_GAP = {
18
+ "48": "gap-2.5",
19
+ "40": "gap-2.5",
20
+ "32": "gap-2"
21
+ };
22
+ const INPUT_TYPOGRAPHY = {
23
+ "48": "typography-body-default-16px-regular autofill-body-lg",
24
+ "40": "typography-body-default-16px-regular autofill-body-lg",
25
+ "32": "typography-body-small-14px-regular autofill-body-md"
26
+ };
27
+ const SIDE_LABEL_TYPOGRAPHY = {
28
+ "48": "typography-body-default-16px-regular",
29
+ "40": "typography-body-default-16px-regular",
30
+ "32": "typography-body-small-14px-regular"
31
+ };
32
32
  function getContainerClassName(size, error, disabled) {
33
33
  return cn(
34
- "relative overflow-hidden rounded-sm border bg-inputs-inputs-primary has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
34
+ "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",
35
+ CONTAINER_PADDING_X[size],
36
+ CONTAINER_GAP[size],
35
37
  error ? "border-error-content" : "border-border-primary",
36
38
  !disabled && !error && "hover:border-neutral-alphas-400",
37
39
  CONTAINER_HEIGHT[size],
38
40
  disabled && "opacity-50"
39
41
  );
40
42
  }
41
- function getInputClassName(size, hasLeftIcon, hasRightIcon) {
42
- return cn(
43
- "h-full w-full rounded-sm bg-transparent text-content-primary no-underline placeholder:text-content-tertiary focus:outline-none disabled:cursor-not-allowed",
44
- INPUT_SIZE_CLASSES[size],
45
- hasLeftIcon ? INPUT_PL[size].withIcon : INPUT_PL[size].default,
46
- hasRightIcon ? INPUT_PR[size].withIcon : INPUT_PR[size].default
43
+ function LeadingIcon({ children }) {
44
+ if (!children) return null;
45
+ return /* @__PURE__ */ jsx("span", { className: "pointer-events-none flex size-4 shrink-0 items-center justify-center text-content-secondary", children });
46
+ }
47
+ function SideLabel({
48
+ id,
49
+ size,
50
+ align,
51
+ children
52
+ }) {
53
+ if (!children) return null;
54
+ return /* @__PURE__ */ jsx(
55
+ "span",
56
+ {
57
+ id,
58
+ className: cn(
59
+ "shrink-0 select-none whitespace-nowrap text-content-tertiary",
60
+ align === "right" && "text-right",
61
+ SIDE_LABEL_TYPOGRAPHY[size]
62
+ ),
63
+ children
64
+ }
47
65
  );
48
66
  }
67
+ function TrailingAdornment({
68
+ rightIcon,
69
+ validated
70
+ }) {
71
+ if (!rightIcon && !validated) return null;
72
+ return /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2 text-content-secondary", children: [
73
+ rightIcon && /* @__PURE__ */ jsx("span", { "data-tf-interactive": "", className: "flex size-4 shrink-0 items-center justify-center", children: rightIcon }),
74
+ validated && /* @__PURE__ */ jsx("span", { className: "pointer-events-none flex size-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx(CheckOutlineIcon, { className: "text-success-content" }) })
75
+ ] });
76
+ }
49
77
  function TextFieldHelperText({
50
78
  id,
51
79
  error,
@@ -82,6 +110,8 @@ const TextField = React.forwardRef(
82
110
  validated = false,
83
111
  leftIcon,
84
112
  rightIcon,
113
+ leftLabel,
114
+ rightLabel,
85
115
  className,
86
116
  id,
87
117
  disabled,
@@ -91,7 +121,31 @@ const TextField = React.forwardRef(
91
121
  const generatedId = React.useId();
92
122
  const inputId = id || generatedId;
93
123
  const helperTextId = `${inputId}-helper`;
124
+ const leftLabelId = `${inputId}-left-label`;
125
+ const rightLabelId = `${inputId}-right-label`;
94
126
  const bottomText = error && errorMessage ? errorMessage : helperText;
127
+ const describedBy = [
128
+ leftLabel != null ? leftLabelId : null,
129
+ rightLabel != null ? rightLabelId : null,
130
+ bottomText ? helperTextId : null
131
+ ].filter(Boolean).join(" ") || void 0;
132
+ const innerRef = React.useRef(null);
133
+ const setRefs = React.useCallback(
134
+ (node) => {
135
+ innerRef.current = node;
136
+ if (typeof ref === "function") ref(node);
137
+ else if (ref) ref.current = node;
138
+ },
139
+ [ref]
140
+ );
141
+ const handleContainerMouseDown = (event) => {
142
+ if (disabled) return;
143
+ const target = event.target;
144
+ if (target === innerRef.current) return;
145
+ if (target.closest("[data-tf-interactive]")) return;
146
+ event.preventDefault();
147
+ innerRef.current?.focus();
148
+ };
95
149
  warnMissingAccessibleName(label, props["aria-label"], props["aria-labelledby"]);
96
150
  return /* @__PURE__ */ jsxs(
97
151
  "div",
@@ -108,46 +162,35 @@ const TextField = React.forwardRef(
108
162
  children: label
109
163
  }
110
164
  ),
111
- /* @__PURE__ */ jsxs("div", { className: getContainerClassName(size, error, disabled), children: [
112
- /* @__PURE__ */ jsx(
113
- "input",
114
- {
115
- ref,
116
- id: inputId,
117
- disabled,
118
- "aria-describedby": bottomText ? helperTextId : void 0,
119
- "aria-invalid": error || void 0,
120
- className: cn(
121
- getInputClassName(size, !!leftIcon, !!(rightIcon || validated)),
122
- "[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none"
123
- ),
124
- ...props
125
- }
126
- ),
127
- leftIcon && /* @__PURE__ */ jsx(
128
- "div",
129
- {
130
- className: cn(
131
- "pointer-events-none absolute inset-y-0 left-0 flex items-center text-content-secondary",
132
- ICON_INSET[size]
133
- ),
134
- children: /* @__PURE__ */ jsx("div", { className: "flex size-4 shrink-0 items-center justify-center", children: leftIcon })
135
- }
136
- ),
137
- (rightIcon || validated) && /* @__PURE__ */ jsxs(
138
- "div",
139
- {
140
- className: cn(
141
- "absolute inset-y-0 right-0 flex items-center gap-2 text-content-secondary",
142
- ICON_INSET[size]
165
+ /* @__PURE__ */ jsxs(
166
+ "div",
167
+ {
168
+ className: getContainerClassName(size, error, disabled),
169
+ onMouseDown: handleContainerMouseDown,
170
+ children: [
171
+ /* @__PURE__ */ jsx(LeadingIcon, { children: leftIcon }),
172
+ /* @__PURE__ */ jsx(SideLabel, { id: leftLabelId, size, align: "left", children: leftLabel }),
173
+ /* @__PURE__ */ jsx(
174
+ "input",
175
+ {
176
+ ref: setRefs,
177
+ id: inputId,
178
+ disabled,
179
+ "aria-describedby": describedBy,
180
+ "aria-invalid": error || void 0,
181
+ className: cn(
182
+ "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",
183
+ INPUT_TYPOGRAPHY[size],
184
+ "[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none"
185
+ ),
186
+ ...props
187
+ }
143
188
  ),
144
- children: [
145
- rightIcon && /* @__PURE__ */ jsx("div", { className: "flex size-4 shrink-0 items-center justify-center", children: rightIcon }),
146
- validated && /* @__PURE__ */ jsx("div", { className: "pointer-events-none flex size-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx(CheckOutlineIcon, { className: "text-success-content" }) })
147
- ]
148
- }
149
- )
150
- ] }),
189
+ /* @__PURE__ */ jsx(SideLabel, { id: rightLabelId, size, align: "right", children: rightLabel }),
190
+ /* @__PURE__ */ jsx(TrailingAdornment, { rightIcon, validated })
191
+ ]
192
+ }
193
+ ),
151
194
  bottomText && /* @__PURE__ */ jsx(TextFieldHelperText, { id: helperTextId, error, children: bottomText })
152
195
  ]
153
196
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.mjs","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 /** 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 INPUT_SIZE_CLASSES: Record<TextFieldSize, 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 INPUT_PL: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"40\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"32\": { default: \"pl-3\", withIcon: \"pl-9\" },\n};\n\nconst INPUT_PR: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"40\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"32\": { default: \"pr-3\", withIcon: \"pr-9\" },\n};\n\nconst ICON_INSET: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative 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 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 getInputClassName(size: TextFieldSize, hasLeftIcon: boolean, hasRightIcon: boolean) {\n return cn(\n \"h-full w-full rounded-sm bg-transparent text-content-primary no-underline placeholder:text-content-tertiary focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n hasLeftIcon ? INPUT_PL[size].withIcon : INPUT_PL[size].default,\n hasRightIcon ? INPUT_PR[size].withIcon : INPUT_PR[size].default,\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, and icon slots.\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 * <TextField\n * label=\"Email\"\n * placeholder=\"you@example.com\"\n * error={!!emailError}\n * errorMessage={emailError}\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 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 bottomText = error && errorMessage ? errorMessage : helperText;\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 <input\n ref={ref}\n id={inputId}\n disabled={disabled}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n getInputClassName(size, !!leftIcon, !!(rightIcon || validated)),\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {leftIcon && (\n <div\n className={cn(\n \"pointer-events-none absolute inset-y-0 left-0 flex items-center text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{leftIcon}</div>\n </div>\n )}\n\n {(rightIcon || validated) && (\n <div\n className={cn(\n \"absolute inset-y-0 right-0 flex items-center gap-2 text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n {rightIcon && (\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{rightIcon}</div>\n )}\n {validated && (\n <div className=\"pointer-events-none flex size-4 shrink-0 items-center justify-center\">\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\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":[],"mappings":"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"}
1
+ {"version":3,"file":"TextField.mjs","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":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -64,14 +64,26 @@ export declare type AccordionProps = React_2.ComponentPropsWithoutRef<typeof Acc
64
64
 
65
65
  /** An interactive button that toggles the visibility of its associated {@link AccordionContent} panel. */
66
66
  export declare const AccordionTrigger: React_2.ForwardRefExoticComponent<Omit<AccordionPrimitive.AccordionTriggerProps & React_2.RefAttributes<HTMLButtonElement>, "ref"> & {
67
- /** Custom icon element. Defaults to `ChevronDownIcon`. Pass `null` to suppress the icon entirely. */
67
+ /** Trailing indicator icon. Defaults to `ChevronDownIcon` (rotates when open). Pass `null` to suppress it. */
68
68
  icon?: React_2.ReactNode | null;
69
+ /** Secondary line rendered under the title, for extra context. */
70
+ description?: React_2.ReactNode;
71
+ /** Leading icon shown before the title (sized to 16px). */
72
+ leadingIcon?: React_2.ReactNode;
73
+ /** Leading avatar shown before the title, for headers representing a person or account. Pass an `Avatar` sized to `24`. */
74
+ avatar?: React_2.ReactNode;
69
75
  } & React_2.RefAttributes<HTMLButtonElement>>;
70
76
 
71
77
  /** Props for the {@link AccordionTrigger} button component. */
72
78
  export declare type AccordionTriggerProps = React_2.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> & {
73
- /** Custom icon element. Defaults to `ChevronDownIcon`. Pass `null` to suppress the icon entirely. */
79
+ /** Trailing indicator icon. Defaults to `ChevronDownIcon` (rotates when open). Pass `null` to suppress it. */
74
80
  icon?: React_2.ReactNode | null;
81
+ /** Secondary line rendered under the title, for extra context. */
82
+ description?: React_2.ReactNode;
83
+ /** Leading icon shown before the title (sized to 16px). */
84
+ leadingIcon?: React_2.ReactNode;
85
+ /** Leading avatar shown before the title, for headers representing a person or account. Pass an `Avatar` sized to `24`. */
86
+ avatar?: React_2.ReactNode;
75
87
  };
76
88
 
77
89
  /**
@@ -2257,6 +2269,11 @@ export declare type DropdownMenuHeaderType = "default" | "search";
2257
2269
  * <DropdownMenuItem destructive>Delete</DropdownMenuItem>
2258
2270
  * <DropdownMenuItem leadingIcon={<EditIcon />}>Edit</DropdownMenuItem>
2259
2271
  *
2272
+ * // Feature-rich row with an avatar and a trailing count
2273
+ * <DropdownMenuItem avatar={<Avatar size={24} src={src} />} count="12">
2274
+ * Jane Doe
2275
+ * </DropdownMenuItem>
2276
+ *
2260
2277
  * // As a link
2261
2278
  * <DropdownMenuItem asChild>
2262
2279
  * <a href="/settings">Settings</a>
@@ -2270,10 +2287,18 @@ export declare interface DropdownMenuItemProps extends React_2.ComponentPropsWit
2270
2287
  size?: DropdownMenuItemSize;
2271
2288
  /** Applies the destructive (error) treatment. Use for irreversible actions. @default false */
2272
2289
  destructive?: boolean;
2273
- /** Icon (or other node) rendered before the label. */
2290
+ /** Icon (or other node) rendered before the label. Ignored when {@link DropdownMenuItemProps.avatar} is set. */
2274
2291
  leadingIcon?: React_2.ReactNode;
2292
+ /**
2293
+ * Leading avatar rendered in place of {@link DropdownMenuItemProps.leadingIcon},
2294
+ * for rows that represent a person or account. Pass an `Avatar` sized to `24`.
2295
+ * Takes precedence over `leadingIcon`.
2296
+ */
2297
+ avatar?: React_2.ReactNode;
2275
2298
  /** Icon (or other node) rendered after the label. */
2276
2299
  trailingIcon?: React_2.ReactNode;
2300
+ /** Trailing count or number (e.g. an unread total) rendered before {@link DropdownMenuItemProps.trailingIcon}. */
2301
+ count?: React_2.ReactNode;
2277
2302
  /**
2278
2303
  * Optional secondary text rendered on a second line below the label. When
2279
2304
  * provided, the row switches to a two-line layout and the leading/trailing
@@ -3678,12 +3703,38 @@ export declare type ProfileStatusSize = "sm" | "md";
3678
3703
  */
3679
3704
  export declare const ProgressBar: React_2.ForwardRefExoticComponent<ProgressBarProps & React_2.RefAttributes<HTMLDivElement>>;
3680
3705
 
3706
+ /**
3707
+ * A single rounded segment used to compose a stepped progress indicator. Render
3708
+ * a row of these (see {@link ProgressBarSteps}) to represent discrete steps.
3709
+ *
3710
+ * @example
3711
+ * ```tsx
3712
+ * <ProgressBarItem active variant="brand" />
3713
+ * ```
3714
+ */
3715
+ export declare const ProgressBarItem: React_2.ForwardRefExoticComponent<ProgressBarItemProps & React_2.RefAttributes<HTMLDivElement>>;
3716
+
3717
+ export declare interface ProgressBarItemProps extends React_2.HTMLAttributes<HTMLDivElement> {
3718
+ /** Whether this item is filled (completed). @default false */
3719
+ active?: boolean;
3720
+ /** Colour mode. @default "brand" */
3721
+ variant?: ProgressBarItemVariant;
3722
+ /** Pill thickness — `"default"` (8px) or `"small"` (4px). @default "default" */
3723
+ size?: ProgressBarItemSize;
3724
+ }
3725
+
3726
+ /** Pill thickness — `"default"` (8px) or `"small"` (4px). */
3727
+ export declare type ProgressBarItemSize = "default" | "small";
3728
+
3729
+ /** Colour mode for a progress bar item — brand green or theme-aware monochrome. */
3730
+ export declare type ProgressBarItemVariant = "brand" | "mono";
3731
+
3681
3732
  export declare interface ProgressBarProps extends Omit<React_2.HTMLAttributes<HTMLDivElement>, "title"> {
3682
3733
  /** Current progress value, clamped to 0–100. */
3683
3734
  value: number;
3684
3735
  /** Track height — `"default"` (12px) or `"small"` (6px). @default "default" */
3685
3736
  size?: ProgressBarSize;
3686
- /** Colour mode `"default"` is colour-coded by value, `"generic"` always uses brand green, `"neutral"` uses a theme-aware inverse colour. @default "default" */
3737
+ /** Colour mode. Prefer `"brand"` or `"mono"` (V2); `"default"`/`"generic"`/`"neutral"` are legacy. @default "default" */
3687
3738
  variant?: ProgressBarVariant;
3688
3739
  /** Title content shown at the top-left of the bar. */
3689
3740
  title?: React_2.ReactNode;
@@ -3706,8 +3757,41 @@ export declare interface ProgressBarProps extends Omit<React_2.HTMLAttributes<HT
3706
3757
  /** Track height — `"default"` (12px) or `"small"` (6px). */
3707
3758
  export declare type ProgressBarSize = "default" | "small";
3708
3759
 
3709
- /** Colour mode — `"default"` uses red/yellow/green by value, `"generic"` always uses brand green, `"neutral"` uses a theme-aware inverse colour. */
3710
- export declare type ProgressBarVariant = "default" | "generic" | "neutral";
3760
+ /**
3761
+ * A segmented (stepped) progress indicator built from {@link ProgressBarItem}
3762
+ * pills. Fills the first `value` of `steps` segments.
3763
+ *
3764
+ * @example
3765
+ * ```tsx
3766
+ * <ProgressBarSteps steps={4} value={2} variant="brand" />
3767
+ * ```
3768
+ */
3769
+ export declare const ProgressBarSteps: React_2.ForwardRefExoticComponent<ProgressBarStepsProps & React_2.RefAttributes<HTMLDivElement>>;
3770
+
3771
+ export declare interface ProgressBarStepsProps extends React_2.HTMLAttributes<HTMLDivElement> {
3772
+ /** Total number of steps (segments). Values below 1 are treated as 1. */
3773
+ steps: number;
3774
+ /** Number of completed steps, clamped to `0`–`steps`. */
3775
+ value: number;
3776
+ /** Colour mode. @default "brand" */
3777
+ variant?: ProgressBarItemVariant;
3778
+ /** Pill thickness — `"default"` (8px) or `"small"` (4px). @default "default" */
3779
+ size?: ProgressBarItemSize;
3780
+ /** Accessible label for the `progressbar` role. @default "Progress" */
3781
+ ariaLabel?: string;
3782
+ /** Human-readable text alternative for the current value (e.g. "Step 2 of 4"). */
3783
+ ariaValueText?: string;
3784
+ }
3785
+
3786
+ /**
3787
+ * Colour mode.
3788
+ * - `"brand"` — V2 brand (green) fill on a tinted track.
3789
+ * - `"mono"` — V2 monochrome fill on a tinted track (theme-aware).
3790
+ * - `"default"` — legacy value-coded red/yellow/green.
3791
+ * - `"generic"` — legacy solid brand green on a neutral track.
3792
+ * - `"neutral"` — legacy theme-aware inverse colour on a neutral track.
3793
+ */
3794
+ export declare type ProgressBarVariant = "brand" | "mono" | "default" | "generic" | "neutral";
3711
3795
 
3712
3796
  /**
3713
3797
  * Queue icon. Renders at sizes 16, 24, or 32 px.
@@ -5011,10 +5095,11 @@ export declare interface TextAreaProps extends Omit<React_2.TextareaHTMLAttribut
5011
5095
  export declare type TextAreaSize = "48" | "40" | "32";
5012
5096
 
5013
5097
  /**
5014
- * A text input field with optional label, helper/error text, and icon slots.
5098
+ * A text input field with optional label, helper/error text, icon slots, and side labels.
5015
5099
  *
5016
- * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for
5017
- * accessibility a console warning is emitted in development if none are set.
5100
+ * Use `leftLabel` / `rightLabel` for fixed unit or prefix affordances (currency symbol,
5101
+ * country code, domain suffix). Provide at least one of `label`, `aria-label`, or
5102
+ * `aria-labelledby` for accessibility — a console warning is emitted in development if none are set.
5018
5103
  *
5019
5104
  * @example
5020
5105
  * ```tsx
@@ -5025,6 +5110,11 @@ export declare type TextAreaSize = "48" | "40" | "32";
5025
5110
  * errorMessage={emailError}
5026
5111
  * />
5027
5112
  * ```
5113
+ *
5114
+ * @example
5115
+ * ```tsx
5116
+ * <TextField label="Price" leftLabel="$" rightLabel="USD" placeholder="0.00" />
5117
+ * ```
5028
5118
  */
5029
5119
  export declare const TextField: React_2.ForwardRefExoticComponent<TextFieldProps & React_2.RefAttributes<HTMLInputElement>>;
5030
5120
 
@@ -5045,6 +5135,10 @@ export declare interface TextFieldProps extends Omit<React_2.InputHTMLAttributes
5045
5135
  leftIcon?: React_2.ReactNode;
5046
5136
  /** Icon element displayed at the right side of the input. */
5047
5137
  rightIcon?: React_2.ReactNode;
5138
+ /** Fixed, non-editable label pinned inside the left edge of the field — for a prefix such as a currency symbol or country code. */
5139
+ leftLabel?: React_2.ReactNode;
5140
+ /** Fixed, non-editable label pinned inside the right edge of the field — for a unit or suffix such as a currency code or domain. */
5141
+ rightLabel?: React_2.ReactNode;
5048
5142
  /** Whether the text field stretches to fill its container width. @default false */
5049
5143
  fullWidth?: boolean;
5050
5144
  }
package/dist/index.mjs CHANGED
@@ -209,6 +209,8 @@ import { Pill } from "./components/Pill/Pill.mjs";
209
209
  import { ProfileOnlineStatus } from "./components/ProfileOnlineStatus/ProfileOnlineStatus.mjs";
210
210
  import { ProfileStatus } from "./components/ProfileStatus/ProfileStatus.mjs";
211
211
  import { ProgressBar } from "./components/ProgressBar/ProgressBar.mjs";
212
+ import { ProgressBarItem } from "./components/ProgressBar/ProgressBarItem.mjs";
213
+ import { ProgressBarSteps } from "./components/ProgressBar/ProgressBarSteps.mjs";
212
214
  import { Radio } from "./components/Radio/Radio.mjs";
213
215
  import { RadioGroup } from "./components/RadioGroup/RadioGroup.mjs";
214
216
  import { RatingSummary } from "./components/RatingSummary/RatingSummary.mjs";
@@ -438,6 +440,8 @@ export {
438
440
  ProfileOnlineStatus,
439
441
  ProfileStatus,
440
442
  ProgressBar,
443
+ ProgressBarItem,
444
+ ProgressBarSteps,
441
445
  QueueIcon,
442
446
  Radio,
443
447
  RadioGroup,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanvue/ui",
3
- "version": "3.12.0",
3
+ "version": "3.13.0",
4
4
  "description": "React component library built with Tailwind CSS for Fanvue ecosystem",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",