Skip to content

Commit

Permalink
Merge pull request #68 from aggmoulik/fix/GH-66
Browse files Browse the repository at this point in the history
Fix mobile version for documentation
  • Loading branch information
ije authored May 22, 2023
2 parents c4d8e44 + 7929281 commit faca5a9
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 51 deletions.
26 changes: 26 additions & 0 deletions components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,32 @@ export default function Header() {
</path>
</svg>
</a>
<div
className="space-y-1 pointer md:hidden"
id="hamburger-menu-button"
>
<div className="w-4 h-0.5 bg-gray-600"></div>
<div className="w-4 h-0.5 bg-gray-600"></div>
<div className="w-4 h-0.5 bg-gray-600"></div>
</div>

<div className="pointer hidden w-[16px]" id="hamburger-menu-close">
<svg
className="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</div>
</nav>
</div>
</header>
Expand Down
19 changes: 19 additions & 0 deletions components/useMediaQuery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useEffect, useState } from "react";

const useMediaQuery = (query: string) => {
const [matches, setMatches] = useState(false);

useEffect(() => {
const media = window.matchMedia(query);
if (media.matches !== matches) {
setMatches(media.matches);
}
const listener = () => setMatches(media.matches);
self.addEventListener("resize", listener);
return () => self.removeEventListener("resize", listener);
}, [matches, query]);

return matches;
};

export default useMediaQuery;
183 changes: 133 additions & 50 deletions routes/docs.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Fragment, useEffect, useMemo, useState } from "react";
import { Head, NavLink, useRouter } from "aleph/react";
import Header from "components/Header.tsx";
import useMediaQuery from "components/useMediaQuery.tsx";

