diff --git a/packages/hooks/src/use-context.ts b/packages/hooks/src/use-context.ts
index 001d8aa6..cea9164f 100644
--- a/packages/hooks/src/use-context.ts
+++ b/packages/hooks/src/use-context.ts
@@ -1,5 +1,4 @@
import { inject, provide } from 'vue';
-import type { InjectionKey } from 'vue';
/**
* Use context
@@ -12,7 +11,7 @@ import type { InjectionKey } from 'vue';
* import { ref } from 'vue';
* import { useContext } from '@sa/hooks';
*
- * export const { setupStore, useStore } = useContext('demo', () => {
+ * export const [provideDemoContext, useDemoContext] = useContext('demo', () => {
* const count = ref(0);
*
* function increment() {
@@ -35,10 +34,10 @@ import type { InjectionKey } from 'vue';
*
A
*
*
* ``` // B.vue
* ```vue
@@ -46,9 +45,9 @@ import type { InjectionKey } from 'vue';
* B
*
*
* ```;
*
@@ -57,40 +56,41 @@ import type { InjectionKey } from 'vue';
* @param contextName Context name
* @param fn Context function
*/
-export default function useContext any>(contextName: string, fn: T) {
- type Context = ReturnType;
+export default function useContext, T>(
+ contextName: string,
+ composable: (...args: Arguments) => T
+) {
+ const key = Symbol(contextName);
- const { useProvide, useInject: useStore } = createContext(contextName);
+ /**
+ * Injects the context value.
+ *
+ * @param consumerName - The name of the component that is consuming the context. If provided, the component must be
+ * used within the context provider.
+ * @param defaultValue - The default value to return if the context is not provided.
+ * @returns The context value.
+ */
+ const useInject = (
+ consumerName?: N,
+ defaultValue?: T
+ ): N extends null | undefined ? T | null : T => {
+ const value = inject(key, defaultValue);
- function setupStore(...args: Parameters) {
- const context: Context = fn(...args);
- return useProvide(context);
- }
+ if (consumerName && !value) {
+ throw new Error(`\`${consumerName}\` must be used within \`${contextName}\``);
+ }
- return {
- /** Setup store in the parent component */
- setupStore,
- /** Use store in the child component */
- useStore
+ // @ts-expect-error - we want to return null if the value is undefined or null
+ return value || null;
};
-}
-/** Create context */
-function createContext(contextName: string) {
- const injectKey: InjectionKey = Symbol(contextName);
+ const useProvide = (...args: Arguments) => {
+ const value = composable(...args);
- function useProvide(context: T) {
- provide(injectKey, context);
+ provide(key, value);
- return context;
- }
-
- function useInject() {
- return inject(injectKey) as T;
- }
-
- return {
- useProvide,
- useInject
+ return value;
};
+
+ return [useProvide, useInject] as const;
}
diff --git a/src/layouts/base-layout/index.vue b/src/layouts/base-layout/index.vue
index c7d5b39d..aab04748 100644
--- a/src/layouts/base-layout/index.vue
+++ b/src/layouts/base-layout/index.vue
@@ -10,7 +10,7 @@ import GlobalTab from '../modules/global-tab/index.vue';
import GlobalContent from '../modules/global-content/index.vue';
import GlobalFooter from '../modules/global-footer/index.vue';
import ThemeDrawer from '../modules/theme-drawer/index.vue';
-import { setupMixMenuContext } from '../context';
+import { provideMixMenuContext } from '../modules/global-menu/context';
defineOptions({
name: 'BaseLayout'
@@ -18,7 +18,7 @@ defineOptions({
const appStore = useAppStore();
const themeStore = useThemeStore();
-const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = setupMixMenuContext();
+const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext();
const GlobalMenu = defineAsyncComponent(() => import('../modules/global-menu/index.vue'));
diff --git a/src/layouts/context/index.ts b/src/layouts/modules/global-menu/context/index.ts
similarity index 96%
rename from src/layouts/context/index.ts
rename to src/layouts/modules/global-menu/context/index.ts
index 9496f072..b83b71b3 100644
--- a/src/layouts/context/index.ts
+++ b/src/layouts/modules/global-menu/context/index.ts
@@ -5,7 +5,7 @@ import type { RouteKey } from '@elegant-router/types';
import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router';
-export const { setupStore: setupMixMenuContext, useStore: useMixMenuContext } = useContext('mix-menu', useMixMenu);
+export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu', useMixMenu);
function useMixMenu() {
const route = useRoute();
diff --git a/src/layouts/modules/global-menu/modules/horizontal-menu.vue b/src/layouts/modules/global-menu/modules/horizontal-menu.vue
index 93df1095..b696a309 100644
--- a/src/layouts/modules/global-menu/modules/horizontal-menu.vue
+++ b/src/layouts/modules/global-menu/modules/horizontal-menu.vue
@@ -2,7 +2,7 @@
import { GLOBAL_HEADER_MENU_ID } from '@/constants/app';
import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router';
-import { useMenu } from '../../../context';
+import { useMenu } from '../context';
defineOptions({
name: 'HorizontalMenu'
diff --git a/src/layouts/modules/global-menu/modules/top-hybrid-header-first.vue b/src/layouts/modules/global-menu/modules/top-hybrid-header-first.vue
index d4c2ca0c..937e5933 100644
--- a/src/layouts/modules/global-menu/modules/top-hybrid-header-first.vue
+++ b/src/layouts/modules/global-menu/modules/top-hybrid-header-first.vue
@@ -7,7 +7,7 @@ import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router';
-import { useMenu, useMixMenuContext } from '../../../context';
+import { useMenu, useMixMenuContext } from '../context';
defineOptions({
name: 'TopHybridHeaderFirst'
@@ -18,7 +18,8 @@ const appStore = useAppStore();
const themeStore = useThemeStore();
const routeStore = useRouteStore();
const { routerPushByKeyWithMetaQuery } = useRouterPush();
-const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } = useMixMenuContext();
+const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
+ useMixMenuContext('TopHybridHeaderFirst');
const { selectedKey } = useMenu();
const expandedKeys = ref([]);
diff --git a/src/layouts/modules/global-menu/modules/top-hybrid-sidebar-first.vue b/src/layouts/modules/global-menu/modules/top-hybrid-sidebar-first.vue
index 8d17b2f3..3bb0b2ce 100644
--- a/src/layouts/modules/global-menu/modules/top-hybrid-sidebar-first.vue
+++ b/src/layouts/modules/global-menu/modules/top-hybrid-sidebar-first.vue
@@ -4,7 +4,7 @@ import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router';
import FirstLevelMenu from '../components/first-level-menu.vue';
-import { useMenu, useMixMenuContext } from '../../../context';
+import { useMenu, useMixMenuContext } from '../context';
defineOptions({
name: 'TopHybridSidebarFirst'
@@ -13,7 +13,8 @@ defineOptions({
const appStore = useAppStore();
const themeStore = useThemeStore();
const { routerPushByKeyWithMetaQuery } = useRouterPush();
-const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } = useMixMenuContext();
+const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
+ useMixMenuContext('TopHybridSidebarFirst');
const { selectedKey } = useMenu();
diff --git a/src/layouts/modules/global-menu/modules/vertical-hybrid-header-first.vue b/src/layouts/modules/global-menu/modules/vertical-hybrid-header-first.vue
index 0fe41d6d..11b89733 100644
--- a/src/layouts/modules/global-menu/modules/vertical-hybrid-header-first.vue
+++ b/src/layouts/modules/global-menu/modules/vertical-hybrid-header-first.vue
@@ -9,7 +9,7 @@ import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router';
-import { useMenu, useMixMenuContext } from '../../../context';
+import { useMenu, useMixMenuContext } from '../context';
import FirstLevelMenu from '../components/first-level-menu.vue';
import GlobalLogo from '../../global-logo/index.vue';
@@ -34,7 +34,7 @@ const {
handleSelectSecondLevelMenu,
getActiveSecondLevelMenuKey,
childLevelMenus
-} = useMixMenuContext();
+} = useMixMenuContext('VerticalHybridHeaderFirst');
const { selectedKey } = useMenu();
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
diff --git a/src/layouts/modules/global-menu/modules/vertical-menu.vue b/src/layouts/modules/global-menu/modules/vertical-menu.vue
index e33e0de7..d42275be 100644
--- a/src/layouts/modules/global-menu/modules/vertical-menu.vue
+++ b/src/layouts/modules/global-menu/modules/vertical-menu.vue
@@ -7,7 +7,7 @@ import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router';
-import { useMenu } from '../../../context';
+import { useMenu } from '../context';
defineOptions({
name: 'VerticalMenu'
diff --git a/src/layouts/modules/global-menu/modules/vertical-mix-menu.vue b/src/layouts/modules/global-menu/modules/vertical-mix-menu.vue
index adbaa869..9d606521 100644
--- a/src/layouts/modules/global-menu/modules/vertical-mix-menu.vue
+++ b/src/layouts/modules/global-menu/modules/vertical-mix-menu.vue
@@ -10,7 +10,7 @@ import { useThemeStore } from '@/store/modules/theme';
import { useRouteStore } from '@/store/modules/route';
import { useRouterPush } from '@/hooks/common/router';
import { $t } from '@/locales';
-import { useMenu, useMixMenuContext } from '../../../context';
+import { useMenu, useMixMenuContext } from '../context';
import FirstLevelMenu from '../components/first-level-menu.vue';
import GlobalLogo from '../../global-logo/index.vue';
@@ -31,7 +31,7 @@ const {
isActiveFirstLevelMenuHasChildren,
getActiveFirstLevelMenuKey,
handleSelectFirstLevelMenu
-} = useMixMenuContext();
+} = useMixMenuContext('VerticalMixMenu');
const { selectedKey } = useMenu();
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);