1 | import { __rest } from "tslib";
|
2 | import * as React from 'react';
|
3 | import { css } from '@patternfly/react-styles';
|
4 | import styles from '@patternfly/react-styles/css/components/DragDrop/drag-drop';
|
5 | import { DroppableContext } from './DroppableContext';
|
6 | import { DragDropContext } from './DragDrop';
|
7 |
|
8 | function getDefaultBackground() {
|
9 | const div = document.createElement('div');
|
10 | document.head.appendChild(div);
|
11 | const bg = window.getComputedStyle(div).backgroundColor;
|
12 | document.head.removeChild(div);
|
13 | return bg;
|
14 | }
|
15 | function getInheritedBackgroundColor(el) {
|
16 | const defaultStyle = getDefaultBackground();
|
17 | const backgroundColor = window.getComputedStyle(el).backgroundColor;
|
18 | if (backgroundColor !== defaultStyle) {
|
19 | return backgroundColor;
|
20 | }
|
21 | else if (!el.parentElement) {
|
22 | return defaultStyle;
|
23 | }
|
24 | return getInheritedBackgroundColor(el.parentElement);
|
25 | }
|
26 | function removeBlankDiv(node) {
|
27 | if (node.getAttribute('blankDiv') === 'true') {
|
28 |
|
29 | for (let i = 0; i < node.children.length; i++) {
|
30 | const child = node.children[i];
|
31 | if (child.getAttribute('blankDiv') === 'true') {
|
32 | node.removeChild(child);
|
33 | node.setAttribute('blankDiv', 'false');
|
34 | break;
|
35 | }
|
36 | }
|
37 | }
|
38 | }
|
39 |
|
40 | function resetDroppableItem(droppableItem) {
|
41 | removeBlankDiv(droppableItem.node);
|
42 | droppableItem.node.classList.remove(styles.modifiers.dragging);
|
43 | droppableItem.node.classList.remove(styles.modifiers.dragOutside);
|
44 | droppableItem.draggableNodes.forEach((n, i) => {
|
45 | n.style.transform = '';
|
46 | n.style.transition = '';
|
47 | droppableItem.draggableNodesRects[i] = n.getBoundingClientRect();
|
48 | });
|
49 | }
|
50 | function overlaps(ev, rect) {
|
51 | return (ev.clientX > rect.x && ev.clientX < rect.x + rect.width && ev.clientY > rect.y && ev.clientY < rect.y + rect.height);
|
52 | }
|
53 | export const Draggable = (_a) => {
|
54 | var { className, children, style: styleProp = {}, hasNoWrapper = false } = _a, props = __rest(_a, ["className", "children", "style", "hasNoWrapper"]);
|
55 |
|
56 | let [style, setStyle] = React.useState(styleProp);
|
57 |
|
58 | const [isDragging, setIsDragging] = React.useState(false);
|
59 | const [isValidDrag, setIsValidDrag] = React.useState(true);
|
60 | const { zone, droppableId } = React.useContext(DroppableContext);
|
61 | const { onDrag, onDragMove, onDrop } = React.useContext(DragDropContext);
|
62 |
|
63 |
|
64 | let startX = 0;
|
65 | let startY = 0;
|
66 | let index = null;
|
67 | let hoveringDroppable;
|
68 | let hoveringIndex = null;
|
69 | let mouseMoveListener;
|
70 | let mouseUpListener;
|
71 |
|
72 | let startYOffset = 0;
|
73 |
|
74 | const onTransitionEnd = (_ev) => {
|
75 | if (isDragging) {
|
76 | setIsDragging(false);
|
77 | setStyle(styleProp);
|
78 | }
|
79 | };
|
80 | function getSourceAndDest() {
|
81 | const hoveringDroppableId = hoveringDroppable ? hoveringDroppable.getAttribute('data-pf-droppableid') : null;
|
82 | const source = {
|
83 | droppableId,
|
84 | index
|
85 | };
|
86 | const dest = hoveringDroppableId !== null && hoveringIndex !== null
|
87 | ? {
|
88 | droppableId: hoveringDroppableId,
|
89 | index: hoveringIndex
|
90 | }
|
91 | : undefined;
|
92 | return { source, dest, hoveringDroppableId };
|
93 | }
|
94 | const onMouseUpWhileDragging = (droppableItems) => {
|
95 | droppableItems.forEach(resetDroppableItem);
|
96 | document.removeEventListener('mousemove', mouseMoveListener);
|
97 | document.removeEventListener('mouseup', mouseUpListener);
|
98 | document.removeEventListener('contextmenu', mouseUpListener);
|
99 | const { source, dest, hoveringDroppableId } = getSourceAndDest();
|
100 | const consumerReordered = onDrop(source, dest);
|
101 | if (consumerReordered && droppableId === hoveringDroppableId) {
|
102 | setIsDragging(false);
|
103 | setStyle(styleProp);
|
104 | }
|
105 | else if (!consumerReordered) {
|
106 |
|
107 | setStyle(Object.assign(Object.assign({}, style), { transition: 'transform 0.5s cubic-bezier(0.2, 1, 0.1, 1) 0s', transform: '', background: styleProp.background, boxShadow: styleProp.boxShadow }));
|
108 | }
|
109 | };
|
110 |
|
111 | const onMouseMoveWhileDragging = (ev, droppableItems, blankDivRect) => {
|
112 |
|
113 | hoveringDroppable = null;
|
114 | droppableItems.forEach(droppableItem => {
|
115 | const { node, rect, isDraggingHost, draggableNodes, draggableNodesRects } = droppableItem;
|
116 | if (overlaps(ev, rect)) {
|
117 |
|
118 | node.classList.remove(styles.modifiers.dragOutside);
|
119 | hoveringDroppable = node;
|
120 |
|
121 | if (node.getAttribute('blankDiv') !== 'true' && !isDraggingHost) {
|
122 | const blankDiv = document.createElement('div');
|
123 | blankDiv.setAttribute('blankDiv', 'true');
|
124 | let blankDivPos = -1;
|
125 | for (let i = 0; i < draggableNodes.length; i++) {
|
126 | const childRect = draggableNodesRects[i];
|
127 | const isLast = i === draggableNodes.length - 1;
|
128 | const startOverlaps = childRect.y >= startY - startYOffset;
|
129 | if ((startOverlaps || isLast) && blankDivPos === -1) {
|
130 | if (isLast && !startOverlaps) {
|
131 | draggableNodes[i].after(blankDiv);
|
132 | }
|
133 | else {
|
134 | draggableNodes[i].before(blankDiv);
|
135 | }
|
136 | blankDiv.style.height = `${blankDivRect.height}px`;
|
137 | blankDiv.style.width = `${blankDivRect.width}px`;
|
138 | node.setAttribute('blankDiv', 'true');
|
139 | blankDivPos = i;
|
140 | }
|
141 | if (blankDivPos !== -1) {
|
142 | childRect.y += blankDivRect.height;
|
143 | }
|
144 | }
|
145 |
|
146 | draggableNodes.splice(blankDivPos, 0, blankDiv);
|
147 | draggableNodesRects.splice(blankDivPos, 0, blankDivRect);
|
148 |
|
149 | rect.height += blankDivRect.height;
|
150 | }
|
151 | }
|
152 | else {
|
153 | resetDroppableItem(droppableItem);
|
154 | node.classList.add(styles.modifiers.dragging);
|
155 | node.classList.add(styles.modifiers.dragOutside);
|
156 | }
|
157 | });
|
158 |
|
159 | setStyle(Object.assign(Object.assign({}, style), { transform: `translate(${ev.pageX - startX}px, ${ev.pageY - startY}px)` }));
|
160 | setIsValidDrag(Boolean(hoveringDroppable));
|
161 |
|
162 | hoveringIndex = null;
|
163 | if (hoveringDroppable) {
|
164 | const { draggableNodes, draggableNodesRects } = droppableItems.find(item => item.node === hoveringDroppable);
|
165 | let lastTranslate = 0;
|
166 | draggableNodes.forEach((n, i) => {
|
167 | n.style.transition = 'transform 0.5s cubic-bezier(0.2, 1, 0.1, 1) 0s';
|
168 | const rect = draggableNodesRects[i];
|
169 | const halfway = rect.y + rect.height / 2;
|
170 | let translateY = 0;
|
171 |
|
172 | if (startY < halfway && ev.pageY + (blankDivRect.height - startYOffset) > halfway) {
|
173 | translateY -= blankDivRect.height;
|
174 | }
|
175 | else if (startY >= halfway && ev.pageY - startYOffset <= halfway) {
|
176 | translateY += blankDivRect.height;
|
177 | }
|
178 |
|
179 | if ((translateY <= lastTranslate && translateY < 0) || (translateY > lastTranslate && translateY > 0)) {
|
180 | hoveringIndex = i;
|
181 | }
|
182 | n.style.transform = `translate(0, ${translateY}px`;
|
183 | lastTranslate = translateY;
|
184 | });
|
185 | }
|
186 | const { source, dest } = getSourceAndDest();
|
187 | onDragMove(source, dest);
|
188 | };
|
189 | const onDragStart = (ev) => {
|
190 |
|
191 |
|
192 |
|
193 | ev.preventDefault();
|
194 | if (isDragging) {
|
195 |
|
196 | return;
|
197 | }
|
198 |
|
199 | const dragging = ev.target;
|
200 | const rect = dragging.getBoundingClientRect();
|
201 | const droppableNodes = Array.from(document.querySelectorAll(`[data-pf-droppable="${zone}"]`));
|
202 | const droppableItems = droppableNodes.reduce((acc, cur) => {
|
203 | cur.classList.add(styles.modifiers.dragging);
|
204 | const draggableNodes = Array.from(cur.querySelectorAll(`[data-pf-draggable-zone="${zone}"]`));
|
205 | const isDraggingHost = cur.contains(dragging);
|
206 | if (isDraggingHost) {
|
207 | index = draggableNodes.indexOf(dragging);
|
208 | }
|
209 | const droppableItem = {
|
210 | node: cur,
|
211 | rect: cur.getBoundingClientRect(),
|
212 | isDraggingHost,
|
213 |
|
214 | draggableNodes: draggableNodes.map(node => (node === dragging ? node.cloneNode(false) : node)),
|
215 | draggableNodesRects: draggableNodes.map(node => node.getBoundingClientRect())
|
216 | };
|
217 | acc.push(droppableItem);
|
218 | return acc;
|
219 | }, []);
|
220 | if (!onDrag({ droppableId, index })) {
|
221 |
|
222 | return;
|
223 | }
|
224 |
|
225 | style = Object.assign(Object.assign({}, style), { top: rect.y, left: rect.x, width: rect.width, height: rect.height, '--pf-c-draggable--m-dragging--BackgroundColor': getInheritedBackgroundColor(dragging), position: 'fixed', zIndex: 5000 });
|
226 | setStyle(style);
|
227 |
|
228 | startX = ev.pageX;
|
229 | startY = ev.pageY;
|
230 | startYOffset = startY - rect.y;
|
231 | setIsDragging(true);
|
232 | mouseMoveListener = ev => onMouseMoveWhileDragging(ev, droppableItems, rect);
|
233 | mouseUpListener = () => onMouseUpWhileDragging(droppableItems);
|
234 | document.addEventListener('mousemove', mouseMoveListener);
|
235 | document.addEventListener('mouseup', mouseUpListener);
|
236 |
|
237 |
|
238 | };
|
239 | const childProps = Object.assign({ 'data-pf-draggable-zone': isDragging ? null : zone, draggable: true, className: css(styles.draggable, isDragging && styles.modifiers.dragging, !isValidDrag && styles.modifiers.dragOutside, className), onDragStart,
|
240 | onTransitionEnd,
|
241 | style }, props);
|
242 | return (React.createElement(React.Fragment, null,
|
243 | isDragging && (React.createElement("div", Object.assign({ draggable: true }, props, { style: Object.assign(Object.assign({}, styleProp), { visibility: 'hidden' }) }), children)),
|
244 | hasNoWrapper ? (React.cloneElement(children, childProps)) : (React.createElement("div", Object.assign({}, childProps), children))));
|
245 | };
|
246 | Draggable.displayName = 'Draggable';
|
247 |
|
\ | No newline at end of file |