import { cloneElement, ReactNode } from 'react'; import classnames from 'classnames'; import Box from './Box'; import focusStyles from './Focus.css'; import layout from './Layout.css'; import styles from './SegmentedControl.css'; import Text from './Text'; import useFocusVisible from './useFocusVisible'; type SizeType = 'sm' | 'md' | 'lg'; type OnChange = (arg1: { event: React.MouseEvent; activeIndex: number }) => void; type Props = { /** * Items for selection. Though typically strings, React.Node is accepted to allow for Icons or other custom UI. */ items: ReadonlyArray; /** * Callback triggered when the user selects an item. */ onChange: OnChange; /** * By default, items have equal widths. If this prop is true, the width of an item is based on its content. See the [responsive example](https://gestalt.pinterest.systems/web/segmentedcontrol#Example:-Responsive) for more details. */ responsive?: boolean; /** * Index of element in `items` that is currently selected. */ selectedItemIndex: number; /** * Size of the Segmented Control. */ size?: SizeType; }; const getDensityStyles = (s: SizeType) => { switch (s) { case 'sm': return { fontSize: '200', iconSize: 16 }; case 'lg': return { fontSize: '300', iconSize: 24 }; case 'md': default: return { fontSize: '300', iconSize: 20 }; } }; const applyDensityStyle = (s: SizeType) => styles[`${s}`]; // layout.css contains a mapping of size to min-height const applyMinHeight = (s: SizeType) => { const lookup = { 'sm': 'small', 'md': 'medium', 'lg': 'large' } as const; return layout[`${lookup[s]}`]; }; function SegmentedControlItem({ index, item, isSelected, onChange, size = 'md', width, }: { index: number; item: ReactNode; isSelected: boolean; onChange: OnChange; width: string | null | undefined; size: SizeType; }) { const { isFocusVisible } = useFocusVisible(); const cs = classnames( styles.item, { [styles.itemIsNotSelected]: !isSelected, [styles.itemIsSelected]: isSelected, [focusStyles.hideOutline]: !isFocusVisible, [focusStyles.accessibilityOutline]: isFocusVisible, }, applyDensityStyle(size), ); const { fontSize, iconSize } = getDensityStyles(size); return ( ); } /** * [SegmentedControl](https://gestalt.pinterest.systems/web/segmentedcontrol) may be used to group multiple selections. The controls display the current state and related state. * * Create layout to convey clear sense of information hierarchy. When a control is engaged, information below the control should also be updated. * * ![SegmentedControl light mode](https://raw.githubusercontent.com/pinterest/gestalt/master/playwright/visual-test/SegmentedControl.spec.ts-snapshots/SegmentedControl-chromium-darwin.png) * ![SegmentedControl dark mode](https://raw.githubusercontent.com/pinterest/gestalt/master/playwright/visual-test/SegmentedControl-dark.spec.ts-snapshots/SegmentedControl-dark-chromium-darwin.png) * */ export default function SegmentedControl({ items, onChange, responsive, selectedItemIndex, size = 'md', }: Props) { const buttonWidth = responsive ? undefined : `${Math.floor(100 / Math.max(1, items.length))}%`; return (
{items.map((item, i) => ( ))}
); } SegmentedControl.displayName = 'SegmentedControl';