Improved graphs (#120)
* Complete chart overhaul * Update package.json * Update pnpm-lock.yaml * run lint * whoops * Update pnpm-lock.yaml * Update analytics.md * Try again? * Update Chart.vue * Added Compact/Spark charts * Added number formatting and cleanup * Touch ups * improve default colors * removed unnecessary tooltip
This commit is contained in:
parent
16a39b364c
commit
c056c4e79e
@ -2,136 +2,168 @@
|
||||
|
||||
<DemoContainer>
|
||||
<client-only>
|
||||
<LineChart
|
||||
:data="{
|
||||
labels: [
|
||||
'2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05',
|
||||
'2021-01-06', '2021-01-07', '2021-01-08', '2021-01-09', '2021-01-10',
|
||||
'2021-01-11', '2021-01-12', '2021-01-13', '2021-01-14', '2021-01-15',
|
||||
'2021-01-16', '2021-01-17'
|
||||
],
|
||||
data: [
|
||||
{
|
||||
title: 'Spirit',
|
||||
color: 16711680,
|
||||
data: [120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280],
|
||||
},
|
||||
{
|
||||
title: 'Ad Astra',
|
||||
color: 65280,
|
||||
data: [150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230],
|
||||
},
|
||||
{
|
||||
title: 'Tempad',
|
||||
color: 255,
|
||||
data: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212],
|
||||
},
|
||||
]
|
||||
}"
|
||||
/>
|
||||
<BarChart
|
||||
:data="{
|
||||
labels: [
|
||||
'2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05',
|
||||
'2021-01-06', '2021-01-07', '2021-01-08', '2021-01-09', '2021-01-10',
|
||||
'2021-01-11', '2021-01-12', '2021-01-13', '2021-01-14', '2021-01-15',
|
||||
'2021-01-16', '2021-01-17'
|
||||
],
|
||||
data: [
|
||||
{
|
||||
title: 'Spirit',
|
||||
color: 16711680,
|
||||
data: [120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280],
|
||||
},
|
||||
{
|
||||
title: 'Ad Astra',
|
||||
color: 65280,
|
||||
data: [150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230],
|
||||
},
|
||||
{
|
||||
title: 'Tempad',
|
||||
color: 255,
|
||||
data: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212],
|
||||
},
|
||||
]
|
||||
}"
|
||||
/>
|
||||
<PieChart
|
||||
:data="{
|
||||
title: 'Downloads',
|
||||
data: [
|
||||
{
|
||||
title: 'Spirit',
|
||||
color: 16711680, // Red in decimal (equivalent to #FF0000 in hexadecimal)
|
||||
data: 120, // Example download numbers for the three dates
|
||||
},
|
||||
{
|
||||
title: 'Ad Astra',
|
||||
color: 65280, // Green in decimal (equivalent to #00FF00 in hexadecimal)
|
||||
data: 150, // Example download numbers for the three dates
|
||||
},
|
||||
{
|
||||
title: 'Tempad',
|
||||
color: 255, // Blue in decimal (equivalent to #0000FF in hexadecimal)
|
||||
data: 180, // Example download numbers for the three dates
|
||||
},
|
||||
],
|
||||
}"
|
||||
/>
|
||||
<Chart
|
||||
name="Chart"
|
||||
type="bar"
|
||||
:stacked="true"
|
||||
:labels="[
|
||||
'2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05',
|
||||
'2021-01-06', '2021-01-07', '2021-01-08', '2021-01-09', '2021-01-10',
|
||||
'2021-01-11', '2021-01-12', '2021-01-13', '2021-01-14', '2021-01-15',
|
||||
'2021-01-16', '2021-01-17'
|
||||
]"
|
||||
:data="[
|
||||
{
|
||||
name: 'Spirit',
|
||||
data: [120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280],
|
||||
},
|
||||
{
|
||||
name: 'Ad Astra',
|
||||
data: [150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230],
|
||||
},
|
||||
{
|
||||
name: 'Tempad',
|
||||
data: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212],
|
||||
},
|
||||
]"
|
||||
:colors="['#FF0000', '#00FF00', '#0000FF']"
|
||||
suffix="<svg xmlns='http://www.w3.org/2000/svg' class='h-6 w-6' fill='none' viewBox='0 0 24 24' stroke='currentColor' stroke-width='2'><path stroke-linecap='round' stroke-linejoin='round' d='M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4' /></svg>"
|
||||
/>
|
||||
</client-only>
|
||||
</DemoContainer>
|
||||
<DemoContainer>
|
||||
<client-only>
|
||||
<Chart
|
||||
name="Chart"
|
||||
type="line"
|
||||
:labels="[
|
||||
'2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05',
|
||||
'2021-01-06', '2021-01-07', '2021-01-08', '2021-01-09', '2021-01-10',
|
||||
'2021-01-11', '2021-01-12', '2021-01-13', '2021-01-14', '2021-01-15',
|
||||
'2021-01-16', '2021-01-17'
|
||||
]"
|
||||
:data="[
|
||||
{
|
||||
name: 'Spirit',
|
||||
data: [120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 1280],
|
||||
},
|
||||
{
|
||||
name: 'Ad Astra',
|
||||
data: [150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 1230],
|
||||
},
|
||||
{
|
||||
name: 'Tempad',
|
||||
data: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212],
|
||||
},
|
||||
]"
|
||||
:colors="['#FF0000', '#00FF00', '#0000FF']"
|
||||
suffix="<svg xmlns='http://www.w3.org/2000/svg' class='h-6 w-6' fill='none' viewBox='0 0 24 24' stroke='currentColor' stroke-width='2'><path stroke-linecap='round' stroke-linejoin='round' d='M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4' /></svg>"
|
||||
/>
|
||||
</client-only>
|
||||
</DemoContainer>
|
||||
<DemoContainer>
|
||||
<client-only>
|
||||
<Chart
|
||||
name="Chart"
|
||||
:labels="[
|
||||
'2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05',
|
||||
'2021-01-06', '2021-01-07', '2021-01-08', '2021-01-09', '2021-01-10',
|
||||
'2021-01-11', '2021-01-12', '2021-01-13', '2021-01-14', '2021-01-15',
|
||||
'2021-01-16', '2021-01-17'
|
||||
]"
|
||||
:data="[
|
||||
{
|
||||
name: 'Downloads',
|
||||
data: [120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280],
|
||||
},
|
||||
{
|
||||
name: 'Revenue',
|
||||
data: [150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230],
|
||||
},
|
||||
{
|
||||
name: 'Page views',
|
||||
data: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212],
|
||||
},
|
||||
]"
|
||||
hide-total
|
||||
suffix="<svg xmlns='http://www.w3.org/2000/svg' class='h-6 w-6' fill='none' viewBox='0 0 24 24' stroke='currentColor' stroke-width='2'><path stroke-linecap='round' stroke-linejoin='round' d='M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4' /></svg>"
|
||||
>
|
||||
Slot for title stuff
|
||||
<Chips :items="['option 1', 'option 3']" />
|
||||
<template #toolbar>
|
||||
<Button>
|
||||
<PlusIcon />
|
||||
Slot for toolbar stuff
|
||||
</Button>
|
||||
</template>
|
||||
</Chart>
|
||||
</client-only>
|
||||
</DemoContainer>
|
||||
<DemoContainer>
|
||||
<client-only>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--gap-md);">
|
||||
<CompactChart
|
||||
v-for="i in 4"
|
||||
title="Downloads"
|
||||
value="10,230"
|
||||
:labels="[
|
||||
'2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05',
|
||||
'2021-01-06', '2021-01-07', '2021-01-08', '2021-01-09', '2021-01-10',
|
||||
'2021-01-11', '2021-01-12', '2021-01-13', '2021-01-14', '2021-01-15',
|
||||
'2021-01-16', '2021-01-17'
|
||||
]"
|
||||
:data="[
|
||||
{
|
||||
name: 'Downloads',
|
||||
data: [240, 180, 210, 160, 250, 130, 220, 270, 120, 260, 200, 230, 140, 280, 190, 150, 170],
|
||||
}
|
||||
]"
|
||||
suffix="<svg xmlns='http://www.w3.org/2000/svg' class='h-6 w-6' fill='none' viewBox='0 0 24 24' stroke='currentColor' stroke-width='2'><path stroke-linecap='round' stroke-linejoin='round' d='M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4' /></svg>"
|
||||
/>
|
||||
</div>
|
||||
</client-only>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<LineChart
|
||||
:formatLabel="(label) => doFormattingThings(label)"
|
||||
:data="{
|
||||
labels: [
|
||||
'2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05',
|
||||
'2021-01-06', '2021-01-07', '2021-01-08', '2021-01-09', '2021-01-10',
|
||||
'2021-01-11', '2021-01-12', '2021-01-13', '2021-01-14', '2021-01-15',
|
||||
'2021-01-16', '2021-01-17'
|
||||
],
|
||||
data: [
|
||||
{
|
||||
title: 'Spirit',
|
||||
color: 16711680,
|
||||
data: [120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280],
|
||||
},
|
||||
{
|
||||
title: 'Ad Astra',
|
||||
color: 65280,
|
||||
data: [150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230],
|
||||
},
|
||||
{
|
||||
title: 'Tempad',
|
||||
color: 255,
|
||||
data: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212],
|
||||
},
|
||||
]
|
||||
}"
|
||||
/>
|
||||
<Chart
|
||||
name="Chart name"
|
||||
:labels="['array', 'of', 'labels', 'for', 'x-axis', 'typically', 'dates']"
|
||||
:data="[
|
||||
{
|
||||
name: 'Spirit',
|
||||
data: ['array', 'of', 'data', 'equal', 'length', 'to', 'x-axis'],
|
||||
},
|
||||
...
|
||||
]"
|
||||
:colors="['array', 'of', 'colors', 'for', 'each', 'series/dataset']"
|
||||
prefix="string or svg icon to append to each data point"
|
||||
suffix="string or svg icon to append to each data point"
|
||||
type="bar|line"
|
||||
:stacked="true|false (default: false) (determines whether or not values overlap/sidebyside instead of stacked)"
|
||||
:hideTotal="true|false (default: false) (hide total value in tooltip)"
|
||||
:hideToolbar="true|false (default: false) (hide toolbar)"
|
||||
:hideLegend="true|false (default: false) (hide legend)"
|
||||
>
|
||||
... slot for title stuff
|
||||
<template #toolbar>
|
||||
... slot for toolbar stuff
|
||||
</template>
|
||||
</Chart>
|
||||
```
|
||||
|
||||
```vue
|
||||
<PieChart
|
||||
:data="{
|
||||
title: 'Downloads',
|
||||
data: [
|
||||
{
|
||||
title: 'Spirit',
|
||||
color: 16711680, // Red in decimal (equivalent to #FF0000 in hexadecimal)
|
||||
data: 120, // Example download numbers for the three dates
|
||||
},
|
||||
{
|
||||
title: 'Ad Astra',
|
||||
color: 65280, // Green in decimal (equivalent to #00FF00 in hexadecimal)
|
||||
data: 150, // Example download numbers for the three dates
|
||||
},
|
||||
{
|
||||
title: 'Tempad',
|
||||
color: 255, // Blue in decimal (equivalent to #0000FF in hexadecimal)
|
||||
data: 180, // Example download numbers for the three dates
|
||||
},
|
||||
],
|
||||
}"
|
||||
<CompactChart
|
||||
title="Chart title"
|
||||
value="Chart value"
|
||||
:labels="['array', 'of', 'labels', 'for', 'x-axis', 'typically', 'dates']"
|
||||
:data="[
|
||||
{
|
||||
name: 'Spirit',
|
||||
data: ['array', 'of', 'data', 'equal', 'length', 'to', 'x-axis'],
|
||||
},
|
||||
...
|
||||
]"
|
||||
prefix="string or svg icon to append to each data point"
|
||||
suffix="string or svg icon to append to each data point"
|
||||
/>
|
||||
```
|
||||
|
||||
1
lib/assets/icons/zoom-in.svg
Normal file
1
lib/assets/icons/zoom-in.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zoom-in"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="11" x2="11" y1="8" y2="14"/><line x1="8" x2="14" y1="11" y2="11"/></svg>
|
||||
|
After Width: | Height: | Size: 369 B |
1
lib/assets/icons/zoom-out.svg
Normal file
1
lib/assets/icons/zoom-out.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zoom-out"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="8" x2="14" y1="11" y2="11"/></svg>
|
||||
|
After Width: | Height: | Size: 332 B |
@ -165,6 +165,13 @@ svg {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
.chart {
|
||||
svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.button-animation {
|
||||
transition: opacity 0.5s ease-in-out, filter 0.2s ease-in-out, transform 0.05s ease-in-out,
|
||||
outline 0.2s ease-in-out;
|
||||
|
||||
@ -1,115 +0,0 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Bar } from 'vue-chartjs'
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
BarElement,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
} from 'chart.js'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
formatLabels: {
|
||||
type: Function,
|
||||
default: (label) => dayjs(label).format('MMM D'),
|
||||
},
|
||||
})
|
||||
|
||||
const decimalToRgba = (decimalColor, alpha = 0.75) => {
|
||||
const red = (decimalColor >> 16) & 255
|
||||
const green = (decimalColor >> 8) & 255
|
||||
const blue = decimalColor & 255
|
||||
|
||||
return `rgba(${red}, ${green}, ${blue}, ${alpha})`
|
||||
}
|
||||
|
||||
const chartData = ref({
|
||||
labels: props.data.labels.map((date) => props.formatLabels(date)),
|
||||
datasets: props.data.data.map((project) => ({
|
||||
label: project.title,
|
||||
borderColor: decimalToRgba(project.color, 1),
|
||||
borderWidth: 2,
|
||||
borderSkipped: 'bottom',
|
||||
backgroundColor: decimalToRgba(project.color, 0.5),
|
||||
data: project.data,
|
||||
})),
|
||||
})
|
||||
|
||||
const chartOptions = ref({
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
grid: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-button-bg'),
|
||||
},
|
||||
ticks: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
},
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
grid: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-button-bg'),
|
||||
},
|
||||
ticks: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'right',
|
||||
align: 'start',
|
||||
labels: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
font: {
|
||||
size: 12,
|
||||
family: 'Inter',
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
position: 'nearest',
|
||||
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue(
|
||||
'--color-raised-bg'
|
||||
),
|
||||
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--color-button-bg'),
|
||||
borderWidth: 1,
|
||||
titleColor: getComputedStyle(document.documentElement).getPropertyValue('--color-contrast'),
|
||||
titleFont: {
|
||||
size: 16,
|
||||
family: 'Inter',
|
||||
},
|
||||
bodyColor: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
bodyFont: {
|
||||
size: 12,
|
||||
family: 'Inter',
|
||||
},
|
||||
boxPadding: 8,
|
||||
intersect: false,
|
||||
padding: 12,
|
||||
displayColors: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Bar id="my-chart-id" :options="chartOptions" :data="chartData" />
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
417
lib/components/chart/Chart.vue
Normal file
417
lib/components/chart/Chart.vue
Normal file
@ -0,0 +1,417 @@
|
||||
<script setup>
|
||||
import dayjs from 'dayjs'
|
||||
import { Button, DownloadIcon, UpdatedIcon, Checkbox, formatNumber } from '@'
|
||||
import { defineAsyncComponent, ref } from 'vue'
|
||||
|
||||
const VueApexCharts = defineAsyncComponent(() => import('vue3-apexcharts'))
|
||||
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
labels: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
formatLabels: {
|
||||
type: Function,
|
||||
default: (label) => dayjs(label).format('MMM D'),
|
||||
},
|
||||
colors: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
'var(--color-brand)',
|
||||
'var(--color-blue)',
|
||||
'var(--color-purple)',
|
||||
'var(--color-red)',
|
||||
'var(--color-orange)',
|
||||
],
|
||||
},
|
||||
prefix: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
suffix: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hideToolbar: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hideLegend: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
stacked: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'bar',
|
||||
},
|
||||
hideTotal: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const chartOptions = ref({
|
||||
chart: {
|
||||
id: props.name,
|
||||
fontFamily:
|
||||
'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif',
|
||||
foreColor: 'var(--color-base)',
|
||||
selection: {
|
||||
enabled: true,
|
||||
fill: {
|
||||
color: 'var(--color-brand)',
|
||||
},
|
||||
},
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
stacked: props.stacked,
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
categories: props.labels,
|
||||
labels: {
|
||||
style: {
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
},
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
colors: props.colors,
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
background: {
|
||||
enabled: true,
|
||||
borderRadius: 20,
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
borderColor: 'var(--color-button-bg)',
|
||||
tickColor: 'var(--color-button-bg)',
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
markers: {
|
||||
size: 0,
|
||||
strokeColor: 'var(--color-contrast)',
|
||||
strokeWidth: 3,
|
||||
strokeOpacity: 1,
|
||||
fillOpacity: 1,
|
||||
hover: {
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
columnWidth: '80%',
|
||||
endingShape: 'rounded',
|
||||
borderRadius: 5,
|
||||
borderRadiusApplication: 'end',
|
||||
borderRadiusWhenStacked: 'last',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
||||
console.log(seriesIndex, w)
|
||||
return (
|
||||
'<div class="bar-tooltip">' +
|
||||
'<div class="seperated-entry title">' +
|
||||
'<div class="label">' +
|
||||
props.formatLabels(w.globals.lastXAxis.categories[dataPointIndex]) +
|
||||
'</div>' +
|
||||
(!props.hideTotal
|
||||
? `<div class="value">
|
||||
${props.prefix}
|
||||
${formatNumber(series.reduce((a, b) => a + b[dataPointIndex], 0).toString(), false)}
|
||||
${props.suffix}
|
||||
</div>`
|
||||
: ``) +
|
||||
'</div><hr class="card-divider" />' +
|
||||
series
|
||||
.map((value, index) =>
|
||||
value[dataPointIndex] > 0
|
||||
? `<div class="list-entry">
|
||||
<span class="circle" style="background-color: ${w.globals.colors[index]}"> </span>
|
||||
<div class="label">
|
||||
${w.globals.seriesNames[index]}
|
||||
</div>
|
||||
<div class="value">
|
||||
${props.prefix}
|
||||
${formatNumber(value[dataPointIndex], false)}
|
||||
${props.suffix}
|
||||
</div>
|
||||
</div>`
|
||||
: ''
|
||||
)
|
||||
.reverse()
|
||||
.reduce((a, b) => a + b) +
|
||||
'</div>'
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const chart = ref(null)
|
||||
|
||||
const legendValues = ref(
|
||||
[...props.data].map((project, index) => {
|
||||
return { name: project.name, visible: true, color: props.colors[index] }
|
||||
})
|
||||
)
|
||||
|
||||
const flipLegend = (legend, newVal) => {
|
||||
legend.visible = newVal
|
||||
chart.value.toggleSeries(legend.name)
|
||||
}
|
||||
|
||||
const downloadCSV = () => {
|
||||
const csvContent =
|
||||
'data:text/csv;charset=utf-8,' +
|
||||
props.labels.join(',') +
|
||||
'\n' +
|
||||
props.data.map((project) => project.data.join(',')).reduce((a, b) => a + '\n' + b)
|
||||
|
||||
const encodedUri = encodeURI(csvContent)
|
||||
const link = document.createElement('a')
|
||||
link.setAttribute('href', encodedUri)
|
||||
link.setAttribute('download', `${props.name}.csv`)
|
||||
document.body.appendChild(link) // Required for FF
|
||||
|
||||
link.click()
|
||||
}
|
||||
|
||||
const resetChart = () => {
|
||||
chart.value.resetSeries()
|
||||
legendValues.value.forEach((legend) => {
|
||||
legend.visible = true
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
resetChart,
|
||||
downloadCSV,
|
||||
flipLegend,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bar-chart">
|
||||
<div class="title-bar">
|
||||
<slot />
|
||||
<div v-if="!hideToolbar" class="toolbar">
|
||||
<Button v-tooltip="'Download data as CSV'" icon-only @click="downloadCSV">
|
||||
<DownloadIcon />
|
||||
</Button>
|
||||
<Button v-tooltip="'Reset chart'" icon-only @click="resetChart">
|
||||
<UpdatedIcon />
|
||||
</Button>
|
||||
<slot name="toolbar" />
|
||||
</div>
|
||||
</div>
|
||||
<VueApexCharts ref="chart" :type="type" :options="chartOptions" :series="data" class="chart" />
|
||||
<div v-if="!hideLegend" class="legend">
|
||||
<Checkbox
|
||||
v-for="legend in legendValues"
|
||||
:key="legend.name"
|
||||
class="legend-checkbox"
|
||||
:style="`--color: ${legend.color};`"
|
||||
:model-value="legend.visible"
|
||||
@update:model-value="(newVal) => flipLegend(legend, newVal)"
|
||||
>
|
||||
{{ legend.name }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bar-chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--gap-xs);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--gap-xs);
|
||||
z-index: 1;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-menu),
|
||||
:deep(.apexcharts-tooltip),
|
||||
:deep(.apexcharts-yaxistooltip) {
|
||||
background: var(--color-raised-bg) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
box-shadow: var(--shadow-floating) !important;
|
||||
font-size: var(--font-size-nm) !important;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-grid-borders) {
|
||||
line {
|
||||
stroke: var(--color-button-bg) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.apexcharts-yaxistooltip),
|
||||
:deep(.apexcharts-xaxistooltip) {
|
||||
background: var(--color-raised-bg) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
font-size: var(--font-size-nm) !important;
|
||||
color: var(--color-base) !important;
|
||||
|
||||
.apexcharts-xaxistooltip-text {
|
||||
font-size: var(--font-size-nm) !important;
|
||||
color: var(--color-base) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.apexcharts-yaxistooltip-left:after) {
|
||||
border-left-color: var(--color-raised-bg) !important;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-yaxistooltip-left:before) {
|
||||
border-left-color: var(--color-button-bg) !important;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-xaxistooltip-bottom:after) {
|
||||
border-bottom-color: var(--color-raised-bg) !important;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-xaxistooltip-bottom:before) {
|
||||
border-bottom-color: var(--color-button-bg) !important;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-menu-item) {
|
||||
border-radius: var(--radius-sm) !important;
|
||||
padding: var(--gap-xs) var(--gap-sm) !important;
|
||||
|
||||
&:hover {
|
||||
transition: all 0.3s !important;
|
||||
color: var(--color-accent-contrast) !important;
|
||||
background: var(--color-brand) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.apexcharts-tooltip) {
|
||||
.bar-tooltip {
|
||||
min-width: 10rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-xs);
|
||||
padding: var(--gap-sm);
|
||||
|
||||
.card-divider {
|
||||
margin: var(--gap-xs) 0;
|
||||
}
|
||||
|
||||
.seperated-entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--color-contrast);
|
||||
}
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--gap-xs);
|
||||
color: var(--color-base);
|
||||
}
|
||||
|
||||
.list-entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: var(--font-size-sm);
|
||||
|
||||
.value {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.label {
|
||||
margin-right: var(--gap-xl);
|
||||
}
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: var(--gap-sm);
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--gap-md);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.legend-checkbox :deep(.checkbox.checked) {
|
||||
background-color: var(--color);
|
||||
}
|
||||
</style>
|
||||
278
lib/components/chart/CompactChart.vue
Normal file
278
lib/components/chart/CompactChart.vue
Normal file
@ -0,0 +1,278 @@
|
||||
<script setup>
|
||||
import { Card, formatNumber } from '@'
|
||||
import { defineAsyncComponent, ref } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
const VueApexCharts = defineAsyncComponent(() => import('vue3-apexcharts'))
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
labels: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
prefix: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
suffix: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
//no grid lines, no toolbar, no legend, no data labels
|
||||
const chartOptions = ref({
|
||||
chart: {
|
||||
id: props.title,
|
||||
fontFamily:
|
||||
'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif',
|
||||
foreColor: 'var(--color-base)',
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
sparkline: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight',
|
||||
},
|
||||
fill: {
|
||||
colors: ['var(--color-brand)'],
|
||||
type: 'gradient',
|
||||
opacity: 1,
|
||||
gradient: {
|
||||
shade: 'light',
|
||||
type: 'vertical',
|
||||
shadeIntensity: 0,
|
||||
gradientToColors: ['var(--color-brand)'],
|
||||
inverseColors: true,
|
||||
opacityFrom: 0.5,
|
||||
opacityTo: 0,
|
||||
stops: [0, 100],
|
||||
colorStops: [],
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
show: false,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
colors: ['var(--color-brand)'],
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
categories: props.labels,
|
||||
labels: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
show: false,
|
||||
},
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
||||
console.log(seriesIndex, w)
|
||||
return (
|
||||
'<div class="bar-tooltip">' +
|
||||
series
|
||||
.map((value) =>
|
||||
value[dataPointIndex] > 0
|
||||
? `<div class="list-entry">
|
||||
<div class="label">
|
||||
<span class="circle" style="background-color: ${w.globals.colors[0]}"> </span>
|
||||
${dayjs(w.globals.lastXAxis.categories[dataPointIndex]).format('MMM D')}
|
||||
</div>
|
||||
<div class="divider">
|
||||
|
|
||||
</div>
|
||||
<div class="value">
|
||||
${props.prefix}
|
||||
${formatNumber(value[dataPointIndex], false)}
|
||||
${props.suffix}
|
||||
</div>
|
||||
</div>`
|
||||
: ''
|
||||
)
|
||||
.reverse()
|
||||
.reduce((a, b) => a + b) +
|
||||
'</div>'
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="compact-chart">
|
||||
<h1 class="value">
|
||||
{{ value }}
|
||||
</h1>
|
||||
<div class="subtitle">
|
||||
{{ title }}
|
||||
</div>
|
||||
<VueApexCharts
|
||||
ref="chart"
|
||||
type="area"
|
||||
height="120"
|
||||
:options="chartOptions"
|
||||
:series="data"
|
||||
class="chart"
|
||||
/>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.compact-chart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-xs);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--color-raised-bg);
|
||||
box-shadow: var(--shadow-floating);
|
||||
color: var(--color-base);
|
||||
font-size: var(--font-size-nm);
|
||||
width: 100%;
|
||||
padding-bottom: 0;
|
||||
|
||||
.value {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: calc(100% + 3rem);
|
||||
margin: 0 -1.5rem 0.25rem -1.5rem;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-menu),
|
||||
:deep(.apexcharts-tooltip),
|
||||
:deep(.apexcharts-yaxistooltip) {
|
||||
background: var(--color-raised-bg) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
box-shadow: var(--shadow-floating) !important;
|
||||
font-size: var(--font-size-nm) !important;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-graphical) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-tooltip) {
|
||||
.bar-tooltip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-xs);
|
||||
padding: var(--gap-sm);
|
||||
|
||||
.card-divider {
|
||||
margin: var(--gap-xs) 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--gap-xs);
|
||||
color: var(--color-base);
|
||||
}
|
||||
|
||||
.list-entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--gap-md);
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: var(--gap-sm);
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
.divider {
|
||||
font-size: var(--font-size-lg);
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--gap-md);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep(.apexcharts-grid-borders) {
|
||||
line {
|
||||
stroke: var(--color-button-bg) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.apexcharts-xaxis) {
|
||||
line {
|
||||
stroke: none;
|
||||
}
|
||||
}
|
||||
|
||||
.legend-checkbox :deep(.checkbox.checked) {
|
||||
background-color: var(--color);
|
||||
}
|
||||
</style>
|
||||
@ -1,127 +0,0 @@
|
||||
<template>
|
||||
<Line :options="chartOptions" :data="chartData" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Line } from 'vue-chartjs'
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
Tooltip,
|
||||
PointElement,
|
||||
LineElement,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
Filler,
|
||||
} from 'chart.js'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
ChartJS.register(Title, Tooltip, PointElement, LineElement, CategoryScale, LinearScale, Filler)
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
formatLabels: {
|
||||
type: Function,
|
||||
default: (label) => dayjs(label).format('MMM D'),
|
||||
},
|
||||
})
|
||||
|
||||
const decimalToRgba = (decimalColor, alpha = 0.75) => {
|
||||
const red = (decimalColor >> 16) & 255
|
||||
const green = (decimalColor >> 8) & 255
|
||||
const blue = decimalColor & 255
|
||||
|
||||
return `rgba(${red}, ${green}, ${blue}, ${alpha})`
|
||||
}
|
||||
|
||||
const chartData = ref({
|
||||
labels: props.data.labels.map((date) => props.formatLabels(date)),
|
||||
datasets: props.data.data.map((project) => ({
|
||||
label: project.title,
|
||||
backgroundColor: decimalToRgba(project.color, 0.5),
|
||||
borderColor: decimalToRgba(project.color),
|
||||
data: project.data,
|
||||
})),
|
||||
})
|
||||
|
||||
const chartOptions = ref({
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-button-bg'),
|
||||
},
|
||||
ticks: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
},
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-button-bg'),
|
||||
},
|
||||
ticks: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
axis: 'xy',
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'right',
|
||||
align: 'start',
|
||||
labels: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
font: {
|
||||
size: 12,
|
||||
family: 'Inter',
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
position: 'nearest',
|
||||
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue(
|
||||
'--color-raised-bg'
|
||||
),
|
||||
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--color-button-bg'),
|
||||
borderWidth: 1,
|
||||
titleColor: getComputedStyle(document.documentElement).getPropertyValue('--color-contrast'),
|
||||
titleFont: {
|
||||
size: 14,
|
||||
family: 'Inter',
|
||||
},
|
||||
bodyColor: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
bodyFont: {
|
||||
size: 12,
|
||||
family: 'Inter',
|
||||
},
|
||||
boxPadding: 8,
|
||||
intersect: false,
|
||||
padding: 12,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
/*
|
||||
The data for the graph should look like this
|
||||
|
||||
downloads, views, likes = {
|
||||
dates: [ '2021-01-01', '2021-01-02', '2021-01-03' ], // Last 2 weeks
|
||||
data: [
|
||||
{
|
||||
title: projectName,
|
||||
color: projectColor,
|
||||
data: [ ... ],
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
*/
|
||||
</script>
|
||||
@ -1,93 +0,0 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Pie } from 'vue-chartjs'
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
Tooltip,
|
||||
PieController,
|
||||
ArcElement,
|
||||
Legend,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
} from 'chart.js'
|
||||
|
||||
ChartJS.register(Title, Tooltip, PieController, ArcElement, Legend, CategoryScale, LinearScale)
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const decimalToRgba = (decimalColor, alpha = 1) => {
|
||||
const red = (decimalColor >> 16) & 255
|
||||
const green = (decimalColor >> 8) & 255
|
||||
const blue = decimalColor & 255
|
||||
|
||||
return `rgba(${red}, ${green}, ${blue}, ${alpha})`
|
||||
}
|
||||
|
||||
const chartData = ref({
|
||||
labels: props.data.data.map((project) => project.title),
|
||||
datasets: [
|
||||
{
|
||||
label: props.data.title,
|
||||
backgroundColor: props.data.data.map((project) => decimalToRgba(project.color, 0.5)),
|
||||
borderColor: props.data.data.map((project) => decimalToRgba(project.color)),
|
||||
data: props.data.data.map((project) => project.data),
|
||||
fill: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const chartOptions = ref({
|
||||
responsive: true,
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'right',
|
||||
labels: {
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
font: {
|
||||
size: 12,
|
||||
family: 'Inter',
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
position: 'nearest',
|
||||
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue(
|
||||
'--color-raised-bg'
|
||||
),
|
||||
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--color-button-bg'),
|
||||
borderWidth: 1,
|
||||
titleColor: getComputedStyle(document.documentElement).getPropertyValue('--color-contrast'),
|
||||
titleFont: {
|
||||
size: 16,
|
||||
family: 'Inter',
|
||||
},
|
||||
bodyColor: getComputedStyle(document.documentElement).getPropertyValue('--color-base'),
|
||||
bodyFont: {
|
||||
size: 12,
|
||||
family: 'Inter',
|
||||
},
|
||||
boxPadding: 8,
|
||||
intersect: false,
|
||||
padding: 12,
|
||||
displayColors: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Pie :options="chartOptions" :data="chartData" />
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@ -30,9 +30,8 @@ export { default as TextLogo } from './brand/TextLogo.vue'
|
||||
export { default as FourOhFourNotFound } from '@/assets/branding/404.svg?component'
|
||||
|
||||
// Charts
|
||||
export { default as BarChart } from './chart/BarChart.vue'
|
||||
export { default as LineChart } from './chart/LineChart.vue'
|
||||
export { default as PieChart } from './chart/PieChart.vue'
|
||||
export { default as Chart } from './chart/Chart.vue'
|
||||
export { default as CompactChart } from './chart/CompactChart.vue'
|
||||
|
||||
// Modals
|
||||
export { default as Modal } from './modal/Modal.vue'
|
||||
@ -185,6 +184,8 @@ export { default as VersionIcon } from '@/assets/icons/version.svg?component'
|
||||
export { default as WikiIcon } from '@/assets/icons/wiki.svg?component'
|
||||
export { default as XIcon } from '@/assets/icons/x.svg?component'
|
||||
export { default as XCircleIcon } from '@/assets/icons/x-circle.svg?component'
|
||||
export { default as ZoomInIcon } from '@/assets/icons/zoom-in.svg?component'
|
||||
export { default as ZoomOutIcon } from '@/assets/icons/zoom-out.svg?component'
|
||||
|
||||
// Editor Icons
|
||||
export { default as BoldIcon } from '@/assets/icons/bold.svg?component'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "omorphia",
|
||||
"type": "module",
|
||||
"version": "0.6.2",
|
||||
"version": "0.6.3",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
@ -30,15 +30,15 @@
|
||||
"@codemirror/language": "^6.9.1",
|
||||
"@codemirror/state": "^6.3.0",
|
||||
"@codemirror/view": "^6.21.3",
|
||||
"chart.js": "^4.3.3",
|
||||
"apexcharts": "^3.44.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"floating-vue": "^2.0.0-beta.20",
|
||||
"highlight.js": "^11.8.0",
|
||||
"markdown-it": "^13.0.1",
|
||||
"qrcode.vue": "^3.4.0",
|
||||
"vue-chartjs": "^5.2.0",
|
||||
"vue-router": "^4.2.1",
|
||||
"vue-select": "^4.0.0-beta.6",
|
||||
"vue3-apexcharts": "^1.4.4",
|
||||
"xss": "^1.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
113
pnpm-lock.yaml
generated
113
pnpm-lock.yaml
generated
@ -20,9 +20,9 @@ dependencies:
|
||||
'@codemirror/view':
|
||||
specifier: ^6.21.3
|
||||
version: 6.21.3
|
||||
chart.js:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3
|
||||
apexcharts:
|
||||
specifier: ^3.44.0
|
||||
version: 3.44.0
|
||||
dayjs:
|
||||
specifier: ^1.11.7
|
||||
version: 1.11.7
|
||||
@ -41,15 +41,15 @@ dependencies:
|
||||
vue:
|
||||
specifier: ^3.3.4
|
||||
version: 3.3.4
|
||||
vue-chartjs:
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0(chart.js@4.3.3)(vue@3.3.4)
|
||||
vue-router:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(vue@3.3.4)
|
||||
vue-select:
|
||||
specifier: ^4.0.0-beta.6
|
||||
version: 4.0.0-beta.6(vue@3.3.4)
|
||||
vue3-apexcharts:
|
||||
specifier: ^1.4.4
|
||||
version: 1.4.4(apexcharts@3.44.0)(vue@3.3.4)
|
||||
xss:
|
||||
specifier: ^1.0.14
|
||||
version: 1.0.14
|
||||
@ -699,10 +699,6 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
dev: true
|
||||
|
||||
/@kurkle/color@0.3.2:
|
||||
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
|
||||
dev: false
|
||||
|
||||
/@lezer/common@1.1.0:
|
||||
resolution: {integrity: sha512-XPIN3cYDXsoJI/oDWoR2tD++juVrhgIago9xyKhZ7IhGlzdDM9QgC8D8saKNCz5pindGcznFr2HBSsEQSWnSjw==}
|
||||
dev: false
|
||||
@ -1364,6 +1360,10 @@ packages:
|
||||
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
|
||||
dev: true
|
||||
|
||||
/@yr/monotone-cubic-spline@1.0.3:
|
||||
resolution: {integrity: sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==}
|
||||
dev: false
|
||||
|
||||
/acorn-import-assertions@1.9.0(acorn@8.8.2):
|
||||
resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
|
||||
peerDependencies:
|
||||
@ -1446,6 +1446,18 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/apexcharts@3.44.0:
|
||||
resolution: {integrity: sha512-u7Xzrbcxc2yWznN78Jh5NMCYVAsWDfBjRl5ea++rVzFAqjU2hLz4RgKIFwYOBDRQtW1e/Qz8azJTqIJ1+Vu9Qg==}
|
||||
dependencies:
|
||||
'@yr/monotone-cubic-spline': 1.0.3
|
||||
svg.draggable.js: 2.2.2
|
||||
svg.easing.js: 2.0.0
|
||||
svg.filter.js: 2.0.2
|
||||
svg.pathmorphing.js: 0.1.3
|
||||
svg.resize.js: 1.4.3
|
||||
svg.select.js: 3.0.1
|
||||
dev: false
|
||||
|
||||
/argparse@1.0.10:
|
||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||
dependencies:
|
||||
@ -1529,13 +1541,6 @@ packages:
|
||||
supports-color: 7.2.0
|
||||
dev: true
|
||||
|
||||
/chart.js@4.3.3:
|
||||
resolution: {integrity: sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==}
|
||||
engines: {pnpm: '>=7'}
|
||||
dependencies:
|
||||
'@kurkle/color': 0.3.2
|
||||
dev: false
|
||||
|
||||
/chokidar@3.5.3:
|
||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
@ -2784,6 +2789,60 @@ packages:
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: true
|
||||
|
||||
/svg.draggable.js@2.2.2:
|
||||
resolution: {integrity: sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
svg.js: 2.7.1
|
||||
dev: false
|
||||
|
||||
/svg.easing.js@2.0.0:
|
||||
resolution: {integrity: sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
svg.js: 2.7.1
|
||||
dev: false
|
||||
|
||||
/svg.filter.js@2.0.2:
|
||||
resolution: {integrity: sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
svg.js: 2.7.1
|
||||
dev: false
|
||||
|
||||
/svg.js@2.7.1:
|
||||
resolution: {integrity: sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==}
|
||||
dev: false
|
||||
|
||||
/svg.pathmorphing.js@0.1.3:
|
||||
resolution: {integrity: sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
svg.js: 2.7.1
|
||||
dev: false
|
||||
|
||||
/svg.resize.js@1.4.3:
|
||||
resolution: {integrity: sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
svg.js: 2.7.1
|
||||
svg.select.js: 2.1.2
|
||||
dev: false
|
||||
|
||||
/svg.select.js@2.1.2:
|
||||
resolution: {integrity: sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
svg.js: 2.7.1
|
||||
dev: false
|
||||
|
||||
/svg.select.js@3.0.1:
|
||||
resolution: {integrity: sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
svg.js: 2.7.1
|
||||
dev: false
|
||||
|
||||
/svgo@3.0.2:
|
||||
resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -3051,16 +3110,6 @@ packages:
|
||||
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
|
||||
dev: true
|
||||
|
||||
/vue-chartjs@5.2.0(chart.js@4.3.3)(vue@3.3.4):
|
||||
resolution: {integrity: sha512-d3zpKmGZr2OWHQ1xmxBcAn5ShTG917+/UCLaSpaCDDqT0U7DBsvFzTs69ZnHCgKoXT55GZDW8YEj9Av+dlONLA==}
|
||||
peerDependencies:
|
||||
chart.js: ^4.1.1
|
||||
vue: ^3.0.0-0 || ^2.7.0
|
||||
dependencies:
|
||||
chart.js: 4.3.3
|
||||
vue: 3.3.4
|
||||
dev: false
|
||||
|
||||
/vue-demi@0.14.5(vue@3.3.4):
|
||||
resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -3138,6 +3187,16 @@ packages:
|
||||
typescript: 5.2.2
|
||||
dev: true
|
||||
|
||||
/vue3-apexcharts@1.4.4(apexcharts@3.44.0)(vue@3.3.4):
|
||||
resolution: {integrity: sha512-TH89uZrxGjaDvkaYAISvj8+k6Bf1rUKFillc8oJirs5XZEPiwM1ELKZQ786wz0rfPqkSHHny2lqqUCK7Rw+LcQ==}
|
||||
peerDependencies:
|
||||
apexcharts: '> 3.0.0'
|
||||
vue: '> 3.0.0'
|
||||
dependencies:
|
||||
apexcharts: 3.44.0
|
||||
vue: 3.3.4
|
||||
dev: false
|
||||
|
||||
/vue@3.3.4:
|
||||
resolution: {integrity: sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==}
|
||||
dependencies:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user