diff --git a/.changeset/hip-bobcats-cover.md b/.changeset/hip-bobcats-cover.md new file mode 100644 index 0000000000..9bd789f85d --- /dev/null +++ b/.changeset/hip-bobcats-cover.md @@ -0,0 +1,5 @@ +--- +"gitbook": minor +--- + +Best effort at preserving current variant when navigating between sections by matching the pathname against site spaces in the new section. diff --git a/packages/gitbook/src/components/Header/SpacesDropdown.tsx b/packages/gitbook/src/components/Header/SpacesDropdown.tsx index 1523774493..dd6887d955 100644 --- a/packages/gitbook/src/components/Header/SpacesDropdown.tsx +++ b/packages/gitbook/src/components/Header/SpacesDropdown.tsx @@ -1,8 +1,7 @@ import type { SiteSpace } from '@gitbook/api'; +import { getSiteSpaceURL } from '@/lib/sites'; import { tcls } from '@/lib/tailwind'; - -import { joinPath } from '@/lib/paths'; import type { GitBookSiteContext } from '@v2/lib/context'; import { DropdownChevron, DropdownMenu } from './DropdownMenu'; import { SpacesDropdownMenuItem } from './SpacesDropdownMenuItem'; @@ -14,7 +13,6 @@ export function SpacesDropdown(props: { className?: string; }) { const { context, siteSpace, siteSpaces, className } = props; - const { linker } = context; return ( @@ -83,14 +79,3 @@ export function SpacesDropdown(props: { ); } - -/** - * When the site is not published yet, `urls.published` is not available. - * To ensure navigation works in preview, we compute a relative URL from the siteSpace path. - */ -function getFallbackSiteSpaceURL(siteSpace: SiteSpace, context: GitBookSiteContext) { - const { linker, sections } = context; - return linker.toPathInSite( - sections?.current ? joinPath(sections.current.path, siteSpace.path) : siteSpace.path - ); -} diff --git a/packages/gitbook/src/components/SiteSections/encodeClientSiteSections.ts b/packages/gitbook/src/components/SiteSections/encodeClientSiteSections.ts index 49b73a8e1a..1c8cd09899 100644 --- a/packages/gitbook/src/components/SiteSections/encodeClientSiteSections.ts +++ b/packages/gitbook/src/components/SiteSections/encodeClientSiteSections.ts @@ -1,3 +1,4 @@ +import { getSectionURL, getSiteSpaceURL } from '@/lib/sites'; import type { SiteSection, SiteSectionGroup } from '@gitbook/api'; import type { GitBookSiteContext, SiteSections } from '@v2/lib/context'; @@ -46,15 +47,35 @@ export function encodeClientSiteSections(context: GitBookSiteContext, sections: } function encodeSection(context: GitBookSiteContext, section: SiteSection) { - const { linker } = context; return { id: section.id, title: section.title, description: section.description, icon: section.icon, object: section.object, - url: section.urls.published - ? linker.toLinkForContent(section.urls.published) - : linker.toPathInSite(section.path), + url: findBestTargetURL(context, section), }; } + +/** + * Find the best default site space to navigate to for a givent section: + * 1. If we are on the default, continue on the default. + * 2. If a site space has the same path as the current one, return it. + * 3. Otherwise, return the default one. + */ +function findBestTargetURL(context: GitBookSiteContext, section: SiteSection) { + const { siteSpace: currentSiteSpace } = context; + + if (section.siteSpaces.length === 1 || currentSiteSpace.default) { + return getSectionURL(context, section); + } + + const bestMatch = section.siteSpaces.find( + (siteSpace) => siteSpace.path === currentSiteSpace.path + ); + if (bestMatch) { + return getSiteSpaceURL(context, bestMatch); + } + + return getSectionURL(context, section); +} diff --git a/packages/gitbook/src/lib/sites.ts b/packages/gitbook/src/lib/sites.ts index 88d63e75d3..1827683e48 100644 --- a/packages/gitbook/src/lib/sites.ts +++ b/packages/gitbook/src/lib/sites.ts @@ -1,4 +1,6 @@ import type { SiteSection, SiteSectionGroup, SiteSpace, SiteStructure } from '@gitbook/api'; +import type { GitBookSiteContext } from '@v2/lib/context'; +import { joinPath } from './paths'; /** * Get all sections from a site structure. @@ -64,6 +66,34 @@ export function findSiteSpaceById(siteStructure: SiteStructure, spaceId: string) return null; } +/** + * Get the URL to navigate to for a section. + * When the site is not published yet, `urls.published` is not available. + * To ensure navigation works in preview, we compute a relative URL from the siteSection path. + */ +export function getSectionURL(context: GitBookSiteContext, section: SiteSection) { + const { linker } = context; + return section.urls.published + ? linker.toLinkForContent(section.urls.published) + : linker.toPathInSite(section.path); +} + +/** + * Get the URL to navigate to for a site space. + * When the site is not published yet, `urls.published` is not available. + * To ensure navigation works in preview, we compute a relative URL from the siteSpace path. + */ +export function getSiteSpaceURL(context: GitBookSiteContext, siteSpace: SiteSpace) { + const { linker, sections } = context; + if (siteSpace.urls.published) { + return linker.toLinkForContent(siteSpace.urls.published); + } + + return linker.toPathInSite( + sections?.current ? joinPath(sections.current.path, siteSpace.path) : siteSpace.path + ); +} + function findSiteSpaceByIdInSections(sections: SiteSection[], spaceId: string): SiteSpace | null { for (const section of sections) { const siteSpace =