2019-09-08 16:57:57 +00:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import React from 'react';
|
|
|
|
|
2020-04-29 22:34:37 +00:00
|
|
|
import EditIcon from '../../../images/pencil.svg';
|
2019-09-08 16:57:57 +00:00
|
|
|
|
2020-05-05 23:03:58 +00:00
|
|
|
// TODO I think this needs a description prop so that it's accessible
|
2019-09-08 16:57:57 +00:00
|
|
|
function EditableInput({
|
|
|
|
validate, value, emptyPlaceholder, InputComponent, inputProps, onChange
|
|
|
|
}) {
|
|
|
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
|
|
const [currentValue, setCurrentValue] = React.useState(value || '');
|
|
|
|
const displayValue = currentValue || emptyPlaceholder;
|
2020-01-15 10:45:37 +00:00
|
|
|
const hasValue = currentValue !== '';
|
|
|
|
const classes = `editable-input editable-input--${isEditing ? 'is-editing' : 'is-not-editing'} editable-input--${hasValue ? 'has-value' : 'has-placeholder'}`;
|
2019-09-08 16:57:57 +00:00
|
|
|
const inputRef = React.createRef();
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
if (isEditing) {
|
|
|
|
inputRef.current.focus();
|
|
|
|
}
|
|
|
|
}, [isEditing]);
|
|
|
|
|
|
|
|
function beginEditing() {
|
|
|
|
setIsEditing(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
function doneEditing() {
|
|
|
|
setIsEditing(false);
|
|
|
|
|
|
|
|
const isValid = typeof validate === 'function' && validate(currentValue);
|
|
|
|
|
|
|
|
if (isValid) {
|
|
|
|
onChange(currentValue);
|
|
|
|
} else {
|
|
|
|
setCurrentValue(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateValue(event) {
|
|
|
|
setCurrentValue(event.target.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
function checkForKeyAction(event) {
|
|
|
|
if (event.key === 'Enter') {
|
|
|
|
doneEditing();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<span className={classes}>
|
2020-05-05 23:03:58 +00:00
|
|
|
<button
|
|
|
|
className="editable-input__label"
|
|
|
|
onClick={beginEditing}
|
|
|
|
aria-label={`Edit ${displayValue} value`}
|
|
|
|
>
|
2019-09-08 16:57:57 +00:00
|
|
|
<span>{displayValue}</span>
|
2020-05-05 23:03:58 +00:00
|
|
|
<EditIcon className="editable-input__icon" focusable="false" aria-hidden="true" />
|
2019-09-08 16:57:57 +00:00
|
|
|
</button>
|
|
|
|
|
|
|
|
<InputComponent
|
|
|
|
className="editable-input__input"
|
|
|
|
type="text"
|
|
|
|
{...inputProps}
|
|
|
|
disabled={!isEditing}
|
|
|
|
onBlur={doneEditing}
|
|
|
|
onChange={updateValue}
|
|
|
|
onKeyPress={checkForKeyAction}
|
|
|
|
ref={inputRef}
|
|
|
|
value={currentValue}
|
|
|
|
/>
|
|
|
|
</span >
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
EditableInput.defaultProps = {
|
|
|
|
emptyPlaceholder: 'No value',
|
|
|
|
InputComponent: 'input',
|
|
|
|
inputProps: {},
|
|
|
|
validate: () => true,
|
|
|
|
value: '',
|
|
|
|
};
|
|
|
|
|
|
|
|
EditableInput.propTypes = {
|
|
|
|
emptyPlaceholder: PropTypes.string,
|
|
|
|
InputComponent: PropTypes.elementType,
|
|
|
|
// eslint-disable-next-line react/forbid-prop-types
|
|
|
|
inputProps: PropTypes.object,
|
|
|
|
onChange: PropTypes.func.isRequired,
|
|
|
|
validate: PropTypes.func,
|
|
|
|
value: PropTypes.string,
|
|
|
|
};
|
|
|
|
|
|
|
|
export default EditableInput;
|