117 lines
3.0 KiB
TypeScript
117 lines
3.0 KiB
TypeScript
|
|
/**
|
|||
|
|
* InlineTextEditor – Floating input overlay for non-blocking text editing.
|
|||
|
|
* Replaces window.prompt() for text element editing.
|
|||
|
|
*/
|
|||
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|||
|
|
|
|||
|
|
export interface InlineTextEditorProps {
|
|||
|
|
initialText: string;
|
|||
|
|
onSubmit: (text: string) => void;
|
|||
|
|
onCancel: () => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const InlineTextEditor: React.FC<InlineTextEditorProps> = ({ initialText, onSubmit, onCancel }) => {
|
|||
|
|
const [text, setText] = useState(initialText);
|
|||
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
inputRef.current?.focus();
|
|||
|
|
inputRef.current?.select();
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|||
|
|
if (e.key === 'Enter') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
onSubmit(text);
|
|||
|
|
} else if (e.key === 'Escape') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
onCancel();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleBlur = () => {
|
|||
|
|
onSubmit(text);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div
|
|||
|
|
style={{
|
|||
|
|
position: 'fixed',
|
|||
|
|
top: '50%',
|
|||
|
|
left: '50%',
|
|||
|
|
transform: 'translate(-50%, -50%)',
|
|||
|
|
zIndex: 10000,
|
|||
|
|
background: 'var(--color-bg-secondary, #2a2a2a)',
|
|||
|
|
border: '1px solid var(--color-border, #444)',
|
|||
|
|
borderRadius: '8px',
|
|||
|
|
padding: '12px 16px',
|
|||
|
|
boxShadow: '0 4px 20px rgba(0,0,0,0.4)',
|
|||
|
|
display: 'flex',
|
|||
|
|
flexDirection: 'column',
|
|||
|
|
gap: '8px',
|
|||
|
|
minWidth: '320px',
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<label
|
|||
|
|
style={{
|
|||
|
|
fontSize: '12px',
|
|||
|
|
color: 'var(--color-text-secondary, #aaa)',
|
|||
|
|
fontFamily: 'sans-serif',
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
Text eingeben
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
ref={inputRef}
|
|||
|
|
type="text"
|
|||
|
|
value={text}
|
|||
|
|
onChange={(e) => setText(e.target.value)}
|
|||
|
|
onKeyDown={handleKeyDown}
|
|||
|
|
onBlur={handleBlur}
|
|||
|
|
style={{
|
|||
|
|
width: '100%',
|
|||
|
|
padding: '8px 10px',
|
|||
|
|
fontSize: '14px',
|
|||
|
|
background: 'var(--color-bg-primary, #1a1a1a)',
|
|||
|
|
color: 'var(--color-text-primary, #fff)',
|
|||
|
|
border: '1px solid var(--color-border, #555)',
|
|||
|
|
borderRadius: '4px',
|
|||
|
|
outline: 'none',
|
|||
|
|
}}
|
|||
|
|
/>
|
|||
|
|
<div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
|
|||
|
|
<button
|
|||
|
|
onClick={onCancel}
|
|||
|
|
style={{
|
|||
|
|
padding: '6px 14px',
|
|||
|
|
fontSize: '13px',
|
|||
|
|
background: 'transparent',
|
|||
|
|
color: 'var(--color-text-secondary, #aaa)',
|
|||
|
|
border: '1px solid var(--color-border, #555)',
|
|||
|
|
borderRadius: '4px',
|
|||
|
|
cursor: 'pointer',
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
Abbrechen (Esc)
|
|||
|
|
</button>
|
|||
|
|
<button
|
|||
|
|
onClick={() => onSubmit(text)}
|
|||
|
|
style={{
|
|||
|
|
padding: '6px 14px',
|
|||
|
|
fontSize: '13px',
|
|||
|
|
background: 'var(--color-accent, #3498db)',
|
|||
|
|
color: '#fff',
|
|||
|
|
border: 'none',
|
|||
|
|
borderRadius: '4px',
|
|||
|
|
cursor: 'pointer',
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
Bestätigen (Enter)
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default InlineTextEditor;
|