Skip to content

Documentation / @finchart/core / index / Plot

Class: Plot ​

Defined in: packages/core/src/plot/plot.ts:149

The chart. Owns the layers, scales, grid, and interaction.

Data representation belongs to Series, swapped out with setSeries. Swapping it leaves everything here alone — overlay annotations, pan/zoom position, all of it.

The implements list is long. That's the point — an extension doesn't require this class, only whichever of the capabilities below it actually uses, and this list is the complete answer to "what does the chart lend out" (see capabilities.ts). The compiler keeps the pairing honest.

Implements ​

Constructors ​

Constructor ​

new Plot(options): Plot

Defined in: packages/core/src/plot/plot.ts:266

Parameters ​

options ​

PlotOptions

Returns ​

Plot

Accessors ​

mainPane ​

Get Signature ​

get mainPane(): Pane

Defined in: packages/core/src/plot/plot.ts:198

The default pane that holds series. Always exists.

If you never create another pane, every series lands here, so having one pane matches the library's original behavior exactly.

Only a narrow face is exposed outward — the frame wiring (setArea, draw, …) belongs to the chart → PaneApi

Returns ​

Pane

The default pane holding series. Always present.

Implementation of ​

PaneHost.mainPane


maximizedPane ​

Get Signature ​

get maximizedPane(): Pane | null

Defined in: packages/core/src/plot/plot.ts:681

The pane that fills the chart, or null when the panes share it by their own flex → maximizePane.

Returns ​

Pane | null

The pane that fills the chart, or null → Plot.maximizePane.

Implementation of ​

PaneHost.maximizedPane


overlay ​

Get Signature ​

get overlay(): unknown

Defined in: packages/core/src/plot/plot.ts:825

The DOM layer that annotations and tooltips mount on. Whatever is attached here survives a canvas redraw or a series swap.

The core doesn't know what this is — it hands out whatever the layers supplied (ChartLayers.overlay) as-is. A DOM consumer narrows it with requireOverlayElement. A headless chart has none (null) — no DOM, nowhere to mount.

Returns ​

unknown

Implementation of ​

OverlayHost.overlay


panes ​

Get Signature ​

get panes(): readonly Pane[]

Defined in: packages/core/src/plot/plot.ts:440

Stacking order, top to bottom.

This is a copy. readonly is only a compiler promise — handing out the original lets splice work at runtime, and that array is the very list the chart draws from. Pulling a pane out from outside would remove it with no detach, no unsubscribe, so an extension's dispose would never be called. The only doors that change the list are addPane and removePane.

Returns ​

readonly Pane[]

Stacking order from the top.

Implementation of ​

PaneHost.panes

Methods ​

addDecoration() ​

addDecoration(decoration, options?): () => void

Defined in: packages/core/src/plot/plot.ts:503

Mounts a decoration over the whole chart. Anything that doesn't need a value axis comes here — crosshair, range shading, watermark.

If y is needed, use pane.addDecoration — x belongs to Plot, y belongs to Pane.

Parameters ​

decoration ​

PlotDecoration

options? ​

DecorationOptions = {}

Returns ​

() => void

Implementation of ​

DecorationHost.addDecoration


addInputConsumer() ​

addInputConsumer(consumer, options?): () => void

Defined in: packages/core/src/plot/plot.ts:1150

Mounts a consumer that gets first look at input — drawing tools and axis drag go here.

Same shape as addDecoration: returns an unsubscribe function, which plugs straight into a plugin's teardown. Higher priority goes first; ties go to whichever registered later — the convention that whatever's drawn on top gets first grab.

Parameters ​

consumer ​

InputConsumer

options? ​

InputConsumerOptions = {}

Returns ​

() => void

Implementation of ​

InputHost.addInputConsumer


addPane() ​

addPane(options?): Pane

Defined in: packages/core/src/plot/plot.ts:640

Stacks one more pane below.

For indicators with a value range that doesn't fit alongside price (RSI, volume). An indicator that only needs to overlay on top of price doesn't need a pane — use mainPane.addSeries instead.

Its value axis is its own — linear by default, with log available if you need it.

A panesChange listener that throws here is rethrown in a microtask, where removePane and setPaneOrder rethrow it to the caller. The pane is already on the chart when the announcement runs, and the caller holds no other way to reach it — throwing would strand a live pane nobody can remove. The other two hand back nothing, so the error can be theirs.

Parameters ​

options? ​

PaneOptions & object = {}

Returns ​

Pane

Implementation of ​

PaneHost.addPane


applyOptions() ​

applyOptions(options): void

Defined in: packages/core/src/plot/plot.ts:1015

Changes only the config. Neither the layers nor the domain is touched.

Recreating a Plot for something like toggling the grid would spin up a new canvas, throwing away pan position and any overlay annotations entirely.

