ControlList.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import React, { PureComponent, GetDerivedStateFromProps } from 'react'
  2. import { fromJS } from 'immutable'
  3. import { IControl, IRenderTreeItem } from '../types'
  4. import { getControlRenderTree, getAllChildren } from '../util'
  5. import { ListItem } from 'components/ListFormLayout'
  6. import styles from '../Control.less'
  7. import { Icon, Tree } from 'antd'
  8. const { TreeNode } = Tree
  9. interface IControlListProps {
  10. list: IControl[]
  11. selected: Omit<IControl, 'relatedItems' | 'relatedViews'>
  12. onSelect: (key: string) => void
  13. onDelete: (keys: string[], selectKey: string) => void
  14. onNameChange: (key: string, name: string) => void
  15. onParentChange: (
  16. key: string,
  17. parentKey: string,
  18. type: string,
  19. dropNextKey?: string
  20. ) => void
  21. }
  22. interface IControlListStates {
  23. renderTree: IRenderTreeItem[]
  24. flatTree: {
  25. [key: string]: IRenderTreeItem
  26. }
  27. selectedKeys: string[]
  28. prevList: IControl[]
  29. prevSelected: Omit<IControl, 'relatedItems' | 'relatedViews'>
  30. }
  31. class ControlList extends PureComponent<IControlListProps, IControlListStates> {
  32. constructor(props) {
  33. super(props)
  34. this.state = {
  35. renderTree: [],
  36. flatTree: {},
  37. selectedKeys: [],
  38. prevList: [],
  39. prevSelected: null
  40. }
  41. }
  42. public static getDerivedStateFromProps: GetDerivedStateFromProps<
  43. IControlListProps,
  44. IControlListStates
  45. > = (props, state) => {
  46. let nextState: Partial<IControlListStates> = {}
  47. if (props.list !== state.prevList) {
  48. const replica = fromJS(props.list).toJS()
  49. nextState = {
  50. ...getControlRenderTree(replica),
  51. prevList: props.list
  52. }
  53. }
  54. if (props.selected !== state.prevSelected) {
  55. nextState.selectedKeys = props.selected ? [props.selected.key] : []
  56. nextState.prevSelected = props.selected
  57. }
  58. return nextState
  59. }
  60. private delete = (key: string) => {
  61. const { selected } = this.props
  62. const { renderTree, flatTree } = this.state
  63. const delKeys = [key].concat(getAllChildren(key, flatTree))
  64. let selectedKey: string
  65. if (selected.key === key) {
  66. if (selected.parent) {
  67. const parentTree = flatTree[selected.parent]
  68. if (parentTree.children.length === 1) {
  69. selectedKey = parentTree.key
  70. } else {
  71. const delIndex = parentTree.children.findIndex((c) => c.key === key)
  72. selectedKey =
  73. delIndex === parentTree.children.length - 1
  74. ? parentTree.children[delIndex - 1].key
  75. : parentTree.children[delIndex + 1].key
  76. }
  77. } else {
  78. if (renderTree.length !== 1) {
  79. const delIndex = renderTree.findIndex((n) => n.key === key)
  80. selectedKey =
  81. delIndex === renderTree.length - 1
  82. ? renderTree[delIndex - 1].key
  83. : renderTree[delIndex + 1].key
  84. }
  85. }
  86. } else {
  87. selectedKey = selected.key
  88. }
  89. this.props.onDelete(delKeys, selectedKey)
  90. }
  91. private select = (selectedKeys: string[]) => {
  92. if (selectedKeys.length) {
  93. this.props.onSelect(selectedKeys[0])
  94. }
  95. }
  96. private dragEnter = (info) => {
  97. }
  98. private drop = (info) => {
  99. const dropKey = info.node.props.eventKey
  100. const dragKey = info.dragNode.props.eventKey
  101. const dropPos = info.node.props.pos.split('-')
  102. const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1])
  103. const { onParentChange } = this.props
  104. const { flatTree } = this.state
  105. let parentKey
  106. let type
  107. let dropNextKey
  108. if (!info.dropToGap) {
  109. parentKey = dropKey
  110. type = 'append'
  111. } else if (
  112. (info.node.props.children || []).length > 0 &&
  113. info.node.props.expanded &&
  114. dropPosition === 1
  115. ) {
  116. parentKey = dropKey
  117. type = 'prepend'
  118. } else {
  119. const dropped = flatTree[dropKey]
  120. parentKey = dropped.parent
  121. dropNextKey = dropKey
  122. type = dropPosition === -1 ? 'prepend' : 'append'
  123. }
  124. onParentChange(dragKey, parentKey, type, dropNextKey)
  125. }
  126. private renderTreeNodes = (renderTree: IRenderTreeItem[]) =>
  127. renderTree.map((node) => {
  128. const { key, name, children } = node
  129. const title = (
  130. <ListItem
  131. id={key}
  132. name={name}
  133. onChange={this.props.onNameChange}
  134. onDelete={this.delete}
  135. />
  136. )
  137. if (children) {
  138. return (
  139. <TreeNode title={title} key={key} dataRef={node}>
  140. {this.renderTreeNodes(node.children)}
  141. </TreeNode>
  142. )
  143. }
  144. return <TreeNode title={title} key={key} dataRef={node} />
  145. })
  146. public render() {
  147. const { renderTree, selectedKeys } = this.state
  148. return (
  149. <Tree
  150. className={styles.tree}
  151. selectedKeys={selectedKeys}
  152. onSelect={this.select}
  153. onDragEnter={this.dragEnter}
  154. onDrop={this.drop}
  155. defaultExpandAll
  156. draggable
  157. blockNode
  158. >
  159. {this.renderTreeNodes(renderTree)}
  160. </Tree>
  161. )
  162. }
  163. }
  164. export default ControlList