From d34af8a1e3ffefee52b72718e3d7c4282c66bc19 Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Thu, 28 Nov 2024 17:16:25 +0530 Subject: [PATCH] fix: add blinking while stationary --- .../core/extensions/smooth-cursor/plugin.ts | 36 ++++++++++++++++--- packages/editor/src/styles/editor.css | 14 ++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/core/extensions/smooth-cursor/plugin.ts b/packages/editor/src/core/extensions/smooth-cursor/plugin.ts index 9d62a0db5ca..086d5d15fa4 100644 --- a/packages/editor/src/core/extensions/smooth-cursor/plugin.ts +++ b/packages/editor/src/core/extensions/smooth-cursor/plugin.ts @@ -2,11 +2,14 @@ import { type Selection, Plugin, PluginKey, TextSelection } from "@tiptap/pm/sta import { type EditorView, Decoration, DecorationSet } from "@tiptap/pm/view"; export const PROSEMIRROR_SMOOTH_CURSOR_CLASS = "prosemirror-smooth-cursor"; +const BLINK_DELAY = 750; // Time in ms before cursor starts blinking export function smoothCursorPlugin(): Plugin { let smoothCursor: HTMLElement | null = typeof document === "undefined" ? null : document.createElement("div"); let rafId: number | undefined; + let blinkTimeoutId: number | undefined; let isEditorFocused = false; + let lastCursorPosition = { x: 0, y: 0 }; function updateCursor(view?: EditorView, cursor?: HTMLElement) { if (!view || !view.dom || view.isDestroyed || !cursor) return; @@ -30,13 +33,31 @@ export function smoothCursorPlugin(): Plugin { const className = PROSEMIRROR_SMOOTH_CURSOR_CLASS; - cursor.className = className; - cursor.style.height = `${cursorRect.bottom - cursorRect.top}px`; - // Calculate the exact position const x = cursorRect.left - editorRect.left; const y = cursorRect.top - editorRect.top; + // Check if cursor position has changed + if (x !== lastCursorPosition.x || y !== lastCursorPosition.y) { + lastCursorPosition = { x, y }; + cursor.classList.remove(`${className}--blinking`); + + // Clear existing timeout + if (blinkTimeoutId) { + window.clearTimeout(blinkTimeoutId); + } + + // Set new timeout for blinking + blinkTimeoutId = window.setTimeout(() => { + if (cursor && isEditorFocused) { + cursor.classList.add(`${className}--blinking`); + } + }, BLINK_DELAY); + } + + cursor.className = className; + cursor.style.height = `${cursorRect.bottom - cursorRect.top}px`; + rafId = requestAnimationFrame(() => { cursor.style.transform = `translate3d(${x}px, ${y}px, 0)`; }); @@ -63,6 +84,10 @@ export function smoothCursorPlugin(): Plugin { const handleBlur = () => { isEditorFocused = false; + if (blinkTimeoutId) { + window.clearTimeout(blinkTimeoutId); + } + cursor.classList.remove(`${PROSEMIRROR_SMOOTH_CURSOR_CLASS}--blinking`); update(); }; @@ -83,10 +108,13 @@ export function smoothCursorPlugin(): Plugin { view.dom.removeEventListener("focus", handleFocus); view.dom.removeEventListener("blur", handleBlur); observer?.unobserve(view.dom); - // Clean up any pending animation frame if (rafId !== undefined) { cancelAnimationFrame(rafId); } + if (blinkTimeoutId) { + window.clearTimeout(blinkTimeoutId); + } + style.remove(); }, }; }, diff --git a/packages/editor/src/styles/editor.css b/packages/editor/src/styles/editor.css index c7f196356ff..28103bc6848 100644 --- a/packages/editor/src/styles/editor.css +++ b/packages/editor/src/styles/editor.css @@ -494,3 +494,17 @@ p + p { will-change: transform; opacity: 0.8; } + +.prosemirror-smooth-cursor--blinking { + animation: blink 1s step-end infinite; +} + +@keyframes blink { + from, + to { + opacity: 1; + } + 50% { + opacity: 0; + } +}