Spaces:
Running
Running
<template> | |
<div | |
ref="chartContainerElRef" | |
class="w-full h-auto origin-top-left min-w-600px flex flex-col justify-start items-start overflow-x-auto select-none" | |
:class="`${classname}`" | |
> | |
<svg | |
ref="svgElRef" | |
class="w-full h-full" | |
@click="handleSVGElementClick" | |
></svg> | |
</div> | |
</template> | |
<script lang="ts" setup> | |
import { onMounted, onUpdated, ref } from "vue"; | |
// For customizing multi mode chart: base on create date or timeline, we have rewrited the chart.xkcd's XY chart with TypeScript. | |
// Here are some reasons about this motivation. | |
// | |
// The shortcomings of the old chart.xkcd (project link: https://github.com/timqian/chart.xkcd): | |
// 1. The X axis label string could be only formatted date or raw text; | |
// 2. It's difficult/not possible to debug and customize; | |
// | |
// The advantages of the new one: | |
// 1. Writing in native TypeScript; | |
// 2. Easy to debug chart internal; | |
// 3. Totally customizable. | |
import XYChart, { XYChartData } from "../../../packages/xy-chart"; | |
import { MIN_CHART_WIDTH } from "../../helpers/consts"; | |
const props = defineProps({ | |
classname: { | |
type: String, | |
default: "", | |
}, | |
data: { | |
type: Object as () => XYChartData, | |
}, | |
chartMode: { | |
type: String, | |
default: "Date", | |
}, | |
timeFormat: String, | |
}); | |
const chartContainerElRef = ref<HTMLDivElement>(); | |
const svgElRef = ref<SVGSVGElement>(); | |
const drawStarChart = (data: XYChartData) => { | |
if (svgElRef.value) { | |
svgElRef.value.innerHTML = ""; | |
XYChart( | |
svgElRef.value, | |
{ | |
title: "Star History", | |
xLabel: props.chartMode === "Timeline" ? "Timeline" : "Date", | |
yLabel: "GitHub Stars", | |
data: { | |
datasets: data.datasets, | |
}, | |
showDots: true, | |
}, | |
{ | |
xTickLabelType: props.chartMode === "Date" ? "Date" : "Number", | |
envType: "browser", | |
} | |
); | |
} | |
}; | |
onMounted(() => { | |
if (props.data) { | |
drawStarChart(props.data); | |
} | |
// Scale chart to a suitable mobile view. | |
if (window.innerWidth < MIN_CHART_WIDTH) { | |
if (chartContainerElRef.value) { | |
const scaleRate = window.innerWidth / MIN_CHART_WIDTH; | |
chartContainerElRef.value.style.marginTop = "8px"; | |
chartContainerElRef.value.style.transform = `scale(${scaleRate})`; | |
if (chartContainerElRef.value.parentElement) { | |
chartContainerElRef.value.parentElement.style.minHeight = "0"; | |
chartContainerElRef.value.parentElement.style.height = `${ | |
chartContainerElRef.value.clientHeight * scaleRate + 16 | |
}px`; | |
} | |
} | |
} | |
}); | |
onUpdated(() => { | |
if (props.data) { | |
drawStarChart(props.data); | |
} | |
}); | |
const handleSVGElementClick = () => { | |
// Maybe we can capture the clicked svg element to expand chart functions. | |
}; | |
</script> | |