diff options
author | HiDeoo | 2023-06-30 18:03:21 +0200 |
---|---|---|
committer | GitHub | 2023-06-30 18:03:21 +0200 |
commit | 810238934ae1a95c53042ca2875bb4033aad0114 (patch) | |
tree | 0a616aaacf42ae5aeab14028cb854b2bc9b1514e | |
parent | 048e948bce650d559517850c73d827733b8164c4 (diff) | |
download | IT.starlight-810238934ae1a95c53042ca2875bb4033aad0114.tar.gz IT.starlight-810238934ae1a95c53042ca2875bb4033aad0114.tar.bz2 IT.starlight-810238934ae1a95c53042ca2875bb4033aad0114.zip |
Add support for collapsed sidebar groups (#259)
Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
-rw-r--r-- | .changeset/clean-monkeys-camp.md | 5 | ||||
-rw-r--r-- | docs/src/content/docs/reference/configuration.md | 37 | ||||
-rw-r--r-- | packages/starlight/components/SidebarSublist.astro | 6 | ||||
-rw-r--r-- | packages/starlight/utils/navigation.ts | 27 | ||||
-rw-r--r-- | packages/starlight/utils/user-config.ts | 24 |
5 files changed, 77 insertions, 22 deletions
diff --git a/.changeset/clean-monkeys-camp.md b/.changeset/clean-monkeys-camp.md new file mode 100644 index 00000000..790908f5 --- /dev/null +++ b/.changeset/clean-monkeys-camp.md @@ -0,0 +1,5 @@ +--- +"@astrojs/starlight": minor +--- + +Add support for collapsed sidebar groups diff --git a/docs/src/content/docs/reference/configuration.md b/docs/src/content/docs/reference/configuration.md index b61aa560..cd7132c2 100644 --- a/docs/src/content/docs/reference/configuration.md +++ b/docs/src/content/docs/reference/configuration.md @@ -123,6 +123,34 @@ starlight({ Autogenerated sidebar groups are sorted by filename alphabetically. For example, a page generated from `astro.md` would appear above the page for `starlight.md`. +#### Collapsing groups + +Groups of links are expanded by default. You can change this behavior by setting a group’s `collapsed` property to `true`. + +Autogenerated subgroups respect the `collapsed` property of their parent group by default. Set the `autogenerate.collapsed` property to override this. + +```js +sidebar: [ + // A collapsed group of links. + { + label: 'Collapsed Links', + collapsed: true, + items: [ + { label: 'Introduction', link: '/intro' }, + { label: 'Next Steps', link: '/next-steps' }, + ], + }, + // An expanded group containing collapsed autogenerated subgroups. + { + label: 'Reference', + autogenerate: { + directory: 'reference', + collapsed: true, + }, + }, +], +``` + #### Translating labels If your site is multilingual, each item’s `label` is considered to be in the default locale. You can set a `translations` property to provide labels for your other supported languages: @@ -146,7 +174,7 @@ sidebar: [ }, ], }, -]; +], ``` #### `SidebarItem` @@ -157,8 +185,11 @@ type SidebarItem = { translations?: Record<string, string>; } & ( | { link: string } - | { items: SidebarItem[] } - | { autogenerate: { directory: string } } + | { items: SidebarItem[]; collapsed?: boolean } + | { + autogenerate: { directory: string; collapsed?: boolean }; + collapsed?: boolean; + } ); ``` diff --git a/packages/starlight/components/SidebarSublist.astro b/packages/starlight/components/SidebarSublist.astro index c2d6aa54..c07bdf20 100644 --- a/packages/starlight/components/SidebarSublist.astro +++ b/packages/starlight/components/SidebarSublist.astro @@ -1,5 +1,5 @@ --- -import type { SidebarEntry } from '../utils/navigation'; +import { flattenSidebar, type SidebarEntry } from '../utils/navigation'; import Icon from '../user-components/Icon.astro'; interface Props { @@ -21,7 +21,9 @@ interface Props { {entry.label} </a> ) : ( - <details open> + <details + open={flattenSidebar(entry.entries).some(i => i.isCurrent) || !entry.collapsed} + > <summary> <h2 class="large">{entry.label}</h2> <Icon name="right-caret" class="caret" size="1.25rem" /> diff --git a/packages/starlight/utils/navigation.ts b/packages/starlight/utils/navigation.ts index 2cf95466..3bf72173 100644 --- a/packages/starlight/utils/navigation.ts +++ b/packages/starlight/utils/navigation.ts @@ -21,6 +21,7 @@ interface Group { type: 'group'; label: string; entries: (Link | Group)[]; + collapsed: boolean; } export type SidebarEntry = Link | Group; @@ -53,6 +54,7 @@ function configItemToEntry( entries: item.items.map((i) => configItemToEntry(i, currentPathname, locale, routes) ), + collapsed: item.collapsed, }; } } @@ -64,7 +66,7 @@ function groupFromAutogenerateConfig( routes: Route[], currentPathname: string ): Group { - const { directory } = item.autogenerate; + const { collapsed: subgroupCollapsed, directory } = item.autogenerate; const localeDir = locale ? locale + '/' + directory : directory; const dirDocs = routes.filter( (doc) => @@ -77,7 +79,8 @@ function groupFromAutogenerateConfig( return { type: 'group', label: pickLang(item.translations, localeToLang(locale)) || item.label, - entries: sidebarFromDir(tree, currentPathname, locale), + entries: sidebarFromDir(tree, currentPathname, locale, subgroupCollapsed ?? item.collapsed), + collapsed: item.collapsed, }; } @@ -168,15 +171,17 @@ function groupFromDir( fullPath: string, dirName: string, currentPathname: string, - locale: string | undefined + locale: string | undefined, + collapsed: boolean ): Group { const entries = Object.entries(dir).map(([key, dirOrSlug]) => - dirToItem(dirOrSlug, `${fullPath}/${key}`, key, currentPathname, locale) + dirToItem(dirOrSlug, `${fullPath}/${key}`, key, currentPathname, locale, collapsed) ); return { type: 'group', label: dirName, entries, + collapsed, }; } @@ -186,21 +191,23 @@ function dirToItem( fullPath: string, dirName: string, currentPathname: string, - locale: string | undefined + locale: string | undefined, + collapsed: boolean ): SidebarEntry { return typeof dirOrSlug === 'string' ? linkFromSlug(dirOrSlug, currentPathname) - : groupFromDir(dirOrSlug, fullPath, dirName, currentPathname, locale); + : groupFromDir(dirOrSlug, fullPath, dirName, currentPathname, locale, collapsed); } /** Create a sidebar entry for a given content directory. */ function sidebarFromDir( tree: Dir, currentPathname: string, - locale: string | undefined + locale: string | undefined, + collapsed: boolean ) { return Object.entries(tree).map(([key, dirOrSlug]) => - dirToItem(dirOrSlug, key, key, currentPathname, locale) + dirToItem(dirOrSlug, key, key, currentPathname, locale, collapsed) ); } @@ -216,12 +223,12 @@ export function getSidebar( ); } else { const tree = treeify(routes, locale || ''); - return sidebarFromDir(tree, pathname, locale); + return sidebarFromDir(tree, pathname, locale, false); } } /** Turn the nested tree structure of a sidebar into a flat list of all the links. */ -function flattenSidebar(sidebar: SidebarEntry[]): Link[] { +export function flattenSidebar(sidebar: SidebarEntry[]): Link[] { return sidebar.flatMap((entry) => entry.type === 'group' ? flattenSidebar(entry.entries) : entry ); diff --git a/packages/starlight/utils/user-config.ts b/packages/starlight/utils/user-config.ts index bd7ca026..4d692d85 100644 --- a/packages/starlight/utils/user-config.ts +++ b/packages/starlight/utils/user-config.ts @@ -35,25 +35,35 @@ const SidebarBaseSchema = z.object({ translations: z.record(z.string()).default({}), }); +const SidebarGroupSchema = SidebarBaseSchema.extend({ + /** Whether this item should be collapsed by default. */ + collapsed: z.boolean().default(false), +}); + const SidebarLinkItemSchema = SidebarBaseSchema.extend({ /** The link to this item’s content. Can be a relative link to local files or the full URL of an external page. */ link: z.string(), }); export type SidebarLinkItem = z.infer<typeof SidebarLinkItemSchema>; -const AutoSidebarGroupSchema = SidebarBaseSchema.extend({ +const AutoSidebarGroupSchema = SidebarGroupSchema.extend({ /** Enable autogenerating a sidebar category from a specific docs directory. */ autogenerate: z.object({ /** The directory to generate sidebar items for. */ directory: z.string(), + /** + * Whether the autogenerated subgroups should be collapsed by default. + * Defaults to the `AutoSidebarGroup` `collapsed` value. + */ + collapsed: z.boolean().optional(), // TODO: not supported by Docusaurus but would be good to have /** How many directories deep to include from this directory in the sidebar. Default: `Infinity`. */ // depth: z.number().optional(), - }), -}); + }) +}) export type AutoSidebarGroup = z.infer<typeof AutoSidebarGroupSchema>; -type ManualSidebarGroupInput = z.input<typeof SidebarBaseSchema> & { +type ManualSidebarGroupInput = z.input<typeof SidebarGroupSchema> & { /** Array of links and subcategories to display in this category. */ items: Array< | z.input<typeof SidebarLinkItemSchema> @@ -62,7 +72,7 @@ type ManualSidebarGroupInput = z.input<typeof SidebarBaseSchema> & { >; }; -type ManualSidebarGroupOutput = z.output<typeof SidebarBaseSchema> & { +type ManualSidebarGroupOutput = z.output<typeof SidebarGroupSchema> & { /** Array of links and subcategories to display in this category. */ items: Array< | z.output<typeof SidebarLinkItemSchema> @@ -75,7 +85,7 @@ const ManualSidebarGroupSchema: z.ZodType< ManualSidebarGroupOutput, z.ZodTypeDef, ManualSidebarGroupInput -> = SidebarBaseSchema.extend({ +> = SidebarGroupSchema.extend({ /** Array of links and subcategories to display in this category. */ items: z.lazy(() => z @@ -86,7 +96,7 @@ const ManualSidebarGroupSchema: z.ZodType< ]) .array() ), -}); +}) const SidebarItemSchema = z.union([ SidebarLinkItemSchema, |