publ-echo 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/bin/cli.js +8 -0
- package/bitbucket-pipelines.yml +35 -0
- package/package.json +51 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +43 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/src/App.tsx +28 -0
- package/src/examples/ReactGridLayout/ReactGridLayoutShowcase01.tsx +80 -0
- package/src/examples/ReactGridLayout/index.ts +1 -0
- package/src/examples/ResponsiveGridLayout/ResponsiveGridLayoutShowcase01.tsx +114 -0
- package/src/examples/ResponsiveGridLayout/index.ts +1 -0
- package/src/examples/index.ts +2 -0
- package/src/examples/utils.ts +15 -0
- package/src/index.tsx +21 -0
- package/src/lib/Draggable/Draggable.tsx +303 -0
- package/src/lib/Draggable/DraggableCore.tsx +352 -0
- package/src/lib/Draggable/constants.ts +12 -0
- package/src/lib/Draggable/index.ts +2 -0
- package/src/lib/Draggable/types.ts +74 -0
- package/src/lib/Draggable/utils/domHelpers.ts +284 -0
- package/src/lib/Draggable/utils/getPrefix.ts +42 -0
- package/src/lib/Draggable/utils/positionHelpers.ts +49 -0
- package/src/lib/Draggable/utils/types.ts +41 -0
- package/src/lib/Draggable/utils/validationHelpers.ts +23 -0
- package/src/lib/GridItem/GridItem.tsx +493 -0
- package/src/lib/GridItem/index.ts +1 -0
- package/src/lib/GridItem/types.ts +121 -0
- package/src/lib/GridItem/utils/calculateUtils.ts +173 -0
- package/src/lib/GridLayoutEditor/ReactGridLayout.tsx +662 -0
- package/src/lib/GridLayoutEditor/ResponsiveGridLayout.tsx +204 -0
- package/src/lib/GridLayoutEditor/index.ts +9 -0
- package/src/lib/GridLayoutEditor/styles/styles.css +133 -0
- package/src/lib/GridLayoutEditor/types.ts +199 -0
- package/src/lib/GridLayoutEditor/utils/renderHelpers.ts +737 -0
- package/src/lib/GridLayoutEditor/utils/responsiveUtils.ts +111 -0
- package/src/lib/PreviewGLE/ReactGridLayoutPreview.tsx +46 -0
- package/src/lib/PreviewGLE/ResponsiveGridLayoutPreview.tsx +54 -0
- package/src/lib/PreviewGLE/index.ts +2 -0
- package/src/lib/Resizable/Resizable.tsx +323 -0
- package/src/lib/Resizable/ResizableBox.tsx +109 -0
- package/src/lib/Resizable/index.ts +1 -0
- package/src/lib/Resizable/styles/styles.css +76 -0
- package/src/lib/Resizable/types.ts +96 -0
- package/src/lib/Resizable/utils/cloneElement.ts +15 -0
- package/src/lib/components/WidthProvider.tsx +71 -0
- package/src/lib/components/index.ts +1 -0
- package/src/lib/components/types.ts +19 -0
- package/src/lib/index.ts +4 -0
- package/src/react-app-env.d.ts +1 -0
- package/src/reportWebVitals.ts +15 -0
- package/src/setupTests.ts +5 -0
- package/src/utils/types.ts +3 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
|
+
|
|
4
|
+
import type { ReactElement } from "react";
|
|
5
|
+
import { PropsWithChildren } from "../../utils/types";
|
|
6
|
+
import {
|
|
7
|
+
DragOverEvent,
|
|
8
|
+
Layout,
|
|
9
|
+
LayoutItem,
|
|
10
|
+
PositionParams,
|
|
11
|
+
ReactGridLayoutProps,
|
|
12
|
+
} from "./types";
|
|
13
|
+
import {
|
|
14
|
+
DroppingPosition,
|
|
15
|
+
GridDragEvent,
|
|
16
|
+
GridResizeEvent,
|
|
17
|
+
} from "../GridItem/types";
|
|
18
|
+
import classNames from "classnames";
|
|
19
|
+
import {
|
|
20
|
+
bottom,
|
|
21
|
+
cloneLayoutItem,
|
|
22
|
+
compact,
|
|
23
|
+
getAllCollisions,
|
|
24
|
+
getLayoutItem,
|
|
25
|
+
moveElement,
|
|
26
|
+
noop,
|
|
27
|
+
synchronizeLayoutWithChildren,
|
|
28
|
+
withLayoutItem,
|
|
29
|
+
} from "./utils/renderHelpers";
|
|
30
|
+
import {
|
|
31
|
+
calcGridColWidth,
|
|
32
|
+
calcXY,
|
|
33
|
+
resolveRowHeight,
|
|
34
|
+
} from "../GridItem/utils/calculateUtils";
|
|
35
|
+
import GridItem from "../GridItem/GridItem";
|
|
36
|
+
import { isEqual } from "lodash";
|
|
37
|
+
|
|
38
|
+
import "./styles/styles.css";
|
|
39
|
+
|
|
40
|
+
const layoutClassName = "react-grid-layout";
|
|
41
|
+
|
|
42
|
+
const ReactGridLayout = ({
|
|
43
|
+
children,
|
|
44
|
+
...props
|
|
45
|
+
}: PropsWithChildren<ReactGridLayoutProps>) => {
|
|
46
|
+
const {
|
|
47
|
+
autoSize = true,
|
|
48
|
+
cols = 12,
|
|
49
|
+
className = "",
|
|
50
|
+
style = {},
|
|
51
|
+
draggableHandle = "",
|
|
52
|
+
draggableCancel = "",
|
|
53
|
+
containerPadding = undefined,
|
|
54
|
+
rowHeight = 150,
|
|
55
|
+
maxRows = Infinity,
|
|
56
|
+
margin = [10, 10],
|
|
57
|
+
isBounded = false,
|
|
58
|
+
isDraggable = true,
|
|
59
|
+
isResizable = true,
|
|
60
|
+
allowOverlap = false,
|
|
61
|
+
isDroppable = false,
|
|
62
|
+
useCSSTransforms = true,
|
|
63
|
+
transformScale = 1,
|
|
64
|
+
compactType = "vertical",
|
|
65
|
+
preventCollision = false,
|
|
66
|
+
droppingItem = {
|
|
67
|
+
i: "__dropping-elem__",
|
|
68
|
+
h: 1,
|
|
69
|
+
w: 1,
|
|
70
|
+
} as any, // TODO fix
|
|
71
|
+
resizeHandles = ["se"],
|
|
72
|
+
width = 0,
|
|
73
|
+
resizeHandle,
|
|
74
|
+
isHiddenVisibility = true,
|
|
75
|
+
innerRef,
|
|
76
|
+
} = props;
|
|
77
|
+
|
|
78
|
+
const [activeDrag, setActiveDrag] = useState<LayoutItem>();
|
|
79
|
+
const [oldDragItem, setOldDragItem] = useState<LayoutItem>();
|
|
80
|
+
const [oldLayout, setOldLayout] = useState<Layout>();
|
|
81
|
+
const [oldResizeItem, setOldResizeItem] = useState<LayoutItem>();
|
|
82
|
+
const [droppingDOMNode, setDroppingDOMNode] = useState<ReactElement>();
|
|
83
|
+
const [droppingPosition, setDroppingPosition] = useState<DroppingPosition>();
|
|
84
|
+
const [isMounted, setIsMounted] = useState<boolean>(false);
|
|
85
|
+
|
|
86
|
+
const [layout, setLayout] = useState<Layout>(() =>
|
|
87
|
+
synchronizeLayoutWithChildren(
|
|
88
|
+
props.layout || [],
|
|
89
|
+
children,
|
|
90
|
+
cols,
|
|
91
|
+
compactType,
|
|
92
|
+
allowOverlap
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const dragEnterCounter = useRef(0);
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
setIsMounted(true);
|
|
100
|
+
onLayoutMaybeChanged(layout, layout);
|
|
101
|
+
}, []);
|
|
102
|
+
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (props.layout && !Array.isArray(props.layout)) {
|
|
105
|
+
console.error("Expecting layout to be an Array but got: ", props.layout);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
setLayout(
|
|
109
|
+
synchronizeLayoutWithChildren(
|
|
110
|
+
props.layout || [],
|
|
111
|
+
children,
|
|
112
|
+
cols,
|
|
113
|
+
compactType,
|
|
114
|
+
allowOverlap
|
|
115
|
+
)
|
|
116
|
+
);
|
|
117
|
+
}, [props.layout]);
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
const newLayout = synchronizeLayoutWithChildren(
|
|
121
|
+
props.layout || layout,
|
|
122
|
+
children,
|
|
123
|
+
props.cols || cols,
|
|
124
|
+
props.compactType || "vertical",
|
|
125
|
+
props.allowOverlap
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
setLayout(newLayout);
|
|
129
|
+
}, [children]);
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Calculates a pixel value for the container.
|
|
133
|
+
* @return {String} Container height in pixels.
|
|
134
|
+
*/
|
|
135
|
+
const containerHeight = (): string | undefined => {
|
|
136
|
+
if (!autoSize) return;
|
|
137
|
+
|
|
138
|
+
const nbRow = bottom(layout);
|
|
139
|
+
const containerPaddingY = containerPadding
|
|
140
|
+
? containerPadding[1]
|
|
141
|
+
: margin[1];
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
nbRow *
|
|
145
|
+
resolveRowHeight(rowHeight, calcGridColWidth(getPositionParams())) +
|
|
146
|
+
(nbRow - 1) * margin[1] +
|
|
147
|
+
containerPaddingY * 2 +
|
|
148
|
+
"px"
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* When dragging starts
|
|
154
|
+
* @param {String} i Id of the child
|
|
155
|
+
* @param {Number} x X position of the move
|
|
156
|
+
* @param {Number} y Y position of the move
|
|
157
|
+
* @param {Event} e The mousedown event
|
|
158
|
+
* @param {Element} node The current dragging DOM element
|
|
159
|
+
*/
|
|
160
|
+
const onDragStartHandler = (
|
|
161
|
+
i: string,
|
|
162
|
+
x: number,
|
|
163
|
+
y: number,
|
|
164
|
+
{ e, node }: GridDragEvent
|
|
165
|
+
) => {
|
|
166
|
+
const l = getLayoutItem(layout, i);
|
|
167
|
+
|
|
168
|
+
if (!l) return;
|
|
169
|
+
|
|
170
|
+
setOldDragItem(cloneLayoutItem(l));
|
|
171
|
+
setOldLayout(layout);
|
|
172
|
+
|
|
173
|
+
props.onDragStart &&
|
|
174
|
+
props.onDragStart({
|
|
175
|
+
layout,
|
|
176
|
+
prev: l,
|
|
177
|
+
item: l,
|
|
178
|
+
placeholder: undefined,
|
|
179
|
+
e,
|
|
180
|
+
node,
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Each drag movement create a new dragelement and move the element to the dragged location
|
|
186
|
+
* @param {String} i Id of the child
|
|
187
|
+
* @param {Number} x X position of the move
|
|
188
|
+
* @param {Number} y Y position of the move
|
|
189
|
+
* @param {Event} e The mousedown event
|
|
190
|
+
* @param {Element} node The current dragging DOM element
|
|
191
|
+
*/
|
|
192
|
+
const onDragHandler = (
|
|
193
|
+
i: string,
|
|
194
|
+
x: number,
|
|
195
|
+
y: number,
|
|
196
|
+
{ e, node }: GridDragEvent
|
|
197
|
+
) => {
|
|
198
|
+
const l = getLayoutItem(layout, i);
|
|
199
|
+
if (!l) return;
|
|
200
|
+
|
|
201
|
+
const placeholder = {
|
|
202
|
+
w: l.w,
|
|
203
|
+
h: l.h,
|
|
204
|
+
x: l.x,
|
|
205
|
+
y: l.y,
|
|
206
|
+
z: l.z,
|
|
207
|
+
placeholder: true,
|
|
208
|
+
i,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const isUserAction = true;
|
|
212
|
+
let newLayout = moveElement({
|
|
213
|
+
layout,
|
|
214
|
+
l,
|
|
215
|
+
x,
|
|
216
|
+
y,
|
|
217
|
+
isUserAction,
|
|
218
|
+
preventCollision,
|
|
219
|
+
compactType,
|
|
220
|
+
cols,
|
|
221
|
+
allowOverlap,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
props.onDrag &&
|
|
225
|
+
props.onDrag({
|
|
226
|
+
layout: newLayout,
|
|
227
|
+
prev: oldDragItem,
|
|
228
|
+
item: l,
|
|
229
|
+
placeholder,
|
|
230
|
+
e,
|
|
231
|
+
node,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
setLayout(allowOverlap ? newLayout : compact(newLayout, compactType, cols));
|
|
235
|
+
setActiveDrag(placeholder);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* When dragging stops, figure out which position the element is closest to and update its x and y.
|
|
240
|
+
* @param {String} i Index of the child.
|
|
241
|
+
* @param {Number} x X position of the move
|
|
242
|
+
* @param {Number} y Y position of the move
|
|
243
|
+
* @param {Event} e The mousedown event
|
|
244
|
+
* @param {Element} node The current dragging DOM element
|
|
245
|
+
*/
|
|
246
|
+
const onDragStopHandler = (
|
|
247
|
+
i: string,
|
|
248
|
+
x: number,
|
|
249
|
+
y: number,
|
|
250
|
+
{ e, node }: GridDragEvent
|
|
251
|
+
) => {
|
|
252
|
+
if (!activeDrag) return;
|
|
253
|
+
|
|
254
|
+
const l = getLayoutItem(layout, i);
|
|
255
|
+
if (!l) return;
|
|
256
|
+
|
|
257
|
+
const isUserAction = true;
|
|
258
|
+
const movedLayout = moveElement({
|
|
259
|
+
layout,
|
|
260
|
+
l,
|
|
261
|
+
x,
|
|
262
|
+
y,
|
|
263
|
+
isUserAction,
|
|
264
|
+
preventCollision,
|
|
265
|
+
compactType,
|
|
266
|
+
cols,
|
|
267
|
+
allowOverlap,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
props.onDragStop &&
|
|
271
|
+
props.onDragStop({
|
|
272
|
+
layout: movedLayout,
|
|
273
|
+
prev: oldDragItem,
|
|
274
|
+
item: l,
|
|
275
|
+
placeholder: undefined,
|
|
276
|
+
e,
|
|
277
|
+
node,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const newLayout = allowOverlap
|
|
281
|
+
? movedLayout
|
|
282
|
+
: compact(movedLayout, compactType, cols);
|
|
283
|
+
setActiveDrag(undefined);
|
|
284
|
+
setLayout(newLayout);
|
|
285
|
+
setOldDragItem(undefined);
|
|
286
|
+
setOldLayout(undefined);
|
|
287
|
+
|
|
288
|
+
props.onLayoutChange && props.onLayoutChange(newLayout);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const onLayoutMaybeChanged = (newLayout: Layout, oldLayout?: Layout) => {
|
|
292
|
+
if (!oldLayout) {
|
|
293
|
+
oldLayout = layout;
|
|
294
|
+
}
|
|
295
|
+
if (!isEqual(oldLayout, newLayout)) {
|
|
296
|
+
props.onLayoutChange && props.onLayoutChange(newLayout);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const onResizeStartHandler = (
|
|
301
|
+
i: string,
|
|
302
|
+
x: number,
|
|
303
|
+
y: number,
|
|
304
|
+
{ e, node }: GridResizeEvent
|
|
305
|
+
) => {
|
|
306
|
+
const l = getLayoutItem(layout, i);
|
|
307
|
+
if (!l) return;
|
|
308
|
+
|
|
309
|
+
setOldResizeItem(cloneLayoutItem(l));
|
|
310
|
+
setOldLayout(layout);
|
|
311
|
+
props.onResizeStart &&
|
|
312
|
+
props.onResizeStart({
|
|
313
|
+
layout,
|
|
314
|
+
prev: l,
|
|
315
|
+
item: l,
|
|
316
|
+
e,
|
|
317
|
+
node,
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const onResizeHandler = (
|
|
322
|
+
i: string,
|
|
323
|
+
w: number,
|
|
324
|
+
h: number,
|
|
325
|
+
{ e, node }: GridResizeEvent,
|
|
326
|
+
x: number,
|
|
327
|
+
y: number
|
|
328
|
+
) => {
|
|
329
|
+
const [newLayout, l] = withLayoutItem(layout, i, (l) => {
|
|
330
|
+
let hasCollisions;
|
|
331
|
+
if (preventCollision && !allowOverlap) {
|
|
332
|
+
const collisions = getAllCollisions(layout, {
|
|
333
|
+
...l,
|
|
334
|
+
w: x,
|
|
335
|
+
h: y,
|
|
336
|
+
}).filter((layoutItem) => layoutItem.i !== l.i);
|
|
337
|
+
hasCollisions = collisions.length > 0;
|
|
338
|
+
|
|
339
|
+
if (hasCollisions) {
|
|
340
|
+
let leastX = Infinity,
|
|
341
|
+
leastY = Infinity;
|
|
342
|
+
collisions.forEach((layoutItem) => {
|
|
343
|
+
if (layoutItem.x > l.x) leastX = Math.min(leastX, layoutItem.x);
|
|
344
|
+
if (layoutItem.y > l.y) leastY = Math.min(leastY, layoutItem.y);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
if (Number.isFinite(leastX)) l.w = leastX - l.x;
|
|
348
|
+
if (Number.isFinite(leastY)) l.h = leastY - l.y;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (!hasCollisions) {
|
|
353
|
+
// NOTE - 여기서 x, y를 추가적으로 세팅해줘야 nw, w, n, sw 방향에서 placeholder가 반대로 resize되지 않고 해당 element와 똑같이 resize
|
|
354
|
+
l.w = w;
|
|
355
|
+
l.h = h;
|
|
356
|
+
l.x = x;
|
|
357
|
+
l.y = y;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return l;
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
if (!l) return;
|
|
364
|
+
|
|
365
|
+
const placeholder = {
|
|
366
|
+
w: l.w,
|
|
367
|
+
h: l.h,
|
|
368
|
+
x: l.x,
|
|
369
|
+
y: l.y,
|
|
370
|
+
z: l.z,
|
|
371
|
+
static: true,
|
|
372
|
+
i: i,
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
props.onResize &&
|
|
376
|
+
props.onResize({
|
|
377
|
+
layout: newLayout,
|
|
378
|
+
prev: oldResizeItem,
|
|
379
|
+
item: l,
|
|
380
|
+
placeholder,
|
|
381
|
+
e,
|
|
382
|
+
node,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
setLayout(allowOverlap ? newLayout : compact(newLayout, compactType, cols));
|
|
386
|
+
setActiveDrag(placeholder);
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const onResizeStopHandler = (
|
|
390
|
+
i: string,
|
|
391
|
+
x: number,
|
|
392
|
+
y: number,
|
|
393
|
+
{ e, node }: GridResizeEvent
|
|
394
|
+
) => {
|
|
395
|
+
const l = getLayoutItem(layout, i);
|
|
396
|
+
props.onResizeStop &&
|
|
397
|
+
props.onResizeStop({ layout, prev: oldResizeItem, item: l, e, node });
|
|
398
|
+
|
|
399
|
+
const newLayout = allowOverlap
|
|
400
|
+
? layout
|
|
401
|
+
: compact(layout, compactType, cols);
|
|
402
|
+
|
|
403
|
+
setActiveDrag(undefined);
|
|
404
|
+
setLayout(newLayout);
|
|
405
|
+
setOldResizeItem(undefined);
|
|
406
|
+
setOldLayout(undefined);
|
|
407
|
+
|
|
408
|
+
onLayoutMaybeChanged(newLayout, oldLayout);
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Create a placeholder object.
|
|
413
|
+
* @return {Element} Placeholder div.
|
|
414
|
+
*/
|
|
415
|
+
const placeholder = () => {
|
|
416
|
+
if (!activeDrag) return;
|
|
417
|
+
|
|
418
|
+
// {...activeDrag} is pretty slow, actually
|
|
419
|
+
|
|
420
|
+
const l = getLayoutItem(props.layout, String(activeDrag.i));
|
|
421
|
+
|
|
422
|
+
if (!l) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// NOTE: toChildren changes key
|
|
427
|
+
// LINK: https://legacy.reactjs.org/docs/react-api.html
|
|
428
|
+
const items = React.Children.toArray(children);
|
|
429
|
+
|
|
430
|
+
const item = items.find((child: any) => {
|
|
431
|
+
const originalKey = String(child.key).split(".$");
|
|
432
|
+
|
|
433
|
+
return originalKey[1] === l.i;
|
|
434
|
+
}) as React.ReactElement;
|
|
435
|
+
|
|
436
|
+
if (!item) {
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return (
|
|
441
|
+
<GridItem
|
|
442
|
+
w={activeDrag.w}
|
|
443
|
+
h={activeDrag.h}
|
|
444
|
+
x={activeDrag.x}
|
|
445
|
+
y={activeDrag.y}
|
|
446
|
+
z={activeDrag.z || 0}
|
|
447
|
+
i={activeDrag.i}
|
|
448
|
+
className={"placeholder"}
|
|
449
|
+
containerWidth={width}
|
|
450
|
+
cols={cols}
|
|
451
|
+
margin={margin}
|
|
452
|
+
containerPadding={containerPadding || margin}
|
|
453
|
+
maxRows={maxRows}
|
|
454
|
+
rowHeight={rowHeight}
|
|
455
|
+
isDraggable={false}
|
|
456
|
+
isResizable={false}
|
|
457
|
+
isBounded={false}
|
|
458
|
+
useCSSTransforms={useCSSTransforms}
|
|
459
|
+
transformScale={transformScale}
|
|
460
|
+
>
|
|
461
|
+
{item && item}
|
|
462
|
+
</GridItem>
|
|
463
|
+
);
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Given a grid item, set its style attributes & surround in a <Draggable>.
|
|
468
|
+
* @param {Element} child React element.
|
|
469
|
+
* @return {Element} Element wrapped in draggable and properly placed.
|
|
470
|
+
*/
|
|
471
|
+
const processGridItem = (
|
|
472
|
+
child: ReactElement<any>,
|
|
473
|
+
isDroppingItem?: boolean
|
|
474
|
+
): ReactElement<any> | undefined => {
|
|
475
|
+
if (!child || !child.key) return;
|
|
476
|
+
const l = getLayoutItem(layout, String(child.key));
|
|
477
|
+
if (!l) return;
|
|
478
|
+
|
|
479
|
+
const draggable =
|
|
480
|
+
typeof l.isDraggable === "boolean"
|
|
481
|
+
? l.isDraggable
|
|
482
|
+
: !l.static && isDraggable;
|
|
483
|
+
const resizable =
|
|
484
|
+
typeof l.isResizable === "boolean"
|
|
485
|
+
? l.isResizable
|
|
486
|
+
: !l.static && isResizable;
|
|
487
|
+
const resizeHandlesOptions = l.resizeHandles || resizeHandles;
|
|
488
|
+
|
|
489
|
+
const bounded = draggable && isBounded && l.isBounded !== false;
|
|
490
|
+
|
|
491
|
+
return (
|
|
492
|
+
<GridItem
|
|
493
|
+
containerWidth={width}
|
|
494
|
+
cols={cols}
|
|
495
|
+
margin={margin}
|
|
496
|
+
containerPadding={containerPadding || margin}
|
|
497
|
+
maxRows={maxRows}
|
|
498
|
+
rowHeight={rowHeight}
|
|
499
|
+
cancel={draggableCancel}
|
|
500
|
+
handle={draggableHandle}
|
|
501
|
+
onDragStop={onDragStopHandler}
|
|
502
|
+
onDragStart={onDragStartHandler}
|
|
503
|
+
onDrag={onDragHandler}
|
|
504
|
+
onResizeStart={onResizeStartHandler}
|
|
505
|
+
onResize={onResizeHandler}
|
|
506
|
+
onResizeStop={onResizeStopHandler}
|
|
507
|
+
isDraggable={draggable}
|
|
508
|
+
isResizable={resizable}
|
|
509
|
+
isBounded={bounded}
|
|
510
|
+
useCSSTransforms={useCSSTransforms && isMounted}
|
|
511
|
+
usePercentages={!isMounted}
|
|
512
|
+
transformScale={transformScale}
|
|
513
|
+
w={l.w}
|
|
514
|
+
h={l.h}
|
|
515
|
+
x={l.x}
|
|
516
|
+
y={l.y}
|
|
517
|
+
z={l.z || 0}
|
|
518
|
+
i={l.i}
|
|
519
|
+
minH={l.minH}
|
|
520
|
+
minW={l.minW}
|
|
521
|
+
maxH={l.maxH}
|
|
522
|
+
maxW={l.maxW}
|
|
523
|
+
static={l.static}
|
|
524
|
+
droppingPosition={isDroppingItem ? droppingPosition : undefined}
|
|
525
|
+
resizeHandles={resizeHandlesOptions}
|
|
526
|
+
resizeHandle={resizeHandle}
|
|
527
|
+
isHiddenVisibility={isHiddenVisibility}
|
|
528
|
+
>
|
|
529
|
+
{child}
|
|
530
|
+
</GridItem>
|
|
531
|
+
);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
const getPositionParams = (): PositionParams => {
|
|
535
|
+
return {
|
|
536
|
+
cols,
|
|
537
|
+
margin,
|
|
538
|
+
maxRows,
|
|
539
|
+
rowHeight,
|
|
540
|
+
containerWidth: width,
|
|
541
|
+
containerPadding: containerPadding || margin,
|
|
542
|
+
};
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
const onDragOverHandler: React.DragEventHandler<HTMLDivElement> = (e) => {
|
|
546
|
+
e.preventDefault();
|
|
547
|
+
e.stopPropagation();
|
|
548
|
+
|
|
549
|
+
const onDragOverResult = props.onDropDragOver?.(e as any as DragOverEvent); // TODO fix
|
|
550
|
+
if (onDragOverResult === false) {
|
|
551
|
+
if (droppingDOMNode) {
|
|
552
|
+
removeDroppingPlaceholder();
|
|
553
|
+
}
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
const finalDroppingItem = { ...droppingItem, ...onDragOverResult };
|
|
557
|
+
|
|
558
|
+
const { layerX, layerY } = e.nativeEvent as any; // TODO fix
|
|
559
|
+
const droppingPosition = {
|
|
560
|
+
left: layerX / transformScale,
|
|
561
|
+
top: layerY / transformScale,
|
|
562
|
+
e,
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
if (!droppingDOMNode) {
|
|
566
|
+
const calculatedPosition = calcXY(
|
|
567
|
+
getPositionParams(),
|
|
568
|
+
layerY,
|
|
569
|
+
layerX,
|
|
570
|
+
finalDroppingItem.w,
|
|
571
|
+
finalDroppingItem.h
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
setDroppingDOMNode(<div key={finalDroppingItem.i} />);
|
|
575
|
+
setDroppingPosition(droppingPosition);
|
|
576
|
+
setLayout([
|
|
577
|
+
...layout,
|
|
578
|
+
{
|
|
579
|
+
...finalDroppingItem,
|
|
580
|
+
x: calculatedPosition.x,
|
|
581
|
+
y: calculatedPosition.y,
|
|
582
|
+
static: false,
|
|
583
|
+
isDraggable: true,
|
|
584
|
+
},
|
|
585
|
+
]);
|
|
586
|
+
} else if (droppingPosition) {
|
|
587
|
+
const { left, top } = droppingPosition;
|
|
588
|
+
const shouldUpdatePosition = left != layerX || top != layerY;
|
|
589
|
+
if (shouldUpdatePosition) {
|
|
590
|
+
setDroppingPosition(droppingPosition);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
const removeDroppingPlaceholder: () => void = () => {
|
|
596
|
+
const newLayout = compact(
|
|
597
|
+
layout.filter((l) => l.i !== droppingItem.i),
|
|
598
|
+
compactType,
|
|
599
|
+
cols,
|
|
600
|
+
allowOverlap
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
setLayout(newLayout);
|
|
604
|
+
setDroppingDOMNode(undefined);
|
|
605
|
+
setActiveDrag(undefined);
|
|
606
|
+
setDroppingPosition(undefined);
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
const onDragLeaveHandler: React.DragEventHandler<HTMLDivElement> = (e) => {
|
|
610
|
+
e.preventDefault();
|
|
611
|
+
e.stopPropagation();
|
|
612
|
+
dragEnterCounter.current = dragEnterCounter.current - 1;
|
|
613
|
+
|
|
614
|
+
if (dragEnterCounter.current === 0) {
|
|
615
|
+
removeDroppingPlaceholder();
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
const onDragEnterHandler: React.DragEventHandler<HTMLDivElement> = (e) => {
|
|
620
|
+
e.preventDefault();
|
|
621
|
+
e.stopPropagation();
|
|
622
|
+
dragEnterCounter.current += 1;
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
const onDropHandler: React.DragEventHandler<HTMLDivElement> = (e) => {
|
|
626
|
+
e.preventDefault();
|
|
627
|
+
e.stopPropagation();
|
|
628
|
+
const item = layout.find((l) => l.i === droppingItem.i);
|
|
629
|
+
|
|
630
|
+
dragEnterCounter.current = 0;
|
|
631
|
+
|
|
632
|
+
removeDroppingPlaceholder();
|
|
633
|
+
|
|
634
|
+
if (item) {
|
|
635
|
+
props.onDrop && props.onDrop(layout, item, e);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
const mergedClassName = classNames(layoutClassName, className);
|
|
640
|
+
const mergedStyle = {
|
|
641
|
+
height: containerHeight(),
|
|
642
|
+
...style,
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
return (
|
|
646
|
+
<div
|
|
647
|
+
ref={innerRef}
|
|
648
|
+
className={mergedClassName}
|
|
649
|
+
style={mergedStyle}
|
|
650
|
+
onDrop={isDroppable ? onDropHandler : noop}
|
|
651
|
+
onDragLeave={isDroppable ? onDragLeaveHandler : noop}
|
|
652
|
+
onDragEnter={isDroppable ? onDragEnterHandler : noop}
|
|
653
|
+
onDragOver={isDroppable ? onDragOverHandler : noop}
|
|
654
|
+
>
|
|
655
|
+
{React.Children.map(children, (child) => processGridItem(child))}
|
|
656
|
+
{/* TODO: 복수개의 placeholder 지원예정 */}
|
|
657
|
+
{placeholder()}
|
|
658
|
+
</div>
|
|
659
|
+
);
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
export default ReactGridLayout;
|