move everything

This commit is contained in:
Joeri Exelmans 2025-05-23 22:35:47 +02:00
parent 3ff7e76694
commit 9050581a10
25 changed files with 37 additions and 42 deletions

View file

@ -1,147 +0,0 @@
import { useEffect, useRef, type ReactNode, type KeyboardEvent, useState } from "react";
interface InputProps {
placeholder: string;
text: string;
suggestion: string;
onTextChange: (text: string) => void;
onEnter: () => void;
onCancel: () => void;
extraHandlers: {[key:string]: (e: KeyboardEvent) => void}
children?: ReactNode | ReactNode[];
}
const autoInputWidth = (ref: React.RefObject<HTMLInputElement| null>, text, emptyWidth=150) => {
if (ref.current) {
ref.current.style.width = `${text.length === 0 ? emptyWidth : (text.length*8.0)}px`;
}
}
function getCaretPosition(ref: React.RefObject<HTMLInputElement| null>): number {
return ref.current?.selectionStart || 0;
}
// Move caret all the way to the right in the currently focused element
function setRightMostCaretPosition(elem) {
const range = document.createRange();
range.selectNode(elem);
if (elem.lastChild) { // if no text is entered, there is no lastChild
range.setStart(elem.lastChild, elem.textContent.length);
range.setEnd(elem.lastChild, elem.textContent.length);
const selection = window.getSelection();
selection?.removeAllRanges();
selection?.addRange(range);
}
}
function focusNextElement() {
const editable = Array.from<any>(document.querySelectorAll('input.editable'));
const index = editable.indexOf(document.activeElement);
const nextElem = editable[index+1];
if (nextElem) {
nextElem.focus();
}
}
function focusPrevElement() {
const editable = Array.from<any>(document.querySelectorAll('input.editable'));
const index = editable.indexOf(document.activeElement);
const prevElem = editable[index-1]
if (prevElem) {
prevElem.focus();
setRightMostCaretPosition(prevElem);
}
}
export function Input({placeholder, text, suggestion, onTextChange, onEnter, onCancel, extraHandlers, children}: InputProps) {
const ref = useRef<HTMLInputElement>(null);
const [focus, setFocus] = useState<"yes"|"hide"|"no">("no");
useEffect(() => autoInputWidth(ref, (text+(focus?suggestion:'')) || placeholder), [ref, text, suggestion, focus]);
const onKeyDown = (e: KeyboardEvent) => {
setFocus("yes");
const keys = {
// auto-complete
Tab: () => {
if (e.shiftKey) {
focusPrevElement();
e.preventDefault();
}
else {
// not shift key
if (suggestion.length > 0) {
// complete with greyed out text
const newText = text + suggestion;
onTextChange(newText);
setRightMostCaretPosition(ref.current);
e.preventDefault();
}
else {
onEnter();
e.preventDefault();
}
}
},
Enter: () => {
onEnter();
e.preventDefault();
},
// cancel
Backspace: () => {
if (text.length === 0) {
onCancel();
focusPrevElement();
e.preventDefault();
}
},
// navigate with arrows
ArrowLeft: () => {
if (getCaretPosition(ref) <= 0) {
focusPrevElement();
e.preventDefault();
}
},
ArrowRight: () => {
if (getCaretPosition(ref) === text.length) {
focusNextElement();
e.preventDefault();
}
},
Escape: () => {
if (focus === "yes") {
setFocus("hide");
e.preventDefault();
}
},
...extraHandlers,
};
const handler = keys[e.key];
if (handler) {
handler(e);
}
};
return <span className="inputBlock">
{(focus === "yes") && children}
<span className="editable suggest">{text}{focus && suggestion}</span>
<input ref={ref}
placeholder={placeholder}
className="editable"
value={text}
onInput={(e) =>
// @ts-ignore
onTextChange(e.target.value)}
onKeyDown={onKeyDown}
onFocus={() => setFocus("yes")}
onBlur={() => setFocus("no")}
spellCheck={false}
/>
</span>;
}