Skip to content

Commit

Permalink
feat(list): add list
Browse files Browse the repository at this point in the history
  • Loading branch information
berber1016 committed Nov 1, 2021
1 parent 6392c3f commit 47dd10b
Show file tree
Hide file tree
Showing 16 changed files with 598 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@types/react-resizable": "^1.7.3",
"classnames": "^2.2.6",
"date-fns": "^2.21.3",
"immutability-helper": "^3.1.1",
"lodash": "^4.17.14",
"moment": "^2.26.0",
"raf": "^3.4.1",
Expand Down
44 changes: 44 additions & 0 deletions src/list/Drag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import classNames from 'classnames';
import React, { useEffect, useState } from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';
import usePrefixCls from '../utils/hooks/use-prefix-cls';
import { PREFIX } from './constants';
import { DragListProps, OptionProps } from './interfance';
import DragItem from './DragItem';



const Drag:React.FC<DragListProps> = (props) => {
const { onChange,className,style,options:propsOPtions,disabled,...rest } = props;

const [options, setOptions] = useState(propsOPtions);
useEffect(() => {
setOptions(propsOPtions);
}, [propsOPtions]);
const prefixCls = `${usePrefixCls(PREFIX)}`;

const onMoved = (dragIndex: number, hoverIndex: number) => {
const dragCard = options[dragIndex];
const updateOptions = update(options, {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, dragCard],
],
});
setOptions(updateOptions);
onChange?.(updateOptions)
};

return (
<DndProvider backend={HTML5Backend}>
<div className={classNames(prefixCls,className)}
style={style}>
{options?.map((option:OptionProps,index:number) => <DragItem {...option} {...rest} index={index} onMoved={onMoved} disabled={option?.disabled ?? disabled}/>)}
</div>
</DndProvider>
)
}

export default Drag
60 changes: 60 additions & 0 deletions src/list/DragItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useRef } from 'react';
import { DragSourceMonitor, DropTargetMonitor, useDrag, useDrop, XYCoord } from 'react-dnd';
import { DragItemProps } from './interfance';
import Item from './Item';



const DragItem:React.FC<DragItemProps> = (props) => {
const { label, value, onMoved,index,...rest} = props;
const ref = useRef<HTMLDivElement>(null)
const [{ handlerId }, drop] = useDrop({
accept: 'drag-item',
collect(monitor) {
return {
handlerId: monitor.getHandlerId(),
}
},
hover(item: {index:number,type:string,id:string}, monitor: DropTargetMonitor) {
if (!ref.current) {
return
}
const dragIndex = item.index
const hoverIndex = index

if (dragIndex === hoverIndex) {
return
}
const hoverBoundingRect = ref.current?.getBoundingClientRect()
const hoverMiddleY =
(hoverBoundingRect.bottom - hoverBoundingRect.top) / 2
const clientOffset = monitor.getClientOffset()
const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return
}

// Dragging upwards
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return
}
onMoved?.((dragIndex as number), hoverIndex)
// eslint-disable-next-line no-param-reassign
item.index = hoverIndex
},
})
const [, drag] = useDrag({
item: { type:'drag-item',id:value,index },
collect: (monitor: DragSourceMonitor) => ({
isDragging: monitor.isDragging(),
}),
})

drag(drop(ref));
return <div ref={ref} data-handler-id={handlerId} key={value}>
<Item label={label} value={value} {...rest} isDrag />
</div>
}


export default DragItem
22 changes: 22 additions & 0 deletions src/list/Group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import usePrefixCls from '../utils/hooks/use-prefix-cls';
import { PREFIX } from './constants';

interface GroupPorps {
title?:string
}

const Group:React.FC<GroupPorps> =(props) =>{
const { title,children } = props;
const prefixCls = `${usePrefixCls(PREFIX)}--group`;
return (
<div className={prefixCls}>
<div className={`${prefixCls}--title`}>
{title}
</div>
{children}
</div>
)
}

