Skip to content

Commit

Permalink
Merge pull request #16 from red-ninjas/gauge-component
Browse files Browse the repository at this point in the history
Gauge component
  • Loading branch information
kalysti authored Nov 7, 2023
2 parents 750cad9 + fe32373 commit ca4239b
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 1 deletion.
103 changes: 103 additions & 0 deletions src/app/components/gauge/gauge.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Layout, Playground, Attributes } from 'lib/components';
import { Gauge, Progress, Spacer, Grid, Box, useTheme, Button } from 'components';
import { useState } from 'react';

export const meta = {
title: 'Gauge',
group: 'Feedback',
};

# Gauge

Display `Gauge` UI component to show progress.

<Playground
scope={{ Gauge }}
code={`
<Gauge value={80} size="medium" showValue={true} />
`}
/>

<Playground
title="With values"
scope={{ Gauge, Box }}
code={`
<Box style={{display: 'flex', gap: 15}}>
<Gauge value={20} size="tiny" type="primary" showValue={true} />
<Gauge value={40} size="small" type="error" showValue={true} />
<Gauge value={60} size="medium" type="success" showValue={true} />
<Gauge value={80} size="large" type="warning" showValue={true} />
</Box>
`}
/>

<Playground
title="Without values"
scope={{ Gauge, Box }}
code={`
<Box style={{display: 'flex', gap: 15}}>
<Gauge value={20} size="tiny" type="primary" />
<Gauge value={40} size="small" type="error" />
<Gauge value={60} size="medium" type="success" />
<Gauge value={80} size="large" type="warning" />
</Box>
`}
/>

<Playground
title="Custom size"
scope={{ Gauge, Box }}
code={`
<Box style={{display: 'flex', gap: 15}}>
<Gauge type="success" colors={{ primary: "red", secondary: "blue" }}
showValue value={80} size="100" />
</Box>
`}
/>

<Playground
title="Custom Colors"
scope={{ Gauge, Grid, Spacer, Button }}
code={`
() => {
const theme = useTheme()
const [value, setValue] = useState(20)
const colors = {
20: theme.palette.error.value,
40: theme.palette.warning.value,
60: theme.palette.success.value,
80: theme.palette.accents_5,
}
return (
<>
<Gauge type="warning" colors={colors} value={value} showValue={true} size="large" />
<Spacer />
<Button onClick={() => setValue((value + 20) > 100 ? 20 : (value + 20))} auto scale={0.5}>Increase</Button>
<Spacer w={.5} inline />
<Button onClick={() => setValue(20)} auto scale={0.5} type="abort">Reset</Button>
</>
)
}
`}
/>

<Attributes edit="/pages/components/gauge.mdx">
<Attributes.Title>Gauge.Props</Attributes.Title>

