Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions resources/assets/js/chart-theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,21 +213,28 @@ export function getAxisThemeConfig(mode) {
const config = {
light: {
x: {
color: "rgba(238,243,245,1)", // theme-secondary-200
color: "rgba(219, 222, 229, 1)", // theme-secondary-300
},
y: {
color: "rgba(238,243,245,1)", // theme-secondary-200
color: "rgba(219, 222, 229, 1)", // theme-secondary-300
},
},
dark: {
x: {
color: "rgba(60,66,73,1)", // theme-secondary-800
color: "rgba(61, 68, 77, 1)", // theme-dark-700
},
y: {
color: "rgba(60,66,73,1)", // theme-secondary-800
color: "rgba(61, 68, 77, 1)", // theme-dark-700
},
},
};

return config[mode];
}

export function getCrosshairColor(mode) {
return {
light: "rgba(99, 114, 130, 1)", // theme-secondary-700,
dark: "rgba(164, 177, 188, 1)", // theme-dark-200,
}[mode];
}
119 changes: 89 additions & 30 deletions resources/assets/js/chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import {
makeGradient,
getFontConfig,
getAxisThemeConfig,
getCrosshairColor,
} from "./chart-theme";

import { Chart, registerables } from "chart.js";
import { Chart, registerables, LineController } from "chart.js";

Chart.register(...registerables);

Expand All @@ -18,6 +19,9 @@ Chart.register(...registerables);
* @param {Array<Object{name, mode}>} theme
* @param {Number} time
* @param {String} currency
* @param {Number} yPadding
* @param {Number} xPadding
* @param {Boolean} showCrosshair
* @return {Object}
*/
const CustomChart = (
Expand All @@ -28,8 +32,64 @@ const CustomChart = (
tooltips,
theme,
time,
currency
currency,
yPadding = 15,
xPadding = 10,
showCrosshair = false
) => {
const themeMode = () => {
if (theme.mode === "auto") {
return ["light", "dark"].includes(localStorage.theme)
? localStorage.theme
: "light";
}

return theme.mode;
};

class LineWithCrosshair extends LineController {
draw() {
super.draw(arguments);

// Based on https://stackoverflow.com/a/70245628/3637093
if (
this.chart.tooltip._active &&
this.chart.tooltip._active.length
) {
const activePoint = this.chart.tooltip._active[0].element;
const ctx = this.chart.ctx;
const x = activePoint.x;
const y = activePoint.y;
const topY = this.chart.legend.bottom;
const bottomY = this.chart.chartArea.bottom;
const left = this.chart.chartArea.left;
const right = this.chart.chartArea.right;

ctx.save();
ctx.lineWidth = 1;
ctx.setLineDash([3, 3]);
ctx.strokeStyle = getCrosshairColor(themeMode());

ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.stroke();

ctx.beginPath();
ctx.moveTo(left, y);
ctx.lineTo(right, y);
ctx.stroke();

ctx.restore();
}
}
}

LineWithCrosshair.id = "lineWithCrosshair";
LineWithCrosshair.defaults = LineController.defaults;

Chart.register(LineWithCrosshair);

return {
time: time,
chart: null,
Expand Down Expand Up @@ -71,16 +131,6 @@ const CustomChart = (
this.chart.update();
},

themeMode() {
if (theme.mode === "auto") {
return ["light", "dark"].includes(localStorage.theme)
? localStorage.theme
: "light";
}

return theme.mode;
},

loadData() {
const datasets = [];

Expand All @@ -95,7 +145,7 @@ const CustomChart = (

values.forEach((value, key) => {
let themeName = value.type === "bar" ? "grey" : theme.name;
let graphic = getInfoFromThemeName(themeName, this.themeMode());
let graphic = getInfoFromThemeName(themeName, themeMode());
let backgroundColor = graphic.backgroundColor;
if (backgroundColor.hasOwnProperty("gradient")) {
backgroundColor = makeGradient(
Expand All @@ -104,12 +154,17 @@ const CustomChart = (
);
}

let chartType = value.type || "line";
if (showCrosshair && chartType === "line") {
chartType = "lineWithCrosshair";
}

datasets.push({
fill: true,
stack: "combined",
label: value.name || "",
data: value.data || value,
type: value.type || "line",
type: chartType,
backgroundColor:
value.type === "bar"
? graphic.borderColor
Expand Down Expand Up @@ -149,17 +204,19 @@ const CustomChart = (
display: grid && key === 0,
type: "linear",
ticks: {
...getFontConfig("axis", this.themeMode()),
padding: 15,
...getFontConfig("axis", themeMode()),
padding: yPadding,
display: grid && key === 0,
suggestedMax: range.max,
callback: (value, index, data) =>
this.getCurrencyValue(value),
},
grid: {
drawTicks: false,
display: grid && key === 0,
drawBorder: false,
color: getAxisThemeConfig(this.themeMode()).y.color,
borderDash: [3, 3],
color: getAxisThemeConfig(themeMode()).y.color,
},
});
});
Expand All @@ -175,9 +232,14 @@ const CustomChart = (
}

this.$watch("time", () => this.updateChart());
window.addEventListener("resize", () =>
window.livewire.emit("updateChart")
);

window.addEventListener("resize", () => {
try {
this.chart.resize();
} catch (e) {
// Hide resize errors - they don't seem to cause any issues
}
});

const data = {
labels: labels,
Expand Down Expand Up @@ -206,13 +268,10 @@ const CustomChart = (
label: (context) =>
this.getCurrencyValue(context.raw),
labelTextColor: (context) =>
getFontConfig("tooltip", this.themeMode())
.fontColor,
getFontConfig("tooltip", themeMode()).fontColor,
},
backgroundColor: getFontConfig(
"tooltip",
this.themeMode()
).backgroundColor,
backgroundColor: getFontConfig("tooltip", themeMode())
.backgroundColor,
},
},
hover: {
Expand All @@ -232,13 +291,13 @@ const CustomChart = (
ticks: {
display: grid,
includeBounds: true,
padding: 10,
...getFontConfig("axis", this.themeMode()),
padding: xPadding,
...getFontConfig("axis", themeMode()),
},
grid: {
display: grid,
display: false,
drawBorder: false,
color: getAxisThemeConfig(this.themeMode()).x.color,
color: getAxisThemeConfig(themeMode()).x.color,
},
},
},
Expand Down
11 changes: 10 additions & 1 deletion resources/views/chart.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
'grid' => false,
'tooltips' => false,
'theme' => collect(['name' => 'grey', 'mode' => 'light']),
'yPadding' => 15,
'xPadding' => 10,
'showCrosshair' => false,
])

<div
Expand All @@ -21,11 +24,17 @@
{{ json_encode($theme->toArray()) }},
'{{ time() }}',
'{{ $currency }}',
{{ $yPadding }},
{{ $xPadding }},
{{ $showCrosshair ? 'true' : 'false' }}
)"
wire:key="{{ $id.time() }}"
{{ $attributes->only('class') }}
>
<div wire:ignore class="relative w-full h-full">
<div
class="relative w-full h-full"
wire:ignore
>
<canvas
x-ref="{{ $id }}"
@if($canvasClass) class="{{ $canvasClass }}" @endif
Expand Down