{
  "name": "preact-ui-inline-menu",
  "title": "preact-ui-inline-menu",
  "type": "registry:component",
  "description": "",
  "registryDependencies": [
    "https://unpkg.com/prosekit-registry/dist/r/preact-ui-button.json"
  ],
  "dependencies": [
    "@egoist/tailwindcss-icons",
    "@iconify-json/lucide",
    "prosekit@^0.22.0"
  ],
  "files": [
    {
      "path": "registry/src/preact/ui/inline-menu/index.ts",
      "type": "registry:component",
      "target": "components/editor/ui/inline-menu/index.ts",
      "content": "export { default as InlineMenu } from './inline-menu.tsx'\n"
    },
    {
      "path": "registry/src/preact/ui/inline-menu/inline-menu.tsx",
      "type": "registry:component",
      "target": "components/editor/ui/inline-menu/inline-menu.tsx",
      "content": "import type { JSX } from 'preact'\nimport { useState } from 'preact/hooks'\nimport type { BasicExtension } from 'prosekit/basic'\nimport type { Editor } from 'prosekit/core'\nimport type { LinkAttrs } from 'prosekit/extensions/link'\nimport type { EditorState } from 'prosekit/pm/state'\nimport { useEditor, useEditorDerivedValue } from 'prosekit/preact'\nimport { InlinePopoverPopup, InlinePopoverPositioner, InlinePopoverRoot } from 'prosekit/preact/inline-popover'\n\nimport { Button } from '../button/index.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\nexport default function InlineMenu() {\n  const editor = useEditor<BasicExtension>()\n  const items = useEditorDerivedValue(getInlineMenuItems)\n\n  const [linkMenuOpen, setLinkMenuOpen] = useState(false)\n  const toggleLinkMenuOpen = () => setLinkMenuOpen((open) => !open)\n\n  const handleLinkUpdate = (href?: string) => {\n    if (href) {\n      editor.commands.addLink({ href })\n    } else {\n      editor.commands.removeLink()\n    }\n\n    setLinkMenuOpen(false)\n    editor.focus()\n  }\n\n  const handleSubmit = (\n    event: JSX.TargetedEvent<HTMLFormElement, SubmitEvent>,\n  ) => {\n    event.preventDefault()\n    const href = event.currentTarget.querySelector('input')?.value?.trim()\n    handleLinkUpdate(href)\n  }\n\n  return (\n    <>\n      <InlinePopoverRoot\n        onOpenChange={(event) => {\n          if (!event.detail) {\n            setLinkMenuOpen(false)\n          }\n        }}\n      >\n        <InlinePopoverPositioner className=\"block overflow-visible w-min h-min z-50 ease-out transition-transform duration-100 motion-reduce:transition-none\">\n          <InlinePopoverPopup\n            data-testid=\"inline-menu-main\"\n            className=\"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            {items.bold && (\n              <Button\n                pressed={items.bold.isActive}\n                disabled={!items.bold.canExec}\n                onClick={items.bold.command}\n                tooltip=\"Bold\"\n              >\n                <div className=\"i-lucide-bold size-5 block\"></div>\n              </Button>\n            )}\n            {items.italic && (\n              <Button\n                pressed={items.italic.isActive}\n                disabled={!items.italic.canExec}\n                onClick={items.italic.command}\n                tooltip=\"Italic\"\n              >\n                <div className=\"i-lucide-italic size-5 block\"></div>\n              </Button>\n            )}\n            {items.underline && (\n              <Button\n                pressed={items.underline.isActive}\n                disabled={!items.underline.canExec}\n                onClick={items.underline.command}\n                tooltip=\"Underline\"\n              >\n                <div className=\"i-lucide-underline size-5 block\"></div>\n              </Button>\n            )}\n            {items.strike && (\n              <Button\n                pressed={items.strike.isActive}\n                disabled={!items.strike.canExec}\n                onClick={items.strike.command}\n                tooltip=\"Strikethrough\"\n              >\n                <div className=\"i-lucide-strikethrough size-5 block\"></div>\n              </Button>\n            )}\n            {items.code && (\n              <Button\n                pressed={items.code.isActive}\n                disabled={!items.code.canExec}\n                onClick={items.code.command}\n                tooltip=\"Code\"\n              >\n                <div className=\"i-lucide-code size-5 block\"></div>\n              </Button>\n            )}\n            {items.link && items.link.canExec && (\n              <Button\n                pressed={items.link.isActive}\n                onClick={() => {\n                  items.link?.command?.()\n                  toggleLinkMenuOpen()\n                }}\n                tooltip=\"Link\"\n              >\n                <div className=\"i-lucide-link size-5 block\"></div>\n              </Button>\n            )}\n          </InlinePopoverPopup>\n        </InlinePopoverPositioner>\n      </InlinePopoverRoot>\n\n      {items.link && (\n        <InlinePopoverRoot\n          defaultOpen={false}\n          open={linkMenuOpen}\n          onOpenChange={(event) => setLinkMenuOpen(event.detail)}\n        >\n          <InlinePopoverPositioner placement=\"bottom\" className=\"block overflow-visible w-min h-min z-50 ease-out transition-transform duration-100 motion-reduce:transition-none\">\n            <InlinePopoverPopup\n              data-testid=\"inline-menu-link\"\n              className=\"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              {linkMenuOpen && (\n                <form onSubmit={handleSubmit}>\n                  <input\n                    placeholder=\"Paste the link...\"\n                    defaultValue={items.link.currentLink}\n                    className=\"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              {items.link.isActive && (\n                <button\n                  onClick={() => handleLinkUpdate()}\n                  onMouseDown={(event) => event.preventDefault()}\n                  className=\"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            </InlinePopoverPopup>\n          </InlinePopoverPositioner>\n        </InlinePopoverRoot>\n      )}\n    </>\n  )\n}\n"
    }
  ],
  "meta": {
    "hasIcons": true,
    "hidden": false,
    "story": "",
    "framework": "preact",
    "accumulatedFiles": [
      "registry/src/preact/ui/button/button.tsx",
      "registry/src/preact/ui/button/index.ts",
      "registry/src/preact/ui/inline-menu/index.ts",
      "registry/src/preact/ui/inline-menu/inline-menu.tsx"
    ],
    "internalDependencies": [
      "preact-ui-button"
    ]
  },
  "css": {
    "@plugin @egoist/tailwindcss-icons": {}
  },
  "$schema": "https://ui.shadcn.com/schema/registry-item.json"
}
