@vc-shell/framework 1.0.71 → 1.0.73

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 (46) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/core/composables/usePermissions/index.ts +21 -8
  3. package/core/composables/useUser/index.ts +5 -4
  4. package/core/plugins/modularity/index.ts +17 -2
  5. package/core/utilities/index.ts +1 -0
  6. package/core/utilities/kebabToCamel.ts +7 -0
  7. package/dist/core/composables/usePermissions/index.d.ts +1 -2
  8. package/dist/core/composables/usePermissions/index.d.ts.map +1 -1
  9. package/dist/core/composables/useUser/index.d.ts.map +1 -1
  10. package/dist/core/plugins/modularity/index.d.ts +4 -1
  11. package/dist/core/plugins/modularity/index.d.ts.map +1 -1
  12. package/dist/core/utilities/index.d.ts +1 -0
  13. package/dist/core/utilities/index.d.ts.map +1 -1
  14. package/dist/core/utilities/kebabToCamel.d.ts +2 -0
  15. package/dist/core/utilities/kebabToCamel.d.ts.map +1 -0
  16. package/dist/framework.mjs +11483 -11161
  17. package/dist/index.css +1 -1
  18. package/dist/index.d.ts +0 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/shared/components/blade-navigation/components/vc-blade-navigation/index.d.ts +5 -7
  21. package/dist/shared/components/blade-navigation/components/vc-blade-navigation/index.d.ts.map +1 -1
  22. package/dist/shared/components/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue.d.ts +5 -5
  23. package/dist/shared/components/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue.d.ts.map +1 -1
  24. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts +20 -4
  25. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
  26. package/dist/shared/components/blade-navigation/types/index.d.ts +1 -1
  27. package/dist/shared/components/blade-navigation/types/index.d.ts.map +1 -1
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/dist/ui/components/atoms/vc-link/vc-link.stories.d.ts.map +1 -1
  30. package/dist/ui/components/molecules/vc-select/index.d.ts +26 -26
  31. package/dist/ui/components/molecules/vc-select/vc-select.vue.d.ts +26 -26
  32. package/dist/ui/components/molecules/vc-select/vc-select.vue.d.ts.map +1 -1
  33. package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue.d.ts.map +1 -1
  34. package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
  35. package/package.json +5 -4
  36. package/shared/components/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue +6 -10
  37. package/shared/components/blade-navigation/composables/useBladeNavigation/index.ts +148 -62
  38. package/shared/components/blade-navigation/types/index.ts +1 -1
  39. package/shared/pages/LoginPage/components/login/Login.vue +1 -1
  40. package/ui/components/atoms/vc-link/vc-link.stories.ts +0 -1
  41. package/ui/components/atoms/vc-link/vc-link.vue +2 -2
  42. package/ui/components/molecules/vc-select/vc-select.vue +5 -5
  43. package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +1 -0
  44. package/ui/components/organisms/vc-app/vc-app.vue +6 -3
  45. package/dist/injectionSymbols.d.ts +0 -6
  46. package/dist/injectionSymbols.d.ts.map +0 -1
@@ -11,7 +11,7 @@ import {
11
11
  nextTick,
12
12
  } from "vue";
13
13
  import * as _ from "lodash-es";
14
- import { useRouter, useRoute } from "vue-router";
14
+ import { useRouter, RouteLocationNormalized } from "vue-router";
15
15
  import { usePermissions } from "../../../../../core/composables";
