FineVision / app /src /components /HtmlEmbed.astro
thibaud frere
update pdf
33aefb9
raw
history blame
4.83 kB
---
interface Props { src: string; title?: string; desc?: string; frameless?: boolean; align?: 'left' | 'center' | 'right' }
const { src, title, desc, frameless = false, align = 'left' } = Astro.props as Props;
// Load all .html embeds under src/content/embeds/** as strings (dev & build)
const embeds = (import.meta as any).glob('../content/embeds/**/*.html', { query: '?raw', import: 'default', eager: true }) as Record<string, string>;
function resolveFragment(requested: string): string | null {
// Allow both "banner.html" and "embeds/banner.html"
const needle = requested.replace(/^\/*/, '');
for (const [key, html] of Object.entries(embeds)) {
if (key.endsWith('/' + needle) || key.endsWith('/' + needle.replace(/^embeds\//, ''))) {
return html;
}
}
return null;
}
const html = resolveFragment(src);
const mountId = `frag-${Math.random().toString(36).slice(2)}`;
---
{ html ? (
<figure class="html-embed">
{title && <figcaption class="html-embed__title" style={`text-align:${align}`}>{title}</figcaption>}
<div class={`html-embed__card${frameless ? ' is-frameless' : ''}`}>
<div id={mountId} set:html={html} />
</div>
{desc && <figcaption class="html-embed__desc" style={`text-align:${align}`} set:html={desc}></figcaption>}
</figure>
) : (
<div><!-- Fragment not found: {src} --></div>
) }
<script>
// Re-execute <script> tags inside the injected fragment (innerHTML doesn't run scripts)
const scriptEl = document.currentScript;
const mount = scriptEl ? scriptEl.previousElementSibling : null;
const execute = () => {
if (!mount) return;
const scripts = mount.querySelectorAll('script');
scripts.forEach(old => {
// ignore non-executable types (e.g., application/json)
if (old.type && old.type !== 'text/javascript' && old.type !== 'module' && old.type !== '') return;
if (old.dataset.executed === 'true') return;
old.dataset.executed = 'true';
if (old.src) {
const s = document.createElement('script');
Array.from(old.attributes).forEach(({ name, value }) => s.setAttribute(name, value));
document.body.appendChild(s);
} else {
try {
// run inline
(0, eval)(old.text || '');
} catch (e) {
console.error('HtmlEmbed inline script error:', e);
}
}
});
};
// Ensure execution when ready: run now if Plotly or D3 is present, or when document is ready; otherwise wait for 'load'
// @ts-expect-error: Plotly/d3 are attached globally at runtime via embeds
if (window.Plotly || window.d3 || document.readyState === 'complete') execute();
else window.addEventListener('load', execute, { once: true });
</script>
<style>
.html-embed { margin: 0; }
.html-embed__title {
text-align: left;
font-weight: 600;
font-size: 0.95rem;
color: var(--text-color);
margin: 0 0 6px 0;
}
.html-embed__card {
background: var(--code-bg);
border: 1px solid var(--border-color);
border-radius: 10px;
padding: 8px;
}
.html-embed__card.is-frameless {
background: transparent;
border-color: transparent;
padding: 0;
}
.html-embed__desc {
text-align: left;
font-size: 0.9rem;
color: var(--muted-color);
margin: 6px 0 0 0;
}
@media (prefers-color-scheme: dark) {
[data-theme="dark"] .html-embed__card:not(.is-frameless) { background: #12151b; border-color: rgba(255,255,255,.15); }
}
@media print {
.html-embed, .html-embed__card { max-width: 100% !important; width: 100% !important; margin-left: 0 !important; margin-right: 0 !important; }
.html-embed__card { padding: 6px; }
.html-embed__card.is-frameless { padding: 0; }
.html-embed__card svg,
.html-embed__card canvas,
.html-embed__card img { max-width: 100% !important; height: auto !important; }
.html-embed__card > div[id^="frag-"] { width: 100% !important; }
}
@media print {
/* Avoid breaks inside embeds */
.html-embed, .html-embed__card { break-inside: avoid; page-break-inside: avoid; }
/* Constrain width and scale inner content */
.html-embed, .html-embed__card { max-width: 100% !important; width: 100% !important; }
.html-embed__card { padding: 6px; }
.html-embed__card.is-frameless { padding: 0; }
.html-embed__card svg,
.html-embed__card canvas,
.html-embed__card img,
.html-embed__card video,
.html-embed__card iframe { max-width: 100% !important; height: auto !important; }
.html-embed__card > div[id^="frag-"] { width: 100% !important; max-width: 100% !important; }
/* Center and constrain the banner (galaxy) when printing */
.html-embed .d3-galaxy { width: 100% !important; max-width: 980px !important; margin-left: auto !important; margin-right: auto !important; }
}
</style>