UNPKG

@spaced-out/ui-design-system

Version:
136 lines (121 loc) 3.7 kB
// @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> ); }, );