116 lines
3 KiB
TypeScript
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;
|