Skip to content

Commit

Permalink
feat(multiselect): complete multiselect component
Browse files Browse the repository at this point in the history
  • Loading branch information
yehuozhili committed Jun 3, 2020
1 parent 3552bf6 commit a83176d
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 9 deletions.
2 changes: 2 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const loaderFn = () => {
require('../src/components/Button/button.stories.mdx'),
require('../src/components/Input/input.stories.mdx'),
require('../src/components/Switch/switch.stories.mdx'),
require('../src/components/Select/select.stories.mdx'),
require('../src/components/MultiSelect/multiselect.stories.mdx'),
require('../src/components/AutoComplete/autocomplete.stories.mdx'),
require('../src/components/List/list.stories.mdx'),
require('../src/components/VirtualList/virtuallist.stories.mdx'),
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bigbear-ui",
"version": "0.1.9",
"version": "0.1.10",
"description": "Neumorphism component library for React",
"keywords": [
"component",
Expand Down
12 changes: 10 additions & 2 deletions src/components/Alert/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,19 @@ export interface AlertProps{
initAnimate?:boolean,
/**是否套一层div */
wrapper?:boolean;
/** 主动关闭逻辑回调函数,如果需要主动消失,需要操作setState,或者使用上层组件的state*/
initiativeCloseCallback?:(setState:React.Dispatch<React.SetStateAction<boolean>>,e:React.MouseEvent<HTMLElement, MouseEvent>)=>void;
/** 自动关闭后的回调函数 */
closeCallback?:()=>void;
/** 使用自定义动画的类名 */
animateClassName?:string;
/** 动画持续时间*/
timeout?:number;

}

export const Alert:FC<AlertProps> = function(props:AlertProps){
const {title,type,timeout,description,animateClassName,directions,autoclosedelay,className,initAnimate,wrapper,closeCallback}=props
const {title,type,timeout,description,animateClassName,directions,autoclosedelay,className,initAnimate,wrapper,closeCallback,initiativeCloseCallback}=props
const classes = classNames('bigbear-alert',`bigbear-alert-${type}`,className?className:'')
const [state,setState]=useState(!initAnimate)
useEffect(()=>{
Expand Down Expand Up @@ -67,7 +70,12 @@ export const Alert:FC<AlertProps> = function(props:AlertProps){
description&&<span>{description}</span>
}
{
props.close&&<Button btnType={type} onClick={()=>setState(false)}><Icon icon='times'></Icon></Button>
props.close&&<Button btnType={type} onClick={(e)=>{
if(initiativeCloseCallback){
initiativeCloseCallback(setState,e)
}else{
setState(false);
}}}><Icon icon='times'></Icon></Button>

}
</div>
Expand Down
68 changes: 68 additions & 0 deletions src/components/MultiSelect/_style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
.bigbear-multiselect{
width: 100%;
position: relative;
.bigbear-multiselect-display{
display: flex;
width: 100%;
padding:10px;
@include neufactory-noactive($white,$neu-whiteshadow1,$neu-whiteshadow2);
.bigbear-multiselect-displaytext{
flex-grow: 1;
display:flex;
flex-wrap: wrap;
border-radius: 5px;
@include ringwrapper($white,$neu-whiteshadow1,$neu-whiteshadow2);
.bigbear-multiselect-item{
display: inline-block;
margin:5px;
.bigbear-alert{
flex-direction:row ;
padding:3px;
& span:nth-child(1){
font-size: 14px;
display: flex;
align-items: center;
margin-right: 2px;
}
.btn{
position: static;
font-size: 14px;
}
}

}
}
.bigbear-multiselect-icon{
display: flex;
width: 10px;
align-items: center;
margin-left: 10px;
}
}
.bigbear-multiselect-options{
position: absolute;
width: 100%;
padding:10px;
overflow-y: auto;
cursor: pointer;
@include neufactory-noactive($white,$neu-whiteshadow1,$neu-whiteshadow2);
>div{
margin:5px;
padding:5px;
@include neufactory($white,$neu-whiteshadow1,$neu-whiteshadow2);
@include neufactory-hover($info,$neu-infoshadow1,$neu-infoshadow2);
&:hover{
color:$white;
}
}
>div.bigbear-multiselect-active{
color:$white;
@include neufactory($primary,$neu-blueshadow1,$neu-blueshadow2);

}
}
&.disabled{
cursor: not-allowed;
opacity: .5;
}
}
3 changes: 3 additions & 0 deletions src/components/MultiSelect/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import MultiSelect from "./multiselect";

export default MultiSelect;
73 changes: 73 additions & 0 deletions src/components/MultiSelect/multiselect.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Meta, Story, Props ,Preview } from '@storybook/addon-docs/blocks';
import MultiSelect from './multiselect';
import Icon from '../Icon'

<Meta title='Display|MultiSelect 多选框' component={MultiSelect} />

<br/>

# MultiSelect 多选框

<br/>

## 基本使用

多选框是在alert组件上进行的封装。

多选框封装不同于别的组件库,只要传数据就可以了,避免使用上过于复杂。

<Preview>
<Story name='multiselect'>
<div style={{height:'300px'}}>
<MultiSelect data={['options1','options2','options3','options4']} ></MultiSelect>
</div>
</Story>
</Preview>

## 默认值

<Preview>
<Story name='multiselect defaultValue'>
<div style={{height:'300px'}}>
<MultiSelect data={['options1','options2','options3','options4']} displayType='dark' defaultIndex={[1,2]} ></MultiSelect>
</div>
</Story>
</Preview>



## 无图标

<Preview>
<Story name='multiselect noicon'>
<div style={{height:'300px'}}>
<MultiSelect displayType='primary' data={['options1','options2','options3','options4']} icon={null} ></MultiSelect>
</div>
</Story>
</Preview>

## 超长数据

option maxHeight可以设置style修改。

<Preview>
<Story name='multiselectlong'>
<div style={{height:'600px'}}>
<MultiSelect data={new Array(100).fill(1).map((x,y)=>y)} callback={(e)=>console.log(e)}></MultiSelect>
</div>
</Story>
</Preview>

## 禁用样式

<Preview>
<Story name='multiselectdisable'>
<MultiSelect data={new Array(100).fill(1).map((x,y)=>y)} disabled ></MultiSelect>
</Story>
</Preview>



## 属性详情

<Props of={MultiSelect} />
106 changes: 106 additions & 0 deletions src/components/MultiSelect/multiselect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React, { useState, ReactNode, useRef, PropsWithChildren, useEffect, CSSProperties } from 'react';
import useClickOutside from '../../hooks/useClickOutside';
import Icon from '../Icon';
import Transition from '../Transition';
import Alert from '../Alert';




interface MultiSelectProps{
/** 选框中数据 */
data:Array<string>
/** 默认选中索引*/
defaultIndex?:Array<number>
/** 展示右侧的图标 */
icon?:ReactNode;
displayType?:"primary" | "default" | "danger" | "secondary" | "success" | "info" | "light" | "warning" | "dark"
/** 下拉动画时间*/
timeout?:number,
/**选择的回调值 */
callback?:(v:Array<string>)=>void;
/** 禁用*/
disabled?:boolean;
/** 选项框样式 */
optionStyle?:CSSProperties;
/** 显示框样式 */
displayStyle?:CSSProperties;
}

function MultiSelect(props:PropsWithChildren<MultiSelectProps>){
const {icon,timeout,callback,data,disabled,displayType,optionStyle,displayStyle,defaultIndex}=props;
const [state,setState]=useState<Array<string>>([])
const [datamap,setDataMap]=useState<Array<number>>(defaultIndex?defaultIndex:[])//增加时候可能会有重名所以要map
const [open,setOpen]=useState(()=>false)
const ref= useRef(null)
useClickOutside(ref,()=>setOpen(false))
useEffect(()=>{
if(callback)callback(state)
},[callback, state])
useEffect(()=>{
if(datamap.length===0){
setState([])
}else{
let newState:Array<string>=[]
datamap.forEach(v=>{
newState.push(data[v])
setState(newState)
})
}
},[data, datamap])


return(
<div className={`bigbear-multiselect ${disabled?'disabled':''}`} ref={ref}>
<div className='bigbear-multiselect-display' onClick={()=>{if(!disabled)setOpen(!open)}} style={displayStyle} >
<div className='bigbear-multiselect-displaytext'>{
state.map((item,index)=>{
return <div className="bigbear-multiselect-item" key={index}><Alert
title={item} close={true} type={displayType}
initiativeCloseCallback={(_,e)=>{
e.stopPropagation()
datamap.splice(index,1)
setDataMap([...datamap])
}} ></Alert></div>
})
}</div>
{icon?<div className='bigbear-multiselect-icon'>{icon}</div>:null}
</div>
<Transition in={open} animation='zoom-in-top' timeout={timeout!} >
<div className='bigbear-multiselect-options' style={optionStyle}>
{
data.map((item,index)=>{
let renderRes = <div onClick={()=>{
let res =datamap.indexOf(index)
if( res=== -1){
setDataMap([...datamap,index])
}else{
datamap.splice(res,1)
setDataMap([...datamap])
}
}} key={index} className={datamap.indexOf(index)===-1?'':'bigbear-multiselect-active'}> {item}</div>
return renderRes
})
}
</div>
</Transition>

</div>
)
}


MultiSelect.defaultProps={
icon:<Icon icon='angle-down'></Icon>,
timeout:300,
data:[],
disabled:false,
displayType:'default',
optionStyle:{maxHeight:'500px'},
displayStyle:{minHeight:'65px'},
defaultIndex:[]
}



export default MultiSelect;
13 changes: 13 additions & 0 deletions src/components/Ratio/ratio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

function Ratio(){

return (
<div>

</div>
)
}


export default Ratio;
12 changes: 8 additions & 4 deletions src/components/Select/select.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, ReactNode, useRef, PropsWithChildren, useEffect } from 'react';
import React, { useState, ReactNode, useRef, PropsWithChildren, useEffect, CSSProperties } from 'react';
import useClickOutside from '../../hooks/useClickOutside';
import Icon from '../Icon';
import Transition from '../Transition/index';
Expand All @@ -19,12 +19,16 @@ interface SelectProps{
callback?:(v:string)=>void;
/** 禁用*/
disabled?:boolean;
/** 外层容器样式*/
style?:CSSProperties;
/** 内层容器样式 */
innerStyle?:CSSProperties;
}



function Select(props:PropsWithChildren<SelectProps>){
const {icon,defaultValue,timeout,renderTemplate,callback,data,disabled}=props;
const {icon,defaultValue,timeout,renderTemplate,callback,data,disabled,style,innerStyle}=props;
const [state,setState]=useState<string>(defaultValue!)
const [open,setOpen]=useState(false)
const ref= useRef(null)
Expand All @@ -33,9 +37,9 @@ function Select(props:PropsWithChildren<SelectProps>){
if(callback)callback(state)
},[callback, state])
return(
<div className={`bigbear-select ${disabled?'disabled':''}`} ref={ref}>
<div className={`bigbear-select ${disabled?'disabled':''}`} ref={ref} style={style}>
<div className='bigbear-select-display' onClick={()=>{if(!disabled)setOpen(!open)}} >
<div className='bigbear-select-displaytext'>{state}</div>
<div className='bigbear-select-displaytext' style={innerStyle}>{state}</div>
{icon?<div className='bigbear-select-icon'>{icon}</div>:null}
</div>
<Transition in={open} animation='zoom-in-top' timeout={timeout!} >
Expand Down
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export {default as VirtualList} from './components/VirtualList';
export {default as Badge} from './components/Badge';
export {default as Switch} from './components/Switch';
export {default as Select} from './components/Select';

export {default as MultiSelect} from './components/MultiSelect';


export {default as useClickOutside}from './hooks/useClickOutside';
Expand Down
5 changes: 4 additions & 1 deletion src/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,7 @@

//select

@import "../components/Select/style";
@import "../components/Select/style";

//multiselect
@import "../components/MultiSelect/style";

0 comments on commit a83176d

Please sign in to comment.