@funnest/charts-react 0.2.0 → 0.2.1

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/index.js CHANGED
@@ -93,6 +93,7 @@ import {
93
93
 
94
94
  // src/viz/context.ts
95
95
  import { createContext as createContext2, useContext as useContext2 } from "react";
96
+ var nearScrub = (ctx, x, within = 56) => ctx.scrubIndex !== null && Math.abs(ctx.x(ctx.scrubIndex) - x) < within;
96
97
  var CartesianContext = createContext2(null);
97
98
  function useCartesianChart() {
98
99
  const ctx = useContext2(CartesianContext);
@@ -244,6 +245,7 @@ function CartesianChart({
244
245
  const [motion, setMotion] = useState2(false);
245
246
  const [hidden, setHidden] = useState2([]);
246
247
  const [emphasis, setEmphasis] = useState2(null);
248
+ const [dimFuture, setDimFuture] = useState2(false);
247
249
  useEffect2(() => setMotion(animate && !prefersReducedMotion()), [animate]);
248
250
  const legendCfg = normalizeLegend(legend);
249
251
  const axes = useMemo(() => {
@@ -325,9 +327,11 @@ function CartesianChart({
325
327
  xData,
326
328
  scrubIndex,
327
329
  focused,
328
- animate: motion
330
+ animate: motion,
331
+ futureMask: dimFuture && !band && scrubIndex !== null && scrubIndex < length ? `url(#${id}-future)` : void 0,
332
+ setDimFuture
329
333
  };
330
- }, [id, width, height, inset, axisSpace, all, visible, axes, firstAxis, xAxis, stacked, scrubIndex, focused, motion]);
334
+ }, [id, width, height, inset, axisSpace, all, visible, axes, firstAxis, xAxis, stacked, scrubIndex, focused, motion, dimFuture]);
331
335
  const xsRef = useRef2([]);
332
336
  xsRef.current = useMemo(() => Array.from({ length: ctx.length }, (_, i) => ctx.x(i)), [ctx.length, ctx.x]);
333
337
  const updateScrub = useCallback(
@@ -415,6 +419,11 @@ function CartesianChart({
415
419
  updateScrub(null);
416
420
  } : void 0,
417
421
  children: [
422
+ ctx.futureMask && // Fade everything right of the scrubber to 35% (luminance mask: works on any background).
423
+ /* @__PURE__ */ jsxs2("mask", { id: `${id}-future`, maskUnits: "userSpaceOnUse", x: -width, y: -height, width: width * 3, height: height * 3, children: [
424
+ /* @__PURE__ */ jsx3("rect", { x: -width, y: -height, width: width + ctx.x(scrubIndex), height: height * 3, fill: "#fff" }),
425
+ /* @__PURE__ */ jsx3("rect", { x: ctx.x(scrubIndex), y: -height, width: width * 2, height: height * 3, fill: "#fff", fillOpacity: 0.35 })
426
+ ] }),
418
427
  width > 0 && hasData && /* @__PURE__ */ jsx3(CartesianContext.Provider, { value: ctx, children }),
419
428
  width > 0 && !hasData && /* @__PURE__ */ jsx3("text", { x: width / 2, y: height / 2, textAnchor: "middle", dominantBaseline: "central", fontSize: 13, style: { fill: VAR.text, fontFamily: VAR.font }, children: typeof emptyState === "string" || emptyState === void 0 ? emptyState ?? "" : null })
420
429
  ]
@@ -572,13 +581,13 @@ function Area({ seriesId, curve = "monotone", fill = "gradient", opacity, connec
572
581
  ] }, key);
573
582
  };
574
583
  if (splitColors && baseline !== void 0 && !run0IsStacked(ctx, s)) {
575
- return /* @__PURE__ */ jsxs4("g", { children: [
584
+ return /* @__PURE__ */ jsxs4("g", { mask: ctx.futureMask, children: [
576
585
  /* @__PURE__ */ jsx5(SplitClips, { id: gid, y: base, ctx }),
577
586
  paint("above", splitColors.above, ctx.plot.top, `${gid}-above`),
578
587
  paint("below", splitColors.below, ctx.plot.bottom, `${gid}-below`)
579
588
  ] });
580
589
  }
581
- return /* @__PURE__ */ jsx5("g", { children: paint("fill", s.color, ctx.plot.top) });
590
+ return /* @__PURE__ */ jsx5("g", { mask: ctx.futureMask, children: paint("fill", s.color, ctx.plot.top) });
582
591
  }
583
592
  var run0IsStacked = (ctx, s) => !!ctx.stacked?.get(s.id);
584
593
  function Line({
@@ -606,7 +615,7 @@ function Line({
606
615
  const gid = `${ctx.id}-line-${s.index}`;
607
616
  const baseY = baselineY(ctx, s.scale, baseline);
608
617
  const strokeOpacity = (opacity ?? 1) * (s.dimmed ? DIM_OPACITY : 1);
609
- const strokes = (color, clip) => /* @__PURE__ */ jsx5("g", { clipPath: clip ? `url(#${clip})` : void 0, children: runs.map((run, k) => /* @__PURE__ */ jsx5(
618
+ const strokes = (color, clip) => /* @__PURE__ */ jsx5("g", { clipPath: clip ? `url(#${clip})` : void 0, mask: ctx.futureMask, children: runs.map((run, k) => /* @__PURE__ */ jsx5(
610
619
  "path",
611
620
  {
612
621
  d: linePath(run.upper, curve),
@@ -640,6 +649,7 @@ function Line({
640
649
  p.label && /* @__PURE__ */ jsx5(
641
650
  "text",
642
651
  {
652
+ opacity: nearScrub(ctx, x) ? 0 : 1,
643
653
  x,
644
654
  y: p.labelPosition === "bottom" ? y + r + 13 : y - r - 6,
645
655
  textAnchor: "middle",
@@ -887,6 +897,7 @@ function HorizontalBarChart({
887
897
  }
888
898
 
889
899
  // src/viz/scrubber.tsx
900
+ import { useEffect as useEffect4 } from "react";
890
901
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
891
902
  var TIP_FONT = 12;
892
903
  var ROW = 18;
@@ -905,6 +916,12 @@ function Scrubber({
905
916
  }) {
906
917
  const ctx = useCartesianChart();
907
918
  const i = ctx.scrubIndex;
919
+ const { setDimFuture } = ctx;
920
+ const fade = dimFuture && !hideOverlay;
921
+ useEffect4(() => {
922
+ setDimFuture(fade);
923
+ return () => setDimFuture(false);
924
+ }, [fade, setDimFuture]);
908
925
  const pick = seriesIds ? ctx.series.filter((s) => seriesIds.includes(s.id)) : ctx.series;
909
926
  const colorOf = (s, v) => beaconColor?.(v, s.id) ?? s.color;
910
927
  if (i === null || i >= ctx.length) {
@@ -944,7 +961,6 @@ function Scrubber({
944
961
  const tipX = tipRight ? x + 12 : x - 12 - tipW;
945
962
  const tipY = plot.top + 4;
946
963
  return /* @__PURE__ */ jsxs6("g", { pointerEvents: "none", children: [
947
- dimFuture && !hideOverlay && !ctx.band && /* @__PURE__ */ jsx8("rect", { x, y: plot.top, width: Math.max(0, plot.right - x + 2), height: plot.height, fillOpacity: 0.55, style: { fill: VAR.bg } }),
948
964
  /* @__PURE__ */ jsx8("line", { x1: x, x2: x, y1: plot.top, y2: plot.bottom, strokeWidth: 1, shapeRendering: "crispEdges", style: { stroke: VAR.line } }),
949
965
  text && /* @__PURE__ */ jsx8("text", { x: textX, y: Math.max(11, plot.top - 2), textAnchor: "middle", fontSize: 11, fontWeight: 600, strokeWidth: 3, strokeLinejoin: "round", paintOrder: "stroke", style: { stroke: VAR.bg, fill: VAR.textStrong }, children: text }),
950
966
  !ctx.band && list.map((s) => {
@@ -1070,7 +1086,7 @@ function Point({ dataX, dataY, seriesId, yAxisId, color, radius = 4, label, labe
1070
1086
  /* @__PURE__ */ jsx9("animate", { attributeName: "fill-opacity", values: "0.35;0", dur: "1.6s", repeatCount: "indefinite" })
1071
1087
  ] }),
1072
1088
  /* @__PURE__ */ jsx9("circle", { cx: x, cy: y, r: radius, strokeWidth: 2, style: { fill, stroke: VAR.bg } }),
1073
- label !== void 0 && /* @__PURE__ */ jsx9("text", { x: lx, y: ly, textAnchor: anchor, fontSize: 11, fontWeight: 600, strokeWidth: 3, strokeLinejoin: "round", paintOrder: "stroke", style: { stroke: VAR.bg, fill }, children: label })
1089
+ label !== void 0 && /* @__PURE__ */ jsx9("text", { opacity: nearScrub(ctx, x) ? 0 : 1, x: lx, y: ly, textAnchor: anchor, fontSize: 11, fontWeight: 600, strokeWidth: 3, strokeLinejoin: "round", paintOrder: "stroke", style: { stroke: VAR.bg, fill }, children: label })
1074
1090
  ] });
1075
1091
  }
1076
1092
 
@@ -1272,7 +1288,7 @@ function AreaChart({
1272
1288
  seriesId: s.id,
1273
1289
  curve,
1274
1290
  fill: own,
1275
- ...defined({ baseline, splitColors, gradientStops, opacity: fillOpacity ?? (chart.stacked && own === "solid" ? 0.55 : void 0) })
1291
+ ...defined({ baseline, splitColors, gradientStops, opacity: fillOpacity ?? (chart.stacked && own === "solid" ? 0.3 : void 0) })
1276
1292
  },
1277
1293
  `a${s.id}`
1278
1294
  ) : null;
@@ -65,7 +65,16 @@ export interface CartesianContextValue {
65
65
  /** The chart has keyboard focus (scrubbing with arrow keys). */
66
66
  focused: boolean;
67
67
  animate: boolean;
68
+ /**
69
+ * `mask` value for series marks while a Scrubber dims the future (right of the pointer);
70
+ * undefined otherwise. Lines and areas apply it, so the fade works on any background.
71
+ */
72
+ futureMask: string | undefined;
73
+ /** Scrubber turns future dimming on/off. */
74
+ setDimFuture(on: boolean): void;
68
75
  }
76
+ /** Hide labels the scrubber line would run through. */
77
+ export declare const nearScrub: (ctx: CartesianContextValue, x: number, within?: number) => boolean;
69
78
  export declare const CartesianContext: import("react").Context<CartesianContextValue | null>;
70
79
  export declare function useCartesianChart(): CartesianContextValue;
71
80
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnest/charts-react",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "React bindings for Funnest Charts: the trading widget plus SVG line, area and bar charts",
5
5
  "keywords": [
6
6
  "react",
@@ -46,9 +46,9 @@
46
46
  "NOTICE"
47
47
  ],
48
48
  "dependencies": {
49
- "@funnest/charts": "0.2.0",
50
- "@funnest/charts-ui": "0.2.0",
51
- "@funnest/charts-viz": "0.2.0"
49
+ "@funnest/charts": "0.2.1",
50
+ "@funnest/charts-viz": "0.2.1",
51
+ "@funnest/charts-ui": "0.2.1"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "react": ">=18",