Skip to content

Commit d38d63f

Browse files
committed
Dragged item obeys flip
1 parent d2c2b54 commit d38d63f

File tree

2 files changed

+90
-47
lines changed

2 files changed

+90
-47
lines changed

popup-page-scripts/popup-page-customButtons.js

Lines changed: 81 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ function autoScroll(e) {
193193

194194
function handleDragOver(e) {
195195
e.preventDefault();
196-
if (!isDragging) return;
196+
if (!isDragging || !draggedItem) return; // Added check for draggedItem
197197

198198
e.dataTransfer.dropEffect = 'move';
199199
autoScroll(e);
@@ -205,15 +205,18 @@ function handleDragOver(e) {
205205

206206
const parent = target.parentNode;
207207
// --- Get ALL child elements BEFORE the DOM change ---
208-
// Convert HTMLCollection to an array for easier handling
209208
const children = Array.from(parent.children);
210209

211-
// --- FLIP: First - Record the starting positions of ALL elements (except the dragged one) ---
210+
// --- FLIP: First - Record the starting positions ---
212211
const firstPositions = new Map();
212+
let firstDraggedRect = null; // Store draggedItem's initial position
213+
213214
children.forEach(child => {
214-
// Exclude the dragged item itself from animation calculations
215-
if (child !== draggedItem) {
216-
firstPositions.set(child, child.getBoundingClientRect());
215+
const rect = child.getBoundingClientRect();
216+
if (child === draggedItem) {
217+
firstDraggedRect = rect; // Record initial position of dragged item
218+
} else {
219+
firstPositions.set(child, rect); // Record for background items
217220
}
218221
});
219222

@@ -222,63 +225,96 @@ function handleDragOver(e) {
222225
const offsetY = e.clientY - bounding.top;
223226
const isBefore = offsetY < bounding.height / 2;
224227

225-
// Remember the current siblings to avoid inserting the item relative to itself
226228
const currentNextSibling = draggedItem.nextSibling;
227229
const currentPreviousSibling = draggedItem.previousSibling;
228230

231+
let domChanged = false; // Flag to track if DOM was actually modified
229232
if (isBefore) {
230-
// Insert ONLY if the target is not the current next sibling
231233
if (target !== currentNextSibling) {
232234
parent.insertBefore(draggedItem, target);
235+
domChanged = true;
233236
}
234237
} else {
235-
// Insert ONLY if the target is not the current previous sibling
236238
if (target !== currentPreviousSibling) {
237-
parent.insertBefore(draggedItem, target.nextSibling);
239+
parent.insertBefore(draggedItem, target.nextSibling);
240+
domChanged = true;
238241
}
239242
}
240243
// --- END of DOM change ---
241244

242-
243-
// --- FLIP: Last, Invert, Play This is for items that are not dragged, the ones at the background ---
244-
children.forEach(child => {
245-
// Again, process only elements that are NOT the dragged item
246-
if (child !== draggedItem && firstPositions.has(child)) {
247-
const firstRect = firstPositions.get(child);
248-
// --- Last: Get the new positions AFTER the DOM change ---
249-
const lastRect = child.getBoundingClientRect();
250-
251-
// --- Invert: Calculate the offset and apply the inverse transform ---
252-
const deltaX = firstRect.left - lastRect.left;
253-
const deltaY = firstRect.top - lastRect.top;
254-
255-
// If the element actually moved
256-
if (deltaX !== 0 || deltaY !== 0) {
257-
// Apply the inverse transform WITHOUT animation
258-
child.style.transition = 'transform 0s';
259-
child.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
260-
261-
// --- Important: Force the browser to recalculate styles ---
262-
// Reading offsetWidth forces the browser to apply the styles immediately
263-
child.offsetWidth;
264-
265-
// --- Play: Enable the animation and remove the transform ---
266-
child.style.transition = 'transform 200ms ease-in-out'; // Set your desired duration and easing
267-
child.style.transform = ''; // Animate back to the natural position (transform: none)
268-
269-
// --- Cleanup (Recommended): Remove the transition after animation completes ---
270-
// so it doesn't interfere with subsequent operations or other styles
271-
child.addEventListener('transitionend', () => {
245+
// --- Only proceed with animations if the DOM actually changed ---
246+
if (domChanged) {
247+
// --- FLIP: Last, Invert, Play for BACKGROUND items ---
248+
children.forEach(child => {
249+
if (child !== draggedItem && firstPositions.has(child)) {
250+
const firstRect = firstPositions.get(child);
251+
const lastRect = child.getBoundingClientRect(); // Last
252+
253+
const deltaX = firstRect.left - lastRect.left;
254+
const deltaY = firstRect.top - lastRect.top;
255+
256+
if (deltaX !== 0 || deltaY !== 0) {
257+
child.style.transition = 'transform 0s'; // Invert (No transition)
258+
child.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
259+
child.offsetWidth; // Force reflow
260+
261+
child.style.transition = 'transform 200ms ease-in-out'; // Play
262+
child.style.transform = '';
263+
264+
child.addEventListener('transitionend', () => {
265+
child.style.transition = '';
266+
}, { once: true });
267+
} else {
272268
child.style.transition = '';
273-
}, { once: true }); // Executes once and removes itself
269+
child.style.transform = '';
270+
}
271+
}
272+
});
273+
274+
// --- FLIP: Last, Invert, Play for DRAGGED item ---
275+
if (firstDraggedRect) { // Ensure we recorded the initial position
276+
const lastDraggedRect = draggedItem.getBoundingClientRect(); // Last
277+
278+
const deltaDraggedX = firstDraggedRect.left - lastDraggedRect.left;
279+
const deltaDraggedY = firstDraggedRect.top - lastDraggedRect.top;
280+
281+
// Check if the element's calculated position actually changed
282+
if (deltaDraggedX !== 0 || deltaDraggedY !== 0) {
283+
// Apply inverse transform immediately WITHOUT transition
284+
draggedItem.style.transition = 'transform 0s';
285+
draggedItem.style.transform = `translate(${deltaDraggedX}px, ${deltaDraggedY}px)`;
286+
287+
// Force reflow is crucial here
288+
draggedItem.offsetWidth;
289+
290+
// Play: Apply transition and animate back to natural position (transform: '')
291+
// Use a slightly shorter duration as it's actively moving
292+
draggedItem.style.transition = 'transform 150ms ease-out';
293+
draggedItem.style.transform = '';
294+
295+
// Cleanup: Remove transition after animation.
296+
// NOTE: This might be problematic due to rapid 'dragover' events.
297+
// A flag or debouncing might be needed for robust cleanup.
298+
// For simplicity here, we'll use 'once', but be aware it might
299+
// get interrupted by the next dragover event.
300+
draggedItem.addEventListener('transitionend', () => {
301+
// Only remove transition if it hasn't been reset by another dragover
302+
if (draggedItem.style.transition.includes('150ms')) {
303+
draggedItem.style.transition = '';
304+
}
305+
}, { once: true });
274306

275307
} else {
276-
// If the element didn't move, ensure it has no animation styles
277-
child.style.transition = '';
278-
child.style.transform = '';
308+
// If it didn't move position-wise, ensure no leftover styles
309+
// Check if a transition is currently active from a previous move
310+
// If not, clear styles. If yes, let it finish.
311+
if (!draggedItem.style.transition.includes('150ms')) {
312+
draggedItem.style.transition = '';
313+
draggedItem.style.transform = '';
314+
}
279315
}
280316
}
281-
});
317+
} // end if(domChanged)
282318
}
283319

284320
function handleDrop(e) {

popup-page-styles/popup-buttons.css

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,19 @@ button.danger:hover {
104104

105105
/* Styling when a button item is being dragged */
106106
.button-item.dragging {
107-
transform: scale(0.8); /* Shrinks to 80% while dragging */
108-
background-color: #d4f8d4 !important; /* Light green highlight */
107+
/* Existing styles */
108+
transform: scale(0.8);
109+
background-color: #d4f8d4 !important;
109110
opacity: 0.8;
110111
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
111112
cursor: grabbing;
112113
z-index: 2;
114+
115+
/* Ensure transform transitions don't conflict horribly */
116+
/* The scale is applied instantly, the FLIP transform handles position */
117+
/* We might need to combine scale and translate in the FLIP transform */
118+
/* Let's adjust the FLIP logic slightly */
119+
will-change: transform, opacity; /* Keep will-change */
113120
}
114121

115122
/* ------------------------------------------------------------------------- */

0 commit comments

Comments
 (0)