mirror of https://github.com/rancher/dashboard.git
158 lines
4.1 KiB
Vue
158 lines
4.1 KiB
Vue
<script>
|
|
import VStack from '@/components/Layout/Stack/VStack';
|
|
|
|
/**
|
|
* Used to display a percentage and indicate the health based on boundaries set.
|
|
*/
|
|
export default {
|
|
name: 'PercentageCircle',
|
|
|
|
components: { VStack },
|
|
|
|
props: {
|
|
/**
|
|
* A value representing the percentage to be displayed. *Must be a value between 0 and 1*.
|
|
*/
|
|
value: {
|
|
type: Number,
|
|
required: true,
|
|
validator: (value) => {
|
|
return value >= 0 && value <= 1;
|
|
}
|
|
},
|
|
/**
|
|
* A number representing the lower bound the `value` can be before it ends up in a **warning** state. *Must be a value between 0 and 1*.
|
|
*/
|
|
lowerWarningBound: { type: Number, default: undefined },
|
|
/**
|
|
* A number representing the upper bound the `value` can be before it ends up in a **warning** state. *Must be a value between 0 and 1*.
|
|
*/
|
|
upperWarningBound: { type: Number, default: undefined },
|
|
/**
|
|
* A number representing the lower bound the `value` can be before it ends up in an **error** state. *Must be a value between 0 and 1*.
|
|
*/
|
|
lowerErrorBound: { type: Number, default: undefined },
|
|
/**
|
|
* A number representing the upper bound the `value` can be before it ends up in an **error** state. *Must be a value between 0 and 1*.
|
|
*/
|
|
upperErrorBound: { type: Number, default: undefined }
|
|
},
|
|
|
|
computed: {
|
|
valueD() {
|
|
return describeArc(50, 50, 45, 180, 180 + (360 * this.value));
|
|
},
|
|
printedValue() {
|
|
return (this.value * 100)
|
|
.toFixed(1)
|
|
.replace(/\.0$/, '');
|
|
},
|
|
valueClass() {
|
|
const errorClass = 'error';
|
|
const warningClass = 'warning';
|
|
const successClass = 'success';
|
|
|
|
if (this.lowerErrorBound && this.value <= this.lowerErrorBound) {
|
|
return errorClass;
|
|
}
|
|
|
|
if (this.lowerWarningBound && this.value <= this.lowerWarningBound) {
|
|
return warningClass;
|
|
}
|
|
|
|
if (this.upperErrorBound && this.value >= this.upperErrorBound) {
|
|
return errorClass;
|
|
}
|
|
|
|
if (this.upperWarningBound && this.value >= this.upperWarningBound) {
|
|
return warningClass;
|
|
}
|
|
|
|
return successClass;
|
|
}
|
|
}
|
|
};
|
|
|
|
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
|
|
const angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
|
|
|
|
return {
|
|
x: centerX + (radius * Math.cos(angleInRadians)),
|
|
y: centerY + (radius * Math.sin(angleInRadians))
|
|
};
|
|
}
|
|
|
|
function describeArc(x, y, radius, startAngle, endAngle) {
|
|
const start = polarToCartesian(x, y, radius, endAngle);
|
|
const end = polarToCartesian(x, y, radius, startAngle);
|
|
|
|
const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
|
|
|
|
const d = [
|
|
'M', start.x, start.y,
|
|
'A', radius, radius, 0, largeArcFlag, 0, end.x, end.y
|
|
].join(' ');
|
|
|
|
return d;
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<VStack class="percentage-circle" horizontal-align="center">
|
|
<svg viewBox="0 0 100 100" class="gauge">
|
|
<circle class="dial" fill="none" cx="50" cy="50" r="45" />
|
|
<path v-if="value < 1" class="value" :class="valueClass" fill="none" :d="valueD" />
|
|
<circle
|
|
v-else
|
|
class="value"
|
|
:class="valueClass"
|
|
fill="none"
|
|
cx="50"
|
|
cy="50"
|
|
r="45"
|
|
/>
|
|
</svg>
|
|
<div class="printed-value mt-10">
|
|
<h1>{{ printedValue }}%</h1>
|
|
</div>
|
|
</VStack>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.percentage-circle {
|
|
text-align: center;
|
|
|
|
svg {
|
|
$size: 150px;
|
|
width: $size;
|
|
height: $size;
|
|
|
|
.dial {
|
|
stroke: var(--muted);
|
|
stroke-width: 5;
|
|
}
|
|
|
|
.value {
|
|
stroke-width: 5.5;
|
|
stroke-linecap: round;
|
|
|
|
&.success {
|
|
stroke: var(--success);
|
|
}
|
|
|
|
&.warning {
|
|
stroke: var(--warning);
|
|
}
|
|
|
|
&.error {
|
|
stroke: var(--error);
|
|
}
|
|
}
|
|
}
|
|
|
|
.printed-value {
|
|
text-align: center;
|
|
}
|
|
}
|
|
</style>
|