import type { ReactNode } from "react"; import { tv } from "../../core/tv"; import { Box, type BoxProps } from "../containers/Box"; import type { SegmentedOrientation, SegmentedVariant, } from "./SelectionContext"; // Vertical: the chips stretch to the bar's width, so the frame at the two ends // is the bar's own `py-xs` — which is also what leaves the first and last focus ring room to // draw, the bar being overflow-hidden. // The bar is content-width, so it never spreads across whatever holds it; // `stretch` opts into the opposite, for a container that is meant to be filled // (the stacked line of an AppHeader). Where the container is content-sized // anyway — the `md` line of that same header — stretching changes nothing. const segmentedBarVariants = tv({ base: "surface lowered items-stretch px-xs py-0", variants: { orientation: { horizontal: "flex-row min-h-control-md rounded-full", vertical: "flex-col py-xs rounded-md", }, stretch: { true: "self-stretch", false: "self-start", }, // A bar of square icon chips is a stadium at the control height, so the // track takes the same radius as the chips it holds. It drops its gap too: // the chip is already inset inside its own tap target, so a gap would add to that // slack and leave the icons floating far apart. variant: { segmented: "gap-xxs", icon: "gap-0", }, }, defaultVariants: { orientation: "horizontal", stretch: false, variant: "segmented", }, }); export interface SegmentedBarProps extends Omit { role: "group" | "navigation" | "radiogroup" | "tablist"; orientation?: SegmentedOrientation; stretch?: boolean; variant?: SegmentedVariant; } /** * Lowered track shared by every segmented group (RadioButtonGroup, NavBar, Tabs). * It is a `control-md` lowered surface with no vertical padding, so each item * pressable fills the full height (the tap target) while rendering a shorter * visible chip inside it. */ export function SegmentedBar({ orientation, stretch, variant, className, ...props }: SegmentedBarProps): ReactNode { return ( ); }