Skip to content

Commit

Permalink
feat: hamburger btn, mobile navigation sidebar animation
Browse files Browse the repository at this point in the history
  • Loading branch information
ncpa0cpl committed Jul 24, 2024
1 parent 2484765 commit ec838f3
Show file tree
Hide file tree
Showing 11 changed files with 215 additions and 15 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"dependencies": {
"adwavecss": "0.1.0",
"adwaveui": "0.0.11",
"dedent": "^1.5.3",
"highlight.js": "^11.10.0"
},
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
Expand Down
12 changes: 7 additions & 5 deletions scripts/build.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async function build() {

await Promise.all([
...templates.map((tmpl) => {
return buildTemplate(tmpl, outDir);
return buildTemplate(p("src"), tmpl, outDir);
}),
buildServiceWorkers(outDir),
fs.promises.cp(p("src/assets"), assetsDir, { recursive: true }),
Expand All @@ -61,7 +61,9 @@ async function startServer() {
const { serve } = await import("@ncpa0cpl/goserve");

const proc = serve(p("docs"), {
watch: true,
hmr: {
watch: true,
},
spa: "index.html",
cacheHeaders: {
nocache: true,
Expand Down Expand Up @@ -104,9 +106,9 @@ build()
if (ev === "addDir") return;

if (
fPath.includes("src/assets")
&& !fPath.includes("src/assets/js")
&& !fPath.includes("src/assets/css")
fPath.includes("src/assets") &&
!fPath.includes("src/assets/js") &&
!fPath.includes("src/assets/css")
) {
console.log(
`Asset changed, copying (${path.relative(p("."), fPath)})`,
Expand Down
48 changes: 43 additions & 5 deletions scripts/tmpl-builder.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ const { evalModule } = require("./eval-module.cjs");
const { changeExt } = require("./change-ext.cjs");
const { ExtFilesCtx } = require("./external-files-context.cjs");
const crypto = require("crypto");
const { default: dedent } = require("dedent");

function createHash(data, len) {
return crypto.createHash("shake256", { outputLength: len }).update(data)
return crypto
.createHash("shake256", { outputLength: len })
.update(data)
.digest("hex");
}

Expand All @@ -24,10 +27,15 @@ const templatesSrc = p("src/templates");
/** @typedef {{ root: string; tsx: string }} Template */

/**
* @param {string} srcDir
* @param {Template} template
* @param {string} outDir
*/
module.exports.buildTemplate = async function buildTemplate(template, outDir) {
module.exports.buildTemplate = async function buildTemplate(
srcDir,
template,
outDir,
) {
const tsxFilename = path.join(template.root, template.tsx);

const result = await esbuild.build({
Expand All @@ -43,7 +51,7 @@ module.exports.buildTemplate = async function buildTemplate(template, outDir) {
sourcemap: IS_DEV ? "inline" : false,
external: ["jsxte", "esbuild", "scripts", "prettier"],
platform: "node",
plugins: [plugin],
plugins: [plugin(srcDir, outDir)],
alias: {
scripts: p("./scripts"),
},
Expand Down Expand Up @@ -116,7 +124,11 @@ module.exports.buildTemplate = async function buildTemplate(template, outDir) {
);
};

const plugin = {
/**
* @param {string} srcDir
* @param {string} outDir
*/
const plugin = (srcDir, outDir) => ({
name: "tmpl-builder-plugin",
/** @param {import("esbuild").PluginBuild} build */
setup(build) {
Expand Down Expand Up @@ -145,5 +157,31 @@ const plugin = {

return { contents: code, loader: "tsx" };
});

const BASE_URL = process.env.BASE_URL ?? "/docs/";
build.onLoad({ filter: /\.svg$/ }, async (args) => {
const relPath = path.relative(srcDir, args.path);
await fs.promises.copyFile(args.path, path.join(outDir, relPath));
const svgPath = path.join(BASE_URL, relPath);

const componentName = path
.basename(args.path, ".svg")
.replace(/^\w/, (char) => char.toUpperCase()) // capitalize first letter
.replace(/-\w/, (char) => char[1].toUpperCase()); // to camel case

const code = /* js */ `
import { jsx } from "jsxte/jsx-runtime";
export default function Svg(props) {
return jsx("img", {
...props,
src: ${JSON.stringify(svgPath)},
});
}
Svg.displayName = ${JSON.stringify(componentName)};
`;
return { contents: dedent(code.trimStart()), loader: "jsx" };
});
},
};
});
11 changes: 11 additions & 0 deletions src/assets/burger-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/assets/burger-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 29 additions & 1 deletion src/components/navbar.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,42 @@ let MobileNavbarModal: HTMLDialogElement | null = null;

const initMobileMenu = () => {
const dialog = document.querySelector("dialog")!;
const btnContainer = dialog.querySelector(".left-navbar-mobile-overlay")!;

const btn = document.querySelector(".navbar-mobile-btn")!;
btn.addEventListener("click", () => {
dialog.showModal();
});

const backdropArea = document.querySelector(".navbar-overlay-backdrop")!;
backdropArea.addEventListener("click", () => {
dialog.close();
backdropArea.animate(
[
{
opacity: 1,
},
{
opacity: 0,
},
],
{
duration: 300,
},
);
const animation = btnContainer.animate(
[
{
left: "0%",
},
{
left: "-66%",
},
],
{
duration: 250,
},
);
animation.onfinish = () => dialog.close();
});

MobileNavbarModal = dialog;
Expand Down
95 changes: 94 additions & 1 deletion src/components/navbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,12 @@ a.navbar-link button {
padding: unset;
min-width: 100vw;
min-height: 100vh;
background-color: #2121214f;
background-color: transparent;
}

.navbar-overlay-backdrop {
flex: 1;
background-color: #00000073;
}

.left-navbar-mobile-overlay {
Expand All @@ -80,4 +81,96 @@ a.navbar-link button {

.navbar-mobile-btn {
margin: 0.3em;
display: flex;
justify-content: center;
align-items: center;
}

.navbar-mobile-btn .light-theme-img,
.navbar-mobile-btn .dark-theme-img {
--btn-image-dim: 2em;
display: none;
width: var(--btn-image-dim);
height: var(--btn-image-dim);
}

.dark-theme .navbar-mobile-btn .dark-theme-img {
display: block;
}

.light-theme .navbar-mobile-btn .light-theme-img {
display: block;
}

.navbar-dialog {
position: relative;

&::backdrop {
background-color: transparent;
}

& .dialog-container {
flex: 1;
}

& .left-navbar-mobile-overlay {
position: fixed;
left: -65%;
animation-duration: 400ms;
animation-fill-mode: forwards;
max-height: 100vh;
overflow-y: auto;
z-index: 2;

& > *:last-child {
/*
* On mobile the button bar can cover the bottom of the scrollable element
* add a padding to it so even when that happens the content appears within the view
*/
padding-bottom: 65px;
}
}

& .navbar-overlay-backdrop {
min-width: 100vw;
min-height: 100vh;
position: fixed;
left: 0;
top: 0;
opacity: 0;
animation-duration: 400ms;
animation-fill-mode: forwards;
}

&[open] {
display: flex;
& .left-navbar-mobile-overlay {
animation-name: slideIn;
}
& .navbar-overlay-backdrop {
animation-name: fadein;
}
}

& .theme-switcher {
padding-inline: 0.35em;
}
}

@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

@keyframes slideIn {
from {
left: -75%;
}
to {
left: 0%;
}
}
9 changes: 7 additions & 2 deletions src/components/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { cls } from "../utils/cls";
import { url } from "../utils/url";
import { FontSizeSelector } from "./font-size-selector";
import { ThemeSwitcher } from "./theme-switcher";
import HamburgerDark from "../assets/burger-dark.svg";
import HamburgerLight from "../assets/burger-light.svg";

const NavbarLink = (props: {
href: string;
Expand Down Expand Up @@ -162,8 +164,11 @@ const LeftNavbarDesktop = (props: { activePage?: string }) => {
const LeftNavbarMobile = (props: { activePage?: string }) => {
return (
<div class="left-navbar-mobile column">
<button class="btn square navbar-mobile-btn">M</button>
<dialog>
<button class="btn square flat navbar-mobile-btn">
<HamburgerDark class="light-theme-img" />
<HamburgerLight class="dark-theme-img" />
</button>
<dialog class="navbar-dialog">
<div class="dialog-container flexbox">
<div class="left-navbar-mobile-overlay box bg-2 column">
<ThemeSwitcher />
Expand Down
2 changes: 1 addition & 1 deletion src/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export function Layout(
</div>
</div>
{__DEV__ && (
<script>
<script defer>
{
/* js */ `
let timeout;
Expand Down
6 changes: 6 additions & 0 deletions src/svg.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare module "*.svg" {
const svgimg: (
props: Omit<JSX.IntrinsicElements["img"], "src">,
) => JSX.Element;
export default svgimg;
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,11 @@ cross-spawn@^7.0.0:
shebang-command "^2.0.0"
which "^2.0.1"

dedent@^1.5.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a"
integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==

dprint@^0.46.1:
version "0.46.1"
resolved "https://registry.yarnpkg.com/dprint/-/dprint-0.46.1.tgz#4678e59f7a27993a7c48ac18c715ce6121ab7c43"
Expand Down

0 comments on commit ec838f3

Please sign in to comment.