Skip to content

Commit c000293

Browse files
Merge pull request #5 from PrathameshDhande22/hamburger-menu
Hamburger Menu Support, Custom Components like Navbar, Navbar Wrapper and Navigation Links
2 parents 693ced5 + af1bb5e commit c000293

18 files changed

+326
-81
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"check": "tsc --noEmit && npx prettier . --check",
1818
"format": "npx prettier . --write",
1919
"test": "tsx ./quartz/util/path.test.ts && tsx ./quartz/depgraph.test.ts",
20-
"profile": "0x -D prof ./quartz/bootstrap-cli.mjs build --concurrency=1"
20+
"profile": "0x -D prof ./quartz/bootstrap-cli.mjs build --concurrency=1",
21+
"dev": "npx quartz build --serve"
2122
},
2223
"engines": {
2324
"npm": ">=9.3.1",

quartz.layout.ts

+21-13
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,32 @@ import * as Component from "./quartz/components"
55
export const sharedPageComponents: SharedLayout = {
66
navbar: [
77
Component.PageTitle(),
8-
Component.MobileOnly(Component.Spacer()),
9-
Component.NavigationLinks({
8+
Component.NavbarWrapper({
9+
components: [
10+
Component.MobileOnly(Component.Spacer()),
11+
Component.NavigationLinks({
12+
links: {
13+
Blogs: "/blogs",
14+
Github: "https://github.com/PrathameshDhande22/Java-Tutorial",
15+
"About Me": "https://github.com/PrathameshDhande22"
16+
}
17+
}),
18+
Component.Search(),
19+
Component.Darkmode()
20+
]
21+
})
22+
],
23+
head: Component.Head(),
24+
header: [Component.MobileOnly(
25+
Component.Drawer({
1026
links: {
1127
Blogs: "/blogs",
1228
Github: "https://github.com/PrathameshDhande22/Java-Tutorial",
1329
"About Me": "https://github.com/PrathameshDhande22"
1430
}
15-
}),
16-
Component.Darkmode()
17-
],
18-
head: Component.Head(),
19-
header: [
20-
21-
],
22-
afterBody: [
23-
],
31+
})
32+
)],
33+
afterBody: [],
2434
footer: Component.Footer({
2535
links: {
2636
GitHub: "https://prathameshdhande22.github.io/Java-Tutorial/",
@@ -32,14 +42,12 @@ export const sharedPageComponents: SharedLayout = {
3242
// components for pages that display a single page (e.g. a single note)
3343
export const defaultContentPageLayout: PageLayout = {
3444
beforeBody: [
35-
3645
Component.Breadcrumbs(),
3746
Component.ArticleTitle(),
3847
Component.ContentMeta(),
3948
Component.TagList(),
4049
],
4150
left: [
42-
Component.Search(),
4351
Component.DesktopOnly(
4452
Component.Explorer({
4553
title: "Patterns",

quartz/components/Drawer.tsx

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
2+
import style from "./styles/drawer.scss"
3+
// @ts-ignore
4+
import script from "./scripts/drawer.inline"
5+
import TableOfContents from "./TableOfContents"
6+
import { classNames } from "../util/lang"
7+
import { FileNode } from "./ExplorerNode"
8+
import { resolveRelative } from "../util/path"
9+
10+
interface Options {
11+
links?: Record<string, string>
12+
}
13+
14+
export default ((opts?: Options) => {
15+
const Drawer: QuartzComponent = ({ displayClass, fileData, allFiles }: QuartzComponentProps) => {
16+
const links = opts?.links ?? []
17+
const fileTree = new FileNode("")
18+
allFiles.forEach((file) => fileTree.add(file))
19+
20+
return (
21+
<div class="drawer">
22+
<div class="drawer-wrapper">
23+
<h3 class="drawer-title">MENU</h3>
24+
<ul class="links">
25+
{Object.entries(links).map(([text, link]) => (
26+
<li>
27+
<a href={link}>{text}</a>
28+
</li>
29+
))}
30+
</ul>
31+
<div class={classNames(displayClass, "toc")}>
32+
<button
33+
type="button"
34+
id="toc"
35+
class={fileData.collapseToc ? "collapsed" : ""}
36+
aria-controls="toc-content"
37+
aria-expanded={!fileData.collapseToc}
38+
>
39+
<h3>Patterns</h3>
40+
<svg
41+
xmlns="http://www.w3.org/2000/svg"
42+
width="24"
43+
height="24"
44+
viewBox="0 0 24 24"
45+
fill="none"
46+
stroke="currentColor"
47+
stroke-width="2"
48+
stroke-linecap="round"
49+
stroke-linejoin="round"
50+
class="fold"
51+
>
52+
<polyline points="6 9 12 15 18 9"></polyline>
53+
</svg>
54+
</button>
55+
<div id="toc-content" class={fileData.collapseToc ? "collapsed" : ""}>
56+
<ul class="overflow">
57+
{allFiles.map((value) => {
58+
return (
59+
<li key={value.slug}>
60+
<a href={resolveRelative(value.slug!, value.slug!)} data-for={value.slug}>{value.frontmatter?.title}</a>
61+
</li>
62+
)
63+
})}
64+
</ul>
65+
</div>
66+
</div>
67+
</div>
68+
</div>
69+
)
70+
}
71+
72+
Drawer.css = style
73+
Drawer.afterDOMLoaded = script
74+
75+
return Drawer
76+
}) satisfies QuartzComponentConstructor

quartz/components/NavbarWrapper.tsx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { concatenateResources } from "../util/resources"
2+
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
3+
import style from "./styles/navbarwrapper.scss"
4+
interface Options {
5+
components: QuartzComponent[]
6+
}
7+
8+
export default ((opts: Options) => {
9+
const NavbarWrapper: QuartzComponent = (props: QuartzComponentProps) => {
10+
return (
11+
<div class="navbar-wrapper">
12+
{opts?.components.map((Component) => {
13+
return <Component {...props}></Component>
14+
})}
15+
</div>
16+
)
17+
}
18+
NavbarWrapper.afterDOMLoaded = concatenateResources(
19+
...opts.components.map((c) => c.afterDOMLoaded),
20+
)
21+
NavbarWrapper.beforeDOMLoaded = concatenateResources(
22+
...opts.components.map((c) => c.beforeDOMLoaded),
23+
)
24+
NavbarWrapper.css = concatenateResources(...opts.components.map((c) => c.css), style)
25+
return NavbarWrapper
26+
}) satisfies QuartzComponentConstructor<Options>

quartz/components/NavigationLinks.tsx

+20-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,26 @@ export default ((opts?: Options) => {
1313
const links = opts?.links ?? []
1414
return (
1515
<nav class="nav-links">
16-
<button className="menu-btn">Menu</button>
16+
<button className="menu-btn">
17+
<svg
18+
xmlns="http://www.w3.org/2000/svg"
19+
width="24"
20+
height="24"
21+
viewBox="0 0 24 24"
22+
stroke-width="2"
23+
stroke-linecap="round"
24+
stroke-linejoin="round"
25+
class="hamburger active"
26+
>
27+
<line x1="4" x2="20" y1="12" y2="12" />
28+
<line x1="4" x2="20" y1="6" y2="6" />
29+
<line x1="4" x2="20" y1="18" y2="18" />
30+
</svg>
31+
<svg width="24" height="24" class="cross">
32+
<line x1="5" y1="5" x2="19" y2="19" stroke-width="2" />
33+
<line x1="19" y1="5" x2="5" y2="19" stroke-width="2" />
34+
</svg>
35+
</button>
1736
<ul class="links">
1837
{Object.entries(links).map(([text, link]) => (
1938
<li>

quartz/components/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ import RecentNotes from "./RecentNotes"
2121
import Breadcrumbs from "./Breadcrumbs"
2222
import Comments from "./Comments"
2323
import NavigationLinks from "./NavigationLinks"
24+
import Drawer from "./Drawer"
25+
import NavbarWrapper from "./NavbarWrapper"
2426

2527
export {
2628
ArticleTitle,
2729
Content,
30+
Drawer,
2831
TagContent,
2932
NavigationLinks,
3033
FolderContent,
@@ -37,6 +40,7 @@ export {
3740
Explorer,
3841
TagList,
3942
Graph,
43+
NavbarWrapper,
4044
Backlinks,
4145
Search,
4246
Footer,
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { assignActiveClassToDrawerButton } from "./util"
2+
3+
const drawerele = document.querySelector(".drawer")
4+
drawerele?.addEventListener("click", () => {
5+
drawerele.classList.remove("active")
6+
7+
const isdrawerActive: boolean = drawerele.classList.contains("active")
8+
assignActiveClassToDrawerButton(isdrawerActive)
9+
})
10+
11+
const toccontentele = document.querySelector(".toc")
12+
toccontentele?.addEventListener("click", (e) => {
13+
e.stopPropagation();
14+
})
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
let links = document.getElementsByClassName("links")[0]
1+
import { assignActiveClassToDrawerButton } from "./util"
2+
3+
let drawer = document.getElementsByClassName("drawer")[0]
24

35
document.getElementsByClassName("menu-btn")[0].addEventListener("click", () => {
4-
links.classList.toggle("active")
5-
})
6+
drawer.classList.add("active")
7+
8+
const isdrawerActive: boolean = drawer.classList.contains("active")
9+
assignActiveClassToDrawerButton(isdrawerActive)
10+
})
11+

quartz/components/scripts/util.ts

+27
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,30 @@ export async function fetchCanonical(url: URL): Promise<Response> {
4343
const [_, redirect] = text.match(canonicalRegex) ?? []
4444
return redirect ? fetch(`${new URL(redirect, url)}`) : res
4545
}
46+
47+
/**
48+
* Toggles the active state of the drawer button and adjusts the document body style.
49+
* This function is used to manage the visual state of a hamburger menu or drawer interface.
50+
*
51+
* @param isactive - A boolean indicating whether the drawer should be in an active state.
52+
* If true, the cross icon is shown and the body scroll is disabled.
53+
* If false, the hamburger icon is shown and the body scroll is enabled.
54+
*
55+
* @returns void This function does not return a value.
56+
*/
57+
export function assignActiveClassToDrawerButton(isactive: boolean) {
58+
const hamburgersvg = document.querySelector(".hamburger")
59+
const cross = document.querySelector(".cross")
60+
61+
if (isactive) {
62+
cross?.classList.add("active")
63+
hamburgersvg?.classList.remove("active")
64+
document.body.style.overflow = "hidden"
65+
document.body.style.height = "100vh"
66+
} else {
67+
cross?.classList.remove("active")
68+
hamburgersvg?.classList.add("active")
69+
document.body.style.overflow = "auto"
70+
document.body.style.height = "100%"
71+
}
72+
}

quartz/components/styles/drawer.scss

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
@use "../../styles/variables.scss" as *;
2+
3+
.drawer {
4+
position: fixed;
5+
top: 0px;
6+
right: 0;
7+
width: 100vw;
8+
transform: translateX(-100%);
9+
transition: transform 0.3s ease;
10+
height: 100vh;
11+
background-color: rgba(0, 0, 0, 0.5);
12+
color: var(--gray);
13+
z-index: 600;
14+
display: flex;
15+
justify-content: center;
16+
align-items: center;
17+
box-shadow:
18+
rgba(85, 86, 86, 0.48) 6px 2px 16px 0px,
19+
rgba(0, 0, 0, 0.8) -6px -2px 16px 0px;
20+
21+
&.active {
22+
transform: translateX(0);
23+
@media all and ($tablet) {
24+
transform: translateX(-100%);
25+
}
26+
27+
@media all and ($desktop) {
28+
transform: translateX(-100%);
29+
}
30+
}
31+
}
32+
33+
.drawer-title{
34+
margin-top: 1rem;
35+
margin-bottom: 0.5rem;
36+
text-align: center;
37+
color: var(--secondary);
38+
}
39+
40+
.drawer-wrapper {
41+
display: flex;
42+
flex-direction: column;
43+
gap: 8px;
44+
width: 70%;
45+
height: 85%;
46+
padding: 10px 10px 10px 20px;
47+
background-color: var(--light);
48+
49+
& > .links {
50+
display: flex;
51+
flex-direction: column;
52+
justify-content: center;
53+
gap: 5px;
54+
padding: 0;
55+
margin: 0;
56+
57+
& > li {
58+
list-style: none;
59+
display: inline-block;
60+
font-size: 18px;
61+
62+
& > a {
63+
display: inline-block;
64+
text-decoration: none;
65+
color: inherit;
66+
padding: 2px 0;
67+
}
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)