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);