Skip to content

Commit dbf0c1b

Browse files
[techMake this a Flowershow website (#8)
* Add flowershow website * remove unwanted files * remove other files * Move assets folder * delete old src folder * undo
1 parent c147d9b commit dbf0c1b

34 files changed

+12491
-0
lines changed

Diff for: .gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.DS_Store
2+
markdown.db
3+
node_modules/
4+
.next/
5+
out/
6+
.obsidian/

Diff for: components/Layout.tsx

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { useEffect, useState } from "react";
2+
import Head from "next/head.js";
3+
import { NextRouter, useRouter } from "next/router.js";
4+
import clsx from "clsx";
5+
import {
6+
useTableOfContents,
7+
collectHeadings,
8+
Nav,
9+
Comments,
10+
Footer,
11+
EditThisPage,
12+
TableOfContents,
13+
SiteToc,
14+
} from "@portaljs/core";
15+
import type {
16+
NavItem,
17+
NavGroup,
18+
CommentsConfig,
19+
NavConfig,
20+
ThemeConfig,
21+
AuthorConfig,
22+
TocSection
23+
} from "@portaljs/core"
24+
25+
26+
interface Props extends React.PropsWithChildren {
27+
showComments: boolean;
28+
showEditLink: boolean;
29+
showSidebar: boolean;
30+
showToc: boolean;
31+
nav: NavConfig;
32+
author: AuthorConfig;
33+
theme: ThemeConfig;
34+
urlPath: string;
35+
commentsConfig: CommentsConfig;
36+
siteMap: Array<NavItem | NavGroup>;
37+
editUrl?: string;
38+
}
39+
40+
export const Layout: React.FC<Props> = ({
41+
children,
42+
nav,
43+
author,
44+
theme,
45+
showEditLink,
46+
showToc,
47+
showSidebar,
48+
urlPath,
49+
showComments,
50+
commentsConfig,
51+
editUrl,
52+
siteMap,
53+
}) => {
54+
const [isScrolled, setIsScrolled] = useState(false);
55+
const [tableOfContents, setTableOfContents] = useState<TocSection[]>([]);
56+
const currentSection = useTableOfContents(tableOfContents);
57+
const router: NextRouter = useRouter();
58+
59+
useEffect(() => {
60+
if (!showToc) return;
61+
const headingNodes: NodeListOf<HTMLHeadingElement> =
62+
document.querySelectorAll("h1,h2,h3");
63+
const toc = collectHeadings(headingNodes);
64+
setTableOfContents(toc ?? []);
65+
}, [router.asPath, showToc]); // update table of contents on route change with next/link
66+
67+
useEffect(() => {
68+
function onScroll() {
69+
setIsScrolled(window.scrollY > 0);
70+
}
71+
onScroll();
72+
window.addEventListener("scroll", onScroll, { passive: true });
73+
return () => {
74+
window.removeEventListener("scroll", onScroll);
75+
};
76+
}, []);
77+
78+
return (
79+
<>
80+
<Head>
81+
<link
82+
rel="icon"
83+
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>💐</text></svg>"
84+
/>
85+
<meta charSet="utf-8" />
86+
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
87+
</Head>
88+
<div className="min-h-screen bg-background dark:bg-background-dark">
89+
{/* NAVBAR */}
90+
<div
91+
className={clsx(
92+
"sticky top-0 z-50 w-full",
93+
isScrolled
94+
? "dark:bg-background-dark/95 bg-background/95 backdrop-blur [@supports(backdrop-filter:blur(0))]:dark:bg-background-dark/75"
95+
: "dark:bg-background-dark bg-background"
96+
)}
97+
>
98+
<div className="h-[4rem] flex flex-col justify-center max-w-8xl mx-auto p-4 md:px-8">
99+
<Nav
100+
title={nav.title}
101+
logo={nav.logo}
102+
links={nav.links}
103+
search={nav.search}
104+
social={nav.social}
105+
defaultTheme={theme.defaultTheme}
106+
themeToggleIcon={theme.themeToggleIcon}
107+
version={nav.version}
108+
>
109+
{showSidebar && <SiteToc currentPath={urlPath} nav={siteMap} />}
110+
</Nav>
111+
</div>
112+
</div>
113+
{/* wrapper for sidebar, main content and ToC */}
114+
<div
115+
className="max-w-8xl mx-auto px-4 md:px-8"
116+
>
117+
{/* SIDEBAR */}
118+
{showSidebar && (
119+
<div className="hidden lg:block fixed z-20 w-[16rem] top-[4rem] right-auto bottom-0 left-[max(0px,calc(50%-44rem))] pt-8 pl-8 overflow-y-auto">
120+
<SiteToc currentPath={urlPath} nav={siteMap} />
121+
</div>
122+
)}
123+
{/* MAIN CONTENT & FOOTER */}
124+
<div className={clsx(
125+
"mx-auto lg:px-[16rem] pt-8",
126+
!showToc && !showSidebar && "lg:px-0",
127+
)}>
128+
{children}
129+
{/* EDIT THIS PAGE LINK */}
130+
{showEditLink && editUrl && <EditThisPage url={editUrl} />}
131+
{/* PAGE COMMENTS */}
132+
{showComments && (
133+
<div
134+
className="prose mx-auto pt-6 pb-6 text-center text-gray-700 dark:text-gray-300"
135+
id="comment"
136+
>
137+
{<Comments commentsConfig={commentsConfig} slug={urlPath} />}
138+
</div>
139+
)}
140+
</div>
141+
{/** TABLE OF CONTENTS */}
142+
{showToc && tableOfContents.length > 0 && (
143+
<div className="hidden xl:block fixed z-20 w-[16rem] top-[4rem] bottom-0 right-[max(0px,calc(50%-44rem))] left-auto pt-8 pr-8 overflow-y-auto"> <TableOfContents
144+
tableOfContents={tableOfContents}
145+
currentSection={currentSection}
146+
/>
147+
</div>
148+
)}
149+
<Footer links={nav.links} author={author} />
150+
</div>
151+
</div>
152+
</>
153+
);
154+
};

Diff for: components/MdxPage.tsx

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { MDXRemote } from "next-mdx-remote";
2+
import { Mermaid, Pre } from "@portaljs/core";
3+
4+
import layouts from "@/layouts";
5+
6+
// Custom components/renderers to pass to MDX.
7+
// Since the MDX files aren't loaded by webpack, they have no knowledge of how
8+
// to handle import statements. Instead, you must include components in scope
9+
// here.
10+
const components = {
11+
mermaid: Mermaid,
12+
pre: Pre,
13+
table: (props) => (
14+
<div className="overflow-x-auto">
15+
<table {...props} />
16+
</div>
17+
),
18+
};
19+
20+
export default function MdxPage({ source, frontMatter }) {
21+
const Layout = ({ children }) => {
22+
if (frontMatter.layout) {
23+
const LayoutComponent = layouts[frontMatter.layout];
24+
return <LayoutComponent {...frontMatter}>{children}</LayoutComponent>;
25+
}
26+
return <>{children}</>;
27+
};
28+
29+
return (
30+
<main id="mdxpage" className="prose max-w-none mx-auto">
31+
<Layout>
32+
<MDXRemote {...source} components={components} />
33+
</Layout>
34+
</main>
35+
);
36+
}

Diff for: config/siteConfig.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { defaultConfig } from "@portaljs/core";
2+
import userConfig from "@/content/config.mjs";
3+
4+
// TODO types
5+
const siteConfig: any = {
6+
...defaultConfig,
7+
...userConfig,
8+
// prevent theme object overrides for
9+
// values not provided in userConfig
10+
theme: {
11+
...defaultConfig.theme,
12+
...userConfig?.theme,
13+
},
14+
};
15+
16+
export default siteConfig;

Diff for: content/config.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default {};

Diff for: layouts/index.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {
2+
SimpleLayout,
3+
DocsLayout,
4+
UnstyledLayout,
5+
BlogLayout,
6+
} from "@portaljs/core";
7+
8+
export default {
9+
simple: SimpleLayout,
10+
docs: DocsLayout,
11+
unstyled: UnstyledLayout,
12+
blog: BlogLayout,
13+
};

Diff for: lib/computeFields.ts

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// This file is a temporary replacement for legacy contentlayer's computeFields + default fields values
2+
import { remark } from "remark";
3+
import stripMarkdown, { Options } from "strip-markdown";
4+
5+
import siteConfig from "@/config/siteConfig";
6+
import getAuthorsDetails from "./getAuthorsDetails";
7+
import sluggify from "./sluggify";
8+
9+
// TODO return type
10+
11+
const computeFields = async ({
12+
frontMatter,
13+
urlPath,
14+
filePath,
15+
source,
16+
}: {
17+
frontMatter: Record<string, any>;
18+
urlPath: string;
19+
filePath: string;
20+
source: string;
21+
}) => {
22+
// Fields with corresponding config options
23+
// TODO see _app.tsx
24+
const showComments =
25+
frontMatter.showComments ?? siteConfig.showComments ?? false;
26+
const showEditLink =
27+
frontMatter.showEditLink ?? siteConfig.showEditLink ?? false;
28+
// TODO take config into accout
29+
const showLinkPreviews =
30+
frontMatter.showLinkPreviews ?? siteConfig.showLinkPreviews ?? false;
31+
const showToc = frontMatter.showToc ?? siteConfig.showToc ?? false;
32+
const showSidebar =
33+
frontMatter.showSidebar ?? siteConfig.showSidebar ?? false;
34+
35+
// Computed fields
36+
// const title = frontMatter.title ?? (await extractTitle(source));
37+
const title = frontMatter.title ?? null;
38+
const description =
39+
frontMatter.description ?? (await extractDescription(source));
40+
const date = frontMatter.date ?? frontMatter.created ?? null;
41+
const layout = (() => {
42+
if (frontMatter.layout) return frontMatter.layout;
43+
if (urlPath.startsWith("blog/")) return "blog";
44+
// if (urlPath.startsWith("docs/")) return "docs";
45+
return "docs"; // TODO default layout from config?
46+
})();
47+
48+
// TODO Temporary, should probably be a column in the database
49+
const slug = sluggify(urlPath);
50+
// TODO take into accout include/exclude fields in config
51+
const isDraft = frontMatter.isDraft ?? false;
52+
const editUrl =
53+
(siteConfig.editLinkRoot && `${siteConfig.editLinkRoot}/${filePath}`) ||
54+
null;
55+
const authors = await getAuthorsDetails(frontMatter.authors);
56+
57+
return {
58+
...frontMatter,
59+
authors,
60+
title,
61+
description,
62+
date,
63+
layout,
64+
slug,
65+
urlPath, // extra for blogs index page; temporary here
66+
isDraft,
67+
editUrl,
68+
showComments,
69+
showEditLink,
70+
showLinkPreviews,
71+
showToc,
72+
showSidebar,
73+
};
74+
};
75+
76+
const extractTitle = async (source: string) => {
77+
const heading = source.trim().match(/^#\s+(.*)/);
78+
if (heading) {
79+
const title = heading[1]
80+
// replace wikilink with only text value
81+
.replace(/\[\[([\S]*?)]]/, "$1");
82+
83+
const stripTitle = await remark().use(stripMarkdown).process(title);
84+
return stripTitle.toString().trim();
85+
}
86+
return null;
87+
};
88+
89+
const extractDescription = async (source: string) => {
90+
const content = source
91+
// remove commented lines
92+
.replace(/{\/\*.*\*\/}/g, "")
93+
// remove import statements
94+
.replace(
95+
/^import\s*(?:\{\s*[\w\s,\n]+\s*\})?(\s*(\w+))?\s*from\s*("|')[^"]+("|');?$/gm,
96+
""
97+
)
98+
// remove youtube links
99+
.replace(/^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/gm, "")
100+
// replace wikilinks with only text
101+
.replace(/([^!])\[\[(\S*?)\]]/g, "$1$2")
102+
// remove wikilink images
103+
.replace(/!\[[\S]*?]]/g, "");
104+
105+
// remove markdown formatting
106+
const stripped = await remark()
107+
.use(stripMarkdown, {
108+
remove: ["heading", "blockquote", "list", "image", "html", "code"],
109+
} as Options)
110+
.process(content);
111+
112+
if (stripped.value) {
113+
const description: string = stripped.value.toString().slice(0, 200);
114+
return description + "...";
115+
}
116+
return null;
117+
};
118+
119+
export default computeFields;

Diff for: lib/getAuthorsDetails.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import siteConfig from "@/config/siteConfig";
2+
import clientPromise from "./mddb.mjs";
3+
import sluggify from "./sluggify";
4+
5+
const getAuthorsDetails = async (authors?: string[]) => {
6+
const mddb = await clientPromise;
7+
const allPeople = await mddb.getFiles({ folder: "people" });
8+
let blogAuthors: string[] = [];
9+
10+
if (authors) {
11+
blogAuthors = authors;
12+
} else if (siteConfig.defaultAuthor) {
13+
blogAuthors = [siteConfig.defaultAuthor];
14+
} else {
15+
blogAuthors = [];
16+
}
17+
18+
return blogAuthors.map((author) => {
19+
const matchedAuthor = allPeople.find((p) => {
20+
// TODO: slug should probably be a separate column in the DB
21+
const slug = sluggify(p.url_path!);
22+
return (
23+
p.metadata?.id === author ||
24+
slug === author ||
25+
p.metadata?.name === author
26+
);
27+
});
28+
return matchedAuthor
29+
? {
30+
name: matchedAuthor.metadata?.name,
31+
avatar:
32+
matchedAuthor.metadata?.avatar ?? siteConfig.avatarPlaceholder,
33+
// TODO find a better way
34+
urlPath: !matchedAuthor.metadata?.isDraft && matchedAuthor.url_path,
35+
}
36+
: {
37+
name: author,
38+
avatar: siteConfig.avatarPlaceholder,
39+
};
40+
});
41+
};
42+
43+
export default getAuthorsDetails;

0 commit comments

Comments
 (0)