@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.
Files changed (119) hide show
  1. package/core/composables/useMenuService/index.ts +20 -110
  2. package/core/composables/useWidgets/index.ts +2 -1
  3. package/core/plugins/modularity/index.ts +3 -3
  4. package/core/services/menu-service.ts +195 -0
  5. package/core/services/widget-service.ts +20 -0
  6. package/core/types/index.ts +1 -1
  7. package/dist/core/composables/useMenuService/index.d.ts +4 -10
  8. package/dist/core/composables/useMenuService/index.d.ts.map +1 -1
  9. package/dist/core/composables/useWidgets/index.d.ts +2 -1
  10. package/dist/core/composables/useWidgets/index.d.ts.map +1 -1
  11. package/dist/core/plugins/modularity/index.d.ts.map +1 -1
  12. package/dist/core/services/menu-service.d.ts +17 -0
  13. package/dist/core/services/menu-service.d.ts.map +1 -0
  14. package/dist/core/services/widget-service.d.ts +4 -0
  15. package/dist/core/services/widget-service.d.ts.map +1 -1
  16. package/dist/core/types/index.d.ts +1 -1
  17. package/dist/core/types/index.d.ts.map +1 -1
  18. package/dist/framework.js +220 -210
  19. package/dist/{index-CrxFDC2b.js → index-3ySdd-mG.js} +1 -1
  20. package/dist/{index-B1YR_MYV.js → index-B-nvqNbp.js} +1 -1
  21. package/dist/{index-xLYzNPa7.js → index-BQF2-UMe.js} +1 -1
  22. package/dist/{index-BBYyHeYA.js → index-BXlxP2d2.js} +1 -1
  23. package/dist/{index-Cf9Tz1ql.js → index-C7P-aBjd.js} +1 -1
  24. package/dist/{index-8LELHzw9.js → index-CO_2IshF.js} +1 -1
  25. package/dist/{index-BA98L1jI.js → index-CfyFpaKq.js} +1 -1
  26. package/dist/{index-DVljTjbf.js → index-Ci23AX3j.js} +1 -1
  27. package/dist/{index-D1JchciU.js → index-CyuFXG83.js} +1 -1
  28. package/dist/{index-CWKrD2Cd.js → index-D1rpRTKf.js} +1 -1
  29. package/dist/{index-BAeTsi-X.js → index-DLxTAT7x.js} +1 -1
  30. package/dist/{index-BuO5ByG9.js → index-DOVhosAY.js} +1 -1
  31. package/dist/{index-DLtsQ_PJ.js → index-DZAq0B3U.js} +23780 -23339
  32. package/dist/{index-BrUitdDo.js → index-DtkJ7xTB.js} +1 -1
  33. package/dist/{index-9lJxZE5w.js → index-DvGVm1rK.js} +1 -1
  34. package/dist/{index-RwX3kiZh.js → index-EDF1MDtU.js} +1 -1
  35. package/dist/{index-CJ5I7vTn.js → index-LjqdX6jw.js} +1 -1
  36. package/dist/index.css +1 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/injection-keys.d.ts +2 -0
  39. package/dist/injection-keys.d.ts.map +1 -1
  40. package/dist/shared/components/draggable-dashboard/DraggableDashboard.vue.d.ts +2 -0
  41. package/dist/shared/components/draggable-dashboard/DraggableDashboard.vue.d.ts.map +1 -1
  42. package/dist/shared/components/draggable-dashboard/composables/useCellSizeCalculator.d.ts +25 -0
  43. package/dist/shared/components/draggable-dashboard/composables/useCellSizeCalculator.d.ts.map +1 -0
  44. package/dist/shared/components/draggable-dashboard/composables/useCollisionDetection.d.ts +27 -0
  45. package/dist/shared/components/draggable-dashboard/composables/useCollisionDetection.d.ts.map +1 -0
  46. package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts +22 -0
  47. package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts.map +1 -1
  48. package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts +12 -4
  49. package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts.map +1 -1
  50. package/dist/shared/components/draggable-dashboard/composables/useDragClone.d.ts +15 -0
  51. package/dist/shared/components/draggable-dashboard/composables/useDragClone.d.ts.map +1 -0
  52. package/dist/shared/components/draggable-dashboard/composables/useEventCoordinates.d.ts +33 -0
  53. package/dist/shared/components/draggable-dashboard/composables/useEventCoordinates.d.ts.map +1 -0
  54. package/dist/shared/components/draggable-dashboard/composables/useGridPosition.d.ts +57 -0
  55. package/dist/shared/components/draggable-dashboard/composables/useGridPosition.d.ts.map +1 -0
  56. package/dist/shared/components/draggable-dashboard/composables/useGridSystem.d.ts +22 -0
  57. package/dist/shared/components/draggable-dashboard/composables/useGridSystem.d.ts.map +1 -0
  58. package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts +19 -0
  59. package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts.map +1 -0
  60. package/dist/shared/components/draggable-dashboard/composables/useResizeObserver.d.ts +18 -0
  61. package/dist/shared/components/draggable-dashboard/composables/useResizeObserver.d.ts.map +1 -0
  62. package/dist/shared/components/draggable-dashboard/composables/useWidgetLayout.d.ts +14 -0
  63. package/dist/shared/components/draggable-dashboard/composables/useWidgetLayout.d.ts.map +1 -0
  64. package/dist/shared/components/draggable-dashboard/composables/useWidgetStyles.d.ts +21 -0
  65. package/dist/shared/components/draggable-dashboard/composables/useWidgetStyles.d.ts.map +1 -0
  66. package/dist/shared/components/draggable-dashboard/types.d.ts +5 -1
  67. package/dist/shared/components/draggable-dashboard/types.d.ts.map +1 -1
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/dist/ui/components/atoms/vc-icon/icons/FulfillmentCentersIcon.vue.d.ts +18 -0
  70. package/dist/ui/components/atoms/vc-icon/icons/FulfillmentCentersIcon.vue.d.ts.map +1 -0
  71. package/dist/ui/components/atoms/vc-icon/icons/OffersIcon.vue.d.ts +18 -0
  72. package/dist/ui/components/atoms/vc-icon/icons/OffersIcon.vue.d.ts.map +1 -0
  73. package/dist/ui/components/atoms/vc-icon/icons/OrdersIcon.vue.d.ts +18 -0
  74. package/dist/ui/components/atoms/vc-icon/icons/OrdersIcon.vue.d.ts.map +1 -0
  75. package/dist/ui/components/atoms/vc-icon/icons/PeopleIcon.vue.d.ts +18 -0
  76. package/dist/ui/components/atoms/vc-icon/icons/PeopleIcon.vue.d.ts.map +1 -0
  77. package/dist/ui/components/atoms/vc-icon/icons/ProductsIcon.vue.d.ts +18 -0
  78. package/dist/ui/components/atoms/vc-icon/icons/ProductsIcon.vue.d.ts.map +1 -0
  79. package/dist/ui/components/atoms/vc-icon/icons/ProfileIcon.vue.d.ts +18 -0
  80. package/dist/ui/components/atoms/vc-icon/icons/ProfileIcon.vue.d.ts.map +1 -0
  81. package/dist/ui/components/atoms/vc-icon/icons/index.d.ts +6 -0
  82. package/dist/ui/components/atoms/vc-icon/icons/index.d.ts.map +1 -1
  83. package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts.map +1 -1
  84. package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/components/app-bar-button/app-bar-button.vue.d.ts +1 -0
  85. package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/components/app-bar-button/app-bar-button.vue.d.ts.map +1 -1
  86. 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
  87. 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
  88. package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
  89. package/dist/ui/components/organisms/vc-table/vc-table.vue.d.ts.map +1 -1
  90. package/package.json +4 -4
  91. package/shared/components/draggable-dashboard/DraggableDashboard.vue +114 -148
  92. package/shared/components/draggable-dashboard/composables/useCellSizeCalculator.ts +121 -0
  93. package/shared/components/draggable-dashboard/composables/useCollisionDetection.ts +219 -0
  94. package/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.ts +126 -331
  95. package/shared/components/draggable-dashboard/composables/useDashboardGrid.ts +74 -220
  96. package/shared/components/draggable-dashboard/composables/useDragClone.ts +97 -0
  97. package/shared/components/draggable-dashboard/composables/useEventCoordinates.ts +91 -0
  98. package/shared/components/draggable-dashboard/composables/useGridPosition.ts +150 -0
  99. package/shared/components/draggable-dashboard/composables/useGridSystem.ts +169 -0
  100. package/shared/components/draggable-dashboard/composables/useLayoutPersistence.ts +89 -0
  101. package/shared/components/draggable-dashboard/composables/useResizeObserver.ts +105 -0
  102. package/shared/components/draggable-dashboard/composables/useWidgetLayout.ts +264 -0
  103. package/shared/components/draggable-dashboard/composables/useWidgetStyles.ts +120 -0
  104. package/shared/components/draggable-dashboard/types.ts +6 -1
  105. package/shared/components/notification-dropdown/notification-dropdown.vue +1 -0
  106. package/ui/components/atoms/vc-icon/icons/FulfillmentCentersIcon.vue +27 -0
  107. package/ui/components/atoms/vc-icon/icons/OffersIcon.vue +23 -0
  108. package/ui/components/atoms/vc-icon/icons/OrdersIcon.vue +19 -0
  109. package/ui/components/atoms/vc-icon/icons/PeopleIcon.vue +21 -0
  110. package/ui/components/atoms/vc-icon/icons/ProductsIcon.vue +23 -0
  111. package/ui/components/atoms/vc-icon/icons/ProfileIcon.vue +18 -0
  112. package/ui/components/atoms/vc-icon/icons/index.ts +6 -0
  113. package/ui/components/atoms/vc-icon/vc-icon.vue +101 -82
  114. package/ui/components/organisms/vc-app/_internal/vc-app-bar/components/app-bar-button/app-bar-button.vue +10 -3
  115. package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +3 -4
  116. package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue +2 -2
  117. package/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +1 -0
  118. package/ui/components/organisms/vc-app/vc-app.vue +2 -3
  119. 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 "./useDashboardGrid";
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
- // Drag state
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
- // Drag clone
26
- const dragClone = ref<HTMLElement | null>(null);
27
- const initialMousePosition = ref({ x: 0, y: 0 });
28
- const isTouchDevice = ref(false);
29
-
30
- // Get cell size
31
- const calculateCellSize = () => {
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
- // Create widget clone for dragging
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 might be unavailable in requestAnimationFrame
64
+ // Save event data, as it may be unavailable in requestAnimationFrame
256
65
  const coords = getEventCoordinates(event);
257
66
 
258
- // Use requestAnimationFrame for UI update optimization
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 { cellWidth, cellHeight } = calculateCellSize();
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
- // Calculate container offset in window
276
- const gridRectLeft = rect.left;
277
- const gridRectTop = rect.top;
278
-
279
- // Calculate mouse position relative to grid with scroll
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
- // Limit boundaries (minimum 0)
288
- gridX = Math.max(0, gridX);
289
- gridY = Math.max(0, gridY);
80
+ updateDragClonePosition(deltaX, deltaY);
290
81
 
291
- // Right and bottom boundaries depend on widget size and grid dimensions
292
- const gridWidth = GRID_COLUMNS;
293
- const gridHeight = getGridRows?.() || 100; // Use large value if getGridRows is not defined
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
- // Limit position to keep widget within grid boundaries
296
- gridX = Math.min(gridX, gridWidth - draggedWidget.value.size.width);
297
- gridY = Math.min(gridY, gridHeight - draggedWidget.value.size.height);
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
- // Update preview position only if it changed
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 on position change
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
- // Enhanced mouse event handler
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
- // Enhanced touch event handler
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 drag
128
+ // Prevent page scrolling during dragging
320
129
  event.preventDefault();
321
130
 
322
131
  updateDragPosition(event);
323
132
  };
