summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiDeoo2023-06-30 18:03:21 +0200
committerGitHub2023-06-30 18:03:21 +0200
commit810238934ae1a95c53042ca2875bb4033aad0114 (patch)
tree0a616aaacf42ae5aeab14028cb854b2bc9b1514e
parent048e948bce650d559517850c73d827733b8164c4 (diff)
downloadIT.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.md5
-rw-r--r--docs/src/content/docs/reference/configuration.md37
-rw-r--r--packages/starlight/components/SidebarSublist.astro6
-rw-r--r--packages/starlight/utils/navigation.ts27
-rw-r--r--packages/starlight/utils/user-config.ts24
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,