diff --git a/src/scripts/components/ServicesManager.js b/src/scripts/components/ServicesManager.js
index 2f02951..777efd7 100644
--- a/src/scripts/components/ServicesManager.js
+++ b/src/scripts/components/ServicesManager.js
@@ -30,6 +30,9 @@ export class ServicesManager {
draggedElement: null,
draggedIndex: -1,
placeholder: null,
+ initialMouseY: 0,
+ initialElementY: 0,
+ mouseOffset: 0,
};
this.elements = {
@@ -363,26 +366,42 @@ export class ServicesManager {
this.dragState.draggedElement = serviceItem;
this.dragState.draggedIndex = parseInt(serviceItem.dataset.index);
- // Create placeholder
+ // Store initial positions for mouse following
+ const rect = serviceItem.getBoundingClientRect();
+ this.dragState.initialMouseY = event.clientY;
+ this.dragState.initialElementY = rect.top;
+ this.dragState.mouseOffset = event.clientY - rect.top;
+
+ // Create placeholder with smooth appearance
this.dragState.placeholder = document.createElement("div");
this.dragState.placeholder.className =
"selected-service-item drag-placeholder";
this.dragState.placeholder.innerHTML = `
- Drop here
+
`;
- // Add dragging class
+ // Add dragging classes with smooth animation
serviceItem.classList.add("dragging");
document.body.classList.add("dragging");
- // Insert placeholder
+ // Insert placeholder with animation
+ this.dragState.placeholder.style.height = "0px";
+ this.dragState.placeholder.style.opacity = "0";
+ this.dragState.placeholder.style.marginBottom = "0px";
serviceItem.parentNode.insertBefore(
this.dragState.placeholder,
serviceItem.nextSibling
);
+
+ // Animate placeholder appearance
+ requestAnimationFrame(() => {
+ this.dragState.placeholder.style.height = serviceItem.offsetHeight + "px";
+ this.dragState.placeholder.style.opacity = "1";
+ this.dragState.placeholder.style.marginBottom = "0.75rem";
+ });
}
/**
@@ -392,6 +411,15 @@ export class ServicesManager {
handleDragMove(event) {
if (!this.dragState.isDragging) return;
+ // Make dragged element follow mouse vertically
+ const draggedElement = this.dragState.draggedElement;
+ if (draggedElement) {
+ const deltaY = event.clientY - this.dragState.initialMouseY;
+ draggedElement.style.transform = `translateY(${deltaY}px) scale(1.05)`;
+ draggedElement.style.zIndex = "1000";
+ draggedElement.style.position = "relative";
+ }
+
const serviceItems = Array.from(
this.elements.selectedServices.querySelectorAll(
".selected-service-item:not(.dragging):not(.drag-placeholder)"
@@ -399,11 +427,20 @@ export class ServicesManager {
);
let targetIndex = serviceItems.length; // Default to end
+ let closestItem = null;
+ let closestDistance = Infinity;
+ // Find the closest item for smoother animation
for (let i = 0; i < serviceItems.length; i++) {
const item = serviceItems[i];
const rect = item.getBoundingClientRect();
const itemCenter = rect.top + rect.height / 2;
+ const distance = Math.abs(event.clientY - itemCenter);
+
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestItem = item;
+ }
if (event.clientY < itemCenter) {
targetIndex = i;
@@ -411,7 +448,21 @@ export class ServicesManager {
}
}
- // Move placeholder to new position
+ // Add smooth animation classes to other items
+ serviceItems.forEach((item, index) => {
+ const rect = item.getBoundingClientRect();
+ const itemCenter = rect.top + rect.height / 2;
+
+ if (event.clientY < itemCenter && index >= targetIndex) {
+ item.style.transform = "translateY(8px)";
+ } else if (event.clientY > itemCenter && index < targetIndex) {
+ item.style.transform = "translateY(-8px)";
+ } else {
+ item.style.transform = "translateY(0)";
+ }
+ });
+
+ // Move placeholder to new position with smooth transition
const targetItem = serviceItems[targetIndex];
if (targetItem) {
targetItem.parentNode.insertBefore(
@@ -431,6 +482,18 @@ export class ServicesManager {
handleDragEnd(event) {
if (!this.dragState.isDragging) return;
+ // Reset transforms on all items with smooth animation
+ const allServiceItems = Array.from(
+ this.elements.selectedServices.querySelectorAll(
+ ".selected-service-item:not(.dragging):not(.drag-placeholder)"
+ )
+ );
+
+ allServiceItems.forEach((item) => {
+ item.style.transform = "";
+ item.style.transition = "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)";
+ });
+
// Calculate new index based on placeholder position
const allItems = Array.from(
this.elements.selectedServices.querySelectorAll(
@@ -446,26 +509,47 @@ export class ServicesManager {
// Update services order
this.reorderServices(this.dragState.draggedIndex, newIndex);
- // Clean up
- this.dragState.draggedElement.classList.remove("dragging");
- document.body.classList.remove("dragging");
+ // Animate placeholder disappearance
if (this.dragState.placeholder && this.dragState.placeholder.parentNode) {
- this.dragState.placeholder.parentNode.removeChild(
- this.dragState.placeholder
- );
+ this.dragState.placeholder.style.height = "0px";
+ this.dragState.placeholder.style.opacity = "0";
+ this.dragState.placeholder.style.marginBottom = "0px";
+
+ setTimeout(() => {
+ if (
+ this.dragState.placeholder &&
+ this.dragState.placeholder.parentNode
+ ) {
+ this.dragState.placeholder.parentNode.removeChild(
+ this.dragState.placeholder
+ );
+ }
+ }, 300);
}
+ // Clean up with smooth animation
+ this.dragState.draggedElement.classList.remove("dragging");
+ this.dragState.draggedElement.style.transform = "";
+ this.dragState.draggedElement.style.zIndex = "";
+ this.dragState.draggedElement.style.position = "";
+ document.body.classList.remove("dragging");
+
// Reset drag state
this.dragState = {
isDragging: false,
draggedElement: null,
draggedIndex: -1,
placeholder: null,
+ initialMouseY: 0,
+ initialElementY: 0,
+ mouseOffset: 0,
};
- // Update UI and trigger change event
- this.renderSelectedServices();
- this.triggerSelectionChange();
+ // Update UI and trigger change event with smooth animation
+ setTimeout(() => {
+ this.renderSelectedServices();
+ this.triggerSelectionChange();
+ }, 50);
}
/**
diff --git a/src/styles/main.css b/src/styles/main.css
index 9e14bba..08fe6a5 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -996,7 +996,7 @@ body::before {
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 12px;
- transition: all 0.3s ease;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
@@ -1578,18 +1578,37 @@ body::before {
========================================================================== */
.dragging .selected-service-item {
z-index: 1000;
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
+ transform: scale(1.02);
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.drag-placeholder {
- border: 2px dashed var(--primary-color) !important;
- background: rgba(37, 99, 235, 0.1) !important;
- opacity: 0.7;
+ border: 2px solid var(--primary-color) !important;
+ background: linear-gradient(
+ 135deg,
+ rgba(37, 99, 235, 0.08),
+ rgba(37, 99, 235, 0.15)
+ ) !important;
+ opacity: 1;
+ animation: pulse-glow 1.5s ease-in-out infinite alternate;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+@keyframes pulse-glow {
+ 0% {
+ box-shadow: 0 0 5px rgba(37, 99, 235, 0.3);
+ transform: scale(1);
+ }
+ 100% {
+ box-shadow: 0 0 20px rgba(37, 99, 235, 0.6);
+ transform: scale(1.02);
+ }
}
.drag-placeholder .service-name {
color: var(--primary-color) !important;
- font-style: italic;
+ opacity: 0.8;
}
body.dragging {
@@ -1604,26 +1623,48 @@ body.dragging * {
.service-drag-handle {
cursor: grab;
opacity: 0.6;
- transition: opacity 0.2s ease;
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.service-drag-handle:hover {
opacity: 1;
color: var(--primary-color);
+ transform: scale(1.1);
}
.service-drag-handle:active {
cursor: grabbing;
+ transform: scale(0.95);
}
.selected-service-item.dragging {
- transition: none;
+ transition: none !important;
z-index: 1000;
position: relative;
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.4);
+ pointer-events: none;
+}
+
+/* Smooth animations for reordering */
+.selected-service-item:not(.dragging):not(.drag-placeholder) {
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+/* Enhanced animations for drag interactions */
+.selected-service-item.drag-over-top {
+ transform: translateY(-8px);
+}
+
+.selected-service-item.drag-over-bottom {
+ transform: translateY(8px);
}
/* Dark theme drag styles */
[data-theme="dark"] .drag-placeholder {
border-color: var(--primary-color) !important;
- background: rgba(37, 99, 235, 0.15) !important;
+ background: linear-gradient(
+ 135deg,
+ rgba(37, 99, 235, 0.12),
+ rgba(37, 99, 235, 0.2)
+ ) !important;
}