@vc-shell/framework 1.0.232 → 1.0.234

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 (75) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/core/types/index.ts +14 -1
  3. package/dist/core/types/index.d.ts +12 -2
  4. package/dist/core/types/index.d.ts.map +1 -1
  5. package/dist/framework.js +14813 -14535
  6. package/dist/index.css +1 -1
  7. package/dist/locales/en.json +8 -2
  8. package/dist/shared/components/notifications/components/notification-container/index.d.ts +1 -1
  9. package/dist/shared/components/user-dropdown-button/user-dropdown-button.vue.d.ts.map +1 -1
  10. package/dist/shared/modules/dynamic/components/fields/GalleryField.d.ts.map +1 -1
  11. package/dist/shared/modules/dynamic/components/fields/StatusField.d.ts.map +1 -1
  12. package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts.map +1 -1
  13. package/dist/shared/modules/dynamic/types/index.d.ts +9 -4
  14. package/dist/shared/modules/dynamic/types/index.d.ts.map +1 -1
  15. package/dist/tsconfig.tsbuildinfo +1 -1
  16. package/dist/ui/components/atoms/vc-badge/vc-badge.stories.d.ts +187 -17
  17. package/dist/ui/components/atoms/vc-badge/vc-badge.stories.d.ts.map +1 -1
  18. package/dist/ui/components/atoms/vc-badge/vc-badge.vue.d.ts +19 -3
  19. package/dist/ui/components/atoms/vc-badge/vc-badge.vue.d.ts.map +1 -1
  20. package/dist/ui/components/atoms/vc-button/vc-button.stories.d.ts +91 -91
  21. package/dist/ui/components/atoms/vc-button/vc-button.vue.d.ts +1 -1
  22. package/dist/ui/components/atoms/vc-icon/vc-icon.stories.d.ts +9 -9
  23. package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts +1 -1
  24. package/dist/ui/components/atoms/vc-image/index.d.ts +12 -3
  25. package/dist/ui/components/atoms/vc-image/index.d.ts.map +1 -1
  26. package/dist/ui/components/atoms/vc-image/vc-image.stories.d.ts +12 -3
  27. package/dist/ui/components/atoms/vc-image/vc-image.stories.d.ts.map +1 -1
  28. package/dist/ui/components/atoms/vc-image/vc-image.vue.d.ts +5 -1
  29. package/dist/ui/components/atoms/vc-image/vc-image.vue.d.ts.map +1 -1
  30. package/dist/ui/components/atoms/vc-widget/index.d.ts +6 -0
  31. package/dist/ui/components/atoms/vc-widget/index.d.ts.map +1 -1
  32. package/dist/ui/components/atoms/vc-widget/vc-widget.stories.d.ts +6 -0
  33. package/dist/ui/components/atoms/vc-widget/vc-widget.stories.d.ts.map +1 -1
  34. package/dist/ui/components/atoms/vc-widget/vc-widget.vue.d.ts +1 -0
  35. package/dist/ui/components/atoms/vc-widget/vc-widget.vue.d.ts.map +1 -1
  36. package/dist/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.stories.d.ts +3 -3
  37. package/dist/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.vue.d.ts +1 -1
  38. package/dist/ui/components/molecules/vc-editor/vc-editor.vue.d.ts.map +1 -1
  39. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue.d.ts +1 -0
  40. 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
  41. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue.d.ts +1 -0
  42. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue.d.ts.map +1 -1
  43. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue.d.ts.map +1 -1
  44. package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
  45. package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue.d.ts +1 -1
  46. package/dist/ui/components/organisms/vc-blade/vc-blade.stories.d.ts +2 -0
  47. package/dist/ui/components/organisms/vc-blade/vc-blade.stories.d.ts.map +1 -1
  48. package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts +2 -0
  49. package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts.map +1 -1
  50. package/dist/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue.d.ts +3 -0
  51. package/dist/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue.d.ts.map +1 -1
  52. package/dist/ui/components/organisms/vc-table/vc-table.vue.d.ts.map +1 -1
  53. package/package.json +4 -4
  54. package/shared/components/app-switcher/components/vc-app-switcher/vc-app-switcher.vue +1 -1
  55. package/shared/components/user-dropdown-button/user-dropdown-button.vue +11 -3
  56. package/shared/modules/dynamic/components/fields/Card.ts +1 -1
  57. package/shared/modules/dynamic/components/fields/GalleryField.ts +1 -0
  58. package/shared/modules/dynamic/components/fields/StatusField.ts +39 -3
  59. package/shared/modules/dynamic/pages/dynamic-blade-form.vue +17 -22
  60. package/shared/modules/dynamic/types/index.ts +14 -6
  61. package/ui/components/atoms/vc-badge/vc-badge.stories.ts +3 -3
  62. package/ui/components/atoms/vc-badge/vc-badge.vue +58 -10
  63. package/ui/components/atoms/vc-container/vc-container.vue +2 -2
  64. package/ui/components/atoms/vc-image/vc-image.vue +3 -1
  65. package/ui/components/atoms/vc-widget/vc-widget.vue +42 -22
  66. package/ui/components/molecules/vc-editor/vc-editor.vue +5 -1
  67. package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +1 -1
  68. package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue +21 -10
  69. package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue +3 -0
  70. package/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +60 -9
  71. package/ui/components/organisms/vc-app/vc-app.vue +0 -1
  72. package/ui/components/organisms/vc-blade/vc-blade.vue +89 -2
  73. package/ui/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue +1 -0
  74. package/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue +27 -7
  75. package/ui/components/organisms/vc-table/vc-table.vue +123 -53
