From 543a1e1f2e84d59dbaf36ba958fc39203db1dbe8 Mon Sep 17 00:00:00 2001 From: Kasra Date: Sat, 24 Feb 2024 23:42:09 -0500 Subject: [PATCH] defer update until key has landed --- src/components/PlainEditor.tsx | 95 ++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 22 deletions(-) diff --git a/src/components/PlainEditor.tsx b/src/components/PlainEditor.tsx index 5db86d3..826a7a0 100644 --- a/src/components/PlainEditor.tsx +++ b/src/components/PlainEditor.tsx @@ -1,43 +1,87 @@ import { useState, useEffect } from "react"; import styles from './PlainEditor.module.css'; -interface CharAnimation { +interface UserKeyPress { id: number; char: string; + selectionStart: number; +} + +const LINE_WIDTH = 92; +const ANIMATION_DURATION = 1000; + +function useKeyPressAndPosition(callback: (keyPressed: string, cursorPosition: number) => void) { + const [keyPressed, setKeyPressed] = useState(''); + const [cursorPosition, setCursorPosition] = useState(0); + const [keyProcessed, setKeyProcessed] = useState(false); + + const handleKeyDown = (event: React.KeyboardEvent) => { + setKeyPressed(event.key); + setKeyProcessed(false); + }; + + const handleInput = (event: React.ChangeEvent) => { + if (!keyProcessed) { + setCursorPosition(event.target.selectionStart); + setKeyProcessed(true); + } + }; + + useEffect(() => { + if (keyPressed && keyProcessed) { + // Execute the callback function + callback(keyPressed, cursorPosition); + + // Reset the state if needed + setKeyPressed(''); + setCursorPosition(0); + setKeyProcessed(false); + } + }, [keyPressed, keyProcessed, cursorPosition, callback]); + + return { handleKeyDown, handleInput }; } export function PlainEditor() { - const [text, setText] = useState(''); - const [animationQueue, setAnimationQueue] = useState([]); - const [nextId, setNextId] = useState(0); const [spin, setSpin] = useState(false); const [curl, setCurl] = useState(false); - const handleChange = (event: React.ChangeEvent) => { - const newValue = event.target.value; - setText(newValue); - - if (newValue.length > text.length) { - const newChar = newValue.slice(-1); + const [text, setText] = useState(''); + const [animationQueue, setAnimationQueue] = useState([]); + const [nextId, setNextId] = useState(0); + const { handleInput, handleKeyDown } = useKeyPressAndPosition((keyPressed, cursorPosition) => { + if (keyPressed.length === 1) { + const curId = nextId; setAnimationQueue((prevQueue) => [ ...prevQueue, - { id: nextId, char: newChar } + { + id: nextId, + char: keyPressed, + selectionStart: cursorPosition + } ]); - setNextId(id => id + 1); + setNextId(nextId + 1); } else { + // handle backspace and so on } - } + }); useEffect(() => { - if (animationQueue.length > 0) { - const timer = setTimeout(() => { - // Remove the first character from the queue after its animation completes - setAnimationQueue((prevQueue) => prevQueue.slice(1)); - }, 2000); // Duration of the animation + // TODO: clean the timeout + const timer = setTimeout(() => { + if (animationQueue.length > 0) { + const update = animationQueue[0]; + setText( + text.slice(0, update.selectionStart) + + update.char + + text.slice(update.selectionStart) + ); + setAnimationQueue(animationQueue.slice(1)); + } + }, ANIMATION_DURATION - 500); - return () => clearTimeout(timer); - } + return () => clearTimeout(timer); }, [animationQueue]); return
@@ -50,9 +94,16 @@ export function PlainEditor() { setCurl(!curl)} /> curl -