{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-grid-keyboard-shortcuts",
  "title": "Data Grid Keyboard Shortcuts",
  "description": "A keyboard shortcuts dialog component for the data grid showing all available shortcuts",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "dialog",
    "input",
    "kbd",
    "separator"
  ],
  "files": [
    {
      "path": "src/components/data-grid/data-grid-keyboard-shortcuts.tsx",
      "content": "\"use client\";\n\nimport { SearchIcon, XIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\";\nimport { useDirection } from \"@/components/ui/direction\";\nimport { Input } from \"@/components/ui/input\";\nimport { Kbd, KbdGroup } from \"@/components/ui/kbd\";\nimport { Separator } from \"@/components/ui/separator\";\n\nconst SHORTCUT_KEY = \"/\";\n\ninterface ShortcutGroup {\n  title: string;\n  shortcuts: Array<{\n    keys: string[];\n    description: string;\n  }>;\n}\n\ninterface DataGridKeyboardShortcutsProps {\n  enableSearch?: boolean;\n  enableUndoRedo?: boolean;\n  enablePaste?: boolean;\n  enableRowAdd?: boolean;\n  enableRowsDelete?: boolean;\n}\n\nexport const DataGridKeyboardShortcuts = React.memo(\n  DataGridKeyboardShortcutsImpl,\n  (prev, next) => {\n    return (\n      prev.enableSearch === next.enableSearch &&\n      prev.enableUndoRedo === next.enableUndoRedo &&\n      prev.enablePaste === next.enablePaste &&\n      prev.enableRowAdd === next.enableRowAdd &&\n      prev.enableRowsDelete === next.enableRowsDelete\n    );\n  },\n);\n\nfunction DataGridKeyboardShortcutsImpl({\n  enableSearch = false,\n  enableUndoRedo = false,\n  enablePaste = false,\n  enableRowAdd = false,\n  enableRowsDelete = false,\n}: DataGridKeyboardShortcutsProps) {\n  const dir = useDirection();\n  const [open, setOpen] = React.useState(false);\n  const [input, setInput] = React.useState(\"\");\n  const inputRef = React.useRef<HTMLInputElement>(null);\n\n  const isMac =\n    typeof navigator !== \"undefined\"\n      ? /Mac|iPhone|iPad|iPod/.test(navigator.userAgent)\n      : false;\n\n  const modKey = isMac ? \"⌘\" : \"Ctrl\";\n\n  const onOpenChange = React.useCallback((isOpen: boolean) => {\n    setOpen(isOpen);\n    if (!isOpen) {\n      setInput(\"\");\n    }\n  }, []);\n\n  const onOpenAutoFocus = React.useCallback((event: Event) => {\n    event.preventDefault();\n    inputRef.current?.focus();\n  }, []);\n\n  const onInputChange = React.useCallback(\n    (event: React.ChangeEvent<HTMLInputElement>) => {\n      setInput(event.target.value);\n    },\n    [],\n  );\n\n  const shortcutGroups: ShortcutGroup[] = React.useMemo(\n    () => [\n      {\n        title: \"Navigation\",\n        shortcuts: [\n          {\n            keys: [\"↑\", \"↓\", \"←\", \"→\"],\n            description: \"Navigate between cells\",\n          },\n          {\n            keys: [\"Tab\"],\n            description: \"Move to next cell\",\n          },\n          {\n            keys: [\"Shift\", \"Tab\"],\n            description: \"Move to previous cell\",\n          },\n          {\n            keys: [\"Home\"],\n            description: \"Move to first column\",\n          },\n          {\n            keys: [\"End\"],\n            description: \"Move to last column\",\n          },\n          {\n            keys: [modKey, \"↑\"],\n            description: \"Move to first row (same column)\",\n          },\n          {\n            keys: [modKey, \"↓\"],\n            description: \"Move to last row (same column)\",\n          },\n          {\n            keys: [modKey, \"←\"],\n            description: \"Move to first column (same row)\",\n          },\n          {\n            keys: [modKey, \"→\"],\n            description: \"Move to last column (same row)\",\n          },\n          {\n            keys: [modKey, \"Home\"],\n            description: \"Move to first cell\",\n          },\n          {\n            keys: [modKey, \"End\"],\n            description: \"Move to last cell\",\n          },\n          {\n            keys: [\"PgUp\"],\n            description: \"Move up one page\",\n          },\n          {\n            keys: [\"PgDn\"],\n            description: \"Move down one page\",\n          },\n          {\n            keys: [\"⌥\", \"↑\"],\n            description: \"Scroll up one page\",\n          },\n          {\n            keys: [\"⌥\", \"↓\"],\n            description: \"Scroll down one page\",\n          },\n          {\n            keys: [\"⌥\", \"PgUp\"],\n            description: \"Scroll left one page of columns\",\n          },\n          {\n            keys: [\"⌥\", \"PgDn\"],\n            description: \"Scroll right one page of columns\",\n          },\n        ],\n      },\n      {\n        title: \"Selection\",\n        shortcuts: [\n          {\n            keys: [\"Shift\", \"↑↓←→\"],\n            description: \"Extend selection\",\n          },\n          {\n            keys: [modKey, \"Shift\", \"↑\"],\n            description: \"Select to top of table\",\n          },\n          {\n            keys: [modKey, \"Shift\", \"↓\"],\n            description: \"Select to bottom of table\",\n          },\n          {\n            keys: [modKey, \"Shift\", \"←\"],\n            description: \"Select to first column\",\n          },\n          {\n            keys: [modKey, \"Shift\", \"→\"],\n            description: \"Select to last column\",\n          },\n          {\n            keys: [modKey, \"A\"],\n            description: \"Select all cells\",\n          },\n          {\n            keys: [modKey, \"Click\"],\n            description: \"Toggle cell selection\",\n          },\n          {\n            keys: [\"Shift\", \"Click\"],\n            description: \"Select range\",\n          },\n          {\n            keys: [\"Esc\"],\n            description: \"Clear selection\",\n          },\n        ],\n      },\n      {\n        title: \"Editing\",\n        shortcuts: [\n          {\n            keys: [\"Enter\"],\n            description: \"Start editing cell\",\n          },\n          {\n            keys: [\"F2\"],\n            description: \"Start editing cell\",\n          },\n          {\n            keys: [\"Double Click\"],\n            description: \"Start editing cell\",\n          },\n          ...(enableRowAdd\n            ? [\n                {\n                  keys: [\"Shift\", \"Enter\"],\n                  description: \"Insert row below\",\n                },\n              ]\n            : []),\n          {\n            keys: [modKey, \"C\"],\n            description: \"Copy selected cells\",\n          },\n          {\n            keys: [modKey, \"X\"],\n            description: \"Cut selected cells\",\n          },\n          ...(enablePaste\n            ? [\n                {\n                  keys: [modKey, \"V\"],\n                  description: \"Paste cells\",\n                },\n              ]\n            : []),\n          {\n            keys: [\"Delete\"],\n            description: \"Clear selected cells\",\n          },\n          {\n            keys: [\"Backspace\"],\n            description: \"Clear selected cells\",\n          },\n          ...(enableRowsDelete\n            ? [\n                {\n                  keys: [modKey, \"Backspace\"],\n                  description: \"Delete selected rows\",\n                },\n                {\n                  keys: [modKey, \"Delete\"],\n                  description: \"Delete selected rows\",\n                },\n              ]\n            : []),\n          ...(enableUndoRedo\n            ? [\n                {\n                  keys: [modKey, \"Z\"],\n                  description: \"Undo last action\",\n                },\n                {\n                  keys: [modKey, \"Shift\", \"Z\"],\n                  description: \"Redo last action\",\n                },\n              ]\n            : []),\n        ],\n      },\n      ...(enableSearch\n        ? [\n            {\n              title: \"Search\",\n              shortcuts: [\n                {\n                  keys: [modKey, \"F\"],\n                  description: \"Open search\",\n                },\n                {\n                  keys: [\"Enter\"],\n                  description: \"Next match\",\n                },\n                {\n                  keys: [\"Shift\", \"Enter\"],\n                  description: \"Previous match\",\n                },\n                {\n                  keys: [\"Esc\"],\n                  description: \"Close search\",\n                },\n              ],\n            },\n          ]\n        : []),\n      {\n        title: \"Filtering\",\n        shortcuts: [\n          {\n            keys: [modKey, \"Shift\", \"F\"],\n            description: \"Toggle the filter menu\",\n          },\n          {\n            keys: [\"Backspace\"],\n            description: \"Remove filter (when focused)\",\n          },\n          {\n            keys: [\"Delete\"],\n            description: \"Remove filter (when focused)\",\n          },\n        ],\n      },\n      {\n        title: \"Sorting\",\n        shortcuts: [\n          {\n            keys: [modKey, \"Shift\", \"S\"],\n            description: \"Toggle the sort menu\",\n          },\n          {\n            keys: [\"Backspace\"],\n            description: \"Remove sort (when focused)\",\n          },\n          {\n            keys: [\"Delete\"],\n            description: \"Remove sort (when focused)\",\n          },\n        ],\n      },\n      {\n        title: \"General\",\n        shortcuts: [\n          {\n            keys: [modKey, \"/\"],\n            description: \"Show keyboard shortcuts\",\n          },\n        ],\n      },\n    ],\n    [\n      modKey,\n      enableSearch,\n      enableUndoRedo,\n      enablePaste,\n      enableRowAdd,\n      enableRowsDelete,\n    ],\n  );\n\n  const filteredGroups = React.useMemo(() => {\n    if (!input.trim()) return shortcutGroups;\n\n    const query = input.toLowerCase();\n    return shortcutGroups\n      .map((group) => ({\n        ...group,\n        shortcuts: group.shortcuts.filter(\n          (shortcut) =>\n            shortcut.description.toLowerCase().includes(query) ||\n            shortcut.keys.some((key) => key.toLowerCase().includes(query)),\n        ),\n      }))\n      .filter((group) => group.shortcuts.length > 0);\n  }, [shortcutGroups, input]);\n\n  React.useEffect(() => {\n    function onKeyDown(event: KeyboardEvent) {\n      if ((event.ctrlKey || event.metaKey) && event.key === SHORTCUT_KEY) {\n        event.preventDefault();\n        setOpen(true);\n      }\n    }\n\n    window.addEventListener(\"keydown\", onKeyDown);\n    return () => {\n      window.removeEventListener(\"keydown\", onKeyDown);\n    };\n  }, []);\n\n  return (\n    <Dialog open={open} onOpenChange={onOpenChange}>\n      <DialogContent\n        dir={dir}\n        className=\"max-w-2xl px-0\"\n        onOpenAutoFocus={onOpenAutoFocus}\n        showCloseButton={false}\n      >\n        <DialogClose className=\"absolute end-6 top-6\" asChild>\n          <Button variant=\"ghost\" size=\"icon\" className=\"size-6\">\n            <XIcon />\n          </Button>\n        </DialogClose>\n        <DialogHeader className=\"px-6\">\n          <DialogTitle>Keyboard shortcuts</DialogTitle>\n          <DialogDescription className=\"sr-only\">\n            Use these keyboard shortcuts to navigate and interact with the data\n            grid more efficiently.\n          </DialogDescription>\n        </DialogHeader>\n        <div className=\"px-6\">\n          <div className=\"relative\">\n            <SearchIcon className=\"absolute start-3 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n            <Input\n              ref={inputRef}\n              placeholder=\"Search shortcuts...\"\n              className=\"h-8 ps-8\"\n              value={input}\n              onChange={onInputChange}\n            />\n          </div>\n        </div>\n        <Separator className=\"mx-auto data-[orientation=horizontal]:w-[calc(100%-(--spacing(12)))]\" />\n        <div className=\"h-[40vh] overflow-y-auto px-6\">\n          {filteredGroups.length === 0 ? (\n            <div className=\"flex h-full flex-col items-center justify-center gap-3 text-center\">\n              <div className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground\">\n                <SearchIcon className=\"pointer-events-none size-6\" />\n              </div>\n              <div className=\"flex flex-col gap-1\">\n                <div className=\"font-medium text-lg tracking-tight\">\n                  No shortcuts found\n                </div>\n                <p className=\"text-muted-foreground text-sm\">\n                  Try searching for a different term.\n                </p>\n              </div>\n            </div>\n          ) : (\n            <div className=\"flex flex-col gap-6\">\n              {filteredGroups.map((shortcutGroup) => (\n                <div key={shortcutGroup.title} className=\"flex flex-col gap-2\">\n                  <h3 className=\"font-semibold text-foreground text-sm\">\n                    {shortcutGroup.title}\n                  </h3>\n                  <div className=\"divide-y divide-border rounded-md border\">\n                    {shortcutGroup.shortcuts.map((shortcut, index) => (\n                      <ShortcutCard\n                        key={index}\n                        keys={shortcut.keys}\n                        description={shortcut.description}\n                      />\n                    ))}\n                  </div>\n                </div>\n              ))}\n            </div>\n          )}\n        </div>\n      </DialogContent>\n    </Dialog>\n  );\n}\n\nfunction ShortcutCard({\n  keys,\n  description,\n}: ShortcutGroup[\"shortcuts\"][number]) {\n  return (\n    <div className=\"flex items-center gap-4 px-3 py-2\">\n      <span className=\"flex-1 text-sm\">{description}</span>\n      <KbdGroup className=\"shrink-0\">\n        {keys.map((key, index) => (\n          <React.Fragment key={key}>\n            {index > 0 && (\n              <span className=\"text-muted-foreground text-xs\">+</span>\n            )}\n            <Kbd>{key}</Kbd>\n          </React.Fragment>\n        ))}\n      </KbdGroup>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "src/components/data-grid/data-grid-keyboard-shortcuts.tsx"
    },
    {
      "path": "src/components/ui/direction.tsx",
      "content": "\"use client\";\n\nimport { Direction } from \"radix-ui\";\nimport type * as React from \"react\";\n\nfunction DirectionProvider({\n  dir,\n  direction,\n  children,\n}: React.ComponentProps<typeof Direction.DirectionProvider> & {\n  direction?: React.ComponentProps<typeof Direction.DirectionProvider>[\"dir\"];\n}) {\n  return (\n    <Direction.DirectionProvider dir={direction ?? dir}>\n      {children}\n    </Direction.DirectionProvider>\n  );\n}\n\nconst useDirection = Direction.useDirection;\n\nexport { DirectionProvider, useDirection };\n",
      "type": "registry:ui",
      "target": "src/components/ui/direction.tsx"
    }
  ],
  "type": "registry:component"
}