{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"button-group","type":"registry:ui","title":"ButtonGroup","description":"Joins a row of adjacent actions into one seamless control — one border, one divider, no doubled edges","author":"fancy-ui <https://fancy-ui.rama.app>","categories":["actions"],"registryDependencies":["https://fancy-ui.rama.app/r/utils.json"],"files":[{"path":"react/src/components/button-group/ButtonGroup.tsx","content":"\"use client\";\n\nimport { forwardRef, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../../utils\";\nimport { BUTTON_GROUP_CONTEXT_KEY } from \"./types\";\nimport type { ButtonGroupContext, ButtonGroupOrientation } from \"./types\";\nimport \"./button-group.css\";\n\nexport interface ButtonGroupProps {\n\t/** Stacking axis for the joined items. Defaults to a row. */\n\torientation?: ButtonGroupOrientation;\n\t/** Accessible name for the group, exposed as `aria-label`. */\n\tlabel?: string;\n\t/** The adjacent actions to join into one seamless control. */\n\tchildren?: ReactNode;\n\t/** Additional CSS classes */\n\tclassName?: string;\n}\n\n/**\n * Joins a row (or a stack) of adjacent actions into one seamless control.\n *\n * The root element arrives through the ref channel rather than a `ref` prop,\n * per PORTING.md — the Svelte source declares `ref = $bindable(null)`.\n *\n * Rest props are not spread: the Svelte source reads only these props off\n * `$props()` and has no `...restProps`, so the port carries no wider\n * attribute surface than the component it mirrors.\n */\nexport const ButtonGroup = forwardRef<HTMLDivElement, ButtonGroupProps>(\n\t({ orientation = \"horizontal\", label, children, className }, ref) => {\n\t\t// Rebuilt only when the orientation actually changes — that rebuild is\n\t\t// what re-renders the nested controls reading it, and it is the React\n\t\t// counterpart of the Svelte context's live getter.\n\t\tconst context = useMemo<ButtonGroupContext>(() => ({ orientation }), [orientation]);\n\n\t\treturn (\n\t\t\t<BUTTON_GROUP_CONTEXT_KEY.Provider value={context}>\n\t\t\t\t<div\n\t\t\t\t\tref={ref}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ft-button-group border-border inline-flex rounded-lg border\",\n\t\t\t\t\t\torientation === \"vertical\" ? \"flex-col\" : \"flex-row\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdata-orientation={orientation}\n\t\t\t\t\trole=\"group\"\n\t\t\t\t\taria-label={label}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t</BUTTON_GROUP_CONTEXT_KEY.Provider>\n\t\t);\n\t}\n);\n\nButtonGroup.displayName = \"ButtonGroup\";\n","type":"registry:ui","target":"@ui/fancy/components/button-group/ButtonGroup.tsx"},{"path":"react/src/components/button-group/button-group.css","content":"/*\n * These rules reach into whatever a caller renders inside ButtonGroup — a\n * Button, a plain <button>, an <a> — which ButtonGroup does not own and has\n * no other way to touch: the seam only reads as one control if every item\n * gives up its own corners and border, and only the container can reach in\n * and say so. Every rule is anchored on the root class `.ft-button-group`\n * (the Svelte source's own name, already the first token of the root class\n * list) so nothing here escapes into the rest of a consumer's page.\n * Unlayered rules beat Tailwind's layered utilities, so this wins over a\n * child's own `rounded-*` / `border` classes without `!important`.\n */\n.ft-button-group > * {\n\tborder-style: none;\n\tborder-radius: 0;\n}\n\n/*\n * The divider reads lighter than the container's own outer border (10%\n * vs. 14% in the mockup) — a seam between two items already in contact,\n * not an edge that has to hold its own against whatever sits outside it.\n */\n\n/* Horizontal: a vertical hairline between items, left-to-right. */\n.ft-button-group:not([data-orientation=\"vertical\"]) > * + * {\n\tborder-left: 1px solid var(--color-border, color-mix(in oklab, currentColor 10%, transparent));\n}\n\n/* Vertical: the same hairline, rotated onto the stacking axis. */\n.ft-button-group[data-orientation=\"vertical\"] > * + * {\n\tborder-top: 1px solid var(--color-border, color-mix(in oklab, currentColor 10%, transparent));\n}\n\n/*\n * The container's own radius only ever shows at its two open ends — the\n * left edge of the first item and the right edge of the last one in a\n * row, or the top and bottom in a stack. `inherit` copies whatever radius\n * the container ends up with (including a caller's override through\n * `className`) instead of a second hardcoded value that could drift from it.\n */\n.ft-button-group:not([data-orientation=\"vertical\"]) > *:first-child {\n\tborder-top-left-radius: inherit;\n\tborder-bottom-left-radius: inherit;\n}\n\n.ft-button-group:not([data-orientation=\"vertical\"]) > *:last-child {\n\tborder-top-right-radius: inherit;\n\tborder-bottom-right-radius: inherit;\n}\n\n.ft-button-group[data-orientation=\"vertical\"] > *:first-child {\n\tborder-top-left-radius: inherit;\n\tborder-top-right-radius: inherit;\n}\n\n.ft-button-group[data-orientation=\"vertical\"] > *:last-child {\n\tborder-bottom-left-radius: inherit;\n\tborder-bottom-right-radius: inherit;\n}\n\n/*\n * No `overflow: hidden` on the root: the first/last child rules above\n * already give the two open ends exactly the container's own radius, so\n * nothing ever renders outside its rounded outline that would need\n * clipping — and a clip here would have cut every item's focus ring off\n * at the container's edge along with it. What is still a real hazard is\n * a focused item's ring bleeding sideways into whichever neighbour sits\n * later in source order, whose opaque background then paints over it.\n * Lifting the focused item into its own stacking context puts the ring\n * back on top of that neighbour instead.\n */\n.ft-button-group > *:focus-visible {\n\tposition: relative;\n\tz-index: 1;\n}\n","type":"registry:ui","target":"@ui/fancy/components/button-group/button-group.css"},{"path":"react/src/components/button-group/index.ts","content":"\"use client\";\n\nexport { ButtonGroup } from \"./ButtonGroup\";\nexport type { ButtonGroupProps } from \"./ButtonGroup\";\nexport { BUTTON_GROUP_CONTEXT_KEY } from \"./types\";\nexport type { ButtonGroupContext, ButtonGroupOrientation } from \"./types\";\n","type":"registry:ui","target":"@ui/fancy/components/button-group/index.ts"},{"path":"react/src/components/button-group/types.ts","content":"\"use client\";\n\n/**\n * The contract ButtonGroup publishes to whatever it wraps.\n *\n * ButtonGroup only owns the seam — the border, the radius, the divider\n * between items. It has no idea what its children are (a Button, a plain\n * `<button>`, an `<a>`), so anything a nested control needs to adapt its own\n * layout has to come through context rather than a prop threaded down\n * through markup ButtonGroup does not control.\n */\n\nimport { createContext } from \"react\";\n\nexport type ButtonGroupOrientation = \"horizontal\" | \"vertical\";\n\n/** What the root publishes. Read-only: only the root decides the orientation. */\nexport interface ButtonGroupContext {\n\t/**\n\t * The root's current orientation. On the Svelte side this is a getter so a\n\t * reactive read tracks the root's prop; here the root rebuilds the context\n\t * object whenever `orientation` changes, which is what re-renders the\n\t * consumers below it — same live behaviour, no getter needed.\n\t */\n\treadonly orientation: ButtonGroupOrientation;\n}\n\n/**\n * The context a nested control reads to learn the surrounding group's\n * orientation. The Svelte source publishes it under a `Symbol` context key;\n * React's own context object plays that role here, so the exported name is\n * kept and the value is a `React.Context` rather than a symbol:\n *\n * ```tsx\n * const group = useContext(BUTTON_GROUP_CONTEXT_KEY);\n * ```\n *\n * Read it as optional — a control rendered outside a `ButtonGroup` gets\n * `undefined` rather than throwing.\n */\nexport const BUTTON_GROUP_CONTEXT_KEY = createContext<ButtonGroupContext | undefined>(undefined);\nBUTTON_GROUP_CONTEXT_KEY.displayName = \"ButtonGroupContext\";\n","type":"registry:ui","target":"@ui/fancy/components/button-group/types.ts"}],"meta":{"license":"MIT","package":"fancy-ui-react","version":"0.7.0","source":"https://fancy-ui.rama.app/docs/components/button-group?framework=react"}}