From 756e85e8e814657c42c4a6f9c299b5bef32aee22 Mon Sep 17 00:00:00 2001
From: Chris Swithinbank
Date: Sat, 7 Sep 2024 00:18:54 +0200
Subject: Refactor sidebar persistence logic for better slow device performance
(#2242)
Co-authored-by: HiDeoo <494699+HiDeoo@users.noreply.github.com>
---
.changeset/heavy-forks-help.md | 5 ++
packages/starlight/components/Sidebar.astro | 38 ++----------
.../starlight/components/SidebarPersistState.ts | 6 +-
.../starlight/components/SidebarPersister.astro | 72 ++++++++++++++++++++++
.../starlight/components/SidebarRestorePoint.astro | 12 ++++
packages/starlight/components/SidebarSublist.astro | 2 +
6 files changed, 98 insertions(+), 37 deletions(-)
create mode 100644 .changeset/heavy-forks-help.md
create mode 100644 packages/starlight/components/SidebarPersister.astro
create mode 100644 packages/starlight/components/SidebarRestorePoint.astro
diff --git a/.changeset/heavy-forks-help.md b/.changeset/heavy-forks-help.md
new file mode 100644
index 00000000..17f017ff
--- /dev/null
+++ b/.changeset/heavy-forks-help.md
@@ -0,0 +1,5 @@
+---
+"@astrojs/starlight": patch
+---
+
+Refactors the logic for persisting and restoring sidebar state across navigations for better performance on slow or busy devices
diff --git a/packages/starlight/components/Sidebar.astro b/packages/starlight/components/Sidebar.astro
index c47d0315..1dd4c047 100644
--- a/packages/starlight/components/Sidebar.astro
+++ b/packages/starlight/components/Sidebar.astro
@@ -2,46 +2,16 @@
import type { Props } from '../props';
import MobileMenuFooter from 'virtual:starlight/components/MobileMenuFooter';
-import { getSidebarHash } from '../utils/navigation';
+import SidebarPersister from './SidebarPersister.astro';
import SidebarSublist from './SidebarSublist.astro';
const { sidebar } = Astro.props;
-const hash = getSidebarHash(sidebar);
---
-
+
-
+
+
-
-{
- /*
- Inline script to restore sidebar state as soon as possible.
- - On smaller viewports, restoring state is skipped as the sidebar is collapsed inside a menu.
- - The state is parsed from session storage and restored.
- - This is a progressive enhancement, so any errors are swallowed silently.
- */
-}
-
-
diff --git a/packages/starlight/components/SidebarPersistState.ts b/packages/starlight/components/SidebarPersistState.ts
index 029dd95c..4c4513ef 100644
--- a/packages/starlight/components/SidebarPersistState.ts
+++ b/packages/starlight/components/SidebarPersistState.ts
@@ -1,7 +1,6 @@
// Collect required elements from the DOM.
const scroller = document.getElementById('starlight__sidebar');
const target = scroller?.querySelector('sl-sidebar-state-persist');
-const details = [...(target?.querySelectorAll('details') || [])];
/** Starlight uses this key to store sidebar state in `sessionStorage`. */
const storageKey = 'sl-sidebar-state';
@@ -58,8 +57,9 @@ target?.addEventListener('click', (event) => {
// This excludes clicks outside of the ``, which don’t trigger toggles.
const toggledDetails = event.target.closest('summary')?.closest('details');
if (!toggledDetails) return;
- const index = details.indexOf(toggledDetails);
- if (index === -1) return;
+ const restoreElement = toggledDetails.querySelector('sl-sidebar-restore');
+ const index = parseInt(restoreElement?.dataset.index || '');
+ if (isNaN(index)) return;
setToggleState(!toggledDetails.open, index);
});
diff --git a/packages/starlight/components/SidebarPersister.astro b/packages/starlight/components/SidebarPersister.astro
new file mode 100644
index 00000000..ae485c97
--- /dev/null
+++ b/packages/starlight/components/SidebarPersister.astro
@@ -0,0 +1,72 @@
+---
+/*
+ This component is designed to wrap the tree of `` components in the sidebar.
+
+ It does the following:
+ - Wraps the tree in an `` custom element
+ - Before the tree renders, adds an inline script which loads state and defines
+ the behaviour for the `` custom element.
+ - After the tree renders, adds an inline script which restores the sidebar scroll state.
+
+ Notes:
+ - On smaller viewports, restoring state is skipped as the sidebar is collapsed inside a menu.
+ - The state is parsed from session storage and restored.
+ - This is a progressive enhancement, so any errors are swallowed silently.
+*/
+
+import type { Props } from '../props';
+import { getSidebarHash } from '../utils/navigation';
+
+const hash = getSidebarHash(Astro.props.sidebar);
+
+declare global {
+ interface Window {
+ /** Restored scroll position. Briefly stored on the `window` global to pass between inline scripts. */
+ _starlightScrollRestore?: number;
+ }
+}
+---
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/starlight/components/SidebarRestorePoint.astro b/packages/starlight/components/SidebarRestorePoint.astro
new file mode 100644
index 00000000..d3d96939
--- /dev/null
+++ b/packages/starlight/components/SidebarRestorePoint.astro
@@ -0,0 +1,12 @@
+---
+/** Unique symbol for storing a running index in `locals`. */
+const currentGroupIndexSymbol = Symbol.for('starlight-sidebar-group-index');
+const locals = Astro.locals as Record;
+
+/** The current sidebar group’s index retrieved from `locals` if set, starting at `0`. */
+const index = locals[currentGroupIndexSymbol] || 0;
+// Increment the index for the next instance.
+locals[currentGroupIndexSymbol] = index + 1;
+---
+
+
diff --git a/packages/starlight/components/SidebarSublist.astro b/packages/starlight/components/SidebarSublist.astro
index 23252501..b521ba13 100644
--- a/packages/starlight/components/SidebarSublist.astro
+++ b/packages/starlight/components/SidebarSublist.astro
@@ -2,6 +2,7 @@
import { flattenSidebar, type SidebarEntry } from '../utils/navigation';
import Icon from '../user-components/Icon.astro';
import Badge from '../user-components/Badge.astro';
+import SidebarRestorePoint from './SidebarRestorePoint.astro';
interface Props {
sublist: SidebarEntry[];
@@ -35,6 +36,7 @@ const { sublist, nested } = Astro.props;
i.isCurrent) || !entry.collapsed}
>
+
{entry.label}
--
cgit