{"version":3,"file":"UnitChart.cjs","names":[],"sources":["../src/Components/Graphs/UnitChart/index.tsx"],"sourcesContent":["import { H2, P } from '@undp/design-system-react/Typography';\r\nimport sum from 'lodash.sum';\r\nimport { AnimatePresence, motion, useInView } from 'motion/react';\r\nimport { useRef } from 'react';\r\nimport { Colors } from '@/Components/ColorPalette';\r\nimport { GraphArea, GraphContainer } from '@/Components/Elements/GraphContainer';\r\nimport { GraphFooter } from '@/Components/Elements/GraphFooter';\r\nimport { GraphHeader } from '@/Components/Elements/GraphHeader';\r\nimport type {\r\n  AnimateDataType,\r\n  ClassNameObject,\r\n  Languages,\r\n  NumberFormatOptions,\r\n  SourcesDataType,\r\n  StyleObject,\r\n  UnitChartDataType,\r\n} from '@/Types';\r\nimport { numberFormattingFunction } from '@/Utils/numberFormattingFunction';\r\n\r\ninterface Props {\r\n  // Data\r\n  /** Array of data objects */\r\n  data: UnitChartDataType[];\r\n\r\n  // Titles, Labels, and Sources\r\n  /** Title of the graph */\r\n  graphTitle?: string | React.ReactNode;\r\n  /** Description of the graph */\r\n  graphDescription?: string | React.ReactNode;\r\n  /** Note with h2 tag just above the graph. Can be used to highlight text */\r\n  note?: string;\r\n  /** Footnote for the graph */\r\n  footNote?: string | React.ReactNode;\r\n  /** Source data for the graph */\r\n  sources?: SourcesDataType[];\r\n  /** Accessibility label */\r\n  ariaLabel?: string;\r\n\r\n  // Colors and Styling\r\n  /** Colors of the highlighted circles */\r\n  colors?: string[];\r\n  /** Background color of the graph */\r\n  backgroundColor?: string | boolean;\r\n  /** Custom styles for the graph. Each object should be a valid React CSS style object. */\r\n  styles?: StyleObject;\r\n  /** Custom class names */\r\n  classNames?: ClassNameObject;\r\n\r\n  // Size and Spacing\r\n  /** Width of the graph */\r\n  width?: number;\r\n  /** Height of the graph */\r\n  height?: number;\r\n  /** Minimum height of the graph */\r\n  minHeight?: number;\r\n  /** Relative height scaling factor. This overwrites the height props */\r\n  relativeHeight?: number;\r\n  /** Padding around the graph. Defaults to 0 if no backgroundColor is mentioned else defaults to 1rem */\r\n  padding?: string;\r\n\r\n  // Graph Parameters\r\n  /** Size of the visualization */\r\n  size?: number;\r\n  /** No. of grid icons in a single row */\r\n  gridSize?: number;\r\n  /** Defines the `d` attribute values for the SVG paths used as grid icons. Each path should be designed for a 24px × 24px viewBox. The order of paths determines the rendering order. If no paths are provided, a circle icon is used as the default. */\r\n  gridIcon?: { width?: number; height?: number; d: string[] };\r\n  /** Spacing between 2 grid icons */\r\n  unitPadding?: number;\r\n  /** Total no. of grid icon that are rendered in the chart */\r\n  totalNoOfDots?: number;\r\n  /** Toggle visibility of stroke for the unfilled grid icons. */\r\n  showStrokeForWhiteDots?: boolean;\r\n  /** Toggles if the graph animates in when loaded.  */\r\n  animate?: boolean | AnimateDataType;\r\n  /** Toggle visibility of color scale */\r\n  showColorScale?: boolean;\r\n  /** Configuration options for controlling number formatting, localization, prefixes/suffixes, precision, and zero padding. */\r\n  numberDisplayOptions?: NumberFormatOptions;\r\n  /** Defines how “NA” values should be displayed/labelled in the graph */\r\n  naLabel?: string;\r\n  /** Enable graph download option as png */\r\n  graphDownload?: boolean;\r\n  /** Enable data download option as a csv */\r\n  dataDownload?: boolean;\r\n\r\n  // Configuration and Options\r\n  /** Language setting  */\r\n  language?: Languages;\r\n  /** Color theme */\r\n  theme?: 'light' | 'dark';\r\n  /** Unique ID for the graph */\r\n  graphID?: string;\r\n}\r\n\r\nexport function UnitChart(props: Props) {\r\n  const {\r\n    data,\r\n    size,\r\n    graphTitle,\r\n    sources,\r\n    colors = Colors.light.categoricalColors.colors,\r\n    graphDescription,\r\n    totalNoOfDots = 100,\r\n    unitPadding = 3,\r\n    gridSize = 10,\r\n    footNote,\r\n    padding,\r\n    backgroundColor = false,\r\n    graphID,\r\n    graphDownload = false,\r\n    language = 'en',\r\n    showColorScale = true,\r\n    showStrokeForWhiteDots = true,\r\n    note,\r\n    dataDownload = false,\r\n    theme = 'light',\r\n    width,\r\n    height,\r\n    minHeight = 0,\r\n    relativeHeight,\r\n    ariaLabel,\r\n    styles,\r\n    classNames,\r\n    animate = false,\r\n    naLabel = 'NA',\r\n    numberDisplayOptions,\r\n    gridIcon,\r\n  } = props;\r\n  const svgRef = useRef(null);\r\n  const animateValue =\r\n    animate === true\r\n      ? { duration: 0.5, once: true, amount: 0.5 }\r\n      : animate || { duration: 0, once: true, amount: 0 };\r\n  const isInView = useInView(svgRef, {\r\n    once: animateValue.once,\r\n    amount: animateValue.amount,\r\n  });\r\n  const sizeVal = size || gridIcon  ? (gridIcon?.width || 24) * gridSize + unitPadding * (gridSize - 1) : 200;\r\n  const totalValue = sum(data.map((d) => d.value));\r\n  const graphParentDiv = useRef<HTMLDivElement>(null);\r\n  const gridDimension = sizeVal / gridSize;\r\n  const radius = (gridDimension - unitPadding * 2) / 2;\r\n  if (radius <= 0) {\r\n    console.error(\r\n      'The size of single unit is less than or equal to zero. Check values for ((dimension / gridSize) - (padding * 2)) / 2 is not less than or equal to 0.',\r\n      `size: ${size}`,\r\n      `gridSize: ${gridSize}`,\r\n      `gridDimension: ${gridDimension}`,\r\n      `unitPadding: ${unitPadding}`,\r\n    );\r\n    return null;\r\n  }\r\n\r\n  const cellsData: { color: string; index: number }[] = [];\r\n  data.forEach((item, index) => {\r\n    const count = Math.round((item.value / totalValue) * totalNoOfDots);\r\n    for (let i = 0; i < count; i += 1) {\r\n      cellsData.push({ color: colors[index], index });\r\n    }\r\n  });\r\n  return (\r\n    <GraphContainer\r\n      className={classNames?.graphContainer}\r\n      style={styles?.graphContainer}\r\n      id={graphID}\r\n      ref={graphParentDiv}\r\n      aria-label={ariaLabel}\r\n      backgroundColor={backgroundColor}\r\n      theme={theme}\r\n      language={language}\r\n      minHeight={minHeight}\r\n      width={width}\r\n      height={height}\r\n      relativeHeight={relativeHeight}\r\n      padding={padding}\r\n    >\r\n      {graphTitle || graphDescription || graphDownload ? (\r\n        <GraphHeader\r\n          styles={{\r\n            title: styles?.title,\r\n            description: styles?.description,\r\n          }}\r\n          classNames={{\r\n            title: classNames?.title,\r\n            description: classNames?.description,\r\n          }}\r\n          graphTitle={graphTitle}\r\n          graphDescription={graphDescription}\r\n          width={width}\r\n          graphDownload={graphDownload ? graphParentDiv : undefined}\r\n          dataDownload={\r\n            dataDownload\r\n              ? data.map((d) => d.data).filter((d) => d !== undefined).length > 0\r\n                ? data.map((d) => d.data).filter((d) => d !== undefined)\r\n                : data.filter((d) => d !== undefined)\r\n              : null\r\n          }\r\n        />\r\n      ) : null}\r\n      {note ? (\r\n        <H2\r\n          marginBottom='2xs'\r\n          className='text-content-primary font-bold'\r\n          style={{ width: width ? `${width}px` : '100%' }}\r\n        >\r\n          {note}\r\n        </H2>\r\n      ) : null}\r\n      {showColorScale ? (\r\n        <div className='mb-4 leading-0' style={{ width: width ? `${width}px` : '100%' }}>\r\n          <div className='flex mb-0 flex-wrap gap-x-4 gap-y-1'>\r\n            {data.map((d, i) => (\r\n              <div className='flex gap-2 items-center' key={d.label}>\r\n                <div className='w-3 h-3 rounded-full' style={{ backgroundColor: colors[i] }} />\r\n                <P marginBottom='none' size='sm' className='text-content-primary'>\r\n                  {d.label}:{' '}\r\n                  <span className='font-bold'>\r\n                    {numberFormattingFunction(\r\n                      d.value,\r\n                      naLabel,\r\n                      numberDisplayOptions?.precision ?? 2,\r\n                      numberDisplayOptions?.prefix,\r\n                      numberDisplayOptions?.suffix,\r\n                      numberDisplayOptions?.locale || 'en',\r\n                      numberDisplayOptions?.padZeros || 'none',\r\n                    )}\r\n                  </span>\r\n                </P>\r\n              </div>\r\n            ))}\r\n          </div>\r\n        </div>\r\n      ) : null}\r\n      <GraphArea>\r\n        <svg\r\n          width={`${width || sizeVal}px`}\r\n          height={`${Math.max(\r\n            minHeight,\r\n            height\r\n              ? relativeHeight && width\r\n                ? minHeight\r\n                  ? width * relativeHeight > minHeight\r\n                    ? width * relativeHeight\r\n                    : minHeight\r\n                  : width * relativeHeight\r\n                : height\r\n              : Math.floor((totalNoOfDots - 1) / gridSize) * gridDimension +\r\n                  gridDimension / 2 +\r\n                  radius +\r\n                  5,\r\n          )}px`}\r\n          ref={svgRef}\r\n          direction='ltr'\r\n          viewBox={`0 0 ${width || sizeVal} ${Math.max(\r\n            minHeight,\r\n            height\r\n              ? relativeHeight && width\r\n                ? minHeight\r\n                  ? width * relativeHeight > minHeight\r\n                    ? width * relativeHeight\r\n                    : minHeight\r\n                  : width * relativeHeight\r\n                : height\r\n              : Math.floor((totalNoOfDots - 1) / gridSize) * gridDimension +\r\n                  gridDimension / 2 +\r\n                  radius +\r\n                  5,\r\n          )}`}\r\n        >\r\n          <AnimatePresence>\r\n            <g>\r\n              {gridIcon\r\n                ? cellsData.map((d, i) => (\r\n                    <motion.svg\r\n                      key={d.index}\r\n                      style={{\r\n                        strokeWidth: 1,\r\n                      }}\r\n                      viewBox={`0 0 ${gridIcon?.width || 24} ${gridIcon?.height || 24}`}\r\n                      width={gridIcon?.width || radius * 2}\r\n                      height={gridIcon?.height || radius * 2}\r\n                      variants={{\r\n                        initial: {\r\n                          fill: '#fff',\r\n                          opacity: 0,\r\n                          ...(!showStrokeForWhiteDots ? { stroke: d.color } : {}),\r\n                          strokeWidth: 1,\r\n                        },\r\n                        whileInView: {\r\n                          fill: d.color,\r\n                          opacity: 1,\r\n                          ...(!showStrokeForWhiteDots ? { stroke: d.color } : {}),\r\n                          strokeWidth: 1,\r\n                          x:\r\n                            (i % gridSize) *\r\n                            (gridIcon?.width ? gridIcon.width + unitPadding : gridDimension),\r\n                          y:\r\n                            Math.floor(i / gridSize) *\r\n                            (gridIcon?.height ? gridIcon.height + unitPadding : gridDimension),\r\n                          transition: {\r\n                            duration: 0,\r\n                            delay: (animateValue.duration / cellsData.length) * i,\r\n                          },\r\n                        },\r\n                      }}\r\n                      initial='initial'\r\n                      animate={isInView ? 'whileInView' : 'initial'}\r\n                      className={\r\n                        (d.color.toLowerCase() === '#fff' ||\r\n                          d.color.toLowerCase() === '#ffffff' ||\r\n                          d.color.toLowerCase() === 'white') &&\r\n                        showStrokeForWhiteDots\r\n                          ? 'stroke-stroke'\r\n                          : ''\r\n                      }\r\n                    >\r\n                      {gridIcon.d.map((el) => (\r\n                        <path d={el} key={el} />\r\n                      ))}\r\n                    </motion.svg>\r\n                  ))\r\n                : cellsData.map((d, i) => (\r\n                    <motion.circle\r\n                      key={d.index}\r\n                      style={{\r\n                        strokeWidth: 1,\r\n                      }}\r\n                      variants={{\r\n                        initial: {\r\n                          fill: '#fff',\r\n                          opacity: 0,\r\n                          ...(!showStrokeForWhiteDots ? { stroke: d.color } : {}),\r\n                          strokeWidth: 1,\r\n                        },\r\n                        whileInView: {\r\n                          fill: d.color,\r\n                          opacity: 1,\r\n                          ...(!showStrokeForWhiteDots ? { stroke: d.color } : {}),\r\n                          strokeWidth: 1,\r\n                          cx: (i % gridSize) * gridDimension + gridDimension / 2,\r\n                          cy: Math.floor(i / gridSize) * gridDimension + gridDimension / 2,\r\n                          transition: {\r\n                            duration: 0,\r\n                            delay: (animateValue.duration / cellsData.length) * i,\r\n                          },\r\n                        },\r\n                      }}\r\n                      initial='initial'\r\n                      animate={isInView ? 'whileInView' : 'initial'}\r\n                      className={\r\n                        (d.color.toLowerCase() === '#fff' ||\r\n                          d.color.toLowerCase() === '#ffffff' ||\r\n                          d.color.toLowerCase() === 'white') &&\r\n                        showStrokeForWhiteDots\r\n                          ? 'stroke-stroke'\r\n                          : ''\r\n                      }\r\n                      r={radius}\r\n                    />\r\n                  ))}\r\n            </g>\r\n          </AnimatePresence>\r\n        </svg>\r\n      </GraphArea>\r\n      {sources || footNote ? (\r\n        <GraphFooter\r\n          styles={{\r\n            footnote: styles?.footnote,\r\n            source: styles?.source,\r\n          }}\r\n          sources={sources}\r\n          footNote={footNote}\r\n          width={width}\r\n        />\r\n      ) : null}\r\n    </GraphContainer>\r\n  );\r\n}\r\n"],"mappings":"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"}