-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
chen_gh
committed
Sep 10, 2020
1 parent
b5307cf
commit a648464
Showing
30 changed files
with
2,317 additions
and
3 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import React from 'react'; | ||
import Checkbox from 'antd/lib/checkbox'; | ||
import noop from 'lodash/noop'; | ||
import { GroupProps } from './interface'; | ||
|
||
export default class Group extends React.PureComponent<GroupProps> { | ||
public static defaultProps = { | ||
onSelect: noop, | ||
style: {}, | ||
}; | ||
|
||
public render() { | ||
const { | ||
name, | ||
icon, | ||
style, | ||
isMultiple, | ||
showGroupCheckBox = false, | ||
isSelected, | ||
indeterminate, | ||
option, | ||
labelRenderer, | ||
} = this.props; | ||
|
||
return ( | ||
<div className="gio-select-option group" style={{ ...style, color: '#1248E9' }} onClick={this.handleSelect}> | ||
{icon} | ||
{isMultiple && showGroupCheckBox && <Checkbox checked={isSelected} indeterminate={indeterminate} />} | ||
{labelRenderer ? labelRenderer(option, true) : name} | ||
</div> | ||
); | ||
} | ||
|
||
private handleSelect = () => { | ||
this.props.onSelect && this.props.onSelect(this.props.option); | ||
}; | ||
} |
139 changes: 139 additions & 0 deletions
139
packages/components/src/components/list/Sortable/SortableItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import React, { cloneElement } from 'react'; | ||
import classnames from 'classnames'; | ||
import { findDOMNode } from 'react-dom'; | ||
import reactDnd, { DragSource, DropTarget } from 'react-dnd'; | ||
|
||
export interface ItemWithId { | ||
value: string | number; | ||
canDrag: boolean; | ||
} | ||
|
||
export interface SortableItemProps { | ||
template: React.ReactElement<any>; | ||
index: number; | ||
position: number; | ||
sortData: ItemWithId; | ||
className?: string; | ||
canDrag: boolean; | ||
onDrop: () => any; | ||
onCancel: () => any; | ||
onHover: (sourcePosition: number, targetPosition: number) => any; | ||
} | ||
|
||
export interface WrappedSortableItemProps extends SortableItemProps { | ||
connectDragSource: reactDnd.ConnectDragSource; | ||
connectDropTarget: reactDnd.ConnectDropTarget; | ||
isDragging: boolean; | ||
isOver: boolean; | ||
} | ||
|
||
const dragSource = { | ||
beginDrag(props: SortableItemProps) { | ||
return { | ||
position: props.position, | ||
}; | ||
}, | ||
|
||
endDrag(props: SortableItemProps, monitor: reactDnd.DragSourceMonitor) { | ||
if (monitor.didDrop()) { | ||
props.onDrop(); | ||
} else { | ||
props.onCancel(); | ||
} | ||
}, | ||
|
||
canDrag(props: SortableItemProps) { | ||
return props.canDrag; | ||
}, | ||
}; | ||
|
||
const dropTarget = { | ||
hover(props: SortableItemProps, monitor: reactDnd.DropTargetMonitor) { | ||
const sourcePosition = (monitor.getItem() as SortableItemProps).position; | ||
const targetPosition = props.position; | ||
|
||
if (sourcePosition !== targetPosition) { | ||
props.onHover(sourcePosition, targetPosition); | ||
} | ||
}, | ||
}; | ||
|
||
function collectSource(connect: reactDnd.DragSourceConnector, monitor: reactDnd.DragSourceMonitor) { | ||
return { | ||
connectDragSource: connect.dragSource(), | ||
connectDragPreview: connect.dragPreview(), | ||
isDragging: monitor.isDragging(), | ||
}; | ||
} | ||
|
||
function collectTarget(connect: reactDnd.DropTargetConnector, monitor: reactDnd.DropTargetMonitor) { | ||
return { | ||
connectDropTarget: connect.dropTarget(), | ||
isOver: monitor.isOver(), | ||
}; | ||
} | ||
|
||
class Item extends React.PureComponent<WrappedSortableItemProps, {}> { | ||
public state = { | ||
hovered: false, | ||
}; | ||
|
||
public componentDidMount() { | ||
const ref = this._createRef(); | ||
const { connectDragSource, connectDropTarget } = this.props; | ||
/*eslint-disable */ | ||
const domNode = (findDOMNode(this.refs[ref]) as any) as React.ReactElement<{}>; | ||
/* eslint-enable */ | ||
connectDragSource(domNode); | ||
connectDropTarget(domNode); | ||
} | ||
|
||
public render() { | ||
const isOver = this.props.isOver; | ||
const isDragging = this.props.isDragging; | ||
return cloneElement(this.props.template, { | ||
sortData: this.props.sortData, | ||
index: this.props.index, | ||
isOver, | ||
isDragging, | ||
ref: this._createRef(), | ||
onMouseEnter: this._onMouseEnter, | ||
onMouseLeave: this._onMouseLeave, | ||
onDragStart: this._onDragStart, | ||
className: classnames(this.props.className, 'gio-sortable-item', { | ||
'is-over': isOver, | ||
'is-dragging': isDragging, | ||
'is-hovered': this.state.hovered, | ||
}), | ||
}); | ||
} | ||
|
||
private _onDragStart = () => { | ||
this.setState({ | ||
hovered: false, | ||
}); | ||
}; | ||
|
||
private _onMouseEnter = () => { | ||
this.setState({ | ||
hovered: true, | ||
}); | ||
}; | ||
|
||
private _onMouseLeave = () => { | ||
this.setState({ | ||
hovered: false, | ||
}); | ||
}; | ||
|
||
private _createRef() { | ||
const position = this.props.position; | ||
const value = this.props.sortData.value; | ||
return `${value}${position}`; | ||
} | ||
} | ||
|
||
export default () => { | ||
const type = 'item'; | ||
return DragSource(type, dragSource, collectSource)(DropTarget(type, dropTarget, collectTarget)(Item)); | ||
}; |
94 changes: 94 additions & 0 deletions
94
packages/components/src/components/list/Sortable/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import React from 'react'; | ||
import createSortableItem, { ItemWithId } from './SortableItem'; | ||
import _ from 'lodash'; | ||
import { DndProvider } from 'react-dnd'; | ||
import HTML5Backend from 'react-dnd-html5-backend'; | ||
|
||
export interface SortableCollectionProps { | ||
container: string; | ||
collection: ItemWithId[]; | ||
onSorted: (collection: ItemWithId[]) => any; | ||
getId?: (item: ItemWithId, index: number) => string; | ||
template: React.ReactElement<any>; | ||
} | ||
|
||
export interface SortableCollectionState { | ||
collection: ItemWithId[]; | ||
SortableItemClass: React.Component; | ||
} | ||
|
||
export default class SortableCollection extends React.PureComponent<SortableCollectionProps, {}> { | ||
public static defaultProps = { container: 'div' }; | ||
|
||
public static getDerivedStateFromProps(nextProps: SortableCollectionProps, state: any) { | ||
if (state.stateChanged) { | ||
return { ...state, stateChanged: false }; | ||
} | ||
return { collection: nextProps.collection }; | ||
} | ||
|
||
public state: any = { | ||
collection: this.props.collection, | ||
SortableItemClass: createSortableItem(), | ||
stateChanged: false, | ||
}; | ||
|
||
public render() { | ||
const SortableItem = this.state.SortableItemClass; | ||
|
||
const children = this.state.collection.map((props: ItemWithId, i: number) => { | ||
const originalPosition = this.props.collection.indexOf(props); | ||
const key = this.props.getId ? this.props.getId(props, i) : props.value; | ||
const canDrag = props.canDrag === false ? props.canDrag : true; | ||
return ( | ||
<SortableItem | ||
sortData={props} | ||
canDrag={canDrag} | ||
index={i} | ||
key={key} | ||
position={originalPosition} | ||
onHover={this._handleHover} | ||
onDrop={this._handleDrop} | ||
onCancel={this._handleCancel} | ||
template={this.props.template} | ||
/> | ||
); | ||
}); | ||
|
||
return ( | ||
<DndProvider backend={HTML5Backend as any}> | ||
{React.createElement( | ||
this.props.container, | ||
_.omit(this.props, ['collection', 'container', 'onSorted', 'template']), | ||
children | ||
)} | ||
</DndProvider> | ||
); | ||
} | ||
|
||
private _handleHover = (originalSourcePosition: number, originalTargetPosition: number) => { | ||
const source = this.props.collection[originalSourcePosition]; | ||
const currentSourcePosition = this.state.collection.indexOf(source); | ||
const target = this.props.collection[originalTargetPosition]; | ||
const currentTargetPosition = this.state.collection.indexOf(target); | ||
console.log('===================================='); | ||
console.log(this.state.collection); | ||
console.log('===================================='); | ||
if (source) { | ||
const newCollection = [...this.state.collection]; | ||
newCollection.splice(currentSourcePosition, 1); | ||
newCollection.splice(currentTargetPosition, 0, source); | ||
this.setState({ collection: newCollection, stateChanged: true }); | ||
} | ||
}; | ||
|
||
private _handleDrop = () => { | ||
if (this.props.collection !== this.state.collection) { | ||
this.props.onSorted(this.state.collection); | ||
} | ||
}; | ||
|
||
private _handleCancel = () => { | ||
this.setState({ collection: this.props.collection, stateChanged: true }); | ||
}; | ||
} |
88 changes: 88 additions & 0 deletions
88
packages/components/src/components/list/Sortable/template.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import React from 'react'; | ||
import classnames from 'classnames'; | ||
import { noop, pick, get } from 'lodash'; | ||
import { DragMove, CloseCircleFilled } from '@gio-design/icons'; | ||
|
||
const bemClsFactor = (blockName: string) => (elemName?: string, modifierName?: string) => { | ||
const elemName_ = elemName ? `__${elemName}` : ''; | ||
const modifierName_ = modifierName ? `--${modifierName}` : ''; | ||
return `${blockName}${elemName_}${modifierName_}`; | ||
}; | ||
|
||
const cls = bemClsFactor('pa_sider-selected-item'); | ||
|
||
interface SiderSelectedItemProps { | ||
onRemove: (sortData: any) => void; | ||
selected?: string; | ||
collapsed?: boolean; | ||
onSelect?: (item: any) => void; | ||
[key: string]: any; | ||
} | ||
|
||
export default class SiderSelectedItem extends React.PureComponent<SiderSelectedItemProps, any> { | ||
public static defaultProps = { | ||
onRemove: noop, | ||
}; | ||
|
||
public constructor(props: SiderSelectedItemProps) { | ||
super(props); | ||
this.state = { | ||
visible: false, | ||
}; | ||
} | ||
|
||
public render() { | ||
const { sortData: item = {} } = this.props; | ||
const props = this.props; | ||
const className = classnames('gio-select-option', cls(), { | ||
indented: props.indented, | ||
selected: item.value === props.selected, | ||
collapsed: props.collapsed, | ||
[this.props.className]: !!this.props.className, | ||
[`v-${item.value}`]: true, | ||
}); | ||
|
||
return ( | ||
<div | ||
{...pick(props, ['onDragStart', 'onMouseEnter', 'onMouseLeave'])} | ||
className={className} | ||
onClick={() => { | ||
if (props.onSelect) { | ||
props.onSelect(item); | ||
} | ||
}} | ||
> | ||
{get(item, 'canDrag') !== false && ( | ||
<IconCircle className={classnames({ selected: item.value === props.selected, collapsed: props.collapsed })}> | ||
<DragMove className="icon-drag" /> | ||
</IconCircle> | ||
)} | ||
|
||
{props.collapsed ? null : ( | ||
<span className={classnames(cls('name-wrap'))}> | ||
<span className={classnames(cls('name'), { selected: item.value === props.selected })} title={item.name}> | ||
{item.label} | ||
</span> | ||
</span> | ||
)} | ||
|
||
<span | ||
onClick={(e) => e.stopPropagation()} | ||
className={classnames('dh-can-remove', { | ||
collapsed: props.collapsed, | ||
})} | ||
> | ||
<CloseCircleFilled size="small" color="#5C4E61" /> | ||
</span> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
const IconCircle = (props: any) => { | ||
const className = classnames('pa-sider-icon-circle', props.className, { | ||
scaled: props.scaled, // 利用 transform scale 解决 font-size < 12 的问题 | ||
}); | ||
|
||
return <span className={className}>{props.children}</span>; | ||
}; |
Oops, something went wrong.