doom-design-system 0.4.9 → 0.4.10

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.
@@ -9,13 +9,19 @@ import { Text } from "../Text/Text.js";
9
9
  import styles from "./Chart.module.css";
10
10
  import { createScales, drawAxes, drawBars, drawGrid, drawLineArea, setupGradient, } from "./renderers.js";
11
11
  export function Chart({ data, type = "line", variant = "default", x, y, d3Config, className, style, renderTooltip, flat, title, render, withFrame = true, onValueChange, }) {
12
+ const TOOLTIP_OFFSET = 20;
13
+ const HIDE_DELAY_MS = 16;
14
+ const MOBILE_BREAKPOINT = 480;
12
15
  const svgRef = useRef(null);
13
16
  const containerRef = useRef(null);
17
+ const wrapperRef = useRef(null);
14
18
  const tooltipRef = useRef(null);
15
19
  const tooltipPosRef = useRef({ x: 0, y: 0 });
16
20
  const hideTimeoutRef = useRef(null);
17
21
  const gradientId = React.useId().replace(/:/g, "");
18
22
  const [activeData, setActiveData] = useState(null);
23
+ const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
24
+ const isMobile = dimensions.width > 0 && dimensions.width < MOBILE_BREAKPOINT;
19
25
  useEffect(() => {
20
26
  onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(activeData !== null && activeData !== void 0 ? activeData : null);
21
27
  }, [activeData, onValueChange]);
@@ -23,19 +29,19 @@ export function Chart({ data, type = "line", variant = "default", x, y, d3Config
23
29
  top: 20,
24
30
  right: 20,
25
31
  bottom: (d3Config === null || d3Config === void 0 ? void 0 : d3Config.xAxisLabel) ? 60 : 50,
26
- left: (d3Config === null || d3Config === void 0 ? void 0 : d3Config.yAxisLabel) ? 65 : 55,
32
+ left: (d3Config === null || d3Config === void 0 ? void 0 : d3Config.yAxisLabel) ? 85 : 55,
27
33
  }, curve: undefined, showAxes: true, grid: false, withGradient: type === "area", showDots: false }, d3Config)), [d3Config, type]);
28
- const wrapperRef = useRef(null);
29
- const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
30
- const isMobile = dimensions.width > 0 && dimensions.width < 480;
31
34
  React.useLayoutEffect(() => {
32
35
  if (activeData && tooltipRef.current && wrapperRef.current) {
33
36
  const { x, y } = tooltipPosRef.current;
34
37
  const rect = wrapperRef.current.getBoundingClientRect();
38
+ const tooltipWidth = tooltipRef.current.offsetWidth;
35
39
  const absX = rect.left + x;
36
- const isRightEdge = absX > window.innerWidth - 220;
37
- const offsetX = isRightEdge ? x - 20 : x + 20;
38
- tooltipRef.current.style.transform = `translate3d(${offsetX}px, calc(${y}px - 50%), 0)`;
40
+ const isRightEdge = absX + tooltipWidth + TOOLTIP_OFFSET > window.innerWidth - 10;
41
+ const xOffset = isRightEdge
42
+ ? -TOOLTIP_OFFSET - tooltipWidth
43
+ : TOOLTIP_OFFSET;
44
+ tooltipRef.current.style.transform = `translate3d(${x + xOffset}px, calc(${y}px - 50%), 0)`;
39
45
  }
40
46
  }, [activeData]);