type Menu = {
name: string;
Expand Down Expand Up @@ -73,37 +74,38 @@ const navMenu: Menu[] = [
];

export default function Docs({ children }: React.PropsWithChildren) {
const isDesktop = useMediaQuery("(min-width: 768px)");
const { url } = useRouter();
const [extended, setExtended] = useState(
navMenu.map((m) => m.items).flat().filter((item) => item.submenu).reduce(
(m, item) => {
navMenu
.map((m) => m.items)
.flat()
.filter((item) => item.submenu)
.reduce((m, item) => {
m[item.path] = url.pathname.startsWith(item.path);
return m;
},
{} as Record<string, boolean>,
),
}, {} as Record<string, boolean>),
);
const [menuIsOpen, setMenuIsOpen] = useState(false);
const [searchWords, setSearchWords] = useState("");
const navLinks = useMemo<[[string, string] | null, [string, string] | null]>(
() => {
const all: [string, string][] = [];
navMenu.forEach((g) =>
g.items.forEach((item) => {
if (item.submenu) {
item.submenu.forEach(({ title, path }) => {
all.push([title, item.path + (path === "/" ? "" : path)]);
});
} else {
all.push([item.title, item.path]);
}
})
);
const index = all.findIndex(([_, path]) => path === url.pathname);
return [all[index - 1] || null, all[index + 1] || null];
},
[url.pathname],
);
const navLinks = useMemo<
[[string, string] | null, [string, string] | null]
>(() => {
const all: [string, string][] = [];
navMenu.forEach((g) =>
g.items.forEach((item) => {
if (item.submenu) {
item.submenu.forEach(({ title, path }) => {
all.push([title, item.path + (path === "/" ? "" : path)]);
});
} else {
all.push([item.title, item.path]);
}
})
);
const index = all.findIndex(([_, path]) => path === url.pathname);
return [all[index - 1] || null, all[index + 1] || null];
}, [url.pathname]);
const editUrl = useMemo(() => {
const md = url.pathname === "/docs"
? url.pathname + "/index.md"
Expand All @@ -114,32 +116,37 @@ export default function Docs({ children }: React.PropsWithChildren) {
if (searchWords === "") {
return navMenu;
}
return navMenu.map((g) => {
const includes = (item: MenuItem) =>
item.title.toLowerCase().includes(searchWords);
return {
...g,
items: g.items.filter((item) => {
return includes(item) || item.submenu?.some(includes);
}).map((item) => ({
...item,
submenu: item.submenu?.filter((subItem) =>
includes(item) || includes(subItem)
),
})),
};
}).filter((g) => g.items.length > 0);
return navMenu
.map((g) => {
const includes = (item: MenuItem) =>
item.title.toLowerCase().includes(searchWords);
return {
...g,
items: g.items
.filter((item) => {
return includes(item) || item.submenu?.some(includes);
})
.map((item) => ({
...item,
submenu: item.submenu?.filter(
(subItem) => includes(item) || includes(subItem),
),
})),
};
})
.filter((g) => g.items.length > 0);
}, [searchWords]);

useEffect(() => {
setExtended(
navMenu.map((m) => m.items).flat().filter((item) => item.submenu).reduce(
(m, item) => {
navMenu
.map((m) => m.items)
.flat()
.filter((item) => item.submenu)
.reduce((m, item) => {
m[item.path] = url.pathname.startsWith(item.path);
return m;
},
{} as Record<string, boolean>,
),
}, {} as Record<string, boolean>),
);
document.querySelectorAll(".makedown-body video").forEach((block) => {
const v = block as HTMLVideoElement;
Expand All @@ -151,11 +158,86 @@ export default function Docs({ children }: React.PropsWithChildren) {
v.requestFullscreen();
}
});
v.addEventListener("playing", () => v.className = "is-playing");
v.addEventListener("pause", () => v.className = "is-paused");
v.addEventListener("playing", () => (v.className = "is-playing"));
v.addEventListener("pause", () => (v.className = "is-paused"));
});
}, [url.pathname]);

// Hamburger menu for mobile devices
useEffect(() => {
const hamburgerMenuButton = document.getElementById(
"hamburger-menu-button",
);
const hamburgerMenuClose = document.getElementById("hamburger-menu-close");
const navMenuButton = document.getElementById("nav-menu");

// Toggle the accordian
function toggleAccordion(event: MouseEvent) {
event.preventDefault();
event.stopPropagation();
if (navMenuButton) {
navMenuButton.classList.toggle("hidden");
}

if (hamburgerMenuButton && hamburgerMenuClose) {
hamburgerMenuButton.classList.toggle("hidden");
hamburgerMenuClose.classList.toggle("hidden");
}
}

// Event Listener for Navigation Mobile Hamburger Icon
if (hamburgerMenuButton) {
hamburgerMenuButton.addEventListener("click", toggleAccordion);
}

// Event Listener for Navigation Mobile Close Icon
if (hamburgerMenuClose) {
hamburgerMenuClose.addEventListener("click", toggleAccordion);
}

// Event listeners should be removed when unmounted
return () => {
hamburgerMenuButton?.removeEventListener("click", toggleAccordion);
hamburgerMenuClose?.removeEventListener("click", toggleAccordion);
};
}, []);

useEffect(() => {
const navMenuButton = document.getElementById("nav-menu");
const hamburgerMenuClose = document.getElementById("hamburger-menu-close");
const hamburgerMenuButton = document.getElementById(
"hamburger-menu-button",
);
const isContainHidden = navMenuButton?.classList.contains("hidden");
if (isDesktop && navMenuButton) {
// When the windows is resized for desktop

// Remove the hidden from navigation for desktop
if (isContainHidden) {
navMenuButton.classList.remove("hidden");
}

// Remove the close button from navigation when resized to desktop
if (hamburgerMenuClose) {
hamburgerMenuClose.classList.add("hidden");
}
} else {
// When the windows is resized to mobile

// Navigation Menu should not be visible at first render
if (!isContainHidden && navMenuButton) {
navMenuButton.classList.add("hidden");
}

// When resize to mobile then hamburger button should show if it is hidden
if (hamburgerMenuButton) {
if (hamburgerMenuButton.classList.contains("hidden")) {
hamburgerMenuButton.classList.remove("hidden");
}
}
}
}, [isDesktop]);

return (
<>
<Head>
Expand All @@ -169,7 +251,10 @@ export default function Docs({ children }: React.PropsWithChildren) {
</Head>
<Header />
<div className="m-auto w-14/16 py-4 max-w-[1080px] h-full flex items-start justify-between gap-18">
<aside className="sticky top-20 w-60 shrink-0">
<aside
className="sticky md:top-20 md:w-60 shrink-0 w-full"
id="nav-menu"
>
<div className="search">
<input
className="border border-gray-200 rounded-md px-3 py-1.5 w-full"
Expand Down Expand Up @@ -269,9 +354,7 @@ export default function Docs({ children }: React.PropsWithChildren) {
))}
</nav>
</aside>
<div className="markdown-body grow-1">
{children}
</div>
<div className="markdown-body grow-1">{children}</div>
</div>
</>
);
Expand Down
1 change: 0 additions & 1 deletion style/markdown.css
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@
color: #333;
background-color: #f6f6f6;
white-space: pre;
overflow-x: auto;
-webkit-overflow-scrolling: touch;

&>code {
Expand Down

0 comments on commit faca5a9

Please sign in to comment.