-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from red-ninjas/gauge-component
Gauge component
- Loading branch information
Showing
6 changed files
with
264 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters