import * as React from "react"; import { _ } from "lodash"; const RESIZE_HANDLE_HEIGHT = 8; const RESIZE_HANDLE_WIDTH = 18; const BOTTOM_CAM_HANDLE_HEIGHT = 10; function usePrevious(value) { const ref = React.useRef(); React.useEffect(() => { ref.current = value; }, [value]); return ref.current; } const renderCursor = ( name, color, x, y, currentPoint, pageState, isMultiUserActive, owner = false, ) => { const z = !owner ? 2 : 1; let _x = null; let _y = null; if (!currentPoint) { _x = (x + pageState?.camera?.point[0]) * pageState?.camera?.zoom; _y = (y + pageState?.camera?.point[1]) * pageState?.camera?.zoom; } return ( <>
{isMultiUserActive &&
{name}
} ); }; const PositionLabel = (props) => { const { currentUser, currentPoint, pageState, publishCursorUpdate, whiteboardId, pos, isMultiUserActive, } = props; const { name, color } = currentUser; const prevCurrentPoint = usePrevious(currentPoint); React.useEffect(() => { try { const point = [pos.x, pos.y]; publishCursorUpdate({ xPercent: point[0] / pageState?.camera?.zoom - pageState?.camera?.point[0], yPercent: point[1] / pageState?.camera?.zoom - pageState?.camera?.point[1], whiteboardId, }); } catch (e) { console.log(e); } }, [pos?.x, pos?.y]); return ( <>
{renderCursor(name, color, pos.x, pos.y, currentPoint, props.pageState, isMultiUserActive(whiteboardId))}
); }; export default function Cursors(props) { let cursorWrapper = React.useRef(null); const [active, setActive] = React.useState(false); const [pos, setPos] = React.useState({ x: 0, y: 0 }); const { whiteboardId, otherCursors, currentUser, tldrawAPI, publishCursorUpdate, children, isViewersCursorLocked, hasMultiUserAccess, isMultiUserActive, application, } = props; const start = () => setActive(true); const end = () => { publishCursorUpdate({ xPercent: -1.0, yPercent: -1.0, whiteboardId: whiteboardId, }); setActive(false); }; const moved = (event) => { const { type, x, y } = event; // If the presentation container is the full screen element we don't need any offsets const fsEl = document?.webkitFullscreenElement || document?.fullscreenElement; if (fsEl?.getAttribute('data-test') === "presentationContainer") { return setPos({ x, y }); } const nav = document.getElementById('Navbar'); let yOffset = parseFloat(nav?.style?.height); const getSibling = (el) => el?.previousSibling || null; const panel = getSibling(nav); const webcams = document.getElementById('cameraDock'); const subPanel = panel && getSibling(panel); let xOffset = (parseFloat(panel?.style?.width) || 0) + (parseFloat(subPanel?.style?.width) || 0); const camPosition = document.getElementById('layout')?.getAttribute('data-cam-position') || null; const sl = document.getElementById('layout')?.getAttribute('data-layout'); if (type === 'touchmove') { !active && setActive(true); return setPos({ x: event?.changedTouches[0]?.clientX - xOffset, y: event?.changedTouches[0]?.clientY - yOffset }); } const handleCustomYOffsets = () => { if (camPosition === 'contentTop' || !camPosition) { yOffset += (parseFloat(webcams?.style?.height) + RESIZE_HANDLE_HEIGHT); } if (camPosition === 'contentBottom') { yOffset -= BOTTOM_CAM_HANDLE_HEIGHT; } } if (document?.documentElement?.dir === 'rtl') { xOffset = 0; if (webcams && sl?.includes('custom')) { handleCustomYOffsets(); if (camPosition === 'contentRight') { xOffset += (parseFloat(webcams?.style?.width) + RESIZE_HANDLE_WIDTH); } } if (webcams && sl?.includes('smart')) { if (panel || subPanel) { const dockPos = webcams?.getAttribute("data-position"); if (dockPos === 'contentRight') { xOffset += (parseFloat(webcams?.style?.width) + RESIZE_HANDLE_WIDTH); } if (dockPos === 'contentTop') { yOffset += (parseFloat(webcams?.style?.height) + RESIZE_HANDLE_WIDTH); } } if (!panel && !subPanel) { xOffset = 0; } } if (webcams && sl?.includes('videoFocus')) { xOffset = parseFloat(nav?.style?.width); yOffset = parseFloat(panel?.style?.height); } } else { if (webcams && sl?.includes('custom')) { handleCustomYOffsets(); if (camPosition === 'contentLeft') { xOffset += (parseFloat(webcams?.style?.width) + RESIZE_HANDLE_WIDTH); } } if (webcams && sl?.includes('smart')) { if (panel || subPanel) { const dockPos = webcams?.getAttribute("data-position"); if (dockPos === 'contentLeft') { xOffset += (parseFloat(webcams?.style?.width) + RESIZE_HANDLE_WIDTH); } if (dockPos === 'contentTop') { yOffset += (parseFloat(webcams?.style?.height) + RESIZE_HANDLE_WIDTH); } } if (!panel && !subPanel) { xOffset = (parseFloat(webcams?.style?.width) + RESIZE_HANDLE_WIDTH); } } if (webcams && sl?.includes('videoFocus')) { xOffset = parseFloat(subPanel?.style?.width); yOffset = parseFloat(panel?.style?.height); } } return setPos({ x: event.x - xOffset, y: event.y - yOffset }); } React.useEffect(() => { !cursorWrapper.hasOwnProperty("mouseenter") && cursorWrapper?.addEventListener("mouseenter", start); !cursorWrapper.hasOwnProperty("mouseleave") && cursorWrapper?.addEventListener("mouseleave", end); !cursorWrapper.hasOwnProperty("touchend") && cursorWrapper?.addEventListener("touchend", end); !cursorWrapper.hasOwnProperty("mousemove") && cursorWrapper?.addEventListener("mousemove", moved); !cursorWrapper.hasOwnProperty("touchmove") && cursorWrapper?.addEventListener("touchmove", moved); }, [cursorWrapper]); React.useEffect(() => { return () => { if (cursorWrapper) { cursorWrapper.removeEventListener('mouseenter', start); cursorWrapper.removeEventListener('mouseleave', end); cursorWrapper.removeEventListener('mousemove', moved); cursorWrapper.removeEventListener('touchend', end); cursorWrapper.removeEventListener('touchmove', moved); } } }); const multiUserAccess = hasMultiUserAccess(whiteboardId, currentUser?.userId); return ( (cursorWrapper = r)}>
{(active && multiUserAccess || (active && currentUser?.presenter)) && ( )} {children}
{otherCursors .filter((c) => c?.xPercent && c?.yPercent) .filter((c) => { if ((isViewersCursorLocked && c?.role !== "VIEWER") || !isViewersCursorLocked || currentUser?.presenter) { return c; } return null; }) .map((c) => { if (c && currentUser.userId !== c?.userId) { if (c.presenter) { return renderCursor( c?.userName, "#C70039", c?.xPercent, c?.yPercent, null, tldrawAPI?.getPageState(), isMultiUserActive(whiteboardId), true ); } return hasMultiUserAccess(whiteboardId, c?.userId) && ( renderCursor( c?.userName, "#AFE1AF", c?.xPercent, c?.yPercent, null, tldrawAPI?.getPageState(), isMultiUserActive(whiteboardId), true ) ); } })}
); }