@spaced-out/ui-design-system
Version:
Sense UI components library
136 lines (121 loc) • 3.7 kB
Flow
// @flow strict
import * as React from 'react';
import invariant from 'invariant';
import clamp from 'lodash/clamp';
import {
colorScoreBarInactive,
colorScoreBarLevel1,
colorScoreBarLevel2,
colorScoreBarLevel3,
colorScoreBarLevel4,
colorScoreBarLevel5,
} from '../../styles/variables/_color.js';
import {size2, size18} from '../../styles/variables/_size';
import {classify} from '../../utils/classify';
import {
getColorByScorePercentage,
getLabelByScorePercentage,
SCORE_BAR_ERRORS,
} from '../../utils/score-bar';
import {SubTitleExtraSmall} from '../Text';
import css from './ScoreBar.module.css';
export const Direction = Object.freeze({
horizontal: 'horizontal',
vertical: 'vertical',
});
export type ClassNames = $ReadOnly<{
container?: string,
wrapper?: string,
bar?: string,
label?: string,
}>;
export type ScoreBarDirection = $Values<typeof Direction>;
export type ScoreBarColorMap = $ReadOnly<{
[key: string]: string,
}>;
export type ScoreBarLabelMap = ScoreBarColorMap;
export type ScoreBarProps = {
classNames?: ClassNames,
direction?: ScoreBarDirection,
colorMap?: ScoreBarColorMap,
labelMap?: ScoreBarLabelMap,
totalBars?: number,
score?: number,
withBorder?: boolean,
};
const DEFAULT_BARS_COUNT = 5;
export const DEFAULT_COLOR_MAP: ScoreBarColorMap = {
inactive: colorScoreBarInactive,
'100': colorScoreBarLevel5,
'80': colorScoreBarLevel4,
'60': colorScoreBarLevel3,
'40': colorScoreBarLevel2,
'20': colorScoreBarLevel1,
};
export const ScoreBar: React$AbstractComponent<ScoreBarProps, HTMLDivElement> =
React.forwardRef<ScoreBarProps, HTMLDivElement>(
(
{
score = 0,
colorMap = DEFAULT_COLOR_MAP,
totalBars = DEFAULT_BARS_COUNT,
direction = 'vertical',
classNames,
withBorder = false,
labelMap,
}: ScoreBarProps,
ref,
): React.Node => {
invariant(
totalBars > 0,
JSON.stringify(SCORE_BAR_ERRORS.INVALID_BAR_COUNT),
);
const activeLabel = getLabelByScorePercentage(score, totalBars, labelMap);
return (
<div
ref={ref}
data-testid="ScoreBar"
className={classify(
css.scoreBarWrapper,
{[css.scoreBarBorder]: withBorder},
classNames?.wrapper,
)}
>
<div
className={classify(css.scoreBarContainer, classNames?.container)}
style={{
'--direction':
direction === Direction.horizontal ? 'row-reverse' : 'column',
'--height': direction === Direction.horizontal ? size18 : 'unset',
'--width': direction === Direction.vertical ? size18 : 'unset',
}}
>
{[...Array(totalBars).keys()].reverse().map((count) => (
<div
className={classify(css.bar, classNames?.bar)}
style={{
'--background-color': getColorByScorePercentage(
totalBars,
clamp(score, 0, totalBars),
count,
colorMap,
),
'--height':
direction === Direction.horizontal ? size18 : size2,
'--width': direction === Direction.vertical ? size18 : size2,
}}
key={count}
/>
))}
</div>
{activeLabel && (
<SubTitleExtraSmall
className={classify(css.scoreBarLabel, classNames?.label)}
>
{activeLabel}
</SubTitleExtraSmall>
)}
</div>
);
},
);