// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { CSSProperties, FC, Ref, ReactNode } from 'react';
import { memo, useId, useMemo } from 'react';
import type { Transition } from 'motion/react';
import { motion } from 'motion/react';
import { tw } from '../tw.dom.tsx';
import { ExperimentalAxoBadge } from '../AxoBadge.dom.tsx';
import { createStrictContext, useStrictContext } from './StrictContext.dom.tsx';
import { variants } from './variants.dom.tsx';
/**
* Used to share styles/animations for SegmentedControls, Toolbar ToggleGroups,
* and Tabs.
*
* @example Anatomy
* ```tsx
*
*
*
*
*
*
*
* ```
*/
export namespace ExperimentalAxoBaseSegmentedControl {
/**
*
* --------------------------------------------------------------------------
*/
/**
* Visual style variant.
*/
export type Variant = 'track' | 'no-track';
/**
* How the control sizes itself horizontally:
* - `fit`: Shrinks to fit its content.
* - `full`: Fills available width.
*/
export type RootWidth = 'fit' | 'full';
/**
* How each item sizes itself within the control:
* - `fit`: Items size to their content.
* - `equal`: All items share equal width.
*/
export type ItemWidth = 'fit' | 'equal';
/**
* The currently selected value(s).
* A string for single-select, an array for multi-select, or `null` for nothing selected.
*/
export type RootValue = string | ReadonlyArray | null;
/** @internal */
type RootContextType = Readonly<{
id: string;
value: RootValue;
variant: Variant;
rootWidth: RootWidth;
itemWidth: ItemWidth;
}>;
/** @internal */
const RootContext = createStrictContext(
`AxoBaseSegmentedControl.Root`
);
const baseRootStyles = tw(
'flex min-w-min flex-row items-center justify-items-stretch',
'rounded-full',
'forced-colors:border',
'forced-colors:border-[ButtonBorder]'
);
const RootStyles = variants(`AxoBaseSegmentedControl.Variant`, {
track: tw(baseRootStyles, 'bg-fill-secondary'),
'no-track': baseRootStyles,
});
const baseIndicatorStyles = tw(
'pointer-events-none absolute inset-0 z-10 rounded-full',
'forced-colors:bg-[SelectedItem]'
);
const IndicatorStyles = variants(`AxoBaseSegmentedControl.Variant`, {
track: tw(baseIndicatorStyles, 'bg-fill-primary', 'shadow-elevation-1'),
'no-track': tw(baseIndicatorStyles, 'bg-fill-selected'),
});
/**
*
* --------------------------------------------------------------------------
*/
const RootWidths = variants(`AxoBaseSegmentedControl.RootWidth`, {
fit: tw('w-fit'),
full: tw('w-full'),
});
export type RootProps = Readonly<{
/** Ref to the underlying `` element. */
ref?: Ref
;
/** The currently selected value(s). */
value: RootValue;
/** Visual style variant. */
variant: Variant;
/** How the control sizes itself horizontally. */
width: RootWidth;
/** How each item sizes itself within the control. */
itemWidth: ItemWidth;
children: ReactNode;
}>;
export const Root: FC = memo(props => {
const { value, variant, width, itemWidth, children, ...rest } = props;
const id = useId();
const context = useMemo(() => {
return { id, value, variant, rootWidth: width, itemWidth };
}, [id, value, variant, width, itemWidth]);
return (
{children}
);
});
Root.displayName = 'AxoBaseSegmentedControl.Root';
/**
*
* --------------------------------------------------------------------------
*/
const ItemWidths = variants(`AxoBaseSegmentedControl.ItemWidth`, {
fit: tw('min-w-0 shrink grow basis-auto'),
equal: tw('flex-1'),
});
const IndicatorTransition: Transition = {
type: 'spring',
stiffness: 422,
damping: 37.3,
mass: 1,
};
export type ItemProps = Readonly<{
/** Ref to the underlying `