@@ -56,172 +56,4 @@ const initialItem = (initialId && items.find((i) => i.id === initialId)) ?? item
5656 </div >
5757 )
5858}
59-
60- <script >
61- const initializedRoots: WeakSet<Element> = new WeakSet();
62-
63- const initRoot = (root: Element) => {
64- if (initializedRoots.has(root)) return;
65- initializedRoots.add(root);
66-
67- const buttons: NodeListOf<HTMLButtonElement> = root.querySelectorAll(
68- '[data-image-toggle-button]',
69- );
70- const img: HTMLImageElement | null = root.querySelector('[data-image-toggle-target]');
71- const toggleLight: HTMLButtonElement | null = root.querySelector('[data-image-toggle-light]');
72- const toggleDark: HTMLButtonElement | null = root.querySelector('[data-image-toggle-dark]');
73-
74- let cardEl: HTMLElement | null = root.querySelector('[data-image-card]');
75- if (!cardEl) cardEl = root as HTMLElement; // fallback to root if card not found
76-
77- const getLocalMode = (): 'dark' | 'light' => {
78- const attr = cardEl?.getAttribute('data-local-mode');
79- if (attr === 'dark' || attr === 'light') return attr as 'dark' | 'light';
80- // Fallback to OS preference if not set locally
81- return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
82- ? 'dark'
83- : 'light';
84- };
85-
86- const setLocalMode = (mode: 'dark' | 'light') => {
87- cardEl?.setAttribute('data-local-mode', mode);
88- };
89-
90- const applyModeToImage = () => {
91- if (!img) return;
92- const mode = getLocalMode();
93- const s = img.getAttribute('src') || '';
94- const next = s.replace(
95- /--(Light|Dark)mode--/,
96- mode === 'dark' ? '--Darkmode--' : '--Lightmode--',
97- );
98- if (next !== s) img.setAttribute('src', next);
99- updateToggleVisibility();
100- };
101-
102- const setActiveButton = (activeId: string) => {
103- buttons.forEach((btn) => {
104- if (btn.getAttribute('data-image-id') === activeId) {
105- btn.setAttribute('data-active', '');
106- } else {
107- btn.removeAttribute('data-active');
108- }
109- });
110- };
111-
112- buttons.forEach((button: HTMLButtonElement) => {
113- button.addEventListener('click', () => {
114- const srcTemplate = button.getAttribute('data-image-src');
115- const alt = button.getAttribute('data-image-alt') ?? '';
116- if (img && srcTemplate) {
117- const mode = getLocalMode();
118- // Normalize template to current shell mode by replacing any Light/Dark marker
119- const next = srcTemplate.replace(
120- /--(Light|Dark)mode--/,
121- mode === 'dark' ? '--Darkmode--' : '--Lightmode--',
122- );
123- img.setAttribute('src', next);
124- img.setAttribute('alt', alt);
125- // update toggle visibility to reflect current local mode after brand change
126- updateToggleVisibility();
127- // Scroll image into viewport after change
128- try {
129- img.scrollIntoView({ behavior: 'smooth', block: 'center' });
130- } catch (e) {}
131- const id = button.getAttribute('data-image-id');
132- if (id) setActiveButton(id);
133- }
134- });
135- });
136-
137- // Helper: show/hide correct toggle button based on current img src
138- const updateToggleVisibility = () => {
139- const mode = getLocalMode();
140- const isLight = mode === 'light';
141- if (toggleLight) toggleLight.style.display = isLight ? '' : 'none';
142- if (toggleDark) toggleDark.style.display = isLight ? 'none' : '';
143- };
144-
145- // Toggle to dark
146- toggleLight?.addEventListener('click', () => {
147- if (!img) return;
148- setLocalMode('dark');
149- applyModeToImage();
150- // Scroll image into viewport after toggle
151- try {
152- img.scrollIntoView({ behavior: 'smooth', block: 'center' });
153- } catch (e) {}
154- });
155- // Toggle to light
156- toggleDark?.addEventListener('click', () => {
157- if (!img) return;
158- setLocalMode('light');
159- applyModeToImage();
160- updateToggleVisibility();
161- });
162-
163- // Initial visibility state
164- applyModeToImage();
165- // Do not override SSR-marked active button; derive from current image src if needed
166- const currentSrc = img?.getAttribute('src') || '';
167- const preMarked = Array.from(buttons).find((b) => b.hasAttribute('data-active'));
168- if (!preMarked && currentSrc) {
169- const match = Array.from(buttons).find((b) => {
170- const tpl = b.getAttribute('data-image-src') || '';
171- // Normalize mode marker for comparison
172- const normalizedTpl = tpl.replace(
173- /--(Light|Dark)mode--/,
174- currentSrc.includes('--Darkmode--') ? '--Darkmode--' : '--Lightmode--',
175- );
176- return normalizedTpl === currentSrc;
177- });
178- const id = match?.getAttribute('data-image-id');
179- if (id) setActiveButton(id);
180- }
181-
182- // Local mode is independent; no observation of global shell mode.
183- };
184-
185- const scanRoots = () => {
186- const roots = document.querySelectorAll('[data-image-toggle-root]');
187- roots.forEach((root) => initRoot(root));
188- };
189-
190- const setup = () => {
191- // check what is already in the DOM
192- scanRoots();
193-
194- // observe future changes
195- const observer = new MutationObserver(() => {
196- scanRoots();
197- });
198-
199- if (document.body) {
200- observer.observe(document.body, {
201- childList: true,
202- subtree: true,
203- });
204- }
205- };
206-
207- // Debounced setup for dynamic swaps
208- let setupTimer: number | undefined;
209- const scheduleSetup = () => {
210- if (setupTimer) window.clearTimeout(setupTimer);
211- setupTimer = window.setTimeout(() => {
212- // wait one frame to allow DOM to settle
213- requestAnimationFrame(() => setup());
214- }, 0);
215- };
216-
217- // Init on first load
218- if (document.readyState === 'loading') {
219- document.addEventListener('DOMContentLoaded', setup, { once: true });
220- } else {
221- setup();
222- }
223-
224- // Also init on Astro client-side navigations (View Transitions / partial swaps)
225- document.addEventListener('astro:page-load', scheduleSetup);
226- document.addEventListener('astro:after-swap', scheduleSetup);
227- </script >
59+ <script src =" ./interactiveDemo.client.js" ></script >
0 commit comments