kaze-design-system 0.6.1 → 0.7.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.
@@ -0,0 +1,43 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn } from "../../lib/utils.js";
4
+ const ChartLegend = forwardRef(
5
+ ({ items, hidden = [], onToggle, className, ...rest }, ref) => {
6
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("chart-legend", className), ...rest, children: items.map((item) => {
7
+ const isHidden = hidden.includes(item.key);
8
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
9
+ /* @__PURE__ */ jsx(
10
+ "span",
11
+ {
12
+ className: "chart-legend__swatch",
13
+ style: { background: item.color },
14
+ "aria-hidden": "true"
15
+ }
16
+ ),
17
+ /* @__PURE__ */ jsx("span", { className: "chart-legend__label", children: item.label })
18
+ ] });
19
+ return onToggle ? /* @__PURE__ */ jsx(
20
+ "button",
21
+ {
22
+ type: "button",
23
+ className: cn("chart-legend__item", isHidden && "chart-legend__item--hidden"),
24
+ "aria-pressed": !isHidden,
25
+ onClick: () => onToggle(item.key),
26
+ children: content
27
+ },
28
+ item.key
29
+ ) : /* @__PURE__ */ jsx(
30
+ "span",
31
+ {
32
+ className: cn("chart-legend__item", isHidden && "chart-legend__item--hidden"),
33
+ children: content
34
+ },
35
+ item.key
36
+ );
37
+ }) });
38
+ }
39
+ );
40
+ ChartLegend.displayName = "ChartLegend";
41
+ export {
42
+ ChartLegend
43
+ };
@@ -0,0 +1,290 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import { forwardRef, useRef, useState, useEffect, useCallback, useMemo } from "react";
4
+ import { cn } from "../../lib/utils.js";
5
+ import { CHART_COLORS } from "../../lib/chartColors.js";
6
+ import { niceScale, linearScale, defaultFormat } from "../../lib/chartScale.js";
7
+ import { ChartLegend } from "../ChartLegend/ChartLegend.js";
8
+ const FALLBACK_WIDTH = 640;
9
+ const PAD_TOP = 8;
10
+ const PAD_RIGHT = 8;
11
+ const BAR_GAP = 2;
12
+ function toNumber(v) {
13
+ const n = typeof v === "number" ? v : Number(v);
14
+ return Number.isFinite(n) ? n : 0;
15
+ }
16
+ function barPath(x, y0, y1, w, radius, roundEnd) {
17
+ const h = Math.abs(y1 - y0);
18
+ if (h === 0 || w <= 0) return "";
19
+ const r = roundEnd ? Math.min(radius, w / 2, h) : 0;
20
+ const x1 = x + w;
21
+ if (y1 <= y0) {
22
+ return `M${x},${y0}V${y1 + r}${r ? `Q${x},${y1} ${x + r},${y1}` : ""}H${x1 - r}${r ? `Q${x1},${y1} ${x1},${y1 + r}` : ""}V${y0}Z`;
23
+ }
24
+ return `M${x},${y0}V${y1 - r}${r ? `Q${x},${y1} ${x + r},${y1}` : ""}H${x1 - r}${r ? `Q${x1},${y1} ${x1},${y1 - r}` : ""}V${y0}Z`;
25
+ }
26
+ const ColumnChart = forwardRef(
27
+ ({
28
+ data,
29
+ index,
30
+ series,
31
+ layout = "grouped",
32
+ height = 240,
33
+ showGrid = true,
34
+ showXAxis = true,
35
+ showYAxis = true,
36
+ showLegend = true,
37
+ yAxisWidth = 48,
38
+ formatValue = defaultFormat,
39
+ barRadius = 2,
40
+ "aria-label": ariaLabel,
41
+ className,
42
+ onKeyDown,
43
+ onBlur,
44
+ ...rest
45
+ }, ref) => {
46
+ const wrapRef = useRef(null);
47
+ const [width, setWidth] = useState(FALLBACK_WIDTH);
48
+ const [hidden, setHidden] = useState([]);
49
+ const [active, setActive] = useState(null);
50
+ useEffect(() => {
51
+ const el = wrapRef.current;
52
+ if (!el) return;
53
+ const measure = () => {
54
+ const w = el.clientWidth;
55
+ if (w > 0) setWidth(w);
56
+ };
57
+ measure();
58
+ if (typeof ResizeObserver === "undefined") return;
59
+ const ro = new ResizeObserver(measure);
60
+ ro.observe(el);
61
+ return () => ro.disconnect();
62
+ }, []);
63
+ const toggle = useCallback((key) => {
64
+ setHidden((prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]);
65
+ }, []);
66
+ const colorOf = (s, i) => s.color ?? CHART_COLORS[i % CHART_COLORS.length] ?? "var(--chart-1)";
67
+ const visible = useMemo(() => series.filter((s) => !hidden.includes(s.key)), [series, hidden]);
68
+ const stacked = layout === "stacked";
69
+ const left = showYAxis ? yAxisWidth : 4;
70
+ const bottom = showXAxis ? 24 : 4;
71
+ const plotW = Math.max(width - left - PAD_RIGHT, 1);
72
+ const plotH = Math.max(height - PAD_TOP - bottom, 1);
73
+ const n = data.length;
74
+ const step = n > 0 ? plotW / n : plotW;
75
+ const scale = useMemo(() => {
76
+ let lo = 0;
77
+ let hi = 0;
78
+ for (const row of data) {
79
+ let pos = 0;
80
+ let neg = 0;
81
+ for (const s of visible) {
82
+ const v = toNumber(row[s.key]);
83
+ if (stacked) {
84
+ if (v >= 0) pos += v;
85
+ else neg += v;
86
+ } else {
87
+ lo = Math.min(lo, v);
88
+ hi = Math.max(hi, v);
89
+ }
90
+ }
91
+ if (stacked) {
92
+ lo = Math.min(lo, neg);
93
+ hi = Math.max(hi, pos);
94
+ }
95
+ }
96
+ return niceScale(lo, hi);
97
+ }, [data, visible, stacked]);
98
+ const y = linearScale([scale.min, scale.max], [PAD_TOP + plotH, PAD_TOP]);
99
+ const y0 = y(0);
100
+ const columns = useMemo(() => {
101
+ const groupW = step * 0.7;
102
+ return data.map((row, ci) => {
103
+ const gx = left + ci * step + (step - groupW) / 2;
104
+ const bars = [];
105
+ if (stacked) {
106
+ let pos = 0;
107
+ let neg = 0;
108
+ const lastPos = [...visible].reverse().find((s) => toNumber(row[s.key]) > 0)?.key;
109
+ const lastNeg = [...visible].reverse().find((s) => toNumber(row[s.key]) < 0)?.key;
110
+ for (const s of visible) {
111
+ const v = toNumber(row[s.key]);
112
+ if (v === 0) continue;
113
+ const from = v > 0 ? pos : neg;
114
+ const to = from + v;
115
+ if (v > 0) pos = to;
116
+ else neg = to;
117
+ bars.push({
118
+ key: s.key,
119
+ x: gx,
120
+ width: groupW,
121
+ from,
122
+ to,
123
+ roundEnd: s.key === (v > 0 ? lastPos : lastNeg)
124
+ });
125
+ }
126
+ } else {
127
+ const k = Math.max(visible.length, 1);
128
+ const bw = Math.max((groupW - BAR_GAP * (k - 1)) / k, 1);
129
+ visible.forEach((s, si) => {
130
+ bars.push({
131
+ key: s.key,
132
+ x: gx + si * (bw + BAR_GAP),
133
+ width: bw,
134
+ from: 0,
135
+ to: toNumber(row[s.key]),
136
+ roundEnd: true
137
+ });
138
+ });
139
+ }
140
+ return bars;
141
+ });
142
+ }, [data, visible, stacked, step, left]);
143
+ const labels = data.map((row) => String(row[index] ?? ""));
144
+ const longest = labels.reduce((m, l) => Math.max(m, l.length), 0);
145
+ const labelEvery = Math.max(1, Math.ceil((longest * 8 + 12) / step));
146
+ const onKey = (e) => {
147
+ onKeyDown?.(e);
148
+ if (e.defaultPrevented || n === 0) return;
149
+ let next;
150
+ if (e.key === "ArrowRight") next = active === null ? 0 : Math.min(active + 1, n - 1);
151
+ else if (e.key === "ArrowLeft") next = active === null ? n - 1 : Math.max(active - 1, 0);
152
+ else if (e.key === "Home") next = 0;
153
+ else if (e.key === "End") next = n - 1;
154
+ else if (e.key === "Escape") next = null;
155
+ else return;
156
+ e.preventDefault();
157
+ setActive(next ?? null);
158
+ };
159
+ const activeRow = active !== null ? data[active] : void 0;
160
+ const activeCenter = active !== null ? left + (active + 0.5) * step : 0;
161
+ const activeTotal = activeRow ? visible.reduce((sum, s) => sum + toNumber(activeRow[s.key]), 0) : 0;
162
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("chart", "column-chart", className), ...rest, children: [
163
+ /* @__PURE__ */ jsxs("div", { ref: wrapRef, className: "column-chart__plot", children: [
164
+ /* @__PURE__ */ jsx(
165
+ "div",
166
+ {
167
+ role: "img",
168
+ "aria-label": ariaLabel,
169
+ tabIndex: 0,
170
+ className: "column-chart__figure",
171
+ onKeyDown: onKey,
172
+ onBlur: (e) => {
173
+ onBlur?.(e);
174
+ setActive(null);
175
+ },
176
+ children: /* @__PURE__ */ jsxs(
177
+ "svg",
178
+ {
179
+ className: "chart__svg column-chart__svg",
180
+ width,
181
+ height,
182
+ viewBox: `0 0 ${width} ${height}`,
183
+ onPointerLeave: () => setActive(null),
184
+ "aria-hidden": "true",
185
+ children: [
186
+ showGrid && /* @__PURE__ */ jsx("g", { className: "chart-grid", children: scale.ticks.map((t) => /* @__PURE__ */ jsx("line", { x1: left, x2: left + plotW, y1: y(t), y2: y(t) }, t)) }),
187
+ showYAxis && /* @__PURE__ */ jsx("g", { className: "chart-axis column-chart__y-axis", children: scale.ticks.map((t) => /* @__PURE__ */ jsx("text", { x: left - 8, y: y(t), textAnchor: "end", dominantBaseline: "middle", children: formatValue(t) }, t)) }),
188
+ active !== null && /* @__PURE__ */ jsx(
189
+ "rect",
190
+ {
191
+ className: "column-chart__band",
192
+ x: left + active * step,
193
+ y: PAD_TOP,
194
+ width: step,
195
+ height: plotH
196
+ }
197
+ ),
198
+ columns.map((bars, ci) => /* @__PURE__ */ jsx("g", { className: "column-chart__column", children: bars.map((b) => {
199
+ const s = series.find((x) => x.key === b.key);
200
+ if (!s) return null;
201
+ return /* @__PURE__ */ jsx(
202
+ "path",
203
+ {
204
+ className: "column-chart__bar",
205
+ d: barPath(b.x, y(b.from), y(b.to), b.width, barRadius, b.roundEnd),
206
+ fill: colorOf(s, series.indexOf(s))
207
+ },
208
+ b.key
209
+ );
210
+ }) }, labels[ci] + String(ci))),
211
+ /* @__PURE__ */ jsxs("g", { className: "chart-axis", children: [
212
+ /* @__PURE__ */ jsx("line", { x1: left, x2: left + plotW, y1: y0, y2: y0 }),
213
+ showXAxis && labels.map(
214
+ (l, ci) => ci % labelEvery === 0 ? /* @__PURE__ */ jsx(
215
+ "text",
216
+ {
217
+ x: left + (ci + 0.5) * step,
218
+ y: PAD_TOP + plotH + 16,
219
+ textAnchor: "middle",
220
+ children: l
221
+ },
222
+ ci
223
+ ) : null
224
+ )
225
+ ] }),
226
+ labels.map((l, ci) => /* @__PURE__ */ jsx(
227
+ "rect",
228
+ {
229
+ className: "column-chart__hit",
230
+ x: left + ci * step,
231
+ y: PAD_TOP,
232
+ width: step,
233
+ height: plotH,
234
+ onPointerEnter: () => setActive(ci),
235
+ children: /* @__PURE__ */ jsx("title", { children: l })
236
+ },
237
+ ci
238
+ ))
239
+ ]
240
+ }
241
+ )
242
+ }
243
+ ),
244
+ activeRow && /* @__PURE__ */ jsxs(
245
+ "div",
246
+ {
247
+ className: "chart-tooltip column-chart__tooltip",
248
+ role: "status",
249
+ style: {
250
+ left: activeCenter,
251
+ top: PAD_TOP,
252
+ transform: activeCenter > width / 2 ? "translateX(calc(-100% - 12px))" : "translateX(12px)"
253
+ },
254
+ children: [
255
+ /* @__PURE__ */ jsx("div", { className: "chart-tooltip__title", children: labels[active ?? 0] }),
256
+ visible.map((s) => /* @__PURE__ */ jsxs("div", { className: "chart-tooltip__row", children: [
257
+ /* @__PURE__ */ jsx(
258
+ "span",
259
+ {
260
+ className: "chart-tooltip__swatch",
261
+ style: { background: colorOf(s, series.indexOf(s)) }
262
+ }
263
+ ),
264
+ /* @__PURE__ */ jsx("span", { className: "chart-tooltip__label", children: s.label }),
265
+ /* @__PURE__ */ jsx("span", { className: "chart-tooltip__value", children: formatValue(toNumber(activeRow[s.key])) })
266
+ ] }, s.key)),
267
+ stacked && visible.length > 1 && /* @__PURE__ */ jsxs("div", { className: "chart-tooltip__row column-chart__total", children: [
268
+ /* @__PURE__ */ jsx("span", { className: "chart-tooltip__label", children: "合計" }),
269
+ /* @__PURE__ */ jsx("span", { className: "chart-tooltip__value", children: formatValue(activeTotal) })
270
+ ] })
271
+ ]
272
+ }
273
+ )
274
+ ] }),
275
+ showLegend && /* @__PURE__ */ jsx(
276
+ ChartLegend,
277
+ {
278
+ className: "column-chart__legend",
279
+ items: series.map((s, i) => ({ key: s.key, label: s.label, color: colorOf(s, i) })),
280
+ hidden,
281
+ onToggle: toggle
282
+ }
283
+ )
284
+ ] });
285
+ }
286
+ );
287
+ ColumnChart.displayName = "ColumnChart";
288
+ export {
289
+ ColumnChart
290
+ };
@@ -0,0 +1,255 @@
1
+ "use client";
2
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
3
+ import { forwardRef, useRef, useState } from "react";
4
+ import { cn } from "../../lib/utils.js";
5
+ import { defaultFormat } from "../../lib/chartScale.js";
6
+ const DAY_MS = 864e5;
7
+ const DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
8
+ const WEEKDAY_LABELS = ["日", "月", "火", "水", "木", "金", "土"];
9
+ const LABELLED_WEEKDAYS = /* @__PURE__ */ new Set([1, 3, 5]);
10
+ function parseDay(date) {
11
+ const m = DATE_RE.exec(date);
12
+ if (!m) return null;
13
+ const ms = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
14
+ const d = new Date(ms);
15
+ if (d.getUTCMonth() !== Number(m[2]) - 1) return null;
16
+ return ms / DAY_MS;
17
+ }
18
+ function formatDay(day) {
19
+ return new Date(day * DAY_MS).toISOString().slice(0, 10);
20
+ }
21
+ function levelOf(value, max, levels) {
22
+ if (value === null || value <= 0 || max <= 0) return 0;
23
+ return Math.min(levels, Math.max(1, Math.ceil(value / max * levels)));
24
+ }
25
+ function levelColor(level, levels) {
26
+ if (level === 0) return void 0;
27
+ const pct = levels === 1 ? 100 : Math.round(25 + 75 * (level - 1) / (levels - 1));
28
+ return `color-mix(in srgb, var(--heatmap-color) ${pct}%, var(--color-bg-tertiary))`;
29
+ }
30
+ const Heatmap = forwardRef(
31
+ ({
32
+ variant = "matrix",
33
+ rows = [],
34
+ columns = [],
35
+ values = [],
36
+ data = [],
37
+ startDate,
38
+ endDate,
39
+ weekStartsOn = 1,
40
+ color = "var(--chart-1)",
41
+ levels = 5,
42
+ max,
43
+ cellSize = 16,
44
+ gap = 3,
45
+ showLegend = true,
46
+ formatValue = defaultFormat,
47
+ formatTooltip,
48
+ "aria-label": ariaLabel,
49
+ className,
50
+ style,
51
+ ...rest
52
+ }, ref) => {
53
+ const rootRef = useRef(null);
54
+ const [tip, setTip] = useState(null);
55
+ const stepCount = Math.max(1, Math.floor(levels));
56
+ const cells = [];
57
+ let gridColumns = 0;
58
+ const monthLabels = [];
59
+ let maxValue = max;
60
+ if (variant === "matrix") {
61
+ gridColumns = columns.length;
62
+ rows.forEach((row, r) => {
63
+ columns.forEach((column, c) => {
64
+ const raw = values[r]?.[c];
65
+ cells.push({
66
+ key: `${r}:${c}`,
67
+ row,
68
+ column,
69
+ value: typeof raw === "number" && Number.isFinite(raw) ? raw : null,
70
+ gridRow: r + 2,
71
+ gridColumn: c + 2
72
+ });
73
+ });
74
+ });
75
+ } else {
76
+ const byDay = /* @__PURE__ */ new Map();
77
+ for (const d of data) {
78
+ const day = parseDay(d.date);
79
+ if (day === null || !Number.isFinite(d.value)) continue;
80
+ byDay.set(day, (byDay.get(day) ?? 0) + d.value);
81
+ }
82
+ const days = [...byDay.keys()];
83
+ const start = startDate ? parseDay(startDate) : days.length ? Math.min(...days) : null;
84
+ const end = endDate ? parseDay(endDate) : days.length ? Math.max(...days) : null;
85
+ if (start !== null && end !== null && end >= start) {
86
+ const rowOf = (day) => (new Date(day * DAY_MS).getUTCDay() - weekStartsOn + 7) % 7;
87
+ const gridStart = start - rowOf(start);
88
+ gridColumns = Math.floor((end - gridStart) / 7) + 1;
89
+ let lastLabelWeek = -Infinity;
90
+ let lastMonth = -1;
91
+ for (let day = start; day <= end; day++) {
92
+ const week = Math.floor((day - gridStart) / 7);
93
+ const month = new Date(day * DAY_MS).getUTCMonth();
94
+ if (month !== lastMonth) {
95
+ if (week - lastLabelWeek >= 3) {
96
+ monthLabels.push({ label: `${month + 1}月`, gridColumn: week + 2 });
97
+ lastLabelWeek = week;
98
+ }
99
+ lastMonth = month;
100
+ }
101
+ cells.push({
102
+ key: formatDay(day),
103
+ date: formatDay(day),
104
+ value: byDay.get(day) ?? null,
105
+ gridRow: rowOf(day) + 2,
106
+ gridColumn: week + 2
107
+ });
108
+ }
109
+ }
110
+ }
111
+ if (maxValue === void 0) {
112
+ maxValue = cells.reduce((acc, c) => c.value !== null && c.value > acc ? c.value : acc, 0);
113
+ }
114
+ const scaleMax = maxValue;
115
+ const show = (cell, el) => {
116
+ const root = rootRef.current;
117
+ if (!root) return;
118
+ const a = el.getBoundingClientRect();
119
+ const b = root.getBoundingClientRect();
120
+ const { row, column, date, value } = cell;
121
+ setTip({
122
+ x: a.left - b.left + a.width / 2,
123
+ y: a.top - b.top,
124
+ cell: { row, column, date, value }
125
+ });
126
+ };
127
+ const tooltipBody = (cell) => {
128
+ if (formatTooltip) return /* @__PURE__ */ jsx("div", { className: "chart-tooltip__title", children: formatTooltip(cell) });
129
+ const title = cell.date ?? `${cell.row} ${cell.column}`;
130
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
131
+ /* @__PURE__ */ jsx("div", { className: "chart-tooltip__title", children: title }),
132
+ /* @__PURE__ */ jsxs("div", { className: "chart-tooltip__row", children: [
133
+ /* @__PURE__ */ jsx("span", { className: "chart-tooltip__label", children: "値" }),
134
+ /* @__PURE__ */ jsx("span", { className: "chart-tooltip__value", children: cell.value === null ? "データなし" : formatValue(cell.value) })
135
+ ] })
136
+ ] });
137
+ };
138
+ const rootStyle = {
139
+ "--heatmap-color": color,
140
+ "--heatmap-cell": `${cellSize}px`,
141
+ "--heatmap-gap": `${gap}px`,
142
+ ...style
143
+ };
144
+ const longestColumnLabel = columns.reduce((m, c) => Math.max(m, String(c).length), 0);
145
+ const columnLabelStep = Math.max(1, Math.ceil((longestColumnLabel * 8 + 6) / (cellSize + gap)));
146
+ const gridStyle = {
147
+ gridTemplateColumns: `auto repeat(${gridColumns}, var(--heatmap-cell))`,
148
+ gridTemplateRows: `auto repeat(${variant === "matrix" ? rows.length : 7}, var(--heatmap-cell))`
149
+ };
150
+ return /* @__PURE__ */ jsxs(
151
+ "div",
152
+ {
153
+ ref: (node) => {
154
+ rootRef.current = node;
155
+ if (typeof ref === "function") ref(node);
156
+ else if (ref) ref.current = node;
157
+ },
158
+ role: "img",
159
+ "aria-label": ariaLabel,
160
+ className: cn("chart", "heatmap", `heatmap--${variant}`, className),
161
+ style: rootStyle,
162
+ ...rest,
163
+ children: [
164
+ /* @__PURE__ */ jsx("div", { className: "heatmap__scroll", children: /* @__PURE__ */ jsxs("div", { className: "heatmap__grid", style: gridStyle, onMouseLeave: () => setTip(null), children: [
165
+ variant === "matrix" ? /* @__PURE__ */ jsxs(Fragment, { children: [
166
+ columns.map((c, i) => /* @__PURE__ */ jsx(
167
+ "span",
168
+ {
169
+ hidden: i % columnLabelStep !== 0,
170
+ className: "heatmap__label heatmap__label--column",
171
+ style: { gridRow: 1, gridColumn: i + 2 },
172
+ children: c
173
+ },
174
+ `c${i}`
175
+ )),
176
+ rows.map((r, i) => /* @__PURE__ */ jsx(
177
+ "span",
178
+ {
179
+ className: "heatmap__label heatmap__label--row",
180
+ style: { gridRow: i + 2, gridColumn: 1 },
181
+ children: r
182
+ },
183
+ `r${i}`
184
+ ))
185
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
186
+ monthLabels.map((m) => /* @__PURE__ */ jsx(
187
+ "span",
188
+ {
189
+ className: "heatmap__label heatmap__label--month",
190
+ style: { gridRow: 1, gridColumn: `${m.gridColumn} / span 3` },
191
+ children: m.label
192
+ },
193
+ m.gridColumn
194
+ )),
195
+ Array.from({ length: 7 }, (_, row) => {
196
+ const dow = (row + weekStartsOn) % 7;
197
+ return LABELLED_WEEKDAYS.has(dow) ? /* @__PURE__ */ jsx(
198
+ "span",
199
+ {
200
+ className: "heatmap__label heatmap__label--row",
201
+ style: { gridRow: row + 2, gridColumn: 1 },
202
+ children: WEEKDAY_LABELS[dow]
203
+ },
204
+ dow
205
+ ) : null;
206
+ })
207
+ ] }),
208
+ cells.map((cell) => {
209
+ const level = levelOf(cell.value, scaleMax, stepCount);
210
+ const { key, gridRow, gridColumn } = cell;
211
+ return (
212
+ // biome-ignore lint/a11y/noStaticElementInteractions: pointer-only tooltip; the chart is exposed as role=img
213
+ /* @__PURE__ */ jsx(
214
+ "span",
215
+ {
216
+ className: cn("heatmap__cell", level === 0 && "heatmap__cell--empty"),
217
+ "data-level": level,
218
+ style: { gridRow, gridColumn, background: levelColor(level, stepCount) },
219
+ onMouseEnter: (e) => show(cell, e.currentTarget)
220
+ },
221
+ key
222
+ )
223
+ );
224
+ })
225
+ ] }) }),
226
+ showLegend && /* @__PURE__ */ jsxs("div", { className: "heatmap__legend", "aria-hidden": "true", children: [
227
+ /* @__PURE__ */ jsx("span", { children: "少" }),
228
+ Array.from({ length: stepCount }, (_, i) => i + 1).map((level) => /* @__PURE__ */ jsx(
229
+ "span",
230
+ {
231
+ className: "heatmap__swatch",
232
+ style: { background: levelColor(level, stepCount) }
233
+ },
234
+ level
235
+ )),
236
+ /* @__PURE__ */ jsx("span", { children: "多" })
237
+ ] }),
238
+ tip && /* @__PURE__ */ jsx(
239
+ "div",
240
+ {
241
+ className: "chart-tooltip heatmap__tooltip",
242
+ style: { left: tip.x, top: tip.y },
243
+ "aria-hidden": "true",
244
+ children: tooltipBody(tip.cell)
245
+ }
246
+ )
247
+ ]
248
+ }
249
+ );
250
+ }
251
+ );
252
+ Heatmap.displayName = "Heatmap";
253
+ export {
254
+ Heatmap
255
+ };