324
133
 
325
- // Enhanced drag end handlers
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 drag finish function
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's a clone, finish dragging smoothly
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 { cellWidth, cellHeight } = calculateCellSize();
157
+ const cellSize = calculateCellSize();
349
158
  const widgetGap = 20; // Widget gap in pixels
350
159
 
351
- // Get current clone position (where user released mouse)
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 position with scroll
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 position inside container with all scrolls
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 move
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 place original widget exactly at clone's position
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 since original is in its place
378
- if (dragClone.value.parentNode) {
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 to apply styles
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 to ensure style changes apply correctly
204
+ }, 20); // Small delay for correct style application
399
205
  } else {
400
- // If original not found, just remove clone and update data
401
- if (dragClone.value.parentNode) {
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, just update data
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 to allow animation to complete
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
- previewPosition.value = null;
436
- displacedWidgets.value.clear();
437
- dragOffset.value = { x: 0, y: 0 };
238
+ resetPosition();
438
239
  }, 50);
439
240
  };
440
241
 
441
- // Enhanced mousedown handler with passive listeners
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
- isTouchDevice.value = "touches" in event;
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 for touch
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
- // Store initial position for drag detection
455
- const initialEvent = getEventCoordinates(event);
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 rect = widgetElement.getBoundingClientRect();
463
- const { cellWidth, cellHeight } = calculateCellSize();
265
+ const cellSize = calculateCellSize();
464
266
 
465
- // Temporary move handler to detect actual drag start
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 drag only if mouse moved more than 3 pixels
474
- if (deltaX > 3 || deltaY > 3) {
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
- // Save exact coordinate offset without rounding
480
- dragOffset.value = {
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
- dragClone.value = createDragClone(element);
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 actual drag handlers
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 up handler to cancel drag detection
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 to detect drag start
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
- // Clean up event handlers when the component is unmounted
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
  }