Skip to content

Instantly share code, notes, and snippets.

@kitschpatrol
Last active November 20, 2023 19:37
Show Gist options
  • Save kitschpatrol/a517d90386c0ebc13b465c6a7ef662fa to your computer and use it in GitHub Desktop.
Save kitschpatrol/a517d90386c0ebc13b465c6a7ef662fa to your computer and use it in GitHub Desktop.
Starlight Sidebar State Persistence
---
import type { Props } from '../props';
import { MobileMenuFooter } from 'virtual:starlight/components';
import SidebarSublist from './SidebarSublist.astro';
const { sidebar } = Astro.props;
---
<SidebarSublist sublist={sidebar} />
<div class="md:sl-hidden">
<MobileMenuFooter {...Astro.props} />
</div>
{/* This is intentionally inlined to avoid FOUSC. */}
<script is:inline>
(() => {
const sidebarContent = document.getElementById('starlight__sidebar')?.firstElementChild;
if (sidebarContent) {
// open ancestor details of the active item
const activeLinkDetailElements = [
...sidebarContent.querySelectorAll('details:has(a[aria-current="page"])')
];
activeLinkDetailElements.forEach((detail) => {
detail.setAttribute('open', '');
});
const detailElements = [...sidebarContent.querySelectorAll('details')];
// Restore state from previous navigation
// (but keep the active item's ancestors open regardless)
const key = 'sl-sidebar-state';
const savedStateJson = sessionStorage.getItem(key);
if (savedStateJson) {
let savedState;
try {
savedState = JSON.parse(savedStateJson);
} catch (e) {
console.error('Error parsing saved position:', e);
}
if (savedState) {
savedState.details.forEach((isOpen, i) => {
if (isOpen) {
detailElements[i].setAttribute('open', '');
} else if (!activeLinkDetailElements.includes(detailElements[i])) {
detailElements[i].removeAttribute('open');
}
});
sidebarContent.scrollTop = savedState.scrollTop;
}
}
// Scroll the active list item into view if necessary
const activeLi = sidebarContent.querySelector('a[aria-current="page"]').parentElement;
if (activeLi) {
const sidebarRect = sidebarContent.getBoundingClientRect();
const liRect = activeLi.getBoundingClientRect();
if (liRect.top < sidebarRect.top || liRect.bottom > sidebarRect.bottom) {
activeLi.scrollIntoView({ behavior: 'instant', block: 'nearest' });
}
}
// Save user state before navigating
window.addEventListener('beforeunload', () => {
sessionStorage.setItem(
key,
JSON.stringify({
details: detailElements.map((detail) => detail.hasAttribute('open')),
scrollTop: sidebarContent.scrollTop
})
);
});
}
})();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment