//
// SPDX-License-Identifier: AGPL-3.0
-import * as React from 'react';
-import { Chips } from '~/components/chips/chips';
+import React from 'react';
+import { Chips } from 'components/chips/chips';
import { Input as MuiInput, withStyles, WithStyles } from '@material-ui/core';
import { StyleRulesCallback } from '@material-ui/core/styles';
import { InputProps } from '@material-ui/core/Input';
interface ChipsInputProps<Value> {
- value: Value[];
+ values: Value[];
getLabel?: (value: Value) => string;
onChange: (value: Value[]) => void;
+ onPartialInput?: (value: boolean) => void;
+ handleFocus?: (e: any) => void;
+ handleBlur?: (e: any) => void;
+ chipsClassName?: string;
createNewValue: (value: string) => Value;
inputComponent?: React.ComponentType<InputProps>;
inputProps?: InputProps;
deletable?: boolean;
orderable?: boolean;
+ disabled?: boolean;
+ pattern?: RegExp;
}
type CssRules = 'chips' | 'input' | 'inputContainer';
timeout = -1;
setText = (event: React.ChangeEvent<HTMLInputElement>) => {
- this.setState({ text: event.target.value });
+ this.setState({ text: event.target.value }, () => {
+ // Update partial input status
+ this.props.onPartialInput && this.props.onPartialInput(this.state.text !== '');
+
+ // If pattern is provided, check for delimiter
+ if (this.props.pattern) {
+ const matches = this.state.text.match(this.props.pattern);
+ // Only create values if 1 match and the last character is a delimiter
+ // (user pressed an invalid character at the end of a token)
+ // or if multiple matches (user pasted text)
+ if (matches &&
+ (
+ matches.length > 1 ||
+ (matches.length === 1 && !this.state.text.endsWith(matches[0]))
+ )) {
+ this.createNewValue(matches.map((i) => i));
+ }
+ }
+ });
}
- handleKeyPress = ({ key }: React.KeyboardEvent<HTMLInputElement>) => {
- if (key === 'Enter') {
+ handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ // Handle special keypresses
+ if (e.key === 'Enter') {
this.createNewValue();
- } else if (key === 'Backspace') {
+ e.preventDefault();
+ } else if (e.key === 'Backspace') {
this.deleteLastValue();
}
}
- createNewValue = () => {
+ createNewValue = (matches?: string[]) => {
if (this.state.text) {
- const newValue = this.props.createNewValue(this.state.text);
- this.setState({ text: '' });
- this.props.onChange([...this.props.value, newValue]);
+ if (matches && matches.length > 0) {
+ const newValues = matches.map((v) => this.props.createNewValue(v));
+ this.setState({ text: '' });
+ this.props.onChange([...this.props.values, ...newValues]);
+ } else {
+ const newValue = this.props.createNewValue(this.state.text);
+ this.setState({ text: '' });
+ this.props.onChange([...this.props.values, newValue]);
+ }
+ this.props.onPartialInput && this.props.onPartialInput(false);
}
}
deleteLastValue = () => {
- if (this.state.text.length === 0 && this.props.value.length > 0) {
- this.props.onChange(this.props.value.slice(0, -1));
+ if (this.state.text.length === 0 && this.props.values.length > 0) {
+ this.props.onChange(this.props.values.slice(0, -1));
}
}
if (this.timeout) {
clearTimeout(this.timeout);
}
- this.timeout = setTimeout(() => this.setState({ ...this.state }));
+ this.timeout = window.setTimeout(() => this.setState({ ...this.state }));
}
getInputStyles = (): React.CSSProperties => ({
}
renderChips() {
- const { classes, value, ...props } = this.props;
- return <div className={classes.chips}>
+ const { classes, ...props } = this.props;
+ return <div className={[classes.chips, this.props.chipsClassName].join(' ')}>
<Chips
{...props}
- values={value}
+ clickable={!props.disabled}
filler={<div ref={this.filler} />}
/>
</div>;
{...InputProps}
value={this.state.text}
onChange={this.setText}
+ disabled={this.props.disabled}
onKeyDown={this.handleKeyPress}
+ onFocus={this.props.handleFocus}
+ onBlur={this.props.handleBlur}
inputProps={{
...(InputProps && InputProps.inputProps),
className: classes.input,
}
componentDidUpdate(prevProps: ChipsInputProps<Value>) {
- if (prevProps.value !== this.props.value) {
+ if (prevProps.values !== this.props.values) {
this.updateCursorPosition();
}
}