UNPKG

@mdxeditor/editor

Version:

React component for rich text markdown editing

232 lines (231 loc) 9.73 kB
import * as RadixPopover from "@radix-ui/react-popover"; import * as Tooltip from "@radix-ui/react-tooltip"; import React__default from "react"; import { editorRootElementRef$, activeEditor$, iconComponentFor$, useTranslation } from "../core/index.js"; import { DownshiftAutoComplete } from "../core/ui/DownshiftAutoComplete.js"; import styles from "../../styles/ui.module.css.js"; import classNames from "classnames"; import { createCommand } from "lexical"; import { useForm } from "react-hook-form"; import { linkDialogState$, linkAutocompleteSuggestions$, onClickLinkCallback$, showLinkTitleField$, onWindowChange$, updateLink$, cancelLinkEdit$, switchFromPreviewToLinkEdit$, removeLink$ } from "./index.js"; import { useCellValues, usePublisher } from "@mdxeditor/gurx"; createCommand(); function LinkEditForm({ url, title, text, onSubmit, onCancel, linkAutocompleteSuggestions, showLinkTitleField, showAnchorTextField }) { const { register, handleSubmit, control, setValue, reset: _ } = useForm({ values: { url, title, text } }); const t = useTranslation(); return /* @__PURE__ */ React__default.createElement( "form", { onSubmit: (e) => { void handleSubmit(onSubmit)(e); e.stopPropagation(); e.preventDefault(); }, onReset: (e) => { e.stopPropagation(); onCancel(); }, onKeyDown: (e) => { if (e.key === "Escape") { e.stopPropagation(); onCancel(); } }, className: classNames(styles.multiFieldForm, styles.linkDialogEditForm) }, /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "link-url" }, t("createLink.url", "URL")), /* @__PURE__ */ React__default.createElement( DownshiftAutoComplete, { register, initialInputValue: url, inputName: "url", suggestions: linkAutocompleteSuggestions, setValue, control, placeholder: t("createLink.urlPlaceholder", "Select or paste an URL"), autofocus: true } )), showAnchorTextField ? /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "link-text", title: t("createLink.textTooltip", "The text to be displayed for the link") }, t("createLink.text", "Anchor text")), /* @__PURE__ */ React__default.createElement("input", { id: "link-text", className: styles.textInput, size: 40, ...register("text") })) : null, showLinkTitleField ? /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "link-title", title: t("createLink.titleTooltip", "The link's title attribute, shown on hover") }, t("createLink.title", "Link title")), /* @__PURE__ */ React__default.createElement("input", { id: "link-title", className: styles.textInput, size: 40, ...register("title") })) : null, /* @__PURE__ */ React__default.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--spacing-2)" } }, /* @__PURE__ */ React__default.createElement( "button", { type: "submit", title: t("createLink.saveTooltip", "Set URL"), "aria-label": t("createLink.saveTooltip", "Set URL"), className: classNames(styles.primaryButton) }, t("dialogControls.save", "Save") ), /* @__PURE__ */ React__default.createElement( "button", { type: "reset", title: t("createLink.cancelTooltip", "Cancel change"), "aria-label": t("createLink.cancelTooltip", "Cancel change"), className: classNames(styles.secondaryButton) }, t("dialogControls.cancel", "Cancel") )) ); } const LinkDialog = () => { const [ editorRootElementRef, activeEditor, iconComponentFor, linkDialogState, linkAutocompleteSuggestions, onClickLinkCallback, showLinkTitleField ] = useCellValues( editorRootElementRef$, activeEditor$, iconComponentFor$, linkDialogState$, linkAutocompleteSuggestions$, onClickLinkCallback$, showLinkTitleField$ ); const publishWindowChange = usePublisher(onWindowChange$); const updateLink = usePublisher(updateLink$); const cancelLinkEdit = usePublisher(cancelLinkEdit$); const switchFromPreviewToLinkEdit = usePublisher(switchFromPreviewToLinkEdit$); const removeLink = usePublisher(removeLink$); React__default.useEffect(() => { const update = () => { activeEditor == null ? void 0 : activeEditor.getEditorState().read(() => { publishWindowChange(true); }); }; window.addEventListener("resize", update); window.addEventListener("scroll", update); return () => { window.removeEventListener("resize", update); window.removeEventListener("scroll", update); }; }, [activeEditor, publishWindowChange]); const [copyUrlTooltipOpen, setCopyUrlTooltipOpen] = React__default.useState(false); const t = useTranslation(); if (linkDialogState.type === "inactive") return null; const theRect = linkDialogState.rectangle; const urlIsExternal = linkDialogState.type === "preview" && linkDialogState.url.startsWith("http"); return /* @__PURE__ */ React__default.createElement(RadixPopover.Root, { open: true }, /* @__PURE__ */ React__default.createElement( RadixPopover.Anchor, { "data-visible": linkDialogState.type === "edit", className: styles.linkDialogAnchor, style: { top: `${theRect.top}px`, left: `${theRect.left}px`, width: `${theRect.width}px`, height: `${theRect.height}px` } } ), /* @__PURE__ */ React__default.createElement(RadixPopover.Portal, { container: editorRootElementRef == null ? void 0 : editorRootElementRef.current }, /* @__PURE__ */ React__default.createElement( RadixPopover.Content, { className: classNames(styles.linkDialogPopoverContent), sideOffset: 5, onOpenAutoFocus: (e) => { e.preventDefault(); }, key: linkDialogState.linkNodeKey }, linkDialogState.type === "edit" && /* @__PURE__ */ React__default.createElement( LinkEditForm, { url: linkDialogState.url, title: linkDialogState.title, text: linkDialogState.text, onSubmit: updateLink, onCancel: cancelLinkEdit.bind(null), linkAutocompleteSuggestions, showLinkTitleField, showAnchorTextField: linkDialogState.withAnchorText } ), linkDialogState.type === "preview" && /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement( "a", { className: styles.linkDialogPreviewAnchor, href: linkDialogState.url, ...urlIsExternal ? { target: "_blank", rel: "noreferrer" } : {}, onClick: (e) => { if (onClickLinkCallback !== null) { e.preventDefault(); onClickLinkCallback(linkDialogState.url); } }, title: urlIsExternal ? t("linkPreview.open", `Open {{url}} in new window`, { url: linkDialogState.url }) : linkDialogState.url }, /* @__PURE__ */ React__default.createElement("span", null, linkDialogState.url), urlIsExternal && iconComponentFor("open_in_new") ), /* @__PURE__ */ React__default.createElement( ActionButton, { onClick: () => { switchFromPreviewToLinkEdit(); }, title: t("linkPreview.edit", "Edit link URL"), "aria-label": t("linkPreview.edit", "Edit link URL") }, iconComponentFor("edit") ), /* @__PURE__ */ React__default.createElement(Tooltip.Provider, null, /* @__PURE__ */ React__default.createElement(Tooltip.Root, { open: copyUrlTooltipOpen }, /* @__PURE__ */ React__default.createElement(Tooltip.Trigger, { asChild: true }, /* @__PURE__ */ React__default.createElement( ActionButton, { title: t("linkPreview.copyToClipboard", "Copy to clipboard"), "aria-label": t("linkPreview.copyToClipboard", "Copy to clipboard"), onClick: () => { void window.navigator.clipboard.writeText(linkDialogState.url).then(() => { setCopyUrlTooltipOpen(true); setTimeout(() => { setCopyUrlTooltipOpen(false); }, 1e3); }); } }, copyUrlTooltipOpen ? iconComponentFor("check") : iconComponentFor("content_copy") )), /* @__PURE__ */ React__default.createElement(Tooltip.Portal, { container: editorRootElementRef == null ? void 0 : editorRootElementRef.current }, /* @__PURE__ */ React__default.createElement(Tooltip.Content, { className: classNames(styles.tooltipContent), sideOffset: 5 }, t("linkPreview.copied", "Copied!"), /* @__PURE__ */ React__default.createElement(Tooltip.Arrow, null))))), /* @__PURE__ */ React__default.createElement( ActionButton, { title: t("linkPreview.remove", "Remove link"), "aria-label": t("linkPreview.remove", "Remove link"), onClick: () => { removeLink(); } }, iconComponentFor("link_off") )), /* @__PURE__ */ React__default.createElement(RadixPopover.Arrow, { className: styles.popoverArrow }) ))); }; const ActionButton = React__default.forwardRef(({ className, ...props }, ref) => { return /* @__PURE__ */ React__default.createElement("button", { className: classNames(styles.actionButton, className), ref, ...props }); }); export { LinkDialog, LinkEditForm };