From 83dd1c93b731bbd337cb4d6e953496d9f343c3c4 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Tue, 20 Mar 2018 02:09:38 +0300 Subject: [PATCH] Clearable dropdown & autocomplete, fix dropdown behaviour, always-controlled inputs --- components/autocomplete/Autocomplete.js | 20 ++++++++++++++------ components/autocomplete/theme.css | 14 ++++++++++++++ components/dropdown/Dropdown.js | 23 ++++++++++++++++------- components/dropdown/theme.css | 14 ++++++++++++++ components/input/Input.js | 2 +- package.json | 2 +- 6 files changed, 60 insertions(+), 15 deletions(-) diff --git a/components/autocomplete/Autocomplete.js b/components/autocomplete/Autocomplete.js index ab0ad309..0180d385 100644 --- a/components/autocomplete/Autocomplete.js +++ b/components/autocomplete/Autocomplete.js @@ -19,6 +19,8 @@ const POSITION = { const factory = (Chip, Input) => { class Autocomplete extends Component { static propTypes = { + allowClear: PropTypes.bool, + clearTooltip: PropTypes.string, allowCreate: PropTypes.bool, className: PropTypes.string, direction: PropTypes.oneOf(['auto', 'up', 'down']), @@ -60,6 +62,8 @@ const factory = (Chip, Input) => { }; static defaultProps = { + allowClear: false, + clearTooltip: 'Clear', allowCreate: false, className: '', direction: 'auto', @@ -390,22 +394,26 @@ const factory = (Chip, Input) => { render() { const { - allowCreate, error, label, source, suggestionMatch, query, // eslint-disable-line no-unused-vars - selectedPosition, keepFocusOnChange, showSuggestionsWhenValueIsSet, showSelectedWhenNotInSource, onQueryChange, // eslint-disable-line no-unused-vars - theme, ...other - } = this.props; + allowClear, allowCreate, clearTooltip, error, label, source, suggestionMatch, query, // eslint-disable-line no-unused-vars + selectedPosition, keepFocusOnChange, showSuggestionsWhenValueIsSet, showSelectedWhenNotInSource, onQueryChange, // eslint-disable-line no-unused-vars + theme, ...other + } = this.props; const className = classnames(theme.autocomplete, { [theme.focus]: this.state.focus, }, this.props.className); return (
- {this.props.selectedPosition === 'above' ? this.renderSelected() : null} + {selectedPosition === 'above' ? this.renderSelected() : null} + {allowClear && this.state.query != '' ? this.handleChange([], e)}>clear : null} { this.inputNode = node; }} autoComplete="off" - className={theme.input} + className={theme.input+(allowClear && this.state.query != '' ? ' '+theme.withclear : '')} error={error} label={label} onBlur={this.handleQueryBlur} diff --git a/components/autocomplete/theme.css b/components/autocomplete/theme.css index 5b175727..f4812adf 100644 --- a/components/autocomplete/theme.css +++ b/components/autocomplete/theme.css @@ -19,6 +19,20 @@ } } +.clear { + cursor: pointer; + z-index: 10; + position: absolute; + display: block; + top: 12px; + left: -4px; + padding: 4px; +} + +.withclear input { + text-indent: 28px !important; +} + .values { flex-direction: row; flex-wrap: wrap; diff --git a/components/dropdown/Dropdown.js b/components/dropdown/Dropdown.js index dce8e648..e0bc3048 100644 --- a/components/dropdown/Dropdown.js +++ b/components/dropdown/Dropdown.js @@ -12,6 +12,8 @@ const factory = (Input) => { class Dropdown extends Component { static propTypes = { allowBlank: PropTypes.bool, + allowClear: PropTypes.bool, + clearTooltip: PropTypes.string, auto: PropTypes.bool, className: PropTypes.string, disabled: PropTypes.bool, @@ -55,6 +57,8 @@ const factory = (Input) => { auto: true, className: '', allowBlank: true, + allowClear: false, + clearTooltip: 'Clear', disabled: false, labelKey: 'label', required: false, @@ -99,7 +103,6 @@ const factory = (Input) => { }; handleSelect = (item, event) => { - if (this.props.onBlur) this.props.onBlur(event); if (!this.props.disabled && this.props.onChange) { if (this.props.name) event.target.name = this.props.name; this.props.onChange(item, event); @@ -122,6 +125,7 @@ const factory = (Input) => { close = () => { if (this.state.active) { this.setState({ active: false }); + if (this.props.onBlur) this.props.onBlur(event); } } @@ -131,18 +135,17 @@ const factory = (Input) => { const screenHeight = window.innerHeight || document.documentElement.offsetHeight; const up = this.props.auto ? client.top > ((screenHeight / 2) + client.height) : false; this.setState({ active: true, up }); + if (this.props.onFocus) this.props.onFocus(event); }; handleFocus = (event) => { event.stopPropagation(); if (!this.props.disabled) this.open(event); - if (this.props.onFocus) this.props.onFocus(event); }; handleBlur = (event) => { event.stopPropagation(); if (this.state.active) this.close(); - if (this.props.onBlur) this.props.onBlur(event); } renderTemplateValue(selected) { @@ -189,7 +192,7 @@ const factory = (Input) => { render() { const { - allowBlank, auto, labelKey, required, onChange, onFocus, onBlur, // eslint-disable-line no-unused-vars + allowBlank, allowClear, clearTooltip, auto, labelKey, required, onChange, onFocus, onBlur, // eslint-disable-line no-unused-vars source, template, theme, valueKey, ...others } = this.props; const selected = this.getSelectedItem(); @@ -198,21 +201,27 @@ const factory = (Input) => { [theme.active]: this.state.active, [theme.disabled]: this.props.disabled, [theme.required]: this.props.required, + [theme.withclear]: allowClear && selected, }, this.props.className); return (
+ {allowClear && selected ? this.props.onChange(null, e)}>clear : null} { value={selected && selected[labelKey] ? selected[labelKey] : ''} /> {template && selected ? this.renderTemplateValue(selected) : null} -
    +
      {source.map(this.renderValue)}
diff --git a/components/dropdown/theme.css b/components/dropdown/theme.css index 02f068e6..12207ccf 100644 --- a/components/dropdown/theme.css +++ b/components/dropdown/theme.css @@ -42,6 +42,10 @@ cursor: normal; pointer-events: none; } + + &.withclear input { + text-indent: 28px !important; + } } .value { @@ -164,3 +168,13 @@ width: 0; } } + +.clear { + cursor: pointer; + z-index: 10; + position: absolute; + display: block; + top: 12px; + left: -4px; + padding: 4px; +} diff --git a/components/input/Input.js b/components/input/Input.js index 274385fc..d18388d3 100644 --- a/components/input/Input.js +++ b/components/input/Input.js @@ -187,7 +187,7 @@ const factory = (FontIcon) => { disabled, required, type, - value, + value: value == null ? '' : value, }; if (!multiline) { inputElementProps.maxLength = maxLength; diff --git a/package.json b/package.json index 79811a08..2174d53d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "react-toolbox", "description": "A set of React components implementing Google's Material Design specification with the power of CSS Modules.", "homepage": "http://www.react-toolbox.io", - "version": "2.0.0-beta.11", + "version": "2.0.0-beta.13", "main": "./lib", "module": "./components", "author": {