diff --git a/components/application/app-navigation/base-components/nav-account-card.tsx b/components/application/app-navigation/base-components/nav-account-card.tsx index f053695..b804915 100644 --- a/components/application/app-navigation/base-components/nav-account-card.tsx +++ b/components/application/app-navigation/base-components/nav-account-card.tsx @@ -46,7 +46,14 @@ export const NavAccountMenu = ({ className, selectedAccountId = "olivia", ...dialogProps -}: AriaDialogProps & { className?: string; accounts?: NavAccountType[]; selectedAccountId?: string }) => { +}: AriaDialogProps & { + /** Additional classes for the popover dialog that wraps the menu. */ + className?: string; + /** The accounts offered in the "Switch account" section. Accepted for parity with `NavAccountCard`, which passes its own list down; the menu itself always renders the built-in demo accounts. */ + accounts?: NavAccountType[]; + /** The `id` of the account to present as the current one — its row is highlighted and its radio button is selected. */ + selectedAccountId?: string; +}) => { const focusManager = useFocusManager(); const dialogRef = useRef(null); @@ -128,8 +135,11 @@ const NavAccountCardMenuItem = ({ shortcut, ...buttonProps }: { + /** Icon rendered at the leading edge of the item, before the label. */ icon?: FC<{ className?: string }>; + /** Text of the menu item. */ label: string; + /** Keyboard shortcut shown as a `kbd` badge at the trailing edge. Display only — the item does not bind the key combination. */ shortcut?: string; } & HTMLAttributes) => { return ( @@ -159,9 +169,16 @@ export const NavAccountCard = ({ items = placeholderAccounts, avatarRounded, }: { + /** + * Where the account menu opens relative to the card. + * @default "right bottom" from the `lg` breakpoint up, "top right" below it + */ popoverPlacement?: AriaPlacement; + /** The `id` of the account from `items` to show in the card. Nothing is rendered if no item matches, and a warning is logged. */ selectedAccountId?: string; + /** The accounts the card can display. The one matching `selectedAccountId` fills the card, and the list is handed to the account menu. */ items?: NavAccountType[]; + /** Whether the account avatar is a circle. Pass `false` for a rounded square. */ avatarRounded?: boolean; }) => { const triggerRef = useRef(null); diff --git a/components/application/carousel/carousel-base.tsx b/components/application/carousel/carousel-base.tsx index ffddd0e..efb1002 100644 --- a/components/application/carousel/carousel-base.tsx +++ b/components/application/carousel/carousel-base.tsx @@ -292,7 +292,9 @@ const CarouselIndicator = ({ index, isSelected = false, children, asChild, class }; interface CarouselIndicatorGroupProps extends Omit, "children"> { + /** The indicators to render. Can be a render prop, which is called once per scroll snap with its index. */ children: ReactNode | ((props: { index: number }) => ReactNode); + /** The class name applied to the indicator group. */ className?: string; } diff --git a/components/application/charts/charts-base.tsx b/components/application/charts/charts-base.tsx index 3356668..2b288b4 100644 --- a/components/application/charts/charts-base.tsx +++ b/components/application/charts/charts-base.tsx @@ -45,7 +45,18 @@ export const selectEvenlySpacedItems = (dataArray: * @param className - The class name of the legend. * @returns The legend content. */ -export const ChartLegendContent = ({ reversed, payload, align, layout, className }: LegendContentProps & { reversed?: boolean; className?: string }) => { +export const ChartLegendContent = ({ + reversed, + payload, + align, + layout, + className, +}: LegendContentProps & { + /** Whether the legend entries are listed in the reverse of the order Recharts supplies them, so a stacked chart's legend reads in the same order as its stack. */ + reversed?: boolean; + /** Additional classes for the legend list, merged after the layout and alignment classes derived from the Recharts `Legend` props. */ + className?: string; +}) => { payload = reversed && payload ? [...payload].reverse() : payload; return ( @@ -74,9 +85,13 @@ export const ChartLegendContent = ({ reversed, payload, align, layout, className }; interface ChartTooltipContentProps extends TooltipProps { + /** Whether the tooltip belongs to a Recharts `RadialBarChart`. When a single point is hovered, the supporting line is read from `payload[0].payload.name` instead of the axis label. */ isRadialChart?: boolean; + /** Whether the tooltip belongs to a Recharts `PieChart`. When a single slice is hovered, the supporting line is the slice's `name` instead of the axis label. */ isPieChart?: boolean; + /** The category label Recharts passes for the hovered position. Used as the tooltip heading when several series are active, and as the supporting line otherwise. */ label?: string; + /** The active data points Recharts passes to the `Tooltip` content. Each entry supplies the series name and value rendered as a row in the tooltip. */ // We have to use `any` here because the `payload` prop is not typed correctly in the `recharts` library. // eslint-disable-next-line @typescript-eslint/no-explicit-any payload?: any; @@ -124,6 +139,7 @@ export const ChartTooltipContent = ({ active, payload, label, isRadialChart, isP }; interface ChartActiveDotProps extends DotProps { + /** The data point Recharts passes to the `activeDot` renderer. Accepted so the dot can be dropped straight into a `Line` or `Area`; the marker itself is drawn from `cx` and `cy` only. */ // We have to use `any` here because the `payload` prop is not typed correctly in the `recharts` library. // eslint-disable-next-line @typescript-eslint/no-explicit-any payload?: any; diff --git a/components/application/date-picker/date-picker.tsx b/components/application/date-picker/date-picker.tsx index 8af7e2a..d4943bc 100644 --- a/components/application/date-picker/date-picker.tsx +++ b/components/application/date-picker/date-picker.tsx @@ -17,6 +17,7 @@ interface DatePickerProps extends AriaDatePickerProps { onApply?: () => void; /** The function to call when the cancel button is clicked. */ onCancel?: () => void; + /** Controls the size of the trigger button that opens the calendar. */ size?: ButtonProps["size"]; } diff --git a/components/application/date-picker/date-range-picker.tsx b/components/application/date-picker/date-range-picker.tsx index df59e78..b74bbec 100644 --- a/components/application/date-picker/date-range-picker.tsx +++ b/components/application/date-picker/date-range-picker.tsx @@ -17,6 +17,7 @@ const now = today(getLocalTimeZone()); const highlightedDates = [today(getLocalTimeZone())]; interface DateRangePickerProps extends AriaDateRangePickerProps { + /** Controls the size of the trigger button that opens the calendar. */ size?: ButtonProps["size"]; /** The function to call when the apply button is clicked. */ onApply?: () => void; diff --git a/components/application/date-picker/range-calendar.tsx b/components/application/date-picker/range-calendar.tsx index 94acdca..910655f 100644 --- a/components/application/date-picker/range-calendar.tsx +++ b/components/application/date-picker/range-calendar.tsx @@ -50,6 +50,7 @@ const RangeCalendarTitle = ({ part }: { part: "start" | "end" }) => { }; interface RangePresetButtonProps extends HTMLAttributes { + /** The date range applied when the preset is pressed. The button is highlighted while this range is the selected one. */ value: { start: DateValue; end: DateValue }; } diff --git a/components/application/empty-state/empty-state.tsx b/components/application/empty-state/empty-state.tsx index b4e1813..cb7b537 100644 --- a/components/application/empty-state/empty-state.tsx +++ b/components/application/empty-state/empty-state.tsx @@ -11,6 +11,7 @@ import { Illustration as Illustrations } from "@/components/shared-assets/illust import { cx } from "@/utils/cx"; interface RootContextProps { + /** Scales the whole empty state — the header icon or illustration, the heading and description type, the avatar decorations, and the spacing between the sections. Read by every sub-component through context. */ size?: "sm" | "md" | "lg"; } @@ -47,7 +48,9 @@ const Illustration = ({ type = "cloud", color = "gray", size = "lg", ...props }: }; interface FileTypeIconProps extends ComponentPropsWithRef<"div"> { + /** Which file type the icon depicts, forwarded to `FileIcon` from `@untitledui/file-icons`. */ type?: ComponentProps["type"]; + /** Drawing style of the file icon, forwarded to `FileIcon` as its `variant`. */ theme?: ComponentProps["variant"]; } @@ -60,7 +63,9 @@ const FileTypeIcon = ({ type = "folder", theme = "solid", ...props }: FileTypeIc }; interface HeaderProps extends ComponentPropsWithRef<"div"> { + /** Decorative background pattern drawn behind the header's icon or illustration and centred on it. Pass `"none"` to render the header without a pattern. */ pattern?: "none" | BackgroundPatternProps["pattern"]; + /** How far the decorative background pattern extends behind the header content. */ patternSize?: "sm" | "md" | "lg"; } @@ -124,6 +129,7 @@ const Description = (props: ComponentPropsWithRef<"p">) => { }; interface AvatarRadiusProps extends ComponentPropsWithRef<"div"> { + /** Avatars scattered across the concentric rings behind the empty state. Each one takes the next predefined ring and angle, so at most nine are drawn and any extras are ignored. */ avatars?: Array<{ src: string; alt?: string }>; } @@ -188,7 +194,9 @@ const AvatarRadius = ({ avatars = [], ...props }: AvatarRadiusProps) => { }; interface AvatarRowProps extends ComponentPropsWithRef<"div"> { + /** Avatars fanned out either side of the children, growing towards the centre. The first few fill the left group and the next few the right — three or four per side depending on the root size — and any extras are ignored. */ avatars?: Array<{ src: string; alt?: string }>; + /** Content placed between the two avatar groups, typically the empty state's featured icon or illustration. */ children?: ReactNode; } @@ -244,6 +252,7 @@ const AvatarRow = ({ avatars = [], children, ...props }: AvatarRowProps) => { }; interface AvatarGridProps extends ComponentPropsWithRef<"div"> { + /** Avatars split into two marquee rows that scroll in opposite directions behind the empty state. The first half of the list fills the top row and the rest the bottom row, each repeated to loop seamlessly. */ avatars?: Array<{ src: string; alt?: string }>; } diff --git a/components/application/file-upload/draggable.tsx b/components/application/file-upload/draggable.tsx index 2cd0698..0e0bdac 100644 --- a/components/application/file-upload/draggable.tsx +++ b/components/application/file-upload/draggable.tsx @@ -5,10 +5,15 @@ import { FileIcon } from "@untitledui/file-icons"; import { motion, useMotionValue, useSpring } from "motion/react"; interface DraggableProps { + /** The file name shown under the icon and used for the simulated `File` that is dropped. */ name: string; + /** The MIME type of the simulated file. Also used as the file icon type when `fileIconType` is not set. */ type: string; + /** Overrides which file icon is shown, when it should differ from the MIME `type`. */ fileIconType?: ComponentProps["type"]; + /** The visual variant of the file icon. */ theme?: ComponentProps["variant"]; + /** The size of the simulated file in bytes. That many random bytes are generated on drop. */ size: number; } diff --git a/components/application/pagination/pagination-base.tsx b/components/application/pagination/pagination-base.tsx index abcf196..1c63472 100644 --- a/components/application/pagination/pagination-base.tsx +++ b/components/application/pagination/pagination-base.tsx @@ -39,6 +39,7 @@ export interface PaginationRootProps { page: number; /** Total number of pages */ total: number; + /** The pagination parts to render, such as `Pagination.PrevTrigger`, `Pagination.Context` and `Pagination.NextTrigger`. */ children: ReactNode; /** The style of the pagination root. */ style?: CSSProperties; @@ -334,9 +335,13 @@ const PaginationItem = ({ value, isCurrent, children, style, className, ariaLabe ); }; interface PaginationEllipsisProps { + /** The identifier of the ellipsis within the generated list of pagination items. */ key: number; + /** The content rendered in place of the skipped pages, such as an ellipsis character. Hidden from assistive technology. */ children?: ReactNode; + /** The style of the ellipsis. */ style?: CSSProperties; + /** The class name of the ellipsis. Can be a function returning the class name. */ className?: string | (() => string); } @@ -351,6 +356,7 @@ const PaginationEllipsis: FC = ({ children, style, clas }; interface PaginationContextComponentProps { + /** A render prop called with the pagination state (pages, current page, total and the page change handler). */ children: (pagination: PaginationContextType) => ReactNode; } diff --git a/components/application/pagination/pagination.tsx b/components/application/pagination/pagination.tsx index 0d95760..ceef3ad 100644 --- a/components/application/pagination/pagination.tsx +++ b/components/application/pagination/pagination.tsx @@ -15,7 +15,18 @@ interface PaginationProps extends Partial> rounded?: boolean; } -const PaginationItem = ({ value, rounded, isCurrent }: { value: number; rounded?: boolean; isCurrent: boolean }) => { +const PaginationItem = ({ + value, + rounded, + isCurrent, +}: { + /** The page number this item navigates to, also used as its label. */ + value: number; + /** Whether the button is a circle rather than a rounded square. */ + rounded?: boolean; + /** Whether this item is the page currently being viewed, which highlights it and marks it as the current page. */ + isCurrent: boolean; +}) => { return ( ( Dialog.displayName = "Dialog"; interface SlideoutMenuProps extends Omit, RefAttributes { + /** The content of the menu. Can be a render prop that receives the modal state along with a `close` function. */ children: ReactNode | ((children: AriaModalRenderProps & { close: () => void }) => ReactNode); + /** The class name applied to the inner dialog, while `className` styles the sliding panel. */ dialogClassName?: string; } @@ -89,6 +91,7 @@ const Content = ({ role = "main", ...props }: ComponentPropsWithRef<"div">) => { Content.displayName = "SlideoutContent"; interface SlideoutHeaderProps extends ComponentPropsWithRef<"header"> { + /** Handler called when the close button in the top right corner is pressed. */ onClose?: () => void; } diff --git a/components/application/table/table.tsx b/components/application/table/table.tsx index 75dac5c..ede0221 100644 --- a/components/application/table/table.tsx +++ b/components/application/table/table.tsx @@ -49,7 +49,15 @@ export const TableRowActionsDropdown = () => ( const TableContext = createContext<{ size: "sm" | "md" }>({ size: "md" }); -const TableCardRoot = ({ children, className, size = "md", ...props }: HTMLAttributes & { size?: "sm" | "md" }) => { +const TableCardRoot = ({ + children, + className, + size = "md", + ...props +}: HTMLAttributes & { + /** Controls the density of the card and every descendant header, row and cell. */ + size?: "sm" | "md"; +}) => { return (
@@ -104,6 +112,7 @@ const TableCardHeader = ({ title, badge, description, contentTrailing, className }; interface TableRootProps extends AriaTableProps, Omit, "className" | "slot" | "style"> { + /** Controls the density of the header, rows and cells. The size of a wrapping `TableCard.Root` takes precedence. */ size?: "sm" | "md"; } @@ -122,7 +131,9 @@ TableRoot.displayName = "Table"; interface TableHeaderProps extends AriaTableHeaderProps, Omit, "children" | "className" | "slot" | "style"> { + /** Whether to draw a hairline border along the bottom edge of the header row. */ bordered?: boolean; + /** Controls the height of the header row. Falls back to the size inherited from the table. */ size?: "sm" | "md"; } @@ -165,7 +176,9 @@ const TableHeader = ({ columns, children, bordered = true, cla TableHeader.displayName = "TableHeader"; interface TableHeadProps extends AriaColumnProps, Omit, "children" | "className" | "style" | "id"> { + /** The column heading text, rendered before any children. */ label?: string; + /** Help text shown in a tooltip when hovering the help icon next to the heading. */ tooltip?: string; } @@ -214,7 +227,9 @@ TableHead.displayName = "TableHead"; interface TableRowProps extends AriaRowProps, Omit, "children" | "className" | "onClick" | "slot" | "style" | "id"> { + /** Whether the row keeps a secondary background while it is selected. */ highlightSelectedRow?: boolean; + /** Controls the height of the row. Falls back to the size inherited from the table. */ size?: "sm" | "md"; } @@ -255,7 +270,9 @@ const TableRow = ({ columns, children, className, highlightSel TableRow.displayName = "TableRow"; interface TableCellProps extends AriaCellProps, Omit, "children" | "className" | "style" | "id"> { + /** A ref to the underlying `td` element. */ ref?: Ref; + /** Controls the horizontal and vertical padding of the cell. Falls back to the size inherited from the table. */ size?: "sm" | "md"; } diff --git a/components/base/avatar/avatar-label-group.tsx b/components/base/avatar/avatar-label-group.tsx index ce12e2c..847290e 100644 --- a/components/base/avatar/avatar-label-group.tsx +++ b/components/base/avatar/avatar-label-group.tsx @@ -11,10 +11,15 @@ const styles = { }; interface AvatarLabelGroupProps extends AvatarProps { + /** The dimensions of the avatar, which also set the text size of the title and subtitle. */ size: "sm" | "md" | "lg"; + /** Whether the avatar is a circle rather than a rounded square. */ rounded?: boolean; + /** The primary line of text shown next to the avatar, such as a person's name. */ title: string | ReactNode; + /** The secondary line of text shown below the title. Truncates when it overflows. */ subtitle: string | ReactNode; + /** The class name applied to the avatar, while `className` styles the whole group. */ avatarClassName?: string; } diff --git a/components/base/avatar/avatar-profile-photo.tsx b/components/base/avatar/avatar-profile-photo.tsx index 64e4e96..04c78f5 100644 --- a/components/base/avatar/avatar-profile-photo.tsx +++ b/components/base/avatar/avatar-profile-photo.tsx @@ -40,6 +40,7 @@ const tickSizeMap = { } as const; interface AvatarProfilePhotoProps extends AvatarProps { + /** The dimensions of the photo, which also scale the surrounding ring, initials and status indicator. */ size: "sm" | "md" | "lg"; } diff --git a/components/base/avatar/avatar.tsx b/components/base/avatar/avatar.tsx index 09dc880..f4d6033 100644 --- a/components/base/avatar/avatar.tsx +++ b/components/base/avatar/avatar.tsx @@ -7,13 +7,25 @@ import { AvatarOnlineIndicator, VerifiedTick } from "./base-components"; import { AvatarCount } from "./base-components/avatar-count"; export interface AvatarProps { + /** + * The dimensions of the avatar, which also scale the initials, placeholder icon and status indicator. + */ size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl"; + /** + * The class name for the outer wrapper of the avatar. + */ className?: string; /** * The class name for the main child of the avatar. */ contentClassName?: string; + /** + * The URL of the avatar image. Falls back to the initials, placeholder icon or placeholder if it is missing or fails to load. + */ src?: string | null; + /** + * The alternative text for the avatar image. + */ alt?: string; /** * Display an inner contrast border around the avatar image. diff --git a/components/base/badges/badge-groups.tsx b/components/base/badges/badge-groups.tsx index b0b437b..f350702 100644 --- a/components/base/badges/badge-groups.tsx +++ b/components/base/badges/badge-groups.tsx @@ -104,16 +104,23 @@ const colorClasses: Record | ReactNode; + /** Additional CSS classes merged onto the group's root element. */ className?: string; } diff --git a/components/base/badges/badges.tsx b/components/base/badges/badges.tsx index 741bc6d..3c70e49 100644 --- a/components/base/badges/badges.tsx +++ b/components/base/badges/badges.tsx @@ -114,10 +114,15 @@ const withBadgeTypes = { export type BadgeColor = BadgeTypeToColorMap[T]; interface BadgeProps { + /** The shape and treatment of the badge: `pill-color` is fully rounded, `color` is a rounded rectangle, and `modern` is a white badge with a border and a subtle shadow. */ type?: T; + /** Controls the padding and font size of the badge. */ size?: Sizes; + /** The color palette used for the badge background, text, and ring. The `modern` type only supports `gray`. */ color?: BadgeColor; + /** The content of the badge, usually a short text label. */ children: ReactNode; + /** Additional CSS classes merged onto the badge element. */ className?: string; } @@ -146,10 +151,15 @@ export const Badge = (props: BadgeProps) => { }; interface BadgeWithDotProps { + /** The shape and treatment of the badge: `pill-color` is fully rounded, `color` is a rounded rectangle, and `modern` is a white badge with a border and a subtle shadow. */ type?: T; + /** Controls the gap, padding, and font size of the badge. */ size?: Sizes; + /** The color palette used for the badge background, text, ring, and the leading dot. With the `modern` type only the dot is colored. */ color?: BadgeTypeToColorMap[T]; + /** Additional CSS classes merged onto the badge element. */ className?: string; + /** The content shown after the dot, usually a short status label. */ children: ReactNode; } @@ -185,12 +195,19 @@ export const BadgeWithDot = (props: BadgeWithDotProps) }; interface BadgeWithIconProps { + /** The shape and treatment of the badge: `pill-color` is fully rounded, `color` is a rounded rectangle, and `modern` is a white badge with a border and a subtle shadow. */ type?: T; + /** Controls the gap, padding, and font size of the badge. */ size?: Sizes; + /** The color palette used for the badge background, text, ring, and icons. With the `modern` type only the icons are colored. */ color?: BadgeTypeToColorMap[T]; + /** An icon component rendered before the label. Providing it also switches the badge to the tighter leading padding. */ iconLeading?: IconComponentType; + /** An icon component rendered after the label. */ iconTrailing?: IconComponentType; + /** The content shown between the icons, usually a short text label. */ children: ReactNode; + /** Additional CSS classes merged onto the badge element. */ className?: string; } @@ -246,10 +263,15 @@ export const BadgeWithIcon = (props: BadgeWithIconProps }; interface BadgeWithFlagProps { + /** The shape and treatment of the badge: `pill-color` is fully rounded, `color` is a rounded rectangle, and `modern` is a white badge with a border and a subtle shadow. */ type?: T; + /** Controls the gap, padding, and font size of the badge. */ size?: Sizes; + /** The ISO country code of the flag shown before the label. It is loaded as a circular SVG from the Untitled UI flag CDN and also used for the image alt text. */ flag?: FlagTypes; + /** The color palette used for the badge background, text, and ring. The `modern` type only supports `gray`. */ color?: BadgeTypeToColorMap[T]; + /** The content shown after the flag, usually a country or region name. */ children: ReactNode; } @@ -284,10 +306,15 @@ export const BadgeWithFlag = (props: BadgeWithFlagProps }; interface BadgeWithImageProps { + /** The shape and treatment of the badge: `pill-color` is fully rounded, `color` is a rounded rectangle, and `modern` is a white badge with a border and a subtle shadow. */ type?: T; + /** Controls the gap, padding, and font size of the badge. */ size?: Sizes; + /** The source URL of the circular image rendered before the label, such as an avatar or a logo. */ imgSrc: string; + /** The color palette used for the badge background, text, and ring. The `modern` type only supports `gray`. */ color?: BadgeTypeToColorMap[T]; + /** The content shown after the image, usually a name or a short label. */ children: ReactNode; } @@ -322,10 +349,15 @@ export const BadgeWithImage = (props: BadgeWithImageProps< }; interface BadgeWithButtonProps { + /** The shape and treatment of the badge: `pill-color` is fully rounded, `color` is a rounded rectangle, and `modern` is a white badge with a border and a subtle shadow. */ type?: T; + /** Controls the gap, padding, and font size of the badge. */ size?: Sizes; + /** The icon component rendered inside the trailing button, typically a dismiss icon. */ icon?: IconComponentType; + /** The color palette used for the badge background, text, ring, and the trailing button's hover state. The `modern` type only supports `gray`. */ color?: BadgeTypeToColorMap[T]; + /** The content shown before the button, usually a short text label. */ children: ReactNode; /** * The label for the button. @@ -379,10 +411,15 @@ export const BadgeWithButton = (props: BadgeWithButtonProp }; interface BadgeIconProps { + /** The shape and treatment of the badge: `pill-color` is fully rounded, `color` is a rounded rectangle, and `modern` is a white badge with a border and a subtle shadow. */ type?: T; + /** Controls the padding around the icon, which sets the overall size of the badge. */ size?: Sizes; + /** The icon component rendered as the sole content of the badge. */ icon: IconComponentType; + /** The color palette used for the badge background, ring, and icon. The `modern` type only supports `gray`. */ color?: BadgeTypeToColorMap[T]; + /** Accepted for consistency with the other badges but not rendered — this badge shows only the icon. */ children?: ReactNode; } diff --git a/components/base/button-group/button-group.tsx b/components/base/button-group/button-group.tsx index e5a0aa6..2433efa 100644 --- a/components/base/button-group/button-group.tsx +++ b/components/base/button-group/button-group.tsx @@ -45,9 +45,13 @@ type ButtonSize = keyof typeof styles.sizes; const ButtonGroupContext = createContext<{ size: ButtonSize }>({ size: "md" }); interface ButtonGroupItemProps extends ToggleButtonProps, RefAttributes { + /** An icon rendered before the label. Passing only an icon with no children renders an icon-only button. */ iconLeading?: FC<{ className?: string }> | ReactNode; + /** An icon rendered after the label. */ iconTrailing?: FC<{ className?: string }> | ReactNode; + /** Handler called when the button is clicked. */ onClick?: () => void; + /** The class name applied to the button. */ className?: string; } @@ -87,7 +91,9 @@ export const ButtonGroupItem = ({ }; interface ButtonGroupProps extends Omit, RefAttributes { + /** Controls the padding, text size and icon size of every item in the group. */ size?: ButtonSize; + /** The class name applied to the group wrapper. */ className?: string; } diff --git a/components/base/buttons/app-store-buttons-outline.tsx b/components/base/buttons/app-store-buttons-outline.tsx index db3640a..367aa58 100644 --- a/components/base/buttons/app-store-buttons-outline.tsx +++ b/components/base/buttons/app-store-buttons-outline.tsx @@ -3,7 +3,13 @@ import type { AnchorHTMLAttributes } from "react"; import { cx } from "@/utils/cx"; -export const GooglePlayButton = ({ size = "md", ...props }: AnchorHTMLAttributes & { size?: "md" | "lg" }) => { +export const GooglePlayButton = ({ + size = "md", + ...props +}: AnchorHTMLAttributes & { + /** Controls the rendered width and height of the badge. */ + size?: "md" | "lg"; +}) => { return ( & { size?: "md" | "lg" }) => { +export const AppStoreButton = ({ + size = "md", + ...props +}: AnchorHTMLAttributes & { + /** Controls the rendered width and height of the badge. */ + size?: "md" | "lg"; +}) => { return ( & { size?: "md" | "lg" }) => { +export const GalaxyStoreButton = ({ + size = "md", + ...props +}: AnchorHTMLAttributes & { + /** Controls the rendered width and height of the badge. */ + size?: "md" | "lg"; +}) => { return ( & { size?: "md" | "lg" }) => { +export const AppGalleryButton = ({ + size = "md", + ...props +}: AnchorHTMLAttributes & { + /** Controls the rendered width and height of the badge. */ + size?: "md" | "lg"; +}) => { return ( & { size?: "md" | "lg" }) => { +export const GooglePlayButton = ({ + size = "md", + ...props +}: AnchorHTMLAttributes & { + /** Controls the rendered width and height of the badge. */ + size?: "md" | "lg"; +}) => { return ( & { size?: "md" | "lg" }) => { +export const GooglePlayWhiteButton = ({ + size = "md", + ...props +}: AnchorHTMLAttributes & { + /** Controls the rendered width and height of the badge. */ + size?: "md" | "lg"; +}) => { return ( & { size?: "md" | "lg" }) => { +export const AppStoreButton = ({ + size = "md", + ...props +}: AnchorHTMLAttributes & { + /** Controls the rendered width and height of the badge. */ + size?: "md" | "lg"; +}) => { return ( & { size?: "md" | "lg" }) => { +export const GalaxyStoreButton = ({ + size = "md", + ...props +}: AnchorHTMLAttributes & { + /** Controls the rendered width and height of the badge. */ + size?: "md" | "lg"; +}) => { return ( & { size?: "md" | "lg" }) => { +export const AppGalleryButton = ({ + size = "md", + ...props +}: AnchorHTMLAttributes & { + /** Controls the rendered width and height of the badge. */ + size?: "md" | "lg"; +}) => { return ( ; + /** Controls the box size, the gap between it and the text, and the label and hint typography. */ size?: "sm" | "md"; + /** Content rendered beside the box. It sits inside the checkbox's label element, so it also acts as the accessible name and toggles the checkbox when clicked. */ label?: ReactNode; + /** Supporting text rendered under the label. Clicks on it are stopped so reading the hint does not toggle the checkbox. */ hint?: ReactNode; } diff --git a/components/base/input/hint-text.tsx b/components/base/input/hint-text.tsx index b2abe9d..be55f7c 100644 --- a/components/base/input/hint-text.tsx +++ b/components/base/input/hint-text.tsx @@ -8,8 +8,11 @@ import { cx } from "@/utils/cx"; interface HintTextProps extends AriaTextProps { /** Indicates that the hint text is an error message. */ isInvalid?: boolean; + /** Ref to the underlying text element. */ ref?: Ref; + /** Controls the hint's text size; `sm` renders it one step smaller. The text also shrinks automatically inside a small input field. */ size?: "sm" | "md"; + /** The helper text, or the error message when `isInvalid` is set. */ children: ReactNode; } diff --git a/components/base/input/input-date.tsx b/components/base/input/input-date.tsx index 0765db8..56f0191 100644 --- a/components/base/input/input-date.tsx +++ b/components/base/input/input-date.tsx @@ -29,7 +29,7 @@ export interface InputDateBaseProps extends Omit tooltip?: string; /** * Input size. - * @default "sm" + * @default "md" */ size?: "sm" | "md" | "lg"; /** Placeholder text. */ @@ -42,11 +42,15 @@ export interface InputDateBaseProps extends Omit tooltipClassName?: string; /** Keyboard shortcut to display. */ shortcut?: string | boolean; + /** Ref for the hidden native input that holds the date value. */ ref?: Ref; + /** Ref for the group element that wraps the date segments, icon and tooltip. */ groupRef?: Ref; /** Icon component to display on the left side of the input. */ icon?: ComponentType>; + /** Whether the input shows the error state, replacing the tooltip icon with an error icon. */ isInvalid?: boolean; + /** Whether the input is dimmed and cannot be edited. */ isDisabled?: boolean; } diff --git a/components/base/input/input-number.tsx b/components/base/input/input-number.tsx index ff6f435..e7fb39d 100644 --- a/components/base/input/input-number.tsx +++ b/components/base/input/input-number.tsx @@ -34,7 +34,7 @@ const styles = { export interface InputNumberBaseProps extends AriaNumberFieldProps { /** * Input size. - * @default "sm" + * @default "md" */ size?: "sm" | "md" | "lg"; /** Placeholder text. */ @@ -43,7 +43,9 @@ export interface InputNumberBaseProps extends AriaNumberFieldProps { inputClassName?: string; /** Class name for the input wrapper. */ wrapperClassName?: string; + /** Ref for the underlying input element. */ ref?: Ref; + /** Ref for the group element that wraps the input and its stepper buttons. */ groupRef?: Ref; /** Orientation of buttons. */ orientation?: "horizontal" | "vertical"; @@ -142,6 +144,7 @@ interface InputProps extends InputNumberBaseProps, Pick { isRequired?: boolean; /** * Input size. - * @default "sm" + * @default "md" */ size?: "sm" | "md" | "lg"; /** Placeholder text. */ @@ -35,7 +35,9 @@ export interface InputBaseProps extends Omit { tooltipClassName?: string; /** Keyboard shortcut to display. */ shortcut?: string | boolean; + /** Ref for the underlying input element. */ ref?: Ref; + /** Ref for the group element that wraps the input, its icon, tooltip and shortcut. */ groupRef?: Ref; /** Icon component to display on the left side of the input. */ icon?: ComponentType>; diff --git a/components/base/input/label.tsx b/components/base/input/label.tsx index 0a62746..66c2678 100644 --- a/components/base/input/label.tsx +++ b/components/base/input/label.tsx @@ -8,11 +8,17 @@ import { Tooltip, TooltipTrigger } from "@/components/base/tooltip/tooltip"; import { cx } from "@/utils/cx"; interface LabelProps extends AriaLabelProps { + /** The label text, rendered before the required asterisk and the tooltip trigger. */ children: ReactNode; + /** Whether the required asterisk is tinted with the error color. When left undefined, it follows the invalid state of the surrounding field instead. */ isInvalid?: boolean; + /** Whether the required asterisk is shown. When left undefined, it appears only if the surrounding field is marked required. */ isRequired?: boolean; + /** Title text for a help tooltip. Setting it renders a help icon after the label that stays interactive even when the field is disabled. */ tooltip?: string; + /** Supporting text shown under the tooltip title. Only rendered when `tooltip` is set. */ tooltipDescription?: string; + /** Ref to the underlying label element. */ ref?: Ref; } diff --git a/components/base/input/pin-input.tsx b/components/base/input/pin-input.tsx index a591829..35f47e4 100644 --- a/components/base/input/pin-input.tsx +++ b/components/base/input/pin-input.tsx @@ -10,9 +10,13 @@ import { cx } from "@/utils/cx"; type PinInputSize = "xxxs" | "xxs" | "xs" | "sm" | "md" | "lg"; type PinInputContextType = { + /** The size the root was rendered with, used by the slots and the caret to pick their dimensions and type scale. */ size: PinInputSize; + /** Whether the root marked the input as disabled. */ disabled: boolean; + /** Generated id shared by the input, its label and its description so they stay wired together. */ id: string; + /** Whether the root marked the entered code as invalid. */ invalid: boolean; }; @@ -34,8 +38,11 @@ export const usePinInputContext = () => { }; interface RootProps extends ComponentPropsWithRef<"div"> { + /** Sets the dimensions, corner radius and type scale of every slot, plus the gap between them. Shared with the sub-components through context. */ size?: PinInputSize; + /** Whether the pin cannot be edited. Disables the underlying input and dims the slots. */ disabled?: boolean; + /** Whether the entered pin failed validation. Renders the slots in the error style and marks each one `aria-invalid`. */ invalid?: boolean; } @@ -60,7 +67,9 @@ const styles = { }; type GroupProps = ComponentPropsWithRef & { + /** Character width forwarded to the hidden input as its `size` attribute. Named `width` so it does not clash with the pin input's own `size`. */ width?: number; + /** Additional classes for the hidden text input that sits behind the slots and receives the typing. */ inputClassName?: string; }; @@ -84,7 +93,14 @@ const Group = ({ inputClassName, containerClassName, width, maxLength = 4, ...pr }; Group.displayName = "Group"; -const Slot = ({ index, className, ...props }: ComponentPropsWithRef<"div"> & { index: number }) => { +const Slot = ({ + index, + className, + ...props +}: ComponentPropsWithRef<"div"> & { + /** Zero-based position of the slot in the group. Selects which character of the entered value it displays, whether it shows the caret, and what its `aria-label` announces. */ + index: number; +}) => { const { size, disabled, invalid } = usePinInputContext(); const { slots, isFocused } = useContext(OTPInputContext); diff --git a/components/base/progress-indicators/progress-circles.tsx b/components/base/progress-indicators/progress-circles.tsx index a19bc6e..acd63bd 100644 --- a/components/base/progress-indicators/progress-circles.tsx +++ b/components/base/progress-indicators/progress-circles.tsx @@ -3,11 +3,17 @@ import { cx as clx, sortCx } from "@/utils/cx"; interface ProgressBarProps { + /** How far along the progress is. Its position between `min` and `max` sets the length of the filled arc and the percentage shown in the middle. */ value: number; + /** Lower bound of the range `value` is measured against. */ min?: number; + /** Upper bound of the range `value` is measured against. */ max?: number; + /** Sets the radius and stroke width of the circle, and the type scale of the value and label inside it. */ size: "xxs" | "xs" | "sm" | "md" | "lg"; + /** Caption for the progress, shown above the value inside the circle — or below the circle at the `xxs` size, where there is no room for it. */ label?: string; + /** Formats the text shown in the middle of the circle. Receives the raw `value` and the percentage it works out to. Without it the percentage is shown with a `%` suffix. */ valueFormatter?: (value: number, valueInPercentage: number) => string | number; } diff --git a/components/base/progress-indicators/simple-circle.tsx b/components/base/progress-indicators/simple-circle.tsx index 92cc2c8..3c22fb3 100644 --- a/components/base/progress-indicators/simple-circle.tsx +++ b/components/base/progress-indicators/simple-circle.tsx @@ -1,6 +1,19 @@ "use client"; -export const CircleProgressBar = (props: { value: number; min?: 0; max?: 100 }) => { +export const CircleProgressBar = (props: { + /** How far along the progress is. Its position between `min` and `max` sets the length of the filled arc and the percentage shown in the middle of the circle. */ + value: number; + /** + * Lower bound of the range `value` is measured against. + * @default 0 + */ + min?: 0; + /** + * Upper bound of the range `value` is measured against. + * @default 100 + */ + max?: 100; +}) => { const { value, min = 0, max = 100 } = props; const percentage = ((value - min) * 100) / (max - min); diff --git a/components/base/radio-buttons/radio-buttons.tsx b/components/base/radio-buttons/radio-buttons.tsx index f2b418d..f8c1ac4 100644 --- a/components/base/radio-buttons/radio-buttons.tsx +++ b/components/base/radio-buttons/radio-buttons.tsx @@ -10,16 +10,22 @@ import { import { cx } from "@/utils/cx"; export interface RadioGroupContextType { + /** Size applied to every `RadioButton` inside the group through context, taking precedence over a `size` set on the individual radio. */ size?: "sm" | "md"; } const RadioGroupContext = createContext(null); export interface RadioButtonBaseProps { + /** Controls the diameter of the circle and of the dot rendered inside it. */ size?: "sm" | "md"; + /** Additional classes merged onto the circle element. */ className?: string; + /** Whether to draw the keyboard focus ring around the circle. Normally passed down by `RadioButton` from React Aria's render state rather than set by hand. */ isFocusVisible?: boolean; + /** Whether to render the checked appearance: brand-filled circle with the inner dot faded in. Normally passed down by `RadioButton` rather than set by hand. */ isSelected?: boolean; + /** Whether to render the dimmed, not-allowed appearance. Normally passed down by `RadioButton` rather than set by hand. */ isDisabled?: boolean; } @@ -43,9 +49,13 @@ export const RadioButtonBase = ({ className, isFocusVisible, isSelected, isDisab RadioButtonBase.displayName = "RadioButtonBase"; interface RadioButtonProps extends AriaRadioProps { + /** Controls the circle size, the gap between it and the text, and the label and hint typography. Ignored when a surrounding `RadioGroup` sets a size. */ size?: "sm" | "md"; + /** Content rendered beside the circle. It sits inside the radio's label element, so it also acts as the accessible name and selects the radio when clicked. */ label?: ReactNode; + /** Supporting text rendered under the label. Clicks on it are stopped so reading the hint does not select the radio. */ hint?: ReactNode; + /** Ref to the underlying label element that wraps the circle and the text. */ ref?: Ref; } @@ -108,7 +118,9 @@ export const RadioButton = ({ label, hint, className, size = "sm", ...ariaRadioP RadioButton.displayName = "RadioButton"; interface RadioGroupProps extends RadioGroupContextType, AriaRadioGroupProps { + /** The `RadioButton` elements belonging to the group, stacked in a vertical column. */ children: ReactNode; + /** Additional classes merged onto the group wrapper that lays the radios out. */ className?: string; } diff --git a/components/base/select/combobox.tsx b/components/base/select/combobox.tsx index eadaf51..b1eb89b 100644 --- a/components/base/select/combobox.tsx +++ b/components/base/select/combobox.tsx @@ -14,23 +14,36 @@ import { cx } from "@/utils/cx"; import { isReactComponent } from "@/utils/is-react-component"; interface ComboBoxProps extends Omit, "children" | "items">, RefAttributes, CommonProps { + /** Whether to pin a ⌘K key hint to the right edge of the input. It is hidden below the medium breakpoint. */ shortcut?: boolean; + /** The options to render in the dropdown. When `children` is a function it is called once per item to render it. */ items?: SelectItemType[]; + /** Additional classes merged onto the dropdown popover, which is sized to match the input. */ popoverClassName?: string; + /** Additional classes merged onto the ⌘K hint container, useful for adjusting its fade and spacing. */ shortcutClassName?: string; /** Leading icon component displayed before the input. */ icon?: FC | ReactNode; + /** The dropdown contents: either `Select.Item` elements, or a render function called with each entry of `items`. */ children: AriaListBoxProps["children"]; } interface ComboBoxValueProps extends AriaGroupProps { + /** Controls the field's padding, text size, and icon size. */ size: "sm" | "md" | "lg"; + /** Whether to pin the ⌘K key hint to the right edge of the field. */ shortcut: boolean; + /** Text shown in the input while it is empty. */ placeholder?: string; + /** Additional classes merged onto the ⌘K hint container. */ shortcutClassName?: string; + /** Leading icon rendered before the input. Falls back to a search icon when omitted. */ icon?: FC | ReactNode; + /** Fires when focus moves into the field. Used to re-measure the field so the popover matches its width. */ onFocus?: FocusEventHandler; + /** Fires when the pointer enters the field. Used to re-measure the field so the popover matches its width. */ onPointerEnter?: PointerEventHandler; + /** Ref to the field wrapper. It anchors the popover and provides the width it is sized to. */ ref?: Ref; } diff --git a/components/base/select/popover.tsx b/components/base/select/popover.tsx index e85d3c0..5ba404a 100644 --- a/components/base/select/popover.tsx +++ b/components/base/select/popover.tsx @@ -6,6 +6,7 @@ import { Popover as AriaPopover } from "react-aria-components"; import { cx } from "@/utils/cx"; interface PopoverProps extends AriaPopoverProps, RefAttributes { + /** Caps how tall the popover can grow before its list starts scrolling: 14rem for `sm`, 16rem for `md`, and 20rem for `lg`. */ size: "sm" | "md" | "lg"; } diff --git a/components/base/select/select-native.tsx b/components/base/select/select-native.tsx index a16cb5f..bff5460 100644 --- a/components/base/select/select-native.tsx +++ b/components/base/select/select-native.tsx @@ -7,10 +7,15 @@ import { Label } from "@/components/base/input/label"; import { cx } from "@/utils/cx"; interface NativeSelectProps extends Omit, "size"> { + /** Text rendered in a label above the select and linked to it by a generated id. */ label?: string; + /** Helper text rendered below the select and referenced by its `aria-describedby`. */ hint?: string; + /** Additional classes merged onto the native select element. Use `className` to style the wrapper around it instead. */ selectClassName?: string; + /** Controls the select's padding, text size, and the size of the chevron. Ignored when the select sits inside an `InputGroup`, which imposes its own sizing. */ size?: "sm" | "md" | "lg"; + /** The options to render. Each entry becomes an `option` element showing `label` and submitting `value`. */ options: { label: string; value: string; disabled?: boolean }[]; } diff --git a/components/base/select/select.tsx b/components/base/select/select.tsx index 93bd1f8..7129bbf 100644 --- a/components/base/select/select.tsx +++ b/components/base/select/select.tsx @@ -18,19 +18,30 @@ import { type CommonProps, SelectContext, type SelectItemType, sizes } from "./s export { SelectContext, sizes, type CommonProps, type SelectItemType } from "./select-shared"; export interface SelectProps extends Omit, "children" | "items">, RefAttributes, CommonProps { + /** The options to render in the dropdown. When `children` is a function it is called once per item to render it. */ items?: SelectItemType[]; + /** Additional classes merged onto the dropdown popover. */ popoverClassName?: string; + /** Leading icon shown in the trigger, used as a fallback when the selected item has no icon of its own. Accepts a component reference or a rendered element. */ icon?: FC | ReactNode; + /** The dropdown contents: either `Select.Item` elements, or a render function called with each entry of `items`. */ children: ReactNode | ((item: SelectItemType) => ReactNode); } interface SelectValueProps { + /** Whether the dropdown is open, which keeps the trigger in its focused ring style. */ isOpen: boolean; + /** Controls the trigger's padding, text size, and icon size. */ size: "sm" | "md" | "lg"; + /** Whether the trigger is focused, which draws the brand focus ring. */ isFocused: boolean; + /** Whether the trigger is dimmed and rejects pointer interaction. */ isDisabled: boolean; + /** Text shown in the trigger while no item is selected. */ placeholder?: string; + /** Ref to the trigger button element. */ ref?: Ref; + /** Leading icon shown in the trigger when the selected item has no icon of its own. */ icon?: FC | ReactNode; } diff --git a/components/base/select/tag-select.tsx b/components/base/select/tag-select.tsx index 40af595..edf1268 100644 --- a/components/base/select/tag-select.tsx +++ b/components/base/select/tag-select.tsx @@ -20,14 +20,23 @@ import { cx } from "@/utils/cx"; import { SelectItem } from "./select-item"; interface TagSelectValueProps extends AriaGroupProps { + /** Controls the height, padding, and text size of the input group, as well as how compact the selected tags are. */ size: "sm" | "md" | "lg"; + /** Whether to show the ⌘K hint at the end of the input on medium screens and up. It also reserves a minimum width for the text input. */ shortcut?: boolean; + /** Whether the tags are disabled. This value is not forwarded to the DOM — the group's own disabled styling comes from the enclosing combo box state. */ isDisabled?: boolean; + /** The placeholder text shown in the search input. */ placeholder?: string; + /** Additional CSS classes merged onto the container of the ⌘K hint. */ shortcutClassName?: string; + /** The icon component rendered at the start of the input group. Pass `null` to render no icon. */ icon?: IconComponentType | null; + /** A ref to the group element. `TagSelect` uses it to measure the trigger so the popover can match its width. */ ref?: RefObject; + /** Fires when focus enters the group. `TagSelect` uses it to re-measure the trigger width before the popover opens. */ onFocus?: FocusEventHandler; + /** Fires when the pointer enters the group. `TagSelect` uses it to re-measure the trigger width before the popover opens. */ onPointerEnter?: PointerEventHandler; } @@ -45,20 +54,38 @@ const TagSelectContext = createContext<{ }); interface TagSelectProps extends Omit, "children" | "items">, RefAttributes { + /** Helper text rendered below the input. It switches to the error style when the field is invalid. */ hint?: string; + /** The label rendered above the input. Nothing is rendered when it is omitted. */ label?: string; + /** Text for the help tooltip shown next to the label. Only rendered when `label` is set. */ tooltip?: string; + /** Controls the height, padding, and text size of the input, the selected tags, and the popover. */ size?: "sm" | "md" | "lg"; + /** The placeholder text shown in the search input. */ placeholder?: string; + /** Whether to show the ⌘K hint at the end of the input on medium screens and up. */ shortcut?: boolean; + /** The full set of options offered in the dropdown. They are filtered as the user types, and already selected items are hidden from the list. */ items?: SelectItemType[]; + /** Additional CSS classes merged onto the dropdown popover. */ popoverClassName?: string; + /** Additional CSS classes merged onto the container of the ⌘K hint. */ shortcutClassName?: string; + /** The list state holding the selected items, typically created with `useListData`. The component appends to it on selection and removes from it when a tag is dismissed. */ selectedItems: ListData; + /** + * The icon component rendered at the start of the input. Pass `null` to render no icon. + * @default SearchLg + */ icon?: IconComponentType | null; + /** The listbox content: either the options themselves or a render function called with each filtered item. */ children: AriaListBoxProps["children"]; + /** Fires with the key of an item after its tag has been removed from the selection. */ onItemCleared?: (key: Key) => void; + /** Fires with the key of an item after it has been added to the selection. */ onItemInserted?: (key: Key) => void; + /** Returns the text to display on the tag for a selected item. Without it the item's `label` is used. */ valueFormatter?: (item: SelectItemType) => string; } diff --git a/components/base/slider/slider.tsx b/components/base/slider/slider.tsx index 0d64952..2a8a508 100644 --- a/components/base/slider/slider.tsx +++ b/components/base/slider/slider.tsx @@ -18,7 +18,9 @@ const styles = sortCx({ }); interface SliderProps extends AriaSliderProps { + /** Where the value label sits relative to the thumb. `default` hides it, `bottom` places it below, and `top-floating` shows it in a tooltip above. */ labelPosition?: keyof typeof styles; + /** Formats the value shown in the label. Called with the raw thumb value. Without it the value is formatted as a percentage. */ labelFormatter?: (value: number) => string; } diff --git a/components/base/tags/tags.tsx b/components/base/tags/tags.tsx index c912011..46d24d5 100644 --- a/components/base/tags/tags.tsx +++ b/components/base/tags/tags.tsx @@ -14,7 +14,15 @@ import { cx } from "@/utils/cx"; import { TagCheckbox } from "./base-components/tag-checkbox"; import { TagCloseX } from "./base-components/tag-close-x"; -export const TagAvatar = ({ src, alt, contrastBorder = true, className }: ImgHTMLAttributes & { contrastBorder?: boolean }) => { +export const TagAvatar = ({ + src, + alt, + contrastBorder = true, + className, +}: ImgHTMLAttributes & { + /** Whether to draw a subtle dark outline around the avatar so it stays visible on light images. */ + contrastBorder?: boolean; +}) => { const [isFailed, setIsFailed] = useState(false); return ( @@ -35,14 +43,23 @@ export const TagAvatar = ({ src, alt, contrastBorder = true, className }: ImgHTM }; export interface TagItem { + /** A unique identifier for the tag, passed back to `onClose` and used for selection. */ id: string; + /** The text displayed inside the tag. */ label: string; + /** A number shown in a rounded counter at the end of the tag. */ count?: number; + /** The URL of an avatar image shown before the label. Takes precedence over `dot`. */ avatarSrc?: string; + /** Whether to draw a subtle dark outline around the avatar so it stays visible on light images. */ avatarContrastBorder?: boolean; + /** Whether to show a small colored dot before the label. Ignored when `avatarSrc` is set. */ dot?: boolean; + /** Additional classes for the leading dot, typically used to change its color. */ dotClassName?: string; + /** Whether the tag is dimmed and cannot be selected or removed. */ isDisabled?: boolean; + /** Handler called with the tag's `id` when its close button is pressed. Providing it renders the close button. */ onClose?: (id: string) => void; } @@ -55,7 +72,9 @@ const TagGroupContext = createContext<{ }); interface TagGroupProps extends AriaTagGroupProps, RefAttributes { + /** An accessible name for the group of tags, applied as `aria-label`. */ label: string; + /** Controls the padding, text size and spacing of every tag in the group. */ size?: "sm" | "md" | "lg"; } diff --git a/components/base/textarea/textarea.tsx b/components/base/textarea/textarea.tsx index fdee347..3c590c6 100644 --- a/components/base/textarea/textarea.tsx +++ b/components/base/textarea/textarea.tsx @@ -14,7 +14,9 @@ const getResizeHandleBg = (color: string) => { }; interface TextAreaBaseProps extends AriaTextAreaProps { + /** Ref for the underlying textarea element. */ ref?: Ref; + /** Controls the padding and text size of the textarea. */ size?: "sm" | "md"; } @@ -71,7 +73,7 @@ interface TextFieldProps extends AriaTextFieldProps { hideRequiredIndicator?: boolean; /** Placeholder text. */ placeholder?: string; - /** Visible height of textarea in rows . */ + /** Visible height of textarea in rows. */ rows?: number; /** Visible width of textarea in columns. */ cols?: number; diff --git a/components/base/toggle/toggle.tsx b/components/base/toggle/toggle.tsx index c5b4c4a..ad8eda6 100644 --- a/components/base/toggle/toggle.tsx +++ b/components/base/toggle/toggle.tsx @@ -6,12 +6,19 @@ import { Switch as AriaSwitch } from "react-aria-components"; import { cx } from "@/utils/cx"; interface ToggleBaseProps { + /** Controls the track and knob dimensions, and how far the knob slides when selected. */ size?: "sm" | "md"; + /** Renders the compact variant: an unpadded track with a bordered knob that overlaps its edges instead of sitting inside them. */ slim?: boolean; + /** Additional classes merged onto the track element. */ className?: string; + /** Whether to darken the track while selected, for the hover appearance. Normally passed down by `Toggle` from React Aria's render state rather than set by hand. */ isHovered?: boolean; + /** Whether to draw the keyboard focus ring around the track. Normally passed down by `Toggle` rather than set by hand. */ isFocusVisible?: boolean; + /** Whether to render the on appearance: brand-filled track with the knob slid to the right. Normally passed down by `Toggle` rather than set by hand. */ isSelected?: boolean; + /** Whether to render the dimmed, not-allowed appearance. Normally passed down by `Toggle` rather than set by hand. */ isDisabled?: boolean; } @@ -91,9 +98,13 @@ const styles = { }; interface ToggleProps extends AriaSwitchProps { + /** Controls the toggle dimensions, the gap between it and the text, and the label and hint typography. */ size?: "sm" | "md"; + /** Text rendered beside the toggle. It sits inside the switch's label element, so it also acts as the accessible name and flips the toggle when clicked. */ label?: string; + /** Supporting text rendered under the label. Clicks on it are stopped so reading the hint does not flip the toggle. */ hint?: ReactNode; + /** Renders the compact toggle variant, with a shorter track and a bordered knob that overlaps its edges. */ slim?: boolean; } diff --git a/components/foundations/featured-icon/featured-icon.tsx b/components/foundations/featured-icon/featured-icon.tsx index 664f763..5177683 100644 --- a/components/foundations/featured-icon/featured-icon.tsx +++ b/components/foundations/featured-icon/featured-icon.tsx @@ -118,12 +118,19 @@ const styles = sortCx({ }); interface FeaturedIconProps { + /** A ref to the wrapper element. */ ref?: Ref; + /** Extra content rendered inside the wrapper, alongside the icon. */ children?: ReactNode; + /** The class name applied to the wrapper. */ className?: string; + /** The icon to display. Accepts a component reference or a rendered element. */ icon?: FC<{ className?: string }> | ReactNode; + /** Controls the dimensions of the wrapper and the icon inside it. */ size?: "sm" | "md" | "lg" | "xl"; + /** The semantic color used for the background, icon and border of the current theme. The `modern` and `modern-neue` themes are only styled for `gray`. */ color: "brand" | "gray" | "success" | "warning" | "error"; + /** The visual treatment of the icon container, from a subtle tinted circle to a solid, outlined or skeuomorphic surface. */ theme?: "light" | "gradient" | "dark" | "outline" | "modern" | "modern-neue"; } diff --git a/components/foundations/rating-badge.tsx b/components/foundations/rating-badge.tsx index 3a73f9b..d50a80b 100644 --- a/components/foundations/rating-badge.tsx +++ b/components/foundations/rating-badge.tsx @@ -118,9 +118,13 @@ export const Wreath = (props: HTMLAttributes) => ( ); interface RatingBadgeProps extends HTMLAttributes { + /** The headline shown between the wreaths, such as the award being highlighted. */ title?: string; + /** The supporting line shown below the title, such as a review count. */ subtitle?: string; + /** The score used to fill the stars above the title. */ rating?: number; + /** Whether the badge is styled for a dark background (`light`) or a light background (`dark`). */ theme?: "light" | "dark"; } diff --git a/components/shared-assets/credit-card/credit-card.tsx b/components/shared-assets/credit-card/credit-card.tsx index f5141b8..992622f 100644 --- a/components/shared-assets/credit-card/credit-card.tsx +++ b/components/shared-assets/credit-card/credit-card.tsx @@ -112,12 +112,19 @@ const CARD_WITH_COLOR_LOGO = ["brand-dark", "brand-light", "gray-dark", "gray-li type CreditCardType = (typeof _NORMAL_TYPES)[number] | (typeof STRIP_TYPES)[number] | (typeof VERTICAL_STRIP_TYPES)[number]; interface CreditCardProps { + /** The issuer name printed in the top left corner of the card. */ company?: string; + /** The card number printed along the bottom of the card. */ cardNumber?: string; + /** The cardholder name printed above the card number. Rendered in uppercase. */ cardHolder?: string; + /** The expiry date printed to the right of the cardholder name. */ cardExpiration?: string; + /** The visual style of the card, which sets its background, text colors and any decorative strip. */ type?: CreditCardType; + /** The class name applied to the outer wrapper of the card. */ className?: string; + /** The rendered width of the card in pixels. The card is scaled proportionally from its intrinsic 316×190 size. */ width?: number; } diff --git a/components/shared-assets/illustrations/index.tsx b/components/shared-assets/illustrations/index.tsx index cccc3ea..832a329 100644 --- a/components/shared-assets/illustrations/index.tsx +++ b/components/shared-assets/illustrations/index.tsx @@ -14,12 +14,20 @@ const types = { }; export interface IllustrationProps extends HTMLAttributes { + /** The overall scale of the illustration, which also selects the background pattern drawn behind it. */ size?: "sm" | "md" | "lg"; + /** Classes applied to the inline `svg` itself, for overriding its stroke or fill. */ svgClassName?: string; + /** Classes applied to the badge that holds `children`, the icon floating over the illustration. */ childrenClassName?: string; } -export const Illustration = (props: IllustrationProps & { type: keyof typeof types }) => { +export const Illustration = ( + props: IllustrationProps & { + /** Which illustration to draw. */ + type: keyof typeof types; + }, +) => { const { type } = props; const Component = types[type];