Only the fields given change. Giving axis: { x } leaves y alone — the old setConfig did a shallow merge, so it silently vanished there. Which fields swap as a unit is documented on PlotOptionsPatch.

Parameters ​

options ​

PlotOptionsPatch

Returns ​

void


claimCursor() ​

claimCursor(cursor): () => void

Defined in: packages/core/src/plot/plot.ts:1167

Claims a cursor shape — this is where a tool's drag, drawing, or axis hover goes. The later claim wins and release falls back to whatever's underneath → cursor-claims.ts

Parameters ​

cursor ​

string

Returns ​

() => void

Implementation of ​

CursorHost.claimCursor


claimFocusArea() ​

claimFocusArea(areaOf): FocusClaim

Defined in: packages/core/src/plot/plot.ts:1319

FocusAreaHost — the door that separates "the cursor went to someone contesting the keyboard" from "it went to no one." Registering is itself the declaration "I contest the keyboard too" → focus-claims.ts

Parameters ​

areaOf ​

() => PlotArea | null

Returns ​

FocusClaim

Implementation of ​

FocusAreaHost.claimFocusArea


click() ​

click(position): void

Defined in: packages/core/src/plot/plot.ts:1282

Click set — the same echo as crosshair, with the same payload shape.

Parameters ​

position ​

Point

Returns ​

void

Implementation of ​

InteractionTarget.click


contextMenu() ​

contextMenu(position): void

Defined in: packages/core/src/plot/plot.ts:1290

Parameters ​

position ​

Point

Returns ​

void

Implementation of ​

InteractionTarget.contextMenu


crosshair() ​

crosshair(position): void

Defined in: packages/core/src/plot/plot.ts:1242

The cursor's position, or null for "it left" — the one of the four pointer doors that can be nowhere, because a tooltip left showing the last value on a live chart reads as the current price.

null is said once. Every emitter passes through here — the default pointer interactions, a drawing tool moving the crosshair itself, a consumer calling this by hand — and a departure told twice (a browser's pointercancel followed by its pointerleave, say) is one departure. A position in between makes the next null new again.

Parameters ​

position ​

Point | null

Returns ​

void

Implementation of ​

InteractionTarget.crosshair


destroy() ​

destroy(): void

Defined in: packages/core/src/plot/plot.ts:1853

Returns ​

void


doubleClick() ​

doubleClick(position): void

Defined in: packages/core/src/plot/plot.ts:1286

Parameters ​

position ​

Point

Returns ​

void

Implementation of ​

InteractionTarget.doubleClick


fitDomains() ​

fitDomains(): void

Defined in: packages/core/src/plot/plot.ts:914

Refits both axes so all data currently held is visible, and hands every pane's value axis back to autoScale — "make everything visible" is a request to see the data, and an axis that then keeps following it is what the eye expects (the double-click reset lands here). To move x while keeping a manual value range, use scrollToRealTime or setVisibleRange instead.

One operation, at most one panesChange: the notification rings once (or not at all, when no pane flipped), after every pane has moved, never with a half-reset stack. A listener that throws part-way (xDomainChange, a pane subscriber, panesChange itself) does not stop the fit — it completes, and what was thrown comes out of this call at the end: one error as itself, several as an AggregateError.

Returns ​

void

Implementation of ​

InteractionTarget.fitDomains


formatX() ​

formatX(value): string

Defined in: packages/core/src/plot/plot.ts:809

The resolved x notation — config.axis.x.format, else what the tick strategy offers (timeTicks labels in its zone), else a rounded integer. x belongs to the chart, so this lives here. The decoration context and the FormatSource default that tooltips fall back to both read this. It's an arrow function because it gets carried as a function into the context.

Parameters ​

value ​

number

Returns ​

string

Implementation of ​

FormatSource.formatX


getDataRange() ​

getDataRange(): Range | null

Defined in: packages/core/src/plot/plot.ts:1443

The x range of the data the chart holds, in data x — every series in every pane, as one union. null when nothing is held. The same value xDomainChange carries as dataRange, readable when no x event fires (a prepend or a declarative update never moves the domain).

Returns ​

Range | null


getOptions() ​

getOptions(): ResolvedPlotConfig

Defined in: packages/core/src/plot/plot.ts:1045

A copy of the current config. Editing it doesn't touch the chart — the one door for changes is applyOptions.

When this was a shallow copy, getOptions().padding.left = 0 mutated the internals directly.

Returns ​

ResolvedPlotConfig


getSeries() ​

getSeries(): unknown

Defined in: packages/core/src/plot/plot.ts:1000

The first series mounted on mainPane. Use mainPane.getSeries() to see more than one. undefined if nothing has been mounted yet.

Not narrowed, since a derived series can have a different point type than its source — this is for identity comparison.

Returns ​

unknown


getVisibleRange() ​

getVisibleRange(): Range | null

Defined in: packages/core/src/plot/plot.ts:1333

The x range in view, in data x — the reading side of setVisibleRange. Indices never leave: under bar-index coordinates the window is translated back to the data's own x.

null until the chart has fitted to data — the scale's default [0,1] isn't a window anyone chose.

Returns ​

Range | null


leadingMargin() ​

leadingMargin(): number

Defined in: packages/core/src/plot/plot.ts:794

How far a fit reaches before the first bar, in data x — half the gap to its neighbour, so the first candle is drawn whole. 0 with no data, and with no series that draws a bar body (lines fit edge to edge). An infinite-history loader reads it so that margin does not look like missing history.

Returns ​

number


maximizePane() ​

maximizePane(pane): void

Defined in: packages/core/src/plot/plot.ts:701

Lets one pane fill the chart — every other pane is laid out at its minHeight, as if its flex were 0 — or, with null, gives the panes back their own split.

A layout mode, not a rewrite. No pane's flex changes: the split reads the panes through the maximize, so the layout the user arranged is still there when the maximize goes, a pane added meanwhile collapses with the rest and comes back at its own flex, and there is nothing to keep in step with the panes. It goes on its own when the pane is removed, and when a divider is dragged — the drag works on the heights on screen, and those become the panes' flex.

Rings panesChange when it changes — once, even for a lone pane, whose height doesn't move. Naming the pane already maximized changes nothing.

Parameters ​

pane ​

Pane | null

Returns ​

void

Implementation of ​

PaneHost.maximizePane


on() ​

on<E>(event, handler): () => void

Defined in: packages/core/src/plot/plot.ts:1087

Returns an unsubscribe function. Safe to call twice.

Type Parameters ​

E ​

E extends keyof PlotEvents

Parameters ​

event ​

E

handler ​

(payload) => void

Returns ​

() => void

Implementation of ​

PlotEventSource.on


pan() ​

pan(offset): void

Defined in: packages/core/src/plot/plot.ts:1190

Shifts the x domain by offset. y is untouched.

offset is in domain units — data x under continuous, bar count under bar-index. panByPixels, which starts from pixels, is correct regardless of coordinate system for that reason.

Parameters ​

offset ​

number

Returns ​

void

Implementation of ​

InteractionTarget.pan


panByPixels() ​

panByPixels(dx): void

Defined in: packages/core/src/plot/plot.ts:1211

Converts a drag distance into a domain shift. Dragging right should reveal the earlier range, so the sign is flipped.

Parameters ​

dx ​

number

Returns ​

void

Implementation of ​

InteractionTarget.panByPixels


pixelAtX() ​

pixelAtX(x): number

Defined in: packages/core/src/plot/plot.ts:783

The screen x (px) where a data x lands.

Parameters ​

x ​

number

Returns ​

number

Implementation of ​

XCoordinates.pixelAtX


removePane() ​

removePane(pane): void

Defined in: packages/core/src/plot/plot.ts:744

mainPane always survives — otherwise series would have nowhere to go.

Parameters ​

pane ​

Pane

Returns ​

void

Implementation of ​

PaneHost.removePane


render() ​

render(): void

Defined in: packages/core/src/plot/plot.ts:1551

Draws now, without waiting for a scheduled slot.

Does nothing once destroyed. It doesn't throw because an event handler arriving late during unmount calling this is a normal path.

Returns ​

void


requestRender() ​

requestRender(): void

Defined in: packages/core/src/plot/plot.ts:520

Requests a redraw on the next frame.

The door a decoration that holds its own state (like the crosshair) uses to refresh the screen. Unlike render(), requests within the same frame are coalesced into one.

Returns ​

void

Implementation of ​

RenderRequester.requestRender


routeInput() ​

routeInput(event): boolean

Defined in: packages/core/src/plot/plot.ts:1179

Proposes normalized input to the stack. A handler calls this before gesture translation — true means it was consumed, and it won't fall through to pan/zoom/crosshair.

When a host drives input directly (no handler), feeding it through this door too keeps consumers behaving under the same rules.

Parameters ​

event ​

InputEvent

Returns ​

boolean

Implementation of ​

InteractionTarget.routeInput


scrollToRealTime() ​

scrollToRealTime(): void

Defined in: packages/core/src/plot/plot.ts:894

Keeps the window's width and returns to live (last bar + half a bar + rightOffset) (the lightweight scrollToRealTime). Unlike fitDomains, the zoom level survives — this is the destination of the "jump back to now" button after browsing the past.

Returns ​

void


setPaneOrder() ​

setPaneOrder(panes): void

Defined in: packages/core/src/plot/plot.ts:733

Restacks the panes top to bottom in panes — every pane of this chart, each once, the main pane anywhere. For a host that declares the layout (a JSX tree inserting a pane above others).

Parameters ​

panes ​

readonly Pane[]

Returns ​

void


setSeries() ​

setSeries<TSource, TPoint>(registration): SeriesHandle<TSource, TPoint>

Defined in: packages/core/src/plot/plot.ts:983

Swaps mainPane for this one registration. Returns a handle.

The x domain is untouched, so the visible range (pan/zoom) is preserved. y is refit because different series occupy different ranges (a line uses close, a candle uses low through high).

Data doesn't carry over — it belongs to the registration, so a new registration brings its own.

Type Parameters ​

TSource ​

TSource extends BaseDataPoint

TPoint ​

TPoint extends BaseDataPoint = TSource

Parameters ​

registration ​

SeriesRegistration<TSource, TPoint> | Series<TSource>

Returns ​

SeriesHandle<TSource, TPoint>


setViewport() ​

setViewport(viewport): void

Defined in: packages/core/src/plot/plot.ts:1064

Keeps "only what's given changes" true to the letter.

This used to be { ...this.viewportSize, ...viewport }. A spread treats an explicit undefined as a value too, so setViewport({ width: 640, height: undefined }) erased the height — and @finchart/react calls it exactly that way (use-chart.ts: receiving only a width prop sends height through as undefined). The erased height became NaN in layout arithmetic and flowed all the way to the scale's range.

1,283 tests didn't catch this — nobody asserted on the resulting range, and NaN draws silently. The numeric door's requireFinite is what caught it.

Parameters ​

viewport ​

Partial<ViewportDimensions>

Returns ​

void


setVisibleRange() ​

setVisibleRange(fromX, toX): void

Defined in: packages/core/src/plot/plot.ts:874

Sets the visible x range in data x (the lightweight setVisibleRange).

Under bar-index coordinates, toDomain recovers the index. If data hasn't arrived yet, it reconciles from pending.

Parameters ​

fromX ​

number

toX ​

number

Returns ​

void

Implementation of ​

ViewportControl.setVisibleRange


takeScreenshot() ​

takeScreenshot(): string

Defined in: packages/core/src/plot/plot.ts:1268

The current screen as a PNG data URL.

Pixels belong to the layers, so this delegates. A frame is drawn now, before the shot is taken — with wiring where the scheduler defers a frame (rAF), taking the shot with only a render scheduled would capture the old picture. Throws if the layers don't offer the capability (headless).

Browser DOM layers composite their axis labels into the PNG. Legend, tooltip, and custom overlay elements are outside the screenshot contract.

Returns ​

string


use() ​

use<Api>(plugin): Api

Defined in: packages/core/src/plot/plot.ts:488

Installs one extension and returns exactly the API it built.

ts
const maximize = plot.use(paneMaximize());  // the type just comes along
maximize.maximize(plot.mainPane);

What splits the chart's things from a pane's things is what they hang off of. The example above used to be drawing tools, and that was the wrong example — drawing tools is pane.use(drawingTools({ plot })) (it uses the pane's value axis). JSDoc ships into the published .d.ts and becomes editor tooltips, so a wrong snippet here travels further than the docs do.

(Don't quote the wrong form even in a comment: snippet-drift.test.ts scans the published source and blocks a call with no arguments, so writing it as an aside gets flagged as a violation too. This trap was hit twice — the first time was a stale token name that slipped through.)

Doesn't merge methods onto the instance. TanStack Table v8 does that with _features (table.getSortedRowModel()), which needs type gymnastics and makes the core type different per plugin. Returning the API instead means Plot's type never changes because of a plugin, and no declaration merging is needed.

Install order is not draw order. z-index decides that.

Type Parameters ​

Api ​

Api extends PluginApi

Parameters ​

plugin ​

Plugin<Plot, Api>

Returns ​

Api

Implementation of ​

PluginHost.use


xAt() ​

xAt(pixel): number

Defined in: packages/core/src/plot/plot.ts:778

The data x under a screen x (px). Not an index even in bar-index coordinates.

The door an extension doing hit testing uses — this door didn't used to exist, so @finchart/tools was stashing the draw context in a variable on the side.

Parameters ​

pixel ​

number

Returns ​

number

Implementation of ​

XCoordinates.xAt


zoom() ​

zoom(factor, center): void

Defined in: packages/core/src/plot/plot.ts:1202

Zooms the x domain by factor (factor > 1 zooms in); the opt-in live-edge rule can move the anchor on zoom-out.

Parameters ​

factor ​

number

center ​

number

Returns ​

void

Implementation of ​

InteractionTarget.zoom


zoomAtPixel() ​

zoomAtPixel(factor, screenX): void

Defined in: packages/core/src/plot/plot.ts:1219

Zooms around the wheel cursor, except when the opt-in live-edge rule limits future space.

Parameters ​

factor ​

number

screenX ​

number

Returns ​

void

Implementation of ​

InteractionTarget.zoomAtPixel