Add screenshots package and fix card-gradient minify bug (#2793)

Co-authored-by: Bartek <xbartoszdobija@gmail.com>
This commit is contained in:
Paweł Kuna
2026-08-07 00:48:28 +02:00
committed by GitHub
co-authored by Bartek
parent cd0b210a87
commit b38cd32d07
82 changed files with 1698 additions and 147 deletions
+22
View File
@@ -0,0 +1,22 @@
---
// .bg-pattern-{type} utility (core/scss/ui/_patterns.scss) as a component;
// combine with a theme color and/or size, e.g. <BackgroundPattern pattern="dots" color="primary" size="lg" />.
interface Props {
pattern: 'diagonal' | 'diagonal-2' | 'dots' | 'rectangles' | 'lines' | 'lines-vertical' | 'grid' | 'grid-diagonal' | 'blueprint' | 'cross-dots' | 'circles' | 'diagonal-stripes' | 'diagonal-stripes-2' | 'zigzag' | 'vertical-stripes' | 'horizontal-stripes'
/** theme color the pattern ramps up to, e.g. 'primary' */
color?: string
size?: 'sm' | 'md' | 'lg' | 'xl'
/** element to render instead of div */
as?: string
class?: string
/** remaining attributes (style, data-*, …) are forwarded to the element */
[key: string]: unknown
}
const { pattern, color, size, as: Element = 'div', class: className, ...rest } = Astro.props
---
<Element class:list={[`bg-pattern-${pattern}`, color && `bg-pattern-${color}`, size && `bg-pattern-${size}`, className]} {...rest}>
<slot />
</Element>
+67
View File
@@ -0,0 +1,67 @@
---
// Self-contained carousel (.carousel). Default slot holds the .carousel-item elements
// (the caller marks the first one "active" — see CarouselCard.astro / hero/Side.astro);
// indicators/controls are generated from props. A "controls" slot overrides the default
// icon-span controls with custom markup (e.g. an <Icon>-based pair).
interface Props {
id?: string
/** slide count, used to generate indicator buttons */
slideCount: number
fade?: boolean
/** data-bs-interval in ms, or false to disable autoplay */
interval?: number | false
indicators?: boolean
indicatorsVariant?: 'dot' | 'thumb'
indicatorsVertical?: boolean
/** ratio ratio-4x3 on each indicator button, typically paired with indicatorsVariant="thumb" */
indicatorsThumbRatio?: boolean
/** background-image per slide, used when indicatorsVariant="thumb" */
indicatorsImages?: string[]
controls?: boolean
class?: string
/** remaining attributes forwarded to the element */
[key: string]: unknown
}
const { id, slideCount, fade, interval, indicators, indicatorsVariant, indicatorsVertical, indicatorsThumbRatio, indicatorsImages, controls, class: className, ...rest } = Astro.props
const target = id ? `#${id}` : undefined
const slideIndexes = Array.from({ length: slideCount }, (_, i) => i)
const hasCustomControls = Astro.slots.has('controls')
---
<div id={id} class:list={['carousel', 'slide', fade && 'carousel-fade', className]} data-bs-ride="carousel" data-bs-interval={interval === false ? 'false' : interval} {...rest}>
{
indicators && (
<div class:list={['carousel-indicators', indicatorsVertical && 'carousel-indicators-vertical', indicatorsVariant === 'dot' && 'carousel-indicators-dot', indicatorsVariant === 'thumb' && 'carousel-indicators-thumb']}>
{slideIndexes.map((i) => (
<button type="button" data-bs-target={target} data-bs-slide-to={i} class:list={[indicatorsThumbRatio && 'ratio ratio-4x3', i === 0 && 'active']} style={indicatorsVariant === 'thumb' && indicatorsImages?.[i] ? `background-image: url(${indicatorsImages[i]})` : undefined} />
))}
</div>
)
}
<div class="carousel-inner">
<slot />
</div>
{
hasCustomControls ? (
<slot name="controls" />
) : (
controls && (
<Fragment>
<a class="carousel-control-prev" href={target} role="button" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true" />
<span class="visually-hidden">Previous</span>
</a>
<a class="carousel-control-next" href={target} role="button" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true" />
<span class="visually-hidden">Next</span>
</a>
</Fragment>
)
)
}
</div>
+31
View File
@@ -50,11 +50,42 @@ const elementId = `chart-${id}`;
<script define:vars={{ chartKey, elementId, config, xFormatterExpr }}>
window.tabler_chart ??= {};
// Heatmap shades each cell by computing off the literal base color in JS
// (light -> full color-mix by value), unlike other chart types where the
// SVG `fill` attribute just holds `var(--chart-...)` and the browser paints
// it directly. A raw var()/color-mix() string can't be parsed by that color
// math, so it falls back to gray — and ApexCharts' heatmap parser only
// accepts hex, not rgb()/rgba() either. Probing a real element's computed
// `color` resolves var()/color-mix() (Chromium serializes it as a CSS Color 4
// `color(srgb ...)` string), then a 1x1 canvas round-trip converts that to hex.
function resolveCssVar(value) {
if (typeof value !== 'string' || !value.includes('var(')) return value;
const probe = document.createElement('span');
probe.style.cssText = 'position:absolute;visibility:hidden';
probe.style.color = value;
document.body.appendChild(probe);
const resolved = getComputedStyle(probe).color;
probe.remove();
if (!resolved) return value;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = 1;
const ctx = canvas.getContext('2d');
ctx.fillStyle = resolved;
ctx.fillRect(0, 0, 1, 1);
const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data;
return `#${[r, g, b].map((c) => c.toString(16).padStart(2, '0')).join('')}`;
}
function initChart() {
if (xFormatterExpr && config.xaxis?.labels) {
config.xaxis.labels.formatter = new Function('val', `return (${xFormatterExpr})`);
}
if (config.chart?.type === 'heatmap' && Array.isArray(config.colors)) {
config.colors = config.colors.map(resolveCssVar);
}
window.ApexCharts &&
(window.tabler_chart[chartKey] = new ApexCharts(document.getElementById(elementId), config)).render();
}
+13
View File
@@ -0,0 +1,13 @@
---
// Data grid container (.datagrid); holds DatagridItem children.
interface Props {
class?: string
}
const { class: className } = Astro.props
---
<div class:list={['datagrid', className]}>
<slot />
</div>
+17
View File
@@ -0,0 +1,17 @@
---
// A single label/value pair (.datagrid-item); slot renders the value.
interface Props {
title: string
class?: string
}
const { title, class: className } = Astro.props
---
<div class:list={['datagrid-item', className]}>
<div class="datagrid-title">{title}</div>
<div class="datagrid-content">
<slot />
</div>
</div>
+15
View File
@@ -0,0 +1,15 @@
---
// A single legend item: a colored .legend dot + label, e.g. the "This year" /
// "Last year" chart legend in SalesOverview.astro.
interface Props {
color?: string
class?: string
}
const { color = 'primary', class: className } = Astro.props
---
<div class:list={['d-flex align-items-center gap-2', className]}>
<span class={`legend bg-${color}`}></span>
<span class="text-secondary"><slot /></span>
</div>
+24
View File
@@ -0,0 +1,24 @@
---
// "Less -> More" gradient legend, e.g. for a choropleth map's color scale.
interface Props {
/** number of swatches */
steps?: number
/** theme color the swatches ramp up to, e.g. 'primary' */
color?: string
minLabel?: string
maxLabel?: string
class?: string
}
const { steps = 10, color = 'primary', minLabel = 'Less', maxLabel = 'More', class: className } = Astro.props
const items = Array.from({ length: steps }, (_, i) => i + 1)
---
<div class:list={['d-flex align-items-center gap-2', className]}>
<div class="text-secondary">{minLabel}</div>
<div class="d-flex gap-1">
{items.map((step) => <span class="legend" style={`background: color-mix(in srgb, transparent, var(--tblr-${color}) ${(step / steps) * 100}%);`} />)}
</div>
<div class="text-secondary">{maxLabel}</div>
</div>