/
githubmirror
/
tabler
Обзор
Документация
Войти
/
githubmirror
/
tabler
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
screenshots/layouts/ScreenshotLayout.astro
166 строк
11 KB
Paweł Kuna
Add screenshots package and fix `card-gradient` minify bug (#2793)
07 авг 2026, 01:48
Не верифицирован
07 авг 2026, 01:48
b38cd32
Код
Авторство
О чём код?
--- // Chrome-free layout for component screenshots: just @tabler/core CSS/JS // around a single component, no navbar/sidebar/demo styling. Modeled 1:1 on // preview/pages/screenshot.astro (the marketing hero-shot page): a fixed // 1024x768 .screenshot canvas with the Tabler logo above an elevated // .screenshot-card, on a soft gradient backdrop. Dark/light is driven by the // same ?theme= query param + dist/js/tabler-theme(.min).js + the // hide-theme-light/hide-theme-dark nav-segmented toggle as that page, so a // screenshot tool can request /button.html?theme=dark without depending on // stored/localStorage state. import Logo from '@shared/components/navbar/Logo.astro' import PageScripts from '@shared/components/PageScripts.astro' import libs from '@tabler/core/libs.json' interface Props { title: string /** dir="rtl" + the .rtl.css variant of tabler.css */ rtl?: boolean /** the Tabler logo above the card (+ balancing spacer below it) */ showLogo?: boolean /** caps the [data-screenshot-target] width at columns * 320px */ columns?: number /** CSS zoom applied to [data-screenshot-target] */ zoom?: number /** third-party libs from libs.json the component needs, e.g. ['apexcharts'] */ pageLibs?: string[] /** core CSS plugins (site.cssPlugins), e.g. ['socials'] for tabler-socials.css */ cssPlugins?: string[] /** extra classes on the component wrapper */ class?: string } const { title, rtl = false, showLogo = true, columns, zoom = 1, pageLibs = [], cssPlugins = [], class: className } = Astro.props const targetStyle = [columns && `max-width: ${columns * 310}px`, `zoom: ${zoom}`].filter(Boolean).join('; ') const rtlSuffix = rtl ? '.rtl' : '' // core/dist only has .min. files after a full `pnpm build`; `astro dev` (this // package's own dev-prepare-less dev script) runs against whatever core's own // dev watcher last emitted, which is unminified — so match the served core // build instead of hardcoding one variant. const min = import.meta.env.DEV ? '' : '.min' type Lib = { npm?: string; js?: string[]; css?: string[] } const pageLibEntries = Object.entries(libs as Record<string, Lib>).filter(([name]) => pageLibs.includes(name)) const libHref = (lib: Lib, file: string) => `/dist/libs/${lib.npm}/${file}` const libCssFiles = pageLibEntries.flatMap(([, lib]) => (lib.css ?? []).map((file) => libHref(lib, file))) const libJsFiles = pageLibEntries.flatMap(([, lib]) => (lib.js ?? []).map((file) => libHref(lib, file))) --- <!doctype html> <html lang="en" dir={rtl ? 'rtl' : undefined}> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> <title>{title} - Tabler screenshots</title> <link rel="icon" href="./favicon-dev.ico" type="image/x-icon" /> <link href={`./dist/css/tabler${rtlSuffix}${min}.css`} rel="stylesheet" /> {cssPlugins.map((plugin) => <link href={`./dist/css/tabler-${plugin}${rtlSuffix}${min}.css`} rel="stylesheet" />)} {libCssFiles.map((href) => <link href={href} rel="stylesheet" />)} <style is:inline> /* Deterministic screenshots: no mid-capture animation/transition frames, no blinking caret. */ *, *::before, *::after { transition: none !important; animation: none !important; caret-color: transparent !important; } .screenshot { width: 1024px; height: 768px; background: var(--tblr-bg-surface-tertiary) linear-gradient(to top left, rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0)); position: relative; padding: 0 4rem; } .screenshot-wrapper { width: 100%; } .screenshot-wrapper > .card { box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.02), 0 8px 24px -4px rgba(0, 0, 0, 0.04), 0 24px 40px -8px rgba(0, 0, 0, 0.1); } .screenshot-logo { display: flex; align-items: center; justify-content: center; padding: 0 5rem; flex: 1; } /* fake mouse pointer, to make the static screenshot read as a live interaction */ .cursor { position: absolute; width: 26px; height: 37px; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 26 37'%3E%3Cg filter='url(%23a)'%3E%3Cpath fill='%23fff' fill-rule='evenodd' d='M8.56 13.902c-.284-.358-.63-1.092-1.243-1.983-.348-.505-1.211-1.454-1.468-1.935-.223-.426-.2-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.306.302.458.214.12-.071-.495-.187-1.342-.355-2.091-.128-.568-.16-.657-.281-1.093-.13-.464-.195-.79-.316-1.281-.084-.348-.235-1.06-.276-1.46-.057-.546-.087-1.438.264-1.848.275-.321.906-.418 1.296-.22.513.259.803 1.003.937 1.3.238.534.386 1.15.515 1.96.165 1.032.466 2.463.476 2.764.024-.37-.068-1.146-.004-1.5.059-.321.329-.694.667-.795.285-.085.62-.116.915-.055.313.064.643.288.767.499.362.624.369 1.899.384 1.83.085-.375.07-1.228.284-1.583.14-.234.496-.445.686-.48.295-.051.655-.067.964-.007.25.049.587.345.677.487.218.344.343 1.317.38 1.658.015.14.073-.392.293-.736.405-.64 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.013.828-.04 1.202-.031.4-.117 1.303-.242 1.742-.087.3-.372.977-.652 1.383 0 0-1.075 1.25-1.192 1.814-.117.562-.079.566-.102.964-.023.398.122.922.122.922s-.802.104-1.235.035c-.39-.063-.875-.84-1-1.079-.171-.328-.539-.264-.681-.023-.226.383-.71 1.07-1.051 1.113-.668.084-2.054.032-3.14.02 0 0 .185-1.01-.226-1.357-.306-.26-.83-.784-1.144-1.06l-.832-.921Z' clip-rule='evenodd'/%3E%3Cpath stroke='%23000' stroke-linecap='round' stroke-linejoin='round' d='M16.794 14.257v-3.459m-2.015 3.47-.016-3.472m-1.98.031.02 3.426m-4.243-.35c-.284-.36-.63-1.094-1.243-1.985-.348-.503-1.211-1.452-1.468-1.934-.223-.426-.2-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.306.302.458.214.12-.071-.495-.187-1.342-.355-2.091-.128-.568-.16-.657-.281-1.093-.13-.464-.195-.79-.316-1.281-.084-.348-.235-1.06-.276-1.46-.057-.546-.087-1.438.264-1.848.275-.321.906-.418 1.296-.22.513.259.803 1.003.937 1.3.238.534.386 1.15.515 1.96.165 1.032.466 2.463.476 2.764.024-.37-.068-1.146-.004-1.5.059-.321.329-.694.667-.795.285-.085.62-.116.915-.055.313.064.643.288.767.499.362.624.369 1.899.384 1.83.085-.375.07-1.228.284-1.583.14-.234.496-.445.686-.48.295-.051.655-.067.964-.007.25.049.587.345.677.487.218.344.343 1.317.38 1.658.015.14.073-.392.293-.736.405-.64 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.013.828-.04 1.202-.031.4-.117 1.303-.242 1.742-.087.3-.372.977-.652 1.383 0 0-1.075 1.25-1.192 1.814-.117.562-.079.566-.102.964-.023.398.122.922.122.922s-.802.104-1.235.035c-.39-.063-.875-.84-1-1.079-.171-.328-.539-.264-.681-.023-.226.383-.71 1.07-1.051 1.113-.668.084-2.054.032-3.14.02 0 0 .185-1.01-.226-1.357-.306-.26-.83-.784-1.144-1.06l-.832-.921Z'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='a' width='30' height='40' x='-2' y='-1' color-interpolation-filters='sRGB' filterUnits='userSpaceOnUse'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='1'/%3E%3CfeGaussianBlur stdDeviation='1'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.05 0'/%3E%3CfeBlend in2='BackgroundImageFix' result='effect1_dropShadow_55_1571'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='3'/%3E%3CfeGaussianBlur stdDeviation='1.5'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.04 0'/%3E%3CfeBlend in2='effect1_dropShadow_55_1571' result='effect2_dropShadow_55_1571'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='8'/%3E%3CfeGaussianBlur stdDeviation='2.5'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.03 0'/%3E%3CfeBlend in2='effect2_dropShadow_55_1571' result='effect3_dropShadow_55_1571'/%3E%3CfeColorMatrix in='SourceAlpha' result='hardAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0'/%3E%3CfeOffset dy='14'/%3E%3CfeGaussianBlur stdDeviation='2.5'/%3E%3CfeColorMatrix values='0 0 0 0 0.156863 0 0 0 0 0.156863 0 0 0 0 0.2 0 0 0 0.01 0'/%3E%3CfeBlend in2='effect3_dropShadow_55_1571' result='effect4_dropShadow_55_1571'/%3E%3CfeBlend in='SourceGraphic' in2='effect4_dropShadow_55_1571' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E"); top: calc(100% - 10px); left: calc(50% - 10px); } </style> <!-- loaded early, before paint, so ?theme=dark applies before first render --> <script is:inline src={`/dist/js/tabler-theme${min}.js`}></script> </head> <body> <div class="page page-center align-items-center"> <div class="screenshot d-flex flex-column justify-content-center align-items-center px-12" id="screenshot"> {showLogo && <div class="screenshot-logo" />} <div class:list={['screenshot-wrapper', className]} style={targetStyle} data-screenshot-target> <slot /> </div> { showLogo && ( <div class="screenshot-logo"> <Logo gray /> </div> ) } </div> <!-- outside .screenshot on purpose: a screenshot tool crops to [data-screenshot-target], so this toggle never ends up in the capture --> <nav class="nav-segmented mt-4"> <a href="?theme=light" class="nav-link hide-theme-light">Light</a> <a href="?theme=light" class="nav-link active hide-theme-dark">Light</a> <a href="?theme=dark" class="nav-link hide-theme-dark">Dark</a> <a href="?theme=dark" class="nav-link active hide-theme-light">Dark</a> </nav> </div> {libJsFiles.map((src) => <script is:inline src={src} defer />)} <script is:inline src={`/dist/js/tabler${min}.js`} defer></script> <!-- components that render via CaptureScript (e.g. Chart) queue their init script into shared/lib/page-scripts instead of inlining it where used; this drains it. --> <PageScripts /> <script> // Signals to the screenshot tool that fonts and JS-driven component init // (dropdowns, tooltips, charts via PageScripts, ...) are settled. // DOMContentLoaded — not window "load" — on purpose: deferred scripts // (tabler(.min).js above, and everything PageScripts drained) already run // before DOMContentLoaded per spec, and unlike "load" it doesn't block on // slow/unreachable subresources (e.g. an external chat GIF). const domReady = new Promise((resolve) => { if (document.readyState !== 'loading') resolve(undefined) else document.addEventListener('DOMContentLoaded', () => resolve(undefined), { once: true }) }) Promise.all([document.fonts.ready, domReady]).then(() => { requestAnimationFrame(() => requestAnimationFrame(() => (document.documentElement.dataset.screenshotReady = 'true'))) }) </script> </body> </html>