mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-11 04:38:18 +00:00
Removing charts for now
This commit is contained in:
@@ -56,6 +56,7 @@
|
||||
--font-weight-normal: 400;
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
--tracking-tighter: -0.05em;
|
||||
--tracking-tight: -0.025em;
|
||||
--tracking-normal: 0em;
|
||||
@@ -257,6 +258,9 @@
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
@@ -349,6 +353,12 @@
|
||||
.mr-auto {
|
||||
margin-right: auto;
|
||||
}
|
||||
.mb-1 {
|
||||
margin-bottom: calc(var(--spacing) * 1);
|
||||
}
|
||||
.mb-2 {
|
||||
margin-bottom: calc(var(--spacing) * 2);
|
||||
}
|
||||
.mb-4 {
|
||||
margin-bottom: calc(var(--spacing) * 4);
|
||||
}
|
||||
@@ -448,6 +458,18 @@
|
||||
.h-14 {
|
||||
height: calc(var(--spacing) * 14);
|
||||
}
|
||||
.h-18 {
|
||||
height: calc(var(--spacing) * 18);
|
||||
}
|
||||
.h-20 {
|
||||
height: calc(var(--spacing) * 20);
|
||||
}
|
||||
.h-24 {
|
||||
height: calc(var(--spacing) * 24);
|
||||
}
|
||||
.h-48 {
|
||||
height: calc(var(--spacing) * 48);
|
||||
}
|
||||
.max-h-64 {
|
||||
max-height: calc(var(--spacing) * 64);
|
||||
}
|
||||
@@ -914,6 +936,10 @@
|
||||
--tw-leading: var(--leading-tight);
|
||||
line-height: var(--leading-tight);
|
||||
}
|
||||
.font-bold {
|
||||
--tw-font-weight: var(--font-weight-bold);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
.font-medium {
|
||||
--tw-font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--font-weight-medium);
|
||||
|
||||
@@ -1,814 +0,0 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Chart
|
||||
description: Displays the path to the current resource using a hierarchy of links.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
---
|
||||
<div class="grid gap-6">
|
||||
<div class="grid grid-cols-2 gap-6">
|
||||
<div class="card">
|
||||
<header>
|
||||
<h2>Area Chart</h2>
|
||||
<p>Showing total visitors for the last 6 months</p>
|
||||
</header>
|
||||
<section>
|
||||
<canvas id="chart-area-default"></canvas>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="grid gap-2 text-sm">
|
||||
<div class="flex items-center gap-2 leading-none font-medium">
|
||||
Trending up by 5.2% this month
|
||||
{% lucide "trending-up", { "class": "size-4" } %}
|
||||
</div>
|
||||
<div class="text-muted-foreground flex items-center gap-2 leading-none">January - June 2024</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>
|
||||
<h2>Area Chart - Stacked</h2>
|
||||
<p>Showing total visitors for the last 6 months</p>
|
||||
</header>
|
||||
<section>
|
||||
<canvas id="chart-area-stacked"></canvas>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="grid gap-2 text-sm">
|
||||
<div class="flex items-center gap-2 leading-none font-medium">
|
||||
Trending up by 5.2% this month
|
||||
{% lucide "trending-up", { "class": "size-4" } %}
|
||||
</div>
|
||||
<div class="text-muted-foreground flex items-center gap-2 leading-none">January - June 2024</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>
|
||||
<h2>Bar Chart - Multiple</h2>
|
||||
<p>Showing total visitors for the last 6 months</p>
|
||||
</header>
|
||||
<section>
|
||||
<canvas id="chart-bar-multiple"></canvas>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="grid gap-2 text-sm">
|
||||
<div class="flex items-center gap-2 leading-none font-medium">
|
||||
Trending up by 5.2% this month
|
||||
{% lucide "trending-up", { "class": "size-4" } %}
|
||||
</div>
|
||||
<div class="text-muted-foreground flex items-center gap-2 leading-none">January - June 2024</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>
|
||||
<h2>Doughnut Chart</h2>
|
||||
<p>Showing total visitors for the last 6 months</p>
|
||||
</header>
|
||||
<section class="relative">
|
||||
<div>
|
||||
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-center">
|
||||
<div class="text-3xl font-semibold">1,125</div>
|
||||
<div class="text-muted-foreground text-xs">Visitors</div>
|
||||
</div>
|
||||
<canvas id="chart-doughnut" class="max-h-[200px] mx-auto"></canvas>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>
|
||||
<h2>Bar Chart - Interactive</h2>
|
||||
<p>Showing total visitors for the last 6 months</p>
|
||||
</header>
|
||||
<section>
|
||||
<canvas id="chart-bar-interactive"></canvas>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>
|
||||
<h2>Bar Chart - Interactive</h2>
|
||||
<p>Showing total visitors for the last 6 months</p>
|
||||
</header>
|
||||
<section class="max-h-[150px]">
|
||||
<canvas id="chart-line-interactive"></canvas>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<script>
|
||||
// Helper function to MULTIPLY the alpha channel of oklch colors by a factor
|
||||
function setAlpha(colorString, alphaFactor) {
|
||||
colorString = colorString.trim();
|
||||
if (colorString.startsWith('oklch(')) {
|
||||
const match = colorString.match(/^oklch\(([^\)]+?)\s*(?:\/\s*([\d.]+)(%?)\s*)?\)$/);
|
||||
if (match && match[1]) {
|
||||
const lchPart = match[1];
|
||||
let currentAlpha = 1.0;
|
||||
|
||||
if (match[2]) {
|
||||
currentAlpha = parseFloat(match[2]);
|
||||
if (match[3] === '%') {
|
||||
currentAlpha /= 100;
|
||||
}
|
||||
}
|
||||
|
||||
let newAlpha = currentAlpha * alphaFactor;
|
||||
newAlpha = Math.max(0, Math.min(1, newAlpha));
|
||||
|
||||
return `oklch(${lchPart} / ${newAlpha.toFixed(3)})`;
|
||||
} else {
|
||||
console.warn('Failed to parse oklch string:', colorString);
|
||||
return colorString;
|
||||
}
|
||||
}
|
||||
|
||||
return colorString;
|
||||
}
|
||||
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const themeColors = {
|
||||
background: computedStyle.getPropertyValue('--background').trim(),
|
||||
border: computedStyle.getPropertyValue('--border').trim(),
|
||||
mutedForeground: computedStyle.getPropertyValue('--muted-foreground').trim(),
|
||||
foreground: computedStyle.getPropertyValue('--foreground').trim(),
|
||||
chart1: computedStyle.getPropertyValue('--chart-1').trim(),
|
||||
chart2: computedStyle.getPropertyValue('--chart-2').trim(),
|
||||
chart3: computedStyle.getPropertyValue('--chart-3').trim(),
|
||||
chart4: computedStyle.getPropertyValue('--chart-4').trim(),
|
||||
chart5: computedStyle.getPropertyValue('--chart-5').trim(),
|
||||
};
|
||||
|
||||
const getOrCreateTooltip = (chart) => {
|
||||
let tooltipEl = chart.canvas.parentNode.querySelector('div[role="tooltip"]');
|
||||
|
||||
if (!tooltipEl) {
|
||||
const tooltipHTML = `
|
||||
<div
|
||||
role="tooltip"
|
||||
class="border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl opacity-0 absolute pointer-events-none transition-all duration-400 space-y-1"
|
||||
>
|
||||
<h3 class="font-medium"></h3>
|
||||
<ul class="grid gap-1"></ul>
|
||||
</div>`;
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(tooltipHTML.trim(), 'text/html');
|
||||
tooltipEl = doc.body.firstChild;
|
||||
chart.canvas.parentNode.appendChild(tooltipEl);
|
||||
}
|
||||
return tooltipEl;
|
||||
};
|
||||
|
||||
const externalTooltipHandler = (context) => {
|
||||
const {chart, tooltip} = context;
|
||||
const chartType = chart.config._config.type;
|
||||
const tooltipEl = getOrCreateTooltip(chart);
|
||||
const titleEl = tooltipEl.querySelector('h3');
|
||||
const bodyEl = tooltipEl.querySelector('ul');
|
||||
|
||||
// Hide if no tooltip
|
||||
if (tooltip.opacity === 0) {
|
||||
tooltipEl.classList.remove('opacity-100');
|
||||
tooltipEl.classList.add('opacity-0');
|
||||
return;
|
||||
}
|
||||
|
||||
let bodyHtml = '';
|
||||
if (tooltip.body) {
|
||||
const titleLines = tooltip.title || [];
|
||||
const bodyLines = tooltip.body.map(b => b.lines);
|
||||
|
||||
if (titleEl) {
|
||||
if (chartType === 'doughnut') {
|
||||
titleEl.remove();
|
||||
} else {
|
||||
titleEl.textContent = titleLines[0] || '';
|
||||
}
|
||||
}
|
||||
|
||||
bodyLines.forEach((body, i) => {
|
||||
const colors = tooltip.labelColors[i];
|
||||
const parts = body[0].split(': ');
|
||||
const label = chartType === 'doughnut'
|
||||
? titleLines[0] || ''
|
||||
: parts[0] || '';
|
||||
const value = parts[1] || '';
|
||||
|
||||
let color;
|
||||
switch (chartType) {
|
||||
case 'doughnut':
|
||||
case 'pie':
|
||||
case 'bar':
|
||||
color = colors.backgroundColor;
|
||||
break;
|
||||
default:
|
||||
color = colors.borderColor;
|
||||
}
|
||||
|
||||
bodyHtml += `<li class="flex w-full flex-wrap items-center justify-between leading-none gap-x-2">
|
||||
<div class="flex items-center gap-x-1.5">
|
||||
<div class="shrink-0 rounded-[2px] size-2.5 block" style="background:${color}"></div>
|
||||
<span class="text-muted-foreground">${label}</span>
|
||||
</div>
|
||||
<span class="text-foreground font-medium font-mono tabular-nums">${value}</span>
|
||||
</li>`;
|
||||
});
|
||||
}
|
||||
|
||||
if (bodyEl) bodyEl.innerHTML = bodyHtml;
|
||||
|
||||
const {offsetLeft: positionX, offsetTop: positionY} = chart.canvas;
|
||||
const tooltipHeight = tooltipEl.offsetHeight;
|
||||
const tooltipWidth = tooltipEl.offsetWidth;
|
||||
const caretX = tooltip.caretX;
|
||||
const caretY = tooltip.caretY;
|
||||
const xAlign = tooltip.xAlign;
|
||||
const yAlign = tooltip.yAlign;
|
||||
const tooltipOffset = 8;
|
||||
|
||||
let left = positionX + caretX;
|
||||
let top = positionY + caretY;
|
||||
|
||||
if (xAlign === 'center') {
|
||||
left -= tooltipWidth / 2;
|
||||
} else if (xAlign === 'right') {
|
||||
left -= (tooltipWidth + tooltipOffset);
|
||||
} else { // xAlign === 'left'
|
||||
left += tooltipOffset;
|
||||
}
|
||||
|
||||
if (yAlign === 'center') {
|
||||
top -= tooltipHeight / 2;
|
||||
} else if (yAlign === 'top') {
|
||||
top -= (tooltipHeight + tooltipOffset);
|
||||
} else {
|
||||
top += tooltipOffset;
|
||||
}
|
||||
|
||||
const canvasBounds = chart.canvas.getBoundingClientRect();
|
||||
if (left < 0) {
|
||||
left = 0;
|
||||
} else if (left + tooltipWidth > canvasBounds.width) {
|
||||
left = canvasBounds.width - tooltipWidth;
|
||||
}
|
||||
if (top < 0) {
|
||||
top = 0;
|
||||
} else if (top + tooltipHeight > window.innerHeight) {
|
||||
const flippedTop = positionY + caretY - tooltipHeight - tooltipOffset;
|
||||
if (flippedTop >= 0) top = flippedTop;
|
||||
}
|
||||
|
||||
tooltipEl.classList.remove('opacity-0');
|
||||
tooltipEl.classList.add('opacity-100');
|
||||
tooltipEl.style.left = left + 'px';
|
||||
tooltipEl.style.top = top + 'px';
|
||||
};
|
||||
|
||||
let ctx = document.getElementById('chart-area-default');
|
||||
let data = {
|
||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
|
||||
datasets: [{
|
||||
label: 'Desktop',
|
||||
data: [186, 305, 237, 73, 209, 214],
|
||||
borderWidth: 1,
|
||||
backgroundColor: setAlpha(themeColors.chart1, 0.2),
|
||||
borderColor: themeColors.chart1,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: themeColors.chart1,
|
||||
pointHoverBorderColor: themeColors.chart1,
|
||||
fill: 'origin'
|
||||
}]
|
||||
};
|
||||
let config = {
|
||||
type: 'line',
|
||||
data: data,
|
||||
options: {
|
||||
tension: 0.4,
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
border: { display: false },
|
||||
grid: {
|
||||
color: setAlpha(themeColors.border, 0.5),
|
||||
lineWidth: 1
|
||||
},
|
||||
ticks: { display: false }
|
||||
},
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: { color: themeColors.mutedForeground }
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
position: 'nearest',
|
||||
external: externalTooltipHandler,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new Chart(ctx, config);
|
||||
|
||||
|
||||
ctx = document.getElementById('chart-area-stacked');
|
||||
data = {
|
||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
|
||||
datasets: [{
|
||||
label: 'Desktop',
|
||||
data: [186, 305, 237, 73, 209, 214],
|
||||
borderWidth: 1,
|
||||
backgroundColor: setAlpha(themeColors.chart1, 0.2),
|
||||
borderColor: themeColors.chart1,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: themeColors.chart1,
|
||||
pointHoverBorderColor: themeColors.chart1,
|
||||
fill: 'origin'
|
||||
}, {
|
||||
label: 'Mobile',
|
||||
data: [80, 200, 120, 190, 130, 140],
|
||||
borderWidth: 1,
|
||||
backgroundColor: setAlpha(themeColors.chart2, 0.2),
|
||||
borderColor: themeColors.chart2,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: themeColors.chart2,
|
||||
pointHoverBorderColor: themeColors.chart2,
|
||||
fill: 'origin'
|
||||
}]
|
||||
};
|
||||
config = {
|
||||
type: 'line',
|
||||
data: data,
|
||||
options: {
|
||||
tension: 0.4,
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
border: { display: false },
|
||||
grid: {
|
||||
color: setAlpha(themeColors.border, 0.5),
|
||||
lineWidth: 1
|
||||
},
|
||||
ticks: { display: false }
|
||||
},
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: { color: themeColors.mutedForeground }
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
position: 'nearest',
|
||||
external: externalTooltipHandler,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new Chart(ctx, config);
|
||||
|
||||
|
||||
ctx = document.getElementById('chart-bar-multiple');
|
||||
data = {
|
||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
|
||||
datasets: [{
|
||||
label: 'Desktop',
|
||||
data: [186, 305, 237, 73, 209, 214],
|
||||
backgroundColor: themeColors.chart1,
|
||||
borderWidth: 0,
|
||||
borderRadius: 4,
|
||||
barPercentage: 0.7,
|
||||
categoryPercentage: 0.8
|
||||
}, {
|
||||
label: 'Mobile',
|
||||
data: [80, 200, 120, 190, 130, 140],
|
||||
backgroundColor: themeColors.chart2,
|
||||
borderWidth: 0,
|
||||
borderRadius: 4,
|
||||
barPercentage: 0.7,
|
||||
categoryPercentage: 0.8
|
||||
}]
|
||||
};
|
||||
|
||||
config = {
|
||||
type: 'bar',
|
||||
data: data,
|
||||
options: {
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
border: { display: false },
|
||||
grid: {
|
||||
color: setAlpha(themeColors.border, 0.5),
|
||||
lineWidth: 1
|
||||
},
|
||||
ticks: { display: false }
|
||||
},
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: { color: themeColors.mutedForeground }
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
position: 'nearest',
|
||||
external: externalTooltipHandler,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new Chart(ctx, config);
|
||||
|
||||
const chartData = [
|
||||
{ date: "2024-04-01", desktop: 222, mobile: 150 },
|
||||
{ date: "2024-04-02", desktop: 97, mobile: 180 },
|
||||
{ date: "2024-04-03", desktop: 167, mobile: 120 },
|
||||
{ date: "2024-04-04", desktop: 242, mobile: 260 },
|
||||
{ date: "2024-04-05", desktop: 373, mobile: 290 },
|
||||
{ date: "2024-04-06", desktop: 301, mobile: 340 },
|
||||
{ date: "2024-04-07", desktop: 245, mobile: 180 },
|
||||
{ date: "2024-04-08", desktop: 409, mobile: 320 },
|
||||
{ date: "2024-04-09", desktop: 59, mobile: 110 },
|
||||
{ date: "2024-04-10", desktop: 261, mobile: 190 },
|
||||
{ date: "2024-04-11", desktop: 327, mobile: 350 },
|
||||
{ date: "2024-04-12", desktop: 292, mobile: 210 },
|
||||
{ date: "2024-04-13", desktop: 342, mobile: 380 },
|
||||
{ date: "2024-04-14", desktop: 137, mobile: 220 },
|
||||
{ date: "2024-04-15", desktop: 120, mobile: 170 },
|
||||
{ date: "2024-04-16", desktop: 138, mobile: 190 },
|
||||
{ date: "2024-04-17", desktop: 446, mobile: 360 },
|
||||
{ date: "2024-04-18", desktop: 364, mobile: 410 },
|
||||
{ date: "2024-04-19", desktop: 243, mobile: 180 },
|
||||
{ date: "2024-04-20", desktop: 89, mobile: 150 },
|
||||
{ date: "2024-04-21", desktop: 137, mobile: 200 },
|
||||
{ date: "2024-04-22", desktop: 224, mobile: 170 },
|
||||
{ date: "2024-04-23", desktop: 138, mobile: 230 },
|
||||
{ date: "2024-04-24", desktop: 387, mobile: 290 },
|
||||
{ date: "2024-04-25", desktop: 215, mobile: 250 },
|
||||
{ date: "2024-04-26", desktop: 75, mobile: 130 },
|
||||
{ date: "2024-04-27", desktop: 383, mobile: 420 },
|
||||
{ date: "2024-04-28", desktop: 122, mobile: 180 },
|
||||
{ date: "2024-04-29", desktop: 315, mobile: 240 },
|
||||
{ date: "2024-04-30", desktop: 454, mobile: 380 },
|
||||
{ date: "2024-05-01", desktop: 165, mobile: 220 },
|
||||
{ date: "2024-05-02", desktop: 293, mobile: 310 },
|
||||
{ date: "2024-05-03", desktop: 247, mobile: 190 },
|
||||
{ date: "2024-05-04", desktop: 385, mobile: 420 },
|
||||
{ date: "2024-05-05", desktop: 481, mobile: 390 },
|
||||
{ date: "2024-05-06", desktop: 498, mobile: 520 },
|
||||
{ date: "2024-05-07", desktop: 388, mobile: 300 },
|
||||
{ date: "2024-05-08", desktop: 149, mobile: 210 },
|
||||
{ date: "2024-05-09", desktop: 227, mobile: 180 },
|
||||
{ date: "2024-05-10", desktop: 293, mobile: 330 },
|
||||
{ date: "2024-05-11", desktop: 335, mobile: 270 },
|
||||
{ date: "2024-05-12", desktop: 197, mobile: 240 },
|
||||
{ date: "2024-05-13", desktop: 197, mobile: 160 },
|
||||
{ date: "2024-05-14", desktop: 448, mobile: 490 },
|
||||
{ date: "2024-05-15", desktop: 473, mobile: 380 },
|
||||
{ date: "2024-05-16", desktop: 338, mobile: 400 },
|
||||
{ date: "2024-05-17", desktop: 499, mobile: 420 },
|
||||
{ date: "2024-05-18", desktop: 315, mobile: 350 },
|
||||
{ date: "2024-05-19", desktop: 235, mobile: 180 },
|
||||
{ date: "2024-05-20", desktop: 177, mobile: 230 },
|
||||
{ date: "2024-05-21", desktop: 82, mobile: 140 },
|
||||
{ date: "2024-05-22", desktop: 81, mobile: 120 },
|
||||
{ date: "2024-05-23", desktop: 252, mobile: 290 },
|
||||
{ date: "2024-05-24", desktop: 294, mobile: 220 },
|
||||
{ date: "2024-05-25", desktop: 201, mobile: 250 },
|
||||
{ date: "2024-05-26", desktop: 213, mobile: 170 },
|
||||
{ date: "2024-05-27", desktop: 420, mobile: 460 },
|
||||
{ date: "2024-05-28", desktop: 233, mobile: 190 },
|
||||
{ date: "2024-05-29", desktop: 78, mobile: 130 },
|
||||
{ date: "2024-05-30", desktop: 340, mobile: 280 },
|
||||
{ date: "2024-05-31", desktop: 178, mobile: 230 },
|
||||
{ date: "2024-06-01", desktop: 178, mobile: 200 },
|
||||
{ date: "2024-06-02", desktop: 470, mobile: 410 },
|
||||
{ date: "2024-06-03", desktop: 103, mobile: 160 },
|
||||
{ date: "2024-06-04", desktop: 439, mobile: 380 },
|
||||
{ date: "2024-06-05", desktop: 88, mobile: 140 },
|
||||
{ date: "2024-06-06", desktop: 294, mobile: 250 },
|
||||
{ date: "2024-06-07", desktop: 323, mobile: 370 },
|
||||
{ date: "2024-06-08", desktop: 385, mobile: 320 },
|
||||
{ date: "2024-06-09", desktop: 438, mobile: 480 },
|
||||
{ date: "2024-06-10", desktop: 155, mobile: 200 },
|
||||
{ date: "2024-06-11", desktop: 92, mobile: 150 },
|
||||
{ date: "2024-06-12", desktop: 492, mobile: 420 },
|
||||
{ date: "2024-06-13", desktop: 81, mobile: 130 },
|
||||
{ date: "2024-06-14", desktop: 426, mobile: 380 },
|
||||
{ date: "2024-06-15", desktop: 307, mobile: 350 },
|
||||
{ date: "2024-06-16", desktop: 371, mobile: 310 },
|
||||
{ date: "2024-06-17", desktop: 475, mobile: 520 },
|
||||
{ date: "2024-06-18", desktop: 107, mobile: 170 },
|
||||
{ date: "2024-06-19", desktop: 341, mobile: 290 },
|
||||
{ date: "2024-06-20", desktop: 408, mobile: 450 },
|
||||
{ date: "2024-06-21", desktop: 169, mobile: 210 },
|
||||
{ date: "2024-06-22", desktop: 317, mobile: 270 },
|
||||
{ date: "2024-06-23", desktop: 480, mobile: 530 },
|
||||
{ date: "2024-06-24", desktop: 132, mobile: 180 },
|
||||
{ date: "2024-06-25", desktop: 141, mobile: 190 },
|
||||
{ date: "2024-06-26", desktop: 434, mobile: 380 },
|
||||
{ date: "2024-06-27", desktop: 448, mobile: 490 },
|
||||
{ date: "2024-06-28", desktop: 149, mobile: 200 },
|
||||
{ date: "2024-06-29", desktop: 103, mobile: 160 },
|
||||
{ date: "2024-06-30", desktop: 446, mobile: 400 },
|
||||
]
|
||||
|
||||
|
||||
data = {
|
||||
labels: chartData.map(d => d.date),
|
||||
datasets: [{
|
||||
label: 'Desktop',
|
||||
data: chartData.map(d => d.desktop),
|
||||
backgroundColor: themeColors.chart2,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0, // Stacking usually looks better without individual radius
|
||||
}, {
|
||||
label: 'Mobile',
|
||||
data: chartData.map(d => d.mobile),
|
||||
backgroundColor: themeColors.chart1,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0, // Stacking usually looks better without individual radius
|
||||
}]
|
||||
};
|
||||
|
||||
ctx = document.getElementById('chart-bar-interactive');
|
||||
|
||||
// Restore helper function to format date for labels/tooltips
|
||||
const formatDateLabel = (dateString) => {
|
||||
try {
|
||||
const date = new Date(dateString + 'T00:00:00');
|
||||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); // e.g., "Apr 1"
|
||||
} catch (e) {
|
||||
return dateString; // Fallback
|
||||
}
|
||||
};
|
||||
|
||||
// Restore data preparation with formatted date labels
|
||||
data = {
|
||||
labels: chartData.map(d => formatDateLabel(d.date)),
|
||||
datasets: [{
|
||||
label: 'Desktop',
|
||||
data: chartData.map(d => d.desktop),
|
||||
backgroundColor: themeColors.chart4,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
}, {
|
||||
label: 'Mobile',
|
||||
data: chartData.map(d => d.mobile),
|
||||
backgroundColor: themeColors.chart5,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
}]
|
||||
};
|
||||
|
||||
config = {
|
||||
type: 'bar',
|
||||
data: data,
|
||||
options: {
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
stacked: true,
|
||||
border: { display: false },
|
||||
grid: {
|
||||
color: setAlpha(themeColors.border, 0.5),
|
||||
lineWidth: 1
|
||||
},
|
||||
ticks: { display: false }
|
||||
},
|
||||
x: {
|
||||
stacked: true,
|
||||
grid: { display: false },
|
||||
ticks: {
|
||||
color: themeColors.mutedForeground,
|
||||
callback: function(value, index, ticks) {
|
||||
const label = this.getLabelForValue(value);
|
||||
return index % 7 === 0 ? label : null;
|
||||
},
|
||||
maxRotation: 0,
|
||||
minRotation: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
position: 'nearest',
|
||||
external: externalTooltipHandler,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new Chart(ctx, config);
|
||||
|
||||
// Same chart but with line chart
|
||||
// We start off from the previous config and just change the type to line
|
||||
|
||||
ctx = document.getElementById('chart-line-interactive');
|
||||
|
||||
|
||||
// Prepare data with formatted date labels AND offset for Mobile
|
||||
const desktopData = chartData.map(d => d.desktop + 400); // Store raw desktop data
|
||||
const mobileDataOffset = chartData.map(d => d.mobile); // Store offset mobile data
|
||||
|
||||
// Prepare data with gradient background
|
||||
data = {
|
||||
labels: chartData.map(d => formatDateLabel(d.date)),
|
||||
datasets: [{
|
||||
label: 'Desktop',
|
||||
data: desktopData,
|
||||
borderColor: themeColors.chart1,
|
||||
// Make backgroundColor scriptable
|
||||
backgroundColor: function(context) {
|
||||
const chart = context.chart;
|
||||
const { ctx, chartArea } = chart;
|
||||
// Use the helper function defined above
|
||||
return createGradient(ctx, chartArea, themeColors.chart1);
|
||||
},
|
||||
borderWidth: 1,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: themeColors.chart1,
|
||||
pointHoverBorderColor: themeColors.chart1,
|
||||
fill: 'start' // Fill from the start (usually y=0 or axis baseline)
|
||||
}, {
|
||||
label: 'Mobile (Offset)', // Adjusted label
|
||||
data: mobileDataOffset,
|
||||
borderColor: themeColors.chart2,
|
||||
// Make backgroundColor scriptable
|
||||
backgroundColor: function(context) {
|
||||
const chart = context.chart;
|
||||
const { ctx, chartArea } = chart;
|
||||
// Use the helper function defined above
|
||||
return createGradient(ctx, chartArea, themeColors.chart2);
|
||||
},
|
||||
borderWidth: 1,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBackgroundColor: themeColors.chart2,
|
||||
pointHoverBorderColor: themeColors.chart2,
|
||||
fill: 'start'
|
||||
}]
|
||||
};
|
||||
|
||||
// Add the createGradient helper function before the data object
|
||||
function createGradient(ctx, chartArea, color) {
|
||||
if (!chartArea) {
|
||||
return setAlpha(color, 0.1); // Fallback during initial setup
|
||||
}
|
||||
const gradient = ctx.createLinearGradient(0, chartArea.top, 0, chartArea.bottom);
|
||||
gradient.addColorStop(0, setAlpha(color, 0.5)); // Start color at top
|
||||
gradient.addColorStop(1, setAlpha(color, 0)); // End color (transparent) at bottom
|
||||
return gradient;
|
||||
}
|
||||
|
||||
// Calculate min/max for Y-axis scaling using the PLOTTED data
|
||||
const allPlottedValues = [...desktopData, ...mobileDataOffset]; // Combine actual plotted values
|
||||
const dataMin = Math.min(...allPlottedValues);
|
||||
const dataMax = Math.max(...allPlottedValues);
|
||||
const yPadding = (dataMax - dataMin) * 0.1; // Add 10% padding
|
||||
|
||||
config = {
|
||||
type: 'line',
|
||||
data: data,
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
aspectRatio: 1,
|
||||
tension: 0.4, // Keep tension from original line config
|
||||
interaction: {
|
||||
mode: 'index', // Use 'index' mode for better hover on multiple lines
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
border: { display: false },
|
||||
grid: {
|
||||
color: setAlpha(themeColors.border, 0.5),
|
||||
lineWidth: 1
|
||||
},
|
||||
ticks: { display: false },
|
||||
// Set min and max for Y-axis
|
||||
min: Math.max(0, dataMin - yPadding), // Ensure min is not negative
|
||||
max: dataMax + yPadding
|
||||
},
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: {
|
||||
color: themeColors.mutedForeground,
|
||||
// Restore callback to reduce ticks
|
||||
callback: function(value, index, ticks) {
|
||||
const label = this.getLabelForValue(value);
|
||||
return index % 7 === 0 ? label : null;
|
||||
},
|
||||
maxRotation: 0,
|
||||
minRotation: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
position: 'nearest',
|
||||
external: externalTooltipHandler,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
new Chart(ctx, config);
|
||||
|
||||
|
||||
const chartDataDoughnut = [
|
||||
{ browser: "chrome", visitors: 275, fill: themeColors.chart1 }, // Use resolved colors
|
||||
{ browser: "safari", visitors: 200, fill: themeColors.chart2 },
|
||||
{ browser: "firefox", visitors: 287, fill: themeColors.chart3 },
|
||||
{ browser: "edge", visitors: 173, fill: themeColors.chart4 },
|
||||
{ browser: "other", visitors: 190, fill: themeColors.chart5 },
|
||||
]
|
||||
|
||||
// Prepare data for the doughnut chart
|
||||
const doughnutData = {
|
||||
labels: chartDataDoughnut.map(d => d.browser),
|
||||
datasets: [{
|
||||
label: 'Visitors by Browser',
|
||||
data: chartDataDoughnut.map(d => d.visitors),
|
||||
backgroundColor: chartDataDoughnut.map(d => d.fill),
|
||||
borderColor: themeColors.background, // Use background for border
|
||||
borderWidth: 2, // Add border between segments
|
||||
hoverBorderColor: themeColors.background,
|
||||
hoverBorderWidth: 2
|
||||
}]
|
||||
};
|
||||
|
||||
// Configure the doughnut chart
|
||||
const doughnutConfig = {
|
||||
type: 'doughnut',
|
||||
data: doughnutData,
|
||||
options: {
|
||||
cutout: '50%', // Adjust hole size
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false, // Hide default legend
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false, // Keep this false as we use external handler
|
||||
position: 'nearest', // Keep this for external handler logic
|
||||
external: externalTooltipHandler, // Use the existing handler
|
||||
},
|
||||
centerText: {} // Keep the custom plugin for center text
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Assume you have a canvas with id="chart-doughnut" in your HTML
|
||||
const doughnutCtx = document.getElementById('chart-doughnut').getContext('2d');
|
||||
new Chart(doughnutCtx, doughnutConfig);
|
||||
</script>
|
||||
@@ -277,6 +277,50 @@ layout: layouts/base.njk
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>
|
||||
<h2>Report an issue</h2>
|
||||
<p>What area are you having problems with?</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<div class="flex gap-4">
|
||||
<div class="grid gap-2 flex-1">
|
||||
<label for="report-issue-area">Area</label>
|
||||
<select id="report-issue-area" class="w-full">
|
||||
<option value="team">Team</option>
|
||||
<option value="billing">Billing</option>
|
||||
<option value="account">Account</option>
|
||||
<option value="deployments">Deployments</option>
|
||||
<option value="support">Support</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid gap-2 flex-1">
|
||||
<label for="report-issue-security-level">Security Level</label>
|
||||
<select id="report-issue-security-level" class="w-full">
|
||||
<option value="1">Severity 1 (Highest)</option>
|
||||
<option value="2">Severity 2</option>
|
||||
<option value="3">Severity 3</option>
|
||||
<option value="4">Severity 4 (Lowest)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<label for="report-issue-subject">Subject</label>
|
||||
<input type="text" id="report-issue-subject" placeholder="I need help with..." class="w-full" />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<label for="report-issue-description">Description</label>
|
||||
<textarea id="report-issue-description" placeholder="Please include all information relevant to your issue." class="w-full"></textarea>
|
||||
</div>
|
||||
<footer class="flex items-center gap-4 justify-between">
|
||||
<button type="button" class="btn-sm-ghost">Cancel</button>
|
||||
<button type="button" class="btn-sm">Continue</button>
|
||||
</footer>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
Reference in New Issue
Block a user