quickshell-web/src/components/navigation/sidebars/toc/index.tsx
2026-04-02 23:13:53 -07:00

116 lines
3 KiB
TypeScript

import {
createEffect,
createSignal,
onMount,
onCleanup,
type Component,
} from "solid-js";
import { Article, MenuToX } from "@icons";
import { Table } from "./Table";
import type { TOCProps } from "../types";
import { buildHierarchy } from "@config/io/helpers";
const TableOfContents: Component<TOCProps> = props => {
const [open, setOpen] = createSignal<boolean>(false);
const [clientWidth, setClientWidth] = createSignal<number>(0);
const [screenWidth, setScreenWidth] = createSignal<number>(0);
const { mobile, title, config, type } = props;
let tocRef: HTMLDivElement;
function toggle(e: MouseEvent) {
e.preventDefault();
setOpen(!open());
}
if (!mobile) {
return type ? (
<Table typeTOC={type} />
) : (
<Table
title={title}
configTOC={buildHierarchy(config!)}
/>
);
}
const handleClickOutside = (event: MouseEvent) => {
const isLink = "href" in (event.target || {});
const isInBody = document.body.contains(event.target as Node);
if (
isLink ||
!isInBody ||
//@ts-expect-error
(isInBody && !tocRef.contains(event.target as Node))
) {
setOpen(false);
}
};
onMount(() => {
setClientWidth(document.body.clientWidth);
setScreenWidth(window.outerWidth);
onCleanup(() => {
window.removeEventListener("click", handleClickOutside);
});
});
createEffect(() => {
if (clientWidth() !== document.body.clientWidth) {
setClientWidth(document.body.clientWidth);
}
if (screenWidth() !== window.outerWidth) {
setScreenWidth(window.outerWidth);
}
if (open()) {
window.addEventListener("click", handleClickOutside);
document.body.classList.add("overflow-toc");
document.body.classList.add("dim-content-toc");
// onsetter
const header = document.getElementsByClassName(
"header"
)[0]! as HTMLElement;
const bodyOffset = screenWidth() - clientWidth();
document.body.style.width = `${screenWidth() - bodyOffset}px`;
header.style.width = `${screenWidth() - bodyOffset}px`;
} else {
window.removeEventListener("click", handleClickOutside);
document.body.classList.remove("overflow-toc");
document.body.classList.remove("dim-content-toc");
// offsetter
const header = document.getElementsByClassName(
"header"
)[0]! as HTMLElement;
document.body.style.width = "";
header.style.width = "";
}
});
return (
<div
class="toc-toggle"
ref={tocRef!}
id="toc-toggle"
>
<div onclick={e => toggle(e)}>
<Article class={`toc-icon ${!open() ? "active" : ""}`} />
<MenuToX class={`toc-icon ${open() ? "active" : ""}`} />
</div>
<div class={`toc-mobile ${open() ? "shown" : ""}`}>
{type ? (
<Table typeTOC={type} />
) : (
<Table
title={title}
configTOC={buildHierarchy(config!)}
/>
)}
</div>
</div>
);
};
export default TableOfContents;