Use Input component in dropdowns and make it homogeneous

old
Javi Velasco 2015-12-20 17:01:02 +01:00
parent f3bcbf9d3b
commit 2c7d308271
6 changed files with 111 additions and 63 deletions

View File

@ -1,5 +1,7 @@
import React from 'react';
import ClassNames from 'classnames';
import Input from '../input';
import events from '../utils/events';
import style from './style';
class Dropdown extends React.Component {
@ -7,6 +9,7 @@ class Dropdown extends React.Component {
auto: React.PropTypes.bool,
className: React.PropTypes.string,
disabled: React.PropTypes.bool,
error: React.PropTypes.string,
label: React.PropTypes.string,
onChange: React.PropTypes.func,
source: React.PropTypes.array.isRequired,
@ -25,7 +28,8 @@ class Dropdown extends React.Component {
up: false
};
handleClick = (event) => {
handleMouseDown = (event) => {
events.pauseEvent(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;
@ -49,7 +53,24 @@ class Dropdown extends React.Component {
}
};
renderItem (item, idx) {
renderTemplateValue (selected) {
const className = ClassNames(style.field, {
[style.errored]: this.props.error,
[style.disabled]: this.props.disabled
});
return (
<div className={className} onMouseDown={this.handleMouseDown}>
<div className={`${style.templateValue} ${style.value}`}>
{this.props.template(selected)}
</div>
{this.props.label ? <label className={style.label}>{this.props.label}</label> : null}
{this.props.error ? <span className={style.error}>{this.props.error}</span> : null}
</div>
);
}
renderValue (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)}>
@ -59,6 +80,7 @@ class Dropdown extends React.Component {
}
render () {
const {template, source, ...others} = this.props;
const selected = this.getSelectedItem();
const className = ClassNames(style.root, {
[style.up]: this.state.up,
@ -68,15 +90,18 @@ class Dropdown extends React.Component {
return (
<div data-react-toolbox='dropdown' className={className}>
{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))}
<Input
{...others}
className={style.value}
onMouseDown={this.handleMouseDown}
readOnly
type={template ? 'hidden' : null}
value={selected.label}
/>
{template ? this.renderTemplateValue(selected) : null}
<ul className={style.values} ref='values'>
{source.map(this.renderValue.bind(this))}
</ul>
<div ref='value' className={style.value} onClick={this.handleClick}>
{this.props.template ? this.props.template(selected) : <span>{selected.label}</span>}
</div>
</div>
);
}

View File

@ -42,6 +42,7 @@ class DropdownTest extends React.Component {
| `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.
| `disabled` | `Boolean` | `false` | Set the component as disabled.
| `error` | `String` | | Give an error string to display under the field.|
| `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.

View File

@ -4,11 +4,6 @@
.root {
position: relative;
width: inherit;
margin-bottom: $dropdown-offset;
color: $color-text;
cursor: pointer;
border-bottom: 1px solid $input-text-bottom-border-color;
&:not(.active) {
> .values {
max-height: 0;
@ -25,15 +20,6 @@
box-shadow: $zdepth-shadow-1;
}
}
&.disabled {
color: $color-text-secondary;
pointer-events: none;
cursor: normal;
border-bottom-style: dotted;
> .value:after {
transform: scale(0);
}
}
&:not(.up) > .values {
top: 0;
bottom: auto;
@ -42,19 +28,84 @@
top: auto;
bottom: 0;
}
&.disabled {
pointer-events: none;
cursor: normal;
}
}
.value {
> input {
cursor: pointer;
}
&:after {
$size: ($input-field-height / 7);
$border: $size solid transparent;
position: absolute;
right: ($dropdown-offset / 2);
top: 50%;
width: 0;
height: 0;
content: "";
border-top: $size solid $input-text-bottom-border-color;
border-right: $border;
border-left: $border;
transition: transform $animation-duration $animation-curve-default;
}
}
.field {
position: relative;
padding: $input-padding 0;
cursor: pointer;
&.errored {
padding-bottom: 0;
> .label {
color: $input-text-error-color;
}
> .valueWrapper {
border-bottom: 1px solid $input-text-error-color;
}
}
&.disabled {
cursor: normal;
pointer-events: none;
> .templateValue {
opacity: .7;
border-bottom-style: dotted;
}
}
}
.templateValue {
border-bottom: 1px solid $input-text-bottom-border-color;
color: $color-text;
min-height: $input-field-height;
background-color: $input-text-background-color;
padding: $input-field-padding 0;
position: relative;
}
.label {
font-size: $font-size-tiny;
color: $color-text-secondary;
position: absolute;
left: 0;
font-size: $input-label-font-size;
line-height: $input-field-font-size;
color: $input-text-label-color;
top: $input-focus-label-top;
}
.error {
margin-bottom: - $input-underline-height;
font-size: $input-label-font-size;
line-height: $input-underline-height;
color: $input-text-error-color;
}
.values {
@include no-webkit-scrollbar;
z-index: $z-index-high;
position: absolute;
z-index: 2;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
list-style: none;
background-color: $dropdown-color-white;
@ -75,30 +126,3 @@
}
}
}
.value {
display: block;
> span {
display: inline-block;
height: $input-field-height;
font-size: $input-field-font-size;
line-height: $input-field-height;
}
> :not(span) {
margin: ($dropdown-offset / 2) 0;
}
&:after {
$size: ($input-field-height / 7);
$border: $size solid transparent;
position: absolute;
right: ($dropdown-offset / 2);
bottom: $dropdown-offset;
width: 0;
height: 0;
content: "";
border-top: $size solid $input-text-bottom-border-color;
border-right: $border;
border-left: $border;
transition: transform $animation-duration $animation-curve-default;
}
}

View File

@ -1,7 +1,7 @@
$input-padding: 2 * $unit;
$input-field-padding: .8 * $unit;
$input-field-font-size: 1.6 * $unit;
$input-field-height: $input-field-padding * 2 + $input-field-font-size;
$input-field-height: $input-field-padding * 2 + $input-field-font-size * 1.4;
$input-label-font-size: 1.2 * $unit;
$input-focus-label-top: .6 * $unit;
$input-text-background-color: transparent !default;

View File

@ -111,6 +111,7 @@
.errored {
padding-bottom: 0;
> .input {
margin-top: 1px;
border-bottom-color: $input-text-error-color;
&:focus {
~ .label:not(.fixed) {

View File

@ -41,6 +41,7 @@ class DropdownTest extends React.Component {
<p>lorem ipsum...</p>
<Dropdown
label="Country"
onChange={this.handleChange.bind(this, 'dropdown1')}
source={countries}
template={this.customDropdownItem}
@ -48,21 +49,17 @@ class DropdownTest extends React.Component {
/>
<Dropdown
label="Countries"
onChange={this.handleChange.bind(this, 'dropdown2')}
source={countries}
/>
<Dropdown
label="Country"
onChange={this.handleChange.bind(this, 'dropdown4')}
source={countries}
value={this.state.dropdown4}
/>
<Dropdown
source={countries}
disabled
label="Country"
onChange={this.handleChange.bind(this, 'dropdown3')}
source={countries}
/>
</section>
);