feat: add localStorage persistence for named code groups

pull/5012/head
juji 1 month ago
parent 3641e70c30
commit aafc201c41

@ -301,6 +301,11 @@ describe('Code Groups', () => {
}) })
test('group-name synchronization across groups', async () => { test('group-name synchronization across groups', async () => {
// Clear localStorage to ensure clean test state
await page.evaluate(() => localStorage.clear())
await page.reload()
await page.waitForSelector('#group-name-basic + div')
const div1 = page.locator('#group-name-basic + div') const div1 = page.locator('#group-name-basic + div')
const div2 = page.locator( const div2 = page.locator(
'#group-name-second-instance-same-name-for-sync-test + div' '#group-name-second-instance-same-name-for-sync-test + div'

@ -858,6 +858,10 @@ yarn docs
Try clicking different tabs above! Notice how both code groups switch together because they share the same `group-name`. Try clicking different tabs above! Notice how both code groups switch together because they share the same `group-name`.
::: :::
::: info
Your tab selection is automatically saved to localStorage. When you return to the page, your preferred tab for each group-name will be automatically selected.
:::
The `group-name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed. The `group-name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed.
Valid examples: Valid examples:

@ -1,5 +1,28 @@
import { inBrowser, onContentUpdated } from 'vitepress' import { inBrowser, onContentUpdated } from 'vitepress'
const STORAGE_KEY = 'vitepress:tabsCache'
function getStoredTabIndex(groupName: string): number | null {
if (!inBrowser) return null
try {
const cache = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}')
return cache[groupName] ?? null
} catch {
return null
}
}
function setStoredTabIndex(groupName: string, index: number) {
if (!inBrowser) return
try {
const cache = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}')
cache[groupName] = index
localStorage.setItem(STORAGE_KEY, JSON.stringify(cache))
} catch {
// Silently ignore localStorage errors
}
}
export function useCodeGroups() { export function useCodeGroups() {
if (import.meta.env.DEV) { if (import.meta.env.DEV) {
onContentUpdated(() => { onContentUpdated(() => {
@ -13,6 +36,38 @@ export function useCodeGroups() {
} }
if (inBrowser) { if (inBrowser) {
// Restore tabs from localStorage on page load, but only on first content load
let hasRestoredTabs = false
onContentUpdated(() => {
if (hasRestoredTabs) return
hasRestoredTabs = true
document
.querySelectorAll('.vp-code-group[data-group-name]')
.forEach((group) => {
const groupName = group.getAttribute('data-group-name')
if (!groupName) return
const storedIndex = getStoredTabIndex(groupName)
if (storedIndex === null) return
const inputs = group.querySelectorAll('input')
const blocks = group.querySelector('.blocks')
if (!blocks || !inputs[storedIndex]) return
// Update radio input
inputs[storedIndex].checked = true
// Update active block
const currentActive = blocks.querySelector('.active')
const newActive = blocks.children[storedIndex]
if (currentActive && newActive && currentActive !== newActive) {
currentActive.classList.remove('active')
newActive.classList.add('active')
}
})
})
window.addEventListener('click', (e) => { window.addEventListener('click', (e) => {
const el = e.target as HTMLInputElement const el = e.target as HTMLInputElement
@ -42,9 +97,10 @@ export function useCodeGroups() {
const label = group?.querySelector(`label[for="${el.id}"]`) const label = group?.querySelector(`label[for="${el.id}"]`)
label?.scrollIntoView({ block: 'nearest' }) label?.scrollIntoView({ block: 'nearest' })
// Sync other groups with same group-name // Sync other groups with same group-name and save to localStorage
const groupName = group.getAttribute('data-group-name') const groupName = group.getAttribute('data-group-name')
if (groupName) { if (groupName) {
setStoredTabIndex(groupName, i)
syncTabsInOtherGroups(groupName, i, group as HTMLElement) syncTabsInOtherGroups(groupName, i, group as HTMLElement)
} }
} }

Loading…
Cancel
Save