move everything
This commit is contained in:
parent
3ff7e76694
commit
9050581a10
25 changed files with 37 additions and 42 deletions
147
src/Input.tsx
147
src/Input.tsx
|
|
@ -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>;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue