@funnest/charts-react 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ import { formatNumber } from './context';
2
+ export interface ReferenceLineProps {
3
+ /** Horizontal line at this value (target price, average entry, 0%). */
4
+ dataY?: number;
5
+ /** Vertical line at this x index (event, earnings date). */
6
+ dataX?: number;
7
+ label?: string;
8
+ /** Where the label sits along the line. Default 'right' (horizontal) / 'top' (vertical). */
9
+ labelPosition?: 'left' | 'right' | 'center' | 'top' | 'bottom';
10
+ color?: string;
11
+ /** Dash pattern; '' for a solid line. Default '4 4'. */
12
+ dash?: string;
13
+ strokeWidth?: number;
14
+ }
15
+ export declare function ReferenceLine({ dataY, dataX, label, labelPosition, color, dash, strokeWidth }: ReferenceLineProps): import("react").JSX.Element | null;
16
+ export interface PointProps {
17
+ /** X index. */
18
+ dataX: number;
19
+ /** Value. Omit with `seriesId` to sit on that series. */
20
+ dataY?: number;
21
+ seriesId?: string;
22
+ color?: string;
23
+ radius?: number;
24
+ /** Text next to the point (e.g. "매수", "고점 12,400"). */
25
+ label?: string;
26
+ labelPosition?: 'top' | 'bottom' | 'left' | 'right';
27
+ /** Live-price beacon. */
28
+ pulse?: boolean;
29
+ onClick?: () => void;
30
+ }
31
+ /** A marker at a data position: trades on a price line, highs/lows, the latest value. */
32
+ export declare function Point({ dataX, dataY, seriesId, color, radius, label, labelPosition, pulse, onClick }: PointProps): import("react").JSX.Element | null;
33
+ /** Label for a value in the default number format (exported for custom annotations). */
34
+ export declare const formatValue: typeof formatNumber;
@@ -0,0 +1,43 @@
1
+ export interface XAxisProps {
2
+ position?: 'bottom' | 'top';
3
+ /** Space reserved for the axis. Default 24. */
4
+ height?: number;
5
+ /** Indices to label. Default: evenly spaced, about one label per 80px. */
6
+ ticks?: readonly number[];
7
+ /** Label for x position `index` (value is `xAxis.data[index]`, if any). */
8
+ tickLabelFormatter?: (value: string | number | undefined, index: number) => string;
9
+ showLine?: boolean;
10
+ showTickMarks?: boolean;
11
+ /** Vertical grid lines at each tick. */
12
+ showGrid?: boolean;
13
+ /** Minimum pixels per label when picking ticks automatically. Default 80. */
14
+ minTickSpacing?: number;
15
+ }
16
+ export declare function XAxis({ position, height, ticks, tickLabelFormatter, showLine, showTickMarks, showGrid, minTickSpacing, }: XAxisProps): import("react").JSX.Element | null;
17
+ export declare namespace XAxis {
18
+ var axisSpace: (p: XAxisProps) => {
19
+ side: "bottom" | "top";
20
+ size: number;
21
+ };
22
+ }
23
+ export interface YAxisProps {
24
+ /** Right is the trading-chart convention. Default 'right'. */
25
+ position?: 'left' | 'right';
26
+ /** Space reserved for labels. Default 48. */
27
+ width?: number;
28
+ /** Explicit tick values. Default: nice numbers, about one per 40px. */
29
+ ticks?: readonly number[];
30
+ tickLabelFormatter?: (value: number) => string;
31
+ /** Horizontal grid lines across the plot. Default true. */
32
+ showGrid?: boolean;
33
+ showLine?: boolean;
34
+ /** Minimum pixels between ticks when picking automatically. Default 32. */
35
+ minTickSpacing?: number;
36
+ }
37
+ export declare function YAxis({ position, width, ticks, tickLabelFormatter, showGrid, showLine, minTickSpacing, }: YAxisProps): import("react").JSX.Element;
38
+ export declare namespace YAxis {
39
+ var axisSpace: (p: YAxisProps) => {
40
+ side: "left" | "right";
41
+ size: number;
42
+ };
43
+ }
@@ -0,0 +1,17 @@
1
+ export interface BarPlotProps {
2
+ /** Only draw these series (default: all). */
3
+ seriesIds?: readonly string[];
4
+ /** Corner radius at the bar's far end. Default 3. */
5
+ radius?: number;
6
+ /** Gap between grouped bars in one slot, px. Default 2. */
7
+ groupGap?: number;
8
+ /** Per-bar color (e.g. up/down volume). Return undefined to use the series color. */
9
+ getBarColor?: (value: number, index: number, seriesId: string) => string | undefined;
10
+ /** Dim bars other than the scrubbed one. Default true. */
11
+ highlightScrubbed?: boolean;
12
+ }
13
+ /**
14
+ * Bars for every series: side by side (grouped) or on top of each other when the chart
15
+ * is `stacked`. Needs `xAxis.scaleType: 'band'` (BarChart sets it).
16
+ */
17
+ export declare function BarPlot({ seriesIds, radius, groupGap, getBarColor, highlightScrubbed }: BarPlotProps): import("react").JSX.Element | null;
@@ -0,0 +1,64 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ import { type ChartSeries } from './context';
3
+ import { type LegendPosition } from './legend';
4
+ export type Inset = number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>;
5
+ export interface XAxisConfig {
6
+ /** Labels or timestamps per x position (for axis labels, scrubber and legend). */
7
+ data?: readonly (string | number)[];
8
+ /** 'band' gives each x position a slot (bar charts); 'linear' spreads points edge to edge. */
9
+ scaleType?: 'linear' | 'band';
10
+ /** Band padding between bars, 0–0.95. Default 0.25. */
11
+ bandPadding?: number;
12
+ }
13
+ export interface YAxisConfig {
14
+ /** Fix either end of the value range; the other end follows the data. */
15
+ domain?: {
16
+ min?: number;
17
+ max?: number;
18
+ };
19
+ /** Always include 0 (default for bar charts). */
20
+ includeZero?: boolean;
21
+ /** Round the range outward to nice numbers. Default false (use the data range). */
22
+ nice?: boolean;
23
+ }
24
+ export interface CartesianChartProps {
25
+ series: readonly ChartSeries[];
26
+ /** Pixel width. Omit to fill the parent (measured with ResizeObserver). */
27
+ width?: number;
28
+ /** Pixel height of the drawing area. Default 240. */
29
+ height?: number;
30
+ /** Space between the SVG edge and the plot (axes add their own space). Default 8. */
31
+ inset?: Inset;
32
+ xAxis?: XAxisConfig;
33
+ yAxis?: YAxisConfig;
34
+ /** Stack the series (areas / bars). */
35
+ stacked?: boolean;
36
+ /** Track the pointer and expose the hovered index to <Scrubber>, legends and headers. */
37
+ enableScrubbing?: boolean;
38
+ onScrubberPositionChange?: (index: number | null) => void;
39
+ /** Draw-in animation on mount. Default true (off when the user prefers reduced motion). */
40
+ animate?: boolean;
41
+ /** Series legend outside the plot. */
42
+ legend?: boolean | LegendPosition;
43
+ /** Format legend values while scrubbing. */
44
+ legendValueFormatter?: (value: number, seriesId: string) => string;
45
+ /** Override the categorical palette. */
46
+ palette?: readonly string[];
47
+ className?: string;
48
+ style?: CSSProperties;
49
+ 'aria-label'?: string;
50
+ children?: ReactNode;
51
+ }
52
+ /** Axis components declare the space they need so the plot can shrink before the first paint. */
53
+ export interface AxisSpace {
54
+ side: 'top' | 'right' | 'bottom' | 'left';
55
+ size: number;
56
+ }
57
+ /**
58
+ * The base of every x/y chart. Computes scales from `series` and provides them to its
59
+ * children (Line, Area, BarPlot, XAxis, YAxis, ReferenceLine, Point, Scrubber), which draw
60
+ * themselves as SVG. Compose your own chart from those parts, or use LineChart / AreaChart /
61
+ * BarChart for the common layouts.
62
+ */
63
+ export declare function CartesianChart({ series, width: fixedWidth, height, inset, xAxis, yAxis, stacked, enableScrubbing, onScrubberPositionChange, animate, legend, legendValueFormatter, palette, className, style, 'aria-label': ariaLabel, children, }: CartesianChartProps): import("react").JSX.Element;
64
+ export declare const DEFAULT_CHART_HEIGHT = 240;
@@ -0,0 +1,68 @@
1
+ import { type Curve } from '@funnest/charts-viz';
2
+ import { type CSSProperties, type ReactNode } from 'react';
3
+ import { type XAxisProps, type YAxisProps } from './axes';
4
+ import { type BarPlotProps } from './bar';
5
+ import { type CartesianChartProps } from './cartesian-chart';
6
+ import { type AreaFill } from './line';
7
+ import { type ScrubberProps } from './scrubber';
8
+ type AxisToggle<P> = boolean | P;
9
+ interface PresetProps extends Omit<CartesianChartProps, 'children'> {
10
+ showXAxis?: AxisToggle<XAxisProps>;
11
+ showYAxis?: AxisToggle<YAxisProps>;
12
+ /** Pointer-follow line + dots (turns on `enableScrubbing`). */
13
+ scrubber?: AxisToggle<ScrubberProps>;
14
+ /** Extra parts drawn on top: ReferenceLine, Point, … */
15
+ children?: ReactNode;
16
+ }
17
+ export interface LineChartProps extends PresetProps {
18
+ curve?: Curve;
19
+ strokeWidth?: number;
20
+ /** Fill under each line. Default 'none'. */
21
+ area?: AreaFill;
22
+ connectNulls?: boolean;
23
+ }
24
+ /**
25
+ * Lines for every series. With no axes it is a sparkline:
26
+ * `<LineChart series={[{ id: 'p', data }]} height={40} inset={2} />`.
27
+ */
28
+ export declare function LineChart({ showXAxis, showYAxis, scrubber, curve, strokeWidth, area, connectNulls, children, ...chart }: LineChartProps): import("react").JSX.Element;
29
+ export interface AreaChartProps extends PresetProps {
30
+ curve?: Curve;
31
+ fill?: Exclude<AreaFill, 'none'>;
32
+ /** Draw the top edge line. Default true. */
33
+ showLines?: boolean;
34
+ strokeWidth?: number;
35
+ }
36
+ /** Filled areas; set `stacked` for a composition over time (e.g. portfolio by asset). */
37
+ export declare function AreaChart({ showXAxis, showYAxis, scrubber, curve, fill, showLines, strokeWidth, children, ...chart }: AreaChartProps): import("react").JSX.Element;
38
+ export interface BarChartProps extends PresetProps, Pick<BarPlotProps, 'radius' | 'getBarColor' | 'groupGap'> {
39
+ }
40
+ /** Grouped (or `stacked`) bars on a categorical x axis. Hover highlights a bar when `scrubber`. */
41
+ export declare function BarChart({ showXAxis, showYAxis, scrubber, radius, getBarColor, groupGap, xAxis, children, ...chart }: BarChartProps): import("react").JSX.Element;
42
+ export interface PercentageBarItem {
43
+ id: string;
44
+ label: string;
45
+ value: number;
46
+ color?: string;
47
+ }
48
+ export interface PercentageBarChartProps {
49
+ data: readonly PercentageBarItem[];
50
+ /** Bar thickness. Default 10. */
51
+ barHeight?: number;
52
+ /** Gap between segments, px. Default 2. */
53
+ gap?: number;
54
+ /** Labels with percentages under the bar. Default true. */
55
+ showLabels?: boolean;
56
+ /** Format the label value. Default: "62.5%". */
57
+ formatLabel?: (item: PercentageBarItem, fraction: number) => string;
58
+ palette?: readonly string[];
59
+ className?: string;
60
+ style?: CSSProperties;
61
+ 'aria-label'?: string;
62
+ }
63
+ /**
64
+ * One horizontal bar split into shares: portfolio weights, buy/sell pressure,
65
+ * long/short ratio.
66
+ */
67
+ export declare function PercentageBarChart({ data, barHeight, gap, showLabels, formatLabel, palette, className, style, 'aria-label': ariaLabel, }: PercentageBarChartProps): import("react").JSX.Element;
68
+ export {};
@@ -0,0 +1,65 @@
1
+ import type { BandScale, LinearScale, StackedSeries } from '@funnest/charts-viz';
2
+ export interface ChartSeries {
3
+ id: string;
4
+ /** Values per x position; null = gap. */
5
+ data: readonly (number | null)[];
6
+ label?: string;
7
+ /** Any CSS color. Defaults to the palette. */
8
+ color?: string;
9
+ }
10
+ /** A series with its resolved color. */
11
+ export interface ResolvedSeries extends ChartSeries {
12
+ color: string;
13
+ index: number;
14
+ }
15
+ export interface PlotRect {
16
+ left: number;
17
+ top: number;
18
+ right: number;
19
+ bottom: number;
20
+ width: number;
21
+ height: number;
22
+ }
23
+ export interface CartesianContextValue {
24
+ /** Unique per chart, for SVG ids (gradients, clip paths). */
25
+ id: string;
26
+ width: number;
27
+ height: number;
28
+ plot: PlotRect;
29
+ series: ResolvedSeries[];
30
+ getSeries(id: string): ResolvedSeries | undefined;
31
+ /** Number of x positions. */
32
+ length: number;
33
+ /** Pixel x of x position `i` (band center for bar charts). */
34
+ x(i: number): number;
35
+ /** Set when the x axis is categorical (bar charts). */
36
+ band: BandScale | null;
37
+ y: LinearScale;
38
+ /** Stacked edges per series id when `stacked`. */
39
+ stacked: Map<string, StackedSeries> | null;
40
+ /** Label for x position `i` (from `xAxis.data`, else the index). */
41
+ xLabel(i: number): string;
42
+ xData: readonly (string | number)[] | undefined;
43
+ /** Index under the pointer, or null. */
44
+ scrubIndex: number | null;
45
+ animate: boolean;
46
+ }
47
+ export declare const CartesianContext: import("react").Context<CartesianContextValue | null>;
48
+ export declare function useCartesianChart(): CartesianContextValue;
49
+ /**
50
+ * Visual tokens. Override with CSS variables on any ancestor. The fallbacks use the CSS
51
+ * system colors (Canvas / CanvasText), so charts follow the page's light/dark
52
+ * `color-scheme` with no setup.
53
+ */
54
+ export declare const VAR: {
55
+ readonly text: 'var(--fnv-text-muted, color-mix(in srgb, CanvasText 50%, Canvas))';
56
+ readonly textStrong: 'var(--fnv-text, CanvasText)';
57
+ readonly grid: 'var(--fnv-grid, color-mix(in srgb, CanvasText 9%, transparent))';
58
+ readonly line: 'var(--fnv-line, color-mix(in srgb, CanvasText 32%, transparent))';
59
+ readonly bg: 'var(--fnv-bg, Canvas)';
60
+ readonly font: 'var(--fnv-font, inherit)';
61
+ };
62
+ /** Default number display: full digits up to 100k, compact (1.2M, 34만) beyond. */
63
+ export declare const formatNumber: (v: number) => string;
64
+ /** Pixel y of series `s` at index `i` (top edge when stacked), or null for a gap. */
65
+ export declare function seriesY(ctx: CartesianContextValue, s: ResolvedSeries, i: number): number | null;
@@ -0,0 +1,10 @@
1
+ export { CartesianChart, type CartesianChartProps, type XAxisConfig, type YAxisConfig, type Inset } from './cartesian-chart';
2
+ export { XAxis, YAxis, type XAxisProps, type YAxisProps } from './axes';
3
+ export { Line, Area, type LineProps, type AreaProps, type AreaFill } from './line';
4
+ export { BarPlot, type BarPlotProps } from './bar';
5
+ export { Scrubber, type ScrubberProps } from './scrubber';
6
+ export { ReferenceLine, Point, type ReferenceLineProps, type PointProps } from './annotations';
7
+ export { Legend, type LegendProps, type LegendItem, type LegendPosition } from './legend';
8
+ export { PeriodSelector, DEFAULT_PERIODS, type PeriodSelectorProps, type Period } from './period-selector';
9
+ export { LineChart, AreaChart, BarChart, PercentageBarChart, type LineChartProps, type AreaChartProps, type BarChartProps, type PercentageBarChartProps, type PercentageBarItem, } from './charts';
10
+ export { useCartesianChart, formatNumber, type ChartSeries, type CartesianContextValue, type PlotRect } from './context';
@@ -0,0 +1,24 @@
1
+ import type { CSSProperties } from 'react';
2
+ export type LegendPosition = 'top' | 'bottom';
3
+ export interface LegendItem {
4
+ id: string;
5
+ label: string;
6
+ color: string;
7
+ /** Shown after the label (e.g. the scrubbed value). */
8
+ value?: string | undefined;
9
+ /** Rendered dimmed (series hidden). */
10
+ inactive?: boolean;
11
+ }
12
+ export interface LegendProps {
13
+ items: readonly LegendItem[];
14
+ /** Click to toggle a series; omit for a static legend. */
15
+ onItemClick?: (id: string) => void;
16
+ /** Pointer enters (id) or leaves (null) an item — e.g. to highlight that series. */
17
+ onItemHover?: (id: string | null) => void;
18
+ /** 'circle' (default), 'square' or 'line' swatches. */
19
+ shape?: 'circle' | 'square' | 'line';
20
+ className?: string;
21
+ style?: CSSProperties;
22
+ }
23
+ /** Series key, HTML (not SVG) so it wraps and stays readable at any width. */
24
+ export declare function Legend({ items, onItemClick, onItemHover, shape, className, style }: LegendProps): import("react").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { type Curve, type XY } from '@funnest/charts-viz';
2
+ import { type CartesianContextValue, type ResolvedSeries } from './context';
3
+ export type AreaFill = 'gradient' | 'solid' | 'none';
4
+ /** Pixel points of a series split at gaps (or joined across them with `connectNulls`). */
5
+ export declare function seriesRuns(ctx: CartesianContextValue, s: ResolvedSeries, connectNulls?: boolean): {
6
+ upper: XY[];
7
+ lower: {
8
+ x: number;
9
+ y: number;
10
+ }[] | null;
11
+ }[];
12
+ export interface AreaProps {
13
+ seriesId: string;
14
+ curve?: Curve;
15
+ fill?: Exclude<AreaFill, 'none'>;
16
+ /** Fill opacity at the line (gradient fades to 0). Default 0.24 (solid 0.16). */
17
+ opacity?: number;
18
+ connectNulls?: boolean;
19
+ }
20
+ /** Filled area under a series (between stacked edges when the chart is stacked). */
21
+ export declare function Area({ seriesId, curve, fill, opacity, connectNulls }: AreaProps): import("react").JSX.Element | null;
22
+ export interface LineProps {
23
+ seriesId: string;
24
+ curve?: Curve;
25
+ strokeWidth?: number;
26
+ /** Dashed line, e.g. "4 4". */
27
+ dash?: string;
28
+ /** Draw across null gaps instead of breaking. */
29
+ connectNulls?: boolean;
30
+ /** Also fill underneath. Default 'none'. */
31
+ area?: AreaFill;
32
+ areaOpacity?: number;
33
+ opacity?: number;
34
+ }
35
+ export declare function Line({ seriesId, curve, strokeWidth, dash, connectNulls, area, areaOpacity, opacity, }: LineProps): import("react").JSX.Element | null;
@@ -0,0 +1,21 @@
1
+ import { type CSSProperties } from 'react';
2
+ export interface Period {
3
+ id: string;
4
+ label: string;
5
+ }
6
+ /** Common ranges for price charts. */
7
+ export declare const DEFAULT_PERIODS: readonly Period[];
8
+ export interface PeriodSelectorProps {
9
+ periods?: readonly Period[];
10
+ value: string;
11
+ onChange: (id: string) => void;
12
+ /** Active pill color. Default Funnest violet (set --fnv-accent to theme). */
13
+ activeColor?: string;
14
+ /** Stretch the tabs across the full width (mobile). */
15
+ fullWidth?: boolean;
16
+ className?: string;
17
+ style?: CSSProperties;
18
+ 'aria-label'?: string;
19
+ }
20
+ /** Range tabs (1D 1W 1M …) with a sliding pill; arrow keys move between tabs. */
21
+ export declare function PeriodSelector({ periods, value, onChange, activeColor, fullWidth, className, style, 'aria-label': ariaLabel, }: PeriodSelectorProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ export interface ScrubberProps {
2
+ /** Text above the scrub line. Default: the x label (`xAxis.data[index]`). Return '' to hide. */
3
+ label?: (index: number) => string;
4
+ /** Only mark these series (default: all). */
5
+ seriesIds?: readonly string[];
6
+ /** Dim the part of each line right of the scrubber. Default true. */
7
+ dimFuture?: boolean;
8
+ /** Tooltip with each series' value at the scrubbed index. Default false. */
9
+ showValues?: boolean;
10
+ /** Format tooltip values. */
11
+ valueFormatter?: (value: number, seriesId: string) => string;
12
+ }
13
+ /**
14
+ * Pointer-follow overlay: vertical line, a dot on each series and a label. Requires
15
+ * `enableScrubbing` on the chart. Read the index yourself with `onScrubberPositionChange`
16
+ * to drive a header (price / change at that point).
17
+ */
18
+ export declare function Scrubber({ label, seriesIds, dimFuture, showValues, valueFormatter }: ScrubberProps): import("react").JSX.Element | null;
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@funnest/charts-react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "React bindings for Funnest Charts: the trading widget plus SVG line, area and bar charts",
5
+ "keywords": [
6
+ "react",
7
+ "chart",
8
+ "trading",
9
+ "candlestick",
10
+ "financial",
11
+ "line-chart",
12
+ "area-chart",
13
+ "bar-chart",
14
+ "sparkline",
15
+ "svg"
16
+ ],
17
+ "license": "Apache-2.0",
18
+ "author": "Funnest",
19
+ "homepage": "https://charts.funnest.xyz",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/funnest-xyz/funnest-charts.git",
23
+ "directory": "packages/react"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/funnest-xyz/funnest-charts/issues"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "type": "module",
32
+ "sideEffects": false,
33
+ "main": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "funnest-source": "./src/index.ts",
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md",
45
+ "LICENSE",
46
+ "NOTICE"
47
+ ],
48
+ "dependencies": {
49
+ "@funnest/charts": "0.1.0",
50
+ "@funnest/charts-viz": "0.1.0",
51
+ "@funnest/charts-ui": "0.1.0"
52
+ },
53
+ "peerDependencies": {
54
+ "react": ">=18",
55
+ "react-dom": ">=18"
56
+ },
57
+ "devDependencies": {
58
+ "@types/react": "^19.3.0",
59
+ "@types/react-dom": "^19.3.0",
60
+ "react": "^19.3.1",
61
+ "react-dom": "^19.3.1"
62
+ },
63
+ "scripts": {
64
+ "build": "tsup src/index.ts --format esm --clean --external react --external react-dom --external @funnest/charts-ui --external @funnest/charts --external @funnest/charts-viz && tsc -p tsconfig.build.json",
65
+ "typecheck": "tsc -p tsconfig.json"
66
+ }
6
67
  }