@vc-shell/framework 1.1.0-alpha.4 → 1.1.0-alpha.6
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/core/composables/useMenuService/index.ts +20 -110
- package/core/composables/useWidgets/index.ts +2 -1
- package/core/plugins/modularity/index.ts +3 -3
- package/core/services/menu-service.ts +195 -0
- package/core/services/widget-service.ts +20 -0
- package/core/types/index.ts +1 -1
- package/dist/core/composables/useMenuService/index.d.ts +4 -10
- package/dist/core/composables/useMenuService/index.d.ts.map +1 -1
- package/dist/core/composables/useWidgets/index.d.ts +2 -1
- package/dist/core/composables/useWidgets/index.d.ts.map +1 -1
- package/dist/core/plugins/modularity/index.d.ts.map +1 -1
- package/dist/core/services/menu-service.d.ts +17 -0
- package/dist/core/services/menu-service.d.ts.map +1 -0
- package/dist/core/services/widget-service.d.ts +4 -0
- package/dist/core/services/widget-service.d.ts.map +1 -1
- package/dist/core/types/index.d.ts +1 -1
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/framework.js +220 -210
- package/dist/{index-CrxFDC2b.js → index-3ySdd-mG.js} +1 -1
- package/dist/{index-B1YR_MYV.js → index-B-nvqNbp.js} +1 -1
- package/dist/{index-xLYzNPa7.js → index-BQF2-UMe.js} +1 -1
- package/dist/{index-BBYyHeYA.js → index-BXlxP2d2.js} +1 -1
- package/dist/{index-Cf9Tz1ql.js → index-C7P-aBjd.js} +1 -1
- package/dist/{index-8LELHzw9.js → index-CO_2IshF.js} +1 -1
- package/dist/{index-BA98L1jI.js → index-CfyFpaKq.js} +1 -1
- package/dist/{index-DVljTjbf.js → index-Ci23AX3j.js} +1 -1
- package/dist/{index-D1JchciU.js → index-CyuFXG83.js} +1 -1
- package/dist/{index-CWKrD2Cd.js → index-D1rpRTKf.js} +1 -1
- package/dist/{index-BAeTsi-X.js → index-DLxTAT7x.js} +1 -1
- package/dist/{index-BuO5ByG9.js → index-DOVhosAY.js} +1 -1
- package/dist/{index-DLtsQ_PJ.js → index-DZAq0B3U.js} +23780 -23339
- package/dist/{index-BrUitdDo.js → index-DtkJ7xTB.js} +1 -1
- package/dist/{index-9lJxZE5w.js → index-DvGVm1rK.js} +1 -1
- package/dist/{index-RwX3kiZh.js → index-EDF1MDtU.js} +1 -1
- package/dist/{index-CJ5I7vTn.js → index-LjqdX6jw.js} +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/injection-keys.d.ts +2 -0
- package/dist/injection-keys.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/DraggableDashboard.vue.d.ts +2 -0
- package/dist/shared/components/draggable-dashboard/DraggableDashboard.vue.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/composables/useCellSizeCalculator.d.ts +25 -0
- package/dist/shared/components/draggable-dashboard/composables/useCellSizeCalculator.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useCollisionDetection.d.ts +27 -0
- package/dist/shared/components/draggable-dashboard/composables/useCollisionDetection.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts +22 -0
- package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts +12 -4
- package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/composables/useDragClone.d.ts +15 -0
- package/dist/shared/components/draggable-dashboard/composables/useDragClone.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useEventCoordinates.d.ts +33 -0
- package/dist/shared/components/draggable-dashboard/composables/useEventCoordinates.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useGridPosition.d.ts +57 -0
- package/dist/shared/components/draggable-dashboard/composables/useGridPosition.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useGridSystem.d.ts +22 -0
- package/dist/shared/components/draggable-dashboard/composables/useGridSystem.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts +19 -0
- package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useResizeObserver.d.ts +18 -0
- package/dist/shared/components/draggable-dashboard/composables/useResizeObserver.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useWidgetLayout.d.ts +14 -0
- package/dist/shared/components/draggable-dashboard/composables/useWidgetLayout.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useWidgetStyles.d.ts +21 -0
- package/dist/shared/components/draggable-dashboard/composables/useWidgetStyles.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/types.d.ts +5 -1
- package/dist/shared/components/draggable-dashboard/types.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/ui/components/atoms/vc-icon/icons/FulfillmentCentersIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/FulfillmentCentersIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/OffersIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/OffersIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/OrdersIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/OrdersIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/PeopleIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/PeopleIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/ProductsIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/ProductsIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/ProfileIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/ProfileIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/index.d.ts +6 -0
- package/dist/ui/components/atoms/vc-icon/icons/index.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/components/app-bar-button/app-bar-button.vue.d.ts +1 -0
- package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/components/app-bar-button/app-bar-button.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue.d.ts +2 -1
- package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/vc-table.vue.d.ts.map +1 -1
- package/package.json +4 -4
- package/shared/components/draggable-dashboard/DraggableDashboard.vue +114 -148
- package/shared/components/draggable-dashboard/composables/useCellSizeCalculator.ts +121 -0
- package/shared/components/draggable-dashboard/composables/useCollisionDetection.ts +219 -0
- package/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.ts +126 -331
- package/shared/components/draggable-dashboard/composables/useDashboardGrid.ts +74 -220
- package/shared/components/draggable-dashboard/composables/useDragClone.ts +97 -0
- package/shared/components/draggable-dashboard/composables/useEventCoordinates.ts +91 -0
- package/shared/components/draggable-dashboard/composables/useGridPosition.ts +150 -0
- package/shared/components/draggable-dashboard/composables/useGridSystem.ts +169 -0
- package/shared/components/draggable-dashboard/composables/useLayoutPersistence.ts +89 -0
- package/shared/components/draggable-dashboard/composables/useResizeObserver.ts +105 -0
- package/shared/components/draggable-dashboard/composables/useWidgetLayout.ts +264 -0
- package/shared/components/draggable-dashboard/composables/useWidgetStyles.ts +120 -0
- package/shared/components/draggable-dashboard/types.ts +6 -1
- package/shared/components/notification-dropdown/notification-dropdown.vue +1 -0
- package/ui/components/atoms/vc-icon/icons/FulfillmentCentersIcon.vue +27 -0
- package/ui/components/atoms/vc-icon/icons/OffersIcon.vue +23 -0
- package/ui/components/atoms/vc-icon/icons/OrdersIcon.vue +19 -0
- package/ui/components/atoms/vc-icon/icons/PeopleIcon.vue +21 -0
- package/ui/components/atoms/vc-icon/icons/ProductsIcon.vue +23 -0
- package/ui/components/atoms/vc-icon/icons/ProfileIcon.vue +18 -0
- package/ui/components/atoms/vc-icon/icons/index.ts +6 -0
- package/ui/components/atoms/vc-icon/vc-icon.vue +101 -82
- package/ui/components/organisms/vc-app/_internal/vc-app-bar/components/app-bar-button/app-bar-button.vue +10 -3
- package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +3 -4
- package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue +2 -2
- package/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +1 -0
- package/ui/components/organisms/vc-app/vc-app.vue +2 -3
- package/ui/components/organisms/vc-table/vc-table.vue +4 -1
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
import { ref, computed, onUnmounted } from "vue";
|
|
2
2
|
import type { IDashboardWidget, DashboardWidgetPosition } from "../types";
|
|
3
|
-
import { GRID_COLUMNS } from "./
|
|
3
|
+
import { GRID_COLUMNS } from "./useGridSystem";
|
|
4
4
|
import { useDashboard } from "../../../../core/composables/useDashboard";
|
|
5
|
+
import { useDragClone } from "./useDragClone";
|
|
6
|
+
import { useEventCoordinates } from "./useEventCoordinates";
|
|
7
|
+
import { useCollisionDetection } from "./useCollisionDetection";
|
|
8
|
+
import { useGridPosition, type CellSize } from "./useGridPosition";
|
|
5
9
|
|
|
6
10
|
// Cell height constant, corresponds to --dashboard-cell-height CSS variable
|
|
7
11
|
// Value of 80px is defined in DraggableDashboard.vue
|
|
8
12
|
const CELL_HEIGHT = 80;
|
|
9
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Composables for managing the dragging of widgets on the dashboard
|
|
16
|
+
*
|
|
17
|
+
* Provides functions for handling drag events, updating widget positions, and handling collisions
|
|
18
|
+
*
|
|
19
|
+
* @param updateWidgetPosition Function to update the position of a widget in the store
|
|
20
|
+
* @param getGridRows Function to get the number of rows in the grid
|
|
21
|
+
* @returns An object with functions and state for managing the dragging
|
|
22
|
+
*/
|
|
10
23
|
export function useDashboardDragAndDrop(
|
|
11
24
|
updateWidgetPosition: (widgetId: string, position: DashboardWidgetPosition) => void,
|
|
12
25
|
getGridRows?: () => number,
|
|
@@ -15,20 +28,27 @@ export function useDashboardDragAndDrop(
|
|
|
15
28
|
const widgets = computed(() => dashboard.getWidgets());
|
|
16
29
|
const gridContainer = ref<HTMLElement | null>(null);
|
|
17
30
|
|
|
18
|
-
//
|
|
31
|
+
// Dragging state
|
|
19
32
|
const draggedWidget = ref<IDashboardWidget | null>(null);
|
|
20
|
-
const previewPosition = ref<DashboardWidgetPosition | null>(null);
|
|
21
33
|
const isDragging = ref(false);
|
|
22
|
-
const dragOffset = ref({ x: 0, y: 0 });
|
|
23
|
-
const displacedWidgets = ref<Map<string, DashboardWidgetPosition>>(new Map());
|
|
24
34
|
|
|
25
|
-
//
|
|
26
|
-
const dragClone
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
// Initialize helper composables
|
|
36
|
+
const { dragClone, createDragClone, updateDragClonePosition, removeDragClone } = useDragClone();
|
|
37
|
+
const { initialPosition, isTouchDevice, getEventCoordinates, saveInitialPosition, hasMovedBeyondThreshold } =
|
|
38
|
+
useEventCoordinates();
|
|
39
|
+
const { displacedWidgets, updateDisplacedWidgets, isWidgetDisplaced, getDisplacedPosition } = useCollisionDetection();
|
|
40
|
+
const {
|
|
41
|
+
previewPosition,
|
|
42
|
+
dragOffset,
|
|
43
|
+
calculateDragOffset,
|
|
44
|
+
mouseToGridCoordinates,
|
|
45
|
+
constrainToGrid,
|
|
46
|
+
updatePreviewPosition,
|
|
47
|
+
resetPosition,
|
|
48
|
+
} = useGridPosition(GRID_COLUMNS, getGridRows);
|
|
49
|
+
|
|
50
|
+
// Getting the size of the cell
|
|
51
|
+
const calculateCellSize = (): CellSize => {
|
|
32
52
|
if (!gridContainer.value) return { cellWidth: 0, cellHeight: CELL_HEIGHT };
|
|
33
53
|
const rect = gridContainer.value.getBoundingClientRect();
|
|
34
54
|
return {
|
|
@@ -37,292 +57,81 @@ export function useDashboardDragAndDrop(
|
|
|
37
57
|
};
|
|
38
58
|
};
|
|
39
59
|
|
|
40
|
-
//
|
|
41
|
-
const createDragClone = (element: HTMLElement) => {
|
|
42
|
-
// Find parent widget element
|
|
43
|
-
const widgetElement = element.closest(".dashboard-widget") as HTMLElement;
|
|
44
|
-
if (!widgetElement) return null;
|
|
45
|
-
|
|
46
|
-
const clone = widgetElement.cloneNode(true) as HTMLElement;
|
|
47
|
-
const rect = widgetElement.getBoundingClientRect();
|
|
48
|
-
|
|
49
|
-
// Учитываем прокрутку страницы
|
|
50
|
-
const scrollX = window.scrollX || window.pageXOffset;
|
|
51
|
-
const scrollY = window.scrollY || window.pageYOffset;
|
|
52
|
-
|
|
53
|
-
// Копируем все вычисленные стили с оригинала
|
|
54
|
-
const computedStyle = window.getComputedStyle(widgetElement);
|
|
55
|
-
|
|
56
|
-
clone.style.position = "fixed";
|
|
57
|
-
clone.style.width = `${rect.width}px`;
|
|
58
|
-
clone.style.height = `${rect.height}px`;
|
|
59
|
-
clone.style.left = `${rect.left + scrollX}px`;
|
|
60
|
-
clone.style.top = `${rect.top + scrollY}px`;
|
|
61
|
-
clone.style.zIndex = "9999";
|
|
62
|
-
clone.style.pointerEvents = "none";
|
|
63
|
-
clone.style.opacity = "0.95";
|
|
64
|
-
clone.style.transform = "scale(1.02)";
|
|
65
|
-
clone.style.transition = "transform 0.1s ease, opacity 0.2s ease";
|
|
66
|
-
clone.style.boxShadow = "0 10px 25px rgba(0,0,0,0.15)";
|
|
67
|
-
clone.style.willChange = "transform";
|
|
68
|
-
|
|
69
|
-
// Копируем важные стили с оригинала
|
|
70
|
-
clone.style.backgroundColor = computedStyle.backgroundColor;
|
|
71
|
-
clone.style.borderRadius = computedStyle.borderRadius;
|
|
72
|
-
|
|
73
|
-
// Добавляем класс для стилизации клона
|
|
74
|
-
clone.classList.add("dashboard-widget-clone");
|
|
75
|
-
|
|
76
|
-
// Анимация появления
|
|
77
|
-
requestAnimationFrame(() => {
|
|
78
|
-
clone.style.transform = "scale(1.02)";
|
|
79
|
-
clone.style.opacity = "0.92";
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
document.body.appendChild(clone);
|
|
83
|
-
return clone;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Optimized intersection check function
|
|
87
|
-
const intersect = (
|
|
88
|
-
widgetA: IDashboardWidget,
|
|
89
|
-
posA: DashboardWidgetPosition,
|
|
90
|
-
widgetB: IDashboardWidget,
|
|
91
|
-
posB: DashboardWidgetPosition,
|
|
92
|
-
) => {
|
|
93
|
-
// Early Y-axis check (if difference is greater than sum of heights, no intersection)
|
|
94
|
-
const verticalDistance = Math.abs(posA.y - posB.y);
|
|
95
|
-
if (verticalDistance > widgetA.size.height + widgetB.size.height) {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Early X-axis check (if difference is greater than sum of widths, no intersection)
|
|
100
|
-
const horizontalDistance = Math.abs(posA.x - posB.x);
|
|
101
|
-
if (horizontalDistance > widgetA.size.width + widgetB.size.width) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Standard rectangle intersection check
|
|
106
|
-
const aLeft = posA.x;
|
|
107
|
-
const aRight = posA.x + widgetA.size.width;
|
|
108
|
-
const aTop = posA.y;
|
|
109
|
-
const aBottom = posA.y + widgetA.size.height;
|
|
110
|
-
|
|
111
|
-
const bLeft = posB.x;
|
|
112
|
-
const bRight = posB.x + widgetB.size.width;
|
|
113
|
-
const bTop = posB.y;
|
|
114
|
-
const bBottom = posB.y + widgetB.size.height;
|
|
115
|
-
|
|
116
|
-
return !(aRight <= bLeft || aLeft >= bRight || aBottom <= bTop || aTop >= bBottom);
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// Optimized function for updating displaced widgets
|
|
120
|
-
const updateDisplacedWidgets = () => {
|
|
121
|
-
if (!draggedWidget.value || !previewPosition.value || !isDragging.value) {
|
|
122
|
-
displacedWidgets.value.clear();
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const layout = new Map(dashboard.getLayout());
|
|
127
|
-
const newDisplacements = new Map<string, DashboardWidgetPosition>();
|
|
128
|
-
const processedWidgets = new Set<string>();
|
|
129
|
-
|
|
130
|
-
// Cache for initial collision checks
|
|
131
|
-
const directCollisionWidgets = new Set<string>();
|
|
132
|
-
|
|
133
|
-
// Check collisions with other widgets
|
|
134
|
-
for (const widget of widgets.value) {
|
|
135
|
-
if (widget.id === draggedWidget.value.id) continue;
|
|
136
|
-
|
|
137
|
-
const pos = layout.get(widget.id);
|
|
138
|
-
if (!pos) continue;
|
|
139
|
-
|
|
140
|
-
if (intersect(draggedWidget.value, previewPosition.value, widget, pos)) {
|
|
141
|
-
directCollisionWidgets.add(widget.id);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// If no direct collisions, skip additional displacement calculations
|
|
146
|
-
if (directCollisionWidgets.size === 0) {
|
|
147
|
-
displacedWidgets.value = newDisplacements;
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Sort widgets top to bottom for more natural rearrangement
|
|
152
|
-
const sortedWidgets = [...widgets.value]
|
|
153
|
-
.filter((w) => w.id !== draggedWidget.value?.id)
|
|
154
|
-
.sort((a, b) => {
|
|
155
|
-
const posA = layout.get(a.id);
|
|
156
|
-
const posB = layout.get(b.id);
|
|
157
|
-
if (!posA || !posB) return 0;
|
|
158
|
-
// Sort by Y first, then by X for more stable results
|
|
159
|
-
return posA.y === posB.y ? posA.x - posB.x : posA.y - posB.y;
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// Function for checking collisions with caching
|
|
163
|
-
const collisionCache = new Map<string, boolean>();
|
|
164
|
-
|
|
165
|
-
const checkCollision = (widget: IDashboardWidget, position: DashboardWidgetPosition) => {
|
|
166
|
-
const cacheKey = `${widget.id}_${position.x}_${position.y}`;
|
|
167
|
-
|
|
168
|
-
if (collisionCache.has(cacheKey)) {
|
|
169
|
-
return collisionCache.get(cacheKey);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Check intersection with dragged widget
|
|
173
|
-
let hasCollision = intersect(draggedWidget.value!, previewPosition.value!, widget, position);
|
|
174
|
-
|
|
175
|
-
// Check intersections with already displaced widgets if no collision with dragged widget
|
|
176
|
-
if (!hasCollision) {
|
|
177
|
-
for (const [id, pos] of newDisplacements) {
|
|
178
|
-
const w = widgets.value.find((w) => w.id === id);
|
|
179
|
-
if (w && intersect(widget, position, w, pos)) {
|
|
180
|
-
hasCollision = true;
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
collisionCache.set(cacheKey, hasCollision);
|
|
187
|
-
return hasCollision;
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
// Process widgets with direct collisions first
|
|
191
|
-
for (const widget of sortedWidgets) {
|
|
192
|
-
if (processedWidgets.has(widget.id)) continue;
|
|
193
|
-
|
|
194
|
-
const currentPos = layout.get(widget.id);
|
|
195
|
-
if (!currentPos) continue;
|
|
196
|
-
|
|
197
|
-
// Focus on widgets with direct collisions first
|
|
198
|
-
if (!directCollisionWidgets.has(widget.id) && newDisplacements.size === 0) continue;
|
|
199
|
-
|
|
200
|
-
// Check collisions at current position
|
|
201
|
-
const hasCollision = checkCollision(widget, currentPos);
|
|
202
|
-
|
|
203
|
-
if (hasCollision) {
|
|
204
|
-
// Find new position below dragged widget
|
|
205
|
-
const newY = Math.max(previewPosition.value.y + draggedWidget.value.size.height, currentPos.y + 1);
|
|
206
|
-
let finalY = newY;
|
|
207
|
-
|
|
208
|
-
// Find nearest position without collisions, optimized
|
|
209
|
-
const maxIterations = 50; // Protection against infinite loop
|
|
210
|
-
let iterations = 0;
|
|
211
|
-
|
|
212
|
-
while (checkCollision(widget, { x: currentPos.x, y: finalY }) && iterations < maxIterations) {
|
|
213
|
-
finalY++;
|
|
214
|
-
iterations++;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
newDisplacements.set(widget.id, { x: currentPos.x, y: finalY });
|
|
218
|
-
processedWidgets.add(widget.id);
|
|
219
|
-
|
|
220
|
-
// If we displaced a widget with direct collision, check if we created new collisions
|
|
221
|
-
for (const w of sortedWidgets) {
|
|
222
|
-
if (w.id !== widget.id && !processedWidgets.has(w.id)) {
|
|
223
|
-
const pos = layout.get(w.id);
|
|
224
|
-
if (pos && intersect(widget, { x: currentPos.x, y: finalY }, w, pos)) {
|
|
225
|
-
directCollisionWidgets.add(w.id);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
displacedWidgets.value = newDisplacements;
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
// Get event coordinates for either mouse or touch events
|
|
236
|
-
const getEventCoordinates = (event: MouseEvent | TouchEvent) => {
|
|
237
|
-
if ("touches" in event) {
|
|
238
|
-
// Touch event
|
|
239
|
-
return {
|
|
240
|
-
clientX: event.touches[0].clientX,
|
|
241
|
-
clientY: event.touches[0].clientY,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
// Mouse event
|
|
245
|
-
return {
|
|
246
|
-
clientX: (event as MouseEvent).clientX,
|
|
247
|
-
clientY: (event as MouseEvent).clientY,
|
|
248
|
-
};
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
// Common logic for mouse and touch move events
|
|
60
|
+
// Common logic for updating the position during dragging
|
|
252
61
|
const updateDragPosition = (event: MouseEvent | TouchEvent) => {
|
|
253
62
|
if (!gridContainer.value || !draggedWidget.value || !dragClone.value) return;
|
|
254
63
|
|
|
255
|
-
// Save event data as it
|
|
64
|
+
// Save event data, as it may be unavailable in requestAnimationFrame
|
|
256
65
|
const coords = getEventCoordinates(event);
|
|
257
66
|
|
|
258
|
-
// Use requestAnimationFrame for UI
|
|
67
|
+
// Use requestAnimationFrame for UI optimization
|
|
259
68
|
requestAnimationFrame(() => {
|
|
260
69
|
if (!gridContainer.value || !draggedWidget.value || !dragClone.value) return;
|
|
261
70
|
|
|
262
71
|
const rect = gridContainer.value.getBoundingClientRect();
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
// Update clone position relative to initial mouse position
|
|
266
|
-
const deltaX = coords.clientX - initialMousePosition.value.x;
|
|
267
|
-
const deltaY = coords.clientY - initialMousePosition.value.y;
|
|
268
|
-
|
|
269
|
-
dragClone.value.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
|
|
270
|
-
|
|
271
|
-
// Calculate container scroll
|
|
272
|
-
const gridScrollLeft = gridContainer.value.scrollLeft;
|
|
273
|
-
const gridScrollTop = gridContainer.value.scrollTop;
|
|
72
|
+
const cellSize = calculateCellSize();
|
|
274
73
|
|
|
275
|
-
//
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const mouseX = coords.clientX - gridRectLeft + gridScrollLeft;
|
|
281
|
-
const mouseY = coords.clientY - gridRectTop + gridScrollTop;
|
|
282
|
-
|
|
283
|
-
// Convert mouse coordinates to grid coordinates with drag offset
|
|
284
|
-
let gridX = Math.round(mouseX / cellWidth - dragOffset.value.x);
|
|
285
|
-
let gridY = Math.round(mouseY / cellHeight - dragOffset.value.y);
|
|
74
|
+
// Update the position of the clone relative to the initial mouse position
|
|
75
|
+
const { deltaX, deltaY } = {
|
|
76
|
+
deltaX: coords.clientX - initialPosition.value.clientX,
|
|
77
|
+
deltaY: coords.clientY - initialPosition.value.clientY,
|
|
78
|
+
};
|
|
286
79
|
|
|
287
|
-
|
|
288
|
-
gridX = Math.max(0, gridX);
|
|
289
|
-
gridY = Math.max(0, gridY);
|
|
80
|
+
updateDragClonePosition(deltaX, deltaY);
|
|
290
81
|
|
|
291
|
-
//
|
|
292
|
-
const
|
|
293
|
-
|
|
82
|
+
// Calculate the scroll offset of the container
|
|
83
|
+
const scrollOffset = {
|
|
84
|
+
left: gridContainer.value.scrollLeft,
|
|
85
|
+
top: gridContainer.value.scrollTop,
|
|
86
|
+
};
|
|
294
87
|
|
|
295
|
-
//
|
|
296
|
-
gridX =
|
|
297
|
-
|
|
88
|
+
// Convert mouse coordinates to grid coordinates
|
|
89
|
+
const { gridX, gridY } = mouseToGridCoordinates(coords, rect, cellSize, scrollOffset);
|
|
90
|
+
|
|
91
|
+
// Constrain the position to the grid boundaries
|
|
92
|
+
const constrainedPosition = constrainToGrid(
|
|
93
|
+
gridX,
|
|
94
|
+
gridY,
|
|
95
|
+
draggedWidget.value.size.width,
|
|
96
|
+
draggedWidget.value.size.height,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Update the preview position only if it has changed
|
|
100
|
+
if (
|
|
101
|
+
!previewPosition.value ||
|
|
102
|
+
previewPosition.value.x !== constrainedPosition.gridX ||
|
|
103
|
+
previewPosition.value.y !== constrainedPosition.gridY
|
|
104
|
+
) {
|
|
105
|
+
const newPosition = {
|
|
106
|
+
x: constrainedPosition.gridX,
|
|
107
|
+
y: constrainedPosition.gridY,
|
|
108
|
+
};
|
|
298
109
|
|
|
299
|
-
|
|
300
|
-
if (!previewPosition.value || previewPosition.value.x !== gridX || previewPosition.value.y !== gridY) {
|
|
301
|
-
previewPosition.value = { x: gridX, y: gridY };
|
|
110
|
+
updatePreviewPosition(newPosition);
|
|
302
111
|
|
|
303
|
-
// Recalculate displaced widgets
|
|
304
|
-
updateDisplacedWidgets();
|
|
112
|
+
// Recalculate displaced widgets when the position changes
|
|
113
|
+
updateDisplacedWidgets(draggedWidget.value, newPosition, widgets.value, new Map(dashboard.getLayout()));
|
|
305
114
|
}
|
|
306
115
|
});
|
|
307
116
|
};
|
|
308
117
|
|
|
309
|
-
//
|
|
118
|
+
// Mouse move event handler
|
|
310
119
|
const handleMouseMove = (event: MouseEvent) => {
|
|
311
120
|
if (!isDragging.value || !draggedWidget.value || !gridContainer.value || !dragClone.value) return;
|
|
312
121
|
updateDragPosition(event);
|
|
313
122
|
};
|
|
314
123
|
|
|
315
|
-
//
|
|
124
|
+
// Touch move event handler
|
|
316
125
|
const handleTouchMove = (event: TouchEvent) => {
|
|
317
126
|
if (!isDragging.value || !draggedWidget.value || !gridContainer.value || !dragClone.value) return;
|
|
318
127
|
|
|
319
|
-
// Prevent page scrolling during
|
|
128
|
+
// Prevent page scrolling during dragging
|
|
320
129
|
event.preventDefault();
|
|
321
130
|
|
|
322
131
|
updateDragPosition(event);
|
|
323
132
|
};
|
|
324
133
|
|
|
325
|
-
//
|
|
134
|
+
// Mouse up event handler
|
|
326
135
|
const handleMouseUp = () => {
|
|
327
136
|
finishDrag();
|
|
328
137
|
};
|
|
@@ -331,91 +140,85 @@ export function useDashboardDragAndDrop(
|
|
|
331
140
|
finishDrag();
|
|
332
141
|
};
|
|
333
142
|
|
|
334
|
-
// Common
|
|
143
|
+
// Common function for finishing the dragging
|
|
335
144
|
const finishDrag = () => {
|
|
336
145
|
if (!isDragging.value || !draggedWidget.value || !previewPosition.value) return;
|
|
337
146
|
|
|
338
|
-
// Get necessary data
|
|
147
|
+
// Get the necessary data
|
|
339
148
|
const draggedWidgetId = draggedWidget.value.id;
|
|
340
149
|
const finalPosition = previewPosition.value;
|
|
341
150
|
|
|
342
|
-
// When there
|
|
151
|
+
// When there is a clone, finish the dragging smoothly
|
|
343
152
|
if (dragClone.value) {
|
|
344
|
-
// Get DOM element of original widget
|
|
153
|
+
// Get the DOM element of the original widget
|
|
345
154
|
const originalWidget = document.querySelector(`.dashboard-widget[data-id="${draggedWidgetId}"]`) as HTMLElement;
|
|
346
155
|
|
|
347
156
|
if (originalWidget) {
|
|
348
|
-
const
|
|
157
|
+
const cellSize = calculateCellSize();
|
|
349
158
|
const widgetGap = 20; // Widget gap in pixels
|
|
350
159
|
|
|
351
|
-
// Get current clone
|
|
160
|
+
// Get the current position of the clone (where the user released the mouse)
|
|
352
161
|
const cloneRect = dragClone.value.getBoundingClientRect();
|
|
353
162
|
|
|
354
|
-
// Get page scroll
|
|
163
|
+
// Get the page scroll
|
|
355
164
|
const scrollX = window.scrollX || window.pageXOffset;
|
|
356
165
|
const scrollY = window.scrollY || window.pageYOffset;
|
|
357
166
|
|
|
358
|
-
// Get grid container
|
|
167
|
+
// Get the position of the grid container with the scroll
|
|
359
168
|
const gridRect = gridContainer.value!.getBoundingClientRect();
|
|
360
169
|
const gridScrollLeft = gridContainer.value!.scrollLeft;
|
|
361
170
|
const gridScrollTop = gridContainer.value!.scrollTop;
|
|
362
171
|
|
|
363
|
-
// Calculate relative clone
|
|
172
|
+
// Calculate the relative position of the clone inside the container with all scrolls
|
|
364
173
|
const relX = cloneRect.left + scrollX - (gridRect.left + scrollX) + gridScrollLeft;
|
|
365
174
|
const relY = cloneRect.top + scrollY - (gridRect.top + scrollY) + gridScrollTop;
|
|
366
175
|
|
|
367
|
-
// Calculate final coordinates where widget should
|
|
368
|
-
const finalX = finalPosition.x * cellWidth + widgetGap / 2;
|
|
369
|
-
const finalY = finalPosition.y * cellHeight + widgetGap / 2;
|
|
176
|
+
// Calculate the final coordinates, where the widget should be moved
|
|
177
|
+
const finalX = finalPosition.x * cellSize.cellWidth + widgetGap / 2;
|
|
178
|
+
const finalY = finalPosition.y * cellSize.cellHeight + widgetGap / 2;
|
|
370
179
|
|
|
371
|
-
// First
|
|
180
|
+
// First, move the original widget exactly to the clone position
|
|
372
181
|
originalWidget.style.transition = "none";
|
|
373
182
|
originalWidget.style.transform = `translate(${relX}px, ${relY}px)`;
|
|
374
183
|
originalWidget.style.opacity = "1";
|
|
375
184
|
originalWidget.style.zIndex = "1000";
|
|
376
185
|
|
|
377
|
-
// Remove clone immediately
|
|
378
|
-
|
|
379
|
-
document.body.removeChild(dragClone.value);
|
|
380
|
-
}
|
|
381
|
-
dragClone.value = null;
|
|
186
|
+
// Remove the clone immediately, as the original is in its place
|
|
187
|
+
removeDragClone();
|
|
382
188
|
|
|
383
|
-
// Force reflow
|
|
189
|
+
// Force reflow for applying styles
|
|
384
190
|
originalWidget.offsetHeight; // eslint-disable-line
|
|
385
191
|
|
|
386
|
-
// Update position in data store
|
|
192
|
+
// Update the position in the data store
|
|
387
193
|
updateWidgetPosition(draggedWidgetId, finalPosition);
|
|
388
194
|
|
|
389
|
-
// Update positions of all displaced widgets
|
|
195
|
+
// Update the positions of all displaced widgets
|
|
390
196
|
for (const [widgetId, position] of displacedWidgets.value.entries()) {
|
|
391
197
|
updateWidgetPosition(widgetId, position);
|
|
392
198
|
}
|
|
393
199
|
|
|
394
|
-
// Now smoothly animate widget to its final position
|
|
200
|
+
// Now smoothly animate the widget to its final position
|
|
395
201
|
setTimeout(() => {
|
|
396
202
|
originalWidget.style.transition = `transform var(--dashboard-transition-duration) var(--dashboard-transition-timing)`;
|
|
397
203
|
originalWidget.style.transform = `translate(${finalX}px, ${finalY}px)`;
|
|
398
|
-
}, 20); // Small delay
|
|
204
|
+
}, 20); // Small delay for correct style application
|
|
399
205
|
} else {
|
|
400
|
-
// If original not found,
|
|
401
|
-
|
|
402
|
-
document.body.removeChild(dragClone.value);
|
|
403
|
-
}
|
|
404
|
-
dragClone.value = null;
|
|
206
|
+
// If the original is not found, simply remove the clone and update the data
|
|
207
|
+
removeDragClone();
|
|
405
208
|
|
|
406
|
-
// Update position in store
|
|
209
|
+
// Update the position in the data store
|
|
407
210
|
updateWidgetPosition(draggedWidgetId, finalPosition);
|
|
408
211
|
|
|
409
|
-
// Update positions of all displaced widgets
|
|
212
|
+
// Update the positions of all displaced widgets
|
|
410
213
|
for (const [widgetId, position] of displacedWidgets.value.entries()) {
|
|
411
214
|
updateWidgetPosition(widgetId, position);
|
|
412
215
|
}
|
|
413
216
|
}
|
|
414
217
|
} else {
|
|
415
|
-
// If no clone,
|
|
218
|
+
// If there is no clone, simply update the data
|
|
416
219
|
updateWidgetPosition(draggedWidgetId, finalPosition);
|
|
417
220
|
|
|
418
|
-
// Update positions of all displaced widgets
|
|
221
|
+
// Update the positions of all displaced widgets
|
|
419
222
|
for (const [widgetId, position] of displacedWidgets.value.entries()) {
|
|
420
223
|
updateWidgetPosition(widgetId, position);
|
|
421
224
|
}
|
|
@@ -428,66 +231,56 @@ export function useDashboardDragAndDrop(
|
|
|
428
231
|
document.removeEventListener("touchend", handleTouchEnd);
|
|
429
232
|
document.removeEventListener("touchcancel", handleTouchEnd);
|
|
430
233
|
|
|
431
|
-
// Reset state with delay
|
|
234
|
+
// Reset the state with a delay, so the animation has time to finish
|
|
432
235
|
setTimeout(() => {
|
|
433
236
|
isDragging.value = false;
|
|
434
237
|
draggedWidget.value = null;
|
|
435
|
-
|
|
436
|
-
displacedWidgets.value.clear();
|
|
437
|
-
dragOffset.value = { x: 0, y: 0 };
|
|
238
|
+
resetPosition();
|
|
438
239
|
}, 50);
|
|
439
240
|
};
|
|
440
241
|
|
|
441
|
-
//
|
|
242
|
+
// Mouse/touch event handler with passive listeners
|
|
442
243
|
const handleMouseDown = (event: MouseEvent | TouchEvent, widget: IDashboardWidget, element: HTMLElement) => {
|
|
443
244
|
if (!gridContainer.value) return;
|
|
444
245
|
|
|
445
|
-
// Check event type
|
|
446
|
-
|
|
246
|
+
// Check the event type
|
|
247
|
+
if ("touches" in event) {
|
|
248
|
+
isTouchDevice.value = true;
|
|
249
|
+
}
|
|
447
250
|
|
|
448
|
-
// Use preventDefault only for mouse events, use passive: false
|
|
251
|
+
// Use preventDefault only for mouse events, for touch events use passive: false
|
|
449
252
|
if (!isTouchDevice.value) {
|
|
450
253
|
event.preventDefault();
|
|
451
254
|
}
|
|
452
255
|
event.stopPropagation();
|
|
453
256
|
|
|
454
|
-
//
|
|
455
|
-
|
|
257
|
+
// Save the initial position for determining the dragging
|
|
258
|
+
saveInitialPosition(event);
|
|
456
259
|
let hasDragStarted = false;
|
|
457
260
|
|
|
458
|
-
// Find parent widget element
|
|
261
|
+
// Find the parent widget element
|
|
459
262
|
const widgetElement = element.closest(".dashboard-widget") as HTMLElement;
|
|
460
263
|
if (!widgetElement) return;
|
|
461
264
|
|
|
462
|
-
const
|
|
463
|
-
const { cellWidth, cellHeight } = calculateCellSize();
|
|
265
|
+
const cellSize = calculateCellSize();
|
|
464
266
|
|
|
465
|
-
// Temporary
|
|
267
|
+
// Temporary handler for movement to determine the start of dragging
|
|
466
268
|
const tempMoveHandler = (moveEvent: MouseEvent | TouchEvent) => {
|
|
467
269
|
if (hasDragStarted) return;
|
|
468
270
|
|
|
469
271
|
const currentCoords = getEventCoordinates(moveEvent);
|
|
470
|
-
const deltaX = Math.abs(currentCoords.clientX - initialEvent.clientX);
|
|
471
|
-
const deltaY = Math.abs(currentCoords.clientY - initialEvent.clientY);
|
|
472
272
|
|
|
473
|
-
// Start
|
|
474
|
-
if (
|
|
273
|
+
// Start dragging only if the mouse has moved more than 3 pixels
|
|
274
|
+
if (hasMovedBeyondThreshold(currentCoords, 3)) {
|
|
475
275
|
hasDragStarted = true;
|
|
476
276
|
isDragging.value = true;
|
|
477
277
|
draggedWidget.value = widget;
|
|
478
278
|
|
|
479
|
-
//
|
|
480
|
-
|
|
481
|
-
x: (initialEvent.clientX - rect.left) / cellWidth,
|
|
482
|
-
y: (initialEvent.clientY - rect.top) / cellHeight,
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
initialMousePosition.value = {
|
|
486
|
-
x: initialEvent.clientX,
|
|
487
|
-
y: initialEvent.clientY,
|
|
488
|
-
};
|
|
279
|
+
// Calculate the drag offset
|
|
280
|
+
calculateDragOffset(event, widgetElement, cellSize);
|
|
489
281
|
|
|
490
|
-
|
|
282
|
+
// Create a clone for dragging
|
|
283
|
+
createDragClone(element);
|
|
491
284
|
|
|
492
285
|
// Remove temporary handlers
|
|
493
286
|
document.removeEventListener("mousemove", tempMoveHandler);
|
|
@@ -495,7 +288,7 @@ export function useDashboardDragAndDrop(
|
|
|
495
288
|
document.removeEventListener("mouseup", tempUpHandler);
|
|
496
289
|
document.removeEventListener("touchend", tempUpHandler);
|
|
497
290
|
|
|
498
|
-
// Add
|
|
291
|
+
// Add the dragging handlers
|
|
499
292
|
if (isTouchDevice.value) {
|
|
500
293
|
document.addEventListener("touchmove", handleTouchMove, { passive: false } as EventListenerOptions);
|
|
501
294
|
document.addEventListener("touchend", handleTouchEnd);
|
|
@@ -507,7 +300,7 @@ export function useDashboardDragAndDrop(
|
|
|
507
300
|
}
|
|
508
301
|
};
|
|
509
302
|
|
|
510
|
-
// Temporary
|
|
303
|
+
// Temporary handler for releasing to cancel the determination of dragging
|
|
511
304
|
const tempUpHandler = () => {
|
|
512
305
|
if (!hasDragStarted) {
|
|
513
306
|
document.removeEventListener("mousemove", tempMoveHandler);
|
|
@@ -517,7 +310,7 @@ export function useDashboardDragAndDrop(
|
|
|
517
310
|
}
|
|
518
311
|
};
|
|
519
312
|
|
|
520
|
-
// Add temporary handlers
|
|
313
|
+
// Add temporary handlers for determining the start of dragging
|
|
521
314
|
if (isTouchDevice.value) {
|
|
522
315
|
document.addEventListener("touchmove", tempMoveHandler, { passive: true });
|
|
523
316
|
document.addEventListener("touchend", tempUpHandler);
|
|
@@ -531,7 +324,7 @@ export function useDashboardDragAndDrop(
|
|
|
531
324
|
gridContainer.value = container;
|
|
532
325
|
};
|
|
533
326
|
|
|
534
|
-
//
|
|
327
|
+
// Clear event handlers when the component is unmounted
|
|
535
328
|
onUnmounted(() => {
|
|
536
329
|
finishDrag();
|
|
537
330
|
});
|
|
@@ -543,5 +336,7 @@ export function useDashboardDragAndDrop(
|
|
|
543
336
|
isDragging,
|
|
544
337
|
handleMouseDown, // Now handles both mouse and touch events
|
|
545
338
|
setGridContainer,
|
|
339
|
+
isWidgetDisplaced,
|
|
340
|
+
getDisplacedPosition,
|
|
546
341
|
};
|
|
547
342
|
}
|