@vc-shell/framework 1.1.65 → 1.1.66

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 (41) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/framework.js +6690 -6570
  3. package/dist/index.css +1 -1
  4. package/dist/shared/components/change-password-button/index.d.ts +1 -1
  5. package/dist/shared/components/generic-dropdown/generic-dropdown.vue.d.ts.map +1 -1
  6. package/dist/shared/components/logout-button/index.d.ts +1 -1
  7. package/dist/shared/modules/dynamic/composables/useFilterBuilder/index.d.ts.map +1 -1
  8. package/dist/shared/modules/dynamic/index.d.ts.map +1 -1
  9. package/dist/tsconfig.tsbuildinfo +1 -1
  10. package/dist/ui/components/atoms/vc-progress/vc-progress.vue.d.ts.map +1 -1
  11. package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-base-button.vue.d.ts.map +1 -1
  12. package/dist/ui/components/organisms/vc-table/_internal/vc-table-column-switcher/vc-table-column-switcher.vue.d.ts.map +1 -1
  13. 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 -1
  14. package/dist/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/vc-table-row/vc-table-row.vue.d.ts.map +1 -1
  15. package/dist/ui/components/organisms/vc-table/_internal/vc-table-empty/vc-table-empty.vue.d.ts.map +1 -1
  16. package/dist/ui/components/organisms/vc-table/composables/useTableColumnReorder.d.ts.map +1 -1
  17. package/dist/ui/components/organisms/vc-table/composables/useTableColumnResize.d.ts.map +1 -1
  18. package/dist/ui/components/organisms/vc-table/composables/useTableState.d.ts +1 -0
  19. package/dist/ui/components/organisms/vc-table/composables/useTableState.d.ts.map +1 -1
  20. package/dist/ui/components/organisms/vc-table/vc-table.vue.d.ts.map +1 -1
  21. package/package.json +4 -4
  22. package/shared/components/change-password-button/index.ts +1 -1
  23. package/shared/components/generic-dropdown/generic-dropdown.vue +10 -2
  24. package/shared/components/logout-button/index.ts +1 -1
  25. package/shared/modules/dynamic/composables/useFilterBuilder/index.ts +1 -2
  26. package/shared/modules/dynamic/index.ts +0 -1
  27. package/ui/components/atoms/index.ts +1 -1
  28. package/ui/components/atoms/vc-col/vc-col.stories.ts +4 -4
  29. package/ui/components/atoms/vc-icon/icons/index.ts +1 -1
  30. package/ui/components/atoms/vc-progress/vc-progress.vue +3 -8
  31. package/ui/components/atoms/vc-skeleton/vc-skeleton.vue +3 -3
  32. package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-base-button.vue +0 -1
  33. package/ui/components/organisms/vc-table/_internal/vc-table-column-switcher/vc-table-column-switcher.vue +1 -0
  34. package/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/vc-table-columns-header/vc-table-columns-header.vue +80 -10
  35. package/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/vc-table-row/vc-table-row.vue +9 -5
  36. package/ui/components/organisms/vc-table/_internal/vc-table-empty/vc-table-empty.vue +0 -1
  37. package/ui/components/organisms/vc-table/composables/useTableColumnReorder.ts +6 -4
  38. package/ui/components/organisms/vc-table/composables/useTableColumnResize.ts +148 -39
  39. package/ui/components/organisms/vc-table/composables/useTableState.ts +61 -8
  40. package/ui/components/organisms/vc-table/vc-table.vue +2 -1
  41. package/ui/composables/useAdaptiveItems.ts +1 -1
@@ -1,6 +1,5 @@
1
1
  import { ref, Ref } from "vue";
2
2
  import { TableColPartial } from "../types";
3
- import * as _ from "lodash-es";
4
3
 
