import { forwardRef, type ComponentType, type Ref } from "react"; import { useDesignSystemComponent } from "./context.js"; import { defaultDesignSystemComponents } from "./default-adapter.js"; import { DesignSystemErrorBoundary } from "./error-boundary.js"; import type { ActionButtonProps, AvatarProps, CheckboxProps, DesignSystemComponentName, DesignSystemComponents, DesignSystemKey, DialogProps, IconButtonProps, MenuProps, PickerProps, PopoverProps, SkeletonProps, SpinnerProps, StatusProps, SurfaceProps, SwitchProps, TabsProps, TextAreaProps, TextFieldProps, TooltipProps, } from "./types.js"; function createComponent( name: DesignSystemComponentName, DefaultComponent: ComponentType, ) { function DesignSystemComponent(props: Props) { const CustomComponent = useDesignSystemComponent(name) as | ComponentType | undefined; const fallback = ; if ( !CustomComponent || CustomComponent === DefaultComponent || CustomComponent === DesignSystemComponent ) { return fallback; } return ( ); } DesignSystemComponent.displayName = `DesignSystem.${name}`; return DesignSystemComponent; } function mergeElementRefs( refs: readonly (Ref | undefined)[], ): Ref { return (node: Element | null) => { for (const ref of refs) { if (typeof ref === "function") ref(node); else if (ref) (ref as { current: Element | null }).current = node; } }; } /** * Like `createComponent`, but also wraps in `forwardRef` and merges the * forwarded ref into `elementRef` before rendering. Radix `asChild`/`Slot` * triggers attach a native `ref` directly to their JSX child to measure it * for popper positioning; without this, that ref is silently dropped by the * plain function component `createComponent` returns, and Radix falls back * to an unmeasured, off-screen position. Scoped to ActionButton/IconButton * because those are the only semantic contracts that declare `elementRef`. */ function createRefForwardingComponent< Props extends { elementRef?: Ref }, Element, >(name: DesignSystemComponentName, DefaultComponent: ComponentType) { const DesignSystemComponent = forwardRef((props, ref) => { const CustomComponent = useDesignSystemComponent(name) as | ComponentType | undefined; const mergedProps = { ...props, elementRef: mergeElementRefs([ref, props.elementRef]), } as Props; const fallback = ; if ( !CustomComponent || CustomComponent === DefaultComponent || (CustomComponent as unknown) === DesignSystemComponent ) { return fallback; } return ( ); }); DesignSystemComponent.displayName = `DesignSystem.${name}`; return DesignSystemComponent; } export const ActionButton = createRefForwardingComponent< ActionButtonProps, HTMLButtonElement >("ActionButton", defaultDesignSystemComponents.ActionButton); export const IconButton = createRefForwardingComponent< IconButtonProps, HTMLButtonElement >("IconButton", defaultDesignSystemComponents.IconButton); export const TextField = createComponent( "TextField", defaultDesignSystemComponents.TextField, ); export const TextArea = createComponent( "TextArea", defaultDesignSystemComponents.TextArea, ); export const Spinner = createComponent( "Spinner", defaultDesignSystemComponents.Spinner, ); export const Skeleton = createComponent( "Skeleton", defaultDesignSystemComponents.Skeleton, ); export const Status = createComponent( "Status", defaultDesignSystemComponents.Status, ); export const Surface = createComponent( "Surface", defaultDesignSystemComponents.Surface, ); export const Avatar = createComponent( "Avatar", defaultDesignSystemComponents.Avatar, ); export const Tooltip = createComponent( "Tooltip", defaultDesignSystemComponents.Tooltip, ); export const Menu = createComponent( "Menu", defaultDesignSystemComponents.Menu, ); export const Popover = createComponent( "Popover", defaultDesignSystemComponents.Popover, ); export const Dialog = createComponent( "Dialog", defaultDesignSystemComponents.Dialog, ); export const Checkbox = createComponent( "Checkbox", defaultDesignSystemComponents.Checkbox, ); export const Switch = createComponent( "Switch", defaultDesignSystemComponents.Switch, ); export function Picker( props: PickerProps, ) { const CustomComponent = useDesignSystemComponent("Picker") as | DesignSystemComponents["Picker"] | undefined; const DefaultComponent = defaultDesignSystemComponents.Picker; const fallback = ; if (!CustomComponent || CustomComponent === DefaultComponent) return fallback; return ( ); } export function Tabs( props: TabsProps, ) { const CustomComponent = useDesignSystemComponent("Tabs") as | DesignSystemComponents["Tabs"] | undefined; const DefaultComponent = defaultDesignSystemComponents.Tabs; const fallback = ; if (!CustomComponent || CustomComponent === DefaultComponent) return fallback; return ( ); }