@vc-shell/framework 1.1.0-alpha.3 → 1.1.0-alpha.4

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 (156) hide show
  1. package/core/composables/index.ts +18 -17
  2. package/core/composables/useDashboard/index.ts +19 -0
  3. package/core/composables/{useGlobalSearch.ts → useGlobalSearch/index.ts} +3 -5
  4. package/core/composables/useWidgets/index.ts +19 -18
  5. package/core/plugins/modularity/loader.ts +2 -1
  6. package/core/services/dashboard-service.ts +121 -0
  7. package/core/services/widget-service.ts +1 -4
  8. package/dist/core/composables/index.d.ts +1 -0
  9. package/dist/core/composables/index.d.ts.map +1 -1
  10. package/dist/core/composables/useDashboard/index.d.ts +5 -0
  11. package/dist/core/composables/useDashboard/index.d.ts.map +1 -0
  12. package/dist/core/composables/{useGlobalSearch.d.ts → useGlobalSearch/index.d.ts} +1 -1
  13. package/dist/core/composables/useGlobalSearch/index.d.ts.map +1 -0
  14. package/dist/core/plugins/modularity/loader.d.ts.map +1 -1
  15. package/dist/core/services/dashboard-service.d.ts +33 -0
  16. package/dist/core/services/dashboard-service.d.ts.map +1 -0
  17. package/dist/core/services/widget-service.d.ts.map +1 -1
  18. package/dist/framework.js +235 -225
  19. package/dist/{index-Bu12RZTu.js → index-8LELHzw9.js} +1 -1
  20. package/dist/{index-Bwl2ND2Q.js → index-9lJxZE5w.js} +1 -1
  21. package/dist/{index-CJi-BbTb.js → index-B1YR_MYV.js} +1 -1
  22. package/dist/{index-BhdwVgUw.js → index-BA98L1jI.js} +1 -1
  23. package/dist/{index-NdrUF1u3.js → index-BAeTsi-X.js} +1 -1
  24. package/dist/{index-CbRqPQTw.js → index-BBYyHeYA.js} +1 -1
  25. package/dist/{index-CsaYfhir.js → index-BrUitdDo.js} +1 -1
  26. package/dist/{index-CZ_pj3nW.js → index-BuO5ByG9.js} +1 -1
  27. package/dist/{index-DFPb-jDP.js → index-CJ5I7vTn.js} +1 -1
  28. package/dist/{index-BdoAu2fz.js → index-CWKrD2Cd.js} +1 -1
  29. package/dist/{index-DVaMW7gL.js → index-Cf9Tz1ql.js} +1 -1
  30. package/dist/{index-B89uIUkS.js → index-CrxFDC2b.js} +1 -1
  31. package/dist/{index-BcQiBkO6.js → index-D1JchciU.js} +1 -1
  32. package/dist/{index-CEvuTGIu.js → index-DLtsQ_PJ.js} +31254 -31134
  33. package/dist/{index-COjjAS6v.js → index-DVljTjbf.js} +1 -1
  34. package/dist/{index-DjQ6Ffv8.js → index-RwX3kiZh.js} +28 -28
  35. package/dist/{index-S9Ht7s3i.js → index-xLYzNPa7.js} +1 -1
  36. package/dist/index.css +1 -1
  37. package/dist/injection-keys.d.ts +28 -0
  38. package/dist/injection-keys.d.ts.map +1 -1
  39. package/dist/shared/components/dashboard-widget-card/dashboard-widget-card.vue.d.ts +25 -0
  40. package/dist/shared/components/dashboard-widget-card/dashboard-widget-card.vue.d.ts.map +1 -0
  41. package/dist/shared/components/dashboard-widget-card/index.d.ts +2 -0
  42. package/dist/shared/components/dashboard-widget-card/index.d.ts.map +1 -0
  43. package/dist/shared/components/draggable-dashboard/DraggableDashboard.vue.d.ts +6 -0
  44. package/dist/shared/components/draggable-dashboard/DraggableDashboard.vue.d.ts.map +1 -0
  45. package/dist/shared/components/draggable-dashboard/_internal/DashboardWidget.vue.d.ts +20 -0
  46. package/dist/shared/components/draggable-dashboard/_internal/DashboardWidget.vue.d.ts.map +1 -0
  47. package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts +354 -0
  48. package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts.map +1 -0
  49. package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts +12 -0
  50. package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts.map +1 -0
  51. package/dist/shared/components/draggable-dashboard/index.d.ts +2 -0
  52. package/dist/shared/components/draggable-dashboard/index.d.ts.map +1 -0
  53. package/dist/shared/components/draggable-dashboard/types.d.ts +80 -0
  54. package/dist/shared/components/draggable-dashboard/types.d.ts.map +1 -0
  55. package/dist/shared/components/index.d.ts +2 -0
  56. package/dist/shared/components/index.d.ts.map +1 -1
  57. package/dist/shared/components/user-dropdown-button/_internal/user-info.vue.d.ts.map +1 -1
  58. package/dist/shared/components/user-dropdown-button/user-dropdown-button.vue.d.ts.map +1 -1
  59. package/dist/shared/composables/useMenuExpanded.d.ts +2 -0
  60. package/dist/shared/composables/useMenuExpanded.d.ts.map +1 -1
  61. package/dist/shared/modules/dynamic/components/fields/storybook/Button.stories.d.ts +0 -41
  62. package/dist/shared/modules/dynamic/components/fields/storybook/Button.stories.d.ts.map +1 -1
  63. package/dist/shared/modules/dynamic/components/fields/storybook/Card.stories.d.ts.map +1 -1
  64. package/dist/shared/modules/dynamic/components/fields/storybook/common/templates.d.ts +1 -1
  65. package/dist/shared/modules/dynamic/components/fields/storybook/common/templates.d.ts.map +1 -1
  66. package/dist/shared/modules/dynamic/pages/dynamic-blade-list.vue.d.ts +2 -25
  67. package/dist/shared/modules/dynamic/pages/dynamic-blade-list.vue.d.ts.map +1 -1
  68. package/dist/tailwind.config.d.ts +1 -81
  69. package/dist/tailwind.config.d.ts.map +1 -1
  70. package/dist/tsconfig.tsbuildinfo +1 -1
  71. package/dist/ui/components/atoms/vc-button/vc-button.stories.d.ts +169 -734
  72. package/dist/ui/components/atoms/vc-button/vc-button.stories.d.ts.map +1 -1
  73. package/dist/ui/components/atoms/vc-button/vc-button.vue.d.ts +18 -2
  74. package/dist/ui/components/atoms/vc-button/vc-button.vue.d.ts.map +1 -1
  75. package/dist/ui/components/atoms/vc-card/index.d.ts +2 -0
  76. package/dist/ui/components/atoms/vc-card/index.d.ts.map +1 -1
  77. package/dist/ui/components/atoms/vc-card/vc-card.stories.d.ts +12 -0
  78. package/dist/ui/components/atoms/vc-card/vc-card.stories.d.ts.map +1 -1
  79. package/dist/ui/components/atoms/vc-card/vc-card.vue.d.ts +2 -0
  80. package/dist/ui/components/atoms/vc-card/vc-card.vue.d.ts.map +1 -1
  81. package/dist/ui/components/atoms/vc-icon/icons/GridDotsIcon.vue.d.ts +18 -0
  82. package/dist/ui/components/atoms/vc-icon/icons/GridDotsIcon.vue.d.ts.map +1 -0
  83. package/dist/ui/components/atoms/vc-icon/icons/ShoppingCardIcon.vue.d.ts +18 -0
  84. package/dist/ui/components/atoms/vc-icon/icons/ShoppingCardIcon.vue.d.ts.map +1 -0
  85. package/dist/ui/components/atoms/vc-icon/icons/index.d.ts +2 -0
  86. package/dist/ui/components/atoms/vc-icon/icons/index.d.ts.map +1 -1
  87. package/dist/ui/components/organisms/vc-app/_internal/composables/useAppMenuState.d.ts +2 -0
  88. package/dist/ui/components/organisms/vc-app/_internal/composables/useAppMenuState.d.ts.map +1 -1
  89. package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue.d.ts +0 -1
  90. package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue.d.ts.map +1 -1
  91. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue.d.ts +2 -1
  92. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue.d.ts.map +1 -1
  93. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue.d.ts.map +1 -1
  94. package/dist/ui/components/organisms/vc-app/vc-app.stories.d.ts +13 -67
  95. package/dist/ui/components/organisms/vc-app/vc-app.stories.d.ts.map +1 -1
  96. package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts +5 -65
  97. package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
  98. package/dist/ui/components/organisms/vc-table/_internal/vc-table-base-header/vc-table-base-header.vue.d.ts.map +1 -1
  99. package/dist/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/{vc-table-header/vc-table-header.vue.d.ts → vc-table-columns-header/vc-table-columns-header.vue.d.ts} +1 -1
  100. package/dist/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/vc-table-columns-header/vc-table-columns-header.vue.d.ts.map +1 -0
  101. package/dist/ui/components/organisms/vc-table/_internal/vc-table-header/vc-table-header.vue.d.ts.map +1 -1
  102. package/dist/ui/components/organisms/vc-table/_internal/vc-table-mobile-view/vc-table-mobile-view.vue.d.ts +33 -3
  103. package/dist/ui/components/organisms/vc-table/_internal/vc-table-mobile-view/vc-table-mobile-view.vue.d.ts.map +1 -1
  104. package/dist/ui/components/organisms/vc-table/composables/useTableRowReorder.d.ts.map +1 -1
  105. package/package.json +10 -5
  106. package/shared/components/dashboard-widget-card/dashboard-widget-card.vue +67 -0
  107. package/shared/components/dashboard-widget-card/index.ts +1 -0
  108. package/shared/components/draggable-dashboard/DraggableDashboard.vue +369 -0
  109. package/shared/components/draggable-dashboard/_internal/DashboardWidget.vue +133 -0
  110. package/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.ts +547 -0
  111. package/shared/components/draggable-dashboard/composables/useDashboardGrid.ts +250 -0
  112. package/shared/components/draggable-dashboard/index.ts +1 -0
  113. package/shared/components/draggable-dashboard/types.ts +91 -0
  114. package/shared/components/index.ts +2 -0
  115. package/shared/components/user-dropdown-button/_internal/user-info.vue +25 -12
  116. package/shared/components/user-dropdown-button/user-dropdown-button.vue +3 -3
  117. package/shared/composables/useMenuExpanded.ts +24 -0
  118. package/shared/modules/assets/components/assets-details/assets-details.vue +1 -1
  119. package/shared/modules/dynamic/components/fields/storybook/Button.stories.ts +186 -247
  120. package/shared/modules/dynamic/components/fields/storybook/Card.stories.ts +175 -176
  121. package/shared/modules/dynamic/components/fields/storybook/common/templates.ts +8 -8
  122. package/shared/modules/dynamic/pages/dynamic-blade-list.vue +153 -187
  123. package/tailwind.config.ts +127 -126
  124. package/ui/components/atoms/vc-button/vc-button.stories.ts +1 -16
  125. package/ui/components/atoms/vc-button/vc-button.vue +74 -63
  126. package/ui/components/atoms/vc-card/vc-card.stories.ts +102 -102
  127. package/ui/components/atoms/vc-card/vc-card.vue +164 -159
  128. package/ui/components/atoms/vc-icon/icons/GridDotsIcon.vue +22 -0
  129. package/ui/components/atoms/vc-icon/icons/ShoppingCardIcon.vue +16 -0
  130. package/ui/components/atoms/vc-icon/icons/index.ts +2 -0
  131. package/ui/components/molecules/vc-field/vc-field.vue +1 -1
  132. package/ui/components/organisms/vc-app/_internal/composables/useAppMenuState.ts +12 -1
  133. package/ui/components/organisms/vc-app/_internal/vc-app-bar/_internal/AppBarContent.vue +1 -2
  134. package/ui/components/organisms/vc-app/_internal/vc-app-bar/_internal/AppBarHeader.vue +1 -1
  135. package/ui/components/organisms/vc-app/_internal/vc-app-bar/_internal/AppBarOverlay.vue +0 -1
  136. package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +274 -112
  137. package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue +81 -37
  138. package/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +7 -5
  139. package/ui/components/organisms/vc-app/vc-app.vue +26 -15
  140. package/ui/components/organisms/vc-table/_internal/vc-table-base-header/vc-table-base-header.vue +5 -7
  141. package/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/{vc-table-header/vc-table-header.vue → vc-table-columns-header/vc-table-columns-header.vue} +23 -21
  142. package/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/vc-table-row/vc-table-row.vue +1 -0
  143. package/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/vc-table-desktop-view.vue +1 -1
  144. package/ui/components/organisms/vc-table/_internal/vc-table-header/vc-table-header.vue +12 -1
  145. package/ui/components/organisms/vc-table/_internal/vc-table-mobile-view/vc-table-mobile-view.vue +45 -2
  146. package/ui/components/organisms/vc-table/composables/useTableColumnReorder.ts +5 -5
  147. package/ui/components/organisms/vc-table/composables/useTableColumnResize.ts +1 -1
  148. package/ui/components/organisms/vc-table/composables/useTableRowReorder.ts +1 -0
  149. package/core/services/toolbarbus-service.ts +0 -34
  150. package/dist/core/composables/useGlobalSearch.d.ts.map +0 -1
  151. package/dist/core/services/toolbarbus-service.d.ts +0 -10
  152. package/dist/core/services/toolbarbus-service.d.ts.map +0 -1
  153. package/dist/ui/components/organisms/vc-app/composables/useToolbarSlots.d.ts +0 -5
  154. package/dist/ui/components/organisms/vc-app/composables/useToolbarSlots.d.ts.map +0 -1
  155. package/dist/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/vc-table-header/vc-table-header.vue.d.ts.map +0 -1
  156. package/ui/components/organisms/vc-app/composables/useToolbarSlots.ts +0 -37
