61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
import React, {
|
||
FunctionComponent, Fragment,
|
||
ReactNode, ChangeEvent,
|
||
useState, useEffect
|
||
} from "react";
|
||
import * as style from "./style.module.css";
|
||
|
||
export interface EditableTextProps {
|
||
value: string
|
||
class?: string
|
||
editIconClass?: string
|
||
onChange?: (value: string) => void
|
||
render?: (value: string) => ReactNode
|
||
}
|
||
|
||
const EditableText: FunctionComponent<EditableTextProps> = ({ onChange, value, render, ...props }) => {
|
||
const [ internalValue, setInternalValue ] = useState(value);
|
||
const [ editMode, setEditMode ] = useState(false);
|
||
|
||
useEffect(() => {
|
||
setInternalValue(value);
|
||
}, [value])
|
||
|
||
const onEditIconClick = () => {
|
||
setEditMode(true);
|
||
};
|
||
|
||
const onValidateButtonClick = () => {
|
||
setEditMode(false);
|
||
if (internalValue === value) return;
|
||
if (onChange) onChange(internalValue);
|
||
}
|
||
|
||
const onValueChange = (evt: ChangeEvent) => {
|
||
const currentTarget = evt.currentTarget as HTMLInputElement;
|
||
setInternalValue(currentTarget.value);
|
||
};
|
||
|
||
return (
|
||
<div className={`${style.editableText} ${props.class ? props.class : ''}`}>
|
||
{
|
||
editMode ?
|
||
<div className="field has-addons">
|
||
<div className="control">
|
||
<input className="input is-expanded" type="text" value={internalValue} onChange={onValueChange} />
|
||
</div>
|
||
<div className="control">
|
||
<a className="button" onClick={onValidateButtonClick}>✔️</a>
|
||
</div>
|
||
</div> :
|
||
<Fragment>
|
||
{ render ? render(internalValue) : <span>{internalValue}</span> }
|
||
<i className={`${style.editIcon} icon ${props.editIconClass ? props.editIconClass : ''}`} onClick={onEditIconClick}>🖋️</i>
|
||
</Fragment>
|
||
}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default EditableText;
|