nanoplot 0.0.16 → 0.0.18
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/bun.lockb +0 -0
- package/dist/src/app/bar-graph/components/Rect.d.ts +10 -0
- package/dist/src/app/bar-graph/components/Rect.jsx +27 -0
- package/dist/src/components/Bars/Bars.d.ts +9 -0
- package/dist/src/components/Bars/Bars.jsx +18 -0
- package/dist/src/components/Bars/components/BarsVerticalLoading.d.ts +1 -0
- package/dist/src/components/Bars/components/BarsVerticalLoading.jsx +19 -0
- package/dist/src/components/Bars/components/HorizontalBars.d.ts +8 -0
- package/dist/src/components/Bars/components/HorizontalBars.jsx +47 -0
- package/dist/src/components/Bars/components/VerticalBars.d.ts +9 -0
- package/dist/src/components/Bars/components/VerticalBars.jsx +51 -0
- package/dist/src/components/Graph/Graph.d.ts +24 -0
- package/dist/src/components/Graph/Graph.jsx +60 -0
- package/dist/src/components/GridLines/GridLines.d.ts +9 -0
- package/dist/src/components/GridLines/GridLines.jsx +13 -0
- package/dist/src/components/Legend/Legend.d.ts +45 -0
- package/dist/src/components/Legend/Legend.jsx +37 -0
- package/dist/src/components/LinearGradient/LinearGradient.d.ts +6 -0
- package/dist/src/components/LinearGradient/LinearGradient.jsx +52 -0
- package/dist/src/components/Lines/Lines.d.ts +17 -0
- package/dist/src/components/Lines/Lines.jsx +53 -0
- package/dist/src/components/Lines/components/LinesLoading.d.ts +2 -0
- package/dist/src/components/Lines/components/LinesLoading.jsx +10 -0
- package/dist/src/components/Lines/components/LinesTooltip.d.ts +10 -0
- package/dist/src/components/Lines/components/LinesTooltip.jsx +109 -0
- package/dist/src/components/Overlay/Overlay.d.ts +9 -0
- package/dist/src/components/Overlay/Overlay.jsx +39 -0
- package/dist/src/components/Pie/Pie.d.ts +11 -0
- package/dist/src/components/Pie/Pie.jsx +105 -0
- package/dist/src/components/Pie/components/PieEmpty.d.ts +8 -0
- package/dist/src/components/Pie/components/PieEmpty.jsx +21 -0
- package/dist/src/components/Pie/components/PieLoading.d.ts +9 -0
- package/dist/src/components/Pie/components/PieLoading.jsx +19 -0
- package/dist/src/components/Radar/Radar.d.ts +9 -0
- package/dist/src/components/Radar/Radar.jsx +160 -0
- package/dist/src/components/Radar/components/RadarSkeleton.d.ts +8 -0
- package/dist/src/components/Radar/components/RadarSkeleton.jsx +13 -0
- package/dist/src/components/Scatter/Scatter.d.ts +8 -0
- package/dist/src/components/Scatter/Scatter.jsx +30 -0
- package/dist/src/components/Scatter/components/ScatterLoading.d.ts +6 -0
- package/dist/src/components/Scatter/components/ScatterLoading.jsx +29 -0
- package/dist/src/components/Sunburst/Sunburst.d.ts +21 -0
- package/dist/src/components/Sunburst/Sunburst.jsx +204 -0
- package/dist/src/components/Sunburst/SunburstLoading.d.ts +2 -0
- package/dist/src/components/Sunburst/SunburstLoading.jsx +11 -0
- package/dist/src/components/Tooltip/Popup.d.ts +9 -0
- package/dist/src/components/Tooltip/Popup.jsx +94 -0
- package/dist/src/components/Worldmap/Worldmap.d.ts +329 -0
- package/dist/src/components/Worldmap/Worldmap.jsx +33 -0
- package/dist/src/components/XAxis/XAxis.d.ts +53 -0
- package/dist/src/components/XAxis/XAxis.jsx +39 -0
- package/dist/src/components/YAxis/YAxis.d.ts +51 -0
- package/dist/src/components/YAxis/YAxis.jsx +27 -0
- package/dist/src/export/index.d.ts +14 -0
- package/dist/src/export/index.jsx +13 -0
- package/dist/src/hooks/use-graph/use-graph.d.ts +58 -0
- package/dist/src/hooks/use-graph/use-graph.jsx +7 -0
- package/dist/src/hooks/use-mouse-coordinates.d.ts +8 -0
- package/dist/src/hooks/use-mouse-coordinates.js +41 -0
- package/dist/src/hooks/use-stateful-ref.d.ts +2 -0
- package/dist/src/hooks/use-stateful-ref.js +11 -0
- package/dist/src/hooks/use-tether.d.ts +38 -0
- package/dist/src/hooks/use-tether.js +151 -0
- package/dist/src/utils/children/children.d.ts +5 -0
- package/dist/src/utils/children/children.js +16 -0
- package/dist/src/utils/color/color.d.ts +4 -0
- package/dist/src/utils/color/color.js +92 -0
- package/dist/src/utils/coordinates/coordinates.d.ts +15 -0
- package/dist/src/utils/coordinates/coordinates.js +67 -0
- package/dist/src/utils/countries.d.ts +181 -0
- package/dist/src/utils/countries.js +181 -0
- package/dist/src/utils/cx/cx.d.ts +3 -0
- package/dist/src/utils/cx/cx.js +11 -0
- package/dist/src/utils/domain/date-domain.d.ts +75 -0
- package/dist/src/utils/domain/date-domain.js +140 -0
- package/dist/src/utils/domain/domain.d.ts +20 -0
- package/dist/src/utils/domain/domain.js +253 -0
- package/dist/src/utils/graph/graph.d.ts +5 -0
- package/dist/src/utils/graph/graph.js +9 -0
- package/dist/src/utils/math/math.d.ts +5 -0
- package/dist/src/utils/math/math.js +12 -0
- package/dist/src/utils/object/object.d.ts +3 -0
- package/dist/src/utils/object/object.js +11 -0
- package/dist/src/utils/path/curve.d.ts +22 -0
- package/dist/src/utils/path/curve.js +111 -0
- package/dist/src/utils/path/path.d.ts +31 -0
- package/dist/src/utils/path/path.js +168 -0
- package/package.json +101 -101
- package/@types/global.d.ts +0 -5
package/bun.lockb
CHANGED
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { JSX } from "react";
|
|
2
|
+
type Props = JSX.IntrinsicElements["path"] & {
|
|
3
|
+
x1: number;
|
|
4
|
+
x2: number;
|
|
5
|
+
y1: number;
|
|
6
|
+
y2: number;
|
|
7
|
+
radius?: number;
|
|
8
|
+
};
|
|
9
|
+
export declare const Rect: ({ x1, x2, y1, y2, radius, ...rest }: Props) => JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React, { useId } from "react";
|
|
13
|
+
import { LinearGradient } from "@/components/LinearGradient/LinearGradient";
|
|
14
|
+
import { PathUtils } from "@/utils/path/path";
|
|
15
|
+
export const Rect = (_a) => {
|
|
16
|
+
var _b;
|
|
17
|
+
var { x1, x2, y1, y2, radius } = _a, rest = __rest(_a, ["x1", "x2", "y1", "y2", "radius"]);
|
|
18
|
+
const id = useId();
|
|
19
|
+
const isFillGradient = (_b = rest.fill) === null || _b === void 0 ? void 0 : _b.includes("gradient");
|
|
20
|
+
const path = radius
|
|
21
|
+
? PathUtils.borderRadius({ x: x1, y: y1 }, { x: x2, y: y2 }, radius)
|
|
22
|
+
: `M ${x1} ${y1} L ${x1} ${y2} L ${x2} ${y2} L ${x2} ${y1}`;
|
|
23
|
+
return (<>
|
|
24
|
+
{isFillGradient && rest.fill && <LinearGradient id={id} gradient={rest.fill}/>}
|
|
25
|
+
<path fill={isFillGradient ? `url(#${id})` : rest.fill} stroke={rest.stroke} d={path} vectorEffect={"non-scaling-stroke"} strokeWidth={1.5}/>
|
|
26
|
+
</>);
|
|
27
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React from "react";
|
|
13
|
+
import { VerticalBars } from "@/components/Bars/components/VerticalBars";
|
|
14
|
+
import { HorizontalBars } from "./components/HorizontalBars";
|
|
15
|
+
export const Bars = (_a) => {
|
|
16
|
+
var { horizontal, loading } = _a, props = __rest(_a, ["horizontal", "loading"]);
|
|
17
|
+
return horizontal ? <HorizontalBars {...props}/> : <VerticalBars loading={loading} {...props}/>;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const BarsVerticalLoading: () => import("react").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { cx } from "@/utils/cx/cx";
|
|
2
|
+
import { useGraph } from "@/hooks/use-graph/use-graph";
|
|
3
|
+
import { useId } from "react";
|
|
4
|
+
export const BarsVerticalLoading = () => {
|
|
5
|
+
const maskId = useId();
|
|
6
|
+
const { viewbox } = useGraph();
|
|
7
|
+
const CX = viewbox.x / 2;
|
|
8
|
+
const CY = viewbox.y / 2;
|
|
9
|
+
const BAR_GAP = 20;
|
|
10
|
+
const BAR_WIDTH = viewbox.x * 0.08;
|
|
11
|
+
return (<svg id="loading" role="status" aria-busy={true} viewBox={`0 0 ${viewbox.x} ${viewbox.y}`} className={cx("[grid-area:graph] h-full w-full")} preserveAspectRatio={"none"}>
|
|
12
|
+
<path vectorEffect={"non-scaling-stroke"} d={`
|
|
13
|
+
M${CX - BAR_WIDTH - BAR_GAP - BAR_WIDTH / 2} ${viewbox.y} L${CX - BAR_WIDTH - BAR_GAP - BAR_WIDTH / 2} ${(viewbox.y / 30) * 20} L${CX - BAR_WIDTH - BAR_GAP + BAR_WIDTH - BAR_WIDTH / 2} ${(viewbox.y / 30) * 20} L${CX - BAR_WIDTH - BAR_GAP + BAR_WIDTH - BAR_WIDTH / 2} ${viewbox.y}
|
|
14
|
+
M ${CX - BAR_WIDTH / 2} ${viewbox.y} L${CX - BAR_WIDTH / 2} ${(viewbox.y / 30) * 10} L${CX + BAR_WIDTH - BAR_WIDTH / 2} ${(viewbox.y / 30) * 10} L${CX + BAR_WIDTH - BAR_WIDTH / 2} ${viewbox.y}
|
|
15
|
+
M${BAR_GAP + CX + BAR_WIDTH - BAR_WIDTH / 2} ${viewbox.y} L${BAR_GAP + CX + BAR_WIDTH - BAR_WIDTH / 2} ${(viewbox.y / 30) * 16} L${BAR_GAP + CX + 2 * BAR_WIDTH - BAR_WIDTH / 2} ${(viewbox.y / 30) * 16} L${BAR_GAP + CX + 2 * BAR_WIDTH - BAR_WIDTH / 2} ${viewbox.y}`} className={"[filter:brightness(300%)] dark:[filter:brightness(100%)]"} mask={`url(#${maskId})`}>
|
|
16
|
+
<animate attributeName="fill" values="#2d2d2d; #3c3c3c; #2d2d2d; #2d2d2d; " dur="2s" repeatCount="indefinite"/>
|
|
17
|
+
</path>
|
|
18
|
+
</svg>);
|
|
19
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
type Props = React.SVGAttributes<SVGSVGElement> & {
|
|
3
|
+
children?: React.ReactNode;
|
|
4
|
+
size?: number;
|
|
5
|
+
radius?: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const HorizontalBars: ({ children, size, radius, className }: Props) => React.JSX.Element | null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ColorUtils } from "@/export";
|
|
2
|
+
import { useGraph } from "@/hooks/use-graph/use-graph";
|
|
3
|
+
import { CoordinatesUtils } from "@/utils/coordinates/coordinates";
|
|
4
|
+
import { cx } from "@/utils/cx/cx";
|
|
5
|
+
import { GraphUtils } from "@/utils/graph/graph";
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { PathUtils } from "@/utils/path/path";
|
|
8
|
+
export const HorizontalBars = ({ children, size = 30, radius = 0, className }) => {
|
|
9
|
+
const context = useGraph();
|
|
10
|
+
if (!GraphUtils.isXYData(context.data))
|
|
11
|
+
return null;
|
|
12
|
+
const xForValue = CoordinatesUtils.xCoordinateFor(context);
|
|
13
|
+
const yForValue = CoordinatesUtils.yCoordinateFor(context);
|
|
14
|
+
const bars = context.data.map((bar, i, bars) => {
|
|
15
|
+
var _a, _b, _c, _d;
|
|
16
|
+
return Object.assign(Object.assign({}, bar), { id: (_a = bar.id) !== null && _a !== void 0 ? _a : bar.name, stroke: (_b = bar.stroke) !== null && _b !== void 0 ? _b : ColorUtils.colorFor(i, bars.length), fill: bar.fill === true ? ((_c = bar.stroke) !== null && _c !== void 0 ? _c : ColorUtils.colorFor(i, bars.length)) : bar.fill, bar: (_d = bar.group) !== null && _d !== void 0 ? _d : bar.name, data: bar.data.map((xy) => ({
|
|
17
|
+
x: xForValue(xy.x),
|
|
18
|
+
y: yForValue(xy.y),
|
|
19
|
+
})) });
|
|
20
|
+
});
|
|
21
|
+
const barGap = context.viewbox.x / 100; // 16% gap
|
|
22
|
+
const barHeight = Math.floor(((context.viewbox.y - barGap) * size) / 1000);
|
|
23
|
+
const groups = [...new Set(bars.map((bar) => bar.group))];
|
|
24
|
+
return (<svg viewBox={`0 0 ${context.viewbox.x} ${context.viewbox.y}`} className={cx("[grid-area:graph] h-full w-full", className)} preserveAspectRatio={"none"}>
|
|
25
|
+
{groups === null || groups === void 0 ? void 0 : groups.map((group, g) => {
|
|
26
|
+
const groupBars = bars.filter((b) => b.group === group);
|
|
27
|
+
const coordinate = [];
|
|
28
|
+
return groupBars.map((bar, index) => {
|
|
29
|
+
var _a;
|
|
30
|
+
if (bar.group === group)
|
|
31
|
+
return (_a = bar.data) === null || _a === void 0 ? void 0 : _a.map((xy, idx) => {
|
|
32
|
+
const y1 = xy.y + barHeight * g - barHeight * (groups.length / 2);
|
|
33
|
+
const y2 = y1 + barHeight;
|
|
34
|
+
const x1 = index === 0 ? 0 : coordinate[idx];
|
|
35
|
+
const x2 = index === 0 ? xy.x : coordinate[idx] + (0 + xy.x);
|
|
36
|
+
const candleRadius = groupBars.length === index + 1
|
|
37
|
+
? PathUtils.borderRadius({ x: x1, y: y1 }, { x: x2, y: y2 }, radius, true)
|
|
38
|
+
: `M ${x1} ${y1} L ${x1} ${y2} L ${x2} ${y2} L ${x2} ${y1}`;
|
|
39
|
+
// recorde the combined x coordinate (use for next stacked bar)
|
|
40
|
+
coordinate[idx] = index === 0 ? xy.x : coordinate[idx] + xy.x;
|
|
41
|
+
return (<path key={idx + index + xy.y + xy.x} d={candleRadius} fill={bar.stroke} stroke={bar.stroke} vectorEffect={"non-scaling-stroke"} strokeWidth={1.5}/>);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
})}
|
|
45
|
+
{children}
|
|
46
|
+
</svg>);
|
|
47
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
type Props = React.SVGAttributes<SVGSVGElement> & {
|
|
3
|
+
children?: ReactNode;
|
|
4
|
+
loading?: boolean;
|
|
5
|
+
size?: number;
|
|
6
|
+
radius?: number;
|
|
7
|
+
};
|
|
8
|
+
export declare const VerticalBars: ({ children, size, radius, className, loading }: Props) => React.JSX.Element | null;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CoordinatesUtils } from "@/utils/coordinates/coordinates";
|
|
3
|
+
import { GraphUtils } from "@/utils/graph/graph";
|
|
4
|
+
import { ColorUtils } from "@/utils/color/color";
|
|
5
|
+
import { cx } from "@/utils/cx/cx";
|
|
6
|
+
import { useGraph } from "@/hooks/use-graph/use-graph";
|
|
7
|
+
import { BarsVerticalLoading } from "./BarsVerticalLoading";
|
|
8
|
+
import { Rect } from "@/app/bar-graph/components/Rect";
|
|
9
|
+
export const VerticalBars = ({ children, size = 30, radius = 0, className, loading }) => {
|
|
10
|
+
const context = useGraph();
|
|
11
|
+
if (!GraphUtils.isXYData(context.data))
|
|
12
|
+
return null;
|
|
13
|
+
const xForValue = CoordinatesUtils.xCoordinateFor(context);
|
|
14
|
+
const yForValue = CoordinatesUtils.yCoordinateFor(context);
|
|
15
|
+
const bars = context.data.map((bar, i, bars) => {
|
|
16
|
+
var _a, _b, _c, _d, _e, _f;
|
|
17
|
+
return Object.assign(Object.assign({}, bar), { id: (_a = bar.id) !== null && _a !== void 0 ? _a : bar.name, group: (_c = (_b = bar.group) !== null && _b !== void 0 ? _b : bar.id) !== null && _c !== void 0 ? _c : bar.name, stroke: (_d = bar.stroke) !== null && _d !== void 0 ? _d : ColorUtils.colorFor(i, bars.length), fill: bar.fill === true ? ((_e = bar.stroke) !== null && _e !== void 0 ? _e : ColorUtils.colorFor(i, bars.length)) : bar.fill, bar: (_f = bar.group) !== null && _f !== void 0 ? _f : bar.name, data: bar.data.map((xy) => ({
|
|
18
|
+
x: xForValue(xy.x),
|
|
19
|
+
y: yForValue(xy.y),
|
|
20
|
+
})) });
|
|
21
|
+
});
|
|
22
|
+
// stacked AND unstacked bars is the same code path.
|
|
23
|
+
// always setting a group (which is how you stack).
|
|
24
|
+
// and because group is defaulted to id or name stacks will be commonly 1/1
|
|
25
|
+
// if consumers of the library use 'group' it will be stacked for members of that group.
|
|
26
|
+
const barGap = context.viewbox.x / 100; // 16% gap
|
|
27
|
+
const barWidth = Math.floor(((context.viewbox.x - barGap) * size) / 1000);
|
|
28
|
+
const groups = [...new Set(bars.map((bar) => bar.group))];
|
|
29
|
+
if (loading) {
|
|
30
|
+
return <BarsVerticalLoading />;
|
|
31
|
+
}
|
|
32
|
+
return (<svg viewBox={`0 0 ${context.viewbox.x} ${context.viewbox.y}`} className={cx("[grid-area:graph] h-full w-full", className)} preserveAspectRatio={"none"}>
|
|
33
|
+
{groups === null || groups === void 0 ? void 0 : groups.map((group, g) => {
|
|
34
|
+
const groupBars = bars.filter((b) => b.group === group);
|
|
35
|
+
const coordinate = [];
|
|
36
|
+
return groupBars.map((bar, index) => {
|
|
37
|
+
var _a;
|
|
38
|
+
if (bar.group === group)
|
|
39
|
+
return (_a = bar.data) === null || _a === void 0 ? void 0 : _a.map((xy, idx) => {
|
|
40
|
+
const x1 = xy.x + barWidth * g - barWidth * (groups.length / 2);
|
|
41
|
+
const x2 = x1 + barWidth;
|
|
42
|
+
const y1 = index === 0 ? context.viewbox.y : coordinate[idx];
|
|
43
|
+
const y2 = index === 0 ? xy.y : coordinate[idx] - (context.viewbox.y - xy.y);
|
|
44
|
+
coordinate[idx] = index === 0 ? xy.y : coordinate[idx] - (context.viewbox.y - xy.y);
|
|
45
|
+
return (<Rect key={idx} x1={x1} x2={x2} y1={y1} y2={y2} fill={bar.stroke} stroke={bar.stroke} radius={groupBars.length === index + 1 ? radius : undefined}/>);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
})}
|
|
49
|
+
{children}
|
|
50
|
+
</svg>);
|
|
51
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { CSSProperties, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import { GraphContext } from "@/hooks/use-graph/use-graph";
|
|
3
|
+
type Props = {
|
|
4
|
+
data?: GraphContext["data"];
|
|
5
|
+
gap?: {
|
|
6
|
+
top?: number;
|
|
7
|
+
right?: number;
|
|
8
|
+
bottom?: number;
|
|
9
|
+
left?: number;
|
|
10
|
+
};
|
|
11
|
+
interactions?: {
|
|
12
|
+
hovered?: string[];
|
|
13
|
+
pinned?: string[];
|
|
14
|
+
};
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
style?: CSSProperties;
|
|
17
|
+
className?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare const Graph: {
|
|
20
|
+
({ data, gap, children, interactions, style, className }: Props): React.JSX.Element;
|
|
21
|
+
Row({ children, ...rest }: HTMLAttributes<HTMLDivElement>): React.JSX.Element;
|
|
22
|
+
Column({ children, ...rest }: HTMLAttributes<HTMLDivElement>): React.JSX.Element;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React, { useId } from "react";
|
|
13
|
+
import { GraphContextProvider } from "@/hooks/use-graph/use-graph";
|
|
14
|
+
import { ChildrenUtils } from "@/utils/children/children";
|
|
15
|
+
import { DomainUtils } from "@/utils/domain/domain";
|
|
16
|
+
import { cx } from "@/utils/cx/cx";
|
|
17
|
+
import { ColorUtils } from "@/utils/color/color";
|
|
18
|
+
import { GraphUtils } from "@/utils/graph/graph";
|
|
19
|
+
export const Graph = ({ data = [], gap, children, interactions, style, className }) => {
|
|
20
|
+
var _a, _b, _c, _d, _e, _f;
|
|
21
|
+
const id = useId();
|
|
22
|
+
const X_SCALE = 3000;
|
|
23
|
+
const Y_SCALE = 3000;
|
|
24
|
+
const ctx = ChildrenUtils.context(children, {
|
|
25
|
+
id,
|
|
26
|
+
layout: { rows: "[graph] auto", columns: "[graph] auto" },
|
|
27
|
+
viewbox: { x: X_SCALE, y: Y_SCALE },
|
|
28
|
+
data: GraphUtils.isXYData(data)
|
|
29
|
+
? data.map((dp, i, dps) => {
|
|
30
|
+
var _a, _b, _c;
|
|
31
|
+
return Object.assign({ id: (_a = dp.id) !== null && _a !== void 0 ? _a : dp.name, stroke: (_b = dp.stroke) !== null && _b !== void 0 ? _b : ColorUtils.colorFor(i, dps.length), fill: dp.fill === true ? ((_c = dp.stroke) !== null && _c !== void 0 ? _c : ColorUtils.colorFor(i, dps.length)) : dp.fill }, dp);
|
|
32
|
+
})
|
|
33
|
+
: data.map((dp, i, dps) => {
|
|
34
|
+
var _a, _b, _c;
|
|
35
|
+
return Object.assign({ id: (_a = dp.id) !== null && _a !== void 0 ? _a : dp.name, stroke: (_b = dp.stroke) !== null && _b !== void 0 ? _b : ColorUtils.colorFor(i, dps.length), fill: dp.fill === true ? ((_c = dp.stroke) !== null && _c !== void 0 ? _c : ColorUtils.colorFor(i, dps.length)) : dp.fill }, dp);
|
|
36
|
+
}),
|
|
37
|
+
gap: { top: (_a = gap === null || gap === void 0 ? void 0 : gap.top) !== null && _a !== void 0 ? _a : 0, left: (_b = gap === null || gap === void 0 ? void 0 : gap.left) !== null && _b !== void 0 ? _b : 0, right: (_c = gap === null || gap === void 0 ? void 0 : gap.right) !== null && _c !== void 0 ? _c : 0, bottom: (_d = gap === null || gap === void 0 ? void 0 : gap.bottom) !== null && _d !== void 0 ? _d : 0 },
|
|
38
|
+
attributes: {
|
|
39
|
+
className: "@container/graph nanoplot relative grid h-full w-full isolate",
|
|
40
|
+
},
|
|
41
|
+
domain: {
|
|
42
|
+
x: DomainUtils.x.ticks({ data, viewbox: { x: X_SCALE, y: Y_SCALE } }),
|
|
43
|
+
y: DomainUtils.y.ticks({ data, viewbox: { x: X_SCALE, y: Y_SCALE } }),
|
|
44
|
+
},
|
|
45
|
+
interactions: { hovered: (_e = interactions === null || interactions === void 0 ? void 0 : interactions.hovered) !== null && _e !== void 0 ? _e : [], pinned: (_f = interactions === null || interactions === void 0 ? void 0 : interactions.pinned) !== null && _f !== void 0 ? _f : [] },
|
|
46
|
+
});
|
|
47
|
+
return (<div id={id} {...ctx.attributes} style={Object.assign(Object.assign(Object.assign({}, style), ctx.attributes.style), { gridTemplateColumns: ctx.layout.columns, gridTemplateRows: ctx.layout.rows, padding: `${ctx.gap.top}px ${ctx.gap.right}px ${ctx.gap.bottom}px ${ctx.gap.left}px` })} className={cx(ctx.attributes.className, className)}>
|
|
48
|
+
<GraphContextProvider value={ctx}>{children}</GraphContextProvider>
|
|
49
|
+
</div>);
|
|
50
|
+
};
|
|
51
|
+
Graph.Row = (_a) => {
|
|
52
|
+
var { children } = _a, rest = __rest(_a, ["children"]);
|
|
53
|
+
return (<div {...rest} className={cx("col-span-full", rest.className)}>
|
|
54
|
+
{children}
|
|
55
|
+
</div>);
|
|
56
|
+
};
|
|
57
|
+
Graph.Column = (_a) => {
|
|
58
|
+
var { children } = _a, rest = __rest(_a, ["children"]);
|
|
59
|
+
return <div {...rest}>{children}</div>;
|
|
60
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface Props extends React.SVGAttributes<SVGSVGElement> {
|
|
3
|
+
trendline?: boolean;
|
|
4
|
+
border?: boolean;
|
|
5
|
+
horizontal?: boolean;
|
|
6
|
+
vertical?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare const GridLines: ({ border, horizontal, vertical, className }: Props) => React.JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cx } from "@/utils/cx/cx";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useGraph } from "@/hooks/use-graph/use-graph";
|
|
4
|
+
export const GridLines = ({ border, horizontal, vertical, className }) => {
|
|
5
|
+
const context = useGraph();
|
|
6
|
+
const { x, y } = context.viewbox;
|
|
7
|
+
const { domain } = context;
|
|
8
|
+
return (<svg viewBox={`0 0 ${x} ${y}`} className={cx("[grid-area:graph] h-full w-full", className)} preserveAspectRatio={"none"}>
|
|
9
|
+
{border && (<path d={`M 0 0 l${x} 0 l0 ${y} l${-x} 0 Z`} strokeWidth={1} vectorEffect={"non-scaling-stroke"} fill={"transparent"} className={cx("stroke-[#DFDFDF] dark:stroke-[#2D2D2D] grid-lines__border")}/>)}
|
|
10
|
+
{horizontal && (<path d={domain.y.map(({ coordinate }) => `M 0 ${coordinate} L ${x} ${coordinate}`).join(" ")} strokeWidth={1} vectorEffect={"non-scaling-stroke"} className={cx("stroke-[#DFDFDF] dark:stroke-[#2D2D2D] grid-lines__horizontal")}/>)}
|
|
11
|
+
{vertical && (<path d={domain.x.map(({ coordinate }) => `M ${coordinate} 0 L ${coordinate} ${y}`).join(" ")} strokeWidth={1} vectorEffect={"non-scaling-stroke"} className={cx("stroke-[#DFDFDF] dark:stroke-[#2D2D2D] grid-lines__vertical")}/>)}
|
|
12
|
+
</svg>);
|
|
13
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { GraphContext } from "@/hooks/use-graph/use-graph";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
type Props = {
|
|
5
|
+
position?: "top" | "bottom" | "left" | "right";
|
|
6
|
+
alignment?: "center" | "start" | "end";
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
};
|
|
9
|
+
export declare const Legend: {
|
|
10
|
+
({ position, alignment }: Props): React.JSX.Element;
|
|
11
|
+
context(ctx: GraphContext, props: Props): {
|
|
12
|
+
layout: {
|
|
13
|
+
rows: string;
|
|
14
|
+
columns: string;
|
|
15
|
+
};
|
|
16
|
+
id: string;
|
|
17
|
+
attributes: React.HTMLAttributes<HTMLDivElement>;
|
|
18
|
+
gap: {
|
|
19
|
+
top: number;
|
|
20
|
+
right: number;
|
|
21
|
+
bottom: number;
|
|
22
|
+
left: number;
|
|
23
|
+
};
|
|
24
|
+
viewbox: {
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
};
|
|
28
|
+
data: import("@/hooks/use-graph/use-graph").XYDataset | import("@/hooks/use-graph/use-graph").SegmentDataset;
|
|
29
|
+
domain: {
|
|
30
|
+
x: Array<{
|
|
31
|
+
coordinate: number;
|
|
32
|
+
tick: string | number | Date;
|
|
33
|
+
}>;
|
|
34
|
+
y: Array<{
|
|
35
|
+
coordinate: number;
|
|
36
|
+
tick: string | number | Date;
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
interactions: {
|
|
40
|
+
hovered: string[];
|
|
41
|
+
pinned: string[];
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useGraph, useGraphColumn } from "@/hooks/use-graph/use-graph";
|
|
3
|
+
import { Graph } from "../Graph/Graph";
|
|
4
|
+
import { ColorUtils } from "@/utils/color/color";
|
|
5
|
+
import { cx } from "@/utils/cx/cx";
|
|
6
|
+
export const Legend = ({ position = "top", alignment = "center" }) => {
|
|
7
|
+
var _a;
|
|
8
|
+
const context = useGraph();
|
|
9
|
+
const Element = position === "top" || position === "bottom" ? Graph.Row : Graph.Column;
|
|
10
|
+
const column = useGraphColumn(context);
|
|
11
|
+
return (<Element className={cx("flex", "gap-2", (position === "left" || position === "right") && "flex-col", position === "right" && "pl-4", position === "left" && "pr-4", alignment === "start" && "justify-start", alignment === "end" && "justify-end", alignment === "center" && "justify-center")} style={position === "top" || position === "bottom" ? { gridColumn: column } : undefined}>
|
|
12
|
+
{(_a = context.data) === null || _a === void 0 ? void 0 : _a.map(({ name, stroke }, i, dps) => {
|
|
13
|
+
return (<div key={i} className={"flex items-center"}>
|
|
14
|
+
<div className={"size-4 mr-1 rounded-full"} style={{ background: stroke !== null && stroke !== void 0 ? stroke : ColorUtils.colorFor(i, dps.length) }}/>
|
|
15
|
+
<div className={"text-nowrap"}>{name}</div>
|
|
16
|
+
</div>);
|
|
17
|
+
})}
|
|
18
|
+
</Element>);
|
|
19
|
+
};
|
|
20
|
+
Legend.context = (ctx, props) => {
|
|
21
|
+
const rows = (() => {
|
|
22
|
+
if (props.position === "top")
|
|
23
|
+
return "max-content " + ctx.layout.rows;
|
|
24
|
+
if (props.position === "bottom")
|
|
25
|
+
return ctx.layout.rows + " max-content";
|
|
26
|
+
return ctx.layout.rows;
|
|
27
|
+
})();
|
|
28
|
+
const columns = (() => {
|
|
29
|
+
if (props.position === "left")
|
|
30
|
+
return "max-content " + ctx.layout.columns;
|
|
31
|
+
if (props.position === "right")
|
|
32
|
+
return ctx.layout.columns + " max-content";
|
|
33
|
+
return ctx.layout.columns;
|
|
34
|
+
})();
|
|
35
|
+
return Object.assign(Object.assign({}, ctx), { layout: Object.assign(Object.assign({}, ctx.layout), { rows,
|
|
36
|
+
columns }) });
|
|
37
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const LinearGradient = ({ id, gradient }) => {
|
|
2
|
+
const parseDirection = (direction) => {
|
|
3
|
+
if (direction.includes("to ")) {
|
|
4
|
+
switch (direction.replace("to ", "").trim()) {
|
|
5
|
+
case "top":
|
|
6
|
+
return { x1: 0, y1: 1, x2: 0, y2: 0 };
|
|
7
|
+
case "right":
|
|
8
|
+
return { x1: 0, y1: 0, x2: 1, y2: 0 };
|
|
9
|
+
case "bottom":
|
|
10
|
+
return { x1: 0, y1: 0, x2: 0, y2: 1 };
|
|
11
|
+
case "left":
|
|
12
|
+
return { x1: 1, y1: 0, x2: 0, y2: 0 };
|
|
13
|
+
case "top left":
|
|
14
|
+
return { x1: 1, y1: 1, x2: 0, y2: 0 };
|
|
15
|
+
case "top right":
|
|
16
|
+
return { x1: 0, y1: 1, x2: 1, y2: 0 };
|
|
17
|
+
case "bottom left":
|
|
18
|
+
return { x1: 1, y1: 0, x2: 0, y2: 1 };
|
|
19
|
+
case "bottom right":
|
|
20
|
+
return { x1: 0, y1: 0, x2: 1, y2: 1 };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else if (direction.match(/\d+deg/)) {
|
|
24
|
+
const angle = parseFloat(direction) * (Math.PI / 180);
|
|
25
|
+
return {
|
|
26
|
+
x1: 0.5 - 0.5 * Math.cos(angle),
|
|
27
|
+
y1: 0.5 - 0.5 * Math.sin(angle),
|
|
28
|
+
x2: 0.5 + 0.5 * Math.cos(angle),
|
|
29
|
+
y2: 0.5 + 0.5 * Math.sin(angle),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return { x1: 0, y1: 0, x2: 0, y2: 1 }; // Default to "to bottom"
|
|
33
|
+
};
|
|
34
|
+
const parseStops = (stops) => stops.map((stop, i, arr) => {
|
|
35
|
+
const [color, offset] = stop.trim().split(/\s+(?![^()]*\))/);
|
|
36
|
+
return {
|
|
37
|
+
color,
|
|
38
|
+
offset: offset || (i === 0 ? "0%" : i === arr.length - 1 ? "100%" : undefined),
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
const { x1, y1, x2, y2, stops } = (() => {
|
|
42
|
+
const match = gradient.match(/linear-gradient\(([^)]+)\)/);
|
|
43
|
+
if (!match)
|
|
44
|
+
throw new Error("Invalid gradient string");
|
|
45
|
+
const parts = match[1].split(/,(?![^()]*\))/).map((p) => p.trim());
|
|
46
|
+
const direction = parts[0].match(/^(to|\d+deg|\d+rad|\d+turn)/) ? parts.shift() : "to bottom";
|
|
47
|
+
return Object.assign(Object.assign({}, parseDirection(direction)), { stops: parseStops(parts) });
|
|
48
|
+
})();
|
|
49
|
+
return (<linearGradient id={id} x1={x1} y1={y1} x2={x2} y2={y2}>
|
|
50
|
+
{stops.map(({ color, offset }, i) => (<stop key={i} stopColor={color} offset={offset}/>))}
|
|
51
|
+
</linearGradient>);
|
|
52
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
import { CurveUtils } from "@/utils/path/curve";
|
|
3
|
+
interface Props extends React.SVGAttributes<SVGSVGElement> {
|
|
4
|
+
children?: ReactNode;
|
|
5
|
+
curve?: keyof typeof CurveUtils;
|
|
6
|
+
loading?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare const Lines: {
|
|
9
|
+
({ className, curve, children, loading }: Props): React.JSX.Element | null;
|
|
10
|
+
Tooltip: ({ tooltip, joints }: {
|
|
11
|
+
tooltip?: (points: Array<Omit<import("@/hooks/use-graph/use-graph").XYDataset[number], "data"> & {
|
|
12
|
+
data: import("@/hooks/use-graph/use-graph").XYDataset[number]["data"][number];
|
|
13
|
+
}>, x: number | string | Date) => React.ReactNode;
|
|
14
|
+
joints?: boolean;
|
|
15
|
+
}) => React.JSX.Element | null;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useGraph } from "@/hooks/use-graph/use-graph";
|
|
3
|
+
import { CoordinatesUtils } from "@/utils/coordinates/coordinates";
|
|
4
|
+
import { GraphUtils } from "@/utils/graph/graph";
|
|
5
|
+
import { ColorUtils } from "@/utils/color/color";
|
|
6
|
+
import { cx } from "@/utils/cx/cx";
|
|
7
|
+
import { LinesTooltip } from "@/components/Lines/components/LinesTooltip";
|
|
8
|
+
import { LinesLoading } from "@/components/Lines/components/LinesLoading";
|
|
9
|
+
import { CurveUtils } from "@/utils/path/curve";
|
|
10
|
+
export const Lines = ({ className, curve = "linear", children, loading }) => {
|
|
11
|
+
const { interactions: { pinned, hovered }, data, viewbox, domain, } = useGraph();
|
|
12
|
+
if (!GraphUtils.isXYData(data))
|
|
13
|
+
return null;
|
|
14
|
+
const xForValue = CoordinatesUtils.xCoordinateFor({ domain, viewbox });
|
|
15
|
+
const yForValue = CoordinatesUtils.yCoordinateFor({ domain, viewbox });
|
|
16
|
+
const lines = data.map((line, i, lines) => {
|
|
17
|
+
var _a, _b, _c;
|
|
18
|
+
return Object.assign(Object.assign({}, line), { id: (_a = line.id) !== null && _a !== void 0 ? _a : line.name, stroke: (_b = line.stroke) !== null && _b !== void 0 ? _b : ColorUtils.colorFor(i, lines.length), fill: line.fill === true ? ((_c = line.stroke) !== null && _c !== void 0 ? _c : ColorUtils.colorFor(i, lines.length)) : line.fill, data: line.data.map((xy) => ({
|
|
19
|
+
x: xForValue(xy.x),
|
|
20
|
+
y: yForValue(xy.y),
|
|
21
|
+
})) });
|
|
22
|
+
});
|
|
23
|
+
if (loading) {
|
|
24
|
+
return <LinesLoading />;
|
|
25
|
+
}
|
|
26
|
+
return (<svg viewBox={`0 0 ${viewbox.x} ${viewbox.y}`} height={"100%"} width={"100%"} preserveAspectRatio={"none"} className={cx("[grid-area:graph]", className)}>
|
|
27
|
+
{lines.map(({ id, stroke, data, fill }, i) => {
|
|
28
|
+
const path = CurveUtils[curve](data);
|
|
29
|
+
const disabled = pinned.length && !pinned.includes(id) && !hovered.includes(id);
|
|
30
|
+
const filled = fill || hovered.includes(id) || (pinned.includes(id) && !disabled);
|
|
31
|
+
const identifier = id.replace(/[^a-zA-Z0-9]/g, "");
|
|
32
|
+
return (<React.Fragment key={i}>
|
|
33
|
+
{filled && !disabled && (<linearGradient id={identifier} x1="0" y1="0" x2="0" y2="1">
|
|
34
|
+
<stop offset="5%" stopColor={stroke} stopOpacity={"0.5"}/>
|
|
35
|
+
<stop offset="95%" stopColor={stroke} stopOpacity={"0"}/>
|
|
36
|
+
</linearGradient>)}
|
|
37
|
+
<path key={i} d={path} fill={"transparent"} stroke={stroke} className={cx(disabled && "stroke-black dark:stroke-white [stroke-opacity:0.1] lines__outlined")} vectorEffect={"non-scaling-stroke"} strokeWidth={1.5}/>
|
|
38
|
+
{filled && (<path d={path + `L ${viewbox.x} ${viewbox.y} L 0 ${viewbox.y} Z`} stroke={stroke} fill={(() => {
|
|
39
|
+
if (typeof fill === "string")
|
|
40
|
+
return fill;
|
|
41
|
+
if (filled)
|
|
42
|
+
return `url(#${identifier})`;
|
|
43
|
+
return undefined;
|
|
44
|
+
})()} strokeOpacity={0} className="lines__filled"/>)}
|
|
45
|
+
</React.Fragment>);
|
|
46
|
+
})}
|
|
47
|
+
{children}
|
|
48
|
+
</svg>);
|
|
49
|
+
};
|
|
50
|
+
/*
|
|
51
|
+
Chart composition
|
|
52
|
+
*/
|
|
53
|
+
Lines.Tooltip = LinesTooltip;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useGraph } from "@/hooks/use-graph/use-graph";
|
|
3
|
+
export const LinesLoading = () => {
|
|
4
|
+
const { viewbox } = useGraph();
|
|
5
|
+
return (<svg viewBox={`0 0 ${viewbox.x} ${viewbox.y}`} height={"100%"} width={"100%"} preserveAspectRatio={"none"} className={"[grid-area:graph] stroke-gray-600"}>
|
|
6
|
+
<path d={`M ${viewbox.x * 0.05} ${viewbox.y * 0.95} L ${viewbox.x * 0.2167} ${viewbox.y * 0.55} L ${viewbox.x * 0.35} ${viewbox.y * 0.75} L ${viewbox.x * 0.55} ${viewbox.y * 0.2833} L ${viewbox.x * 0.7167} ${viewbox.y * 0.5167} L ${viewbox.x * 0.95} ${viewbox.y * 0.05}`} strokeWidth={5} stroke={"stroke-gray-600"} strokeLinecap={"round"} fill="none" vectorEffect={"non-scaling-stroke"}>
|
|
7
|
+
<animate attributeName="stroke" values="#4b5563; #374151; #4b5563; #4b5563;" dur="2s" repeatCount="indefinite" calcMode="spline" keyTimes="0; 0.3; 0.6; 1" keySplines="0.15 0.25 0.25 0.15; 0.15 0.25 0.25 0.15; 0 0 0 0"/>
|
|
8
|
+
</path>
|
|
9
|
+
</svg>);
|
|
10
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { XYDataset } from "@/hooks/use-graph/use-graph";
|
|
3
|
+
type Props = {
|
|
4
|
+
tooltip?: (points: Array<Omit<XYDataset[number], "data"> & {
|
|
5
|
+
data: XYDataset[number]["data"][number];
|
|
6
|
+
}>, x: number | string | Date) => React.ReactNode;
|
|
7
|
+
joints?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare const LinesTooltip: ({ tooltip, joints }: Props) => React.JSX.Element | null;
|
|
10
|
+
export {};
|