{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "shortcuts-dialog",
  "title": "Shortcuts Dialog",
  "type": "registry:block",
  "description": "Linear-style keyboard reference sheet: a '?' dialog listing shortcut groups (label, description, Kbd combo) in a responsive two-column grid. Ships a built-in trigger button, controlled/uncontrolled open, custom groups via prop, and an Esc footer hint. Display only — wire real handlers in your app.",
  "categories": [
    "layout",
    "dashboard",
    "navigation"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/blocks/shortcuts-dialog/shortcuts-dialog.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { Keyboard } from 'lucide'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { icon } from '../../lib/icon'\n// Side-effect imports: register the composed elements for standalone use\n// (consumers who install only this block still get working primitives).\nimport '../../components/button/button'\nimport '../../components/dialog/dialog'\nimport '../../components/kbd/kbd'\n\nexport interface Shortcut {\n  keys: string[]\n  description: string\n}\n\nexport interface ShortcutGroup {\n  label: string\n  shortcuts: Shortcut[]\n}\n\nconst defaultGroups: ShortcutGroup[] = [\n  {\n    label: 'Navigation',\n    shortcuts: [\n      { keys: ['G', 'I'], description: 'Go to inbox' },\n      { keys: ['G', 'D'], description: 'Go to drafts' },\n      { keys: ['⌘', 'K'], description: 'Open command menu' },\n      { keys: ['⌥', '↑', '↓'], description: 'Move between items' },\n    ],\n  },\n  {\n    label: 'Actions',\n    shortcuts: [\n      { keys: ['C'], description: 'Compose new' },\n      { keys: ['/'], description: 'Focus search' },\n      { keys: ['⌘', 'S'], description: 'Save changes' },\n      { keys: ['E'], description: 'Archive selection' },\n    ],\n  },\n  {\n    label: 'General',\n    shortcuts: [\n      { keys: ['?'], description: 'Show this dialog' },\n      { keys: ['⇧', '?'], description: 'Show cheat sheet' },\n      { keys: ['Esc'], description: 'Close dialogs' },\n    ],\n  },\n]\n\n// Boolean props that default to `true`: `attr=\"false\"` turns them off.\nconst trueByDefault = { fromAttribute: (v: string | null) => v !== 'false' }\n\n/**\n * <uip-shortcuts-dialog> — the registry ShortcutsDialog block as a web component.\n *\n * Class strings are React's ShortcutsDialog verbatim. `groups` is a JS\n * property (same shape as React's prop, defaults to the built-in\n * Navigation/Actions/General groups). `trigger` / `footer` default to true\n * (`trigger=\"false\"` swaps in your own trigger via the `trigger` slot —\n * React's `triggerSlot`; `footer=\"false\"` drops the Esc hint). `open` is\n * React's controlled `open`; `onOpenChange` is the bubbling `open-change`\n * CustomEvent from the inner <uip-dialog> (detail: { open }).\n */\nexport class UipShortcutsDialog extends LitElement {\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  // :host display can't be a utility class (the host has no template of its own).\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties = {\n    groups: { attribute: false },\n    trigger: { type: Boolean, converter: trueByDefault },\n    footer: { type: Boolean, converter: trueByDefault },\n    open: { type: Boolean },\n  }\n\n  groups: ShortcutGroup[] = defaultGroups\n  trigger = true\n  footer = true\n  open = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'shortcuts-dialog')\n  }\n\n  render() {\n    return html`\n      <uip-dialog\n        heading=\"Keyboard shortcuts\"\n        description=\"Move faster without leaving the keyboard.\"\n        class=\"[&::part(content)]:max-w-lg\"\n        ?open=${this.open}\n        @open-change=${(e: CustomEvent<{ open: boolean }>) => {\n          this.open = e.detail.open\n        }}\n      >\n        ${this.trigger\n          ? html`<uip-button slot=\"trigger\" variant=\"outline\" size=\"sm\">\n              ${icon(Keyboard, 'keyboard')} Shortcuts <uip-kbd>?</uip-kbd>\n            </uip-button>`\n          : html`<slot name=\"trigger\" slot=\"trigger\"></slot>`}\n        <div class=\"grid max-h-[60vh] gap-x-8 gap-y-6 overflow-y-auto pr-1 sm:grid-cols-2\">\n          ${this.groups.map(\n            (group) => html`\n              <div class=\"space-y-2\">\n                <p class=\"text-muted-foreground text-xs font-medium tracking-widest uppercase\">${group.label}</p>\n                <ul class=\"divide-y\">\n                  ${group.shortcuts.map(\n                    (shortcut) => html`\n                      <li class=\"flex items-center justify-between gap-4 py-1.5\">\n                        <span class=\"text-sm\">${shortcut.description}</span>\n                        <span class=\"flex shrink-0 items-center gap-1\">\n                          ${shortcut.keys.map((key) => html`<uip-kbd>${key}</uip-kbd>`)}\n                        </span>\n                      </li>\n                    `,\n                  )}\n                </ul>\n              </div>\n            `,\n          )}\n        </div>\n        ${this.footer\n          ? html`<div slot=\"footer\" class=\"flex w-full justify-start\">\n              <p class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n                Press <uip-kbd>Esc</uip-kbd> to close\n              </p>\n            </div>`\n          : nothing}\n      </uip-dialog>\n    `\n  }\n}\n\ncustomElements.get('uip-shortcuts-dialog') || customElements.define('uip-shortcuts-dialog', UipShortcutsDialog)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-shortcuts-dialog': UipShortcutsDialog\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/blocks/shortcuts-dialog/shortcuts-dialog.ts"
    }
  ],
  "dependencies": [
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/button.json",
    "https://uipkge.dev/r/lit/dialog.json",
    "https://uipkge.dev/r/lit/kbd.json",
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
