cleanup CSS a bit

This commit is contained in:
Joeri Exelmans 2025-05-28 11:24:37 +02:00
parent 955bb17f9a
commit dcd213102f
11 changed files with 113 additions and 79 deletions

View file

@ -40,6 +40,11 @@ footer {
color: white; color: white;
} }
footer ::selection {
color: dodgerblue;
background: lightblue;
}
footer a { footer a {
color: white; color: white;
} }

View file

@ -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') : '')}>

View file

@ -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;

View file

@ -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=""

View file

@ -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);

View file

@ -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,29 +145,38 @@ export function InputBlock({ state, setState, score, onCancel, typeInfo }: Input
}, },
}; };
return <><Input const err = typeInfo.err || evalExpr(state, env).err;
placeholder="<name or literal>"
onCancel={onCancel} return <>
onEnter={onSelectSuggestion} <Input
onTextChange={onTextChange} placeholder="<name or literal>"
text={text} onCancel={onCancel}
suggestion={singleSuggestion} onEnter={onSelectSuggestion}
extraHandlers={extraHandlers} onTextChange={onTextChange}
> text={text}
<span className="suggestionsPlaceholder"> suggestion={singleSuggestion}
<Suggestions extraHandlers={extraHandlers}
suggestions={suggestions} >
onSelect={onSelectSuggestion} <span className="dropdownContainer">
i={i} setI={setI} /> <span className="dropdown">
{(err !== undefined) &&
(<div className="errorMessage">
{err.message.trim()}
</div>)}
<Suggestions
suggestions={suggestions}
onSelect={onSelectSuggestion}
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,

View 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;
}

View file

@ -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}
/> />
</span>; {(focus === "yes") && children}
</span>;
} }

View file

@ -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;
} }

View file

@ -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>;
} }

View file

@ -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;
}