Skip to content

Commit

Permalink
Enable pane support (#1557)
Browse files Browse the repository at this point in the history
* fix pane widget and separator feature previously implemented in 22c6b03

* Add prepare scripts to package.json

* Add empty .npmignore

This file is being used in conjunction with the `prepare` script.
See this for details: yarnpkg/yarn#5235 (comment)

* implement subscribePaneResize and unsubscribePaneResize

* add ChartApi#removePane and ChartApi#swapPane, update Series#applyOptions for pane option

* update .size-limit.js

* Update package.json

* Revert "implement subscribePaneResize and unsubscribePaneResize"

This reverts commit 2194ebc

* add ChartApi#getPaneElements()

* fix pane widget and separator feature previously implemented in 22c6b03

* implement subscribePaneResize and unsubscribePaneResize

* add ChartApi#removePane and ChartApi#swapPane, update Series#applyOptions for pane option

* Revert "implement subscribePaneResize and unsubscribePaneResize"

This reverts commit 2194ebc

* add ChartApi#getPaneElements()

* update .size-limit.js and linter errors

* add paneIndex to MouseEventParams (click and crosshair event)

* add nonPrimaryPriceScale to ChartOptions for non primary pane

* fix: resize panes

* fix: remove empty panes

* fix: be able to move series to 0 index pane

* fix: paneSize method receive paneIndex

* chore: add method description

* update size limit

* add basic e2e tests

* add coverage tests

* add coverage test

* add customisable options

* improve coverage

* add PaneApi

* add paneApi coverage test

* remove dedicated pane options

* rename method

* remove pane index mapping

* remove paneIndex from series options

* fix tests

* fix line series

* remove PaneInfo interface

* remove paneIndex from Series

* cleanup removePane and swapPane

* fix crosshair horizontal line on all panes

* fix prevent removing last pane

* fix: remove non-primary price scale

* cleanup

* Pane separator & panes resize improvements

* fix add jsdoc comment

* fix: rename details to point

* remove getPaneElements api

* add CrosshairMarksPaneView per pane

* cleanup tests

* fix invalidation

* fix CrosshairMarksPaneView order

* add getSeriesByPane and getPaneBySeries apis

* add series method to pane

* update size limit

* createSeries fix

* add PaneApi getHTMLElement + change getSeriesByPane

* rename method

* add integration tests

* fix: remove methods from ChartApi

* Update src/api/iseries-api.ts

Co-authored-by: Mark Silverwood <[email protected]>

* Update src/model/chart-model.ts

Co-authored-by: Mark Silverwood <[email protected]>

* fixed code review suggestions

* move method to private

* Update src/api/ichart-api.ts

Co-authored-by: Mark Silverwood <[email protected]>

* fix paneIndex types

* change iseries-api description

* add how to guide

* add sidebar page

* add documentation links

* fix multiple logos

* add e2e tests

* add docs suggestions

* fix links

* fix docs links

* Update website/tutorials/how_to/panes.mdx

Co-authored-by: Evgeniia Riazanova <[email protected]>

* update docs

* fix markdown formatting

* add tm signs

* fix typo

* rename createPane method

* make getOrCreatePane private

* _getOrCreatePane refactoring

* fix: series priceScaleId option

* fix: add target blank to attr-logo

* test: disable attribution logo on unrelated tests

---------

Co-authored-by: Adrian Ng <[email protected]>
Co-authored-by: Kirill Chetverikov <[email protected]>
Co-authored-by: Mark Silverwood <[email protected]>
Co-authored-by: Evgeniia Riazanova <[email protected]>
  • Loading branch information
5 people authored May 8, 2024
1 parent 46e8894 commit 940635f
Show file tree
Hide file tree
Showing 54 changed files with 1,688 additions and 260 deletions.
8 changes: 4 additions & 4 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ module.exports = [
{
name: 'CJS',
path: 'dist/lightweight-charts.production.cjs',
limit: '48.97 KB',
limit: '50.81 KB',
},
{
name: 'ESM',
path: 'dist/lightweight-charts.production.mjs',
limit: '48.91 KB',
limit: '50.72 KB',
},
{
name: 'Standalone-ESM',
path: 'dist/lightweight-charts.standalone.production.mjs',
limit: '50.62 KB',
limit: '52.41 KB',
},
{
name: 'Standalone',
path: 'dist/lightweight-charts.standalone.production.js',
limit: '50.67 KB',
limit: '52.46 KB',
},
];
1 change: 1 addition & 0 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ There are several included e2e tests available which can be run individually. Pl
1. Bump the library's version in root `package.json` file to the next one (either major or minor depending on the planning and expected breaking changes).
1. Push the changes back to github (don't forget to push tags).
1. Create and publish a release on github.
1. Check that none of the tutorials pages are using links to 'next' api interfaces. If you find any then you should be able to update the link to use the new 'current' release we have just released.
1. Close the milestone.

## Deploying a pre-release version
Expand Down
67 changes: 48 additions & 19 deletions src/api/chart-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { DataUpdatesConsumer, isFulfilledData, SeriesDataItemTypeMap, Whitespace
import { DataLayer, DataUpdateResponse, SeriesChanges } from '../model/data-layer';
import { CustomData, ICustomSeriesPaneView } from '../model/icustom-series';
import { IHorzScaleBehavior } from '../model/ihorz-scale-behavior';
import { Pane } from '../model/pane';
import { Series } from '../model/series';
import { SeriesPlotRow } from '../model/series-data';
import {
Expand All @@ -35,6 +36,7 @@ import { Logical } from '../model/time-data';

import { getSeriesDataCreator } from './get-series-data-creator';
import { IChartApiBase, MouseEventHandler, MouseEventParams, PaneSize } from './ichart-api';
import { IPaneApi } from './ipane-api';
import { IPriceScaleApi } from './iprice-scale-api';
import { ISeriesApi } from './iseries-api';
import { ITimeScaleApi } from './itime-scale-api';
Expand All @@ -49,6 +51,7 @@ import {
lineStyleDefaults,
seriesOptionsDefaults,
} from './options/series-options-defaults';
import { PaneApi } from './pane-api';
import { PriceScaleApi } from './price-scale-api';
import { SeriesApi } from './series-api';
import { TimeScaleApi } from './time-scale-api';
Expand Down Expand Up @@ -124,7 +127,7 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
private readonly _crosshairMovedDelegate: Delegate<MouseEventParams<HorzScaleItem>> = new Delegate();

private readonly _timeScaleApi: TimeScaleApi<HorzScaleItem>;

private readonly _panes: WeakMap<Pane, PaneApi<HorzScaleItem>> = new WeakMap();
private readonly _horzScaleBehavior: IHorzScaleBehavior<HorzScaleItem>;

public constructor(container: HTMLElement, horzScaleBehavior: IHorzScaleBehavior<HorzScaleItem>, options?: DeepPartial<ChartOptionsImpl<HorzScaleItem>>) {
Expand Down Expand Up @@ -195,10 +198,11 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
public addCustomSeries<
TData extends CustomData<HorzScaleItem>,
TOptions extends CustomSeriesOptions,
TPartialOptions extends CustomSeriesPartialOptions = SeriesPartialOptions<TOptions>
TPartialOptions extends CustomSeriesPartialOptions = SeriesPartialOptions<TOptions>,
>(
customPaneView: ICustomSeriesPaneView<HorzScaleItem, TData, TOptions>,
options?: SeriesPartialOptions<TOptions>
options?: SeriesPartialOptions<TOptions>,
paneIndex?: number
): ISeriesApi<'Custom', HorzScaleItem, TData, TOptions, TPartialOptions> {
const paneView = ensure(customPaneView);
const defaults = {
Expand All @@ -209,34 +213,35 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
'Custom',
defaults,
options,
paneIndex,
paneView
);
}

public addAreaSeries(options?: AreaSeriesPartialOptions): ISeriesApi<'Area', HorzScaleItem> {
return this._addSeriesImpl('Area', areaStyleDefaults, options);
public addAreaSeries(options?: AreaSeriesPartialOptions, paneIndex?: number): ISeriesApi<'Area', HorzScaleItem> {
return this._addSeriesImpl('Area', areaStyleDefaults, options, paneIndex);
}

public addBaselineSeries(options?: BaselineSeriesPartialOptions): ISeriesApi<'Baseline', HorzScaleItem> {
return this._addSeriesImpl('Baseline', baselineStyleDefaults, options);
public addBaselineSeries(options?: BaselineSeriesPartialOptions, paneIndex?: number): ISeriesApi<'Baseline', HorzScaleItem> {
return this._addSeriesImpl('Baseline', baselineStyleDefaults, options, paneIndex);
}

public addBarSeries(options?: BarSeriesPartialOptions): ISeriesApi<'Bar', HorzScaleItem> {
return this._addSeriesImpl('Bar', barStyleDefaults, options);
public addBarSeries(options?: BarSeriesPartialOptions, paneIndex?: number): ISeriesApi<'Bar', HorzScaleItem> {
return this._addSeriesImpl('Bar', barStyleDefaults, options, paneIndex);
}

public addCandlestickSeries(options: CandlestickSeriesPartialOptions = {}): ISeriesApi<'Candlestick', HorzScaleItem> {
public addCandlestickSeries(options: CandlestickSeriesPartialOptions = {}, paneIndex?: number): ISeriesApi<'Candlestick', HorzScaleItem> {
fillUpDownCandlesticksColors(options);

return this._addSeriesImpl('Candlestick', candlestickStyleDefaults, options);
return this._addSeriesImpl('Candlestick', candlestickStyleDefaults, options, paneIndex);
}

public addHistogramSeries(options?: HistogramSeriesPartialOptions): ISeriesApi<'Histogram', HorzScaleItem> {
return this._addSeriesImpl('Histogram', histogramStyleDefaults, options);
public addHistogramSeries(options?: HistogramSeriesPartialOptions, paneIndex?: number): ISeriesApi<'Histogram', HorzScaleItem> {
return this._addSeriesImpl('Histogram', histogramStyleDefaults, options, paneIndex);
}

public addLineSeries(options?: LineSeriesPartialOptions): ISeriesApi<'Line', HorzScaleItem> {
return this._addSeriesImpl('Line', lineStyleDefaults, options);
public addLineSeries(options?: LineSeriesPartialOptions, paneIndex?: number): ISeriesApi<'Line', HorzScaleItem> {
return this._addSeriesImpl('Line', lineStyleDefaults, options, paneIndex);
}

public removeSeries(seriesApi: SeriesApi<SeriesType, HorzScaleItem>): void {
Expand Down Expand Up @@ -304,6 +309,14 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
return this._chartWidget.takeScreenshot();
}

public removePane(index: number): void {
this._chartWidget.model().removePane(index);
}

public swapPanes(first: number, second: number): void {
this._chartWidget.model().swapPanes(first, second);
}

public autoSizeActive(): boolean {
return this._chartWidget.autoSizeActive();
}
Expand All @@ -312,8 +325,12 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
return this._chartWidget.element();
}

public paneSize(): PaneSize {
const size = this._chartWidget.paneSize();
public panes(): IPaneApi<HorzScaleItem>[] {
return this._chartWidget.model().panes().map((pane: Pane) => this._getPaneApi(pane));
}

public paneSize(paneIndex: number = 0): PaneSize {
const size = this._chartWidget.paneSize(paneIndex);
return {
height: size.height,
width: size.width,
Expand Down Expand Up @@ -349,14 +366,15 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
type: TSeries,
styleDefaults: SeriesStyleOptionsMap[TSeries],
options: SeriesPartialOptionsMap[TSeries] = {},
paneIndex?: number,
customPaneView?: ICustomSeriesPaneView<HorzScaleItem>
): ISeriesApi<TSeries, HorzScaleItem, TData, TOptions, TPartialOptions> {
patchPriceFormat(options.priceFormat);

const strictOptions = merge(clone(seriesOptionsDefaults), clone(styleDefaults), options) as SeriesOptionsMap[TSeries];
const series = this._chartWidget.model().createSeries(type, strictOptions, customPaneView);
const series = this._chartWidget.model().createSeries(type, strictOptions, paneIndex, customPaneView);

const res = new SeriesApi<TSeries, HorzScaleItem, TData, TOptions, TPartialOptions>(series, this, this, this, this._horzScaleBehavior);
const res = new SeriesApi<TSeries, HorzScaleItem, TData, TOptions, TPartialOptions>(series, this, this, this, this._horzScaleBehavior, (pane: Pane) => this._getPaneApi(pane));
this._seriesMap.set(res, series);
this._seriesMapReversed.set(series, res);

Expand Down Expand Up @@ -396,10 +414,21 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
time: param.originalTime as HorzScaleItem,
logical: param.index as Logical | undefined,
point: param.point,
paneIndex: param.paneIndex,
hoveredSeries,
hoveredObjectId: param.hoveredObject,
seriesData,
sourceEvent: param.touchMouseEventData,
};
}

private _getPaneApi(pane: Pane): PaneApi<HorzScaleItem> {
let result = this._panes.get(pane);
if (!result) {
result = new PaneApi(this._chartWidget, (series: Series<SeriesType>) => this._mapSeriesToApi(series), pane);
this._panes.set(pane, result);
}

return result;
}
}
31 changes: 30 additions & 1 deletion src/api/ichart-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { Logical } from '../model/time-data';
import { TouchMouseEventData } from '../model/touch-mouse-event-data';

import { IPaneApi } from './ipane-api';
import { IPriceScaleApi } from './iprice-scale-api';
import { ISeriesApi } from './iseries-api';
import { ITimeScaleApi } from './itime-scale-api';
Expand Down Expand Up @@ -54,6 +55,10 @@ export interface MouseEventParams<HorzScaleItem = Time> {
* The value will be `undefined` if the event is fired outside the chart, for example a mouse leave event.
*/
point?: Point;
/**
* The index of the Pane
*/
paneIndex?: number;
/**
* Data of all series at the location of the event in the chart.
*
Expand Down Expand Up @@ -330,6 +335,28 @@ export interface IChartApiBase<HorzScaleItem = Time> {
*/
takeScreenshot(): HTMLCanvasElement;

/**
* Returns array of panes' API
*
* @returns array of pane's Api
*/
panes(): IPaneApi<HorzScaleItem>[];

/**
* Removes a pane with index
*
* @param index - the pane to be removed
*/
removePane(index: number): void;

/**
* swap the position of two panes.
*
* @param first - the first index
* @param second - the second index
*/
swapPanes(first: number, second: number): void;

/**
* Returns the active state of the `autoSize` option. This can be used to check
* whether the chart is handling resizing automatically with a `ResizeObserver`.
Expand Down Expand Up @@ -367,7 +394,9 @@ export interface IChartApiBase<HorzScaleItem = Time> {
* Returns the dimensions of the chart pane (the plot surface which excludes time and price scales).
* This would typically only be useful for plugin development.
*
* @param paneIndex - The index of the pane
* @defaultValue `0`
* @returns Dimensions of the chart pane
*/
paneSize(): PaneSize;
paneSize(paneIndex?: number): PaneSize;
}
50 changes: 50 additions & 0 deletions src/api/ipane-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { SeriesType } from '../model/series-options';

import { ISeriesApi } from './iseries-api';

/**
* Represents the interface for interacting with a pane in a lightweight chart.
*/
export interface IPaneApi<HorzScaleItem> {
/**
* Retrieves the height of the pane in pixels.
*
* @returns The height of the pane in pixels.
*/
getHeight(): number;

/**
* Sets the height of the pane.
*
* @param height - The number of pixels to set as the height of the pane.
*/
setHeight(height: number): void;

/**
* Moves the pane to a new position.
*
* @param paneIndex - The target index of the pane. Should be a number between 0 and the total number of panes - 1.
*/
moveTo(paneIndex: number): void;

/**
* Retrieves the index of the pane.
*
* @returns The index of the pane. It is a number between 0 and the total number of panes - 1.
*/
paneIndex(): number;

/**
* Retrieves the array of series for the current pane.
*
* @returns An array of series.
*/
getSeries(): ISeriesApi<SeriesType, HorzScaleItem>[];

/**
* Retrieves the HTML element of the pane.
*
* @returns The HTML element of the pane.
*/
getHTMLElement(): HTMLElement;
}
17 changes: 17 additions & 0 deletions src/api/iseries-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '../model/series-options';
import { Range } from '../model/time-data';

import { IPaneApi } from './ipane-api';
import { IPriceLine } from './iprice-line';
import { IPriceScaleApi } from './iprice-scale-api';
import { ISeriesPrimitive } from './iseries-primitive-api';
Expand Down Expand Up @@ -335,4 +336,20 @@ export interface ISeriesApi<
* Does nothing if specified primitive was not attached
*/
detachPrimitive(primitive: ISeriesPrimitive<HorzScaleItem>): void;

/**
* Move the series to another pane.
*
* If the pane with the specified index does not exist, the pane will be created.
*
* @param paneIndex - The index of the pane. Should be a number between 0 and the total number of panes.
*/
moveToPane(paneIndex: number): void;

/**
* Returns the pane to which the series is currently attached.
*
* @returns Pane API object to control the pane
*/
getPane(): IPaneApi<HorzScaleItem>;
}
5 changes: 5 additions & 0 deletions src/api/options/layout-options-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ export const layoutOptionsDefaults: LayoutOptions = {
textColor: '#191919',
fontSize: 12,
fontFamily: defaultFontFamily,
panes: {
enableResize: true,
separatorColor: '#E0E3EB',
separatorHoverColor: 'rgba(178, 181, 189, 0.2)',
},
attributionLogo: true,
};
Loading

0 comments on commit 940635f

Please sign in to comment.