{"version":3,"file":"Calendar.mjs","names":[],"sources":["../../src/Heatmaps/Calendar.tsx"],"sourcesContent":["import { Tooltip } from '@lobehub/ui/base-ui';\nimport { keyframes } from 'antd-style';\nimport isEqual from 'fast-deep-equal';\nimport { CSSProperties, ReactNode, memo, useMemo } from 'react';\n\nimport type { Activity, Labels, Week } from '@/types/charts';\n\n// Move keyframes outside component to avoid recreation on each render\nconst loadingAnimation = keyframes`\n  0% {\n    fill: var(--lobe-heatmaps-loading);\n  }\n  50% {\n    fill: var(--lobe-heatmaps-loading-active);\n  }\n  100% {\n    fill: var(--lobe-heatmaps-loading);\n  }\n`;\n\n// Helper function to generate tooltip title (moved outside component)\nconst getTooltipTitle = (\n  activity: Activity,\n  customTooltip?: (activity: Activity) => ReactNode,\n  labels?: Labels,\n): ReactNode => {\n  if (customTooltip) {\n    return customTooltip(activity);\n  }\n  if (labels?.tooltip) {\n    return labels.tooltip\n      .replace('{{count}}', String(activity.count))\n      .replace('{{date}}', String(activity.date));\n  }\n  return `${activity.count} activities on ${activity.date}`;\n};\n\ninterface CalendarBlockProps {\n  activity: Activity;\n  animationDelay: number;\n  blockMargin: number;\n  blockRadius: number;\n  blockSize: number;\n  color: string;\n  customTooltip?: (activity: Activity) => ReactNode;\n  dayIndex: number;\n  enableAnimation: boolean;\n  labelHeight: number;\n  labels?: Labels;\n  onValueChange?: (value: Activity) => void;\n  showTooltip: boolean;\n}\n\n// Memoized block component - only re-renders when its own props change\nconst CalendarBlock = memo<CalendarBlockProps>(\n  ({\n    activity,\n    blockSize,\n    blockRadius,\n    color,\n    labelHeight,\n    blockMargin,\n    dayIndex,\n    enableAnimation,\n    animationDelay,\n    onValueChange,\n    showTooltip,\n    customTooltip,\n    labels,\n  }) => {\n    const style = useMemo<CSSProperties>(\n      () => ({\n        cursor: onValueChange ? 'pointer' : undefined,\n        ...(enableAnimation && {\n          animation: `${loadingAnimation} 1.75s ease-in-out infinite`,\n          animationDelay: `${animationDelay}ms`,\n        }),\n      }),\n      [enableAnimation, animationDelay, onValueChange],\n    );\n\n    const y = labelHeight + (blockSize + blockMargin) * dayIndex;\n    const tooltipTitle = useMemo(\n      () => getTooltipTitle(activity, customTooltip, labels),\n      [activity, customTooltip, labels],\n    );\n\n    const block = (\n      <rect\n        data-date={activity.date}\n        data-level={activity.level}\n        fill={color}\n        height={blockSize}\n        onClick={onValueChange ? () => onValueChange(activity) : undefined}\n        rx={blockRadius}\n        ry={blockRadius}\n        style={style}\n        width={blockSize}\n        x={0}\n        y={y}\n      />\n    );\n\n    return showTooltip ? <Tooltip title={tooltipTitle}>{block}</Tooltip> : block;\n  },\n  isEqual,\n);\n\nCalendarBlock.displayName = 'CalendarBlock';\n\ninterface CalendarWeekProps {\n  blockMargin: number;\n  blockRadius: number;\n  blockSize: number;\n  colorScale: string[];\n  customTooltip?: (activity: Activity) => ReactNode;\n  enableAnimation: boolean;\n  labelHeight: number;\n  labels: Labels;\n  maxLevel: number;\n  onValueChange?: (value: Activity) => void;\n  showTooltip: boolean;\n  week: Week;\n  weekIndex: number;\n}\n\n// Memoized week component - only re-renders when its own week data changes\nconst CalendarWeek = memo<CalendarWeekProps>(\n  ({\n    week,\n    weekIndex,\n    blockMargin,\n    blockRadius,\n    blockSize,\n    colorScale,\n    customTooltip,\n    enableAnimation,\n    labelHeight,\n    labels,\n    maxLevel,\n    onValueChange,\n    showTooltip,\n  }) => {\n    return (\n      <g transform={`translate(${(blockSize + blockMargin) * weekIndex}, 0)`}>\n        {week.map((activity, dayIndex) => {\n          if (!activity) {\n            return null;\n          }\n\n          if (activity.level < 0 || activity.level > maxLevel) {\n            throw new RangeError(\n              `Provided activity level ${activity.level} for ${activity.date} is out of range. It must be between 0 and ${maxLevel}.`,\n            );\n          }\n\n          const animationDelay = weekIndex * 20 + dayIndex * 20;\n          const color = colorScale[activity.level];\n\n          return (\n            <CalendarBlock\n              activity={activity}\n              animationDelay={animationDelay}\n              blockMargin={blockMargin}\n              blockRadius={blockRadius}\n              blockSize={blockSize}\n              color={color}\n              customTooltip={customTooltip}\n              dayIndex={dayIndex}\n              enableAnimation={enableAnimation}\n              key={activity.date}\n              labelHeight={labelHeight}\n              labels={labels}\n              onValueChange={onValueChange}\n              showTooltip={showTooltip}\n            />\n          );\n        })}\n      </g>\n    );\n  },\n  isEqual,\n);\n\nCalendarWeek.displayName = 'CalendarWeek';\n\ninterface CalendarProps {\n  blockMargin: number;\n  blockRadius: number;\n  blockSize: number;\n  colorScale: string[];\n  customTooltip?: (activity: Activity) => ReactNode;\n  enableAnimation: boolean;\n  labelHeight: number;\n  labels: Labels;\n  maxLevel: number;\n  onValueChange?: (value: Activity) => void;\n  showTooltip: boolean;\n  weeks: Week[];\n}\n\nconst Calendar = memo<CalendarProps>(\n  ({\n    weeks,\n    maxLevel,\n    enableAnimation,\n    colorScale,\n    blockSize,\n    blockRadius,\n    blockMargin,\n    labelHeight,\n    showTooltip,\n    customTooltip,\n    labels,\n    onValueChange,\n  }) => {\n    return (\n      <>\n        {weeks.map((week, weekIndex) => (\n          <CalendarWeek\n            blockMargin={blockMargin}\n            blockRadius={blockRadius}\n            blockSize={blockSize}\n            colorScale={colorScale}\n            customTooltip={customTooltip}\n            enableAnimation={enableAnimation}\n            key={weekIndex}\n            labelHeight={labelHeight}\n            labels={labels}\n            maxLevel={maxLevel}\n            onValueChange={onValueChange}\n            showTooltip={showTooltip}\n            week={week}\n            weekIndex={weekIndex}\n          />\n        ))}\n      </>\n    );\n  },\n  isEqual,\n);\n\nCalendar.displayName = 'Calendar';\n\nexport default Calendar;\n"],"mappings":";;;;;;AAQA,MAAM,mBAAmB,SAAS;;;;;;;;;;;AAalC,MAAM,mBACJ,UACA,eACA,WACc;CACd,IAAI,eACF,OAAO,cAAc,QAAQ;CAE/B,IAAI,QAAQ,SACV,OAAO,OAAO,QACX,QAAQ,aAAa,OAAO,SAAS,KAAK,CAAC,CAAC,CAC5C,QAAQ,YAAY,OAAO,SAAS,IAAI,CAAC;CAE9C,OAAO,GAAG,SAAS,MAAM,iBAAiB,SAAS;AACrD;AAmBA,MAAM,gBAAgB,MACnB,EACC,UACA,WACA,aACA,OACA,aACA,aACA,UACA,iBACA,gBACA,eACA,aACA,eACA,aACI;CACJ,MAAM,QAAQ,eACL;EACL,QAAQ,gBAAgB,YAAY,KAAA;EACpC,GAAI,mBAAmB;GACrB,WAAW,GAAG,iBAAiB;GAC/B,gBAAgB,GAAG,eAAe;EACpC;CACF,IACA;EAAC;EAAiB;EAAgB;CAAa,CACjD;CAEA,MAAM,IAAI,eAAe,YAAY,eAAe;CACpD,MAAM,eAAe,cACb,gBAAgB,UAAU,eAAe,MAAM,GACrD;EAAC;EAAU;EAAe;CAAM,CAClC;CAEA,MAAM,QACJ,oBAAC,QAAD;EACE,aAAW,SAAS;EACpB,cAAY,SAAS;EACrB,MAAM;EACN,QAAQ;EACR,SAAS,sBAAsB,cAAc,QAAQ,IAAI,KAAA;EACzD,IAAI;EACJ,IAAI;EACG;EACP,OAAO;EACP,GAAG;EACA;CACJ,CAAA;CAGH,OAAO,cAAc,oBAAC,SAAD;EAAS,OAAO;EAAe,UAAA;CAAe,CAAA,IAAI;AACzE,GACA,OACF;AAEA,cAAc,cAAc;AAmB5B,MAAM,eAAe,MAClB,EACC,MACA,WACA,aACA,aACA,WACA,YACA,eACA,iBACA,aACA,QACA,UACA,eACA,kBACI;CACJ,OACE,oBAAC,KAAD;EAAG,WAAW,cAAc,YAAY,eAAe,UAAU;EAC9D,UAAA,KAAK,KAAK,UAAU,aAAa;GAChC,IAAI,CAAC,UACH,OAAO;GAGT,IAAI,SAAS,QAAQ,KAAK,SAAS,QAAQ,UACzC,MAAM,IAAI,WACR,2BAA2B,SAAS,MAAM,OAAO,SAAS,KAAK,6CAA6C,SAAS,EACvH;GAGF,MAAM,iBAAiB,YAAY,KAAK,WAAW;GACnD,MAAM,QAAQ,WAAW,SAAS;GAElC,OACE,oBAAC,eAAD;IACY;IACM;IACH;IACA;IACF;IACJ;IACQ;IACL;IACO;IAEJ;IACL;IACO;IACF;GACd,GALM,SAAS,IAKf;EAEL,CAAC;CACA,CAAA;AAEP,GACA,OACF;AAEA,aAAa,cAAc;AAiB3B,MAAM,WAAW,MACd,EACC,OACA,UACA,iBACA,YACA,WACA,aACA,aACA,aACA,aACA,eACA,QACA,oBACI;CACJ,OACE,oBAAA,YAAA,EAAA,UACG,MAAM,KAAK,MAAM,cAChB,oBAAC,cAAD;EACe;EACA;EACF;EACC;EACG;EACE;EAEJ;EACL;EACE;EACK;EACF;EACP;EACK;CACZ,GARM,SAQN,CACF,EACD,CAAA;AAEN,GACA,OACF;AAEA,SAAS,cAAc"}