@@ -1,12 +1,16 @@
1
1
  <template>
2
2
  <div
3
3
  v-if="isMenuVisible"
4
- class="tw-relative tw-w-[var(--app-menu-width)] tw-transition tw-duration-100 tw-pt-[22px]"
4
+ class="tw-relative tw-w-[var(--app-menu-width)] [transition:width_300ms_cubic-bezier(0.2,0,0,1)_0s] tw-pt-[22px]"
5
5
  :class="{
6
6
  'vc-app-menu_mobile tw-hidden !tw-fixed !tw-left-0 !tw-top-0 !tw-w-full !tw-bottom-0 !tw-z-[9999]':
7
7
  $isMobile.value,
8
8
  '!tw-block': isMobileVisible,
9
+ '!tw-w-[63px]': $isDesktop.value && !isExpanded,
9
10
  }"
11
+ @mouseenter="!isExpanded && expandOverHandler(true)"
12
+ @mouseover="!isExpanded && expandOverHandler(true)"
13
+ @mouseleave="expandOverHandler(false)"
10
14
  >
11
15
  <!-- Show backdrop overlay on mobile devices -->
12
16
  <div
@@ -14,17 +18,41 @@
14
18
  class="tw-absolute tw-left-0 tw-top-0 tw-right-0 tw-bottom-0 tw-z-[9998] tw-bg-[#808c99] tw-opacity-60"
15
19
  @click="isMobileVisible = false"
16
20
  ></div>
17
- <div class="vc-app-menu__inner tw-flex tw-flex-col tw-h-full">
21
+ <div
22
+ class="!tw-absolute vc-app-menu__inner tw-flex tw-flex-col tw-h-full [transition:width_300ms_cubic-bezier(0.2,0,0,1)_0s] tw-z-[9999] tw-top-0 tw-bottom-0 tw-bg-[color:var(--app-background)] tw-shadow-[inset_0px_2px_5px_0px_#3654751A]"
23
+ :class="{
24
+ 'tw-left-0 tw-pt-[22px]': $isDesktop.value,
25
+ '!tw-w-[63px] !tw-shadow-[inset_4px_2px_5px_0px_#3654751A]': $isDesktop.value && !isExpanded && !isExpandedOver,
26
+ 'tw-w-[var(--app-menu-width)]': $isDesktop.value && (isExpanded || isExpandedOver),
27
+ 'tw-shadow-none': $isDesktop.value && isExpanded,
28
+ }"
29
+ >
18
30
  <!-- Show menu close handler on mobile devices -->
19
31
  <div
20
32
  v-if="$isMobile.value"
21
33
  class="tw-text-[#319ed4] tw-flex tw-justify-end tw-items-center tw-p-4 tw-cursor-pointer"
22
34
  >
23
- <VcIcon
24
- icon="fas fa-times"
25
- size="xl"
26
- @click="isMobileVisible = false"
27
- ></VcIcon>
35
+ <button @click="isMobileVisible = false">
36
+ <VcIcon
37
+ icon="fas fa-times"
38
+ size="xl"
39
+ ></VcIcon>
40
+ </button>
41
+ </div>
42
+
43
+ <div
44
+ v-if="$isDesktop.value"
45
+ class="tw-pl-[21px] tw-pb-2"
46
+ >
47
+ <button
48
+ class="tw-p-[10px] tw-h-[var(--app-menu-item-height)] tw-rounded tw-text-[color:var(--app-menu-burger-color)] hover:tw-bg-[color:var(--app-menu-burger-background-color)]"
49
+ @click="toggleMenu"
50
+ >
51
+ <VcIcon
52
+ icon="fas fa-bars"
53
+ size="xl"
54
+ ></VcIcon>
55
+ </button>
28
56
  </div>
29
57
 
30
58
  <!-- Show scrollable area with menu items -->
@@ -32,7 +60,13 @@
32
60
  :no-padding="true"
33
61
  class="tw-grow tw-basis-0"
34
62
  >
35
- <div class="tw-gap-[5px] tw-flex tw-flex-col tw-px-6 tw-h-full">
63
+ <div
64
+ class="tw-gap-[5px] tw-flex tw-flex-col tw-h-full"
65
+ :class="{
66
+ 'tw-px-[21px]': ($isDesktop.value && (isExpanded || isExpandedOver)) || $isMobile.value,
67
+ 'tw-pl-[21px] tw-pr-[2px]': $isDesktop.value && !isExpanded && !isExpandedOver,
68
+ }"
69
+ >
36
70
  <slot
37
71
  v-if="!$isDesktop.value"
38
72
  name="mobile"
@@ -48,6 +82,7 @@
48
82
  :icon="item.icon"
49
83
  :title="item.title as string"
50
84
  :children="item.children"
85
+ :expand="$isDesktop.value ? isExpanded || isExpandedOver : true"
51
86
  @click="
52
87
  (event) => {
53
88
  $emit('item:click', event ? event : item);
@@ -58,7 +93,7 @@
58
93
  </div>
59
94
  </VcContainer>
60
95
  <div
61
- class="tw-text-[color:var(--app-menu-version-color)] tw-text-xs tw-mt-auto tw-self-center tw-p-1"
96
+ class="tw-text-[color:var(--app-menu-version-color)] tw-text-xs tw-mt-auto tw-self-start tw-py-1 tw-pl-4"
62
97
  @click="$emit('version:click')"
63
98
  >
64
99
  {{ version }}
@@ -73,6 +108,7 @@ import VcAppMenuItem from "./_internal/vc-app-menu-item/vc-app-menu-item.vue";
73
108
  import { VcContainer, VcIcon } from "./../../../../";
74
109
  import { useMenuService } from "../../../../../../core/composables";
75
110
  import { MenuItem } from "../../../../../../core/types";
111
+ import { useLocalStorage } from "@vueuse/core";
76
112
 
77
113
  export interface Props {
78
114
  version: string;
@@ -91,6 +127,8 @@ withDefaults(defineProps<Props>(), {
91
127
 
92
128
  defineEmits<Emits>();
93
129
  const { menuItems } = useMenuService();
130
+ const isExpanded = useLocalStorage("VC_APP_MENU_EXPANDED", true);
131
+ const isExpandedOver = ref(false);
94
132
 
95
133
  const isMobileVisible = ref(false);
96
134
 
@@ -98,6 +136,16 @@ const isMenuVisible = computed(() => {
98
136
  return !!menuItems.value.length;
99
137
  });
100
138
 
139
+ function toggleMenu() {
140
+ isExpanded.value = !isExpanded.value;
141
+ }
142
+
143
+ function expandOverHandler(state: boolean) {
144
+ if (isExpandedOver.value !== state) {
145
+ isExpandedOver.value = state;
146
+ }
147
+ }
148
+
101
149
  defineExpose({
102
150
  isMobileVisible,
103
151
  });
@@ -108,6 +156,9 @@ defineExpose({
108
156
  --app-menu-width: 230px;
109
157
  --app-menu-background-color: #ffffff;
110
158
  --app-menu-version-color: #838d9a;
159
+
160
+ --app-menu-burger-background-color: rgba(255, 255, 255, 0.5);
161
+ --app-menu-burger-color: #319ed4;
111
162
  }
112
163
 
113
164
  .vc-app-menu {
@@ -10,7 +10,6 @@
10
10
  :class="[
11
11
  {
12
12
  'vc-app_touch': $isTouch,
13
- 'vc-app_phone': $isPhone.value,
14
13
  'vc-app_mobile': $isMobile.value,
15
14
  },
16
15
  ]"
@@ -70,12 +70,64 @@
70
70
  class="tw-shrink-0"
71
71
  :items="toolbarItems"
72
72
  ></VcBladeToolbar>
73
- <slot></slot>
73
+
74
+ <div class="tw-flex-1 tw-overflow-auto">
75
+ <div
76
+ class="tw-flex tw-flex-row tw-h-full"
77
+ :class="{
78
+ 'tw-flex-col': $isMobile.value,
79
+ }"
80
+ >
81
+ <slot></slot>
82
+
83
+ <div
84
+ v-show="$slots['widgets'] && !isWidgetContainerEmpty"
85
+ ref="widgetsRef"
86
+ class="vc-blade__widgets tw-flex"
87
+ :class="{
88
+ 'tw-w-[var(--blade-widgets-width)] tw-flex-col': $isDesktop.value && !isExpanded,
89
+ 'tw-w-[var(--blade-widgets-width-expanded)] tw-flex-col': $isDesktop.value && isExpanded,
90
+ 'tw-w-auto tw-border-t tw-border-solid tw-border-t-[#eaedf3] tw-flex-row': $isMobile.value,
91
+ }"
92
+ >
93
+ <div
94
+ ref="widgetsContainerRef"
95
+ class="vc-blade__widget-container tw-flex tw-overflow-auto"
96
+ :class="{
97
+ 'tw-flex-col': $isDesktop.value,
98
+ 'tw-flex-row': $isMobile.value,
99
+ }"
100
+ >
101
+ <slot
102
+ name="widgets"
103
+ :is-expanded="isExpanded"
104
+ ></slot>
105
+ </div>
106
+
107
+ <div
108
+ class="tw-flex tw-flex-auto"
109
+ :class="{
110
+ 'tw-flex-col tw-justify-end': $isDesktop.value,
111
+ 'tw-w-12 tw-max-w-12 tw-bg-white tw-items-center tw-justify-center tw-px-4 tw-ml-auto': $isMobile.value,
112
+ }"
113
+ >
114
+ <VcIcon
115
+ class="tw-self-center tw-justify-self-center tw-text-[#a1c0d4] tw-cursor-pointer hover:tw-text-[#7ea8c4]"
116
+ :class="{
117
+ 'tw-mb-4': $isDesktop.value,
118
+ }"
119
+ :icon="`fas fa-chevron-${$isDesktop.value ? (isExpanded ? 'right' : 'left') : isExpanded ? 'up' : 'down'}`"
120
+ @click="toggleWidgets"
121
+ ></VcIcon>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </div>
74
126
  </div>
