2022-03-28 08:43:25 +00:00
|
|
|
import { useRouter } from "next/router";
|
2022-03-12 12:23:09 +00:00
|
|
|
import { slugify } from "queries/helpers";
|
|
|
|
import { preprocessMarkDawn } from "./Markdawn";
|
|
|
|
|
|
|
|
type TOCProps = {
|
|
|
|
text: string;
|
|
|
|
title?: string;
|
|
|
|
};
|
|
|
|
|
2022-03-27 15:01:14 +00:00
|
|
|
export default function TOCComponent(props: TOCProps): JSX.Element {
|
2022-03-28 08:43:25 +00:00
|
|
|
const { text, title } = props;
|
2022-03-12 17:00:19 +00:00
|
|
|
const toc = getTocFromMarkdawn(preprocessMarkDawn(text), title);
|
2022-03-28 08:43:25 +00:00
|
|
|
const router = useRouter();
|
2022-03-12 12:23:09 +00:00
|
|
|
|
|
|
|
return (
|
2022-03-14 11:31:39 +00:00
|
|
|
<>
|
2022-03-12 12:23:09 +00:00
|
|
|
<h3 className="text-xl">Table of content</h3>
|
2022-03-14 11:31:39 +00:00
|
|
|
<div className="text-left max-w-[14.5rem]">
|
|
|
|
<p className="my-2 overflow-x-hidden relative text-ellipsis whitespace-nowrap text-left">
|
2022-03-27 15:01:14 +00:00
|
|
|
<a className="" onClick={async () => router.replace(`#${toc.slug}`)}>
|
2022-03-12 12:23:09 +00:00
|
|
|
{<abbr title={toc.title}>{toc.title}</abbr>}
|
|
|
|
</a>
|
2022-03-14 11:31:39 +00:00
|
|
|
</p>
|
2022-03-28 08:43:25 +00:00
|
|
|
<TOCLevel tocchildren={toc.children} parentNumbering="" />
|
2022-03-14 11:31:39 +00:00
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
type TOCLevelProps = {
|
|
|
|
tocchildren: TOC[];
|
|
|
|
parentNumbering: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
function TOCLevel(props: TOCLevelProps): JSX.Element {
|
2022-03-28 08:43:25 +00:00
|
|
|
const router = useRouter();
|
|
|
|
const { tocchildren, parentNumbering } = props;
|
2022-03-14 11:31:39 +00:00
|
|
|
return (
|
|
|
|
<ol className="pl-4 text-left">
|
|
|
|
{tocchildren.map((child, childIndex) => (
|
|
|
|
<>
|
|
|
|
<li
|
|
|
|
key={child.slug}
|
|
|
|
className="my-2 overflow-x-hidden w-full text-ellipsis whitespace-nowrap"
|
|
|
|
>
|
|
|
|
<span className="text-dark">{`${parentNumbering}${
|
|
|
|
childIndex + 1
|
2022-03-19 16:35:17 +00:00
|
|
|
}.`}</span>{" "}
|
2022-03-27 15:01:14 +00:00
|
|
|
<a onClick={async () => router.replace(`#${child.slug}`)}>
|
2022-03-14 11:31:39 +00:00
|
|
|
{<abbr title={child.title}>{child.title}</abbr>}
|
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<TOCLevel
|
|
|
|
tocchildren={child.children}
|
|
|
|
parentNumbering={`${parentNumbering}${childIndex + 1}.`}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
))}
|
|
|
|
</ol>
|
2022-03-12 12:23:09 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export type TOC = {
|
|
|
|
title: string;
|
|
|
|
slug: string;
|
|
|
|
children: TOC[];
|
|
|
|
};
|
|
|
|
|
|
|
|
export function getTocFromMarkdawn(text: string, title?: string): TOC {
|
2022-03-27 15:01:14 +00:00
|
|
|
const toc: TOC = {
|
|
|
|
title: title ?? "Return to top",
|
|
|
|
slug: slugify(title) ?? "",
|
|
|
|
children: [],
|
|
|
|
};
|
2022-03-12 12:23:09 +00:00
|
|
|
let h2 = -1;
|
|
|
|
let h3 = -1;
|
|
|
|
let h4 = -1;
|
|
|
|
let h5 = -1;
|
|
|
|
let scenebreak = 0;
|
|
|
|
let scenebreakIndex = 0;
|
2022-03-14 11:31:39 +00:00
|
|
|
|
|
|
|
function getTitle(line: string): string {
|
|
|
|
return line.slice(line.indexOf(`">`) + 2, line.indexOf("</"));
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSlug(line: string): string {
|
|
|
|
return line.slice(line.indexOf(`id="`) + 4, line.indexOf(`">`));
|
|
|
|
}
|
|
|
|
|
2022-03-12 12:23:09 +00:00
|
|
|
text.split("\n").map((line) => {
|
2022-03-14 11:31:39 +00:00
|
|
|
if (line.startsWith("<h1 id=")) {
|
|
|
|
toc.title = getTitle(line);
|
|
|
|
toc.slug = getSlug(line);
|
|
|
|
} else if (line.startsWith("<h2 id=")) {
|
2022-03-12 12:23:09 +00:00
|
|
|
toc.children.push({
|
2022-03-14 11:31:39 +00:00
|
|
|
title: getTitle(line),
|
|
|
|
slug: getSlug(line),
|
2022-03-12 12:23:09 +00:00
|
|
|
children: [],
|
|
|
|
});
|
2022-03-27 15:01:14 +00:00
|
|
|
h2 += 1;
|
2022-03-12 12:23:09 +00:00
|
|
|
h3 = -1;
|
|
|
|
h4 = -1;
|
|
|
|
h5 = -1;
|
|
|
|
scenebreak = 0;
|
2022-03-14 11:31:39 +00:00
|
|
|
} else if (line.startsWith("<h3 id=")) {
|
2022-03-12 12:23:09 +00:00
|
|
|
toc.children[h2].children.push({
|
2022-03-14 11:31:39 +00:00
|
|
|
title: getTitle(line),
|
|
|
|
slug: getSlug(line),
|
2022-03-12 12:23:09 +00:00
|
|
|
children: [],
|
|
|
|
});
|
2022-03-27 15:01:14 +00:00
|
|
|
h3 += 1;
|
2022-03-12 12:23:09 +00:00
|
|
|
h4 = -1;
|
|
|
|
h5 = -1;
|
|
|
|
scenebreak = 0;
|
2022-03-14 11:31:39 +00:00
|
|
|
} else if (line.startsWith("<h4 id=")) {
|
2022-03-12 12:23:09 +00:00
|
|
|
toc.children[h2].children[h3].children.push({
|
2022-03-14 11:31:39 +00:00
|
|
|
title: getTitle(line),
|
|
|
|
slug: getSlug(line),
|
2022-03-12 12:23:09 +00:00
|
|
|
children: [],
|
|
|
|
});
|
2022-03-27 15:01:14 +00:00
|
|
|
h4 += 1;
|
2022-03-12 12:23:09 +00:00
|
|
|
h5 = -1;
|
|
|
|
scenebreak = 0;
|
2022-03-14 11:31:39 +00:00
|
|
|
} else if (line.startsWith("<h5 id=")) {
|
2022-03-12 12:23:09 +00:00
|
|
|
toc.children[h2].children[h3].children[h4].children.push({
|
2022-03-14 11:31:39 +00:00
|
|
|
title: getTitle(line),
|
|
|
|
slug: getSlug(line),
|
2022-03-12 12:23:09 +00:00
|
|
|
children: [],
|
|
|
|
});
|
2022-03-27 15:01:14 +00:00
|
|
|
h5 += 1;
|
2022-03-12 12:23:09 +00:00
|
|
|
scenebreak = 0;
|
2022-03-14 11:31:39 +00:00
|
|
|
} else if (line.startsWith("<h6 id=")) {
|
2022-03-12 12:23:09 +00:00
|
|
|
toc.children[h2].children[h3].children[h4].children[h5].children.push({
|
2022-03-14 11:31:39 +00:00
|
|
|
title: getTitle(line),
|
|
|
|
slug: getSlug(line),
|
2022-03-12 12:23:09 +00:00
|
|
|
children: [],
|
|
|
|
});
|
|
|
|
} else if (line.startsWith(`<SceneBreak`)) {
|
2022-03-27 15:01:14 +00:00
|
|
|
scenebreak += 1;
|
|
|
|
scenebreakIndex += 1;
|
2022-03-14 11:31:39 +00:00
|
|
|
|
|
|
|
const child = {
|
|
|
|
title: `Scene break ${scenebreak}`,
|
|
|
|
slug: slugify(`scene-break-${scenebreakIndex}`),
|
|
|
|
children: [],
|
|
|
|
};
|
|
|
|
|
2022-03-12 12:23:09 +00:00
|
|
|
if (h5 >= 0) {
|
2022-03-14 11:31:39 +00:00
|
|
|
toc.children[h2].children[h3].children[h4].children[h5].children.push(
|
|
|
|
child
|
|
|
|
);
|
2022-03-12 12:23:09 +00:00
|
|
|
} else if (h4 >= 0) {
|
2022-03-14 11:31:39 +00:00
|
|
|
toc.children[h2].children[h3].children[h4].children.push(child);
|
2022-03-12 12:23:09 +00:00
|
|
|
} else if (h3 >= 0) {
|
2022-03-14 11:31:39 +00:00
|
|
|
toc.children[h2].children[h3].children.push(child);
|
2022-03-12 12:23:09 +00:00
|
|
|
} else if (h2 >= 0) {
|
2022-03-14 11:31:39 +00:00
|
|
|
toc.children[h2].children.push(child);
|
2022-03-12 12:23:09 +00:00
|
|
|
} else {
|
2022-03-14 11:31:39 +00:00
|
|
|
toc.children.push(child);
|
2022-03-12 12:23:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2022-03-14 11:31:39 +00:00
|
|
|
|
2022-03-12 12:23:09 +00:00
|
|
|
return toc;
|
|
|
|
}
|