{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toolbar",
  "title": "Toolbar",
  "description": "A row of tools that is one tab stop: arrow keys rove inside it, with groups, separators, links and toggles, and it wraps or scrolls instead of breaking the layout.",
  "dependencies": [
    "motion",
    "radix-ui"
  ],
  "registryDependencies": [
    "utils",
    "https://miseui.junsu4621.workers.dev/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/miseui/ui/toolbar.tsx",
      "content": "import type * as React from \"react\"\nimport { createContext, useContext, useId, useMemo, useState } from \"react\"\nimport { Toolbar as ToolbarPrimitive } from \"radix-ui\"\nimport { LayoutGroup, motion, useReducedMotion } from \"motion/react\"\nimport { SPRING_DEFAULT } from \"@/registry/miseui/lib/motion\"\nimport { cn } from \"@/registry/miseui/lib/utils\"\n\ntype ToolbarVariant = \"plain\" | \"floating\" | \"pill\" | \"vertical\"\ntype ToolbarEffect = \"spring\"\ntype ToolbarOverflow = \"wrap\" | \"scroll\"\n\nconst ROOT_CLASS: Record<ToolbarVariant, string> = {\n  plain: \"rounded-md border bg-background p-1 shadow-xs\",\n  floating: \"rounded-xl border bg-popover p-1 text-popover-foreground shadow-lg\",\n  // 20 px is a full pill at one row, and a soft rectangle if the tools wrap to two.\n  pill: \"rounded-[20px] bg-muted p-1\",\n  vertical: \"rounded-xl border bg-background p-1 shadow-xs\",\n}\n\nconst ITEM_BASE_CLASS = [\n  \"relative inline-flex h-8 min-w-8 shrink-0 items-center justify-center gap-2 px-2 text-[13px] font-medium whitespace-nowrap outline-none select-none\",\n  \"pointer-coarse:h-10 pointer-coarse:min-w-10\",\n  \"transition-[color,background-color,scale] duration-(--duration-fast) ease-(--ease-mise)\",\n  \"focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50\",\n  \"active:scale-[0.97] active:duration-100 motion-reduce:active:scale-100\",\n  \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n]\nconst ITEM_SHAPE_CLASS: Record<ToolbarVariant, string> = {\n  plain: \"rounded-sm\",\n  floating: \"rounded-md\",\n  pill: \"rounded-full\",\n  vertical: \"rounded-md\",\n}\nconst IDLE_CLASS = \"text-muted-foreground hover:text-foreground\"\nconst PRESSED_CLASS = \"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground\"\nconst PRESSED_SPRING_CLASS = \"isolate data-[state=on]:text-foreground\"\n\nconst ToolbarContext = createContext<{ variant: ToolbarVariant; effect?: ToolbarEffect }>({ variant: \"plain\" })\nconst ToggleGroupContext = createContext<{ shared: boolean; pressed: readonly string[] }>({ shared: false, pressed: [] })\n\ntype ToolbarProps = React.ComponentProps<typeof ToolbarPrimitive.Root> & {\n  /** `plain` a bordered strip, `floating` a raised palette, `pill` a rounded track, `vertical` a column beside the work. */\n  variant?: ToolbarVariant\n  /** `spring` slides one highlight between the items of a single-choice group. */\n  effect?: ToolbarEffect\n  /** What happens when the tools do not fit: `wrap` to more rows or `scroll` inside itself. Ignored by `vertical`. */\n  overflow?: ToolbarOverflow\n}\n\nfunction Toolbar({ variant = \"plain\", effect, overflow = \"wrap\", className, orientation, ...props }: ToolbarProps) {\n  // layoutId is global to the page; without a per-instance group two toolbars would share one highlight.\n  const groupId = useId()\n  const context = useMemo(() => ({ variant, effect }), [variant, effect])\n  const vertical = variant === \"vertical\"\n  const scrolls = !vertical && overflow === \"scroll\"\n  return (\n    <ToolbarContext.Provider value={context}>\n      <LayoutGroup id={groupId}>\n        <ToolbarPrimitive.Root\n          data-slot=\"toolbar\"\n          data-variant={variant}\n          data-effect={effect}\n          data-overflow={vertical ? undefined : overflow}\n          orientation={orientation ?? (vertical ? \"vertical\" : \"horizontal\")}\n          className={cn(\n            \"flex min-w-0 gap-1\",\n            ROOT_CLASS[variant],\n            vertical ? \"w-fit flex-col items-center\" : \"items-center\",\n            !vertical && (scrolls ? \"overflow-x-auto\" : \"flex-wrap\"),\n            // A rounded clip on a scroller cuts off whatever runs past its edge.\n            scrolls && \"rounded-none\",\n            className,\n          )}\n          {...props}\n        />\n      </LayoutGroup>\n    </ToolbarContext.Provider>\n  )\n}\n\nfunction ToolbarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  const { variant } = useContext(ToolbarContext)\n  return (\n    <div\n      role=\"group\"\n      data-slot=\"toolbar-group\"\n      className={cn(\"flex shrink-0 items-center gap-1\", variant === \"vertical\" && \"flex-col\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ToolbarSeparator({ className, ...props }: React.ComponentProps<typeof ToolbarPrimitive.Separator>) {\n  return (\n    <ToolbarPrimitive.Separator\n      data-slot=\"toolbar-separator\"\n      className={cn(\n        \"shrink-0 bg-border data-[orientation=horizontal]:my-1 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-6\",\n        \"data-[orientation=vertical]:mx-1 data-[orientation=vertical]:h-6 data-[orientation=vertical]:w-px\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ToolbarButton({ className, ...props }: React.ComponentProps<typeof ToolbarPrimitive.Button>) {\n  const { variant } = useContext(ToolbarContext)\n  return (\n    <ToolbarPrimitive.Button\n      data-slot=\"toolbar-button\"\n      className={cn(ITEM_BASE_CLASS, ITEM_SHAPE_CLASS[variant], IDLE_CLASS, \"hover:bg-accent\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ToolbarLink({ className, ...props }: React.ComponentProps<typeof ToolbarPrimitive.Link>) {\n  const { variant } = useContext(ToolbarContext)\n  return (\n    <ToolbarPrimitive.Link\n      data-slot=\"toolbar-link\"\n      className={cn(ITEM_BASE_CLASS, ITEM_SHAPE_CLASS[variant], \"font-normal text-foreground underline decoration-current/40 underline-offset-4\", className)}\n      {...props}\n    />\n  )\n}\n\ntype GroupProps = React.ComponentProps<typeof ToolbarPrimitive.ToggleGroup>\n\nfunction toPressed(value: string | readonly string[] | undefined): readonly string[] {\n  if (value === undefined || value === \"\") return []\n  return typeof value === \"string\" ? [value] : value\n}\n\nfunction ToolbarToggleGroup({ className, value, defaultValue, onValueChange, type, ...props }: GroupProps) {\n  const { variant, effect } = useContext(ToolbarContext)\n  const [uncontrolled, setUncontrolled] = useState<string | string[] | undefined>(defaultValue)\n  const pressed = toPressed(value ?? uncontrolled)\n  // Only one thing can be chosen, so only a single group has one highlight to slide. Several would have to split.\n  const context = useMemo(() => ({ shared: effect === \"spring\" && type === \"single\", pressed }), [effect, type, pressed])\n  const shared = {\n    \"data-slot\": \"toolbar-toggle-group\",\n    className: cn(\"flex shrink-0 items-center gap-1\", variant === \"vertical\" && \"flex-col\", className),\n  }\n  const report = (next: string | string[]) => {\n    setUncontrolled(next)\n    ;(onValueChange as ((value: string | string[]) => void) | undefined)?.(next)\n  }\n  return (\n    <ToggleGroupContext.Provider value={context}>\n      {type === \"multiple\" ? (\n        // Radix gives this group the toolbar role, which would announce a toolbar inside the toolbar.\n        <ToolbarPrimitive.ToggleGroup {...(props as object)} {...shared} role=\"group\" type=\"multiple\" value={pressed as string[]} onValueChange={report} />\n      ) : (\n        <ToolbarPrimitive.ToggleGroup {...(props as object)} {...shared} type=\"single\" value={pressed[0] ?? \"\"} onValueChange={report} />\n      )}\n    </ToggleGroupContext.Provider>\n  )\n}\n\nfunction ToolbarToggleItem({ className, children, value, ...props }: React.ComponentProps<typeof ToolbarPrimitive.ToggleItem>) {\n  const { variant } = useContext(ToolbarContext)\n  const { shared, pressed } = useContext(ToggleGroupContext)\n  const reduceMotion = useReducedMotion() ?? false\n  return (\n    <ToolbarPrimitive.ToggleItem\n      data-slot=\"toolbar-toggle-item\"\n      value={value}\n      className={cn(ITEM_BASE_CLASS, ITEM_SHAPE_CLASS[variant], IDLE_CLASS, \"hover:bg-accent\", shared ? PRESSED_SPRING_CLASS : PRESSED_CLASS, className)}\n      {...props}\n    >\n      {shared && pressed.includes(value) ? (\n        <motion.span\n          aria-hidden=\"true\"\n          data-slot=\"toolbar-highlight\"\n          data-reduced={reduceMotion ? \"true\" : \"false\"}\n          layoutId={reduceMotion ? undefined : \"toolbar-highlight\"}\n          transition={SPRING_DEFAULT}\n          className={cn(\"absolute inset-0 -z-10 bg-mise-soft\", ITEM_SHAPE_CLASS[variant])}\n        />\n      ) : null}\n      {children}\n    </ToolbarPrimitive.ToggleItem>\n  )\n}\n\nexport {\n  Toolbar,\n  ToolbarGroup,\n  ToolbarSeparator,\n  ToolbarButton,\n  ToolbarLink,\n  ToolbarToggleGroup,\n  ToolbarToggleItem,\n  type ToolbarProps,\n  type ToolbarVariant,\n  type ToolbarEffect,\n  type ToolbarOverflow,\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}