chore(toc): reintroduce basic collapse support

This commit is contained in:
Elian Doran 2025-12-18 10:49:33 +02:00
parent 87a98201b4
commit 73f1b91d34
No known key found for this signature in database
3 changed files with 120 additions and 10 deletions

View File

@ -1,8 +1,16 @@
interface IconProps {
import clsx from "clsx";
import { HTMLAttributes } from "preact";
interface IconProps extends Pick<HTMLAttributes<HTMLSpanElement>, "className" | "onClick"> {
icon?: string;
className?: string;
}
export default function Icon({ icon, className }: IconProps) {
return <span class={`${icon ?? "bx bx-empty"} ${className ?? ""}`}></span>
}
export default function Icon({ icon, className, ...restProps }: IconProps) {
return (
<span
class={clsx(icon ?? "bx bx-empty", className)}
{...restProps}
/>
);
}

View File

@ -0,0 +1,82 @@
.toc ol {
position: relative;
overflow: hidden;
padding-inline-start: 0px;
transition: max-height 0.3s ease;
}
.toc li.collapsed + ol {
display:none;
}
.toc li + ol:before {
content: "";
position: absolute;
height: 100%;
border-inline-start: 1px solid var(--main-border-color);
z-index: 10;
}
.toc li {
display: flex;
position: relative;
list-style: none;
align-items: center;
padding-inline-start: 7px;
cursor: pointer;
text-align: justify;
word-wrap: break-word;
hyphens: auto;
}
.toc > ol {
--toc-depth-level: 1;
}
.toc > ol > ol {
--toc-depth-level: 2;
}
.toc > ol > ol > ol {
--toc-depth-level: 3;
}
.toc > ol > ol > ol > ol {
--toc-depth-level: 4;
}
.toc > ol > ol > ol > ol > ol {
--toc-depth-level: 5;
}
.toc > ol ol::before {
inset-inline-start: calc((var(--toc-depth-level) - 2) * 20px + 14px);
}
.toc li {
padding-inline-start: calc((var(--toc-depth-level) - 1) * 20px + 4px);
}
.toc li .collapse-button {
display: flex;
position: relative;
width: 21px;
height: 21px;
flex-shrink: 0;
align-items: center;
justify-content: center;
transition: transform 0.3s ease;
}
.toc li.collapsed .collapse-button {
transform: rotate(-90deg);
}
.toc li .item-content {
margin-inline-start: 25px;
flex: 1;
}
.toc li .collapse-button + .item-content {
margin-inline-start: 4px;
}
.toc li:hover {
font-weight: bold;
}

View File

@ -1,8 +1,12 @@
import "./TableOfContents.css";
import { CKTextEditor, ModelElement } from "@triliumnext/ckeditor5";
import clsx from "clsx";
import { useEffect, useState } from "preact/hooks";
import { t } from "../../services/i18n";
import { useActiveNoteContext, useIsNoteReadOnly, useNoteProperty, useTextEditor } from "../react/hooks";
import Icon from "../react/Icon";
import RightPanelWidget from "./RightPanelWidget";
interface RawHeading {
@ -66,19 +70,35 @@ function AbstractTableOfContents({ headings }: {
headings: RawHeading[];
}) {
const nestedHeadings = buildHeadingTree(headings);
return nestedHeadings.map(heading => <TableOfContentsHeading heading={heading} />);
return (
<span className="toc">
<ol>
{nestedHeadings.map(heading => <TableOfContentsHeading heading={heading} />)}
</ol>
</span>
);
}
function TableOfContentsHeading({ heading }: { heading: HeadingsWithNesting }) {
const [ collapsed, setCollapsed ] = useState(false);
return (
<li>
<span className="title">{heading.text}</span>
<>
<li className={clsx(collapsed && "collapsed")}>
{heading.children.length > 0 && (
<Icon
className="collapse-button"
icon="bx bx-chevron-down"
onClick={() => setCollapsed(!collapsed)}
/>
)}
<span className="item-content">{heading.text}</span>
</li>
{heading.children && (
<ul>
<ol>
{heading.children.map(heading => <TableOfContentsHeading heading={heading} />)}
</ul>
</ol>
)}
</li>
</>
);
}