trying to simplify things a bit more... will get rid of 'onResolve'
This commit is contained in:
parent
897824e07d
commit
9c0c2dab90
7 changed files with 85 additions and 45 deletions
15
src/App.tsx
15
src/App.tsx
|
|
@ -50,11 +50,11 @@ export function App() {
|
||||||
window.onkeydown = onKeyDown;
|
window.onkeydown = onKeyDown;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const commands = [
|
const commands: [string, string[], string][] = [
|
||||||
["call" , "[c] call" ],
|
["call" , ['c' ], "call" ],
|
||||||
["eval" , "[e] [Tab] [Enter] eval"],
|
["eval" , ['e','Tab','Enter' ], "eval" ],
|
||||||
["transform", "[t] [.] transform" ],
|
["transform", ['t', '.' ], "transform" ],
|
||||||
["let" , "[l] [=] let ... in ..." ],
|
["let" , ['l', '=', 'a' ], "let ... in ..."],
|
||||||
];
|
];
|
||||||
|
|
||||||
const [highlighted, setHighlighted] = useState(
|
const [highlighted, setHighlighted] = useState(
|
||||||
|
|
@ -74,8 +74,9 @@ export function App() {
|
||||||
<button disabled={future.length===0} onClick={onRedo}>Redo ({future.length}) [Ctrl+Shift+Z]</button>
|
<button disabled={future.length===0} onClick={onRedo}>Redo ({future.length}) [Ctrl+Shift+Z]</button>
|
||||||
Commands:
|
Commands:
|
||||||
{
|
{
|
||||||
commands.map(([_, descr], i) =>
|
commands.map(([_, keys, descr], i) =>
|
||||||
<span key={i} className={'command' + (highlighted[i] ? (' highlighted') : '')}>
|
<span key={i} className={'command' + (highlighted[i] ? (' highlighted') : '')}>
|
||||||
|
{keys.map((key, j) => <kbd key={j}>{key}</kbd>)}
|
||||||
{descr}
|
{descr}
|
||||||
</span>)
|
</span>)
|
||||||
}
|
}
|
||||||
|
|
@ -91,6 +92,8 @@ export function App() {
|
||||||
filter={() => true}
|
filter={() => true}
|
||||||
/>
|
/>
|
||||||
</CommandContext>
|
</CommandContext>
|
||||||
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,26 @@
|
||||||
border-left-color: darkred;
|
border-left-color: darkred;
|
||||||
}
|
}
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam {
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam {
|
||||||
background-color: rgb(95, 4, 4);
|
/* background-color: rgb(95, 4, 4); */
|
||||||
color: white;
|
background-color: pink;
|
||||||
|
/* color: white; */
|
||||||
|
color: black;
|
||||||
}
|
}
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam:after {
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam:after {
|
||||||
border-left-color: rgb(95, 4, 4);
|
/* border-left-color: rgb(95, 4, 4); */
|
||||||
|
border-left-color: pink;
|
||||||
}
|
}
|
||||||
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam {
|
||||||
|
/* background-color: rgb(95, 4, 4); */
|
||||||
|
background-color: pink;
|
||||||
|
/* color: white; */
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam:after {
|
||||||
|
/* border-left-color: rgb(95, 4, 4); */
|
||||||
|
border-left-color: pink;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam {
|
.functionBlock.unifyError > .functionParams > .outputParam {
|
||||||
background-color: pink;
|
background-color: pink;
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ function headlessCallBlock({state, setState, onResolve}: CallBlockProps) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onFnResolve = (fnState) => {
|
const onFnResolve = (fnState) => {
|
||||||
if (input.resolved) {
|
if (fnState && input.resolved) {
|
||||||
makeTheCall(input, fnState);
|
makeTheCall(input, fnState);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -70,7 +70,7 @@ function headlessCallBlock({state, setState, onResolve}: CallBlockProps) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onInputResolve = (inputState) => {
|
const onInputResolve = (inputState) => {
|
||||||
if (fn.resolved) {
|
if (fn.resolved && inputState) {
|
||||||
makeTheCall(inputState, fn);
|
makeTheCall(inputState, fn);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -90,13 +90,14 @@ function headlessCallBlock({state, setState, onResolve}: CallBlockProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CallBlock({ state, setState, onResolve }: CallBlockProps) {
|
export function CallBlock({ state, setState, onResolve }: CallBlockProps) {
|
||||||
const {unifyError, setFn, setInput, onFnResolve, onInputResolve, onInputCancel}
|
const {unifyError, setFn, setInput, onFnResolve, onFnCancel, onInputResolve, onInputCancel}
|
||||||
= headlessCallBlock({ state, setState, onResolve });
|
= headlessCallBlock({ state, setState, onResolve });
|
||||||
return <span className={"functionBlock" + (unifyError ? " unifyError" : "")}>
|
return <span className={"functionBlock" + (unifyError ? " unifyError" : "")}>
|
||||||
<FunctionHeader
|
<FunctionHeader
|
||||||
fn={state.fn}
|
fn={state.fn}
|
||||||
setFn={setFn}
|
setFn={setFn}
|
||||||
onFnResolve={onFnResolve}
|
onFnResolve={onFnResolve}
|
||||||
|
onFnCancel={onFnCancel}
|
||||||
input={state.input} />
|
input={state.input} />
|
||||||
<div className="functionParams">
|
<div className="functionParams">
|
||||||
<div className="outputParam">
|
<div className="outputParam">
|
||||||
|
|
@ -109,26 +110,29 @@ export function CallBlock({ state, setState, onResolve }: CallBlockProps) {
|
||||||
|
|
||||||
{/* Output (or Error) */}
|
{/* Output (or Error) */}
|
||||||
{state.resolved && <Value dynamic={state.resolved} />}
|
{state.resolved && <Value dynamic={state.resolved} />}
|
||||||
|
{ state.resolved && <>☑</>}
|
||||||
{unifyError && unifyError.toString()}
|
{unifyError && unifyError.toString()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function FunctionHeader({ fn, setFn, input, onFnResolve }) {
|
function FunctionHeader({ fn, setFn, input, onFnResolve, onFnCancel }) {
|
||||||
if (fn.kind === "call") {
|
if (fn.kind === "call") {
|
||||||
// if the function we're calling is itself the result of a function call,
|
// if the function we're calling is itself the result of a function call,
|
||||||
// then we are anonymous, and so we don't draw a function name
|
// then we are anonymous, and so we don't draw a function name
|
||||||
|
|
||||||
// recurse:
|
// recurse:
|
||||||
const {
|
const {
|
||||||
onFnResolve : onFnFnResolve
|
onFnResolve : onFnFnResolve,
|
||||||
|
onFnCancel : onFnFnCancel,
|
||||||
} = headlessCallBlock({state: fn, setState: setFn, onResolve: onFnResolve});
|
} = headlessCallBlock({state: fn, setState: setFn, onResolve: onFnResolve});
|
||||||
|
|
||||||
return <FunctionHeader
|
return <FunctionHeader
|
||||||
fn={fn.fn}
|
fn={fn.fn}
|
||||||
setFn={fnFn => setFn({...fn, fn: fnFn})}
|
setFn={fnFn => setFn({...fn, fn: fnFn})}
|
||||||
onFnResolve={onFnFnResolve}
|
onFnResolve={onFnFnResolve}
|
||||||
|
onFnCancel={onFnFnCancel}
|
||||||
input={fn.input} />;
|
input={fn.input} />;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -151,15 +155,15 @@ function FunctionHeader({ fn, setFn, input, onFnResolve }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// end of recursion - draw function name
|
// end of recursion - draw function name
|
||||||
return <div className="functionName">
|
return <span className="functionName">
|
||||||
𝑓𝑛
|
𝑓𝑛
|
||||||
<Editor
|
<Editor
|
||||||
state={fn}
|
state={fn}
|
||||||
setState={setFn}
|
setState={setFn}
|
||||||
onResolve={onFnResolve}
|
onResolve={onFnResolve}
|
||||||
onCancel={() => {/*todo*/}}
|
onCancel={onFnCancel}
|
||||||
filter={filterCompatibleFns} />
|
filter={filterCompatibleFns} />
|
||||||
</div>;
|
</span>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.commandInput {
|
.commandInput {
|
||||||
width: 160px;
|
width: 90px;
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +62,10 @@ export function Editor({state, setState, onResolve, onCancel, filter}: EditorPro
|
||||||
}, [needCommand]);
|
}, [needCommand]);
|
||||||
const onMyResolve = (editorState: EditorState) => {
|
const onMyResolve = (editorState: EditorState) => {
|
||||||
setState(editorState);
|
setState(editorState);
|
||||||
|
onResolve(editorState);
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
if (editorState.resolved) {
|
if (editorState.resolved) {
|
||||||
setNeedCommand(true);
|
setNeedCommand(true);
|
||||||
}
|
}
|
||||||
|
|
@ -74,8 +78,9 @@ export function Editor({state, setState, onResolve, onCancel, filter}: EditorPro
|
||||||
|
|
||||||
const globalContext = useContext(CommandContext);
|
const globalContext = useContext(CommandContext);
|
||||||
const onCommand = (e: React.KeyboardEvent) => {
|
const onCommand = (e: React.KeyboardEvent) => {
|
||||||
const type = getType(state.resolved);
|
// const type = getType(state.resolved);
|
||||||
const commands = getCommands(type);
|
// const commands = getCommands(type);
|
||||||
|
const commands = ['e', 't', 'Enter', 'Backspace', 'ArrowLeft', 'ArrowRight', 'Tab', 'l', '=', '.', 'c'];
|
||||||
if (!commands.includes(e.key)) {
|
if (!commands.includes(e.key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -172,19 +177,16 @@ export function Editor({state, setState, onResolve, onCancel, filter}: EditorPro
|
||||||
(state.resolved)
|
(state.resolved)
|
||||||
? <div className="typeSignature">
|
? <div className="typeSignature">
|
||||||
:: <Type type={getType(state.resolved)} />
|
:: <Type type={getType(state.resolved)} />
|
||||||
{ (needCommand)
|
|
||||||
? <input
|
|
||||||
ref={commandInputRef}
|
|
||||||
spellCheck={false}
|
|
||||||
className="editable commandInput"
|
|
||||||
placeholder={`<command: ${getShortCommands(getType(state.resolved))}>`}
|
|
||||||
onKeyDown={onCommand}
|
|
||||||
value={""}
|
|
||||||
onChange={() => {}} /> /* gets rid of React warning */
|
|
||||||
: <></>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
: <></>
|
: <></>
|
||||||
}
|
}
|
||||||
|
<input
|
||||||
|
ref={commandInputRef}
|
||||||
|
spellCheck={false}
|
||||||
|
className="editable commandInput"
|
||||||
|
placeholder={`<command>`}
|
||||||
|
onKeyDown={onCommand}
|
||||||
|
value={""}
|
||||||
|
onChange={() => {}} />
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,8 @@ export function InputBlock({ state, setState, filter, onResolve, onCancel }: Inp
|
||||||
}, [focus]);
|
}, [focus]);
|
||||||
|
|
||||||
const onSelectSuggestion = ([name, dynamic]) => {
|
const onSelectSuggestion = ([name, dynamic]) => {
|
||||||
|
console.log('resolving input block', text, '->', name);
|
||||||
|
|
||||||
onResolve({
|
onResolve({
|
||||||
kind: "input",
|
kind: "input",
|
||||||
text: name,
|
text: name,
|
||||||
|
|
@ -69,23 +71,10 @@ export function InputBlock({ state, setState, filter, onResolve, onCancel }: Inp
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onInput = e => {
|
|
||||||
setText(e.target.value);
|
|
||||||
if (resolved) {
|
|
||||||
// un-resolve
|
|
||||||
onResolve({
|
|
||||||
kind: "input",
|
|
||||||
text: e.target.value,
|
|
||||||
resolved: undefined,
|
|
||||||
focus: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCaretPosition = () => {
|
const getCaretPosition = () => {
|
||||||
return inputRef.current?.selectionStart || -1;
|
return inputRef.current?.selectionStart || -1;
|
||||||
}
|
}
|
||||||
|
// fired before onInput
|
||||||
const onKeyDown = (e: React.KeyboardEvent) => {
|
const onKeyDown = (e: React.KeyboardEvent) => {
|
||||||
const fns = {
|
const fns = {
|
||||||
Tab: () => {
|
Tab: () => {
|
||||||
|
|
@ -141,6 +130,24 @@ export function InputBlock({ state, setState, filter, onResolve, onCancel }: Inp
|
||||||
fns[e.key]?.();
|
fns[e.key]?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onInput = e => {
|
||||||
|
const found = trie.get(env.name2dyn)(e.target.value);
|
||||||
|
if (found) {
|
||||||
|
console.log('resolving input block..', e.target.value);
|
||||||
|
onResolve({...state, text: e.target.value, resolved: found});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (resolved) {
|
||||||
|
// un-resolve
|
||||||
|
console.log('un-resolving input block..', e.target.value);
|
||||||
|
onResolve({...state, text: e.target.value, resolved: undefined});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setText(e.target.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return <span>
|
return <span>
|
||||||
<span className="">
|
<span className="">
|
||||||
{/* Dropdown suggestions */}
|
{/* Dropdown suggestions */}
|
||||||
|
|
@ -164,6 +171,7 @@ export function InputBlock({ state, setState, filter, onResolve, onCancel }: Inp
|
||||||
spellCheck={false}/>
|
spellCheck={false}/>
|
||||||
{/* Single 'grey' suggestion */}
|
{/* Single 'grey' suggestion */}
|
||||||
<span className="text-block suggest">{singleSuggestion}</span>
|
<span className="text-block suggest">{singleSuggestion}</span>
|
||||||
|
{ resolved && <>☑</>}
|
||||||
</span>
|
</span>
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,12 @@ body {
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-variation-settings: "wdth" 100;
|
font-variation-settings: "wdth" 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
border: 2px darkgrey;
|
||||||
|
color: rgb(63, 63, 63);
|
||||||
|
border-style: outset;
|
||||||
|
background-color: whitesmoke;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 0 2px 0 2px;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue