@vc-shell/framework 1.1.77 → 1.1.78

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 (22) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/core/services/dashboard-service.ts +5 -11
  3. package/dist/core/services/dashboard-service.d.ts.map +1 -1
  4. package/dist/framework.js +2541 -2520
  5. package/dist/index.css +1 -1
  6. package/dist/shared/components/draggable-dashboard/DraggableDashboard.vue.d.ts.map +1 -1
  7. package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts +1 -0
  8. package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts.map +1 -1
  9. package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts +1 -0
  10. package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts.map +1 -1
  11. package/dist/shared/components/draggable-dashboard/composables/useWidgetLayout.d.ts +1 -0
  12. package/dist/shared/components/draggable-dashboard/composables/useWidgetLayout.d.ts.map +1 -1
  13. package/dist/tsconfig.tsbuildinfo +1 -1
  14. package/dist/ui/components/molecules/vc-field/vc-field.vue.d.ts.map +1 -1
  15. package/dist/ui/components/molecules/vc-radio-button/vc-radio-button.vue.d.ts.map +1 -1
  16. package/package.json +4 -4
  17. package/shared/components/draggable-dashboard/DraggableDashboard.vue +23 -0
  18. package/shared/components/draggable-dashboard/composables/useDashboardGrid.ts +25 -0
  19. package/shared/components/draggable-dashboard/composables/useLayoutPersistence.ts +10 -0
  20. package/shared/components/draggable-dashboard/composables/useWidgetLayout.ts +39 -0
  21. package/ui/components/molecules/vc-field/vc-field.vue +1 -6
  22. package/ui/components/molecules/vc-radio-button/vc-radio-button.vue +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"vc-field.vue.d.ts","sourceRoot":"","sources":["../../../../../ui/components/molecules/vc-field/vc-field.vue"],"names":[],"mappings":"AAmHA,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;IACvD;;OAEG;IAEH,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;;UAlBQ,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO;iBAaxC,YAAY,GAAG,UAAU;iBAIzB,CAAC,MAAM,EAAE,MAAM,CAAC;;AAsJhC,wBAOG"}
1
+ {"version":3,"file":"vc-field.vue.d.ts","sourceRoot":"","sources":["../../../../../ui/components/molecules/vc-field/vc-field.vue"],"names":[],"mappings":"AA8GA,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;IACvD;;OAEG;IAEH,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;;UAlBQ,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO;iBAaxC,YAAY,GAAG,UAAU;iBAIzB,CAAC,MAAM,EAAE,MAAM,CAAC;;AAmJhC,wBAOG"}
@@ -1 +1 @@
1
- {"version":3,"file":"vc-radio-button.vue.d.ts","sourceRoot":"","sources":["../../../../../ui/components/molecules/vc-radio-button/vc-radio-button.vue"],"names":[],"mappings":"AA0MA,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC;IACX;;OAEG;IACH,UAAU,EAAE,GAAG,CAAC;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;CAChD;AAyED,QAAA,IAAI,OAAO,IAAW,CAAE;AACxB,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,CAAC;AAuB7C,QAAA,MAAM,eAAe;;;;;UAtHZ,MAAM;6EA8Hb,CAAC;wBACkB,eAAe,CAAC,OAAO,eAAe,EAAE,WAAW,CAAC;AAAzE,wBAA0E;AAa1E,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"vc-radio-button.vue.d.ts","sourceRoot":"","sources":["../../../../../ui/components/molecules/vc-radio-button/vc-radio-button.vue"],"names":[],"mappings":"AA2MA,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC;IACX;;OAEG;IACH,UAAU,EAAE,GAAG,CAAC;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;CAChD;AAyED,QAAA,IAAI,OAAO,IAAW,CAAE;AACxB,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,CAAC;AAuB7C,QAAA,MAAM,eAAe;;;;;UAtHZ,MAAM;6EA8Hb,CAAC;wBACkB,eAAe,CAAC,OAAO,eAAe,EAAE,WAAW,CAAC;AAAzE,wBAA0E;AAa1E,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vc-shell/framework",
3
- "version": "1.1.77",
3
+ "version": "1.1.78",
4
4
  "type": "module",
5
5
  "main": "./dist/framework.js",
6
6
  "types": "./dist/index.d.ts",
@@ -90,9 +90,9 @@
90
90
  "@fullhuman/postcss-purgecss": "^7.0.2",
91
91
  "@laynezh/vite-plugin-lib-assets": "v1.1.0",
92
92
  "@types/dompurify": "^3.0.5",
93
- "@vc-shell/api-client-generator": "^1.1.77",
94
- "@vc-shell/config-generator": "^1.1.77",
95
- "@vc-shell/ts-config": "^1.1.77",
93
+ "@vc-shell/api-client-generator": "^1.1.78",
94
+ "@vc-shell/config-generator": "^1.1.78",
95
+ "@vc-shell/ts-config": "^1.1.78",
96
96
  "@vitejs/plugin-vue": "^5.2.3",
97
97
  "@vue/test-utils": "^2.4.5",
98
98
  "cypress-signalr-mock": "^1.5.0",
@@ -84,6 +84,7 @@ const {
84
84
  saveLayoutToLocalStorage,
85
85
  loadLayoutFromLocalStorage,
86
86
  initializeWithBuiltInPositions,
87
+ handleNewWidget,
87
88
  } = useDashboardGrid();
88
89
 
89
90
  // Initialize cellSize calculator
@@ -116,6 +117,28 @@ watch(gridContainerRef, (container) => {
116
117
  }
117
118
  });
