mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 15:45:13 +00:00
Support for hidden sections (#3831)
This commit is contained in:
@@ -25,12 +25,12 @@ export function Header(props: {
|
||||
};
|
||||
}) {
|
||||
const { context, withTopHeader, variants } = props;
|
||||
const { siteSpace, siteSpaces, sections, customization } = context;
|
||||
const { siteSpace, visibleSiteSpaces, visibleSections, customization } = context;
|
||||
|
||||
const withSections = Boolean(
|
||||
sections &&
|
||||
(sections.list.length > 1 || // Show section tabs if there are at least 2 sections or at least 1 section group
|
||||
sections.list.some((s) => s.object === 'site-section-group'))
|
||||
visibleSections &&
|
||||
(visibleSections.list.length > 1 || // Show section tabs if there are at least 2 sections or at least 1 section group
|
||||
visibleSections.list.some((s) => s.object === 'site-section-group'))
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -139,20 +139,22 @@ export function Header(props: {
|
||||
style={customization.styling.search}
|
||||
withVariants={variants.generic.length > 1}
|
||||
withSiteVariants={
|
||||
sections?.list.some(
|
||||
visibleSections?.list.some(
|
||||
(s) =>
|
||||
s.object === 'site-section' && s.siteSpaces.length > 1
|
||||
) ?? false
|
||||
}
|
||||
withSections={sections ? sections.list.length > 1 : false}
|
||||
withSections={
|
||||
visibleSections ? visibleSections.list.length > 1 : false
|
||||
}
|
||||
section={
|
||||
sections
|
||||
? // Client-encode to avoid a serialisation issue that was causing the language selector to disappear
|
||||
encodeClientSiteSections(context, sections).current
|
||||
visibleSections
|
||||
? // Client-encode to avoid a serialization issue that was causing the language selector to disappear
|
||||
encodeClientSiteSections(context, visibleSections).current
|
||||
: undefined
|
||||
}
|
||||
siteSpace={siteSpace}
|
||||
siteSpaces={siteSpaces}
|
||||
siteSpaces={visibleSiteSpaces}
|
||||
viewport={!withTopHeader ? 'mobile' : undefined}
|
||||
/>
|
||||
</div>
|
||||
@@ -196,9 +198,9 @@ export function Header(props: {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{sections && withSections ? (
|
||||
{visibleSections && withSections ? (
|
||||
<div className="transition-[padding] duration-300 lg:chat-open:pr-80 xl:chat-open:pr-96">
|
||||
<SiteSectionTabs sections={encodeClientSiteSections(context, sections)}>
|
||||
<SiteSectionTabs sections={encodeClientSiteSections(context, visibleSections)}>
|
||||
{variants.translations.length > 1 ? (
|
||||
<TranslationsDropdown
|
||||
context={context}
|
||||
|
||||
@@ -123,7 +123,7 @@ export async function generateSitePageViewport(context: GitBookSiteContext): Pro
|
||||
* A string concatenation of the site structure (sections and variants) titles.
|
||||
*/
|
||||
function getSiteStructureTitle(context: GitBookSiteContext): string | null {
|
||||
const { sections, siteSpace, siteSpaces } = context;
|
||||
const { visibleSections: sections, siteSpace, visibleSiteSpaces: siteSpaces } = context;
|
||||
|
||||
const title = [];
|
||||
if (
|
||||
@@ -259,7 +259,7 @@ export async function getSitePageData(props: SitePageProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const { customization, sections } = context;
|
||||
const { customization, visibleSections } = context;
|
||||
const { page, ancestors } = pageTarget;
|
||||
|
||||
const withTopHeader = customization.header.preset !== CustomizationHeaderPreset.None;
|
||||
@@ -270,7 +270,7 @@ export async function getSitePageData(props: SitePageProps) {
|
||||
);
|
||||
const withPageFeedback = customization.feedback.enabled;
|
||||
|
||||
const withSections = Boolean(sections && sections.list.length > 0);
|
||||
const withSections = Boolean(visibleSections && visibleSections.list.length > 0);
|
||||
|
||||
const document = await getPageDocument(context, page);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ function makeContext(current: FakeSiteSpace, all: FakeSiteSpace[]) {
|
||||
// Only the properties used by categorizeVariants are required for these tests
|
||||
siteSpace: current,
|
||||
siteSpaces: all,
|
||||
visibleSiteSpaces: all,
|
||||
} as unknown as Parameters<typeof categorizeVariants>[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -99,11 +99,11 @@ export function SpaceLayoutServerContext(props: SpaceLayoutProps) {
|
||||
*/
|
||||
export function SpaceLayout(props: SpaceLayoutProps) {
|
||||
const { context, children } = props;
|
||||
const { siteSpace, customization, sections, siteSpaces } = context;
|
||||
const { siteSpace, customization, visibleSections, visibleSiteSpaces } = context;
|
||||
|
||||
const withTopHeader = customization.header.preset !== CustomizationHeaderPreset.None;
|
||||
|
||||
const withSections = Boolean(sections && sections.list.length > 1);
|
||||
const withSections = Boolean(visibleSections && visibleSections.list.length > 1);
|
||||
const variants = categorizeVariants(context);
|
||||
|
||||
const withFooter =
|
||||
@@ -181,25 +181,28 @@ export function SpaceLayout(props: SpaceLayoutProps) {
|
||||
style={CustomizationSearchStyle.Subtle}
|
||||
withVariants={variants.generic.length > 1}
|
||||
withSiteVariants={
|
||||
sections?.list.some(
|
||||
visibleSections?.list.some(
|
||||
(s) =>
|
||||
s.object === 'site-section' &&
|
||||
s.siteSpaces.length > 1
|
||||
) ?? false
|
||||
}
|
||||
withSections={withSections}
|
||||
section={sections?.current}
|
||||
section={visibleSections?.current}
|
||||
siteSpace={siteSpace}
|
||||
siteSpaces={siteSpaces}
|
||||
siteSpaces={visibleSiteSpaces}
|
||||
className="max-lg:hidden"
|
||||
viewport="desktop"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!withTopHeader && withSections && sections && (
|
||||
{!withTopHeader && withSections && visibleSections && (
|
||||
<SiteSectionList
|
||||
className={tcls('hidden', 'lg:block')}
|
||||
sections={encodeClientSiteSections(context, sections)}
|
||||
sections={encodeClientSiteSections(
|
||||
context,
|
||||
visibleSections
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{variants.generic.length > 1 ? (
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { GitBookSiteContext } from '@/lib/context';
|
||||
* Categorize the variants of the space into generic and translation variants.
|
||||
*/
|
||||
export function categorizeVariants(context: GitBookSiteContext) {
|
||||
const { siteSpace, siteSpaces } = context;
|
||||
const { siteSpace, visibleSiteSpaces: siteSpaces } = context;
|
||||
const currentLanguage = siteSpace.space.language;
|
||||
|
||||
// Get all languages of the variants.
|
||||
|
||||
@@ -120,9 +120,15 @@ export type GitBookSiteContext = GitBookSpaceContext & {
|
||||
/** All site spaces in the current section / or entire site */
|
||||
siteSpaces: SiteSpace[];
|
||||
|
||||
/** Site spaces that are not hidden (visible to visitors). */
|
||||
visibleSiteSpaces: SiteSpace[];
|
||||
|
||||
/** Sections of the site. */
|
||||
sections: null | SiteSections;
|
||||
|
||||
/** Sections filtered to visible site spaces only. */
|
||||
visibleSections: null | SiteSections;
|
||||
|
||||
/** Customizations of the site. */
|
||||
customization: SiteCustomizationSettings;
|
||||
|
||||
@@ -261,9 +267,16 @@ export async function fetchSiteContextByIds(
|
||||
const sections = ids.siteSection
|
||||
? parseSiteSectionsAndGroups(siteStructure, ids.siteSection)
|
||||
: null;
|
||||
const visibleSections = ids.siteSection
|
||||
? parseVisibleSiteSectionsAndGroups(siteStructure, ids.siteSection)
|
||||
: null;
|
||||
|
||||
// Parse the current siteSpace and siteSpaces based on the site structure type.
|
||||
const { siteSpaces, siteSpace }: { siteSpaces: SiteSpace[]; siteSpace: SiteSpace } = (() => {
|
||||
const {
|
||||
siteSpaces,
|
||||
siteSpace,
|
||||
visibleSiteSpaces,
|
||||
}: { siteSpaces: SiteSpace[]; siteSpace: SiteSpace; visibleSiteSpaces: SiteSpace[] } = (() => {
|
||||
if (siteStructure.type === 'siteSpaces') {
|
||||
const siteSpaces = siteStructure.structure;
|
||||
const siteSpace = siteSpaces.find((siteSpace) => siteSpace.id === ids.siteSpace);
|
||||
@@ -274,7 +287,7 @@ export async function fetchSiteContextByIds(
|
||||
);
|
||||
}
|
||||
|
||||
return { siteSpaces: filterHiddenSiteSpaces(siteSpaces), siteSpace };
|
||||
return { siteSpaces, siteSpace, visibleSiteSpaces: filterHiddenSiteSpaces(siteSpaces) };
|
||||
}
|
||||
|
||||
if (siteStructure.type === 'sections') {
|
||||
@@ -295,7 +308,11 @@ export async function fetchSiteContextByIds(
|
||||
);
|
||||
}
|
||||
|
||||
return { siteSpaces: filterHiddenSiteSpaces(siteSpaces), siteSpace };
|
||||
return {
|
||||
siteSpaces,
|
||||
siteSpace,
|
||||
visibleSiteSpaces: filterHiddenSiteSpaces(siteSpaces),
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
@@ -327,10 +344,12 @@ export async function fetchSiteContextByIds(
|
||||
organizationId: ids.organization,
|
||||
site,
|
||||
siteSpaces,
|
||||
visibleSiteSpaces,
|
||||
siteSpace,
|
||||
customization,
|
||||
structure: siteStructure,
|
||||
sections,
|
||||
visibleSections,
|
||||
scripts,
|
||||
contextId: ids.contextId,
|
||||
isFallback: ids.isFallback,
|
||||
@@ -434,13 +453,58 @@ function filterHiddenSiteSpaces(siteSpaces: SiteSpace[]): SiteSpace[] {
|
||||
}
|
||||
|
||||
function parseSiteSectionsAndGroups(structure: SiteStructure, siteSectionId: string) {
|
||||
const sectionsAndGroups = getSiteStructureSections(structure, { ignoreGroups: false });
|
||||
const sectionsAndGroups = getSiteStructureSections(structure);
|
||||
const section = parseCurrentSection(structure, siteSectionId);
|
||||
assert(section, `couldn't find section "${siteSectionId}" in site structure`);
|
||||
return { list: sectionsAndGroups, current: section } satisfies SiteSections;
|
||||
}
|
||||
|
||||
function parseVisibleSiteSectionsAndGroups(structure: SiteStructure, siteSectionId: string) {
|
||||
const { list: sectionsAndGroups, current: section } = parseSiteSectionsAndGroups(
|
||||
structure,
|
||||
siteSectionId
|
||||
);
|
||||
const visibleSectionsAndGroups = filterSectionsAndGroupsWithHiddenSiteSpaces(sectionsAndGroups);
|
||||
const current = section && !sectionHasOnlyHiddenSiteSpaces(section) ? section : null;
|
||||
assert(current, `couldn't find section "${siteSectionId}" in site structure`);
|
||||
return { list: visibleSectionsAndGroups, current } satisfies SiteSections;
|
||||
}
|
||||
|
||||
function parseCurrentSection(structure: SiteStructure, siteSectionId: string) {
|
||||
const sections = getSiteStructureSections(structure, { ignoreGroups: true });
|
||||
return sections.find((section) => section.id === siteSectionId);
|
||||
}
|
||||
|
||||
type SectionOrGroup = SiteSection | SiteSectionGroup;
|
||||
|
||||
/**
|
||||
* Filter out sections where all site spaces are hidden and groups that become empty after filtering.
|
||||
*/
|
||||
function filterSectionsAndGroupsWithHiddenSiteSpaces(
|
||||
sectionsOrGroups: SectionOrGroup[]
|
||||
): SectionOrGroup[] {
|
||||
return sectionsOrGroups
|
||||
.map((entry) => {
|
||||
if (entry.object === 'site-section') {
|
||||
return sectionHasOnlyHiddenSiteSpaces(entry) ? null : entry;
|
||||
}
|
||||
|
||||
const visibleChildren: SectionOrGroup[] = filterSectionsAndGroupsWithHiddenSiteSpaces(
|
||||
entry.children
|
||||
);
|
||||
|
||||
if (visibleChildren.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...entry,
|
||||
children: visibleChildren,
|
||||
};
|
||||
})
|
||||
.filter((entry): entry is SiteSection | SiteSectionGroup => Boolean(entry));
|
||||
}
|
||||
|
||||
function sectionHasOnlyHiddenSiteSpaces(section: SiteSection) {
|
||||
return section.siteSpaces.every((siteSpace) => siteSpace.hidden);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user