Refactor dropdown and put value state out

old
Javi Velasco 2015-11-12 00:12:33 +01:00
parent 7311489ade
commit 459036719e
5 changed files with 110 additions and 119 deletions

View File

@ -1,28 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Ripple from '../ripple';
import style from './style';
const _selectValue = (value, dataSource) => {
let item;
if (value) {
for (item of dataSource) {
if (item.value.toString() === value.toString()) break;
}
return item;
} else {
return dataSource[0];
}
};
class Dropdown extends React.Component {
static propTypes = {
auto: React.PropTypes.bool,
className: React.PropTypes.string,
dataSource: React.PropTypes.array.isRequired,
disabled: React.PropTypes.bool,
label: React.PropTypes.string,
onChange: React.PropTypes.func,
source: React.PropTypes.array.isRequired,
template: React.PropTypes.func,
value: React.PropTypes.string
};
@ -35,91 +21,64 @@ class Dropdown extends React.Component {
state = {
active: false,
selected: _selectValue(this.props.value, this.props.dataSource),
up: false,
width: undefined
up: false
};
componentDidMount () {
this.setState({
width: ReactDOM.findDOMNode(this).getBoundingClientRect().width
});
}
componentDidUpdate (prev_props, prev_state) {
if (this.props.onChange && prev_state.selected !== this.state.selected && prev_state.active) {
this.props.onChange(this);
}
}
handleClick = (event) => {
const client = event.target.getBoundingClientRect();
const screen_height = window.innerHeight || document.documentElement.offsetHeight;
const up = this.props.auto ? client.top > ((screen_height / 2) + client.height) : false;
this.setState({ active: true, up: up });
this.setState({active: true, up: up});
};
handleClickValue = (id) => {
if (!this.props.disabled) {
const value = id.toString();
for (const item of this.props.dataSource) {
if (item.value.toString() === value) {
this.setState({active: false, selected: item});
break;
}
}
handleSelect = (item) => {
if (!this.props.disabled && this.props.onChange) {
this.props.onChange(item);
this.setState({active: false});
}
};
renderValues () {
const items = this.props.dataSource.map((item, index) => {
let className;
if (item.value === this.state.selected.value) className = ` ${style.selected}`;
getSelectedItem = () => {
if (this.props.value) {
for (const item of this.props.source) {
if (item.value === this.props.value) return item;
}
} else {
return this.props.source[0];
}
};
return (
<li
key={index}
className={className}
id={item.value}
onClick={this.handleClickValue.bind(this, item.value)}
>
{ this.props.template ? this.props.template(item) : item.label }
<Ripple className={style.ripple}/>
</li>
);
});
let className = style.values;
const valuesStyle = {width: this.state.width};
if (this.state.up) className += ` ${style.up}`;
return <ul ref='values' className={className} style={valuesStyle}>{ items }</ul>;
renderItem (item, idx) {
const className = item.value === this.props.value ? style.selected : null;
return (
<li key={idx} className={className} onMouseDown={this.handleSelect.bind(this, item.value)}>
{ this.props.template ? this.props.template(item) : item.label }
</li>
);
}
render () {
let className = style.root;
if (this.props.className) className += ` ${this.props.className}`;
if (this.props.disabled) className += ` ${style.disabled}`;
const selected = this.getSelectedItem();
if (this.state.up) className += ` ${style.up}`;
if (this.state.active) className += ` ${style.active}`;
if (this.props.disabled) className += ` ${style.disabled}`;
if (this.props.className) className += ` ${this.props.className}`;
return (
<div data-react-toolbox='dropdown' className={className}>
{this.props.label ? <label className={style.label}>{this.props.label}</label> : null}
{ this.renderValues() }
{ this.props.label ? <label className={style.label}>{this.props.label}</label> : null }
<ul ref='values' className={style.values}>
{ this.props.source.map(this.renderItem.bind(this)) }
</ul>
<div ref='value' className={style.value} onClick={this.handleClick}>
{ this.props.template ? this.props.template(this.state.selected) : <span>{this.state.selected.label}</span> }
{ this.props.template ? this.props.template(selected) : <span>{selected.label}</span> }
</div>
</div>
);
}
getValue () {
return this.state.selected.value;
}
setValue (data) {
this.setState({selected: data});
}
}
export default Dropdown;

View File

@ -13,13 +13,26 @@ const countries: [
{ value: 'EN-en', label: 'USA'}
];
const DropdownTest = () => (
<Dropdown
auto
dataSource={countries}
value='TH-th'
/>
);
class DropdownTest extends React.Component {
state = {
value: 'ES-es',
};
handleChange = (value) => {
this.setState({value: value});
};
render () {
return (
<Dropdown
auto={true}
onChange={this.handleChange}
source={countries}
value={this.state.value}
/>
);
}
}
```
## Properties
@ -28,16 +41,9 @@ const DropdownTest = () => (
|:-----|:-----|:-----|:-----|
| `auto` | `Boolean` | `true` | If true, the dropdown will open up or down depending on the position in the screen .|
| `className` | `String` | `''` | Set the class to give custom styles to the dropdown.
| `dataSource` | `Array` | | Array of data objects with the data to represent in the dropdown.
| `disabled` | `Boolean` | `false` | Set the component as disabled.
| `label` | `String` | | The text string to use for the floating label element.
| `onChange` | `Function` | | Callback function that is fired when the component's value changes.
| `source` | `Array` | | Array of data objects with the data to represent in the dropdown.
| `template` | `Function` | | Callback function that returns a JSX template to represent the element.
| `value` | `String` | | Default value using JSON data.
## Methods
This component has state to control its value and how is it rendered. It exposes the following instance methods:
- `getValue` is used to retrieve the current value.
- `setValue` to force a new value.

View File

@ -34,6 +34,14 @@
transform: scale(0);
}
}
&:not(.up) > .values {
top: 0;
bottom: auto;
}
&.up > .values {
top: auto;
bottom: 0;
}
}
.label {
@ -44,6 +52,7 @@
.values {
position: absolute;
z-index: 2;
width: 100%;
overflow-x: hidden;
overflow-y: scroll;
list-style: none;
@ -64,14 +73,6 @@
color: $dropdown-color-primary;
}
}
&:not(.up) {
top: 0;
bottom: auto;
}
&.up {
top: auto;
bottom: 0;
}
}
.value {
@ -100,8 +101,3 @@
transition: transform $animation-duration $animation-curve-default;
}
}
.ripple {
z-index: 0;
background-color: $color-divider;
}

View File

@ -1,6 +1,6 @@
class DropdownTest extends React.Component {
state = {
selected: null
selected: 3
};
albums = [
@ -10,6 +10,10 @@ class DropdownTest extends React.Component {
{ value: 4, artist: 'Pixies', album: 'Doolittle', img: 'http://www.resident-music.com/image/cache/data/Emilys_Packshots/Pixies/Pixies_Doolittlke-500x500.jpg' }
];
handleChange = (value) => {
this.setState({selected: value});
};
customItem (item) {
const style = {
width: 36,
@ -33,7 +37,8 @@ class DropdownTest extends React.Component {
return (
<Dropdown
auto={false}
dataSource={this.albums}
source={this.albums}
onChange={this.handleChange}
label='Select your favorite album'
template={this.customItem}
value={this.state.selected}

View File

@ -1,20 +1,24 @@
import React from 'react';
import Dropdown from '../../components/dropdown';
const countries = [
{ value: 'EN-gb', label: 'England', img: 'http://' },
{ value: 'ES-es', label: 'Spain', img: 'http://' },
{ value: 'TH-th', label: 'Thailand', img: 'http://' },
{ value: 'EN-en', label: 'USA', img: 'http://' },
{ value: 'FR-fr', label: 'France', img: 'http://' }
];
class DropdownTest extends React.Component {
state = {
countries: [
{ value: 'ES-es', label: 'Spain', img: 'http://' },
{ value: 'TH-th', label: 'Thailand', img: 'http://' },
{ value: 'EN-gb', label: 'England', img: 'http://' },
{ value: 'EN-en', label: 'USA', img: 'http://' },
{ value: 'FR-fr', label: 'France', img: 'http://' }
],
selected: 'TH-th'
dropdown1: 'ES-es',
dropdown4: 'TH-th'
};
handleChange = (dropdown) => {
console.log('[DROPDOWN]', dropdown.getValue());
handleChange = (dropdown, value) => {
const newState = {};
newState[dropdown] = value;
this.setState(newState);
};
customDropdownItem (data) {
@ -41,10 +45,31 @@ class DropdownTest extends React.Component {
<section>
<h5>Dropdown</h5>
<p>lorem ipsum...</p>
<Dropdown dataSource={this.state.countries} label="Countries" onChange={this.handleChange}/>
<Dropdown dataSource={this.state.countries} disabled={true} onChange={this.handleChange}/>
<Dropdown dataSource={this.state.countries} value={this.state.selected} onChange={this.handleChange}/>
<Dropdown dataSource={this.state.countries} value={this.state.selected} template={this.customDropdownItem} onChange={this.handleChange}/>
<Dropdown
onChange={this.handleChange.bind(this, 'dropdown1')}
source={countries}
template={this.customDropdownItem}
value={this.state.dropdown1}
/>
<Dropdown
label="Countries"
onChange={this.handleChange.bind(this, 'dropdown2')}
source={countries}
/>
<Dropdown
onChange={this.handleChange.bind(this, 'dropdown4')}
source={countries}
value={this.state.dropdown4}
/>
<Dropdown
source={countries}
disabled={true}
onChange={this.handleChange.bind(this, 'dropdown3')}
/>
</section>
);
}