{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "category-bar",
  "title": "Category Bar",
  "description": "The Edgecom Category Bar component.",
  "registryDependencies": [
    "edgecom-ai/design-system/theme"
  ],
  "files": [
    {
      "path": "src/components/ui/category-bar.tsx",
      "content": "'use client'\n\nimport React from 'react'\n\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\n\nimport { cn } from '@/lib/utils'\n\nconst DEFAULT_BAR_COLOR = 'bg-muted'\n\nconst getMarkerBgColor = (marker: number | undefined, values: number[], colors: string[]): string => {\n  if (marker === undefined) return ''\n\n  if (marker === 0) {\n    for (let index = 0; index < values.length; index++) {\n      if (values[index] > 0) {\n        return colors[index] ?? DEFAULT_BAR_COLOR\n      }\n    }\n  }\n\n  let prefixSum = 0\n\n  for (let index = 0; index < values.length; index++) {\n    prefixSum += values[index]\n\n    if (prefixSum >= marker) {\n      return colors[index] ?? DEFAULT_BAR_COLOR\n    }\n  }\n\n  return colors[values.length - 1] ?? DEFAULT_BAR_COLOR\n}\n\nconst getPositionLeft = (value: number | undefined, maxValue: number): number => (value ? (value / maxValue) * 100 : 0)\n\nconst sumNumericArray = (arr: number[]) => arr.reduce((prefixSum, num) => prefixSum + num, 0)\n\nconst formatNumber = (num: number): string => {\n  if (Number.isInteger(num)) {\n    return num.toString()\n  }\n\n  return num.toFixed(1)\n}\n\nconst BarLabels = ({ values }: { values: number[] }) => {\n  const sumValues = React.useMemo(() => sumNumericArray(values), [values])\n\n  const labels = React.useMemo(() => {\n    type Label = { showLabel: boolean; prefixSum: number; widthPositionLeft: number }\n\n    return values.reduce<{ prefixSum: number; hidden: number; items: Label[] }>(\n      (state, widthPercentage) => {\n        const prefixSum = state.prefixSum + widthPercentage\n\n        const showLabel =\n          (widthPercentage >= 0.1 * sumValues || state.hidden >= 0.09 * sumValues) &&\n          sumValues - prefixSum >= 0.1 * sumValues &&\n          prefixSum >= 0.1 * sumValues &&\n          prefixSum < 0.9 * sumValues\n\n        return {\n          prefixSum,\n          hidden: showLabel ? 0 : state.hidden + widthPercentage,\n          items: [...state.items, { showLabel, prefixSum, widthPositionLeft: getPositionLeft(widthPercentage, sumValues) }]\n        }\n      },\n      { prefixSum: 0, hidden: 0, items: [] }\n    ).items\n  }, [values, sumValues])\n\n  return (\n    <div\n      className={cn(\n        'relative mb-2 flex h-5 w-full text-sm font-medium',\n\n        // text color\n        'text-muted-foreground'\n      )}\n    >\n      <div className='absolute bottom-0 left-0 flex items-center'>0</div>\n      {labels.map((label, index) => (\n        <div\n          key={`item-${index}`}\n          className='flex items-center justify-end pr-0.5'\n          style={{ width: `${label.widthPositionLeft}%` }}\n        >\n          {label.showLabel ? (\n            <span className={cn('block translate-x-1/2 text-sm tabular-nums')}>{formatNumber(label.prefixSum)}</span>\n          ) : null}\n        </div>\n      ))}\n      <div className='absolute right-0 bottom-0 flex items-center'>{formatNumber(sumValues)}</div>\n    </div>\n  )\n}\n\ninterface CategoryBarProps extends React.HTMLAttributes<HTMLDivElement> {\n  values: number[]\n  colors?: string[]\n  marker?: { value: number; tooltip?: string; showAnimation?: boolean }\n  showLabels?: boolean\n}\n\nconst CategoryBar = React.forwardRef<HTMLDivElement, CategoryBarProps>(\n  ({ values = [], colors = [], marker, showLabels = true, className, ...props }, forwardedRef) => {\n    const markerBgColor = React.useMemo(() => getMarkerBgColor(marker?.value, values, colors), [marker, values, colors])\n\n    const maxValue = React.useMemo(() => sumNumericArray(values), [values])\n\n    const adjustedMarkerValue = React.useMemo(() => {\n      if (marker === undefined) return undefined\n      if (marker.value < 0) return 0\n      if (marker.value > maxValue) return maxValue\n\n      return marker.value\n    }, [marker, maxValue])\n\n    const markerPositionLeft: number = React.useMemo(\n      () => getPositionLeft(adjustedMarkerValue, maxValue),\n      [adjustedMarkerValue, maxValue]\n    )\n\n    return (\n      <div\n        ref={forwardedRef}\n        className={cn(className)}\n        aria-label='Category bar'\n        aria-valuenow={marker?.value}\n        {...props}\n      >\n        {showLabels ? <BarLabels values={values} /> : null}\n        <div className='relative flex h-2 w-full items-center'>\n          <div\n            className={cn('flex h-full flex-1 items-center overflow-hidden rounded-full', showLabels ? 'gap-0.5' : '')}\n          >\n            {values.map((value, index) => {\n              const barColor = colors[index] ?? DEFAULT_BAR_COLOR\n              const percentage = (value / maxValue) * 100\n\n              return (\n                <div\n                  key={`item-${index}`}\n                  className={cn('h-full', barColor, percentage === 0 && 'hidden')}\n                  style={{ width: `${percentage}%` }}\n                />\n              )\n            })}\n          </div>\n\n          {marker !== undefined ? (\n            <div\n              className={cn(\n                'absolute w-2 -translate-x-1/2',\n                marker.showAnimation && 'transform-gpu transition-all duration-300 ease-in-out'\n              )}\n              style={{\n                left: `${markerPositionLeft}%`\n              }}\n            >\n              {marker.tooltip ? (\n                <Tooltip>\n                  <TooltipTrigger\n                    render={\n                      <div\n                        aria-hidden='true'\n                        className={cn('relative mx-auto h-4 w-1 rounded-full ring-2', 'ring-background', markerBgColor)}\n                      >\n                        <div aria-hidden className='absolute size-7 -translate-x-[45%] -translate-y-[15%]'></div>\n                      </div>\n                    }\n                  />\n                  <TooltipContent>{marker.tooltip}</TooltipContent>\n                </Tooltip>\n              ) : (\n                <div className={cn('mx-auto h-4 w-1 rounded-full ring-2', 'ring-background', markerBgColor)} />\n              )}\n            </div>\n          ) : null}\n        </div>\n      </div>\n    )\n  }\n)\n\nCategoryBar.displayName = 'CategoryBar'\n\nexport { CategoryBar, type CategoryBarProps }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}