cleanup CSS a bit
This commit is contained in:
parent
955bb17f9a
commit
dcd213102f
11 changed files with 113 additions and 79 deletions
|
|
@ -40,6 +40,11 @@ footer {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
footer ::selection {
|
||||||
|
color: dodgerblue;
|
||||||
|
background: lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
footer a {
|
footer a {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,11 +142,35 @@ export function App() {
|
||||||
// dynamic evalutions
|
// dynamic evalutions
|
||||||
const evalResult = evalExpr(currentState, extendedEnv);
|
const evalResult = evalExpr(currentState, extendedEnv);
|
||||||
|
|
||||||
|
const onCopy = () => {
|
||||||
|
const serialized = JSON.stringify(currentState);
|
||||||
|
navigator.clipboard.write([new ClipboardItem({"text/plain": serialized})]);
|
||||||
|
}
|
||||||
|
const onPaste = () => {
|
||||||
|
navigator.clipboard.read().then(items => {
|
||||||
|
if (items.length === 1) {
|
||||||
|
items[0].getType("text/plain")
|
||||||
|
.then(value => value.text())
|
||||||
|
.then(text => {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(text);
|
||||||
|
pushHistory(_ => parsed);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header>
|
<header>
|
||||||
<button disabled={appState.history.length===1} onClick={onUndo}>Undo ({appState.history.length-1}) <kbd>Ctrl</kbd>+<kbd>Z</kbd></button>
|
<button disabled={appState.history.length===1} onClick={onUndo}>Undo ({appState.history.length-1}) <kbd>Ctrl</kbd>+<kbd>Z</kbd></button>
|
||||||
<button disabled={appState.future.length===0} onClick={onRedo}>Redo ({appState.future.length}) <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd></button>
|
<button disabled={appState.future.length===0} onClick={onRedo}>Redo ({appState.future.length}) <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd></button>
|
||||||
|
<button onClick={onCopy}>Copy <kbd>Ctrl</kbd>+<kbd>C</kbd></button>
|
||||||
|
<button onClick={onPaste}>Paste <kbd>Ctrl</kbd>+<kbd>V</kbd></button>
|
||||||
{
|
{
|
||||||
actionShortcuts.map(([_, keys, descr], i) =>
|
actionShortcuts.map(([_, keys, descr], i) =>
|
||||||
<span key={i} className={'command' + (highlighted[i] ? (' highlighted') : '')}>
|
<span key={i} className={'command' + (highlighted[i] ? (' highlighted') : '')}>
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,11 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.editor.error {
|
.editor.error {
|
||||||
border: 1px solid red;
|
border: 1px solid red !important;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.errorMessage {
|
.errorMessage {
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
color: darkred;
|
color: darkred;
|
||||||
background-color: pink;
|
background-color: pink;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
|
@ -20,11 +18,6 @@
|
||||||
width: max-content;
|
width: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor:hover > .errorMessage {
|
|
||||||
display: block;
|
|
||||||
/* z-index: 9999; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor.unknown {
|
.editor.unknown {
|
||||||
border: 1px dashed dodgerblue;
|
border: 1px dashed dodgerblue;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,14 @@ export function ExprBlock(props: ExprBlockProps) {
|
||||||
const actions = getActions(globalContext, props.setState);
|
const actions = getActions(globalContext, props.setState);
|
||||||
const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) =>
|
const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) =>
|
||||||
[shortcut, (e) => { e.preventDefault(); action(); }]));
|
[shortcut, (e) => { e.preventDefault(); action(); }]));
|
||||||
const err = props.typeInfo.err || evalExpr(props.state, env).err;
|
const evalResult = evalExpr(props.state, env);
|
||||||
return <span className={"editor" + (err ? " error" : "")}>
|
const err = props.typeInfo.err || evalResult.err;
|
||||||
|
return <span className={"editor" + (err ? " error" : "") + ((evalResult.val === undefined) ? " unknown" : "")}>
|
||||||
{renderBlock[props.state.kind]()}
|
{renderBlock[props.state.kind]()}
|
||||||
{(err !== undefined) &&
|
{/* {(err !== undefined) &&
|
||||||
(<div className="errorMessage">
|
(<div className="errorMessage">
|
||||||
{err.message.trim()}
|
{err.message.trim()}
|
||||||
</div>)}
|
</div>)} */}
|
||||||
<Input
|
<Input
|
||||||
placeholder="<c>"
|
placeholder="<c>"
|
||||||
text=""
|
text=""
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,7 @@
|
||||||
.inputBlock {
|
.suggestionsScrollableBox {
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.editable {
|
|
||||||
position: relative;
|
|
||||||
border: 0;
|
|
||||||
font-size: 10pt;
|
|
||||||
font-family: var(--my-monospace-font);
|
|
||||||
background-color: transparent;
|
|
||||||
color: inherit;
|
|
||||||
padding: 0;
|
|
||||||
cursor: text;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.suggest {
|
|
||||||
top: 2.4px;
|
|
||||||
position: absolute;
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
.suggestionsPlaceholder {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
vertical-align: bottom;
|
|
||||||
}
|
|
||||||
.suggestions {
|
|
||||||
display: block;
|
display: block;
|
||||||
color: black;
|
color: black;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
position: absolute;
|
|
||||||
border: solid 1px dodgerblue;
|
border: solid 1px dodgerblue;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
max-height: calc(100vh - 64px);
|
max-height: calc(100vh - 64px);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { Type as TypeBlock, TypeInfoBlock } from "../other/Type";
|
||||||
import type { ExprBlockState, State2Props } from "./ExprBlock";
|
import type { ExprBlockState, State2Props } from "./ExprBlock";
|
||||||
|
|
||||||
import "./InputBlock.css";
|
import "./InputBlock.css";
|
||||||
|
import { evalExpr } from "../../eval/eval";
|
||||||
|
|
||||||
interface Literal {
|
interface Literal {
|
||||||
kind: "literal";
|
kind: "literal";
|
||||||
|
|
@ -95,7 +96,6 @@ export function InputBlock({ state, setState, score, onCancel, typeInfo }: Input
|
||||||
const singleSuggestion = trie.growPrefix(env.names)(text);
|
const singleSuggestion = trie.growPrefix(env.names)(text);
|
||||||
const suggestions = useMemo(() => computeSuggestions(text, env, score), [text, score, env]);
|
const suggestions = useMemo(() => computeSuggestions(text, env, score), [text, score, env]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (focus) {
|
if (focus) {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
|
|
@ -145,7 +145,10 @@ export function InputBlock({ state, setState, score, onCancel, typeInfo }: Input
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return <><Input
|
const err = typeInfo.err || evalExpr(state, env).err;
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<Input
|
||||||
placeholder="<name or literal>"
|
placeholder="<name or literal>"
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onEnter={onSelectSuggestion}
|
onEnter={onSelectSuggestion}
|
||||||
|
|
@ -154,20 +157,26 @@ export function InputBlock({ state, setState, score, onCancel, typeInfo }: Input
|
||||||
suggestion={singleSuggestion}
|
suggestion={singleSuggestion}
|
||||||
extraHandlers={extraHandlers}
|
extraHandlers={extraHandlers}
|
||||||
>
|
>
|
||||||
<span className="suggestionsPlaceholder">
|
<span className="dropdownContainer">
|
||||||
|
<span className="dropdown">
|
||||||
|
{(err !== undefined) &&
|
||||||
|
(<div className="errorMessage">
|
||||||
|
{err.message.trim()}
|
||||||
|
</div>)}
|
||||||
<Suggestions
|
<Suggestions
|
||||||
suggestions={suggestions}
|
suggestions={suggestions}
|
||||||
onSelect={onSelectSuggestion}
|
onSelect={onSelectSuggestion}
|
||||||
i={i} setI={setI} />
|
i={i} setI={setI} />
|
||||||
</span>
|
</span>
|
||||||
|
</span>
|
||||||
</Input>
|
</Input>
|
||||||
::<TypeInfoBlock typeInfo={typeInfo} />
|
::<TypeInfoBlock typeInfo={typeInfo} />
|
||||||
</>
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Suggestions({ suggestions, onSelect, i, setI }) {
|
function Suggestions({ suggestions, onSelect, i, setI }) {
|
||||||
return <>{(suggestions.length > 0) &&
|
return <>{(suggestions.length > 0) &&
|
||||||
<div className={"suggestions"}>
|
<div className={"suggestionsScrollableBox"}>
|
||||||
{suggestions.map((suggestion, j) =>
|
{suggestions.map((suggestion, j) =>
|
||||||
<SuggestionMemo key={j}
|
<SuggestionMemo key={j}
|
||||||
{...{setI, j,
|
{...{setI, j,
|
||||||
|
|
|
||||||
19
src/component/other/Input.css
Normal file
19
src/component/other/Input.css
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
.textboxContainer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.textbox {
|
||||||
|
position: relative;
|
||||||
|
border: 0;
|
||||||
|
font-size: 10pt;
|
||||||
|
font-family: var(--my-monospace-font);
|
||||||
|
background-color: transparent;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0;
|
||||||
|
cursor: text;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
.textbox.hint {
|
||||||
|
top: 2.4px;
|
||||||
|
position: absolute;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { useEffect, useRef, type ReactNode, type KeyboardEvent, useState } from "react";
|
import { useEffect, useRef, type ReactNode, type KeyboardEvent, useState } from "react";
|
||||||
|
|
||||||
|
import "./Input.css";
|
||||||
|
|
||||||
interface InputProps {
|
interface InputProps {
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
|
@ -128,12 +130,11 @@ export function Input({placeholder, text, suggestion, onTextChange, onEnter, onC
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return <span className="inputBlock">
|
return <span className="textboxContainer">
|
||||||
{(focus === "yes") && children}
|
<span className="textbox hint">{text}{focus && suggestion}</span>
|
||||||
<span className="editable suggest">{text}{focus && suggestion}</span>
|
|
||||||
<input ref={ref}
|
<input ref={ref}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
className="editable"
|
className="textbox"
|
||||||
value={text}
|
value={text}
|
||||||
onInput={(e) =>
|
onInput={(e) =>
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
@ -143,5 +144,6 @@ export function Input({placeholder, text, suggestion, onTextChange, onEnter, onC
|
||||||
onBlur={() => setFocus("no")}
|
onBlur={() => setFocus("no")}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
/>
|
/>
|
||||||
|
{(focus === "yes") && children}
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,11 @@
|
||||||
.type {
|
.type {
|
||||||
margin:1px;
|
margin:1px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-optical-sizing: auto;
|
font-optical-sizing: auto;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-variation-settings: "wdth" 100;
|
font-variation-settings: "wdth" 100;
|
||||||
|
|
||||||
color: darkgrey;
|
color: darkgrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +58,7 @@
|
||||||
|
|
||||||
.iteratorType {
|
.iteratorType {
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
/* animation: flickerAnimation 500ms steps(1) normal infinite; */
|
animation: flickerAnimation 500ms steps(1) normal infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animations */
|
/* Animations */
|
||||||
|
|
@ -74,25 +72,13 @@
|
||||||
|
|
||||||
.typeSignature {
|
.typeSignature {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeDebug {
|
.typeDebug {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.typeSignature:hover > .typeDebug {
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
white-space-collapse: preserve;
|
white-space-collapse: preserve;
|
||||||
width: max-content;
|
width: max-content;
|
||||||
background-color: #d2ebf1e0;
|
background-color: #d2ebf1e0;
|
||||||
color: black;
|
color: black;
|
||||||
font-family: var(--my-monospace-font);
|
font-family: var(--my-monospace-font);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
z-index: 100;
|
font-size: 10pt;
|
||||||
}
|
|
||||||
|
|
||||||
.editor:hover > .typeSignature {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,9 @@ import { ValueUnknown } from "./Value";
|
||||||
import type { TypeInfo } from "../../eval/infer_type";
|
import type { TypeInfo } from "../../eval/infer_type";
|
||||||
|
|
||||||
export function TypeInfoBlock({typeInfo}: {typeInfo: TypeInfo}) {
|
export function TypeInfoBlock({typeInfo}: {typeInfo: TypeInfo}) {
|
||||||
return <span className="typeSignature gotDebug">
|
return <span className="typeSignature mouseOver dropdownContainer">
|
||||||
<Type type={typeInfo.type}/>
|
<Type type={typeInfo.type}/>
|
||||||
<br/>
|
<span className="typeDebug hideIfMouseAway dropdown">{prettySS(typeInfo.subs)}</span>
|
||||||
<span className="typeDebug">{prettySS(typeInfo.subs)}</span>
|
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,24 @@ kbd {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
margin: 0 2px 0 2px;
|
margin: 0 2px 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.dropdownContainer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.dropdown {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.hideIfMouseAway {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.mouseOver:hover .dropdown {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
color: white;
|
||||||
|
background: dodgerblue;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue