@vc-shell/framework 1.0.232 → 1.0.233
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.
- package/CHANGELOG.md +21 -0
- package/core/types/index.ts +14 -1
- package/dist/core/types/index.d.ts +12 -2
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/framework.js +14812 -14535
- package/dist/index.css +1 -1
- package/dist/locales/en.json +8 -2
- package/dist/shared/components/notifications/components/notification-container/index.d.ts +1 -1
- package/dist/shared/components/user-dropdown-button/user-dropdown-button.vue.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/components/fields/GalleryField.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/components/fields/StatusField.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/types/index.d.ts +9 -4
- package/dist/shared/modules/dynamic/types/index.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/ui/components/atoms/vc-badge/vc-badge.stories.d.ts +187 -17
- package/dist/ui/components/atoms/vc-badge/vc-badge.stories.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-badge/vc-badge.vue.d.ts +19 -3
- package/dist/ui/components/atoms/vc-badge/vc-badge.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-button/vc-button.stories.d.ts +91 -91
- package/dist/ui/components/atoms/vc-button/vc-button.vue.d.ts +1 -1
- package/dist/ui/components/atoms/vc-icon/vc-icon.stories.d.ts +9 -9
- package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts +1 -1
- package/dist/ui/components/atoms/vc-image/index.d.ts +12 -3
- package/dist/ui/components/atoms/vc-image/index.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-image/vc-image.stories.d.ts +12 -3
- package/dist/ui/components/atoms/vc-image/vc-image.stories.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-image/vc-image.vue.d.ts +5 -1
- package/dist/ui/components/atoms/vc-image/vc-image.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-widget/index.d.ts +6 -0
- package/dist/ui/components/atoms/vc-widget/index.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-widget/vc-widget.stories.d.ts +6 -0
- package/dist/ui/components/atoms/vc-widget/vc-widget.stories.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-widget/vc-widget.vue.d.ts +1 -0
- package/dist/ui/components/atoms/vc-widget/vc-widget.vue.d.ts.map +1 -1
- package/dist/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.stories.d.ts +3 -3
- package/dist/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.vue.d.ts +1 -1
- package/dist/ui/components/molecules/vc-editor/vc-editor.vue.d.ts.map +1 -1
- 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
- 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
- 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
- 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
- package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
- 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
- package/dist/ui/components/organisms/vc-blade/vc-blade.stories.d.ts +2 -0
- package/dist/ui/components/organisms/vc-blade/vc-blade.stories.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts +2 -0
- package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue.d.ts +3 -0
- package/dist/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/vc-table.vue.d.ts.map +1 -1
- package/package.json +4 -4
- package/shared/components/app-switcher/components/vc-app-switcher/vc-app-switcher.vue +1 -1
- package/shared/components/user-dropdown-button/user-dropdown-button.vue +11 -3
- package/shared/modules/dynamic/components/fields/Card.ts +1 -1
- package/shared/modules/dynamic/components/fields/GalleryField.ts +1 -0
- package/shared/modules/dynamic/components/fields/StatusField.ts +39 -3
- package/shared/modules/dynamic/pages/dynamic-blade-form.vue +17 -22
- package/shared/modules/dynamic/types/index.ts +14 -6
- package/ui/components/atoms/vc-badge/vc-badge.stories.ts +3 -3
- package/ui/components/atoms/vc-badge/vc-badge.vue +58 -10
- package/ui/components/atoms/vc-container/vc-container.vue +2 -2
- package/ui/components/atoms/vc-image/vc-image.vue +3 -1
- package/ui/components/atoms/vc-widget/vc-widget.vue +42 -22
- package/ui/components/molecules/vc-editor/vc-editor.vue +5 -1
- package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +1 -1
- package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue +21 -10
- package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue +3 -0
- package/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +59 -9
- package/ui/components/organisms/vc-app/vc-app.vue +0 -1
- package/ui/components/organisms/vc-blade/vc-blade.vue +89 -2
- package/ui/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue +1 -0
- package/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue +27 -7
- 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)]
|
|
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,40 @@
|
|
|
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
|
|
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-left-0 tw-top-0 tw-bottom-0 tw-bg-[color:var(--app-background)] tw-pt-[22px] tw-shadow-[inset_0px_2px_5px_0px_#3654751A]"
|
|
23
|
+
:class="{
|
|
24
|
+
'!tw-w-[63px] !tw-shadow-[inset_4px_2px_5px_0px_#3654751A]': $isDesktop.value && !isExpanded && !isExpandedOver,
|
|
25
|
+
'tw-w-[var(--app-menu-width)]': $isDesktop.value && (isExpanded || isExpandedOver),
|
|
26
|
+
'tw-shadow-none': $isDesktop.value && isExpanded,
|
|
27
|
+
}"
|
|
28
|
+
>
|
|
18
29
|
<!-- Show menu close handler on mobile devices -->
|
|
19
30
|
<div
|
|
20
31
|
v-if="$isMobile.value"
|
|
21
32
|
class="tw-text-[#319ed4] tw-flex tw-justify-end tw-items-center tw-p-4 tw-cursor-pointer"
|
|
22
33
|
>
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
<button @click="isMobileVisible = false">
|
|
35
|
+
<VcIcon
|
|
36
|
+
icon="fas fa-times"
|
|
37
|
+
size="xl"
|
|
38
|
+
></VcIcon>
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div
|
|
43
|
+
v-if="$isDesktop.value"
|
|
44
|
+
class="tw-pl-[21px] tw-pb-2"
|
|
45
|
+
>
|
|
46
|
+
<button
|
|
47
|
+
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)]"
|
|
48
|
+
@click="toggleMenu"
|
|
49
|
+
>
|
|
50
|
+
<VcIcon
|
|
51
|
+
icon="fas fa-bars"
|
|
52
|
+
size="xl"
|
|
53
|
+
></VcIcon>
|
|
54
|
+
</button>
|
|
28
55
|
</div>
|
|
29
56
|
|
|
30
57
|
<!-- Show scrollable area with menu items -->
|
|
@@ -32,7 +59,13 @@
|
|
|
32
59
|
:no-padding="true"
|
|
33
60
|
class="tw-grow tw-basis-0"
|
|
34
61
|
>
|
|
35
|
-
<div
|
|
62
|
+
<div
|
|
63
|
+
class="tw-gap-[5px] tw-flex tw-flex-col tw-h-full"
|
|
64
|
+
:class="{
|
|
65
|
+
'tw-px-[21px]': ($isDesktop.value && (isExpanded || isExpandedOver)) || $isMobile.value,
|
|
66
|
+
'tw-pl-[21px] tw-pr-[2px]': $isDesktop.value && !isExpanded && !isExpandedOver,
|
|
67
|
+
}"
|
|
68
|
+
>
|
|
36
69
|
<slot
|
|
37
70
|
v-if="!$isDesktop.value"
|
|
38
71
|
name="mobile"
|
|
@@ -48,6 +81,7 @@
|
|
|
48
81
|
:icon="item.icon"
|
|
49
82
|
:title="item.title as string"
|
|
50
83
|
:children="item.children"
|
|
84
|
+
:expand="$isDesktop.value ? isExpanded || isExpandedOver : true"
|
|
51
85
|
@click="
|
|
52
86
|
(event) => {
|
|
53
87
|
$emit('item:click', event ? event : item);
|
|
@@ -58,7 +92,7 @@
|
|
|
58
92
|
</div>
|
|
59
93
|
</VcContainer>
|
|
60
94
|
<div
|
|
61
|
-
class="tw-text-[color:var(--app-menu-version-color)] tw-text-xs tw-mt-auto tw-self-
|
|
95
|
+
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
96
|
@click="$emit('version:click')"
|
|
63
97
|
>
|
|
64
98
|
{{ version }}
|
|
@@ -73,6 +107,7 @@ import VcAppMenuItem from "./_internal/vc-app-menu-item/vc-app-menu-item.vue";
|
|
|
73
107
|
import { VcContainer, VcIcon } from "./../../../../";
|
|
74
108
|
import { useMenuService } from "../../../../../../core/composables";
|
|
75
109
|
import { MenuItem } from "../../../../../../core/types";
|
|
110
|
+
import { useLocalStorage } from "@vueuse/core";
|
|
76
111
|
|
|
77
112
|
export interface Props {
|
|
78
113
|
version: string;
|
|
@@ -91,6 +126,8 @@ withDefaults(defineProps<Props>(), {
|
|
|
91
126
|
|
|
92
127
|
defineEmits<Emits>();
|
|
93
128
|
const { menuItems } = useMenuService();
|
|
129
|
+
const isExpanded = useLocalStorage("VC_APP_MENU_EXPANDED", true);
|
|
130
|
+
const isExpandedOver = ref(false);
|
|
94
131
|
|
|
95
132
|
const isMobileVisible = ref(false);
|
|
96
133
|
|
|
@@ -98,6 +135,16 @@ const isMenuVisible = computed(() => {
|
|
|
98
135
|
return !!menuItems.value.length;
|
|
99
136
|
});
|
|
100
137
|
|
|
138
|
+
function toggleMenu() {
|
|
139
|
+
isExpanded.value = !isExpanded.value;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function expandOverHandler(state: boolean) {
|
|
143
|
+
if (isExpandedOver.value !== state) {
|
|
144
|
+
isExpandedOver.value = state;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
101
148
|
defineExpose({
|
|
102
149
|
isMobileVisible,
|
|
103
150
|
});
|
|
@@ -108,6 +155,9 @@ defineExpose({
|
|
|
108
155
|
--app-menu-width: 230px;
|
|
109
156
|
--app-menu-background-color: #ffffff;
|
|
110
157
|
--app-menu-version-color: #838d9a;
|
|
158
|
+
|
|
159
|
+
--app-menu-burger-background-color: rgba(255, 255, 255, 0.5);
|
|
160
|
+
--app-menu-burger-color: #319ed4;
|
|
111
161
|
}
|
|
112
162
|
|
|
113
163
|
.vc-app-menu {
|
|
@@ -70,12 +70,64 @@
|
|
|
70
70
|
class="tw-shrink-0"
|
|
71
71
|
:items="toolbarItems"
|
|
72
72
|
></VcBladeToolbar>
|
|
73
|
-
|
|
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>
|
package/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue
CHANGED
|
@@ -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
|
-
|
|
17
|
-
|
|
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,
|
|
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
|
-
(
|
|
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
|
-
(
|
|
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
|
|
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="
|
|
57
|
+
v-else-if="
|
|
58
|
+
($slots['header'] || header) && (!columnsInit || searchValue || searchValue === '' || activeFilterCount)
|
|
59
|
+
"
|
|
9
60
|
name="header"
|
|
10
61
|
>
|
|
11
|
-
<div
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
|
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;
|