Java's third mode button, the sidebar's text and structure field (dropped with its middle under the pointer), items over the network that are moved, resized by Java's rule, typed into on their Y.Text, coloured and set in size and style from Java's menu, deleted; Java's paint per mode, measured in F4.8a. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
80 lines
2.7 KiB
TypeScript
80 lines
2.7 KiB
TypeScript
// The field a focused text is typed into (Java's JDocuTextArea): over the
|
|
// text, at its size, in its font and colour, scaled with the view. It
|
|
// follows the document's Y.Text — what others type comes in, the caret kept —
|
|
// and writes what is typed with this editor's origin. Escape ends the focus;
|
|
// Ctrl+Delete removes the text, as in Java (Delete alone types).
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
import type * as Y from 'yjs';
|
|
import { replaceText, type DocumentationView } from '@mdg-filius/doc';
|
|
import type { ScreenFrame } from '../canvas/Canvas.js';
|
|
import { fontFamily, textColor } from '../canvas/documentationPaint.js';
|
|
import { followText } from '../desktop/textBinding.js';
|
|
import { useTranslate } from '../i18n/i18n.js';
|
|
|
|
export interface DocumentationTextProps {
|
|
readonly item: DocumentationView;
|
|
readonly frame: ScreenFrame;
|
|
readonly text: Y.Text;
|
|
readonly origin: object;
|
|
readonly onDone: () => void;
|
|
readonly onRemove: () => void;
|
|
}
|
|
|
|
export function DocumentationText({ item, frame, text, origin, onDone, onRemove }: DocumentationTextProps) {
|
|
const t = useTranslate();
|
|
const area = useRef<HTMLTextAreaElement>(null);
|
|
useEffect(() => {
|
|
const element = area.current;
|
|
if (element === null) return undefined;
|
|
const stop = followText(element, text, origin);
|
|
// Java gives a text the focus when it gets it (a new one at once).
|
|
element.focus();
|
|
return stop;
|
|
}, [text, origin]);
|
|
return (
|
|
<textarea
|
|
ref={area}
|
|
aria-label={t('docu.text')}
|
|
data-doc-text={item.id}
|
|
spellCheck={false}
|
|
onInput={(event) => {
|
|
const next = event.currentTarget.value;
|
|
text.doc?.transact(() => replaceText(text, next), origin);
|
|
}}
|
|
onKeyDown={(event) => {
|
|
if (event.key === 'Escape') {
|
|
event.preventDefault();
|
|
onDone();
|
|
} else if (event.key === 'Delete' && (event.ctrlKey || event.metaKey)) {
|
|
event.preventDefault();
|
|
onRemove();
|
|
}
|
|
}}
|
|
style={{
|
|
position: 'absolute',
|
|
left: frame.left,
|
|
top: frame.top,
|
|
width: item.width,
|
|
height: item.height,
|
|
transform: `scale(${frame.scale})`,
|
|
transformOrigin: 'top left',
|
|
boxSizing: 'border-box',
|
|
margin: 0,
|
|
padding: 1,
|
|
border: 'none',
|
|
outline: 'none',
|
|
resize: 'none',
|
|
overflow: 'hidden',
|
|
background: 'transparent',
|
|
color: textColor(item.color),
|
|
font: `${item.italic ? 'italic ' : ''}${item.bold ? 'bold ' : ''}${item.size}px ${fontFamily(item.font)}`,
|
|
lineHeight: 'normal',
|
|
whiteSpace: 'pre-wrap',
|
|
overflowWrap: 'break-word',
|
|
zIndex: 3,
|
|
}}
|
|
/>
|
|
);
|
|
}
|