react-miniui
Version:
A mini react UI library
272 lines (226 loc) • 6.41 kB
JavaScript
/* @flow */
import * as React from 'react';
import '../scss/dropdown.scss';
import {Icon} from './icon';
type Props = {
value: string;
text?: React.Node;
onChange?: Function;
className?: string;
dropArrow?: bool,
placeholder?: string;
isSelect?: bool;
name: string;
children?: React.Node;
}
/*!
* 下拉菜单对象基类,所有下单菜单组件继承此类
* @type {Object}
*/
export class Dropdown extends React.Component <Props, {
value: string,
opend: bool,
text: React.Node
}> {
constructor(props: Props) {
super(props)
this.state = {
opend: false,
value: (typeof props.value === 'undefined') ? '' : props.value,
text: (typeof props.text === 'undefined') ? '' : props.text
}
}
//document 点击事件,关闭 dromdown
mouseupCallback() {
this.hide();
}
//展开或关闭 dropdown
handleClick() {
this.state.opend ? this.hide() : this.show();
}
show() {
// document 绑定 mouseup事件, 触发时关闭 dromdown
document.addEventListener('mouseup', this.mouseupCallback.bind(this), false);
this.setState({
opend: true
});
}
hide() {
// 关闭时移除 document mouseup 事件
document.removeEventListener('mouseup', this.mouseupCallback);
this.setState({
opend: false
});
}
//阻止 dropdown 鼠标 onMouseUp 事件冒泡
mouseupEvent(event: SyntheticEvent<HTMLElement>) {
event.nativeEvent.stopImmediatePropagation();
event.stopPropagation();
}
selectEvent(e: SyntheticEvent<HTMLElement>) {
const target = e.target;
if (target instanceof HTMLElement) {
let targetEvery = target;
while (!targetEvery.matches || !targetEvery.matches('.item')) {
if (targetEvery === e.currentTarget) {
return false;
}
if (targetEvery.parentNode) {
targetEvery = targetEvery.parentNode;
} else {
return false;
}
}
if (targetEvery && targetEvery instanceof HTMLElement) {
const value = targetEvery.getAttribute('data-value') || '';
const text = targetEvery.innerText;
this.setState({
value,
text
});
this.props.onChange && this.props.onChange(value, text);
}
}
this.hide();
}
render() {
const {className, children, isSelect} = this.props;
const {opend, value, text} = this.state;
const classNames = ['dropdown'];
if (className) {
classNames.push(className);
}
if (isSelect) {
classNames.push('dropdown-select');
}
let childrenToggler = isSelect
? (
<div className="dropdown-header">
{text}
<Icon icon="fa-down-dir" />
</div>
)
: <div className="dropdown-header">{this.props.text}<Icon icon="fa-down-dir" /></div>;
let childrenMenu = children;
if (React.Children.count(children) > 1) {
childrenToggler = React.Children.toArray(this.props.children);
childrenMenu = childrenToggler.pop();
}
return (
<div className={classNames.join(' ')} onMouseUp={this.mouseupEvent.bind(this)}>
<input type="hidden" name={this.props.name} value={value} />
<div className="dropdown-toggler" onClick={this.handleClick.bind(this)}>
{childrenToggler}
</div>
<div className={`dropdown-main ${opend ? 'visible' : ''}`} onClick={this.selectEvent.bind(this)}>
{childrenMenu}
</div>
</div>
)
}
}
export class Menu extends React.Component <{
className?: string,
dropArrow?: bool,
search?: bool,
children: React.ChildrenArray<React.Element<typeof Option>>
}, {
searchValue: string
}> {
constructor() {
super()
this.state = {
searchValue: ''
}
}
handleChange(event: any) {
const searchValue = event.target.value;
this.setState({
searchValue
});
}
render() {
const classNames = ['menu'];
const {className, dropArrow, search} = this.props;
const children = React.Children.toArray(this.props.children);
if (className) {
classNames.push(className);
}
if (dropArrow) {
classNames.push('drop-arrow');
}
const searchRegExp = new RegExp(this.state.searchValue);
return (
<div className={classNames.join(' ')}>
{search && (
<div className="dropdown-search">
<label className="input-append input-block">
<input className="input" type="text" onChange={this.handleChange.bind(this)} value={this.state.searchValue} placeholder="serach" />
<span className="add-on"><i className="fa-search" /></span>
</label>
</div>
)}
{search ? children.filter(v => searchRegExp.test(v.props.value)) : children}
</div>
)
}
}
export const Item = (props: {
className?: string,
disabled?: bool,
href?: string,
value: string,
children: React.Node
}) => {
const classNames = ['item'];
const {className, disabled, value, ...others} = props;
if (className) {
classNames.push(className);
}
if (disabled) {
classNames.push('disabled');
}
if (props.href) {
return (<a className={classNames.join(' ')} data-value={value} {...others}>{props.children}</a>);
} else {
return (<div className={classNames.join(' ')} data-value={value} {...others}>{props.children}</div>);
}
}
export class Option extends React.Component<{
className?: string,
disabled?: bool,
children: React.Node,
value: string
}> {
render() {
const classNames = ['item'];
const {className, disabled, value, ...others} = this.props;
if (className) {
classNames.push(className);
}
if (disabled) {
classNames.push('disabled');
}
return <div className={classNames.join(' ')} data-value={value} {...others}>{this.props.children}</div>
}
}
export class Select extends React.Component<{
name: string,
value?: string,
search?: bool,
children: React.ChildrenArray<React.Element<typeof Option>>
}> {
render() {
const children: Array<React.Element<typeof Option>> = React.Children.toArray(this.props.children);
const {name, search, value = children[0] && children[0].props.value} = this.props;
const item = children.find((v) => (v.props.value === value));
const text = item ? item.props.children : '';
return (
<Dropdown isSelect={true} value={value} text={text} name={name}>
<Menu search={search || false}>
{this.props.children}
</Menu>
</Dropdown>
)
}
}