@sckyzo/cosmos-charts 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +12 -1
  3. package/dist/BarChart.cjs +18 -18
  4. package/dist/BarChart.js +5 -5
  5. package/dist/BoxPlotChart.cjs +18 -18
  6. package/dist/BoxPlotChart.js +5 -5
  7. package/dist/BumpChart.cjs +6 -6
  8. package/dist/BumpChart.js +3 -3
  9. package/dist/CalendarChart.cjs +9 -7
  10. package/dist/CalendarChart.cjs.map +1 -1
  11. package/dist/CalendarChart.js +5 -3
  12. package/dist/CalendarChart.js.map +1 -1
  13. package/dist/DonutChart.cjs +8 -8
  14. package/dist/DonutChart.js +3 -3
  15. package/dist/FunnelChart.cjs +8 -6
  16. package/dist/FunnelChart.cjs.map +1 -1
  17. package/dist/FunnelChart.js +4 -2
  18. package/dist/FunnelChart.js.map +1 -1
  19. package/dist/GradientRadial.cjs +3 -3
  20. package/dist/GradientRadial.js +2 -2
  21. package/dist/HeatmapChart.cjs +8 -6
  22. package/dist/HeatmapChart.cjs.map +1 -1
  23. package/dist/HeatmapChart.js +5 -3
  24. package/dist/HeatmapChart.js.map +1 -1
  25. package/dist/LineAreaChart.cjs +13 -13
  26. package/dist/LineAreaChart.js +4 -4
  27. package/dist/MixedChart.cjs +16 -16
  28. package/dist/MixedChart.js +5 -5
  29. package/dist/RadarChart.cjs +9 -9
  30. package/dist/RadarChart.js +3 -3
  31. package/dist/RadialBarChart.cjs +3 -3
  32. package/dist/RadialBarChart.js +1 -1
  33. package/dist/RangeBarChart.cjs +13 -13
  34. package/dist/RangeBarChart.js +4 -4
  35. package/dist/RealtimeChart.cjs +8 -8
  36. package/dist/RealtimeChart.js +2 -2
  37. package/dist/ScatterChart.cjs +12 -12
  38. package/dist/ScatterChart.js +4 -4
  39. package/dist/SemiCircleGauge.cjs +2 -2
  40. package/dist/SemiCircleGauge.js +1 -1
  41. package/dist/Sparkline.cjs +4 -4
  42. package/dist/Sparkline.js +1 -1
  43. package/dist/StrokedGauge.cjs +3 -3
  44. package/dist/StrokedGauge.js +2 -2
  45. package/dist/TreemapChart.cjs +2 -2
  46. package/dist/TreemapChart.js +1 -1
  47. package/dist/WaterfallChart.cjs +12 -12
  48. package/dist/WaterfallChart.js +3 -3
  49. package/dist/{chunk-6ALKZLN2.js → chunk-4AKHZNWP.js} +3 -3
  50. package/dist/{chunk-6ALKZLN2.js.map → chunk-4AKHZNWP.js.map} +1 -1
  51. package/dist/{chunk-3N43QT36.js → chunk-56WD3OHA.js} +6 -4
  52. package/dist/chunk-56WD3OHA.js.map +1 -0
  53. package/dist/{chunk-YWYF5CCJ.js → chunk-6FGJZBTC.js} +3 -3
  54. package/dist/{chunk-YWYF5CCJ.js.map → chunk-6FGJZBTC.js.map} +1 -1
  55. package/dist/{chunk-MN4CDVOS.cjs → chunk-CMBNNLIV.cjs} +4 -4
  56. package/dist/{chunk-MN4CDVOS.cjs.map → chunk-CMBNNLIV.cjs.map} +1 -1
  57. package/dist/{chunk-RILUG7JB.js → chunk-DI2FBGKM.js} +3 -3
  58. package/dist/{chunk-RILUG7JB.js.map → chunk-DI2FBGKM.js.map} +1 -1
  59. package/dist/{chunk-SGG7XKOY.js → chunk-ESVGCUJN.js} +3 -3
  60. package/dist/{chunk-SGG7XKOY.js.map → chunk-ESVGCUJN.js.map} +1 -1
  61. package/dist/{chunk-RLHGPHC7.cjs → chunk-G4WOPX4D.cjs} +4 -4
  62. package/dist/{chunk-RLHGPHC7.cjs.map → chunk-G4WOPX4D.cjs.map} +1 -1
  63. package/dist/{chunk-GZRMVE4X.cjs → chunk-RCDTBZC2.cjs} +4 -4
  64. package/dist/{chunk-GZRMVE4X.cjs.map → chunk-RCDTBZC2.cjs.map} +1 -1
  65. package/dist/{chunk-6NRRABPF.cjs → chunk-SR2ESX3R.cjs} +6 -4
  66. package/dist/chunk-SR2ESX3R.cjs.map +1 -0
  67. package/dist/{chunk-ICL5K2CW.js → chunk-UL3SNXQR.js} +3 -3
  68. package/dist/{chunk-ICL5K2CW.js.map → chunk-UL3SNXQR.js.map} +1 -1
  69. package/dist/{chunk-6BKLL7LZ.cjs → chunk-XMFBVMW7.cjs} +4 -4
  70. package/dist/{chunk-6BKLL7LZ.cjs.map → chunk-XMFBVMW7.cjs.map} +1 -1
  71. package/dist/{chunk-Z4LZFMDH.cjs → chunk-XUGQ6VXO.cjs} +4 -4
  72. package/dist/{chunk-Z4LZFMDH.cjs.map → chunk-XUGQ6VXO.cjs.map} +1 -1
  73. package/dist/{chunk-HMH6DI7U.js → chunk-YMRNTIOK.js} +3 -3
  74. package/dist/{chunk-HMH6DI7U.js.map → chunk-YMRNTIOK.js.map} +1 -1
  75. package/dist/{chunk-TPH5T3DA.cjs → chunk-ZWHCUYIN.cjs} +4 -4
  76. package/dist/{chunk-TPH5T3DA.cjs.map → chunk-ZWHCUYIN.cjs.map} +1 -1
  77. package/package.json +1 -1
  78. package/dist/chunk-3N43QT36.js.map +0 -1
  79. package/dist/chunk-6NRRABPF.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/calendarGrid.ts","../src/CalendarChart.tsx"],"names":["metrics"],"mappings":"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","file":"CalendarChart.js","sourcesContent":["// The date maths of `CalendarChart`. A day is a whole number: the days since\n// 1970-01-01 in UTC, so nothing here reads the time zone of the machine or the\n// date of today.\n\nexport const DAY_MS = 86_400_000;\n\n/** The weekday a week starts on: 0 is Sunday, 1 is Monday. */\nexport type WeekStart = 0 | 1;\n\nconst mod = (n: number, by: number) => ((n % by) + by) % by;\n\n/** The day number of an ISO date (`2026-03-14`), or `undefined` when it is not a real date. */\nexport const parseDay = (iso: string): number | undefined => {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(iso);\n if (!match) return undefined;\n const [year, month, date] = [Number(match[1]), Number(match[2]), Number(match[3])];\n const time = Date.UTC(year, month - 1, date);\n const check = new Date(time);\n // `Date.UTC` rolls 02-30 over to 03-02: a date that moved is not a date.\n return check.getUTCFullYear() === year &&\n check.getUTCMonth() === month - 1 &&\n check.getUTCDate() === date\n ? time / DAY_MS\n : undefined;\n};\n\n/** The day number of a date: `month` is 0 to 11. */\nexport const dayOf = (year: number, month: number, date: number): number =>\n Date.UTC(year, month, date) / DAY_MS;\n\n/** The ISO date of a day number. */\nexport const formatDay = (day: number): string => new Date(day * DAY_MS).toISOString().slice(0, 10);\n\n/** The weekday of a day number, 0 (Sunday) to 6. 1970-01-01 was a Thursday. */\nexport const weekdayOf = (day: number): number => mod(day + 4, 7);\n\n/** The year, month (0 to 11) and day of the month of a day number. */\nexport const partsOf = (day: number) => {\n const date = new Date(day * DAY_MS);\n return { year: date.getUTCFullYear(), month: date.getUTCMonth(), date: date.getUTCDate() };\n};\n\n/** The row of a day in a week that starts on `weekStart`: 0 for the first day of the week. */\nexport const rowOf = (day: number, weekStart: WeekStart): number =>\n mod(weekdayOf(day) - weekStart, 7);\n\nexport type GridDay = { day: number; row: number; column: number };\n\nexport type WeekGrid = {\n /** The first day of the first column: on or before `from`. */\n start: number;\n /** The number of columns. 0 when the range is empty. */\n weeks: number;\n /** Every day of the range, in order. The slots before `from` and after `to` have no day. */\n days: GridDay[];\n};\n\n/** The days from `from` to `to` (inclusive) laid out as weeks in columns and weekdays in rows. */\nexport const weekGrid = (from: number, to: number, weekStart: WeekStart): WeekGrid => {\n if (to < from) return { start: from, weeks: 0, days: [] };\n const start = from - rowOf(from, weekStart);\n const days = Array.from({ length: to - from + 1 }, (_, i): GridDay => {\n const day = from + i;\n return { day, row: rowOf(day, weekStart), column: Math.floor((day - start) / 7) };\n });\n return { start, weeks: Math.floor((to - start) / 7) + 1, days };\n};\n\nexport type YearSpan = { year: number; from: number; to: number };\n\n/** `from` to `to` cut at the turn of each year: one block of the `calendar` layout per span. */\nexport const yearSpans = (from: number, to: number): YearSpan[] => {\n const spans: YearSpan[] = [];\n if (to < from) return spans;\n for (let year = partsOf(from).year; year <= partsOf(to).year; year++) {\n spans.push({\n year,\n from: Math.max(from, dayOf(year, 0, 1)),\n to: Math.min(to, dayOf(year, 11, 31)),\n });\n }\n return spans;\n};\n\nexport type MonthMark = { column: number; month: number };\n\n// Room a month name needs, in columns: two months closer than this would\n// print over each other.\nconst MIN_MARK_GAP = 3;\n\n/**\n * Where the month names go: above the first full week of each month, that is\n * the first column whose first day falls in the first seven days of the\n * month. A range that starts in the middle of a month also names that month\n * above its first column, unless the next name is too close to leave room.\n */\nexport const monthMarks = (grid: WeekGrid): MonthMark[] => {\n if (grid.weeks === 0) return [];\n const marks: MonthMark[] = [];\n for (let column = 0; column < grid.weeks; column++) {\n const { month, date } = partsOf(grid.start + column * 7);\n if (date <= 7) marks.push({ column, month });\n }\n const first = grid.days[0];\n const leading = partsOf(first.day).month;\n const next = marks[0];\n if (!next || (next.month !== leading && next.column >= MIN_MARK_GAP)) {\n marks.unshift({ column: 0, month: leading });\n }\n return marks;\n};\n\n/** The 0-based step a value takes on the scale: 0 is empty, 1 to `steps` split `(0, max]` evenly. */\nexport const stepOf = (value: number, max: number, steps = 4): number =>\n !(value > 0) || !(max > 0) ? 0 : Math.min(steps, Math.max(1, Math.ceil((value / max) * steps)));\n\n/** How big the cells are: a short range gets larger ones, a long one fits more weeks on screen. */\nexport const cellMetrics = (weeks: number) =>\n weeks <= 26 ? { size: 18, gap: 4, radius: 4 } : { size: 12, gap: 3, radius: 3 };\n\nexport type DayValues = {\n /** The summed value of each valid day. */\n values: Map<number, number>;\n first: number;\n last: number;\n};\n\n/** The valid days of `data` by day number, a day listed twice summed. `undefined` when none is valid. */\nexport const collectDays = (\n data: readonly { day: string; value: number }[]\n): DayValues | undefined => {\n const values = new Map<number, number>();\n for (const { day, value } of data) {\n const n = parseDay(day);\n if (n === undefined || !Number.isFinite(value)) continue;\n values.set(n, (values.get(n) ?? 0) + value);\n }\n if (values.size === 0) return undefined;\n const days = [...values.keys()];\n return { values, first: Math.min(...days), last: Math.max(...days) };\n};\n","import { TEXT_SIZE } from './core/axes';\nimport { CALENDAR_DARK_CLASS, CALENDAR_STEPS, MUTED, TEXT } from './core/colors';\nimport { ChartEmpty } from './core/frame';\nimport { HeatGrid, type HeatCell } from './core/HeatGrid';\nimport {\n cellMetrics,\n collectDays,\n DAY_MS,\n dayOf,\n monthMarks,\n parseDay,\n partsOf,\n stepOf,\n weekGrid,\n yearSpans,\n type WeekStart,\n} from './calendarGrid';\nimport type { ChartEmptyProps, ChartProps } from './types';\n\nexport type CalendarDay = {\n /** The date, `YYYY-MM-DD`. A day listed twice adds up; anything else is ignored. */\n day: string;\n value: number;\n};\n\nexport type CalendarChartProps = Omit<ChartProps, 'children' | 'height'> &\n ChartEmptyProps & {\n data: CalendarDay[];\n /**\n * First day shown, `YYYY-MM-DD`. Default: the first day of the earliest\n * year of `data` in the `calendar` layout, the earliest day in `range`.\n */\n from?: string;\n /** Last day shown, `YYYY-MM-DD`. Default: the end of the latest year, or the latest day in `range`. */\n to?: string;\n /**\n * `calendar` draws one block per year, with the months named above their\n * first full week. `range` draws one continuous grid from `from` to `to`,\n * with larger cells when the range is short. Default `calendar`.\n */\n layout?: 'calendar' | 'range';\n /** The weekday a week starts on: 1 for Monday (default), 0 for Sunday. */\n weekStart?: WeekStart;\n /** Format the value in the tooltip and in the name of each day. */\n formatValue?: (value: number) => string;\n /** Text at the low end of the scale under the grid. Default `Less`. */\n lessLabel?: string;\n /** Text at the high end of the scale under the grid. Default `More`. */\n moreLabel?: string;\n /** The language of the month, weekday and date names (BCP 47). Default `en-US`. */\n locale?: string;\n };\n\n// Height of the empty state, which has no grid to take one from.\nconst EMPTY_HEIGHT = 120;\n\n// Tuesday, Thursday and the weekend carry no name: a name every second row\n// leaves each one room.\nconst NAMED_WEEKDAYS = [1, 3, 5];\n\ntype Block = { year: number | undefined; from: number; to: number };\n\nexport const CalendarChart = ({\n data,\n from,\n to,\n layout = 'calendar',\n weekStart = 1,\n formatValue = String,\n locale = 'en-US',\n lessLabel = 'Less',\n moreLabel = 'More',\n emptyMessage = 'No data',\n ariaLabel,\n width,\n}: CalendarChartProps) => {\n const name = ariaLabel ?? 'Calendar chart';\n const days = collectDays(data);\n if (!days) {\n return (\n <ChartEmpty message={emptyMessage} ariaLabel={name} height={EMPTY_HEIGHT} width={width} />\n );\n }\n const given = {\n from: from === undefined ? undefined : parseDay(from),\n to: to === undefined ? undefined : parseDay(to),\n };\n const calendar = layout === 'calendar';\n const start = given.from ?? (calendar ? dayOf(partsOf(days.first).year, 0, 1) : days.first);\n const end = given.to ?? (calendar ? dayOf(partsOf(days.last).year, 11, 31) : days.last);\n const blocks: Block[] = calendar\n ? yearSpans(start, end)\n : end >= start\n ? [{ year: undefined, from: start, to: end }]\n : [];\n if (blocks.length === 0) {\n return (\n <ChartEmpty message={emptyMessage} ariaLabel={name} height={EMPTY_HEIGHT} width={width} />\n );\n }\n\n const month = new Intl.DateTimeFormat(locale, { month: 'short', timeZone: 'UTC' });\n const date = new Intl.DateTimeFormat(locale, { dateStyle: 'full', timeZone: 'UTC' });\n const weekday = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });\n const monthName = (m: number) => month.format(Date.UTC(2024, m, 1));\n // 2024-01-07 was a Sunday.\n const weekdayName = (d: number) => weekday.format(Date.UTC(2024, 0, 7 + d));\n const rows = Array.from({ length: 7 }, (_, row) => {\n const d = (row + weekStart) % 7;\n return NAMED_WEEKDAYS.includes(d) ? weekdayName(d) : '';\n });\n\n // One scale for every block, so two years compare.\n const max = Math.max(0, ...days.values.values());\n const color = (value: number) => CALENDAR_STEPS[stepOf(value, max)];\n\n const grids = blocks.map((block) => {\n const grid = weekGrid(block.from, block.to, weekStart);\n const metrics = cellMetrics(grid.weeks);\n const columns = Array.from({ length: grid.weeks }, () => '');\n for (const mark of monthMarks(grid)) columns[mark.column] = monthName(mark.month);\n const cells = grid.days.map((d): HeatCell => {\n const value = days.values.get(d.day) ?? 0;\n return {\n row: d.row,\n column: d.column,\n color: color(value),\n title: date.format(d.day * DAY_MS),\n label: '',\n value: formatValue(value),\n };\n });\n const label = block.year !== undefined && blocks.length > 1 ? `${name}, ${block.year}` : name;\n return { block, grid, metrics, columns, cells, label };\n });\n\n const metrics = grids[0].metrics;\n return (\n <div\n className={CALENDAR_DARK_CLASS}\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '0.75rem',\n // As wide as the grid, so the scale lines up with its right edge.\n width: width ?? 'fit-content',\n maxWidth: '100%',\n minWidth: 0,\n fontSize: TEXT_SIZE,\n color: MUTED,\n }}\n >\n {grids.map(({ block, metrics: m, columns, cells, label }) => (\n <div key={block.year ?? 'range'} data-calendar-block=\"\" style={{ minWidth: 0 }}>\n {blocks.length > 1 && (\n <p style={{ margin: '0 0 0.25rem', fontWeight: 600, color: TEXT }}>{block.year}</p>\n )}\n <HeatGrid\n rows={rows}\n columns={columns}\n cells={cells}\n cellHeight={m.size}\n columnWidth={m.size}\n gap={m.gap}\n cellRadius={m.radius}\n labelsAbove\n thinLabels={false}\n ariaLabel={label}\n />\n </div>\n ))}\n <div\n data-calendar-legend=\"\"\n aria-hidden\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n // The grid sits in a box padded by 3px, room for its focus rings.\n paddingRight: 3,\n gap: metrics.gap,\n }}\n >\n <span style={{ marginRight: 4 }}>{lessLabel}</span>\n {CALENDAR_STEPS.map((step, i) => (\n <span\n key={i}\n data-calendar-step=\"\"\n style={{\n width: metrics.size,\n height: metrics.size,\n borderRadius: metrics.radius,\n background: step,\n }}\n />\n ))}\n <span style={{ marginLeft: 4 }}>{moreLabel}</span>\n </div>\n </div>\n );\n};\n"]}
