mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 05:06:55 +04:00
Rework the charts page into sections; add .page-section-title (#3101)
This commit is contained in:
1 parent
88811afaff
commit
a984752c84
20 files changed
+632
-112
No files matched your search
+131
-40
@@ -6,6 +6,8 @@ import Chart from '@components/demo/Chart.astro'
|
||||
import Card from '@ui/Card.astro'
|
||||
import CardHeader from '@ui/CardHeader.astro'
|
||||
import CardBody from '@ui/CardBody.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import CardSubtitle from '@ui/CardSubtitle.astro'
|
||||
import chartsData from '@data/charts.json'
|
||||
import { capitalize } from '@shared/lib/string-format'
|
||||
import DocsLink from '@ui/DocsLink.astro'
|
||||
@@ -13,7 +15,99 @@ import CaptureScript from '@shared/components/CaptureScript.astro'
|
||||
import LegendList from '@ui/LegendList.astro'
|
||||
import Legend from '@ui/Legend.astro'
|
||||
|
||||
const demoCharts = Object.entries(chartsData as Record<string, { demo?: boolean; name?: string }>).filter(([, chart]) => chart && chart.demo)
|
||||
type DemoChart = { name?: string; extend?: string; type?: string; series?: { name?: string; color?: string }[] }
|
||||
|
||||
const charts = chartsData as Record<string, DemoChart>
|
||||
|
||||
// Legend rules: one serie needs no legend, two to four series get a <Legend> list above the
|
||||
// chart, and only circular charts or charts with five and more series keep the legend below.
|
||||
const circular = ['pie', 'donut', 'radialBar', 'polarArea', 'radar']
|
||||
|
||||
function topLegend(chartId: string) {
|
||||
const own = charts[chartId]
|
||||
const chart = own?.extend ? { ...charts[own.extend], ...own } : own
|
||||
const series = chart?.series ?? []
|
||||
if (circular.includes(chart?.type ?? 'bar') || series.length < 2 || series.length > 4) return undefined
|
||||
return series.map((serie) => ({ text: serie.name ?? '', color: serie.color ?? 'primary' }))
|
||||
}
|
||||
|
||||
const third = 'col-md-6 col-lg-4'
|
||||
const half = 'col-lg-6'
|
||||
const wide = 'col-lg-8'
|
||||
|
||||
// Every chart is placed by hand, section by section and row by row (each row adds up to
|
||||
// 12 columns). Charts with many points, several series or long labels get a wider column.
|
||||
const sections: { title: string; description: string; charts: [chartId: string, col: string, subtitle: string][] }[] = [
|
||||
{
|
||||
title: 'Line charts',
|
||||
description: 'Trends over time, where the change between points matters more than single values.',
|
||||
charts: [
|
||||
['completion-tasks-2', third, 'Tickets solved per day with a smooth curve, the default.'],
|
||||
['completion-tasks-4', third, 'The same tickets drawn with straight segments.'],
|
||||
['completion-tasks-5', third, 'A stepped line for values that change in jumps.'],
|
||||
|
||||
['completion-tasks-7', third, 'Tickets solved per day, labeled with values.'],
|
||||
['tasks', wide, 'Issues opened, closed and reopened per day over the last 17 days.'],
|
||||
|
||||
['line', third, 'Monthly revenue of each plan, January to June.'],
|
||||
['line-stroke', third, 'Monthly signups, dashed for paid and referral.'],
|
||||
['spline', third, 'CPU load of web servers and workers in one day.'],
|
||||
|
||||
['temperature', half, 'Monthly average temperature in Tokyo and London.'],
|
||||
['stepline', half, 'Number of running instances during the day.'],
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Area charts',
|
||||
description: 'Lines with a filled area, to stress volume or to show how parts add up to a total.',
|
||||
charts: [
|
||||
['completion-tasks-3', third, 'Tickets solved per day as a filled area.'],
|
||||
['completion-tasks-10', third, 'Solved and reopened tickets as two overlapping areas.'],
|
||||
['completion-tasks-11', third, 'Solved and reopened tickets stacked on top of each other.'],
|
||||
|
||||
['area', third, 'Monthly sessions on mobile and desktop.'],
|
||||
['area-spline', third, 'Monthly egress and ingress traffic.'],
|
||||
['area-spline-stacked', third, 'Storage used by media and backups, stacked.'],
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Bar charts',
|
||||
description: 'Comparison of separate categories, side by side or stacked.',
|
||||
charts: [
|
||||
['completion-tasks', third, 'Tickets solved per day, last week.'],
|
||||
['completion-tasks-6', third, 'The same tickets as horizontal bars.'],
|
||||
['completion-tasks-8', third, 'Solved and reopened tickets side by side.'],
|
||||
|
||||
['completion-tasks-9', third, 'Solved and reopened tickets in one stacked bar per day.'],
|
||||
['cache-status', third, 'Cache status of requests per day, colored by meaning.'],
|
||||
['funnel', third, 'Visitors left at each step from first visit to customer.'],
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Circular charts',
|
||||
description: 'Parts of a whole and progress toward a goal, best with a few values.',
|
||||
charts: [
|
||||
['demo-pie', third, 'Share of visits by source.'],
|
||||
['campaigns', third, 'Delivery, open and click rates of one campaign.'],
|
||||
['polar-area', third, 'Sales volume of each region.'],
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Other charts',
|
||||
description: 'Mixed series, distributions, schedules and hierarchies.',
|
||||
charts: [
|
||||
['combination', wide, 'Monthly spend as columns with leads and trials as lines.'],
|
||||
['radar', third, 'Pro and Enterprise plans rated in six areas.'],
|
||||
|
||||
['scatter', third, 'Page load time samples on desktop and mobile.'],
|
||||
['timeline', wide, 'Planned duration of each release phase.'],
|
||||
|
||||
['bubble', third, 'Search and social campaigns on three metrics.'],
|
||||
['treemap', third, 'Disk space taken by each folder.'],
|
||||
['box-plot', third, 'Spread of API response times in each month.'],
|
||||
],
|
||||
},
|
||||
]
|
||||
---
|
||||
|
||||
<DefaultLayout title="Charts" pageMenu="plugins.charts" pageLibs={['apexcharts']}>
|
||||
@@ -23,16 +117,15 @@ const demoCharts = Object.entries(chartsData as Record<string, { demo?: boolean;
|
||||
<Activity />
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="col-12">
|
||||
<Card>
|
||||
<CardHeader title="Chart with legend">
|
||||
<LegendList class="ms-auto" id="legend-demo-list">
|
||||
<Legend button color="yellow" text="Total visits" value="6.7k" data-series="Total visits" />
|
||||
<Legend button color="green" text="Unique visitors" value="4.7k" data-series="Unique visitors" />
|
||||
<Legend button color="primary" text="New users" value="1.6k" data-series="New users" />
|
||||
</LegendList>
|
||||
</CardHeader>
|
||||
<CardHeader title="Chart with legend" />
|
||||
<CardBody>
|
||||
<LegendList divided class="mb-3" id="legend-demo-list">
|
||||
<Legend button size="lg" color="yellow" text="Total visits" value="63.0k" data-series="Total visits" />
|
||||
<Legend button size="lg" color="green" text="Unique visitors" value="43.0k" data-series="Unique visitors" />
|
||||
<Legend button size="lg" color="primary" text="New users" value="13.5k" data-series="New users" />
|
||||
</LegendList>
|
||||
<Chart chartId="legend-demo" height={15} label="Site traffic" />
|
||||
<CaptureScript>
|
||||
<!-- BEGIN SCRIPT OF LEGEND -->
|
||||
@@ -56,28 +149,6 @@ const demoCharts = Object.entries(chartsData as Record<string, { demo?: boolean;
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<Card>
|
||||
<CardHeader title="Legend variants" />
|
||||
<CardBody>
|
||||
<LegendList class="mb-3">
|
||||
<Legend color="blue" text="Allowed" value="339.0k" />
|
||||
<Legend color="red" text="Denied" value="1.2k" />
|
||||
<Legend color="orange" text="Challenged" value={815} />
|
||||
</LegendList>
|
||||
<LegendList class="mb-3">
|
||||
<Legend href="#" color="purple" text="Logged" value="-" active />
|
||||
<Legend href="#" color="teal" text="Rate limited" value="-" off />
|
||||
</LegendList>
|
||||
<LegendList vertical>
|
||||
<Legend color="primary" text="This year" />
|
||||
<Legend color="secondary" text="Last year" />
|
||||
<Legend text="Free" />
|
||||
</LegendList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<SocialReferrals />
|
||||
</div>
|
||||
@@ -92,15 +163,35 @@ const demoCharts = Object.entries(chartsData as Record<string, { demo?: boolean;
|
||||
</div>
|
||||
|
||||
{
|
||||
demoCharts.map(([chartId, chart]) => (
|
||||
<div class="col-lg-6 col-xl-4">
|
||||
<Card>
|
||||
{chart.name && <CardHeader title={chart.name} />}
|
||||
<CardBody>
|
||||
<Chart chartId={chartId} height={15} label={chart.name ?? capitalize(chartId.replace(/-/g, ' '))} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
sections.map((section) => (
|
||||
<Fragment>
|
||||
<div class="col-12">
|
||||
<h2 class="page-section-title">{section.title}</h2>
|
||||
<p class="page-section-description">{section.description}</p>
|
||||
</div>
|
||||
{section.charts.map(([chartId, col, subtitle]) => (
|
||||
<div class={col}>
|
||||
<Card class="h-100">
|
||||
<CardHeader>
|
||||
<div>
|
||||
<CardTitle as="h3">{charts[chartId]?.name ?? capitalize(chartId.replace(/-/g, ' '))}</CardTitle>
|
||||
<CardSubtitle>{subtitle}</CardSubtitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{topLegend(chartId) && (
|
||||
<LegendList class="justify-content-end mb-3">
|
||||
{topLegend(chartId)?.map((item) => (
|
||||
<Legend color={item.color} text={item.text} />
|
||||
))}
|
||||
</LegendList>
|
||||
)}
|
||||
<Chart chartId={chartId} height={15} label={charts[chartId]?.name ?? capitalize(chartId.replace(/-/g, ' '))} legend={topLegend(chartId) ? false : undefined} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
))}
|
||||
</Fragment>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
+125
-17
@@ -32,6 +32,13 @@ const requests = [
|
||||
{ name: 'Rate limited', color: 'teal', value: '-' },
|
||||
]
|
||||
|
||||
const latency = [
|
||||
{ name: 'P99', color: 'primary', value: 124 },
|
||||
{ name: 'P95', color: 'azure', value: 76 },
|
||||
{ name: 'P75', color: 'green', value: 32 },
|
||||
{ name: 'P50', color: 'yellow', value: 10 },
|
||||
]
|
||||
|
||||
const storage = [
|
||||
{ name: 'Documents', color: 'primary', value: '3.5 GB', width: 44 },
|
||||
{ name: 'Media', color: 'info', value: '1.5 GB', width: 19 },
|
||||
@@ -119,13 +126,128 @@ const storage = [
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Large</CardTitle>
|
||||
<CardSubtitle>Set <code>size="lg"</code> to put a big value under the label. Add <code>unit</code> for the unit and <code>divided</code> on the list for the lines.</CardSubtitle>
|
||||
<LegendList divided>
|
||||
<Legend size="lg" color="primary" text="P99" value={124} unit="ms" />
|
||||
<Legend size="lg" color="azure" text="P95" value={76} unit="ms" />
|
||||
<Legend size="lg" color="green" text="P75" value={32} unit="ms" />
|
||||
<Legend size="lg" color="yellow" text="Requests" value="1,234" off />
|
||||
</LegendList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Under a stat</CardTitle>
|
||||
<CardSubtitle>A legend row explains the split behind a single number.</CardSubtitle>
|
||||
<Subheader>Requests</Subheader>
|
||||
<div class="h1 mb-3">341.0k</div>
|
||||
<LegendList class="border-top pt-3">
|
||||
{requests.map((item) => <Legend color={item.color} text={item.name} value={item.value} />)}
|
||||
</LegendList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Above a chart</CardTitle>
|
||||
<CardSubtitle>Large items show the latest value of each series, so the chart needs no legend of its own.</CardSubtitle>
|
||||
<LegendList divided class="mb-3">
|
||||
{latency.map((item) => <Legend size="lg" color={item.color} text={item.name} value={item.value} unit="ms" />)}
|
||||
</LegendList>
|
||||
<Chart chartId="legend-latency" height={12} label="Read latency by percentile" />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Large vertical</CardTitle>
|
||||
<CardSubtitle>Combine <code>size="lg"</code> with a <code>vertical</code> list for a side panel.</CardSubtitle>
|
||||
<LegendList vertical>
|
||||
{storage.map((item) => <Legend size="lg" color={item.color} text={item.name} value={item.value.replace(' GB', '')} unit="GB" />)}
|
||||
</LegendList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Large links</CardTitle>
|
||||
<CardSubtitle>Large items work as links and buttons too. Mark the current one with <code>active</code> and a hidden series with <code>off</code>.</CardSubtitle>
|
||||
<LegendList divided>
|
||||
<Legend href="#" size="lg" color="green" text="Passed" value={128} active />
|
||||
<Legend href="#" size="lg" color="red" text="Failed" value={4} />
|
||||
<Legend href="#" size="lg" color="yellow" text="Skipped" value={12} />
|
||||
<Legend href="#" size="lg" color="secondary" text="Pending" value={3} off />
|
||||
</LegendList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>With units</CardTitle>
|
||||
<CardSubtitle>Add <code>unit</code> to a regular item to show the unit after the value.</CardSubtitle>
|
||||
<LegendList>
|
||||
{latency.slice(0, 3).map((item) => <Legend color={item.color} text={item.name} value={item.value} unit="ms" />)}
|
||||
</LegendList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Without lines</CardTitle>
|
||||
<CardSubtitle>Leave out <code>divided</code> when the items sit in a tight space or wrap to more rows.</CardSubtitle>
|
||||
<LegendList>
|
||||
{traffic.map((item) => <Legend size="lg" color={item.color} text={item.name} value={item.value} />)}
|
||||
</LegendList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Loading</CardTitle>
|
||||
<CardSubtitle>Use <code>placeholder</code> blocks for the label and the value while the data loads.</CardSubtitle>
|
||||
<div class="legend-list legend-list-divided placeholder-glow" aria-hidden="true">
|
||||
{
|
||||
[1, 2].map(() => (
|
||||
<span class="legend legend-lg">
|
||||
<span class="legend-dot" />
|
||||
<span class="placeholder col-9" />
|
||||
<span class="legend-value">
|
||||
<span class="placeholder col-6" />
|
||||
</span>
|
||||
</span>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<Card>
|
||||
<CardHeader title="Interactive legend">
|
||||
<LegendList class="ms-auto" id="legend-page-list">
|
||||
<Legend button color="yellow" text="Total visits" value="6.7k" data-series="Total visits" />
|
||||
<Legend button color="green" text="Unique visitors" value="4.7k" data-series="Unique visitors" />
|
||||
<Legend button color="primary" text="New users" value="1.6k" data-series="New users" />
|
||||
<Legend button color="yellow" text="Total visits" value="63.0k" data-series="Total visits" />
|
||||
<Legend button color="green" text="Unique visitors" value="43.0k" data-series="Unique visitors" />
|
||||
<Legend button color="primary" text="New users" value="13.5k" data-series="New users" />
|
||||
</LegendList>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
@@ -170,20 +292,6 @@ const storage = [
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<CardTitle>Under a stat</CardTitle>
|
||||
<CardSubtitle>A legend row explains the split behind a single number.</CardSubtitle>
|
||||
<Subheader>Requests</Subheader>
|
||||
<div class="h1 mb-3">341.0k</div>
|
||||
<LegendList class="border-top pt-3">
|
||||
{requests.map((item) => <Legend color={item.color} text={item.name} value={item.value} />)}
|
||||
</LegendList>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<Card>
|
||||
<CardBody>
|
||||
|
||||
Reference in new issue
Block a user