{"version":3,"file":"Heatmap.mjs","names":["classes"],"sources":["../../src/Heatmap/Heatmap.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n  Box,\n  BoxProps,\n  ElementProps,\n  factory,\n  Factory,\n  StylesApiProps,\n  Tooltip,\n  TooltipFloatingProps,\n  useProps,\n  useStyles,\n} from '@mantine/core';\nimport { getBoundaries } from './get-boundaries/get-boundaries';\nimport { getColumns, getFirstMonthColumnIndex, HeatmapColumn } from './get-columns/get-columns';\nimport { getDatesRange } from './get-dates-range/get-dates-range';\nimport { getMonthsRange } from './get-months-range/get-months-range';\nimport { HeatmapSplitWeeks } from './HeatmapSplitWeeks';\nimport { HeatmapWeeks } from './HeatmapWeeks';\nimport { rotateWeekdaysNames } from './rotate-weekdays-names/rotate-weekdays-names';\nimport classes from './Heatmap.module.css';\n\nexport type HeatmapStylesNames = 'root' | 'rect' | 'weekdayLabel' | 'monthLabel';\n\ninterface HeatmapRectData {\n  date: string;\n  value: number | null;\n}\n\nexport interface HeatmapProps\n  extends\n    BoxProps,\n    StylesApiProps<HeatmapFactory>,\n    ElementProps<'svg', 'display' | 'opacity' | 'viewBox' | 'width' | 'height'> {\n  /** Heatmap data, key is date in `YYYY-MM-DD` format (interpreted as a UTC calendar day) */\n  data: Record<string, number>;\n\n  /** Heatmap domain, array of 2 numbers, min and max values, calculated from data by default */\n  domain?: [number, number];\n\n  /** Heatmap start date. Current date - 1 year by default. Date is normalized to UTC midnight of the intended calendar day. */\n  startDate?: Date | string;\n\n  /** Heatmap end date. Current date by default. Date is normalized to UTC midnight of the intended calendar day. */\n  endDate?: Date | string;\n\n  /** If set, month labels are displayed @default false */\n  withMonthLabels?: boolean;\n\n  /** Month labels, array of 12 elements, can be used for localization */\n  monthLabels?: string[];\n\n  /** If set, weekday labels are displayed @default false */\n  withWeekdayLabels?: boolean;\n\n  /** Weekday labels, array of 7 elements, can be used for localization */\n  weekdayLabels?: string[];\n\n  /** If set, trailing dates that do not fall into the given `startDate` – `endDate` range are displayed to fill empty space. @default true */\n  withOutsideDates?: boolean;\n\n  /** First day of week, 0 – Sunday, 1 – Monday. @default 1 – Monday */\n  firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n\n  /** Size of day rect in px @default 10 */\n  rectSize?: number;\n\n  /** Gap between rects in px @default 1 */\n  gap?: number;\n\n  /** Rect radius in px @default 2 */\n  rectRadius?: number;\n\n  /** Colors array, used to calculate color for each value, by default 4 shades of green colors are used */\n  colors?: string[];\n\n  /** Width of weekday labels column @default 30 */\n  weekdaysLabelsWidth?: number;\n\n  /** Height of month labels row @default 30 */\n  monthsLabelsHeight?: number;\n\n  /** Font size of month and weekday labels @default 12 */\n  fontSize?: number;\n\n  /** A function to generate tooltip label based on the hovered rect date and value, required for the tooltip to be visible */\n  getTooltipLabel?: (input: HeatmapRectData) => React.ReactNode;\n\n  /** If set, tooltip is displayed on rect hover @default false */\n  withTooltip?: boolean;\n\n  /** Props passed down to the `Tooltip.Floating` component */\n  tooltipProps?: Partial<TooltipFloatingProps>;\n\n  /** Props passed down to each rect depending on its date and associated value */\n  getRectProps?: (input: HeatmapRectData) => React.ComponentProps<'rect'>;\n\n  /** If set, inserts a spacer column between months @default false */\n  splitMonths?: boolean;\n}\n\nexport type HeatmapFactory = Factory<{\n  props: HeatmapProps;\n  ref: SVGSVGElement;\n  stylesNames: HeatmapStylesNames;\n}>;\n\nconst defaultProps = {\n  monthLabels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n  weekdayLabels: ['Sun', 'Mon', '', 'Wed', '', 'Fri', ''],\n  withOutsideDates: true,\n  firstDayOfWeek: 1,\n  rectSize: 10,\n  weekdaysLabelsWidth: 30,\n  monthsLabelsHeight: 14,\n  gap: 1,\n  rectRadius: 2,\n  fontSize: 12,\n  colors: [\n    'var(--heatmap-level-1)',\n    'var(--heatmap-level-2)',\n    'var(--heatmap-level-3)',\n    'var(--heatmap-level-4)',\n  ],\n} satisfies Partial<HeatmapProps>;\n\nexport const Heatmap = factory<HeatmapFactory>((_props) => {\n  const props = useProps('Heatmap', defaultProps, _props);\n  const {\n    classNames,\n    className,\n    style,\n    styles,\n    unstyled,\n    vars,\n    data,\n    startDate,\n    endDate,\n    withMonthLabels,\n    withWeekdayLabels,\n    weekdayLabels,\n    withOutsideDates,\n    monthLabels,\n    firstDayOfWeek,\n    rectSize = 10,\n    gap = 1,\n    rectRadius,\n    domain,\n    colors,\n    weekdaysLabelsWidth,\n    monthsLabelsHeight,\n    fontSize,\n    getTooltipLabel,\n    withTooltip,\n    tooltipProps,\n    getRectProps,\n    splitMonths,\n    attributes,\n    ...others\n  } = props;\n\n  const getStyles = useStyles<HeatmapFactory>({\n    name: 'Heatmap',\n    classes,\n    props,\n    className,\n    style,\n    classNames,\n    styles,\n    unstyled,\n    attributes,\n    vars,\n  });\n\n  const [hoveredRect, setHoveredRect] = useState<HeatmapRectData | null>(null);\n  const rectSizeWithGap = rectSize + gap;\n  const weekdaysOffset = withWeekdayLabels ? weekdaysLabelsWidth : 0;\n  const monthsOffset = withMonthLabels ? monthsLabelsHeight : 0;\n  const [min, max] = getBoundaries({ data, domain });\n  const rotatedWeekdayLabels = useMemo(\n    () => rotateWeekdaysNames(weekdayLabels, firstDayOfWeek),\n    [weekdayLabels, firstDayOfWeek]\n  );\n\n  const datesRange = getDatesRange({\n    startDate,\n    endDate,\n    withOutsideDates,\n    firstDayOfWeek,\n  });\n\n  // Calculate months range for labels and optional split between months\n  const monthsRange = withMonthLabels || splitMonths ? getMonthsRange(datesRange) : [];\n\n  // Shared props for weeks rendering components\n  const weeksProps = {\n    data,\n    datesRange,\n    rectSize,\n    gap,\n    rectRadius,\n    min,\n    max,\n    colors,\n    withTooltip,\n    setHoveredRect,\n    getRectProps,\n    getStyles,\n  };\n\n  // Use different rendering logic based on splitMonths\n  const weeks = splitMonths ? (\n    <HeatmapSplitWeeks {...weeksProps} />\n  ) : (\n    <HeatmapWeeks {...weeksProps} />\n  );\n\n  // Calculate total columns based on whether splitMonths is enabled\n  const totalColumns = splitMonths ? getColumns(datesRange, splitMonths).length : datesRange.length;\n\n  const computeMonthLabelX = (monthPosition: number, monthIndex: number) => {\n    if (!splitMonths) {\n      return monthPosition * rectSizeWithGap + gap + weekdaysOffset;\n    }\n\n    // For split months, find the first column index that has this month and shift label by 1 column\n    const firstMonth = monthsRange[monthIndex];\n    const columns: HeatmapColumn[] = getColumns(datesRange, splitMonths);\n    const i = getFirstMonthColumnIndex(columns, firstMonth.month);\n    const base = i >= 0 ? i : monthPosition;\n    // shift right by one column\n    return (base + 1) * rectSizeWithGap + gap + weekdaysOffset;\n  };\n\n  const monthsLabelsNodes =\n    withMonthLabels && monthLabels\n      ? monthsRange.map((month, monthIndex) => {\n          // For non-split months, use original logic with minimum size of 3\n          // For split months, use minimum size of 2\n          const minSize = splitMonths ? 2 : 3;\n          if (month.size < minSize) {\n            return null;\n          }\n\n          const monthLabel = monthLabels[month.month];\n\n          return (\n            <text\n              key={monthIndex}\n              x={computeMonthLabelX(month.position, monthIndex)}\n              y={monthsLabelsHeight - 4}\n              width={month.size * rectSizeWithGap}\n              fontSize={fontSize}\n              {...getStyles('monthLabel')}\n            >\n              {monthLabel}\n            </text>\n          );\n        })\n      : null;\n\n  const weekdayLabelsNodes =\n    withWeekdayLabels && weekdayLabels\n      ? rotatedWeekdayLabels.map((weekdayLabel, dayIndex) => (\n          <text\n            key={dayIndex}\n            x={0}\n            y={(dayIndex + 1) * rectSizeWithGap - gap + monthsOffset}\n            width={weekdaysLabelsWidth}\n            fontSize={fontSize}\n            {...getStyles('weekdayLabel')}\n          >\n            {weekdayLabel}\n          </text>\n        ))\n      : null;\n\n  const label = getTooltipLabel && hoveredRect && withTooltip ? getTooltipLabel(hoveredRect) : null;\n\n  return (\n    <Box\n      component=\"svg\"\n      width={rectSizeWithGap * totalColumns + gap + weekdaysOffset}\n      height={rectSizeWithGap * 7 + gap + monthsOffset}\n      {...getStyles('root')}\n      {...others}\n    >\n      <Tooltip.Floating\n        label={label}\n        disabled={!withTooltip || !label}\n        position=\"top\"\n        {...tooltipProps}\n      >\n        <g transform={`translate(${weekdaysOffset}, ${monthsOffset})`} data-id=\"all-weeks\">\n          {/* Required for tooltip to remain visible while gaps between rects are hovered */}\n          {withTooltip && (\n            <rect\n              fill=\"transparent\"\n              width={rectSizeWithGap * totalColumns + gap}\n              height={rectSizeWithGap * 7 + gap}\n            />\n          )}\n          {weeks}\n        </g>\n      </Tooltip.Floating>\n      {weekdayLabelsNodes}\n      {monthsLabelsNodes}\n    </Box>\n  );\n});\n\nHeatmap.displayName = '@mantine/charts/Heatmap';\nHeatmap.classes = classes;\n\nexport namespace Heatmap {\n  export type Props = HeatmapProps;\n  export type StylesNames = HeatmapStylesNames;\n  export type Factory = HeatmapFactory;\n}\n"],"mappings":"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"}