a bit more progress
This commit is contained in:
parent
a9ae4f9888
commit
2b0d8bc2c6
8 changed files with 109 additions and 28 deletions
|
|
@ -5,7 +5,7 @@
|
|||
}
|
||||
|
||||
.functionName {
|
||||
text-align: center;
|
||||
/* text-align: center; */
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
}
|
||||
|
||||
.inputParam {
|
||||
height: 20px;
|
||||
/* height: 20px; */
|
||||
margin-right: 20px;
|
||||
display: inline-block;
|
||||
background-color: rgba(242, 253, 146);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ interface CallBlockProps extends State2Props<CallBlockState> {
|
|||
}
|
||||
|
||||
export function CallBlock({ state: {kind, env, fn, input, resolved, rollback }, setState, onResolve }: CallBlockProps) {
|
||||
const setResolved = (resolved: Dynamic) => {
|
||||
const setResolved = (resolved?: Dynamic) => {
|
||||
setState({kind, env, fn, input, resolved, rollback});
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +53,11 @@ export function CallBlock({ state: {kind, env, fn, input, resolved, rollback },
|
|||
makeTheCall(input, fnState);
|
||||
}
|
||||
else {
|
||||
setFn(fnState);
|
||||
// setFn(fnState);
|
||||
setResolved(undefined);
|
||||
onResolve({
|
||||
kind, env, fn: fnState, input, resolved: undefined, rollback
|
||||
});
|
||||
}
|
||||
}
|
||||
const onInputResolve = (inputState) => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useEffect, useState } from "react";
|
|||
import { Type } from "./Type";
|
||||
|
||||
import "./Editor.css"
|
||||
import { focusNextElement } from "./util/dom_trickery";
|
||||
import { focusNextElement, focusPrevElement } from "./util/dom_trickery";
|
||||
|
||||
interface LetInBlockState {
|
||||
kind: "let";
|
||||
|
|
@ -49,22 +49,43 @@ interface EditorProps extends State2Props<EditorState> {
|
|||
|
||||
const dontFilter = () => true;
|
||||
|
||||
function getCommands(type) {
|
||||
const commands = ['u', 't', 'Enter', 'Backspace', 'ArrowLeft', 'Tab'];
|
||||
if (getSymbol(type) === symbolFunction) {
|
||||
commands.push('c');
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
export function Editor({state, setState, onResolve, onCancel}: EditorProps) {
|
||||
const [proxyState, setProxyState] = useState<'unresolved'|'command'|'resolved'>('unresolved');
|
||||
const [needCommand, setNeedCommand] = useState(false);
|
||||
const onMyResolve = (editorState: EditorState) => {
|
||||
setState(editorState);
|
||||
if (editorState.resolved) {
|
||||
setProxyState('command');
|
||||
setNeedCommand(true);
|
||||
}
|
||||
else {
|
||||
setProxyState('unresolved');
|
||||
// unresolved
|
||||
setNeedCommand(false);
|
||||
onResolve(editorState); // pass up the fact that we're unresolved
|
||||
}
|
||||
}
|
||||
// const onMyCancel
|
||||
const onCommand = (e: React.KeyboardEvent) => {
|
||||
const type = getType(state.resolved);
|
||||
if (e.key === "c" && getSymbol(type) === symbolFunction) {
|
||||
e.preventDefault();
|
||||
const commands = getCommands(type);
|
||||
if (!commands.includes(e.key)) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
setNeedCommand(false);
|
||||
// u -> pass Up
|
||||
if (e.key === "u" || e.key === "Enter" || e.key === "Tab") {
|
||||
onResolve(state);
|
||||
return;
|
||||
}
|
||||
// c -> Call
|
||||
if (e.key === "c") {
|
||||
// we become CallBlock
|
||||
setState({
|
||||
kind: "call",
|
||||
|
|
@ -74,11 +95,24 @@ export function Editor({state, setState, onResolve, onCancel}: EditorProps) {
|
|||
resolved: undefined,
|
||||
rollback: state,
|
||||
});
|
||||
setProxyState('resolved');
|
||||
return;
|
||||
}
|
||||
if (e.key === "u" || e.key === "Enter") {
|
||||
setProxyState('resolved');
|
||||
onResolve(state);
|
||||
// t -> Transform
|
||||
if (e.key === "t") {
|
||||
// we become CallBlock
|
||||
setState({
|
||||
kind: "call",
|
||||
env: state.env,
|
||||
fn: initialEditorState,
|
||||
input: state,
|
||||
resolved: undefined,
|
||||
rollback: state,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (e.key === "Backspace" || e.key === "ArrowLeft") {
|
||||
focusPrevElement();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -100,7 +134,7 @@ export function Editor({state, setState, onResolve, onCancel}: EditorProps) {
|
|||
(state.resolved)
|
||||
? <div className="typeSignature">
|
||||
:: <Type type={getType(state.resolved)} />
|
||||
{ (proxyState === 'command')
|
||||
{ (needCommand)
|
||||
? <input autoFocus={true} className="editable command" placeholder="<enter command>" onKeyDown={onCommand} value=""/>
|
||||
: <></>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,11 @@
|
|||
font-variation-settings: "wdth" 100;
|
||||
|
||||
}
|
||||
.suggestions {
|
||||
.suggestions {
|
||||
display: none;
|
||||
}
|
||||
.suggestions {
|
||||
display: block;
|
||||
text-align: left;
|
||||
position: absolute;
|
||||
border: solid 1px dodgerblue;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ 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";
|
||||
|
|
@ -27,9 +28,6 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt
|
|||
const ref = useRef<any>(null);
|
||||
useEffect(() => {
|
||||
ref.current?.focus();
|
||||
if (ref.current) {
|
||||
// ref.current.textContent = text;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const [i, setI] = useState(0); // selected suggestion
|
||||
|
|
@ -111,6 +109,10 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt
|
|||
setRightMostCaretPosition(ref.current);
|
||||
e.preventDefault();
|
||||
}
|
||||
else {
|
||||
onSelectSuggestion(suggestions[i]);
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
},
|
||||
ArrowDown: () => {
|
||||
|
|
@ -149,21 +151,38 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt
|
|||
|
||||
return <span>
|
||||
<span className="">
|
||||
<input ref={ref} placeholder="start typing..." autoFocus={true} className="editable" value={text} onInput={onInput} onKeyDown={onKeyDown} onFocus={() => setHaveFocus(true)} onBlur={() => setHaveFocus(false)}/>
|
||||
<input ref={ref} placeholder="start typing..." className="editable" value={text} onInput={onInput} onKeyDown={onKeyDown} onFocus={() => setHaveFocus(true)} onBlur={() => setTimeout(() => setHaveFocus(false), 200)}/>
|
||||
<span className="text-block suggest">{singleSuggestion}</span>
|
||||
</span>
|
||||
{
|
||||
(haveFocus)
|
||||
? <Suggestions suggestions={suggestions} onSelect={onSelectSuggestion} i={i} setI={setI} />
|
||||
: <></>
|
||||
}
|
||||
<ShowIf cond={haveFocus}>
|
||||
<Suggestions
|
||||
suggestions={suggestions}
|
||||
onSelect={onSelectSuggestion}
|
||||
i={i} setI={setI} />
|
||||
</ShowIf>
|
||||
</span>;
|
||||
}
|
||||
|
||||
function Suggestions({ suggestions, onSelect, i, setI }) {
|
||||
const onMouseEnter = j => () => {
|
||||
setI(j);
|
||||
};
|
||||
const onMouseDown = j => () => {
|
||||
setI(j);
|
||||
onSelect(suggestions[i]);
|
||||
};
|
||||
|
||||
return (suggestions.length > 0) ?
|
||||
<div className="suggestions">
|
||||
{suggestions.map(([name, dynamic], j) => <div key={`${j}_${name}`} className={i === j ? "selected" : ""} onClick={() => onSelect(suggestions[i])}>{name} :: <Type type={getType(dynamic)} /></div>)}
|
||||
<div className={"suggestions"}>
|
||||
{suggestions.map(([name, dynamic], j) =>
|
||||
<div
|
||||
key={`${j}_${name}`}
|
||||
className={i === j ? "selected" : ""}
|
||||
onMouseEnter={onMouseEnter(j)}
|
||||
onMouseDown={onMouseDown(j)}>
|
||||
{name} :: <Type type={getType(dynamic)} />
|
||||
</div>)
|
||||
}
|
||||
</div>
|
||||
: <></>;
|
||||
}
|
||||
9
src/ShowIf.tsx
Normal file
9
src/ShowIf.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// syntactic sugar
|
||||
export function ShowIf({cond, children}) {
|
||||
if (cond) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
else {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import {getType, getInst, getSymbol, Double, Int, symbolFunction, symbolProduct, symbolSum, symbolDict, symbolSet, symbolList, eqType, match} from "dope2";
|
||||
import {getType, getInst, getSymbol, Double, Int, symbolFunction, symbolProduct, symbolSum, symbolDict, symbolSet, symbolList, eqType, match, getLeft, getRight} from "dope2";
|
||||
|
||||
import "./Value.css";
|
||||
|
||||
|
|
@ -20,6 +20,9 @@ export function Value({dynamic}) {
|
|||
// return <BinaryType type={type} cssClass="productType" infix="⨯" prefix="" suffix=""/>;
|
||||
case symbolSum:
|
||||
return <ValueSum val={inst} leftType={type.params[0](type)} rightType={type.params[1](type)}/>;
|
||||
case symbolProduct:
|
||||
return <ValueProduct val={inst} leftType={type.params[0](type)} rightType={type.params[1](type)}/>;
|
||||
|
||||
// case symbolDict:
|
||||
// return <BinaryType type={type} cssClass="dictType" infix="⇒" prefix="{" suffix="}"/>;
|
||||
// case symbolSet:
|
||||
|
|
@ -51,4 +54,7 @@ function ValueSum({val, leftType, rightType}) {
|
|||
return match(val)
|
||||
(l => <>L <Value dynamic={{i:l, t:leftType}}/></>)
|
||||
(r => <>R <Value dynamic={{i:r, t:rightType}}/></>);
|
||||
}
|
||||
function ValueProduct({val, leftType, rightType}) {
|
||||
return <>(<Value dynamic={{i:getLeft(val), t:leftType}}/>, <Value dynamic={{i:getRight(val), t:rightType}} />)</>;
|
||||
}
|
||||
|
|
@ -10,4 +10,9 @@ body {
|
|||
font-variation-settings:
|
||||
"wdth" 100;
|
||||
|
||||
}
|
||||
|
||||
:focus-within:not(body) {
|
||||
/* outline: 2px solid black; */
|
||||
/* background-color: aqua; */
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue