{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "shortcuts-dialog",
  "title": "Shortcuts Dialog",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/shortcuts-dialog/shortcuts-dialog.component.ts",
      "content": "// Linear-style keyboard reference sheet: a '?' dialog listing shortcut groups (label,\n// description, Kbd combo) in a responsive two-column grid. Ships a built-in trigger button,\n// controlled/uncontrolled open, custom groups via input, and an Esc footer hint.\n// Display only -- wire real handlers in your app. Port of the React block 1:1.\nimport {\n  ChangeDetectionStrategy,\n  Component,\n  EventEmitter,\n  Input,\n  Output,\n  booleanAttribute,\n  signal,\n} from '@angular/core'\nimport { Keyboard, LucideAngularModule } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiDialogComponent,\n  UiDialogContentComponent,\n  UiDialogDescriptionComponent,\n  UiDialogFooterComponent,\n  UiDialogHeaderComponent,\n  UiDialogTitleComponent,\n  UiDialogTriggerComponent,\n} from '@/ui/dialog/dialog.component'\nimport { UiKbdComponent } from '@/ui/kbd/kbd.component'\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@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-shortcuts-dialog, [ui-shortcuts-dialog]',\n  standalone: true,\n  // React renders the Dialog root itself (no element): the host stays out of layout.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiButtonComponent,\n    UiDialogComponent,\n    UiDialogContentComponent,\n    UiDialogDescriptionComponent,\n    UiDialogFooterComponent,\n    UiDialogHeaderComponent,\n    UiDialogTitleComponent,\n    UiDialogTriggerComponent,\n    UiKbdComponent,\n  ],\n  template: `\n    <ui-dialog [open]=\"dialogOpen\" (openChange)=\"onOpenChange($event)\">\n      <div data-slot=\"shortcuts-dialog\" class=\"contents\">\n        @if (trigger) {\n          <button type=\"button\" ui-button ui-dialog-trigger variant=\"outline\" size=\"sm\">\n            <lucide-icon [img]=\"Keyboard\" aria-hidden=\"true\" />\n            Shortcuts\n            <span ui-kbd>?</span>\n          </button>\n        } @else {\n          <!-- Custom trigger: projected content cannot inject the dialog (Angular resolves\n               projected DI at the declaration site), so clicks are wired here instead.\n               Project any button -- no ui-dialog-trigger needed. -->\n          <span class=\"contents\" (click)=\"openDialog()\">\n            <ng-content />\n          </span>\n        }\n        <ui-dialog-content [class]=\"contentClass\">\n          <ui-dialog-header>\n            <h2 ui-dialog-title>Keyboard shortcuts</h2>\n            <p ui-dialog-description>Move faster without leaving the keyboard.</p>\n          </ui-dialog-header>\n          <div class=\"grid max-h-[60vh] gap-x-8 gap-y-6 overflow-y-auto pr-1 sm:grid-cols-2\">\n            @for (group of groups; track group.label) {\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                  @for (shortcut of group.shortcuts; track shortcut.description) {\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                        @for (key of shortcut.keys; track key) {\n                          <span ui-kbd>{{ key }}</span>\n                        }\n                      </span>\n                    </li>\n                  }\n                </ul>\n              </div>\n            }\n          </div>\n          @if (footer) {\n            <ui-dialog-footer class=\"justify-start\">\n              <p class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n                Press <span ui-kbd>Esc</span> to close\n              </p>\n            </ui-dialog-footer>\n          }\n        </ui-dialog-content>\n      </div>\n    </ui-dialog>\n  `,\n})\nexport class UiShortcutsDialogComponent {\n  /** Shortcut sections to render. Defaults to the Navigation / Actions / General set. */\n  @Input() groups: ShortcutGroup[] = defaultGroups\n  /** Render the built-in trigger button. Set false to project your own trigger (clicks open the dialog). */\n  @Input({ transform: booleanAttribute }) trigger = true\n  /** Show the 'Press Esc to close' footer hint. */\n  @Input({ transform: booleanAttribute }) footer = true\n  /** Controlled open state (pair with openChange); omit for uncontrolled. */\n  @Input() set open(v: boolean | undefined) {\n    this.controlledOpen.set(v)\n  }\n  @Output() openChange = new EventEmitter<boolean>()\n  @Input('class') className?: string\n\n  private readonly controlledOpen = signal<boolean | undefined>(undefined)\n  private readonly internalOpen = signal(false)\n\n  protected readonly Keyboard = Keyboard\n\n  get dialogOpen(): boolean {\n    return this.controlledOpen() ?? this.internalOpen()\n  }\n\n  get contentClass(): string {\n    return cn('max-w-lg', this.className)\n  }\n\n  onOpenChange(value: boolean): void {\n    this.internalOpen.set(value)\n    this.openChange.emit(value)\n  }\n\n  /** Custom-trigger path: the projected button has no trigger directive, so open directly. */\n  openDialog(): void {\n    this.onOpenChange(true)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/shortcuts-dialog/shortcuts-dialog.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/shortcuts-dialog/index.ts",
      "content": "export { UiShortcutsDialogComponent, type Shortcut, type ShortcutGroup } from './shortcuts-dialog.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/shortcuts-dialog/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/dialog.json",
    "https://uipkge.dev/r/angular/kbd.json"
  ],
  "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 input, and an Esc footer hint. Display only — wire real handlers in your app.",
  "categories": [
    "layout",
    "dashboard",
    "navigation"
  ]
}