feat: change sidebar to use dropdown rather than tabs

This commit is contained in:
DuroCodes
2024-05-24 16:52:21 -04:00
parent e5efba21db
commit 8b177a8f40

View File

@@ -1,9 +1,10 @@
---
// Heavily inspired from https://github.com/lorenzolewis/starlight-multi-sidebar/blob/main/src/components/Sidebar.astro
// Heavily inspired from starlight-utils (https://github.com/lorenzolewis/starlight-utils) — adapted so we can disable for blog pages
import type { Props } from "@astrojs/starlight/props";
import StarlightSidebar from "@astrojs/starlight/components/Sidebar.astro";
import BlogSidebar from "starlight-blog/overrides/Sidebar.astro";
import { AstroError } from "astro/errors";
import { Icon } from "@astrojs/starlight/components";
const sidebarConfig: [string, boolean, Props][] = Astro.props.sidebar.map(
(s) => {
@@ -13,7 +14,7 @@ const sidebarConfig: [string, boolean, Props][] = Astro.props.sidebar.map(
Each top-level \`sidebar\` item in the Starlight config must be either a group or autogenerated.
See https://starlight.astro.build/guides/sidebar/#groups and https://starlight.astro.build/guides/sidebar/#autogenerated-groups`
See https://starlight.astro.build/guides/sidebar/#groups and https://starlight.astro.build/guides/sidebar/#autogenerated-groups`,
);
}
@@ -21,7 +22,7 @@ const sidebarConfig: [string, boolean, Props][] = Astro.props.sidebar.map(
s.type === "link" ? s.isCurrent : s.entries.some(isCurrent);
return [s.label, isCurrent(s), { ...Astro.props, sidebar: [...s.entries] }];
}
},
);
const isBlog = Astro.url.pathname.split("/")[1] === "blog";
@@ -33,67 +34,102 @@ const isBlog = Astro.url.pathname.split("/")[1] === "blog";
<slot />
</BlogSidebar>
) : (
<div class="__collapse">
<starlight-multi-sidebar-select>
<label>
<span class="sr-only">
{Astro.props.labels["menuButton.accessibleLabel"]}
</span>
<select>
{sidebarConfig.map(([label, isCurrent]) => (
<option value={label} selected={isCurrent} set:html={label} />
))}
</select>
<Icon name="down-caret" class="icon" />
</label>
{sidebarConfig.map(([label, isCurrent, props]) => (
<>
<input
type="radio"
name="sidebar"
role="tab"
aria-label={label}
checked={isCurrent}
/>
<div class="__collapse-content">
<StarlightSidebar {...props}>
<slot />
</StarlightSidebar>
</div>
</>
<div hidden={!isCurrent} data-starlight-multi-sidebar-label={label}>
<StarlightSidebar {...props} />
</div>
))}
</div>
</starlight-multi-sidebar-select>
)
}
<script>
class StarlightMultiSidebarSelect extends HTMLElement {
constructor() {
super();
const select = this.querySelector("select");
if (select) {
select.addEventListener("change", (e) => {
if (e.currentTarget instanceof HTMLSelectElement) {
const sidebarEntries = this.querySelectorAll(
`[data-starlight-multi-sidebar-label]`,
);
sidebarEntries.forEach((entry) => {
if (entry instanceof HTMLDivElement) {
if (
entry.dataset["starlightMultiSidebarLabel"] === select.value
) {
entry.hidden = false;
} else {
entry.hidden = true;
}
}
});
}
});
}
}
}
customElements.define(
"starlight-multi-sidebar-select",
StarlightMultiSidebarSelect,
);
</script>
<style>
.__collapse {
display: grid;
}
.__collapse > input {
label {
--sl-caret-size: 1.25rem;
position: relative;
display: inline-flex;
grid-row-start: 1;
appearance: none;
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--sl-color-gray-1);
}
border-radius: 0.25rem;
padding: 0.2em 0.5rem;
line-height: 1.4;
font-size: var(--sl-text-lg);
font-weight: 600;
cursor: pointer;
user-select: none;
margin-bottom: var(--sl-nav-pad-y);
label:hover {
color: var(--sl-color-gray-2);
}
label:hover > select {
border-color: var(--sl-color-gray-5);
}
.icon {
position: absolute;
top: 0;
transform: translateY(50%);
pointer-events: none;
font-size: var(--sl-caret-size);
inset-inline-end: 0.5rem;
}
select {
background-color: var(--sl-color-bg-nav);
box-shadow: var(--sl-shadow-sm);
border: 1px solid var(--sl-color-hairline-light);
padding-block: 0.3rem;
margin-bottom: 0.625rem;
padding-inline: 0.5rem calc(var(--sl-caret-size) + 0.5rem);
width: 100%;
min-height: fit-content;
text-overflow: ellipsis;
color: inherit;
cursor: pointer;
appearance: none;
}
.__collapse > input::after {
content: attr(aria-label);
}
.__collapse > input:checked {
color: var(--sl-color-text-invert);
background-color: var(--sl-color-text-accent);
}
.__collapse > .__collapse-content {
display: none;
grid-column-start: 1;
grid-column-end: span 999;
grid-row-start: 2;
}
.__collapse > input:checked + .__collapse-content {
display: block;
option {
background-color: var(--sl-color-bg-nav);
color: var(--sl-color-gray-1);
}
</style>