{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "core-toggle-lane",
  "type": "registry:ui",
  "title": "Toggle Lane",
  "description": "A row of on/off cells for choosing several values from a range, such as the notes of a chord on a keyboard strip or the semitones of a set, on Base UI's ToggleGroup with multiple selection: one Tab stop, arrow keys, Home and End to move, Space or Enter to toggle, aria-pressed and a label on every cell. Cells are at least 24px wide and as wide as the face's small control height (44px on a touch face), black keys follow the accidental pitch classes unless told otherwise, markers under cells start groups, on cells use the neutral --uikit-value colour, and the lane scrolls sideways when it overflows. The selection maths sits in a pure core beside it.",
  "dependencies": [
    "@base-ui/react",
    "maruaudio@>=0.0.3 <0.1.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/ui/toggle-lane.tsx",
      "content": "/**\n * Source: https://maru.audio/ui-kit/toggle-lane\n * Copyright © [Maru](https://maru.audio)\n *\n * Maru UI Kit — https://maru.audio/ui-kit\n *\n * Installed from the Maru UI registry. This file is yours now: edit it\n * freely, it will not be overwritten unless you re-run the add command.\n *\n * Keep this item’s companion files and CSS imports together. Dependencies\n * declared by the registry are installed with the source.\n */\n/**\n * ToggleLane — a row of on/off cells for picking several values out of a\n * range: the notes of a chord on a keyboard strip, the semitones of an\n * interval set, the steps of a pattern.\n *\n *   <ToggleLane\n *     aria-label=\"Notes\"\n *     items={[{ value: 60, label: \"C4\", on: true, marker: \"C4\" }, { value: 61, label: \"C#4\", on: false }, …]}\n *     onToggle={(value) => toggle(value)}\n *   />\n *\n * Built on Base UI's ToggleGroup (`multiple`), so the lane is one Tab stop:\n * the arrow keys, Home and End move between cells (roving tabindex), Space or\n * Enter toggles the focused cell, and every cell reports `aria-pressed` and\n * carries its `label` as its accessible name. Controlled: `onToggle(value)`\n * asks for a cell to flip, and the lane shows whatever `items` say.\n *\n * Cells are at least 24px wide, and as wide as the face's small control\n * height (`--uikit-control-h-sm`, 44px on a touch face); `cellWidth` asks for\n * wider ones. They are as tall as the large control height. The lane scrolls\n * sideways when it does not fit. Black keys (accidental pitch classes unless\n * `black` says otherwise) are drawn darker; a cell's `marker` is printed under\n * it and starts a group.\n *\n * Colours: an off cell has a muted outline (5.3:1 against the surface with\n * the default tokens) and an on cell is filled with the neutral value colour\n * `--uikit-value` (9.7:1 against an off cell), not the accent. Markers are\n * muted text at 11px (5.3:1).\n */\n\nimport {\n  createElement,\n  type CSSProperties,\n  type JSX,\n  type ReactElement,\n} from \"react\"\nimport { Toggle } from \"@base-ui/react/toggle\"\nimport { ToggleGroup } from \"@base-ui/react/toggle-group\"\nimport { token } from \"maruaudio/ui/shared/theme\"\nimport {\n  isBlackLaneItem,\n  laneCellWidth,\n  pressedLaneValues,\n  startsLaneGroup,\n  type ToggleLaneItem,\n} from \"maruaudio/ui/shared/toggle-lane-math\"\n\nexport type { ToggleLaneItem } from \"maruaudio/ui/shared/toggle-lane-math\"\n\nexport interface ToggleLaneProps {\n  readonly items: readonly ToggleLaneItem[]\n  /** A cell was pressed: flip `value`. */\n  readonly onToggle: (value: number) => void\n  /** The lane's name: \"Notes\", \"Semitones above the root\". */\n  readonly \"aria-label\": string\n  /** Minimum cell width in pixels; never below 24 or the face's small control height. */\n  readonly cellWidth?: number\n  readonly disabled?: boolean\n  readonly className?: string\n  readonly style?: CSSProperties\n}\n\nconst MUTED = \"var(--uikit-text-muted, rgba(232, 234, 240, 0.55))\"\nconst VALUE = \"var(--uikit-value, #cfd6df)\"\nconst CAP = \"var(--uikit-cap, #262b36)\"\nconst SURFACE = \"var(--uikit-surface, #14161d)\"\nconst FOCUS = \"var(--uikit-focus, var(--uikit-accent, #6366f1))\"\n\n/** The `href` React dedupes the lane stylesheet by. */\nexport const TOGGLE_LANE_STYLES_HREF = \"uikit-toggle-lane\"\n\n/** Cell colours for off, black, on, hover, focus and disabled. */\nexport const TOGGLE_LANE_STYLES = String.raw`\n[data-toggle-lane-cell]{background:${CAP};border-color:${MUTED}}\n[data-toggle-lane-cell][data-black]{background:color-mix(in srgb, ${SURFACE} 70%, #000)}\n[data-toggle-lane-cell][data-pressed]{background:${VALUE};border-color:${VALUE}}\n[data-toggle-lane-cell][data-pressed][data-black]{background:color-mix(in srgb, ${VALUE} 82%, #000);border-color:${VALUE}}\n@media (hover: hover){\n[data-toggle-lane-cell]:hover:not([data-disabled]){filter:brightness(1.25)}\n[data-toggle-lane-cell][data-pressed]:hover:not([data-disabled]){filter:brightness(.92)}\n}\n[data-toggle-lane-cell]:focus-visible{outline:2px solid ${FOCUS};outline-offset:1px;position:relative;z-index:1}\n[data-toggle-lane-cell][data-disabled]{opacity:.45;cursor:not-allowed}\n@media (prefers-reduced-motion: no-preference){\n[data-toggle-lane-cell]{transition:background-color 100ms ease-out,border-color 100ms ease-out,filter 100ms ease-out}\n}\n`.replace(/\\n/g, \"\")\n\nfunction ToggleLaneStyles(): ReactElement {\n  return createElement(\n    \"style\",\n    { href: TOGGLE_LANE_STYLES_HREF, precedence: \"default\" },\n    TOGGLE_LANE_STYLES\n  )\n}\n\n/** A lane of on/off cells with one Tab stop and arrow-key movement. */\nexport function ToggleLane({\n  items,\n  onToggle,\n  \"aria-label\": ariaLabel,\n  cellWidth,\n  disabled = false,\n  className,\n  style,\n}: ToggleLaneProps): JSX.Element {\n  const width = laneCellWidth(cellWidth)\n  const hasMarkers = items.some((item) => item.marker)\n  return (\n    <ToggleGroup\n      multiple\n      value={pressedLaneValues(items)}\n      disabled={disabled}\n      aria-label={ariaLabel}\n      data-toggle-lane=\"\"\n      className={className}\n      style={{\n        boxSizing: \"border-box\",\n        display: \"flex\",\n        alignItems: \"flex-start\",\n        gap: 2,\n        maxWidth: \"100%\",\n        minWidth: 0,\n        overflowX: \"auto\",\n        overflowY: \"hidden\",\n        // Room for the focus ring inside the scrolling box.\n        padding: 3,\n        fontFamily: token.font,\n        ...style,\n      }}\n    >\n      <ToggleLaneStyles />\n      {items.map((item, index) => (\n        <span\n          key={item.value}\n          data-toggle-lane-slot=\"\"\n          style={{\n            display: \"inline-flex\",\n            flexDirection: \"column\",\n            alignItems: \"stretch\",\n            gap: 2,\n            flex: \"none\",\n            marginLeft: startsLaneGroup(items, index) ? 4 : 0,\n          }}\n        >\n          <Toggle\n            value={String(item.value)}\n            aria-label={item.label}\n            onPressedChange={() => onToggle(item.value)}\n            data-toggle-lane-cell=\"\"\n            data-black={isBlackLaneItem(item) ? \"\" : undefined}\n            style={{\n              boxSizing: \"border-box\",\n              display: \"block\",\n              width,\n              height: \"var(--uikit-control-h-lg, 32px)\",\n              padding: 0,\n              margin: 0,\n              borderWidth: 1,\n              borderStyle: \"solid\",\n              borderRadius: 3,\n              cursor: \"pointer\",\n              WebkitTapHighlightColor: \"transparent\",\n            }}\n          />\n          {hasMarkers ? (\n            <span\n              aria-hidden\n              data-toggle-lane-marker=\"\"\n              style={{\n                display: \"block\",\n                height: 13,\n                // Markers may be wider than a cell (\"C#4\"): let them spill.\n                width: 0,\n                overflow: \"visible\",\n                color: MUTED,\n                fontSize: 11,\n                lineHeight: \"13px\",\n                whiteSpace: \"nowrap\",\n              }}\n            >\n              {item.marker ?? \"\"}\n            </span>\n          ) : null}\n        </span>\n      ))}\n    </ToggleGroup>\n  )\n}\n",
      "type": "registry:component",
      "target": "@ui/toggle-lane.tsx"
    }
  ]
}