16
16
  import {
17
17
  IBladeContainer,
@@ -22,98 +22,136 @@ import {
22
22
  BladeComponentInternalInstance,
23
23
  BladeNavigationPlugin,
24
24
  notification,
25
+ BladePageComponent,
25
26
  } from "../../../..";
26
27
  import { bladeNavigationInstance } from "./../../plugin";
28
+ import pattern from "url-pattern";
29
+ import { useLocalStorage } from "@vueuse/core";
27
30
 
31
+ interface BladeData {
32
+ blade?: string;
33
+ param?: string;
34
+ options?: string;
35
+ }
28
36
  interface IUseBladeNavigation {
29
37
  readonly blades: Ref<IBladeContainer[]>;
30
- readonly parentBladeOptions: Ref<Record<string, unknown>>;
31
- readonly parentBladeParam: Ref<string>;
38
+ readonly workspaceOptions: Ref<Record<string, unknown>>;
39
+ readonly workspaceParam: Ref<string>;
40
+ readonly lastBladeData: Ref<BladeData>;
32
41
  bladesRefs: Ref<IBladeRef[]>;
33
- openBlade: <Blade extends ComponentPublicInstance = ComponentPublicInstance>({
34
- blade,
35
- param,
36
- options,
37
- onOpen,
38
- onClose,
39
- }: IBladeEvent<Blade>) => void;
42
+ openBlade: <Blade extends ComponentPublicInstance = ComponentPublicInstance>(
43
+ { blade, param, options, onOpen, onClose }: IBladeEvent<Blade>,
44
+ isWorkspace?: boolean
45
+ ) => void;
40
46
  closeBlade: (index: number) => Promise<boolean>;
41
47
  onParentCall: (index: number, args: IParentCallArgs) => void;
48
+ /**
49
+ * Resolves blades from vue-router's navigation guard 'to' param. Used to display blades after page reload or accessing via direct link.
50
+ * Returns a string containing the URL of the latest opened workspace.
51
+ * @param to
52
+ * @returns string
53
+ */
54
+ resolveBlades: (to: RouteLocationNormalized) => string;
55
+ resolveLastBlade: (pages: BladePageComponent[]) => void;
56
+ resolveUnknownRoutes: (to: RouteLocationNormalized) => string;
42
57
  }
43
58
 
44
59
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
- const parentBladeOptions: Ref<Record<string, any>> = ref();
46
- const parentBladeParam: Ref<string> = ref();
60
+ const workspaceOptions: Ref<Record<string, any>> = ref();
61
+ const workspaceParam: Ref<string> = ref();
62
+
63
+ const lastBladeData = useLocalStorage<BladeData>("VC_BLADE_DATA", {});
47
64
 
48
65
  export function useBladeNavigation(): IUseBladeNavigation {
49
66
  const router = useRouter();
50
- const route = useRoute();
67
+ const urlPattern = new pattern("(/:workspace(/:blade(/:param)))");
51
68
  const { checkPermission } = usePermissions();
52
69
  const isPrevented = ref(false);
70
+ const routes = router.getRoutes();
53
71
 
54
72
  const instance: BladeComponentInternalInstance = getCurrentInstance();
55
73
  const navigationInstance =
56
74
  (instance && inject<BladeNavigationPlugin>("bladeNavigationPlugin")) || bladeNavigationInstance;
57
75
 
58
76
  watch(
59
- navigationInstance.blades,
77
+ navigationInstance?.blades,
60
78
  (newVal) => {
61
- const lastBlade = newVal[newVal.length - 1];
79
+ if (newVal) {
80
+ const workspace = navigationInstance.bladesRefs.value[0]?.blade;
81
+ const lastBlade = newVal[newVal.length - 1];
62
82
 
63
- if (lastBlade && lastBlade.blade.url) {
64
- if (lastBlade.param) {
65
- addEntryToLocation(lastBlade.blade.url + "/" + lastBlade.param);
66
- } else {
67
- addEntryToLocation(lastBlade.blade.url);
83
+ if (workspace && workspace.blade.url) {
84
+ if (lastBlade && lastBlade.blade.url) {
85
+ const url = urlPattern.stringify({
86
+ workspace: workspace?.blade.url.substring(1),
87
+ blade: lastBlade?.blade.url.substring(1),
88
+ param: lastBlade?.param,
89
+ });
90
+
91
+ addEntryToLocation(url);
92
+ } else {
93
+ const url = workspace?.blade.url;
94
+ if (url) {
95
+ clearParentData();
96
+ addEntryToLocation(url);
97
+ }
98
+ }
68
99
  }
69
- } else if (!lastBlade) {
70
- clearParentData();
71
- addEntryToLocation(route.path);
72
100
  }
73
101
  },
74
102
  { deep: true }
75
103
  );
76
104
 
77
- async function openBlade<Blade extends ComponentPublicInstance>({
78
- blade,
79
- param,
80
- options,
81
- onOpen,
82
- onClose,
83
- }: IBladeEvent<Blade>) {
84
- const caller = instance && instance.vnode.type;
105
+ async function openWorkspace({ blade, param, options }: IBladeEvent) {
106
+ await closeBlade(0);
85
107
 
86
108
  const bladeComponent = unref(blade);
109
+
110
+ if (!isPrevented.value) {
111
+ workspaceOptions.value = unref(options);
112
+ workspaceParam.value = unref(param);
113
+
114
+ await router.replace(bladeComponent.url);
115
+ }
116
+ }
117
+
118
+ async function openBlade<Blade extends ComponentPublicInstance>(
119
+ { blade, param, options, onOpen, onClose }: IBladeEvent<Blade>,
120
+ isWorkspace = false
121
+ ) {
122
+ if (isWorkspace) {
123
+ openWorkspace({ blade, param, options });
124
+ return;
125
+ }
126
+
127
+ // caller blade component from instance
128
+ const instanceComponent = instance && instance.vnode.type;
129
+
130
+ // Caller blade index in blades array
87
131
  const callerIndex = navigationInstance.bladesRefs.value.findIndex((item) => {
88
- return _.isEqual(item.blade.blade, caller);
132
+ return _.isEqual(item.blade.blade, instanceComponent);
89
133
  });
90
- const existingChild =
91
- callerIndex >= 0 ? navigationInstance.bladesRefs.value[callerIndex + 1]?.blade.blade : undefined;
92
- const index = caller?.idx ? caller.idx : 0;
93
134
 
94
- const initiator = caller?.url && navigationInstance.bladesRefs.value.find((x) => x.blade?.blade.url === caller.url);
135
+ // Trying to determine if the calling blade already has a child in order to replace it
136
+ const isBladeAlreadyExist =
137
+ callerIndex >= 0 ? navigationInstance.bladesRefs.value[callerIndex + 1]?.blade.blade : undefined;
95
138
 
96
- if (!initiator && bladeComponent.url) {
97
- await closeBlade(0);
139
+ // Blade we want to open
140
+ const bladeComponent = unref(blade);
98
141
 
99
- if (!isPrevented.value) {
100
- parentBladeOptions.value = unref(options);
101
- parentBladeParam.value = unref(param);
142
+ // Check if caller blade has idx
143
+ const index = instanceComponent?.idx ? instanceComponent.idx : 0;
102
144
 
103
- await router.push(bladeComponent.url);
104
- }
105
- } else {
106
- if (existingChild === undefined) {
107
- bladeComponent.idx = index ? index + 1 : 1;
108
- } else if (existingChild) {
109
- await closeBlade(
110
- navigationInstance.blades.value.findIndex((x: IBladeContainer) => x.idx === existingChild.idx)
111
- );
112
- bladeComponent.idx = existingChild.idx;
113
- }
114
- if (!isPrevented.value) {
115
- await addBlade(bladeComponent, param, options, onOpen, onClose, index);
116
- }
145
+ if (isBladeAlreadyExist === undefined) {
146
+ bladeComponent.idx = index ? index + 1 : 1;
147
+ } else if (isBladeAlreadyExist) {
148
+ await closeBlade(
149
+ navigationInstance.blades.value.findIndex((x: IBladeContainer) => x.idx === isBladeAlreadyExist.idx)
150
+ );
151
+ bladeComponent.idx = isBladeAlreadyExist.idx;
152
+ }
153
+ if (!isPrevented.value) {
154
+ await addBlade(bladeComponent, param, options, onOpen, onClose, index);
117
155
  }
118
156
  }
119
157
 
@@ -136,8 +174,9 @@ export function useBladeNavigation(): IUseBladeNavigation {
136
174
  }
137
175
  }
138
176
  if (!isPrevented.value) {
139
- if (typeof navigationInstance.blades.value[index]?.onClose === "function") {
140
- navigationInstance.blades.value[index]?.onClose?.();
177
+ const blade = navigationInstance.blades.value[index];
178
+ if (typeof blade?.onClose === "function") {
179
+ blade?.onClose?.();
141
180
  }
142
181
 
143
182
  navigationInstance.blades.value.splice(index);
@@ -199,23 +238,70 @@ export function useBladeNavigation(): IUseBladeNavigation {
199
238
  }
200
239
 
201
240
  function addEntryToLocation(params: string) {
202
- history.pushState({}, null, "#" + params);
241
+ history.replaceState({}, null, "#" + params);
203
242
  }
204
243
 
205
244
  async function clearParentData() {
206
245
  nextTick(() => {
207
- parentBladeOptions.value = undefined;
208
- parentBladeParam.value = undefined;
246
+ workspaceOptions.value = undefined;
247
+ workspaceParam.value = undefined;
209
248
  });
210
249
  }
211
250
 
251
+ function resolveBlades(to: RouteLocationNormalized) {
252
+ const data = urlPattern.match(to.path);
253
+
254
+ const resolvedRoute = (bladeUrl: string) => routes.find((r) => r.path === "/" + bladeUrl);
255
+
256
+ if (resolvedRoute(data?.workspace) && to.path !== "/" + data.workspace) {
257
+ if (resolvedRoute(data?.blade)) {
258
+ lastBladeData.value = {
259
+ blade: "/" + data?.blade,
260
+ param: data?.param,
261
+ };
262
+ }
263
+
264
+ return "/" + data.workspace;
265
+ }
266
+ }
267
+
268
+ function resolveUnknownRoutes(to: RouteLocationNormalized) {
269
+ if (routes.find((r) => r.path !== to.path)) {
270
+ closeBlade(0);
271
+ return "/";
272
+ }
273
+ }
274
+
275
+ function clearSavedBladeData() {
276
+ lastBladeData.value = {};
277
+ }
278
+
279
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
280
+ async function setParentData({ param, options }: { param?: string; options?: Record<string, any> }) {
281
+ workspaceOptions.value = options;
282
+ workspaceParam.value = param;
283
+ }
284
+
285
+ function resolveLastBlade(pages: BladePageComponent[]) {
286
+ if (lastBladeData.value?.blade) {
287
+ const blade = pages?.find((b) => b.url === lastBladeData.value?.blade);
288
+ setParentData({ param: lastBladeData.value?.param });
289
+ openBlade({ blade, param: lastBladeData.value?.param });
290
+ clearSavedBladeData();
291
+ }
292
+ }
293
+
212
294
  return {
213
295
  blades: computed(() => navigationInstance.blades.value),
214
- parentBladeOptions: computed(() => parentBladeOptions.value),
215
- parentBladeParam: computed(() => parentBladeParam.value),
296
+ workspaceOptions: computed(() => workspaceOptions.value),
297
+ workspaceParam: computed(() => workspaceParam.value),
298
+ lastBladeData: computed(() => lastBladeData.value),
216
299
  bladesRefs: navigationInstance.bladesRefs,
217
300
  openBlade,
218
301
  closeBlade,
219
302
  onParentCall,
303
+ resolveBlades,
304
+ resolveUnknownRoutes,
305
+ resolveLastBlade,
220
306
  };
221
307
  }
@@ -23,7 +23,7 @@ export type CoreBladeComponentProps = {
23
23
  export type BladePageComponent = BladeConstructor;
24
24
 
25
25
  export type CoreBladeAdditionalSettings = {
26
- url?: string;
26
+ url?: `/${string}`;
27
27
  permissions?: string | string[];
28
28
  scope?: {
29
29
  notificationClick?: (notification: PushNotification | Record<string, any>) => IBladeEvent;
@@ -176,7 +176,7 @@ import { useRouter } from "vue-router";
176
176
  import { useIsFormValid, Field, useIsFormDirty, useForm } from "vee-validate";
177
177
  import { useSettings, useUser } from "./../../../../../core/composables";
178
178
  import { RequestPasswordResult, SignInResults } from "./../../../../../core/types";
179
- import { CommonPageComposables } from "typings";
179
+ import { CommonPageComposables } from "./../../../../../typings";
180
180
  import { asyncComputed } from "@vueuse/core";
181
181
  import AzureAdIcon from "./../../../../../assets/img/AzureAd.svg";
182
182
 
@@ -18,7 +18,6 @@ export const Primary: Story = {
18
18
  template: '<vc-link v-bind="args">This is a link</vc-link>',
19
19
  }),
20
20
  args: {
21
- to: undefined,
22
21
  active: false,
23
22
  disabled: false,
24
23
  },
@@ -6,7 +6,7 @@
6
6
  'tw-cursor-not-allowed tw-text-[color:var(--link-text-color-disabled)] hover:tw-text-[color:var(--link-text-color-disabled)] tw-no-underline':
7
7
  disabled,
8
8
  }"
9
- @click="onClick"
9
+ @click="onClickFn"
10
10
  >
11
11
  <slot></slot>
12
12
  </div>
@@ -26,7 +26,7 @@ export interface Emits {
26
26
  const props = defineProps<Props>();
27
27
  const emit = defineEmits<Emits>();
28
28
 
29
- function onClick(): void {
29
+ function onClickFn(): void {
30
30
  if (!props.disabled) {
31
31
  emit("click");
32
32
  }
@@ -202,7 +202,7 @@
202
202
  <div
203
203
  v-if="isOpened"
204
204
  ref="dropdownRef"
205
- v-on-click-outside="closeDropdown"
205
+ v-on-click-outside="[toggleDropdown, { ignore: [dropdownToggleRef] }]"
206
206
  class="tw-flex tw-flex-col tw-box-border tw-max-h-[300px] tw-h-auto tw-z-10 tw-overflow-hidden tw-absolute tw-bg-[color:var(--select-background-color)] tw-border tw-border-solid tw-border-[color:var(--select-border-color)] tw-border-t-[color:var(--select-background-color)] tw-rounded-b-[var(--select-border-radius)] tw-p-2"
207
207
  :style="dropdownStyle"
208
208
  >
@@ -251,7 +251,7 @@
251
251
  </template>
252
252
 
253
253
  <!-- eslint-disable @typescript-eslint/no-explicit-any -->
254
- <script lang="ts" setup generic="T extends Record<string, any>, P extends {results?: T[]; totalCount?: number }">
254
+ <script lang="ts" setup generic="T, P extends {results?: T[]; totalCount?: number }">
255
255
  import { ref, computed, watch, toRefs, nextTick, Ref } from "vue";
256
256
  import { vOnClickOutside } from "@vueuse/components";
257
257
  import * as _ from "lodash-es";
@@ -272,7 +272,7 @@ type FloatingInstanceType = UseFloatingReturn & {
272
272
  };
273
273
  };
274
274
 
275
- type Option = T | P["results"][number];
275
+ type Option = T & P["results"][number];
276
276
 
277
277
  defineSlots<{
278
278
  /**
@@ -882,10 +882,10 @@ function onInput(e: Event) {
882
882
  }
883
883
 
884
884
  const newValue = (e.target as HTMLInputElement).value;
885
- emitValue(newValue);
885
+ emitValueFunc(newValue);
886
886
  }
887
887
 
888
- function emitValue(val: string) {
888
+ function emitValueFunc(val: string) {
889
889
  emitValueFn = () => {
890
890
  emit("search", val);
891
891
  onSearch(val);
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <div
3
3
  class="tw-relative tw-flex tw-items-center tw-content-between tw-h-[var(--app-bar-height)] tw-bg-[color:var(--app-bar-background-color)] tw-px-4"
4
+ :class="{ '!tw-pr-0 !tw-pl-[10px]': $isMobile.value }"
4
5
  >
5
6
  <slot name="appSwitcher"></slot>
6
7
 
@@ -109,9 +109,12 @@ const onMenuItemClick = function ({ item }: { item: BladeMenu }) {
109
109
  if (item.clickHandler && typeof item.clickHandler === "function") {
110
110
  item.clickHandler(instance?.exposed);
111
111
  } else {
112
- openBlade({
113
- blade: markRaw(item.component),
114
- });
112
+ openBlade(
113
+ {
114
+ blade: markRaw(item.component),
115
+ },
116
+ true
117
+ );
115
118
  }
116
119
  };
117
120
 
@@ -1,6 +0,0 @@
1
- import { NotificationTemplateConstructor } from "./core/types";
2
- import { BladePageComponent } from "./shared/components/blade-navigation/types";
3
- import { InjectionKey } from "vue";
4
- export declare const pagesSymbol: InjectionKey<BladePageComponent[]>;
5
- export declare const notificationTemplatesSymbol: InjectionKey<NotificationTemplateConstructor[]>;
6
- //# sourceMappingURL=injectionSymbols.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"injectionSymbols.d.ts","sourceRoot":"","sources":["../injectionSymbols.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAEnC,eAAO,MAAM,WAAW,oCAAwD,CAAC;AACjF,eAAO,MAAM,2BAA2B,iDAEvC,CAAC"}