{
  "name": "lit-ui-inline-menu",
  "title": "lit-ui-inline-menu",
  "type": "registry:component",
  "description": "",
  "registryDependencies": [
    "https://unpkg.com/prosekit-registry/dist/r/lit-ui-button.json",
    "https://unpkg.com/prosekit-registry/dist/r/lit-ui-editor-context.json"
  ],
  "dependencies": [
    "@egoist/tailwindcss-icons",
    "@iconify-json/lucide",
    "@lit/context",
    "prosekit@^0.22.0"
  ],
  "files": [
    {
      "path": "registry/src/lit/ui/inline-menu/index.ts",
      "type": "registry:component",
      "target": "components/editor/ui/inline-menu/index.ts",
      "content": "export { registerLitEditorInlineMenu } from './inline-menu.ts'\n"
    },
    {
      "path": "registry/src/lit/ui/inline-menu/inline-menu.ts",
      "type": "registry:component",
      "target": "components/editor/ui/inline-menu/inline-menu.ts",
      "content": "import { ContextConsumer } from '@lit/context'\nimport { html, LitElement, nothing, type PropertyDeclaration, type PropertyValues } from 'lit'\nimport type { BasicExtension } from 'prosekit/basic'\nimport { defineUpdateHandler, type Editor } from 'prosekit/core'\nimport type { LinkAttrs } from 'prosekit/extensions/link'\nimport {\n  registerInlinePopoverPopupElement,\n  registerInlinePopoverPositionerElement,\n  registerInlinePopoverRootElement,\n  type OpenChangeEvent,\n} from 'prosekit/lit/inline-popover'\nimport type { EditorState } from 'prosekit/pm/state'\n\nimport { registerLitEditorButton } from '../button/index.ts'\nimport { editorContext } from '../editor-context.ts'\n\nfunction getInlineMenuItems(editor: Editor<BasicExtension>) {\n  return {\n    bold: editor.commands.toggleBold\n      ? {\n          isActive: editor.marks.bold.isActive(),\n          canExec: editor.commands.toggleBold.canExec(),\n          command: () => editor.commands.toggleBold(),\n        }\n      : undefined,\n    italic: editor.commands.toggleItalic\n      ? {\n          isActive: editor.marks.italic.isActive(),\n          canExec: editor.commands.toggleItalic.canExec(),\n          command: () => editor.commands.toggleItalic(),\n        }\n      : undefined,\n    underline: editor.commands.toggleUnderline\n      ? {\n          isActive: editor.marks.underline.isActive(),\n          canExec: editor.commands.toggleUnderline.canExec(),\n          command: () => editor.commands.toggleUnderline(),\n        }\n      : undefined,\n    strike: editor.commands.toggleStrike\n      ? {\n          isActive: editor.marks.strike.isActive(),\n          canExec: editor.commands.toggleStrike.canExec(),\n          command: () => editor.commands.toggleStrike(),\n        }\n      : undefined,\n    code: editor.commands.toggleCode\n      ? {\n          isActive: editor.marks.code.isActive(),\n          canExec: editor.commands.toggleCode.canExec(),\n          command: () => editor.commands.toggleCode(),\n        }\n      : undefined,\n    link: editor.commands.addLink\n      ? {\n          isActive: editor.marks.link.isActive(),\n          canExec: editor.commands.addLink.canExec({ href: '' }),\n          command: () => editor.commands.expandLink(),\n          currentLink: getCurrentLink(editor.state) || '',\n        }\n      : undefined,\n  }\n}\n\nfunction getCurrentLink(state: EditorState): string | undefined {\n  const { $from } = state.selection\n  const marks = $from.marksAcross($from)\n  if (!marks) {\n    return\n  }\n  for (const mark of marks) {\n    if (mark.type.name === 'link') {\n      return (mark.attrs as LinkAttrs).href\n    }\n  }\n}\n\nclass LitInlineMenu extends LitElement {\n  static override properties = {\n    linkMenuOpen: { state: true, attribute: false } satisfies PropertyDeclaration<boolean>,\n  }\n\n  private linkMenuOpen = false\n\n  private editorConsumer = new ContextConsumer(this, {\n    context: editorContext,\n    subscribe: true,\n  })\n\n  private removeUpdateExtension?: VoidFunction\n  private attachedEditor?: Editor\n\n  override createRenderRoot() {\n    return this\n  }\n\n  override connectedCallback() {\n    super.connectedCallback()\n    this.classList.add('contents')\n    this.attachEditorListener()\n  }\n\n  override disconnectedCallback() {\n    this.detachEditorListener()\n    super.disconnectedCallback()\n  }\n\n  override updated(changedProperties: PropertyValues) {\n    super.updated(changedProperties)\n    this.attachEditorListener()\n  }\n\n  private attachEditorListener() {\n    const editor = this.editorConsumer.value\n    if (editor === this.attachedEditor) return\n\n    this.detachEditorListener()\n    this.attachedEditor = editor\n\n    if (!editor) return\n\n    this.removeUpdateExtension = editor.use(defineUpdateHandler(() => this.requestUpdate()))\n  }\n\n  private detachEditorListener() {\n    this.removeUpdateExtension?.()\n    this.removeUpdateExtension = undefined\n    this.attachedEditor = undefined\n  }\n\n  private handleLinkUpdate(editor: Editor<BasicExtension>, href?: string) {\n    if (href) {\n      editor.commands.addLink({ href })\n    } else {\n      editor.commands.removeLink()\n    }\n\n    this.linkMenuOpen = false\n    editor.focus()\n  }\n\n  override render() {\n    const editor = this.editorConsumer.value as Editor<BasicExtension> | undefined\n    if (!editor) {\n      return nothing\n    }\n\n    const items = getInlineMenuItems(editor)\n\n    return html`\n      <prosekit-inline-popover-root\n        .editor=${editor}\n        @openChange=${(event: OpenChangeEvent) => {\n          if (!event.detail) {\n            this.linkMenuOpen = false\n          }\n        }}\n      >\n        <prosekit-inline-popover-positioner class=\"block overflow-visible w-min h-min z-50 ease-out transition-transform duration-100 motion-reduce:transition-none\">\n          <prosekit-inline-popover-popup\n            data-testid=\"inline-menu-main\"\n            class=\"box-border origin-(--transform-origin) transition-[opacity,scale] transition-discrete motion-reduce:transition-none data-[state=closed]:duration-150 data-[state=closed]:opacity-0 starting:opacity-0 data-[state=closed]:scale-95 starting:scale-95 duration-40 border border-gray-200 dark:border-gray-800 shadow-lg bg-[canvas] relative flex min-w-32 space-x-1 overflow-auto whitespace-nowrap rounded-lg p-1\"\n          >\n            ${\n              items.bold\n                ? html`\n                    <lit-editor-button\n                      .pressed=${items.bold.isActive}\n                      .disabled=${!items.bold.canExec}\n                      tooltip=\"Bold\"\n                      icon=\"i-lucide-bold size-5 block\"\n                      @click=${items.bold.command}\n                    ></lit-editor-button>\n                  `\n                : nothing\n            }\n            ${\n              items.italic\n                ? html`\n                    <lit-editor-button\n                      .pressed=${items.italic.isActive}\n                      .disabled=${!items.italic.canExec}\n                      tooltip=\"Italic\"\n                      icon=\"i-lucide-italic size-5 block\"\n                      @click=${items.italic.command}\n                    ></lit-editor-button>\n                  `\n                : nothing\n            }\n            ${\n              items.underline\n                ? html`\n                    <lit-editor-button\n                      .pressed=${items.underline.isActive}\n                      .disabled=${!items.underline.canExec}\n                      tooltip=\"Underline\"\n                      icon=\"i-lucide-underline size-5 block\"\n                      @click=${items.underline.command}\n                    ></lit-editor-button>\n                  `\n                : nothing\n            }\n            ${\n              items.strike\n                ? html`\n                    <lit-editor-button\n                      .pressed=${items.strike.isActive}\n                      .disabled=${!items.strike.canExec}\n                      tooltip=\"Strikethrough\"\n                      icon=\"i-lucide-strikethrough size-5 block\"\n                      @click=${items.strike.command}\n                    ></lit-editor-button>\n                  `\n                : nothing\n            }\n            ${\n              items.code\n                ? html`\n                    <lit-editor-button\n                      .pressed=${items.code.isActive}\n                      .disabled=${!items.code.canExec}\n                      tooltip=\"Code\"\n                      icon=\"i-lucide-code size-5 block\"\n                      @click=${items.code.command}\n                    ></lit-editor-button>\n                  `\n                : nothing\n            }\n            ${\n              items.link && items.link.canExec\n                ? html`\n                    <lit-editor-button\n                      .pressed=${items.link.isActive}\n                      tooltip=\"Link\"\n                      icon=\"i-lucide-link size-5 block\"\n                      @click=${() => {\n                        items.link?.command?.()\n                        this.linkMenuOpen = !this.linkMenuOpen\n                      }}\n                    ></lit-editor-button>\n                  `\n                : nothing\n            }\n          </prosekit-inline-popover-popup>\n        </prosekit-inline-popover-positioner>\n      </prosekit-inline-popover-root>\n\n      ${\n        items.link\n          ? html`\n              <prosekit-inline-popover-root\n                .editor=${editor}\n                .defaultOpen=${false}\n                .open=${this.linkMenuOpen}\n                @openChange=${(event: OpenChangeEvent) => {\n                  this.linkMenuOpen = event.detail\n                }}\n              >\n                <prosekit-inline-popover-positioner\n                  placement=\"bottom\"\n                  class=\"block overflow-visible w-min h-min z-50 ease-out transition-transform duration-100 motion-reduce:transition-none\"\n                >\n                  <prosekit-inline-popover-popup\n                    data-testid=\"inline-menu-link\"\n                    class=\"box-border origin-(--transform-origin) transition-[opacity,scale] transition-discrete motion-reduce:transition-none data-[state=closed]:duration-150 data-[state=closed]:opacity-0 starting:opacity-0 data-[state=closed]:scale-95 starting:scale-95 duration-40 border border-gray-200 dark:border-gray-800 shadow-lg bg-[canvas] relative flex flex-col w-xs rounded-lg p-4 gap-y-2 items-stretch\"\n                  >\n                    ${\n                      this.linkMenuOpen\n                        ? html`\n                            <form\n                              @submit=${(event: SubmitEvent) => {\n                                event.preventDefault()\n                                const target = event.target as HTMLFormElement | null\n                                const href = target?.querySelector('input')?.value?.trim()\n                                this.handleLinkUpdate(editor, href)\n                              }}\n                            >\n                              <input\n                                placeholder=\"Paste the link...\"\n                                value=${items.link.currentLink}\n                                class=\"flex h-9 rounded-md w-full bg-[canvas] px-3 py-2 text-sm placeholder:text-gray-500 dark:placeholder:text-gray-500 transition border box-border border-gray-200 dark:border-gray-800 border-solid ring-0 ring-transparent focus-visible:ring-2 focus-visible:ring-gray-900 dark:focus-visible:ring-gray-300 focus-visible:ring-offset-0 outline-hidden focus-visible:outline-hidden file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-50\"\n                              />\n                            </form>\n                          `\n                        : nothing\n                    }\n                    ${\n                      items.link.isActive\n                        ? html`\n                            <button\n                              @click=${() => this.handleLinkUpdate(editor)}\n                              @mousedown=${(event: MouseEvent) => event.preventDefault()}\n                              class=\"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white dark:ring-offset-gray-950 transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-gray-900 dark:focus-visible:ring-gray-300 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border-0 bg-gray-900 dark:bg-gray-50 text-gray-50 dark:text-gray-900 hover:bg-gray-900/90 dark:hover:bg-gray-50/90 h-9 px-3\"\n                            >\n                              Remove link\n                            </button>\n                          `\n                        : nothing\n                    }\n                  </prosekit-inline-popover-popup>\n                </prosekit-inline-popover-positioner>\n              </prosekit-inline-popover-root>\n            `\n          : nothing\n      }\n    `\n  }\n}\n\nexport function registerLitEditorInlineMenu() {\n  registerLitEditorButton()\n  registerInlinePopoverRootElement()\n  registerInlinePopoverPositionerElement()\n  registerInlinePopoverPopupElement()\n\n  if (customElements.get('lit-editor-inline-menu')) return\n  customElements.define('lit-editor-inline-menu', LitInlineMenu)\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'lit-editor-inline-menu': LitInlineMenu\n  }\n}\n"
    }
  ],
  "meta": {
    "hasIcons": true,
    "hidden": false,
    "story": "",
    "framework": "lit",
    "accumulatedFiles": [
      "registry/src/lit/ui/button/button.ts",
      "registry/src/lit/ui/button/index.ts",
      "registry/src/lit/ui/editor-context.ts",
      "registry/src/lit/ui/inline-menu/index.ts",
      "registry/src/lit/ui/inline-menu/inline-menu.ts"
    ],
    "internalDependencies": [
      "lit-ui-button",
      "lit-ui-editor-context"
    ]
  },
  "css": {
    "@plugin @egoist/tailwindcss-icons": {}
  },
  "$schema": "https://ui.shadcn.com/schema/registry-item.json"
}