@@ -0,0 +1,250 @@
1
+ import { computed, ref, watch } from "vue";
2
+ import type { IDashboardWidget, DashboardWidgetPosition, DashboardGridConfig } from "../types";
3
+ import { useDashboard } from "../../../../core/composables/useDashboard";
4
+
5
+ // Constants for grid
6
+ export const GRID_COLUMNS = 12;
7
+ // Initial rows, but now it's the minimum value
8
+ export const MIN_GRID_ROWS = 12;
9
+ // Increase height buffer for "infinite" scroll
10
+ const ROWS_BUFFER = 10;
11
+ // Dynamically calculated number of rows
12
+ const dynamicRows = ref(MIN_GRID_ROWS);
13
+
14
+ // Constants for position search optimization
15
+ const SEARCH_DIRECTIONS = [
16
+ { dx: 0, dy: 0 }, // Current position
17
+ { dx: 0, dy: 1 }, // Down
18
+ { dx: 1, dy: 0 }, // Right
19
+ { dx: 0, dy: -1 }, // Up
20
+ { dx: -1, dy: 0 }, // Left
21
+ { dx: 1, dy: 1 }, // Right-down
22
+ { dx: -1, dy: 1 }, // Left-down
23
+ { dx: 1, dy: -1 }, // Right-up
24
+ { dx: -1, dy: -1 }, // Left-up
25
+ ];
26
+
27
+ export function useDashboardGrid() {
28
+ const dashboard = useDashboard();
29
+ const widgets = computed(() => dashboard.getWidgets());
30
+ const layout = computed(() => dashboard.getLayout());
31
+
32
+ // Calculate current maximum number of rows based on existing widgets
33
+ const currentRows = computed(() => {
34
+ if (widgets.value.length === 0) return MIN_GRID_ROWS;
35
+
36
+ const maxY = Math.max(
37
+ ...Array.from(layout.value.values()).map((pos: DashboardWidgetPosition, index) => {
38
+ const widget = widgets.value.find((w) => w.id === Array.from(layout.value.keys())[index]);
39
+ return widget ? pos.y + widget.size.height : pos.y + 1;
40
+ }),
41
+ 0,
42
+ );
43
+
44
+ // Update dynamic number of rows with large buffer
45
+ dynamicRows.value = Math.max(maxY + ROWS_BUFFER, MIN_GRID_ROWS);
46
+ return dynamicRows.value;
47
+ });
48
+
49
+ // Get current number of rows
50
+ const getGridRows = () => currentRows.value;
51
+
52
+ // Improved collision detection function
53
+ const hasCollision = (
54
+ widget: IDashboardWidget,
55
+ position: DashboardWidgetPosition,
56
+ excludeWidgetId?: string,
57
+ ): boolean => {
58
+ // Check only bottom grid boundary
59
+ if (position.x < 0 || position.x + widget.size.width > GRID_COLUMNS || position.y < 0) {
60
+ return true;
61
+ }
62
+
63
+ // Create occupied cells map for optimization
64
+ const occupiedCells = new Set<string>();
65
+
66
+ // Fill occupied cells map
67
+ widgets.value.forEach((other) => {
68
+ if (other.id === widget.id || (excludeWidgetId && other.id === excludeWidgetId)) {
69
+ return;
70
+ }
71
+
72
+ const otherPos = layout.value.get(other.id);
73
+ if (!otherPos) return;
74
+
75
+ for (let x = otherPos.x; x < otherPos.x + other.size.width; x++) {
76
+ for (let y = otherPos.y; y < otherPos.y + other.size.height; y++) {
77
+ occupiedCells.add(`${x},${y}`);
78
+ }
79
+ }
80
+ });
81
+
82
+ // Check collisions for each widget cell
83
+ for (let x = position.x; x < position.x + widget.size.width; x++) {
84
+ for (let y = position.y; y < position.y + widget.size.height; y++) {
85
+ if (occupiedCells.has(`${x},${y}`)) {
86
+ return true;
87
+ }
88
+ }
89
+ }
90
+
91
+ return false;
92
+ };
93
+
94
+ // Improved free position search function
95
+ const findFreePosition = (widget: IDashboardWidget): DashboardWidgetPosition => {
96
+ const currentPos = layout.value.get(widget.id);
97
+
98
+ // If current position is free, use it
99
+ if (currentPos && !hasCollision(widget, currentPos, widget.id)) {
100
+ return currentPos;
101
+ }
102
+
103
+ // Start search from top-left corner or current position
104
+ const startX = currentPos?.x || 0;
105
+ const startY = currentPos?.y || 0;
106
+
107
+ // Spiral search for free position
108
+ let layer = 0;
109
+ const maxLayers = Math.max(GRID_COLUMNS, currentRows.value);
110
+
111
+ while (layer < maxLayers) {
112
+ // Check all directions in current layer
113
+ for (const { dx, dy } of SEARCH_DIRECTIONS) {
114
+ const x = startX + dx * layer;
115
+ const y = startY + dy * layer;
116
+
117
+ const position = { x, y };
118
+
119
+ // Check position
120
+ if (!hasCollision(widget, position, widget.id)) {
121
+ return position;
122
+ }
123
+ }
124
+
125
+ layer++;
126
+ }
127
+
128
+ // If no position found, add to the end
129
+ return {
130
+ x: 0,
131
+ y: currentRows.value,
132
+ };
133
+ };
134
+
135
+ // Arrange widgets in rows, minimizing empty spaces
136
+ const arrangeWidgetsInRows = (widgetsToArrange: IDashboardWidget[]): void => {
137
+ if (widgetsToArrange.length === 0) return;
138
+
139
+ const sortedWidgets = [...widgetsToArrange].sort((a, b) => {
140
+ // Sort by size (largest first)
141
+ const aSize = a.size.width * a.size.height;
142
+ const bSize = b.size.width * b.size.height;
143
+ return bSize - aSize;
144
+ });
145
+
146
+ // Current position for placement
147
+ let currentX = 0;
148
+ let currentY = 0;
149
+ let rowHeight = 0;
150
+
151
+ sortedWidgets.forEach((widget) => {
152
+ // Check if widget fits in current row
153
+ if (currentX + widget.size.width > GRID_COLUMNS) {
154
+ // If it doesn't fit, move to the next row
155
+ currentX = 0;
156
+ currentY += rowHeight;
157
+ rowHeight = 0;
158
+ }
159
+
160
+ // Place widget on current position
161
+ const position = { x: currentX, y: currentY };
162
+ dashboard.updateWidgetPosition(widget.id, position);
163
+
164
+ // Update current position
165
+ currentX += widget.size.width;
166
+
167
+ // Update maximum height for current row
168
+ rowHeight = Math.max(rowHeight, widget.size.height);
169
+ });
170
+ };
171
+
172
+ // Initialize initial widget positions
173
+ const initializeLayout = () => {
174
+ // Check if layout rebuild is needed
175
+
176
+ // Check if not all widgets have positioned yet
177
+ if (widgets.value.length !== layout.value.size) {
178
+ arrangeWidgetsInRows(widgets.value);
179
+ return;
180
+ }
181
+
182
+ // Check if all widgets have a position
183
+ const hasUnpositionedWidgets = widgets.value.some((widget: IDashboardWidget) => !layout.value.has(widget.id));
184
+
185
+ // If there are widgets without a position, then rearrange
186
+ if (hasUnpositionedWidgets) {
187
+ arrangeWidgetsInRows(widgets.value);
188
+ return;
189
+ }
190
+
191
+ // Check for collisions
192
+ const hasCollisions = widgets.value.some((widget: IDashboardWidget) => {
193
+ const pos = layout.value.get(widget.id);
194
+ return pos && hasCollision(widget, pos, widget.id);
195
+ });
196
+
197
+ // If there are collisions, then rearrange
198
+ if (hasCollisions) {
199
+ arrangeWidgetsInRows(widgets.value);
200
+ return;
201
+ }
202
+
203
+ // Check if widgets are compact
204
+ const maxY = Math.max(...Array.from(layout.value.values()).map((pos: DashboardWidgetPosition) => pos.y + 1), 0);
205
+
206
+ // If widgets occupy many rows but are not fully occupied, then consider this placement suboptimal
207
+ const totalCells = GRID_COLUMNS * maxY;
208
+ const occupiedCells = widgets.value.reduce((sum: number, widget: IDashboardWidget) => {
209
+ return sum + widget.size.width * widget.size.height;
210
+ }, 0);
211
+
212
+ // If less than 70% of the grid is occupied and widgets occupy many rows, then rearrange
213
+ const fillRatio = occupiedCells / totalCells;
214
+ if (maxY > 1 && fillRatio < 0.7) {
215
+ arrangeWidgetsInRows(widgets.value);
216
+ return;
217
+ }
218
+
219
+ // For widgets with positions, check and eliminate only collisions
220
+ const sortedWidgets = [...widgets.value].sort((a, b) => {
221
+ const posA = layout.value.get(a.id);
222
+ const posB = layout.value.get(b.id);
223
+ if (!posA && !posB) return 0;
224
+ if (!posA) return 1;
225
+ if (!posB) return -1;
226
+ return posA.y - posB.y || posA.x - posB.x;
227
+ });
228
+
229
+ // Rearrange widgets, avoiding collisions
230
+ sortedWidgets.forEach((widget) => {
231
+ const currentPos = layout.value.get(widget.id);
232
+ if (!currentPos) {
233
+ const freePosition = findFreePosition(widget);
234
+ dashboard.updateWidgetPosition(widget.id, freePosition);
235
+ } else if (hasCollision(widget, currentPos, widget.id)) {
236
+ const freePosition = findFreePosition(widget);
237
+ dashboard.updateWidgetPosition(widget.id, freePosition);
238
+ }
239
+ });
240
+ };
241
+
242
+ return {
243
+ widgets,
244
+ layout,
245
+ arrangeWidgetsInRows,
246
+ initializeLayout,
247
+ GRID_COLUMNS,
248
+ getGridRows,
249
+ };
250
+ }
@@ -0,0 +1 @@
1
+ export { default as DraggableDashboard } from "./DraggableDashboard.vue";
@@ -0,0 +1,91 @@
1
+ import type { Component } from 'vue';
2
+
3
+ /**
4
+ * Position type for a widget on the dashboard grid
5
+ */
6
+ export interface DashboardWidgetPosition {
7
+ /**
8
+ * X coordinate (left to right)
9
+ * @minimum 0
10
+ */
11
+ x: number;
12
+
13
+ /**
14
+ * Y coordinate (top to bottom)
15
+ * @minimum 0
16
+ */
17
+ y: number;
18
+ }
19
+
20
+ /**
21
+ * Widget size type
22
+ */
23
+ export interface DashboardWidgetSize {
24
+ /**
25
+ * Width in grid cells
26
+ * @minimum 1
27
+ * @maximum 12
28
+ */
29
+ width: number;
30
+
31
+ /**
32
+ * Height in grid cells
33
+ * @minimum 1
34
+ */
35
+ height: number;
36
+ }
37
+
38
+ /**
39
+ * Dashboard widget type
40
+ */
41
+ export interface IDashboardWidget {
42
+ /**
43
+ * Unique widget identifier
44
+ */
45
+ id: string;
46
+
47
+ /**
48
+ * Widget title
49
+ */
50
+ name?: string;
51
+
52
+ /**
53
+ * Rendering component
54
+ */
55
+ component: Component;
56
+
57
+ /**
58
+ * Component properties
59
+ */
60
+ props?: Record<string, unknown>;
61
+
62
+ /**
63
+ * Widget size
64
+ */
65
+ size: DashboardWidgetSize;
66
+ }
67
+
68
+ /**
69
+ * Drag event type
70
+ */
71
+ export interface DashboardDragEvent {
72
+ widget: IDashboardWidget;
73
+ position: DashboardWidgetPosition;
74
+ }
75
+
76
+ /**
77
+ * Grid configuration
78
+ */
79
+ export interface DashboardGridConfig {
80
+ /**
81
+ * Number of columns in the grid
82
+ * @default 12
83
+ */
84
+ columns: number;
85
+
86
+ /**
87
+ * Cell height in pixels
88
+ * @default 80
89
+ */
90
+ cellHeight: number;
91
+ }
@@ -13,3 +13,5 @@ export * from "./theme-selector";
13
13
  export * from "./sign-in";
