2017-06-12 04:18:32 +00:00
|
|
|
import React, { PureComponent } from 'react';
|
2018-04-05 19:13:32 +00:00
|
|
|
import { stringWidth } from 'utils';
|
2017-06-12 04:18:32 +00:00
|
|
|
|
|
|
|
export default class Editable extends PureComponent {
|
2017-06-21 05:23:07 +00:00
|
|
|
static defaultProps = {
|
|
|
|
editable: true
|
|
|
|
};
|
|
|
|
|
|
|
|
state = {
|
|
|
|
editing: false
|
|
|
|
};
|
|
|
|
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
if (this.state.editing && nextProps.value !== this.props.value) {
|
|
|
|
this.setState({
|
|
|
|
width: this.getInputWidth(nextProps.value)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps, prevState) {
|
|
|
|
if (!prevState.editing && this.state.editing) {
|
|
|
|
// eslint-disable-next-line react/no-did-update-set-state
|
|
|
|
this.setState({
|
|
|
|
width: this.getInputWidth(this.props.value)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getInputWidth(value) {
|
|
|
|
if (this.input) {
|
|
|
|
const style = window.getComputedStyle(this.input);
|
2018-04-05 23:46:22 +00:00
|
|
|
const padding =
|
|
|
|
parseInt(style.paddingLeft, 10) + parseInt(style.paddingRight, 10);
|
2017-06-21 05:23:07 +00:00
|
|
|
// Make sure the width is atleast 1px so the caret always shows
|
|
|
|
const width = stringWidth(value, style.font) || 1;
|
|
|
|
return padding + width;
|
|
|
|
}
|
|
|
|
}
|
2017-06-12 04:18:32 +00:00
|
|
|
|
|
|
|
startEditing = () => {
|
|
|
|
if (this.props.editable) {
|
|
|
|
this.initialValue = this.props.value;
|
|
|
|
this.setState({ editing: true });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
stopEditing = () => {
|
|
|
|
const { validate, value, onChange } = this.props;
|
|
|
|
if (validate && !validate(value)) {
|
|
|
|
onChange(this.initialValue);
|
|
|
|
}
|
|
|
|
this.setState({ editing: false });
|
|
|
|
};
|
|
|
|
|
2017-06-21 05:23:07 +00:00
|
|
|
handleBlur = e => {
|
|
|
|
const { onBlur } = this.props;
|
|
|
|
this.stopEditing();
|
|
|
|
if (onBlur) {
|
|
|
|
onBlur(e.target.value);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
handleChange = e => this.props.onChange(e.target.value);
|
|
|
|
|
2017-06-12 04:18:32 +00:00
|
|
|
handleKey = e => {
|
|
|
|
if (e.key === 'Enter') {
|
2017-06-21 05:23:07 +00:00
|
|
|
this.handleBlur(e);
|
2017-06-12 04:18:32 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-05 23:46:22 +00:00
|
|
|
inputRef = el => {
|
|
|
|
this.input = el;
|
|
|
|
};
|
2017-06-12 04:18:32 +00:00
|
|
|
|
|
|
|
render() {
|
|
|
|
const { children, className, value } = this.props;
|
2017-06-21 05:23:07 +00:00
|
|
|
|
|
|
|
const style = {
|
|
|
|
width: this.state.width
|
|
|
|
};
|
|
|
|
|
2018-04-05 23:46:22 +00:00
|
|
|
return this.state.editing ? (
|
|
|
|
<input
|
|
|
|
autoFocus
|
|
|
|
ref={this.inputRef}
|
|
|
|
className={className}
|
|
|
|
type="text"
|
|
|
|
value={value}
|
|
|
|
onBlur={this.handleBlur}
|
|
|
|
onChange={this.handleChange}
|
|
|
|
onKeyDown={this.handleKey}
|
|
|
|
style={style}
|
|
|
|
spellCheck={false}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
<div onClick={this.startEditing}>{children}</div>
|
2017-06-12 04:18:32 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|