import { Double, getType, Int, newDynamic, trie } from "dope2"; import { focusNextElement, focusPrevElement, getCaretPosition, setRightMostCaretPosition } from "./util/dom_trickery"; import { parseDouble, parseInt } from "./util/parse"; import { useEffect, useRef, useState } from "react"; import { Type } from "./Type"; import "./InputBlock.css"; import type { Dynamic, State2Props } from "./util/extra"; import type { EditorState } from "./Editor"; import { ShowIf } from "./ShowIf"; export interface InputBlockState { kind: "input"; env: any; text: string; resolved: undefined | Dynamic; rollback?: EditorState; } interface InputBlockProps extends State2Props { filter: (ls: any[]) => boolean; onResolve: (state: InputBlockState) => void; onCancel: () => void; } export function InputBlock({ state: {kind, env, text, resolved, rollback}, setState, filter, onResolve, onCancel }: InputBlockProps) { const ref = useRef(null); useEffect(() => { ref.current?.focus(); }, []); const [i, setI] = useState(0); // selected suggestion const [haveFocus, setHaveFocus] = useState(false); // whether to render suggestions or not const setText = (text: string) => { setState({kind, env, text, resolved, rollback}); } const setResolved = (resolved: Dynamic) => { setState({kind, env, text, resolved, rollback}); } const singleSuggestion = trie.growPrefix(env.name2dyn)(text); const asDouble = parseDouble(text); const asInt = parseInt(text); const suggestions = [ ... (asDouble ? [[asDouble.toString(), newDynamic(asDouble)(Double)]] : []), ... (asInt ? [[asInt.toString(), newDynamic(BigInt(asInt))(Int)]] : []), ... (text !== '') ? trie.suggest(env.name2dyn)(text)(Infinity) : [], ].filter(filter); useEffect(() => { setI(0); // reset if (ref.current) { ref.current.style.width = `${text.length === 0 ? 140 : (text.length*8.7)}px`; } }, [text]); const onSelectSuggestion = ([name, dynamic]) => { console.log(name); // setText(name); // ref.current.textContent = name; // setRightMostCaretPosition(ref.current); // setI(0); // setResolved(dynamic); console.log("onResolve inputblock..") onResolve({ kind: "input", env, text: name, resolved: dynamic, rollback, }); }; const onInput = e => { setText(e.target.value); if (resolved) { onResolve({ kind: "input", env, text: e.target.value, resolved: undefined, rollback, }); } }; const getCaretPosition = () => { return ref.current.selectionStart; } const onKeyDown = (e: React.KeyboardEvent) => { const fns = { Tab: () => { if (e.shiftKey) { focusPrevElement(); e.preventDefault(); } else { // not shift key if (singleSuggestion.length > 0) { const newText = text + singleSuggestion; setText(newText); // ref.current.textContent = newText; setRightMostCaretPosition(ref.current); e.preventDefault(); } else { onSelectSuggestion(suggestions[i]); e.preventDefault(); } } }, ArrowDown: () => { setI((i + 1) % suggestions.length); e.preventDefault(); }, ArrowUp: () => { setI((i - 1) % suggestions.length); e.preventDefault(); }, ArrowLeft: () => { if (getCaretPosition() === 0) { focusPrevElement(); e.preventDefault(); } }, ArrowRight: () => { if (getCaretPosition() === text.length) { focusNextElement(); e.preventDefault(); } }, Enter: () => { onSelectSuggestion(suggestions[i]); e.preventDefault(); }, Backspace: () => { if (text.length === 0) { onCancel(); e.preventDefault(); } } }; fns[e.key]?.(); }; return setHaveFocus(true)} onBlur={() => setTimeout(() => setHaveFocus(false), 200)}/> {singleSuggestion} ; } function Suggestions({ suggestions, onSelect, i, setI }) { const onMouseEnter = j => () => { setI(j); }; const onMouseDown = j => () => { setI(j); onSelect(suggestions[i]); }; return (suggestions.length > 0) ?
{suggestions.map(([name, dynamic], j) =>
{name} ::
) }
: <>; }