1
0
mirror of https://github.com/tabler/tabler.git synced 2026-08-05 19:03:18 +04:00

Refactor ScreenshotLayout and remove unused pages

This commit is contained in:
codecalm
2026-08-04 00:38:58 +02:00
parent ce97770ed8
commit d05429a785
16 changed files with 257 additions and 86 deletions
+116 -12
View File
@@ -1,19 +1,49 @@
---
// Chrome-free layout for component screenshots: just @tabler/core CSS/JS
// around a single component, no navbar/sidebar/demo styling. Dark/light is
// driven by the same ?theme= query param and dist/js/tabler-theme.min.js
// as preview/pages/screenshot.astro, so a screenshot tool can request
// /button.html?theme=dark without depending on stored/localStorage state.
// 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 NavbarLogo from '@shared/components/navbar/NavbarLogo.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
/** extra classes on the wrapping container the component is rendered into */
/** the Tabler logo above the card (+ balancing spacer below it) */
showLogo?: boolean
/** wrap the component in the elevated .screenshot-card; when false the slot renders bare */
showCard?: 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[]
/** extra classes on the component wrapper */
class?: string
}
const { title, rtl = false, class: className } = Astro.props
const { title, rtl = false, showLogo = true, showCard = true, columns, zoom = 1, pageLibs = [], 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>
@@ -26,7 +56,9 @@ const rtlSuffix = rtl ? '.rtl' : ''
<link rel="icon" href="/favicon-dev.ico" type="image/x-icon" />
<link href={`/dist/css/tabler${rtlSuffix}.css`} rel="stylesheet" />
<link href={`/dist/css/tabler${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. */
@@ -37,22 +69,94 @@ const rtlSuffix = rtl ? '.rtl' : ''
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,
.screenshot-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-card {
padding: 64px;
border-radius: 16px;
width: 100%;
}
.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>
<script is:inline src={`/dist/js/tabler-theme${min}.js`}></script>
</head>
<body>
<div class:list={['container-xl', 'p-4', className]}>
<slot />
<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">
<NavbarLogo class="brand-gray" />
</div>
)
}
<div class:list={['screenshot-wrapper', showCard && ['screenshot-card', 'card'], className]} style={targetStyle} data-screenshot-target>
<slot />
</div>
{showLogo && <div class="screenshot-logo" />}
</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>
<script is:inline src="/dist/js/tabler.min.js" defer></script>
{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, images and JS-driven
// component init (dropdowns, tooltips, ...) are all settled.
// window "load" (not just fonts.ready) guarantees the deferred
// tabler.min.js above has already run.
// tabler(.min).js above has already run.
const loaded = new Promise((resolve) => window.addEventListener('load', resolve, { once: true }))
Promise.all([document.fonts.ready, loaded]).then(() => {
requestAnimationFrame(() => requestAnimationFrame(() => (document.documentElement.dataset.screenshotReady = 'true')))
@@ -0,0 +1,8 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import ActiveUsers from '@shared/components/cards/charts/ActiveUsers.astro'
---
<ScreenshotLayout title="Active subscriptions" showCard={false} columns={1} pageLibs={['apexcharts']}>
<ActiveUsers />
</ScreenshotLayout>
-14
View File
@@ -1,14 +0,0 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import AvatarList from '@ui/AvatarList.astro'
---
<ScreenshotLayout title="Avatar list">
<div class="space-y">
<AvatarList limit={5} />
<AvatarList limit={5} stacked />
<AvatarList limit={3} stacked text="+4" />
<AvatarList limit={5} size="sm" stacked />
<AvatarList limit={5} size="lg" stacked />
</div>
</ScreenshotLayout>
-24
View File
@@ -1,24 +0,0 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import Badge from '@ui/Badge.astro'
import BadgesList from '@ui/BadgesList.astro'
import site from '@data/site.json'
const colors = ['default', ...Object.keys(site.colors), 'dark']
---
<ScreenshotLayout title="Badge">
<div class="space-y">
<BadgesList>
{colors.map((color) => <Badge text={color} color={color === 'default' ? undefined : color} />)}
</BadgesList>
<BadgesList>
{colors.map((color) => <Badge text={color} color={color === 'default' ? undefined : color} light />)}
</BadgesList>
<BadgesList>
<Badge text="With icon" icon="check" color="green" />
<Badge text="With addon" addon="12" addonColor="red" color="azure" />
<Badge text="Small" scale="sm" color="primary" />
</BadgesList>
</div>
</ScreenshotLayout>
-32
View File
@@ -1,32 +0,0 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import Button from '@ui/Button.astro'
import ButtonList from '@ui/ButtonList.astro'
import site from '@data/site.json'
const colors = ['default', ...Object.keys(site.colors), 'dark']
---
<ScreenshotLayout title="Button">
<div class="space-y">
<ButtonList>
{colors.map((color) => <Button text={color} color={color === 'default' ? undefined : color} element="button" />)}
</ButtonList>
<ButtonList>
{colors.map((color) => <Button text={color} color={color === 'default' ? undefined : color} element="button" outline />)}
</ButtonList>
<ButtonList>
{colors.map((color) => <Button text={color} color={color === 'default' ? undefined : color} element="button" ghost />)}
</ButtonList>
<ButtonList>
<Button text="Small" element="button" size="sm" />
<Button text="Default" element="button" />
<Button text="Large" element="button" size="lg" />
<Button text="Disabled" element="button" disabled />
<Button text="Loading" element="button" loading />
<Button text="With icon" element="button" icon="heart" />
<Button text="Icon only" element="button" icon="heart" iconOnly />
<Button text="Pill" element="button" pill color="primary" />
</ButtonList>
</div>
</ScreenshotLayout>
+8
View File
@@ -0,0 +1,8 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import ConversionRate from '@shared/components/cards/charts/ConversionRate.astro'
---
<ScreenshotLayout title="Conversion rate" showCard={false} columns={1}>
<ConversionRate />
</ScreenshotLayout>
+3 -4
View File
@@ -11,12 +11,11 @@ const pages = Object.keys(pageModules)
.sort()
---
<ScreenshotLayout title="Components">
<h1 class="mb-4">Components</h1>
<div class="list-group">
<ScreenshotLayout title="Components" showCard={false} columns={2}>
<div>
{
pages.map((slug) => (
<a href={`/${slug}`} class="list-group-item list-group-item-action">
<a href={`/${slug}`} class="d-block">
{slug.replace(/-/g, ' ')}
</a>
))
+8
View File
@@ -0,0 +1,8 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import NewClients from '@shared/components/cards/charts/NewClients.astro'
---
<ScreenshotLayout title="New clients" showCard={false} columns={1} pageLibs={['apexcharts']}>
<NewClients />
</ScreenshotLayout>
+8
View File
@@ -0,0 +1,8 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import Revenue from '@shared/components/cards/charts/Revenue.astro'
---
<ScreenshotLayout title="Revenue" showCard={false} columns={1} pageLibs={['apexcharts']}>
<Revenue />
</ScreenshotLayout>
+8
View File
@@ -0,0 +1,8 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import SalesOverview from '@shared/components/cards/charts/SalesOverview.astro'
---
<ScreenshotLayout title="Sales overview" showCard={false} pageLibs={['apexcharts']} columns={2}>
<SalesOverview />
</ScreenshotLayout>
+8
View File
@@ -0,0 +1,8 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import Sales from '@shared/components/cards/charts/Sales.astro'
---
<ScreenshotLayout title="Sales" showCard={false} columns={1}>
<Sales />
</ScreenshotLayout>
+8
View File
@@ -0,0 +1,8 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import TotalUsers from '@shared/components/cards/charts/TotalUsers.astro'
---
<ScreenshotLayout title="Total Users" showCard={false} columns={1} pageLibs={['apexcharts']}>
<TotalUsers />
</ScreenshotLayout>
+8
View File
@@ -0,0 +1,8 @@
---
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
import Welcome from '@shared/components/cards/Welcome.astro'
---
<ScreenshotLayout title="Welcome" showCard={false} columns={2}>
<Welcome />
</ScreenshotLayout>
@@ -0,0 +1,24 @@
---
import Subheader from '@ui/Subheader.astro'
import DropdownDays from '@ui/DropdownDays.astro'
import Trending from '@ui/Trending.astro'
import Progress from '@ui/Progress.astro'
---
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<Subheader>Conversion rate</Subheader>
<div class="ms-auto lh-1">
<DropdownDays id="conversion-rate" label="Select time range for conversion rate" />
</div>
</div>
<div class="d-flex align-items-baseline">
<div class="h1 mb-3 me-2">42.8%</div>
<div class="me-auto">
<Trending value={5} />
</div>
</div>
<Progress value={43} color="primary" size="sm" />
</div>
</div>
@@ -0,0 +1,31 @@
---
import Subheader from '@ui/Subheader.astro'
import Chart from '@ui/Chart.astro'
---
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between gap-3 mb-2">
<div class="mb-3">
<Subheader>Sales overview</Subheader>
<div class="d-flex align-items-baseline gap-2 mt-1">
<div class="h1 mb-0">$284,300</div>
<div class="text-green fw-medium">+12.4%</div>
</div>
</div>
<div class="d-flex align-items-center gap-3">
<div class="d-flex align-items-center gap-2">
<span class="legend bg-primary"></span>
<span class="text-secondary">This year</span>
</div>
<div class="d-flex align-items-center gap-2">
<span class="legend bg-secondary"></span>
<span class="text-secondary">Last year</span>
</div>
</div>
</div>
<Chart chartId="sales-overview" height={15} />
</div>
</div>
+19
View File
@@ -691,6 +691,25 @@
"categories": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
"y-max": 110
},
"sales-overview": {
"type": "line",
"stroke-width": [3, 2],
"stroke-dash": [0, 6],
"series": [
{
"name": "This year",
"color": "primary",
"data": [180, 195, 210, 205, 225, 240, 235, 260, 255, 270, 285, 300]
},
{
"name": "Last year",
"color": "secondary",
"data": [150, 160, 168, 172, 180, 188, 190, 200, 205, 210, 215, 225]
}
],
"categories": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
"y-max": 320
},
"new-clients": {
"type": "line",
"datetime": true,