14
14
  export * from "./settings-menu-item";
15
15
  export * from "./generic-dropdown";
16
+ export * from "./draggable-dashboard";
17
+ export * from "./dashboard-widget-card";
@@ -10,19 +10,22 @@
10
10
  icon="fas fa-user-circle"
11
11
  class="vc-user-info__icon"
12
12
  />
13
- <div
14
- v-if="isExpanded || $isMobile.value"
15
- class="vc-user-info__info"
16
- >
17
- <div class="vc-user-info__name">
18
- {{ name || (user && "fullName" in user && user.fullName) || user?.userName }}
19
- </div>
20
- <div class="vc-user-info__role">
21
- {{
22
- (role && $t(`SHELL.USER.ROLE.${role}`)) || (user?.isAdministrator ? $t("SHELL.USER.ROLE.ADMINISTRATOR") : "")
23
- }}
13
+ <Transition name="opacity">
14
+ <div
15
+ v-show="isExpanded || $isMobile.value"
16
+ class="vc-user-info__info"
17
+ >
18
+ <div class="vc-user-info__name">
19
+ {{ name || (user && "fullName" in user && user.fullName) || user?.userName }}
20
+ </div>
21
+ <div class="vc-user-info__role">
22
+ {{
23
+ (role && $t(`SHELL.USER.ROLE.${role}`)) ||
24
+ (user?.isAdministrator ? $t("SHELL.USER.ROLE.ADMINISTRATOR") : "")
25
+ }}
26
+ </div>
24
27
  </div>
