Clearable dropdown & autocomplete, fix dropdown behaviour, always-controlled inputs

old
Vitaliy Filippov 2018-03-20 02:09:38 +03:00
parent 9020a14bba
commit 83dd1c93b7
6 changed files with 60 additions and 15 deletions

View File

@ -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 (
<div data-react-toolbox="autocomplete" className={className}>
{this.props.selectedPosition === 'above' ? this.renderSelected() : null}
{selectedPosition === 'above' ? this.renderSelected() : null}
{allowClear && this.state.query != '' ? <span
className={'material-icons '+theme.clear}
title={clearTooltip}
onClick={(e) => this.handleChange([], e)}>clear</span> : null}
<Input
{...other}
ref={(node) => { this.inputNode = node; }}
autoComplete="off"
className={theme.input}
className={theme.input+(allowClear && this.state.query != '' ? ' '+theme.withclear : '')}
error={error}
label={label}
onBlur={this.handleQueryBlur}

View File

@ -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;

View File

@ -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 (
<div
className={className}
style={{outline: 'none'}}
data-react-toolbox="dropdown"
onBlur={this.handleBlur}
onFocus={this.handleFocus}
tabIndex="-1"
>
{allowClear && selected ? <span
className={'material-icons '+theme.clear}
title={clearTooltip}
onClick={(e) => this.props.onChange(null, e)}>clear</span> : null}
<Input
{...others}
tabIndex="0"
className={theme.value}
onClick={this.handleClick}
onBlur={this.handleBlur}
onFocus={this.handleFocus}
required={this.props.required}
readOnly
type={template && selected ? 'hidden' : null}
@ -221,7 +230,7 @@ const factory = (Input) => {
value={selected && selected[labelKey] ? selected[labelKey] : ''}
/>
{template && selected ? this.renderTemplateValue(selected) : null}
<ul className={theme.values}>
<ul className={theme.values} style={this.state.up ? {bottom: '100%'} : {top: '100%'}}>
{source.map(this.renderValue)}
</ul>
</div>

View File

@ -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;
}

View File

@ -187,7 +187,7 @@ const factory = (FontIcon) => {
disabled,
required,
type,
value,
value: value == null ? '' : value,
};
if (!multiline) {
inputElementProps.maxLength = maxLength;

View File

@ -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": {