diff --git a/giraffe/README.md b/giraffe/README.md index d34f3e3c..5d36b001 100644 --- a/giraffe/README.md +++ b/giraffe/README.md @@ -717,6 +717,8 @@ TableGraphLayerConfig uses the `fluxResponse` property from `config` as the data - **tickSuffix**: _string. Optional._ The text that appears after each tick label. Use an empty string if no text is preferred. + - **gaugeUnit**: _select. Optional._ It allows the user to pick a unit of byte, usd, or time. Automatically set to none. This will change the tick prefix to change to the preset units. If Gauge units is selected lineCount is overwritten and will not respond to inputs. + - **decimalPlaces**: _Object. Optional._ - **isEnforced**: _boolean. Optional. Defaults to false when not included._ Indicates whether the number of decimal places ("**digits**") will be enforced. When **isEnforced** is falsy or omitted, **digits** will be locked to 2 for stat values with a decimal and 0 for stat values that are integers, and the **digits** option will be ignored. diff --git a/giraffe/src/components/Gauge.tsx b/giraffe/src/components/Gauge.tsx index 5f8d5e7f..d1fb41a4 100644 --- a/giraffe/src/components/Gauge.tsx +++ b/giraffe/src/components/Gauge.tsx @@ -29,6 +29,7 @@ interface Props { decimalPlaces: DecimalPlaces theme: GaugeTheme gaugeSize: number + gaugeUnit: string[] } const resetCanvas = ( @@ -55,7 +56,7 @@ const updateCanvas = ( canvasRef: React.MutableRefObject, props: Props ): void => { - const {width, height, colors, theme, gaugeSize} = props + const {width, height, colors, theme, gaugeSize, gaugeUnit} = props resetCanvas(canvasRef, width, height) const canvas = canvasRef.current @@ -89,6 +90,15 @@ const updateCanvas = ( ) ) + //gauge line needs to be changed if gauge unit is being used + if (gaugeUnit.toString() === 'bytes') { + theme.lineCount = 5 + } else if (gaugeUnit.toString() === 'time') { + theme.lineCount = 5 + } else if (gaugeUnit.toString() === 'USD') { + theme.lineCount = 5 + } + // The following functions must be called in the specified order if (colors.length === MIN_THRESHOLDS) { drawGradientGauge( @@ -122,9 +132,17 @@ const updateCanvas = ( theme, gaugeSize ) - drawGaugeLabels(ctx, radius, gradientThickness, minValue, maxValue, props) + drawGaugeLabels( + ctx, + radius, + gradientThickness, + minValue, + maxValue, + gaugeUnit, + props + ) drawGaugeValue(ctx, radius, labelValueFontSize, props) - drawNeedle(ctx, radius, minValue, maxValue, props) + drawNeedle(ctx, radius, minValue, maxValue, gaugeUnit, props) } const drawGradientGauge = ( @@ -295,55 +313,121 @@ const drawGaugeLabels = ( gradientThickness: number, minValue: number, maxValue: number, + gaugeUnit: string[], props: Props ): void => { const {tickPrefix, tickSuffix, decimalPlaces, gaugeSize} = props const {lineCount, labelColor, labelFontSize} = props.theme - - const tickValues = [ - ...range(minValue, maxValue, Math.abs(maxValue - minValue) / lineCount), - maxValue, - ] - - const labels = tickValues.map(tick => - formatStatValue(tick, { - decimalPlaces, - prefix: tickPrefix, - suffix: tickSuffix, - }) - ) const startDegree = Math.PI - (gaugeSize - Math.PI) / 2 const arcLength = gaugeSize const arcIncrement = arcLength / lineCount + let labelRadius: number // Format labels text ctx.font = `bold ${labelFontSize}px Rubik` ctx.fillStyle = labelColor ctx.textBaseline = 'middle' ctx.textAlign = 'right' - let labelRadius: number - for (let i = 0; i <= lineCount; i++) { - labelRadius = radius + gradientThickness + 23 - if (lineCount === 1 && i === 1) { - ctx.textAlign = 'left' - } else if (lineCount % 2 === 0 && lineCount / i === 2) { - ctx.textAlign = 'center' - } else if (i / (lineCount + 1) >= 0.5) { - ctx.textAlign = 'left' + if (gaugeUnit.toString() === 'bytes') { + const labels = ['0b', '1024Kb', '1024Mb', '1024Gb', '1024Tb', '1024Pb'] + const lineCount = 5 + for (let i = 0; i <= lineCount; i++) { + labelRadius = radius + gradientThickness + 23 + if (i / (lineCount + 1) >= 0.5) { + ctx.textAlign = 'left' + } + + ctx.rotate(startDegree) + ctx.rotate(i * arcIncrement) + ctx.translate(labelRadius, 0) + ctx.rotate(i * -arcIncrement) + ctx.rotate(-startDegree) + ctx.fillText(labels[i], 0, 0) + ctx.rotate(startDegree) + ctx.rotate(i * arcIncrement) + ctx.translate(-labelRadius, 0) + ctx.rotate(i * -arcIncrement) + ctx.rotate(-startDegree) + } + } else if (gaugeUnit.toString() === 'time') { + const labels = ['0', '60s', '60m', '12h', '24h', '30d'] + const lineCount = 5 + for (let i = 0; i <= lineCount; i++) { + labelRadius = radius + gradientThickness + 23 + if (i / (lineCount + 1) >= 0.5) { + ctx.textAlign = 'left' + } + + ctx.rotate(startDegree) + ctx.rotate(i * arcIncrement) + ctx.translate(labelRadius, 0) + ctx.rotate(i * -arcIncrement) + ctx.rotate(-startDegree) + ctx.fillText(labels[i], 0, 0) + ctx.rotate(startDegree) + ctx.rotate(i * arcIncrement) + ctx.translate(-labelRadius, 0) + ctx.rotate(i * -arcIncrement) + ctx.rotate(-startDegree) + } + } else if (gaugeUnit.toString() === 'USD') { + const labels = ['0', '100', '1000', '1000m', '1000b', '1000t'] + const lineCount = 5 + for (let i = 0; i <= lineCount; i++) { + labelRadius = radius + gradientThickness + 23 + if (i / (lineCount + 1) >= 0.5) { + ctx.textAlign = 'left' + } + + ctx.rotate(startDegree) + ctx.rotate(i * arcIncrement) + ctx.translate(labelRadius, 0) + ctx.rotate(i * -arcIncrement) + ctx.rotate(-startDegree) + ctx.fillText(labels[i], 0, 0) + ctx.rotate(startDegree) + ctx.rotate(i * arcIncrement) + ctx.translate(-labelRadius, 0) + ctx.rotate(i * -arcIncrement) + ctx.rotate(-startDegree) } + } else { + const tickValues = [ + ...range(minValue, maxValue, Math.abs(maxValue - minValue) / lineCount), + maxValue, + ] + + const labels = tickValues.map(tick => + formatStatValue(tick, { + decimalPlaces, + prefix: tickPrefix, + suffix: tickSuffix, + }) + ) - ctx.rotate(startDegree) - ctx.rotate(i * arcIncrement) - ctx.translate(labelRadius, 0) - ctx.rotate(i * -arcIncrement) - ctx.rotate(-startDegree) - ctx.fillText(labels[i], 0, 0) - ctx.rotate(startDegree) - ctx.rotate(i * arcIncrement) - ctx.translate(-labelRadius, 0) - ctx.rotate(i * -arcIncrement) - ctx.rotate(-startDegree) + for (let i = 0; i <= lineCount; i++) { + labelRadius = radius + gradientThickness + 23 + if (lineCount === 1 && i === 1) { + ctx.textAlign = 'left' + } else if (lineCount % 2 === 0 && lineCount / i === 2) { + ctx.textAlign = 'center' + } else if (i / (lineCount + 1) >= 0.5) { + ctx.textAlign = 'left' + } + + ctx.rotate(startDegree) + ctx.rotate(i * arcIncrement) + ctx.translate(labelRadius, 0) + ctx.rotate(i * -arcIncrement) + ctx.rotate(-startDegree) + ctx.fillText(labels[i], 0, 0) + ctx.rotate(startDegree) + ctx.rotate(i * arcIncrement) + ctx.translate(-labelRadius, 0) + ctx.rotate(i * -arcIncrement) + ctx.rotate(-startDegree) + } } } @@ -377,6 +461,7 @@ const drawNeedle = ( radius: number, minValue: number, maxValue: number, + gaugeUnit: string[], props: Props ): void => { const {gaugePosition, gaugeSize, decimalPlaces} = props @@ -394,6 +479,9 @@ const drawNeedle = ( digits = Math.min(digits, MAX_DECIMAL_PLACES) const formattedGaugePosition = Number(gaugePosition.toFixed(digits)) + if (gaugeUnit.toString() === 'bytes') { + maxValue = 5120 + } if (formattedGaugePosition < minValue) { needleRotation = 0 - overflowDelta diff --git a/giraffe/src/components/GaugeLayer.tsx b/giraffe/src/components/GaugeLayer.tsx index 25295be8..cec40d50 100644 --- a/giraffe/src/components/GaugeLayer.tsx +++ b/giraffe/src/components/GaugeLayer.tsx @@ -25,6 +25,7 @@ export const GaugeLayer: FunctionComponent = (props: Props) => { gaugeColors, gaugeSize = Math.PI, gaugeTheme = {}, + gaugeUnit, } = config const MAX_PI_DECIMALS = 3 // values above 3 distort Gauge styling @@ -57,6 +58,7 @@ export const GaugeLayer: FunctionComponent = (props: Props) => { decimalPlaces={decimalPlaces} gaugeSize={validGaugeSize} theme={{...GAUGE_THEME_DARK, ...gaugeTheme}} + gaugeUnit={gaugeUnit} /> )} diff --git a/giraffe/src/types/index.ts b/giraffe/src/types/index.ts index 1d87344c..0676fc22 100644 --- a/giraffe/src/types/index.ts +++ b/giraffe/src/types/index.ts @@ -301,6 +301,7 @@ export interface GaugeLayerConfig { gaugeColors: Color[] gaugeSize?: number gaugeTheme?: Partial + gaugeUnit: string[] } export interface GaugeTheme { diff --git a/stories/src/gauge.stories.tsx b/stories/src/gauge.stories.tsx index 73c65016..44d0b5a5 100644 --- a/stories/src/gauge.stories.tsx +++ b/stories/src/gauge.stories.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import {storiesOf} from '@storybook/react' -import {withKnobs, number, text} from '@storybook/addon-knobs' +import {withKnobs, number, text, select} from '@storybook/addon-knobs' import {Config, Plot, GaugeTheme} from '../../giraffe/src' import {DEFAULT_GAUGE_COLORS} from '../../giraffe/src' @@ -43,6 +43,11 @@ storiesOf('Gauge', module) const suffix = text('Suffix', '') const tickPrefix = text('TickPrefix', '') const tickSuffix = text('TickSuffix', '') + const gaugeUnit = select( + 'Unit', + {bytes: 'bytes', time: 'time', usd: 'USD', none: ''}, + '' + ) const config: Config = { table: gaugeTable(gaugeMin, gaugeMax), @@ -62,6 +67,7 @@ storiesOf('Gauge', module) {...DEFAULT_GAUGE_COLORS[1], value: gaugeMax}, ], gaugeSize, + gaugeUnit, gaugeTheme: { valuePositionYOffset, valuePositionXOffset,