@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.
Files changed (142) hide show
  1. package/components/atoms/vc-badge/vc-badge.stories.ts +27 -0
  2. package/components/atoms/vc-badge/vc-badge.vue +63 -0
  3. package/components/atoms/vc-button/vc-button.stories.ts +34 -0
  4. package/components/atoms/vc-button/vc-button.vue +219 -0
  5. package/components/atoms/vc-card/vc-card.vue +137 -0
  6. package/components/atoms/vc-checkbox/vc-checkbox.stories.ts +25 -0
  7. package/components/atoms/vc-checkbox/vc-checkbox.vue +130 -0
  8. package/components/atoms/vc-col/vc-col.vue +22 -0
  9. package/components/atoms/vc-container/vc-container.stories.ts +31 -0
  10. package/components/atoms/vc-container/vc-container.vue +220 -0
  11. package/components/atoms/vc-hint/vc-hint.stories.ts +23 -0
  12. package/components/atoms/vc-hint/vc-hint.vue +11 -0
  13. package/components/atoms/vc-icon/vc-icon.stories.ts +32 -0
  14. package/components/atoms/vc-icon/vc-icon.vue +36 -0
  15. package/components/atoms/vc-image/vc-image.stories.ts +40 -0
  16. package/components/atoms/vc-image/vc-image.vue +122 -0
  17. package/components/atoms/vc-info-row/vc-info-row.vue +42 -0
  18. package/components/atoms/vc-label/vc-label.stories.ts +23 -0
  19. package/components/atoms/vc-label/vc-label.vue +49 -0
  20. package/components/atoms/vc-link/vc-link.stories.ts +30 -0
  21. package/components/atoms/vc-link/vc-link.vue +46 -0
  22. package/components/atoms/vc-loading/vc-loading.vue +30 -0
  23. package/components/atoms/vc-progress/vc-progress.stories.ts +25 -0
  24. package/components/atoms/vc-progress/vc-progress.vue +65 -0
  25. package/components/atoms/vc-row/vc-row.vue +13 -0
  26. package/components/atoms/vc-status/vc-status.stories.ts +26 -0
  27. package/components/atoms/vc-status/vc-status.vue +78 -0
  28. package/components/atoms/vc-status-icon/vc-status-icon.vue +21 -0
  29. package/components/atoms/vc-switch/vc-switch.stories.ts +27 -0
  30. package/components/atoms/vc-switch/vc-switch.vue +100 -0
  31. package/components/atoms/vc-widget/vc-widget.vue +85 -0
  32. package/components/index.ts +43 -0
  33. package/components/molecules/vc-breadcrumbs/_internal/vc-breadcrumbs-item/vc-breadcrumbs-item.vue +103 -0
  34. package/components/molecules/vc-breadcrumbs/vc-breadcrumbs.stories.ts +39 -0
  35. package/components/molecules/vc-breadcrumbs/vc-breadcrumbs.vue +21 -0
  36. package/components/molecules/vc-editor/vc-editor.vue +117 -0
  37. package/components/molecules/vc-file-upload/vc-file-upload.vue +134 -0
  38. package/components/molecules/vc-form/vc-form.stories.ts +23 -0
  39. package/components/molecules/vc-form/vc-form.vue +5 -0
  40. package/components/molecules/vc-input/vc-input.stories.ts +26 -0
  41. package/components/molecules/vc-input/vc-input.vue +443 -0
  42. package/components/molecules/vc-multivalue/vc-multivalue.vue +447 -0
  43. package/components/molecules/vc-notification/vc-notification.vue +101 -0
  44. package/components/molecules/vc-pagination/vc-pagination.stories.ts +23 -0
  45. package/components/molecules/vc-pagination/vc-pagination.vue +169 -0
  46. package/components/molecules/vc-rating/vc-rating.stories.ts +23 -0
  47. package/components/molecules/vc-rating/vc-rating.vue +77 -0
  48. package/components/molecules/vc-select/vc-select.stories.ts +25 -0
  49. package/components/molecules/vc-select/vc-select.vue +402 -0
  50. package/components/molecules/vc-slider/vc-slider.vue +106 -0
  51. package/components/molecules/vc-textarea/vc-textarea.stories.ts +23 -0
  52. package/components/molecules/vc-textarea/vc-textarea.vue +155 -0
  53. package/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +146 -0
  54. package/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue +148 -0
  55. package/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue +157 -0
  56. package/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +110 -0
  57. package/components/organisms/vc-app/vc-app.stories.ts +75 -0
  58. package/components/organisms/vc-app/vc-app.vue +171 -0
  59. package/components/organisms/vc-blade/_internal/vc-blade-header/vc-blade-header.vue +126 -0
  60. package/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue +223 -0
  61. package/components/organisms/vc-blade/_internal/vc-blade-toolbar/vc-blade-toolbar.vue +67 -0
  62. package/components/organisms/vc-blade/vc-blade.stories.ts +46 -0
  63. package/components/organisms/vc-blade/vc-blade.vue +87 -0
  64. package/components/organisms/vc-dynamic-property/vc-dynamic-property.vue +292 -0
  65. package/components/organisms/vc-gallery/_internal/vc-gallery-item/vc-gallery-item.vue +123 -0
  66. package/components/organisms/vc-gallery/_internal/vc-gallery-preview/vc-gallery-preview.vue +93 -0
  67. package/components/organisms/vc-gallery/vc-gallery.vue +186 -0
  68. package/components/organisms/vc-login-form/vc-login-form.stories.ts +55 -0
  69. package/components/organisms/vc-login-form/vc-login-form.vue +48 -0
  70. package/components/organisms/vc-popup/vc-popup.stories.ts +23 -0
  71. package/components/organisms/vc-popup/vc-popup.vue +97 -0
  72. package/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue +113 -0
  73. package/components/organisms/vc-table/_internal/vc-table-counter/vc-table-counter.vue +29 -0
  74. package/components/organisms/vc-table/_internal/vc-table-filter/vc-table-filter.vue +152 -0
  75. package/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue +272 -0
  76. package/components/organisms/vc-table/vc-table.stories.ts +99 -0
  77. package/components/organisms/vc-table/vc-table.vue +638 -0
  78. package/core/api/index.ts +1 -0
  79. package/core/api/platform.ts +8332 -0
  80. package/core/composables/index.ts +8 -0
  81. package/core/composables/useAutosave/index.ts +57 -0
  82. package/core/composables/useFunctions/debounce.ts +18 -0
  83. package/core/composables/useFunctions/delay.ts +7 -0
  84. package/core/composables/useFunctions/index.ts +21 -0
  85. package/core/composables/useFunctions/once.ts +14 -0
  86. package/core/composables/useFunctions/sleep.ts +4 -0
  87. package/core/composables/useFunctions/throttle.ts +17 -0
  88. package/core/composables/useI18n/index.ts +28 -0
  89. package/core/composables/useLogger/index.ts +24 -0
  90. package/core/composables/useNotifications/index.ts +116 -0
  91. package/core/composables/usePermissions/index.ts +32 -0
  92. package/core/composables/useSettings/index.ts +36 -0
  93. package/core/composables/useUser/index.ts +266 -0
  94. package/core/directives/autofocus/index.ts +9 -0
  95. package/core/directives/click-outside/index.ts +21 -0
  96. package/core/directives/index.ts +4 -0
  97. package/core/directives/loading/index.ts +28 -0
  98. package/core/directives/permissions/index.ts +20 -0
  99. package/core/plugins/index.ts +1 -0
  100. package/core/plugins/validation/index.ts +2 -0
  101. package/core/plugins/validation/rules.ts +196 -0
  102. package/core/types/index.ts +92 -0
  103. package/core/utilities/camelToSnake.ts +7 -0
  104. package/core/utilities/index.ts +1 -0
  105. package/dist/core/composables/useNotifications/index.d.ts +1 -1
  106. package/dist/core/composables/useNotifications/index.d.ts.map +1 -1
  107. package/dist/core/composables/useUser/index.d.ts +2 -2
  108. package/dist/core/composables/useUser/index.d.ts.map +1 -1
  109. package/dist/core/plugins/validation/index.d.ts.map +1 -1
  110. package/dist/core/types/index.d.ts +1 -1
  111. package/dist/core/types/index.d.ts.map +1 -1
  112. package/dist/framework.js +70 -97
  113. package/dist/framework.js.map +1 -1
  114. package/dist/shared/app-switcher/composables/useAppSwitcher/index.d.ts +1 -1
  115. package/dist/shared/app-switcher/composables/useAppSwitcher/index.d.ts.map +1 -1
  116. package/dist/shared/app-switcher/index.d.ts +2 -2
  117. package/dist/shared/app-switcher/index.d.ts.map +1 -1
  118. package/dist/shared/blade-navigation/composables/useBladeNavigation/index.d.ts +1 -1
  119. package/dist/shared/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
  120. package/dist/shared/blade-navigation/types/index.d.ts +1 -1
  121. package/dist/shared/blade-navigation/types/index.d.ts.map +1 -1
  122. package/dist/style.css +1 -1
  123. package/dist/tsconfig.tsbuildinfo +1 -0
  124. package/dist/vite.config.d.ts.map +1 -1
  125. package/package.json +11 -8
  126. package/shared/app-switcher/components/index.ts +1 -0
  127. package/shared/app-switcher/components/vc-app-switcher/vc-app-switcher.vue +90 -0
  128. package/shared/app-switcher/composables/index.ts +1 -0
  129. package/shared/app-switcher/composables/useAppSwitcher/index.ts +54 -0
  130. package/shared/app-switcher/index.ts +14 -0
  131. package/shared/assets/components/assets-details/assets-details.vue +138 -0
  132. package/shared/assets/components/index.ts +1 -0
  133. package/shared/assets/index.ts +19 -0
  134. package/shared/assets/locales/en.json +29 -0
  135. package/shared/assets/locales/index.ts +2 -0
  136. package/shared/blade-navigation/components/index.ts +1 -0
  137. package/shared/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue +84 -0
  138. package/shared/blade-navigation/composables/index.ts +1 -0
  139. package/shared/blade-navigation/composables/useBladeNavigation/index.ts +216 -0
  140. package/shared/blade-navigation/index.ts +15 -0
  141. package/shared/blade-navigation/types/index.ts +52 -0
  142. package/shared/index.ts +16 -0
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <VcBlade
3
+ :title="options.editableAsset.name"
4
+ :subtitle="$t('ASSETS.PAGES.DETAILS.SUBTITLE')"
5
+ :expanded="expanded"
6
+ :closable="closable"
7
+ :toolbarItems="bladeToolbar"
8
+ @close="$emit('close:blade')"
9
+ >
10
+ <!-- Blade contents -->
11
+ <div class="flex grow-1 border-t border-solid border-t-[#eaedf3]">
12
+ <div class="assets-details__content grow basis-0">
13
+ <VcContainer :no-padding="true">
14
+ <div class="p-4">
15
+ <VcForm>
16
+ <VcImage
17
+ class="mb-4"
18
+ :src="localImage.url"
19
+ size="xl"
20
+ :bordered="true"
21
+ ></VcImage>
22
+ <VcInput
23
+ class="mb-4"
24
+ :label="$t('ASSETS.PAGES.DETAILS.FIELDS.NAME.TITLE')"
25
+ v-model="localImage.name"
26
+ :clearable="true"
27
+ is-required
28
+ :placeholder="
29
+ $t('ASSETS.PAGES.DETAILS.FIELDS.NAME.PLACEHOLDER')
30
+ "
31
+ ></VcInput>
32
+ <VcInput
33
+ class="mb-4"
34
+ :label="$t('ASSETS.PAGES.DETAILS.FIELDS.ALT.TITLE')"
35
+ v-model="localImage.altText"
36
+ :clearable="true"
37
+ :placeholder="$t('ASSETS.PAGES.DETAILS.FIELDS.ALT.PLACEHOLDER')"
38
+ :tooltip="$t('ASSETS.PAGES.DETAILS.FIELDS.ALT.TOOLTIP')"
39
+ is-required
40
+ ></VcInput>
41
+ <VcTextarea
42
+ class="mb-4"
43
+ :label="$t('ASSETS.PAGES.DETAILS.FIELDS.DESCRIPTION.TITLE')"
44
+ v-model="localImage.description"
45
+ :placeholder="
46
+ $t('ASSETS.PAGES.DETAILS.FIELDS.DESCRIPTION.PLACEHOLDER')
47
+ "
48
+ is-required
49
+ ></VcTextarea>
50
+ </VcForm>
51
+ </div>
52
+ </VcContainer>
53
+ </div>
54
+ </div>
55
+ </VcBlade>
56
+ </template>
57
+
58
+ <script lang="ts" setup>
59
+ import { computed, reactive, unref } from "vue";
60
+ import { useI18n } from "@/core/composables";
61
+ import { IParentCallArgs } from "@/shared";
62
+ import {
63
+ VcBlade,
64
+ VcContainer,
65
+ VcForm,
66
+ VcImage,
67
+ VcInput,
68
+ VcTextarea,
69
+ } from "@/components";
70
+
71
+ interface ILocalImage {
72
+ url: string;
73
+ name: string;
74
+ altText: string;
75
+ description: string;
76
+ }
77
+
78
+ export interface Props {
79
+ expanded?: boolean;
80
+ closable?: boolean;
81
+ options?: {
82
+ editableAsset?: ILocalImage;
83
+ sortHandler?: (remove: boolean, localImage: ILocalImage) => void;
84
+ };
85
+ }
86
+
87
+ export interface Emits {
88
+ (event: "parent:call", args: IParentCallArgs): void;
89
+ (event: "close:blade"): void;
90
+ }
91
+
92
+ const props = withDefaults(defineProps<Props>(), {
93
+ expanded: true,
94
+ closable: true,
95
+ options: () => ({}),
96
+ });
97
+
98
+ const emit = defineEmits<Emits>();
99
+ const { t } = useI18n();
100
+ const localImage = reactive({ ...props.options.editableAsset });
101
+
102
+ const bladeToolbar = [
103
+ {
104
+ id: "save",
105
+ title: t("ASSETS.PAGES.DETAILS.TOOLBAR.SAVE"),
106
+ icon: "fas fa-save",
107
+ clickHandler() {
108
+ if (
109
+ props.options.sortHandler &&
110
+ typeof props.options.sortHandler === "function"
111
+ ) {
112
+ props.options.sortHandler(false, localImage);
113
+ emit("close:blade");
114
+ }
115
+ },
116
+ },
117
+ {
118
+ id: "delete",
119
+ title: t("ASSETS.PAGES.DETAILS.TOOLBAR.DELETE"),
120
+ icon: "fas fa-trash",
121
+ clickHandler() {
122
+ if (
123
+ window.confirm(
124
+ unref(computed(() => t("ASSETS.PAGES.DETAILS.DELETE_CONFIRMATION")))
125
+ )
126
+ ) {
127
+ if (
128
+ props.options.sortHandler &&
129
+ typeof props.options.sortHandler === "function"
130
+ ) {
131
+ props.options.sortHandler(true, localImage);
132
+ emit("close:blade");
133
+ }
134
+ }
135
+ },
136
+ },
137
+ ];
138
+ </script>
@@ -0,0 +1 @@
1
+ export {default as AssetsDetails} from './assets-details/assets-details.vue'
@@ -0,0 +1,19 @@
1
+ import { App } from "vue";
2
+ import * as components from "./components";
3
+ import * as locales from "./locales";
4
+
5
+ export default {
6
+ install(app: App): void {
7
+ Object.entries(components).forEach(([pageName, page]) => {
8
+ app.component(pageName, page);
9
+ app.config.globalProperties.pages?.push(page);
10
+ });
11
+
12
+ // Load locales
13
+ Object.entries(locales).forEach(([key, message]) => {
14
+ app.config.globalProperties.$mergeLocaleMessage(key, message);
15
+ });
16
+ },
17
+ };
18
+
19
+ export * from './components'
@@ -0,0 +1,29 @@
1
+ {
2
+ "ASSETS": {
3
+ "PAGES": {
4
+ "DETAILS": {
5
+ "SUBTITLE": "Image edit",
6
+ "TOOLBAR": {
7
+ "SAVE": "Save",
8
+ "DELETE": "Delete"
9
+ },
10
+ "FIELDS": {
11
+ "NAME": {
12
+ "TITLE": "Image name",
13
+ "PLACEHOLDER": "Enter image name"
14
+ },
15
+ "ALT": {
16
+ "TITLE": "Image alternative text",
17
+ "PLACEHOLDER": "Enter image alt text",
18
+ "TOOLTIP": "This text will be displayed in case of disabled/unloaded image on the page"
19
+ },
20
+ "DESCRIPTION": {
21
+ "TITLE": "Meta description",
22
+ "PLACEHOLDER": "Enter meta description"
23
+ }
24
+ },
25
+ "DELETE_CONFIRMATION": "Are you sure you want to delete this image?"
26
+ }
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,2 @@
1
+ import * as en from "./en.json";
2
+ export { en };
@@ -0,0 +1 @@
1
+ export { default as VcBladeNavigation } from "./vc-blade-navigation/vc-blade-navigation.vue";
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <router-view v-slot="{ Component, route }">
3
+ <component
4
+ :is="Component"
5
+ :closable="false"
6
+ v-show="$isMobile.value ? !blades.length : blades.length <= 1"
7
+ @open:blade="$emit('onOpen', { blade: $event, id: 0 })"
8
+ :options="parentBladeOptions"
9
+ :expanded="blades.length === 0"
10
+ :param="resolveParam"
11
+ :key="route"
12
+ ref="parentRef"
13
+ >
14
+ </component>
15
+ </router-view>
16
+ <component
17
+ v-for="(blade, i) in blades"
18
+ v-show="i >= blades.length - ($isMobile.value ? 1 : 2)"
19
+ :key="`blade_${i}`"
20
+ :is="blade.component"
21
+ :param="blade.param"
22
+ :closable="i >= 0"
23
+ :expanded="i === blades.length - 1"
24
+ :options="blade.bladeOptions"
25
+ @open:blade="$emit('onOpen', { blade: $event, id: blade.idx })"
26
+ @close:blade="$emit('onClose', i)"
27
+ @close:children="$emit('onClose', i + 1)"
28
+ @parent:call="$emit('onParentCall', { id: i, cb: $event })"
29
+ :ref="setBladesRef"
30
+ ></component>
31
+ </template>
32
+
33
+ <script lang="ts" setup>
34
+ import { computed, onBeforeUpdate, ref } from "vue";
35
+ import { useRoute } from "vue-router";
36
+ import {
37
+ IBladeContainer,
38
+ IBladeElement,
39
+ IBladeEvent,
40
+ IParentCallArgs,
41
+ } from "@/shared";
42
+
43
+ export interface Props {
44
+ blades: IBladeContainer[];
45
+ parentBladeOptions: Record<string, unknown>;
46
+ parentBladeParam: string;
47
+ }
48
+
49
+ export interface Emits {
50
+ (event: "onOpen", blade: { blade: IBladeEvent; id: number }): void;
51
+ (event: "onClose", index: number): void;
52
+ (event: "onParentCall", args: { id: number; args: IParentCallArgs }): void;
53
+ }
54
+
55
+ defineEmits<Emits>();
56
+
57
+ const props = withDefaults(defineProps<Props>(), {
58
+ blades: () => [],
59
+ parentBladeOptions: () => ({}),
60
+ parentBladeParam: undefined,
61
+ });
62
+
63
+ const route = useRoute();
64
+ const bladesRefs = ref([]);
65
+ const parentRef = ref();
66
+
67
+ onBeforeUpdate(() => {
68
+ bladesRefs.value = [parentRef.value];
69
+ });
70
+
71
+ const setBladesRef = (el: IBladeElement) => {
72
+ if (el && Object.keys(el).length) {
73
+ bladesRefs.value.push(el);
74
+ }
75
+ };
76
+
77
+ const resolveParam = computed(() => {
78
+ return props.parentBladeParam ? props.parentBladeParam : route.params.param;
79
+ });
80
+
81
+ defineExpose({
82
+ bladesRefs,
83
+ });
84
+ </script>
@@ -0,0 +1 @@
1
+ export {default as useBladeNavigation} from './useBladeNavigation'
@@ -0,0 +1,216 @@
1
+ import { computed, ref, unref, watch, Ref } from "vue";
2
+ import { isEqual } from "lodash-es";
3
+ import { useRouter, useRoute, NavigationFailure } from "vue-router";
4
+ import { usePermissions } from "@/core/composables";
5
+ import {
6
+ ExtendedComponent,
7
+ IBladeContainer,
8
+ IBladeElement,
9
+ IBladeEvent,
10
+ IParentCallArgs,
11
+ } from "@/shared";
12
+
13
+ interface IUseBladeNavigation {
14
+ readonly blades: Ref<IBladeContainer[]>;
15
+ readonly parentBladeOptions: Ref<Record<string, unknown>>;
16
+ readonly parentBladeParam: Ref<string>;
17
+ bladesRefs: Ref<IBladeElement[]>;
18
+ openBlade: (
19
+ {
20
+ parentBlade,
21
+ component,
22
+ param,
23
+ bladeOptions,
24
+ onOpen,
25
+ onClose,
26
+ }: IBladeEvent,
27
+ index?: number,
28
+ navigationCb?: () => Promise<void | NavigationFailure>
29
+ ) => void;
30
+ closeBlade: (index: number) => void;
31
+ onParentCall: (index: number, args: IParentCallArgs) => void;
32
+ }
33
+
34
+ const blades = ref<IBladeContainer[]>([]);
35
+ const bladesRefs = ref<IBladeElement[]>([]);
36
+ const parentBladeOptions = ref<Record<string, unknown>>();
37
+ const parentBladeParam = ref<string>();
38
+
39
+ export default (): IUseBladeNavigation => {
40
+ const router = useRouter();
41
+ const route = useRoute();
42
+ const { checkPermission } = usePermissions();
43
+ const isPrevented = ref(false);
44
+
45
+ watch(
46
+ () => blades.value,
47
+ (newVal) => {
48
+ const lastBlade = newVal[newVal.length - 1];
49
+
50
+ if (lastBlade && lastBlade.component.url) {
51
+ if (lastBlade.param) {
52
+ addEntryToLocation(lastBlade.component.url + "/" + lastBlade.param);
53
+ } else {
54
+ addEntryToLocation(lastBlade.component.url);
55
+ }
56
+ } else if (!lastBlade) {
57
+ addEntryToLocation(route.path);
58
+ }
59
+ },
60
+ { deep: true }
61
+ );
62
+
63
+ async function openBlade(
64
+ {
65
+ parentBlade,
66
+ component: blade,
67
+ param,
68
+ bladeOptions,
69
+ onOpen,
70
+ onClose,
71
+ }: IBladeEvent,
72
+ index?: number,
73
+ navigationCb?: () => Promise<void | NavigationFailure>
74
+ ) {
75
+ console.debug(`openBlade(${1}) called.`);
76
+
77
+ const parent = unref(parentBlade);
78
+ const child = unref(blade);
79
+ const existingChild = findBlade(child);
80
+
81
+ if (parent && parent.url) {
82
+ await closeBlade(0);
83
+
84
+ if (!isPrevented.value) {
85
+ parentBladeOptions.value = unref(bladeOptions);
86
+ parentBladeParam.value = unref(param);
87
+ if (navigationCb && typeof navigationCb === "function") {
88
+ try {
89
+ await navigationCb();
90
+ } catch (e) {
91
+ throw "Navigation failure";
92
+ }
93
+ } else if (!navigationCb) {
94
+ await router.push(parent.url);
95
+ }
96
+ }
97
+ }
98
+
99
+ if (child) {
100
+ if (existingChild === undefined) {
101
+ child.idx = index + 1;
102
+ } else if (existingChild) {
103
+ await closeBlade(
104
+ blades.value.findIndex((x) => x.idx === existingChild.idx)
105
+ );
106
+ child.idx = existingChild.idx;
107
+ }
108
+
109
+ await addBlade(child, param, bladeOptions, onOpen, onClose, index);
110
+ }
111
+ }
112
+
113
+ async function closeBlade(index: number) {
114
+ if (index < bladesRefs.value.length) {
115
+ const children = bladesRefs.value.slice(index).reverse();
116
+
117
+ isPrevented.value = false;
118
+ for (let i = 0; i < children.length; i++) {
119
+ if (
120
+ children[i]?.onBeforeClose &&
121
+ typeof children[i].onBeforeClose === "function"
122
+ ) {
123
+ const result = await children[i].onBeforeClose();
124
+ if (result === false) {
125
+ isPrevented.value = true;
126
+ break;
127
+ }
128
+ }
129
+ }
130
+ if (!isPrevented.value) {
131
+ if (typeof blades.value[index]?.onClose === "function") {
132
+ blades.value[index]?.onClose?.();
133
+ }
134
+ blades.value.splice(index);
135
+ } else {
136
+ throw "Closing prevented";
137
+ }
138
+ }
139
+ }
140
+
141
+ async function addBlade(
142
+ blade: ExtendedComponent,
143
+ param: string,
144
+ bladeOptions: Record<string, unknown>,
145
+ onOpen: () => void,
146
+ onClose: () => void,
147
+ index: number
148
+ ) {
149
+ if (blades.value.length > index) {
150
+ await closeBlade(index);
151
+ }
152
+
153
+ if (blade && checkPermission(blade.permissions)) {
154
+ blades.value.push({
155
+ component: blade,
156
+ bladeOptions,
157
+ param,
158
+ onOpen,
159
+ onClose,
160
+ idx: blade.idx,
161
+ });
162
+
163
+ if (onOpen && typeof onOpen === "function") {
164
+ onOpen();
165
+ }
166
+ } else {
167
+ // TODO temporary access alert
168
+ alert("Access restricted");
169
+ }
170
+ }
171
+
172
+ async function onParentCall(index: number, args: IParentCallArgs) {
173
+ console.debug(
174
+ `vc-app#onParentCall(${index}, { method: ${args.method} }) called.`
175
+ );
176
+
177
+ if (index >= 0) {
178
+ const currentParent = unref(
179
+ bladesRefs.value[bladesRefs.value.length - 2]
180
+ );
181
+
182
+ if (currentParent) {
183
+ if (args.method && typeof currentParent[args.method] === "function") {
184
+ const method = currentParent[args.method] as (
185
+ args: unknown
186
+ ) => Promise<unknown>;
187
+ const result = await method(args.args);
188
+ if (typeof args.callback === "function") {
189
+ args.callback(result);
190
+ }
191
+ } else {
192
+ // TODO temporary alert
193
+ console.error(`No such method: ${args.method}.`);
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ function addEntryToLocation(params: string) {
200
+ history.pushState({}, null, "#" + params);
201
+ }
202
+
203
+ function findBlade(blade: ExtendedComponent) {
204
+ return blades.value.find((x) => isEqual(x.component, blade));
205
+ }
206
+
207
+ return {
208
+ blades: computed(() => blades.value),
209
+ parentBladeOptions: computed(() => parentBladeOptions.value),
210
+ parentBladeParam: computed(() => parentBladeParam.value),
211
+ bladesRefs,
212
+ openBlade,
213
+ closeBlade,
214
+ onParentCall,
215
+ };
216
+ };
@@ -0,0 +1,15 @@
1
+ import { App } from "vue";
2
+ import * as components from "./components";
3
+
4
+ export default {
5
+ install(app: App): void {
6
+ // Register exported pages
7
+ Object.entries(components).forEach(([componentName, component]) => {
8
+ app.component(componentName, component);
9
+ });
10
+ },
11
+ };
12
+
13
+ export * from "./components";
14
+ export * from "./composables";
15
+ export * from './types'
@@ -0,0 +1,52 @@
1
+ import {Component, ComponentPublicInstance} from "vue";
2
+ import {IMenuItems} from "@/core/types";
3
+ import {NavigationFailure} from "vue-router";
4
+
5
+ /* onParentCall event interface */
6
+ export interface IParentCallArgs {
7
+ method: string;
8
+ args?: unknown;
9
+ callback?: (args: unknown) => void;
10
+ }
11
+
12
+ /* extended component */
13
+ export type ExtendedComponent = Component & {
14
+ url?: string;
15
+ permissions?: string | string[];
16
+ idx?: number;
17
+ };
18
+
19
+ /* blade interface for navigation */
20
+ export interface IBladeContainer extends IBladeEvent{
21
+ idx?: number;
22
+ }
23
+
24
+ /* blade exposed methods */
25
+ export interface IBladeElement extends ComponentPublicInstance {
26
+ onBeforeClose?: () => Promise<boolean>;
27
+ title?: string;
28
+ reloadParent?: () => void;
29
+ openDashboard?: () => void;
30
+ }
31
+
32
+ /* emitted blade event */
33
+ export interface IBladeEvent {
34
+ parentBlade?: ExtendedComponent;
35
+ component?: ExtendedComponent;
36
+ bladeOptions?: Record<string, unknown>;
37
+ param?: string;
38
+ onOpen?: () => void;
39
+ onClose?: () => void;
40
+ }
41
+
42
+ /* menu item event */
43
+ export interface IMenuClickEvent {
44
+ item: IMenuItems
45
+ navigationCb: () => Promise<void | NavigationFailure>
46
+ }
47
+
48
+ /* openBlade args interface */
49
+ export interface IOpenBlade extends IBladeEvent {
50
+ id?: number
51
+ navigationCb?: () => Promise<void | NavigationFailure>
52
+ }
@@ -0,0 +1,16 @@
1
+ import { App } from "vue";
2
+ import { default as AssetsDetails } from "./assets";
3
+ import { default as VcAppSwitcher } from "./app-switcher";
4
+ import { default as VcBladeNavigation } from "./blade-navigation";
5
+
6
+ const components = [AssetsDetails, VcAppSwitcher, VcBladeNavigation];
7
+
8
+ export function init(app: App): App {
9
+ components.forEach((component) => app.use(component));
10
+
11
+ return app;
12
+ }
13
+
14
+ export * from "./assets";
15
+ export * from "./app-switcher";
16
+ export * from "./blade-navigation";