Skip to content

Commit d34af8a

Browse files
fix: add blinking while stationary
1 parent d798833 commit d34af8a

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

packages/editor/src/core/extensions/smooth-cursor/plugin.ts

+32-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import { type Selection, Plugin, PluginKey, TextSelection } from "@tiptap/pm/sta
22
import { type EditorView, Decoration, DecorationSet } from "@tiptap/pm/view";
33

44
export const PROSEMIRROR_SMOOTH_CURSOR_CLASS = "prosemirror-smooth-cursor";
5+
const BLINK_DELAY = 750; // Time in ms before cursor starts blinking
56

67
export function smoothCursorPlugin(): Plugin {
78
let smoothCursor: HTMLElement | null = typeof document === "undefined" ? null : document.createElement("div");
89
let rafId: number | undefined;
10+
let blinkTimeoutId: number | undefined;
911
let isEditorFocused = false;
12+
let lastCursorPosition = { x: 0, y: 0 };
1013

1114
function updateCursor(view?: EditorView, cursor?: HTMLElement) {
1215
if (!view || !view.dom || view.isDestroyed || !cursor) return;
@@ -30,13 +33,31 @@ export function smoothCursorPlugin(): Plugin {
3033

3134
const className = PROSEMIRROR_SMOOTH_CURSOR_CLASS;
3235

33-
cursor.className = className;
34-
cursor.style.height = `${cursorRect.bottom - cursorRect.top}px`;
35-
3636
// Calculate the exact position
3737
const x = cursorRect.left - editorRect.left;
3838
const y = cursorRect.top - editorRect.top;
3939

40+
// Check if cursor position has changed
41+
if (x !== lastCursorPosition.x || y !== lastCursorPosition.y) {
42+
lastCursorPosition = { x, y };
43+
cursor.classList.remove(`${className}--blinking`);
44+
45+
// Clear existing timeout
46+
if (blinkTimeoutId) {
47+
window.clearTimeout(blinkTimeoutId);
48+
}
49+
50+
// Set new timeout for blinking
51+
blinkTimeoutId = window.setTimeout(() => {
52+
if (cursor && isEditorFocused) {
53+
cursor.classList.add(`${className}--blinking`);
54+
}
55+
}, BLINK_DELAY);
56+
}
57+
58+
cursor.className = className;
59+
cursor.style.height = `${cursorRect.bottom - cursorRect.top}px`;
60+
4061
rafId = requestAnimationFrame(() => {
4162
cursor.style.transform = `translate3d(${x}px, ${y}px, 0)`;
4263
});
@@ -63,6 +84,10 @@ export function smoothCursorPlugin(): Plugin {
6384

6485
const handleBlur = () => {
6586
isEditorFocused = false;
87+
if (blinkTimeoutId) {
88+
window.clearTimeout(blinkTimeoutId);
89+
}
90+
cursor.classList.remove(`${PROSEMIRROR_SMOOTH_CURSOR_CLASS}--blinking`);
6691
update();
6792
};
6893

@@ -83,10 +108,13 @@ export function smoothCursorPlugin(): Plugin {
83108
view.dom.removeEventListener("focus", handleFocus);
84109
view.dom.removeEventListener("blur", handleBlur);
85110
observer?.unobserve(view.dom);
86-
// Clean up any pending animation frame
87111
if (rafId !== undefined) {
88112
cancelAnimationFrame(rafId);
89113
}
114+
if (blinkTimeoutId) {
115+
window.clearTimeout(blinkTimeoutId);
116+
}
117+
style.remove();
90118
},
91119
};
92120
},

packages/editor/src/styles/editor.css

+14
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,17 @@ p + p {
494494
will-change: transform;
495495
opacity: 0.8;
496496
}
497+
498+
.prosemirror-smooth-cursor--blinking {
499+
animation: blink 1s step-end infinite;
500+
}
501+
502+
@keyframes blink {
503+
from,
504+
to {
505+
opacity: 1;
506+
}
507+
50% {
508+
opacity: 0;
509+
}
510+
}

0 commit comments

Comments
 (0)