| Attribute | Description | Type | Accepted values | Default |
| ------------- | ------------------------- | --------------------------- | ----------------------------------------------- | --------- |
| **value** | current value | `number` | - | 0 |
| **showValue** | show current value or not | `boolean` | `true`/s`false` | `false` |
| **max** | max value | `number` | - | 100 |
| **colors** | custom colors | `{ [key: number]: string }` | - | - |
| **type** | predefined state types | `guageTypes` | [GuageTypes](#guagetypes) | `default` |
| **size** | size of gauge | `string` | `tiny` / `small` / `medium` / `large`/ `string` | `medium` |
| ... | native props | `ProgressHTMLAttributes` | `'aria-busy', ...` | - |

<Attributes.Title>GaugeTypes</Attributes.Title>

```ts
type guageTypes = 'default' | 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error';
```

</Attributes>
6 changes: 6 additions & 0 deletions src/app/components/gauge/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use client';
import Documentation from './gauge.mdx';

export default function Page() {
return <Documentation />;
}
2 changes: 1 addition & 1 deletion src/components/data-view/chart-data-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const GraphDataView: React.FC<ChartProps> = ({ height = defaultDataViewHeight, t
};

return (
<div className="data-view-core" style={{ height: height }}>
<div className="data-view-core">
<div className="dataViewSwitcher">
<Text style={{ color: theme.palette.accents_5 }} mb={0} font={'14px'} mt={0} mr={0}>
Data view
Expand Down
147 changes: 147 additions & 0 deletions src/components/gauge/gauge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
'use client';
import React from 'react';
import useTheme from '../use-theme';
import { useProportions } from '../utils/calculations';
import { UIThemesPalette } from '../themes/presets';
import { NormalTypes } from '../utils/prop-types';
import useScale, { withScale } from '../use-scale';
import useClasses from '../use-classes';

export type GaugeColors = {
[key: number]: string;
};
export type GaugeTypes = NormalTypes;

interface Props {
value?: number;
max?: number;
colors?: GaugeColors;
type?: GaugeTypes;
showValue?: boolean;
size?: 'tiny' | 'small' | 'medium' | 'large' | string;
}

type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>;
export type GaugeProps = Props & NativeAttrs;

const getCurrentColor = (ratio: number, palette: UIThemesPalette, type: GaugeTypes, colors: GaugeColors = {}): string => {
const defaultColors: { [key in GaugeTypes]: string } = {
default: palette.foreground,
success: palette.success.value,
secondary: palette.secondary.value,
primary: palette.primary.value,
tertiary: palette.tertiary.value,
warning: palette.warning.value,
error: palette.error.value,
};
const colorKeys = Object.keys(colors);
if (colorKeys.length === 0) return defaultColors[type];

const customColorKey = colorKeys.find(key => ratio <= +key);
if (!customColorKey || Number.isNaN(+customColorKey)) return defaultColors[type];
return colors[+customColorKey];
};

const GaugeComponent: React.FC<GaugeProps> = ({
value = 0,
max = 100,
className = '',
type = 'default' as GaugeTypes,
colors,
showValue = false,
size,
...props
}: GaugeProps) => {
const theme = useTheme();
const { SCALES } = useScale();
const percentValue = useProportions(value, max);
const currentColor = getCurrentColor(percentValue, theme.palette, type, colors);
const classes = useClasses('gauge', className);

const radius = size === 'tiny' ? 45 : size === 'small' ? 60 : size === 'medium' ? 75 : size === 'large' ? 90 : parseFloat(size ?? '45');
const textSizes = size === 'tiny' ? SCALES.font(1) : size === 'small' ? SCALES.font(1.5) : size === 'medium' ? SCALES.font(2) : SCALES.font(2.5);
const fontWeight = size === 'tiny' || size === 'small' ? 500 : 600;
const strokeWidth = radius < 45 ? 5 : 10;
const circumference = 2 * Math.PI * (radius - strokeWidth / 2);
const dashArray = circumference;
const dashOffset = (1 - value / 100) * dashArray;

return (
<div className={classes} {...props}>
<svg width={2 * radius} height={2 * radius} viewBox={`0 0 ${2 * radius} ${2 * radius}`}>
<circle
cx={radius}
cy={radius}
r={radius - strokeWidth / 2}
fill="none"
stroke={theme.palette.accents_2}
strokeWidth={strokeWidth}
strokeDasharray={dashArray}
/>
<circle
cx={radius}
cy={radius}
r={radius - strokeWidth / 2}
fill="none"
stroke={currentColor}
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeDasharray={dashArray}
strokeDashoffset={dashOffset}
/>
</svg>
{showValue && <div className="gauge-content">{percentValue}</div>}
<style jsx>{`
svg {
shape-rendering: crispEdges;
> circle {
shape-rendering: geometricprecision;
}
}
.gauge {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
* {
transition: all 1s ease;
}
> .gauge-content {
animation: gauge-fadein 1s ease forwards;
animation-delay: 0s;
display: flex;
opacity: 0;
position: absolute;
font-size: ${textSizes};
font-weight: ${fontWeight};
}
}
@keyframes gauge-fadein {
to {
opacity: 1;
}
}
@keyframes indeterminate {
0% {
transform: translateX(0) scaleX(0);
}
40% {
transform: translateX(0) scaleX(0.4);
}
100% {
transform: translateX(100%) scaleX(0.5);
}
}
`}</style>
</div>
);
};

GaugeComponent.displayName = 'HimalayGauge';
const Gauge = withScale(GaugeComponent);
export default Gauge;
4 changes: 4 additions & 0 deletions src/components/gauge/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Gauge from './gauge';

export type { GaugeProps, GaugeColors, GaugeTypes } from './gauge';
export default Gauge;
3 changes: 3 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,7 @@ export { default as Entity } from './entity';

export { default as Menu } from './menu';

export { default as Gauge } from './gauge';
export type { GaugeProps } from './gauge';

export * from './constants';

0 comments on commit ca4239b

Please sign in to comment.