{ "name": "segmented-input", "type": "registry:ui", "files": [ { "path": "ui/segmented-input.tsx", "type": "registry:ui", "content": "\"use client\";\n\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { useDirection } from \"@/registry/bases/base/ui/direction\";\nimport { Input } from \"@/registry/bases/base/ui/input\";\n\nconst ROOT_NAME = \"SegmentedInput\";\nconst ITEM_NAME = \"SegmentedInputItem\";\n\ntype Direction = \"ltr\" | \"rtl\";\ntype Orientation = \"horizontal\" | \"vertical\";\ntype Size = \"default\" | \"sm\" | \"lg\";\ntype Position = \"isolated\" | \"first\" | \"middle\" | \"last\";\n\ninterface SegmentedInputContextValue {\n dir?: Direction;\n orientation?: Orientation;\n size?: Size;\n disabled?: boolean;\n invalid?: boolean;\n required?: boolean;\n}\n\nconst SegmentedInputContext =\n React.createContext(null);\n\nfunction useSegmentedInputContext(consumerName: string) {\n const context = React.useContext(SegmentedInputContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` must be used within \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface SegmentedInputProps\n extends React.ComponentProps<\"div\">,\n useRender.ComponentProps<\"div\"> {\n dir?: Direction;\n orientation?: Orientation;\n size?: Size;\n disabled?: boolean;\n invalid?: boolean;\n required?: boolean;\n}\n\nfunction SegmentedInput(props: SegmentedInputProps) {\n const {\n size = \"default\",\n dir: dirProp,\n orientation = \"horizontal\",\n children,\n className,\n render,\n disabled,\n invalid,\n required,\n ...rootProps\n } = props;\n\n const contextDir = useDirection();\n const dir = dirProp ?? contextDir;\n\n const contextValue = React.useMemo(\n () => ({\n dir,\n orientation,\n size,\n disabled,\n invalid,\n required,\n }),\n [dir, orientation, size, disabled, invalid, required],\n );\n\n const childrenArray = React.Children.toArray(children);\n const childrenCount = childrenArray.length;\n\n const segmentedInputItems = React.Children.map(children, (child, index) => {\n if (React.isValidElement(child)) {\n if (!child.props.position) {\n let position: Position;\n\n if (childrenCount === 1) {\n position = \"isolated\";\n } else if (index === 0) {\n position = \"first\";\n } else if (index === childrenCount - 1) {\n position = \"last\";\n } else {\n position = \"middle\";\n }\n\n return React.cloneElement(child, { position });\n }\n }\n return child;\n });\n\n return (\n \n {useRender({\n defaultTagName: \"div\",\n props: mergeProps<\"div\">(\n {\n role: \"group\",\n \"aria-orientation\": orientation,\n dir,\n className: cn(\n \"flex\",\n orientation === \"horizontal\" ? \"flex-row\" : \"flex-col\",\n className,\n ),\n children: segmentedInputItems,\n },\n rootProps,\n ),\n render,\n state: {\n slot: \"segmented-input\",\n orientation,\n ...(disabled && { disabled: \"\" }),\n ...(invalid && { invalid: \"\" }),\n ...(required && { required: \"\" }),\n },\n })}\n \n );\n}\n\nconst segmentedInputItemVariants = cva(\"\", {\n variants: {\n position: {\n isolated: \"\",\n first: \"rounded-e-none\",\n middle: \"-ms-px rounded-none border-l-0\",\n last: \"-ms-px rounded-s-none border-l-0\",\n },\n orientation: {\n horizontal: \"\",\n vertical: \"\",\n },\n size: {\n sm: \"h-8 px-2 text-xs\",\n default: \"h-9 px-3\",\n lg: \"h-11 px-4\",\n },\n },\n compoundVariants: [\n {\n position: \"first\",\n orientation: \"vertical\",\n class: \"ms-0 rounded-e-md rounded-b-none border-l\",\n },\n {\n position: \"middle\",\n orientation: \"vertical\",\n class: \"ms-0 -mt-px rounded-none border-t-0 border-l\",\n },\n {\n position: \"last\",\n orientation: \"vertical\",\n class: \"ms-0 -mt-px rounded-s-md rounded-t-none border-t-0 border-l\",\n },\n ],\n defaultVariants: {\n position: \"isolated\",\n orientation: \"horizontal\",\n size: \"default\",\n },\n});\n\ninterface SegmentedInputItemProps\n extends React.ComponentProps<\"input\">,\n Omit, \"size\">,\n useRender.ComponentProps<\"input\"> {}\n\nfunction SegmentedInputItem(props: SegmentedInputItemProps) {\n const { render, className, position, disabled, required, ...inputProps } =\n props;\n const context = useSegmentedInputContext(ITEM_NAME);\n\n const isDisabled = disabled ?? context.disabled;\n const isRequired = required ?? context.required;\n\n const itemClassName = cn(\n segmentedInputItemVariants({\n position,\n orientation: context.orientation,\n size: context.size,\n className,\n }),\n );\n\n const renderedElement = useRender({\n defaultTagName: \"input\",\n props: mergeProps<\"input\">(\n {\n \"aria-invalid\": context.invalid,\n \"aria-required\": isRequired,\n disabled: isDisabled,\n required: isRequired,\n className: itemClassName,\n },\n inputProps,\n ),\n render,\n state: {\n slot: \"segmented-input-item\",\n orientation: context.orientation,\n position,\n ...(isDisabled && { disabled: \"\" }),\n ...(context.invalid && { invalid: \"\" }),\n ...(isRequired && { required: \"\" }),\n },\n });\n\n if (render) {\n return renderedElement;\n }\n\n return (\n \n );\n}\n\nexport { SegmentedInput, SegmentedInputItem, type SegmentedInputProps };\n", "target": "" } ], "registryDependencies": [ "input", "direction" ], "dependencies": [ "@base-ui/react" ] }