UNPKG

sveltekit-sync

Version:
102 lines (101 loc) 2.97 kB
/** * Track cursor positions for collaborative editing * * @param presence - The presence hook instance * @param options - Configuration options * @returns Cursor tracking API * * @example * ```typescript * const presence = usePresence(channel, currentUser); * const cursorTracking = useCursorTracking(presence, { * throttle: 50, * container: canvasElement * }); * * cursorTracking.startTracking(); * * // Access cursors map * const cursors = cursorTracking.cursors; * for (const [userId, { user, position }] of cursors) { * renderCursor(user, position); * } * ``` */ export function useCursorTracking(presence, options) { const opts = { throttle: 50, container: typeof document !== 'undefined' ? document.body : null, ...options }; const cursors = $state(new Map()); let isTracking = $state(false); let throttleTimer = null; let unsubscribeJoin = null; let unsubscribeUpdate = null; let unsubscribeLeave = null; function updateCursorsMap() { cursors.clear(); for (const state of presence.others) { if (state.cursor) { cursors.set(state.user.id, { user: state.user, position: state.cursor }); } } } function handleMouseMove(e) { if (throttleTimer) return; const container = opts.container; const rect = container?.getBoundingClientRect(); const position = { x: rect ? e.clientX - rect.left : e.clientX, y: rect ? e.clientY - rect.top : e.clientY }; presence.updateCursor(position); throttleTimer = window.setTimeout(() => { throttleTimer = null; }, opts.throttle); } function startTracking() { if (isTracking) return; isTracking = true; const container = opts.container; if (container) { container.addEventListener('mousemove', handleMouseMove); } // Listen for presence changes unsubscribeJoin = presence.on('join', updateCursorsMap); unsubscribeUpdate = presence.on('update', updateCursorsMap); unsubscribeLeave = presence.on('leave', updateCursorsMap); // Initial update updateCursorsMap(); } function stopTracking() { if (!isTracking) return; isTracking = false; const container = opts.container; if (container) { container.removeEventListener('mousemove', handleMouseMove); } if (throttleTimer) { clearTimeout(throttleTimer); throttleTimer = null; } unsubscribeJoin?.(); unsubscribeUpdate?.(); unsubscribeLeave?.(); cursors.clear(); } return { get cursors() { return cursors; }, startTracking, stopTracking }; }