{"$schema":"https:\/\/ui.particle.academy\/schema\/registry-item.json","name":"time-grid","type":"registry:ui","title":"TimeGrid","description":"TimeGrid from react-fancy","package":"react-fancy","dependencies":[],"registryDependencies":[],"files":[{"path":"components\/fancy\/time-grid\/TimeGrid.tsx","content":"import { forwardRef, useState } from \"react\";\r\nimport { cn } from \"..\/..\/utils\/cn\";\r\nimport type { TimeGridProps, TimeGridTone } from \".\/TimeGrid.types\";\r\n\r\nconst TONE_CLASSES: Record<TimeGridTone, string> = {\r\n  violet: \"bg-violet-500 hover:bg-violet-600\",\r\n  emerald: \"bg-emerald-500 hover:bg-emerald-600\",\r\n  sky: \"bg-sky-500 hover:bg-sky-600\",\r\n  rose: \"bg-rose-500 hover:bg-rose-600\",\r\n  amber: \"bg-amber-500 hover:bg-amber-600\",\r\n  indigo: \"bg-indigo-500 hover:bg-indigo-600\",\r\n  blue: \"bg-blue-500 hover:bg-blue-600\",\r\n  zinc: \"bg-zinc-500 hover:bg-zinc-600\",\r\n};\r\n\r\nconst OFF_CLASSES =\r\n  \"bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700\";\r\n\r\nexport const TimeGrid = forwardRef<HTMLDivElement, TimeGridProps>(\r\n  (\r\n    {\r\n      rows,\r\n      cols,\r\n      value,\r\n      onChange,\r\n      toneOn = \"violet\",\r\n      cellWidth = 20,\r\n      cellHeight = 16,\r\n      sparseColLabels = true,\r\n      toggleStripsOnHeaderClick = true,\r\n      ariaCell,\r\n      cellId,\r\n      className,\r\n    },\r\n    ref,\r\n  ) => {\r\n    const [drag, setDrag] = useState<boolean | null>(null);\r\n\r\n    const setCell = (r: number, c: number, v: boolean) => {\r\n      if (value[r]?.[c] === v) return;\r\n      const next = value.map((row) => row.slice());\r\n      next[r][c] = v;\r\n      onChange(next);\r\n    };\r\n\r\n    const toggleRow = (r: number) => {\r\n      if (!toggleStripsOnHeaderClick) return;\r\n      const all = value[r].every(Boolean);\r\n      const next = value.map((row) => row.slice());\r\n      for (let c = 0; c < cols.length; c++) next[r][c] = !all;\r\n      onChange(next);\r\n    };\r\n\r\n    const toggleCol = (c: number) => {\r\n      if (!toggleStripsOnHeaderClick) return;\r\n      const all = value.every((row) => row[c]);\r\n      const next = value.map((row) => row.slice());\r\n      for (let r = 0; r < rows.length; r++) next[r][c] = !all;\r\n      onChange(next);\r\n    };\r\n\r\n    const colStep = Math.max(1, Math.floor(cols.length \/ 6));\r\n    const onTone = TONE_CLASSES[toneOn];\r\n\r\n    return (\r\n      <div\r\n        ref={ref}\r\n        data-react-fancy-timegrid=\"\"\r\n        className={cn(\r\n          \"select-none overflow-x-auto\",\r\n          className,\r\n        )}\r\n        onMouseLeave={() => setDrag(null)}\r\n        onMouseUp={() => setDrag(null)}\r\n      >\r\n        <table className=\"text-[10px]\">\r\n          <thead>\r\n            <tr>\r\n              <th \/>\r\n              {cols.map((label, c) => (\r\n                <th\r\n                  key={c}\r\n                  onClick={() => toggleCol(c)}\r\n                  style={{ width: cellWidth }}\r\n                  data-react-fancy-timegrid-col={c}\r\n                  className={cn(\r\n                    \"text-zinc-400\",\r\n                    toggleStripsOnHeaderClick &&\r\n                      \"cursor-pointer hover:text-zinc-700 dark:hover:text-zinc-200\",\r\n                  )}\r\n                >\r\n                  {sparseColLabels ? (c % colStep === 0 ? label : \"\") : label}\r\n                <\/th>\r\n              ))}\r\n            <\/tr>\r\n          <\/thead>\r\n          <tbody>\r\n            {rows.map((label, r) => (\r\n              <tr key={r}>\r\n                <th\r\n                  onClick={() => toggleRow(r)}\r\n                  data-react-fancy-timegrid-row={r}\r\n                  className={cn(\r\n                    \"pr-1 text-right font-medium text-zinc-500\",\r\n                    toggleStripsOnHeaderClick &&\r\n                      \"cursor-pointer hover:text-zinc-800 dark:hover:text-zinc-200\",\r\n                  )}\r\n                >\r\n                  {label}\r\n                <\/th>\r\n                {cols.map((_, c) => {\r\n                  const on = value[r]?.[c] ?? false;\r\n                  return (\r\n                    <td key={c} className=\"p-px\">\r\n                      <button\r\n                        type=\"button\"\r\n                        onMouseDown={() => {\r\n                          const v = !on;\r\n                          setDrag(v);\r\n                          setCell(r, c, v);\r\n                        }}\r\n                        onMouseEnter={() => drag !== null && setCell(r, c, drag)}\r\n                        style={{ width: cellWidth, height: cellHeight }}\r\n                        data-react-fancy-timegrid-cell={\r\n                          cellId ? cellId(r, c) : `${r}:${c}`\r\n                        }\r\n                        aria-pressed={on}\r\n                        aria-label={\r\n                          ariaCell\r\n                            ? ariaCell(r, c, on)\r\n                            : `${rows[r]} ${cols[c]} ${on ? \"on\" : \"off\"}`\r\n                        }\r\n                        className={cn(\r\n                          \"block rounded-sm transition\",\r\n                          on ? onTone : OFF_CLASSES,\r\n                        )}\r\n                      \/>\r\n                    <\/td>\r\n                  );\r\n                })}\r\n              <\/tr>\r\n            ))}\r\n          <\/tbody>\r\n        <\/table>\r\n      <\/div>\r\n    );\r\n  },\r\n);\r\n\r\nTimeGrid.displayName = \"TimeGrid\";\r\n","type":"registry:ui","target":"components\/fancy\/time-grid\/TimeGrid.tsx"},{"path":"components\/fancy\/time-grid\/TimeGrid.types.ts","content":"\/**\r\n * Subset of palette colors the TimeGrid offers for the \"cell-on\" tone \u2014\r\n * small enough to keep the compiled CSS tight, broad enough to cover most\r\n * use cases.\r\n *\/\r\nexport type TimeGridTone =\r\n  | \"violet\"\r\n  | \"emerald\"\r\n  | \"sky\"\r\n  | \"rose\"\r\n  | \"amber\"\r\n  | \"indigo\"\r\n  | \"blue\"\r\n  | \"zinc\";\r\n\r\nexport interface TimeGridProps {\r\n  \/** Row labels, one per row in `value` (e.g. [\"Sun\",\"Mon\",\u2026]). *\/\r\n  rows: string[];\r\n  \/** Column labels, one per column in `value` (e.g. [\"0\",\"1\",\u2026,\"23\"]). *\/\r\n  cols: string[];\r\n  \/**\r\n   * Controlled cell state. Must be a rectangular `rows.length \u00d7 cols.length`\r\n   * matrix of booleans. The component never mutates it \u2014 every change\r\n   * produces a new array passed to `onChange`.\r\n   *\/\r\n  value: boolean[][];\r\n  \/** Called with the next matrix whenever a cell, row, or column toggles. *\/\r\n  onChange: (next: boolean[][]) => void;\r\n\r\n  \/** Cell-on tone. Default `\"violet\"`. *\/\r\n  toneOn?: TimeGridTone;\r\n  \/** Cell width in px. Default `20`. *\/\r\n  cellWidth?: number;\r\n  \/** Cell height in px. Default `16`. *\/\r\n  cellHeight?: number;\r\n  \/**\r\n   * If true (default), only every Nth column label is rendered to keep the\r\n   * top axis legible at small `cellWidth`. Set false to show all labels.\r\n   *\/\r\n  sparseColLabels?: boolean;\r\n  \/** If true (default), clicking a row\/column header toggles that strip. *\/\r\n  toggleStripsOnHeaderClick?: boolean;\r\n\r\n  \/**\r\n   * Optional accessible label per cell. Default produces\r\n   * `${rows[r]} ${cols[c]} on|off`. Override for richer context\r\n   * (e.g. include unit: `${rows[r]} ${cols[c]}:00 ${on ? \"on\" : \"off\"}`).\r\n   *\/\r\n  ariaCell?: (rowIdx: number, colIdx: number, on: boolean) => string;\r\n\r\n  \/**\r\n   * Stable per-cell identifier. Emitted as `data-react-fancy-timegrid-cell`\r\n   * on the button so MCP bridges can address a specific cell from a tool\r\n   * call without DOM walking. Default `${row}:${col}`.\r\n   *\/\r\n  cellId?: (rowIdx: number, colIdx: number) => string;\r\n\r\n  \/** Additional CSS classes on the outer scroll container. *\/\r\n  className?: string;\r\n}\r\n","type":"registry:ui","target":"components\/fancy\/time-grid\/TimeGrid.types.ts"},{"path":"components\/fancy\/time-grid\/index.ts","content":"export { TimeGrid } from \".\/TimeGrid\";\r\nexport type { TimeGridProps, TimeGridTone } from \".\/TimeGrid.types\";\r\n","type":"registry:ui","target":"components\/fancy\/time-grid\/index.ts"}]}