UNPKG

tldraw

Version:

A tiny little drawing editor.

172 lines (171 loc) • 7.21 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var DefaultRichTextToolbar_exports = {}; __export(DefaultRichTextToolbar_exports, { DefaultRichTextToolbar: () => DefaultRichTextToolbar }); module.exports = __toCommonJS(DefaultRichTextToolbar_exports); var import_jsx_runtime = require("react/jsx-runtime"); var import_core = require("@tiptap/core"); var import_editor = require("@tldraw/editor"); var import_react = require("react"); var import_useTranslation = require("../../hooks/useTranslation/useTranslation"); var import_TldrawUiContextualToolbar = require("../primitives/TldrawUiContextualToolbar"); var import_DefaultRichTextToolbarContent = require("./DefaultRichTextToolbarContent"); var import_LinkEditor = require("./LinkEditor"); const DefaultRichTextToolbar = (0, import_editor.track)(function DefaultRichTextToolbar2({ children }) { const editor = (0, import_editor.useEditor)(); const textEditor = (0, import_editor.useValue)("textEditor", () => editor.getRichTextEditor(), [editor]); if (editor.getInstanceState().isCoarsePointer || !textEditor) return null; return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextualToolbarInner, { textEditor, children }); }); function ContextualToolbarInner({ textEditor, children }) { const { isEditingLink, onEditLinkStart, onEditLinkClose } = useEditingLinkBehavior(textEditor); const [currentSelection, setCurrentSelection] = (0, import_react.useState)(null); const previousSelectionBounds = (0, import_react.useRef)(void 0); const isMousingDown = useIsMousingDownOnTextEditor(textEditor); const msg = (0, import_useTranslation.useTranslation)(); const getSelectionBounds = (0, import_react.useCallback)(() => { if (isEditingLink) { return previousSelectionBounds.current; } const selection = window.getSelection(); if (!currentSelection || !selection || selection.rangeCount === 0 || selection.isCollapsed) return; const rangeBoxes = []; for (let i = 0; i < selection.rangeCount; i++) { const range = selection.getRangeAt(i); rangeBoxes.push((0, import_TldrawUiContextualToolbar.rectToBox)(range.getBoundingClientRect())); } const bounds = import_editor.Box.Common(rangeBoxes); previousSelectionBounds.current = bounds; return bounds; }, [currentSelection, isEditingLink]); (0, import_react.useEffect)(() => { const handleSelectionUpdate = ({ editor: textEditor2 }) => setCurrentSelection(textEditor2.state.selection); textEditor.on("selectionUpdate", handleSelectionUpdate); handleSelectionUpdate({ editor: textEditor }); return () => { textEditor.off("selectionUpdate", handleSelectionUpdate); }; }, [textEditor]); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_TldrawUiContextualToolbar.TldrawUiContextualToolbar, { className: "tlui-rich-text__toolbar", getSelectionBounds, isMousingDown, changeOnlyWhenYChanges: true, label: msg("tool.rich-text-toolbar-title"), children: children ? children : isEditingLink ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_LinkEditor.LinkEditor, { textEditor, value: textEditor.isActive("link") ? textEditor.getAttributes("link").href : "", onClose: onEditLinkClose } ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_DefaultRichTextToolbarContent.DefaultRichTextToolbarContent, { textEditor, onEditLinkStart }) } ); } function useEditingLinkBehavior(textEditor) { const [isEditingLink, setIsEditingLink] = (0, import_react.useState)(false); (0, import_react.useEffect)(() => { if (!textEditor) { setIsEditingLink(false); return; } const handleClick = () => { const isLinkActive = textEditor.isActive("link"); setIsEditingLink(isLinkActive); }; textEditor.view.dom.addEventListener("click", handleClick); return () => { if (textEditor.isInitialized) { textEditor.view.dom.removeEventListener("click", handleClick); } }; }, [textEditor, isEditingLink]); (0, import_react.useEffect)(() => { if (!textEditor) { return; } if (textEditor.isActive("link")) { try { const { from, to } = (0, import_core.getMarkRange)( textEditor.state.doc.resolve(textEditor.state.selection.from), textEditor.schema.marks.link ); if (textEditor.state.selection.empty) { textEditor.commands.setTextSelection({ from, to }); } } catch { } } }, [textEditor, isEditingLink]); const onEditLinkStart = (0, import_react.useCallback)(() => { setIsEditingLink(true); }, []); const onEditLinkCancel = (0, import_react.useCallback)(() => { setIsEditingLink(false); }, []); const onEditLinkClose = (0, import_react.useCallback)(() => { setIsEditingLink(false); if (!textEditor) return; const from = textEditor.state.selection.from; textEditor.commands.setTextSelection({ from, to: from }); }, [textEditor]); return { isEditingLink, onEditLinkStart, onEditLinkClose, onEditLinkCancel }; } function useIsMousingDownOnTextEditor(textEditor) { const [isMousingDown, setIsMousingDown] = (0, import_react.useState)(false); (0, import_react.useEffect)(() => { if (!textEditor) return; const handlePointingStateChange = (0, import_editor.debounce)(({ isPointing }) => { setIsMousingDown(isPointing); }, 16); const handlePointingDown = () => handlePointingStateChange({ isPointing: true }); const handlePointingUp = () => handlePointingStateChange({ isPointing: false }); const touchDownEvents = ["touchstart", "pointerdown", "mousedown"]; const touchUpEvents = ["touchend", "pointerup", "mouseup"]; touchDownEvents.forEach((eventName) => { textEditor.view.dom.addEventListener(eventName, handlePointingDown); }); touchUpEvents.forEach((eventName) => { document.body.addEventListener(eventName, handlePointingUp); }); return () => { touchDownEvents.forEach((eventName) => { if (textEditor.isInitialized) { textEditor.view.dom.removeEventListener(eventName, handlePointingDown); } }); touchUpEvents.forEach((eventName) => { document.body.removeEventListener(eventName, handlePointingUp); }); }; }, [textEditor]); return isMousingDown; } //# sourceMappingURL=DefaultRichTextToolbar.js.map