import React from 'react'; import {ThemeProps, themeable} from '../theme'; import {LocaleProps, localeable} from '../locale'; import InputBox from './InputBox'; import {Icon} from './icons'; import Button from './Button'; import {autobind, guid} from '../utils/helper'; import {uncontrollable} from 'uncontrollable'; import Sortable from 'sortablejs'; import {findDOMNode} from 'react-dom'; export interface ArrayInputProps extends ThemeProps, LocaleProps { value?: Array; onChange?: (value: Array) => void; placeholder: string; itemRender: (props: { value: any; onChange: (value: any) => void; index: number; disabled?: boolean; }) => JSX.Element; itemInitalValue?: any; maxLength?: number; minLength?: number; disabled?: boolean; sortable?: boolean; removable?: boolean; addable?: boolean; editable?: boolean; sortTip?: string; } export class ArrayInput extends React.Component { static defaultProps = { placeholder: 'empty', itemRender: ({ value, onChange }: { value: any; onChange: (value: any) => void; index: number; disabled?: boolean; }) => }; id: string = guid(); dragTip?: HTMLElement; sortable?: Sortable; handleItemOnChange(index: number, itemValue: any) { const {onChange} = this.props; const value = this.props.value; const newValue = Array.isArray(value) ? value.concat() : []; newValue.splice(index, 1, itemValue); onChange?.(newValue); } @autobind dragTipRef(ref: any) { if (!this.dragTip && ref) { this.initDragging(); } else if (this.dragTip && !ref) { this.destroyDragging(); } this.dragTip = ref; } @autobind handleAdd() { const {value, onChange, itemInitalValue} = this.props; const newValue = Array.isArray(value) ? value.concat() : []; newValue.push(itemInitalValue); onChange?.(newValue); } @autobind handleRemove(e: React.MouseEvent) { const indx = parseInt(e.currentTarget.getAttribute('data-index')!, 10); const {value, onChange, itemInitalValue} = this.props; const newValue = Array.isArray(value) ? value.concat() : []; newValue.splice(indx, 1); onChange?.(newValue); } initDragging() { const onChange = this.props.onChange; const ns = this.props.classPrefix; const dom = findDOMNode(this) as HTMLElement; this.sortable = new Sortable( dom.querySelector(`.drag-group`) as HTMLElement, { group: `array-input-${this.id}`, animation: 150, handle: `.drag-bar`, ghostClass: `${ns}ArrayInput-item--dragging`, onEnd: (e: any) => { // 没有移动 if (e.newIndex === e.oldIndex) { return; } // 换回来 const parent = e.to as HTMLElement; if ( e.newIndex < e.oldIndex && e.oldIndex < parent.childNodes.length - 1 ) { parent.insertBefore(e.item, parent.childNodes[e.oldIndex + 1]); } else if (e.oldIndex < parent.childNodes.length - 1) { parent.insertBefore(e.item, parent.childNodes[e.oldIndex]); } else { parent.appendChild(e.item); } const value = this.props.value; if (!Array.isArray(value)) { return; } const newValue = value.concat(); newValue.splice(e.newIndex, 0, newValue.splice(e.oldIndex, 1)[0]); onChange?.(newValue); } } ); } destroyDragging() { this.sortable && this.sortable.destroy(); } renderItem(value: any, index: number, collection: Array) { const { itemRender, disabled, classnames: cx, sortable, removable, minLength } = this.props; return (
{sortable && collection.length > 1 && !disabled ? ( ) : null} {itemRender({ value, onChange: this.handleItemOnChange.bind(this, index), index, disabled })} {removable !== false && !disabled && (!minLength || collection.length > minLength) ? ( ) : null}
); } render() { const { classnames: cx, value, placeholder, translate: __, maxLength, sortable, sortTip, disabled } = this.props; return (
{Array.isArray(value) && value.length ? (
{value.map((item, index) => this.renderItem(item, index, value))}
) : (
{__(placeholder)}
)}
1 ? 'ArrayInput-toolbar--dnd' : '' )} > {!Array.isArray(value) || !maxLength || value.length < maxLength ? ( ) : null} {sortable && Array.isArray(value) && value.length ? ( {Array.isArray(value) && value.length > 1 ? __(sortTip) : ''} ) : null}
); } } export default themeable( localeable( uncontrollable(ArrayInput, { value: 'onChange' }) ) );