From 7654c3c137c5c70064f3773b6a87f5f962891fd4 Mon Sep 17 00:00:00 2001 From: hyperoot Date: Sat, 10 Aug 2024 12:33:45 +0000 Subject: [PATCH] Fixes #1: Added support for custom order of nested items. --- config.ts | 3 + .../docs/Guides/sidebar-navigation.mdx | 23 +++-- src/lib/utils.ts | 83 ++++++++++++------- 3 files changed, 73 insertions(+), 36 deletions(-) diff --git a/config.ts b/config.ts index 823fac9..82636a3 100644 --- a/config.ts +++ b/config.ts @@ -25,6 +25,9 @@ export const menu_items: { title: string; href: string }[] = [ export const side_nav_menu_order: string[] = [ "getting-started", "guides", + "guides/pages", + "guides/table-of-contents", + "guides/sidebar-navigation", "custom-components", "reference", ]; diff --git a/src/content/docs/Guides/sidebar-navigation.mdx b/src/content/docs/Guides/sidebar-navigation.mdx index c3796c3..e7aac70 100644 --- a/src/content/docs/Guides/sidebar-navigation.mdx +++ b/src/content/docs/Guides/sidebar-navigation.mdx @@ -5,19 +5,32 @@ By default, CelestialDocs sites include sidebar navigation. It is generated afte ## Custom order -By default, it will order the top-level items (folders and files that are immediately inside the `docs` folder) alphabetically. But we can provide a custom order to the top level items. +By default, it will order the side navigation items (folders and files) alphabetically. But we can provide a custom order to the side navigation items. In `config.js` you can set `side_nav_menu_order` with an order of items. Mind that the type is an array of strings. Each string is just the `slug` of that page. ```js // config.js -export const side_nav_menu_order: string[] = ["getting-started", "guides"]; -// getting-started.mdx (file) -// guides (folder) +export const side_nav_menu_order: string[] = [ + "getting-started", + "guides", + "guides/pages", + "guides/table-of-contents", + "guides/sidebar-navigation", + "custom-components", + "reference", +]; ``` -You can include `folder-name`, `md` & `mdx` files. If you don't order every items, then the left out folders and files will the ordered after that. Don't add extensions for `md` & `mdx` files. +You can include `folder-name`, `md` & `mdx` files. If you don't order every items, then the left out folders and files will the ordered after that alphabetically. Don't add extensions for `md` & `mdx` files. + +You can find the slug of any page, if you simply run an instance of your project and look for the path after the port number in the url. For instance, the url in my local machine for this exact page is +``` +http://localhost:4321/guides/sidebar-navigation +``` + +The slug will be `guides/sidebar-navigation`. ## Hide Sidebar Navigation diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 416e0d8..e3ff067 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -26,23 +26,68 @@ export const capitalizeFirstLetter = (str: string) => { return str.charAt(0).toUpperCase() + str.slice(1); }; +// Helper function to sort items according to side_nav_menu_order +function sortItems( + items: MenuItemWithDraft[], + orderMap: Map, +): MenuItemWithDraft[] { + return items.slice().sort((a, b) => { + const aIndex = orderMap.get(a.slug) ?? Infinity; + const bIndex = orderMap.get(b.slug) ?? Infinity; + return aIndex - bIndex; + }); +} + // Function to build nested menu structure function buildMenu(items: DocsEntry[]): MenuItem[] { const menu: MenuItemWithDraft[] = []; - // Create a map to quickly look up the order of top-level items + // Create a map to quickly look up the order of all items const orderMap = new Map( side_nav_menu_order.map((item, index) => [item, index]), ); // Helper function to sort top-level items function sortTopLevel(items: MenuItemWithDraft[]): MenuItemWithDraft[] { - const sortedItems = items.slice().sort((a, b) => { - const aIndex = orderMap.get(a.slug) ?? Infinity; - const bIndex = orderMap.get(b.slug) ?? Infinity; - return aIndex - bIndex; + const topLevelItems = items.filter((item) => !item.slug.includes("/")); + const nestedItems = items.filter((item) => item.slug.includes("/")); + + // Sort top-level items + const sortedTopLevelItems = sortItems(topLevelItems, orderMap); + + // Sort nested items by their respective parent folders + const nestedMenu: MenuItemWithDraft[] = []; + nestedItems.forEach((item) => { + const parts = item.slug.split("/"); + let currentLevel = nestedMenu; + + // Traverse and insert items into the correct position + parts.forEach((part: string, index: number) => { + let existingItem = currentLevel.find( + (i) => i.slug === parts.slice(0, index + 1).join("/"), + ); + + if (!existingItem) { + existingItem = { + title: capitalizeFirstLetter(part), + slug: parts.slice(0, index + 1).join("/"), + draft: item.draft, + children: [], + }; + currentLevel.push(existingItem); + } + currentLevel = existingItem.children; + }); + }); + + // For each top-level item, attach sorted nested items + sortedTopLevelItems.forEach((item) => { + if (item.children) { + item.children = sortItems(item.children, orderMap); + } }); - return sortedItems; + + return sortedTopLevelItems; } items.forEach((item) => { @@ -57,7 +102,6 @@ function buildMenu(items: DocsEntry[]): MenuItem[] { if (!existingItem) { existingItem = { - // Use title from item.data if it's the last part title: index === parts.length - 1 ? capitalizeFirstLetter(item.data.title || "") @@ -78,36 +122,13 @@ function buildMenu(items: DocsEntry[]): MenuItem[] { }); }); - // Sort top-level items based on menu_order + // Sort top-level items based on menu_order and attach nested items const topLevelMenu = sortTopLevel(menu); return topLevelMenu; } -// Function to flatten nested menu structure into a linear array -function flattenMenu( - menu: { title?: string; slug: string; children: any[] }[], -) { - const flatMenu: { title?: string; slug: string }[] = []; - - const traverse = ( - items: { title?: string; slug: string; children: any[] }[], - ) => { - items.forEach((item) => { - flatMenu.push({ title: item.title, slug: item.slug }); - if (item.children.length > 0) { - traverse(item.children); - } - }); - }; - - traverse(menu); - - return flatMenu; -} - export const menu = buildMenu(docs); -export const flatMenu = flattenMenu(menu); // Function to build breadcrumb structure export function buildBreadcrumbs(