@funnest/charts-react 0.4.0 → 0.5.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/fn-chart.d.ts +3 -1
- package/dist/fn-multi-chart.d.ts +20 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +230 -185
- package/package.json +4 -4
package/dist/fn-chart.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ChartEvents } from '@funnest/charts';
|
|
2
2
|
import { type TradingChart, type TradingChartOptions, type TradingChartSlots } from '@funnest/charts-ui';
|
|
3
3
|
import { type CSSProperties, type ReactNode } from 'react';
|
|
4
|
+
/** Slots of the surrounding <FnChart> or <FnMultiChart>, for <FnChartSlot>. */
|
|
5
|
+
export declare const SlotsContext: import("react").Context<TradingChartSlots | null>;
|
|
4
6
|
export interface FnChartProps extends TradingChartOptions {
|
|
5
7
|
className?: string;
|
|
6
8
|
/** The widget fills its container; give it a height (default 100%). */
|
|
@@ -21,7 +23,7 @@ export declare function useFnChart(): TradingChart | null;
|
|
|
21
23
|
/** Subscribes to a chart event of the surrounding `<FnChart>` for the component's lifetime. */
|
|
22
24
|
export declare function useChartEvent<K extends keyof ChartEvents>(event: K, handler: (payload: ChartEvents[K]) => void): void;
|
|
23
25
|
/**
|
|
24
|
-
* Renders React children into one of the widget's slots ('toolbar', 'belowToolbar', 'footer')
|
|
26
|
+
* Renders React children into one of the widget's slots ('toolbar', 'toolbarEnd', 'belowToolbar', 'bottom', 'footer')
|
|
25
27
|
* — e.g. a market stats bar or account info — styled with the widget's `--fnc-*` variables.
|
|
26
28
|
*/
|
|
27
29
|
export declare function FnChartSlot({ name, children }: {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type MultiChart, type MultiChartOptions } from '@funnest/charts-ui';
|
|
2
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
3
|
+
export interface FnMultiChartProps extends MultiChartOptions {
|
|
4
|
+
className?: string;
|
|
5
|
+
/** The widget fills its container; give it a height (default 100%). */
|
|
6
|
+
style?: CSSProperties;
|
|
7
|
+
/** Called once per created widget (also after it's recreated for a new datafeed). */
|
|
8
|
+
onReady?: (widget: MultiChart) => void;
|
|
9
|
+
/** `<FnChartSlot>` elements and other children that use `useFnMultiChart()`. */
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* React wrapper around `createMultiChart`: split layout of 1–8 linked charts with one toolbar.
|
|
14
|
+
* `panes`, `layout` and `sync` are read on mount (the user changes them from the toolbar; use
|
|
15
|
+
* the ref's `setLayout`/`setSync`/`load` to drive them). `locale`, `theme` and `colorScheme`
|
|
16
|
+
* stay in sync with props; a new `datafeed` recreates the widget.
|
|
17
|
+
*/
|
|
18
|
+
export declare const FnMultiChart: import("react").ForwardRefExoticComponent<FnMultiChartProps & import("react").RefAttributes<MultiChart | null>>;
|
|
19
|
+
/** The surrounding `<FnMultiChart>`'s widget (null until mounted). */
|
|
20
|
+
export declare function useFnMultiChart(): MultiChart | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,5 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export { FnChart, FnChartSlot, useChartEvent, useFnChart } from './fn-chart';
|
|
15
15
|
export type { FnChartProps } from './fn-chart';
|
|
16
|
-
export
|
|
16
|
+
export { FnMultiChart, useFnMultiChart } from './fn-multi-chart';
|
|
17
|
+
export type { FnMultiChartProps } from './fn-multi-chart';
|
|
18
|
+
export type { LayoutId, LayoutSync, MultiChart, MultiChartEvents, MultiChartOptions, MultiChartState, PaneSpec, TradingChart, TradingChartOptions, TradingChartSlots, } from '@funnest/charts-ui';
|
|
17
19
|
export * from './viz';
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { createPortal } from "react-dom";
|
|
13
13
|
import { jsx } from "react/jsx-runtime";
|
|
14
14
|
var WidgetContext = createContext(null);
|
|
15
|
+
var SlotsContext = createContext(null);
|
|
15
16
|
var FnChart = forwardRef(function FnChart2(props, ref) {
|
|
16
17
|
const { className, style, onReady, children, ...options } = props;
|
|
17
18
|
const hostRef = useRef(null);
|
|
@@ -51,7 +52,7 @@ var FnChart = forwardRef(function FnChart2(props, ref) {
|
|
|
51
52
|
useEffect(() => {
|
|
52
53
|
if (widget && theme) widget.setTheme(theme);
|
|
53
54
|
}, [widget, themeKey]);
|
|
54
|
-
return /* @__PURE__ */ jsx("div", { ref: hostRef, className, style: { width: "100%", height: "100%", ...style }, children: /* @__PURE__ */ jsx(WidgetContext.Provider, { value: widget, children }) });
|
|
55
|
+
return /* @__PURE__ */ jsx("div", { ref: hostRef, className, style: { width: "100%", height: "100%", ...style }, children: /* @__PURE__ */ jsx(WidgetContext.Provider, { value: widget, children: /* @__PURE__ */ jsx(SlotsContext.Provider, { value: widget?.slots ?? null, children }) }) });
|
|
55
56
|
});
|
|
56
57
|
function useFnChart() {
|
|
57
58
|
return useContext(WidgetContext);
|
|
@@ -63,8 +64,50 @@ function useChartEvent(event, handler) {
|
|
|
63
64
|
useEffect(() => widget?.chart.on(event, (payload) => handlerRef.current(payload)), [widget, event]);
|
|
64
65
|
}
|
|
65
66
|
function FnChartSlot({ name, children }) {
|
|
66
|
-
const
|
|
67
|
-
return
|
|
67
|
+
const slots = useContext(SlotsContext);
|
|
68
|
+
return slots ? createPortal(children, slots[name]) : null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/fn-multi-chart.tsx
|
|
72
|
+
import { createMultiChart } from "@funnest/charts-ui";
|
|
73
|
+
import { createContext as createContext2, forwardRef as forwardRef2, useContext as useContext2, useEffect as useEffect2, useImperativeHandle as useImperativeHandle2, useRef as useRef2, useState as useState2 } from "react";
|
|
74
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
75
|
+
var MultiContext = createContext2(null);
|
|
76
|
+
var FnMultiChart = forwardRef2(function FnMultiChart2(props, ref) {
|
|
77
|
+
const { className, style, onReady, children, ...options } = props;
|
|
78
|
+
const hostRef = useRef2(null);
|
|
79
|
+
const [widget, setWidget] = useState2(null);
|
|
80
|
+
const latest = useRef2(options);
|
|
81
|
+
latest.current = options;
|
|
82
|
+
const readyRef = useRef2(onReady);
|
|
83
|
+
readyRef.current = onReady;
|
|
84
|
+
useImperativeHandle2(ref, () => widget, [widget]);
|
|
85
|
+
useEffect2(() => {
|
|
86
|
+
const host = hostRef.current;
|
|
87
|
+
if (!host) return;
|
|
88
|
+
const instance = createMultiChart(host, latest.current);
|
|
89
|
+
setWidget(instance);
|
|
90
|
+
readyRef.current?.(instance);
|
|
91
|
+
return () => {
|
|
92
|
+
setWidget(null);
|
|
93
|
+
instance.destroy();
|
|
94
|
+
};
|
|
95
|
+
}, [options.datafeed]);
|
|
96
|
+
const { locale, theme, colorScheme } = options;
|
|
97
|
+
useEffect2(() => {
|
|
98
|
+
if (widget && locale) widget.setLocale(locale);
|
|
99
|
+
}, [widget, locale]);
|
|
100
|
+
useEffect2(() => {
|
|
101
|
+
if (widget && colorScheme) widget.setColorScheme(colorScheme);
|
|
102
|
+
}, [widget, colorScheme]);
|
|
103
|
+
const themeKey = JSON.stringify(theme ?? null);
|
|
104
|
+
useEffect2(() => {
|
|
105
|
+
if (widget && theme) widget.setTheme(theme);
|
|
106
|
+
}, [widget, themeKey]);
|
|
107
|
+
return /* @__PURE__ */ jsx2("div", { ref: hostRef, className, style: { width: "100%", height: "100%", ...style }, children: /* @__PURE__ */ jsx2(MultiContext.Provider, { value: widget, children: /* @__PURE__ */ jsx2(SlotsContext.Provider, { value: widget?.slots ?? null, children }) }) });
|
|
108
|
+
});
|
|
109
|
+
function useFnMultiChart() {
|
|
110
|
+
return useContext2(MultiContext);
|
|
68
111
|
}
|
|
69
112
|
|
|
70
113
|
// src/viz/cartesian-chart.tsx
|
|
@@ -84,19 +127,19 @@ import {
|
|
|
84
127
|
Children,
|
|
85
128
|
isValidElement,
|
|
86
129
|
useCallback,
|
|
87
|
-
useEffect as
|
|
130
|
+
useEffect as useEffect3,
|
|
88
131
|
useId,
|
|
89
132
|
useMemo,
|
|
90
|
-
useRef as
|
|
91
|
-
useState as
|
|
133
|
+
useRef as useRef3,
|
|
134
|
+
useState as useState3
|
|
92
135
|
} from "react";
|
|
93
136
|
|
|
94
137
|
// src/viz/context.ts
|
|
95
|
-
import { createContext as
|
|
138
|
+
import { createContext as createContext3, useContext as useContext3 } from "react";
|
|
96
139
|
var nearScrub = (ctx, x, within = 56) => ctx.scrubIndex !== null && Math.abs(ctx.x(ctx.scrubIndex) - x) < within;
|
|
97
|
-
var CartesianContext =
|
|
140
|
+
var CartesianContext = createContext3(null);
|
|
98
141
|
function useCartesianChart() {
|
|
99
|
-
const ctx =
|
|
142
|
+
const ctx = useContext3(CartesianContext);
|
|
100
143
|
if (!ctx) throw new Error("Funnest chart components must be rendered inside <CartesianChart> (or LineChart, AreaChart, BarChart).");
|
|
101
144
|
return ctx;
|
|
102
145
|
}
|
|
@@ -126,7 +169,7 @@ function seriesY(ctx, s, i) {
|
|
|
126
169
|
var scaleFor = (ctx, axisId) => (axisId ? ctx.yScales[axisId] : void 0) ?? ctx.y;
|
|
127
170
|
|
|
128
171
|
// src/viz/legend.tsx
|
|
129
|
-
import { Fragment, jsx as
|
|
172
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
130
173
|
function Legend({ items, onItemClick, onItemHover, shape = "circle", direction = "row", justify = "flex-start", className, style }) {
|
|
131
174
|
const swatch = (color) => {
|
|
132
175
|
switch (shape) {
|
|
@@ -142,7 +185,7 @@ function Legend({ items, onItemClick, onItemHover, shape = "circle", direction =
|
|
|
142
185
|
return { width: 8, height: 8, borderRadius: "50%", background: color };
|
|
143
186
|
}
|
|
144
187
|
};
|
|
145
|
-
return /* @__PURE__ */
|
|
188
|
+
return /* @__PURE__ */ jsx3(
|
|
146
189
|
"div",
|
|
147
190
|
{
|
|
148
191
|
className,
|
|
@@ -150,13 +193,13 @@ function Legend({ items, onItemClick, onItemHover, shape = "circle", direction =
|
|
|
150
193
|
style: { display: "flex", flexDirection: direction, flexWrap: direction === "row" ? "wrap" : "nowrap", justifyContent: justify, gap: direction === "row" ? "4px 14px" : 8, padding: "4px 0", fontSize: 12, color: VAR.text, fontFamily: VAR.font, ...style },
|
|
151
194
|
children: items.map((item) => {
|
|
152
195
|
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
153
|
-
/* @__PURE__ */
|
|
154
|
-
/* @__PURE__ */
|
|
155
|
-
item.value !== void 0 && /* @__PURE__ */
|
|
196
|
+
/* @__PURE__ */ jsx3("span", { "aria-hidden": "true", style: { flex: "none", ...swatch(item.color) } }),
|
|
197
|
+
/* @__PURE__ */ jsx3("span", { children: item.label }),
|
|
198
|
+
item.value !== void 0 && /* @__PURE__ */ jsx3("b", { style: { color: VAR.textStrong, fontWeight: 600, fontVariantNumeric: "tabular-nums" }, children: item.value })
|
|
156
199
|
] });
|
|
157
200
|
const itemStyle = { display: "inline-flex", alignItems: "center", gap: 6, opacity: item.inactive ? 0.4 : 1, transition: "opacity 0.15s" };
|
|
158
201
|
const hover = onItemHover ? { onPointerEnter: () => onItemHover(item.id), onPointerLeave: () => onItemHover(null) } : {};
|
|
159
|
-
return onItemClick ? /* @__PURE__ */
|
|
202
|
+
return onItemClick ? /* @__PURE__ */ jsx3(
|
|
160
203
|
"button",
|
|
161
204
|
{
|
|
162
205
|
type: "button",
|
|
@@ -167,14 +210,14 @@ function Legend({ items, onItemClick, onItemHover, shape = "circle", direction =
|
|
|
167
210
|
children: content
|
|
168
211
|
},
|
|
169
212
|
item.id
|
|
170
|
-
) : /* @__PURE__ */
|
|
213
|
+
) : /* @__PURE__ */ jsx3("span", { role: "listitem", style: itemStyle, ...hover, children: content }, item.id);
|
|
171
214
|
})
|
|
172
215
|
}
|
|
173
216
|
);
|
|
174
217
|
}
|
|
175
218
|
|
|
176
219
|
// src/viz/cartesian-chart.tsx
|
|
177
|
-
import { jsx as
|
|
220
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
178
221
|
var DEFAULT_HEIGHT = 240;
|
|
179
222
|
var DEFAULT_AXIS = "default";
|
|
180
223
|
function resolveInset(inset) {
|
|
@@ -195,9 +238,9 @@ function collectAxisSpace(children, acc) {
|
|
|
195
238
|
});
|
|
196
239
|
}
|
|
197
240
|
function useElementWidth(fixed) {
|
|
198
|
-
const ref =
|
|
199
|
-
const [width, setWidth] =
|
|
200
|
-
|
|
241
|
+
const ref = useRef3(null);
|
|
242
|
+
const [width, setWidth] = useState3(fixed ?? 0);
|
|
243
|
+
useEffect3(() => {
|
|
201
244
|
if (fixed !== void 0) {
|
|
202
245
|
setWidth(fixed);
|
|
203
246
|
return;
|
|
@@ -240,13 +283,13 @@ function CartesianChart({
|
|
|
240
283
|
}) {
|
|
241
284
|
const id = `fnv${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
242
285
|
const [wrapRef, width] = useElementWidth(fixedWidth);
|
|
243
|
-
const [scrubIndex, setScrubIndex] =
|
|
244
|
-
const [focused, setFocused] =
|
|
245
|
-
const [motion, setMotion] =
|
|
246
|
-
const [hidden, setHidden] =
|
|
247
|
-
const [emphasis, setEmphasis] =
|
|
248
|
-
const [dimFuture, setDimFuture] =
|
|
249
|
-
|
|
286
|
+
const [scrubIndex, setScrubIndex] = useState3(null);
|
|
287
|
+
const [focused, setFocused] = useState3(false);
|
|
288
|
+
const [motion, setMotion] = useState3(false);
|
|
289
|
+
const [hidden, setHidden] = useState3([]);
|
|
290
|
+
const [emphasis, setEmphasis] = useState3(null);
|
|
291
|
+
const [dimFuture, setDimFuture] = useState3(false);
|
|
292
|
+
useEffect3(() => setMotion(animate && !prefersReducedMotion()), [animate]);
|
|
250
293
|
const legendCfg = normalizeLegend(legend);
|
|
251
294
|
const axes = useMemo(() => {
|
|
252
295
|
const list = Array.isArray(yAxis) ? yAxis : [yAxis ?? {}];
|
|
@@ -332,7 +375,7 @@ function CartesianChart({
|
|
|
332
375
|
setDimFuture
|
|
333
376
|
};
|
|
334
377
|
}, [id, width, height, inset, axisSpace, all, visible, axes, firstAxis, xAxis, stacked, scrubIndex, focused, motion, dimFuture]);
|
|
335
|
-
const xsRef =
|
|
378
|
+
const xsRef = useRef3([]);
|
|
336
379
|
xsRef.current = useMemo(() => Array.from({ length: ctx.length }, (_, i) => ctx.x(i)), [ctx.length, ctx.x]);
|
|
337
380
|
const updateScrub = useCallback(
|
|
338
381
|
(next) => {
|
|
@@ -369,7 +412,7 @@ function CartesianChart({
|
|
|
369
412
|
};
|
|
370
413
|
const hasData = all.some((s) => s.data.some((v) => v !== null && Number.isFinite(v)));
|
|
371
414
|
const a11yText = (i) => getScrubberAccessibilityLabel ? getScrubberAccessibilityLabel(i) : [ctx.xLabel(i), ...ctx.series.map((s) => `${s.label ?? s.id} ${s.data[i] === null || s.data[i] === void 0 ? "\u2014" : formatNumber(s.data[i])}`)].join(", ");
|
|
372
|
-
const legendEl = legendCfg && /* @__PURE__ */
|
|
415
|
+
const legendEl = legendCfg && /* @__PURE__ */ jsx4(
|
|
373
416
|
Legend,
|
|
374
417
|
{
|
|
375
418
|
direction: legendCfg.position === "left" || legendCfg.position === "right" ? "column" : "row",
|
|
@@ -421,11 +464,11 @@ function CartesianChart({
|
|
|
421
464
|
children: [
|
|
422
465
|
ctx.futureMask && // Fade everything right of the scrubber to 35% (luminance mask: works on any background).
|
|
423
466
|
/* @__PURE__ */ jsxs2("mask", { id: `${id}-future`, maskUnits: "userSpaceOnUse", x: -width, y: -height, width: width * 3, height: height * 3, children: [
|
|
424
|
-
/* @__PURE__ */
|
|
425
|
-
/* @__PURE__ */
|
|
467
|
+
/* @__PURE__ */ jsx4("rect", { x: -width, y: -height, width: width + ctx.x(scrubIndex), height: height * 3, fill: "#fff" }),
|
|
468
|
+
/* @__PURE__ */ jsx4("rect", { x: ctx.x(scrubIndex), y: -height, width: width * 2, height: height * 3, fill: "#fff", fillOpacity: 0.35 })
|
|
426
469
|
] }),
|
|
427
|
-
width > 0 && hasData && /* @__PURE__ */
|
|
428
|
-
width > 0 && !hasData && /* @__PURE__ */
|
|
470
|
+
width > 0 && hasData && /* @__PURE__ */ jsx4(CartesianContext.Provider, { value: ctx, children }),
|
|
471
|
+
width > 0 && !hasData && /* @__PURE__ */ jsx4("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 })
|
|
429
472
|
]
|
|
430
473
|
}
|
|
431
474
|
);
|
|
@@ -442,11 +485,11 @@ function CartesianChart({
|
|
|
442
485
|
},
|
|
443
486
|
children: [
|
|
444
487
|
legendCfg?.position === "top" || legendCfg && !legendCfg.position ? legendEl : null,
|
|
445
|
-
side ? /* @__PURE__ */
|
|
488
|
+
side ? /* @__PURE__ */ jsx4("div", { style: { flex: 1, minWidth: 0 }, children: svg }) : svg,
|
|
446
489
|
side && legendEl,
|
|
447
490
|
legendCfg?.position === "bottom" && legendEl,
|
|
448
|
-
!hasData && emptyState !== void 0 && typeof emptyState !== "string" && /* @__PURE__ */
|
|
449
|
-
enableScrubbing && /* @__PURE__ */
|
|
491
|
+
!hasData && emptyState !== void 0 && typeof emptyState !== "string" && /* @__PURE__ */ jsx4("div", { style: { position: "absolute", inset: 0, display: "grid", placeItems: "center" }, children: emptyState }),
|
|
492
|
+
enableScrubbing && /* @__PURE__ */ jsx4("span", { "aria-live": "polite", style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: focused && scrubIndex !== null ? a11yText(scrubIndex) : "" })
|
|
450
493
|
]
|
|
451
494
|
}
|
|
452
495
|
);
|
|
@@ -454,7 +497,7 @@ function CartesianChart({
|
|
|
454
497
|
|
|
455
498
|
// src/viz/axes.tsx
|
|
456
499
|
import { logTicks, sampleIndices, ticks as niceTicks } from "@funnest/charts-viz";
|
|
457
|
-
import { jsx as
|
|
500
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
458
501
|
function XAxis({
|
|
459
502
|
position = "bottom",
|
|
460
503
|
height = 24,
|
|
@@ -472,15 +515,15 @@ function XAxis({
|
|
|
472
515
|
const lineY = position === "bottom" ? ctx.height - height : height;
|
|
473
516
|
const textY = position === "bottom" ? lineY + height / 2 + 4 : lineY - height / 2 + 4;
|
|
474
517
|
return /* @__PURE__ */ jsxs3("g", { "aria-hidden": "true", style: { fontFamily: VAR.font }, children: [
|
|
475
|
-
showGrid && indices.map((i) => /* @__PURE__ */
|
|
476
|
-
showLine && /* @__PURE__ */
|
|
518
|
+
showGrid && indices.map((i) => /* @__PURE__ */ jsx5("line", { x1: ctx.x(i), x2: ctx.x(i), y1: plot.top, y2: plot.bottom, shapeRendering: "crispEdges", style: { stroke: VAR.grid } }, `g${i}`)),
|
|
519
|
+
showLine && /* @__PURE__ */ jsx5("line", { x1: plot.left, x2: plot.right, y1: lineY, y2: lineY, shapeRendering: "crispEdges", style: { stroke: VAR.line } }),
|
|
477
520
|
indices.map((i) => {
|
|
478
521
|
const x = ctx.x(i);
|
|
479
522
|
const label = tickLabelFormatter ? tickLabelFormatter(ctx.xData?.[i], i) : ctx.xLabel(i);
|
|
480
523
|
const anchor = x - plot.left < 24 ? "start" : plot.right - x < 24 ? "end" : "middle";
|
|
481
524
|
return /* @__PURE__ */ jsxs3("g", { children: [
|
|
482
|
-
showTickMarks && /* @__PURE__ */
|
|
483
|
-
/* @__PURE__ */
|
|
525
|
+
showTickMarks && /* @__PURE__ */ jsx5("line", { x1: x, x2: x, y1: lineY, y2: position === "bottom" ? lineY + 4 : lineY - 4, style: { stroke: VAR.line } }),
|
|
526
|
+
/* @__PURE__ */ jsx5("text", { x, y: textY, textAnchor: anchor, fontSize: 11, style: { fill: VAR.text }, children: label })
|
|
484
527
|
] }, i);
|
|
485
528
|
})
|
|
486
529
|
] });
|
|
@@ -508,16 +551,16 @@ function YAxis({
|
|
|
508
551
|
const textX = right ? ctx.width - width + 8 : width - 8;
|
|
509
552
|
const lineX = right ? ctx.width - width : width;
|
|
510
553
|
return /* @__PURE__ */ jsxs3("g", { "aria-hidden": "true", style: { fontFamily: VAR.font }, children: [
|
|
511
|
-
showGrid && values.map((v) => /* @__PURE__ */
|
|
512
|
-
showLine && /* @__PURE__ */
|
|
513
|
-
values.map((v) => /* @__PURE__ */
|
|
554
|
+
showGrid && values.map((v) => /* @__PURE__ */ jsx5("line", { x1: plot.left, x2: plot.right, y1: y(v), y2: y(v), shapeRendering: "crispEdges", style: { stroke: VAR.grid } }, `g${v}`)),
|
|
555
|
+
showLine && /* @__PURE__ */ jsx5("line", { x1: lineX, x2: lineX, y1: plot.top, y2: plot.bottom, shapeRendering: "crispEdges", style: { stroke: VAR.line } }),
|
|
556
|
+
values.map((v) => /* @__PURE__ */ jsx5("text", { x: textX, y: y(v) + 4, textAnchor: right ? "start" : "end", fontSize: 11, style: { fill: VAR.text }, children: tickLabelFormatter(v) }, v))
|
|
514
557
|
] });
|
|
515
558
|
}
|
|
516
559
|
YAxis.axisSpace = (p) => ({ side: p.position ?? "right", size: p.width ?? 48 });
|
|
517
560
|
|
|
518
561
|
// src/viz/line.tsx
|
|
519
562
|
import { areaPath, linePath, segments } from "@funnest/charts-viz";
|
|
520
|
-
import { Fragment as Fragment2, jsx as
|
|
563
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
521
564
|
function seriesRuns(ctx, s, connectNulls = false) {
|
|
522
565
|
const st = ctx.stacked?.get(s.id);
|
|
523
566
|
const y = s.scale;
|
|
@@ -543,8 +586,8 @@ function SplitClips({ id, y, ctx }) {
|
|
|
543
586
|
const { left, right, top, bottom } = ctx.plot;
|
|
544
587
|
const pad = 20;
|
|
545
588
|
return /* @__PURE__ */ jsxs4("defs", { children: [
|
|
546
|
-
/* @__PURE__ */
|
|
547
|
-
/* @__PURE__ */
|
|
589
|
+
/* @__PURE__ */ jsx6("clipPath", { id: `${id}-above`, children: /* @__PURE__ */ jsx6("rect", { x: left - pad, y: top - pad, width: right - left + pad * 2, height: Math.max(0, y - top + pad) }) }),
|
|
590
|
+
/* @__PURE__ */ jsx6("clipPath", { id: `${id}-below`, children: /* @__PURE__ */ jsx6("rect", { x: left - pad, y, width: right - left + pad * 2, height: Math.max(0, bottom - y + pad) }) })
|
|
548
591
|
] });
|
|
549
592
|
}
|
|
550
593
|
function Area({ seriesId, curve = "monotone", fill = "gradient", opacity, connectNulls, baseline, splitColors, gradientStops }) {
|
|
@@ -564,17 +607,17 @@ function Area({ seriesId, curve = "monotone", fill = "gradient", opacity, connec
|
|
|
564
607
|
];
|
|
565
608
|
return /* @__PURE__ */ jsxs4("g", { clipPath: clip ? `url(#${clip})` : void 0, opacity: dim, children: [
|
|
566
609
|
/* @__PURE__ */ jsxs4("defs", { children: [
|
|
567
|
-
fill === "gradient" && /* @__PURE__ */
|
|
568
|
-
fill === "dotted" && /* @__PURE__ */
|
|
610
|
+
fill === "gradient" && /* @__PURE__ */ jsx6("linearGradient", { id: fillId, x1: "0", y1: from, x2: "0", y2: base, gradientUnits: "userSpaceOnUse", children: stops.map((st, i) => /* @__PURE__ */ jsx6("stop", { offset: st.offset, stopOpacity: st.opacity, style: { stopColor: st.color ?? color } }, i)) }),
|
|
611
|
+
fill === "dotted" && /* @__PURE__ */ jsx6("pattern", { id: fillId, width: "6", height: "6", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx6("circle", { cx: "1.5", cy: "1.5", r: "1.1", style: { fill: color } }) })
|
|
569
612
|
] }),
|
|
570
|
-
runs.map((run, k) => /* @__PURE__ */
|
|
613
|
+
runs.map((run, k) => /* @__PURE__ */ jsx6(
|
|
571
614
|
"path",
|
|
572
615
|
{
|
|
573
616
|
d: areaPath(run.upper, run.lower ?? base, curve),
|
|
574
617
|
fillOpacity: fill === "gradient" ? void 0 : alpha,
|
|
575
618
|
stroke: "none",
|
|
576
619
|
style: { fill: fill === "solid" ? color : `url(#${fillId})` },
|
|
577
|
-
children: ctx.animate && /* @__PURE__ */
|
|
620
|
+
children: ctx.animate && /* @__PURE__ */ jsx6("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.6s", fill: "freeze" })
|
|
578
621
|
},
|
|
579
622
|
k
|
|
580
623
|
))
|
|
@@ -582,12 +625,12 @@ function Area({ seriesId, curve = "monotone", fill = "gradient", opacity, connec
|
|
|
582
625
|
};
|
|
583
626
|
if (splitColors && baseline !== void 0 && !run0IsStacked(ctx, s)) {
|
|
584
627
|
return /* @__PURE__ */ jsxs4("g", { mask: ctx.futureMask, children: [
|
|
585
|
-
/* @__PURE__ */
|
|
628
|
+
/* @__PURE__ */ jsx6(SplitClips, { id: gid, y: base, ctx }),
|
|
586
629
|
paint("above", splitColors.above, ctx.plot.top, `${gid}-above`),
|
|
587
630
|
paint("below", splitColors.below, ctx.plot.bottom, `${gid}-below`)
|
|
588
631
|
] });
|
|
589
632
|
}
|
|
590
|
-
return /* @__PURE__ */
|
|
633
|
+
return /* @__PURE__ */ jsx6("g", { mask: ctx.futureMask, children: paint("fill", s.color, ctx.plot.top) });
|
|
591
634
|
}
|
|
592
635
|
var run0IsStacked = (ctx, s) => !!ctx.stacked?.get(s.id);
|
|
593
636
|
function Line({
|
|
@@ -615,7 +658,7 @@ function Line({
|
|
|
615
658
|
const gid = `${ctx.id}-line-${s.index}`;
|
|
616
659
|
const baseY = baselineY(ctx, s.scale, baseline);
|
|
617
660
|
const strokeOpacity = (opacity ?? 1) * (s.dimmed ? DIM_OPACITY : 1);
|
|
618
|
-
const strokes = (color, clip) => /* @__PURE__ */
|
|
661
|
+
const strokes = (color, clip) => /* @__PURE__ */ jsx6("g", { clipPath: clip ? `url(#${clip})` : void 0, mask: ctx.futureMask, children: runs.map((run, k) => /* @__PURE__ */ jsx6(
|
|
619
662
|
"path",
|
|
620
663
|
{
|
|
621
664
|
d: linePath(run.upper, curve),
|
|
@@ -627,7 +670,7 @@ function Line({
|
|
|
627
670
|
strokeOpacity,
|
|
628
671
|
pathLength: drawIn ? 1 : void 0,
|
|
629
672
|
style: { stroke: color },
|
|
630
|
-
children: drawIn && /* @__PURE__ */
|
|
673
|
+
children: drawIn && /* @__PURE__ */ jsx6("animate", { attributeName: "stroke-dashoffset", from: "1", to: "0", dur: "0.8s", fill: "freeze", calcMode: "spline", keySplines: "0.3 0 0.2 1", keyTimes: "0;1" })
|
|
631
674
|
},
|
|
632
675
|
k
|
|
633
676
|
)) });
|
|
@@ -645,8 +688,8 @@ function Line({
|
|
|
645
688
|
const r = p.radius ?? Math.max(3, strokeWidth + 1);
|
|
646
689
|
dots.push(
|
|
647
690
|
/* @__PURE__ */ jsxs4("g", { opacity: s.dimmed ? DIM_OPACITY : 1, children: [
|
|
648
|
-
/* @__PURE__ */
|
|
649
|
-
p.label && /* @__PURE__ */
|
|
691
|
+
/* @__PURE__ */ jsx6("circle", { cx: x, cy: y, r, strokeWidth: 2, style: { fill: color, stroke: VAR.bg } }),
|
|
692
|
+
p.label && /* @__PURE__ */ jsx6(
|
|
650
693
|
"text",
|
|
651
694
|
{
|
|
652
695
|
opacity: nearScrub(ctx, x) ? 0 : 1,
|
|
@@ -667,7 +710,7 @@ function Line({
|
|
|
667
710
|
});
|
|
668
711
|
}
|
|
669
712
|
return /* @__PURE__ */ jsxs4("g", { children: [
|
|
670
|
-
area !== "none" && /* @__PURE__ */
|
|
713
|
+
area !== "none" && /* @__PURE__ */ jsx6(
|
|
671
714
|
Area,
|
|
672
715
|
{
|
|
673
716
|
seriesId,
|
|
@@ -680,7 +723,7 @@ function Line({
|
|
|
680
723
|
}
|
|
681
724
|
),
|
|
682
725
|
split ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
683
|
-
/* @__PURE__ */
|
|
726
|
+
/* @__PURE__ */ jsx6(SplitClips, { id: gid, y: baseY, ctx }),
|
|
684
727
|
strokes(splitColors.above, `${gid}-above`),
|
|
685
728
|
strokes(splitColors.below, `${gid}-below`)
|
|
686
729
|
] }) : strokes(s.color),
|
|
@@ -690,7 +733,7 @@ function Line({
|
|
|
690
733
|
|
|
691
734
|
// src/viz/bar.tsx
|
|
692
735
|
import { barPath } from "@funnest/charts-viz";
|
|
693
|
-
import { jsx as
|
|
736
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
694
737
|
function BarPlot({
|
|
695
738
|
seriesIds,
|
|
696
739
|
radius = 3,
|
|
@@ -712,7 +755,7 @@ function BarPlot({
|
|
|
712
755
|
return scale(Math.min(Math.max(0, Math.min(d0, d1)), Math.max(d0, d1)));
|
|
713
756
|
};
|
|
714
757
|
const dim = highlightScrubbed && ctx.scrubIndex !== null;
|
|
715
|
-
return /* @__PURE__ */
|
|
758
|
+
return /* @__PURE__ */ jsx7("g", { children: list.map((s, k) => {
|
|
716
759
|
const st = ctx.stacked?.get(s.id);
|
|
717
760
|
const y = s.scale;
|
|
718
761
|
const zero = zeroOf(y);
|
|
@@ -721,7 +764,7 @@ function BarPlot({
|
|
|
721
764
|
const ov = o.data[i];
|
|
722
765
|
return ov !== null && ov !== void 0 && ov !== 0 && Math.sign(ov) === Math.sign(v);
|
|
723
766
|
});
|
|
724
|
-
return /* @__PURE__ */
|
|
767
|
+
return /* @__PURE__ */ jsx7("g", { opacity: s.dimmed ? DIM_OPACITY : 1, children: s.data.map((v, i) => {
|
|
725
768
|
if (v === null || v === void 0 || !Number.isFinite(v) || v === 0) return null;
|
|
726
769
|
const x = band(i) + offset;
|
|
727
770
|
const low = s.low?.[i];
|
|
@@ -737,13 +780,13 @@ function BarPlot({
|
|
|
737
780
|
if (minSize > 0 && Math.abs(y1 - y0) < minSize) y1 = y0 + dir * minSize;
|
|
738
781
|
const color = getBarColor?.(v, i, s.id) ?? s.color;
|
|
739
782
|
const r = top ? radius : st && stackGap > 0 ? Math.min(radius, 2) : 0;
|
|
740
|
-
return /* @__PURE__ */
|
|
783
|
+
return /* @__PURE__ */ jsx7(
|
|
741
784
|
"path",
|
|
742
785
|
{
|
|
743
786
|
d: barPath(x, barW, y0, y1, s.low ? radius : r, roundBaseline && atBase || !!s.low),
|
|
744
787
|
fillOpacity: dim && ctx.scrubIndex !== i ? 0.45 : 1,
|
|
745
788
|
style: { fill: color },
|
|
746
|
-
children: ctx.animate && /* @__PURE__ */
|
|
789
|
+
children: ctx.animate && /* @__PURE__ */ jsx7("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.5s", fill: "freeze" })
|
|
747
790
|
},
|
|
748
791
|
i
|
|
749
792
|
);
|
|
@@ -753,8 +796,8 @@ function BarPlot({
|
|
|
753
796
|
|
|
754
797
|
// src/viz/horizontal-bar.tsx
|
|
755
798
|
import { bandScale as bandScale2, barPath as barPath2, extent as extent2, linearScale as linearScale2, seriesColor as seriesColor2, stack as stack2, stackedExtent as stackedExtent2, ticks as niceTicks2 } from "@funnest/charts-viz";
|
|
756
|
-
import { useEffect as
|
|
757
|
-
import { Fragment as Fragment3, jsx as
|
|
799
|
+
import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
|
|
800
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
758
801
|
var textWidth = (t, size = 12) => [...t].reduce((w, ch) => w + (ch.charCodeAt(0) > 11904 ? size : size * 0.6), 0);
|
|
759
802
|
function HorizontalBarChart({
|
|
760
803
|
series,
|
|
@@ -777,11 +820,11 @@ function HorizontalBarChart({
|
|
|
777
820
|
style,
|
|
778
821
|
"aria-label": ariaLabel
|
|
779
822
|
}) {
|
|
780
|
-
const wrapRef =
|
|
781
|
-
const [measured, setMeasured] =
|
|
782
|
-
const [hover, setHover] =
|
|
783
|
-
const [focused, setFocused] =
|
|
784
|
-
|
|
823
|
+
const wrapRef = useRef4(null);
|
|
824
|
+
const [measured, setMeasured] = useState4(fixedWidth ?? 0);
|
|
825
|
+
const [hover, setHover] = useState4(null);
|
|
826
|
+
const [focused, setFocused] = useState4(false);
|
|
827
|
+
useEffect4(() => {
|
|
785
828
|
if (fixedWidth !== void 0) return setMeasured(fixedWidth);
|
|
786
829
|
const el = wrapRef.current;
|
|
787
830
|
if (!el || typeof ResizeObserver === "undefined") return;
|
|
@@ -845,14 +888,14 @@ function HorizontalBarChart({
|
|
|
845
888
|
const rowY = hover === null ? 0 : band.center(hover);
|
|
846
889
|
const tipY = Math.min(Math.max(top, rowY - tipH / 2), bottom - tipH);
|
|
847
890
|
return /* @__PURE__ */ jsxs5("div", { ref: wrapRef, className, style: { position: "relative", width: fixedWidth ?? "100%", fontFamily: VAR.font, ...style }, children: [
|
|
848
|
-
legend && /* @__PURE__ */
|
|
891
|
+
legend && /* @__PURE__ */ jsx8(
|
|
849
892
|
Legend,
|
|
850
893
|
{
|
|
851
894
|
...typeof legend === "object" && legend.shape ? { shape: legend.shape } : {},
|
|
852
895
|
items: resolved.map((s) => ({ id: s.id, label: s.label ?? s.id, color: s.color }))
|
|
853
896
|
}
|
|
854
897
|
),
|
|
855
|
-
/* @__PURE__ */
|
|
898
|
+
/* @__PURE__ */ jsx8(
|
|
856
899
|
"svg",
|
|
857
900
|
{
|
|
858
901
|
width: w || "100%",
|
|
@@ -870,12 +913,12 @@ function HorizontalBarChart({
|
|
|
870
913
|
setHover(null);
|
|
871
914
|
} : void 0,
|
|
872
915
|
children: w > 0 && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
873
|
-
tickValues.map((t) => /* @__PURE__ */
|
|
874
|
-
min < 0 && /* @__PURE__ */
|
|
875
|
-
hover !== null && /* @__PURE__ */
|
|
916
|
+
tickValues.map((t) => /* @__PURE__ */ jsx8("line", { x1: xs(t), x2: xs(t), y1: top, y2: bottom, shapeRendering: "crispEdges", style: { stroke: VAR.grid } }, `g${t}`)),
|
|
917
|
+
min < 0 && /* @__PURE__ */ jsx8("line", { x1: zero, x2: zero, y1: top, y2: bottom, shapeRendering: "crispEdges", style: { stroke: VAR.line } }),
|
|
918
|
+
hover !== null && /* @__PURE__ */ jsx8("rect", { x: left - 4, y: band.center(hover) - band.step / 2, width: right - left + 8, height: band.step, rx: 6, style: { fill: VAR.grid } }),
|
|
876
919
|
resolved.map((s, k) => {
|
|
877
920
|
const st = stackedMap?.get(s.id);
|
|
878
|
-
return /* @__PURE__ */
|
|
921
|
+
return /* @__PURE__ */ jsx8("g", { children: s.high.map((v, i) => {
|
|
879
922
|
if (v === null || v === void 0 || !Number.isFinite(v) || v === 0) return null;
|
|
880
923
|
const yTop = band(i) + (stacked ? 0 : k * (barH + groupGap));
|
|
881
924
|
const lo = s.low?.[i];
|
|
@@ -889,7 +932,7 @@ function HorizontalBarChart({
|
|
|
889
932
|
}
|
|
890
933
|
const color = getBarColor?.(v, i, s.id) ?? s.color;
|
|
891
934
|
const d = barPath2(yTop, barH, x0, x1, isTop || s.low ? radius : 0, !!s.low);
|
|
892
|
-
return /* @__PURE__ */
|
|
935
|
+
return /* @__PURE__ */ jsx8(
|
|
893
936
|
"path",
|
|
894
937
|
{
|
|
895
938
|
d,
|
|
@@ -901,27 +944,27 @@ function HorizontalBarChart({
|
|
|
901
944
|
);
|
|
902
945
|
}) }, s.id);
|
|
903
946
|
}),
|
|
904
|
-
showCategoryAxis && (categories ?? []).map((c, i) => /* @__PURE__ */
|
|
905
|
-
showValueAxis && tickValues.map((t) => /* @__PURE__ */
|
|
947
|
+
showCategoryAxis && (categories ?? []).map((c, i) => /* @__PURE__ */ jsx8("text", { x: labelW, y: band.center(i), textAnchor: "end", dominantBaseline: "central", fontSize: 12, style: { fill: hover === i ? VAR.textStrong : VAR.text, fontWeight: hover === i ? 700 : 500 }, children: c }, c + i)),
|
|
948
|
+
showValueAxis && tickValues.map((t) => /* @__PURE__ */ jsx8("text", { x: xs(t), y: h - 6, textAnchor: "middle", fontSize: 11, style: { fill: VAR.text }, children: valueFormatter(t) }, t)),
|
|
906
949
|
tipRows.length > 0 && /* @__PURE__ */ jsxs5("g", { transform: `translate(${Math.min(right - tipW, Math.max(left, xs(max) - tipW))},${tipY})`, pointerEvents: "none", children: [
|
|
907
|
-
/* @__PURE__ */
|
|
950
|
+
/* @__PURE__ */ jsx8("rect", { width: tipW, height: tipH, rx: 8, strokeWidth: 1, style: { fill: VAR.bg, stroke: VAR.grid } }),
|
|
908
951
|
tipRows.map((r, k) => /* @__PURE__ */ jsxs5("g", { transform: `translate(10,${7 + 9 + k * 18})`, children: [
|
|
909
|
-
/* @__PURE__ */
|
|
910
|
-
/* @__PURE__ */
|
|
911
|
-
/* @__PURE__ */
|
|
952
|
+
/* @__PURE__ */ jsx8("circle", { cx: 3, cy: 0, r: 3, style: { fill: r.color } }),
|
|
953
|
+
/* @__PURE__ */ jsx8("text", { x: 12, dominantBaseline: "central", fontSize: 12, style: { fill: VAR.text }, children: r.s.label ?? r.s.id }),
|
|
954
|
+
/* @__PURE__ */ jsx8("text", { x: tipW - 20, textAnchor: "end", dominantBaseline: "central", fontSize: 12, fontWeight: 700, style: { fill: VAR.textStrong, fontVariantNumeric: "tabular-nums" }, children: r.text })
|
|
912
955
|
] }, r.s.id))
|
|
913
956
|
] })
|
|
914
957
|
] })
|
|
915
958
|
}
|
|
916
959
|
),
|
|
917
|
-
enableScrubbing && /* @__PURE__ */
|
|
960
|
+
enableScrubbing && /* @__PURE__ */ jsx8("span", { "aria-live": "polite", style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: focused && hover !== null ? [categories?.[hover] ?? String(hover), ...tipRows.map((r) => `${r.s.label ?? r.s.id} ${r.text}`)].join(", ") : "" })
|
|
918
961
|
] });
|
|
919
962
|
}
|
|
920
963
|
|
|
921
964
|
// src/viz/donut.tsx
|
|
922
965
|
import { arcPath, arcs, seriesColor as seriesColor3 } from "@funnest/charts-viz";
|
|
923
|
-
import { useId as useId2, useState as
|
|
924
|
-
import { jsx as
|
|
966
|
+
import { useId as useId2, useState as useState5 } from "react";
|
|
967
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
925
968
|
var pctText = (f) => `${formatNumber(Math.round(f * 1e3) / 10)}%`;
|
|
926
969
|
var isCenter = (v) => !!v && typeof v === "object" && "value" in v && typeof v.value === "string";
|
|
927
970
|
function DonutChart({
|
|
@@ -945,8 +988,8 @@ function DonutChart({
|
|
|
945
988
|
"aria-label": ariaLabel
|
|
946
989
|
}) {
|
|
947
990
|
const uid = useId2();
|
|
948
|
-
const [own, setOwn] =
|
|
949
|
-
const [focused, setFocused] =
|
|
991
|
+
const [own, setOwn] = useState5(null);
|
|
992
|
+
const [focused, setFocused] = useState5(false);
|
|
950
993
|
const active = activeId !== void 0 ? activeId : own;
|
|
951
994
|
const setActive = (id) => {
|
|
952
995
|
if (activeId === void 0) setOwn(id);
|
|
@@ -999,13 +1042,13 @@ function DonutChart({
|
|
|
999
1042
|
},
|
|
1000
1043
|
onPointerLeave: () => setActive(null),
|
|
1001
1044
|
children: [
|
|
1002
|
-
total <= 0 && /* @__PURE__ */
|
|
1045
|
+
total <= 0 && /* @__PURE__ */ jsx9("circle", { cx: c, cy: c, r: (r + ir) / 2, fill: "none", strokeWidth: Math.max(1, r - ir), style: { stroke: VAR.grid } }),
|
|
1003
1046
|
visible.map((i) => {
|
|
1004
1047
|
const on = i.id === active;
|
|
1005
1048
|
const mid = (i.seg.startAngle + i.seg.endAngle) / 2;
|
|
1006
1049
|
const dx = on ? Math.sin(mid) * pop : 0;
|
|
1007
1050
|
const dy = on ? -Math.cos(mid) * pop : 0;
|
|
1008
|
-
return /* @__PURE__ */
|
|
1051
|
+
return /* @__PURE__ */ jsx9(
|
|
1009
1052
|
"path",
|
|
1010
1053
|
{
|
|
1011
1054
|
d: arcPath(c, c, ir, r, i.seg.startAngle, i.seg.endAngle, cornerRadius),
|
|
@@ -1013,13 +1056,13 @@ function DonutChart({
|
|
|
1013
1056
|
opacity: active && !on ? 0.35 : 1,
|
|
1014
1057
|
onPointerEnter: () => setActive(i.id),
|
|
1015
1058
|
style: { fill: i.color, transition: "transform 0.15s ease, opacity 0.15s", cursor: "default" },
|
|
1016
|
-
children: /* @__PURE__ */
|
|
1059
|
+
children: /* @__PURE__ */ jsx9("title", { children: describe(i) })
|
|
1017
1060
|
},
|
|
1018
1061
|
i.id
|
|
1019
1062
|
);
|
|
1020
1063
|
}),
|
|
1021
1064
|
ir > 24 && centerContent !== null && (isCenter(centerContent) ? /* @__PURE__ */ jsxs6("g", { pointerEvents: "none", style: { fontFamily: VAR.font }, children: [
|
|
1022
|
-
/* @__PURE__ */
|
|
1065
|
+
/* @__PURE__ */ jsx9(
|
|
1023
1066
|
"text",
|
|
1024
1067
|
{
|
|
1025
1068
|
x: c,
|
|
@@ -1032,8 +1075,8 @@ function DonutChart({
|
|
|
1032
1075
|
children: centerContent.value
|
|
1033
1076
|
}
|
|
1034
1077
|
),
|
|
1035
|
-
centerContent.label && /* @__PURE__ */
|
|
1036
|
-
] }) : /* @__PURE__ */
|
|
1078
|
+
centerContent.label && /* @__PURE__ */ jsx9("text", { x: c, y: c + 13, textAnchor: "middle", dominantBaseline: "central", fontSize: 12, style: { fill: VAR.text }, children: centerContent.label })
|
|
1079
|
+
] }) : /* @__PURE__ */ jsx9("foreignObject", { x: c - ir * 0.72, y: c - ir * 0.72, width: ir * 1.44, height: ir * 1.44, pointerEvents: "none", children: /* @__PURE__ */ jsx9("div", { style: { width: "100%", height: "100%", display: "grid", placeItems: "center", textAlign: "center" }, children: centerContent }) }))
|
|
1037
1080
|
]
|
|
1038
1081
|
}
|
|
1039
1082
|
);
|
|
@@ -1052,7 +1095,7 @@ function DonutChart({
|
|
|
1052
1095
|
},
|
|
1053
1096
|
children: [
|
|
1054
1097
|
svg,
|
|
1055
|
-
showLegend && /* @__PURE__ */
|
|
1098
|
+
showLegend && /* @__PURE__ */ jsx9(
|
|
1056
1099
|
Legend,
|
|
1057
1100
|
{
|
|
1058
1101
|
direction: side ? "column" : "row",
|
|
@@ -1062,7 +1105,7 @@ function DonutChart({
|
|
|
1062
1105
|
onItemHover: setActive
|
|
1063
1106
|
}
|
|
1064
1107
|
),
|
|
1065
|
-
/* @__PURE__ */
|
|
1108
|
+
/* @__PURE__ */ jsx9("span", { id: `${uid}-live`, "aria-live": "polite", style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: focused && hit ? describe(hit) : "" })
|
|
1066
1109
|
]
|
|
1067
1110
|
}
|
|
1068
1111
|
);
|
|
@@ -1070,8 +1113,8 @@ function DonutChart({
|
|
|
1070
1113
|
|
|
1071
1114
|
// src/viz/treemap.tsx
|
|
1072
1115
|
import { seriesColor as seriesColor4, treemap } from "@funnest/charts-viz";
|
|
1073
|
-
import { useEffect as
|
|
1074
|
-
import { jsx as
|
|
1116
|
+
import { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef5, useState as useState6 } from "react";
|
|
1117
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1075
1118
|
var hex = (c) => {
|
|
1076
1119
|
const m = /^#([0-9a-f]{6})$/i.exec(c);
|
|
1077
1120
|
if (!m) return null;
|
|
@@ -1107,11 +1150,11 @@ function TreemapChart({
|
|
|
1107
1150
|
style,
|
|
1108
1151
|
"aria-label": ariaLabel
|
|
1109
1152
|
}) {
|
|
1110
|
-
const wrapRef =
|
|
1111
|
-
const [measured, setMeasured] =
|
|
1112
|
-
const [active, setActive] =
|
|
1113
|
-
const [focused, setFocused] =
|
|
1114
|
-
|
|
1153
|
+
const wrapRef = useRef5(null);
|
|
1154
|
+
const [measured, setMeasured] = useState6(fixedWidth ?? 0);
|
|
1155
|
+
const [active, setActive] = useState6(null);
|
|
1156
|
+
const [focused, setFocused] = useState6(false);
|
|
1157
|
+
useEffect5(() => {
|
|
1115
1158
|
if (fixedWidth !== void 0) return setMeasured(fixedWidth);
|
|
1116
1159
|
const el = wrapRef.current;
|
|
1117
1160
|
if (!el || typeof ResizeObserver === "undefined") return;
|
|
@@ -1191,7 +1234,7 @@ function TreemapChart({
|
|
|
1191
1234
|
const fitsSub = !!sub && fitsLabel && r.height >= fs * 2 + 14 && r.width >= textWidth2(sub, fs * 0.8) + 10;
|
|
1192
1235
|
const cy = r.y + r.height / 2;
|
|
1193
1236
|
return /* @__PURE__ */ jsxs7("g", { onPointerEnter: () => setActive(r.id), onClick: onItemClick ? () => onItemClick(d) : void 0, style: { cursor: onItemClick ? "pointer" : "default" }, children: [
|
|
1194
|
-
/* @__PURE__ */
|
|
1237
|
+
/* @__PURE__ */ jsx10(
|
|
1195
1238
|
"rect",
|
|
1196
1239
|
{
|
|
1197
1240
|
x: r.x,
|
|
@@ -1203,7 +1246,7 @@ function TreemapChart({
|
|
|
1203
1246
|
opacity: active && !on ? 0.55 : 1
|
|
1204
1247
|
}
|
|
1205
1248
|
),
|
|
1206
|
-
on && /* @__PURE__ */
|
|
1249
|
+
on && /* @__PURE__ */ jsx10(
|
|
1207
1250
|
"rect",
|
|
1208
1251
|
{
|
|
1209
1252
|
x: r.x + 1,
|
|
@@ -1216,7 +1259,7 @@ function TreemapChart({
|
|
|
1216
1259
|
style: { stroke: "#fff" }
|
|
1217
1260
|
}
|
|
1218
1261
|
),
|
|
1219
|
-
fitsLabel && /* @__PURE__ */
|
|
1262
|
+
fitsLabel && /* @__PURE__ */ jsx10(
|
|
1220
1263
|
"text",
|
|
1221
1264
|
{
|
|
1222
1265
|
x: r.x + r.width / 2,
|
|
@@ -1230,7 +1273,7 @@ function TreemapChart({
|
|
|
1230
1273
|
children: d.label
|
|
1231
1274
|
}
|
|
1232
1275
|
),
|
|
1233
|
-
fitsSub && /* @__PURE__ */
|
|
1276
|
+
fitsSub && /* @__PURE__ */ jsx10(
|
|
1234
1277
|
"text",
|
|
1235
1278
|
{
|
|
1236
1279
|
x: r.x + r.width / 2,
|
|
@@ -1247,10 +1290,10 @@ function TreemapChart({
|
|
|
1247
1290
|
] }, r.id);
|
|
1248
1291
|
}),
|
|
1249
1292
|
tip && /* @__PURE__ */ jsxs7("g", { transform: `translate(${tip.x},${tip.y})`, pointerEvents: "none", children: [
|
|
1250
|
-
/* @__PURE__ */
|
|
1293
|
+
/* @__PURE__ */ jsx10("rect", { width: tip.w, height: tip.h, rx: 8, strokeWidth: 1, style: { fill: VAR.bg, stroke: VAR.grid } }),
|
|
1251
1294
|
tip.rows.map(([a, b], k) => /* @__PURE__ */ jsxs7("g", { transform: `translate(10,${7 + 9 + k * 18})`, children: [
|
|
1252
|
-
/* @__PURE__ */
|
|
1253
|
-
/* @__PURE__ */
|
|
1295
|
+
/* @__PURE__ */ jsx10("text", { dominantBaseline: "central", fontSize: 12, style: { fill: VAR.text }, children: a }),
|
|
1296
|
+
/* @__PURE__ */ jsx10(
|
|
1254
1297
|
"text",
|
|
1255
1298
|
{
|
|
1256
1299
|
x: tip.w - 20,
|
|
@@ -1267,12 +1310,12 @@ function TreemapChart({
|
|
|
1267
1310
|
]
|
|
1268
1311
|
}
|
|
1269
1312
|
),
|
|
1270
|
-
/* @__PURE__ */
|
|
1313
|
+
/* @__PURE__ */ jsx10("span", { "aria-live": "polite", style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: focused && hitItem ? describe(hitItem) : "" })
|
|
1271
1314
|
] });
|
|
1272
1315
|
}
|
|
1273
1316
|
|
|
1274
1317
|
// src/viz/candles.tsx
|
|
1275
|
-
import { jsx as
|
|
1318
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1276
1319
|
var KO = { open: "\uC2DC\uAC00", high: "\uACE0\uAC00", low: "\uC800\uAC00", close: "\uC885\uAC00", volume: "\uAC70\uB798\uB7C9" };
|
|
1277
1320
|
var fade = (color, a) => {
|
|
1278
1321
|
const m = /^#([0-9a-f]{6})$/i.exec(color);
|
|
@@ -1316,12 +1359,12 @@ function CandlePlot({
|
|
|
1316
1359
|
const x = band.center(i);
|
|
1317
1360
|
const right = x + 12 + tw <= ctx.plot.right;
|
|
1318
1361
|
tip = /* @__PURE__ */ jsxs8("g", { pointerEvents: "none", children: [
|
|
1319
|
-
/* @__PURE__ */
|
|
1362
|
+
/* @__PURE__ */ jsx11("line", { x1: x, x2: x, y1: ctx.plot.top, y2: ctx.plot.bottom, strokeWidth: 1, shapeRendering: "crispEdges", style: { stroke: VAR.line } }),
|
|
1320
1363
|
/* @__PURE__ */ jsxs8("g", { transform: `translate(${right ? x + 12 : x - 12 - tw},${ctx.plot.top + 4})`, children: [
|
|
1321
|
-
/* @__PURE__ */
|
|
1364
|
+
/* @__PURE__ */ jsx11("rect", { width: tw, height: th, rx: 8, strokeWidth: 1, style: { fill: VAR.bg, stroke: VAR.grid } }),
|
|
1322
1365
|
rows.map(([a, b, color], k) => /* @__PURE__ */ jsxs8("g", { transform: `translate(10,${16 + k * 18})`, children: [
|
|
1323
|
-
/* @__PURE__ */
|
|
1324
|
-
/* @__PURE__ */
|
|
1366
|
+
/* @__PURE__ */ jsx11("text", { dominantBaseline: "central", fontSize: 12, style: { fill: VAR.text }, children: a }),
|
|
1367
|
+
/* @__PURE__ */ jsx11("text", { x: tw - 20, textAnchor: "end", dominantBaseline: "central", fontSize: 12, fontWeight: 700, style: { fill: color ?? VAR.textStrong, fontVariantNumeric: "tabular-nums" }, children: b })
|
|
1325
1368
|
] }, a))
|
|
1326
1369
|
] })
|
|
1327
1370
|
] });
|
|
@@ -1337,8 +1380,8 @@ function CandlePlot({
|
|
|
1337
1380
|
const h = Math.max(1, bottom - top);
|
|
1338
1381
|
const hollow = hollowUp && up && bw >= 4;
|
|
1339
1382
|
return /* @__PURE__ */ jsxs8("g", { opacity: i !== null && i !== k ? 0.55 : 1, children: [
|
|
1340
|
-
/* @__PURE__ */
|
|
1341
|
-
/* @__PURE__ */
|
|
1383
|
+
/* @__PURE__ */ jsx11("line", { x1: cx, x2: cx, y1: y(d.high), y2: y(d.low), strokeWidth: 1, style: { stroke: color } }),
|
|
1384
|
+
/* @__PURE__ */ jsx11(
|
|
1342
1385
|
"rect",
|
|
1343
1386
|
{
|
|
1344
1387
|
x: cx - bw / 2,
|
|
@@ -1406,9 +1449,9 @@ function CandlestickChart({
|
|
|
1406
1449
|
return d ? `${l.open} ${valueFormatter(d.open)}, ${l.high} ${valueFormatter(d.high)}, ${l.low} ${valueFormatter(d.low)}, ${l.close} ${valueFormatter(d.close)}` : "";
|
|
1407
1450
|
}),
|
|
1408
1451
|
children: [
|
|
1409
|
-
yAx && /* @__PURE__ */
|
|
1410
|
-
xAx && /* @__PURE__ */
|
|
1411
|
-
hasVolume && /* @__PURE__ */
|
|
1452
|
+
yAx && /* @__PURE__ */ jsx11(YAxis, { tickLabelFormatter: valueFormatter, ...yAx, axisId: "price" }, "y"),
|
|
1453
|
+
xAx && /* @__PURE__ */ jsx11(XAxis, { ...xAx }, "x"),
|
|
1454
|
+
hasVolume && /* @__PURE__ */ jsx11(
|
|
1412
1455
|
BarPlot,
|
|
1413
1456
|
{
|
|
1414
1457
|
seriesIds: ["volume"],
|
|
@@ -1420,7 +1463,7 @@ function CandlestickChart({
|
|
|
1420
1463
|
}
|
|
1421
1464
|
}
|
|
1422
1465
|
),
|
|
1423
|
-
/* @__PURE__ */
|
|
1466
|
+
/* @__PURE__ */ jsx11(
|
|
1424
1467
|
CandlePlot,
|
|
1425
1468
|
{
|
|
1426
1469
|
data,
|
|
@@ -1441,8 +1484,8 @@ function CandlestickChart({
|
|
|
1441
1484
|
}
|
|
1442
1485
|
|
|
1443
1486
|
// src/viz/scrubber.tsx
|
|
1444
|
-
import { useEffect as
|
|
1445
|
-
import { jsx as
|
|
1487
|
+
import { useEffect as useEffect6 } from "react";
|
|
1488
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1446
1489
|
var TIP_FONT = 12;
|
|
1447
1490
|
var ROW = 18;
|
|
1448
1491
|
var TIP_PAD = 7;
|
|
@@ -1462,7 +1505,7 @@ function Scrubber({
|
|
|
1462
1505
|
const i = ctx.scrubIndex;
|
|
1463
1506
|
const { setDimFuture } = ctx;
|
|
1464
1507
|
const fade2 = dimFuture && !hideOverlay;
|
|
1465
|
-
|
|
1508
|
+
useEffect6(() => {
|
|
1466
1509
|
setDimFuture(fade2);
|
|
1467
1510
|
return () => setDimFuture(false);
|
|
1468
1511
|
}, [fade2, setDimFuture]);
|
|
@@ -1470,7 +1513,7 @@ function Scrubber({
|
|
|
1470
1513
|
const colorOf = (s, v) => beaconColor?.(v, s.id) ?? s.color;
|
|
1471
1514
|
if (i === null || i >= ctx.length) {
|
|
1472
1515
|
if (!idlePulse || ctx.band) return null;
|
|
1473
|
-
return /* @__PURE__ */
|
|
1516
|
+
return /* @__PURE__ */ jsx12("g", { pointerEvents: "none", children: pick.map((s) => {
|
|
1474
1517
|
let last = s.data.length - 1;
|
|
1475
1518
|
while (last >= 0 && (s.data[last] === null || s.data[last] === void 0)) last--;
|
|
1476
1519
|
if (last < 0) return null;
|
|
@@ -1480,10 +1523,10 @@ function Scrubber({
|
|
|
1480
1523
|
const color = colorOf(s, s.data[last]);
|
|
1481
1524
|
return /* @__PURE__ */ jsxs9("g", { children: [
|
|
1482
1525
|
/* @__PURE__ */ jsxs9("circle", { cx: x2, cy: y, r: 4, fillOpacity: 0.35, style: { fill: color }, children: [
|
|
1483
|
-
/* @__PURE__ */
|
|
1484
|
-
/* @__PURE__ */
|
|
1526
|
+
/* @__PURE__ */ jsx12("animate", { attributeName: "r", values: "4;13", dur: "1.6s", repeatCount: "indefinite" }),
|
|
1527
|
+
/* @__PURE__ */ jsx12("animate", { attributeName: "fill-opacity", values: "0.35;0", dur: "1.6s", repeatCount: "indefinite" })
|
|
1485
1528
|
] }),
|
|
1486
|
-
/* @__PURE__ */
|
|
1529
|
+
/* @__PURE__ */ jsx12("circle", { cx: x2, cy: y, r: 4, strokeWidth: 2, style: { fill: color, stroke: VAR.bg } })
|
|
1487
1530
|
] }, s.id);
|
|
1488
1531
|
}) });
|
|
1489
1532
|
}
|
|
@@ -1505,8 +1548,8 @@ function Scrubber({
|
|
|
1505
1548
|
const tipX = tipRight ? x + 12 : x - 12 - tipW;
|
|
1506
1549
|
const tipY = plot.top + 4;
|
|
1507
1550
|
return /* @__PURE__ */ jsxs9("g", { pointerEvents: "none", children: [
|
|
1508
|
-
/* @__PURE__ */
|
|
1509
|
-
text && /* @__PURE__ */
|
|
1551
|
+
/* @__PURE__ */ jsx12("line", { x1: x, x2: x, y1: plot.top, y2: plot.bottom, strokeWidth: 1, shapeRendering: "crispEdges", style: { stroke: VAR.line } }),
|
|
1552
|
+
text && /* @__PURE__ */ jsx12("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 }),
|
|
1510
1553
|
!ctx.band && list.map((s) => {
|
|
1511
1554
|
const y = seriesY(ctx, s, i);
|
|
1512
1555
|
if (y === null) return null;
|
|
@@ -1515,9 +1558,9 @@ function Scrubber({
|
|
|
1515
1558
|
const text2 = beaconLabels ? typeof beaconLabels === "function" ? beaconLabels(v, s.id) : valueFormatter(v, s.id) : "";
|
|
1516
1559
|
const right = x + 12 + textWidth3(text2) <= plot.right;
|
|
1517
1560
|
return /* @__PURE__ */ jsxs9("g", { children: [
|
|
1518
|
-
/* @__PURE__ */
|
|
1519
|
-
/* @__PURE__ */
|
|
1520
|
-
text2 && /* @__PURE__ */
|
|
1561
|
+
/* @__PURE__ */ jsx12("circle", { cx: x, cy: y, r: 7, fillOpacity: 0.18, style: { fill: color } }),
|
|
1562
|
+
/* @__PURE__ */ jsx12("circle", { cx: x, cy: y, r: 4, strokeWidth: 2, style: { fill: color, stroke: VAR.bg } }),
|
|
1563
|
+
text2 && /* @__PURE__ */ jsx12(
|
|
1521
1564
|
"text",
|
|
1522
1565
|
{
|
|
1523
1566
|
x: right ? x + 10 : x - 10,
|
|
@@ -1536,31 +1579,31 @@ function Scrubber({
|
|
|
1536
1579
|
] }, s.id);
|
|
1537
1580
|
}),
|
|
1538
1581
|
rows.length > 0 && /* @__PURE__ */ jsxs9("g", { transform: `translate(${tipX},${tipY})`, children: [
|
|
1539
|
-
/* @__PURE__ */
|
|
1582
|
+
/* @__PURE__ */ jsx12("rect", { width: tipW, height: tipH, rx: 8, style: { fill: VAR.bg, stroke: VAR.grid }, strokeWidth: 1 }),
|
|
1540
1583
|
rows.map((r, k) => /* @__PURE__ */ jsxs9("g", { transform: `translate(10,${TIP_PAD + ROW / 2 + k * ROW})`, children: [
|
|
1541
|
-
/* @__PURE__ */
|
|
1542
|
-
(rows.length > 1 || r.s.label) && /* @__PURE__ */
|
|
1543
|
-
/* @__PURE__ */
|
|
1584
|
+
/* @__PURE__ */ jsx12("circle", { cx: 3, cy: 0, r: 3, style: { fill: r.s.color } }),
|
|
1585
|
+
(rows.length > 1 || r.s.label) && /* @__PURE__ */ jsx12("text", { x: 12, dominantBaseline: "central", fontSize: TIP_FONT, style: { fill: VAR.text }, children: r.s.label ?? r.s.id }),
|
|
1586
|
+
/* @__PURE__ */ jsx12("text", { x: tipW - 20, textAnchor: "end", dominantBaseline: "central", fontSize: TIP_FONT, fontWeight: 700, style: { fill: VAR.textStrong, fontVariantNumeric: "tabular-nums" }, children: r.text })
|
|
1544
1587
|
] }, r.s.id))
|
|
1545
1588
|
] })
|
|
1546
1589
|
] });
|
|
1547
1590
|
}
|
|
1548
1591
|
|
|
1549
1592
|
// src/viz/annotations.tsx
|
|
1550
|
-
import { jsx as
|
|
1593
|
+
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1551
1594
|
var LABEL_FONT = 11;
|
|
1552
1595
|
var labelWidth = (t) => [...t].reduce((w, ch) => w + (ch.charCodeAt(0) > 11904 ? LABEL_FONT : LABEL_FONT * 0.62), 0);
|
|
1553
1596
|
function LineLabel(props) {
|
|
1554
1597
|
const { x, y, text, anchor, color, pill } = props;
|
|
1555
1598
|
const textColor = color === VAR.line ? VAR.text : color;
|
|
1556
1599
|
if (!pill) {
|
|
1557
|
-
return /* @__PURE__ */
|
|
1600
|
+
return /* @__PURE__ */ jsx13("text", { x, y, textAnchor: anchor, dominantBaseline: "central", fontSize: LABEL_FONT, fontWeight: 600, strokeWidth: 3, strokeLinejoin: "round", paintOrder: "stroke", style: { stroke: VAR.bg, fill: textColor }, children: text });
|
|
1558
1601
|
}
|
|
1559
1602
|
const w = labelWidth(text) + 14;
|
|
1560
1603
|
const left = anchor === "start" ? x : anchor === "end" ? x - w : x - w / 2;
|
|
1561
1604
|
return /* @__PURE__ */ jsxs10("g", { children: [
|
|
1562
|
-
/* @__PURE__ */
|
|
1563
|
-
/* @__PURE__ */
|
|
1605
|
+
/* @__PURE__ */ jsx13("rect", { x: left, y: y - 10, width: w, height: 20, rx: 10, style: { fill: color === VAR.line ? VAR.textStrong : color } }),
|
|
1606
|
+
/* @__PURE__ */ jsx13("text", { x: left + w / 2, y, textAnchor: "middle", dominantBaseline: "central", fontSize: LABEL_FONT, fontWeight: 700, style: { fill: VAR.bg }, children: text })
|
|
1564
1607
|
] });
|
|
1565
1608
|
}
|
|
1566
1609
|
function ReferenceLine({
|
|
@@ -1589,16 +1632,16 @@ function ReferenceLine({
|
|
|
1589
1632
|
const off = pill ? 13 : 9;
|
|
1590
1633
|
const ly = labelAlign === "center" ? y : labelAlign === "below" ? y + off : y - off;
|
|
1591
1634
|
return /* @__PURE__ */ jsxs10("g", { pointerEvents: "none", children: [
|
|
1592
|
-
/* @__PURE__ */
|
|
1593
|
-
label !== void 0 && /* @__PURE__ */
|
|
1635
|
+
/* @__PURE__ */ jsx13("line", { x1: plot.left, x2: plot.right, y1: y, y2: y, strokeWidth, strokeDasharray: dash || void 0, shapeRendering: "crispEdges", style: { stroke: color } }),
|
|
1636
|
+
label !== void 0 && /* @__PURE__ */ jsx13(LineLabel, { x: tx + labelDx, y: ly + labelDy, text: label, anchor, color, pill })
|
|
1594
1637
|
] });
|
|
1595
1638
|
}
|
|
1596
1639
|
if (dataX !== void 0) {
|
|
1597
1640
|
const x = ctx.x(dataX);
|
|
1598
1641
|
const pos = labelPosition ?? "top";
|
|
1599
1642
|
return /* @__PURE__ */ jsxs10("g", { pointerEvents: "none", children: [
|
|
1600
|
-
/* @__PURE__ */
|
|
1601
|
-
label !== void 0 && /* @__PURE__ */
|
|
1643
|
+
/* @__PURE__ */ jsx13("line", { x1: x, x2: x, y1: plot.top, y2: plot.bottom, strokeWidth, strokeDasharray: dash || void 0, shapeRendering: "crispEdges", style: { stroke: color } }),
|
|
1644
|
+
label !== void 0 && /* @__PURE__ */ jsx13(
|
|
1602
1645
|
LineLabel,
|
|
1603
1646
|
{
|
|
1604
1647
|
x: x + (pill ? 0 : 4) + labelDx,
|
|
@@ -1626,24 +1669,24 @@ function Point({ dataX, dataY, seriesId, yAxisId, color, radius = 4, label, labe
|
|
|
1626
1669
|
const [lx, ly, anchor] = labelPosition === "bottom" ? [x, y + gap + 8, "middle"] : labelPosition === "left" ? [x - gap, y + 4, "end"] : labelPosition === "right" ? [x + gap, y + 4, "start"] : [x, y - gap, "middle"];
|
|
1627
1670
|
return /* @__PURE__ */ jsxs10("g", { style: onClick ? { cursor: "pointer" } : void 0, onClick, pointerEvents: onClick ? "auto" : "none", children: [
|
|
1628
1671
|
pulse && /* @__PURE__ */ jsxs10("circle", { cx: x, cy: y, r: radius, fillOpacity: 0.35, style: { fill }, children: [
|
|
1629
|
-
/* @__PURE__ */
|
|
1630
|
-
/* @__PURE__ */
|
|
1672
|
+
/* @__PURE__ */ jsx13("animate", { attributeName: "r", values: `${radius};${radius * 3.2}`, dur: "1.6s", repeatCount: "indefinite" }),
|
|
1673
|
+
/* @__PURE__ */ jsx13("animate", { attributeName: "fill-opacity", values: "0.35;0", dur: "1.6s", repeatCount: "indefinite" })
|
|
1631
1674
|
] }),
|
|
1632
|
-
/* @__PURE__ */
|
|
1633
|
-
label !== void 0 && /* @__PURE__ */
|
|
1675
|
+
/* @__PURE__ */ jsx13("circle", { cx: x, cy: y, r: radius, strokeWidth: 2, style: { fill, stroke: VAR.bg } }),
|
|
1676
|
+
label !== void 0 && /* @__PURE__ */ jsx13("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 })
|
|
1634
1677
|
] });
|
|
1635
1678
|
}
|
|
1636
1679
|
|
|
1637
1680
|
// src/viz/period-selector.tsx
|
|
1638
|
-
import { useLayoutEffect, useRef as
|
|
1639
|
-
import { jsx as
|
|
1681
|
+
import { useLayoutEffect, useRef as useRef6, useState as useState7 } from "react";
|
|
1682
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1640
1683
|
function LiveTabLabel({ label, color = "var(--fnv-live, #F04452)" }) {
|
|
1641
1684
|
return /* @__PURE__ */ jsxs11("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
|
|
1642
1685
|
/* @__PURE__ */ jsxs11("span", { "aria-hidden": "true", style: { position: "relative", width: 7, height: 7 }, children: [
|
|
1643
|
-
/* @__PURE__ */
|
|
1644
|
-
/* @__PURE__ */
|
|
1645
|
-
/* @__PURE__ */
|
|
1646
|
-
/* @__PURE__ */
|
|
1686
|
+
/* @__PURE__ */ jsx14("span", { style: { position: "absolute", inset: 0, borderRadius: "50%", background: color } }),
|
|
1687
|
+
/* @__PURE__ */ jsx14("svg", { width: "7", height: "7", viewBox: "0 0 7 7", style: { position: "absolute", inset: 0, overflow: "visible" }, children: /* @__PURE__ */ jsxs11("circle", { cx: "3.5", cy: "3.5", r: "3.5", style: { fill: color }, fillOpacity: 0.45, children: [
|
|
1688
|
+
/* @__PURE__ */ jsx14("animate", { attributeName: "r", values: "3.5;8", dur: "1.6s", repeatCount: "indefinite" }),
|
|
1689
|
+
/* @__PURE__ */ jsx14("animate", { attributeName: "fill-opacity", values: "0.45;0", dur: "1.6s", repeatCount: "indefinite" })
|
|
1647
1690
|
] }) })
|
|
1648
1691
|
] }),
|
|
1649
1692
|
label
|
|
@@ -1670,8 +1713,8 @@ function PeriodSelector({
|
|
|
1670
1713
|
}) {
|
|
1671
1714
|
const activeIsLive = periods.find((p) => p.id === value)?.live ?? false;
|
|
1672
1715
|
const pillColor = activeIsLive ? liveColor : activeColor;
|
|
1673
|
-
const listRef =
|
|
1674
|
-
const [pill, setPill] =
|
|
1716
|
+
const listRef = useRef6(null);
|
|
1717
|
+
const [pill, setPill] = useState7(null);
|
|
1675
1718
|
useLayoutEffect(() => {
|
|
1676
1719
|
const list = listRef.current;
|
|
1677
1720
|
const active = list?.querySelector('[aria-selected="true"]');
|
|
@@ -1703,7 +1746,7 @@ function PeriodSelector({
|
|
|
1703
1746
|
onKeyDown,
|
|
1704
1747
|
style: { position: "relative", display: fullWidth ? "flex" : "inline-flex", gap: 2, fontFamily: VAR.font, ...style },
|
|
1705
1748
|
children: [
|
|
1706
|
-
pill && /* @__PURE__ */
|
|
1749
|
+
pill && /* @__PURE__ */ jsx14(
|
|
1707
1750
|
"span",
|
|
1708
1751
|
{
|
|
1709
1752
|
"aria-hidden": "true",
|
|
@@ -1722,7 +1765,7 @@ function PeriodSelector({
|
|
|
1722
1765
|
),
|
|
1723
1766
|
periods.map((p) => {
|
|
1724
1767
|
const active = p.id === value;
|
|
1725
|
-
return /* @__PURE__ */
|
|
1768
|
+
return /* @__PURE__ */ jsx14(
|
|
1726
1769
|
"button",
|
|
1727
1770
|
{
|
|
1728
1771
|
type: "button",
|
|
@@ -1746,7 +1789,7 @@ function PeriodSelector({
|
|
|
1746
1789
|
fontWeight: active ? 700 : 500,
|
|
1747
1790
|
cursor: "pointer"
|
|
1748
1791
|
},
|
|
1749
|
-
children: p.live ? /* @__PURE__ */
|
|
1792
|
+
children: p.live ? /* @__PURE__ */ jsx14(LiveTabLabel, { label: p.label, color: liveColor }) : p.label
|
|
1750
1793
|
},
|
|
1751
1794
|
p.id
|
|
1752
1795
|
);
|
|
@@ -1758,12 +1801,12 @@ function PeriodSelector({
|
|
|
1758
1801
|
|
|
1759
1802
|
// src/viz/charts.tsx
|
|
1760
1803
|
import { percentages, seriesColor as seriesColor5 } from "@funnest/charts-viz";
|
|
1761
|
-
import { useState as
|
|
1762
|
-
import { jsx as
|
|
1804
|
+
import { useState as useState8 } from "react";
|
|
1805
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1763
1806
|
var axisProps2 = (v) => v ? v === true ? {} : v : null;
|
|
1764
1807
|
var axisElements = (x, y) => [
|
|
1765
|
-
y && /* @__PURE__ */
|
|
1766
|
-
x && /* @__PURE__ */
|
|
1808
|
+
y && /* @__PURE__ */ jsx15(YAxis, { ...y }, "y"),
|
|
1809
|
+
x && /* @__PURE__ */ jsx15(XAxis, { ...x }, "x")
|
|
1767
1810
|
];
|
|
1768
1811
|
var defined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
1769
1812
|
var KR_SPLIT = { above: "#F04452", below: "#3182F6" };
|
|
@@ -1786,7 +1829,7 @@ function LineChart({
|
|
|
1786
1829
|
const split = baseline !== void 0 ? splitColors ?? KR_SPLIT : void 0;
|
|
1787
1830
|
return /* @__PURE__ */ jsxs12(CartesianChart, { enableScrubbing: !!sc, ...chart, children: [
|
|
1788
1831
|
axisElements(axisProps2(showXAxis), axisProps2(showYAxis)),
|
|
1789
|
-
chart.series.map((s) => /* @__PURE__ */
|
|
1832
|
+
chart.series.map((s) => /* @__PURE__ */ jsx15(
|
|
1790
1833
|
Line,
|
|
1791
1834
|
{
|
|
1792
1835
|
seriesId: s.id,
|
|
@@ -1796,7 +1839,7 @@ function LineChart({
|
|
|
1796
1839
|
s.id
|
|
1797
1840
|
)),
|
|
1798
1841
|
children,
|
|
1799
|
-
sc && /* @__PURE__ */
|
|
1842
|
+
sc && /* @__PURE__ */ jsx15(
|
|
1800
1843
|
Scrubber,
|
|
1801
1844
|
{
|
|
1802
1845
|
...split && baseline !== void 0 ? { beaconColor: (v) => v >= baseline ? split.above : split.below } : {},
|
|
@@ -1826,7 +1869,7 @@ function AreaChart({
|
|
|
1826
1869
|
axisElements(axisProps2(showXAxis), axisProps2(showYAxis)),
|
|
1827
1870
|
chart.series.map((s) => {
|
|
1828
1871
|
const own = s.areaStyle === "none" ? null : s.areaStyle ?? (chart.stacked && fill === "gradient" ? "solid" : fill);
|
|
1829
|
-
return own ? /* @__PURE__ */
|
|
1872
|
+
return own ? /* @__PURE__ */ jsx15(
|
|
1830
1873
|
Area,
|
|
1831
1874
|
{
|
|
1832
1875
|
seriesId: s.id,
|
|
@@ -1837,16 +1880,16 @@ function AreaChart({
|
|
|
1837
1880
|
`a${s.id}`
|
|
1838
1881
|
) : null;
|
|
1839
1882
|
}),
|
|
1840
|
-
showLines && chart.series.map((s) => /* @__PURE__ */
|
|
1883
|
+
showLines && chart.series.map((s) => /* @__PURE__ */ jsx15(Line, { seriesId: s.id, curve, strokeWidth, ...defined({ lineStyle, baseline, splitColors }) }, `l${s.id}`)),
|
|
1841
1884
|
children,
|
|
1842
|
-
sc && /* @__PURE__ */
|
|
1885
|
+
sc && /* @__PURE__ */ jsx15(Scrubber, { ...sc })
|
|
1843
1886
|
] });
|
|
1844
1887
|
}
|
|
1845
1888
|
function BarChart({ showXAxis, showYAxis, scrubber, radius, getBarColor, groupGap, stackGap, roundBaseline, barMinSize, layout = "vertical", xAxis, children, ...chart }) {
|
|
1846
1889
|
const sc = axisProps2(scrubber);
|
|
1847
1890
|
if (layout === "horizontal") {
|
|
1848
1891
|
const yAx = axisProps2(showYAxis);
|
|
1849
|
-
return /* @__PURE__ */
|
|
1892
|
+
return /* @__PURE__ */ jsx15(
|
|
1850
1893
|
HorizontalBarChart,
|
|
1851
1894
|
{
|
|
1852
1895
|
series: chart.series,
|
|
@@ -1875,9 +1918,9 @@ function BarChart({ showXAxis, showYAxis, scrubber, radius, getBarColor, groupGa
|
|
|
1875
1918
|
}
|
|
1876
1919
|
return /* @__PURE__ */ jsxs12(CartesianChart, { enableScrubbing: !!sc, xAxis: { ...xAxis, scaleType: "band" }, ...chart, children: [
|
|
1877
1920
|
axisElements(axisProps2(showXAxis), axisProps2(showYAxis)),
|
|
1878
|
-
/* @__PURE__ */
|
|
1921
|
+
/* @__PURE__ */ jsx15(BarPlot, { ...defined({ radius, getBarColor, groupGap, stackGap, roundBaseline, minSize: barMinSize }), highlightScrubbed: !!sc }),
|
|
1879
1922
|
children,
|
|
1880
|
-
sc && /* @__PURE__ */
|
|
1923
|
+
sc && /* @__PURE__ */ jsx15(Scrubber, { ...sc })
|
|
1881
1924
|
] });
|
|
1882
1925
|
}
|
|
1883
1926
|
var pct = (_item, f) => `${formatNumber(Math.round(f * 1e3) / 10)}%`;
|
|
@@ -1913,8 +1956,8 @@ function PercentageBarChart({
|
|
|
1913
1956
|
colorOf.set(d.id, d.color ?? seriesColor5(colorOf.size, palette));
|
|
1914
1957
|
legendItems.push(d);
|
|
1915
1958
|
}
|
|
1916
|
-
const [hovered, setHovered] =
|
|
1917
|
-
const [hoverRow, setHoverRow] =
|
|
1959
|
+
const [hovered, setHovered] = useState8(null);
|
|
1960
|
+
const [hoverRow, setHoverRow] = useState8(null);
|
|
1918
1961
|
const rowSegs = allRows.map((r) => {
|
|
1919
1962
|
const segs = percentages(r.data);
|
|
1920
1963
|
return { row: r, items: r.data.map((d, i) => ({ ...d, color: colorOf.get(d.id), fraction: segs[i].fraction })) };
|
|
@@ -1922,7 +1965,7 @@ function PercentageBarChart({
|
|
|
1922
1965
|
const renderBar = (items, rowId) => {
|
|
1923
1966
|
const visible = items.filter((i) => i.fraction > 0);
|
|
1924
1967
|
const ordered = vertical ? [...visible].reverse() : visible;
|
|
1925
|
-
return /* @__PURE__ */
|
|
1968
|
+
return /* @__PURE__ */ jsx15(
|
|
1926
1969
|
"div",
|
|
1927
1970
|
{
|
|
1928
1971
|
style: {
|
|
@@ -1934,7 +1977,7 @@ function PercentageBarChart({
|
|
|
1934
1977
|
children: ordered.map((i, k) => {
|
|
1935
1978
|
const a = k === 0 ? end : inner;
|
|
1936
1979
|
const b = k === ordered.length - 1 ? end : inner;
|
|
1937
|
-
return /* @__PURE__ */
|
|
1980
|
+
return /* @__PURE__ */ jsx15(
|
|
1938
1981
|
"span",
|
|
1939
1982
|
{
|
|
1940
1983
|
title: `${i.label} ${formatLabel(i, i.fraction)}`,
|
|
@@ -1963,7 +2006,7 @@ function PercentageBarChart({
|
|
|
1963
2006
|
);
|
|
1964
2007
|
};
|
|
1965
2008
|
const legendRow = multi ? rowSegs.find((r) => r.row.id === hoverRow) : rowSegs[0];
|
|
1966
|
-
const legend = showLabels && /* @__PURE__ */
|
|
2009
|
+
const legend = showLabels && /* @__PURE__ */ jsx15(
|
|
1967
2010
|
Legend,
|
|
1968
2011
|
{
|
|
1969
2012
|
style: legendAt === "top" ? { marginBottom: 10 } : legendAt === "bottom" ? { marginTop: 10 } : {},
|
|
@@ -1982,14 +2025,14 @@ function PercentageBarChart({
|
|
|
1982
2025
|
onItemHover: setHovered
|
|
1983
2026
|
}
|
|
1984
2027
|
);
|
|
1985
|
-
const label = (r) => multi && r.label ? /* @__PURE__ */
|
|
1986
|
-
const bars = vertical ? /* @__PURE__ */
|
|
2028
|
+
const label = (r) => multi && r.label ? /* @__PURE__ */ jsx15("span", { style: { fontSize: 12, color: hoverRow === r.id ? VAR.textStrong : VAR.text, fontWeight: hoverRow === r.id ? 700 : 500, whiteSpace: "nowrap" }, children: r.label }) : null;
|
|
2029
|
+
const bars = vertical ? /* @__PURE__ */ jsx15("div", { style: { display: "flex", gap: 14, alignItems: "flex-end" }, children: rowSegs.map(({ row, items }) => /* @__PURE__ */ jsxs12("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 6 }, children: [
|
|
1987
2030
|
renderBar(items, row.id),
|
|
1988
2031
|
label(row)
|
|
1989
|
-
] }, row.id)) }) : multi ? /* @__PURE__ */
|
|
1990
|
-
label(row) ?? /* @__PURE__ */
|
|
2032
|
+
] }, row.id)) }) : multi ? /* @__PURE__ */ jsx15("div", { style: { display: "grid", gridTemplateColumns: "max-content 1fr", columnGap: 12, rowGap: 10, alignItems: "center", flex: legendAt === "right" ? 1 : void 0 }, children: rowSegs.map(({ row, items }) => /* @__PURE__ */ jsxs12("div", { style: { display: "contents" }, children: [
|
|
2033
|
+
label(row) ?? /* @__PURE__ */ jsx15("span", {}),
|
|
1991
2034
|
renderBar(items, row.id)
|
|
1992
|
-
] }, row.id)) }) : /* @__PURE__ */
|
|
2035
|
+
] }, row.id)) }) : /* @__PURE__ */ jsx15("div", { style: { display: "flex", flex: legendAt === "right" ? 1 : void 0 }, children: renderBar(rowSegs[0].items, rowSegs[0].row.id) });
|
|
1993
2036
|
return /* @__PURE__ */ jsxs12(
|
|
1994
2037
|
"div",
|
|
1995
2038
|
{
|
|
@@ -2017,6 +2060,7 @@ export {
|
|
|
2017
2060
|
DonutChart,
|
|
2018
2061
|
FnChart,
|
|
2019
2062
|
FnChartSlot,
|
|
2063
|
+
FnMultiChart,
|
|
2020
2064
|
HorizontalBarChart,
|
|
2021
2065
|
Legend,
|
|
2022
2066
|
Line,
|
|
@@ -2033,5 +2077,6 @@ export {
|
|
|
2033
2077
|
formatNumber,
|
|
2034
2078
|
useCartesianChart,
|
|
2035
2079
|
useChartEvent,
|
|
2036
|
-
useFnChart
|
|
2080
|
+
useFnChart,
|
|
2081
|
+
useFnMultiChart
|
|
2037
2082
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnest/charts-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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
|
|
50
|
-
"@funnest/charts": "0.
|
|
51
|
-
"@funnest/charts-
|
|
49
|
+
"@funnest/charts": "0.5.1",
|
|
50
|
+
"@funnest/charts-viz": "0.5.1",
|
|
51
|
+
"@funnest/charts-ui": "0.5.1"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"react": ">=18",
|