5
4
  export function useTableColumnResize(
6
5
  internalColumns: Ref<TableColPartial[]>,
@@ -30,11 +29,43 @@ export function useTableColumnResize(
30
29
  }
31
30
 
32
31
  function handleMouseDown(e: MouseEvent, item: TableColPartial) {
32
+ // Prevent any default behavior and stop propagation
33
+ e.preventDefault();
34
+ e.stopPropagation();
35
+ e.stopImmediatePropagation();
36
+
37
+ if (!item.id) {
38
+ console.warn("Column has no id, cannot resize:", item);
39
+ return;
40
+ }
41
+
33
42
  const containerLeft = getOffset(headerRef.value as HTMLElement).left;
34
43
  resizeColumnElement.value = item;
35
44
  columnResizing.value = true;
36
45
  lastResize.value = e.pageX - containerLeft + (headerRef.value as HTMLDivElement).scrollLeft;
37
46
 
47
+ // Prevent body scroll during resize
48
+ document.body.style.overflow = "hidden";
49
+ document.body.style.userSelect = "none";
50
+
51
+
52
+ // If resizing any column and there are flexible columns, fix all column widths
53
+ if (headerRef.value) {
54
+ const hasFlexibleColumns = internalColumns.value.some((col) => col.visible !== false && !col.width);
55
+
56
+ if (hasFlexibleColumns) {
57
+ // Fix current widths for all visible columns
58
+ internalColumns.value.forEach((col) => {
59
+ if (col.visible !== false && col.id && !col.width) {
60
+ const columnElement = headerRef.value!.querySelector(`#${CSS.escape(col.id)}`) as HTMLElement;
61
+ if (columnElement) {
62
+ col.width = columnElement.offsetWidth + "px";
63
+ }
64
+ }
65
+ });
66
+ }
67
+ }
68
+
38
69
  bindColumnResizeEvents();
39
70
  }
40
71
 
@@ -53,6 +84,9 @@ export function useTableColumnResize(
53
84
  columnResizing.value = false;
54
85
  onColumnResizeEnd();
55
86
  }
87
+ // Restore body scroll in case resize was cancelled
88
+ document.body.style.overflow = "";
89
+ document.body.style.userSelect = "";
56
90
  };
57
91
  document.addEventListener("mouseup", columnResizeEndListener);
58
92
  }
@@ -70,12 +104,33 @@ export function useTableColumnResize(
70
104
  }
71
105
 
