Radial gauge
A progress-toward-a-goal dial over a value and a target.
<script setup lang="ts">
import RadialGauge from '@shared/components/common/charts/RadialGauge.vue'
const actual = 42800
const target = 60000
</script>
<template>
<RadialGauge
:value="actual"
:max="target"
:caption="`$${actual.toLocaleString('en-US')} / $${target.toLocaleString('en-US')}`"
/>
</template>Usage
value and max are any pair, and caption is the line under the percentage. It takes a formatted string rather than a currency setting, so the same component works for money, seats, tickets or anything else.
The arc is open at the bottom rather than a full circle, which leaves room for the caption and makes it read as progress along a track.
The percentage and the caption are rendered as DOM, not as an ECharts label, so they inherit the app's font tokens and stay crisp on a high-density screen.
Like the other charts, it fills a wrapper of a definite height. See Donut chart.
Examples
Driven by a control
The dial animates to whatever value becomes.
<script setup lang="ts">
import RadialGauge from '@shared/components/common/charts/RadialGauge.vue'
import { Slider } from '@shared/components/ui/slider'
import { computed, ref } from 'vue'
const seats = ref([34])
const total = 50
const caption = computed(() => `${seats.value[0]} of ${total} seats used`)
</script>
<template>
<div class="max-w-sm space-y-4">
<RadialGauge :value="seats[0]" :max="total" :height="220" :caption="caption" />
<Slider v-model="seats" :min="0" :max="total" :step="1" aria-label="Seats used" />
</div>
</template>Over target
The arc is clamped to 100%, so a value past the target fills it without drawing past the end. The caption still tells the truth.
<script setup lang="ts">
import RadialGauge from '@shared/components/common/charts/RadialGauge.vue'
</script>
<template>
<RadialGauge :value="138" :max="100" :height="220" caption="138 of 100 tickets closed" />
</template>Loading
<script setup lang="ts">
import RadialGauge from '@shared/components/common/charts/RadialGauge.vue'
import { Button } from '@shared/components/ui/button'
import { ref } from 'vue'
const loading = ref(true)
</script>
<template>
<div class="space-y-3">
<Button size="sm" variant="outline" @click="loading = !loading">
{{ loading ? 'Show the gauge' : 'Show the skeleton' }}
</Button>
<RadialGauge :value="72" :max="100" :loading="loading" :height="200" />
</div>
</template>API
Source: src/shared/components/common/charts/RadialGauge.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | required | The current figure. |
max | number | required | The target. 0 renders 0%. |
loading | boolean | false | Renders a skeleton of the same height. |
height | number | 280 | The wrapper height in pixels. |
caption | string | — | The line under the percentage, already formatted. |
Emits
None.
Slots
None.
Accessibility
- The percentage is real text in the page, not a label painted into the canvas, so a screen reader reads it and a browser's zoom and font settings apply to it.
- The caption carries the underlying figures, so the reader is not left with a percentage and no idea what it is a percentage of.
- The track and the progress arc are the theme's border and primary colors, which keeps the contrast between them in both color modes.