dragact.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. import * as React from "react";
  2. import GridItem, { GridItemEvent } from './GridItem'
  3. import { compactLayout } from './util/compact';
  4. import { getMaxContainerHeight } from './util/sort';
  5. import { layoutCheck } from './util/collison';
  6. import { correctLayout } from './util/correction';
  7. import { stringJoin } from './utils';
  8. import { layoutItemForkey, syncLayout } from './util/initiate';
  9. import './style.css';
  10. export interface DragactLayoutItem {
  11. GridX: number
  12. GridY: number
  13. static?: Boolean
  14. w: number
  15. h: number
  16. isUserMove?: Boolean
  17. key?: number | string
  18. handle?: Boolean
  19. canDrag?: Boolean
  20. canResize?: Boolean
  21. }
  22. export interface DragactProps {
  23. layout: DragactLayoutItem[]
  24. /**
  25. * 宽度切分比
  26. * 这个参数会把容器的宽度平均分为col等份
  27. * 于是容器内元素的最小宽度就等于 containerWidth/col
  28. */
  29. col: number,
  30. /**
  31. * 容器的宽度
  32. */
  33. width: number,
  34. /**容器内每个元素的最小高度 */
  35. rowHeight: number,
  36. /**
  37. * 容器内部的padding
  38. */
  39. padding?: number,
  40. children: (Item: DragactLayoutItem, provided: GridItemProvided) => any,
  41. //
  42. // interface GridItemEvent {
  43. // event: any //浏览器拖动事件
  44. // GridX: number //在布局中的x格子
  45. // GridY: number //在布局中的y格子
  46. // w: number //元素的宽度
  47. // h: number //元素的高度
  48. // UniqueKey: string | number //元素的唯一key
  49. // }
  50. /**
  51. * 拖动开始的回调
  52. */
  53. onDragStart?: (event: GridItemEvent) => void
  54. /**
  55. * 拖动中的回调
  56. */
  57. onDrag?: (event: GridItemEvent) => void
  58. /**
  59. * 拖动结束的回调
  60. */
  61. onDragEnd?: (event: GridItemEvent) => void
  62. /**
  63. * 每个元素的margin,第一个参数是左右,第二个参数是上下
  64. */
  65. margin: [number, number]
  66. /**
  67. * layout的名字
  68. */
  69. className: number | string
  70. /**是否有placeholder */
  71. placeholder?: Boolean
  72. style?: React.CSSProperties
  73. }
  74. export interface mapLayout {
  75. [key: string]: DragactLayoutItem
  76. }
  77. interface DragactState {
  78. GridXMoving: number
  79. GridYMoving: number
  80. wMoving: number
  81. hMoving: number
  82. placeholderShow: Boolean
  83. placeholderMoving: Boolean
  84. layout: DragactLayoutItem[]
  85. containerHeight: number
  86. dragType: 'drag' | 'resize'
  87. mapLayout: mapLayout | undefined
  88. }
  89. export interface GridItemProvided {
  90. isDragging: Boolean
  91. dragHandle: any;
  92. resizeHandle: any;
  93. props: any;
  94. }
  95. export class Dragact extends React.Component<DragactProps, DragactState> {
  96. constructor(props: DragactProps) {
  97. super(props)
  98. this.onDrag = this.onDrag.bind(this)
  99. this.onDragStart = this.onDragStart.bind(this)
  100. this.onDragEnd = this.onDragEnd.bind(this)
  101. const layout = props.layout;
  102. this.state = {
  103. GridXMoving: 0,
  104. GridYMoving: 0,
  105. wMoving: 0,
  106. hMoving: 0,
  107. placeholderShow: false,
  108. placeholderMoving: false,
  109. layout: layout,
  110. containerHeight: 500,
  111. dragType: 'drag',
  112. mapLayout: undefined
  113. }
  114. }
  115. onResizeStart = (layoutItem: GridItemEvent) => {
  116. const { GridX, GridY, w, h } = layoutItem
  117. if (this.state.mapLayout) {
  118. const newlayout = syncLayout(this.state.mapLayout, layoutItem)
  119. this.setState({
  120. GridXMoving: GridX,
  121. GridYMoving: GridY,
  122. wMoving: w,
  123. hMoving: h,
  124. placeholderShow: true,
  125. placeholderMoving: true,
  126. mapLayout: newlayout,
  127. dragType: 'resize'
  128. })
  129. }
  130. this.props.onDragStart && this.props.onDragStart(layoutItem);
  131. }
  132. onResizing = (layoutItem: GridItemEvent) => {
  133. const newLayout = layoutCheck(this.state.layout, layoutItem, layoutItem.UniqueKey + '', layoutItem.UniqueKey + '', 0);
  134. const { compacted, mapLayout } = compactLayout(newLayout, layoutItem, this.state.mapLayout)
  135. this.setState({
  136. layout: compacted,
  137. wMoving: layoutItem.w,
  138. hMoving: layoutItem.h,
  139. mapLayout: mapLayout,
  140. containerHeight: getMaxContainerHeight(compacted, this.props.rowHeight, this.props.margin[1], this.state.containerHeight)
  141. })
  142. }
  143. onResizeEnd = (layoutItem: GridItemEvent) => {
  144. const { compacted, mapLayout } = compactLayout(this.state.layout, undefined, this.state.mapLayout)
  145. this.setState({
  146. placeholderShow: false,
  147. layout: compacted,
  148. mapLayout: mapLayout,
  149. containerHeight: getMaxContainerHeight(compacted, this.props.rowHeight, this.props.margin[1], this.state.containerHeight)
  150. })
  151. this.props.onDragEnd && this.props.onDragEnd(layoutItem);
  152. }
  153. onDragStart(bundles: GridItemEvent) {
  154. const { GridX, GridY, w, h } = bundles
  155. if (this.state.mapLayout) {
  156. this.setState({
  157. GridXMoving: GridX,
  158. GridYMoving: GridY,
  159. wMoving: w,
  160. hMoving: h,
  161. placeholderShow: true,
  162. placeholderMoving: true,
  163. mapLayout: syncLayout(this.state.mapLayout, bundles),
  164. dragType: 'drag'
  165. })
  166. }
  167. this.props.onDragStart && this.props.onDragStart(bundles);
  168. }
  169. onDrag(layoutItem: GridItemEvent) {
  170. const { GridY, UniqueKey } = layoutItem;
  171. const moving = GridY - this.state.GridYMoving;
  172. const newLayout = layoutCheck(this.state.layout, layoutItem, UniqueKey + '', UniqueKey + ''/*用户移动方块的key */, moving);
  173. const { compacted, mapLayout } = compactLayout(newLayout, layoutItem, this.state.mapLayout);
  174. this.setState({
  175. GridXMoving: layoutItem.GridX,
  176. GridYMoving: layoutItem.GridY,
  177. layout: compacted,
  178. mapLayout: mapLayout,
  179. containerHeight: getMaxContainerHeight(compacted, this.props.rowHeight, this.props.margin[1], this.state.containerHeight)
  180. })
  181. this.props.onDrag && this.props.onDrag(layoutItem);
  182. }
  183. onDragEnd(layoutItem: GridItemEvent) {
  184. const { compacted, mapLayout } = compactLayout(this.state.layout, undefined, this.state.mapLayout)
  185. this.setState({
  186. placeholderShow: false,
  187. layout: compacted,
  188. mapLayout: mapLayout,
  189. containerHeight: getMaxContainerHeight(compacted, this.props.rowHeight, this.props.margin[1], this.state.containerHeight)
  190. })
  191. this.props.onDragEnd && this.props.onDragEnd(layoutItem);
  192. }
  193. renderPlaceholder() {
  194. if (!this.state.placeholderShow) return null
  195. var { col, width, padding, rowHeight, margin, placeholder } = this.props
  196. const { GridXMoving, GridYMoving, wMoving, hMoving, placeholderMoving, dragType } = this.state
  197. if (!placeholder) return null;
  198. if (!padding) padding = 0;
  199. return (
  200. <GridItem
  201. margin={margin}
  202. col={col}
  203. containerWidth={width}
  204. containerPadding={[padding, padding]}
  205. rowHeight={rowHeight}
  206. GridX={GridXMoving}
  207. GridY={GridYMoving}
  208. w={wMoving}
  209. h={hMoving}
  210. style={{ background: 'rgba(15,15,15,0.3)', zIndex: dragType === 'drag' ? 1 : 10, transition: ' all .15s ease-out' }}
  211. isUserMove={!placeholderMoving}
  212. dragType={dragType}
  213. canDrag={false}
  214. canResize={false}
  215. >
  216. {(p: any, resizerProps: any) => <div {...p} />}
  217. </GridItem>
  218. )
  219. }
  220. componentWillReceiveProps(nextProps: any) {
  221. if (this.props.layout.length > nextProps.layout.length) { //remove
  222. const mapLayoutCopy = { ...this.state.mapLayout };
  223. nextProps.layout.forEach((child: any) => {
  224. if ((mapLayoutCopy as any)[child.key + ''] !== void 666) delete (mapLayoutCopy as any)[child.key + ''];
  225. })
  226. for (const key in mapLayoutCopy) {
  227. const newLayout = this.state.layout.filter((child) => {
  228. if (child.key + '' !== key + '') return child
  229. })
  230. const { compacted, mapLayout } = compactLayout(newLayout, undefined, this.state.mapLayout);
  231. this.setState({
  232. containerHeight: getMaxContainerHeight(compacted, this.props.rowHeight, this.props.margin[1], this.state.containerHeight),
  233. layout: compacted,
  234. mapLayout
  235. })
  236. }
  237. } else if (this.props.layout.length < nextProps.layout.length) {//add
  238. var item;
  239. for (const idx in nextProps.layout) {
  240. const i = nextProps.layout[idx];
  241. if (this.state.mapLayout && !this.state.mapLayout[i.key + '']) {
  242. item = i;
  243. break;
  244. }
  245. }
  246. if (item !== void 666) {
  247. const dataSet = { ...item, isUserMove: false, key: item.key + '' };
  248. var newLayout = [...this.state.layout, dataSet]
  249. const { compacted, mapLayout } = compactLayout(newLayout, undefined, this.state.mapLayout);
  250. this.setState({
  251. containerHeight: getMaxContainerHeight(compacted,
  252. this.props.rowHeight,
  253. this.props.margin[1],
  254. this.state.containerHeight,
  255. false),
  256. layout: compacted,
  257. mapLayout
  258. })
  259. }
  260. } else {
  261. this.recalculateLayout(nextProps.layout, nextProps.col);
  262. }
  263. }
  264. recalculateLayout = (layout: DragactLayoutItem[], col: number) => {
  265. const corrected = correctLayout(layout, col)
  266. const { compacted, mapLayout } = compactLayout(corrected, undefined, undefined);
  267. this.setState({
  268. layout: compacted,
  269. mapLayout: mapLayout,
  270. containerHeight: getMaxContainerHeight(compacted, this.props.rowHeight, this.props.margin[1], this.state.containerHeight, false)
  271. })
  272. }
  273. componentDidMount() {
  274. setTimeout(() => {
  275. this.recalculateLayout(this.state.layout, this.props.col)
  276. }, 1);
  277. }
  278. getGridItem(child: any, index: number) {
  279. const { dragType, mapLayout } = this.state;
  280. var { col, width, padding, rowHeight, margin } = this.props;
  281. if (mapLayout) {
  282. const renderItem = layoutItemForkey(mapLayout, child.key + '');
  283. if (!padding) padding = 0;
  284. return (
  285. <GridItem
  286. {...renderItem}
  287. margin={margin}
  288. col={col}
  289. containerWidth={width}
  290. containerPadding={[padding, padding]}
  291. rowHeight={rowHeight}
  292. onDrag={this.onDrag}
  293. onDragStart={this.onDragStart}
  294. onDragEnd={this.onDragEnd}
  295. isUserMove={renderItem.isUserMove !== void 666 ? renderItem.isUserMove : false}
  296. UniqueKey={child.key}
  297. onResizing={this.onResizing}
  298. onResizeStart={this.onResizeStart}
  299. onResizeEnd={this.onResizeEnd}
  300. dragType={dragType}
  301. key={child.key}
  302. >
  303. {(GridItemProvided, dragHandle, resizeHandle) => this.props.children(child, {
  304. isDragging: renderItem.isUserMove !== void 666 ? renderItem.isUserMove : false,
  305. props: GridItemProvided,
  306. dragHandle,
  307. resizeHandle
  308. })}
  309. </GridItem >
  310. )
  311. }
  312. }
  313. render() {
  314. const {
  315. width,
  316. className,
  317. layout,
  318. style
  319. } = this.props;
  320. const { containerHeight } = this.state;
  321. return (
  322. <div
  323. className={stringJoin('DraggerLayout', className + '')}
  324. style={{
  325. ...style,
  326. left: 100,
  327. width: width,
  328. height: containerHeight,
  329. zIndex: 1
  330. }}
  331. >
  332. {layout.map((item, index) => {
  333. return this.getGridItem(item, index)
  334. })}
  335. {this.renderPlaceholder()}
  336. </div>
  337. )
  338. }
  339. //api
  340. getLayout() {
  341. return this.state.layout;
  342. }
  343. //api
  344. deleteItem(key: any) {
  345. }
  346. }