118
119
 
120
+ // Watch for new widgets and handle their placement
121
+ watch(
122
+ widgets,
123
+ (newWidgets, oldWidgets) => {
124
+ if (!oldWidgets || newWidgets.length > oldWidgets.length) {
125
+ // New widgets have been added
126
+ const newWidgetIds = newWidgets
127
+ .filter(w => !oldWidgets?.some(ow => ow.id === w.id))
128
+ .map(w => w.id);
129
+
130
+ newWidgetIds.forEach(widgetId => {
131
+ const widget = newWidgets.find(w => w.id === widgetId);
132
+ if (widget && !layout.value.has(widgetId)) {
133
+ // Widget doesn't have a position yet, find a free one
134
+ handleNewWidget(widget);
135
+ }
136
+ });
137
+ }
138
+ },
139
+ { immediate: false },
140
+ );
141
+
119
142
  // Watch for layout changes and save to localStorage
120
143
  watch(
121
144
  layout,
@@ -78,6 +78,15 @@ export function useDashboardGrid() {
78
78
  const initializeLayout = (): void => {
79
79
  // Priority 1: Loading from localStorage
80
80
  const layoutLoadedFromStorage = loadLayoutFromLocalStorage();
81
+
82
+ // Check for new widgets that don't have positions in localStorage
83
+ const savedPositions = persistence.getSavedPositions();
84
+ const widgetsWithoutSavedPosition = widgets.value.filter(w => !savedPositions[w.id] && !layout.value.has(w.id));
85
+
86
+ if (widgetsWithoutSavedPosition.length > 0) {
87
+ // Place new widgets without collisions
88
+ widgetLayout.placeNewWidgets(widgetsWithoutSavedPosition, widgets.value, layout.value);
89
+ }
81
90
 
82
91
  // Priority 2: Using built-in widget positions
83
92
  if (!layoutLoadedFromStorage) {
@@ -90,6 +99,21 @@ export function useDashboardGrid() {
90
99
  }
91
100
  };
92
101
 
102
+ /**
103
+ * Handles registration of new widgets after initialization
104
+ */
105
+ const handleNewWidget = (widget: IDashboardWidget): void => {
106
+ // Check if widget already has a valid position from localStorage
107
+ const savedPositions = persistence.getSavedPositions();
108
+ if (savedPositions[widget.id]) {
109
+ return; // Position already loaded from localStorage
110
+ }
111
+
112
+ // Find a free position for the new widget
113
+ const position = widgetLayout.findFreePosition(widget, widgets.value, layout.value);
114
+ dashboard.updateWidgetPosition(widget.id, position);
115
+ };
116
+
93
117
  return {
94
118
  widgets,
95
119
  layout,
@@ -100,5 +124,6 @@ export function useDashboardGrid() {
100
124
  arrangeWidgetsInRows,
101
125
  initializeWithBuiltInPositions,
102
126
  initializeLayout,
127
+ handleNewWidget,
103
128
  };
104
129
  }
@@ -66,6 +66,15 @@ export function useLayoutPersistence(
66
66
  }
67
67
  };
68
68
 
69
+ /**
70
+ * Gets saved positions for specific widgets
71
+ *
72
+ * @returns The saved positions
73
+ */
74
+ const getSavedPositions = (): Record<string, DashboardWidgetPosition> => {
75
+ return savedLayout.value;
76
+ };
77
+
69
78
  /**
70
79
  * Clears the saved layout from localStorage
71
80
  */
@@ -85,5 +94,6 @@ export function useLayoutPersistence(
85
94
  loadLayout,
86
95
  clearSavedLayout,
87
96
  hasSavedLayout,
97
+ getSavedPositions,
88
98
  };
89
99
  }
@@ -255,10 +255,49 @@ export function useWidgetLayout(updatePositionCallback: (widgetId: string, posit
255
255
  return true;
256
256
  };
257
257
 
258
+ /**
259
+ * Places new widgets without collisions with existing ones
260
+ *
261
+ * @param newWidgets The array of new widgets to place
262
+ * @param allWidgets The array of all widgets
263
+ * @param layout The map of widget positions
264
+ */
265
+ const placeNewWidgets = (
266
+ newWidgets: IDashboardWidget[],
267
+ allWidgets: IDashboardWidget[],
268
+ layout: Map<string, DashboardWidgetPosition>,
269
+ ): void => {
270
+ if (newWidgets.length === 0) return;
271
+
272
+ // Create a map of occupied cells from existing widgets
273
+ const occupiedCells = grid.createOccupiedCellsMap(allWidgets, layout);
274
+
275
+ // Sort new widgets by size (bigger first)
276
+ const sortedWidgets = [...newWidgets].sort((a, b) => {
277
+ const aSize = a.size.width * a.size.height;
278
+ const bSize = b.size.width * b.size.height;
279
+ return bSize - aSize;
280
+ });
281
+
282
+ // Place each new widget
283
+ for (const widget of sortedWidgets) {
284
+ const position = findOptimalPosition(widget, occupiedCells, grid.dynamicRows.value + grid.ROWS_BUFFER);
285
+ updatePositionCallback(widget.id, position);
286
+
287
+ // Update the map of occupied cells
288
+ for (let x = position.x; x < position.x + widget.size.width; x++) {
289
+ for (let y = position.y; y < position.y + widget.size.height; y++) {
290
+ occupiedCells.add(`${x},${y}`);
291
+ }
292
+ }
293
+ }
294
+ };
295
+
258
296
  return {
259
297
  findFreePosition,
260
298
  findOptimalPosition,
261
299
  arrangeWidgetsInRows,
262
300
  initializeWithBuiltInPositions,
301
+ placeNewWidgets,
263
302
  };
264
303
  }
@@ -10,7 +10,6 @@
10
10
  <!-- Field label -->
11
11
  <VcLabel
12
12
  v-if="label"
13
- class="vc-field__label"
14
13
  >
15
14
  <span>{{ label }}</span>
16
15
  <template
@@ -92,17 +91,13 @@ function copy(value: string) {
92
91
  @apply tw-flex;
93
92
 
94
93
  &--vertical {
95
- @apply tw-flex-col;
94
+ @apply tw-flex-col tw-gap-2;
96
95
  }
97
96
 
98
97
  &--horizontal {
99
98
  @apply tw-flex-row tw-items-center;
100
99
  }
101
100
 
102
- &__label {
103
- @apply tw-mb-2;
104
- }
105
-
106
101
  &__copy-button {
107
102
  @apply tw-ml-2;
108
103
  }
@@ -118,6 +118,7 @@ function onChange() {
118
118
  }
119
119
 
120
120
  input[type="radio"] {
121
+ flex-shrink: 0;
121
122
  border-radius: 50%;
122
123
  appearance: none;
123
124
  height: var(--radio-size);