41
47
  useEffect(() => {
@@ -46,8 +52,9 @@ export function Chart({ data, type = "line", variant = "default", x, y, d3Config
46
52
  };
47
53
  }, []);
48
54
  useEffect(() => {
49
- if (!wrapperRef.current)
55
+ if (!wrapperRef.current) {
50
56
  return;
57
+ }
51
58
  const resizeObserver = new ResizeObserver((entries) => {
52
59
  const entry = entries[0];
53
60
  if (entry) {
@@ -72,9 +79,10 @@ export function Chart({ data, type = "line", variant = "default", x, y, d3Config
72
79
  ? (d3Config === null || d3Config === void 0 ? void 0 : d3Config.xAxisLabel)
73
80
  ? 50
74
81
  : 30
75
- : config.margin.bottom, left: isMobile ? ((d3Config === null || d3Config === void 0 ? void 0 : d3Config.yAxisLabel) ? 50 : 30) : config.margin.left });
76
- if (width <= 0 || height <= 0)
82
+ : config.margin.bottom, left: isMobile ? ((d3Config === null || d3Config === void 0 ? void 0 : d3Config.yAxisLabel) ? 65 : 30) : config.margin.left });
83
+ if (width <= 0 || height <= 0) {
77
84
  return;
85
+ }
78
86
  const svg = select(svgRef.current);
79
87
  svg.selectAll("*").remove();
80
88
  const g = svg
@@ -86,8 +94,9 @@ export function Chart({ data, type = "line", variant = "default", x, y, d3Config
86
94
  setupGradient(svg, gradientId);
87
95
  }
88
96
  const { xScale, yScale, innerWidth, innerHeight } = createScales(data, width, height, margin, x, y, type);
89
- if (innerWidth <= 0 || innerHeight <= 0)
97
+ if (innerWidth <= 0 || innerHeight <= 0) {
90
98
  return;
99
+ }
91
100
  if (config.grid) {
92
101
  drawGrid(g, yScale, innerWidth, styles.grid);
93
102
  }
@@ -102,10 +111,13 @@ export function Chart({ data, type = "line", variant = "default", x, y, d3Config
102
111
  tooltipPosRef.current = { x: tx, y: ty };
103
112
  if (tooltipRef.current && wrapperRef.current) {
104
113
  const rect = wrapperRef.current.getBoundingClientRect();
114
+ const tooltipWidth = tooltipRef.current.offsetWidth;
105
115
  const absX = rect.left + tx;
106
- const isRightEdge = absX > window.innerWidth - 220;
107
- const offsetX = isRightEdge ? tx - 20 : tx + 20;
108
- tooltipRef.current.style.transform = `translate3d(${offsetX}px, calc(${ty}px - 50%), 0)`;
116
+ const isRightEdge = absX + tooltipWidth + TOOLTIP_OFFSET > window.innerWidth - 10;
117
+ const xOffset = isRightEdge
118
+ ? -TOOLTIP_OFFSET - tooltipWidth
119
+ : TOOLTIP_OFFSET;
120
+ tooltipRef.current.style.transform = `translate3d(${tx + xOffset}px, calc(${ty}px - 50%), 0)`;
109
121
  }
110
122
  setActiveData((prev) => (prev === data ? prev : data));
111
123
  };
@@ -113,13 +125,12 @@ export function Chart({ data, type = "line", variant = "default", x, y, d3Config
113
125
  const [px, py] = select(svgRef.current).select("g").empty()
114
126
  ? [0, 0]
115
127
  : pointer(event, g.node());
116
- const tx = px + margin.left + 20;
128
+ const tx = px + margin.left;
117
129
  const ty = py + margin.top;
118
130
  updateTooltip(tx, ty, data);
119
131
  };
120
132
  const hideTooltip = () => {
121
- // Delay prevents flickering when moving between adjacent elements
122
- hideTimeoutRef.current = setTimeout(() => setActiveData(null), 16);
133
+ hideTimeoutRef.current = setTimeout(() => setActiveData(null), HIDE_DELAY_MS);
123
134
  };
124
135
  const ctx = {
125
136
  g,
@@ -1,23 +1,50 @@
1
+ /**
2
+ * Chart Renderers
3
+ *
4
+ * D3-based rendering functions for charts. Each renderer handles
5
+ * drawing a specific chart type (line, area, bar) and managing
6
+ * user interactions (hover, touch).
7
+ */
1
8
  import * as d3Scale from "d3-scale";
2
9
  import { ChartConfig, D3Selection, DrawContext, SVGSelection } from "./types";
10
+ /**
11
+ * Creates X and Y scales based on data and chart dimensions.
12
+ * Automatically selects appropriate scale type (linear, point, band).
13
+ */
3
14
  export declare function createScales<T>(data: T[], width: number, height: number, margin: {
4
15
  top: number;
5
16
  right: number;
6
17
  bottom: number;
7
18
  left: number;
8
- }, x: (d: T) => any, y: (d: T) => number, type?: "line" | "area" | "bar"): {
9
- xScale: any;
19
+ }, x: (d: T) => string | number, y: (d: T) => number, type?: "line" | "area" | "bar"): {
20
+ xScale: d3Scale.ScaleLinear<number, number, never> | d3Scale.ScalePoint<string> | d3Scale.ScaleBand<string>;
10
21
  yScale: d3Scale.ScaleLinear<number, number, never>;
11
22
  innerWidth: number;
12
23
  innerHeight: number;
13
24
  };
25
+ /**
26
+ * Sets up an SVG gradient for area chart fills.
27
+ */
14
28
  export declare function setupGradient(svg: SVGSelection, gradientId: string): void;
15
- export declare function drawGrid(g: D3Selection, yScale: any, innerWidth: number, className: string): void;
16
- export declare function drawAxes(g: D3Selection, xScale: any, yScale: any, innerWidth: number, innerHeight: number, margin: {
29
+ /**
30
+ * Draws horizontal grid lines behind the chart.
31
+ */
32
+ export declare function drawGrid(g: D3Selection, yScale: d3Scale.ScaleLinear<number, number>, innerWidth: number, className: string): void;
33
+ /**
34
+ * Draws X and Y axes with labels.
35
+ * Automatically adjusts tick count to prevent label overlap.
36
+ */
37
+ export declare function drawAxes(g: D3Selection, xScale: d3Scale.ScaleLinear<number, number> | d3Scale.ScalePoint<string> | d3Scale.ScaleBand<string>, yScale: d3Scale.ScaleLinear<number, number>, innerWidth: number, innerHeight: number, margin: {
17
38
  top: number;
18
39
  right: number;
19
40
  bottom: number;
20
41
  left: number;
21
42
  }, config: ChartConfig, styles: Record<string, string>, isMobile: boolean): void;
43
+ /**
44
+ * Renders line and area charts with interactive cursor tracking.
45
+ */
22
46
  export declare function drawLineArea<T>({ g, data, xScale, yScale, x, y, innerWidth, innerHeight, config, styles, gradientId, setHoverState, margin, type, }: DrawContext<T>): void;
23
- export declare function drawBars<T>({ g, data, xScale, yScale, x, y, innerHeight, styles, setHoverState, margin, }: DrawContext<T>): void;
47
+ /**
48
+ * Renders bar charts with hover highlighting.
49
+ */
50
+ export declare function drawBars<T>({ g, data, xScale, yScale, x, y, innerWidth, innerHeight, styles, setHoverState, margin, }: DrawContext<T>): void;
@@ -1,9 +1,71 @@
1
+ /**
2
+ * Chart Renderers
3
+ *
4
+ * D3-based rendering functions for charts. Each renderer handles
5
+ * drawing a specific chart type (line, area, bar) and managing
6
+ * user interactions (hover, touch).
7
+ */
1
8
  import * as d3Array from "d3-array";
2
9
  import * as d3Axis from "d3-axis";
3
10
  import * as d3Scale from "d3-scale";
4
11
  import * as d3Selection from "d3-selection";
5
12
  import * as d3Shape from "d3-shape";
6
13
  const d3 = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, d3Scale), d3Shape), d3Selection), d3Axis), d3Array);
14
+ // ============================================================================
15
+ // UTILITIES
16
+ // ============================================================================
17
+ /**
18
+ * Extracts pointer coordinates from mouse or touch events.
19
+ * Uses the parent SVG as reference for accurate positioning across all devices.
20
+ */
21
+ function getPointerCoords(event, gElement, margin) {
22
+ if (!gElement) {
23
+ return [0, 0];
24
+ }
25
+ const svg = gElement.ownerSVGElement;
26
+ if (!svg) {
27
+ return [0, 0];
28
+ }
29
+ const rect = svg.getBoundingClientRect();
30
+ let clientX;
31
+ let clientY;
32
+ if ("touches" in event && event.touches.length > 0) {
33
+ clientX = event.touches[0].clientX;
34
+ clientY = event.touches[0].clientY;
35
+ }
36
+ else if ("changedTouches" in event && event.changedTouches.length > 0) {
37
+ clientX = event.changedTouches[0].clientX;
38
+ clientY = event.changedTouches[0].clientY;
39
+ }
40
+ else {
41
+ clientX = event.clientX;
42
+ clientY = event.clientY;
43
+ }
44
+ return [clientX - rect.left - margin.left, clientY - rect.top - margin.top];
45
+ }
46
+ /**
47
+ * Creates an SVG path for a rectangle with rounded top corners.
48
+ * Used for bar charts to create a softer, modern look.
49
+ */
50
+ function createRoundedTopBarPath(xPos, yPos, width, height, radius) {
51
+ const r = Math.min(radius, width / 2, height);
52
+ return `
53
+ M ${xPos},${yPos + height}
54
+ L ${xPos},${yPos + r}
55
+ A ${r},${r} 0 0 1 ${xPos + r},${yPos}
56
+ L ${xPos + width - r},${yPos}
57
+ A ${r},${r} 0 0 1 ${xPos + width},${yPos + r}
58
+ L ${xPos + width},${yPos + height}
59
+ Z
60
+ `;
61
+ }
62
+ // ============================================================================
63
+ // SCALE & AXIS SETUP
64
+ // ============================================================================
65
+ /**
66
+ * Creates X and Y scales based on data and chart dimensions.
67
+ * Automatically selects appropriate scale type (linear, point, band).
68
+ */
7
69
  export function createScales(data, width, height, margin, x, y, type) {
8
70
  const innerWidth = width - margin.left - margin.right;
9
71
  const innerHeight = height - margin.top - margin.bottom;
@@ -37,6 +99,9 @@ export function createScales(data, width, height, margin, x, y, type) {
37
99
  .range([innerHeight, 0]);
38
100
  return { xScale, yScale, innerWidth, innerHeight };
39
101
  }
102
+ /**
103
+ * Sets up an SVG gradient for area chart fills.
104
+ */
40
105
  export function setupGradient(svg, gradientId) {
41
106
  const defs = svg.append("defs");
42
107
  const gradient = defs
@@ -57,6 +122,9 @@ export function setupGradient(svg, gradientId) {
57
122
  .attr("stop-color", "var(--primary)")
58
123
  .attr("stop-opacity", 0);
59
124
  }
125
+ /**
126
+ * Draws horizontal grid lines behind the chart.
127
+ */
60
128
  export function drawGrid(g, yScale, innerWidth, className) {
61
129
  g.append("g")
62
130
  .attr("class", className)
@@ -65,12 +133,15 @@ export function drawGrid(g, yScale, innerWidth, className) {
65
133
  .tickSize(-innerWidth)
66
134
  .tickFormat(() => ""));
67
135
  }
136
+ /**
137
+ * Draws X and Y axes with labels.
138
+ * Automatically adjusts tick count to prevent label overlap.
139
+ */
68
140
  export function drawAxes(g, xScale, yScale, innerWidth, innerHeight, margin, config, styles, isMobile) {
69
- const tickWidth = 40; // Approx max width of a label "SEPT"
70
- const maxTicks = Math.floor(innerWidth / tickWidth);
141
+ const TICK_WIDTH = 40; // Approximate width of a tick label
142
+ const maxTicks = Math.floor(innerWidth / TICK_WIDTH);
71
143
  const axisBottom = d3.axisBottom(xScale);
72
- // If discrete scale (has domain array), reduce ticks to prevent overlap
73
- if (xScale.domain && Array.isArray(xScale.domain())) {
144
+ if ("domain" in xScale && Array.isArray(xScale.domain())) {
74
145
  const domain = xScale.domain();
75
146
  if (domain.length > maxTicks) {
76
147
  const step = Math.ceil(domain.length / maxTicks);
@@ -80,8 +151,7 @@ export function drawAxes(g, xScale, yScale, innerWidth, innerHeight, margin, con
80
151
  else {
81
152
  axisBottom.ticks(Math.min(5, maxTicks));
82
153
  }
83
- const xAxis = g
84
- .append("g")
154
+ g.append("g")
85
155
  .attr("transform", `translate(0,${innerHeight})`)
86
156
  .call(axisBottom);
87
157
  const yAxis = g.append("g").call(d3
@@ -107,6 +177,12 @@ export function drawAxes(g, xScale, yScale, innerWidth, innerHeight, margin, con
107
177
  .text(config.yAxisLabel);
108
178
  }
109
179
  }
180
+ // ============================================================================
181
+ // CHART RENDERERS
182
+ // ============================================================================
183
+ /**
184
+ * Renders line and area charts with interactive cursor tracking.
185
+ */
110
186
  export function drawLineArea({ g, data, xScale, yScale, x, y, innerWidth, innerHeight, config, styles, gradientId, setHoverState, margin, type, }) {
111
187
  const lineGenerator = d3
112
188
  .line()
@@ -146,14 +222,14 @@ export function drawLineArea({ g, data, xScale, yScale, x, y, innerWidth, innerH
146
222
  .attr("cy", (d) => yScale(y(d)))
147
223
  .attr("r", 5);
148
224
  }
149
- // Interaction Overlay
150
225
  const overlay = g
151
226
  .append("rect")
152
227
  .attr("class", "overlay")
153
228
  .attr("width", innerWidth)
154
229
  .attr("height", innerHeight)
155
230
  .style("fill", "transparent")
156
- .style("cursor", "crosshair");
231
+ .style("cursor", "crosshair")
232
+ .style("touch-action", "none");
157
233
  const cursorLine = g
158
234
  .append("line")
159
235
  .attr("class", styles.cursorLine)
@@ -165,59 +241,65 @@ export function drawLineArea({ g, data, xScale, yScale, x, y, innerWidth, innerH
165
241
  .attr("class", styles.cursorPoint)
166
242
  .attr("r", 6)
167
243
  .style("opacity", 0);
168
- const interactionHandler = (event) => {
169
- var _a;
170
- if (event.type.startsWith("touch")) {
171
- event.preventDefault();
172
- }
173
- const [pointerX, pointerY] = d3.pointer(event);
174
- let selectedData = null;
175
- if (xScale.invert) {
244
+ const findNearestPoint = (pointerX) => {
245
+ if ("invert" in xScale && typeof xScale.invert === "function") {
176
246
  const x0 = xScale.invert(pointerX);
177
247
  const bisect = d3.bisector(x).left;
178
248
  const i = bisect(data, x0, 1);
179
249
  const d0 = data[i - 1];
180
250
  const d1 = data[i];
181
251
  if (!d0) {
182
- selectedData = d1;
183
- }
184
- else if (!d1) {
185
- selectedData = d0;
252
+ return d1;
186
253
  }
187
- else {
188
- const d0Dist = x0 - x(d0);
189
- const d1Dist = x(d1) - x0;
190
- selectedData = d0Dist > d1Dist ? d1 : d0;
254
+ if (!d1) {
255
+ return d0;
191
256
  }
257
+ const d0Dist = x0 - x(d0);
258
+ const d1Dist = x(d1) - x0;
259
+ return d0Dist > d1Dist ? d1 : d0;
192
260
  }
193
- else {
261
+ else if ("step" in xScale && typeof xScale.step === "function") {
194
262
  const step = xScale.step();
195
263
  const index = Math.round(pointerX / step);
196
- selectedData = data[Math.min(Math.max(0, index), data.length - 1)];
264
+ return data[Math.min(Math.max(0, index), data.length - 1)];
265
+ }
266
+ return null;
267
+ };
268
+ const handleInteraction = (event) => {
269
+ var _a;
270
+ if (event.type.startsWith("touch") && event.cancelable) {
271
+ event.preventDefault();
197
272
  }
273
+ const [pointerX, pointerY] = getPointerCoords(event, g.node(), margin);
274
+ const selectedData = findNearestPoint(pointerX);
198
275
  if (selectedData) {
199
276
  const cx = (_a = xScale(x(selectedData))) !== null && _a !== void 0 ? _a : 0;
200
277
  const cy = yScale(y(selectedData));
201
278
  cursorLine.attr("x1", cx).attr("x2", cx).style("opacity", 1);
202
279
  cursorDot.attr("cx", cx).attr("cy", cy).style("opacity", 1);
280
+ const isTouch = event.type.startsWith("touch");
203
281
  setHoverState({
204
- x: pointerX + margin.left,
205
- y: pointerY + margin.top,
282
+ x: (isTouch ? cx : pointerX) + margin.left,
283
+ y: (isTouch ? cy : pointerY) + margin.top, // Snap Y to point on touch, cursor on mouse
206
284
  data: selectedData,
207
285
  });
208
286
  }
209
287
  };
210
288
  overlay
211
- .on("mousemove touchmove touchstart", interactionHandler)
212
- .on("mouseleave touchend", () => {
289
+ .on("mousemove touchmove touchstart", handleInteraction)
290
+ .on("mouseleave touchend touchcancel", () => {
213
291
  cursorLine.style("opacity", 0);
214
292
  cursorDot.style("opacity", 0);
215
293
  setHoverState(null);
216
294
  });
217
295
  }
218
- export function drawBars({ g, data, xScale, yScale, x, y, innerHeight, styles, setHoverState, margin, }) {
219
- const radius = 4; // Matching --radius token
220
- g.selectAll(".bar")
296
+ /**
297
+ * Renders bar charts with hover highlighting.
298
+ */
299
+ export function drawBars({ g, data, xScale, yScale, x, y, innerWidth, innerHeight, styles, setHoverState, margin, }) {
300
+ const BAR_RADIUS = 4;
301
+ const bars = g
302
+ .selectAll(".bar")
221
303
  .data(data)
222
304
  .enter()
223
305
  .append("path")
@@ -225,28 +307,60 @@ export function drawBars({ g, data, xScale, yScale, x, y, innerHeight, styles, s
225
307
  .attr("d", (d) => {
226
308
  const xVal = xScale(x(d)) || 0;
227
309
  const yVal = yScale(y(d));
228
- const w = xScale.bandwidth ? xScale.bandwidth() : 10;
310
+ const w = "bandwidth" in xScale ? xScale.bandwidth() : 10;
229
311
  const h = innerHeight - yVal;
230
- const r = Math.min(radius, w / 2, h);
231
- return `
232
- M ${xVal},${yVal + h}
233
- L ${xVal},${yVal + r}
234
- A ${r},${r} 0 0 1 ${xVal + r},${yVal}
235
- L ${xVal + w - r},${yVal}
236
- A ${r},${r} 0 0 1 ${xVal + w},${yVal + r}
237
- L ${xVal + w},${yVal + h}
238
- Z
239
- `;
240
- })
241
- .on("mouseenter mousemove touchstart", (event, d) => {
242
- if (event.type === "touchstart")
312
+ return createRoundedTopBarPath(xVal, yVal, w, h, BAR_RADIUS);
313
+ });
314
+ const overlay = g
315
+ .append("rect")
316
+ .attr("class", "overlay")
317
+ .attr("width", innerWidth)
318
+ .attr("height", innerHeight)
319
+ .style("fill", "transparent")
320
+ .style("cursor", "crosshair")
321
+ .style("touch-action", "none");
322
+ const findNearestBar = (pointerX) => {
323
+ const bandwidth = "bandwidth" in xScale ? xScale.bandwidth() : 10;
324
+ let nearestData = null;
325
+ let minDist = Infinity;
326
+ for (const d of data) {
327
+ const barX = xScale(x(d)) || 0;
328
+ const barCenter = barX + bandwidth / 2;
329
+ const dist = Math.abs(pointerX - barCenter);
330
+ if (dist < minDist) {
331
+ minDist = dist;
332
+ nearestData = d;
333
+ }
334
+ }
335
+ return nearestData;
336
+ };
337
+ const handleInteraction = (event) => {
338
+ if (event.type.startsWith("touch") && event.cancelable) {
243
339
  event.preventDefault();
244
- const [px, py] = d3Selection.pointer(event, g.node());
245
- setHoverState({
246
- x: px + margin.left,
247
- y: py + margin.top,
248
- data: d,
249
- });
250
- })
251
- .on("mouseleave touchend", () => setHoverState(null));
340
+ }
341
+ const [pointerX, pointerY] = getPointerCoords(event, g.node(), margin);
342
+ const selectedData = findNearestBar(pointerX);
343
+ if (selectedData) {
344
+ bars.style("opacity", (d) => (d === selectedData ? 1 : 0.6));
345
+ let hoverX = pointerX;
346
+ let hoverY = pointerY;
347
+ if (event.type.startsWith("touch")) {
348
+ const xVal = xScale(x(selectedData)) || 0;
349
+ const bandwidth = "bandwidth" in xScale ? xScale.bandwidth() : 10;
350
+ hoverX = xVal + bandwidth / 2;
351
+ hoverY = yScale(y(selectedData));
352
+ }
353
+ setHoverState({
354
+ x: hoverX + margin.left,
355
+ y: hoverY + margin.top,
356
+ data: selectedData,
357
+ });
358
+ }
359
+ };
360
+ overlay
361
+ .on("mousemove touchmove touchstart", handleInteraction)
362
+ .on("mouseleave touchend touchcancel", () => {
363
+ bars.style("opacity", 1);
364
+ setHoverState(null);
365
+ });
252
366
  }
@@ -95,7 +95,7 @@ export function Image(_a) {
95
95
  }
96
96
  }
97
97
  setStatus("error");
98
- setShowSkeleton(false); // Remove skeleton immediately on error to show broken image icon or alt
98
+ setShowSkeleton(false); // Remove skeleton immediately on error
99
99
  onError === null || onError === void 0 ? void 0 : onError(e);
100
100
  }, [fallbackSrc, status, onError]);
101
101
  return (_jsxs("div", { className: clsx(styles.wrapper, rounded && styles.rounded, className), style: Object.assign({ aspectRatio: computedAspectRatio, width: toCssValue(width), height: toCssValue(height) }, style), children: [_jsx("div", { "aria-hidden": "true", className: clsx(styles.skeletonLayer, status === "loaded" && styles.fadeOut), children: showSkeleton && (_jsx(Skeleton, { style: {
@@ -25,7 +25,8 @@ export interface LayoutProps extends React.HTMLAttributes<HTMLElement> {
25
25
  export interface GridProps extends LayoutProps {
26
26
  /**
27
27
  * Defines the columns of the grid.
28
- * Accepts a number (e.g., `3` for 3 equal columns) or a CSS string (e.g., `"1fr 2fr"`).
28
+ * Accepts a number (e.g., `3` for 3 equal columns) or a CSS string
29
+ * (e.g., `"1fr 2fr"`).
29
30
  * @default "1fr"
30
31
  */
31
32
  columns?: string | number;
@@ -38,7 +39,8 @@ export interface GridProps extends LayoutProps {
38
39
  }
39
40
  /**
40
41
  * A CSS Grid container for creating two-dimensional layouts.
41
- * Use this when you need precise control over columns and rows, or complex grid placements.
42
+ * Use this when you need precise control over columns and rows, or complex
43
+ * grid placements.
42
44
  */
43
45
  export declare function Grid({ children, columns, gap, className, style, as: Component, ...props }: GridProps): import("react/jsx-runtime").JSX.Element;
44
46
  export interface FlexProps extends LayoutProps {
@@ -64,7 +66,8 @@ export interface FlexProps extends LayoutProps {
64
66
  */
65
67
  gap?: Spacing;
66
68
  /**
67
- * Controls whether flex items are forced onto one line or can wrap onto multiple lines.
69
+ * Controls whether flex items are forced onto one line or can wrap onto
70
+ * multiple lines.
68
71
  * @default false
69
72
  */
70
73
  wrap?: boolean | "wrap" | "nowrap" | "wrap-reverse";
@@ -87,20 +90,22 @@ export interface StackProps extends Omit<FlexProps, "direction"> {
87
90
  * A specialized Flex container explicitly optimized for vertical stacking.
88
91
  * Use this for lists, form fields, card content, or any group of elements
89
92
  * that should be arranged vertically with consistent spacing.
90
- * Note: While it defaults to column, `direction="row"` is supported for semantic overrides.
93
+ * Note: While it defaults to column, `direction="row"` is supported for
94
+ * semantic overrides.
91
95
  */
92
96
  export declare function Stack({ children, direction, gap, align, ...props }: StackProps): import("react/jsx-runtime").JSX.Element;
93
97
  export interface SwitcherProps extends FlexProps {
94
98
  /**
95
- * The breakpoint threshold at which the layout switches from horizontal to vertical.
96
- * e.g., "xs" means it will be horizontal on screens larger than "xs" (480px), and vertical below.
99
+ * The breakpoint threshold at which the layout switches from horizontal to
100
+ * vertical. e.g., "xs" means it will be horizontal on screens larger than
101
+ * "xs" (480px), and vertical below.
97
102
  * @default "xs"
98
103
  */
99
104
  threshold?: "xxs" | "xs" | "sm" | "md";
100
105
  }
101
106
  /**
102
- * A responsive layout component that switches from horizontal to vertical layout
103
- * based on a container query or breakpoint threshold.
107
+ * A responsive layout component that switches from horizontal to vertical
108
+ * layout based on a container query or breakpoint threshold.
104
109
  * Use this for "sidebar + main content" layouts or any pattern that needs
105
110
  * to stack on smaller screens but sit side-by-side on larger ones.
106
111
  */
@@ -34,7 +34,8 @@ function resolveGap(gap) {
34
34
  }
35
35
  /**
36
36
  * A CSS Grid container for creating two-dimensional layouts.
37
- * Use this when you need precise control over columns and rows, or complex grid placements.
37
+ * Use this when you need precise control over columns and rows, or complex
38
+ * grid placements.
38
39
  */
39
40
  export function Grid(_a) {
40
41
  var { children, columns = "1fr", gap = 4, className, style, as: Component = "div" } = _a, props = __rest(_a, ["children", "columns", "gap", "className", "style", "as"]);
@@ -57,15 +58,16 @@ export function Flex(_a) {
57
58
  * A specialized Flex container explicitly optimized for vertical stacking.
58
59
  * Use this for lists, form fields, card content, or any group of elements
59
60
  * that should be arranged vertically with consistent spacing.
60
- * Note: While it defaults to column, `direction="row"` is supported for semantic overrides.
61
+ * Note: While it defaults to column, `direction="row"` is supported for
62
+ * semantic overrides.
61
63
  */
62
64
  export function Stack(_a) {
63
65
  var { children, direction = "column", gap = 4, align = "stretch" } = _a, props = __rest(_a, ["children", "direction", "gap", "align"]);
64
66
  return (_jsx(Flex, Object.assign({ align: align, direction: direction, gap: gap }, props, { children: children })));
65
67
  }
66
68
  /**
67
- * A responsive layout component that switches from horizontal to vertical layout
68
- * based on a container query or breakpoint threshold.
69
+ * A responsive layout component that switches from horizontal to vertical
70
+ * layout based on a container query or breakpoint threshold.
69
71
  * Use this for "sidebar + main content" layouts or any pattern that needs
70
72
  * to stack on smaller screens but sit side-by-side on larger ones.
71
73
  */
@@ -29,7 +29,8 @@ export function Table({ data, columns, enablePagination = true, enableFiltering
29
29
  onGlobalFilterChange: setGlobalFilter,
30
30
  onPaginationChange: setPagination,
31
31
  getCoreRowModel: getCoreRowModel(),
32
- getSortedRowModel: getSortedRowModel(), // Always provide the model, enableSorting controls if it's used
32
+ // Always provide the model, enableSorting controls usage
33
+ getSortedRowModel: getSortedRowModel(),
33
34
  getPaginationRowModel: enablePagination
34
35
  ? getPaginationRowModel()
35
36
  : undefined,
@@ -1,11 +1,14 @@
1
1
  /* _reset.scss */
2
- *, *::before, *::after {
2
+ *,
3
+ *::before,
4
+ *::after {
3
5
  box-sizing: border-box;
4
6
  margin: 0;
5
7
  padding: 0;
6
8
  }
7
9
 
8
- html, body {
10
+ html,
11
+ body {
9
12
  height: 100%;
10
13
  }
11
14
 
@@ -17,16 +20,29 @@ body {
17
20
  font-family: var(--font-sans, system-ui, sans-serif);
18
21
  }
19
22
 
20
- img, picture, video, canvas, svg {
23
+ img,
24
+ picture,
25
+ video,
26
+ canvas,
27
+ svg {
21
28
  display: block;
22
29
  max-width: 100%;
23
30
  }
24
31
 
25
- input, button, textarea, select {
32
+ input,
33
+ button,
34
+ textarea,
35
+ select {
26
36
  font: inherit;
27
37
  }
28
38
 
29
- p, h1, h2, h3, h4, h5, h6 {
39
+ p,
40
+ h1,
41
+ h2,
42
+ h3,
43
+ h4,
44
+ h5,
45
+ h6 {
30
46
  overflow-wrap: break-word;
31
47
  }
32
48