@pie-lib/text-select
Version:
Some react components for text selection
150 lines (121 loc) • 3.7 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import Controls from './controls';
import { withStyles } from '@material-ui/core/styles';
import { words, sentences, paragraphs } from './builder';
import clone from 'lodash/clone';
import isEqual from 'lodash/isEqual';
import differenceWith from 'lodash/differenceWith';
import classNames from 'classnames';
import { noSelect } from '@pie-lib/style-utils';
import TokenText from './token-text';
export class Tokenizer extends React.Component {
static propTypes = {
text: PropTypes.string.isRequired,
tokens: PropTypes.arrayOf(
PropTypes.shape({
text: PropTypes.string,
correct: PropTypes.bool,
start: PropTypes.number,
end: PropTypes.number,
}),
),
classes: PropTypes.object.isRequired,
className: PropTypes.string,
onChange: PropTypes.func.isRequired,
};
static defaultProps = {};
constructor(props) {
super(props);
this.state = {
setCorrectMode: false,
mode: '',
};
}
onChangeHandler = (token, mode) => {
this.props.onChange(token, mode);
this.setState({
mode,
});
};
toggleCorrectMode = () => this.setState({ setCorrectMode: !this.state.setCorrectMode });
clear = () => {
this.onChangeHandler([], '');
};
buildTokens = (type, fn) => {
const { text } = this.props;
const tokens = fn(text);
this.onChangeHandler(tokens, type);
};
selectToken = (newToken, tokensToRemove) => {
const { tokens } = this.props;
const update = differenceWith(clone(tokens), tokensToRemove, isEqual);
update.push(newToken);
this.onChangeHandler(update, this.state.mode);
};
tokenClick = (token) => {
const { setCorrectMode } = this.state;
if (setCorrectMode) {
this.setCorrect(token);
} else {
this.removeToken(token);
}
};
tokenIndex = (token) => {
const { tokens } = this.props;
return tokens.findIndex((t) => {
return t.text == token.text && t.start == token.start && t.end == token.end;
});
};
setCorrect = (token) => {
const { tokens } = this.props;
const index = this.tokenIndex(token);
if (index !== -1) {
const t = tokens[index];
t.correct = !t.correct;
const update = clone(tokens);
update.splice(index, 1, t);
this.onChangeHandler(update, this.state.mode);
}
};
removeToken = (token) => {
const { tokens } = this.props;
const index = this.tokenIndex(token);
if (index !== -1) {
const update = clone(tokens);
update.splice(index, 1);
this.onChangeHandler(update, this.state.mode);
}
};
render() {
const { text, tokens, classes, className } = this.props;
const { setCorrectMode } = this.state;
const tokenClassName = classNames(classes.text, setCorrectMode && classes.noselect);
const rootName = classNames(classes.tokenizer, className);
return (
<div className={rootName}>
<Controls
onClear={this.clear}
onWords={() => this.buildTokens('words', words)}
onSentences={() => this.buildTokens('sentence', sentences)}
onParagraphs={() => this.buildTokens('paragraphs', paragraphs)}
setCorrectMode={setCorrectMode}
onToggleCorrectMode={this.toggleCorrectMode}
/>
<TokenText
className={tokenClassName}
text={text}
tokens={tokens}
onTokenClick={this.tokenClick}
onSelectToken={this.selectToken}
/>
</div>
);
}
}
export default withStyles(() => ({
text: {
whiteSpace: 'pre-wrap',
},
noselect: { ...noSelect() },
}))(Tokenizer);