tldraw
Version:
A tiny little drawing editor.
8 lines (7 loc) • 6.54 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../../../src/lib/ui/components/Toolbar/LinkEditor.tsx"],
"sourcesContent": ["import { openWindow, preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = useCallback(\n\t\t(link: string) => {\n\t\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\t\tlink = `https://${link}`\n\t\t\t}\n\n\t\t\ttextEditor.chain().setLink({ href: link }).run()\n\t\t\t// N.B. We shouldn't focus() on mobile because it causes the\n\t\t\t// Return key to replace the link with a newline :facepalm:\n\t\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\t\ttextEditor.commands.blur()\n\t\t\t} else {\n\t\t\t\ttextEditor.commands.focus()\n\t\t\t}\n\t\t\tonClose()\n\t\t},\n\t\t[trackEvent, source, textEditor, editor, onClose]\n\t)\n\n\tconst handleVisitLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-visit', source })\n\t\topenWindow(linkifiedValue, '_blank')\n\t\tonClose()\n\t}\n\n\tconst handleRemoveLink = useCallback(() => {\n\t\ttrackEvent('rich-text', { operation: 'link-remove', source })\n\t\ttextEditor.chain().unsetLink().focus().run()\n\t\tonClose()\n\t}, [trackEvent, source, textEditor, onClose])\n\n\tconst handleLinkCancel = () => onClose()\n\n\tuseEffect(() => {\n\t\tconst handlePointerDown = (e: PointerEvent) => {\n\t\t\tconst toolbar = document.querySelector('.tlui-rich-text__toolbar')\n\t\t\tif (toolbar?.contains(e.target as Node)) return\n\t\t\t// If the pointer down is not in the toolbar, complete the link\n\t\t\tif (value) {\n\t\t\t\thandleLinkComplete(value)\n\t\t\t} else {\n\t\t\t\thandleRemoveLink()\n\t\t\t}\n\t\t}\n\t\tdocument.addEventListener('pointerdown', handlePointerDown, { capture: true })\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('pointerdown', handlePointerDown, { capture: true })\n\t\t}\n\t}, [handleLinkComplete, handleRemoveLink, value])\n\n\tuseEffect(() => {\n\t\tref.current?.focus()\n\t}, [value])\n\n\tuseEffect(() => {\n\t\tsetValue(initialValue)\n\t}, [initialValue])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tdata-testid=\"rich-text.link-input\"\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-input\"\n\t\t\t\tvalue={value}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleLinkComplete}\n\t\t\t\tonCancel={handleLinkCancel}\n\t\t\t\tplaceholder=\"example.com\"\n\t\t\t\taria-label=\"example.com\"\n\t\t\t/>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-visit\"\n\t\t\t\ttitle={msg('tool.rich-text-link-visit')}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleVisitLink}\n\t\t\t\tdisabled={!value}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"external-link\" />\n\t\t\t</TldrawUiButton>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-remove\"\n\t\t\t\ttitle={msg('tool.rich-text-link-remove')}\n\t\t\t\tdata-testid=\"rich-text.link-remove\"\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleRemoveLink}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"trash\" />\n\t\t\t</TldrawUiButton>\n\t\t</>\n\t)\n}\n"],
"mappings": "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",
"names": ["value"]
}