diff --git a/docs/guide/global-context.md b/docs/guide/global-context.md index 213ec3bf5a..d1f90093bb 100644 --- a/docs/guide/global-context.md +++ b/docs/guide/global-context.md @@ -35,7 +35,7 @@ If you want to get the context programmatically (also type-safely), you can impo import { onSlideEnter, onSlideLeave, useDarkMode, useIsSlideActive, useNav, useSlideContext } from '@slidev/client' const { $slidev } = useSlideContext() -const { currentPage, currentLayout, currentSlideRoute } = useNav() +const { currentPage, currentLayout, currentFrontmatter, currentSlideRoute } = useNav() const { isDark } = useDarkMode() const isActive = useIsSlideActive() onSlideEnter((to, from) => { /* ... */ }) @@ -86,6 +86,7 @@ $nav.go(10) // go slide #10 $nav.currentPage // current slide number $nav.currentLayout // current layout name +$nav.currentFrontmatter // current slide frontmatter, including custom fields ``` For more properties available, refer to the [`SlidevContextNav` interface](https://github.com/slidevjs/slidev/blob/main/packages/client/composables/useNav.ts). diff --git a/packages/client/composables/useNav.test.ts b/packages/client/composables/useNav.test.ts new file mode 100644 index 0000000000..ebcaef5e3a --- /dev/null +++ b/packages/client/composables/useNav.test.ts @@ -0,0 +1,56 @@ +import type { ClicksContext, SlideRoute } from '@slidev/types' +import { describe, expect, it, vi } from 'vitest' +import { computed, ref } from 'vue' +import { useNavBase } from './useNav' + +vi.mock('#slidev/slides', async () => { + const { ref } = await import('vue') + return { slides: ref([]) } +}) + +vi.mock('../env', async () => { + const { ref } = await import('vue') + return { + configs: {}, + slideAspect: ref(16 / 9), + } +}) + +vi.mock('../state', async () => { + const { ref } = await import('vue') + return { hmrSkipTransition: ref(false) } +}) + +function route(no: number, frontmatter: Record): SlideRoute { + return { + no, + meta: { + slide: { frontmatter }, + }, + } as unknown as SlideRoute +} + +describe('useNavBase', () => { + it('exposes reactive frontmatter for the current slide', () => { + const currentRoute = ref(route(1, { title: 'Intro', section: 'start' })) + const nav = useNavBase( + computed(() => currentRoute.value), + computed(() => ({ current: 0, clicksStart: 0, total: 0 }) as ClicksContext), + ref(0), + ref(false), + ref(false), + ) + + expect(nav.currentFrontmatter.value).toEqual({ + title: 'Intro', + section: 'start', + }) + + currentRoute.value = route(2, { title: 'Details', section: 'body' }) + + expect(nav.currentFrontmatter.value).toEqual({ + title: 'Details', + section: 'body', + }) + }) +}) diff --git a/packages/client/composables/useNav.ts b/packages/client/composables/useNav.ts index 8df13fe5c5..f539596095 100644 --- a/packages/client/composables/useNav.ts +++ b/packages/client/composables/useNav.ts @@ -26,6 +26,7 @@ export interface SlidevContextNav { currentSlideRoute: ComputedRef currentTransition: ComputedRef currentLayout: ComputedRef + currentFrontmatter: ComputedRef> nextRoute: ComputedRef prevRoute: ComputedRef @@ -105,6 +106,7 @@ export function useNavBase( const currentPath = computed(() => getSlidePath(currentSlideRoute.value, isPresenter.value)) const currentSlideNo = computed(() => currentSlideRoute.value.no) const currentLayout = computed(() => currentSlideRoute.value.meta?.layout || (currentSlideNo.value === 1 ? 'cover' : 'default')) + const currentFrontmatter = computed(() => currentSlideRoute.value.meta.slide.frontmatter) const clicks = computed(() => clicksContext.value.current) const clicksStart = computed(() => clicksContext.value.clicksStart) @@ -223,6 +225,7 @@ export function useNavBase( currentPage: currentSlideNo, currentSlideRoute, currentLayout, + currentFrontmatter, currentTransition, clicksDirection, nextRoute,