Dropdown SaSS

old
@soyjavi 2015-10-09 20:55:00 +07:00
parent 96c4c5f4fa
commit 39f4d54064
3 changed files with 154 additions and 126 deletions

View File

@ -1,8 +1,8 @@
/* global React */
import { addons } from 'react/addons';
import style from './style';
import Ripple from '../ripple';
import style from './style';
export default React.createClass({
mixins: [addons.PureRenderMixin],
@ -16,7 +16,6 @@ export default React.createClass({
label: React.PropTypes.string,
onChange: React.PropTypes.func,
template: React.PropTypes.func,
type: React.PropTypes.string,
value: React.PropTypes.string
},
@ -24,20 +23,21 @@ export default React.createClass({
return {
className: '',
dataSource: [],
type: 'normal'
up: false
};
},
getInitialState () {
return {
active: false,
selected: _selectValue(this.props.value, this.props.dataSource)
selected: _selectValue(this.props.value, this.props.dataSource),
width: undefined
};
},
componentDidMount () {
this.setState({
height: this.refs.values.getDOMNode().firstElementChild.getBoundingClientRect().height
width: React.findDOMNode(this).getBoundingClientRect().width
});
},
@ -47,13 +47,17 @@ export default React.createClass({
}
},
onSelect () {
if (!this.props.disabled) {
this.setState({active: true});
}
handleClick (event) {
let client = event.target.getBoundingClientRect();
let screen_height = window.innerHeight || document.documentElement.offsetHeight;
this.setState({
active: true,
up: client.top > ((screen_height / 2) + client.height)
});
},
onItem (id) {
handleClickValue (id) {
if (!this.props.disabled) {
let value = id.toString();
for (let item of this.props.dataSource) {
@ -68,24 +72,17 @@ export default React.createClass({
}
},
render () {
let stylesheet;
let className = `${style.root} ${this.props.className}`;
if (this.props.type) className += ` ${this.props.type}`;
if (this.props.disabled) className += ' disabled';
if (this.state.active === true) {
className += ' active';
stylesheet = { height: this.state.height * this.props.dataSource.length };
}
renderValues () {
let items = this.props.dataSource.map((item, index) => {
let className;
if (item.value === this.state.selected.value) className = ` ${style.selected}`;
const items = this.props.dataSource.map((item, index) => {
return (
<li
key={index}
className={className}
id={item.value}
onClick={this.onItem.bind(this, item.value)}
style={{position: 'relative'}}
className={ item.value === this.state.selected.value ? 'selected' : null}
onClick={this.handleClickValue.bind(this, item.value)}
>
{ this.props.template ? this.props.template(item) : item.label }
<Ripple className={style.ripple}/>
@ -93,11 +90,25 @@ export default React.createClass({
);
});
let className = style.values;
if (this.state.up) className += ` ${style.up}`;
let valuesStyle = {width: this.state.width};
return (
<ul ref='values' className={className} style={valuesStyle}>{ items }</ul>
)
},
render () {
let className = style.root;
if (this.props.disabled) className += ` ${style.disabled}`;
if (this.state.active) className += ` ${style.active}`;
return (
<div data-react-toolbox='dropdown' className={className}>
{this.props.label ? <label>{this.props.label}</label> : null}
<ul ref='values' className={style.values} style={stylesheet}>{ items }</ul>
<div ref='value' className={style.value} onClick={this.onSelect}>
{this.props.label ? <label className={style.label}>{this.props.label}</label> : null}
{ this.renderValues() }
<div ref='value' className={style.value} onClick={this.handleClick}>
{ this.props.template ? this.props.template(this.state.selected) : <span>{this.state.selected.label}</span> }
</div>
</div>

View File

@ -0,0 +1,117 @@
@import "../variables";
@import "../mixins";
$dropdown-input-height: 3 * $unit;
$droppdown-background: unquote("rgb(#{$color-white})");
$dropdown-value-background: unquote("rgb(#{$color-primary})");
$dropdown-value-color: unquote("rgb(#{$color-primary-contrast})");
$dropdown-value-hover-background: unquote("rgb(#{$palette-grey-200})");
.root {
position : relative;
margin-bottom : $offset;
width : inherit;
color : $color-text;
border-bottom : 1px solid $color-divider;
&.active {
> .label, > .value {
opacity : 0.5;
}
> .values {
transform : translateY(0%);
max-height : 40vh;
}
}
&.disabled {
color : $color-text-secondary;
border-bottom-style : dotted;
> .value:after {
transform : scale(0);
}
}
}
.label, .value {
transition: opacity $animation-duration $animation-curve-fast-out-slow-in;
}
.label {
position : relative;
font-size : $font-size-tiny;
color : $color-text-secondary;
}
.values {
z-index : 2;
position : absolute;
top : 0;
left : 0;
width : auto;
max-height : 0;
list-style : none;
overflow-x : hidden;
overflow-y : scroll;
width : auto;
background-color : $droppdown-background;
border-radius : $border-radius;
box-shadow: $zdepth-shadow-1;
transition-property : max-height, box-shadow, transform;
transition-duration : $animation-duration;
transition-timing-function : $animation-curve-fast-out-slow-in;
> * {
position : relative;
padding : $unit;
overflow : hidden;
cursor : pointer;
&:hover {
background-color: $dropdown-value-hover-background;
}
&.selected {
color: $dropdown-value-background;
}
}
&:not(.up) {
top: 1rem;
bottom: auto;
transform : translateY(-$dropdown-input-height);
}
&.up {
top: auto;
bottom: 1rem;
transform : translateY($dropdown-input-height);
}
}
.value {
display : block;
> span {
height : $dropdown-input-height;
font-size : $font-size-normal;
line-height : $dropdown-input-height;
}
> :not(span) {
margin : ($offset / 2) 0;
}
&:after {
$size : ($dropdown-input-height / 7);
$border : $size solid transparent;
position : absolute;
content : "";
right : ($offset / 2);
bottom : $offset;
width : 0;
height : 0;
border-left : $border;
border-right : $border;
border-top : $size solid $color-divider;
transition : transform $animation-duration $animation-curve-fast-out-slow-in;
}
}
.ripple {
background-color : $color-divider;
opacity : 1;
z-index : 0;
}

View File

@ -1,100 +0,0 @@
@import '../constants'
:local(.values)
z-index : 2
position : absolute
top : 0
left : -(OFFSET = (SPACE / 1.25))
right : -(OFFSET)
width : auto
height : 0
max-height : 50vh
list-style : none
opacity : 0
overflow : hidden
background-color : WHITE
border-radius : BORDER_RADIUS
transform : translateY(-(INPUT_HEIGHT / 2))
transition-property : height, box-shadow, opacity, transform
transition-duration : ANIMATION_DURATION
transition-timing-function : ANIMATION_EASE
> li
position : relative
padding : (SPACE / 2) OFFSET
opacity : 0
overflow : hidden
cursor : pointer
&.selected
color : PRIMARY
:local(.value)
display : block
> span
height : INPUT_HEIGHT
font-size : FONT_SIZE_NORMAL
line-height : INPUT_HEIGHT
> :not(span)
margin : (SPACE / 2) 0
&:after
SIZE = (INPUT_HEIGHT / 7)
position : absolute
content : ""
right : (SPACE / 2)
bottom : SPACE
width : 0
height : 0
border-left : BORDER = SIZE solid transparent
border-right : BORDER
border-top : SIZE solid DIVIDER
transition : transform ANIMATION_DURATION ANIMATION_EASE
:local(.root)
position : relative
margin-bottom : SPACE
width : inherit
color : TEXT
border-bottom : 1px solid DIVIDER
// -- Overrides
&.active
> label, > div
opacity : 0
transform : translateY((INPUT_HEIGHT / 4))
> div:after
transform : scale(0)
> ul
opacity : 1
transform : translateY(0%)
box-shadow : ZDEPTH_SHADOW_1
> li
opacity : 1
transition : opacity ANIMATION_DURATION ANIMATION_EASE
&:not(.active)
> *, > * > li
transition-delay : ANIMATION_DURATION
&.disabled
color : TEXT_SECONDARY
border-bottom-style : dotted
> div:after
transform : scale(0)
// -- Children
> *
width : 100%
> label, > div
transition-property : opacity, transform
transition-duration : ANIMATION_DURATION
transition-timing-function : ANIMATION_EASE
> label + ul
top : SPACE
> label
position : relative
bottom : -(SPACE / 4)
font-size : FONT_SIZE_TINY
color : TEXT_SECONDARY
:local(.ripple)
background-color : DIVIDER
opacity : 1
z-index : Z_INDEX_LOW