1
+ {"version":3,"sources":["../src/calendarGrid.ts","../src/CalendarChart.tsx"],"names":["metrics"],"mappings":";;;;;;;;;;AAIO,IAAM,MAAA,GAAS,KAAA;AAKtB,IAAM,MAAM,CAAC,CAAA,EAAW,EAAA,KAAA,CAAiB,CAAA,GAAI,KAAM,EAAA,IAAM,EAAA;AAGlD,IAAM,QAAA,GAAW,CAAC,GAAA,KAAoC;AAC3D,EAAA,MAAM,KAAA,GAAQ,2BAAA,CAA4B,IAAA,CAAK,GAAG,CAAA;AAClD,EAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AACnB,EAAA,MAAM,CAAC,MAAM,KAAA,EAAO,IAAI,IAAI,CAAC,MAAA,CAAO,MAAM,CAAC,CAAC,GAAG,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,EAAG,OAAO,KAAA,CAAM,CAAC,CAAC,CAAC,CAAA;AACjF,EAAA,MAAM,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,KAAA,GAAQ,GAAG,IAAI,CAAA;AAC3C,EAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,IAAI,CAAA;AAE3B,EAAA,OAAO,KAAA,CAAM,cAAA,EAAe,KAAM,IAAA,IAChC,MAAM,WAAA,EAAY,KAAM,KAAA,GAAQ,CAAA,IAChC,KAAA,CAAM,UAAA,EAAW,KAAM,IAAA,GACrB,OAAO,MAAA,GACP,MAAA;AACN,CAAA;AAGO,IAAM,KAAA,GAAQ,CAAC,IAAA,EAAc,KAAA,EAAe,IAAA,KACjD,KAAK,GAAA,CAAI,IAAA,EAAM,KAAA,EAAO,IAAI,CAAA,GAAI,MAAA;AAMzB,IAAM,YAAY,CAAC,GAAA,KAAwB,GAAA,CAAI,GAAA,GAAM,GAAG,CAAC,CAAA;AAGzD,IAAM,OAAA,GAAU,CAAC,GAAA,KAAgB;AACtC,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,GAAA,GAAM,MAAM,CAAA;AAClC,EAAA,OAAO,EAAE,IAAA,EAAM,IAAA,CAAK,cAAA,EAAe,EAAG,KAAA,EAAO,IAAA,CAAK,WAAA,EAAY,EAAG,IAAA,EAAM,IAAA,CAAK,UAAA,EAAW,EAAE;AAC3F,CAAA;AAGO,IAAM,KAAA,GAAQ,CAAC,GAAA,EAAa,SAAA,KACjC,IAAI,SAAA,CAAU,GAAG,CAAA,GAAI,SAAA,EAAW,CAAC,CAAA;AAc5B,IAAM,QAAA,GAAW,CAAC,IAAA,EAAc,EAAA,EAAY,SAAA,KAAmC;AACpF,EAAA,IAAI,EAAA,GAAK,IAAA,EAAM,OAAO,EAAE,KAAA,EAAO,MAAM,KAAA,EAAO,CAAA,EAAG,IAAA,EAAM,EAAC,EAAE;AACxD,EAAA,MAAM,KAAA,GAAQ,IAAA,GAAO,KAAA,CAAM,IAAA,EAAM,SAAS,CAAA;AAC1C,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,EAAA,GAAK,IAAA,GAAO,CAAA,EAAE,EAAG,CAAC,CAAA,EAAG,CAAA,KAAe;AACpE,IAAA,MAAM,MAAM,IAAA,GAAO,CAAA;AACnB,IAAA,OAAO,EAAE,GAAA,EAAK,GAAA,EAAK,KAAA,CAAM,GAAA,EAAK,SAAS,CAAA,EAAG,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAA,CAAO,GAAA,GAAM,KAAA,IAAS,CAAC,CAAA,EAAE;AAAA,EAClF,CAAC,CAAA;AACD,EAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,IAAA,CAAK,KAAA,CAAA,CAAO,KAAK,KAAA,IAAS,CAAC,CAAA,GAAI,CAAA,EAAG,IAAA,EAAK;AAChE,CAAA;AAKO,IAAM,SAAA,GAAY,CAAC,IAAA,EAAc,EAAA,KAA2B;AACjE,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,IAAI,EAAA,GAAK,MAAM,OAAO,KAAA;AACtB,EAAA,KAAA,IAAS,IAAA,GAAO,OAAA,CAAQ,IAAI,CAAA,CAAE,IAAA,EAAM,QAAQ,OAAA,CAAQ,EAAE,CAAA,CAAE,IAAA,EAAM,IAAA,EAAA,EAAQ;AACpE,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,IAAA;AAAA,MACA,IAAA,EAAM,KAAK,GAAA,CAAI,IAAA,EAAM,MAAM,IAAA,EAAM,CAAA,EAAG,CAAC,CAAC,CAAA;AAAA,MACtC,EAAA,EAAI,KAAK,GAAA,CAAI,EAAA,EAAI,MAAM,IAAA,EAAM,EAAA,EAAI,EAAE,CAAC;AAAA,KACrC,CAAA;AAAA,EACH;AACA,EAAA,OAAO,KAAA;AACT,CAAA;AAMA,IAAM,YAAA,GAAe,CAAA;AAQd,IAAM,UAAA,GAAa,CAAC,IAAA,KAAgC;AACzD,EAAA,IAAI,IAAA,CAAK,KAAA,KAAU,CAAA,EAAG,OAAO,EAAC;AAC9B,EAAA,MAAM,QAAqB,EAAC;AAC5B,EAAA,KAAA,IAAS,MAAA,GAAS,CAAA,EAAG,MAAA,GAAS,IAAA,CAAK,OAAO,MAAA,EAAA,EAAU;AAClD,IAAA,MAAM,EAAE,OAAO,IAAA,EAAK,GAAI,QAAQ,IAAA,CAAK,KAAA,GAAQ,SAAS,CAAC,CAAA;AACvD,IAAA,IAAI,QAAQ,CAAA,EAAG,KAAA,CAAM,KAAK,EAAE,MAAA,EAAQ,OAAO,CAAA;AAAA,EAC7C;AACA,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA;AACzB,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA,CAAE,KAAA;AACnC,EAAA,MAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AACpB,EAAA,IAAI,CAAC,IAAA,IAAS,IAAA,CAAK,UAAU,OAAA,IAAW,IAAA,CAAK,UAAU,YAAA,EAAe;AACpE,IAAA,KAAA,CAAM,QAAQ,EAAE,MAAA,EAAQ,CAAA,EAAG,KAAA,EAAO,SAAS,CAAA;AAAA,EAC7C;AACA,EAAA,OAAO,KAAA;AACT,CAAA;AAGO,IAAM,MAAA,GAAS,CAAC,KAAA,EAAe,GAAA,EAAa,KAAA,GAAQ,MACzD,EAAE,KAAA,GAAQ,CAAA,CAAA,IAAM,EAAE,GAAA,GAAM,CAAA,CAAA,GAAK,IAAI,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,IAAA,CAAM,KAAA,GAAQ,GAAA,GAAO,KAAK,CAAC,CAAC,CAAA;AAGzF,IAAM,cAAc,CAAC,KAAA,KAC1B,SAAS,EAAA,GAAK,EAAE,MAAM,EAAA,EAAI,GAAA,EAAK,GAAG,MAAA,EAAQ,CAAA,KAAM,EAAE,IAAA,EAAM,IAAI,GAAA,EAAK,CAAA,EAAG,QAAQ,CAAA,EAAE;AAUzE,IAAM,WAAA,GAAc,CACzB,IAAA,KAC0B;AAC1B,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAoB;AACvC,EAAA,KAAA,MAAW,EAAE,GAAA,EAAK,KAAA,EAAM,IAAK,IAAA,EAAM;AACjC,IAAA,MAAM,CAAA,GAAI,SAAS,GAAG,CAAA;AACtB,IAAA,IAAI,MAAM,MAAA,IAAa,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,EAAG;AAChD,IAAA,MAAA,CAAO,IAAI,CAAA,EAAA,CAAI,MAAA,CAAO,IAAI,CAAC,CAAA,IAAK,KAAK,KAAK,CAAA;AAAA,EAC5C;AACA,EAAA,IAAI,MAAA,CAAO,IAAA,KAAS,CAAA,EAAG,OAAO,MAAA;AAC9B,EAAA,MAAM,IAAA,GAAO,CAAC,GAAG,MAAA,CAAO,MAAM,CAAA;AAC9B,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,GAAG,IAAI,CAAA,EAAG,IAAA,EAAM,IAAA,CAAK,GAAA,CAAI,GAAG,IAAI,CAAA,EAAE;AACrE,CAAA;ACtFA,IAAM,YAAA,GAAe,GAAA;AAIrB,IAAM,cAAA,GAAiB,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAIxB,IAAM,gBAAgB,CAAC;AAAA,EAC5B,IAAA;AAAA,EACA,IAAA;AAAA,EACA,EAAA;AAAA,EACA,MAAA,GAAS,UAAA;AAAA,EACT,SAAA,GAAY,CAAA;AAAA,EACZ,WAAA,GAAc,MAAA;AAAA,EACd,MAAA,GAAS,OAAA;AAAA,EACT,SAAA,GAAY,MAAA;AAAA,EACZ,SAAA,GAAY,MAAA;AAAA,EACZ,YAAA,GAAe,SAAA;AAAA,EACf,SAAA;AAAA,EACA;AACF,CAAA,KAA0B;AACxB,EAAA,MAAM,OAAO,SAAA,IAAa,gBAAA;AAC1B,EAAA,MAAM,IAAA,GAAO,YAAY,IAAI,CAAA;AAC7B,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,uBACE,GAAA,CAAC,cAAW,OAAA,EAAS,YAAA,EAAc,WAAW,IAAA,EAAM,MAAA,EAAQ,cAAc,KAAA,EAAc,CAAA;AAAA,EAE5F;AACA,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,IAAA,EAAM,IAAA,KAAS,MAAA,GAAY,MAAA,GAAY,SAAS,IAAI,CAAA;AAAA,IACpD,EAAA,EAAI,EAAA,KAAO,MAAA,GAAY,MAAA,GAAY,SAAS,EAAE;AAAA,GAChD;AACA,EAAA,MAAM,WAAW,MAAA,KAAW,UAAA;AAC5B,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,KAAS,QAAA,GAAW,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA,CAAE,IAAA,EAAM,CAAA,EAAG,CAAC,IAAI,IAAA,CAAK,KAAA,CAAA;AACrF,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,EAAA,KAAO,QAAA,GAAW,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA,CAAE,IAAA,EAAM,EAAA,EAAI,EAAE,IAAI,IAAA,CAAK,IAAA,CAAA;AAClF,EAAA,MAAM,SAAkB,QAAA,GACpB,SAAA,CAAU,OAAO,GAAG,CAAA,GACpB,OAAO,KAAA,GACL,CAAC,EAAE,IAAA,EAAM,QAAW,IAAA,EAAM,KAAA,EAAO,IAAI,GAAA,EAAK,IAC1C,EAAC;AACP,EAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,IAAA,uBACE,GAAA,CAAC,cAAW,OAAA,EAAS,YAAA,EAAc,WAAW,IAAA,EAAM,MAAA,EAAQ,cAAc,KAAA,EAAc,CAAA;AAAA,EAE5F;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ,EAAE,KAAA,EAAO,OAAA,EAAS,QAAA,EAAU,KAAA,EAAO,CAAA;AACjF,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ,EAAE,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAU,KAAA,EAAO,CAAA;AACnF,EAAA,MAAM,OAAA,GAAU,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ,EAAE,OAAA,EAAS,OAAA,EAAS,QAAA,EAAU,KAAA,EAAO,CAAA;AACrF,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAc,KAAA,CAAM,MAAA,CAAO,KAAK,GAAA,CAAI,IAAA,EAAM,CAAA,EAAG,CAAC,CAAC,CAAA;AAElE,EAAA,MAAM,WAAA,GAAc,CAAC,CAAA,KAAc,OAAA,CAAQ,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,EAAG,CAAA,GAAI,CAAC,CAAC,CAAA;AAC1E,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,EAAE,QAAQ,CAAA,EAAE,EAAG,CAAC,CAAA,EAAG,GAAA,KAAQ;AACjD,IAAA,MAAM,CAAA,GAAA,CAAK,MAAM,SAAA,IAAa,CAAA;AAC9B,IAAA,OAAO,eAAe,QAAA,CAAS,CAAC,CAAA,GAAI,WAAA,CAAY,CAAC,CAAA,GAAI,EAAA;AAAA,EACvD,CAAC,CAAA;AAGD,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,CAAI,CAAA,EAAG,GAAG,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA;AAC/C,EAAA,MAAM,QAAQ,CAAC,KAAA,KAAkB,eAAe,MAAA,CAAO,KAAA,EAAO,GAAG,CAAC,CAAA;AAElE,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,KAAU;AAClC,IAAA,MAAM,OAAO,QAAA,CAAS,KAAA,CAAM,IAAA,EAAM,KAAA,CAAM,IAAI,SAAS,CAAA;AACrD,IAAA,MAAMA,QAAAA,GAAU,WAAA,CAAY,IAAA,CAAK,KAAK,CAAA;AACtC,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,EAAE,QAAQ,IAAA,CAAK,KAAA,EAAM,EAAG,MAAM,EAAE,CAAA;AAC3D,IAAA,KAAA,MAAW,IAAA,IAAQ,UAAA,CAAW,IAAI,CAAA,EAAG,OAAA,CAAQ,KAAK,MAAM,CAAA,GAAI,SAAA,CAAU,IAAA,CAAK,KAAK,CAAA;AAChF,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAgB;AAC3C,MAAA,MAAM,QAAQ,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA,IAAK,CAAA;AACxC,MAAA,OAAO;AAAA,QACL,KAAK,CAAA,CAAE,GAAA;AAAA,QACP,QAAQ,CAAA,CAAE,MAAA;AAAA,QACV,KAAA,EAAO,MAAM,KAAK,CAAA;AAAA,QAClB,KAAA,EAAO,IAAA,CAAK,MAAA,CAAO,CAAA,CAAE,MAAM,MAAM,CAAA;AAAA,QACjC,KAAA,EAAO,EAAA;AAAA,QACP,KAAA,EAAO,YAAY,KAAK;AAAA,OAC1B;AAAA,IACF,CAAC,CAAA;AACD,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,KAAS,MAAA,IAAa,MAAA,CAAO,MAAA,GAAS,CAAA,GAAI,CAAA,EAAG,IAAI,CAAA,EAAA,EAAK,KAAA,CAAM,IAAI,CAAA,CAAA,GAAK,IAAA;AACzF,IAAA,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,SAAAA,QAAAA,EAAS,OAAA,EAAS,OAAO,KAAA,EAAM;AAAA,EACvD,CAAC,CAAA;AAED,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,CAAC,CAAA,CAAE,OAAA;AACzB,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,mBAAA;AAAA,MACX,KAAA,EAAO;AAAA;AAAA,QAEL,SAAA,EAAW,KAAA;AAAA,QACX,OAAA,EAAS,MAAA;AAAA,QACT,aAAA,EAAe,QAAA;AAAA,QACf,GAAA,EAAK,SAAA;AAAA;AAAA,QAEL,OAAO,KAAA,IAAS,aAAA;AAAA,QAChB,QAAA,EAAU,MAAA;AAAA,QACV,QAAA,EAAU,CAAA;AAAA,QACV,QAAA,EAAU,SAAA;AAAA,QACV,KAAA,EAAO;AAAA,OACT;AAAA,MAEC,QAAA,EAAA;AAAA,QAAA,KAAA,CAAM,IAAI,CAAC,EAAE,OAAO,OAAA,EAAS,CAAA,EAAG,SAAS,KAAA,EAAO,KAAA,EAAM,qBACrD,IAAA,CAAC,SAAgC,qBAAA,EAAoB,EAAA,EAAG,OAAO,EAAE,QAAA,EAAU,GAAE,EAC1E,QAAA,EAAA;AAAA,UAAA,MAAA,CAAO,MAAA,GAAS,CAAA,oBACf,GAAA,CAAC,GAAA,EAAA,EAAE,OAAO,EAAE,MAAA,EAAQ,aAAA,EAAe,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,IAAA,EAAK,EAAI,gBAAM,IAAA,EAAK,CAAA;AAAA,0BAEjF,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA;AAAA,cACA,OAAA;AAAA,cACA,KAAA;AAAA,cACA,YAAY,CAAA,CAAE,IAAA;AAAA,cACd,aAAa,CAAA,CAAE,IAAA;AAAA,cACf,KAAK,CAAA,CAAE,GAAA;AAAA,cACP,YAAY,CAAA,CAAE,MAAA;AAAA,cACd,WAAA,EAAW,IAAA;AAAA,cACX,UAAA,EAAY,KAAA;AAAA,cACZ,SAAA,EAAW;AAAA;AAAA;AACb,SAAA,EAAA,EAfQ,KAAA,CAAM,IAAA,IAAQ,OAgBxB,CACD,CAAA;AAAA,wBACD,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,sBAAA,EAAqB,EAAA;AAAA,YACrB,aAAA,EAAW,IAAA;AAAA,YACX,KAAA,EAAO;AAAA,cACL,OAAA,EAAS,MAAA;AAAA,cACT,UAAA,EAAY,QAAA;AAAA,cACZ,cAAA,EAAgB,UAAA;AAAA;AAAA,cAEhB,YAAA,EAAc,CAAA;AAAA,cACd,KAAK,OAAA,CAAQ;AAAA,aACf;AAAA,YAEA,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,UAAK,KAAA,EAAO,EAAE,WAAA,EAAa,CAAA,IAAM,QAAA,EAAA,SAAA,EAAU,CAAA;AAAA,cAC3C,cAAA,CAAe,GAAA,CAAI,CAAC,IAAA,EAAM,CAAA,qBACzB,GAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBAEC,oBAAA,EAAmB,EAAA;AAAA,kBACnB,KAAA,EAAO;AAAA,oBACL,OAAO,OAAA,CAAQ,IAAA;AAAA,oBACf,QAAQ,OAAA,CAAQ,IAAA;AAAA,oBAChB,cAAc,OAAA,CAAQ,MAAA;AAAA,oBACtB,UAAA,EAAY;AAAA;AACd,iBAAA;AAAA,gBAPK;AAAA,eASR,CAAA;AAAA,kCACA,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,UAAA,EAAY,CAAA,IAAM,QAAA,EAAA,SAAA,EAAU;AAAA;AAAA;AAAA;AAC7C;AAAA;AAAA,GACF;AAEJ","file":"CalendarChart.js","sourcesContent":["// The date maths of `CalendarChart`. A day is a whole number: the days since\n// 1970-01-01 in UTC, so nothing here reads the time zone of the machine or the\n// date of today.\n\nexport const DAY_MS = 86_400_000;\n\n/** The weekday a week starts on: 0 is Sunday, 1 is Monday. */\nexport type WeekStart = 0 | 1;\n\nconst mod = (n: number, by: number) => ((n % by) + by) % by;\n\n/** The day number of an ISO date (`2026-03-14`), or `undefined` when it is not a real date. */\nexport const parseDay = (iso: string): number | undefined => {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(iso);\n if (!match) return undefined;\n const [year, month, date] = [Number(match[1]), Number(match[2]), Number(match[3])];\n const time = Date.UTC(year, month - 1, date);\n const check = new Date(time);\n // `Date.UTC` rolls 02-30 over to 03-02: a date that moved is not a date.\n return check.getUTCFullYear() === year &&\n check.getUTCMonth() === month - 1 &&\n check.getUTCDate() === date\n ? time / DAY_MS\n : undefined;\n};\n\n/** The day number of a date: `month` is 0 to 11. */\nexport const dayOf = (year: number, month: number, date: number): number =>\n Date.UTC(year, month, date) / DAY_MS;\n\n/** The ISO date of a day number. */\nexport const formatDay = (day: number): string => new Date(day * DAY_MS).toISOString().slice(0, 10);\n\n/** The weekday of a day number, 0 (Sunday) to 6. 1970-01-01 was a Thursday. */\nexport const weekdayOf = (day: number): number => mod(day + 4, 7);\n\n/** The year, month (0 to 11) and day of the month of a day number. */\nexport const partsOf = (day: number) => {\n const date = new Date(day * DAY_MS);\n return { year: date.getUTCFullYear(), month: date.getUTCMonth(), date: date.getUTCDate() };\n};\n\n/** The row of a day in a week that starts on `weekStart`: 0 for the first day of the week. */\nexport const rowOf = (day: number, weekStart: WeekStart): number =>\n mod(weekdayOf(day) - weekStart, 7);\n\nexport type GridDay = { day: number; row: number; column: number };\n\nexport type WeekGrid = {\n /** The first day of the first column: on or before `from`. */\n start: number;\n /** The number of columns. 0 when the range is empty. */\n weeks: number;\n /** Every day of the range, in order. The slots before `from` and after `to` have no day. */\n days: GridDay[];\n};\n\n/** The days from `from` to `to` (inclusive) laid out as weeks in columns and weekdays in rows. */\nexport const weekGrid = (from: number, to: number, weekStart: WeekStart): WeekGrid => {\n if (to < from) return { start: from, weeks: 0, days: [] };\n const start = from - rowOf(from, weekStart);\n const days = Array.from({ length: to - from + 1 }, (_, i): GridDay => {\n const day = from + i;\n return { day, row: rowOf(day, weekStart), column: Math.floor((day - start) / 7) };\n });\n return { start, weeks: Math.floor((to - start) / 7) + 1, days };\n};\n\nexport type YearSpan = { year: number; from: number; to: number };\n\n/** `from` to `to` cut at the turn of each year: one block of the `calendar` layout per span. */\nexport const yearSpans = (from: number, to: number): YearSpan[] => {\n const spans: YearSpan[] = [];\n if (to < from) return spans;\n for (let year = partsOf(from).year; year <= partsOf(to).year; year++) {\n spans.push({\n year,\n from: Math.max(from, dayOf(year, 0, 1)),\n to: Math.min(to, dayOf(year, 11, 31)),\n });\n }\n return spans;\n};\n\nexport type MonthMark = { column: number; month: number };\n\n// Room a month name needs, in columns: two months closer than this would\n// print over each other.\nconst MIN_MARK_GAP = 3;\n\n/**\n * Where the month names go: above the first full week of each month, that is\n * the first column whose first day falls in the first seven days of the\n * month. A range that starts in the middle of a month also names that month\n * above its first column, unless the next name is too close to leave room.\n */\nexport const monthMarks = (grid: WeekGrid): MonthMark[] => {\n if (grid.weeks === 0) return [];\n const marks: MonthMark[] = [];\n for (let column = 0; column < grid.weeks; column++) {\n const { month, date } = partsOf(grid.start + column * 7);\n if (date <= 7) marks.push({ column, month });\n }\n const first = grid.days[0];\n const leading = partsOf(first.day).month;\n const next = marks[0];\n if (!next || (next.month !== leading && next.column >= MIN_MARK_GAP)) {\n marks.unshift({ column: 0, month: leading });\n }\n return marks;\n};\n\n/** The 0-based step a value takes on the scale: 0 is empty, 1 to `steps` split `(0, max]` evenly. */\nexport const stepOf = (value: number, max: number, steps = 4): number =>\n !(value > 0) || !(max > 0) ? 0 : Math.min(steps, Math.max(1, Math.ceil((value / max) * steps)));\n\n/** How big the cells are: a short range gets larger ones, a long one fits more weeks on screen. */\nexport const cellMetrics = (weeks: number) =>\n weeks <= 26 ? { size: 18, gap: 4, radius: 4 } : { size: 12, gap: 3, radius: 3 };\n\nexport type DayValues = {\n /** The summed value of each valid day. */\n values: Map<number, number>;\n first: number;\n last: number;\n};\n\n/** The valid days of `data` by day number, a day listed twice summed. `undefined` when none is valid. */\nexport const collectDays = (\n data: readonly { day: string; value: number }[]\n): DayValues | undefined => {\n const values = new Map<number, number>();\n for (const { day, value } of data) {\n const n = parseDay(day);\n if (n === undefined || !Number.isFinite(value)) continue;\n values.set(n, (values.get(n) ?? 0) + value);\n }\n if (values.size === 0) return undefined;\n const days = [...values.keys()];\n return { values, first: Math.min(...days), last: Math.max(...days) };\n};\n","import { TEXT_SIZE } from './core/axes';\nimport { CALENDAR_DARK_CLASS, CALENDAR_STEPS, MUTED, TEXT } from './core/colors';\nimport { ChartEmpty } from './core/frame';\nimport { HeatGrid, type HeatCell } from './core/HeatGrid';\nimport {\n cellMetrics,\n collectDays,\n DAY_MS,\n dayOf,\n monthMarks,\n parseDay,\n partsOf,\n stepOf,\n weekGrid,\n yearSpans,\n type WeekStart,\n} from './calendarGrid';\nimport type { ChartEmptyProps, ChartProps } from './types';\n\nexport type CalendarDay = {\n /** The date, `YYYY-MM-DD`. A day listed twice adds up; anything else is ignored. */\n day: string;\n value: number;\n};\n\nexport type CalendarChartProps = Omit<ChartProps, 'children' | 'height'> &\n ChartEmptyProps & {\n data: CalendarDay[];\n /**\n * First day shown, `YYYY-MM-DD`. Default: the first day of the earliest\n * year of `data` in the `calendar` layout, the earliest day in `range`.\n */\n from?: string;\n /** Last day shown, `YYYY-MM-DD`. Default: the end of the latest year, or the latest day in `range`. */\n to?: string;\n /**\n * `calendar` draws one block per year, with the months named above their\n * first full week. `range` draws one continuous grid from `from` to `to`,\n * with larger cells when the range is short. Default `calendar`.\n */\n layout?: 'calendar' | 'range';\n /** The weekday a week starts on: 1 for Monday (default), 0 for Sunday. */\n weekStart?: WeekStart;\n /** Format the value in the tooltip and in the name of each day. */\n formatValue?: (value: number) => string;\n /** Text at the low end of the scale under the grid. Default `Less`. */\n lessLabel?: string;\n /** Text at the high end of the scale under the grid. Default `More`. */\n moreLabel?: string;\n /** The language of the month, weekday and date names (BCP 47). Default `en-US`. */\n locale?: string;\n };\n\n// Height of the empty state, which has no grid to take one from.\nconst EMPTY_HEIGHT = 120;\n\n// Tuesday, Thursday and the weekend carry no name: a name every second row\n// leaves each one room.\nconst NAMED_WEEKDAYS = [1, 3, 5];\n\ntype Block = { year: number | undefined; from: number; to: number };\n\nexport const CalendarChart = ({\n data,\n from,\n to,\n layout = 'calendar',\n weekStart = 1,\n formatValue = String,\n locale = 'en-US',\n lessLabel = 'Less',\n moreLabel = 'More',\n emptyMessage = 'No data',\n ariaLabel,\n width,\n}: CalendarChartProps) => {\n const name = ariaLabel ?? 'Calendar chart';\n const days = collectDays(data);\n if (!days) {\n return (\n <ChartEmpty message={emptyMessage} ariaLabel={name} height={EMPTY_HEIGHT} width={width} />\n );\n }\n const given = {\n from: from === undefined ? undefined : parseDay(from),\n to: to === undefined ? undefined : parseDay(to),\n };\n const calendar = layout === 'calendar';\n const start = given.from ?? (calendar ? dayOf(partsOf(days.first).year, 0, 1) : days.first);\n const end = given.to ?? (calendar ? dayOf(partsOf(days.last).year, 11, 31) : days.last);\n const blocks: Block[] = calendar\n ? yearSpans(start, end)\n : end >= start\n ? [{ year: undefined, from: start, to: end }]\n : [];\n if (blocks.length === 0) {\n return (\n <ChartEmpty message={emptyMessage} ariaLabel={name} height={EMPTY_HEIGHT} width={width} />\n );\n }\n\n const month = new Intl.DateTimeFormat(locale, { month: 'short', timeZone: 'UTC' });\n const date = new Intl.DateTimeFormat(locale, { dateStyle: 'full', timeZone: 'UTC' });\n const weekday = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });\n const monthName = (m: number) => month.format(Date.UTC(2024, m, 1));\n // 2024-01-07 was a Sunday.\n const weekdayName = (d: number) => weekday.format(Date.UTC(2024, 0, 7 + d));\n const rows = Array.from({ length: 7 }, (_, row) => {\n const d = (row + weekStart) % 7;\n return NAMED_WEEKDAYS.includes(d) ? weekdayName(d) : '';\n });\n\n // One scale for every block, so two years compare.\n const max = Math.max(0, ...days.values.values());\n const color = (value: number) => CALENDAR_STEPS[stepOf(value, max)];\n\n const grids = blocks.map((block) => {\n const grid = weekGrid(block.from, block.to, weekStart);\n const metrics = cellMetrics(grid.weeks);\n const columns = Array.from({ length: grid.weeks }, () => '');\n for (const mark of monthMarks(grid)) columns[mark.column] = monthName(mark.month);\n const cells = grid.days.map((d): HeatCell => {\n const value = days.values.get(d.day) ?? 0;\n return {\n row: d.row,\n column: d.column,\n color: color(value),\n title: date.format(d.day * DAY_MS),\n label: '',\n value: formatValue(value),\n };\n });\n const label = block.year !== undefined && blocks.length > 1 ? `${name}, ${block.year}` : name;\n return { block, grid, metrics, columns, cells, label };\n });\n\n const metrics = grids[0].metrics;\n return (\n <div\n className={CALENDAR_DARK_CLASS}\n style={{\n // Time keeps its order under `dir=\"rtl\"`, as a chart's axis does.\n direction: 'ltr',\n display: 'flex',\n flexDirection: 'column',\n gap: '0.75rem',\n // As wide as the grid, so the scale lines up with its right edge.\n width: width ?? 'fit-content',\n maxWidth: '100%',\n minWidth: 0,\n fontSize: TEXT_SIZE,\n color: MUTED,\n }}\n >\n {grids.map(({ block, metrics: m, columns, cells, label }) => (\n <div key={block.year ?? 'range'} data-calendar-block=\"\" style={{ minWidth: 0 }}>\n {blocks.length > 1 && (\n <p style={{ margin: '0 0 0.25rem', fontWeight: 600, color: TEXT }}>{block.year}</p>\n )}\n <HeatGrid\n rows={rows}\n columns={columns}\n cells={cells}\n cellHeight={m.size}\n columnWidth={m.size}\n gap={m.gap}\n cellRadius={m.radius}\n labelsAbove\n thinLabels={false}\n ariaLabel={label}\n />\n </div>\n ))}\n <div\n data-calendar-legend=\"\"\n aria-hidden\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n // The grid sits in a box padded by 3px, room for its focus rings.\n paddingRight: 3,\n gap: metrics.gap,\n }}\n >\n <span style={{ marginRight: 4 }}>{lessLabel}</span>\n {CALENDAR_STEPS.map((step, i) => (\n <span\n key={i}\n data-calendar-step=\"\"\n style={{\n width: metrics.size,\n height: metrics.size,\n borderRadius: metrics.radius,\n background: step,\n }}\n />\n ))}\n <span style={{ marginLeft: 4 }}>{moreLabel}</span>\n </div>\n </div>\n );\n};\n"]}
@@ -1,13 +1,13 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var chunkGZRMVE4X_cjs = require('./chunk-GZRMVE4X.cjs');
4
+ var chunkRCDTBZC2_cjs = require('./chunk-RCDTBZC2.cjs');
5
5
  var chunkX3YZP5WF_cjs = require('./chunk-X3YZP5WF.cjs');