export default Group;
53 changes: 53 additions & 0 deletions src/list/Item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import classNames from 'classnames';
import React from 'react';
import { isArray } from 'lodash';
import { MoveOutlined } from '@gio-design/icons';
import usePrefixCls from '../utils/hooks/use-prefix-cls';
import { PREFIX } from './constants';
import { ItemProps } from './interfance';
import Checkbox from '../checkbox/Checkbox';
import BaseItem from './inner/baseItem';

const Item:React.FC<ItemProps> = (props)=>{
const { label,className,style,prefix,suffix,children,disabled,selectedValue,disabledTooltip,isMultiple,isDrag,value,onMouseEnter,onMouseLeave,onClick} = props;

const prefixCls = `${usePrefixCls(PREFIX)}__item`;

const handleClick = () => {
if (!disabled && onClick) {
onClick({label:children ?? label,value});
}
}
// multiple check
const actived = isMultiple && isArray(selectedValue)
? (selectedValue as (string | number)[])?.indexOf(value) !== -1
: selectedValue === value;

return (
<li style={style} className={classNames(className,prefixCls,{
[`${prefixCls}--disabled`]: disabled,
[`${prefixCls}--actived`]: actived,
},

className)}
key={value}
aria-hidden="true"
onClick={handleClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}>
{
isMultiple && <Checkbox
className={`${prefixCls}--checkbox`}
checked={actived}
disabled={disabled}
/>
}
{
isDrag && <MoveOutlined className={`${prefixCls}--drag`} />
}
<BaseItem label={label} value={value} disabled={disabled} disabledTooltip={disabledTooltip} prefix={prefix} suffix={suffix} >{children}</BaseItem>
</li>
)
}

export default Item;
60 changes: 60 additions & 0 deletions src/list/List.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import classNames from 'classnames';
import React from 'react';
import { difference, isArray, isNil } from 'lodash';
import { OptionProps, ItemProps, BaseListProps } from './interfance';
import usePrefixCls from '../utils/hooks/use-prefix-cls';
import { PREFIX } from './constants';
import Item from './Item';

const List: React.FC<BaseListProps> = (props)=> {
const { className, style, options, children,disabled = false,value:controlledValue,isMultiple,prefix,suffix,onChange } = props;
const prefixCls = `${usePrefixCls(PREFIX)}`;
const handleClick = (option:OptionProps) => {
if(isMultiple){
let multipleValues = [];
if((controlledValue as (string | number)[])?.indexOf(option.value) !== -1 && !isNil(controlledValue)){
multipleValues = difference((controlledValue as (string | number)[]),[option.value]);
} else {
multipleValues = isArray(controlledValue) ? [...controlledValue,option.value] : [option.value];
}
onChange?.(multipleValues)
} else if(controlledValue !== option.value){
onChange?.(option.value)
}

}
const renderChildrens = ()=> {
if(isArray(options)){
return options?.map((option:OptionProps) => <Item {...option} prefix={prefix} suffix={suffix} disabled={option?.disabled ?? disabled} isMultiple={isMultiple} selectedValue={controlledValue} onClick={handleClick} />)
}
return React.Children.map(children, (node:React.ReactElement<ItemProps>,index) => {
const { props:{className:itemClassName,disabled:itemDisabled,prefix:itemPrefix,suffix:itemSuffix,onClick,...rest} } = node;
return React.cloneElement(node,{
className:classNames(itemClassName, {
[`${prefixCls}__item--interval`]: index !== 0
}),
disabled:itemDisabled ?? disabled,
prefix:itemPrefix ?? prefix,
suffix:itemSuffix ?? suffix,
isMultiple,
selectedValue:controlledValue,
onClick:(option:OptionProps)=>{
handleClick(option);
onClick?.(option);
},
...rest
})
})
}

return (
<div className={
classNames(prefixCls,className)
}
style={style}>
{renderChildrens()}
</div>
)
}

export default List
7 changes: 7 additions & 0 deletions src/list/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const DEFAULT_SHOW_ITEMS_COUNT = 10;
export const PREFIX = 'list-new';

export default {
DEFAULT_SHOW_ITEMS_COUNT,
PREFIX,
};
Loading

0 comments on commit 47dd10b

Please sign in to comment.