88 lines
2.0 KiB
Vue
88 lines
2.0 KiB
Vue
<script setup lang="ts">
|
|
import { computed, PropType } from "vue";
|
|
|
|
const props = defineProps({
|
|
points: {
|
|
type: Array as PropType<number[]>,
|
|
default: () => [],
|
|
},
|
|
width: {
|
|
type: Number,
|
|
default: 280,
|
|
},
|
|
height: {
|
|
type: Number,
|
|
default: 14,
|
|
},
|
|
padding: {
|
|
type: Number,
|
|
default: 4,
|
|
},
|
|
});
|
|
|
|
const validPoints = computed(() =>
|
|
(props.points || [])
|
|
.map((value) => (Number.isFinite(value) ? Number(value) : null))
|
|
.filter((value): value is number => value !== null)
|
|
);
|
|
|
|
const pointRange = computed(() => {
|
|
const points = validPoints.value;
|
|
if (points.length === 0) {
|
|
return { min: 0, max: 1 };
|
|
}
|
|
const min = Math.min(...points);
|
|
const max = Math.max(...points);
|
|
return { min, max };
|
|
});
|
|
|
|
const svgPoints = computed(() => {
|
|
const points = validPoints.value;
|
|
const { min, max } = pointRange.value;
|
|
if (points.length === 0) {
|
|
return "";
|
|
}
|
|
const delta = max - min || 1;
|
|
const availableWidth = props.width - props.padding * 2;
|
|
const availableHeight = props.height - props.padding * 2;
|
|
const step = points.length > 1 ? availableWidth / (points.length - 1) : 0;
|
|
return points
|
|
.map((value, index) => {
|
|
const x = props.padding + step * index;
|
|
const normalized = (value - min) / delta;
|
|
const y = props.padding + availableHeight * (1 - normalized);
|
|
return `${x},${y}`;
|
|
})
|
|
.join(" ");
|
|
});
|
|
|
|
const hasPoints = computed(() => validPoints.value.length > 1);
|
|
</script>
|
|
|
|
<template>
|
|
<div class="mini-sparkline">
|
|
<svg
|
|
:width="width"
|
|
:height="height"
|
|
:viewBox="`0 0 ${width} ${height}`"
|
|
role="presentation"
|
|
aria-hidden="true"
|
|
>
|
|
<polyline
|
|
v-if="hasPoints"
|
|
:points="svgPoints"
|
|
class="sparkline-polyline"
|
|
fill="none"
|
|
/>
|
|
<line
|
|
v-else
|
|
:x1="padding"
|
|
:y1="height / 2"
|
|
:x2="width - padding"
|
|
:y2="height / 2"
|
|
class="sparkline-polyline"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</template>
|