75
127
  </template>
76
128
 
77
129
  <script lang="ts" setup>
78
- import { computed, Ref, reactive, useAttrs, toRefs, toValue } from "vue";
130
+ import { computed, Ref, reactive, useAttrs, toRefs, toValue, ref, onMounted, onUpdated } from "vue";
79
131
  import { IBladeToolbar } from "../../../../core/types";
80
132
  import { usePopup } from "./../../../../shared";
81
133
  import { useI18n } from "vue-i18n";
@@ -83,6 +135,7 @@ import VcBladeHeader from "./_internal/vc-blade-header/vc-blade-header.vue";
83
135
  import VcBladeToolbar from "./_internal/vc-blade-toolbar/vc-blade-toolbar.vue";
84
136
  import { VcButton, VcIcon } from "./../../";
85
137
  import vcPopupError from "../../../../shared/components/common/popup/vc-popup-error.vue";
138
+ import { useLocalStorage } from "@vueuse/core";
86
139
 
87
140
  export interface Props {
88
141
  icon?: string;
@@ -117,12 +170,39 @@ withDefaults(defineProps<Props>(), {
117
170
  defineSlots<{
118
171
  actions: void;
119
172
  default: void;
173
+ widgets: void;
120
174
  }>();
121
175
 
122
176
  defineEmits<Emits>();
123
177
  const attrs = useAttrs();
124
178
  const { maximized, error }: { maximized?: Ref<boolean>; error?: Ref<string> } = toRefs(reactive(attrs));
125
179
  const { t } = useI18n({ useScope: "global" });
180
+ const widgetsRef = ref();
181
+ const widgetsContainerRef = ref();
182
+
183
+ const isExpanded = useLocalStorage("VC_BLADE_WIDGETS_IS_EXPANDED", true);
184
+
185
+ const toggleWidgets = () => {
186
+ isExpanded.value = !isExpanded.value;
187
+ };
188
+ const isWidgetContainerEmpty = ref(false);
189
+
190
+ const checkEmpty = (el: HTMLElement) => {
191
+ const isEmpty = !el.innerText.trim() && Array.from(el.children).every((node) => node.nodeType === Node.COMMENT_NODE);
192
+ isWidgetContainerEmpty.value = isEmpty;
193
+ };
194
+
195
+ onMounted(() => {
196
+ if (widgetsRef.value) {
197
+ checkEmpty(widgetsContainerRef.value);
198
+ }
199
+ });
200
+
201
+ onUpdated(() => {
202
+ if (widgetsRef.value) {
203
+ checkEmpty(widgetsContainerRef.value);
204
+ }
205
+ });
126
206
 
127
207
  const { open } = usePopup({
128
208
  component: vcPopupError,
@@ -142,9 +222,16 @@ const { open } = usePopup({
142
222
  --blade-color-error: #f14e4e;
143
223
  --blade-color-unsaved-changes: #82a6bd;
144
224
  --blade-color-unsaved-changes: #82a6bd;
225
+
226
+ --blade-widgets-width: 36px;
227
+ --blade-widgets-width-expanded: 80px;
145
228
  }
146
229
 
147
230
  .vc-app_mobile .vc-blade {
148
231
  @apply tw-m-0 tw-rounded-none;
149
232
  }
233
+
234
+ .vc-app_mobile .vc-blade__widgets {
235
+ @apply tw-flex tw-flex-row;
236
+ }
150
237
  </style>
@@ -129,6 +129,7 @@
129
129
  size="s"
130
130
  aspect="1x1"
131
131
  :src="value as string"
132
+ :empty-icon="('emptyIcon' in cell && cell.emptyIcon) || undefined"
132
133
  background="contain"
133
134
  />
134
135
  </template>
@@ -9,12 +9,21 @@
9
9
  >
10
10
  <div
11
11
  ref="target"
12
- class="tw-top-0 tw-left-0 tw-bottom-0 tw-right-0 tw-w-full tw-h-full tw-absolute tw-flex-shrink-0 tw-bg-white"
12
+ class="tw-top-0 tw-left-0 tw-bottom-0 tw-right-0 tw-w-full tw-h-full tw-absolute tw-flex-shrink-0 tw-bg-white tw-flex tw-flex-row"
13
13
  :class="{ animated: !isSwiping, 'vc-table-mobile__item_selected': isSelected }"
14
14
  :style="{ left }"
15
15
  >
16
- <!-- Mobile item slot content -->
17
- <slot></slot>
16
+ <div
17
+ v-if="anySelected"
18
+ class="tw-pl-4 tw-flex tw-items-center tw-justify-center tw-border-b tw-border-solid tw-border-b-[#e3e7ec]"
19
+ >
20
+ <VcCheckbox :model-value="unref(isSelected ?? false)"></VcCheckbox>
21
+ </div>
22
+ <div class="tw-flex-1 tw-w-full">
23
+ <div class="tw-flex tw-flex-col tw-h-full tw-border-b tw-border-solid tw-border-b-[#e3e7ec]">
24
+ <slot></slot>
25
+ </div>
26
+ </div>
18
27
  </div>
19
28
  <div class="tw-flex tw-justify-between tw-flex-auto">
20
29
  <!-- Left swipe actions -->
@@ -116,10 +125,10 @@
116
125
  </template>
117
126
 
118
127
  <script lang="ts" setup generic="T extends TableItem | string">
119
- import { Ref, computed, ref, onMounted, watch, onUpdated, nextTick } from "vue";
128
+ import { Ref, computed, ref, onMounted, watch, onUpdated, unref } from "vue";
120
129
  import { IActionBuilderResult } from "../../../../../../core/types";
121
130
  import { useI18n } from "vue-i18n";
122
- import { useSwipe, watchDebounced } from "@vueuse/core";
131
+ import { useElementVisibility, useSwipe, watchDebounced } from "@vueuse/core";
123
132
  import { vOnClickOutside } from "@vueuse/components";
124
133
 
125
134
  export interface Emits {
@@ -139,6 +148,7 @@ const props = defineProps<{
139
148
  swipingItem?: string;
140
149
  isSelected?: boolean;
141
150
  index: number;
151
+ selection?: T[];
142
152
  }>();
143
153
 
144
154
  const emit = defineEmits<Emits>();
@@ -150,6 +160,8 @@ const target = ref<HTMLElement | null>(null);
150
160
  const container = ref<HTMLElement | null>(null);
151
161
  const containerWidth = computed(() => container.value?.offsetWidth);
152
162
  const left = ref("0");
163
+ const anySelected = computed(() => props.selection && props.selection.length > 0);
164
+ const targetIsVisible = useElementVisibility(container);
153
165
 
154
166
  const actionsWidth = ref("0");
155
167
 
@@ -193,14 +205,16 @@ const { isSwiping, lengthX } = useSwipe(target, {
193
205
 
194
206
  const rightSwipeActions = computed(
195
207
  () =>
196
- (itemActions.value &&
208
+ (props.selection?.length === 0 &&
209
+ itemActions.value &&
197
210
  itemActions.value.length &&
198
211
  itemActions.value.filter((actions: IActionBuilderResult) => actions.position === "right")) ||
199
212
  undefined,
200
213
  );
201
214
  const leftSwipeActions = computed(
202
215
  () =>
203
- (itemActions.value &&
216
+ (props.selection?.length === 0 &&
217
+ itemActions.value &&
204
218
  itemActions.value.length &&
205
219
  itemActions.value.filter((actions: IActionBuilderResult) => actions.position === "left")) ||
206
220
  undefined,
@@ -233,6 +247,8 @@ watchDebounced(
233
247
  { deep: true, debounce: 450 },
234
248
  );
235
249
 
250
+ watch(() => targetIsVisible.value, adjustHeight);
251
+
236
252
  function reset() {
237
253
  left.value = "0";
238
254
  actionsWidth.value = "0";
@@ -264,6 +280,10 @@ function handleHold() {
264
280
  }
265
281
 
266
282
  function handleClick() {
283
+ if (anySelected.value) {
284
+ emit("select");
285
+ return;
286
+ }
267
287
  emit("click");
268
288
  }
269
289
  </script>
@@ -1,14 +1,71 @@
1
1
  <template>
2
2
  <div
3
3
  v-loading="unref(loading) || columnsInit"
4
- class="tw-relative tw-overflow-hidden tw-flex tw-flex-col tw-grow tw-basis-0 tw-border tw-border-[color:#eef0f2] tw-border-solid tw-border-t-0"
4
+ class="tw-relative tw-overflow-hidden tw-flex tw-flex-col tw-grow tw-basis-0 tw-border-[color:#eef0f2] tw-border-solid tw-border-t-0"
5
5
  >
6
+ <div
7
+ v-if="$isMobile.value && selection.length > 0"
8
+ class="tw-flex tw-flex-col"
9
+ >
10
+ <div
11
+ class="tw-flex tw-flex-row tw-items-center tw-justify-between tw-px-4 tw-py-2 tw-min-h-[56px] tw-font-bold tw-text-lg tw-border-[color:#eef0f2] tw-border-b tw-border-solid tw-box-border"
12
+ >
13
+ <div class="tw-flex tw-flex-row tw-w-full tw-justify-between">
14
+ <div class="tw-flex tw-flex-row tw-items-center tw-justify-center tw-gap-3">
15
+ <VcCheckbox
16
+ v-model="headerCheckbox"
17
+ class="tw-font-normal tw-self-center tw-flex"
18
+ @click.stop
19
+ >{{ $t("COMPONENTS.ORGANISMS.VC_TABLE.SELECT_ALL_TRUNCATED") }}</VcCheckbox
20
+ >
21
+ {{ $t("COMPONENTS.ORGANISMS.VC_TABLE.SELECTED") }}: {{ selection.length }}
22
+ </div>
23
+
24
+ <VcButton
25
+ text
26
+ @click="() => (selection = [])"
27
+ >{{ $t("COMPONENTS.ORGANISMS.VC_TABLE.CANCEL") }}</VcButton
28
+ >
29
+ </div>
30
+ </div>
31
+ <div
32
+ v-if="selectAll && showSelectionChoice"
33
+ class="tw-w-full tw-flex tw-py-2"
34
+ >
35
+ <div class="tw-w-full tw-flex tw-items-center tw-justify-center">
36
+ <div>
37
+ {{
38
+ allSelected
39
+ ? t("COMPONENTS.ORGANISMS.VC_TABLE.ALL_SELECTED")
40
+ : t("COMPONENTS.ORGANISMS.VC_TABLE.CURRENT_PAGE_SELECTED")
41
+ }}
42
+ <VcButton
43
+ text
44
+ class="tw-text-[13px]"
45
+ @click="handleSelectAll"
46
+ >{{
47
+ allSelected ? t("COMPONENTS.ORGANISMS.VC_TABLE.CANCEL") : t("COMPONENTS.ORGANISMS.VC_TABLE.SELECT_ALL")
48
+ }}</VcButton
49
+ >
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </div>
54
+
6
55
  <!-- Header slot with filter and searchbar -->
7
56
  <slot
8
- v-if="($slots['header'] || header) && (!columnsInit || searchValue || searchValue === '' || activeFilterCount)"
57
+ v-else-if="
58
+ ($slots['header'] || header) && (!columnsInit || searchValue || searchValue === '' || activeFilterCount)
59
+ "
9
60
  name="header"
10
61
  >
11
- <div class="tw-shrink-0 tw-flex tw-items-center tw-justify-between tw-p-4">
62
+ <div
63
+ class="tw-shrink-0 tw-flex tw-items-center tw-justify-between tw-box-border"
64
+ :class="{
65
+ 'tw-px-4 tw-py-2 tw-border-[color:#eef0f2] tw-border-solid tw-border-b ': $isMobile.value,
66
+ 'tw-p-4': $isDesktop.value,
67
+ }"
68
+ >
12
69
  <!-- Table filter mobile button -->
13
70
  <div
14
71
  v-if="$isMobile.value && $slots['filters']"
@@ -32,7 +89,14 @@
32
89
  name="table_search"
33
90
  :model-value="searchValue"
34
91
  @update:model-value="$emit('search:change', $event)"
35
- ></VcInput>
92
+ >
93
+ <template #prepend-inner>
94
+ <VcIcon
95
+ icon="fas fa-search"
96
+ class="tw-text-[color:#a5a5a5]"
97
+ ></VcIcon>
98
+ </template>
99
+ </VcInput>
36
100
 
37
101
  <!-- Table filter desktop button -->
38
102
  <div
@@ -61,7 +125,7 @@
61
125
  ref="scrollContainer"
62
126
  :no-padding="true"
63
127
  class="tw-grow tw-basis-0 tw-relative"
64
- :use-ptr="pullToReload"
128
+ :use-ptr="selection.length === 0 ? pullToReload : undefined"
65
129
  @scroll:ptr="$emit('scroll:ptr')"
66
130
  >
67
131
  <!-- Mobile table view -->
@@ -74,6 +138,7 @@
74
138
  :items="items"
75
139
  :action-builder="itemActionBuilder"
76
140
  :swiping-item="mobileSwipeItem"
141
+ :selection="selection"
77
142
  :is-selected="isSelected(item)"
78
143
  @click="$emit('itemClick', item)"
79
144
  @swipe-start="handleSwipe"
@@ -130,20 +195,6 @@
130
195
  @mouseleave="handleHeaderMouseOver(false)"
131
196
  >
132
197
  <div class="vc-table__header-row tw-flex tw-flex-row">
133
- <div
134
- v-if="multiselect"
135
- class="tw-flex-1 tw-flex tw-items-center tw-justify-center tw-w-[28px] tw-max-w-[28px] tw-min-w-[28px] tw-bg-[#f9f9f9] !tw-border-0 tw-shadow-[inset_0px_1px_0px_#eaedf3,_inset_0px_-1px_0px_#eaedf3] tw-box-border tw-sticky tw-top-0 tw-select-none tw-overflow-hidden tw-z-[1]"
136
- >
137
- <div class="tw-flex tw-justify-center tw-items-center">
138
- <VcCheckbox
139
- v-model="headerCheckbox"
140
- @click.stop
141
- ></VcCheckbox>
142
- </div>
143
- <div class="tw-top-0 tw-bottom-0 tw-absolute tw-right-0 tw-flex tw-justify-end">
144
- <div class="tw-w-px tw-bg-[#e5e7eb] tw-h-full"></div>
145
- </div>
146
- </div>
147
198
  <div
148
199
  v-for="(item, index) in filteredCols"
149
200
  :id="item.id"
@@ -164,6 +215,17 @@
164
215
  @drop="onColumnHeaderDrop($event, item)"
165
216
  @click="handleHeaderClick(item)"
166
217
  >
218
+ <div
219
+ v-if="multiselect && index === 0 && isHeaderHover"
220
+ class="tw-flex tw-pl-3 tw-items-center tw-justify-center tw-w-auto tw-bg-[#f9f9f9] tw-box-border tw-select-none tw-overflow-hidden tw-z-[1]"
221
+ >
222
+ <div class="tw-flex tw-justify-center tw-items-center">
223
+ <VcCheckbox
224
+ v-model="headerCheckbox"
225
+ @click.stop
226
+ ></VcCheckbox>
227
+ </div>
228
+ </div>
167
229
  <div class="tw-flex tw-items-center tw-flex-nowrap tw-truncate tw-px-3 tw-font-bold">
168
230
  <div class="tw-truncate">
169
231
  <span
@@ -275,20 +337,6 @@
275
337
  @drop="onRowDrop"
276
338
  @mouseover="showActions(itemIndex)"
277
339
  >
278
- <div
279
- v-if="multiselect && typeof item === 'object'"
280
- class="tw-w-[28px] tw-max-w-[28px] tw-min-w-[28px] tw-relative tw-flex-1 tw-flex tw-items-center tw-justify-center"
281
- @click.stop
282
- >
283
- <div class="tw-flex tw-justify-center tw-items-center">
284
- <VcCheckbox
285
- :model-value="isSelected(item)"
286
- @update:model-value="rowCheckbox(item)"
287
- ></VcCheckbox>
288
- </div>
289
- <div class="tw-w-px tw-top-0 tw-bottom-0 tw-absolute tw-right-0 tw-bg-[#e5e7eb]"></div>
290
- </div>
291
-
292
340
  <div
293
341
  v-for="cell in filteredCols"
294
342
  :id="`${(typeof item === 'object' && 'id' in item && item.id) || itemIndex}_${cell.id}`"
@@ -298,28 +346,11 @@
298
346
  :style="{ maxWidth: cell.width, width: cell.width }"
299
347
  >
300
348
  <div class="tw-truncate">
301
- <slot
302
- :name="`item_${cell.id}`"
349
+ <renderCellSlot
303
350
  :item="item"
304
351
  :cell="cell"
305
352
  :index="itemIndex"
306
- >
307
- <VcTableCell
308
- v-if="typeof item === 'object'"
309
- class="tw-flex-1"
310
- :cell="cell"
311
- :item="item"
312
- :editing="editing"
313
- :index="itemIndex"
314
- :width="
315
- calculateElWidth(
316
- `${(typeof item === 'object' && 'id' in item && item.id) || itemIndex}_${cell.id}`,
317
- )
318
- "
319
- @update="$emit('onEditComplete', { event: $event, index: itemIndex })"
320
- @blur="$emit('onCellBlur', $event)"
321
- ></VcTableCell>
322
- </slot>
353
+ />
323
354
  </div>
324
355
  </div>
325
356
  <div
@@ -400,7 +431,11 @@
400
431
  name="footer"
401
432
  >
402
433
  <div
403
- class="tw-bg-[#fbfdfe] tw-border-t tw-border-solid tw-border-[#eaedf3] tw-flex-shrink-0 tw-flex tw-items-center tw-justify-between tw-p-4"
434
+ class="tw-bg-[#fbfdfe] tw-border-t tw-border-solid tw-border-[#eaedf3] tw-flex-shrink-0 tw-flex tw-items-center tw-justify-between"
435
+ :class="{
436
+ 'tw-py-2 tw-px-4': $isMobile.value,
437
+ 'tw-p-4': $isDesktop.value,
438
+ }"
404
439
  >
405
440
  <!-- Table pagination -->
406
441
  <VcPagination
@@ -596,6 +631,41 @@ const sortField = computed(() => {
596
631
 
597
632
  const hasClickListener = typeof instance?.vnode.props?.["onItemClick"] === "function";
598
633
 
634
+ const renderCellSlot = ({ item, cell, index }: { item: T; cell: ITableColumns; index: number }) => {
635
+ const isSlotExist = slots[`item_${cell.id}`];
636
+
637
+ const isFirstCell = filteredCols.value.indexOf(cell) === 0;
638
+
639
+ const isRowSelected = selection.value.includes(item);
640
+
641
+ if ((isFirstCell && selectedRowIndex.value === index) || (isRowSelected && isFirstCell)) {
642
+ return h(
643
+ "div",
644
+ { onClick: (e) => e.stopPropagation() },
645
+ h(VcCheckbox, {
646
+ modelValue: selection.value.includes(item),
647
+ "onUpdate:modelValue": (value: boolean) => {
648
+ rowCheckbox(item);
649
+ },
650
+ }),
651
+ );
652
+ }
653
+
654
+ if (!isSlotExist) {
655
+ return h(VcTableCell, {
656
+ cell,
657
+ item: item as TableItem,
658
+ index,
659
+ editing: props.editing,
660
+ onUpdate: (event) => {
661
+ emit("onEditComplete", { event, index });
662
+ },
663
+ onBlur: (event) => emit("onCellBlur", event),
664
+ });
665
+ }
666
+ return slots[`item_${cell.id}`]?.({ item, cell, index });
667
+ };
668
+
599
669
  const calculateElWidth = (id: string) => {
600
670
  const el = document.getElementById(id);
601
671
  return el ? el.offsetWidth : 0;