Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | 2x 13x 13x 10x 10x 10x 10x 10x 10x 10x 10x 7x 6x 13x 13x 9x 6x 5x 6x 6x 7x 7x 7x 7x 6x 6x 1x 1x 1x 1x 1x 1x 1x 6x 6x 6x 6x 7x | 'use client';
import { useEffect } from 'react';
/**
* Keeps every Storybook iframe on the page (`.component-demo` and
* `.storybook-embed`) in sync with the current site theme. Watches
* `data-theme` on `<html>` and re-points each iframe's `src` at the
* theme-appropriate Storybook URL. Individual embeds can still
* be toggled locally via a `.storybook-theme-toggle` button.
*/
function currentTheme(): 'light' | 'dark' {
const attr = document.documentElement.getAttribute('data-theme');
return attr === 'dark' ? 'dark' : 'light';
}
function applyTheme(embed: HTMLElement, theme: 'light' | 'dark') {
const iframe =
embed.tagName === 'IFRAME'
? (embed as HTMLIFrameElement)
: (embed.querySelector('iframe') as HTMLIFrameElement | null);
Iif (!iframe) return;
const baseUrl = iframe.dataset.storybookUrl || iframe.getAttribute('data-src') || '';
Iif (!baseUrl) return;
const sep = baseUrl.includes('?') ? '&' : '?';
const target =
theme === 'dark' ? `${baseUrl}${sep}globals=theme:dark;backgrounds.value:!hex(333333)` : baseUrl;
if (iframe.src !== target) iframe.src = target;
embed.setAttribute('data-sb-theme', theme);
}
export function StorybookThemeToggle() {
useEffect(() => {
const syncAll = () => {
const theme = currentTheme();
document
.querySelectorAll<HTMLElement>('.component-demo, .storybook-embed')
.forEach((el) => applyTheme(el, theme));
};
// Preserve `src` in `data-storybook-url` so we can rewrite it later.
document.querySelectorAll<HTMLIFrameElement>('iframe.storybook-embed').forEach((iframe) => {
Eif (!iframe.dataset.storybookUrl) iframe.dataset.storybookUrl = iframe.src;
});
syncAll();
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
Eif (m.type === 'attributes' && m.attributeName === 'data-theme') {
syncAll();
break;
}
}
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme'],
});
const onClick = (e: Event) => {
Iif (!(e.target instanceof Element)) return;
const btn = e.target.closest<HTMLElement>('.storybook-theme-toggle');
Iif (!btn) return;
const embed = btn.closest<HTMLElement>('.component-demo, .storybook-embed');
Iif (!embed) return;
const current = embed.getAttribute('data-sb-theme') === 'dark' ? 'dark' : 'light';
applyTheme(embed, current === 'light' ? 'dark' : 'light');
};
document.addEventListener('click', onClick);
return () => {
observer.disconnect();
document.removeEventListener('click', onClick);
};
}, []);
return null;
}
|