UNPKG

tldraw

Version:

A tiny little drawing editor.

163 lines (162 loc) 4.72 kB
import { jsx } from "react/jsx-runtime"; import { preventDefault, track, useEditor } from "@tldraw/editor"; import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react"; import { useTranslation } from "../hooks/useTranslation/useTranslation.mjs"; const CHAT_MESSAGE_TIMEOUT_CLOSING = 2e3; const CHAT_MESSAGE_TIMEOUT_CHATTING = 5e3; const CursorChatBubble = track(function CursorChatBubble2() { const editor = useEditor(); const { isChatting, chatMessage } = editor.getInstanceState(); const rTimeout = useRef(-1); const [value, setValue] = useState(""); useEffect(() => { const closingUp = !isChatting && chatMessage; if (closingUp || isChatting) { const duration = isChatting ? CHAT_MESSAGE_TIMEOUT_CHATTING : CHAT_MESSAGE_TIMEOUT_CLOSING; rTimeout.current = editor.timers.setTimeout(() => { editor.updateInstanceState({ chatMessage: "", isChatting: false }); setValue(""); editor.focus(); }, duration); } return () => { clearTimeout(rTimeout.current); }; }, [editor, chatMessage, isChatting]); if (isChatting) return /* @__PURE__ */ jsx(CursorChatInput, { value, setValue, chatMessage }); return chatMessage.trim() ? /* @__PURE__ */ jsx(NotEditingChatMessage, { chatMessage }) : null; }); function usePositionBubble(ref) { const editor = useEditor(); useLayoutEffect(() => { const elm = ref.current; if (!elm) return; const { x, y } = editor.inputs.currentScreenPoint; ref.current?.style.setProperty("transform", `translate(${x}px, ${y}px)`); function positionChatBubble(e) { const { minX, minY } = editor.getViewportScreenBounds(); ref.current?.style.setProperty( "transform", `translate(${e.clientX - minX}px, ${e.clientY - minY}px)` ); } window.addEventListener("pointermove", positionChatBubble); return () => { window.removeEventListener("pointermove", positionChatBubble); }; }, [ref, editor]); } const NotEditingChatMessage = ({ chatMessage }) => { const editor = useEditor(); const ref = useRef(null); usePositionBubble(ref); return /* @__PURE__ */ jsx( "div", { ref, className: "tl-cursor-chat tl-cursor-chat__bubble", style: { backgroundColor: editor.user.getColor() }, children: chatMessage } ); }; const CursorChatInput = track(function CursorChatInput2({ chatMessage, value, setValue }) { const editor = useEditor(); const msg = useTranslation(); const ref = useRef(null); const placeholder = chatMessage || msg("cursor-chat.type-to-chat"); usePositionBubble(ref); useLayoutEffect(() => { const elm = ref.current; if (!elm) return; const textMeasurement = editor.textMeasure.measureText(value || placeholder, { fontFamily: "var(--font-body)", fontSize: 12, fontWeight: "500", fontStyle: "normal", maxWidth: null, lineHeight: 1, padding: "6px" }); elm.style.setProperty("width", textMeasurement.w + "px"); }, [editor, value, placeholder]); useLayoutEffect(() => { const raf = editor.timers.requestAnimationFrame(() => { ref.current?.focus(); }); return () => { cancelAnimationFrame(raf); }; }, [editor]); const stopChatting = useCallback(() => { editor.updateInstanceState({ isChatting: false }); editor.focus(); }, [editor]); const handleChange = useCallback( (e) => { const { value: value2 } = e.target; setValue(value2.slice(0, 64)); editor.updateInstanceState({ chatMessage: value2 }); }, [editor, setValue] ); const handleKeyDown = useCallback( (e) => { const elm = ref.current; if (!elm) return; const { value: currentValue } = elm; switch (e.key) { case "Enter": { preventDefault(e); e.stopPropagation(); if (!currentValue) { stopChatting(); return; } setValue(""); break; } case "Escape": { preventDefault(e); e.stopPropagation(); stopChatting(); break; } } }, [stopChatting, setValue] ); const handlePaste = useCallback((e) => { e.stopPropagation(); }, []); return /* @__PURE__ */ jsx( "input", { ref, className: `tl-cursor-chat`, style: { backgroundColor: editor.user.getColor() }, onBlur: stopChatting, onChange: handleChange, onKeyDown: handleKeyDown, onPaste: handlePaste, value, placeholder, spellCheck: false } ); }); export { CursorChatBubble }; //# sourceMappingURL=CursorChatBubble.mjs.map