@epilot/spark-ui 0.22.1 → 0.23.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.
- package/dist/components/attachment.d.cts +1 -1
- package/dist/components/attachment.d.ts +1 -1
- package/dist/components/badge.cjs +1 -1
- package/dist/components/badge.cjs.map +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/badge.js.map +1 -1
- package/dist/components/bubble.d.cts +1 -1
- package/dist/components/bubble.d.ts +1 -1
- package/dist/components/button.cjs.map +1 -1
- package/dist/components/button.js.map +1 -1
- package/dist/components/calendar.cjs +4 -3
- package/dist/components/calendar.cjs.map +1 -1
- package/dist/components/calendar.js +4 -3
- package/dist/components/calendar.js.map +1 -1
- package/dist/components/callout.d.cts +1 -1
- package/dist/components/callout.d.ts +1 -1
- package/dist/components/checkbox.cjs.map +1 -1
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/circular-gauge.cjs +1 -1
- package/dist/components/circular-gauge.cjs.map +1 -1
- package/dist/components/circular-gauge.js +1 -1
- package/dist/components/circular-gauge.js.map +1 -1
- package/dist/components/drawer.cjs +2 -2
- package/dist/components/drawer.cjs.map +1 -1
- package/dist/components/drawer.js +2 -2
- package/dist/components/drawer.js.map +1 -1
- package/dist/components/energy-breakdown.cjs +1 -1
- package/dist/components/energy-breakdown.cjs.map +1 -1
- package/dist/components/energy-breakdown.js +1 -1
- package/dist/components/energy-breakdown.js.map +1 -1
- package/dist/components/line-chart.cjs +1 -1
- package/dist/components/line-chart.cjs.map +1 -1
- package/dist/components/line-chart.js +2 -2
- package/dist/components/line-chart.js.map +1 -1
- package/dist/components/pill.cjs +2 -2
- package/dist/components/pill.cjs.map +1 -1
- package/dist/components/pill.js +2 -2
- package/dist/components/pill.js.map +1 -1
- package/dist/components/progress.cjs +1 -1
- package/dist/components/progress.cjs.map +1 -1
- package/dist/components/progress.js +1 -1
- package/dist/components/progress.js.map +1 -1
- package/dist/components/segmented-control.cjs +4 -4
- package/dist/components/segmented-control.cjs.map +1 -1
- package/dist/components/segmented-control.d.cts +2 -16
- package/dist/components/segmented-control.d.ts +2 -16
- package/dist/components/segmented-control.js +5 -5
- package/dist/components/segmented-control.js.map +1 -1
- package/dist/components/switch.cjs +1 -1
- package/dist/components/switch.cjs.map +1 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/switch.js.map +1 -1
- package/dist/components/tabs.cjs +2 -2
- package/dist/components/tabs.cjs.map +1 -1
- package/dist/components/tabs.d.cts +1 -8
- package/dist/components/tabs.d.ts +1 -8
- package/dist/components/tabs.js +2 -2
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/toggle-group.cjs +40 -0
- package/dist/components/toggle-group.cjs.map +1 -0
- package/dist/components/toggle-group.d.cts +28 -0
- package/dist/components/toggle-group.d.ts +28 -0
- package/dist/components/toggle-group.js +37 -0
- package/dist/components/toggle-group.js.map +1 -0
- package/dist/components/toggle.cjs +69 -0
- package/dist/components/toggle.cjs.map +1 -0
- package/dist/components/toggle.d.cts +23 -0
- package/dist/components/toggle.d.ts +23 -0
- package/dist/components/toggle.js +66 -0
- package/dist/components/toggle.js.map +1 -0
- package/dist/components/tooltip.cjs +1 -1
- package/dist/components/tooltip.cjs.map +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/components/tooltip.js.map +1 -1
- package/dist/lib/generate-radix-colors.cjs +1 -1
- package/dist/lib/generate-radix-colors.cjs.map +1 -1
- package/dist/lib/generate-radix-colors.js +1 -1
- package/dist/lib/generate-radix-colors.js.map +1 -1
- package/dist/lib/toggle-group-context.cjs +12 -0
- package/dist/lib/toggle-group-context.cjs.map +1 -0
- package/dist/lib/toggle-group-context.d.cts +17 -0
- package/dist/lib/toggle-group-context.d.ts +17 -0
- package/dist/lib/toggle-group-context.js +9 -0
- package/dist/lib/toggle-group-context.js.map +1 -0
- package/dist/styles/theme.css +15 -4
- package/dist/utils/format.cjs +1 -0
- package/dist/utils/format.cjs.map +1 -1
- package/dist/utils/format.d.cts +2 -1
- package/dist/utils/format.d.ts +2 -1
- package/dist/utils/format.js +1 -1
- package/dist/utils/format.js.map +1 -1
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ function formatBreakdownCenter(v) {
|
|
|
12
12
|
return twoDecimalFormat.format(v);
|
|
13
13
|
}
|
|
14
14
|
function EnergyBreakdown({ title, subtitle, items, centerSub = "kWh" }) {
|
|
15
|
-
const sortedItems = useMemo(() =>
|
|
15
|
+
const sortedItems = useMemo(() => items.toSorted((a, b) => b.value - a.value), [items]);
|
|
16
16
|
const total = items.reduce((s, i) => s + i.value, 0);
|
|
17
17
|
const centerTotal = items.filter((i) => !i.excludeFromCenterTotal).reduce((s, i) => s + i.value, 0);
|
|
18
18
|
if (total === 0) return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"energy-breakdown.js","names":[],"sources":["../../src/components/energy-breakdown.tsx"],"sourcesContent":["import { useMemo } from 'react'\nimport { NARROW_NBSP } from '../utils/chars'\nimport {\n formatEnergy,\n formatThousands,\n integerFormat,\n oneDecimalFormat,\n twoDecimalFormat,\n} from '../utils/format'\nimport { DonutChart } from './donut-chart'\n\nexport type BreakdownItem = {\n label: string\n value: number\n color: string\n excludeFromCenterTotal?: boolean\n}\n\nexport type EnergyBreakdownProps = {\n title: string\n subtitle: string\n items: BreakdownItem[]\n centerSub?: string\n}\n\nfunction formatBreakdownCenter(v: number) {\n if (v >= 1000) return formatThousands(v)\n if (v >= 100) return integerFormat.format(v)\n if (v >= 10) return oneDecimalFormat.format(v)\n return twoDecimalFormat.format(v)\n}\n\nexport function EnergyBreakdown({\n title,\n subtitle,\n items,\n centerSub = 'kWh',\n}: EnergyBreakdownProps) {\n const sortedItems = useMemo(\n () =>
|
|
1
|
+
{"version":3,"file":"energy-breakdown.js","names":[],"sources":["../../src/components/energy-breakdown.tsx"],"sourcesContent":["import { useMemo } from 'react'\nimport { NARROW_NBSP } from '../utils/chars'\nimport {\n formatEnergy,\n formatThousands,\n integerFormat,\n oneDecimalFormat,\n twoDecimalFormat,\n} from '../utils/format'\nimport { DonutChart } from './donut-chart'\n\nexport type BreakdownItem = {\n label: string\n value: number\n color: string\n excludeFromCenterTotal?: boolean\n}\n\nexport type EnergyBreakdownProps = {\n title: string\n subtitle: string\n items: BreakdownItem[]\n centerSub?: string\n}\n\nfunction formatBreakdownCenter(v: number) {\n if (v >= 1000) return formatThousands(v)\n if (v >= 100) return integerFormat.format(v)\n if (v >= 10) return oneDecimalFormat.format(v)\n return twoDecimalFormat.format(v)\n}\n\nexport function EnergyBreakdown({\n title,\n subtitle,\n items,\n centerSub = 'kWh',\n}: EnergyBreakdownProps) {\n const sortedItems = useMemo(\n () => items.toSorted((a, b) => b.value - a.value),\n [items],\n )\n\n const total = items.reduce((s, i) => s + i.value, 0)\n const centerTotal = items\n .filter((i) => !i.excludeFromCenterTotal)\n .reduce((s, i) => s + i.value, 0)\n\n const dataEmpty = total === 0\n\n if (dataEmpty) return null\n\n return (\n <div className=\"flex flex-col gap-4 pt-3 pb-5\">\n <div className=\"flex flex-col gap-0.5\">\n <span className=\"text-sm font-semibold text-gray\">{title}</span>\n <span className=\"text-xs text-gray-light\">{subtitle}</span>\n </div>\n\n <div className=\"flex items-center gap-6\">\n <DonutChart\n centerLabel={formatBreakdownCenter(centerTotal)}\n centerSub={centerSub}\n segments={sortedItems.map(({ value, color }) => ({ value, color }))}\n />\n\n <div className=\"flex flex-1 flex-col gap-3\">\n {sortedItems.map(({ label, value, color }) => {\n const pct = (value / total) * 100\n return (\n <div key={label} className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"min-w-0 truncate text-sm font-medium text-gray\">\n {label}\n </span>\n <span className=\"flex shrink-0 items-baseline gap-1.5 text-xs tabular-nums\">\n <span className=\"text-gray-light\">\n {formatEnergy(value)}\n </span>\n <span className=\"font-semibold text-gray\">\n {`${integerFormat.format(pct)}${NARROW_NBSP}%`}\n </span>\n </span>\n </div>\n <div className=\"h-1 w-full overflow-hidden rounded-full bg-gray-a4\">\n <div\n className=\"h-full rounded-full\"\n style={{ width: `${pct}%`, background: color }}\n />\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":";;;;;;;AAyBA,SAAS,sBAAsB,GAAW;CACxC,IAAI,KAAK,KAAM,OAAO,gBAAgB,CAAC;CACvC,IAAI,KAAK,KAAK,OAAO,cAAc,OAAO,CAAC;CAC3C,IAAI,KAAK,IAAI,OAAO,iBAAiB,OAAO,CAAC;CAC7C,OAAO,iBAAiB,OAAO,CAAC;AAClC;AAEA,SAAgB,gBAAgB,EAC9B,OACA,UACA,OACA,YAAY,SACW;CACvB,MAAM,cAAc,cACZ,MAAM,UAAU,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,GAChD,CAAC,KAAK,CACR;CAEA,MAAM,QAAQ,MAAM,QAAQ,GAAG,MAAM,IAAI,EAAE,OAAO,CAAC;CACnD,MAAM,cAAc,MACjB,QAAQ,MAAM,CAAC,EAAE,sBAAsB,EACvC,QAAQ,GAAG,MAAM,IAAI,EAAE,OAAO,CAAC;CAIlC,IAFkB,UAAU,GAEb,OAAO;CAEtB,OACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD;IAAM,WAAU;cAAmC;GAAY,CAAA,GAC/D,oBAAC,QAAD;IAAM,WAAU;cAA2B;GAAe,CAAA,CACvD;MAEL,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,YAAD;IACE,aAAa,sBAAsB,WAAW;IACnC;IACX,UAAU,YAAY,KAAK,EAAE,OAAO,aAAa;KAAE;KAAO;IAAM,EAAE;GACnE,CAAA,GAED,oBAAC,OAAD;IAAK,WAAU;cACZ,YAAY,KAAK,EAAE,OAAO,OAAO,YAAY;KAC5C,MAAM,MAAO,QAAQ,QAAS;KAC9B,OACE,qBAAC,OAAD;MAAiB,WAAU;gBAA3B,CACE,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,oBAAC,QAAD;QAAM,WAAU;kBACb;OACG,CAAA,GACN,qBAAC,QAAD;QAAM,WAAU;kBAAhB,CACE,oBAAC,QAAD;SAAM,WAAU;mBACb,aAAa,KAAK;QACf,CAAA,GACN,oBAAC,QAAD;SAAM,WAAU;mBACb,GAAG,cAAc,OAAO,GAAG;QACxB,CAAA,CACF;SACH;UACL,oBAAC,OAAD;OAAK,WAAU;iBACb,oBAAC,OAAD;QACE,WAAU;QACV,OAAO;SAAE,OAAO,GAAG,IAAI;SAAI,YAAY;QAAM;OAC9C,CAAA;MACE,CAAA,CACF;QApBK,KAoBL;IAET,CAAC;GACE,CAAA,CACF;IACF;;AAET"}
|
|
@@ -69,7 +69,7 @@ function nearestDatum(data, date) {
|
|
|
69
69
|
if (!d1) return d0;
|
|
70
70
|
return date.valueOf() - d0.date.valueOf() > d1.date.valueOf() - date.valueOf() ? d1 : d0;
|
|
71
71
|
}
|
|
72
|
-
const defaultFormatDate = (date) => date.toLocaleDateString(
|
|
72
|
+
const defaultFormatDate = (date) => date.toLocaleDateString(require_utils_format.getDocumentLang(), {
|
|
73
73
|
month: "short",
|
|
74
74
|
day: "numeric"
|
|
75
75
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"line-chart.cjs","names":["curveMonotoneX","curveStep","curveStepAfter","curveStepBefore","curveLinear","localeNumberFormat","formatYAxisTick","React","useChartZoom","useReducedMotion","buildYAxisScale","useAnimatedChartDomain","buildTimeTicks","useMeasuredSize","clampTooltipPosition","Group","LinePath","ChartZoomResetButton","ChartTooltipCard"],"sources":["../../src/components/line-chart.tsx"],"sourcesContent":["import {\n curveLinear,\n curveMonotoneX,\n curveStep,\n curveStepAfter,\n curveStepBefore,\n} from '@visx/curve'\nimport { localPoint } from '@visx/event'\nimport { Group } from '@visx/group'\nimport { scaleLinear, scaleTime } from '@visx/scale'\nimport { LinePath } from '@visx/shape'\nimport { useTooltip } from '@visx/tooltip'\nimport { bisector, extent } from '@visx/vendor/d3-array'\nimport * as React from 'react'\nimport { useCallback, useMemo } from 'react'\nimport { useAnimatedChartDomain, useChartZoom } from '../hooks/use-chart-zoom'\nimport { useReducedMotion } from '../hooks/use-reduced-motion'\nimport { buildYAxisScale, formatYAxisTick } from '../utils/chart-scale'\nimport { buildTimeTicks } from '../utils/chart-time-ticks'\nimport type { TimeDomain } from '../utils/chart-zoom'\nimport { cn } from '../utils/cn'\nimport { localeNumberFormat } from '../utils/format'\nimport {\n ChartTooltipCard,\n type ChartTooltipRow,\n ChartZoomResetButton,\n clampTooltipPosition,\n useMeasuredSize,\n} from './chart'\n\nexport interface LineChartDatum {\n date: Date\n value: number\n}\n\nexport type LineChartCurve =\n | 'monotone'\n | 'step'\n | 'step-after'\n | 'step-before'\n | 'linear'\n\nexport interface LineChartSeries {\n id: string\n data: LineChartDatum[]\n /** Label shown in the hover tooltip. Defaults to `id`. */\n label?: React.ReactNode\n className?: string\n /**\n * CSS color for the tooltip swatch and hover dot (e.g. `var(--orange-9)`).\n * Defaults to the palette color for the series index. Set this whenever\n * `className` overrides the stroke so the tooltip stays in sync.\n */\n color?: string\n /** Curve interpolation type. Default: inherits from LineChartProps.curve */\n curve?: LineChartCurve\n /** Render as a dashed line */\n dashed?: boolean\n /** Custom dash pattern (e.g. \"8,4\" or \"4,2,1,2\"). Only applies when dashed is true. */\n dashPattern?: string\n}\n\nexport interface LineChartProps {\n series: LineChartSeries[]\n width: number\n height: number\n margin?: { top: number; right: number; bottom: number; left: number }\n /** Default curve for all series. Default: 'monotone' */\n curve?: LineChartCurve\n showGrid?: boolean\n showTooltip?: boolean\n className?: string\n xTickCount?: number\n yTickCount?: number\n /** Fixes the y-axis to an explicit `[min, max]`. Defaults to the data extent. */\n yDomain?: [number, number]\n /** Formats the x-axis tick labels. */\n formatDate?: (date: Date) => string\n /** Formats the tooltip title. Defaults to `formatDate`. */\n formatTooltipDate?: (date: Date) => string\n formatValue?: (value: number) => string\n /**\n * Unit shown once at the top of the y-axis (e.g. \"kW\"). When set, y-axis tick\n * labels render as plain numbers (matching BarChart / PowerHistoryChart)\n * instead of repeating `formatValue` on every tick. `formatValue` still feeds\n * the tooltip. Omit to keep the per-tick `formatValue` labels.\n */\n yAxisUnit?: string\n /** Overrides the y-axis tick formatter used when `yAxisUnit` is set. Defaults to the shared compact formatter. */\n formatYTick?: (value: number) => string\n /**\n * Vertical reference markers positioned on the time axis (e.g. sunrise /\n * sunset). Each renders a dashed line across the plot with an optional label.\n */\n referenceLines?: Array<{ key: string; date: Date; label?: React.ReactNode }>\n /**\n * HTML content pinned to the top of the plot at a given time (e.g. an hourly\n * weather icon). Positioned by the same time scale as the series, so it lines\n * up with the curve below it.\n */\n topContent?: Array<{ key: string; date: Date; content: React.ReactNode }>\n zoomable?: boolean\n zoomResetLabel?: string\n zoomDomain?: [Date, Date] | null\n onZoomDomainChange?: (domain: [Date, Date] | null) => void\n}\n\nconst DEFAULT_MARGIN = { top: 16, right: 16, bottom: 40, left: 48 }\nconst DETAILED_X_AXIS_THRESHOLD = 6 * 60 * 60 * 1000\n\nconst CURVE_MAP = {\n monotone: curveMonotoneX,\n step: curveStep,\n 'step-after': curveStepAfter,\n 'step-before': curveStepBefore,\n linear: curveLinear,\n} as const\n\nconst resolveCurve = (type: LineChartCurve) => CURVE_MAP[type]\n\nconst dateBisector = bisector<LineChartDatum, Date>((d) => d.date)\nconst bisectDate = (data: LineChartDatum[], x: Date, lo?: number) =>\n dateBisector.left(data, x, lo)\n\n/** Whether `date` falls within a series' own x-range (data is sorted ascending). */\nfunction withinDomain(data: LineChartDatum[], date: Date): boolean {\n const first = data[0]\n const last = data[data.length - 1]\n if (!first || !last) return false\n const t = date.valueOf()\n return t >= first.date.valueOf() && t <= last.date.valueOf()\n}\n\n/**\n * y-domain padded 10% beyond the data extent. The baseline is pinned to 0 so\n * all-positive series render from the axis up; a series that dips below 0\n * (signed grid / battery power) extends the domain downward to keep the zero\n * crossing visible.\n */\nfunction computeYDomain(data: LineChartDatum[]): [number, number] {\n let min = 0\n let max = 0\n for (const d of data) {\n if (d.value < min) min = d.value\n if (d.value > max) max = d.value\n }\n if (min === max) return [min, min + 1]\n return [min * 1.1, max * 1.1]\n}\n\nfunction nearestDatum(\n data: LineChartDatum[],\n date: Date,\n): LineChartDatum | undefined {\n if (data.length === 0) return undefined\n const index = bisectDate(data, date, 1)\n const d0 = data[index - 1]\n const d1 = data[index]\n if (!d0) return d1\n if (!d1) return d0\n return date.valueOf() - d0.date.valueOf() > d1.date.valueOf() - date.valueOf()\n ? d1\n : d0\n}\n\nconst defaultFormatDate = (date: Date) =>\n date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })\n\nconst defaultValueFormat = localeNumberFormat()\nconst defaultFormatValue = (value: number) => defaultValueFormat.format(value)\n\nconst LINE_COLORS = [\n 'stroke-spark-9',\n 'stroke-green-9',\n 'stroke-blue-9',\n 'stroke-orange-9',\n 'stroke-red-9',\n]\n\nconst DOT_COLORS = [\n 'fill-spark-9',\n 'fill-green-9',\n 'fill-blue-9',\n 'fill-orange-9',\n 'fill-red-9',\n]\n\n// CSS-var colours matching LINE_COLORS, used for the tooltip legend dots.\nconst LEGEND_COLORS = [\n 'var(--spark-9)',\n 'var(--green-9)',\n 'var(--blue-9)',\n 'var(--orange-9)',\n 'var(--red-9)',\n]\n\nfunction toTimeDomain(dates: [Date, Date]): TimeDomain {\n return [dates[0].valueOf(), dates[1].valueOf()]\n}\n\nfunction fromTimeDomain(domain: TimeDomain): [Date, Date] {\n return [new Date(domain[0]), new Date(domain[1])]\n}\n\nfunction touchDistance(a: React.Touch, b: React.Touch): number {\n return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY)\n}\n\nfunction touchMidpoint(\n a: React.Touch,\n b: React.Touch,\n): {\n clientX: number\n clientY: number\n} {\n return {\n clientX: (a.clientX + b.clientX) / 2,\n clientY: (a.clientY + b.clientY) / 2,\n }\n}\n\n// oxlint-disable-next-line eslint/complexity -- pre-existing; split it before adding branches\nconst LineChart: React.FC<LineChartProps> = ({\n series,\n width,\n height,\n margin = DEFAULT_MARGIN,\n curve: defaultCurve = 'monotone',\n showGrid = true,\n showTooltip: enableTooltip = true,\n className,\n xTickCount = 5,\n yTickCount = 4,\n formatDate = defaultFormatDate,\n formatTooltipDate,\n formatValue = defaultFormatValue,\n yAxisUnit,\n formatYTick = formatYAxisTick,\n yDomain,\n referenceLines,\n topContent,\n zoomable = false,\n zoomResetLabel,\n zoomDomain: zoomDomainProp,\n onZoomDomainChange,\n}) => {\n const plotClipId = React.useId()\n const {\n showTooltip,\n hideTooltip,\n tooltipData,\n tooltipLeft,\n tooltipTop,\n tooltipOpen,\n } = useTooltip<{ date: Date }>()\n\n const innerWidth = Math.max(0, width - margin.left - margin.right)\n const innerHeight = Math.max(0, height - margin.top - margin.bottom)\n\n const allData = useMemo(() => series.flatMap((s) => s.data), [series])\n\n const fullDomain = useMemo((): TimeDomain | null => {\n const ext = extent(allData, (d) => d.date.valueOf())\n if (ext[0] == null || ext[1] == null) return null\n return [ext[0], ext[1]]\n }, [allData])\n\n const controlledZoomDomain =\n zoomDomainProp === undefined\n ? undefined\n : zoomDomainProp == null\n ? null\n : toTimeDomain(zoomDomainProp)\n\n const handleZoomDomainChange = useCallback(\n (domain: TimeDomain | null) => {\n onZoomDomainChange?.(domain ? fromTimeDomain(domain) : null)\n },\n [onZoomDomainChange],\n )\n\n const zoom = useChartZoom({\n enabled: zoomable,\n fullDomain,\n zoomDomain: controlledZoomDomain,\n onZoomDomainChange: handleZoomDomainChange,\n })\n\n const xDomain = useMemo((): [Date, Date] => {\n if (zoom.zoomWindow) return fromTimeDomain(zoom.zoomWindow)\n const ext = extent(allData, (d) => d.date) as\n | [Date, Date]\n | [undefined, undefined]\n if (ext[0] && ext[1]) return [ext[0], ext[1]]\n const now = new Date()\n return [now, now]\n }, [allData, zoom.zoomWindow])\n\n const xScale = useMemo(\n () =>\n scaleTime({\n range: [0, innerWidth],\n domain: xDomain,\n }),\n [innerWidth, xDomain],\n )\n\n const visibleData = useMemo(() => {\n const [start, end] = xDomain\n const t0 = start.valueOf()\n const t1 = end.valueOf()\n return allData.filter((d) => {\n const t = d.date.valueOf()\n return t >= t0 && t <= t1\n })\n }, [allData, xDomain])\n\n const reducedMotion = useReducedMotion()\n\n // With a unit, the y-axis is generated by the shared scale util (nice ticks\n // + rounded bounds) so it matches BarChart / PowerHistoryChart exactly.\n const yAxisScale = useMemo(() => {\n if (yAxisUnit === undefined) return null\n let min = 0\n let max = 0\n for (const d of visibleData) {\n if (d.value < min) min = d.value\n if (d.value > max) max = d.value\n }\n return buildYAxisScale(min, max)\n }, [visibleData, yAxisUnit])\n\n const targetYDomain = useMemo((): TimeDomain => {\n if (yDomain) return yDomain\n if (yAxisScale) return [yAxisScale.min, yAxisScale.max]\n return scaleLinear({\n domain: computeYDomain(visibleData),\n nice: true,\n }).domain() as [number, number]\n }, [visibleData, yAxisScale, yDomain])\n\n const displayedYDomain = useAnimatedChartDomain(targetYDomain, {\n animateChanges: zoom.isPinching,\n reducedMotion,\n })\n\n const yScale = useMemo(\n () =>\n scaleLinear({\n range: [innerHeight, 0],\n domain: [displayedYDomain[0], displayedYDomain[1]],\n }),\n [displayedYDomain, innerHeight],\n )\n\n const xTicks = zoom.isZoomed\n ? buildTimeTicks(\n xDomain[0].valueOf(),\n xDomain[1].valueOf(),\n xTickCount,\n ).map((tick) => new Date(tick))\n : xScale.ticks(xTickCount)\n const yTicks = yAxisScale ? yAxisScale.ticks : yScale.ticks(yTickCount)\n const formatAxisDate =\n zoom.isZoomed &&\n xDomain[1].valueOf() - xDomain[0].valueOf() <= DETAILED_X_AXIS_THRESHOLD\n ? (formatTooltipDate ?? formatDate)\n : formatDate\n\n const domainXFromEvent = useCallback(\n (\n event:\n | React.MouseEvent<SVGRectElement>\n | React.PointerEvent<SVGRectElement>\n | React.TouchEvent<SVGRectElement>,\n ) => {\n const point = localPoint(event)\n if (!point) return null\n return xScale.invert(point.x - margin.left).valueOf()\n },\n [margin.left, xScale],\n )\n\n const handleTooltip = useCallback(\n (\n event:\n | React.TouchEvent<SVGRectElement>\n | React.MouseEvent<SVGRectElement>,\n ) => {\n if (!enableTooltip || zoom.isBrushing || zoom.isPanning) return\n\n const point = localPoint(event)\n if (!point) return\n\n const x0 = xScale.invert(point.x - margin.left)\n\n // Snap to the data point closest to the cursor across all series, so the\n // cursor line + tooltip line up with real values.\n let anchorDate: Date | null = null\n let bestDistance = Infinity\n for (const s of series) {\n const d = nearestDatum(s.data, x0)\n if (!d) continue\n const distance = Math.abs(d.date.valueOf() - x0.valueOf())\n if (distance < bestDistance) {\n bestDistance = distance\n anchorDate = d.date\n }\n }\n\n if (!anchorDate) return\n\n showTooltip({\n tooltipData: { date: anchorDate },\n tooltipLeft: xScale(anchorDate) + margin.left,\n tooltipTop: margin.top,\n })\n },\n [\n enableTooltip,\n margin,\n series,\n showTooltip,\n xScale,\n zoom.isBrushing,\n zoom.isPanning,\n ],\n )\n\n const tooltipRows: ChartTooltipRow[] = tooltipData\n ? series.flatMap((s, i) => {\n const d = nearestDatum(s.data, tooltipData.date)\n if (!d || !withinDomain(s.data, tooltipData.date)) return []\n return [\n {\n key: s.id,\n label: s.label ?? s.id,\n color:\n s.color ?? (LEGEND_COLORS[i % LEGEND_COLORS.length] as string),\n value: formatValue(d.value),\n marker: s.dashed ? ('dashed-line' as const) : ('dot' as const),\n },\n ]\n })\n : []\n\n const [tooltipRef, tooltipSize] = useMeasuredSize<HTMLDivElement>()\n const tooltipPos = clampTooltipPosition({\n anchorX: tooltipLeft ?? 0,\n anchorY: tooltipTop ?? margin.top,\n width,\n height,\n tipWidth: tooltipSize.width,\n tipHeight: tooltipSize.height,\n placement: 'beside',\n })\n\n if (width < 10 || height < 10) return null\n\n return (\n <div className={cn('relative', className)}>\n <svg className=\"block\" height={height} width={width}>\n <defs>\n <clipPath id={plotClipId}>\n <rect height={innerHeight} width={innerWidth} x={0} y={0} />\n </clipPath>\n </defs>\n <Group left={margin.left} top={margin.top}>\n {/* Grid lines */}\n {showGrid &&\n yTicks.map((tick) => (\n <line\n className=\"stroke-gray-a4\"\n key={`grid-y-${tick}`}\n strokeDasharray=\"4,4\"\n x1={0}\n x2={innerWidth}\n y1={yScale(tick)}\n y2={yScale(tick)}\n />\n ))}\n\n {/* X axis */}\n <line\n className=\"stroke-gray-a6\"\n x1={0}\n x2={innerWidth}\n y1={innerHeight}\n y2={innerHeight}\n />\n\n {/* Reference markers (e.g. sunrise / sunset) */}\n {referenceLines?.map((ref) => {\n const x = xScale(ref.date)\n if (!Number.isFinite(x) || x < 0 || x > innerWidth) return null\n // Sit the label beside the line, on whichever side keeps it in\n // frame: right of a left-half marker, left of a right-half one.\n const anchorEnd = x > innerWidth / 2\n return (\n <g key={ref.key} pointerEvents=\"none\">\n <line\n className=\"stroke-orange-8\"\n strokeDasharray=\"3,3\"\n x1={x}\n x2={x}\n y1={0}\n y2={innerHeight}\n />\n {ref.label != null && (\n <text\n className=\"fill-orange-11 text-[10px]\"\n dy=\"0.9em\"\n textAnchor={anchorEnd ? 'end' : 'start'}\n x={x + (anchorEnd ? -4 : 4)}\n y={0}\n >\n {ref.label}\n </text>\n )}\n </g>\n )\n })}\n\n {/* X axis labels */}\n {xTicks.map((tick, index) => (\n <text\n dy=\"1em\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n key={`x-${tick.valueOf()}`}\n textAnchor={\n index === 0\n ? 'start'\n : index === xTicks.length - 1\n ? 'end'\n : 'middle'\n }\n x={xScale(tick)}\n y={innerHeight + 8}\n >\n {formatAxisDate(tick)}\n </text>\n ))}\n\n {/* Y axis. With `yAxisUnit`, matches BarChart: a vertical axis line,\n short tick marks, plain numeric ticks, and the unit as a bold\n top-left header (top tick hidden). Without it, ticks repeat\n `formatValue` and no axis line/marks are drawn. */}\n {yAxisUnit !== undefined && (\n <line\n stroke=\"var(--neutral-a6)\"\n strokeWidth={1}\n x1={0}\n x2={0}\n y1={0}\n y2={innerHeight}\n />\n )}\n {yTicks.map((tick) => {\n const y = yScale(tick)\n return (\n <g key={`y-${tick}`}>\n {yAxisUnit !== undefined && (\n <line\n stroke=\"var(--neutral-a8)\"\n strokeWidth={1}\n x1={-4}\n x2={0}\n y1={y}\n y2={y}\n />\n )}\n <text\n dy=\"0.32em\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n textAnchor=\"end\"\n x={yAxisUnit !== undefined ? -7 : -4}\n y={y}\n >\n {yAxisUnit !== undefined\n ? formatYTick(tick)\n : formatValue(tick)}\n </text>\n </g>\n )\n })}\n {yAxisUnit !== undefined && (\n <text\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n fontWeight={600}\n textAnchor=\"start\"\n x={-margin.left}\n y={-6}\n >\n {yAxisUnit}\n </text>\n )}\n\n <g clipPath={`url(#${plotClipId})`} data-testid=\"chart-plot-clip\">\n {series.map((s, i) => (\n <LinePath\n className={cn(LINE_COLORS[i % LINE_COLORS.length], s.className)}\n curve={resolveCurve(s.curve ?? defaultCurve)}\n data={s.data}\n fill=\"none\"\n key={s.id}\n strokeDasharray={\n s.dashed ? (s.dashPattern ?? '6,4') : undefined\n }\n strokeWidth={2}\n x={(d) => xScale(d.date)}\n y={(d) => yScale(d.value)}\n />\n ))}\n </g>\n\n {zoom.brushBand && (\n <rect\n data-testid=\"chart-brush-band\"\n fill=\"var(--spark-a4)\"\n height={innerHeight}\n pointerEvents=\"none\"\n width={Math.abs(\n xScale(new Date(zoom.brushBand[1])) -\n xScale(new Date(zoom.brushBand[0])),\n )}\n x={Math.min(\n xScale(new Date(zoom.brushBand[0])),\n xScale(new Date(zoom.brushBand[1])),\n )}\n y={0}\n />\n )}\n\n {(enableTooltip || zoomable) && (\n <rect\n fill=\"transparent\"\n height={innerHeight}\n style={zoomable ? { touchAction: 'pan-y' } : undefined}\n width={innerWidth}\n x={0}\n y={0}\n onMouseLeave={() => {\n if (!zoom.isBrushing) hideTooltip()\n }}\n onMouseMove={(event) => {\n if (zoom.isBrushing) {\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n return\n }\n handleTooltip(event)\n }}\n onPointerDown={(event) => {\n if (!zoomable || event.pointerType === 'touch') return\n if (event.button !== 0) return\n const domainX = domainXFromEvent(event)\n if (domainX == null) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n hideTooltip()\n zoom.onPointerDown(domainX)\n }}\n onPointerMove={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n }}\n onPointerUp={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerUp(domainX)\n else zoom.onPointerCancel()\n }}\n onPointerCancel={() => {\n zoom.onPointerCancel()\n }}\n onDoubleClick={() => {\n if (zoomable) zoom.onDoubleClick()\n }}\n onTouchStart={(event) => {\n if (!zoomable) return\n if (event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - margin.left) / innerWidth),\n )\n zoom.onPinchStart(touchDistance(a, b), midpoint)\n hideTooltip()\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanStart({\n x: (point.x - margin.left) / innerWidth,\n y: (point.y - margin.top) / innerHeight,\n })\n hideTooltip()\n }\n }}\n onTouchMove={(event) => {\n if (zoomable && event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - margin.left) / innerWidth),\n )\n zoom.onPinchMove(touchDistance(a, b), midpoint)\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanMove({\n x: (point.x - margin.left) / innerWidth,\n y: (point.y - margin.top) / innerHeight,\n })\n return\n }\n if (event.touches.length === 1) handleTooltip(event)\n }}\n onTouchEnd={(event) => {\n if (zoomable && event.touches.length < 2) zoom.onPinchEnd()\n if (event.touches.length === 0) {\n if (zoomable) zoom.onPanEnd()\n if (zoomable) zoom.onTap()\n hideTooltip()\n }\n }}\n onTouchCancel={() => {\n zoom.onPinchEnd()\n zoom.onPanEnd()\n }}\n />\n )}\n\n {/* Cursor line + a dot per series */}\n {tooltipOpen &&\n tooltipData &&\n !zoom.isBrushing &&\n !zoom.isPanning && (\n <>\n <line\n className=\"stroke-gray-a6\"\n pointerEvents=\"none\"\n strokeDasharray=\"4,4\"\n x1={xScale(tooltipData.date)}\n x2={xScale(tooltipData.date)}\n y1={0}\n y2={innerHeight}\n />\n {series.map((s, i) => {\n const d = nearestDatum(s.data, tooltipData.date)\n if (!d || !withinDomain(s.data, tooltipData.date)) return null\n return (\n <circle\n key={s.id}\n className={\n s.color\n ? undefined\n : cn(DOT_COLORS[i % DOT_COLORS.length])\n }\n cx={xScale(d.date)}\n cy={yScale(d.value)}\n fill={s.color}\n pointerEvents=\"none\"\n r={4}\n stroke=\"white\"\n strokeWidth={2}\n />\n )\n })}\n </>\n )}\n </Group>\n </svg>\n\n {topContent && topContent.length > 0 && (\n <div\n className=\"pointer-events-none absolute overflow-hidden\"\n style={{\n height: margin.top,\n left: margin.left,\n top: 0,\n width: innerWidth,\n }}\n >\n {topContent.map((item) => {\n const x = xScale(item.date)\n if (!Number.isFinite(x) || x < 0 || x > innerWidth) return null\n return (\n <div\n key={item.key}\n className=\"absolute -translate-x-1/2\"\n style={{ left: x, top: 0 }}\n >\n {item.content}\n </div>\n )\n })}\n </div>\n )}\n\n {zoom.isZoomed && zoomResetLabel && (\n <ChartZoomResetButton label={zoomResetLabel} onReset={zoom.resetZoom} />\n )}\n\n <div\n ref={tooltipRef}\n className=\"pointer-events-none absolute z-10\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n opacity:\n tooltipOpen && tooltipData && !zoom.isPanning && !zoom.isBrushing\n ? 1\n : 0,\n transition: reducedMotion\n ? 'opacity 140ms ease-out'\n : 'left 140ms ease-out, top 140ms ease-out, opacity 140ms ease-out',\n }}\n >\n {tooltipData && (\n <ChartTooltipCard\n label={(formatTooltipDate ?? formatDate)(tooltipData.date)}\n rows={tooltipRows}\n />\n )}\n </div>\n </div>\n )\n}\n\nexport { LineChart }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA2GA,MAAM,iBAAiB;CAAE,KAAK;CAAI,OAAO;CAAI,QAAQ;CAAI,MAAM;AAAG;AAClE,MAAM,4BAA4B,MAAS,KAAK;AAEhD,MAAM,YAAY;CAChB,UAAUA,YAAAA;CACV,MAAMC,YAAAA;CACN,cAAcC,YAAAA;CACd,eAAeC,YAAAA;CACf,QAAQC,YAAAA;AACV;AAEA,MAAM,gBAAgB,SAAyB,UAAU;AAEzD,MAAM,gBAAA,GAAA,sBAAA,WAA+C,MAAM,EAAE,IAAI;AACjE,MAAM,cAAc,MAAwB,GAAS,OACnD,aAAa,KAAK,MAAM,GAAG,EAAE;;AAG/B,SAAS,aAAa,MAAwB,MAAqB;CACjE,MAAM,QAAQ,KAAK;CACnB,MAAM,OAAO,KAAK,KAAK,SAAS;CAChC,IAAI,CAAC,SAAS,CAAC,MAAM,OAAO;CAC5B,MAAM,IAAI,KAAK,QAAQ;CACvB,OAAO,KAAK,MAAM,KAAK,QAAQ,KAAK,KAAK,KAAK,KAAK,QAAQ;AAC7D;;;;;;;AAQA,SAAS,eAAe,MAA0C;CAChE,IAAI,MAAM;CACV,IAAI,MAAM;CACV,KAAK,MAAM,KAAK,MAAM;EACpB,IAAI,EAAE,QAAQ,KAAK,MAAM,EAAE;EAC3B,IAAI,EAAE,QAAQ,KAAK,MAAM,EAAE;CAC7B;CACA,IAAI,QAAQ,KAAK,OAAO,CAAC,KAAK,MAAM,CAAC;CACrC,OAAO,CAAC,MAAM,KAAK,MAAM,GAAG;AAC9B;AAEA,SAAS,aACP,MACA,MAC4B;CAC5B,IAAI,KAAK,WAAW,GAAG,OAAO,KAAA;CAC9B,MAAM,QAAQ,WAAW,MAAM,MAAM,CAAC;CACtC,MAAM,KAAK,KAAK,QAAQ;CACxB,MAAM,KAAK,KAAK;CAChB,IAAI,CAAC,IAAI,OAAO;CAChB,IAAI,CAAC,IAAI,OAAO;CAChB,OAAO,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,IAAI,KAAK,QAAQ,IACzE,KACA;AACN;AAEA,MAAM,qBAAqB,SACzB,KAAK,mBAAmB,KAAA,GAAW;CAAE,OAAO;CAAS,KAAK;AAAU,CAAC;AAEvE,MAAM,qBAAqBC,qBAAAA,mBAAmB;AAC9C,MAAM,sBAAsB,UAAkB,mBAAmB,OAAO,KAAK;AAE7E,MAAM,cAAc;CAClB;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;AACF;AAGA,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,aAAa,OAAiC;CACrD,OAAO,CAAC,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAChD;AAEA,SAAS,eAAe,QAAkC;CACxD,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC;AAClD;AAEA,SAAS,cAAc,GAAgB,GAAwB;CAC7D,OAAO,KAAK,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO;AAChE;AAEA,SAAS,cACP,GACA,GAIA;CACA,OAAO;EACL,UAAU,EAAE,UAAU,EAAE,WAAW;EACnC,UAAU,EAAE,UAAU,EAAE,WAAW;CACrC;AACF;AAGA,MAAM,aAAuC,EAC3C,QACA,OACA,QACA,SAAS,gBACT,OAAO,eAAe,YACtB,WAAW,MACX,aAAa,gBAAgB,MAC7B,WACA,aAAa,GACb,aAAa,GACb,aAAa,mBACb,mBACA,cAAc,oBACd,WACA,cAAcC,0BAAAA,iBACd,SACA,gBACA,YACA,WAAW,OACX,gBACA,YAAY,gBACZ,yBACI;CACJ,MAAM,aAAaC,MAAM,MAAM;CAC/B,MAAM,EACJ,aACA,aACA,aACA,aACA,YACA,iBAAA,GAAA,cAAA,YAC6B;CAE/B,MAAM,aAAa,KAAK,IAAI,GAAG,QAAQ,OAAO,OAAO,OAAO,KAAK;CACjE,MAAM,cAAc,KAAK,IAAI,GAAG,SAAS,OAAO,MAAM,OAAO,MAAM;CAEnE,MAAM,WAAA,GAAA,MAAA,eAAwB,OAAO,SAAS,MAAM,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC;CAsBrE,MAAM,OAAOC,6BAAAA,aAAa;EACxB,SAAS;EACT,aAAA,GAAA,MAAA,eAtBkD;GAClD,MAAM,OAAA,GAAA,sBAAA,QAAa,UAAU,MAAM,EAAE,KAAK,QAAQ,CAAC;GACnD,IAAI,IAAI,MAAM,QAAQ,IAAI,MAAM,MAAM,OAAO;GAC7C,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE;EACxB,GAAG,CAAC,OAAO,CAkBA;EACT,YAhBA,mBAAmB,KAAA,IACf,KAAA,IACA,kBAAkB,OAChB,OACA,aAAa,cAAc;EAajC,qBAAA,GAAA,MAAA,cAVC,WAA8B;GAC7B,qBAAqB,SAAS,eAAe,MAAM,IAAI,IAAI;EAC7D,GACA,CAAC,kBAAkB,CAOsB;CAC3C,CAAC;CAED,MAAM,WAAA,GAAA,MAAA,eAAsC;EAC1C,IAAI,KAAK,YAAY,OAAO,eAAe,KAAK,UAAU;EAC1D,MAAM,OAAA,GAAA,sBAAA,QAAa,UAAU,MAAM,EAAE,IAAI;EAGzC,IAAI,IAAI,MAAM,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE;EAC5C,MAAM,sBAAM,IAAI,KAAK;EACrB,OAAO,CAAC,KAAK,GAAG;CAClB,GAAG,CAAC,SAAS,KAAK,UAAU,CAAC;CAE7B,MAAM,UAAA,GAAA,MAAA,gBAAA,GAAA,YAAA,WAEQ;EACR,OAAO,CAAC,GAAG,UAAU;EACrB,QAAQ;CACV,CAAC,GACH,CAAC,YAAY,OAAO,CACtB;CAEA,MAAM,eAAA,GAAA,MAAA,eAA4B;EAChC,MAAM,CAAC,OAAO,OAAO;EACrB,MAAM,KAAK,MAAM,QAAQ;EACzB,MAAM,KAAK,IAAI,QAAQ;EACvB,OAAO,QAAQ,QAAQ,MAAM;GAC3B,MAAM,IAAI,EAAE,KAAK,QAAQ;GACzB,OAAO,KAAK,MAAM,KAAK;EACzB,CAAC;CACH,GAAG,CAAC,SAAS,OAAO,CAAC;CAErB,MAAM,gBAAgBC,iCAAAA,iBAAiB;CAIvC,MAAM,cAAA,GAAA,MAAA,eAA2B;EAC/B,IAAI,cAAc,KAAA,GAAW,OAAO;EACpC,IAAI,MAAM;EACV,IAAI,MAAM;EACV,KAAK,MAAM,KAAK,aAAa;GAC3B,IAAI,EAAE,QAAQ,KAAK,MAAM,EAAE;GAC3B,IAAI,EAAE,QAAQ,KAAK,MAAM,EAAE;EAC7B;EACA,OAAOC,0BAAAA,gBAAgB,KAAK,GAAG;CACjC,GAAG,CAAC,aAAa,SAAS,CAAC;CAW3B,MAAM,mBAAmBC,6BAAAA,wBAAAA,GAAAA,MAAAA,eATuB;EAC9C,IAAI,SAAS,OAAO;EACpB,IAAI,YAAY,OAAO,CAAC,WAAW,KAAK,WAAW,GAAG;EACtD,QAAA,GAAA,YAAA,aAAmB;GACjB,QAAQ,eAAe,WAAW;GAClC,MAAM;EACR,CAAC,EAAE,OAAO;CACZ,GAAG;EAAC;EAAa;EAAY;CAAO,CAEwB,GAAG;EAC7D,gBAAgB,KAAK;EACrB;CACF,CAAC;CAED,MAAM,UAAA,GAAA,MAAA,gBAAA,GAAA,YAAA,aAEU;EACV,OAAO,CAAC,aAAa,CAAC;EACtB,QAAQ,CAAC,iBAAiB,IAAI,iBAAiB,EAAE;CACnD,CAAC,GACH,CAAC,kBAAkB,WAAW,CAChC;CAEA,MAAM,SAAS,KAAK,WAChBC,+BAAAA,eACE,QAAQ,GAAG,QAAQ,GACnB,QAAQ,GAAG,QAAQ,GACnB,UACF,EAAE,KAAK,SAAS,IAAI,KAAK,IAAI,CAAC,IAC9B,OAAO,MAAM,UAAU;CAC3B,MAAM,SAAS,aAAa,WAAW,QAAQ,OAAO,MAAM,UAAU;CACtE,MAAM,iBACJ,KAAK,YACL,QAAQ,GAAG,QAAQ,IAAI,QAAQ,GAAG,QAAQ,KAAK,4BAC1C,qBAAqB,aACtB;CAEN,MAAM,oBAAA,GAAA,MAAA,cAEF,UAIG;EACH,MAAM,SAAA,GAAA,YAAA,YAAmB,KAAK;EAC9B,IAAI,CAAC,OAAO,OAAO;EACnB,OAAO,OAAO,OAAO,MAAM,IAAI,OAAO,IAAI,EAAE,QAAQ;CACtD,GACA,CAAC,OAAO,MAAM,MAAM,CACtB;CAEA,MAAM,iBAAA,GAAA,MAAA,cAEF,UAGG;EACH,IAAI,CAAC,iBAAiB,KAAK,cAAc,KAAK,WAAW;EAEzD,MAAM,SAAA,GAAA,YAAA,YAAmB,KAAK;EAC9B,IAAI,CAAC,OAAO;EAEZ,MAAM,KAAK,OAAO,OAAO,MAAM,IAAI,OAAO,IAAI;EAI9C,IAAI,aAA0B;EAC9B,IAAI,eAAe;EACnB,KAAK,MAAM,KAAK,QAAQ;GACtB,MAAM,IAAI,aAAa,EAAE,MAAM,EAAE;GACjC,IAAI,CAAC,GAAG;GACR,MAAM,WAAW,KAAK,IAAI,EAAE,KAAK,QAAQ,IAAI,GAAG,QAAQ,CAAC;GACzD,IAAI,WAAW,cAAc;IAC3B,eAAe;IACf,aAAa,EAAE;GACjB;EACF;EAEA,IAAI,CAAC,YAAY;EAEjB,YAAY;GACV,aAAa,EAAE,MAAM,WAAW;GAChC,aAAa,OAAO,UAAU,IAAI,OAAO;GACzC,YAAY,OAAO;EACrB,CAAC;CACH,GACA;EACE;EACA;EACA;EACA;EACA;EACA,KAAK;EACL,KAAK;CACP,CACF;CAEA,MAAM,cAAiC,cACnC,OAAO,SAAS,GAAG,MAAM;EACvB,MAAM,IAAI,aAAa,EAAE,MAAM,YAAY,IAAI;EAC/C,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,MAAM,YAAY,IAAI,GAAG,OAAO,CAAC;EAC3D,OAAO,CACL;GACE,KAAK,EAAE;GACP,OAAO,EAAE,SAAS,EAAE;GACpB,OACE,EAAE,SAAU,cAAc,IAAI,cAAc;GAC9C,OAAO,YAAY,EAAE,KAAK;GAC1B,QAAQ,EAAE,SAAU,gBAA2B;EACjD,CACF;CACF,CAAC,IACD,CAAC;CAEL,MAAM,CAAC,YAAY,eAAeC,yBAAAA,gBAAgC;CAClE,MAAM,aAAaC,yBAAAA,qBAAqB;EACtC,SAAS,eAAe;EACxB,SAAS,cAAc,OAAO;EAC9B;EACA;EACA,UAAU,YAAY;EACtB,WAAW,YAAY;EACvB,WAAW;CACb,CAAC;CAED,IAAI,QAAQ,MAAM,SAAS,IAAI,OAAO;CAEtC,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EAAK,YAAA,GAAA,GAAA,IAAc,YAAY,SAAS;YAAxC;GACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;IAAK,WAAU;IAAgB;IAAe;cAA9C,CACE,iBAAA,GAAA,kBAAA,KAAC,QAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,KAAC,YAAD;KAAU,IAAI;eACZ,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAM,QAAQ;MAAa,OAAO;MAAY,GAAG;MAAG,GAAG;KAAI,CAAA;IACnD,CAAA,EACN,CAAA,GACN,iBAAA,GAAA,kBAAA,MAACC,YAAAA,OAAD;KAAO,MAAM,OAAO;KAAM,KAAK,OAAO;eAAtC;MAEG,YACC,OAAO,KAAK,SACV,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,WAAU;OAEV,iBAAgB;OAChB,IAAI;OACJ,IAAI;OACJ,IAAI,OAAO,IAAI;OACf,IAAI,OAAO,IAAI;MAChB,GANM,UAAU,MAMhB,CACF;MAGH,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,WAAU;OACV,IAAI;OACJ,IAAI;OACJ,IAAI;OACJ,IAAI;MACL,CAAA;MAGA,gBAAgB,KAAK,QAAQ;OAC5B,MAAM,IAAI,OAAO,IAAI,IAAI;OACzB,IAAI,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,KAAK,IAAI,YAAY,OAAO;OAG3D,MAAM,YAAY,IAAI,aAAa;OACnC,OACE,iBAAA,GAAA,kBAAA,MAAC,KAAD;QAAiB,eAAc;kBAA/B,CACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;SACE,WAAU;SACV,iBAAgB;SAChB,IAAI;SACJ,IAAI;SACJ,IAAI;SACJ,IAAI;QACL,CAAA,GACA,IAAI,SAAS,QACZ,iBAAA,GAAA,kBAAA,KAAC,QAAD;SACE,WAAU;SACV,IAAG;SACH,YAAY,YAAY,QAAQ;SAChC,GAAG,KAAK,YAAY,KAAK;SACzB,GAAG;mBAEF,IAAI;QACD,CAAA,CAEP;UApBK,IAAI,GAoBT;MAEP,CAAC;MAGA,OAAO,KAAK,MAAM,UACjB,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,IAAG;OACH,MAAK;OACL,UAAU;OAEV,YACE,UAAU,IACN,UACA,UAAU,OAAO,SAAS,IACxB,QACA;OAER,GAAG,OAAO,IAAI;OACd,GAAG,cAAc;iBAEhB,eAAe,IAAI;MAChB,GAZC,KAAK,KAAK,QAAQ,GAYnB,CACP;MAMA,cAAc,KAAA,KACb,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,QAAO;OACP,aAAa;OACb,IAAI;OACJ,IAAI;OACJ,IAAI;OACJ,IAAI;MACL,CAAA;MAEF,OAAO,KAAK,SAAS;OACpB,MAAM,IAAI,OAAO,IAAI;OACrB,OACE,iBAAA,GAAA,kBAAA,MAAC,KAAD,EAAA,UAAA,CACG,cAAc,KAAA,KACb,iBAAA,GAAA,kBAAA,KAAC,QAAD;QACE,QAAO;QACP,aAAa;QACb,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,IAAI;OACL,CAAA,GAEH,iBAAA,GAAA,kBAAA,KAAC,QAAD;QACE,IAAG;QACH,MAAK;QACL,UAAU;QACV,YAAW;QACX,GAAG,cAAc,KAAA,IAAY,KAAK;QAC/B;kBAEF,cAAc,KAAA,IACX,YAAY,IAAI,IAChB,YAAY,IAAI;OAChB,CAAA,CACL,EAAA,GAvBK,KAAK,MAuBV;MAEP,CAAC;MACA,cAAc,KAAA,KACb,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,MAAK;OACL,UAAU;OACV,YAAY;OACZ,YAAW;OACX,GAAG,CAAC,OAAO;OACX,GAAG;iBAEF;MACG,CAAA;MAGR,iBAAA,GAAA,kBAAA,KAAC,KAAD;OAAG,UAAU,QAAQ,WAAW;OAAI,eAAY;iBAC7C,OAAO,KAAK,GAAG,MACd,iBAAA,GAAA,kBAAA,KAACC,YAAAA,UAAD;QACE,YAAA,GAAA,GAAA,IAAc,YAAY,IAAI,YAAY,SAAS,EAAE,SAAS;QAC9D,OAAO,aAAa,EAAE,SAAS,YAAY;QAC3C,MAAM,EAAE;QACR,MAAK;QAEL,iBACE,EAAE,SAAU,EAAE,eAAe,QAAS,KAAA;QAExC,aAAa;QACb,IAAI,MAAM,OAAO,EAAE,IAAI;QACvB,IAAI,MAAM,OAAO,EAAE,KAAK;OACzB,GAPM,EAAE,EAOR,CACF;MACA,CAAA;MAEF,KAAK,aACJ,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,eAAY;OACZ,MAAK;OACL,QAAQ;OACR,eAAc;OACd,OAAO,KAAK,IACV,OAAO,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC,IAChC,OAAO,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC,CACtC;OACA,GAAG,KAAK,IACN,OAAO,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC,GAClC,OAAO,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC,CACpC;OACA,GAAG;MACJ,CAAA;OAGD,iBAAiB,aACjB,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,MAAK;OACL,QAAQ;OACR,OAAO,WAAW,EAAE,aAAa,QAAQ,IAAI,KAAA;OAC7C,OAAO;OACP,GAAG;OACH,GAAG;OACH,oBAAoB;QAClB,IAAI,CAAC,KAAK,YAAY,YAAY;OACpC;OACA,cAAc,UAAU;QACtB,IAAI,KAAK,YAAY;SACnB,MAAM,UAAU,iBAAiB,KAAK;SACtC,IAAI,WAAW,MAAM,KAAK,cAAc,OAAO;SAC/C;QACF;QACA,cAAc,KAAK;OACrB;OACA,gBAAgB,UAAU;QACxB,IAAI,CAAC,YAAY,MAAM,gBAAgB,SAAS;QAChD,IAAI,MAAM,WAAW,GAAG;QACxB,MAAM,UAAU,iBAAiB,KAAK;QACtC,IAAI,WAAW,MAAM;QACrB,MAAM,cAAc,oBAAoB,MAAM,SAAS;QACvD,YAAY;QACZ,KAAK,cAAc,OAAO;OAC5B;OACA,gBAAgB,UAAU;QACxB,IAAI,CAAC,UAAU;QACf,MAAM,UAAU,iBAAiB,KAAK;QACtC,IAAI,WAAW,MAAM,KAAK,cAAc,OAAO;OACjD;OACA,cAAc,UAAU;QACtB,IAAI,CAAC,UAAU;QACf,MAAM,UAAU,iBAAiB,KAAK;QACtC,IAAI,WAAW,MAAM,KAAK,YAAY,OAAO;aACxC,KAAK,gBAAgB;OAC5B;OACA,uBAAuB;QACrB,KAAK,gBAAgB;OACvB;OACA,qBAAqB;QACnB,IAAI,UAAU,KAAK,cAAc;OACnC;OACA,eAAe,UAAU;QACvB,IAAI,CAAC,UAAU;QACf,IAAI,MAAM,QAAQ,WAAW,GAAG;SAC9B,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,QAAQ,IAAI,MAAM,QAAQ,EAAE;SAClD,IAAI,CAAC,KAAK,CAAC,GAAG;SACd,MAAM,SAAA,GAAA,YAAA,YACJ,MAAM,eACN,cAAc,GAAG,CAAC,CACpB;SACA,IAAI,CAAC,OAAO;SACZ,MAAM,WAAW,KAAK,IACpB,GACA,KAAK,IAAI,IAAI,MAAM,IAAI,OAAO,QAAQ,UAAU,CAClD;SACA,KAAK,aAAa,cAAc,GAAG,CAAC,GAAG,QAAQ;SAC/C,YAAY;SACZ;QACF;QACA,IAAI,MAAM,QAAQ,WAAW,KAAK,KAAK,UAAU;SAC/C,MAAM,SAAA,GAAA,YAAA,YAAmB,KAAK;SAC9B,IAAI,CAAC,OAAO;SACZ,KAAK,WAAW;UACd,IAAI,MAAM,IAAI,OAAO,QAAQ;UAC7B,IAAI,MAAM,IAAI,OAAO,OAAO;SAC9B,CAAC;SACD,YAAY;QACd;OACF;OACA,cAAc,UAAU;QACtB,IAAI,YAAY,MAAM,QAAQ,WAAW,GAAG;SAC1C,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,QAAQ,IAAI,MAAM,QAAQ,EAAE;SAClD,IAAI,CAAC,KAAK,CAAC,GAAG;SACd,MAAM,SAAA,GAAA,YAAA,YACJ,MAAM,eACN,cAAc,GAAG,CAAC,CACpB;SACA,IAAI,CAAC,OAAO;SACZ,MAAM,WAAW,KAAK,IACpB,GACA,KAAK,IAAI,IAAI,MAAM,IAAI,OAAO,QAAQ,UAAU,CAClD;SACA,KAAK,YAAY,cAAc,GAAG,CAAC,GAAG,QAAQ;SAC9C;QACF;QACA,IAAI,MAAM,QAAQ,WAAW,KAAK,KAAK,UAAU;SAC/C,MAAM,SAAA,GAAA,YAAA,YAAmB,KAAK;SAC9B,IAAI,CAAC,OAAO;SACZ,KAAK,UAAU;UACb,IAAI,MAAM,IAAI,OAAO,QAAQ;UAC7B,IAAI,MAAM,IAAI,OAAO,OAAO;SAC9B,CAAC;SACD;QACF;QACA,IAAI,MAAM,QAAQ,WAAW,GAAG,cAAc,KAAK;OACrD;OACA,aAAa,UAAU;QACrB,IAAI,YAAY,MAAM,QAAQ,SAAS,GAAG,KAAK,WAAW;QAC1D,IAAI,MAAM,QAAQ,WAAW,GAAG;SAC9B,IAAI,UAAU,KAAK,SAAS;SAC5B,IAAI,UAAU,KAAK,MAAM;SACzB,YAAY;QACd;OACF;OACA,qBAAqB;QACnB,KAAK,WAAW;QAChB,KAAK,SAAS;OAChB;MACD,CAAA;MAIF,eACC,eACA,CAAC,KAAK,cACN,CAAC,KAAK,aACJ,iBAAA,GAAA,kBAAA,MAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,WAAU;OACV,eAAc;OACd,iBAAgB;OAChB,IAAI,OAAO,YAAY,IAAI;OAC3B,IAAI,OAAO,YAAY,IAAI;OAC3B,IAAI;OACJ,IAAI;MACL,CAAA,GACA,OAAO,KAAK,GAAG,MAAM;OACpB,MAAM,IAAI,aAAa,EAAE,MAAM,YAAY,IAAI;OAC/C,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,MAAM,YAAY,IAAI,GAAG,OAAO;OAC1D,OACE,iBAAA,GAAA,kBAAA,KAAC,UAAD;QAEE,WACE,EAAE,QACE,KAAA,KAAA,GAAA,GAAA,IACG,WAAW,IAAI,WAAW,OAAO;QAE1C,IAAI,OAAO,EAAE,IAAI;QACjB,IAAI,OAAO,EAAE,KAAK;QAClB,MAAM,EAAE;QACR,eAAc;QACd,GAAG;QACH,QAAO;QACP,aAAa;OACd,GAbM,EAAE,EAaR;MAEL,CAAC,CACD,EAAA,CAAA;KAED;MACJ;;GAEJ,cAAc,WAAW,SAAS,KACjC,iBAAA,GAAA,kBAAA,KAAC,OAAD;IACE,WAAU;IACV,OAAO;KACL,QAAQ,OAAO;KACf,MAAM,OAAO;KACb,KAAK;KACL,OAAO;IACT;cAEC,WAAW,KAAK,SAAS;KACxB,MAAM,IAAI,OAAO,KAAK,IAAI;KAC1B,IAAI,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,KAAK,IAAI,YAAY,OAAO;KAC3D,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;MAEE,WAAU;MACV,OAAO;OAAE,MAAM;OAAG,KAAK;MAAE;gBAExB,KAAK;KACH,GALE,KAAK,GAKP;IAET,CAAC;GACE,CAAA;GAGN,KAAK,YAAY,kBAChB,iBAAA,GAAA,kBAAA,KAACC,yBAAAA,sBAAD;IAAsB,OAAO;IAAgB,SAAS,KAAK;GAAY,CAAA;GAGzE,iBAAA,GAAA,kBAAA,KAAC,OAAD;IACE,KAAK;IACL,WAAU;IACV,OAAO;KACL,MAAM,WAAW;KACjB,KAAK,WAAW;KAChB,SACE,eAAe,eAAe,CAAC,KAAK,aAAa,CAAC,KAAK,aACnD,IACA;KACN,YAAY,gBACR,2BACA;IACN;cAEC,eACC,iBAAA,GAAA,kBAAA,KAACC,yBAAAA,kBAAD;KACE,QAAQ,qBAAqB,YAAY,YAAY,IAAI;KACzD,MAAM;IACP,CAAA;GAEA,CAAA;EACF;;AAET"}
|
|
1
|
+
{"version":3,"file":"line-chart.cjs","names":["curveMonotoneX","curveStep","curveStepAfter","curveStepBefore","curveLinear","getDocumentLang","localeNumberFormat","formatYAxisTick","React","useChartZoom","useReducedMotion","buildYAxisScale","useAnimatedChartDomain","buildTimeTicks","useMeasuredSize","clampTooltipPosition","Group","LinePath","ChartZoomResetButton","ChartTooltipCard"],"sources":["../../src/components/line-chart.tsx"],"sourcesContent":["import {\n curveLinear,\n curveMonotoneX,\n curveStep,\n curveStepAfter,\n curveStepBefore,\n} from '@visx/curve'\nimport { localPoint } from '@visx/event'\nimport { Group } from '@visx/group'\nimport { scaleLinear, scaleTime } from '@visx/scale'\nimport { LinePath } from '@visx/shape'\nimport { useTooltip } from '@visx/tooltip'\nimport { bisector, extent } from '@visx/vendor/d3-array'\nimport * as React from 'react'\nimport { useCallback, useMemo } from 'react'\nimport { useAnimatedChartDomain, useChartZoom } from '../hooks/use-chart-zoom'\nimport { useReducedMotion } from '../hooks/use-reduced-motion'\nimport { buildYAxisScale, formatYAxisTick } from '../utils/chart-scale'\nimport { buildTimeTicks } from '../utils/chart-time-ticks'\nimport type { TimeDomain } from '../utils/chart-zoom'\nimport { cn } from '../utils/cn'\nimport { getDocumentLang, localeNumberFormat } from '../utils/format'\nimport {\n ChartTooltipCard,\n type ChartTooltipRow,\n ChartZoomResetButton,\n clampTooltipPosition,\n useMeasuredSize,\n} from './chart'\n\nexport interface LineChartDatum {\n date: Date\n value: number\n}\n\nexport type LineChartCurve =\n | 'monotone'\n | 'step'\n | 'step-after'\n | 'step-before'\n | 'linear'\n\nexport interface LineChartSeries {\n id: string\n data: LineChartDatum[]\n /** Label shown in the hover tooltip. Defaults to `id`. */\n label?: React.ReactNode\n className?: string\n /**\n * CSS color for the tooltip swatch and hover dot (e.g. `var(--orange-9)`).\n * Defaults to the palette color for the series index. Set this whenever\n * `className` overrides the stroke so the tooltip stays in sync.\n */\n color?: string\n /** Curve interpolation type. Default: inherits from LineChartProps.curve */\n curve?: LineChartCurve\n /** Render as a dashed line */\n dashed?: boolean\n /** Custom dash pattern (e.g. \"8,4\" or \"4,2,1,2\"). Only applies when dashed is true. */\n dashPattern?: string\n}\n\nexport interface LineChartProps {\n series: LineChartSeries[]\n width: number\n height: number\n margin?: { top: number; right: number; bottom: number; left: number }\n /** Default curve for all series. Default: 'monotone' */\n curve?: LineChartCurve\n showGrid?: boolean\n showTooltip?: boolean\n className?: string\n xTickCount?: number\n yTickCount?: number\n /** Fixes the y-axis to an explicit `[min, max]`. Defaults to the data extent. */\n yDomain?: [number, number]\n /** Formats the x-axis tick labels. */\n formatDate?: (date: Date) => string\n /** Formats the tooltip title. Defaults to `formatDate`. */\n formatTooltipDate?: (date: Date) => string\n formatValue?: (value: number) => string\n /**\n * Unit shown once at the top of the y-axis (e.g. \"kW\"). When set, y-axis tick\n * labels render as plain numbers (matching BarChart / PowerHistoryChart)\n * instead of repeating `formatValue` on every tick. `formatValue` still feeds\n * the tooltip. Omit to keep the per-tick `formatValue` labels.\n */\n yAxisUnit?: string\n /** Overrides the y-axis tick formatter used when `yAxisUnit` is set. Defaults to the shared compact formatter. */\n formatYTick?: (value: number) => string\n /**\n * Vertical reference markers positioned on the time axis (e.g. sunrise /\n * sunset). Each renders a dashed line across the plot with an optional label.\n */\n referenceLines?: Array<{ key: string; date: Date; label?: React.ReactNode }>\n /**\n * HTML content pinned to the top of the plot at a given time (e.g. an hourly\n * weather icon). Positioned by the same time scale as the series, so it lines\n * up with the curve below it.\n */\n topContent?: Array<{ key: string; date: Date; content: React.ReactNode }>\n zoomable?: boolean\n zoomResetLabel?: string\n zoomDomain?: [Date, Date] | null\n onZoomDomainChange?: (domain: [Date, Date] | null) => void\n}\n\nconst DEFAULT_MARGIN = { top: 16, right: 16, bottom: 40, left: 48 }\nconst DETAILED_X_AXIS_THRESHOLD = 6 * 60 * 60 * 1000\n\nconst CURVE_MAP = {\n monotone: curveMonotoneX,\n step: curveStep,\n 'step-after': curveStepAfter,\n 'step-before': curveStepBefore,\n linear: curveLinear,\n} as const\n\nconst resolveCurve = (type: LineChartCurve) => CURVE_MAP[type]\n\nconst dateBisector = bisector<LineChartDatum, Date>((d) => d.date)\nconst bisectDate = (data: LineChartDatum[], x: Date, lo?: number) =>\n dateBisector.left(data, x, lo)\n\n/** Whether `date` falls within a series' own x-range (data is sorted ascending). */\nfunction withinDomain(data: LineChartDatum[], date: Date): boolean {\n const first = data[0]\n const last = data[data.length - 1]\n if (!first || !last) return false\n const t = date.valueOf()\n return t >= first.date.valueOf() && t <= last.date.valueOf()\n}\n\n/**\n * y-domain padded 10% beyond the data extent. The baseline is pinned to 0 so\n * all-positive series render from the axis up; a series that dips below 0\n * (signed grid / battery power) extends the domain downward to keep the zero\n * crossing visible.\n */\nfunction computeYDomain(data: LineChartDatum[]): [number, number] {\n let min = 0\n let max = 0\n for (const d of data) {\n if (d.value < min) min = d.value\n if (d.value > max) max = d.value\n }\n if (min === max) return [min, min + 1]\n return [min * 1.1, max * 1.1]\n}\n\nfunction nearestDatum(\n data: LineChartDatum[],\n date: Date,\n): LineChartDatum | undefined {\n if (data.length === 0) return undefined\n const index = bisectDate(data, date, 1)\n const d0 = data[index - 1]\n const d1 = data[index]\n if (!d0) return d1\n if (!d1) return d0\n return date.valueOf() - d0.date.valueOf() > d1.date.valueOf() - date.valueOf()\n ? d1\n : d0\n}\n\nconst defaultFormatDate = (date: Date) =>\n date.toLocaleDateString(getDocumentLang(), { month: 'short', day: 'numeric' })\n\nconst defaultValueFormat = localeNumberFormat()\nconst defaultFormatValue = (value: number) => defaultValueFormat.format(value)\n\nconst LINE_COLORS = [\n 'stroke-spark-9',\n 'stroke-green-9',\n 'stroke-blue-9',\n 'stroke-orange-9',\n 'stroke-red-9',\n]\n\nconst DOT_COLORS = [\n 'fill-spark-9',\n 'fill-green-9',\n 'fill-blue-9',\n 'fill-orange-9',\n 'fill-red-9',\n]\n\n// CSS-var colours matching LINE_COLORS, used for the tooltip legend dots.\nconst LEGEND_COLORS = [\n 'var(--spark-9)',\n 'var(--green-9)',\n 'var(--blue-9)',\n 'var(--orange-9)',\n 'var(--red-9)',\n]\n\nfunction toTimeDomain(dates: [Date, Date]): TimeDomain {\n return [dates[0].valueOf(), dates[1].valueOf()]\n}\n\nfunction fromTimeDomain(domain: TimeDomain): [Date, Date] {\n return [new Date(domain[0]), new Date(domain[1])]\n}\n\nfunction touchDistance(a: React.Touch, b: React.Touch): number {\n return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY)\n}\n\nfunction touchMidpoint(\n a: React.Touch,\n b: React.Touch,\n): {\n clientX: number\n clientY: number\n} {\n return {\n clientX: (a.clientX + b.clientX) / 2,\n clientY: (a.clientY + b.clientY) / 2,\n }\n}\n\n// oxlint-disable-next-line eslint/complexity -- pre-existing; split it before adding branches\nconst LineChart: React.FC<LineChartProps> = ({\n series,\n width,\n height,\n margin = DEFAULT_MARGIN,\n curve: defaultCurve = 'monotone',\n showGrid = true,\n showTooltip: enableTooltip = true,\n className,\n xTickCount = 5,\n yTickCount = 4,\n formatDate = defaultFormatDate,\n formatTooltipDate,\n formatValue = defaultFormatValue,\n yAxisUnit,\n formatYTick = formatYAxisTick,\n yDomain,\n referenceLines,\n topContent,\n zoomable = false,\n zoomResetLabel,\n zoomDomain: zoomDomainProp,\n onZoomDomainChange,\n}) => {\n const plotClipId = React.useId()\n const {\n showTooltip,\n hideTooltip,\n tooltipData,\n tooltipLeft,\n tooltipTop,\n tooltipOpen,\n } = useTooltip<{ date: Date }>()\n\n const innerWidth = Math.max(0, width - margin.left - margin.right)\n const innerHeight = Math.max(0, height - margin.top - margin.bottom)\n\n const allData = useMemo(() => series.flatMap((s) => s.data), [series])\n\n const fullDomain = useMemo((): TimeDomain | null => {\n const ext = extent(allData, (d) => d.date.valueOf())\n if (ext[0] == null || ext[1] == null) return null\n return [ext[0], ext[1]]\n }, [allData])\n\n const controlledZoomDomain =\n zoomDomainProp === undefined\n ? undefined\n : zoomDomainProp == null\n ? null\n : toTimeDomain(zoomDomainProp)\n\n const handleZoomDomainChange = useCallback(\n (domain: TimeDomain | null) => {\n onZoomDomainChange?.(domain ? fromTimeDomain(domain) : null)\n },\n [onZoomDomainChange],\n )\n\n const zoom = useChartZoom({\n enabled: zoomable,\n fullDomain,\n zoomDomain: controlledZoomDomain,\n onZoomDomainChange: handleZoomDomainChange,\n })\n\n const xDomain = useMemo((): [Date, Date] => {\n if (zoom.zoomWindow) return fromTimeDomain(zoom.zoomWindow)\n const ext = extent(allData, (d) => d.date) as\n | [Date, Date]\n | [undefined, undefined]\n if (ext[0] && ext[1]) return [ext[0], ext[1]]\n const now = new Date()\n return [now, now]\n }, [allData, zoom.zoomWindow])\n\n const xScale = useMemo(\n () =>\n scaleTime({\n range: [0, innerWidth],\n domain: xDomain,\n }),\n [innerWidth, xDomain],\n )\n\n const visibleData = useMemo(() => {\n const [start, end] = xDomain\n const t0 = start.valueOf()\n const t1 = end.valueOf()\n return allData.filter((d) => {\n const t = d.date.valueOf()\n return t >= t0 && t <= t1\n })\n }, [allData, xDomain])\n\n const reducedMotion = useReducedMotion()\n\n // With a unit, the y-axis is generated by the shared scale util (nice ticks\n // + rounded bounds) so it matches BarChart / PowerHistoryChart exactly.\n const yAxisScale = useMemo(() => {\n if (yAxisUnit === undefined) return null\n let min = 0\n let max = 0\n for (const d of visibleData) {\n if (d.value < min) min = d.value\n if (d.value > max) max = d.value\n }\n return buildYAxisScale(min, max)\n }, [visibleData, yAxisUnit])\n\n const targetYDomain = useMemo((): TimeDomain => {\n if (yDomain) return yDomain\n if (yAxisScale) return [yAxisScale.min, yAxisScale.max]\n return scaleLinear({\n domain: computeYDomain(visibleData),\n nice: true,\n }).domain() as [number, number]\n }, [visibleData, yAxisScale, yDomain])\n\n const displayedYDomain = useAnimatedChartDomain(targetYDomain, {\n animateChanges: zoom.isPinching,\n reducedMotion,\n })\n\n const yScale = useMemo(\n () =>\n scaleLinear({\n range: [innerHeight, 0],\n domain: [displayedYDomain[0], displayedYDomain[1]],\n }),\n [displayedYDomain, innerHeight],\n )\n\n const xTicks = zoom.isZoomed\n ? buildTimeTicks(\n xDomain[0].valueOf(),\n xDomain[1].valueOf(),\n xTickCount,\n ).map((tick) => new Date(tick))\n : xScale.ticks(xTickCount)\n const yTicks = yAxisScale ? yAxisScale.ticks : yScale.ticks(yTickCount)\n const formatAxisDate =\n zoom.isZoomed &&\n xDomain[1].valueOf() - xDomain[0].valueOf() <= DETAILED_X_AXIS_THRESHOLD\n ? (formatTooltipDate ?? formatDate)\n : formatDate\n\n const domainXFromEvent = useCallback(\n (\n event:\n | React.MouseEvent<SVGRectElement>\n | React.PointerEvent<SVGRectElement>\n | React.TouchEvent<SVGRectElement>,\n ) => {\n const point = localPoint(event)\n if (!point) return null\n return xScale.invert(point.x - margin.left).valueOf()\n },\n [margin.left, xScale],\n )\n\n const handleTooltip = useCallback(\n (\n event:\n | React.TouchEvent<SVGRectElement>\n | React.MouseEvent<SVGRectElement>,\n ) => {\n if (!enableTooltip || zoom.isBrushing || zoom.isPanning) return\n\n const point = localPoint(event)\n if (!point) return\n\n const x0 = xScale.invert(point.x - margin.left)\n\n // Snap to the data point closest to the cursor across all series, so the\n // cursor line + tooltip line up with real values.\n let anchorDate: Date | null = null\n let bestDistance = Infinity\n for (const s of series) {\n const d = nearestDatum(s.data, x0)\n if (!d) continue\n const distance = Math.abs(d.date.valueOf() - x0.valueOf())\n if (distance < bestDistance) {\n bestDistance = distance\n anchorDate = d.date\n }\n }\n\n if (!anchorDate) return\n\n showTooltip({\n tooltipData: { date: anchorDate },\n tooltipLeft: xScale(anchorDate) + margin.left,\n tooltipTop: margin.top,\n })\n },\n [\n enableTooltip,\n margin,\n series,\n showTooltip,\n xScale,\n zoom.isBrushing,\n zoom.isPanning,\n ],\n )\n\n const tooltipRows: ChartTooltipRow[] = tooltipData\n ? series.flatMap((s, i) => {\n const d = nearestDatum(s.data, tooltipData.date)\n if (!d || !withinDomain(s.data, tooltipData.date)) return []\n return [\n {\n key: s.id,\n label: s.label ?? s.id,\n color:\n s.color ?? (LEGEND_COLORS[i % LEGEND_COLORS.length] as string),\n value: formatValue(d.value),\n marker: s.dashed ? ('dashed-line' as const) : ('dot' as const),\n },\n ]\n })\n : []\n\n const [tooltipRef, tooltipSize] = useMeasuredSize<HTMLDivElement>()\n const tooltipPos = clampTooltipPosition({\n anchorX: tooltipLeft ?? 0,\n anchorY: tooltipTop ?? margin.top,\n width,\n height,\n tipWidth: tooltipSize.width,\n tipHeight: tooltipSize.height,\n placement: 'beside',\n })\n\n if (width < 10 || height < 10) return null\n\n return (\n <div className={cn('relative', className)}>\n <svg className=\"block\" height={height} width={width}>\n <defs>\n <clipPath id={plotClipId}>\n <rect height={innerHeight} width={innerWidth} x={0} y={0} />\n </clipPath>\n </defs>\n <Group left={margin.left} top={margin.top}>\n {/* Grid lines */}\n {showGrid &&\n yTicks.map((tick) => (\n <line\n className=\"stroke-gray-a4\"\n key={`grid-y-${tick}`}\n strokeDasharray=\"4,4\"\n x1={0}\n x2={innerWidth}\n y1={yScale(tick)}\n y2={yScale(tick)}\n />\n ))}\n\n {/* X axis */}\n <line\n className=\"stroke-gray-a6\"\n x1={0}\n x2={innerWidth}\n y1={innerHeight}\n y2={innerHeight}\n />\n\n {/* Reference markers (e.g. sunrise / sunset) */}\n {referenceLines?.map((ref) => {\n const x = xScale(ref.date)\n if (!Number.isFinite(x) || x < 0 || x > innerWidth) return null\n // Sit the label beside the line, on whichever side keeps it in\n // frame: right of a left-half marker, left of a right-half one.\n const anchorEnd = x > innerWidth / 2\n return (\n <g key={ref.key} pointerEvents=\"none\">\n <line\n className=\"stroke-orange-8\"\n strokeDasharray=\"3,3\"\n x1={x}\n x2={x}\n y1={0}\n y2={innerHeight}\n />\n {ref.label != null && (\n <text\n className=\"fill-orange-11 text-[10px]\"\n dy=\"0.9em\"\n textAnchor={anchorEnd ? 'end' : 'start'}\n x={x + (anchorEnd ? -4 : 4)}\n y={0}\n >\n {ref.label}\n </text>\n )}\n </g>\n )\n })}\n\n {/* X axis labels */}\n {xTicks.map((tick, index) => (\n <text\n dy=\"1em\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n key={`x-${tick.valueOf()}`}\n textAnchor={\n index === 0\n ? 'start'\n : index === xTicks.length - 1\n ? 'end'\n : 'middle'\n }\n x={xScale(tick)}\n y={innerHeight + 8}\n >\n {formatAxisDate(tick)}\n </text>\n ))}\n\n {/* Y axis. With `yAxisUnit`, matches BarChart: a vertical axis line,\n short tick marks, plain numeric ticks, and the unit as a bold\n top-left header (top tick hidden). Without it, ticks repeat\n `formatValue` and no axis line/marks are drawn. */}\n {yAxisUnit !== undefined && (\n <line\n stroke=\"var(--neutral-a6)\"\n strokeWidth={1}\n x1={0}\n x2={0}\n y1={0}\n y2={innerHeight}\n />\n )}\n {yTicks.map((tick) => {\n const y = yScale(tick)\n return (\n <g key={`y-${tick}`}>\n {yAxisUnit !== undefined && (\n <line\n stroke=\"var(--neutral-a8)\"\n strokeWidth={1}\n x1={-4}\n x2={0}\n y1={y}\n y2={y}\n />\n )}\n <text\n dy=\"0.32em\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n textAnchor=\"end\"\n x={yAxisUnit !== undefined ? -7 : -4}\n y={y}\n >\n {yAxisUnit !== undefined\n ? formatYTick(tick)\n : formatValue(tick)}\n </text>\n </g>\n )\n })}\n {yAxisUnit !== undefined && (\n <text\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n fontWeight={600}\n textAnchor=\"start\"\n x={-margin.left}\n y={-6}\n >\n {yAxisUnit}\n </text>\n )}\n\n <g clipPath={`url(#${plotClipId})`} data-testid=\"chart-plot-clip\">\n {series.map((s, i) => (\n <LinePath\n className={cn(LINE_COLORS[i % LINE_COLORS.length], s.className)}\n curve={resolveCurve(s.curve ?? defaultCurve)}\n data={s.data}\n fill=\"none\"\n key={s.id}\n strokeDasharray={\n s.dashed ? (s.dashPattern ?? '6,4') : undefined\n }\n strokeWidth={2}\n x={(d) => xScale(d.date)}\n y={(d) => yScale(d.value)}\n />\n ))}\n </g>\n\n {zoom.brushBand && (\n <rect\n data-testid=\"chart-brush-band\"\n fill=\"var(--spark-a4)\"\n height={innerHeight}\n pointerEvents=\"none\"\n width={Math.abs(\n xScale(new Date(zoom.brushBand[1])) -\n xScale(new Date(zoom.brushBand[0])),\n )}\n x={Math.min(\n xScale(new Date(zoom.brushBand[0])),\n xScale(new Date(zoom.brushBand[1])),\n )}\n y={0}\n />\n )}\n\n {(enableTooltip || zoomable) && (\n <rect\n fill=\"transparent\"\n height={innerHeight}\n style={zoomable ? { touchAction: 'pan-y' } : undefined}\n width={innerWidth}\n x={0}\n y={0}\n onMouseLeave={() => {\n if (!zoom.isBrushing) hideTooltip()\n }}\n onMouseMove={(event) => {\n if (zoom.isBrushing) {\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n return\n }\n handleTooltip(event)\n }}\n onPointerDown={(event) => {\n if (!zoomable || event.pointerType === 'touch') return\n if (event.button !== 0) return\n const domainX = domainXFromEvent(event)\n if (domainX == null) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n hideTooltip()\n zoom.onPointerDown(domainX)\n }}\n onPointerMove={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n }}\n onPointerUp={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerUp(domainX)\n else zoom.onPointerCancel()\n }}\n onPointerCancel={() => {\n zoom.onPointerCancel()\n }}\n onDoubleClick={() => {\n if (zoomable) zoom.onDoubleClick()\n }}\n onTouchStart={(event) => {\n if (!zoomable) return\n if (event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - margin.left) / innerWidth),\n )\n zoom.onPinchStart(touchDistance(a, b), midpoint)\n hideTooltip()\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanStart({\n x: (point.x - margin.left) / innerWidth,\n y: (point.y - margin.top) / innerHeight,\n })\n hideTooltip()\n }\n }}\n onTouchMove={(event) => {\n if (zoomable && event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - margin.left) / innerWidth),\n )\n zoom.onPinchMove(touchDistance(a, b), midpoint)\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanMove({\n x: (point.x - margin.left) / innerWidth,\n y: (point.y - margin.top) / innerHeight,\n })\n return\n }\n if (event.touches.length === 1) handleTooltip(event)\n }}\n onTouchEnd={(event) => {\n if (zoomable && event.touches.length < 2) zoom.onPinchEnd()\n if (event.touches.length === 0) {\n if (zoomable) zoom.onPanEnd()\n if (zoomable) zoom.onTap()\n hideTooltip()\n }\n }}\n onTouchCancel={() => {\n zoom.onPinchEnd()\n zoom.onPanEnd()\n }}\n />\n )}\n\n {/* Cursor line + a dot per series */}\n {tooltipOpen &&\n tooltipData &&\n !zoom.isBrushing &&\n !zoom.isPanning && (\n <>\n <line\n className=\"stroke-gray-a6\"\n pointerEvents=\"none\"\n strokeDasharray=\"4,4\"\n x1={xScale(tooltipData.date)}\n x2={xScale(tooltipData.date)}\n y1={0}\n y2={innerHeight}\n />\n {series.map((s, i) => {\n const d = nearestDatum(s.data, tooltipData.date)\n if (!d || !withinDomain(s.data, tooltipData.date)) return null\n return (\n <circle\n key={s.id}\n className={\n s.color\n ? undefined\n : cn(DOT_COLORS[i % DOT_COLORS.length])\n }\n cx={xScale(d.date)}\n cy={yScale(d.value)}\n fill={s.color}\n pointerEvents=\"none\"\n r={4}\n stroke=\"white\"\n strokeWidth={2}\n />\n )\n })}\n </>\n )}\n </Group>\n </svg>\n\n {topContent && topContent.length > 0 && (\n <div\n className=\"pointer-events-none absolute overflow-hidden\"\n style={{\n height: margin.top,\n left: margin.left,\n top: 0,\n width: innerWidth,\n }}\n >\n {topContent.map((item) => {\n const x = xScale(item.date)\n if (!Number.isFinite(x) || x < 0 || x > innerWidth) return null\n return (\n <div\n key={item.key}\n className=\"absolute -translate-x-1/2\"\n style={{ left: x, top: 0 }}\n >\n {item.content}\n </div>\n )\n })}\n </div>\n )}\n\n {zoom.isZoomed && zoomResetLabel && (\n <ChartZoomResetButton label={zoomResetLabel} onReset={zoom.resetZoom} />\n )}\n\n <div\n ref={tooltipRef}\n className=\"pointer-events-none absolute z-10\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n opacity:\n tooltipOpen && tooltipData && !zoom.isPanning && !zoom.isBrushing\n ? 1\n : 0,\n transition: reducedMotion\n ? 'opacity 140ms ease-out'\n : 'left 140ms ease-out, top 140ms ease-out, opacity 140ms ease-out',\n }}\n >\n {tooltipData && (\n <ChartTooltipCard\n label={(formatTooltipDate ?? formatDate)(tooltipData.date)}\n rows={tooltipRows}\n />\n )}\n </div>\n </div>\n )\n}\n\nexport { LineChart }\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useAnimatedChartDomain, useChartZoom } from "../hooks/use-chart-zoom.js";
|
|
3
3
|
import { useReducedMotion } from "../hooks/use-reduced-motion.js";
|
|
4
4
|
import { cn } from "../utils/cn.js";
|
|
5
|
-
import { localeNumberFormat } from "../utils/format.js";
|
|
5
|
+
import { getDocumentLang, localeNumberFormat } from "../utils/format.js";
|
|
6
6
|
import { buildYAxisScale, formatYAxisTick } from "../utils/chart-scale.js";
|
|
7
7
|
import { buildTimeTicks } from "../utils/chart-time-ticks.js";
|
|
8
8
|
import { ChartTooltipCard, ChartZoomResetButton, clampTooltipPosition, useMeasuredSize } from "./chart.js";
|
|
@@ -67,7 +67,7 @@ function nearestDatum(data, date) {
|
|
|
67
67
|
if (!d1) return d0;
|
|
68
68
|
return date.valueOf() - d0.date.valueOf() > d1.date.valueOf() - date.valueOf() ? d1 : d0;
|
|
69
69
|
}
|
|
70
|
-
const defaultFormatDate = (date) => date.toLocaleDateString(
|
|
70
|
+
const defaultFormatDate = (date) => date.toLocaleDateString(getDocumentLang(), {
|
|
71
71
|
month: "short",
|
|
72
72
|
day: "numeric"
|
|
73
73
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"line-chart.js","names":["React"],"sources":["../../src/components/line-chart.tsx"],"sourcesContent":["import {\n curveLinear,\n curveMonotoneX,\n curveStep,\n curveStepAfter,\n curveStepBefore,\n} from '@visx/curve'\nimport { localPoint } from '@visx/event'\nimport { Group } from '@visx/group'\nimport { scaleLinear, scaleTime } from '@visx/scale'\nimport { LinePath } from '@visx/shape'\nimport { useTooltip } from '@visx/tooltip'\nimport { bisector, extent } from '@visx/vendor/d3-array'\nimport * as React from 'react'\nimport { useCallback, useMemo } from 'react'\nimport { useAnimatedChartDomain, useChartZoom } from '../hooks/use-chart-zoom'\nimport { useReducedMotion } from '../hooks/use-reduced-motion'\nimport { buildYAxisScale, formatYAxisTick } from '../utils/chart-scale'\nimport { buildTimeTicks } from '../utils/chart-time-ticks'\nimport type { TimeDomain } from '../utils/chart-zoom'\nimport { cn } from '../utils/cn'\nimport { localeNumberFormat } from '../utils/format'\nimport {\n ChartTooltipCard,\n type ChartTooltipRow,\n ChartZoomResetButton,\n clampTooltipPosition,\n useMeasuredSize,\n} from './chart'\n\nexport interface LineChartDatum {\n date: Date\n value: number\n}\n\nexport type LineChartCurve =\n | 'monotone'\n | 'step'\n | 'step-after'\n | 'step-before'\n | 'linear'\n\nexport interface LineChartSeries {\n id: string\n data: LineChartDatum[]\n /** Label shown in the hover tooltip. Defaults to `id`. */\n label?: React.ReactNode\n className?: string\n /**\n * CSS color for the tooltip swatch and hover dot (e.g. `var(--orange-9)`).\n * Defaults to the palette color for the series index. Set this whenever\n * `className` overrides the stroke so the tooltip stays in sync.\n */\n color?: string\n /** Curve interpolation type. Default: inherits from LineChartProps.curve */\n curve?: LineChartCurve\n /** Render as a dashed line */\n dashed?: boolean\n /** Custom dash pattern (e.g. \"8,4\" or \"4,2,1,2\"). Only applies when dashed is true. */\n dashPattern?: string\n}\n\nexport interface LineChartProps {\n series: LineChartSeries[]\n width: number\n height: number\n margin?: { top: number; right: number; bottom: number; left: number }\n /** Default curve for all series. Default: 'monotone' */\n curve?: LineChartCurve\n showGrid?: boolean\n showTooltip?: boolean\n className?: string\n xTickCount?: number\n yTickCount?: number\n /** Fixes the y-axis to an explicit `[min, max]`. Defaults to the data extent. */\n yDomain?: [number, number]\n /** Formats the x-axis tick labels. */\n formatDate?: (date: Date) => string\n /** Formats the tooltip title. Defaults to `formatDate`. */\n formatTooltipDate?: (date: Date) => string\n formatValue?: (value: number) => string\n /**\n * Unit shown once at the top of the y-axis (e.g. \"kW\"). When set, y-axis tick\n * labels render as plain numbers (matching BarChart / PowerHistoryChart)\n * instead of repeating `formatValue` on every tick. `formatValue` still feeds\n * the tooltip. Omit to keep the per-tick `formatValue` labels.\n */\n yAxisUnit?: string\n /** Overrides the y-axis tick formatter used when `yAxisUnit` is set. Defaults to the shared compact formatter. */\n formatYTick?: (value: number) => string\n /**\n * Vertical reference markers positioned on the time axis (e.g. sunrise /\n * sunset). Each renders a dashed line across the plot with an optional label.\n */\n referenceLines?: Array<{ key: string; date: Date; label?: React.ReactNode }>\n /**\n * HTML content pinned to the top of the plot at a given time (e.g. an hourly\n * weather icon). Positioned by the same time scale as the series, so it lines\n * up with the curve below it.\n */\n topContent?: Array<{ key: string; date: Date; content: React.ReactNode }>\n zoomable?: boolean\n zoomResetLabel?: string\n zoomDomain?: [Date, Date] | null\n onZoomDomainChange?: (domain: [Date, Date] | null) => void\n}\n\nconst DEFAULT_MARGIN = { top: 16, right: 16, bottom: 40, left: 48 }\nconst DETAILED_X_AXIS_THRESHOLD = 6 * 60 * 60 * 1000\n\nconst CURVE_MAP = {\n monotone: curveMonotoneX,\n step: curveStep,\n 'step-after': curveStepAfter,\n 'step-before': curveStepBefore,\n linear: curveLinear,\n} as const\n\nconst resolveCurve = (type: LineChartCurve) => CURVE_MAP[type]\n\nconst dateBisector = bisector<LineChartDatum, Date>((d) => d.date)\nconst bisectDate = (data: LineChartDatum[], x: Date, lo?: number) =>\n dateBisector.left(data, x, lo)\n\n/** Whether `date` falls within a series' own x-range (data is sorted ascending). */\nfunction withinDomain(data: LineChartDatum[], date: Date): boolean {\n const first = data[0]\n const last = data[data.length - 1]\n if (!first || !last) return false\n const t = date.valueOf()\n return t >= first.date.valueOf() && t <= last.date.valueOf()\n}\n\n/**\n * y-domain padded 10% beyond the data extent. The baseline is pinned to 0 so\n * all-positive series render from the axis up; a series that dips below 0\n * (signed grid / battery power) extends the domain downward to keep the zero\n * crossing visible.\n */\nfunction computeYDomain(data: LineChartDatum[]): [number, number] {\n let min = 0\n let max = 0\n for (const d of data) {\n if (d.value < min) min = d.value\n if (d.value > max) max = d.value\n }\n if (min === max) return [min, min + 1]\n return [min * 1.1, max * 1.1]\n}\n\nfunction nearestDatum(\n data: LineChartDatum[],\n date: Date,\n): LineChartDatum | undefined {\n if (data.length === 0) return undefined\n const index = bisectDate(data, date, 1)\n const d0 = data[index - 1]\n const d1 = data[index]\n if (!d0) return d1\n if (!d1) return d0\n return date.valueOf() - d0.date.valueOf() > d1.date.valueOf() - date.valueOf()\n ? d1\n : d0\n}\n\nconst defaultFormatDate = (date: Date) =>\n date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })\n\nconst defaultValueFormat = localeNumberFormat()\nconst defaultFormatValue = (value: number) => defaultValueFormat.format(value)\n\nconst LINE_COLORS = [\n 'stroke-spark-9',\n 'stroke-green-9',\n 'stroke-blue-9',\n 'stroke-orange-9',\n 'stroke-red-9',\n]\n\nconst DOT_COLORS = [\n 'fill-spark-9',\n 'fill-green-9',\n 'fill-blue-9',\n 'fill-orange-9',\n 'fill-red-9',\n]\n\n// CSS-var colours matching LINE_COLORS, used for the tooltip legend dots.\nconst LEGEND_COLORS = [\n 'var(--spark-9)',\n 'var(--green-9)',\n 'var(--blue-9)',\n 'var(--orange-9)',\n 'var(--red-9)',\n]\n\nfunction toTimeDomain(dates: [Date, Date]): TimeDomain {\n return [dates[0].valueOf(), dates[1].valueOf()]\n}\n\nfunction fromTimeDomain(domain: TimeDomain): [Date, Date] {\n return [new Date(domain[0]), new Date(domain[1])]\n}\n\nfunction touchDistance(a: React.Touch, b: React.Touch): number {\n return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY)\n}\n\nfunction touchMidpoint(\n a: React.Touch,\n b: React.Touch,\n): {\n clientX: number\n clientY: number\n} {\n return {\n clientX: (a.clientX + b.clientX) / 2,\n clientY: (a.clientY + b.clientY) / 2,\n }\n}\n\n// oxlint-disable-next-line eslint/complexity -- pre-existing; split it before adding branches\nconst LineChart: React.FC<LineChartProps> = ({\n series,\n width,\n height,\n margin = DEFAULT_MARGIN,\n curve: defaultCurve = 'monotone',\n showGrid = true,\n showTooltip: enableTooltip = true,\n className,\n xTickCount = 5,\n yTickCount = 4,\n formatDate = defaultFormatDate,\n formatTooltipDate,\n formatValue = defaultFormatValue,\n yAxisUnit,\n formatYTick = formatYAxisTick,\n yDomain,\n referenceLines,\n topContent,\n zoomable = false,\n zoomResetLabel,\n zoomDomain: zoomDomainProp,\n onZoomDomainChange,\n}) => {\n const plotClipId = React.useId()\n const {\n showTooltip,\n hideTooltip,\n tooltipData,\n tooltipLeft,\n tooltipTop,\n tooltipOpen,\n } = useTooltip<{ date: Date }>()\n\n const innerWidth = Math.max(0, width - margin.left - margin.right)\n const innerHeight = Math.max(0, height - margin.top - margin.bottom)\n\n const allData = useMemo(() => series.flatMap((s) => s.data), [series])\n\n const fullDomain = useMemo((): TimeDomain | null => {\n const ext = extent(allData, (d) => d.date.valueOf())\n if (ext[0] == null || ext[1] == null) return null\n return [ext[0], ext[1]]\n }, [allData])\n\n const controlledZoomDomain =\n zoomDomainProp === undefined\n ? undefined\n : zoomDomainProp == null\n ? null\n : toTimeDomain(zoomDomainProp)\n\n const handleZoomDomainChange = useCallback(\n (domain: TimeDomain | null) => {\n onZoomDomainChange?.(domain ? fromTimeDomain(domain) : null)\n },\n [onZoomDomainChange],\n )\n\n const zoom = useChartZoom({\n enabled: zoomable,\n fullDomain,\n zoomDomain: controlledZoomDomain,\n onZoomDomainChange: handleZoomDomainChange,\n })\n\n const xDomain = useMemo((): [Date, Date] => {\n if (zoom.zoomWindow) return fromTimeDomain(zoom.zoomWindow)\n const ext = extent(allData, (d) => d.date) as\n | [Date, Date]\n | [undefined, undefined]\n if (ext[0] && ext[1]) return [ext[0], ext[1]]\n const now = new Date()\n return [now, now]\n }, [allData, zoom.zoomWindow])\n\n const xScale = useMemo(\n () =>\n scaleTime({\n range: [0, innerWidth],\n domain: xDomain,\n }),\n [innerWidth, xDomain],\n )\n\n const visibleData = useMemo(() => {\n const [start, end] = xDomain\n const t0 = start.valueOf()\n const t1 = end.valueOf()\n return allData.filter((d) => {\n const t = d.date.valueOf()\n return t >= t0 && t <= t1\n })\n }, [allData, xDomain])\n\n const reducedMotion = useReducedMotion()\n\n // With a unit, the y-axis is generated by the shared scale util (nice ticks\n // + rounded bounds) so it matches BarChart / PowerHistoryChart exactly.\n const yAxisScale = useMemo(() => {\n if (yAxisUnit === undefined) return null\n let min = 0\n let max = 0\n for (const d of visibleData) {\n if (d.value < min) min = d.value\n if (d.value > max) max = d.value\n }\n return buildYAxisScale(min, max)\n }, [visibleData, yAxisUnit])\n\n const targetYDomain = useMemo((): TimeDomain => {\n if (yDomain) return yDomain\n if (yAxisScale) return [yAxisScale.min, yAxisScale.max]\n return scaleLinear({\n domain: computeYDomain(visibleData),\n nice: true,\n }).domain() as [number, number]\n }, [visibleData, yAxisScale, yDomain])\n\n const displayedYDomain = useAnimatedChartDomain(targetYDomain, {\n animateChanges: zoom.isPinching,\n reducedMotion,\n })\n\n const yScale = useMemo(\n () =>\n scaleLinear({\n range: [innerHeight, 0],\n domain: [displayedYDomain[0], displayedYDomain[1]],\n }),\n [displayedYDomain, innerHeight],\n )\n\n const xTicks = zoom.isZoomed\n ? buildTimeTicks(\n xDomain[0].valueOf(),\n xDomain[1].valueOf(),\n xTickCount,\n ).map((tick) => new Date(tick))\n : xScale.ticks(xTickCount)\n const yTicks = yAxisScale ? yAxisScale.ticks : yScale.ticks(yTickCount)\n const formatAxisDate =\n zoom.isZoomed &&\n xDomain[1].valueOf() - xDomain[0].valueOf() <= DETAILED_X_AXIS_THRESHOLD\n ? (formatTooltipDate ?? formatDate)\n : formatDate\n\n const domainXFromEvent = useCallback(\n (\n event:\n | React.MouseEvent<SVGRectElement>\n | React.PointerEvent<SVGRectElement>\n | React.TouchEvent<SVGRectElement>,\n ) => {\n const point = localPoint(event)\n if (!point) return null\n return xScale.invert(point.x - margin.left).valueOf()\n },\n [margin.left, xScale],\n )\n\n const handleTooltip = useCallback(\n (\n event:\n | React.TouchEvent<SVGRectElement>\n | React.MouseEvent<SVGRectElement>,\n ) => {\n if (!enableTooltip || zoom.isBrushing || zoom.isPanning) return\n\n const point = localPoint(event)\n if (!point) return\n\n const x0 = xScale.invert(point.x - margin.left)\n\n // Snap to the data point closest to the cursor across all series, so the\n // cursor line + tooltip line up with real values.\n let anchorDate: Date | null = null\n let bestDistance = Infinity\n for (const s of series) {\n const d = nearestDatum(s.data, x0)\n if (!d) continue\n const distance = Math.abs(d.date.valueOf() - x0.valueOf())\n if (distance < bestDistance) {\n bestDistance = distance\n anchorDate = d.date\n }\n }\n\n if (!anchorDate) return\n\n showTooltip({\n tooltipData: { date: anchorDate },\n tooltipLeft: xScale(anchorDate) + margin.left,\n tooltipTop: margin.top,\n })\n },\n [\n enableTooltip,\n margin,\n series,\n showTooltip,\n xScale,\n zoom.isBrushing,\n zoom.isPanning,\n ],\n )\n\n const tooltipRows: ChartTooltipRow[] = tooltipData\n ? series.flatMap((s, i) => {\n const d = nearestDatum(s.data, tooltipData.date)\n if (!d || !withinDomain(s.data, tooltipData.date)) return []\n return [\n {\n key: s.id,\n label: s.label ?? s.id,\n color:\n s.color ?? (LEGEND_COLORS[i % LEGEND_COLORS.length] as string),\n value: formatValue(d.value),\n marker: s.dashed ? ('dashed-line' as const) : ('dot' as const),\n },\n ]\n })\n : []\n\n const [tooltipRef, tooltipSize] = useMeasuredSize<HTMLDivElement>()\n const tooltipPos = clampTooltipPosition({\n anchorX: tooltipLeft ?? 0,\n anchorY: tooltipTop ?? margin.top,\n width,\n height,\n tipWidth: tooltipSize.width,\n tipHeight: tooltipSize.height,\n placement: 'beside',\n })\n\n if (width < 10 || height < 10) return null\n\n return (\n <div className={cn('relative', className)}>\n <svg className=\"block\" height={height} width={width}>\n <defs>\n <clipPath id={plotClipId}>\n <rect height={innerHeight} width={innerWidth} x={0} y={0} />\n </clipPath>\n </defs>\n <Group left={margin.left} top={margin.top}>\n {/* Grid lines */}\n {showGrid &&\n yTicks.map((tick) => (\n <line\n className=\"stroke-gray-a4\"\n key={`grid-y-${tick}`}\n strokeDasharray=\"4,4\"\n x1={0}\n x2={innerWidth}\n y1={yScale(tick)}\n y2={yScale(tick)}\n />\n ))}\n\n {/* X axis */}\n <line\n className=\"stroke-gray-a6\"\n x1={0}\n x2={innerWidth}\n y1={innerHeight}\n y2={innerHeight}\n />\n\n {/* Reference markers (e.g. sunrise / sunset) */}\n {referenceLines?.map((ref) => {\n const x = xScale(ref.date)\n if (!Number.isFinite(x) || x < 0 || x > innerWidth) return null\n // Sit the label beside the line, on whichever side keeps it in\n // frame: right of a left-half marker, left of a right-half one.\n const anchorEnd = x > innerWidth / 2\n return (\n <g key={ref.key} pointerEvents=\"none\">\n <line\n className=\"stroke-orange-8\"\n strokeDasharray=\"3,3\"\n x1={x}\n x2={x}\n y1={0}\n y2={innerHeight}\n />\n {ref.label != null && (\n <text\n className=\"fill-orange-11 text-[10px]\"\n dy=\"0.9em\"\n textAnchor={anchorEnd ? 'end' : 'start'}\n x={x + (anchorEnd ? -4 : 4)}\n y={0}\n >\n {ref.label}\n </text>\n )}\n </g>\n )\n })}\n\n {/* X axis labels */}\n {xTicks.map((tick, index) => (\n <text\n dy=\"1em\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n key={`x-${tick.valueOf()}`}\n textAnchor={\n index === 0\n ? 'start'\n : index === xTicks.length - 1\n ? 'end'\n : 'middle'\n }\n x={xScale(tick)}\n y={innerHeight + 8}\n >\n {formatAxisDate(tick)}\n </text>\n ))}\n\n {/* Y axis. With `yAxisUnit`, matches BarChart: a vertical axis line,\n short tick marks, plain numeric ticks, and the unit as a bold\n top-left header (top tick hidden). Without it, ticks repeat\n `formatValue` and no axis line/marks are drawn. */}\n {yAxisUnit !== undefined && (\n <line\n stroke=\"var(--neutral-a6)\"\n strokeWidth={1}\n x1={0}\n x2={0}\n y1={0}\n y2={innerHeight}\n />\n )}\n {yTicks.map((tick) => {\n const y = yScale(tick)\n return (\n <g key={`y-${tick}`}>\n {yAxisUnit !== undefined && (\n <line\n stroke=\"var(--neutral-a8)\"\n strokeWidth={1}\n x1={-4}\n x2={0}\n y1={y}\n y2={y}\n />\n )}\n <text\n dy=\"0.32em\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n textAnchor=\"end\"\n x={yAxisUnit !== undefined ? -7 : -4}\n y={y}\n >\n {yAxisUnit !== undefined\n ? formatYTick(tick)\n : formatValue(tick)}\n </text>\n </g>\n )\n })}\n {yAxisUnit !== undefined && (\n <text\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n fontWeight={600}\n textAnchor=\"start\"\n x={-margin.left}\n y={-6}\n >\n {yAxisUnit}\n </text>\n )}\n\n <g clipPath={`url(#${plotClipId})`} data-testid=\"chart-plot-clip\">\n {series.map((s, i) => (\n <LinePath\n className={cn(LINE_COLORS[i % LINE_COLORS.length], s.className)}\n curve={resolveCurve(s.curve ?? defaultCurve)}\n data={s.data}\n fill=\"none\"\n key={s.id}\n strokeDasharray={\n s.dashed ? (s.dashPattern ?? '6,4') : undefined\n }\n strokeWidth={2}\n x={(d) => xScale(d.date)}\n y={(d) => yScale(d.value)}\n />\n ))}\n </g>\n\n {zoom.brushBand && (\n <rect\n data-testid=\"chart-brush-band\"\n fill=\"var(--spark-a4)\"\n height={innerHeight}\n pointerEvents=\"none\"\n width={Math.abs(\n xScale(new Date(zoom.brushBand[1])) -\n xScale(new Date(zoom.brushBand[0])),\n )}\n x={Math.min(\n xScale(new Date(zoom.brushBand[0])),\n xScale(new Date(zoom.brushBand[1])),\n )}\n y={0}\n />\n )}\n\n {(enableTooltip || zoomable) && (\n <rect\n fill=\"transparent\"\n height={innerHeight}\n style={zoomable ? { touchAction: 'pan-y' } : undefined}\n width={innerWidth}\n x={0}\n y={0}\n onMouseLeave={() => {\n if (!zoom.isBrushing) hideTooltip()\n }}\n onMouseMove={(event) => {\n if (zoom.isBrushing) {\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n return\n }\n handleTooltip(event)\n }}\n onPointerDown={(event) => {\n if (!zoomable || event.pointerType === 'touch') return\n if (event.button !== 0) return\n const domainX = domainXFromEvent(event)\n if (domainX == null) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n hideTooltip()\n zoom.onPointerDown(domainX)\n }}\n onPointerMove={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n }}\n onPointerUp={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerUp(domainX)\n else zoom.onPointerCancel()\n }}\n onPointerCancel={() => {\n zoom.onPointerCancel()\n }}\n onDoubleClick={() => {\n if (zoomable) zoom.onDoubleClick()\n }}\n onTouchStart={(event) => {\n if (!zoomable) return\n if (event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - margin.left) / innerWidth),\n )\n zoom.onPinchStart(touchDistance(a, b), midpoint)\n hideTooltip()\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanStart({\n x: (point.x - margin.left) / innerWidth,\n y: (point.y - margin.top) / innerHeight,\n })\n hideTooltip()\n }\n }}\n onTouchMove={(event) => {\n if (zoomable && event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - margin.left) / innerWidth),\n )\n zoom.onPinchMove(touchDistance(a, b), midpoint)\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanMove({\n x: (point.x - margin.left) / innerWidth,\n y: (point.y - margin.top) / innerHeight,\n })\n return\n }\n if (event.touches.length === 1) handleTooltip(event)\n }}\n onTouchEnd={(event) => {\n if (zoomable && event.touches.length < 2) zoom.onPinchEnd()\n if (event.touches.length === 0) {\n if (zoomable) zoom.onPanEnd()\n if (zoomable) zoom.onTap()\n hideTooltip()\n }\n }}\n onTouchCancel={() => {\n zoom.onPinchEnd()\n zoom.onPanEnd()\n }}\n />\n )}\n\n {/* Cursor line + a dot per series */}\n {tooltipOpen &&\n tooltipData &&\n !zoom.isBrushing &&\n !zoom.isPanning && (\n <>\n <line\n className=\"stroke-gray-a6\"\n pointerEvents=\"none\"\n strokeDasharray=\"4,4\"\n x1={xScale(tooltipData.date)}\n x2={xScale(tooltipData.date)}\n y1={0}\n y2={innerHeight}\n />\n {series.map((s, i) => {\n const d = nearestDatum(s.data, tooltipData.date)\n if (!d || !withinDomain(s.data, tooltipData.date)) return null\n return (\n <circle\n key={s.id}\n className={\n s.color\n ? undefined\n : cn(DOT_COLORS[i % DOT_COLORS.length])\n }\n cx={xScale(d.date)}\n cy={yScale(d.value)}\n fill={s.color}\n pointerEvents=\"none\"\n r={4}\n stroke=\"white\"\n strokeWidth={2}\n />\n )\n })}\n </>\n )}\n </Group>\n </svg>\n\n {topContent && topContent.length > 0 && (\n <div\n className=\"pointer-events-none absolute overflow-hidden\"\n style={{\n height: margin.top,\n left: margin.left,\n top: 0,\n width: innerWidth,\n }}\n >\n {topContent.map((item) => {\n const x = xScale(item.date)\n if (!Number.isFinite(x) || x < 0 || x > innerWidth) return null\n return (\n <div\n key={item.key}\n className=\"absolute -translate-x-1/2\"\n style={{ left: x, top: 0 }}\n >\n {item.content}\n </div>\n )\n })}\n </div>\n )}\n\n {zoom.isZoomed && zoomResetLabel && (\n <ChartZoomResetButton label={zoomResetLabel} onReset={zoom.resetZoom} />\n )}\n\n <div\n ref={tooltipRef}\n className=\"pointer-events-none absolute z-10\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n opacity:\n tooltipOpen && tooltipData && !zoom.isPanning && !zoom.isBrushing\n ? 1\n : 0,\n transition: reducedMotion\n ? 'opacity 140ms ease-out'\n : 'left 140ms ease-out, top 140ms ease-out, opacity 140ms ease-out',\n }}\n >\n {tooltipData && (\n <ChartTooltipCard\n label={(formatTooltipDate ?? formatDate)(tooltipData.date)}\n rows={tooltipRows}\n />\n )}\n </div>\n </div>\n )\n}\n\nexport { LineChart }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"line-chart.js","names":["React"],"sources":["../../src/components/line-chart.tsx"],"sourcesContent":["import {\n curveLinear,\n curveMonotoneX,\n curveStep,\n curveStepAfter,\n curveStepBefore,\n} from '@visx/curve'\nimport { localPoint } from '@visx/event'\nimport { Group } from '@visx/group'\nimport { scaleLinear, scaleTime } from '@visx/scale'\nimport { LinePath } from '@visx/shape'\nimport { useTooltip } from '@visx/tooltip'\nimport { bisector, extent } from '@visx/vendor/d3-array'\nimport * as React from 'react'\nimport { useCallback, useMemo } from 'react'\nimport { useAnimatedChartDomain, useChartZoom } from '../hooks/use-chart-zoom'\nimport { useReducedMotion } from '../hooks/use-reduced-motion'\nimport { buildYAxisScale, formatYAxisTick } from '../utils/chart-scale'\nimport { buildTimeTicks } from '../utils/chart-time-ticks'\nimport type { TimeDomain } from '../utils/chart-zoom'\nimport { cn } from '../utils/cn'\nimport { getDocumentLang, localeNumberFormat } from '../utils/format'\nimport {\n ChartTooltipCard,\n type ChartTooltipRow,\n ChartZoomResetButton,\n clampTooltipPosition,\n useMeasuredSize,\n} from './chart'\n\nexport interface LineChartDatum {\n date: Date\n value: number\n}\n\nexport type LineChartCurve =\n | 'monotone'\n | 'step'\n | 'step-after'\n | 'step-before'\n | 'linear'\n\nexport interface LineChartSeries {\n id: string\n data: LineChartDatum[]\n /** Label shown in the hover tooltip. Defaults to `id`. */\n label?: React.ReactNode\n className?: string\n /**\n * CSS color for the tooltip swatch and hover dot (e.g. `var(--orange-9)`).\n * Defaults to the palette color for the series index. Set this whenever\n * `className` overrides the stroke so the tooltip stays in sync.\n */\n color?: string\n /** Curve interpolation type. Default: inherits from LineChartProps.curve */\n curve?: LineChartCurve\n /** Render as a dashed line */\n dashed?: boolean\n /** Custom dash pattern (e.g. \"8,4\" or \"4,2,1,2\"). Only applies when dashed is true. */\n dashPattern?: string\n}\n\nexport interface LineChartProps {\n series: LineChartSeries[]\n width: number\n height: number\n margin?: { top: number; right: number; bottom: number; left: number }\n /** Default curve for all series. Default: 'monotone' */\n curve?: LineChartCurve\n showGrid?: boolean\n showTooltip?: boolean\n className?: string\n xTickCount?: number\n yTickCount?: number\n /** Fixes the y-axis to an explicit `[min, max]`. Defaults to the data extent. */\n yDomain?: [number, number]\n /** Formats the x-axis tick labels. */\n formatDate?: (date: Date) => string\n /** Formats the tooltip title. Defaults to `formatDate`. */\n formatTooltipDate?: (date: Date) => string\n formatValue?: (value: number) => string\n /**\n * Unit shown once at the top of the y-axis (e.g. \"kW\"). When set, y-axis tick\n * labels render as plain numbers (matching BarChart / PowerHistoryChart)\n * instead of repeating `formatValue` on every tick. `formatValue` still feeds\n * the tooltip. Omit to keep the per-tick `formatValue` labels.\n */\n yAxisUnit?: string\n /** Overrides the y-axis tick formatter used when `yAxisUnit` is set. Defaults to the shared compact formatter. */\n formatYTick?: (value: number) => string\n /**\n * Vertical reference markers positioned on the time axis (e.g. sunrise /\n * sunset). Each renders a dashed line across the plot with an optional label.\n */\n referenceLines?: Array<{ key: string; date: Date; label?: React.ReactNode }>\n /**\n * HTML content pinned to the top of the plot at a given time (e.g. an hourly\n * weather icon). Positioned by the same time scale as the series, so it lines\n * up with the curve below it.\n */\n topContent?: Array<{ key: string; date: Date; content: React.ReactNode }>\n zoomable?: boolean\n zoomResetLabel?: string\n zoomDomain?: [Date, Date] | null\n onZoomDomainChange?: (domain: [Date, Date] | null) => void\n}\n\nconst DEFAULT_MARGIN = { top: 16, right: 16, bottom: 40, left: 48 }\nconst DETAILED_X_AXIS_THRESHOLD = 6 * 60 * 60 * 1000\n\nconst CURVE_MAP = {\n monotone: curveMonotoneX,\n step: curveStep,\n 'step-after': curveStepAfter,\n 'step-before': curveStepBefore,\n linear: curveLinear,\n} as const\n\nconst resolveCurve = (type: LineChartCurve) => CURVE_MAP[type]\n\nconst dateBisector = bisector<LineChartDatum, Date>((d) => d.date)\nconst bisectDate = (data: LineChartDatum[], x: Date, lo?: number) =>\n dateBisector.left(data, x, lo)\n\n/** Whether `date` falls within a series' own x-range (data is sorted ascending). */\nfunction withinDomain(data: LineChartDatum[], date: Date): boolean {\n const first = data[0]\n const last = data[data.length - 1]\n if (!first || !last) return false\n const t = date.valueOf()\n return t >= first.date.valueOf() && t <= last.date.valueOf()\n}\n\n/**\n * y-domain padded 10% beyond the data extent. The baseline is pinned to 0 so\n * all-positive series render from the axis up; a series that dips below 0\n * (signed grid / battery power) extends the domain downward to keep the zero\n * crossing visible.\n */\nfunction computeYDomain(data: LineChartDatum[]): [number, number] {\n let min = 0\n let max = 0\n for (const d of data) {\n if (d.value < min) min = d.value\n if (d.value > max) max = d.value\n }\n if (min === max) return [min, min + 1]\n return [min * 1.1, max * 1.1]\n}\n\nfunction nearestDatum(\n data: LineChartDatum[],\n date: Date,\n): LineChartDatum | undefined {\n if (data.length === 0) return undefined\n const index = bisectDate(data, date, 1)\n const d0 = data[index - 1]\n const d1 = data[index]\n if (!d0) return d1\n if (!d1) return d0\n return date.valueOf() - d0.date.valueOf() > d1.date.valueOf() - date.valueOf()\n ? d1\n : d0\n}\n\nconst defaultFormatDate = (date: Date) =>\n date.toLocaleDateString(getDocumentLang(), { month: 'short', day: 'numeric' })\n\nconst defaultValueFormat = localeNumberFormat()\nconst defaultFormatValue = (value: number) => defaultValueFormat.format(value)\n\nconst LINE_COLORS = [\n 'stroke-spark-9',\n 'stroke-green-9',\n 'stroke-blue-9',\n 'stroke-orange-9',\n 'stroke-red-9',\n]\n\nconst DOT_COLORS = [\n 'fill-spark-9',\n 'fill-green-9',\n 'fill-blue-9',\n 'fill-orange-9',\n 'fill-red-9',\n]\n\n// CSS-var colours matching LINE_COLORS, used for the tooltip legend dots.\nconst LEGEND_COLORS = [\n 'var(--spark-9)',\n 'var(--green-9)',\n 'var(--blue-9)',\n 'var(--orange-9)',\n 'var(--red-9)',\n]\n\nfunction toTimeDomain(dates: [Date, Date]): TimeDomain {\n return [dates[0].valueOf(), dates[1].valueOf()]\n}\n\nfunction fromTimeDomain(domain: TimeDomain): [Date, Date] {\n return [new Date(domain[0]), new Date(domain[1])]\n}\n\nfunction touchDistance(a: React.Touch, b: React.Touch): number {\n return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY)\n}\n\nfunction touchMidpoint(\n a: React.Touch,\n b: React.Touch,\n): {\n clientX: number\n clientY: number\n} {\n return {\n clientX: (a.clientX + b.clientX) / 2,\n clientY: (a.clientY + b.clientY) / 2,\n }\n}\n\n// oxlint-disable-next-line eslint/complexity -- pre-existing; split it before adding branches\nconst LineChart: React.FC<LineChartProps> = ({\n series,\n width,\n height,\n margin = DEFAULT_MARGIN,\n curve: defaultCurve = 'monotone',\n showGrid = true,\n showTooltip: enableTooltip = true,\n className,\n xTickCount = 5,\n yTickCount = 4,\n formatDate = defaultFormatDate,\n formatTooltipDate,\n formatValue = defaultFormatValue,\n yAxisUnit,\n formatYTick = formatYAxisTick,\n yDomain,\n referenceLines,\n topContent,\n zoomable = false,\n zoomResetLabel,\n zoomDomain: zoomDomainProp,\n onZoomDomainChange,\n}) => {\n const plotClipId = React.useId()\n const {\n showTooltip,\n hideTooltip,\n tooltipData,\n tooltipLeft,\n tooltipTop,\n tooltipOpen,\n } = useTooltip<{ date: Date }>()\n\n const innerWidth = Math.max(0, width - margin.left - margin.right)\n const innerHeight = Math.max(0, height - margin.top - margin.bottom)\n\n const allData = useMemo(() => series.flatMap((s) => s.data), [series])\n\n const fullDomain = useMemo((): TimeDomain | null => {\n const ext = extent(allData, (d) => d.date.valueOf())\n if (ext[0] == null || ext[1] == null) return null\n return [ext[0], ext[1]]\n }, [allData])\n\n const controlledZoomDomain =\n zoomDomainProp === undefined\n ? undefined\n : zoomDomainProp == null\n ? null\n : toTimeDomain(zoomDomainProp)\n\n const handleZoomDomainChange = useCallback(\n (domain: TimeDomain | null) => {\n onZoomDomainChange?.(domain ? fromTimeDomain(domain) : null)\n },\n [onZoomDomainChange],\n )\n\n const zoom = useChartZoom({\n enabled: zoomable,\n fullDomain,\n zoomDomain: controlledZoomDomain,\n onZoomDomainChange: handleZoomDomainChange,\n })\n\n const xDomain = useMemo((): [Date, Date] => {\n if (zoom.zoomWindow) return fromTimeDomain(zoom.zoomWindow)\n const ext = extent(allData, (d) => d.date) as\n | [Date, Date]\n | [undefined, undefined]\n if (ext[0] && ext[1]) return [ext[0], ext[1]]\n const now = new Date()\n return [now, now]\n }, [allData, zoom.zoomWindow])\n\n const xScale = useMemo(\n () =>\n scaleTime({\n range: [0, innerWidth],\n domain: xDomain,\n }),\n [innerWidth, xDomain],\n )\n\n const visibleData = useMemo(() => {\n const [start, end] = xDomain\n const t0 = start.valueOf()\n const t1 = end.valueOf()\n return allData.filter((d) => {\n const t = d.date.valueOf()\n return t >= t0 && t <= t1\n })\n }, [allData, xDomain])\n\n const reducedMotion = useReducedMotion()\n\n // With a unit, the y-axis is generated by the shared scale util (nice ticks\n // + rounded bounds) so it matches BarChart / PowerHistoryChart exactly.\n const yAxisScale = useMemo(() => {\n if (yAxisUnit === undefined) return null\n let min = 0\n let max = 0\n for (const d of visibleData) {\n if (d.value < min) min = d.value\n if (d.value > max) max = d.value\n }\n return buildYAxisScale(min, max)\n }, [visibleData, yAxisUnit])\n\n const targetYDomain = useMemo((): TimeDomain => {\n if (yDomain) return yDomain\n if (yAxisScale) return [yAxisScale.min, yAxisScale.max]\n return scaleLinear({\n domain: computeYDomain(visibleData),\n nice: true,\n }).domain() as [number, number]\n }, [visibleData, yAxisScale, yDomain])\n\n const displayedYDomain = useAnimatedChartDomain(targetYDomain, {\n animateChanges: zoom.isPinching,\n reducedMotion,\n })\n\n const yScale = useMemo(\n () =>\n scaleLinear({\n range: [innerHeight, 0],\n domain: [displayedYDomain[0], displayedYDomain[1]],\n }),\n [displayedYDomain, innerHeight],\n )\n\n const xTicks = zoom.isZoomed\n ? buildTimeTicks(\n xDomain[0].valueOf(),\n xDomain[1].valueOf(),\n xTickCount,\n ).map((tick) => new Date(tick))\n : xScale.ticks(xTickCount)\n const yTicks = yAxisScale ? yAxisScale.ticks : yScale.ticks(yTickCount)\n const formatAxisDate =\n zoom.isZoomed &&\n xDomain[1].valueOf() - xDomain[0].valueOf() <= DETAILED_X_AXIS_THRESHOLD\n ? (formatTooltipDate ?? formatDate)\n : formatDate\n\n const domainXFromEvent = useCallback(\n (\n event:\n | React.MouseEvent<SVGRectElement>\n | React.PointerEvent<SVGRectElement>\n | React.TouchEvent<SVGRectElement>,\n ) => {\n const point = localPoint(event)\n if (!point) return null\n return xScale.invert(point.x - margin.left).valueOf()\n },\n [margin.left, xScale],\n )\n\n const handleTooltip = useCallback(\n (\n event:\n | React.TouchEvent<SVGRectElement>\n | React.MouseEvent<SVGRectElement>,\n ) => {\n if (!enableTooltip || zoom.isBrushing || zoom.isPanning) return\n\n const point = localPoint(event)\n if (!point) return\n\n const x0 = xScale.invert(point.x - margin.left)\n\n // Snap to the data point closest to the cursor across all series, so the\n // cursor line + tooltip line up with real values.\n let anchorDate: Date | null = null\n let bestDistance = Infinity\n for (const s of series) {\n const d = nearestDatum(s.data, x0)\n if (!d) continue\n const distance = Math.abs(d.date.valueOf() - x0.valueOf())\n if (distance < bestDistance) {\n bestDistance = distance\n anchorDate = d.date\n }\n }\n\n if (!anchorDate) return\n\n showTooltip({\n tooltipData: { date: anchorDate },\n tooltipLeft: xScale(anchorDate) + margin.left,\n tooltipTop: margin.top,\n })\n },\n [\n enableTooltip,\n margin,\n series,\n showTooltip,\n xScale,\n zoom.isBrushing,\n zoom.isPanning,\n ],\n )\n\n const tooltipRows: ChartTooltipRow[] = tooltipData\n ? series.flatMap((s, i) => {\n const d = nearestDatum(s.data, tooltipData.date)\n if (!d || !withinDomain(s.data, tooltipData.date)) return []\n return [\n {\n key: s.id,\n label: s.label ?? s.id,\n color:\n s.color ?? (LEGEND_COLORS[i % LEGEND_COLORS.length] as string),\n value: formatValue(d.value),\n marker: s.dashed ? ('dashed-line' as const) : ('dot' as const),\n },\n ]\n })\n : []\n\n const [tooltipRef, tooltipSize] = useMeasuredSize<HTMLDivElement>()\n const tooltipPos = clampTooltipPosition({\n anchorX: tooltipLeft ?? 0,\n anchorY: tooltipTop ?? margin.top,\n width,\n height,\n tipWidth: tooltipSize.width,\n tipHeight: tooltipSize.height,\n placement: 'beside',\n })\n\n if (width < 10 || height < 10) return null\n\n return (\n <div className={cn('relative', className)}>\n <svg className=\"block\" height={height} width={width}>\n <defs>\n <clipPath id={plotClipId}>\n <rect height={innerHeight} width={innerWidth} x={0} y={0} />\n </clipPath>\n </defs>\n <Group left={margin.left} top={margin.top}>\n {/* Grid lines */}\n {showGrid &&\n yTicks.map((tick) => (\n <line\n className=\"stroke-gray-a4\"\n key={`grid-y-${tick}`}\n strokeDasharray=\"4,4\"\n x1={0}\n x2={innerWidth}\n y1={yScale(tick)}\n y2={yScale(tick)}\n />\n ))}\n\n {/* X axis */}\n <line\n className=\"stroke-gray-a6\"\n x1={0}\n x2={innerWidth}\n y1={innerHeight}\n y2={innerHeight}\n />\n\n {/* Reference markers (e.g. sunrise / sunset) */}\n {referenceLines?.map((ref) => {\n const x = xScale(ref.date)\n if (!Number.isFinite(x) || x < 0 || x > innerWidth) return null\n // Sit the label beside the line, on whichever side keeps it in\n // frame: right of a left-half marker, left of a right-half one.\n const anchorEnd = x > innerWidth / 2\n return (\n <g key={ref.key} pointerEvents=\"none\">\n <line\n className=\"stroke-orange-8\"\n strokeDasharray=\"3,3\"\n x1={x}\n x2={x}\n y1={0}\n y2={innerHeight}\n />\n {ref.label != null && (\n <text\n className=\"fill-orange-11 text-[10px]\"\n dy=\"0.9em\"\n textAnchor={anchorEnd ? 'end' : 'start'}\n x={x + (anchorEnd ? -4 : 4)}\n y={0}\n >\n {ref.label}\n </text>\n )}\n </g>\n )\n })}\n\n {/* X axis labels */}\n {xTicks.map((tick, index) => (\n <text\n dy=\"1em\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n key={`x-${tick.valueOf()}`}\n textAnchor={\n index === 0\n ? 'start'\n : index === xTicks.length - 1\n ? 'end'\n : 'middle'\n }\n x={xScale(tick)}\n y={innerHeight + 8}\n >\n {formatAxisDate(tick)}\n </text>\n ))}\n\n {/* Y axis. With `yAxisUnit`, matches BarChart: a vertical axis line,\n short tick marks, plain numeric ticks, and the unit as a bold\n top-left header (top tick hidden). Without it, ticks repeat\n `formatValue` and no axis line/marks are drawn. */}\n {yAxisUnit !== undefined && (\n <line\n stroke=\"var(--neutral-a6)\"\n strokeWidth={1}\n x1={0}\n x2={0}\n y1={0}\n y2={innerHeight}\n />\n )}\n {yTicks.map((tick) => {\n const y = yScale(tick)\n return (\n <g key={`y-${tick}`}>\n {yAxisUnit !== undefined && (\n <line\n stroke=\"var(--neutral-a8)\"\n strokeWidth={1}\n x1={-4}\n x2={0}\n y1={y}\n y2={y}\n />\n )}\n <text\n dy=\"0.32em\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n textAnchor=\"end\"\n x={yAxisUnit !== undefined ? -7 : -4}\n y={y}\n >\n {yAxisUnit !== undefined\n ? formatYTick(tick)\n : formatValue(tick)}\n </text>\n </g>\n )\n })}\n {yAxisUnit !== undefined && (\n <text\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n fontWeight={600}\n textAnchor=\"start\"\n x={-margin.left}\n y={-6}\n >\n {yAxisUnit}\n </text>\n )}\n\n <g clipPath={`url(#${plotClipId})`} data-testid=\"chart-plot-clip\">\n {series.map((s, i) => (\n <LinePath\n className={cn(LINE_COLORS[i % LINE_COLORS.length], s.className)}\n curve={resolveCurve(s.curve ?? defaultCurve)}\n data={s.data}\n fill=\"none\"\n key={s.id}\n strokeDasharray={\n s.dashed ? (s.dashPattern ?? '6,4') : undefined\n }\n strokeWidth={2}\n x={(d) => xScale(d.date)}\n y={(d) => yScale(d.value)}\n />\n ))}\n </g>\n\n {zoom.brushBand && (\n <rect\n data-testid=\"chart-brush-band\"\n fill=\"var(--spark-a4)\"\n height={innerHeight}\n pointerEvents=\"none\"\n width={Math.abs(\n xScale(new Date(zoom.brushBand[1])) -\n xScale(new Date(zoom.brushBand[0])),\n )}\n x={Math.min(\n xScale(new Date(zoom.brushBand[0])),\n xScale(new Date(zoom.brushBand[1])),\n )}\n y={0}\n />\n )}\n\n {(enableTooltip || zoomable) && (\n <rect\n fill=\"transparent\"\n height={innerHeight}\n style={zoomable ? { touchAction: 'pan-y' } : undefined}\n width={innerWidth}\n x={0}\n y={0}\n onMouseLeave={() => {\n if (!zoom.isBrushing) hideTooltip()\n }}\n onMouseMove={(event) => {\n if (zoom.isBrushing) {\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n return\n }\n handleTooltip(event)\n }}\n onPointerDown={(event) => {\n if (!zoomable || event.pointerType === 'touch') return\n if (event.button !== 0) return\n const domainX = domainXFromEvent(event)\n if (domainX == null) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n hideTooltip()\n zoom.onPointerDown(domainX)\n }}\n onPointerMove={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n }}\n onPointerUp={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerUp(domainX)\n else zoom.onPointerCancel()\n }}\n onPointerCancel={() => {\n zoom.onPointerCancel()\n }}\n onDoubleClick={() => {\n if (zoomable) zoom.onDoubleClick()\n }}\n onTouchStart={(event) => {\n if (!zoomable) return\n if (event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - margin.left) / innerWidth),\n )\n zoom.onPinchStart(touchDistance(a, b), midpoint)\n hideTooltip()\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanStart({\n x: (point.x - margin.left) / innerWidth,\n y: (point.y - margin.top) / innerHeight,\n })\n hideTooltip()\n }\n }}\n onTouchMove={(event) => {\n if (zoomable && event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - margin.left) / innerWidth),\n )\n zoom.onPinchMove(touchDistance(a, b), midpoint)\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanMove({\n x: (point.x - margin.left) / innerWidth,\n y: (point.y - margin.top) / innerHeight,\n })\n return\n }\n if (event.touches.length === 1) handleTooltip(event)\n }}\n onTouchEnd={(event) => {\n if (zoomable && event.touches.length < 2) zoom.onPinchEnd()\n if (event.touches.length === 0) {\n if (zoomable) zoom.onPanEnd()\n if (zoomable) zoom.onTap()\n hideTooltip()\n }\n }}\n onTouchCancel={() => {\n zoom.onPinchEnd()\n zoom.onPanEnd()\n }}\n />\n )}\n\n {/* Cursor line + a dot per series */}\n {tooltipOpen &&\n tooltipData &&\n !zoom.isBrushing &&\n !zoom.isPanning && (\n <>\n <line\n className=\"stroke-gray-a6\"\n pointerEvents=\"none\"\n strokeDasharray=\"4,4\"\n x1={xScale(tooltipData.date)}\n x2={xScale(tooltipData.date)}\n y1={0}\n y2={innerHeight}\n />\n {series.map((s, i) => {\n const d = nearestDatum(s.data, tooltipData.date)\n if (!d || !withinDomain(s.data, tooltipData.date)) return null\n return (\n <circle\n key={s.id}\n className={\n s.color\n ? undefined\n : cn(DOT_COLORS[i % DOT_COLORS.length])\n }\n cx={xScale(d.date)}\n cy={yScale(d.value)}\n fill={s.color}\n pointerEvents=\"none\"\n r={4}\n stroke=\"white\"\n strokeWidth={2}\n />\n )\n })}\n </>\n )}\n </Group>\n </svg>\n\n {topContent && topContent.length > 0 && (\n <div\n className=\"pointer-events-none absolute overflow-hidden\"\n style={{\n height: margin.top,\n left: margin.left,\n top: 0,\n width: innerWidth,\n }}\n >\n {topContent.map((item) => {\n const x = xScale(item.date)\n if (!Number.isFinite(x) || x < 0 || x > innerWidth) return null\n return (\n <div\n key={item.key}\n className=\"absolute -translate-x-1/2\"\n style={{ left: x, top: 0 }}\n >\n {item.content}\n </div>\n )\n })}\n </div>\n )}\n\n {zoom.isZoomed && zoomResetLabel && (\n <ChartZoomResetButton label={zoomResetLabel} onReset={zoom.resetZoom} />\n )}\n\n <div\n ref={tooltipRef}\n className=\"pointer-events-none absolute z-10\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n opacity:\n tooltipOpen && tooltipData && !zoom.isPanning && !zoom.isBrushing\n ? 1\n : 0,\n transition: reducedMotion\n ? 'opacity 140ms ease-out'\n : 'left 140ms ease-out, top 140ms ease-out, opacity 140ms ease-out',\n }}\n >\n {tooltipData && (\n <ChartTooltipCard\n label={(formatTooltipDate ?? formatDate)(tooltipData.date)}\n rows={tooltipRows}\n />\n )}\n </div>\n </div>\n )\n}\n\nexport { LineChart }\n"],"mappings":"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"}
|
package/dist/components/pill.cjs
CHANGED
|
@@ -42,7 +42,7 @@ const Pill = react.forwardRef(function Pill({ className, children, styleVariant,
|
|
|
42
42
|
"data-accent-color": color ? require_lib_accent_colors.accentByColor[color] : void 0,
|
|
43
43
|
...interaction,
|
|
44
44
|
ref,
|
|
45
|
-
className: (0, cn.cn)("inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none", "bg-(--spark-style-bg,var(--
|
|
45
|
+
className: (0, cn.cn)("inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none", "bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--spark-badge-fg,var(--neutral-a11)))", "shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)]", focusable && "transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none", clickable && "cursor-pointer hover:bg-(--spark-style-bg-hover,var(--neutral-a4))", disabled && "pointer-events-none opacity-50", className),
|
|
46
46
|
...props,
|
|
47
47
|
children: [children, removable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
48
48
|
className: "sr-only",
|
|
@@ -54,7 +54,7 @@ const Pill = react.forwardRef(function Pill({ className, children, styleVariant,
|
|
|
54
54
|
event.stopPropagation();
|
|
55
55
|
onRemove?.(event);
|
|
56
56
|
},
|
|
57
|
-
className: "-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--
|
|
57
|
+
className: "-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--neutral-a4)) motion-reduce:transition-none",
|
|
58
58
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_epilot_spark_icons.Close, { className: "[--spark-icon-size:14px]" })
|
|
59
59
|
})] }) : null]
|
|
60
60
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pill.cjs","names":["React","accentByColor","Close"],"sources":["../../src/components/pill.tsx"],"sourcesContent":["import { Close } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkAccentColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\n\ntype PillRemoveProps =\n | { onRemove?: undefined; removeLabel?: string }\n | {\n /** Fires on a pointer click of the (decorative) glyph, or Delete/Backspace while the pill has focus. */\n onRemove: (event: React.SyntheticEvent) => void\n /** Visually-hidden text folded into the pill's accessible name -- the glyph itself is `aria-hidden`. */\n removeLabel: string\n }\n\ntype PillProps = Omit<React.HTMLAttributes<HTMLSpanElement>, 'onClick'> &\n PillRemoveProps & {\n styleVariant?: SparkStyle\n color?: SparkAccentColor\n disabled?: boolean\n onClick?: (event: React.SyntheticEvent<HTMLSpanElement>) => void\n }\n\n/**\n * Like Badge, but interactive: `onClick` makes the whole pill a button\n * (Enter/Space), `onRemove` adds a Delete/Backspace-and-click affordance.\n * Both are optional -- with neither, it's a static label.\n */\nconst Pill = React.forwardRef<HTMLSpanElement, PillProps>(function Pill(\n {\n className,\n children,\n styleVariant,\n color,\n disabled = false,\n onClick,\n onRemove,\n removeLabel,\n onKeyDown,\n ...props\n },\n ref,\n) {\n const clickable = !disabled && onClick !== undefined\n const removable = !disabled && onRemove !== undefined\n const focusable = clickable || removable\n const hasLook = styleVariant !== undefined || color !== undefined\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {\n onKeyDown?.(event)\n if (clickable && (event.key === 'Enter' || event.key === ' ')) {\n if (event.key === ' ') event.preventDefault()\n onClick?.(event)\n }\n if (removable && (event.key === 'Delete' || event.key === 'Backspace')) {\n onRemove?.(event)\n }\n }\n\n // button role and click handler travel together, so a clickable pill always\n // answers Enter/Space; a removable-only pill stays generic and answers Delete\n const interaction = {\n onKeyDown: handleKeyDown,\n ...(clickable && { role: 'button' as const, onClick }),\n ...(focusable && { tabIndex: 0 }),\n }\n\n return (\n <span\n aria-disabled={disabled || undefined}\n aria-keyshortcuts={removable ? 'Delete' : undefined}\n data-slot=\"pill\"\n data-style={hasLook ? (styleVariant ?? 'soft') : undefined}\n data-accent-color={color ? accentByColor[color] : undefined}\n {...interaction}\n ref={ref}\n className={cn(\n 'inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none',\n 'bg-(--spark-style-bg,var(--
|
|
1
|
+
{"version":3,"file":"pill.cjs","names":["React","accentByColor","Close"],"sources":["../../src/components/pill.tsx"],"sourcesContent":["import { Close } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkAccentColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\n\ntype PillRemoveProps =\n | { onRemove?: undefined; removeLabel?: string }\n | {\n /** Fires on a pointer click of the (decorative) glyph, or Delete/Backspace while the pill has focus. */\n onRemove: (event: React.SyntheticEvent) => void\n /** Visually-hidden text folded into the pill's accessible name -- the glyph itself is `aria-hidden`. */\n removeLabel: string\n }\n\ntype PillProps = Omit<React.HTMLAttributes<HTMLSpanElement>, 'onClick'> &\n PillRemoveProps & {\n styleVariant?: SparkStyle\n color?: SparkAccentColor\n disabled?: boolean\n onClick?: (event: React.SyntheticEvent<HTMLSpanElement>) => void\n }\n\n/**\n * Like Badge, but interactive: `onClick` makes the whole pill a button\n * (Enter/Space), `onRemove` adds a Delete/Backspace-and-click affordance.\n * Both are optional -- with neither, it's a static label.\n */\nconst Pill = React.forwardRef<HTMLSpanElement, PillProps>(function Pill(\n {\n className,\n children,\n styleVariant,\n color,\n disabled = false,\n onClick,\n onRemove,\n removeLabel,\n onKeyDown,\n ...props\n },\n ref,\n) {\n const clickable = !disabled && onClick !== undefined\n const removable = !disabled && onRemove !== undefined\n const focusable = clickable || removable\n const hasLook = styleVariant !== undefined || color !== undefined\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {\n onKeyDown?.(event)\n if (clickable && (event.key === 'Enter' || event.key === ' ')) {\n if (event.key === ' ') event.preventDefault()\n onClick?.(event)\n }\n if (removable && (event.key === 'Delete' || event.key === 'Backspace')) {\n onRemove?.(event)\n }\n }\n\n // button role and click handler travel together, so a clickable pill always\n // answers Enter/Space; a removable-only pill stays generic and answers Delete\n const interaction = {\n onKeyDown: handleKeyDown,\n ...(clickable && { role: 'button' as const, onClick }),\n ...(focusable && { tabIndex: 0 }),\n }\n\n return (\n <span\n aria-disabled={disabled || undefined}\n aria-keyshortcuts={removable ? 'Delete' : undefined}\n data-slot=\"pill\"\n data-style={hasLook ? (styleVariant ?? 'soft') : undefined}\n data-accent-color={color ? accentByColor[color] : undefined}\n {...interaction}\n ref={ref}\n className={cn(\n 'inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none',\n 'bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--spark-badge-fg,var(--neutral-a11)))',\n 'shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)]',\n focusable &&\n 'transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none',\n clickable &&\n 'cursor-pointer hover:bg-(--spark-style-bg-hover,var(--neutral-a4))',\n disabled && 'pointer-events-none opacity-50',\n className,\n )}\n {...props}\n >\n {children}\n {removable ? (\n <>\n {/* aria-hidden hides the glyph itself; this carries removeLabel into the pill's own accessible name */}\n <span className=\"sr-only\">{removeLabel}</span>\n <span\n aria-hidden=\"true\"\n data-slot=\"pill-remove\"\n onClick={(event) => {\n event.stopPropagation()\n onRemove?.(event)\n }}\n className=\"-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--neutral-a4)) motion-reduce:transition-none\"\n >\n <Close className=\"[--spark-icon-size:14px]\" />\n </span>\n </>\n ) : null}\n </span>\n )\n})\n\nPill.displayName = 'Pill'\n\nexport { Pill }\nexport type { PillProps }\n"],"mappings":";;;;;;;;;;;;;;;AA6BA,MAAM,OAAOA,MAAM,WAAuC,SAAS,KACjE,EACE,WACA,UACA,cACA,OACA,WAAW,OACX,SACA,UACA,aACA,WACA,GAAG,SAEL,KACA;CACA,MAAM,YAAY,CAAC,YAAY,YAAY,KAAA;CAC3C,MAAM,YAAY,CAAC,YAAY,aAAa,KAAA;CAC5C,MAAM,YAAY,aAAa;CAC/B,MAAM,UAAU,iBAAiB,KAAA,KAAa,UAAU,KAAA;CAExD,MAAM,iBAAiB,UAAgD;EACrE,YAAY,KAAK;EACjB,IAAI,cAAc,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;GAC7D,IAAI,MAAM,QAAQ,KAAK,MAAM,eAAe;GAC5C,UAAU,KAAK;EACjB;EACA,IAAI,cAAc,MAAM,QAAQ,YAAY,MAAM,QAAQ,cACxD,WAAW,KAAK;CAEpB;CAIA,MAAM,cAAc;EAClB,WAAW;EACX,GAAI,aAAa;GAAE,MAAM;GAAmB;EAAQ;EACpD,GAAI,aAAa,EAAE,UAAU,EAAE;CACjC;CAEA,OACE,iBAAA,GAAA,kBAAA,MAAC,QAAD;EACE,iBAAe,YAAY,KAAA;EAC3B,qBAAmB,YAAY,WAAW,KAAA;EAC1C,aAAU;EACV,cAAY,UAAW,gBAAgB,SAAU,KAAA;EACjD,qBAAmB,QAAQC,0BAAAA,cAAc,SAAS,KAAA;EAClD,GAAI;EACC;EACL,YAAA,GAAA,GAAA,IACE,0FACA,4GACA,kGACA,aACE,iIACF,aACE,sEACF,YAAY,kCACZ,SACF;EACA,GAAI;YAnBN,CAqBG,UACA,YACC,iBAAA,GAAA,kBAAA,MAAA,kBAAA,UAAA,EAAA,UAAA,CAEE,iBAAA,GAAA,kBAAA,KAAC,QAAD;GAAM,WAAU;aAAW;EAAkB,CAAA,GAC7C,iBAAA,GAAA,kBAAA,KAAC,QAAD;GACE,eAAY;GACZ,aAAU;GACV,UAAU,UAAU;IAClB,MAAM,gBAAgB;IACtB,WAAW,KAAK;GAClB;GACA,WAAU;aAEV,iBAAA,GAAA,kBAAA,KAACC,oBAAAA,OAAD,EAAO,WAAU,2BAA4B,CAAA;EACzC,CAAA,CACN,EAAA,CAAA,IACA,IACA;;AAEV,CAAC;AAED,KAAK,cAAc"}
|
package/dist/components/pill.js
CHANGED
|
@@ -39,7 +39,7 @@ const Pill = React$1.forwardRef(function Pill({ className, children, styleVarian
|
|
|
39
39
|
"data-accent-color": color ? accentByColor[color] : void 0,
|
|
40
40
|
...interaction,
|
|
41
41
|
ref,
|
|
42
|
-
className: cn("inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none", "bg-(--spark-style-bg,var(--
|
|
42
|
+
className: cn("inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none", "bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--spark-badge-fg,var(--neutral-a11)))", "shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)]", focusable && "transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none", clickable && "cursor-pointer hover:bg-(--spark-style-bg-hover,var(--neutral-a4))", disabled && "pointer-events-none opacity-50", className),
|
|
43
43
|
...props,
|
|
44
44
|
children: [children, removable ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
45
45
|
className: "sr-only",
|
|
@@ -51,7 +51,7 @@ const Pill = React$1.forwardRef(function Pill({ className, children, styleVarian
|
|
|
51
51
|
event.stopPropagation();
|
|
52
52
|
onRemove?.(event);
|
|
53
53
|
},
|
|
54
|
-
className: "-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--
|
|
54
|
+
className: "-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--neutral-a4)) motion-reduce:transition-none",
|
|
55
55
|
children: /* @__PURE__ */ jsx(Close, { className: "[--spark-icon-size:14px]" })
|
|
56
56
|
})] }) : null]
|
|
57
57
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pill.js","names":["React"],"sources":["../../src/components/pill.tsx"],"sourcesContent":["import { Close } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkAccentColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\n\ntype PillRemoveProps =\n | { onRemove?: undefined; removeLabel?: string }\n | {\n /** Fires on a pointer click of the (decorative) glyph, or Delete/Backspace while the pill has focus. */\n onRemove: (event: React.SyntheticEvent) => void\n /** Visually-hidden text folded into the pill's accessible name -- the glyph itself is `aria-hidden`. */\n removeLabel: string\n }\n\ntype PillProps = Omit<React.HTMLAttributes<HTMLSpanElement>, 'onClick'> &\n PillRemoveProps & {\n styleVariant?: SparkStyle\n color?: SparkAccentColor\n disabled?: boolean\n onClick?: (event: React.SyntheticEvent<HTMLSpanElement>) => void\n }\n\n/**\n * Like Badge, but interactive: `onClick` makes the whole pill a button\n * (Enter/Space), `onRemove` adds a Delete/Backspace-and-click affordance.\n * Both are optional -- with neither, it's a static label.\n */\nconst Pill = React.forwardRef<HTMLSpanElement, PillProps>(function Pill(\n {\n className,\n children,\n styleVariant,\n color,\n disabled = false,\n onClick,\n onRemove,\n removeLabel,\n onKeyDown,\n ...props\n },\n ref,\n) {\n const clickable = !disabled && onClick !== undefined\n const removable = !disabled && onRemove !== undefined\n const focusable = clickable || removable\n const hasLook = styleVariant !== undefined || color !== undefined\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {\n onKeyDown?.(event)\n if (clickable && (event.key === 'Enter' || event.key === ' ')) {\n if (event.key === ' ') event.preventDefault()\n onClick?.(event)\n }\n if (removable && (event.key === 'Delete' || event.key === 'Backspace')) {\n onRemove?.(event)\n }\n }\n\n // button role and click handler travel together, so a clickable pill always\n // answers Enter/Space; a removable-only pill stays generic and answers Delete\n const interaction = {\n onKeyDown: handleKeyDown,\n ...(clickable && { role: 'button' as const, onClick }),\n ...(focusable && { tabIndex: 0 }),\n }\n\n return (\n <span\n aria-disabled={disabled || undefined}\n aria-keyshortcuts={removable ? 'Delete' : undefined}\n data-slot=\"pill\"\n data-style={hasLook ? (styleVariant ?? 'soft') : undefined}\n data-accent-color={color ? accentByColor[color] : undefined}\n {...interaction}\n ref={ref}\n className={cn(\n 'inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none',\n 'bg-(--spark-style-bg,var(--
|
|
1
|
+
{"version":3,"file":"pill.js","names":["React"],"sources":["../../src/components/pill.tsx"],"sourcesContent":["import { Close } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkAccentColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\n\ntype PillRemoveProps =\n | { onRemove?: undefined; removeLabel?: string }\n | {\n /** Fires on a pointer click of the (decorative) glyph, or Delete/Backspace while the pill has focus. */\n onRemove: (event: React.SyntheticEvent) => void\n /** Visually-hidden text folded into the pill's accessible name -- the glyph itself is `aria-hidden`. */\n removeLabel: string\n }\n\ntype PillProps = Omit<React.HTMLAttributes<HTMLSpanElement>, 'onClick'> &\n PillRemoveProps & {\n styleVariant?: SparkStyle\n color?: SparkAccentColor\n disabled?: boolean\n onClick?: (event: React.SyntheticEvent<HTMLSpanElement>) => void\n }\n\n/**\n * Like Badge, but interactive: `onClick` makes the whole pill a button\n * (Enter/Space), `onRemove` adds a Delete/Backspace-and-click affordance.\n * Both are optional -- with neither, it's a static label.\n */\nconst Pill = React.forwardRef<HTMLSpanElement, PillProps>(function Pill(\n {\n className,\n children,\n styleVariant,\n color,\n disabled = false,\n onClick,\n onRemove,\n removeLabel,\n onKeyDown,\n ...props\n },\n ref,\n) {\n const clickable = !disabled && onClick !== undefined\n const removable = !disabled && onRemove !== undefined\n const focusable = clickable || removable\n const hasLook = styleVariant !== undefined || color !== undefined\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {\n onKeyDown?.(event)\n if (clickable && (event.key === 'Enter' || event.key === ' ')) {\n if (event.key === ' ') event.preventDefault()\n onClick?.(event)\n }\n if (removable && (event.key === 'Delete' || event.key === 'Backspace')) {\n onRemove?.(event)\n }\n }\n\n // button role and click handler travel together, so a clickable pill always\n // answers Enter/Space; a removable-only pill stays generic and answers Delete\n const interaction = {\n onKeyDown: handleKeyDown,\n ...(clickable && { role: 'button' as const, onClick }),\n ...(focusable && { tabIndex: 0 }),\n }\n\n return (\n <span\n aria-disabled={disabled || undefined}\n aria-keyshortcuts={removable ? 'Delete' : undefined}\n data-slot=\"pill\"\n data-style={hasLook ? (styleVariant ?? 'soft') : undefined}\n data-accent-color={color ? accentByColor[color] : undefined}\n {...interaction}\n ref={ref}\n className={cn(\n 'inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none',\n 'bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--spark-badge-fg,var(--neutral-a11)))',\n 'shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)]',\n focusable &&\n 'transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none',\n clickable &&\n 'cursor-pointer hover:bg-(--spark-style-bg-hover,var(--neutral-a4))',\n disabled && 'pointer-events-none opacity-50',\n className,\n )}\n {...props}\n >\n {children}\n {removable ? (\n <>\n {/* aria-hidden hides the glyph itself; this carries removeLabel into the pill's own accessible name */}\n <span className=\"sr-only\">{removeLabel}</span>\n <span\n aria-hidden=\"true\"\n data-slot=\"pill-remove\"\n onClick={(event) => {\n event.stopPropagation()\n onRemove?.(event)\n }}\n className=\"-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--neutral-a4)) motion-reduce:transition-none\"\n >\n <Close className=\"[--spark-icon-size:14px]\" />\n </span>\n </>\n ) : null}\n </span>\n )\n})\n\nPill.displayName = 'Pill'\n\nexport { Pill }\nexport type { PillProps }\n"],"mappings":";;;;;;;;;;;;AA6BA,MAAM,OAAOA,QAAM,WAAuC,SAAS,KACjE,EACE,WACA,UACA,cACA,OACA,WAAW,OACX,SACA,UACA,aACA,WACA,GAAG,SAEL,KACA;CACA,MAAM,YAAY,CAAC,YAAY,YAAY,KAAA;CAC3C,MAAM,YAAY,CAAC,YAAY,aAAa,KAAA;CAC5C,MAAM,YAAY,aAAa;CAC/B,MAAM,UAAU,iBAAiB,KAAA,KAAa,UAAU,KAAA;CAExD,MAAM,iBAAiB,UAAgD;EACrE,YAAY,KAAK;EACjB,IAAI,cAAc,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;GAC7D,IAAI,MAAM,QAAQ,KAAK,MAAM,eAAe;GAC5C,UAAU,KAAK;EACjB;EACA,IAAI,cAAc,MAAM,QAAQ,YAAY,MAAM,QAAQ,cACxD,WAAW,KAAK;CAEpB;CAIA,MAAM,cAAc;EAClB,WAAW;EACX,GAAI,aAAa;GAAE,MAAM;GAAmB;EAAQ;EACpD,GAAI,aAAa,EAAE,UAAU,EAAE;CACjC;CAEA,OACE,qBAAC,QAAD;EACE,iBAAe,YAAY,KAAA;EAC3B,qBAAmB,YAAY,WAAW,KAAA;EAC1C,aAAU;EACV,cAAY,UAAW,gBAAgB,SAAU,KAAA;EACjD,qBAAmB,QAAQ,cAAc,SAAS,KAAA;EAClD,GAAI;EACC;EACL,WAAW,GACT,0FACA,4GACA,kGACA,aACE,iIACF,aACE,sEACF,YAAY,kCACZ,SACF;EACA,GAAI;YAnBN,CAqBG,UACA,YACC,qBAAA,UAAA,EAAA,UAAA,CAEE,oBAAC,QAAD;GAAM,WAAU;aAAW;EAAkB,CAAA,GAC7C,oBAAC,QAAD;GACE,eAAY;GACZ,aAAU;GACV,UAAU,UAAU;IAClB,MAAM,gBAAgB;IACtB,WAAW,KAAK;GAClB;GACA,WAAU;aAEV,oBAAC,OAAD,EAAO,WAAU,2BAA4B,CAAA;EACzC,CAAA,CACN,EAAA,CAAA,IACA,IACA;;AAEV,CAAC;AAED,KAAK,cAAc"}
|
|
@@ -22,7 +22,7 @@ const Progress = react.forwardRef(({ className, value, max = 100, size = "defaul
|
|
|
22
22
|
role: "progressbar",
|
|
23
23
|
...props,
|
|
24
24
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
25
|
-
className: "h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300",
|
|
25
|
+
className: "h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300 motion-reduce:transition-none",
|
|
26
26
|
style: { width: `${percentage}%` }
|
|
27
27
|
})
|
|
28
28
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.cjs","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-gray-a4',\n sizeClasses[size],\n className,\n )}\n ref={ref}\n role=\"progressbar\"\n {...props}\n >\n <div\n className=\"h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n )\n },\n)\n\nProgress.displayName = 'Progress'\n\nexport { Progress }\n"],"mappings":";;;;;;;;AAYA,MAAM,cAAc;CAClB,IAAI;CACJ,SAAS;CACT,IAAI;AACN;AAEA,MAAM,WAAWA,MAAM,YACpB,EAAE,WAAW,OAAO,MAAM,KAAK,OAAO,WAAW,GAAG,SAAS,QAAQ;CACpE,MAAM,aAAa,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,GAAG,CAAC;CAEjE,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,YAAA,GAAA,GAAA,IACE,kDACA,YAAY,OACZ,SACF;EACK;EACL,MAAK;EACL,GAAI;YAEJ,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,WAAW,GAAG;EAClC,CAAA;CACE,CAAA;AAET,CACF;AAEA,SAAS,cAAc"}
|
|
1
|
+
{"version":3,"file":"progress.cjs","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-gray-a4',\n sizeClasses[size],\n className,\n )}\n ref={ref}\n role=\"progressbar\"\n {...props}\n >\n <div\n className=\"h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300 motion-reduce:transition-none\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n )\n },\n)\n\nProgress.displayName = 'Progress'\n\nexport { Progress }\n"],"mappings":";;;;;;;;AAYA,MAAM,cAAc;CAClB,IAAI;CACJ,SAAS;CACT,IAAI;AACN;AAEA,MAAM,WAAWA,MAAM,YACpB,EAAE,WAAW,OAAO,MAAM,KAAK,OAAO,WAAW,GAAG,SAAS,QAAQ;CACpE,MAAM,aAAa,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,GAAG,CAAC;CAEjE,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,YAAA,GAAA,GAAA,IACE,kDACA,YAAY,OACZ,SACF;EACK;EACL,MAAK;EACL,GAAI;YAEJ,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,WAAW,GAAG;EAClC,CAAA;CACE,CAAA;AAET,CACF;AAEA,SAAS,cAAc"}
|