@vc-shell/framework 1.0.38 → 1.0.39
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/components/atoms/vc-badge/vc-badge.stories.ts +27 -0
- package/components/atoms/vc-badge/vc-badge.vue +63 -0
- package/components/atoms/vc-button/vc-button.stories.ts +34 -0
- package/components/atoms/vc-button/vc-button.vue +219 -0
- package/components/atoms/vc-card/vc-card.vue +137 -0
- package/components/atoms/vc-checkbox/vc-checkbox.stories.ts +25 -0
- package/components/atoms/vc-checkbox/vc-checkbox.vue +130 -0
- package/components/atoms/vc-col/vc-col.vue +22 -0
- package/components/atoms/vc-container/vc-container.stories.ts +31 -0
- package/components/atoms/vc-container/vc-container.vue +220 -0
- package/components/atoms/vc-hint/vc-hint.stories.ts +23 -0
- package/components/atoms/vc-hint/vc-hint.vue +11 -0
- package/components/atoms/vc-icon/vc-icon.stories.ts +32 -0
- package/components/atoms/vc-icon/vc-icon.vue +36 -0
- package/components/atoms/vc-image/vc-image.stories.ts +40 -0
- package/components/atoms/vc-image/vc-image.vue +122 -0
- package/components/atoms/vc-info-row/vc-info-row.vue +42 -0
- package/components/atoms/vc-label/vc-label.stories.ts +23 -0
- package/components/atoms/vc-label/vc-label.vue +49 -0
- package/components/atoms/vc-link/vc-link.stories.ts +30 -0
- package/components/atoms/vc-link/vc-link.vue +46 -0
- package/components/atoms/vc-loading/vc-loading.vue +30 -0
- package/components/atoms/vc-progress/vc-progress.stories.ts +25 -0
- package/components/atoms/vc-progress/vc-progress.vue +65 -0
- package/components/atoms/vc-row/vc-row.vue +13 -0
- package/components/atoms/vc-status/vc-status.stories.ts +26 -0
- package/components/atoms/vc-status/vc-status.vue +78 -0
- package/components/atoms/vc-status-icon/vc-status-icon.vue +21 -0
- package/components/atoms/vc-switch/vc-switch.stories.ts +27 -0
- package/components/atoms/vc-switch/vc-switch.vue +100 -0
- package/components/atoms/vc-widget/vc-widget.vue +85 -0
- package/components/index.ts +43 -0
- package/components/molecules/vc-breadcrumbs/_internal/vc-breadcrumbs-item/vc-breadcrumbs-item.vue +103 -0
- package/components/molecules/vc-breadcrumbs/vc-breadcrumbs.stories.ts +39 -0
- package/components/molecules/vc-breadcrumbs/vc-breadcrumbs.vue +21 -0
- package/components/molecules/vc-editor/vc-editor.vue +117 -0
- package/components/molecules/vc-file-upload/vc-file-upload.vue +134 -0
- package/components/molecules/vc-form/vc-form.stories.ts +23 -0
- package/components/molecules/vc-form/vc-form.vue +5 -0
- package/components/molecules/vc-input/vc-input.stories.ts +26 -0
- package/components/molecules/vc-input/vc-input.vue +443 -0
- package/components/molecules/vc-multivalue/vc-multivalue.vue +447 -0
- package/components/molecules/vc-notification/vc-notification.vue +101 -0
- package/components/molecules/vc-pagination/vc-pagination.stories.ts +23 -0
- package/components/molecules/vc-pagination/vc-pagination.vue +169 -0
- package/components/molecules/vc-rating/vc-rating.stories.ts +23 -0
- package/components/molecules/vc-rating/vc-rating.vue +77 -0
- package/components/molecules/vc-select/vc-select.stories.ts +25 -0
- package/components/molecules/vc-select/vc-select.vue +402 -0
- package/components/molecules/vc-slider/vc-slider.vue +106 -0
- package/components/molecules/vc-textarea/vc-textarea.stories.ts +23 -0
- package/components/molecules/vc-textarea/vc-textarea.vue +155 -0
- package/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +146 -0
- package/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue +148 -0
- package/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue +157 -0
- package/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +110 -0
- package/components/organisms/vc-app/vc-app.stories.ts +75 -0
- package/components/organisms/vc-app/vc-app.vue +171 -0
- package/components/organisms/vc-blade/_internal/vc-blade-header/vc-blade-header.vue +126 -0
- package/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue +223 -0
- package/components/organisms/vc-blade/_internal/vc-blade-toolbar/vc-blade-toolbar.vue +67 -0
- package/components/organisms/vc-blade/vc-blade.stories.ts +46 -0
- package/components/organisms/vc-blade/vc-blade.vue +87 -0
- package/components/organisms/vc-dynamic-property/vc-dynamic-property.vue +292 -0
- package/components/organisms/vc-gallery/_internal/vc-gallery-item/vc-gallery-item.vue +123 -0
- package/components/organisms/vc-gallery/_internal/vc-gallery-preview/vc-gallery-preview.vue +93 -0
- package/components/organisms/vc-gallery/vc-gallery.vue +186 -0
- package/components/organisms/vc-login-form/vc-login-form.stories.ts +55 -0
- package/components/organisms/vc-login-form/vc-login-form.vue +48 -0
- package/components/organisms/vc-popup/vc-popup.stories.ts +23 -0
- package/components/organisms/vc-popup/vc-popup.vue +97 -0
- package/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue +113 -0
- package/components/organisms/vc-table/_internal/vc-table-counter/vc-table-counter.vue +29 -0
- package/components/organisms/vc-table/_internal/vc-table-filter/vc-table-filter.vue +152 -0
- package/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue +272 -0
- package/components/organisms/vc-table/vc-table.stories.ts +99 -0
- package/components/organisms/vc-table/vc-table.vue +638 -0
- package/core/api/index.ts +1 -0
- package/core/api/platform.ts +8332 -0
- package/core/composables/index.ts +8 -0
- package/core/composables/useAutosave/index.ts +57 -0
- package/core/composables/useFunctions/debounce.ts +18 -0
- package/core/composables/useFunctions/delay.ts +7 -0
- package/core/composables/useFunctions/index.ts +21 -0
- package/core/composables/useFunctions/once.ts +14 -0
- package/core/composables/useFunctions/sleep.ts +4 -0
- package/core/composables/useFunctions/throttle.ts +17 -0
- package/core/composables/useI18n/index.ts +28 -0
- package/core/composables/useLogger/index.ts +24 -0
- package/core/composables/useNotifications/index.ts +116 -0
- package/core/composables/usePermissions/index.ts +32 -0
- package/core/composables/useSettings/index.ts +36 -0
- package/core/composables/useUser/index.ts +266 -0
- package/core/directives/autofocus/index.ts +9 -0
- package/core/directives/click-outside/index.ts +21 -0
- package/core/directives/index.ts +4 -0
- package/core/directives/loading/index.ts +28 -0
- package/core/directives/permissions/index.ts +20 -0
- package/core/plugins/index.ts +1 -0
- package/core/plugins/validation/index.ts +2 -0
- package/core/plugins/validation/rules.ts +196 -0
- package/core/types/index.ts +92 -0
- package/core/utilities/camelToSnake.ts +7 -0
- package/core/utilities/index.ts +1 -0
- package/dist/core/composables/useNotifications/index.d.ts +1 -1
- package/dist/core/composables/useNotifications/index.d.ts.map +1 -1
- package/dist/core/composables/useUser/index.d.ts +2 -2
- package/dist/core/composables/useUser/index.d.ts.map +1 -1
- package/dist/core/plugins/validation/index.d.ts.map +1 -1
- package/dist/core/types/index.d.ts +1 -1
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/framework.js +70 -97
- package/dist/framework.js.map +1 -1
- package/dist/shared/app-switcher/composables/useAppSwitcher/index.d.ts +1 -1
- package/dist/shared/app-switcher/composables/useAppSwitcher/index.d.ts.map +1 -1
- package/dist/shared/app-switcher/index.d.ts +2 -2
- package/dist/shared/app-switcher/index.d.ts.map +1 -1
- package/dist/shared/blade-navigation/composables/useBladeNavigation/index.d.ts +1 -1
- package/dist/shared/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
- package/dist/shared/blade-navigation/types/index.d.ts +1 -1
- package/dist/shared/blade-navigation/types/index.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/vite.config.d.ts.map +1 -1
- package/package.json +11 -8
- package/shared/app-switcher/components/index.ts +1 -0
- package/shared/app-switcher/components/vc-app-switcher/vc-app-switcher.vue +90 -0
- package/shared/app-switcher/composables/index.ts +1 -0
- package/shared/app-switcher/composables/useAppSwitcher/index.ts +54 -0
- package/shared/app-switcher/index.ts +14 -0
- package/shared/assets/components/assets-details/assets-details.vue +138 -0
- package/shared/assets/components/index.ts +1 -0
- package/shared/assets/index.ts +19 -0
- package/shared/assets/locales/en.json +29 -0
- package/shared/assets/locales/index.ts +2 -0
- package/shared/blade-navigation/components/index.ts +1 -0
- package/shared/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue +84 -0
- package/shared/blade-navigation/composables/index.ts +1 -0
- package/shared/blade-navigation/composables/useBladeNavigation/index.ts +216 -0
- package/shared/blade-navigation/index.ts +15 -0
- package/shared/blade-navigation/types/index.ts +52 -0
- package/shared/index.ts +16 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<template v-if="component && component.url">
|
|
4
|
+
<router-link :to="component.url" custom v-slot="{ isActive, navigate }">
|
|
5
|
+
<vc-app-menu-link
|
|
6
|
+
:isActive="isActive"
|
|
7
|
+
:children="children"
|
|
8
|
+
:sticky="sticky"
|
|
9
|
+
:icon="icon"
|
|
10
|
+
:title="title"
|
|
11
|
+
@onClick="onMenuItemClick(navigate)"
|
|
12
|
+
/>
|
|
13
|
+
</router-link>
|
|
14
|
+
</template>
|
|
15
|
+
<template v-else>
|
|
16
|
+
<vc-app-menu-link
|
|
17
|
+
:children="children"
|
|
18
|
+
:sticky="sticky"
|
|
19
|
+
:icon="icon"
|
|
20
|
+
:title="title"
|
|
21
|
+
@onClick="onMenuItemClick"
|
|
22
|
+
:isActive="isHomePage"
|
|
23
|
+
/>
|
|
24
|
+
|
|
25
|
+
<!-- Nested menu items -->
|
|
26
|
+
<div class="vc-app-menu-item__child" v-if="isOpened">
|
|
27
|
+
<template v-for="(nested, i) in children" :key="i">
|
|
28
|
+
<router-link
|
|
29
|
+
:to="nested.component.url"
|
|
30
|
+
custom
|
|
31
|
+
v-slot="{ isActive, navigate }"
|
|
32
|
+
>
|
|
33
|
+
<div
|
|
34
|
+
:class="[
|
|
35
|
+
{
|
|
36
|
+
'vc-app-menu-item__child-item_active': isActive,
|
|
37
|
+
},
|
|
38
|
+
'vc-app-menu-item__child-item',
|
|
39
|
+
]"
|
|
40
|
+
v-if="nested.isVisible === undefined || nested.isVisible"
|
|
41
|
+
:key="i"
|
|
42
|
+
@click="
|
|
43
|
+
$emit('child:click', { item: nested, navigationCb: navigate })
|
|
44
|
+
"
|
|
45
|
+
>
|
|
46
|
+
{{ nested.title }}
|
|
47
|
+
</div>
|
|
48
|
+
</router-link>
|
|
49
|
+
</template>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script lang="ts" setup>
|
|
56
|
+
import { onMounted, ref, computed } from "vue";
|
|
57
|
+
import { ExtendedComponent, IMenuItems } from "@/core/types";
|
|
58
|
+
import VcAppMenuLink from "./_internal/vc-app-menu-link.vue";
|
|
59
|
+
import {NavigationFailure, useRoute} from "vue-router";
|
|
60
|
+
|
|
61
|
+
export interface Props {
|
|
62
|
+
sticky?: boolean;
|
|
63
|
+
isVisible?: boolean;
|
|
64
|
+
component?: ExtendedComponent;
|
|
65
|
+
bladeOptions?: Record<string, unknown>;
|
|
66
|
+
clickHandler?: () => void;
|
|
67
|
+
icon: string;
|
|
68
|
+
title: string;
|
|
69
|
+
children?: IMenuItems[];
|
|
70
|
+
isCollapsed?: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface Emits {
|
|
74
|
+
(event: "click", navigationCb: () => Promise<void | NavigationFailure>): void;
|
|
75
|
+
(
|
|
76
|
+
event: "child:click",
|
|
77
|
+
{
|
|
78
|
+
item,
|
|
79
|
+
navigationCb,
|
|
80
|
+
}: {
|
|
81
|
+
item: IMenuItems;
|
|
82
|
+
navigationCb: () => Promise<void | NavigationFailure>;
|
|
83
|
+
}
|
|
84
|
+
): void;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
88
|
+
sticky: true,
|
|
89
|
+
isVisible: false,
|
|
90
|
+
component: undefined,
|
|
91
|
+
bladeOptions: () => ({}),
|
|
92
|
+
clickHandler: undefined,
|
|
93
|
+
icon: "",
|
|
94
|
+
title: "",
|
|
95
|
+
children: () => [],
|
|
96
|
+
isCollapsed: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const route = useRoute();
|
|
100
|
+
|
|
101
|
+
const emit = defineEmits<Emits>();
|
|
102
|
+
|
|
103
|
+
const isOpened = ref(false);
|
|
104
|
+
|
|
105
|
+
const isHomePage = computed(() => route.path === '/')
|
|
106
|
+
|
|
107
|
+
onMounted(() => {
|
|
108
|
+
if (
|
|
109
|
+
props.children &&
|
|
110
|
+
props.children.length &&
|
|
111
|
+
props.children.find((x) => x.component?.url === route?.path)
|
|
112
|
+
) {
|
|
113
|
+
isOpened.value = true;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
function onMenuItemClick(
|
|
118
|
+
navigationCb?: () => Promise<void | NavigationFailure>
|
|
119
|
+
) {
|
|
120
|
+
if (!props.children?.length) {
|
|
121
|
+
emit("click", navigationCb);
|
|
122
|
+
} else {
|
|
123
|
+
isOpened.value = !isOpened.value;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<style lang="scss">
|
|
129
|
+
:root {
|
|
130
|
+
--app-menu-item-height: 38px;
|
|
131
|
+
--app-menu-item-icon-width: 20px;
|
|
132
|
+
--app-menu-item-icon-color: #337599;
|
|
133
|
+
--app-menu-item-icon-color-active: #ffffff;
|
|
134
|
+
--app-menu-item-handler-width: 10px;
|
|
135
|
+
--app-menu-item-background-color-hover: #337599;
|
|
136
|
+
--app-menu-item-hover-radius: 4px;
|
|
137
|
+
--app-menu-item-title-color: #465769;
|
|
138
|
+
--app-menu-item-title-color-active: #ffffff;
|
|
139
|
+
--app-menu-item-handler-color: #bdd1df;
|
|
140
|
+
}
|
|
141
|
+
.vc-app-menu-item {
|
|
142
|
+
&__child {
|
|
143
|
+
@apply ml-[42px] gap-[4px] flex flex-col;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
&__child-item {
|
|
147
|
+
@apply cursor-pointer w-fit py-[5px] px-[9px] rounded-[4px]
|
|
148
|
+
hover:bg-[color:var(--app-menu-item-background-color-hover)]
|
|
149
|
+
hover:text-[color:var(--app-menu-item-title-color-active)];
|
|
150
|
+
|
|
151
|
+
&_active {
|
|
152
|
+
@apply bg-[color:var(--app-menu-item-background-color-hover)]
|
|
153
|
+
text-[color:var(--app-menu-item-title-color-active)] font-bold;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
</style>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="relative w-[var(--app-menu-width)] transition duration-100 pt-4"
|
|
4
|
+
:class="{
|
|
5
|
+
'vc-app-menu_mobile hidden !fixed !left-0 !top-0 !w-full !bottom-0 !z-[9999]':
|
|
6
|
+
$isMobile.value,
|
|
7
|
+
'!block': isMobileVisible,
|
|
8
|
+
}"
|
|
9
|
+
>
|
|
10
|
+
<!-- Show backdrop overlay on mobile devices -->
|
|
11
|
+
<div
|
|
12
|
+
v-if="$isMobile.value"
|
|
13
|
+
class="absolute left-0 top-0 right-0 bottom-0 z-[9998] bg-[#808c99] opacity-60"
|
|
14
|
+
@click="isMobileVisible = false"
|
|
15
|
+
></div>
|
|
16
|
+
<div class="vc-app-menu__inner flex flex-col h-full">
|
|
17
|
+
<!-- Show menu close handler on mobile devices -->
|
|
18
|
+
<div
|
|
19
|
+
v-if="$isMobile.value"
|
|
20
|
+
class="text-[#319ed4] flex justify-end items-center p-4"
|
|
21
|
+
>
|
|
22
|
+
<VcIcon
|
|
23
|
+
icon="fas fa-times"
|
|
24
|
+
size="xl"
|
|
25
|
+
@click="isMobileVisible = false"
|
|
26
|
+
></VcIcon>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<!-- Show scrollable area with menu items -->
|
|
30
|
+
<VcContainer :noPadding="true" class="grow basis-0">
|
|
31
|
+
<div class="gap-[5px] flex flex-col px-4">
|
|
32
|
+
<template
|
|
33
|
+
v-for="(item, index) in mobileMenuItems"
|
|
34
|
+
:key="`info_item_${index}`"
|
|
35
|
+
>
|
|
36
|
+
<template v-if="item.isVisible === undefined || item.isVisible">
|
|
37
|
+
<component
|
|
38
|
+
v-if="item.component"
|
|
39
|
+
:is="item.component"
|
|
40
|
+
v-bind="item.bladeOptions"
|
|
41
|
+
class="p-0 mb-2 w-full"
|
|
42
|
+
></component>
|
|
43
|
+
</template>
|
|
44
|
+
</template>
|
|
45
|
+
<template v-for="(item, index) in items" :key="index">
|
|
46
|
+
<VcAppMenuItem
|
|
47
|
+
v-if="item.isVisible === undefined || item.isVisible"
|
|
48
|
+
v-bind="item"
|
|
49
|
+
@click="
|
|
50
|
+
(navigationCb) => {
|
|
51
|
+
$emit('item:click', { item, navigationCb });
|
|
52
|
+
isMobileVisible = false;
|
|
53
|
+
}
|
|
54
|
+
"
|
|
55
|
+
@child:click="
|
|
56
|
+
({ item: blade, navigationCb }) => {
|
|
57
|
+
$emit('item:click', { item: blade, navigationCb });
|
|
58
|
+
isMobileVisible = false;
|
|
59
|
+
}
|
|
60
|
+
"
|
|
61
|
+
/>
|
|
62
|
+
</template>
|
|
63
|
+
</div>
|
|
64
|
+
</VcContainer>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</template>
|
|
68
|
+
|
|
69
|
+
<script lang="ts" setup>
|
|
70
|
+
import { ref } from "vue";
|
|
71
|
+
import VcAppMenuItem from "./_internal/vc-app-menu-item/vc-app-menu-item.vue";
|
|
72
|
+
import { VcContainer } from "@/components";
|
|
73
|
+
import { IMenuItems } from "@/core/types";
|
|
74
|
+
import { IMenuClickEvent } from "@/shared";
|
|
75
|
+
|
|
76
|
+
export interface Props {
|
|
77
|
+
items?: IMenuItems[];
|
|
78
|
+
mobileMenuItems?: IMenuItems[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface Emits {
|
|
82
|
+
(event: "item:click", { item, navigationCb }: IMenuClickEvent): void;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
withDefaults(defineProps<Props>(), {
|
|
86
|
+
items: () => [],
|
|
87
|
+
mobileMenuItems: () => [],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
defineEmits<Emits>();
|
|
91
|
+
|
|
92
|
+
const isMobileVisible = ref(false);
|
|
93
|
+
|
|
94
|
+
defineExpose({
|
|
95
|
+
isMobileVisible,
|
|
96
|
+
});
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<style lang="scss">
|
|
100
|
+
:root {
|
|
101
|
+
--app-menu-width: 230px;
|
|
102
|
+
--app-menu-background-color: #ffffff;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.vc-app-menu {
|
|
106
|
+
&_mobile &__inner {
|
|
107
|
+
@apply absolute z-[9999] right-0 top-0 bottom-0 w-[300px] max-w-[60%] bg-[color:var(--app-menu-background-color)];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
</style>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App component.
|
|
3
|
+
* @author Iurii A Taranov <me@flanker72.ru>
|
|
4
|
+
*/
|
|
5
|
+
import { Story } from "@storybook/vue3";
|
|
6
|
+
import VcApp from "./vc-app.vue";
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: "organisms/vc-app",
|
|
10
|
+
component: VcApp,
|
|
11
|
+
parameters: {
|
|
12
|
+
backgrounds: {
|
|
13
|
+
default: "default",
|
|
14
|
+
values: [
|
|
15
|
+
{
|
|
16
|
+
name: "default",
|
|
17
|
+
value:
|
|
18
|
+
"linear-gradient(180deg, #E4F5FB 5.06%, #E8F3F2 100%), linear-gradient(0deg, #E8F2F3, #E8F2F3), #EEF2F8;",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const Template: Story = (args) => ({
|
|
26
|
+
components: { VcApp },
|
|
27
|
+
setup() {
|
|
28
|
+
return { args };
|
|
29
|
+
},
|
|
30
|
+
template: '<vc-app v-bind="args"></vc-app>',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const App = Template.bind({});
|
|
34
|
+
App.storyName = "vc-app";
|
|
35
|
+
App.args = {
|
|
36
|
+
menuItems: [
|
|
37
|
+
{
|
|
38
|
+
title: "Dashboard",
|
|
39
|
+
icon: "fas fa-home",
|
|
40
|
+
isVisible: true,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
title: "Orders",
|
|
44
|
+
icon: "fas fa-file-alt",
|
|
45
|
+
isVisible: true,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
title: "Products",
|
|
49
|
+
icon: "fas fa-box-open",
|
|
50
|
+
isVisible: true,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: "Offers",
|
|
54
|
+
icon: "fas fa-file-invoice",
|
|
55
|
+
isVisible: true,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
title: "Import",
|
|
59
|
+
icon: "fas fa-file-import",
|
|
60
|
+
isVisible: true,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
title: "Logout",
|
|
64
|
+
icon: "fas fa-sign-out-alt",
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
toolbarItems: [
|
|
68
|
+
{ id: 1, icon: "fas fa-globe", title: "Language selector" },
|
|
69
|
+
{ id: 2, icon: "fas fa-bell", title: "Notifications", isAccent: true },
|
|
70
|
+
],
|
|
71
|
+
isReady: true,
|
|
72
|
+
isAuthorized: true,
|
|
73
|
+
version: "0.0.100",
|
|
74
|
+
logo: "images/main-logo.svg",
|
|
75
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="vc-app flex flex-col w-full h-full box-border m-0 overflow-hidden text-base"
|
|
4
|
+
:class="[
|
|
5
|
+
`vc-theme_${theme}`,
|
|
6
|
+
{
|
|
7
|
+
'vc-app_touch': $isTouch,
|
|
8
|
+
'vc-app_phone': $isPhone.value,
|
|
9
|
+
'vc-app_mobile': $isMobile.value,
|
|
10
|
+
},
|
|
11
|
+
]"
|
|
12
|
+
>
|
|
13
|
+
<!-- Init application top bar -->
|
|
14
|
+
<VcAppBar
|
|
15
|
+
class="shrink-0"
|
|
16
|
+
:logo="logo"
|
|
17
|
+
:blades="bladesRefs"
|
|
18
|
+
:version="version"
|
|
19
|
+
:buttons="toolbarItems"
|
|
20
|
+
@toolbarbutton:click="onToolbarButtonClick"
|
|
21
|
+
@menubutton:click="$refs.menu.isMobileVisible = true"
|
|
22
|
+
@backlink:click="$emit('backlink:click', bladesRefs.length - 2)"
|
|
23
|
+
@logo:click="openDashboard"
|
|
24
|
+
>
|
|
25
|
+
<template v-slot:appSwitcher>
|
|
26
|
+
<slot name="appSwitcher"></slot>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<template v-slot:productName v-if="$slots['productName']">
|
|
30
|
+
<slot name="productName"></slot>
|
|
31
|
+
</template>
|
|
32
|
+
</VcAppBar>
|
|
33
|
+
|
|
34
|
+
<div class="overflow-hidden flex grow basis-0">
|
|
35
|
+
<!-- Init main menu -->
|
|
36
|
+
<VcAppMenu
|
|
37
|
+
ref="menu"
|
|
38
|
+
class="shrink-0"
|
|
39
|
+
:items="menuItems"
|
|
40
|
+
:mobileMenuItems="mobileMenuItems"
|
|
41
|
+
@item:click="onMenuItemClick"
|
|
42
|
+
></VcAppMenu>
|
|
43
|
+
|
|
44
|
+
<!-- Workspace blades -->
|
|
45
|
+
<div
|
|
46
|
+
class="vc-app__workspace px-2 w-full overflow-hidden flex grow basis-0"
|
|
47
|
+
>
|
|
48
|
+
<slot name="bladeNavigation"></slot>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div
|
|
52
|
+
class="[pointer-events:painted] absolute flex z-[1000] overflow-hidden top-0 left-2/4 -translate-x-2/4 flex-col items-center p-2 box-border"
|
|
53
|
+
>
|
|
54
|
+
<slot name="notifications"></slot>
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
<slot name="passwordChange"></slot>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<script lang="ts">
|
|
64
|
+
import { defineComponent, getCurrentInstance } from "vue";
|
|
65
|
+
|
|
66
|
+
export default defineComponent({
|
|
67
|
+
inheritAttrs: false,
|
|
68
|
+
});
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<script lang="ts" setup>
|
|
72
|
+
import { useRouter } from "vue-router";
|
|
73
|
+
import VcAppBar from "./_internal/vc-app-bar/vc-app-bar.vue";
|
|
74
|
+
import VcAppMenu from "./_internal/vc-app-menu/vc-app-menu.vue";
|
|
75
|
+
import { IBladeToolbar, IMenuItems } from "@/core/types";
|
|
76
|
+
import {
|
|
77
|
+
IBladeElement,
|
|
78
|
+
ExtendedComponent,
|
|
79
|
+
IMenuClickEvent,
|
|
80
|
+
IOpenBlade,
|
|
81
|
+
} from "@/shared";
|
|
82
|
+
|
|
83
|
+
export interface Props {
|
|
84
|
+
pages: ExtendedComponent[];
|
|
85
|
+
menuItems: IMenuItems[];
|
|
86
|
+
mobileMenuItems: IMenuItems[];
|
|
87
|
+
toolbarItems: IBladeToolbar[];
|
|
88
|
+
isReady: boolean;
|
|
89
|
+
isAuthorized: boolean;
|
|
90
|
+
logo: string;
|
|
91
|
+
version: string;
|
|
92
|
+
theme?: "light" | "dark";
|
|
93
|
+
bladesRefs: IBladeElement[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface Emits {
|
|
97
|
+
(event: "onOpen", args: IOpenBlade): void;
|
|
98
|
+
(event: "onClose", index: number): void;
|
|
99
|
+
(event: "backlink:click", index: number): void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
withDefaults(defineProps<Props>(), {
|
|
103
|
+
pages: () => [],
|
|
104
|
+
menuItems: () => [],
|
|
105
|
+
mobileMenuItems: () => [],
|
|
106
|
+
toolbarItems: () => [],
|
|
107
|
+
isReady: false,
|
|
108
|
+
isAuthorized: false,
|
|
109
|
+
logo: undefined,
|
|
110
|
+
version: undefined,
|
|
111
|
+
theme: "light",
|
|
112
|
+
bladesRefs: () => [],
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const emit = defineEmits<Emits>();
|
|
116
|
+
|
|
117
|
+
console.debug("vc-app: Init vc-app");
|
|
118
|
+
|
|
119
|
+
const instance = getCurrentInstance();
|
|
120
|
+
|
|
121
|
+
const router = useRouter();
|
|
122
|
+
|
|
123
|
+
const onMenuItemClick = function ({ item, navigationCb }: IMenuClickEvent) {
|
|
124
|
+
console.debug(`vc-app#onMenuItemClick() called.`);
|
|
125
|
+
if (item.clickHandler && typeof item.clickHandler === "function") {
|
|
126
|
+
item.clickHandler(instance?.exposed);
|
|
127
|
+
} else {
|
|
128
|
+
emit("onOpen", { parentBlade: item.component, id: 0, navigationCb });
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const onToolbarButtonClick = function (item: Record<string, unknown>) {
|
|
133
|
+
console.debug(`vc-app#onToolbarButtonClick() called.`);
|
|
134
|
+
|
|
135
|
+
if (item.clickHandler && typeof item.clickHandler === "function") {
|
|
136
|
+
item.clickHandler(instance?.proxy);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const openDashboard = async () => {
|
|
141
|
+
console.debug(`openDashboard() called.`);
|
|
142
|
+
|
|
143
|
+
// Close all opened pages with onBeforeClose callback
|
|
144
|
+
await emit("onClose", 0);
|
|
145
|
+
|
|
146
|
+
router.push("/");
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
defineExpose({
|
|
150
|
+
openDashboard,
|
|
151
|
+
onToolbarButtonClick,
|
|
152
|
+
onMenuItemClick,
|
|
153
|
+
});
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<style lang="scss">
|
|
157
|
+
:root {
|
|
158
|
+
--app-background: linear-gradient(180deg, #e4f5fb 5.06%, #e8f3f2 100%),
|
|
159
|
+
linear-gradient(0deg, #e8f2f3, #e8f2f3), #eef2f8;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.vc-app {
|
|
163
|
+
background: var(--app-background);
|
|
164
|
+
|
|
165
|
+
&__workspace {
|
|
166
|
+
.vc-app_mobile & {
|
|
167
|
+
@apply p-0;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
</style>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="shrink-0 h-[var(--blade-header-height)] bg-[color:var(--blade-header-background-color)] flex items-center py-0 px-4 border-solid border-b border-b-[color:#eaedf3]"
|
|
4
|
+
>
|
|
5
|
+
<div v-if="icon" class="text-[color:var(--blade-header-icon-color)] mr-3">
|
|
6
|
+
<VcIcon :icon="icon" size="xxl"></VcIcon>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="overflow-hidden grow basis-0">
|
|
10
|
+
<div
|
|
11
|
+
class="text-[color:var(--blade-header-title-color)] text-lg truncate"
|
|
12
|
+
:class="{
|
|
13
|
+
'!text-[length:var(--blade-header-title-font-size)] font-medium':
|
|
14
|
+
!subtitle,
|
|
15
|
+
}"
|
|
16
|
+
>
|
|
17
|
+
{{ title }}
|
|
18
|
+
</div>
|
|
19
|
+
<div
|
|
20
|
+
v-if="subtitle"
|
|
21
|
+
class="text-[color:var(--blade-header-subtitle-color)] text-xs mt-1"
|
|
22
|
+
>
|
|
23
|
+
{{ subtitle }}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div v-if="$slots['actions']">
|
|
28
|
+
<slot name="actions"></slot>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div v-if="!$isMobile.value" class="flex items-center">
|
|
32
|
+
<template v-if="expandable">
|
|
33
|
+
<div
|
|
34
|
+
v-if="expanded"
|
|
35
|
+
class="text-[color:var(--blade-header-button-color)] ml-4 cursor-pointer hover:text-[color:var(--blade-header-button-color-hover)]"
|
|
36
|
+
@click="onCollapse"
|
|
37
|
+
>
|
|
38
|
+
<VcIcon icon="fas fa-window-minimize" size="s"></VcIcon>
|
|
39
|
+
</div>
|
|
40
|
+
<div v-else class="vc-blade-header__button" @click="onExpand">
|
|
41
|
+
<VcIcon icon="fas fa-window-maximize" size="s"></VcIcon>
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
<div
|
|
45
|
+
v-if="closable"
|
|
46
|
+
class="text-[color:var(--blade-header-button-color)] ml-4 cursor-pointer hover:text-[color:var(--blade-header-button-color-hover)]"
|
|
47
|
+
@click="onClose"
|
|
48
|
+
>
|
|
49
|
+
<VcIcon icon="fas fa-times"></VcIcon>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script lang="ts" setup>
|
|
56
|
+
import { VcIcon } from "@/components";
|
|
57
|
+
|
|
58
|
+
const props = defineProps({
|
|
59
|
+
expandable: {
|
|
60
|
+
type: Boolean,
|
|
61
|
+
default: false,
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
expanded: {
|
|
65
|
+
type: Boolean,
|
|
66
|
+
default: false,
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
closable: {
|
|
70
|
+
type: Boolean,
|
|
71
|
+
default: false,
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
title: {
|
|
75
|
+
type: String,
|
|
76
|
+
default: undefined,
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
subtitle: {
|
|
80
|
+
type: String,
|
|
81
|
+
default: undefined,
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
icon: {
|
|
85
|
+
type: String,
|
|
86
|
+
default: undefined,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const emit = defineEmits(["close", "expand", "collapse"]);
|
|
91
|
+
|
|
92
|
+
function onExpand(): void {
|
|
93
|
+
if (props.expandable) {
|
|
94
|
+
emit("expand");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function onCollapse(): void {
|
|
99
|
+
if (props.expandable) {
|
|
100
|
+
emit("collapse");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function onClose(): void {
|
|
105
|
+
if (props.closable) {
|
|
106
|
+
emit("close");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
<style lang="scss">
|
|
112
|
+
:root {
|
|
113
|
+
--blade-header-height: 50px;
|
|
114
|
+
--blade-header-background-color: #ffffff;
|
|
115
|
+
|
|
116
|
+
--blade-header-button-color: #a1c0d4;
|
|
117
|
+
--blade-header-button-color-hover: #7ea8c4;
|
|
118
|
+
|
|
119
|
+
--blade-header-icon-color: #a1c0d4;
|
|
120
|
+
|
|
121
|
+
--blade-header-title-font-size: 19px;
|
|
122
|
+
--blade-header-title-color: #2e3d4e;
|
|
123
|
+
|
|
124
|
+
--blade-header-subtitle-color: #a1c0d4;
|
|
125
|
+
}
|
|
126
|
+
</style>
|