import * as React from 'react'; import type { SegmentedLabeledOption as RcSegmentedLabeledOption, SegmentedProps as RCSegmentedProps, SegmentedValue as RcSegmentedValue, SegmentedRawOption, } from '@rc-component/segmented'; import RcSegmented from '@rc-component/segmented'; import useId from '@rc-component/util/lib/hooks/useId'; import classNames from 'classnames'; import useOrientation from '../_util/hooks/useOrientation'; import type { Orientation } from '../_util/hooks/useOrientation'; import { useComponentConfig } from '../config-provider/context'; import useSize from '../config-provider/hooks/useSize'; import type { SizeType } from '../config-provider/SizeContext'; import useStyle from './style'; export type { SegmentedValue } from '@rc-component/segmented'; export type SemanticName = 'root' | 'icon' | 'label' | 'item'; interface SegmentedLabeledOptionWithoutIcon extends RcSegmentedLabeledOption { label: RcSegmentedLabeledOption['label']; } interface SegmentedLabeledOptionWithIcon extends Omit, 'label'> { label?: RcSegmentedLabeledOption['label']; /** Set icon for Segmented item */ icon: React.ReactNode; } function isSegmentedLabeledOptionWithIcon( option: SegmentedRawOption | SegmentedLabeledOptionWithIcon | SegmentedLabeledOptionWithoutIcon, ): option is SegmentedLabeledOptionWithIcon { return typeof option === 'object' && !!(option as SegmentedLabeledOptionWithIcon)?.icon; } export type SegmentedLabeledOption = | SegmentedLabeledOptionWithIcon | SegmentedLabeledOptionWithoutIcon; export type SegmentedOptions = (T | SegmentedLabeledOption)[]; export interface SegmentedProps extends Omit, 'size' | 'options'> { rootClassName?: string; options: SegmentedOptions; /** Option to fit width to its parent's width */ block?: boolean; /** Option to control the display size */ size?: SizeType; vertical?: boolean; orientation?: Orientation; classNames?: Partial>; styles?: Partial>; shape?: 'default' | 'round'; } const InternalSegmented = React.forwardRef((props, ref) => { const defaultName = useId(); const { prefixCls: customizePrefixCls, className, rootClassName, block, options = [], size: customSize = 'middle', style, vertical, orientation, shape = 'default', name = defaultName, styles, classNames: segmentedClassNames, ...restProps } = props; const { getPrefixCls, direction, className: contextClassName, style: contextStyle, classNames: contextClassNames, styles: contextStyles, } = useComponentConfig('segmented'); const prefixCls = getPrefixCls('segmented', customizePrefixCls); // Style const [hashId, cssVarCls] = useStyle(prefixCls); // ===================== Size ===================== const mergedSize = useSize(customSize); // syntactic sugar to support `icon` for Segmented Item const extendedOptions = React.useMemo( () => options.map((option) => { if (isSegmentedLabeledOptionWithIcon(option)) { const { icon, label, ...restOption } = option; return { ...restOption, label: ( <> {icon} {label && {label}} ), }; } return option; }), [options, prefixCls], ); const [, mergedVertical] = useOrientation(orientation, vertical); const cls = classNames( className, rootClassName, contextClassName, segmentedClassNames?.root, contextClassNames.root, { [`${prefixCls}-block`]: block, [`${prefixCls}-sm`]: mergedSize === 'small', [`${prefixCls}-lg`]: mergedSize === 'large', [`${prefixCls}-vertical`]: mergedVertical, [`${prefixCls}-shape-${shape}`]: shape === 'round', }, hashId, cssVarCls, ); const mergedStyle: React.CSSProperties = { ...contextStyles.root, ...contextStyle, ...styles?.root, ...style, }; return ( ); }); const Segmented = InternalSegmented as (( props: SegmentedProps & React.RefAttributes, ) => ReturnType) & Pick; if (process.env.NODE_ENV !== 'production') { Segmented.displayName = 'Segmented'; } export default Segmented;