25
- </div>
28
+ </Transition>
26
29
  </div>
27
30
  </template>
28
31
 
@@ -79,4 +82,14 @@ const imageHandler = computed(() => {
79
82
  @apply tw-text-sm tw-text-[color:var(--user-dropdown-account-info-role-color)];
80
83
  }
81
84
  }
85
+
86
+ .opacity-enter-active,
87
+ .opacity-leave-active {
88
+ transition: opacity 0.3s ease;
89
+ }
90
+
91
+ .opacity-enter-from,
92
+ .opacity-leave-to {
93
+ opacity: 0;
94
+ }
82
95
  </style>
@@ -21,7 +21,7 @@
21
21
  :avatar-url="avatarUrl"
22
22
  :name="name"
23
23
  :role="role"
24
- :is-expanded="isExpanded"
24
+ :is-expanded="isExpanded || isHoverExpanded"
25
25
  />
26
26
  <!-- <UserActions
27
27
  :profile-menu="profileMenu"
@@ -59,7 +59,7 @@ defineProps<Props>();
59
59
 
60
60
  const { t } = useI18n({ useScope: "global" });
61
61
  const isSidebarOpened = ref(false);
62
- const { isExpanded } = useMenuExpanded();
62
+ const { isExpanded, isHoverExpanded } = useMenuExpanded();
63
63
 
