Files
online.tbbank.gov.tm-larave…/nova/resources/js/components/Metrics/Base/BaseTrendMetric.vue
2024-09-01 18:54:23 +05:00

232 lines
5.1 KiB
Vue

<template>
<LoadingCard :loading="loading" class="px-6 py-4">
<div class="h-6 flex items-center mb-4">
<h3 class="mr-3 leading-tight text-sm font-bold">{{ title }}</h3>
<HelpTextTooltip :text="helpText" :width="helpWidth" />
<SelectControl
v-if="ranges.length > 0"
class="ml-auto w-[6rem] shrink-0"
size="xxs"
:options="ranges"
:selected="selectedRangeKey"
@change="handleChange"
:aria-label="__('Select Ranges')"
/>
</div>
<p class="flex items-center text-4xl mb-4">
{{ formattedValue }}
<span v-if="suffix" class="ml-2 text-sm font-bold">{{
formattedSuffix
}}</span>
</p>
<div
ref="chart"
class="absolute inset-0 rounded-b-lg ct-chart"
style="top: 60%"
/>
</LoadingCard>
</template>
<script>
import debounce from 'lodash/debounce'
import Chartist from 'chartist'
import 'chartist/dist/chartist.min.css'
import { singularOrPlural } from '@/util'
import ChartistTooltip from 'chartist-plugin-tooltips-updated'
import 'chartist-plugin-tooltips-updated/dist/chartist-plugin-tooltip.css'
export default {
name: 'BaseTrendMetric',
emits: ['selected'],
props: {
loading: Boolean,
title: {},
helpText: {},
helpWidth: {},
value: {},
chartData: {},
maxWidth: {},
prefix: '',
suffix: '',
suffixInflection: { type: Boolean, default: true },
ranges: { type: Array, default: () => [] },
selectedRangeKey: [String, Number],
format: {
type: String,
default: '0[.]00a',
},
},
data: () => ({
chartist: null,
resizeObserver: null,
}),
watch: {
selectedRangeKey: function (newRange, oldRange) {
this.renderChart()
},
chartData: function (newData, oldData) {
this.renderChart()
},
},
created() {
const debouncer = debounce(callback => callback(), Nova.config('debounce'))
this.resizeObserver = new ResizeObserver(entries => {
debouncer(() => {
this.renderChart()
})
})
},
mounted() {
const low = Math.min(...this.chartData)
const high = Math.max(...this.chartData)
// Use zero as the graph base if the lowest value is greater than or equal to zero.
// This avoids the awkward situation where the chart doesn't appear filled in.
const areaBase = low >= 0 ? 0 : low
this.chartist = new Chartist.Line(this.$refs.chart, this.chartData, {
lineSmooth: Chartist.Interpolation.none(),
fullWidth: true,
showPoint: true,
showLine: true,
showArea: true,
chartPadding: {
top: 10,
right: 0,
bottom: 0,
left: 0,
},
low,
high,
areaBase,
axisX: {
showGrid: false,
showLabel: true,
offset: 0,
},
axisY: {
showGrid: false,
showLabel: true,
offset: 0,
},
plugins: [
ChartistTooltip({
pointClass: 'ct-point',
anchorToPoint: false,
}),
ChartistTooltip({
pointClass: 'ct-point__left',
anchorToPoint: false,
tooltipOffset: {
x: 50,
y: -20,
},
}),
ChartistTooltip({
pointClass: 'ct-point__right',
anchorToPoint: false,
tooltipOffset: {
x: -50,
y: -20,
},
}),
],
})
this.chartist.on('draw', data => {
if (data.type === 'point') {
data.element.attr({
'ct:value': this.transformTooltipText(data.value.y),
})
data.element.addClass(
this.transformTooltipClass(data.axisX.ticks.length, data.index) ?? ''
)
}
})
this.resizeObserver.observe(this.$refs.chart)
},
beforeUnmount() {
this.resizeObserver.unobserve(this.$refs.chart)
},
methods: {
renderChart() {
this.chartist.update(this.chartData)
},
handleChange(event) {
const value = event?.target?.value || event
this.$emit('selected', value)
},
transformTooltipText(value) {
let formattedValue = Nova.formatNumber(new String(value), this.format)
if (this.prefix) {
return `${this.prefix}${formattedValue}`
}
if (this.suffix) {
const suffix = this.suffixInflection
? singularOrPlural(value, this.suffix)
: this.suffix
return `${formattedValue} ${suffix}`
}
return `${formattedValue}`
},
transformTooltipClass(total, index) {
if (index < 2) {
return 'ct-point__left'
} else if (index > total - 3) {
return 'ct-point__right'
}
return 'ct-point'
},
},
computed: {
isNullValue() {
return this.value == null
},
formattedValue() {
if (!this.isNullValue) {
const value = Nova.formatNumber(new String(this.value), this.format)
return `${this.prefix}${value}`
}
return ''
},
formattedSuffix() {
if (this.suffixInflection === false) {
return this.suffix
}
return singularOrPlural(this.value, this.suffix)
},
},
}
</script>