import { Component, Fragment } from 'react'; import { Icon, Button, Message } from '@alifd/next'; import Sortable from './sortable'; import { SettingField, SetterType, FieldConfig } from '../../main'; import './style.less'; import { createSettingFieldView } from '../../settings-pane'; import { PopupContext, PopupPipe } from '../../popup'; import Title from '../../title'; interface ArraySetterState { items: SettingField[]; itemsMap: Map; prevLength: number; } interface ArraySetterProps { value: any[]; field: SettingField; itemConfig?: { setter?: SetterType; defaultValue?: any | ((field: SettingField) => any); required?: boolean; }; multiValue?: boolean; } export class ListSetter extends Component { static getDerivedStateFromProps(props: ArraySetterProps, state: ArraySetterState) { const { value, field } = props; const newLength = value && Array.isArray(value) ? value.length : 0; if (state && state.prevLength === newLength) { return null; } // props value length change will go here const originLength = state ? state.items.length : 0; if (state && originLength === newLength) { return { prevLength: newLength, }; } const itemsMap = state ? state.itemsMap : new Map(); let items = state ? state.items.slice() : []; if (newLength > originLength) { for (let i = originLength; i < newLength; i++) { const item = field.createField({ ...props.itemConfig, name: i, forceInline: 2, }); items[i] = item; itemsMap.set(item.id, item); } } else if (newLength < originLength) { const deletes = items.splice(newLength); deletes.forEach(item => { itemsMap.delete(item.id); }); } return { items, itemsMap, prevLength: newLength, }; } state: ArraySetterState = { items: [], itemsMap: new Map(), prevLength: 0, }; onSort(sortedIds: Array) { const { itemsMap } = this.state; const items = sortedIds.map((id, index) => { const item = itemsMap.get(id)!; item.setKey(index); return item; }); this.setState({ items, }); } private scrollToLast: boolean = false; onAdd() { const { items, itemsMap } = this.state; const { itemConfig } = this.props; const defaultValue = itemConfig ? itemConfig.defaultValue : null; const item = this.props.field.createField({ ...itemConfig, name: items.length, forceInline: 1, }); items.push(item); itemsMap.set(item.id, item); item.setValue(typeof defaultValue === 'function' ? defaultValue(item) : defaultValue); this.scrollToLast = true; this.setState({ items: items.slice(), }); } onRemove(field: SettingField) { const { items } = this.state; let i = items.indexOf(field); if (i < 0) { return; } items.splice(i, 1); const l = items.length; while (i < l) { items[i].setKey(i); i++; } field.remove(); this.setState({ items: items.slice() }); } componentWillUnmount() { this.state.items.forEach(field => { field.purge(); }); } shouldComponentUpdate(_: any, nextState: ArraySetterState) { if (nextState.items !== this.state.items) { return true; } return false; } render() { // mini Button: depends popup if (this.props.itemConfig) { // check is ObjectSetter then check if show columns } const { items } = this.state; const scrollToLast = this.scrollToLast; this.scrollToLast = false; const lastIndex = items.length - 1; const content = items.length > 0 ? (
{items.map((field, index) => ( ))}
) : this.props.multiValue ? ( 当前选择了多个节点,且值不一致,修改会覆盖所有值 ) : ( 当前项目为空 ); return (
{/*
*/} {content}
); } } class ArrayItem extends Component<{ field: SettingField; onRemove: () => void; scrollIntoView: boolean; }> { shouldComponentUpdate() { return false; } private shell?: HTMLDivElement | null; componentDidMount() { if (this.props.scrollIntoView && this.shell) { this.shell.parentElement!.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } } render() { const { onRemove, field } = this.props; return (
(this.shell = ref)}>
{createSettingFieldView(field, field.parent)}
); } } class TableSetter extends ListSetter { // todo: // forceInline = 1 // has more actions } export default class ArraySetter extends Component<{ value: any[]; field: SettingField; itemConfig?: { setter?: SetterType; defaultValue?: any | ((field: SettingField) => any); required?: boolean; }; mode?: 'popup' | 'list'; forceInline?: boolean; multiValue?: boolean; }> { static contextType = PopupContext; private pipe: any; render() { const { mode, forceInline, ...props } = this.props; const { field, itemConfig } = props; if (mode === 'popup' || forceInline) { const title = ( 编辑: </Fragment> ); if (!this.pipe) { let width = 360; const setter: any = itemConfig?.setter; if (setter?.componentName === 'ObjectSetter') { const items: FieldConfig[] = setter.props?.config?.items; if (items && Array.isArray(items)) { const length = items.filter(item => item.required || item.important).length; if (length === 3) { width = 480; } else if (length > 3) { width = 600; } } } this.pipe = (this.context as PopupPipe).create({ width }); } this.pipe.send( <TableSetter key={field.id} {...props} />, title, ); return ( <Button onClick={e => { this.pipe.show((e as any).target); }} > <Icon type="edit" /> {forceInline ? title : '编辑数组'} </Button> ); } else { return <ListSetter {...props} />; } } }