205 lines
5.4 KiB
Plaintext
205 lines
5.4 KiB
Plaintext
---
|
|
import TableOfContentsList from "./TableOfContentsList.astro";
|
|
|
|
const parseFlag = (value) => {
|
|
if (typeof value === "boolean") return value;
|
|
if (typeof value === "string") {
|
|
const normalized = value.trim().toLowerCase();
|
|
if (["true", "1", "yes", "y", "on"].includes(normalized)) return true;
|
|
if (["false", "0", "no", "n", "off"].includes(normalized)) return false;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const route = Astro.locals.starlightRoute ?? {};
|
|
const { toc } = route;
|
|
const entryData = route.entry?.data ?? {};
|
|
const frontmatter = entryData.frontmatter ?? entryData;
|
|
const enableNumbering = parseFlag(frontmatter.numberedHeadings);
|
|
const filteredItems = (toc?.items ?? []).filter((item) => item.slug !== "_top");
|
|
const title = Astro.locals.t("tableOfContents.onThisPage");
|
|
---
|
|
|
|
{
|
|
filteredItems.length > 0 && (
|
|
<mobile-starlight-toc data-min-h={toc?.minHeadingLevel} data-max-h={toc?.maxHeadingLevel}>
|
|
<nav aria-labelledby="starlight__on-this-page--mobile">
|
|
<details id="starlight__mobile-toc">
|
|
<summary id="starlight__on-this-page--mobile" class="sl-flex">
|
|
<div class="toggle sl-flex">
|
|
<a href="#_top">{title}</a>
|
|
<svg
|
|
aria-hidden="true"
|
|
class="caret"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="currentColor"
|
|
style="--sl-icon-size: 1rem;"
|
|
>
|
|
<path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z" />
|
|
</svg>
|
|
</div>
|
|
<span class="display-current" />
|
|
</summary>
|
|
<div class="dropdown">
|
|
<TableOfContentsList toc={filteredItems} isMobile enableNumbering={enableNumbering} />
|
|
</div>
|
|
</details>
|
|
</nav>
|
|
</mobile-starlight-toc>
|
|
)
|
|
}
|
|
|
|
<style>
|
|
@layer starlight.core {
|
|
:global(mobile-starlight-toc) {
|
|
display: block !important;
|
|
visibility: visible !important;
|
|
position: relative;
|
|
}
|
|
|
|
nav {
|
|
position: fixed;
|
|
z-index: var(--sl-z-index-toc);
|
|
top: calc(var(--sl-nav-height) - 1px);
|
|
inset-inline: 0;
|
|
border-top: 1px solid var(--sl-color-gray-5);
|
|
background-color: var(--sl-color-bg-nav);
|
|
display: block !important;
|
|
}
|
|
@media (min-width: 50rem) {
|
|
nav {
|
|
inset-inline-start: var(--sl-content-inline-start, 0);
|
|
}
|
|
}
|
|
:global(.sidebar-collapsed) nav {
|
|
inset-inline-start: 11px !important;
|
|
inset-inline-end: 2px !important;
|
|
}
|
|
@media (min-width: 72rem) {
|
|
nav {
|
|
display: none !important;
|
|
}
|
|
}
|
|
@media (max-width: 87.499rem) {
|
|
:global([data-has-toc] mobile-starlight-toc nav) {
|
|
display: block !important;
|
|
visibility: visible !important;
|
|
opacity: 1 !important;
|
|
}
|
|
}
|
|
|
|
summary {
|
|
gap: 0.5rem;
|
|
align-items: center;
|
|
height: var(--sl-mobile-toc-height);
|
|
border-bottom: 1px solid var(--sl-color-hairline-shade);
|
|
padding: 0.5rem 1rem;
|
|
font-size: var(--sl-text-xs);
|
|
outline-offset: var(--sl-outline-offset-inside);
|
|
}
|
|
summary::marker,
|
|
summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
|
|
.toggle {
|
|
flex-shrink: 0;
|
|
gap: 1rem;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
border: 1px solid var(--sl-color-gray-5);
|
|
border-radius: 0.5rem;
|
|
padding-block: 0.5rem;
|
|
padding-inline-start: 0.75rem;
|
|
padding-inline-end: 0.5rem;
|
|
line-height: 1;
|
|
background-color: var(--sl-color-black);
|
|
user-select: none;
|
|
cursor: pointer;
|
|
}
|
|
.toggle a {
|
|
color: inherit;
|
|
text-decoration: none;
|
|
}
|
|
.toggle a:hover,
|
|
.toggle a:focus-visible {
|
|
text-decoration: underline;
|
|
}
|
|
details[open] .toggle {
|
|
color: var(--sl-color-white);
|
|
border-color: var(--sl-color-accent);
|
|
}
|
|
details .toggle:hover {
|
|
color: var(--sl-color-white);
|
|
border-color: var(--sl-color-gray-2);
|
|
}
|
|
|
|
:global([dir="rtl"]) .caret {
|
|
transform: rotateZ(180deg);
|
|
}
|
|
details[open] .caret {
|
|
transform: rotateZ(90deg);
|
|
}
|
|
|
|
.display-current {
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
overflow: hidden;
|
|
color: var(--sl-color-white);
|
|
}
|
|
|
|
.dropdown {
|
|
--border-top: 1px;
|
|
margin-top: calc(-1 * var(--border-top));
|
|
border: var(--border-top) solid var(--sl-color-gray-6);
|
|
border-top-color: var(--sl-color-hairline-shade);
|
|
max-height: calc(85vh - var(--sl-nav-height) - var(--sl-mobile-toc-height));
|
|
overflow-y: auto;
|
|
background-color: var(--sl-color-black);
|
|
box-shadow: var(--sl-shadow-md);
|
|
overscroll-behavior: contain;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
import { StarlightTOC } from "./starlight-toc";
|
|
|
|
class MobileStarlightTOC extends StarlightTOC {
|
|
override set current(link) {
|
|
super.current = link;
|
|
const display = this.querySelector(".display-current");
|
|
if (display) display.textContent = link.textContent;
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
const details = this.querySelector("details");
|
|
if (!details) return;
|
|
const closeToC = () => {
|
|
details.open = false;
|
|
};
|
|
details.querySelectorAll("a").forEach((a) => {
|
|
a.addEventListener("click", closeToC);
|
|
});
|
|
window.addEventListener("click", (e) => {
|
|
if (!details.contains(e.target)) closeToC();
|
|
});
|
|
window.addEventListener("keydown", (e) => {
|
|
if (e.key === "Escape" && details.open) {
|
|
const hasFocus = details.contains(document.activeElement);
|
|
closeToC();
|
|
if (hasFocus) {
|
|
const summary = details.querySelector("summary");
|
|
if (summary) summary.focus();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
customElements.define("mobile-starlight-toc", MobileStarlightTOC);
|
|
</script>
|