72
106
  function onColumnResize(event: MouseEvent) {
73
- if (columnResizing.value && resizer.value && headerRef.value) {
107
+ if (columnResizing.value && resizer.value && headerRef.value && resizeColumnElement.value) {
74
108
  const containerLeft = getOffset(headerRef.value).left;
75
109
  resizer.value.style.top = "0px";
76
- const leftOffset = event.pageX - containerLeft + headerRef.value.scrollLeft;
77
- resizer.value.style.left =
78
- Math.min(leftOffset, headerRef.value.offsetWidth - resizer.value.offsetWidth - 70) + "px";
110
+
111
+ // Calculate the left offset based on mouse position
112
+ let leftOffset = event.pageX - containerLeft + headerRef.value.scrollLeft;
113
+
114
+ // Check if this is the last visible column
115
+ const currentColIndex = internalColumns.value.findIndex((col) => col.id === resizeColumnElement.value!.id);
116
+ let isLastColumn = true;
117
+ if (currentColIndex !== -1) {
118
+ for (let i = currentColIndex + 1; i < internalColumns.value.length; i++) {
119
+ if (internalColumns.value[i].visible !== false) {
120
+ isLastColumn = false;
121
+ break;
122
+ }
123
+ }
124
+ }
125
+
126
+ // For the last column, limit the resizer position to prevent it from going beyond the table
127
+ if (isLastColumn) {
128
+ const maxLeft = headerRef.value.offsetWidth - resizer.value.offsetWidth - 1;
129
+ leftOffset = Math.min(leftOffset, maxLeft);
130
+ }
131
+
132
+ // Ensure resizer doesn't go beyond the right edge of the table
133
+ resizer.value.style.left = Math.min(leftOffset, headerRef.value.offsetWidth - resizer.value.offsetWidth) + "px";
79
134
  resizer.value.style.display = "block";
80
135
  }
81
136
  }
@@ -83,8 +138,47 @@ export function useTableColumnResize(
83
138
  function onColumnResizeEnd() {
84
139
  if (!resizer.value || !headerRef.value || !resizeColumnElement.value || lastResize.value === undefined) return;
85
140
 
86
- const delta = resizer.value.offsetLeft - lastResize.value;
87
- const columnElement = headerRef.value.querySelector(`#${resizeColumnElement.value.id}`);
141
+ // Calculate delta but limit it to prevent overflow
142
+ let delta = resizer.value.offsetLeft - lastResize.value;
143
+
144
+ // Check if this is the last visible column
145
+ const currentColIndex = internalColumns.value.findIndex((col) => col.id === resizeColumnElement.value!.id);
146
+ let isLastColumn = true;
147
+ if (currentColIndex !== -1) {
148
+ for (let i = currentColIndex + 1; i < internalColumns.value.length; i++) {
149
+ if (internalColumns.value[i].visible !== false) {
150
+ isLastColumn = false;
151
+ break;
152
+ }
153
+ }
154
+ }
155
+
156
+ // For the last column, limit delta to prevent overflow
157
+ if (isLastColumn && delta > 0) {
158
+ const columnElement = headerRef.value.querySelector(`#${CSS.escape(resizeColumnElement.value?.id ?? "")}`);
159
+ if (columnElement) {
160
+ const currentWidth = (columnElement as HTMLElement).offsetWidth;
161
+ const containerWidth = headerRef.value.offsetWidth;
162
+ let totalWidthOtherColumns = 0;
163
+
164
+ // Calculate total width of other visible columns
165
+ internalColumns.value.forEach((col, index) => {
166
+ if (index !== currentColIndex && col.visible !== false && col.id) {
167
+ const colEl = headerRef.value!.querySelector(`#${CSS.escape(col.id)}`) as HTMLElement;
168
+ if (colEl) {
169
+ totalWidthOtherColumns += colEl.offsetWidth;
170
+ }
171
+ }
172
+ });
173
+
174
+ // Maximum delta that won't cause overflow
175
+ const maxDelta = containerWidth - totalWidthOtherColumns - currentWidth - 1;
176
+ delta = Math.min(delta, maxDelta);
177
+ }
178
+ }
179
+ // Escape special characters in ID for querySelector
180
+ const escapedId = CSS.escape(resizeColumnElement.value?.id ?? "");
181
+ const columnElement = headerRef.value.querySelector(`#${escapedId}`);
88
182
 
89
183
  if (columnElement) {
90
184
  const columnWidth = (columnElement as HTMLElement).offsetWidth;
@@ -92,50 +186,65 @@ export function useTableColumnResize(
92
186
  const minWidth = 15;
93
187
 
94
188
  if (columnWidth + delta > parseInt(minWidth.toString(), 10)) {
95
- nextColumn.value = internalColumns.value[internalColumns.value.indexOf(resizeColumnElement.value) + 1];
189
+ const currentColIndex = internalColumns.value.findIndex((col) => col.id === resizeColumnElement.value?.id);
190
+
191
+ // Find next visible column
192
+ let nextVisibleColumn = undefined;
193
+ if (currentColIndex !== -1) {
194
+ for (let i = currentColIndex + 1; i < internalColumns.value.length; i++) {
195
+ if (internalColumns.value[i].visible !== false) {
196
+ nextVisibleColumn = internalColumns.value[i];
197
+ break;
198
+ }
199
+ }
200
+ }
201
+
202
+ nextColumn.value = nextVisibleColumn;
203
+
204
+ // Simply resize the current column without affecting others
205
+ if (newColumnWidth > minWidth) {
206
+ const colIndex = internalColumns.value.findIndex((col) => col.id === resizeColumnElement.value?.id);
96
207
 
97
- if (nextColumn.value) {
98
- const nextColElement = headerRef.value.querySelector(`#${nextColumn.value.id}`);
208
+ if (colIndex !== -1 && internalColumns.value[colIndex]) {
209
+ // For the last column, check max width to prevent overflow
210
+ if (!nextColumn.value && headerRef.value) {
211
+ const tableWidth = headerRef.value.offsetWidth;
212
+ let totalWidthBeforeLastColumn = 0;
99
213
 
100
- if (nextColElement) {
101
- const nextColumnWidth = (nextColElement as HTMLElement).offsetWidth - delta;
214
+ // Sum up widths of all columns before the last one
215
+ internalColumns.value.forEach((col, index) => {
216
+ if (index < colIndex && col.visible !== false && col.id) {
217
+ const colElement = headerRef.value!.querySelector(`#${CSS.escape(col.id)}`) as HTMLElement;
218
+ if (colElement) {
219
+ totalWidthBeforeLastColumn += colElement.offsetWidth;
220
+ }
221
+ }
222
+ });
102
223
 
103
- if (newColumnWidth > minWidth && nextColumnWidth > minWidth) {
104
- resizeTableCells(newColumnWidth, nextColumnWidth);
224
+ // Calculate the maximum allowed width for the last column
225
+ const maxAllowedWidth = tableWidth - totalWidthBeforeLastColumn - 1;
226
+ const finalWidth = Math.min(newColumnWidth, maxAllowedWidth);
227
+
228
+ if (finalWidth > minWidth) {
229
+ internalColumns.value[colIndex].width = finalWidth + "px";
230
+ }
231
+ } else {
232
+ // For non-last columns, just set the new width
233
+ internalColumns.value[colIndex].width = newColumnWidth + "px";
105
234
  }
106
235
  }
107
- } else {
108
- if (newColumnWidth > minWidth) {
109
- resizeColumnElement.value.width = newColumnWidth + "px";
110
- }
111
236
  }
112
237
  }
113
238
 
114
239
  resizer.value.style.display = "none";
115
240
  unbindColumnResizeEvents();
116
- onStateChange();
117
- }
118
- }
119
-
120
- function resizeTableCells(newColumnWidth: number, nextColumnWidth: number) {
121
- if (!headerRef.value || !resizeColumnElement.value) return;
122
241
 
123
- const colIndex = internalColumns.value.findIndex((col) => col.id === resizeColumnElement.value?.id);
124
- const widths: number[] = [];
125
- const tableHeaders = headerRef.value.querySelectorAll(".vc-table-columns-header__cell") as NodeListOf<HTMLElement>;
242
+ // Restore body scroll after resize
243
+ document.body.style.overflow = "";
244
+ document.body.style.userSelect = "";
126
245
 
127
- tableHeaders.forEach((header) => widths.push(header.offsetWidth));
128
-
129
- internalColumns.value.forEach((col, index) => {
130
- col.width = widths[index] + "px";
131
- });
132
-
133
- widths.forEach((width, index) => {
134
- const colWidth =
135
- index === colIndex ? newColumnWidth : nextColumnWidth && index === colIndex + 1 ? nextColumnWidth : width;
136
-
137
- internalColumns.value[index].width = colWidth + "px";
138
- });
246
+ onStateChange();
247
+ }
139
248
  }
140
249
 
141
250
  return {
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { ref, Ref, computed, toValue, toRefs, MaybeRef } from "vue";
2
+ import { ref, Ref, computed, toValue, toRefs, MaybeRef, nextTick } from "vue";
3
3
  import { useLocalStorage } from "@vueuse/core";
4
4
  import { cloneDeep, pick, unionBy } from "lodash-es";
5
5
  import { TableColPartial } from "../types";
@@ -12,10 +12,11 @@ export interface UseTableStateOptions {
12
12
  "auto" | "defined" | MaybeRef<ITableColumns[]> | (() => ITableColumns[])
13
13
  >;
14
14
  expanded: Ref<boolean, boolean>;
15
+ headerRef?: Ref<HTMLElement | undefined>;
15
16
  }
16
17
 
17
18
  export function useTableState(options: UseTableStateOptions) {
18
- const { stateKey, columnSelector, expanded } = toRefs(options);
19
+ const { stateKey, columnSelector, expanded, headerRef } = toRefs(options);
19
20
 
20
21
  const state = useLocalStorage<TableColPartial[]>(`VC_TABLE_STATE_${stateKey.value.toUpperCase()}`, []);
21
22
  const internalColumns = ref<TableColPartial[]>([]) as Ref<TableColPartial[]>;
@@ -41,25 +42,30 @@ export function useTableState(options: UseTableStateOptions) {
41
42
  if (!expanded?.value) {
42
43
  return x.alwaysVisible;
43
44
  }
44
- return x;
45
+ return true;
45
46
  });
46
47
  });
47
48
 
48
49
  function saveState() {
49
50
  console.debug("[@vc-shell/framework#vc-table.vue] - Save state");
50
51
  const colsClone = cloneDeep(internalColumns.value);
51
- state.value = colsClone.map((col) => pick(col, "id", "visible", "width", "predefined"));
52
+ state.value = colsClone.map((col) => pick(col, "id", "visible", "width", "predefined", "title"));
52
53
  }
53
54
 
54
55
  function mergeColumns(storedCol: TableColPartial, predefinedCol: TableColPartial | undefined) {
55
56
  if (predefinedCol) {
56
57
  if (predefinedCol.predefined && !storedCol.predefined) {
57
- return { ...predefinedCol, predefined: true };
58
+ return {
59
+ ...predefinedCol,
60
+ predefined: true,
61
+ width: storedCol.width || predefinedCol.width,
62
+ visible: storedCol.visible !== undefined ? storedCol.visible : predefinedCol.visible
63
+ };
58
64
  } else {
59
65
  return {
60
66
  ...predefinedCol,
61
67
  visible: storedCol.visible,
62
- width: storedCol.width,
68
+ width: storedCol.width || predefinedCol.width,
63
69
  title: predefinedCol.title || storedCol.title || "",
64
70
  };
65
71
  }
@@ -145,15 +151,62 @@ export function useTableState(options: UseTableStateOptions) {
145
151
  }
146
152
 
147
153
  function toggleColumn(item: ITableColumns) {
154
+ // Find the original column to preserve its title
155
+ const originalColumn = allColumns.value.find((col) => col.id === item.id);
156
+
148
157
  // if item is not in internalColumns, add it
149
158
  if (!internalColumns.value.find((x) => x.id === item.id)) {
150
- internalColumns.value.push(item);
159
+ const newCol = {
160
+ ...item,
161
+ title: item.title || originalColumn?.title || "",
162
+ width: item.width || originalColumn?.width,
163
+ };
164
+
165
+ // Don't set any width for new columns - they'll take available space
166
+ // The flexbox will handle the distribution
167
+ if (!newCol.width) {
168
+ // Keep it undefined to use flexbox
169
+ newCol.width = undefined;
170
+ }
171
+
172
+ internalColumns.value.push(newCol);
173
+
174
+ // When adding a new column, check if we need to adjust widths to prevent overflow
175
+ const visibleColumns = internalColumns.value.filter(col => col.visible !== false);
176
+ const hasFixedWidths = visibleColumns.some(col => col.width);
177
+
178
+ if (hasFixedWidths) {
179
+ // Calculate total fixed width
180
+ let totalFixedWidth = 0;
181
+ visibleColumns.forEach(col => {
182
+ if (col.width && typeof col.width === 'string') {
183
+ const width = parseInt(col.width);
184
+ if (!isNaN(width)) {
185
+ totalFixedWidth += width;
186
+ }
187
+ }
188
+ });
189
+
190
+ // If total fixed width is too large, remove fixed widths to allow flex distribution
191
+ if (totalFixedWidth > 800) { // Assuming a reasonable container width
192
+ internalColumns.value = internalColumns.value.map(col => ({
193
+ ...col,
194
+ width: undefined // Let flexbox handle it
195
+ }));
196
+ }
197
+ }
151
198
  }
152
199
 
153
200
  if (item) {
154
201
  internalColumns.value = internalColumns.value.map((x) => {
155
202
  if (x.id === item.id) {
156
- x = item;
203
+ const updatedCol = {
204
+ ...item,
205
+ title: item.title || originalColumn?.title || x.title || "",
206
+ width: item.width || originalColumn?.width || x.width,
207
+ };
208
+
209
+ return updatedCol;
157
210
  }
158
211
  return x;
159
212
  });
@@ -273,7 +273,8 @@ const emit = defineEmits<{
273
273
 
274
274
  const instance = getCurrentInstance();
275
275
 
276
- const { items, columns, stateKey, columnSelector, expanded, selectionItems, enableItemActions, itemActionBuilder } = toRefs(props);
276
+ const { items, columns, stateKey, columnSelector, expanded, selectionItems, enableItemActions, itemActionBuilder } =
277
+ toRefs(props);
277
278
 
278
279
  // template refs
279
280
  const tableBody = ref<HTMLElement | null>();
@@ -207,4 +207,4 @@ export function useAdaptiveItems<T>(options: {
207
207
  // Export function for updating observer
208
208
  updateObserver: setupElementsObserver,
209
209
  };
210
- }
210
+ }