[#809,docs,navigation][xl]: initial commit
This commit is contained in:
@@ -5,6 +5,7 @@ import Link from 'next/link';
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import Nav from './Nav';
|
import Nav from './Nav';
|
||||||
|
import { SiteToc } from '@/components/SiteToc';
|
||||||
|
|
||||||
function useTableOfContents(tableOfContents) {
|
function useTableOfContents(tableOfContents) {
|
||||||
const [currentSection, setCurrentSection] = useState(tableOfContents[0]?.id);
|
const [currentSection, setCurrentSection] = useState(tableOfContents[0]?.id);
|
||||||
@@ -53,10 +54,14 @@ export default function Layout({
|
|||||||
children,
|
children,
|
||||||
title,
|
title,
|
||||||
tableOfContents = [],
|
tableOfContents = [],
|
||||||
|
urlPath,
|
||||||
|
siteMap = []
|
||||||
}: {
|
}: {
|
||||||
children;
|
children;
|
||||||
title?: string;
|
title?: string;
|
||||||
tableOfContents?;
|
tableOfContents?;
|
||||||
|
urlPath?: string;
|
||||||
|
siteMap?: [];
|
||||||
}) {
|
}) {
|
||||||
// const { toc } = children.props;
|
// const { toc } = children.props;
|
||||||
const { theme, setTheme } = useTheme();
|
const { theme, setTheme } = useTheme();
|
||||||
@@ -145,6 +150,12 @@ export default function Layout({
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{/* LHS NAVIGATION */}
|
||||||
|
{/* {showSidebar && ( */}
|
||||||
|
<div className="hidden lg:block fixed z-20 w-[18rem] top-[4.6rem] right-auto bottom-0 left-[max(0px,calc(50%-44rem))] pt-8 pl-8 overflow-y-auto">
|
||||||
|
<SiteToc currentPath={urlPath} nav={siteMap} />
|
||||||
|
</div>
|
||||||
|
{/* )} */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
110
site/components/SiteToc.tsx
Normal file
110
site/components/SiteToc.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import Link from "next/link.js";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { Disclosure, Transition } from "@headlessui/react";
|
||||||
|
|
||||||
|
export interface NavItem {
|
||||||
|
name: string;
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NavGroup {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
level: number;
|
||||||
|
children: Array<NavItem | NavGroup>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
currentPath: string;
|
||||||
|
nav: Array<NavItem | NavGroup>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNavGroup(item: NavItem | NavGroup): item is NavGroup {
|
||||||
|
return (item as NavGroup).children !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function navItemBeforeNavGroup(a, b) {
|
||||||
|
if (isNavGroup(a) === isNavGroup(b)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (isNavGroup(a) && !isNavGroup(b)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortNavGroupChildren(items: Array<NavItem | NavGroup>) {
|
||||||
|
return items.sort(
|
||||||
|
(a, b) => navItemBeforeNavGroup(a, b) || a.name.localeCompare(b.name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SiteToc: React.FC<Props> = ({ currentPath, nav }) => {
|
||||||
|
function isActiveItem(item: NavItem) {
|
||||||
|
return item.href === currentPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav data-testid="lhs-sidebar" className="flex flex-col space-y-3 text-sm">
|
||||||
|
{/* {sortNavGroupChildren(nav).map((n) => ( */}
|
||||||
|
{nav.map((n) => (
|
||||||
|
<NavComponent item={n} isActive={false} />
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const NavComponent: React.FC<{
|
||||||
|
item: NavItem | NavGroup;
|
||||||
|
isActive: boolean;
|
||||||
|
}> = ({ item, isActive }) => {
|
||||||
|
return !isNavGroup(item) ? (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
href={item.href}
|
||||||
|
className={clsx(
|
||||||
|
isActive
|
||||||
|
? "text-sky-500"
|
||||||
|
: "font-normal text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-300",
|
||||||
|
"block"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Disclosure as="div" key={item.name} className="flex flex-col space-y-3">
|
||||||
|
{({ open }) => (
|
||||||
|
<div>
|
||||||
|
<Disclosure.Button className="group w-full flex items-center text-left text-md font-medium text-slate-900 dark:text-white">
|
||||||
|
<svg
|
||||||
|
className={clsx(
|
||||||
|
open ? "text-slate-400 rotate-90" : "text-slate-300",
|
||||||
|
"h-3 w-3 mr-2 flex-shrink-0 transform transition-colors duration-150 ease-in-out group-hover:text-slate-400"
|
||||||
|
)}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M6 6L14 10L6 14V6Z" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
{item.name}
|
||||||
|
</Disclosure.Button>
|
||||||
|
<Transition
|
||||||
|
enter="transition duration-100 ease-out"
|
||||||
|
enterFrom="transform scale-95 opacity-0"
|
||||||
|
enterTo="transform scale-100 opacity-100"
|
||||||
|
leave="transition duration-75 ease-out"
|
||||||
|
leaveFrom="transform scale-100 opacity-100"
|
||||||
|
leaveTo="transform scale-95 opacity-0"
|
||||||
|
>
|
||||||
|
<Disclosure.Panel className="flex flex-col space-y-3 pl-5 mt-3">
|
||||||
|
{/* {sortNavGroupChildren(item.children).map((subItem) => ( */}
|
||||||
|
{item.children.map((subItem) => (
|
||||||
|
<NavComponent item={subItem} isActive={false} />
|
||||||
|
))}
|
||||||
|
</Disclosure.Panel>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Disclosure>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
showSidebar: true
|
||||||
|
---
|
||||||
|
|
||||||
# Getting Started
|
# Getting Started
|
||||||
|
|
||||||
Welcome to the PortalJS documentation!
|
Welcome to the PortalJS documentation!
|
||||||
|
|||||||
19
site/content/docs/sidebarTree.json
Normal file
19
site/content/docs/sidebarTree.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Docs",
|
||||||
|
"href": "/docs",
|
||||||
|
"level": 0,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"name": "Getting Started",
|
||||||
|
"href": "/docs",
|
||||||
|
"level": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Creating new datasets",
|
||||||
|
"href": "/tutorial-i-creating-new-datasets",
|
||||||
|
"level": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
121
site/lib/computeFields.ts
Normal file
121
site/lib/computeFields.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// This file is a temporary replacement for legacy contentlayer's computeFields + default fields values
|
||||||
|
import { remark } from "remark";
|
||||||
|
import stripMarkdown, { Options } from "strip-markdown";
|
||||||
|
|
||||||
|
import { siteConfig } from "../config/siteConfig";
|
||||||
|
import { getAuthorsDetails } from "./getAuthorsDetails";
|
||||||
|
import sluggify from "./sluggify";
|
||||||
|
|
||||||
|
// TODO return type
|
||||||
|
|
||||||
|
const computeFields = async ({
|
||||||
|
frontMatter,
|
||||||
|
urlPath,
|
||||||
|
filePath,
|
||||||
|
source,
|
||||||
|
}: {
|
||||||
|
frontMatter: Record<string, any>;
|
||||||
|
urlPath: string;
|
||||||
|
filePath: string;
|
||||||
|
source: string;
|
||||||
|
}) => {
|
||||||
|
// Fields with corresponding config options
|
||||||
|
// TODO see _app.tsx
|
||||||
|
const showComments =
|
||||||
|
frontMatter.showComments ?? siteConfig.showComments ?? false;
|
||||||
|
const showEditLink =
|
||||||
|
frontMatter.showEditLink ?? siteConfig.showEditLink ?? false;
|
||||||
|
// TODO take config into accout
|
||||||
|
const showLinkPreviews =
|
||||||
|
frontMatter.showLinkPreviews ?? siteConfig.showLinkPreviews ?? false;
|
||||||
|
const showToc = frontMatter.showToc ?? siteConfig.showToc ?? false;
|
||||||
|
const showSidebar =
|
||||||
|
frontMatter.showSidebar ?? siteConfig.showSidebar ?? false;
|
||||||
|
const sidebarTreeFile = frontMatter.sidebarTreeFile ?? null;
|
||||||
|
|
||||||
|
// Computed fields
|
||||||
|
// const title = frontMatter.title ?? (await extractTitle(source));
|
||||||
|
const title = frontMatter.title ?? null;
|
||||||
|
const description =
|
||||||
|
frontMatter.description ?? (await extractDescription(source));
|
||||||
|
const date = frontMatter.date ?? frontMatter.created ?? null;
|
||||||
|
const layout = (() => {
|
||||||
|
if (frontMatter.layout) return frontMatter.layout;
|
||||||
|
if (urlPath.startsWith("blog/")) return "blog";
|
||||||
|
// if (urlPath.startsWith("docs/")) return "docs";
|
||||||
|
return "docs"; // TODO default layout from config?
|
||||||
|
})();
|
||||||
|
|
||||||
|
// TODO Temporary, should probably be a column in the database
|
||||||
|
const slug = sluggify(urlPath);
|
||||||
|
// TODO take into accout include/exclude fields in config
|
||||||
|
const isDraft = frontMatter.isDraft ?? false;
|
||||||
|
const editUrl =
|
||||||
|
(siteConfig.editLinkRoot && `${siteConfig.editLinkRoot}/${filePath}`) ||
|
||||||
|
null;
|
||||||
|
const authors = await getAuthorsDetails(frontMatter.authors);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...frontMatter,
|
||||||
|
authors,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
date,
|
||||||
|
layout,
|
||||||
|
slug,
|
||||||
|
urlPath, // extra for blogs index page; temporary here
|
||||||
|
isDraft,
|
||||||
|
editUrl,
|
||||||
|
showComments,
|
||||||
|
showEditLink,
|
||||||
|
showLinkPreviews,
|
||||||
|
showToc,
|
||||||
|
showSidebar,
|
||||||
|
sidebarTreeFile
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractTitle = async (source: string) => {
|
||||||
|
const heading = source.trim().match(/^#\s+(.*)/);
|
||||||
|
if (heading) {
|
||||||
|
const title = heading[1]
|
||||||
|
// replace wikilink with only text value
|
||||||
|
.replace(/\[\[([\S]*?)]]/, "$1");
|
||||||
|
|
||||||
|
const stripTitle = await remark().use(stripMarkdown).process(title);
|
||||||
|
return stripTitle.toString().trim();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractDescription = async (source: string) => {
|
||||||
|
const content = source
|
||||||
|
// remove commented lines
|
||||||
|
.replace(/{\/\*.*\*\/}/g, "")
|
||||||
|
// remove import statements
|
||||||
|
.replace(
|
||||||
|
/^import\s*(?:\{\s*[\w\s,\n]+\s*\})?(\s*(\w+))?\s*from\s*("|')[^"]+("|');?$/gm,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
// remove youtube links
|
||||||
|
.replace(/^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/gm, "")
|
||||||
|
// replace wikilinks with only text
|
||||||
|
.replace(/([^!])\[\[(\S*?)\]]/g, "$1$2")
|
||||||
|
// remove wikilink images
|
||||||
|
.replace(/!\[[\S]*?]]/g, "");
|
||||||
|
|
||||||
|
// remove markdown formatting
|
||||||
|
const stripped = await remark()
|
||||||
|
.use(stripMarkdown, {
|
||||||
|
remove: ["heading", "blockquote", "list", "image", "html", "code"],
|
||||||
|
} as Options)
|
||||||
|
.process(content);
|
||||||
|
|
||||||
|
if (stripped.value) {
|
||||||
|
const description: string = stripped.value.toString().slice(0, 200);
|
||||||
|
return description + "...";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default computeFields;
|
||||||
5
site/lib/sluggify.ts
Normal file
5
site/lib/sluggify.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const sluggify = (urlPath: string) => {
|
||||||
|
return urlPath.replace(/^(.+?\/)*/, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
export default sluggify;
|
||||||
3152
site/package-lock.json
generated
3152
site/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,8 @@
|
|||||||
"mddb": "mddb content"
|
"mddb": "mddb content"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@flowershow/core": "^0.4.9",
|
"@flowershow/core": "^0.4.11",
|
||||||
"@flowershow/markdowndb": "^0.1.0",
|
"@flowershow/markdowndb": "^0.1.1",
|
||||||
"@flowershow/remark-callouts": "^1.0.0",
|
"@flowershow/remark-callouts": "^1.0.0",
|
||||||
"@flowershow/remark-embed": "^1.0.0",
|
"@flowershow/remark-embed": "^1.0.0",
|
||||||
"@flowershow/remark-wiki-link": "^1.0.1",
|
"@flowershow/remark-wiki-link": "^1.0.1",
|
||||||
@@ -46,7 +46,8 @@
|
|||||||
"remark-smartypants": "^2.0.0",
|
"remark-smartypants": "^2.0.0",
|
||||||
"remark-toc": "^7.2.0",
|
"remark-toc": "^7.2.0",
|
||||||
"vega": "^5.20.2",
|
"vega": "^5.20.2",
|
||||||
"vega-lite": "^5.1.0"
|
"vega-lite": "^5.1.0",
|
||||||
|
"strip-markdown": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
@@ -54,6 +55,7 @@
|
|||||||
"postcss": "^8.4.22",
|
"postcss": "^8.4.22",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^2.8.7",
|
||||||
"tailwindcss": "^3.3.1",
|
"tailwindcss": "^3.3.1",
|
||||||
"typescript": "^5.0.4"
|
"typescript": "^5.0.4",
|
||||||
|
"remark": "^14.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ import parse from '../lib/markdown.mjs';
|
|||||||
|
|
||||||
import MDXPage from '../components/MDXPage';
|
import MDXPage from '../components/MDXPage';
|
||||||
import clientPromise from '@/lib/mddb';
|
import clientPromise from '@/lib/mddb';
|
||||||
import { getAuthorsDetails } from 'lib/getAuthorsDetails';
|
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/Layout';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRouter } from 'next/router.js';
|
import { useRouter } from 'next/router.js';
|
||||||
import { collectHeadings } from '@flowershow/core';
|
import { NavGroup, NavItem, collectHeadings } from '@flowershow/core';
|
||||||
|
import { GetStaticProps, GetStaticPropsResult } from 'next';
|
||||||
|
import { CustomAppProps } from './_app.jsx';
|
||||||
|
import computeFields from '@/lib/computeFields';
|
||||||
|
import { getAuthorsDetails } from '@/lib/getAuthorsDetails';
|
||||||
|
|
||||||
export default function DRDPage({ source, frontMatter }) {
|
export default function DRDPage({ source, meta, siteMap }) {
|
||||||
source = JSON.parse(source);
|
source = JSON.parse(source);
|
||||||
frontMatter = JSON.parse(frontMatter);
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@@ -27,53 +29,73 @@ export default function DRDPage({ source, frontMatter }) {
|
|||||||
}, [router.asPath]); // update table of contents on route change with next/link
|
}, [router.asPath]); // update table of contents on route change with next/link
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout tableOfContents={tableOfContents} title={frontMatter.title}>
|
<Layout
|
||||||
<MDXPage source={source} frontMatter={frontMatter} />
|
tableOfContents={tableOfContents}
|
||||||
|
title={meta.title}
|
||||||
|
siteMap={siteMap}
|
||||||
|
>
|
||||||
|
<MDXPage source={source} frontMatter={meta} />
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getStaticProps = async ({ params }) => {
|
interface SlugPageProps extends CustomAppProps {
|
||||||
const urlPath = params.slug ? params.slug.join('/') : '';
|
source: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getStaticProps: GetStaticProps = async ({
|
||||||
|
params,
|
||||||
|
}): Promise<GetStaticPropsResult<SlugPageProps>> => {
|
||||||
|
const urlPath = params?.slug ? (params.slug as string[]).join('/') : '/';
|
||||||
|
|
||||||
const mddb = await clientPromise;
|
const mddb = await clientPromise;
|
||||||
const dbFile = await mddb.getFileByUrl(urlPath);
|
const dbFile = await mddb.getFileByUrl(urlPath);
|
||||||
|
const filePath = dbFile!.file_path;
|
||||||
const dbBacklinks = await mddb.getLinks({
|
const frontMatter = dbFile!.metadata ?? {};
|
||||||
fileId: dbFile._id,
|
|
||||||
direction: 'backward',
|
|
||||||
});
|
|
||||||
// TODO temporary solution, we will have a method on MddbFile to get these links
|
|
||||||
const dbBacklinkFilesPromises = dbBacklinks.map((link) =>
|
|
||||||
mddb.getFileById(link.from)
|
|
||||||
);
|
|
||||||
const dbBacklinkFiles = await Promise.all(dbBacklinkFilesPromises);
|
|
||||||
const dbBacklinkUrls = dbBacklinkFiles.map(
|
|
||||||
(file) => file.toObject().url_path
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO we can already get frontmatter from dbFile.metadata
|
|
||||||
// so parse could only return mdxSource
|
|
||||||
const source = fs.readFileSync(dbFile.file_path, { encoding: 'utf-8' });
|
|
||||||
const { mdxSource, frontMatter } = await parse(source, 'mdx', {
|
|
||||||
backlinks: dbBacklinkUrls,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Temporary, so that blogs work properly
|
// Temporary, so that blogs work properly
|
||||||
if (
|
if (dbFile.metadata.filetype === 'blog') {
|
||||||
dbFile.url_path.startsWith('blog/') ||
|
|
||||||
(dbFile.url_path.startsWith('docs/') && dbFile.metadata.filetype === 'blog')
|
|
||||||
) {
|
|
||||||
frontMatter.layout = 'blog';
|
frontMatter.layout = 'blog';
|
||||||
frontMatter.authorsDetails = await getAuthorsDetails(
|
frontMatter.authorsDetails = await getAuthorsDetails(
|
||||||
dbFile.metadata.authors
|
dbFile.metadata.authors
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const source = fs.readFileSync(filePath, { encoding: 'utf-8' });
|
||||||
|
const { mdxSource } = await parse(source, 'mdx', {});
|
||||||
|
|
||||||
|
// TODO temporary replacement for contentlayer's computedFields
|
||||||
|
const frontMatterWithComputedFields = await computeFields({
|
||||||
|
frontMatter,
|
||||||
|
urlPath,
|
||||||
|
filePath,
|
||||||
|
source,
|
||||||
|
});
|
||||||
|
|
||||||
|
let sidebarTree: Array<NavGroup | NavItem> = [];
|
||||||
|
|
||||||
|
if (frontMatterWithComputedFields?.showSidebar) {
|
||||||
|
let sidebarTreeFile = frontMatterWithComputedFields?.sidebarTreeFile;
|
||||||
|
|
||||||
|
if (sidebarTreeFile) {
|
||||||
|
const tree = fs.readFileSync(sidebarTreeFile, { encoding: "utf-8" });
|
||||||
|
|
||||||
|
sidebarTree = JSON.parse(tree);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const allPages = await mddb.getFiles({ extensions: ['md', 'mdx'] });
|
||||||
|
const pages = allPages.filter((p) => !p.metadata?.isDraft);
|
||||||
|
pages.forEach((page) => {
|
||||||
|
addPageToSitemap(page, sidebarTree);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
source: JSON.stringify(mdxSource),
|
source: JSON.stringify(mdxSource),
|
||||||
frontMatter: JSON.stringify(frontMatter),
|
meta: frontMatterWithComputedFields,
|
||||||
|
siteMap: sidebarTree,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -82,18 +104,70 @@ export async function getStaticPaths() {
|
|||||||
const mddb = await clientPromise;
|
const mddb = await clientPromise;
|
||||||
let allDocuments = await mddb.getFiles({ extensions: ['md', 'mdx'] });
|
let allDocuments = await mddb.getFiles({ extensions: ['md', 'mdx'] });
|
||||||
|
|
||||||
// Avoid duplicate path
|
const paths = allDocuments
|
||||||
allDocuments = allDocuments.filter(
|
.filter((page) => page.metadata?.isDraft !== true)
|
||||||
(doc) => !doc.url_path.startsWith('data-literate/')
|
.map((page) => {
|
||||||
);
|
const parts = page.url_path!.split('/');
|
||||||
|
return { params: { slug: parts } };
|
||||||
const paths = allDocuments.map((page) => {
|
});
|
||||||
const parts = page.url_path.split('/');
|
|
||||||
return { params: { slug: parts } };
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paths,
|
paths,
|
||||||
fallback: false,
|
fallback: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function capitalize(str: string) {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function addPageToGroup(page: MddbFile, sitemap: Array<NavGroup>) { */
|
||||||
|
function addPageToSitemap(page: any, sitemap: Array<NavGroup | NavItem>) {
|
||||||
|
const urlParts = page.url_path!.split('/').filter((part) => part);
|
||||||
|
// don't add home page to the sitemap
|
||||||
|
if (urlParts.length === 0) return;
|
||||||
|
// top level, root pages
|
||||||
|
if (urlParts.length === 1) {
|
||||||
|
sitemap.push({
|
||||||
|
name: page.metadata?.title || urlParts[0],
|
||||||
|
href: page.url_path,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// /blog/blogtest
|
||||||
|
const nestingLevel = urlParts.length - 1; // 1
|
||||||
|
let currArray: Array<NavItem | NavGroup> = sitemap;
|
||||||
|
|
||||||
|
for (let level = 0; level <= nestingLevel; level++) {
|
||||||
|
if (level === nestingLevel) {
|
||||||
|
currArray.push({
|
||||||
|
name: urlParts[level],
|
||||||
|
href: page.url_path,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchingGroup = currArray
|
||||||
|
.filter(isNavGroup)
|
||||||
|
.find(
|
||||||
|
(group) =>
|
||||||
|
group.path !== undefined && page.url_path.startsWith(group.path)
|
||||||
|
);
|
||||||
|
if (!matchingGroup) {
|
||||||
|
const newGroup: NavGroup = {
|
||||||
|
name: capitalize(urlParts[level]),
|
||||||
|
path: urlParts.slice(0, level + 1).join('/'),
|
||||||
|
level,
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
currArray.push(newGroup);
|
||||||
|
currArray = newGroup.children;
|
||||||
|
} else {
|
||||||
|
currArray = matchingGroup.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNavGroup(item: NavItem | NavGroup): item is NavGroup {
|
||||||
|
return (item as NavGroup).children !== undefined;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,25 @@ import Script from "next/script";
|
|||||||
|
|
||||||
import { DefaultSeo } from "next-seo";
|
import { DefaultSeo } from "next-seo";
|
||||||
|
|
||||||
import { pageview, ThemeProvider } from "@flowershow/core";
|
import { NavGroup, NavItem, pageview, ThemeProvider } from "@flowershow/core";
|
||||||
import { siteConfig } from "../config/siteConfig";
|
import { siteConfig } from "../config/siteConfig";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useRouter } from "next/dist/client/router";
|
import { useRouter } from "next/dist/client/router";
|
||||||
|
|
||||||
|
export interface CustomAppProps {
|
||||||
|
meta: {
|
||||||
|
showToc: boolean;
|
||||||
|
showEditLink: boolean;
|
||||||
|
showSidebar: boolean;
|
||||||
|
showComments: boolean;
|
||||||
|
urlPath: string; // not sure what's this for
|
||||||
|
editUrl?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
siteMap?: Array<NavItem | NavGroup>;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }) {
|
function MyApp({ Component, pageProps }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user