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

Add @tabler/screenshots package with initial configuration and components

This commit is contained in:
codecalm
2026-08-03 23:52:40 +02:00
parent 52fe729781
commit ce97770ed8
16 changed files with 312 additions and 2 deletions
+2 -2
View File
@@ -18,10 +18,10 @@
"lint:fix": "pnpm run lint-md-fix && pnpm run format-prettier",
"lint-md": "markdownlint --config .markdownlint-docs.json \"docs/pages/**/*.mdx\"",
"lint-md-fix": "markdownlint --fix --config .markdownlint-docs.json \"docs/pages/**/*.mdx\"",
"lint-prettier": "prettier --check \"core/scss/**/*.scss\" \"core/js/**/*.{js,ts}\" \"core/.build/**/*.{ts,mts,mjs}\" \".build/**/*.ts\" \"preview/scss/**/*.scss\" \"preview/js/**/*.{js,ts}\" \"{preview,docs,shared}/**/*.astro\" \"{preview,docs}/**/*.{mjs,mts}\" \"shared/**/*.{js,mjs,mts,ts}\" --cache",
"lint-prettier": "prettier --check \"core/scss/**/*.scss\" \"core/js/**/*.{js,ts}\" \"core/.build/**/*.{ts,mts,mjs}\" \".build/**/*.ts\" \"preview/scss/**/*.scss\" \"preview/js/**/*.{js,ts}\" \"{preview,docs,screenshots,shared}/**/*.astro\" \"{preview,docs,screenshots}/**/*.{mjs,mts}\" \"shared/**/*.{js,mjs,mts,ts}\" --cache",
"lint-scss-vars": "pnpm --filter @tabler/core css-lint",
"format": "pnpm run format-prettier && pnpm run reformat-md",
"format-prettier": "prettier --write \"core/scss/**/*.scss\" \"core/js/**/*.{js,ts}\" \"core/.build/**/*.{ts,mts,mjs}\" \".build/**/*.ts\" \"preview/scss/**/*.scss\" \"preview/js/**/*.{js,ts}\" \"{preview,docs,shared}/**/*.astro\" \"{preview,docs}/**/*.{mjs,mts}\" \"shared/**/*.{js,mjs,mts,ts}\" --cache",
"format-prettier": "prettier --write \"core/scss/**/*.scss\" \"core/js/**/*.{js,ts}\" \"core/.build/**/*.{ts,mts,mjs}\" \".build/**/*.ts\" \"preview/scss/**/*.scss\" \"preview/js/**/*.{js,ts}\" \"{preview,docs,screenshots,shared}/**/*.astro\" \"{preview,docs,screenshots}/**/*.{mjs,mts}\" \"shared/**/*.{js,mjs,mts,ts}\" --cache",
"check": "pnpm run lint && pnpm run type-check",
"zip-package": "tsx .build/zip-package.ts",
"start": "pnpm dev"
+19
View File
@@ -271,6 +271,25 @@ importers:
specifier: ^5.7.3
version: 5.9.3
screenshots:
dependencies:
'@tabler/core':
specifier: workspace:*
version: link:../core
astro:
specifier: ^7.1.6
version: 7.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.1.1)(sass@1.102.0)(terser@5.49.0)(tsx@4.23.1)(yaml@2.9.0)
devDependencies:
'@astrojs/check':
specifier: ^0.9.4
version: 0.9.10(prettier-plugin-astro@0.14.1)(prettier@3.9.6)(typescript@5.9.3)
shx:
specifier: ^0.4.0
version: 0.4.0
typescript:
specifier: ^5.7.3
version: 5.9.3
shared:
dependencies:
'@tabler/core':
+1
View File
@@ -2,6 +2,7 @@ packages:
- core
- preview
- docs
- screenshots
- shared
allowBuilds:
+6
View File
@@ -0,0 +1,6 @@
# build output
dist/
.astro/
# public/ is fully generated by .build/copy-assets.ts
public/
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+66
View File
@@ -0,0 +1,66 @@
// @ts-check
import { defineConfig } from 'astro/config'
import { fileURLToPath } from 'node:url'
import { copyAssets } from '../.build/copy-assets'
/** @param {string} p */
const path = (p) => fileURLToPath(new URL(p, import.meta.url))
// https://astro.build/config
export default defineConfig({
// pages live at the package root (./pages) — components/lib/data are shared
// (see the @shared alias)
srcDir: '.',
server: {
port: 3020,
// bind on all interfaces so the dev server is reachable from Docker
// port mappings and other devices on the local network
host: true,
},
vite: {
resolve: {
alias: {
'@data': fileURLToPath(new URL('../shared/data', import.meta.url)),
'@shared': fileURLToPath(new URL('../shared', import.meta.url)),
'@ui': fileURLToPath(new URL('../shared/ui', import.meta.url)),
'@components': fileURLToPath(new URL('../shared/components', import.meta.url)),
},
},
},
build: {
// Emit button.html instead of button/index.html — a stable, predictable
// URL per component for the screenshot tool to iterate over.
format: 'file',
},
compressHTML: false,
integrations: [
copyAssets({
repo: path('..'),
publicDir: path('./public'),
copies: [
{
// @tabler/core dist (css/js/fonts/img/libs).
// Fallback keeps current assets if core rebuilds dist mid-copy in turbo dev.
from: path('./node_modules/@tabler/core/dist'),
to: path('./public/dist'),
label: '@tabler/core',
allowDestinationFallback: true,
},
{
// static assets referenced by demo data (avatar photos, brand svgs...).
from: path('../shared/static'),
to: path('./public/static'),
label: 'shared assets',
},
{ from: path('./assets/favicon.ico'), to: path('./public/favicon.ico'), label: '@tabler/screenshots' },
{ from: path('./assets/favicon-dev.ico'), to: path('./public/favicon-dev.ico'), label: '@tabler/screenshots' },
],
// Watch the real core/dist, not the node_modules symlink the startup copy
// reads from — same directory.
syncDirs: [
{ from: path('../core/dist'), to: path('./public/dist') },
{ from: path('../shared/static'), to: path('./public/static') },
],
}),
],
})
+1
View File
@@ -0,0 +1 @@
/// <reference types="astro/client" />
@@ -0,0 +1,62 @@
---
// 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.
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 */
class?: string
}
const { title, rtl = false, class: className } = Astro.props
const rtlSuffix = rtl ? '.rtl' : ''
---
<!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}.css`} 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;
}
</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:list={['container-xl', 'p-4', className]}>
<slot />
</div>
<script is:inline src="/dist/js/tabler.min.js" defer></script>
<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.
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')))
})
</script>
</body>
</html>
+26
View File
@@ -0,0 +1,26 @@
{
"name": "@tabler/screenshots",
"private": true,
"type": "module",
"version": "0.0.1",
"description": "Astro app: one page per UI component, for automated screenshotting",
"engines": {
"node": ">=22.12.0"
},
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"clean": "shx rm -rf dist public .astro",
"type-check": "astro check"
},
"dependencies": {
"@tabler/core": "workspace:*",
"astro": "^7.1.6"
},
"devDependencies": {
"@astrojs/check": "^0.9.4",
"shx": "^0.4.0",
"typescript": "^5.7.3"
}
}
+14
View File
@@ -0,0 +1,14 @@
---
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
@@ -0,0 +1,24 @@
---
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
@@ -0,0 +1,32 @@
---
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>
+25
View File
@@ -0,0 +1,25 @@
---
// Every other page under pages/ is a screenshot target. Listing them via a
// glob (instead of a hand-maintained array) means adding a new component
// page is the only step needed for it to show up here.
import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
const pageModules = import.meta.glob('./*.astro')
const pages = Object.keys(pageModules)
.map((path) => path.replace(/^\.\//, '').replace(/\.astro$/, ''))
.filter((slug) => slug !== 'index')
.sort()
---
<ScreenshotLayout title="Components">
<h1 class="mb-4">Components</h1>
<div class="list-group">
{
pages.map((slug) => (
<a href={`/${slug}`} class="list-group-item list-group-item-action">
{slug.replace(/-/g, ' ')}
</a>
))
}
</div>
</ScreenshotLayout>
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist", "public"],
"compilerOptions": {
"paths": {
"@data/*": ["../shared/data/*"],
"@shared/*": ["../shared/*"],
"@ui/*": ["../shared/ui/*"],
"@components/*": ["../shared/components/*"]
},
"baseUrl": "."
}
}
+20
View File
@@ -47,6 +47,19 @@
],
"cache": true
},
"@tabler/screenshots#build": {
"dependsOn": [
"^build"
],
"inputs": [
"$TURBO_DEFAULT$",
"$TURBO_ROOT$/shared/**"
],
"outputs": [
"dist/**"
],
"cache": true
},
"dev": {
"cache": false,
"persistent": true
@@ -74,6 +87,13 @@
"cache": false,
"persistent": true
},
"@tabler/screenshots#dev": {
"dependsOn": [
"@tabler/core#dev-prepare"
],
"cache": false,
"persistent": true
},
"@tabler/docs#dev": {
"dependsOn": [
"@tabler/core#dev-prepare",