Skip to content

Commit

Permalink
Fixes #1: Added support for custom order of nested items.
Browse files Browse the repository at this point in the history
  • Loading branch information
HYP3R00T committed Aug 10, 2024
1 parent 40778b4 commit 7654c3c
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 36 deletions.
3 changes: 3 additions & 0 deletions config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
];
Expand Down
23 changes: 18 additions & 5 deletions src/content/docs/Guides/sidebar-navigation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
83 changes: 52 additions & 31 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, number>,
): 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) => {
Expand All @@ -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 || "")
Expand All @@ -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(
Expand Down

0 comments on commit 7654c3c

Please sign in to comment.