Files
nand2tetris/web-ide-main/components/src/inline_edit.tsx
T
2026-04-09 14:14:56 +02:00

86 lines
2.0 KiB
TypeScript

import { width } from "@davidsouther/jiffies/lib/esm/dom/css/sizing.js";
import { useCallback, useState } from "react";
import { useStateInitializer } from "./react.js";
import { Action } from "@nand2tetris/simulator/types.js";
const Mode = { VIEW: 0, EDIT: 1 };
export const InlineEdit = (props: {
mode?: keyof typeof Mode;
value: string;
highlight: boolean;
onChange: Action<string>;
onFocus?: () => void;
}) => {
const [mode, setMode] = useState(props.mode ?? Mode.VIEW);
const [value, setValue] = useStateInitializer(props.value);
const render = () => {
switch (mode) {
case Mode.EDIT:
return edit();
case Mode.VIEW:
return view();
default:
return <span />;
}
};
const view = () => (
<div
style={{
cursor: "text",
...width("full", "inline"),
}}
onClick={() => {
setMode(Mode.EDIT);
}}
>
{value}&nbsp;
</div>
);
const doSelect = useCallback(
(ref: HTMLInputElement | null) => ref?.select(),
[],
);
const doChange = useCallback(
(target: HTMLInputElement) => {
setMode(Mode.VIEW);
setValue(target.value ?? "");
props.onChange(target.value ?? "");
},
[props, setMode, setValue],
);
const edit = () => {
const edit = (
<span style={{ display: "block", position: "relative" }}>
<input
ref={doSelect}
style={{
zIndex: "10",
position: "absolute",
left: "0",
marginTop: "-0.375rem",
color: "var(--text-color)",
}}
onFocus={props.onFocus}
onBlur={({ target }) => doChange(target)}
onKeyPress={({ key, target }) => {
if (key === "Enter") {
doChange(target as HTMLInputElement);
}
}}
type="text"
defaultValue={value}
/>
</span>
);
return edit;
};
return render();
};
export default InlineEdit;