{"version":3,"file":"index.mjs","names":[],"sources":["../../src/Heatmaps/index.tsx"],"sourcesContent":["'use client';\n\nimport { Flexbox, FlexboxProps } from '@lobehub/ui';\nimport { TooltipGroup } from '@lobehub/ui/base-ui';\nimport { cx, useTheme, useThemeMode } from 'antd-style';\nimport type { Day as WeekDay } from 'date-fns';\nimport { getYear, parseISO } from 'date-fns';\nimport { ReactNode, forwardRef, useMemo } from 'react';\n\nimport NoData, { NoDataProps } from '@/common/NoData';\nimport { usePrefersReducedMotion } from '@/hooks/usePrefersReducedMotion';\nimport type { Activity, Labels, Week } from '@/types/charts';\nimport { isOnSeverSide } from '@/utils';\nimport {\n  DEFAULT_LABELS,\n  generateEmptyData,\n  groupByWeeks,\n  maxWeekdayLabelLength,\n} from '@/utils/calendar';\nimport { createTheme } from '@/utils/theme';\n\nimport Calendar from './Calendar';\nimport ChartLabels from './ChartLabels';\nimport Footer from './Footer';\nimport { styles } from './styles';\n\nconst LABEL_MARGIN = 8;\n\nexport interface HeatmapsProps extends FlexboxProps {\n  blockMargin?: number;\n  blockRadius?: number;\n  blockSize?: number;\n  colors?: string[];\n  customTooltip?: (activity: Activity) => ReactNode;\n  data: Array<Activity>;\n  fontSize?: number;\n  hideColorLegend?: boolean;\n  hideMonthLabels?: boolean;\n  hideTotalCount?: boolean;\n  labels?: Labels;\n  loading?: boolean;\n  maxLevel?: number;\n  noDataText?: NoDataProps['noDataText'];\n  onValueChange?: (value: Activity) => void;\n  showTooltip?: boolean;\n  showWeekdayLabels?: boolean;\n  totalCount?: number;\n  weekStart?: WeekDay;\n}\n\nconst Heatmaps = forwardRef<HTMLDivElement, HeatmapsProps>((props, ref) => {\n  const {\n    data,\n    blockMargin = 4,\n    blockRadius = 2,\n    blockSize = 12,\n    onValueChange,\n    fontSize = 12,\n    hideColorLegend = false,\n    hideMonthLabels = false,\n    hideTotalCount = false,\n    labels: labelsProp,\n    maxLevel = Math.max(1, props.maxLevel || 4),\n    loading = false,\n    showTooltip = true,\n    customTooltip,\n    showWeekdayLabels = false,\n    style = {},\n    colors,\n    totalCount: totalCountProp,\n    weekStart = 0, // Sunday\n    className,\n    noDataText,\n    ...rest\n  } = props;\n\n  const activities = useMemo(() => {\n    if (loading) return generateEmptyData();\n    return data.map((item) => ({\n      ...item,\n      level: item.level > maxLevel ? maxLevel : item.level,\n    }));\n  }, [data, maxLevel, loading]);\n\n  const { isDarkMode } = useThemeMode();\n  const theme = useTheme();\n\n  // Note: defaultColors must use theme (actual color values) instead of cssVar\n  // because createTheme() uses chroma.valid() which requires actual color values, not CSS variables\n  const defaultColors = useMemo(() => {\n    switch (maxLevel) {\n      case 1: {\n        return [theme.colorFillSecondary, isDarkMode ? theme.lime8 : theme.green8];\n      }\n      case 2: {\n        return [\n          theme.colorFillSecondary,\n          isDarkMode ? theme.lime4 : theme.green4,\n          isDarkMode ? theme.lime8 : theme.green8,\n        ];\n      }\n      case 3: {\n        return [\n          theme.colorFillSecondary,\n          isDarkMode ? theme.lime3 : theme.green3,\n          isDarkMode ? theme.lime6 : theme.green6,\n          isDarkMode ? theme.lime9 : theme.green9,\n        ];\n      }\n      case 4: {\n        return [\n          theme.colorFillSecondary,\n          isDarkMode ? theme.lime2 : theme.green2,\n          isDarkMode ? theme.lime4 : theme.green4,\n          isDarkMode ? theme.lime6 : theme.green6,\n          isDarkMode ? theme.lime8 : theme.green8,\n        ];\n      }\n      default: {\n        return [theme.colorFillSecondary, theme.colorSuccess];\n      }\n    }\n  }, [theme, isDarkMode, maxLevel]);\n\n  const useAnimation = !usePrefersReducedMotion();\n\n  // Memoize weeks calculation - expensive operation\n  const weeks = useMemo(() => {\n    if (activities.length === 0) return [];\n    return groupByWeeks(activities, weekStart);\n  }, [activities, weekStart]);\n\n  // Memoize color scale calculation\n  const colorScale = useMemo(() => {\n    return createTheme(colors || defaultColors, maxLevel + 1);\n  }, [colors, defaultColors, maxLevel]);\n\n  // Memoize year calculation\n  const year = useMemo(() => {\n    if (activities.length === 0) return new Date().getFullYear();\n    const firstActivity = activities[0] as Activity;\n    return getYear(parseISO(firstActivity.date));\n  }, [activities]);\n\n  // Memoize labels object merge\n  const labels = useMemo(() => {\n    return Object.assign({}, DEFAULT_LABELS, labelsProp);\n  }, [labelsProp]);\n\n  // Memoize label height calculation\n  const labelHeight = useMemo(() => {\n    return hideMonthLabels ? 0 : fontSize + LABEL_MARGIN;\n  }, [hideMonthLabels, fontSize]);\n\n  // Memoize weekday label offset calculation\n  const weekdayLabelOffset = useMemo(() => {\n    if (!showWeekdayLabels || weeks.length === 0) return undefined;\n    const firstWeek = weeks[0] as Week;\n    return maxWeekdayLabelLength(firstWeek, weekStart, labels.weekdays, fontSize) + LABEL_MARGIN;\n  }, [showWeekdayLabels, weeks, weekStart, labels.weekdays, fontSize]);\n\n  // Memoize dimensions calculation\n  const dimensions = useMemo(() => {\n    return {\n      height: labelHeight + (blockSize + blockMargin) * 7 - blockMargin,\n      width: weeks.length * (blockSize + blockMargin) - blockMargin,\n    };\n  }, [labelHeight, blockSize, blockMargin, weeks.length]);\n\n  const { width, height } = dimensions;\n\n  // Memoize total count calculation\n  const totalCount = useMemo(() => {\n    if (typeof totalCountProp === 'number') return totalCountProp;\n    return activities.reduce((sum, activity) => sum + activity.count, 0);\n  }, [totalCountProp, activities]);\n\n  // Memoize container styles\n  const containerStyles = useMemo(() => {\n    const zeroColor = colorScale[0];\n    return {\n      fontSize,\n      ...(useAnimation && {\n        [`--lobe-heatmaps-loading`]: zeroColor,\n        [`--lobe-heatmaps-loading-active`]: theme.colorFill,\n      }),\n    };\n  }, [fontSize, useAnimation, colorScale, theme.colorFill]);\n\n  if (activities.length === 0) return <NoData noDataText={noDataText} />;\n\n  if (showWeekdayLabels && isOnSeverSide) return null;\n\n  return (\n    <TooltipGroup>\n      <Flexbox\n        className={cx(styles.container, className)}\n        ref={ref}\n        style={{ ...style, ...containerStyles }}\n        {...rest}\n      >\n        <div className={styles.scrollContainer}>\n          <svg\n            className={styles.calendar}\n            height={height}\n            style={{ marginLeft: weekdayLabelOffset }}\n            viewBox={`0 0 ${width} ${height}`}\n            width={width}\n          >\n            {!loading && !showWeekdayLabels && hideMonthLabels ? undefined : (\n              <ChartLabels\n                blockMargin={blockMargin}\n                blockSize={blockSize}\n                hideMonthLabels={hideMonthLabels}\n                labelHeight={labelHeight}\n                labelMargin={LABEL_MARGIN}\n                labels={labels}\n                showWeekdayLabels={showWeekdayLabels}\n                weekStart={weekStart}\n                weeks={weeks}\n              />\n            )}\n            <Calendar\n              blockMargin={blockMargin}\n              blockRadius={blockRadius}\n              blockSize={blockSize}\n              colorScale={colorScale}\n              customTooltip={customTooltip}\n              enableAnimation={loading && useAnimation}\n              labelHeight={labelHeight}\n              labels={labels}\n              maxLevel={maxLevel}\n              onValueChange={onValueChange}\n              showTooltip={showTooltip}\n              weeks={weeks}\n            />\n          </svg>\n        </div>\n        {hideTotalCount && hideColorLegend ? undefined : (\n          <Footer\n            blockRadius={blockRadius}\n            blockSize={blockSize}\n            colorScale={colorScale}\n            hideColorLegend={hideColorLegend}\n            hideTotalCount={hideTotalCount}\n            labels={labels}\n            loading={loading}\n            maxLevel={maxLevel}\n            totalCount={totalCount}\n            weekdayLabelOffset={weekdayLabelOffset}\n            year={year}\n          />\n        )}\n      </Flexbox>\n    </TooltipGroup>\n  );\n});\n\nHeatmaps.displayName = 'Heatmaps';\n\nexport default Heatmaps;\n"],"mappings":"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"}