mirror of
https://github.com/tabler/tabler.git
synced 2026-08-29 13:21:29 +04:00
167 lines
11 KiB
Plaintext
167 lines
11 KiB
Plaintext
---
|
|
// 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>
|