X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/c72c1ba8048825f15d864753dd247080e29f227b..e125aa439a61755852c3bd16412e2fb621aa58c2:/src/components/chips-input/chips-input.tsx diff --git a/src/components/chips-input/chips-input.tsx b/src/components/chips-input/chips-input.tsx index 96e7b70a..7b9ff4a6 100644 --- a/src/components/chips-input/chips-input.tsx +++ b/src/components/chips-input/chips-input.tsx @@ -2,33 +2,44 @@ // // SPDX-License-Identifier: AGPL-3.0 -import * as React from 'react'; -import { Chips } from '~/components/chips/chips'; -import { Input, withStyles, WithStyles } from '@material-ui/core'; +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 { 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; + deletable?: boolean; + orderable?: boolean; + disabled?: boolean; + pattern?: RegExp; } type CssRules = 'chips' | 'input' | 'inputContainer'; -const styles: StyleRulesCallback = () => ({ +const styles: StyleRulesCallback = ({ spacing }) => ({ chips: { - minHeight: '40px', + minHeight: spacing.unit * 5, zIndex: 1, position: 'relative', }, input: { - position: 'relative', - top: '-5px', zIndex: 1, + marginBottom: 8, + position: 'relative', }, inputContainer: { - top: '-24px', + marginTop: -34 }, }); @@ -43,22 +54,49 @@ export const ChipsInput = withStyles(styles)( timeout = -1; setText = (event: React.ChangeEvent) => { - 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) => { - if (key === 'Enter') { + handleKeyPress = (e: React.KeyboardEvent) => { + // 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.values, 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); } } @@ -72,12 +110,12 @@ export const ChipsInput = withStyles(styles)( if (this.timeout) { clearTimeout(this.timeout); } - this.timeout = setTimeout(() => this.setState({ ...this.state })); + this.timeout = window.setTimeout(() => this.setState({ ...this.state })); } getInputStyles = (): React.CSSProperties => ({ width: this.filler.current - ? this.filler.current.offsetWidth + 8 + ? this.filler.current.offsetWidth : '100%', right: this.filler.current ? `calc(${this.filler.current.offsetWidth}px - 100%)` @@ -91,25 +129,41 @@ export const ChipsInput = withStyles(styles)( render() { return <> -
- } - /> -
- + {this.renderChips()} + {this.renderInput()} ; } + renderChips() { + const { classes, ...props } = this.props; + return
+ } + /> +
; + } + + renderInput() { + const { inputProps: InputProps, inputComponent: Input = MuiInput, classes } = this.props; + return ; + } + componentDidUpdate(prevProps: ChipsInputProps) { if (prevProps.values !== this.props.values) { this.updateCursorPosition();