6
6
  var chunkTYQWZR3S_cjs = require('./chunk-TYQWZR3S.cjs');
7
- require('./chunk-RLHGPHC7.cjs');
7
+ require('./chunk-G4WOPX4D.cjs');
8
8
  var chunkT3K6Q34U_cjs = require('./chunk-T3K6Q34U.cjs');
9
9
  var chunkK3R7JGOW_cjs = require('./chunk-K3R7JGOW.cjs');
10
- var chunk6NRRABPF_cjs = require('./chunk-6NRRABPF.cjs');
10
+ var chunkSR2ESX3R_cjs = require('./chunk-SR2ESX3R.cjs');
11
11
  var chunkLT3UC65O_cjs = require('./chunk-LT3UC65O.cjs');
12
12
  var react = require('react');
13
13
  var recharts = require('recharts');
@@ -26,7 +26,7 @@ var shares = (values) => {
26
26
  return whole;
27
27
  };
28
28
  var PieLegend = ({ payload, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
29
- chunkGZRMVE4X_cjs.ChartLegend,
29
+ chunkRCDTBZC2_cjs.ChartLegend,
30
30
  {
31
31
  ...props,
32
32
  payload: payload?.map((entry, i) => ({ ...entry, dataKey: chunkK3R7JGOW_cjs.seriesKey(i) }))
@@ -48,12 +48,12 @@ var DonutChart = ({
48
48
  height = 280,
49
49
  width
50
50
  }) => {
51
- const hidden = chunkGZRMVE4X_cjs.useHiddenSeries();
51
+ const hidden = chunkRCDTBZC2_cjs.useHiddenSeries();
52
52
  const [active, setActive] = react.useState();
53
53
  const pie = variant === "pie";
54
54
  const name = ariaLabel ?? `${pie ? "Pie" : "Donut"} chart: ${labels.join(", ")}`;
55
55
  if (series.length === 0) {
56
- return /* @__PURE__ */ jsxRuntime.jsx(chunk6NRRABPF_cjs.ChartEmpty, { message: emptyMessage, ariaLabel: name, height, width });
56
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkSR2ESX3R_cjs.ChartEmpty, { message: emptyMessage, ariaLabel: name, height, width });
57
57
  }
58
58
  const data = labels.map((label, i) => ({
59
59
  name: label,
@@ -62,13 +62,13 @@ var DonutChart = ({
62
62
  }));
63
63
  const total = data.reduce((sum, slice2) => sum + slice2.value, 0);
64
64
  const slice = active === void 0 ? void 0 : data[active];
65
- const legend = chunkGZRMVE4X_cjs.legendProps(legendPosition, hidden);
65
+ const legend = chunkRCDTBZC2_cjs.legendProps(legendPosition, hidden);
66
66
  const percents = shares(series);
67
67
  const detail = legendValues ? (index) => ({ value: formatValue(series[index]), share: `${percents[index]}%` }) : void 0;
68
68
  return /* @__PURE__ */ jsxRuntime.jsxs(
69
69
  recharts.PieChart,
70
70
  {
71
- ...chunk6NRRABPF_cjs.frameProps({ height, width, ariaLabel: name }),
71
+ ...chunkSR2ESX3R_cjs.frameProps({ height, width, ariaLabel: name }),
72
72
  margin: { top: 0, right: 0, bottom: 0, left: 0 },
73
73
  children: [
74
74
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1,11 +1,11 @@
1
1
  "use client";
2
- import { useHiddenSeries, legendProps, ChartLegend } from './chunk-6ALKZLN2.js';
2
+ import { useHiddenSeries, legendProps, ChartLegend } from './chunk-4AKHZNWP.js';
3
3
  import { CenterText } from './chunk-URNXLVSK.js';
4
4
  import { ANIMATION } from './chunk-5C2RLJYZ.js';
5
- import './chunk-RILUG7JB.js';
5
+ import './chunk-DI2FBGKM.js';
6
6
  import { ChartTooltip } from './chunk-VIOSQN4Z.js';
7
7
  import { seriesKey } from './chunk-UJIV5PVI.js';
8
- import { ChartEmpty, frameProps } from './chunk-3N43QT36.js';
8
+ import { ChartEmpty, frameProps } from './chunk-56WD3OHA.js';
9
9
  import { seriesColor, PANEL } from './chunk-UATB2IRY.js';
10
10
  import { useState } from 'react';
11
11
  import { PieChart, Pie, Tooltip, Legend } from 'recharts';
@@ -2,9 +2,9 @@
2
2
  'use strict';
3
3
 
4
4
  var chunkIW6EWB26_cjs = require('./chunk-IW6EWB26.cjs');
5
- var chunkRLHGPHC7_cjs = require('./chunk-RLHGPHC7.cjs');
5
+ var chunkG4WOPX4D_cjs = require('./chunk-G4WOPX4D.cjs');
6
6
  var chunkT3K6Q34U_cjs = require('./chunk-T3K6Q34U.cjs');
7
- var chunk6NRRABPF_cjs = require('./chunk-6NRRABPF.cjs');
7
+ var chunkSR2ESX3R_cjs = require('./chunk-SR2ESX3R.cjs');
8
8
  var chunkLT3UC65O_cjs = require('./chunk-LT3UC65O.cjs');
9
9
  var react = require('react');
10
10
  var jsxRuntime = require('react/jsx-runtime');
@@ -70,7 +70,7 @@ var FunnelChart = ({
70
70
  const [hover, setHover] = react.useState();
71
71
  const [focus, setFocus] = react.useState();
72
72
  if (isEmptyFunnel(stages)) {
73
- return /* @__PURE__ */ jsxRuntime.jsx(chunk6NRRABPF_cjs.ChartEmpty, { message: emptyMessage, ariaLabel: name, height: height ?? 160, width });
73
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkSR2ESX3R_cjs.ChartEmpty, { message: emptyMessage, ariaLabel: name, height: height ?? 160, width });
74
74
  }
75
75
  const rows = funnelRows(stages);
76
76
  const first = rows[0];
@@ -142,7 +142,7 @@ var FunnelChart = ({
142
142
  "span",
143
143
  {
144
144
  "data-funnel-name": "",
145
- style: { fontSize: chunkRLHGPHC7_cjs.TEXT_SIZE, color: chunkLT3UC65O_cjs.MUTED, minWidth: 0, ...textStyle, ...extra },
145
+ style: { fontSize: chunkG4WOPX4D_cjs.TEXT_SIZE, color: chunkLT3UC65O_cjs.MUTED, minWidth: 0, ...textStyle, ...extra },
146
146
  children: row.label
147
147
  }
148
148
  );
@@ -166,7 +166,7 @@ var FunnelChart = ({
166
166
  {
167
167
  "data-funnel-chip": "",
168
168
  style: {
169
- fontSize: chunkRLHGPHC7_cjs.TEXT_SIZE,
169
+ fontSize: chunkG4WOPX4D_cjs.TEXT_SIZE,
170
170
  fontWeight: 600,
171
171
  color: chunkLT3UC65O_cjs.TEXT,
172
172
  background: chunkLT3UC65O_cjs.TRACK,
@@ -182,7 +182,7 @@ var FunnelChart = ({
182
182
  "span",
183
183
  {
184
184
  "data-funnel-note": "",
185
- style: { fontSize: chunkRLHGPHC7_cjs.TEXT_SIZE, color: chunkLT3UC65O_cjs.MUTED, minWidth: 0, ...textStyle },
185
+ style: { fontSize: chunkG4WOPX4D_cjs.TEXT_SIZE, color: chunkLT3UC65O_cjs.MUTED, minWidth: 0, ...textStyle },
186
186
  children: note(row)
187
187
  }
188
188
  )
@@ -362,6 +362,8 @@ var FunnelChart = ({
362
362
  {
363
363
  ref: frameRef,
364
364
  style: {
365
+ // The stages keep their order under `dir="rtl"`, as a chart's axis does.
366
+ direction: "ltr",
365
367
  position: "relative",
366
368
  width: width ?? "100%",
367
369
  height,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/funnel.ts","../src/FunnelChart.tsx"],"names":["stage","useRef","useElementWidth","useCallback","useState","jsx","ChartEmpty","seriesColor","SERIES","PANEL","TEXT_SIZE","MUTED","jsxs","Fragment","TEXT","TRACK","TooltipCard"],"mappings":"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","file":"FunnelChart.cjs","sourcesContent":["// The maths of FunnelChart: bar widths, conversion rates and the flow between\n// two bars. No DOM and no colors, so it runs in plain Node.\n\nexport type FunnelStage = { label: string; value: number };\n\nexport type FunnelRow = {\n label: string;\n /** The value drawn: a negative or non-finite one counts as 0. */\n value: number;\n /** Width of the bar, 0 to 1, against the largest stage. */\n share: number;\n /** Rate from the previous stage; 1 for the first. `null` when the previous stage is 0. */\n fromPrevious: number | null;\n /** Rate from the first stage; `null` when the first stage is 0. */\n fromFirst: number | null;\n};\n\nconst clean = (value: number) => (Number.isFinite(value) && value > 0 ? value : 0);\n\n/** Whether there is nothing to draw: no stage, or only stages at 0. */\nexport const isEmptyFunnel = (stages: readonly FunnelStage[]): boolean =>\n stages.every((stage) => clean(stage.value) === 0);\n\n/**\n * One row per stage, in the order given. A bar is as wide as its value against\n * the largest stage, which is the first one unless a stage grows (a merge of\n * sources, say): that stage then has a rate over 1 and the widest bar, so no\n * bar ever leaves the box. A rate over a stage at 0 is `null`.\n */\nexport const funnelRows = (stages: readonly FunnelStage[]): FunnelRow[] => {\n const values = stages.map((stage) => clean(stage.value));\n const widest = Math.max(0, ...values);\n const rate = (value: number, over: number) => (over > 0 ? value / over : null);\n return stages.map((stage, i) => ({\n label: stage.label,\n value: values[i],\n share: widest > 0 ? values[i] / widest : 0,\n fromPrevious: i === 0 ? 1 : rate(values[i], values[i - 1]),\n fromFirst: rate(values[i], values[0]),\n }));\n};\n\n/** A rate as a percentage with one decimal (`0.654` is `65.4%`); `–` when there is none. */\nexport const formatRate = (rate: number | null): string =>\n rate === null ? '–' : `${(rate * 100).toFixed(1)}%`;\n\n// What the last bar keeps of the accent: still 1.5:1 against the card in the dark themes (see SEQUENTIAL).\nconst FADE_FLOOR = 42;\n\n/** How much of the accent stage `index` of `count` keeps, in percent: 100 for the first, 42 for the last. */\nexport const fadeShare = (index: number, count: number): number =>\n count <= 1 ? 100 : Math.round((100 - ((100 - FADE_FLOOR) * index) / (count - 1)) * 10) / 10;\n\n/**\n * The `points` of the flow from a bar to the next, in a box 100 wide and 1\n * tall, stretched to the gap between the two bars: the top edge is the upper\n * bar's width, the bottom edge the lower bar's, both centred.\n */\nexport const flowPoints = (upper: number, lower: number): string => {\n const edge = (share: number) => [(100 - 100 * share) / 2, (100 + 100 * share) / 2];\n const [topLeft, topRight] = edge(upper);\n const [bottomLeft, bottomRight] = edge(lower);\n return `${topLeft},0 ${topRight},0 ${bottomRight},1 ${bottomLeft},1`;\n};\n","import {\n useCallback,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { TooltipCard } from './core/ChartTooltip';\nimport { MUTED, PANEL, SERIES, TEXT, TRACK, seriesColor } from './core/colors';\nimport { ChartEmpty } from './core/frame';\nimport { useElementWidth } from './core/useElementWidth';\nimport {\n fadeShare,\n flowPoints,\n formatRate,\n funnelRows,\n isEmptyFunnel,\n type FunnelRow,\n} from './funnel';\nimport { TEXT_SIZE } from './core/axes';\nimport type { ChartEmptyProps, ChartProps } from './types';\n\nexport type { FunnelStage } from './funnel';\n\nexport type FunnelChartProps = Omit<ChartProps, 'children'> &\n ChartEmptyProps & {\n /**\n * The stages, drawn top to bottom in the order given: the wrapper does not\n * sort them. Each bar is as wide as its value against the largest stage,\n * which is the first one unless a stage grows.\n */\n stages: { label: string; value: number }[];\n /**\n * One color per stage, cycling when there are fewer. Default: the accent,\n * fading from the first stage to the last.\n */\n colors?: string[];\n /** Format the values, in the labels and the tooltip. Percentages always have one decimal. */\n formatValue?: (value: number) => string;\n /**\n * Show the conversion: a chip with the rate from the previous stage, the\n * rate from the first stage beside it, and both in the tooltip. Off, only\n * the values show. Default true.\n */\n showConversion?: boolean;\n };\n\n// A bar is 30px at the normal size and 24px on a narrow box; the flow takes\n// the rest of the stage's row, at least this much.\nconst BAR = '1.875rem';\nconst BAR_NARROW = '1.5rem';\nconst FLOW = '1.75rem';\nconst FLOW_NARROW = '1.25rem';\nconst FLOW_OPACITY = 0.14;\n// Below this width the names and values go above their bar.\nconst NARROW = 520;\n// Room the tooltip card leaves the bar.\nconst TIP_GAP = 6;\n\ntype Tip = {\n index: number;\n x: number;\n top: number;\n bottom: number;\n align: 'start' | 'center' | 'end';\n below: boolean;\n};\n\nconst textStyle: CSSProperties = {\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nexport const FunnelChart = ({\n stages,\n colors,\n formatValue = String,\n showConversion = true,\n emptyMessage = 'No data',\n ariaLabel,\n height,\n width,\n}: FunnelChartProps) => {\n const name = ariaLabel ?? `Funnel: ${stages.map((stage) => stage.label).join(', ')}`;\n const frame = useRef<HTMLDivElement | null>(null);\n const list = useRef<HTMLUListElement>(null);\n const { ref: measure, width: measured } = useElementWidth();\n // The box is read for the tooltip's position and measured for the layout.\n const frameRef = useCallback(\n (element: HTMLDivElement | null) => {\n frame.current = element;\n measure(element);\n },\n [measure]\n );\n const [roving, setRoving] = useState(0);\n const [hover, setHover] = useState<Tip>();\n const [focus, setFocus] = useState<Tip>();\n if (isEmptyFunnel(stages)) {\n return (\n <ChartEmpty message={emptyMessage} ariaLabel={name} height={height ?? 160} width={width} />\n );\n }\n\n const rows = funnelRows(stages);\n const first = rows[0];\n const narrow = measured !== undefined && measured < NARROW;\n const bar = narrow ? BAR_NARROW : BAR;\n const flow = narrow ? FLOW_NARROW : FLOW;\n const tabbable = Math.min(roving, rows.length - 1);\n const tip = hover ?? focus;\n\n const color = (i: number) =>\n colors?.length\n ? seriesColor(i, colors)\n : `color-mix(in oklab, ${SERIES[0]} ${fadeShare(i, rows.length)}%, ${PANEL})`;\n const flowColor = (i: number) => (colors?.length ? seriesColor(i, colors) : SERIES[0]);\n\n const chip = (row: FunnelRow, i: number) => (i === 0 ? '100%' : formatRate(row.fromPrevious));\n const note = (row: FunnelRow) => `${formatRate(row.fromFirst)} of ${first.label}`;\n const nameOf = (row: FunnelRow, i: number) =>\n [\n `${row.label}: ${formatValue(row.value)}`,\n ...(showConversion && i > 0\n ? [`${formatRate(row.fromPrevious)} of previous stage`, note(row)]\n : []),\n ].join(', ');\n\n const tipFor = (index: number, item: HTMLElement): Tip => {\n const host = (frame.current as HTMLElement).getBoundingClientRect();\n const box = (item.querySelector('[data-funnel-bar]') ?? item).getBoundingClientRect();\n const middle = box.left - host.left + box.width / 2;\n // The card hangs from the side of the bar nearest the middle of the chart,\n // so it never leaves the box.\n const align =\n middle < host.width / 3 ? 'start' : middle > (2 * host.width) / 3 ? 'end' : 'center';\n return {\n index,\n x: { start: box.left - host.left, center: middle, end: box.right - host.left }[align],\n top: box.top - host.top,\n bottom: box.bottom - host.top,\n align,\n below: box.top - host.top < 110,\n };\n };\n\n const move = (event: KeyboardEvent<HTMLUListElement>) => {\n const origin = (event.target as HTMLElement).closest<HTMLElement>('[data-funnel-stage]');\n if (!origin) return;\n const index = Number(origin.dataset.index);\n const targets: Record<string, number> = {\n ArrowDown: index + 1,\n ArrowRight: index + 1,\n ArrowUp: index - 1,\n ArrowLeft: index - 1,\n Home: 0,\n End: rows.length - 1,\n };\n const target = targets[event.key];\n if (target === undefined) return;\n event.preventDefault();\n list.current\n ?.querySelector<HTMLElement>(\n `[data-index=\"${Math.min(Math.max(target, 0), rows.length - 1)}\"]`\n )\n ?.focus();\n };\n\n const focusIn = (index: number, event: FocusEvent<HTMLElement>) => {\n setRoving(index);\n // A click focuses a stage too, and would leave its tooltip up: only the keyboard shows it.\n if (event.currentTarget.matches(':focus-visible')) {\n setFocus(tipFor(index, event.currentTarget));\n }\n };\n const focusOut = (event: FocusEvent<HTMLUListElement>) => {\n if (!event.currentTarget.contains(event.relatedTarget)) setFocus(undefined);\n };\n\n const tipRow = tip && rows[tip.index];\n const tipStyle: CSSProperties | undefined = tip && {\n position: 'absolute',\n left: tip.x,\n top: tip.below ? tip.bottom + TIP_GAP : tip.top - TIP_GAP,\n transform: `translate(${{ start: '0', center: '-50%', end: '-100%' }[tip.align]}, ${tip.below ? '0' : '-100%'})`,\n zIndex: 10,\n pointerEvents: 'none',\n };\n\n const label = (row: FunnelRow, extra?: CSSProperties) => (\n <span\n data-funnel-name=\"\"\n style={{ fontSize: TEXT_SIZE, color: MUTED, minWidth: 0, ...textStyle, ...extra }}\n >\n {row.label}\n </span>\n );\n const figures = (row: FunnelRow, i: number) => (\n <>\n <span\n data-funnel-value=\"\"\n style={{\n fontSize: 'var(--text-sm, 0.875rem)',\n fontWeight: 600,\n color: TEXT,\n fontVariantNumeric: 'tabular-nums',\n whiteSpace: 'nowrap',\n }}\n >\n {formatValue(row.value)}\n </span>\n {showConversion && (\n <span\n data-funnel-chip=\"\"\n style={{\n fontSize: TEXT_SIZE,\n fontWeight: 600,\n color: TEXT,\n background: TRACK,\n padding: '0.0625rem 0.4375rem',\n borderRadius: 999,\n fontVariantNumeric: 'tabular-nums',\n whiteSpace: 'nowrap',\n }}\n >\n {chip(row, i)}\n </span>\n )}\n {showConversion && i > 0 && !narrow && (\n <span\n data-funnel-note=\"\"\n style={{ fontSize: TEXT_SIZE, color: MUTED, minWidth: 0, ...textStyle }}\n >\n {note(row)}\n </span>\n )}\n </>\n );\n\n const flowSvg = (i: number, style: CSSProperties) => (\n <svg\n aria-hidden\n data-funnel-flow=\"\"\n viewBox=\"0 0 100 1\"\n preserveAspectRatio=\"none\"\n style={{ display: 'block', ...style }}\n >\n <polygon\n points={flowPoints(rows[i - 1].share, rows[i].share)}\n fill={flowColor(i - 1)}\n fillOpacity={FLOW_OPACITY}\n />\n </svg>\n );\n\n const barBox = (i: number): CSSProperties => ({\n width: `${rows[i].share * 100}%`,\n minWidth: rows[i].value > 0 ? 3 : 0,\n height: bar,\n borderRadius: 6,\n background: color(i),\n // The same ring for the pointer and the keyboard, in the text color: an\n // accent ring would vanish on a bar that is the accent.\n outline: tip?.index === i ? `2px solid ${TEXT}` : 'none',\n outlineOffset: 1,\n });\n\n const stage = (row: FunnelRow, i: number): ReactNode => {\n const common = {\n 'data-funnel-stage': '',\n 'data-index': i,\n tabIndex: i === tabbable ? 0 : -1,\n 'aria-label': nameOf(row, i),\n onFocus: (event: FocusEvent<HTMLElement>) => focusIn(i, event),\n onMouseEnter: (event: MouseEvent<HTMLElement>) => setHover(tipFor(i, event.currentTarget)),\n onMouseLeave: () => setHover(undefined),\n };\n if (narrow) {\n return (\n <li\n key={i}\n {...common}\n style={{\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr) auto',\n gridTemplateRows: '1fr auto',\n columnGap: '0.75rem',\n outline: 'none',\n }}\n >\n {i > 0 && flowSvg(i, { gridRow: 1, gridColumn: '1 / -1', width: '100%', height: '100%' })}\n <span\n aria-hidden\n style={{\n gridRow: 1,\n gridColumn: 1,\n alignSelf: 'end',\n display: 'flex',\n minWidth: 0,\n padding: `${i > 0 ? flow : '0'} 0 0.25rem`,\n }}\n >\n {label(row, { color: TEXT })}\n </span>\n <span\n aria-hidden\n style={{\n gridRow: 1,\n gridColumn: 2,\n alignSelf: 'end',\n display: 'flex',\n alignItems: 'baseline',\n gap: '0.5rem',\n padding: `${i > 0 ? flow : '0'} 0 0.25rem`,\n }}\n >\n {figures(row, i)}\n </span>\n <div\n aria-hidden\n style={{ gridRow: 2, gridColumn: '1 / -1', display: 'flex', justifyContent: 'center' }}\n >\n <div data-funnel-bar=\"\" style={barBox(i)} />\n </div>\n </li>\n );\n }\n return (\n <li\n key={i}\n {...common}\n style={{\n display: 'grid',\n gridColumn: '1 / -1',\n gridTemplateColumns: 'subgrid',\n outline: 'none',\n }}\n >\n <span\n aria-hidden\n style={{\n gridColumn: 1,\n alignSelf: 'end',\n height: bar,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n minWidth: 0,\n }}\n >\n {label(row)}\n </span>\n <div\n aria-hidden\n style={{\n gridColumn: 2,\n position: 'relative',\n display: 'flex',\n alignItems: 'flex-end',\n justifyContent: 'center',\n minHeight: i === 0 ? bar : `calc(${bar} + ${flow})`,\n minWidth: 0,\n }}\n >\n {i > 0 &&\n flowSvg(i, {\n position: 'absolute',\n left: 0,\n top: 0,\n width: '100%',\n height: `calc(100% - ${bar})`,\n })}\n <div data-funnel-bar=\"\" style={barBox(i)} />\n </div>\n <span\n aria-hidden\n style={{\n gridColumn: 3,\n alignSelf: 'end',\n height: bar,\n display: 'flex',\n alignItems: 'center',\n gap: '0.5rem',\n minWidth: 0,\n }}\n >\n {figures(row, i)}\n </span>\n </li>\n );\n };\n\n return (\n <div\n ref={frameRef}\n style={{\n position: 'relative',\n width: width ?? '100%',\n height,\n maxWidth: '100%',\n minWidth: 0,\n }}\n >\n <ul\n ref={list}\n role=\"list\"\n aria-label={name}\n onKeyDown={move}\n onBlur={focusOut}\n style={{\n display: 'grid',\n gridTemplateColumns: narrow\n ? 'minmax(0, 1fr)'\n : 'fit-content(30%) minmax(0, 1fr) fit-content(45%)',\n gridTemplateRows: `auto repeat(${rows.length - 1}, minmax(min-content, 1fr))`,\n columnGap: '1rem',\n width: '100%',\n height: height === undefined ? undefined : '100%',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n }}\n >\n {rows.map(stage)}\n </ul>\n {tip && tipRow && tipStyle && (\n <div data-funnel-tooltip=\"\" aria-hidden style={tipStyle}>\n <TooltipCard\n title={tipRow.label}\n rows={[\n { name: 'Value', value: formatValue(tipRow.value), color: color(tip.index) },\n ...(showConversion && tip.index > 0\n ? [\n {\n name: 'Of previous stage',\n value: formatRate(tipRow.fromPrevious),\n color: color(tip.index),\n },\n {\n name: `Of ${first.label}`,\n value: formatRate(tipRow.fromFirst),\n color: color(tip.index),\n },\n ]\n : []),\n ]}\n />\n </div>\n )}\n </div>\n );\n};\n"]}
1
+ {"version":3,"sources":["../src/funnel.ts","../src/FunnelChart.tsx"],"names":["stage","useRef","useElementWidth","useCallback","useState","jsx","ChartEmpty","seriesColor","SERIES","PANEL","TEXT_SIZE","MUTED","jsxs","Fragment","TEXT","TRACK","TooltipCard"],"mappings":"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","file":"FunnelChart.cjs","sourcesContent":["// The maths of FunnelChart: bar widths, conversion rates and the flow between\n// two bars. No DOM and no colors, so it runs in plain Node.\n\nexport type FunnelStage = { label: string; value: number };\n\nexport type FunnelRow = {\n label: string;\n /** The value drawn: a negative or non-finite one counts as 0. */\n value: number;\n /** Width of the bar, 0 to 1, against the largest stage. */\n share: number;\n /** Rate from the previous stage; 1 for the first. `null` when the previous stage is 0. */\n fromPrevious: number | null;\n /** Rate from the first stage; `null` when the first stage is 0. */\n fromFirst: number | null;\n};\n\nconst clean = (value: number) => (Number.isFinite(value) && value > 0 ? value : 0);\n\n/** Whether there is nothing to draw: no stage, or only stages at 0. */\nexport const isEmptyFunnel = (stages: readonly FunnelStage[]): boolean =>\n stages.every((stage) => clean(stage.value) === 0);\n\n/**\n * One row per stage, in the order given. A bar is as wide as its value against\n * the largest stage, which is the first one unless a stage grows (a merge of\n * sources, say): that stage then has a rate over 1 and the widest bar, so no\n * bar ever leaves the box. A rate over a stage at 0 is `null`.\n */\nexport const funnelRows = (stages: readonly FunnelStage[]): FunnelRow[] => {\n const values = stages.map((stage) => clean(stage.value));\n const widest = Math.max(0, ...values);\n const rate = (value: number, over: number) => (over > 0 ? value / over : null);\n return stages.map((stage, i) => ({\n label: stage.label,\n value: values[i],\n share: widest > 0 ? values[i] / widest : 0,\n fromPrevious: i === 0 ? 1 : rate(values[i], values[i - 1]),\n fromFirst: rate(values[i], values[0]),\n }));\n};\n\n/** A rate as a percentage with one decimal (`0.654` is `65.4%`); `–` when there is none. */\nexport const formatRate = (rate: number | null): string =>\n rate === null ? '–' : `${(rate * 100).toFixed(1)}%`;\n\n// What the last bar keeps of the accent: still 1.5:1 against the card in the dark themes (see SEQUENTIAL).\nconst FADE_FLOOR = 42;\n\n/** How much of the accent stage `index` of `count` keeps, in percent: 100 for the first, 42 for the last. */\nexport const fadeShare = (index: number, count: number): number =>\n count <= 1 ? 100 : Math.round((100 - ((100 - FADE_FLOOR) * index) / (count - 1)) * 10) / 10;\n\n/**\n * The `points` of the flow from a bar to the next, in a box 100 wide and 1\n * tall, stretched to the gap between the two bars: the top edge is the upper\n * bar's width, the bottom edge the lower bar's, both centred.\n */\nexport const flowPoints = (upper: number, lower: number): string => {\n const edge = (share: number) => [(100 - 100 * share) / 2, (100 + 100 * share) / 2];\n const [topLeft, topRight] = edge(upper);\n const [bottomLeft, bottomRight] = edge(lower);\n return `${topLeft},0 ${topRight},0 ${bottomRight},1 ${bottomLeft},1`;\n};\n","import {\n useCallback,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { TooltipCard } from './core/ChartTooltip';\nimport { MUTED, PANEL, SERIES, TEXT, TRACK, seriesColor } from './core/colors';\nimport { ChartEmpty } from './core/frame';\nimport { useElementWidth } from './core/useElementWidth';\nimport {\n fadeShare,\n flowPoints,\n formatRate,\n funnelRows,\n isEmptyFunnel,\n type FunnelRow,\n} from './funnel';\nimport { TEXT_SIZE } from './core/axes';\nimport type { ChartEmptyProps, ChartProps } from './types';\n\nexport type { FunnelStage } from './funnel';\n\nexport type FunnelChartProps = Omit<ChartProps, 'children'> &\n ChartEmptyProps & {\n /**\n * The stages, drawn top to bottom in the order given: the wrapper does not\n * sort them. Each bar is as wide as its value against the largest stage,\n * which is the first one unless a stage grows.\n */\n stages: { label: string; value: number }[];\n /**\n * One color per stage, cycling when there are fewer. Default: the accent,\n * fading from the first stage to the last.\n */\n colors?: string[];\n /** Format the values, in the labels and the tooltip. Percentages always have one decimal. */\n formatValue?: (value: number) => string;\n /**\n * Show the conversion: a chip with the rate from the previous stage, the\n * rate from the first stage beside it, and both in the tooltip. Off, only\n * the values show. Default true.\n */\n showConversion?: boolean;\n };\n\n// A bar is 30px at the normal size and 24px on a narrow box; the flow takes\n// the rest of the stage's row, at least this much.\nconst BAR = '1.875rem';\nconst BAR_NARROW = '1.5rem';\nconst FLOW = '1.75rem';\nconst FLOW_NARROW = '1.25rem';\nconst FLOW_OPACITY = 0.14;\n// Below this width the names and values go above their bar.\nconst NARROW = 520;\n// Room the tooltip card leaves the bar.\nconst TIP_GAP = 6;\n\ntype Tip = {\n index: number;\n x: number;\n top: number;\n bottom: number;\n align: 'start' | 'center' | 'end';\n below: boolean;\n};\n\nconst textStyle: CSSProperties = {\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nexport const FunnelChart = ({\n stages,\n colors,\n formatValue = String,\n showConversion = true,\n emptyMessage = 'No data',\n ariaLabel,\n height,\n width,\n}: FunnelChartProps) => {\n const name = ariaLabel ?? `Funnel: ${stages.map((stage) => stage.label).join(', ')}`;\n const frame = useRef<HTMLDivElement | null>(null);\n const list = useRef<HTMLUListElement>(null);\n const { ref: measure, width: measured } = useElementWidth();\n // The box is read for the tooltip's position and measured for the layout.\n const frameRef = useCallback(\n (element: HTMLDivElement | null) => {\n frame.current = element;\n measure(element);\n },\n [measure]\n );\n const [roving, setRoving] = useState(0);\n const [hover, setHover] = useState<Tip>();\n const [focus, setFocus] = useState<Tip>();\n if (isEmptyFunnel(stages)) {\n return (\n <ChartEmpty message={emptyMessage} ariaLabel={name} height={height ?? 160} width={width} />\n );\n }\n\n const rows = funnelRows(stages);\n const first = rows[0];\n const narrow = measured !== undefined && measured < NARROW;\n const bar = narrow ? BAR_NARROW : BAR;\n const flow = narrow ? FLOW_NARROW : FLOW;\n const tabbable = Math.min(roving, rows.length - 1);\n const tip = hover ?? focus;\n\n const color = (i: number) =>\n colors?.length\n ? seriesColor(i, colors)\n : `color-mix(in oklab, ${SERIES[0]} ${fadeShare(i, rows.length)}%, ${PANEL})`;\n const flowColor = (i: number) => (colors?.length ? seriesColor(i, colors) : SERIES[0]);\n\n const chip = (row: FunnelRow, i: number) => (i === 0 ? '100%' : formatRate(row.fromPrevious));\n const note = (row: FunnelRow) => `${formatRate(row.fromFirst)} of ${first.label}`;\n const nameOf = (row: FunnelRow, i: number) =>\n [\n `${row.label}: ${formatValue(row.value)}`,\n ...(showConversion && i > 0\n ? [`${formatRate(row.fromPrevious)} of previous stage`, note(row)]\n : []),\n ].join(', ');\n\n const tipFor = (index: number, item: HTMLElement): Tip => {\n const host = (frame.current as HTMLElement).getBoundingClientRect();\n const box = (item.querySelector('[data-funnel-bar]') ?? item).getBoundingClientRect();\n const middle = box.left - host.left + box.width / 2;\n // The card hangs from the side of the bar nearest the middle of the chart,\n // so it never leaves the box.\n const align =\n middle < host.width / 3 ? 'start' : middle > (2 * host.width) / 3 ? 'end' : 'center';\n return {\n index,\n x: { start: box.left - host.left, center: middle, end: box.right - host.left }[align],\n top: box.top - host.top,\n bottom: box.bottom - host.top,\n align,\n below: box.top - host.top < 110,\n };\n };\n\n const move = (event: KeyboardEvent<HTMLUListElement>) => {\n const origin = (event.target as HTMLElement).closest<HTMLElement>('[data-funnel-stage]');\n if (!origin) return;\n const index = Number(origin.dataset.index);\n const targets: Record<string, number> = {\n ArrowDown: index + 1,\n ArrowRight: index + 1,\n ArrowUp: index - 1,\n ArrowLeft: index - 1,\n Home: 0,\n End: rows.length - 1,\n };\n const target = targets[event.key];\n if (target === undefined) return;\n event.preventDefault();\n list.current\n ?.querySelector<HTMLElement>(\n `[data-index=\"${Math.min(Math.max(target, 0), rows.length - 1)}\"]`\n )\n ?.focus();\n };\n\n const focusIn = (index: number, event: FocusEvent<HTMLElement>) => {\n setRoving(index);\n // A click focuses a stage too, and would leave its tooltip up: only the keyboard shows it.\n if (event.currentTarget.matches(':focus-visible')) {\n setFocus(tipFor(index, event.currentTarget));\n }\n };\n const focusOut = (event: FocusEvent<HTMLUListElement>) => {\n if (!event.currentTarget.contains(event.relatedTarget)) setFocus(undefined);\n };\n\n const tipRow = tip && rows[tip.index];\n const tipStyle: CSSProperties | undefined = tip && {\n position: 'absolute',\n left: tip.x,\n top: tip.below ? tip.bottom + TIP_GAP : tip.top - TIP_GAP,\n transform: `translate(${{ start: '0', center: '-50%', end: '-100%' }[tip.align]}, ${tip.below ? '0' : '-100%'})`,\n zIndex: 10,\n pointerEvents: 'none',\n };\n\n const label = (row: FunnelRow, extra?: CSSProperties) => (\n <span\n data-funnel-name=\"\"\n style={{ fontSize: TEXT_SIZE, color: MUTED, minWidth: 0, ...textStyle, ...extra }}\n >\n {row.label}\n </span>\n );\n const figures = (row: FunnelRow, i: number) => (\n <>\n <span\n data-funnel-value=\"\"\n style={{\n fontSize: 'var(--text-sm, 0.875rem)',\n fontWeight: 600,\n color: TEXT,\n fontVariantNumeric: 'tabular-nums',\n whiteSpace: 'nowrap',\n }}\n >\n {formatValue(row.value)}\n </span>\n {showConversion && (\n <span\n data-funnel-chip=\"\"\n style={{\n fontSize: TEXT_SIZE,\n fontWeight: 600,\n color: TEXT,\n background: TRACK,\n padding: '0.0625rem 0.4375rem',\n borderRadius: 999,\n fontVariantNumeric: 'tabular-nums',\n whiteSpace: 'nowrap',\n }}\n >\n {chip(row, i)}\n </span>\n )}\n {showConversion && i > 0 && !narrow && (\n <span\n data-funnel-note=\"\"\n style={{ fontSize: TEXT_SIZE, color: MUTED, minWidth: 0, ...textStyle }}\n >\n {note(row)}\n </span>\n )}\n </>\n );\n\n const flowSvg = (i: number, style: CSSProperties) => (\n <svg\n aria-hidden\n data-funnel-flow=\"\"\n viewBox=\"0 0 100 1\"\n preserveAspectRatio=\"none\"\n style={{ display: 'block', ...style }}\n >\n <polygon\n points={flowPoints(rows[i - 1].share, rows[i].share)}\n fill={flowColor(i - 1)}\n fillOpacity={FLOW_OPACITY}\n />\n </svg>\n );\n\n const barBox = (i: number): CSSProperties => ({\n width: `${rows[i].share * 100}%`,\n minWidth: rows[i].value > 0 ? 3 : 0,\n height: bar,\n borderRadius: 6,\n background: color(i),\n // The same ring for the pointer and the keyboard, in the text color: an\n // accent ring would vanish on a bar that is the accent.\n outline: tip?.index === i ? `2px solid ${TEXT}` : 'none',\n outlineOffset: 1,\n });\n\n const stage = (row: FunnelRow, i: number): ReactNode => {\n const common = {\n 'data-funnel-stage': '',\n 'data-index': i,\n tabIndex: i === tabbable ? 0 : -1,\n 'aria-label': nameOf(row, i),\n onFocus: (event: FocusEvent<HTMLElement>) => focusIn(i, event),\n onMouseEnter: (event: MouseEvent<HTMLElement>) => setHover(tipFor(i, event.currentTarget)),\n onMouseLeave: () => setHover(undefined),\n };\n if (narrow) {\n return (\n <li\n key={i}\n {...common}\n style={{\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr) auto',\n gridTemplateRows: '1fr auto',\n columnGap: '0.75rem',\n outline: 'none',\n }}\n >\n {i > 0 && flowSvg(i, { gridRow: 1, gridColumn: '1 / -1', width: '100%', height: '100%' })}\n <span\n aria-hidden\n style={{\n gridRow: 1,\n gridColumn: 1,\n alignSelf: 'end',\n display: 'flex',\n minWidth: 0,\n padding: `${i > 0 ? flow : '0'} 0 0.25rem`,\n }}\n >\n {label(row, { color: TEXT })}\n </span>\n <span\n aria-hidden\n style={{\n gridRow: 1,\n gridColumn: 2,\n alignSelf: 'end',\n display: 'flex',\n alignItems: 'baseline',\n gap: '0.5rem',\n padding: `${i > 0 ? flow : '0'} 0 0.25rem`,\n }}\n >\n {figures(row, i)}\n </span>\n <div\n aria-hidden\n style={{ gridRow: 2, gridColumn: '1 / -1', display: 'flex', justifyContent: 'center' }}\n >\n <div data-funnel-bar=\"\" style={barBox(i)} />\n </div>\n </li>\n );\n }\n return (\n <li\n key={i}\n {...common}\n style={{\n display: 'grid',\n gridColumn: '1 / -1',\n gridTemplateColumns: 'subgrid',\n outline: 'none',\n }}\n >\n <span\n aria-hidden\n style={{\n gridColumn: 1,\n alignSelf: 'end',\n height: bar,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n minWidth: 0,\n }}\n >\n {label(row)}\n </span>\n <div\n aria-hidden\n style={{\n gridColumn: 2,\n position: 'relative',\n display: 'flex',\n alignItems: 'flex-end',\n justifyContent: 'center',\n minHeight: i === 0 ? bar : `calc(${bar} + ${flow})`,\n minWidth: 0,\n }}\n >\n {i > 0 &&\n flowSvg(i, {\n position: 'absolute',\n left: 0,\n top: 0,\n width: '100%',\n height: `calc(100% - ${bar})`,\n })}\n <div data-funnel-bar=\"\" style={barBox(i)} />\n </div>\n <span\n aria-hidden\n style={{\n gridColumn: 3,\n alignSelf: 'end',\n height: bar,\n display: 'flex',\n alignItems: 'center',\n gap: '0.5rem',\n minWidth: 0,\n }}\n >\n {figures(row, i)}\n </span>\n </li>\n );\n };\n\n return (\n <div\n ref={frameRef}\n style={{\n // The stages keep their order under `dir=\"rtl\"`, as a chart's axis does.\n direction: 'ltr',\n position: 'relative',\n width: width ?? '100%',\n height,\n maxWidth: '100%',\n minWidth: 0,\n }}\n >\n <ul\n ref={list}\n role=\"list\"\n aria-label={name}\n onKeyDown={move}\n onBlur={focusOut}\n style={{\n display: 'grid',\n gridTemplateColumns: narrow\n ? 'minmax(0, 1fr)'\n : 'fit-content(30%) minmax(0, 1fr) fit-content(45%)',\n gridTemplateRows: `auto repeat(${rows.length - 1}, minmax(min-content, 1fr))`,\n columnGap: '1rem',\n width: '100%',\n height: height === undefined ? undefined : '100%',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n }}\n >\n {rows.map(stage)}\n </ul>\n {tip && tipRow && tipStyle && (\n <div data-funnel-tooltip=\"\" aria-hidden style={tipStyle}>\n <TooltipCard\n title={tipRow.label}\n rows={[\n { name: 'Value', value: formatValue(tipRow.value), color: color(tip.index) },\n ...(showConversion && tip.index > 0\n ? [\n {\n name: 'Of previous stage',\n value: formatRate(tipRow.fromPrevious),\n color: color(tip.index),\n },\n {\n name: `Of ${first.label}`,\n value: formatRate(tipRow.fromFirst),\n color: color(tip.index),\n },\n ]\n : []),\n ]}\n />\n </div>\n )}\n </div>\n );\n};\n"]}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { useElementWidth } from './chunk-AOSO5QHL.js';
3
- import { TEXT_SIZE } from './chunk-RILUG7JB.js';
3
+ import { TEXT_SIZE } from './chunk-DI2FBGKM.js';
4
4
  import { TooltipCard } from './chunk-VIOSQN4Z.js';
5
- import { ChartEmpty } from './chunk-3N43QT36.js';
5
+ import { ChartEmpty } from './chunk-56WD3OHA.js';
6
6
  import { SERIES, PANEL, seriesColor, MUTED, TEXT, TRACK } from './chunk-UATB2IRY.js';
7
7
  import { useRef, useCallback, useState } from 'react';
8
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -360,6 +360,8 @@ var FunnelChart = ({
360
360
  {
361
361
  ref: frameRef,
362
362
  style: {
363
+ // The stages keep their order under `dir="rtl"`, as a chart's axis does.
364
+ direction: "ltr",
363
365
  position: "relative",
364
366
  width: width ?? "100%",
365
367
  height,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/funnel.ts","../src/FunnelChart.tsx"],"names":["stage"],"mappings":"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","file":"FunnelChart.js","sourcesContent":["// The maths of FunnelChart: bar widths, conversion rates and the flow between\n// two bars. No DOM and no colors, so it runs in plain Node.\n\nexport type FunnelStage = { label: string; value: number };\n\nexport type FunnelRow = {\n label: string;\n /** The value drawn: a negative or non-finite one counts as 0. */\n value: number;\n /** Width of the bar, 0 to 1, against the largest stage. */\n share: number;\n /** Rate from the previous stage; 1 for the first. `null` when the previous stage is 0. */\n fromPrevious: number | null;\n /** Rate from the first stage; `null` when the first stage is 0. */\n fromFirst: number | null;\n};\n\nconst clean = (value: number) => (Number.isFinite(value) && value > 0 ? value : 0);\n\n/** Whether there is nothing to draw: no stage, or only stages at 0. */\nexport const isEmptyFunnel = (stages: readonly FunnelStage[]): boolean =>\n stages.every((stage) => clean(stage.value) === 0);\n\n/**\n * One row per stage, in the order given. A bar is as wide as its value against\n * the largest stage, which is the first one unless a stage grows (a merge of\n * sources, say): that stage then has a rate over 1 and the widest bar, so no\n * bar ever leaves the box. A rate over a stage at 0 is `null`.\n */\nexport const funnelRows = (stages: readonly FunnelStage[]): FunnelRow[] => {\n const values = stages.map((stage) => clean(stage.value));\n const widest = Math.max(0, ...values);\n const rate = (value: number, over: number) => (over > 0 ? value / over : null);\n return stages.map((stage, i) => ({\n label: stage.label,\n value: values[i],\n share: widest > 0 ? values[i] / widest : 0,\n fromPrevious: i === 0 ? 1 : rate(values[i], values[i - 1]),\n fromFirst: rate(values[i], values[0]),\n }));\n};\n\n/** A rate as a percentage with one decimal (`0.654` is `65.4%`); `–` when there is none. */\nexport const formatRate = (rate: number | null): string =>\n rate === null ? '–' : `${(rate * 100).toFixed(1)}%`;\n\n// What the last bar keeps of the accent: still 1.5:1 against the card in the dark themes (see SEQUENTIAL).\nconst FADE_FLOOR = 42;\n\n/** How much of the accent stage `index` of `count` keeps, in percent: 100 for the first, 42 for the last. */\nexport const fadeShare = (index: number, count: number): number =>\n count <= 1 ? 100 : Math.round((100 - ((100 - FADE_FLOOR) * index) / (count - 1)) * 10) / 10;\n\n/**\n * The `points` of the flow from a bar to the next, in a box 100 wide and 1\n * tall, stretched to the gap between the two bars: the top edge is the upper\n * bar's width, the bottom edge the lower bar's, both centred.\n */\nexport const flowPoints = (upper: number, lower: number): string => {\n const edge = (share: number) => [(100 - 100 * share) / 2, (100 + 100 * share) / 2];\n const [topLeft, topRight] = edge(upper);\n const [bottomLeft, bottomRight] = edge(lower);\n return `${topLeft},0 ${topRight},0 ${bottomRight},1 ${bottomLeft},1`;\n};\n","import {\n useCallback,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { TooltipCard } from './core/ChartTooltip';\nimport { MUTED, PANEL, SERIES, TEXT, TRACK, seriesColor } from './core/colors';\nimport { ChartEmpty } from './core/frame';\nimport { useElementWidth } from './core/useElementWidth';\nimport {\n fadeShare,\n flowPoints,\n formatRate,\n funnelRows,\n isEmptyFunnel,\n type FunnelRow,\n} from './funnel';\nimport { TEXT_SIZE } from './core/axes';\nimport type { ChartEmptyProps, ChartProps } from './types';\n\nexport type { FunnelStage } from './funnel';\n\nexport type FunnelChartProps = Omit<ChartProps, 'children'> &\n ChartEmptyProps & {\n /**\n * The stages, drawn top to bottom in the order given: the wrapper does not\n * sort them. Each bar is as wide as its value against the largest stage,\n * which is the first one unless a stage grows.\n */\n stages: { label: string; value: number }[];\n /**\n * One color per stage, cycling when there are fewer. Default: the accent,\n * fading from the first stage to the last.\n */\n colors?: string[];\n /** Format the values, in the labels and the tooltip. Percentages always have one decimal. */\n formatValue?: (value: number) => string;\n /**\n * Show the conversion: a chip with the rate from the previous stage, the\n * rate from the first stage beside it, and both in the tooltip. Off, only\n * the values show. Default true.\n */\n showConversion?: boolean;\n };\n\n// A bar is 30px at the normal size and 24px on a narrow box; the flow takes\n// the rest of the stage's row, at least this much.\nconst BAR = '1.875rem';\nconst BAR_NARROW = '1.5rem';\nconst FLOW = '1.75rem';\nconst FLOW_NARROW = '1.25rem';\nconst FLOW_OPACITY = 0.14;\n// Below this width the names and values go above their bar.\nconst NARROW = 520;\n// Room the tooltip card leaves the bar.\nconst TIP_GAP = 6;\n\ntype Tip = {\n index: number;\n x: number;\n top: number;\n bottom: number;\n align: 'start' | 'center' | 'end';\n below: boolean;\n};\n\nconst textStyle: CSSProperties = {\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nexport const FunnelChart = ({\n stages,\n colors,\n formatValue = String,\n showConversion = true,\n emptyMessage = 'No data',\n ariaLabel,\n height,\n width,\n}: FunnelChartProps) => {\n const name = ariaLabel ?? `Funnel: ${stages.map((stage) => stage.label).join(', ')}`;\n const frame = useRef<HTMLDivElement | null>(null);\n const list = useRef<HTMLUListElement>(null);\n const { ref: measure, width: measured } = useElementWidth();\n // The box is read for the tooltip's position and measured for the layout.\n const frameRef = useCallback(\n (element: HTMLDivElement | null) => {\n frame.current = element;\n measure(element);\n },\n [measure]\n );\n const [roving, setRoving] = useState(0);\n const [hover, setHover] = useState<Tip>();\n const [focus, setFocus] = useState<Tip>();\n if (isEmptyFunnel(stages)) {\n return (\n <ChartEmpty message={emptyMessage} ariaLabel={name} height={height ?? 160} width={width} />\n );\n }\n\n const rows = funnelRows(stages);\n const first = rows[0];\n const narrow = measured !== undefined && measured < NARROW;\n const bar = narrow ? BAR_NARROW : BAR;\n const flow = narrow ? FLOW_NARROW : FLOW;\n const tabbable = Math.min(roving, rows.length - 1);\n const tip = hover ?? focus;\n\n const color = (i: number) =>\n colors?.length\n ? seriesColor(i, colors)\n : `color-mix(in oklab, ${SERIES[0]} ${fadeShare(i, rows.length)}%, ${PANEL})`;\n const flowColor = (i: number) => (colors?.length ? seriesColor(i, colors) : SERIES[0]);\n\n const chip = (row: FunnelRow, i: number) => (i === 0 ? '100%' : formatRate(row.fromPrevious));\n const note = (row: FunnelRow) => `${formatRate(row.fromFirst)} of ${first.label}`;\n const nameOf = (row: FunnelRow, i: number) =>\n [\n `${row.label}: ${formatValue(row.value)}`,\n ...(showConversion && i > 0\n ? [`${formatRate(row.fromPrevious)} of previous stage`, note(row)]\n : []),\n ].join(', ');\n\n const tipFor = (index: number, item: HTMLElement): Tip => {\n const host = (frame.current as HTMLElement).getBoundingClientRect();\n const box = (item.querySelector('[data-funnel-bar]') ?? item).getBoundingClientRect();\n const middle = box.left - host.left + box.width / 2;\n // The card hangs from the side of the bar nearest the middle of the chart,\n // so it never leaves the box.\n const align =\n middle < host.width / 3 ? 'start' : middle > (2 * host.width) / 3 ? 'end' : 'center';\n return {\n index,\n x: { start: box.left - host.left, center: middle, end: box.right - host.left }[align],\n top: box.top - host.top,\n bottom: box.bottom - host.top,\n align,\n below: box.top - host.top < 110,\n };\n };\n\n const move = (event: KeyboardEvent<HTMLUListElement>) => {\n const origin = (event.target as HTMLElement).closest<HTMLElement>('[data-funnel-stage]');\n if (!origin) return;\n const index = Number(origin.dataset.index);\n const targets: Record<string, number> = {\n ArrowDown: index + 1,\n ArrowRight: index + 1,\n ArrowUp: index - 1,\n ArrowLeft: index - 1,\n Home: 0,\n End: rows.length - 1,\n };\n const target = targets[event.key];\n if (target === undefined) return;\n event.preventDefault();\n list.current\n ?.querySelector<HTMLElement>(\n `[data-index=\"${Math.min(Math.max(target, 0), rows.length - 1)}\"]`\n )\n ?.focus();\n };\n\n const focusIn = (index: number, event: FocusEvent<HTMLElement>) => {\n setRoving(index);\n // A click focuses a stage too, and would leave its tooltip up: only the keyboard shows it.\n if (event.currentTarget.matches(':focus-visible')) {\n setFocus(tipFor(index, event.currentTarget));\n }\n };\n const focusOut = (event: FocusEvent<HTMLUListElement>) => {\n if (!event.currentTarget.contains(event.relatedTarget)) setFocus(undefined);\n };\n\n const tipRow = tip && rows[tip.index];\n const tipStyle: CSSProperties | undefined = tip && {\n position: 'absolute',\n left: tip.x,\n top: tip.below ? tip.bottom + TIP_GAP : tip.top - TIP_GAP,\n transform: `translate(${{ start: '0', center: '-50%', end: '-100%' }[tip.align]}, ${tip.below ? '0' : '-100%'})`,\n zIndex: 10,\n pointerEvents: 'none',\n };\n\n const label = (row: FunnelRow, extra?: CSSProperties) => (\n <span\n data-funnel-name=\"\"\n style={{ fontSize: TEXT_SIZE, color: MUTED, minWidth: 0, ...textStyle, ...extra }}\n >\n {row.label}\n </span>\n );\n const figures = (row: FunnelRow, i: number) => (\n <>\n <span\n data-funnel-value=\"\"\n style={{\n fontSize: 'var(--text-sm, 0.875rem)',\n fontWeight: 600,\n color: TEXT,\n fontVariantNumeric: 'tabular-nums',\n whiteSpace: 'nowrap',\n }}\n >\n {formatValue(row.value)}\n </span>\n {showConversion && (\n <span\n data-funnel-chip=\"\"\n style={{\n fontSize: TEXT_SIZE,\n fontWeight: 600,\n color: TEXT,\n background: TRACK,\n padding: '0.0625rem 0.4375rem',\n borderRadius: 999,\n fontVariantNumeric: 'tabular-nums',\n whiteSpace: 'nowrap',\n }}\n >\n {chip(row, i)}\n </span>\n )}\n {showConversion && i > 0 && !narrow && (\n <span\n data-funnel-note=\"\"\n style={{ fontSize: TEXT_SIZE, color: MUTED, minWidth: 0, ...textStyle }}\n >\n {note(row)}\n </span>\n )}\n </>\n );\n\n const flowSvg = (i: number, style: CSSProperties) => (\n <svg\n aria-hidden\n data-funnel-flow=\"\"\n viewBox=\"0 0 100 1\"\n preserveAspectRatio=\"none\"\n style={{ display: 'block', ...style }}\n >\n <polygon\n points={flowPoints(rows[i - 1].share, rows[i].share)}\n fill={flowColor(i - 1)}\n fillOpacity={FLOW_OPACITY}\n />\n </svg>\n );\n\n const barBox = (i: number): CSSProperties => ({\n width: `${rows[i].share * 100}%`,\n minWidth: rows[i].value > 0 ? 3 : 0,\n height: bar,\n borderRadius: 6,\n background: color(i),\n // The same ring for the pointer and the keyboard, in the text color: an\n // accent ring would vanish on a bar that is the accent.\n outline: tip?.index === i ? `2px solid ${TEXT}` : 'none',\n outlineOffset: 1,\n });\n\n const stage = (row: FunnelRow, i: number): ReactNode => {\n const common = {\n 'data-funnel-stage': '',\n 'data-index': i,\n tabIndex: i === tabbable ? 0 : -1,\n 'aria-label': nameOf(row, i),\n onFocus: (event: FocusEvent<HTMLElement>) => focusIn(i, event),\n onMouseEnter: (event: MouseEvent<HTMLElement>) => setHover(tipFor(i, event.currentTarget)),\n onMouseLeave: () => setHover(undefined),\n };\n if (narrow) {\n return (\n <li\n key={i}\n {...common}\n style={{\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr) auto',\n gridTemplateRows: '1fr auto',\n columnGap: '0.75rem',\n outline: 'none',\n }}\n >\n {i > 0 && flowSvg(i, { gridRow: 1, gridColumn: '1 / -1', width: '100%', height: '100%' })}\n <span\n aria-hidden\n style={{\n gridRow: 1,\n gridColumn: 1,\n alignSelf: 'end',\n display: 'flex',\n minWidth: 0,\n padding: `${i > 0 ? flow : '0'} 0 0.25rem`,\n }}\n >\n {label(row, { color: TEXT })}\n </span>\n <span\n aria-hidden\n style={{\n gridRow: 1,\n gridColumn: 2,\n alignSelf: 'end',\n display: 'flex',\n alignItems: 'baseline',\n gap: '0.5rem',\n padding: `${i > 0 ? flow : '0'} 0 0.25rem`,\n }}\n >\n {figures(row, i)}\n </span>\n <div\n aria-hidden\n style={{ gridRow: 2, gridColumn: '1 / -1', display: 'flex', justifyContent: 'center' }}\n >\n <div data-funnel-bar=\"\" style={barBox(i)} />\n </div>\n </li>\n );\n }\n return (\n <li\n key={i}\n {...common}\n style={{\n display: 'grid',\n gridColumn: '1 / -1',\n gridTemplateColumns: 'subgrid',\n outline: 'none',\n }}\n >\n <span\n aria-hidden\n style={{\n gridColumn: 1,\n alignSelf: 'end',\n height: bar,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n minWidth: 0,\n }}\n >\n {label(row)}\n </span>\n <div\n aria-hidden\n style={{\n gridColumn: 2,\n position: 'relative',\n display: 'flex',\n alignItems: 'flex-end',\n justifyContent: 'center',\n minHeight: i === 0 ? bar : `calc(${bar} + ${flow})`,\n minWidth: 0,\n }}\n >\n {i > 0 &&\n flowSvg(i, {\n position: 'absolute',\n left: 0,\n top: 0,\n width: '100%',\n height: `calc(100% - ${bar})`,\n })}\n <div data-funnel-bar=\"\" style={barBox(i)} />\n </div>\n <span\n aria-hidden\n style={{\n gridColumn: 3,\n alignSelf: 'end',\n height: bar,\n display: 'flex',\n alignItems: 'center',\n gap: '0.5rem',\n minWidth: 0,\n }}\n >\n {figures(row, i)}\n </span>\n </li>\n );\n };\n\n return (\n <div\n ref={frameRef}\n style={{\n position: 'relative',\n width: width ?? '100%',\n height,\n maxWidth: '100%',\n minWidth: 0,\n }}\n >\n <ul\n ref={list}\n role=\"list\"\n aria-label={name}\n onKeyDown={move}\n onBlur={focusOut}\n style={{\n display: 'grid',\n gridTemplateColumns: narrow\n ? 'minmax(0, 1fr)'\n : 'fit-content(30%) minmax(0, 1fr) fit-content(45%)',\n gridTemplateRows: `auto repeat(${rows.length - 1}, minmax(min-content, 1fr))`,\n columnGap: '1rem',\n width: '100%',\n height: height === undefined ? undefined : '100%',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n }}\n >\n {rows.map(stage)}\n </ul>\n {tip && tipRow && tipStyle && (\n <div data-funnel-tooltip=\"\" aria-hidden style={tipStyle}>\n <TooltipCard\n title={tipRow.label}\n rows={[\n { name: 'Value', value: formatValue(tipRow.value), color: color(tip.index) },\n ...(showConversion && tip.index > 0\n ? [\n {\n name: 'Of previous stage',\n value: formatRate(tipRow.fromPrevious),\n color: color(tip.index),\n },\n {\n name: `Of ${first.label}`,\n value: formatRate(tipRow.fromFirst),\n color: color(tip.index),\n },\n ]\n : []),\n ]}\n />\n </div>\n )}\n </div>\n );\n};\n"]}
1
+ {"version":3,"sources":["../src/funnel.ts","../src/FunnelChart.tsx"],"names":["stage"],"mappings":"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","file":"FunnelChart.js","sourcesContent":["// The maths of FunnelChart: bar widths, conversion rates and the flow between\n// two bars. No DOM and no colors, so it runs in plain Node.\n\nexport type FunnelStage = { label: string; value: number };\n\nexport type FunnelRow = {\n label: string;\n /** The value drawn: a negative or non-finite one counts as 0. */\n value: number;\n /** Width of the bar, 0 to 1, against the largest stage. */\n share: number;\n /** Rate from the previous stage; 1 for the first. `null` when the previous stage is 0. */\n fromPrevious: number | null;\n /** Rate from the first stage; `null` when the first stage is 0. */\n fromFirst: number | null;\n};\n\nconst clean = (value: number) => (Number.isFinite(value) && value > 0 ? value : 0);\n\n/** Whether there is nothing to draw: no stage, or only stages at 0. */\nexport const isEmptyFunnel = (stages: readonly FunnelStage[]): boolean =>\n stages.every((stage) => clean(stage.value) === 0);\n\n/**\n * One row per stage, in the order given. A bar is as wide as its value against\n * the largest stage, which is the first one unless a stage grows (a merge of\n * sources, say): that stage then has a rate over 1 and the widest bar, so no\n * bar ever leaves the box. A rate over a stage at 0 is `null`.\n */\nexport const funnelRows = (stages: readonly FunnelStage[]): FunnelRow[] => {\n const values = stages.map((stage) => clean(stage.value));\n const widest = Math.max(0, ...values);\n const rate = (value: number, over: number) => (over > 0 ? value / over : null);\n return stages.map((stage, i) => ({\n label: stage.label,\n value: values[i],\n share: widest > 0 ? values[i] / widest : 0,\n fromPrevious: i === 0 ? 1 : rate(values[i], values[i - 1]),\n fromFirst: rate(values[i], values[0]),\n }));\n};\n\n/** A rate as a percentage with one decimal (`0.654` is `65.4%`); `–` when there is none. */\nexport const formatRate = (rate: number | null): string =>\n rate === null ? '–' : `${(rate * 100).toFixed(1)}%`;\n\n// What the last bar keeps of the accent: still 1.5:1 against the card in the dark themes (see SEQUENTIAL).\nconst FADE_FLOOR = 42;\n\n/** How much of the accent stage `index` of `count` keeps, in percent: 100 for the first, 42 for the last. */\nexport const fadeShare = (index: number, count: number): number =>\n count <= 1 ? 100 : Math.round((100 - ((100 - FADE_FLOOR) * index) / (count - 1)) * 10) / 10;\n\n/**\n * The `points` of the flow from a bar to the next, in a box 100 wide and 1\n * tall, stretched to the gap between the two bars: the top edge is the upper\n * bar's width, the bottom edge the lower bar's, both centred.\n */\nexport const flowPoints = (upper: number, lower: number): string => {\n const edge = (share: number) => [(100 - 100 * share) / 2, (100 + 100 * share) / 2];\n const [topLeft, topRight] = edge(upper);\n const [bottomLeft, bottomRight] = edge(lower);\n return `${topLeft},0 ${topRight},0 ${bottomRight},1 ${bottomLeft},1`;\n};\n","import {\n useCallback,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { TooltipCard } from './core/ChartTooltip';\nimport { MUTED, PANEL, SERIES, TEXT, TRACK, seriesColor } from './core/colors';\nimport { ChartEmpty } from './core/frame';\nimport { useElementWidth } from './core/useElementWidth';\nimport {\n fadeShare,\n flowPoints,\n formatRate,\n funnelRows,\n isEmptyFunnel,\n type FunnelRow,\n} from './funnel';\nimport { TEXT_SIZE } from './core/axes';\nimport type { ChartEmptyProps, ChartProps } from './types';\n\nexport type { FunnelStage } from './funnel';\n\nexport type FunnelChartProps = Omit<ChartProps, 'children'> &\n ChartEmptyProps & {\n /**\n * The stages, drawn top to bottom in the order given: the wrapper does not\n * sort them. Each bar is as wide as its value against the largest stage,\n * which is the first one unless a stage grows.\n */\n stages: { label: string; value: number }[];\n /**\n * One color per stage, cycling when there are fewer. Default: the accent,\n * fading from the first stage to the last.\n */\n colors?: string[];\n /** Format the values, in the labels and the tooltip. Percentages always have one decimal. */\n formatValue?: (value: number) => string;\n /**\n * Show the conversion: a chip with the rate from the previous stage, the\n * rate from the first stage beside it, and both in the tooltip. Off, only\n * the values show. Default true.\n */\n showConversion?: boolean;\n };\n\n// A bar is 30px at the normal size and 24px on a narrow box; the flow takes\n// the rest of the stage's row, at least this much.\nconst BAR = '1.875rem';\nconst BAR_NARROW = '1.5rem';\nconst FLOW = '1.75rem';\nconst FLOW_NARROW = '1.25rem';\nconst FLOW_OPACITY = 0.14;\n// Below this width the names and values go above their bar.\nconst NARROW = 520;\n// Room the tooltip card leaves the bar.\nconst TIP_GAP = 6;\n\ntype Tip = {\n index: number;\n x: number;\n top: number;\n bottom: number;\n align: 'start' | 'center' | 'end';\n below: boolean;\n};\n\nconst textStyle: CSSProperties = {\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nexport const FunnelChart = ({\n stages,\n colors,\n formatValue = String,\n showConversion = true,\n emptyMessage = 'No data',\n ariaLabel,\n height,\n width,\n}: FunnelChartProps) => {\n const name = ariaLabel ?? `Funnel: ${stages.map((stage) => stage.label).join(', ')}`;\n const frame = useRef<HTMLDivElement | null>(null);\n const list = useRef<HTMLUListElement>(null);\n const { ref: measure, width: measured } = useElementWidth();\n // The box is read for the tooltip's position and measured for the layout.\n const frameRef = useCallback(\n (element: HTMLDivElement | null) => {\n frame.current = element;\n measure(element);\n },\n [measure]\n );\n const [roving, setRoving] = useState(0);\n const [hover, setHover] = useState<Tip>();\n const [focus, setFocus] = useState<Tip>();\n if (isEmptyFunnel(stages)) {\n return (\n <ChartEmpty message={emptyMessage} ariaLabel={name} height={height ?? 160} width={width} />\n );\n }\n\n const rows = funnelRows(stages);\n const first = rows[0];\n const narrow = measured !== undefined && measured < NARROW;\n const bar = narrow ? BAR_NARROW : BAR;\n const flow = narrow ? FLOW_NARROW : FLOW;\n const tabbable = Math.min(roving, rows.length - 1);\n const tip = hover ?? focus;\n\n const color = (i: number) =>\n colors?.length\n ? seriesColor(i, colors)\n : `color-mix(in oklab, ${SERIES[0]} ${fadeShare(i, rows.length)}%, ${PANEL})`;\n const flowColor = (i: number) => (colors?.length ? seriesColor(i, colors) : SERIES[0]);\n\n const chip = (row: FunnelRow, i: number) => (i === 0 ? '100%' : formatRate(row.fromPrevious));\n const note = (row: FunnelRow) => `${formatRate(row.fromFirst)} of ${first.label}`;\n const nameOf = (row: FunnelRow, i: number) =>\n [\n `${row.label}: ${formatValue(row.value)}`,\n ...(showConversion && i > 0\n ? [`${formatRate(row.fromPrevious)} of previous stage`, note(row)]\n : []),\n ].join(', ');\n\n const tipFor = (index: number, item: HTMLElement): Tip => {\n const host = (frame.current as HTMLElement).getBoundingClientRect();\n const box = (item.querySelector('[data-funnel-bar]') ?? item).getBoundingClientRect();\n const middle = box.left - host.left + box.width / 2;\n // The card hangs from the side of the bar nearest the middle of the chart,\n // so it never leaves the box.\n const align =\n middle < host.width / 3 ? 'start' : middle > (2 * host.width) / 3 ? 'end' : 'center';\n return {\n index,\n x: { start: box.left - host.left, center: middle, end: box.right - host.left }[align],\n top: box.top - host.top,\n bottom: box.bottom - host.top,\n align,\n below: box.top - host.top < 110,\n };\n };\n\n const move = (event: KeyboardEvent<HTMLUListElement>) => {\n const origin = (event.target as HTMLElement).closest<HTMLElement>('[data-funnel-stage]');\n if (!origin) return;\n const index = Number(origin.dataset.index);\n const targets: Record<string, number> = {\n ArrowDown: index + 1,\n ArrowRight: index + 1,\n ArrowUp: index - 1,\n ArrowLeft: index - 1,\n Home: 0,\n End: rows.length - 1,\n };\n const target = targets[event.key];\n if (target === undefined) return;\n event.preventDefault();\n list.current\n ?.querySelector<HTMLElement>(\n `[data-index=\"${Math.min(Math.max(target, 0), rows.length - 1)}\"]`\n )\n ?.focus();\n };\n\n const focusIn = (index: number, event: FocusEvent<HTMLElement>) => {\n setRoving(index);\n // A click focuses a stage too, and would leave its tooltip up: only the keyboard shows it.\n if (event.currentTarget.matches(':focus-visible')) {\n setFocus(tipFor(index, event.currentTarget));\n }\n };\n const focusOut = (event: FocusEvent<HTMLUListElement>) => {\n if (!event.currentTarget.contains(event.relatedTarget)) setFocus(undefined);\n };\n\n const tipRow = tip && rows[tip.index];\n const tipStyle: CSSProperties | undefined = tip && {\n position: 'absolute',\n left: tip.x,\n top: tip.below ? tip.bottom + TIP_GAP : tip.top - TIP_GAP,\n transform: `translate(${{ start: '0', center: '-50%', end: '-100%' }[tip.align]}, ${tip.below ? '0' : '-100%'})`,\n zIndex: 10,\n pointerEvents: 'none',\n };\n\n const label = (row: FunnelRow, extra?: CSSProperties) => (\n <span\n data-funnel-name=\"\"\n style={{ fontSize: TEXT_SIZE, color: MUTED, minWidth: 0, ...textStyle, ...extra }}\n >\n {row.label}\n </span>\n );\n const figures = (row: FunnelRow, i: number) => (\n <>\n <span\n data-funnel-value=\"\"\n style={{\n fontSize: 'var(--text-sm, 0.875rem)',\n fontWeight: 600,\n color: TEXT,\n fontVariantNumeric: 'tabular-nums',\n whiteSpace: 'nowrap',\n }}\n >\n {formatValue(row.value)}\n </span>\n {showConversion && (\n <span\n data-funnel-chip=\"\"\n style={{\n fontSize: TEXT_SIZE,\n fontWeight: 600,\n color: TEXT,\n background: TRACK,\n padding: '0.0625rem 0.4375rem',\n borderRadius: 999,\n fontVariantNumeric: 'tabular-nums',\n whiteSpace: 'nowrap',\n }}\n >\n {chip(row, i)}\n </span>\n )}\n {showConversion && i > 0 && !narrow && (\n <span\n data-funnel-note=\"\"\n style={{ fontSize: TEXT_SIZE, color: MUTED, minWidth: 0, ...textStyle }}\n >\n {note(row)}\n </span>\n )}\n </>\n );\n\n const flowSvg = (i: number, style: CSSProperties) => (\n <svg\n aria-hidden\n data-funnel-flow=\"\"\n viewBox=\"0 0 100 1\"\n preserveAspectRatio=\"none\"\n style={{ display: 'block', ...style }}\n >\n <polygon\n points={flowPoints(rows[i - 1].share, rows[i].share)}\n fill={flowColor(i - 1)}\n fillOpacity={FLOW_OPACITY}\n />\n </svg>\n );\n\n const barBox = (i: number): CSSProperties => ({\n width: `${rows[i].share * 100}%`,\n minWidth: rows[i].value > 0 ? 3 : 0,\n height: bar,\n borderRadius: 6,\n background: color(i),\n // The same ring for the pointer and the keyboard, in the text color: an\n // accent ring would vanish on a bar that is the accent.\n outline: tip?.index === i ? `2px solid ${TEXT}` : 'none',\n outlineOffset: 1,\n });\n\n const stage = (row: FunnelRow, i: number): ReactNode => {\n const common = {\n 'data-funnel-stage': '',\n 'data-index': i,\n tabIndex: i === tabbable ? 0 : -1,\n 'aria-label': nameOf(row, i),\n onFocus: (event: FocusEvent<HTMLElement>) => focusIn(i, event),\n onMouseEnter: (event: MouseEvent<HTMLElement>) => setHover(tipFor(i, event.currentTarget)),\n onMouseLeave: () => setHover(undefined),\n };\n if (narrow) {\n return (\n <li\n key={i}\n {...common}\n style={{\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr) auto',\n gridTemplateRows: '1fr auto',\n columnGap: '0.75rem',\n outline: 'none',\n }}\n >\n {i > 0 && flowSvg(i, { gridRow: 1, gridColumn: '1 / -1', width: '100%', height: '100%' })}\n <span\n aria-hidden\n style={{\n gridRow: 1,\n gridColumn: 1,\n alignSelf: 'end',\n display: 'flex',\n minWidth: 0,\n padding: `${i > 0 ? flow : '0'} 0 0.25rem`,\n }}\n >\n {label(row, { color: TEXT })}\n </span>\n <span\n aria-hidden\n style={{\n gridRow: 1,\n gridColumn: 2,\n alignSelf: 'end',\n display: 'flex',\n alignItems: 'baseline',\n gap: '0.5rem',\n padding: `${i > 0 ? flow : '0'} 0 0.25rem`,\n }}\n >\n {figures(row, i)}\n </span>\n <div\n aria-hidden\n style={{ gridRow: 2, gridColumn: '1 / -1', display: 'flex', justifyContent: 'center' }}\n >\n <div data-funnel-bar=\"\" style={barBox(i)} />\n </div>\n </li>\n );\n }\n return (\n <li\n key={i}\n {...common}\n style={{\n display: 'grid',\n gridColumn: '1 / -1',\n gridTemplateColumns: 'subgrid',\n outline: 'none',\n }}\n >\n <span\n aria-hidden\n style={{\n gridColumn: 1,\n alignSelf: 'end',\n height: bar,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n minWidth: 0,\n }}\n >\n {label(row)}\n </span>\n <div\n aria-hidden\n style={{\n gridColumn: 2,\n position: 'relative',\n display: 'flex',\n alignItems: 'flex-end',\n justifyContent: 'center',\n minHeight: i === 0 ? bar : `calc(${bar} + ${flow})`,\n minWidth: 0,\n }}\n >\n {i > 0 &&\n flowSvg(i, {\n position: 'absolute',\n left: 0,\n top: 0,\n width: '100%',\n height: `calc(100% - ${bar})`,\n })}\n <div data-funnel-bar=\"\" style={barBox(i)} />\n </div>\n <span\n aria-hidden\n style={{\n gridColumn: 3,\n alignSelf: 'end',\n height: bar,\n display: 'flex',\n alignItems: 'center',\n gap: '0.5rem',\n minWidth: 0,\n }}\n >\n {figures(row, i)}\n </span>\n </li>\n );\n };\n\n return (\n <div\n ref={frameRef}\n style={{\n // The stages keep their order under `dir=\"rtl\"`, as a chart's axis does.\n direction: 'ltr',\n position: 'relative',\n width: width ?? '100%',\n height,\n maxWidth: '100%',\n minWidth: 0,\n }}\n >\n <ul\n ref={list}\n role=\"list\"\n aria-label={name}\n onKeyDown={move}\n onBlur={focusOut}\n style={{\n display: 'grid',\n gridTemplateColumns: narrow\n ? 'minmax(0, 1fr)'\n : 'fit-content(30%) minmax(0, 1fr) fit-content(45%)',\n gridTemplateRows: `auto repeat(${rows.length - 1}, minmax(min-content, 1fr))`,\n columnGap: '1rem',\n width: '100%',\n height: height === undefined ? undefined : '100%',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n }}\n >\n {rows.map(stage)}\n </ul>\n {tip && tipRow && tipStyle && (\n <div data-funnel-tooltip=\"\" aria-hidden style={tipStyle}>\n <TooltipCard\n title={tipRow.label}\n rows={[\n { name: 'Value', value: formatValue(tipRow.value), color: color(tip.index) },\n ...(showConversion && tip.index > 0\n ? [\n {\n name: 'Of previous stage',\n value: formatRate(tipRow.fromPrevious),\n color: color(tip.index),\n },\n {\n name: `Of ${first.label}`,\n value: formatRate(tipRow.fromFirst),\n color: color(tip.index),\n },\n ]\n : []),\n ]}\n />\n </div>\n )}\n </div>\n );\n};\n"]}
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var chunkMN4CDVOS_cjs = require('./chunk-MN4CDVOS.cjs');
4
+ var chunkCMBNNLIV_cjs = require('./chunk-CMBNNLIV.cjs');
5
5
  var chunkAAYHG7YK_cjs = require('./chunk-AAYHG7YK.cjs');
6
6
  require('./chunk-X3YZP5WF.cjs');
7
7
  require('./chunk-TYQWZR3S.cjs');
8
8
  var chunkK3R7JGOW_cjs = require('./chunk-K3R7JGOW.cjs');
9
- require('./chunk-6NRRABPF.cjs');
9
+ require('./chunk-SR2ESX3R.cjs');
10
10
  var chunkLT3UC65O_cjs = require('./chunk-LT3UC65O.cjs');
11
11
  var react = require('react');
12
12
  var jsxRuntime = require('react/jsx-runtime');
@@ -25,7 +25,7 @@ var GradientRadial = ({
25
25
  }) => {
26
26
  const id = chunkK3R7JGOW_cjs.safeId(react.useId());
27
27
  return /* @__PURE__ */ jsxRuntime.jsx(
28
- chunkMN4CDVOS_cjs.RadialGauge,
28
+ chunkCMBNNLIV_cjs.RadialGauge,
29
29
  {
30
30
  value,
31
31
  label,