import React, { Fragment, useContext, useEffect, useReducer } from 'react'; import classnames from 'classnames'; // By default, hide the percentage bar below screen widths of this width. const PERCENTAGE_BAR_DEFAULT_BREAK_WIDTH = 768; const MDASH = String.fromCharCode(8212); import styles from './styles.module.scss'; // Classes to merge in, depending on context const CONTEXT_CLASSES = { LIST_VIEW: styles.contextListView, }; export const PercentageBarContext = React.createContext(null); export default function PercentageBar({ percentage, breakWidth, percentageFormatter, }: { percentage: number; breakWidth?: number; percentageFormatter?: (number) => string; }) { const [, forceUpdate] = useReducer((x) => x + 1, 0); // Context const context = useContext(PercentageBarContext); const isListViewContext = context === 'LIST_VIEW'; useEffect(() => { if (!isListViewContext) { window.addEventListener('resize', forceUpdate); return () => window.removeEventListener('resize', forceUpdate); } }, [forceUpdate, isListViewContext]); breakWidth = breakWidth || PERCENTAGE_BAR_DEFAULT_BREAK_WIDTH; // First, check for a custom percentage formatter. // If no custom formatter, then check if value is less than 1% but nonzero, and render `<1%` // Else, return the rounded percentage value percentageFormatter = percentageFormatter || ((n) => { if (n > 0 && n < 0.01) { return isListViewContext ? `<1%` : `<1.0%`; } else { return `${(n * 100).toFixed(isListViewContext ? 0 : 1)}%`; } }); // Ensure that percentage never overflows the bar. if (percentage > 1) { percentage = 1; } const formattedPercentage = percentageFormatter(percentage); // Check if the percentage bar has enough screen width to render the bar or not. const isExpanded = isListViewContext || (document.body && document.body.clientWidth >= breakWidth); const percentageGraphic = (
0 && percentage < 0.01, })} style={{ width: `${percentage * 100}%` }} />
); const percentageText = ( {formattedPercentage === null ? MDASH : formattedPercentage} ); return (
{isListViewContext ? ( {percentageText} {percentageGraphic} ) : ( {percentageGraphic} {percentageText} )}
); }