Files
IronCalc/webapp/app.ironcalc.com/frontend/src/components/WorkbookTitle.tsx
2025-03-12 18:24:15 +01:00

119 lines
2.6 KiB
TypeScript

import styled from "@emotion/styled";
import {
type ChangeEvent,
useEffect,
useLayoutEffect,
useRef,
useState,
} from "react";
// This element has a in situ editable text
// We use a virtual element to compute the size of the input
export function WorkbookTitle(properties: {
name: string;
onNameChange: (name: string) => void;
maxWidth: number;
}) {
const [width, setWidth] = useState(0);
const [name, setName] = useState(properties.name);
const mirrorDivRef = useRef<HTMLDivElement>(null);
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setName(event.target.value);
if (mirrorDivRef.current) {
setWidth(mirrorDivRef.current.scrollWidth);
}
};
useEffect(() => {
setName(properties.name);
}, [properties.name]);
// biome-ignore lint/correctness/useExhaustiveDependencies: We need to change the width with every value change
useLayoutEffect(() => {
if (mirrorDivRef.current) {
setWidth(mirrorDivRef.current.scrollWidth);
}
}, [name]);
return (
<Container
style={{
width: Math.min(width, properties.maxWidth),
}}
>
<TitleInput
value={name}
onInput={handleChange}
onBlur={(event) => {
properties.onNameChange(event.target.value);
}}
onKeyDown={(event) => {
switch (event.key) {
case "Enter": {
// If we hit "Enter" finish editing
event.currentTarget.blur();
break;
}
case "Escape": {
// revert changes
setName(properties.name);
break;
}
}
}}
style={{ width: Math.min(width, properties.maxWidth) }}
spellCheck="false"
/>
<MirrorDiv ref={mirrorDivRef}>{name}</MirrorDiv>
</Container>
);
}
const Container = styled("div")`
text-align: center;
padding: 8px;
font-size: 14px;
font-weight: 700;
font-family: Inter;
`;
const MirrorDiv = styled("div")`
position: absolute;
top: -9999px;
left: -9999px;
white-space: pre-wrap;
text-wrap: nowrap;
visibility: hidden;
font-family: inherit;
font-size: inherit;
line-height: inherit;
padding: inherit;
border: inherit;
`;
const TitleInput = styled("input")`
vertical-align: middle;
text-align: center;
height: 20px;
line-height: 20px;
border-radius: 4px;
padding: inherit;
outline: none;
resize: none;
text-wrap: nowrap;
border: none;
&:hover {
background-color: #f2f2f2;
}
&:focus {
border: 1px solid grey;
}
font-weight: inherit;
font-family: inherit;
font-size: inherit;
overflow: ellipsis;
white-space: nowrap;
`;