@strapi/admin 5.27.0 → 5.28.0

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 (81) hide show
  1. package/dist/admin/admin/src/components/DragLayer.js +67 -0
  2. package/dist/admin/admin/src/components/DragLayer.js.map +1 -0
  3. package/dist/admin/admin/src/components/DragLayer.mjs +64 -0
  4. package/dist/admin/admin/src/components/DragLayer.mjs.map +1 -0
  5. package/dist/admin/admin/src/components/GapDropZone.js +292 -0
  6. package/dist/admin/admin/src/components/GapDropZone.js.map +1 -0
  7. package/dist/admin/admin/src/components/GapDropZone.mjs +268 -0
  8. package/dist/admin/admin/src/components/GapDropZone.mjs.map +1 -0
  9. package/dist/admin/admin/src/components/ResizeIndicator.js +353 -0
  10. package/dist/admin/admin/src/components/ResizeIndicator.js.map +1 -0
  11. package/dist/admin/admin/src/components/ResizeIndicator.mjs +332 -0
  12. package/dist/admin/admin/src/components/ResizeIndicator.mjs.map +1 -0
  13. package/dist/admin/admin/src/components/WidgetRoot.js +216 -0
  14. package/dist/admin/admin/src/components/WidgetRoot.js.map +1 -0
  15. package/dist/admin/admin/src/components/WidgetRoot.mjs +195 -0
  16. package/dist/admin/admin/src/components/WidgetRoot.mjs.map +1 -0
  17. package/dist/admin/admin/src/features/Tracking.js.map +1 -1
  18. package/dist/admin/admin/src/features/Tracking.mjs.map +1 -1
  19. package/dist/admin/admin/src/features/Widgets.js +276 -0
  20. package/dist/admin/admin/src/features/Widgets.js.map +1 -0
  21. package/dist/admin/admin/src/features/Widgets.mjs +255 -0
  22. package/dist/admin/admin/src/features/Widgets.mjs.map +1 -0
  23. package/dist/admin/admin/src/hooks/useAPIErrorHandler.js +1 -1
  24. package/dist/admin/admin/src/hooks/useAPIErrorHandler.js.map +1 -1
  25. package/dist/admin/admin/src/hooks/useAPIErrorHandler.mjs +1 -1
  26. package/dist/admin/admin/src/hooks/useAPIErrorHandler.mjs.map +1 -1
  27. package/dist/admin/admin/src/pages/Home/HomePage.js +160 -91
  28. package/dist/admin/admin/src/pages/Home/HomePage.js.map +1 -1
  29. package/dist/admin/admin/src/pages/Home/HomePage.mjs +162 -93
  30. package/dist/admin/admin/src/pages/Home/HomePage.mjs.map +1 -1
  31. package/dist/admin/admin/src/pages/Home/components/AddWidgetModal.js +189 -0
  32. package/dist/admin/admin/src/pages/Home/components/AddWidgetModal.js.map +1 -0
  33. package/dist/admin/admin/src/pages/Home/components/AddWidgetModal.mjs +168 -0
  34. package/dist/admin/admin/src/pages/Home/components/AddWidgetModal.mjs.map +1 -0
  35. package/dist/admin/admin/src/services/homepage.js +11 -4
  36. package/dist/admin/admin/src/services/homepage.js.map +1 -1
  37. package/dist/admin/admin/src/services/homepage.mjs +11 -4
  38. package/dist/admin/admin/src/services/homepage.mjs.map +1 -1
  39. package/dist/admin/admin/src/translations/en.json.js +6 -1
  40. package/dist/admin/admin/src/translations/en.json.js.map +1 -1
  41. package/dist/admin/admin/src/translations/en.json.mjs +6 -1
  42. package/dist/admin/admin/src/translations/en.json.mjs.map +1 -1
  43. package/dist/admin/admin/src/translations/uk.json.js +9 -9
  44. package/dist/admin/admin/src/translations/uk.json.mjs +9 -9
  45. package/dist/admin/admin/src/utils/resizeHandlers.js +109 -0
  46. package/dist/admin/admin/src/utils/resizeHandlers.js.map +1 -0
  47. package/dist/admin/admin/src/utils/resizeHandlers.mjs +100 -0
  48. package/dist/admin/admin/src/utils/resizeHandlers.mjs.map +1 -0
  49. package/dist/admin/admin/src/utils/widgetLayout.js +293 -0
  50. package/dist/admin/admin/src/utils/widgetLayout.js.map +1 -0
  51. package/dist/admin/admin/src/utils/widgetLayout.mjs +273 -0
  52. package/dist/admin/admin/src/utils/widgetLayout.mjs.map +1 -0
  53. package/dist/admin/src/components/DragLayer.d.ts +8 -4
  54. package/dist/admin/src/components/GapDropZone.d.ts +36 -0
  55. package/dist/admin/src/components/ResizeIndicator.d.ts +12 -0
  56. package/dist/admin/src/components/WidgetRoot.d.ts +14 -0
  57. package/dist/admin/src/features/Tracking.d.ts +1 -1
  58. package/dist/admin/src/features/Widgets.d.ts +29 -0
  59. package/dist/admin/src/pages/Home/HomePage.d.ts +4 -5
  60. package/dist/admin/src/pages/Home/components/AddWidgetModal.d.ts +10 -0
  61. package/dist/admin/src/services/homepage.d.ts +3 -3
  62. package/dist/admin/src/utils/resizeHandlers.d.ts +58 -0
  63. package/dist/admin/src/utils/widgetLayout.d.ts +78 -0
  64. package/dist/ee/server/src/controllers/authentication-utils/middlewares.d.ts.map +1 -1
  65. package/dist/server/ee/server/src/controllers/authentication-utils/middlewares.js +4 -2
  66. package/dist/server/ee/server/src/controllers/authentication-utils/middlewares.js.map +1 -1
  67. package/dist/server/ee/server/src/controllers/authentication-utils/middlewares.mjs +4 -2
  68. package/dist/server/ee/server/src/controllers/authentication-utils/middlewares.mjs.map +1 -1
  69. package/dist/server/server/src/bootstrap.js +5 -0
  70. package/dist/server/server/src/bootstrap.js.map +1 -1
  71. package/dist/server/server/src/bootstrap.mjs +5 -0
  72. package/dist/server/server/src/bootstrap.mjs.map +1 -1
  73. package/dist/server/shared/utils/session-auth.js +4 -2
  74. package/dist/server/shared/utils/session-auth.js.map +1 -1
  75. package/dist/server/shared/utils/session-auth.mjs +4 -2
  76. package/dist/server/shared/utils/session-auth.mjs.map +1 -1
  77. package/dist/server/src/bootstrap.d.ts.map +1 -1
  78. package/dist/shared/contracts/homepage.d.ts +8 -4
  79. package/dist/shared/contracts/homepage.d.ts.map +1 -1
  80. package/dist/shared/utils/session-auth.d.ts.map +1 -1
  81. package/package.json +7 -7
