import type { Snippet } from 'svelte'; import type { HTMLAttributes } from 'svelte/elements'; import type { MintProp } from '../../mint/index.js'; import type { ComponentIntent, ComponentSize, ComponentVariant, InteractiveTier } from '../../utils/index.js'; import type { ButtonVariants } from '../Button/button.variants.js'; import type { ButtonGroupSlots } from './buttongroup.variants.js'; export type ButtonGroupSelection = 'none' | 'single' | 'multiple'; export type ButtonGroupOrientation = 'horizontal' | 'vertical'; export type ButtonGroupValue = string | string[] | undefined; /** * @summary Related buttons as one control, optionally with a selected state. * @description Groups related buttons with shared styling, layout, and optional selection behaviour. * Supports single-select (radio), multi-select (checkbox), or no selection. * Note the default `variant` is `outlined`, not `filled` as on a lone Button: a connected group * with a selected state reads as a segmented control, which needs an outline to sit in and a * filled active segment to stand out — three filled buttons side by side carry no hierarchy. * Pass `variant="filled"` explicitly for a row of equally weighted actions. * * @tag action * @related Button * @related Toolbar * * @example * ```svelte * * * * * ``` * * @example * ```svelte * * * * * * ``` */ export interface ButtonGroupProps extends Omit, 'children'> { /** Button children to group. */ children?: Snippet; /** Stack direction. */ orientation?: ButtonGroupOrientation; /** Visually connect buttons (overlapping borders, shared rounding). When `false`, buttons are spaced with a small gap. */ connected?: boolean; /** Size propagated to child Buttons (the group value wins over a Button's own `size`). */ size?: ComponentSize; /** Semantic colour propagated to child Buttons. */ intent?: ComponentIntent; /** Visual weight propagated to child Buttons. */ variant?: ButtonVariants['variant']; /** * Semantic radius tier propagated to child Buttons. `commit` → pill caps for * the group; `modify` → soft caps. Inherits from a wrapping Toolbar via * TierContext when not set explicitly. The unset default is `commit`, except * on a connected vertical group, where the pill cap domes the stack into a * lozenge — that one defaults to `modify`. Set `tier="commit"` explicitly to * get the capsule back (right for a narrow, icon-only stack). * @summary Corner rounding for the group: pill caps, or soft ones. */ tier?: InteractiveTier; /** Disable the entire group and all child Buttons. */ disabled?: boolean; /** Selection mode. `"single"` = radio-group, `"multiple"` = checkbox-group, `"none"` = no selection. */ selection?: ButtonGroupSelection; /** Current selection value. Bind with `bind:value` for two-way sync. String for single, string[] for multiple. */ value?: ButtonGroupValue; /** * Micro-interaction preset applied to each child Button (per-item via * context); overrides each button's own `mint` prop. * * The `'none'` default also flattens each button's press sink, so a connected * group's shared seam stays still on click instead of one segment shrinking * away from its neighbours. Buttons keep reporting the press in depth and * colour. Name any real mint here to give the whole group its movement back. * @default 'none' * @summary Decorative feedback effect on every button in the group. */ mint?: MintProp; /** Fired when selection changes. Receives the new value and an array of all selected values. */ onSelectionChange?: (value: ButtonGroupValue, selectedValues: string[]) => void; /** Extra classes merged onto the root element. */ class?: string; /** Remove all default tv classes. */ unstyled?: boolean; /** Per-slot class overrides. Slots: base */ slotClasses?: Partial>; /** * Apply a named preset registered via ``. * Prefer this over `class` overrides when the requested look falls outside the * semantic intent palette — presets keep hover/active/dark-mode logic coherent * and make the custom look reusable across the project. */ preset?: string; /** Accessible label for the group (prefer this over `aria-label` for correct HTML attribute). */ ariaLabel?: string; /** ID of the element that labels the group. */ ariaLabelledBy?: string; } /** Reactive context exposed to child Button components via `getButtonGroupContext()`. */ export interface ButtonGroupContext { readonly orientation: ButtonGroupOrientation; readonly connected: boolean; readonly size: ComponentSize; readonly intent: ComponentIntent; readonly variant: ComponentVariant; readonly selection: ButtonGroupSelection; readonly disabled: boolean; readonly mint: MintProp; readonly selectedValues: Set; registerButton: (value: string | undefined) => { readonly isSelected: boolean; onClick: () => void; getButtonProps: () => { role?: 'radio' | 'checkbox'; 'aria-checked'?: boolean; /** * The Button's selection value, exposed on the element so the group can * resolve the selected radio by matching value instead of by position * (robust against duplicate values and runtime add/remove). */ 'data-value'?: string; }; }; } export { default as ButtonGroup } from './ButtonGroup.svelte'; export { type ButtonGroupVariants, buttonGroupVariants } from './buttongroup.variants.js';