64
64
  function handleMenuItemClick(item: IMenuItem) {
65
65
  item.clickHandler?.();
@@ -115,7 +115,7 @@ function handleClick() {
115
115
  }
116
116
 
117
117
  &__actions {
118
- @apply tw-relative tw-h-full;
118
+ @apply tw-h-full;
119
119
  }
120
120
 
121
121
  &__trigger {
@@ -1,16 +1,40 @@
1
1
  import { useLocalStorage } from "@vueuse/core";
2
+ import { ref } from "vue";
2
3
 
3
4
  const STORAGE_KEY = "VC_APP_MENU_EXPANDED";
5
+ const HOVER_DELAY = 200;
6
+
7
+ const isHoverExpanded = ref(false);
4
8
 
5
9
  export const useMenuExpanded = () => {
6
10
  const isExpanded = useLocalStorage(STORAGE_KEY, true);
7
11
 
12
+ let expandTimeout: ReturnType<typeof setTimeout> | null = null;
13
+
8
14
  const toggleExpanded = () => {
9
15
  isExpanded.value = !isExpanded.value;
10
16
  };
11
17
 
18
+ const toggleHoverExpanded = (shouldExpand?: boolean) => {
19
+ if (expandTimeout) {
20
+ clearTimeout(expandTimeout);
21
+ }
22
+
23
+ if (shouldExpand) {
24
+ expandTimeout = setTimeout(() => {
25
+ if (isHoverExpanded.value !== shouldExpand) {
26
+ isHoverExpanded.value = shouldExpand;
27
+ }
28
+ }, HOVER_DELAY);
29
+ } else if (shouldExpand === false) {
30
+ isHoverExpanded.value = shouldExpand;
31
+ }
32
+ };
33
+
12
34
  return {
13
35
  isExpanded,
14
36
  toggleExpanded,
37
+ isHoverExpanded,
38
+ toggleHoverExpanded,
15
39
  };
16
40
  };
@@ -54,7 +54,7 @@
54
54
  </div>
55
55
  <VcButton
56
56
  icon="far fa-copy"
57
- size="m"
57
+ icon-size="m"
58
58
  class="tw-ml-2"
59
59
  text
60
60
  :title="t('ASSETS.PAGES.DETAILS.FIELDS.COPY')"