@@ -0,0 +1,268 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { Box } from '@strapi/design-system';
4
+ import { useDrop } from 'react-dnd';
5
+ import { styled } from 'styled-components';
6
+ import { calculateWidgetRows, getWidgetGridContainer, getWidgetElement } from '../utils/widgetLayout.mjs';
7
+
8
+ const DROP_ZONE_SIZE = 20;
9
+ const getRowInfo = (row)=>{
10
+ const firstWidgetElement = getWidgetElement(row.widgets[0].uid);
11
+ const lastWidgetElement = getWidgetElement(row.widgets[row.widgets.length - 1].uid);
12
+ const containerElement = getWidgetGridContainer();
13
+ if (!firstWidgetElement || !lastWidgetElement || !containerElement) {
14
+ return null;
15
+ }
16
+ const firstRect = firstWidgetElement.getBoundingClientRect();
17
+ const lastRect = lastWidgetElement.getBoundingClientRect();
18
+ const containerRect = containerElement.getBoundingClientRect();
19
+ return {
20
+ firstWidgetElement,
21
+ lastWidgetElement,
22
+ containerElement,
23
+ firstRect,
24
+ lastRect,
25
+ containerRect,
26
+ rowHeight: Math.max(firstRect.height, lastRect.height),
27
+ rowTop: firstRect.top - containerRect.top
28
+ };
29
+ };
30
+ const addVerticalDropZones = (row, rowInfo, rowIndex)=>{
31
+ if (!rowInfo) return [];
32
+ const { containerRect, rowTop, rowHeight } = rowInfo;
33
+ const widgetCount = row.widgets.length;
34
+ // Get widget positions relative to container
35
+ const widgetPositions = row.widgets.map((widget)=>{
36
+ const element = getWidgetElement(widget.uid);
37
+ if (!element) return null;
38
+ const rect = element.getBoundingClientRect();
39
+ return {
40
+ left: rect.left - containerRect.left,
41
+ width: rect.width
42
+ };
43
+ }).filter((pos)=>pos !== null);
44
+ if (widgetPositions.length !== widgetCount) return [];
45
+ const gapDropZones = [];
46
+ // Always add drop zone before the first widget
47
+ gapDropZones.push({
48
+ insertIndex: row.startIndex,
49
+ position: {
50
+ left: widgetPositions[0].left - DROP_ZONE_SIZE,
51
+ top: rowTop,
52
+ height: rowHeight,
53
+ width: DROP_ZONE_SIZE
54
+ },
55
+ isVisible: true,
56
+ type: 'vertical',
57
+ targetRowIndex: rowIndex
58
+ });
59
+ // Add drop zones between widgets
60
+ widgetPositions.slice(0, -1).forEach((currentWidget, i)=>{
61
+ gapDropZones.push({
62
+ insertIndex: row.startIndex + i + 1,
63
+ position: {
64
+ left: currentWidget.left + currentWidget.width,
65
+ top: rowTop,
66
+ height: rowHeight,
67
+ width: DROP_ZONE_SIZE
68
+ },
69
+ isVisible: true,
70
+ type: 'vertical',
71
+ targetRowIndex: rowIndex
72
+ });
73
+ });
74
+ // Always add drop zone after the last widget
75
+ const lastWidget = widgetPositions[widgetCount - 1];
76
+ gapDropZones.push({
77
+ insertIndex: row.endIndex + 1,
78
+ position: {
79
+ left: lastWidget.left + lastWidget.width,
80
+ top: rowTop,
81
+ height: rowHeight,
82
+ width: DROP_ZONE_SIZE
83
+ },
84
+ isVisible: true,
85
+ type: 'vertical',
86
+ targetRowIndex: rowIndex
87
+ });
88
+ return gapDropZones;
89
+ };
90
+ const addHorizontalDropZones = (row, rowIndex, rowInfo, widgetRows, filteredWidgets)=>{
91
+ if (!rowInfo) return [];
92
+ // Don't show horizontal drop zones if there's only one row with one widget
93
+ if (widgetRows.length === 1 && row.widgets.length === 1) return [];
94
+ const { containerRect } = rowInfo;
95
+ const containerWidth = containerRect.width;
96
+ const horizontalDropZoneHeight = DROP_ZONE_SIZE;
97
+ const gapDropZones = [];
98
+ // Add horizontal drop zone above the first row
99
+ if (rowIndex === 0) {
100
+ const firstRowRect = rowInfo.firstRect;
101
+ const firstRowTop = firstRowRect.top - containerRect.top;
102
+ gapDropZones.push({
103
+ insertIndex: 0,
104
+ position: {
105
+ left: 0,
106
+ top: firstRowTop - horizontalDropZoneHeight,
107
+ height: horizontalDropZoneHeight,
108
+ width: containerWidth
109
+ },
110
+ isVisible: true,
111
+ type: 'horizontal',
112
+ isHorizontalDrop: true
113
+ });
114
+ }
115
+ // Add horizontal drop zone below the current row (between rows or after last row)
116
+ if (rowIndex < widgetRows.length - 1) {
117
+ // Between rows: position above the next row
118
+ const nextRow = widgetRows[rowIndex + 1];
119
+ const nextRowFirstWidgetElement = getWidgetElement(nextRow.widgets[0].uid);
120
+ if (nextRowFirstWidgetElement) {
121
+ const nextRowRect = nextRowFirstWidgetElement.getBoundingClientRect();
122
+ const nextRowTop = nextRowRect.top - containerRect.top;
123
+ gapDropZones.push({
124
+ insertIndex: row.endIndex + 1,
125
+ position: {
126
+ left: 0,
127
+ top: nextRowTop - horizontalDropZoneHeight,
128
+ height: horizontalDropZoneHeight,
129
+ width: containerWidth
130
+ },
131
+ isVisible: true,
132
+ type: 'horizontal',
133
+ isHorizontalDrop: true
134
+ });
135
+ }
136
+ } else {
137
+ // After the last row: position below the current row
138
+ const lastRowRect = rowInfo.lastRect;
139
+ const lastRowBottom = lastRowRect.bottom - containerRect.top;
140
+ gapDropZones.push({
141
+ insertIndex: filteredWidgets.length,
142
+ position: {
143
+ left: 0,
144
+ top: lastRowBottom,
145
+ height: horizontalDropZoneHeight,
146
+ width: containerWidth
147
+ },
148
+ isVisible: true,
149
+ type: 'horizontal',
150
+ isHorizontalDrop: true
151
+ });
152
+ }
153
+ return gapDropZones;
154
+ };
155
+ const GapDropZoneManager = ({ filteredWidgets, columnWidths, draggedWidgetId, moveWidget })=>{
156
+ const [positions, setPositions] = React.useState([]);
157
+ // Calculate widget rows
158
+ const widgetRows = React.useMemo(()=>{
159
+ return calculateWidgetRows(filteredWidgets, columnWidths);
160
+ }, [
161
+ filteredWidgets,
162
+ columnWidths
163
+ ]);
164
+ // Main function to calculate GapDropZone positions
165
+ const calculateGapDropZonePositions = React.useCallback(()=>{
166
+ const gapDropZones = [];
167
+ // Find which row the dragged widget is from
168
+ const draggedWidgetRow = draggedWidgetId ? widgetRows.find((row)=>row.widgets.some((widget)=>widget.uid === draggedWidgetId)) : null;
169
+ widgetRows.forEach((row, rowIndex)=>{
170
+ const rowInfo = getRowInfo(row);
171
+ if (!rowInfo) return;
172
+ const widgetCount = row.widgets.length;
173
+ // Determine if we should show vertical drop zones for this row
174
+ const isDraggingFromThisRow = draggedWidgetRow && draggedWidgetRow === row;
175
+ const isDraggingFromAnotherRow = draggedWidgetRow && draggedWidgetRow !== row;
176
+ const canAcceptMoreWidgets = widgetCount < 3;
177
+ const shouldShowVerticalDropZones = isDraggingFromThisRow || isDraggingFromAnotherRow && canAcceptMoreWidgets;
178
+ // Add vertical drop zones based on widget count
179
+ if (shouldShowVerticalDropZones) {
180
+ const verticalDropZones = addVerticalDropZones(row, rowInfo, rowIndex);
181
+ gapDropZones.push(...verticalDropZones);
182
+ }
183
+ // Add horizontal drop zones
184
+ const horizontalDropZones = addHorizontalDropZones(row, rowIndex, rowInfo, widgetRows, filteredWidgets);
185
+ gapDropZones.push(...horizontalDropZones);
186
+ });
187
+ return gapDropZones;
188
+ }, [
189
+ widgetRows,
190
+ draggedWidgetId,
191
+ filteredWidgets
192
+ ]);
193
+ React.useLayoutEffect(()=>{
194
+ const updatePositions = ()=>{
195
+ const newPositions = calculateGapDropZonePositions();
196
+ setPositions(newPositions);
197
+ };
198
+ updatePositions();
199
+ // Update positions on container resize using ResizeObserver
200
+ const containerElement = getWidgetGridContainer();
201
+ if (!containerElement) return;
202
+ const resizeObserver = new ResizeObserver(()=>{
203
+ updatePositions();
204
+ });
205
+ resizeObserver.observe(containerElement);
206
+ return ()=>{
207
+ resizeObserver.disconnect();
208
+ };
209
+ }, [
210
+ calculateGapDropZonePositions
211
+ ]);
212
+ return positions.map((gapDropZone)=>/*#__PURE__*/ jsx(GapDropZone, {
213
+ insertIndex: gapDropZone.insertIndex,
214
+ position: gapDropZone.position,
215
+ isVisible: gapDropZone.isVisible,
216
+ type: gapDropZone.type,
217
+ moveWidget: moveWidget,
218
+ targetRowIndex: gapDropZone.targetRowIndex
219
+ }, `gap-drop-zone-${gapDropZone.type}-${gapDropZone.insertIndex}-${gapDropZone.targetRowIndex ?? 'no-row'}`));
220
+ };
221
+ const GapDropZoneContainer = styled(Box)`
222
+ background-color: ${({ $isOver, theme })=>$isOver ? `${theme.colors.primary100}` : 'transparent'};
223
+ border: ${({ $isOver, theme })=>$isOver ? `2px solid ${theme.colors.primary500}` : '2px solid transparent'};
224
+ opacity: ${({ $isOver })=>$isOver ? 1 : 0.6};
225
+ transition: all 0.2s ease-in-out;
226
+ cursor: pointer;
227
+ border-radius: ${({ theme })=>theme.borderRadius};
228
+ display: flex;
229
+ align-items: center;
230
+ justify-content: center;
231
+ position: absolute;
232
+ top: 0;
233
+ bottom: 0;
234
+ width: ${DROP_ZONE_SIZE}px;
235
+ z-index: 1;
236
+ `;
237
+ const GapDropZone = ({ insertIndex, position, isVisible, type, moveWidget, targetRowIndex })=>{
238
+ const isHorizontalDrop = type === 'horizontal';
239
+ const [{ isOver }, drop] = useDrop(()=>({
240
+ accept: 'widget',
241
+ drop: (item)=>{
242
+ moveWidget(item.id, insertIndex, targetRowIndex, isHorizontalDrop);
243
+ },
244
+ collect: (monitor)=>({
245
+ isOver: monitor.isOver()
246
+ })
247
+ }), [
248
+ insertIndex,
249
+ isHorizontalDrop,
250
+ moveWidget,
251
+ targetRowIndex
252
+ ]);
253
+ if (!isVisible) {
254
+ return null;
255
+ }
256
+ return /*#__PURE__*/ jsx(GapDropZoneContainer, {
257
+ ref: drop,
258
+ $isOver: isOver,
259
+ style: {
260
+ transform: `translate(${position.left}px, ${position.top}px)`,
261
+ height: `${position.height}px`,
262
+ width: `${position.width}px`
263
+ }
264
+ });
265
+ };
266
+
267
+ export { DROP_ZONE_SIZE, GapDropZoneManager, addHorizontalDropZones, addVerticalDropZones };
268
+ //# sourceMappingURL=GapDropZone.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GapDropZone.mjs","sources":["../../../../../admin/src/components/GapDropZone.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Box } from '@strapi/design-system';\n// TODO: use @dnd-kit/core instead\nimport { useDrop } from 'react-dnd';\nimport { styled } from 'styled-components';\n\nimport {\n calculateWidgetRows,\n type WidgetRow,\n getWidgetElement,\n getWidgetGridContainer,\n} from '../utils/widgetLayout';\n\nimport type { WidgetWithUID } from '../core/apis/Widgets';\n\nexport const DROP_ZONE_SIZE = 20;\n\nexport interface GapDropZonePosition {\n insertIndex: number;\n position: { left: number; top: number; height: number; width: number };\n isVisible: boolean;\n type: 'vertical' | 'horizontal';\n isHorizontalDrop?: boolean;\n targetRowIndex?: number;\n}\n\ninterface GapDropZoneManagerProps {\n filteredWidgets: WidgetWithUID[];\n columnWidths: Record<string, number>;\n draggedWidgetId?: string;\n moveWidget: (id: string, to: number, targetRowIndex?: number, isHorizontalDrop?: boolean) => void;\n}\n\nconst getRowInfo = (row: WidgetRow) => {\n const firstWidgetElement = getWidgetElement(row.widgets[0].uid);\n const lastWidgetElement = getWidgetElement(row.widgets[row.widgets.length - 1].uid);\n const containerElement = getWidgetGridContainer();\n\n if (!firstWidgetElement || !lastWidgetElement || !containerElement) {\n return null;\n }\n\n const firstRect = firstWidgetElement.getBoundingClientRect();\n const lastRect = lastWidgetElement.getBoundingClientRect();\n const containerRect = containerElement.getBoundingClientRect();\n\n return {\n firstWidgetElement,\n lastWidgetElement,\n containerElement,\n firstRect,\n lastRect,\n containerRect,\n rowHeight: Math.max(firstRect.height, lastRect.height),\n rowTop: firstRect.top - containerRect.top,\n };\n};\n\nexport const addVerticalDropZones = (\n row: WidgetRow,\n rowInfo: ReturnType<typeof getRowInfo>,\n rowIndex: number\n): GapDropZonePosition[] => {\n if (!rowInfo) return [];\n\n const { containerRect, rowTop, rowHeight } = rowInfo;\n const widgetCount = row.widgets.length;\n\n // Get widget positions relative to container\n const widgetPositions = row.widgets\n .map((widget) => {\n const element = getWidgetElement(widget.uid);\n if (!element) return null;\n\n const rect = element.getBoundingClientRect();\n return {\n left: rect.left - containerRect.left,\n width: rect.width,\n };\n })\n .filter((pos): pos is NonNullable<typeof pos> => pos !== null);\n\n if (widgetPositions.length !== widgetCount) return [];\n\n const gapDropZones: GapDropZonePosition[] = [];\n\n // Always add drop zone before the first widget\n gapDropZones.push({\n insertIndex: row.startIndex,\n position: {\n left: widgetPositions[0].left - DROP_ZONE_SIZE,\n top: rowTop,\n height: rowHeight,\n width: DROP_ZONE_SIZE,\n },\n isVisible: true,\n type: 'vertical',\n targetRowIndex: rowIndex,\n });\n\n // Add drop zones between widgets\n widgetPositions.slice(0, -1).forEach((currentWidget, i) => {\n gapDropZones.push({\n insertIndex: row.startIndex + i + 1,\n position: {\n left: currentWidget.left + currentWidget.width,\n top: rowTop,\n height: rowHeight,\n width: DROP_ZONE_SIZE,\n },\n isVisible: true,\n type: 'vertical',\n targetRowIndex: rowIndex,\n });\n });\n\n // Always add drop zone after the last widget\n const lastWidget = widgetPositions[widgetCount - 1];\n gapDropZones.push({\n insertIndex: row.endIndex + 1,\n position: {\n left: lastWidget.left + lastWidget.width,\n top: rowTop,\n height: rowHeight,\n width: DROP_ZONE_SIZE,\n },\n isVisible: true,\n type: 'vertical',\n targetRowIndex: rowIndex,\n });\n\n return gapDropZones;\n};\n\nexport const addHorizontalDropZones = (\n row: WidgetRow,\n rowIndex: number,\n rowInfo: ReturnType<typeof getRowInfo>,\n widgetRows: WidgetRow[],\n filteredWidgets: WidgetWithUID[]\n): GapDropZonePosition[] => {\n if (!rowInfo) return [];\n\n // Don't show horizontal drop zones if there's only one row with one widget\n if (widgetRows.length === 1 && row.widgets.length === 1) return [];\n\n const { containerRect } = rowInfo;\n const containerWidth = containerRect.width;\n const horizontalDropZoneHeight = DROP_ZONE_SIZE;\n\n const gapDropZones: GapDropZonePosition[] = [];\n\n // Add horizontal drop zone above the first row\n if (rowIndex === 0) {\n const firstRowRect = rowInfo.firstRect;\n const firstRowTop = firstRowRect.top - containerRect.top;\n\n gapDropZones.push({\n insertIndex: 0,\n position: {\n left: 0,\n top: firstRowTop - horizontalDropZoneHeight,\n height: horizontalDropZoneHeight,\n width: containerWidth,\n },\n isVisible: true,\n type: 'horizontal',\n isHorizontalDrop: true,\n });\n }\n\n // Add horizontal drop zone below the current row (between rows or after last row)\n if (rowIndex < widgetRows.length - 1) {\n // Between rows: position above the next row\n const nextRow = widgetRows[rowIndex + 1];\n const nextRowFirstWidgetElement = getWidgetElement(nextRow.widgets[0].uid);\n\n if (nextRowFirstWidgetElement) {\n const nextRowRect = nextRowFirstWidgetElement.getBoundingClientRect();\n const nextRowTop = nextRowRect.top - containerRect.top;\n\n gapDropZones.push({\n insertIndex: row.endIndex + 1,\n position: {\n left: 0,\n top: nextRowTop - horizontalDropZoneHeight,\n height: horizontalDropZoneHeight,\n width: containerWidth,\n },\n isVisible: true,\n type: 'horizontal',\n isHorizontalDrop: true,\n });\n }\n } else {\n // After the last row: position below the current row\n const lastRowRect = rowInfo.lastRect;\n const lastRowBottom = lastRowRect.bottom - containerRect.top;\n\n gapDropZones.push({\n insertIndex: filteredWidgets.length,\n position: {\n left: 0,\n top: lastRowBottom,\n height: horizontalDropZoneHeight,\n width: containerWidth,\n },\n isVisible: true,\n type: 'horizontal',\n isHorizontalDrop: true,\n });\n }\n\n return gapDropZones;\n};\n\nexport const GapDropZoneManager = ({\n filteredWidgets,\n columnWidths,\n draggedWidgetId,\n moveWidget,\n}: GapDropZoneManagerProps) => {\n const [positions, setPositions] = React.useState<GapDropZonePosition[]>([]);\n\n // Calculate widget rows\n const widgetRows = React.useMemo(() => {\n return calculateWidgetRows(filteredWidgets, columnWidths);\n }, [filteredWidgets, columnWidths]);\n\n // Main function to calculate GapDropZone positions\n const calculateGapDropZonePositions = React.useCallback(() => {\n const gapDropZones: GapDropZonePosition[] = [];\n\n // Find which row the dragged widget is from\n const draggedWidgetRow = draggedWidgetId\n ? widgetRows.find((row) => row.widgets.some((widget) => widget.uid === draggedWidgetId))\n : null;\n\n widgetRows.forEach((row, rowIndex) => {\n const rowInfo = getRowInfo(row);\n if (!rowInfo) return;\n\n const widgetCount = row.widgets.length;\n\n // Determine if we should show vertical drop zones for this row\n const isDraggingFromThisRow = draggedWidgetRow && draggedWidgetRow === row;\n const isDraggingFromAnotherRow = draggedWidgetRow && draggedWidgetRow !== row;\n const canAcceptMoreWidgets = widgetCount < 3;\n\n const shouldShowVerticalDropZones =\n isDraggingFromThisRow || (isDraggingFromAnotherRow && canAcceptMoreWidgets);\n\n // Add vertical drop zones based on widget count\n if (shouldShowVerticalDropZones) {\n const verticalDropZones = addVerticalDropZones(row, rowInfo, rowIndex);\n gapDropZones.push(...verticalDropZones);\n }\n\n // Add horizontal drop zones\n const horizontalDropZones = addHorizontalDropZones(\n row,\n rowIndex,\n rowInfo,\n widgetRows,\n filteredWidgets\n );\n gapDropZones.push(...horizontalDropZones);\n });\n\n return gapDropZones;\n }, [widgetRows, draggedWidgetId, filteredWidgets]);\n\n React.useLayoutEffect(() => {\n const updatePositions = () => {\n const newPositions = calculateGapDropZonePositions();\n setPositions(newPositions);\n };\n\n updatePositions();\n\n // Update positions on container resize using ResizeObserver\n const containerElement = getWidgetGridContainer();\n if (!containerElement) return;\n\n const resizeObserver = new ResizeObserver(() => {\n updatePositions();\n });\n\n resizeObserver.observe(containerElement);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [calculateGapDropZonePositions]);\n\n return positions.map((gapDropZone) => (\n <GapDropZone\n key={`gap-drop-zone-${gapDropZone.type}-${gapDropZone.insertIndex}-${gapDropZone.targetRowIndex ?? 'no-row'}`}\n insertIndex={gapDropZone.insertIndex}\n position={gapDropZone.position}\n isVisible={gapDropZone.isVisible}\n type={gapDropZone.type}\n moveWidget={moveWidget}\n targetRowIndex={gapDropZone.targetRowIndex}\n />\n ));\n};\n\ninterface GapDropZoneProps {\n insertIndex: number;\n position: { left: number; top: number; height: number; width: number };\n isVisible: boolean;\n type: 'vertical' | 'horizontal';\n moveWidget: (id: string, to: number, targetRowIndex?: number, isHorizontalDrop?: boolean) => void;\n targetRowIndex?: number;\n}\n\nconst GapDropZoneContainer = styled(Box)<{\n $isOver: boolean;\n}>`\n background-color: ${({ $isOver, theme }) =>\n $isOver ? `${theme.colors.primary100}` : 'transparent'};\n border: ${({ $isOver, theme }) =>\n $isOver ? `2px solid ${theme.colors.primary500}` : '2px solid transparent'};\n opacity: ${({ $isOver }) => ($isOver ? 1 : 0.6)};\n transition: all 0.2s ease-in-out;\n cursor: pointer;\n border-radius: ${({ theme }) => theme.borderRadius};\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n bottom: 0;\n width: ${DROP_ZONE_SIZE}px;\n z-index: 1;\n`;\n\nconst GapDropZone = ({\n insertIndex,\n position,\n isVisible,\n type,\n moveWidget,\n targetRowIndex,\n}: GapDropZoneProps) => {\n const isHorizontalDrop = type === 'horizontal';\n\n const [{ isOver }, drop] = useDrop(\n () => ({\n accept: 'widget',\n drop: (item: { id: string }) => {\n moveWidget(item.id, insertIndex, targetRowIndex, isHorizontalDrop);\n },\n collect: (monitor) => ({\n isOver: monitor.isOver(),\n }),\n }),\n [insertIndex, isHorizontalDrop, moveWidget, targetRowIndex]\n );\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <GapDropZoneContainer\n ref={drop}\n $isOver={isOver}\n style={{\n transform: `translate(${position.left}px, ${position.top}px)`,\n height: `${position.height}px`,\n width: `${position.width}px`,\n }}\n />\n );\n};\n"],"names":["DROP_ZONE_SIZE","getRowInfo","row","firstWidgetElement","getWidgetElement","widgets","uid","lastWidgetElement","length","containerElement","getWidgetGridContainer","firstRect","getBoundingClientRect","lastRect","containerRect","rowHeight","Math","max","height","rowTop","top","addVerticalDropZones","rowInfo","rowIndex","widgetCount","widgetPositions","map","widget","element","rect","left","width","filter","pos","gapDropZones","push","insertIndex","startIndex","position","isVisible","type","targetRowIndex","slice","forEach","currentWidget","i","lastWidget","endIndex","addHorizontalDropZones","widgetRows","filteredWidgets","containerWidth","horizontalDropZoneHeight","firstRowRect","firstRowTop","isHorizontalDrop","nextRow","nextRowFirstWidgetElement","nextRowRect","nextRowTop","lastRowRect","lastRowBottom","bottom","GapDropZoneManager","columnWidths","draggedWidgetId","moveWidget","positions","setPositions","React","useState","useMemo","calculateWidgetRows","calculateGapDropZonePositions","useCallback","draggedWidgetRow","find","some","isDraggingFromThisRow","isDraggingFromAnotherRow","canAcceptMoreWidgets","shouldShowVerticalDropZones","verticalDropZones","horizontalDropZones","useLayoutEffect","updatePositions","newPositions","resizeObserver","ResizeObserver","observe","disconnect","gapDropZone","_jsx","GapDropZone","GapDropZoneContainer","styled","Box","$isOver","theme","colors","primary100","primary500","borderRadius","isOver","drop","useDrop","accept","item","id","collect","monitor","ref","style","transform"],"mappings":";;;;;;;AAgBO,MAAMA,iBAAiB;AAkB9B,MAAMC,aAAa,CAACC,GAAAA,GAAAA;AAClB,IAAA,MAAMC,qBAAqBC,gBAAiBF,CAAAA,GAAAA,CAAIG,OAAO,CAAC,CAAA,CAAE,CAACC,GAAG,CAAA;AAC9D,IAAA,MAAMC,iBAAoBH,GAAAA,gBAAAA,CAAiBF,GAAIG,CAAAA,OAAO,CAACH,GAAAA,CAAIG,OAAO,CAACG,MAAM,GAAG,CAAE,CAAA,CAACF,GAAG,CAAA;AAClF,IAAA,MAAMG,gBAAmBC,GAAAA,sBAAAA,EAAAA;AAEzB,IAAA,IAAI,CAACP,kBAAAA,IAAsB,CAACI,iBAAAA,IAAqB,CAACE,gBAAkB,EAAA;QAClE,OAAO,IAAA;AACT;IAEA,MAAME,SAAAA,GAAYR,mBAAmBS,qBAAqB,EAAA;IAC1D,MAAMC,QAAAA,GAAWN,kBAAkBK,qBAAqB,EAAA;IACxD,MAAME,aAAAA,GAAgBL,iBAAiBG,qBAAqB,EAAA;IAE5D,OAAO;AACLT,QAAAA,kBAAAA;AACAI,QAAAA,iBAAAA;AACAE,QAAAA,gBAAAA;AACAE,QAAAA,SAAAA;AACAE,QAAAA,QAAAA;AACAC,QAAAA,aAAAA;AACAC,QAAAA,SAAAA,EAAWC,KAAKC,GAAG,CAACN,UAAUO,MAAM,EAAEL,SAASK,MAAM,CAAA;AACrDC,QAAAA,MAAAA,EAAQR,SAAUS,CAAAA,GAAG,GAAGN,aAAAA,CAAcM;AACxC,KAAA;AACF,CAAA;AAEaC,MAAAA,oBAAAA,GAAuB,CAClCnB,GAAAA,EACAoB,OACAC,EAAAA,QAAAA,GAAAA;IAEA,IAAI,CAACD,OAAS,EAAA,OAAO,EAAE;AAEvB,IAAA,MAAM,EAAER,aAAa,EAAEK,MAAM,EAAEJ,SAAS,EAAE,GAAGO,OAAAA;AAC7C,IAAA,MAAME,WAActB,GAAAA,GAAAA,CAAIG,OAAO,CAACG,MAAM;;AAGtC,IAAA,MAAMiB,kBAAkBvB,GAAIG,CAAAA,OAAO,CAChCqB,GAAG,CAAC,CAACC,MAAAA,GAAAA;QACJ,MAAMC,OAAAA,GAAUxB,gBAAiBuB,CAAAA,MAAAA,CAAOrB,GAAG,CAAA;QAC3C,IAAI,CAACsB,SAAS,OAAO,IAAA;QAErB,MAAMC,IAAAA,GAAOD,QAAQhB,qBAAqB,EAAA;QAC1C,OAAO;AACLkB,YAAAA,IAAAA,EAAMD,IAAKC,CAAAA,IAAI,GAAGhB,aAAAA,CAAcgB,IAAI;AACpCC,YAAAA,KAAAA,EAAOF,KAAKE;AACd,SAAA;AACF,KAAA,CAAA,CACCC,MAAM,CAAC,CAACC,GAAAA,GAAwCA,GAAQ,KAAA,IAAA,CAAA;AAE3D,IAAA,IAAIR,eAAgBjB,CAAAA,MAAM,KAAKgB,WAAAA,EAAa,OAAO,EAAE;AAErD,IAAA,MAAMU,eAAsC,EAAE;;AAG9CA,IAAAA,YAAAA,CAAaC,IAAI,CAAC;AAChBC,QAAAA,WAAAA,EAAalC,IAAImC,UAAU;QAC3BC,QAAU,EAAA;AACRR,YAAAA,IAAAA,EAAML,eAAe,CAAC,CAAE,CAAA,CAACK,IAAI,GAAG9B,cAAAA;YAChCoB,GAAKD,EAAAA,MAAAA;YACLD,MAAQH,EAAAA,SAAAA;YACRgB,KAAO/B,EAAAA;AACT,SAAA;QACAuC,SAAW,EAAA,IAAA;QACXC,IAAM,EAAA,UAAA;QACNC,cAAgBlB,EAAAA;AAClB,KAAA,CAAA;;IAGAE,eAAgBiB,CAAAA,KAAK,CAAC,CAAG,EAAA,CAAC,GAAGC,OAAO,CAAC,CAACC,aAAeC,EAAAA,CAAAA,GAAAA;AACnDX,QAAAA,YAAAA,CAAaC,IAAI,CAAC;YAChBC,WAAalC,EAAAA,GAAAA,CAAImC,UAAU,GAAGQ,CAAI,GAAA,CAAA;YAClCP,QAAU,EAAA;AACRR,gBAAAA,IAAAA,EAAMc,aAAcd,CAAAA,IAAI,GAAGc,aAAAA,CAAcb,KAAK;gBAC9CX,GAAKD,EAAAA,MAAAA;gBACLD,MAAQH,EAAAA,SAAAA;gBACRgB,KAAO/B,EAAAA;AACT,aAAA;YACAuC,SAAW,EAAA,IAAA;YACXC,IAAM,EAAA,UAAA;YACNC,cAAgBlB,EAAAA;AAClB,SAAA,CAAA;AACF,KAAA,CAAA;;AAGA,IAAA,MAAMuB,UAAarB,GAAAA,eAAe,CAACD,WAAAA,GAAc,CAAE,CAAA;AACnDU,IAAAA,YAAAA,CAAaC,IAAI,CAAC;QAChBC,WAAalC,EAAAA,GAAAA,CAAI6C,QAAQ,GAAG,CAAA;QAC5BT,QAAU,EAAA;AACRR,YAAAA,IAAAA,EAAMgB,UAAWhB,CAAAA,IAAI,GAAGgB,UAAAA,CAAWf,KAAK;YACxCX,GAAKD,EAAAA,MAAAA;YACLD,MAAQH,EAAAA,SAAAA;YACRgB,KAAO/B,EAAAA;AACT,SAAA;QACAuC,SAAW,EAAA,IAAA;QACXC,IAAM,EAAA,UAAA;QACNC,cAAgBlB,EAAAA;AAClB,KAAA,CAAA;IAEA,OAAOW,YAAAA;AACT;MAEac,sBAAyB,GAAA,CACpC9C,GACAqB,EAAAA,QAAAA,EACAD,SACA2B,UACAC,EAAAA,eAAAA,GAAAA;IAEA,IAAI,CAAC5B,OAAS,EAAA,OAAO,EAAE;;IAGvB,IAAI2B,UAAAA,CAAWzC,MAAM,KAAK,CAAKN,IAAAA,GAAAA,CAAIG,OAAO,CAACG,MAAM,KAAK,CAAG,EAAA,OAAO,EAAE;IAElE,MAAM,EAAEM,aAAa,EAAE,GAAGQ,OAAAA;IAC1B,MAAM6B,cAAAA,GAAiBrC,cAAciB,KAAK;AAC1C,IAAA,MAAMqB,wBAA2BpD,GAAAA,cAAAA;AAEjC,IAAA,MAAMkC,eAAsC,EAAE;;AAG9C,IAAA,IAAIX,aAAa,CAAG,EAAA;QAClB,MAAM8B,YAAAA,GAAe/B,QAAQX,SAAS;AACtC,QAAA,MAAM2C,WAAcD,GAAAA,YAAAA,CAAajC,GAAG,GAAGN,cAAcM,GAAG;AAExDc,QAAAA,YAAAA,CAAaC,IAAI,CAAC;YAChBC,WAAa,EAAA,CAAA;YACbE,QAAU,EAAA;gBACRR,IAAM,EAAA,CAAA;AACNV,gBAAAA,GAAAA,EAAKkC,WAAcF,GAAAA,wBAAAA;gBACnBlC,MAAQkC,EAAAA,wBAAAA;gBACRrB,KAAOoB,EAAAA;AACT,aAAA;YACAZ,SAAW,EAAA,IAAA;YACXC,IAAM,EAAA,YAAA;YACNe,gBAAkB,EAAA;AACpB,SAAA,CAAA;AACF;;AAGA,IAAA,IAAIhC,QAAW0B,GAAAA,UAAAA,CAAWzC,MAAM,GAAG,CAAG,EAAA;;AAEpC,QAAA,MAAMgD,OAAUP,GAAAA,UAAU,CAAC1B,QAAAA,GAAW,CAAE,CAAA;AACxC,QAAA,MAAMkC,4BAA4BrD,gBAAiBoD,CAAAA,OAAAA,CAAQnD,OAAO,CAAC,CAAA,CAAE,CAACC,GAAG,CAAA;AAEzE,QAAA,IAAImD,yBAA2B,EAAA;YAC7B,MAAMC,WAAAA,GAAcD,0BAA0B7C,qBAAqB,EAAA;AACnE,YAAA,MAAM+C,UAAaD,GAAAA,WAAAA,CAAYtC,GAAG,GAAGN,cAAcM,GAAG;AAEtDc,YAAAA,YAAAA,CAAaC,IAAI,CAAC;gBAChBC,WAAalC,EAAAA,GAAAA,CAAI6C,QAAQ,GAAG,CAAA;gBAC5BT,QAAU,EAAA;oBACRR,IAAM,EAAA,CAAA;AACNV,oBAAAA,GAAAA,EAAKuC,UAAaP,GAAAA,wBAAAA;oBAClBlC,MAAQkC,EAAAA,wBAAAA;oBACRrB,KAAOoB,EAAAA;AACT,iBAAA;gBACAZ,SAAW,EAAA,IAAA;gBACXC,IAAM,EAAA,YAAA;gBACNe,gBAAkB,EAAA;AACpB,aAAA,CAAA;AACF;KACK,MAAA;;QAEL,MAAMK,WAAAA,GAActC,QAAQT,QAAQ;AACpC,QAAA,MAAMgD,aAAgBD,GAAAA,WAAAA,CAAYE,MAAM,GAAGhD,cAAcM,GAAG;AAE5Dc,QAAAA,YAAAA,CAAaC,IAAI,CAAC;AAChBC,YAAAA,WAAAA,EAAac,gBAAgB1C,MAAM;YACnC8B,QAAU,EAAA;gBACRR,IAAM,EAAA,CAAA;gBACNV,GAAKyC,EAAAA,aAAAA;gBACL3C,MAAQkC,EAAAA,wBAAAA;gBACRrB,KAAOoB,EAAAA;AACT,aAAA;YACAZ,SAAW,EAAA,IAAA;YACXC,IAAM,EAAA,YAAA;YACNe,gBAAkB,EAAA;AACpB,SAAA,CAAA;AACF;IAEA,OAAOrB,YAAAA;AACT;AAEO,MAAM6B,kBAAqB,GAAA,CAAC,EACjCb,eAAe,EACfc,YAAY,EACZC,eAAe,EACfC,UAAU,EACc,GAAA;AACxB,IAAA,MAAM,CAACC,SAAWC,EAAAA,YAAAA,CAAa,GAAGC,KAAMC,CAAAA,QAAQ,CAAwB,EAAE,CAAA;;IAG1E,MAAMrB,UAAAA,GAAaoB,KAAME,CAAAA,OAAO,CAAC,IAAA;AAC/B,QAAA,OAAOC,oBAAoBtB,eAAiBc,EAAAA,YAAAA,CAAAA;KAC3C,EAAA;AAACd,QAAAA,eAAAA;AAAiBc,QAAAA;AAAa,KAAA,CAAA;;IAGlC,MAAMS,6BAAAA,GAAgCJ,KAAMK,CAAAA,WAAW,CAAC,IAAA;AACtD,QAAA,MAAMxC,eAAsC,EAAE;;AAG9C,QAAA,MAAMyC,mBAAmBV,eACrBhB,GAAAA,UAAAA,CAAW2B,IAAI,CAAC,CAAC1E,GAAQA,GAAAA,GAAAA,CAAIG,OAAO,CAACwE,IAAI,CAAC,CAAClD,SAAWA,MAAOrB,CAAAA,GAAG,KAAK2D,eACrE,CAAA,CAAA,GAAA,IAAA;QAEJhB,UAAWN,CAAAA,OAAO,CAAC,CAACzC,GAAKqB,EAAAA,QAAAA,GAAAA;AACvB,YAAA,MAAMD,UAAUrB,UAAWC,CAAAA,GAAAA,CAAAA;AAC3B,YAAA,IAAI,CAACoB,OAAS,EAAA;AAEd,YAAA,MAAME,WAActB,GAAAA,GAAAA,CAAIG,OAAO,CAACG,MAAM;;YAGtC,MAAMsE,qBAAAA,GAAwBH,oBAAoBA,gBAAqBzE,KAAAA,GAAAA;YACvE,MAAM6E,wBAAAA,GAA2BJ,oBAAoBA,gBAAqBzE,KAAAA,GAAAA;AAC1E,YAAA,MAAM8E,uBAAuBxD,WAAc,GAAA,CAAA;YAE3C,MAAMyD,2BAAAA,GACJH,yBAA0BC,wBAA4BC,IAAAA,oBAAAA;;AAGxD,YAAA,IAAIC,2BAA6B,EAAA;gBAC/B,MAAMC,iBAAAA,GAAoB7D,oBAAqBnB,CAAAA,GAAAA,EAAKoB,OAASC,EAAAA,QAAAA,CAAAA;AAC7DW,gBAAAA,YAAAA,CAAaC,IAAI,CAAI+C,GAAAA,iBAAAA,CAAAA;AACvB;;AAGA,YAAA,MAAMC,mBAAsBnC,GAAAA,sBAAAA,CAC1B9C,GACAqB,EAAAA,QAAAA,EACAD,SACA2B,UACAC,EAAAA,eAAAA,CAAAA;AAEFhB,YAAAA,YAAAA,CAAaC,IAAI,CAAIgD,GAAAA,mBAAAA,CAAAA;AACvB,SAAA,CAAA;QAEA,OAAOjD,YAAAA;KACN,EAAA;AAACe,QAAAA,UAAAA;AAAYgB,QAAAA,eAAAA;AAAiBf,QAAAA;AAAgB,KAAA,CAAA;AAEjDmB,IAAAA,KAAAA,CAAMe,eAAe,CAAC,IAAA;AACpB,QAAA,MAAMC,eAAkB,GAAA,IAAA;AACtB,YAAA,MAAMC,YAAeb,GAAAA,6BAAAA,EAAAA;YACrBL,YAAakB,CAAAA,YAAAA,CAAAA;AACf,SAAA;AAEAD,QAAAA,eAAAA,EAAAA;;AAGA,QAAA,MAAM5E,gBAAmBC,GAAAA,sBAAAA,EAAAA;AACzB,QAAA,IAAI,CAACD,gBAAkB,EAAA;QAEvB,MAAM8E,cAAAA,GAAiB,IAAIC,cAAe,CAAA,IAAA;AACxCH,YAAAA,eAAAA,EAAAA;AACF,SAAA,CAAA;AAEAE,QAAAA,cAAAA,CAAeE,OAAO,CAAChF,gBAAAA,CAAAA;QAEvB,OAAO,IAAA;AACL8E,YAAAA,cAAAA,CAAeG,UAAU,EAAA;AAC3B,SAAA;KACC,EAAA;AAACjB,QAAAA;AAA8B,KAAA,CAAA;AAElC,IAAA,OAAON,SAAUzC,CAAAA,GAAG,CAAC,CAACiE,4BACpBC,GAACC,CAAAA,WAAAA,EAAAA;AAECzD,YAAAA,WAAAA,EAAauD,YAAYvD,WAAW;AACpCE,YAAAA,QAAAA,EAAUqD,YAAYrD,QAAQ;AAC9BC,YAAAA,SAAAA,EAAWoD,YAAYpD,SAAS;AAChCC,YAAAA,IAAAA,EAAMmD,YAAYnD,IAAI;YACtB0B,UAAYA,EAAAA,UAAAA;AACZzB,YAAAA,cAAAA,EAAgBkD,YAAYlD;AANvB,SAAA,EAAA,CAAC,cAAc,EAAEkD,WAAAA,CAAYnD,IAAI,CAAC,CAAC,EAAEmD,WAAAA,CAAYvD,WAAW,CAAC,CAAC,EAAEuD,WAAAA,CAAYlD,cAAc,IAAI,SAAS,CAAC,CAAA,CAAA;AASnH;AAWA,MAAMqD,oBAAAA,GAAuBC,MAAOC,CAAAA,GAAAA,CAElC;AACkB,oBAAA,EAAE,CAAC,EAAEC,OAAO,EAAEC,KAAK,EAAE,GACrCD,OAAAA,GAAU,CAAC,EAAEC,MAAMC,MAAM,CAACC,UAAU,CAAC,CAAC,GAAG,aAAc,CAAA;AACjD,UAAA,EAAE,CAAC,EAAEH,OAAO,EAAEC,KAAK,EAAE,GAC3BD,OAAU,GAAA,CAAC,UAAU,EAAEC,MAAMC,MAAM,CAACE,UAAU,CAAC,CAAC,GAAG,uBAAwB,CAAA;AACpE,WAAA,EAAE,CAAC,EAAEJ,OAAO,EAAE,GAAMA,OAAAA,GAAU,IAAI,GAAK,CAAA;;;AAGjC,iBAAA,EAAE,CAAC,EAAEC,KAAK,EAAE,GAAKA,KAAAA,CAAMI,YAAY,CAAC;;;;;;;AAO5C,SAAA,EAAEtG,cAAe,CAAA;;AAE1B,CAAC;AAED,MAAM6F,WAAc,GAAA,CAAC,EACnBzD,WAAW,EACXE,QAAQ,EACRC,SAAS,EACTC,IAAI,EACJ0B,UAAU,EACVzB,cAAc,EACG,GAAA;AACjB,IAAA,MAAMc,mBAAmBf,IAAS,KAAA,YAAA;IAElC,MAAM,CAAC,EAAE+D,MAAM,EAAE,EAAEC,IAAK,CAAA,GAAGC,OACzB,CAAA,KAAO;YACLC,MAAQ,EAAA,QAAA;AACRF,YAAAA,IAAAA,EAAM,CAACG,IAAAA,GAAAA;AACLzC,gBAAAA,UAAAA,CAAWyC,IAAKC,CAAAA,EAAE,EAAExE,WAAAA,EAAaK,cAAgBc,EAAAA,gBAAAA,CAAAA;AACnD,aAAA;YACAsD,OAAS,EAAA,CAACC,WAAa;AACrBP,oBAAAA,MAAAA,EAAQO,QAAQP,MAAM;iBACxB;AACF,SAAA,CACA,EAAA;AAACnE,QAAAA,WAAAA;AAAamB,QAAAA,gBAAAA;AAAkBW,QAAAA,UAAAA;AAAYzB,QAAAA;AAAe,KAAA,CAAA;AAG7D,IAAA,IAAI,CAACF,SAAW,EAAA;QACd,OAAO,IAAA;AACT;AAEA,IAAA,qBACEqD,GAACE,CAAAA,oBAAAA,EAAAA;QACCiB,GAAKP,EAAAA,IAAAA;QACLP,OAASM,EAAAA,MAAAA;QACTS,KAAO,EAAA;AACLC,YAAAA,SAAAA,EAAW,CAAC,UAAU,EAAE3E,QAAAA,CAASR,IAAI,CAAC,IAAI,EAAEQ,QAASlB,CAAAA,GAAG,CAAC,GAAG,CAAC;AAC7DF,YAAAA,MAAAA,EAAQ,CAAC,EAAEoB,QAAAA,CAASpB,MAAM,CAAC,EAAE,CAAC;AAC9Ba,YAAAA,KAAAA,EAAO,CAAC,EAAEO,QAAAA,CAASP,KAAK,CAAC,EAAE;AAC7B;;AAGN,CAAA;;;;"}
@@ -0,0 +1,353 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+ var designSystem = require('@strapi/design-system');
6
+ var styled = require('styled-components');
7
+ var resizeHandlers = require('../utils/resizeHandlers.js');
8
+ var widgetLayout = require('../utils/widgetLayout.js');
9
+
10
+ function _interopNamespaceDefault(e) {
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
28
+
29
+ const INDICATOR_SIZE = 20;
30
+ const IndicatorContainer = styled.styled(designSystem.Box)`
31
+ position: absolute;
32
+ z-index: 1;
33
+ pointer-events: none;
34
+ opacity: ${({ $isVisible })=>$isVisible ? 1 : 0};
35
+ transition: opacity 0.2s ease-in-out;
36
+ background: transparent;
37
+ height: ${INDICATOR_SIZE}px;
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ `;
42
+ const DotContainer = styled.styled(designSystem.Box)`
43
+ position: absolute;
44
+ top: 50%;
45
+ left: ${({ $position })=>$position}%;
46
+ transform: translate(-50%, -50%);
47
+ `;
48
+ const Dot = styled.styled(designSystem.Box)`
49
+ width: 6px;
50
+ height: 6px;
51
+ border-radius: 50%;
52
+ background-color: ${({ $isActive, $isCurrent, theme })=>{
53
+ if ($isCurrent) return theme.colors.primary600;
54
+ if ($isActive) return theme.colors.primary500;
55
+ return theme.colors.neutral300;
56
+ }};
57
+ transition: all 0.2s ease-in-out;
58
+ box-shadow: ${({ $isCurrent, theme })=>$isCurrent ? `0 0 0 3px ${theme.colors.primary100}` : 'none'};
59
+ transform: ${({ $isCurrent })=>$isCurrent ? 'scale(1.2)' : 'scale(1)'};
60
+ `;
61
+ const calculateGapAdjustment = (rowWidth, leftColumns)=>{
62
+ const dotWidth = 6;
63
+ const gapAdjustmentPixels = dotWidth / 2; // Half dot width to center on boundary
64
+ const gapAdjustmentPercent = gapAdjustmentPixels / rowWidth * 100;
65
+ // Different adjustments for different positions
66
+ switch(leftColumns){
67
+ case 4:
68
+ return -gapAdjustmentPercent; // Left dot
69
+ case 8:
70
+ return gapAdjustmentPercent; // Right dot
71
+ default:
72
+ return 0;
73
+ }
74
+ };
75
+ const ResizeIndicator = ({ isVisible, position, currentLeftWidth, currentRightWidth, totalColumns = 12, rowPosition })=>{
76
+ // Calculate available resize positions accounting for grid gaps
77
+ const availablePositions = React__namespace.useMemo(()=>{
78
+ const rowWidth = rowPosition?.width || 800;
79
+ return [
80
+ 4,
81
+ 6,
82
+ 8
83
+ ].map((left)=>{
84
+ const right = totalColumns - left;
85
+ const basePosition = left / totalColumns * 100;
86
+ const gapAdjustment = calculateGapAdjustment(rowWidth, left);
87
+ const positionPercent = basePosition + gapAdjustment;
88
+ return {
89
+ left,
90
+ right,
91
+ positionPercent
92
+ };
93
+ });
94
+ }, [
95
+ totalColumns,
96
+ rowPosition?.width
97
+ ]);
98
+ // Find the current position index
99
+ const currentPositionIndex = React__namespace.useMemo(()=>{
100
+ return availablePositions.findIndex((pos)=>pos.left === currentLeftWidth && pos.right === currentRightWidth);
101
+ }, [
102
+ availablePositions,
103
+ currentLeftWidth,
104
+ currentRightWidth
105
+ ]);
106
+ if (!isVisible) {
107
+ return null;
108
+ }
109
+ // Calculate positioning - indicator always spans the full row width
110
+ const indicatorTop = rowPosition ? rowPosition.top - INDICATOR_SIZE : Math.max(10, position.top + position.height / 2 - 40);
111
+ const isCurrent = (index)=>index === currentPositionIndex;
112
+ const isActive = (index)=>Math.abs(index - currentPositionIndex) <= 1;
113
+ return /*#__PURE__*/ jsxRuntime.jsx(IndicatorContainer, {
114
+ $isVisible: isVisible,
115
+ style: {
116
+ left: rowPosition ? `${rowPosition.left}px` : `${position.left + 10}px`,
117
+ top: `${indicatorTop}px`,
118
+ width: rowPosition ? `${rowPosition.width}px` : 'auto'
119
+ },
120
+ children: availablePositions.map((pos, index)=>{
121
+ return /*#__PURE__*/ jsxRuntime.jsx(DotContainer, {
122
+ $position: pos.positionPercent,
123
+ children: /*#__PURE__*/ jsxRuntime.jsx(Dot, {
124
+ $isActive: isActive(index),
125
+ $isCurrent: isCurrent(index)
126
+ })
127
+ }, `${pos.left}-${pos.right}`);
128
+ })
129
+ });
130
+ };
131
+ const ResizeHandleContainer = styled.styled(designSystem.Box)`
132
+ position: absolute;
133
+ top: 0;
134
+ bottom: 0;
135
+ width: ${INDICATOR_SIZE}px;
136
+ z-index: 1;
137
+ display: flex;
138
+ align-items: center;
139
+ justify-content: center;
140
+ opacity: 0.8;
141
+ transition: opacity 0.2s ease-in-out;
142
+ cursor: col-resize;
143
+ background-color: ${({ $isDragging })=>$isDragging ? 'rgba(0, 0, 0, 0.1)' : 'transparent'};
144
+ `;
145
+ const ResizeHandleBar = styled.styled(designSystem.Box)`
146
+ width: 2px;
147
+ height: 100%;
148
+ background-color: ${({ theme })=>theme.colors.primary500};
149
+ border-radius: 1px;
150
+ opacity: 0;
151
+ transition: opacity 0.2s ease-in-out;
152
+
153
+ ${ResizeHandleContainer}:hover & {
154
+ opacity: 0.8;
155
+ }
156
+
157
+ ${({ $isDragging })=>$isDragging && `opacity: 0.8;`}
158
+ `;
159
+ const WidgetResizeHandle = ({ leftWidgetId, rightWidgetId, leftWidgetWidth, rightWidgetWidth, onResize, saveLayout })=>{
160
+ const [state, setState] = React__namespace.useState({
161
+ isDragging: false,
162
+ startX: 0,
163
+ startLeftWidth: 0,
164
+ startRightWidth: 0,
165
+ position: {
166
+ left: 0,
167
+ top: 0,
168
+ height: 0
169
+ },
170
+ lastResizeValues: {
171
+ leftWidth: 0,
172
+ rightWidth: 0
173
+ },
174
+ currentResizeValues: {
175
+ leftWidth: leftWidgetWidth,
176
+ rightWidth: rightWidgetWidth
177
+ },
178
+ rowPosition: null
179
+ });
180
+ const throttleRef = React__namespace.useRef(null);
181
+ const handleResize = React__namespace.useCallback((deltaColumns)=>{
182
+ // Only resize if there's significant movement (dead zone)
183
+ if (Math.abs(deltaColumns) < 0.25) {
184
+ return;
185
+ }
186
+ // Calculate target widths
187
+ const { targetLeftWidth, targetRightWidth } = resizeHandlers.calculateTargetWidths(deltaColumns, state.startLeftWidth, state.startRightWidth);
188
+ // Validate the resize
189
+ if (!resizeHandlers.isValidResize(targetLeftWidth, targetRightWidth)) {
190
+ return;
191
+ }
192
+ // Update current resize values for the indicator
193
+ setState((prev)=>({
194
+ ...prev,
195
+ currentResizeValues: {
196
+ leftWidth: targetLeftWidth,
197
+ rightWidth: targetRightWidth
198
+ }
199
+ }));
200
+ // Only trigger resize if values have changed
201
+ if (resizeHandlers.shouldTriggerResize(targetLeftWidth, targetRightWidth, state.lastResizeValues)) {
202
+ setState((prev)=>({
203
+ ...prev,
204
+ lastResizeValues: {
205
+ leftWidth: targetLeftWidth,
206
+ rightWidth: targetRightWidth
207
+ }
208
+ }));
209
+ onResize(leftWidgetId, rightWidgetId, targetLeftWidth, targetRightWidth);
210
+ }
211
+ }, [
212
+ leftWidgetId,
213
+ rightWidgetId,
214
+ onResize,
215
+ state.startLeftWidth,
216
+ state.startRightWidth,
217
+ state.lastResizeValues
218
+ ]);
219
+ const handlePointerMove = React__namespace.useCallback((e)=>{
220
+ if (!state.isDragging) return;
221
+ // Clear any existing throttle timeout
222
+ if (throttleRef.current) {
223
+ clearTimeout(throttleRef.current);
224
+ }
225
+ // Throttle the resize calls to prevent excessive updates
226
+ throttleRef.current = setTimeout(()=>{
227
+ const deltaX = e.clientX - state.startX;
228
+ const threshold = 120; // Pixels per column unit
229
+ const deltaColumns = Math.round(deltaX / threshold);
230
+ handleResize(deltaColumns);
231
+ }, 0);
232
+ }, [
233
+ state.isDragging,
234
+ state.startX,
235
+ handleResize
236
+ ]);
237
+ // Handle pointer up to end drag
238
+ const handlePointerUp = React__namespace.useCallback(()=>{
239
+ // Clear any pending throttle timeout
240
+ if (throttleRef.current) {
241
+ clearTimeout(throttleRef.current);
242
+ throttleRef.current = null;
243
+ }
244
+ // Save the layout
245
+ saveLayout();
246
+ // Reset last resize values and stop dragging
247
+ setState((prev)=>({
248
+ ...prev,
249
+ lastResizeValues: {
250
+ leftWidth: 0,
251
+ rightWidth: 0
252
+ },
253
+ currentResizeValues: {
254
+ leftWidth: leftWidgetWidth,
255
+ rightWidth: rightWidgetWidth
256
+ },
257
+ isDragging: false
258
+ }));
259
+ }, [
260
+ leftWidgetWidth,
261
+ rightWidgetWidth,
262
+ saveLayout
263
+ ]);
264
+ // Handle pointer down to start drag
265
+ const handlePointerDown = React__namespace.useCallback((e)=>{
266
+ e.preventDefault();
267
+ e.stopPropagation();
268
+ setState((prev)=>({
269
+ ...prev,
270
+ isDragging: true,
271
+ startX: e.clientX,
272
+ startLeftWidth: leftWidgetWidth,
273
+ startRightWidth: rightWidgetWidth
274
+ }));
275
+ }, [
276
+ leftWidgetWidth,
277
+ rightWidgetWidth
278
+ ]);
279
+ // Set up drag event listeners
280
+ React__namespace.useEffect(()=>{
281
+ if (state.isDragging) {
282
+ document.addEventListener('pointermove', handlePointerMove);
283
+ document.addEventListener('pointerup', handlePointerUp);
284
+ return ()=>{
285
+ document.removeEventListener('pointermove', handlePointerMove);
286
+ document.removeEventListener('pointerup', handlePointerUp);
287
+ };
288
+ }
289
+ }, [
290
+ state.isDragging,
291
+ handlePointerMove,
292
+ handlePointerUp
293
+ ]);
294
+ // Set up resize observer for position updates - watching widgets and grid container
295
+ React__namespace.useLayoutEffect(()=>{
296
+ const leftElement = widgetLayout.getWidgetElement(leftWidgetId);
297
+ const rightElement = widgetLayout.getWidgetElement(rightWidgetId);
298
+ const containerElement = widgetLayout.getWidgetGridContainer();
299
+ const updatePosition = ()=>{
300
+ const position = resizeHandlers.calculateResizeHandlePosition(leftElement, rightElement, containerElement);
301
+ const rowPosition = resizeHandlers.calculateRowBounds(leftElement, rightElement, containerElement);
302
+ setState((prev)=>({
303
+ ...prev,
304
+ position,
305
+ rowPosition
306
+ }));
307
+ };
308
+ // Create ResizeObserver to watch widgets and grid container
309
+ const resizeObserver = new ResizeObserver(updatePosition);
310
+ // Observe all relevant elements
311
+ if (leftElement) resizeObserver.observe(leftElement);
312
+ if (rightElement) resizeObserver.observe(rightElement);
313
+ if (containerElement) resizeObserver.observe(containerElement);
314
+ return ()=>{
315
+ resizeObserver.disconnect();
316
+ };
317
+ }, [
318
+ leftWidgetId,
319
+ rightWidgetId
320
+ ]);
321
+ // Cleanup throttle timeout on unmount
322
+ React__namespace.useEffect(()=>{
323
+ return ()=>{
324
+ if (throttleRef.current) {
325
+ clearTimeout(throttleRef.current);
326
+ }
327
+ };
328
+ }, []);
329
+ return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
330
+ children: [
331
+ /*#__PURE__*/ jsxRuntime.jsx(ResizeHandleContainer, {
332
+ onPointerDown: handlePointerDown,
333
+ style: {
334
+ transform: `translate(${state.position.left}px, ${state.position.top}px)`,
335
+ height: `${state.position.height}px`
336
+ },
337
+ children: /*#__PURE__*/ jsxRuntime.jsx(ResizeHandleBar, {
338
+ $isDragging: state.isDragging
339
+ })
340
+ }),
341
+ /*#__PURE__*/ jsxRuntime.jsx(ResizeIndicator, {
342
+ isVisible: state.isDragging,
343
+ position: state.position,
344
+ currentLeftWidth: state.currentResizeValues.leftWidth,
345
+ currentRightWidth: state.currentResizeValues.rightWidth,
346
+ rowPosition: state.rowPosition
347
+ })
348
+ ]
349
+ });
350
+ };
351
+
352
+ exports.WidgetResizeHandle = WidgetResizeHandle;
353
+ //# sourceMappingURL=ResizeIndicator.js.map