diff --git a/.size-limit.js b/.size-limit.js index b9e58ab0d0..91cdcda788 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -4,21 +4,21 @@ module.exports = [ { name: 'CJS', path: 'dist/lightweight-charts.production.cjs', - limit: '47.07 KB', + limit: '47.58 KB', }, { name: 'ESM', path: 'dist/lightweight-charts.production.mjs', - limit: '47.01 KB', + limit: '47.53 KB', }, { name: 'Standalone-ESM', path: 'dist/lightweight-charts.standalone.production.mjs', - limit: '48.70 KB', + limit: '49.25 KB', }, { name: 'Standalone', path: 'dist/lightweight-charts.standalone.production.js', - limit: '48.74 KB', + limit: '49.3 KB', }, ]; diff --git a/src/api/candlestick-series-api.ts b/src/api/candlestick-series-api.ts index db44370cca..2453f91267 100644 --- a/src/api/candlestick-series-api.ts +++ b/src/api/candlestick-series-api.ts @@ -5,7 +5,7 @@ import { import { SeriesApi } from './series-api'; -export class CandlestickSeriesApi extends SeriesApi<'Candlestick'> { +export class CandlestickSeriesApi extends SeriesApi<'Candlestick', HorzScaleItem> { public override applyOptions(options: CandlestickSeriesPartialOptions): void { fillUpDownCandlesticksColors(options); super.applyOptions(options); diff --git a/src/api/chart-api.ts b/src/api/chart-api.ts index e90d11a269..a586a1ade1 100644 --- a/src/api/chart-api.ts +++ b/src/api/chart-api.ts @@ -5,8 +5,11 @@ import { Delegate } from '../helpers/delegate'; import { warn } from '../helpers/logger'; import { clone, DeepPartial, isBoolean, merge } from '../helpers/strict-type-checks'; -import { ChartOptions, ChartOptionsInternal } from '../model/chart-model'; +import { ChartOptionsImpl, ChartOptionsInternal } from '../model/chart-model'; +import { DataUpdatesConsumer, isFulfilledData, SeriesDataItemTypeMap, WhitespaceData } from '../model/data-consumer'; +import { DataLayer, DataUpdateResponse, SeriesChanges } from '../model/data-layer'; import { CustomData, ICustomSeriesPaneView } from '../model/icustom-series'; +import { IHorzScaleBehavior } from '../model/ihorz-scale-behavior'; import { Series } from '../model/series'; import { SeriesPlotRow } from '../model/series-data'; import { @@ -28,12 +31,10 @@ import { SeriesStyleOptionsMap, SeriesType, } from '../model/series-options'; -import { Logical, Time } from '../model/time-data'; +import { Logical } from '../model/time-data'; -import { DataUpdatesConsumer, isFulfilledData, SeriesDataItemTypeMap, WhitespaceData } from './data-consumer'; -import { DataLayer, DataUpdateResponse, SeriesChanges } from './data-layer'; import { getSeriesDataCreator } from './get-series-data-creator'; -import { IChartApi, MouseEventHandler, MouseEventParams } from './ichart-api'; +import { IChartApiBase, MouseEventHandler, MouseEventParams } from './ichart-api'; import { IPriceScaleApi } from './iprice-scale-api'; import { ISeriesApi } from './iseries-api'; import { ITimeScaleApi } from './itime-scale-api'; @@ -62,7 +63,7 @@ function patchPriceFormat(priceFormat?: DeepPartial): void { } } -function migrateHandleScaleScrollOptions(options: DeepPartial): void { +function migrateHandleScaleScrollOptions(options: DeepPartial>): void { if (isBoolean(options.handleScale)) { const handleScale = options.handleScale; options.handleScale = { @@ -104,32 +105,36 @@ function migrateHandleScaleScrollOptions(options: DeepPartial): vo } } -function toInternalOptions(options: DeepPartial): DeepPartial { +function toInternalOptions(options: DeepPartial>): DeepPartial> { migrateHandleScaleScrollOptions(options); - return options as DeepPartial; + return options as DeepPartial>; } -export type IPriceScaleApiProvider = Pick; +export type IPriceScaleApiProvider = Pick, 'priceScale'>; -export class ChartApi implements IChartApi, DataUpdatesConsumer { - private _chartWidget: ChartWidget; - private _dataLayer: DataLayer = new DataLayer(); - private readonly _seriesMap: Map, Series> = new Map(); - private readonly _seriesMapReversed: Map> = new Map(); +export class ChartApi implements IChartApiBase, DataUpdatesConsumer { + private _chartWidget: ChartWidget; + private _dataLayer: DataLayer; + private readonly _seriesMap: Map, Series> = new Map(); + private readonly _seriesMapReversed: Map, SeriesApi> = new Map(); - private readonly _clickedDelegate: Delegate = new Delegate(); - private readonly _dblClickedDelegate: Delegate = new Delegate(); - private readonly _crosshairMovedDelegate: Delegate = new Delegate(); + private readonly _clickedDelegate: Delegate> = new Delegate(); + private readonly _dblClickedDelegate: Delegate> = new Delegate(); + private readonly _crosshairMovedDelegate: Delegate> = new Delegate(); - private readonly _timeScaleApi: TimeScaleApi; + private readonly _timeScaleApi: TimeScaleApi; - public constructor(container: HTMLElement, options?: DeepPartial) { + private readonly _horzScaleBehavior: IHorzScaleBehavior; + + public constructor(container: HTMLElement, horzScaleBehavior: IHorzScaleBehavior, options?: DeepPartial>) { + this._dataLayer = new DataLayer(horzScaleBehavior); const internalOptions = (options === undefined) ? - clone(chartOptionsDefaults) : - merge(clone(chartOptionsDefaults), toInternalOptions(options)) as ChartOptionsInternal; + clone(chartOptionsDefaults()) : + merge(clone(chartOptionsDefaults()), toInternalOptions(options)) as ChartOptionsInternal; - this._chartWidget = new ChartWidget(container, internalOptions); + this._horzScaleBehavior = horzScaleBehavior; + this._chartWidget = new ChartWidget(container, internalOptions, horzScaleBehavior); this._chartWidget.clicked().subscribe( (paramSupplier: MouseEventParamsImplSupplier) => { @@ -157,7 +162,7 @@ export class ChartApi implements IChartApi, DataUpdatesConsumer { ); const model = this._chartWidget.model(); - this._timeScaleApi = new TimeScaleApi(model, this._chartWidget.timeAxisWidget()); + this._timeScaleApi = new TimeScaleApi(model, this._chartWidget.timeAxisWidget(), this._horzScaleBehavior); } public remove(): void { @@ -188,13 +193,13 @@ export class ChartApi implements IChartApi, DataUpdatesConsumer { } public addCustomSeries< - TData extends CustomData, + TData extends CustomData, TOptions extends CustomSeriesOptions, TPartialOptions extends CustomSeriesPartialOptions = SeriesPartialOptions >( - customPaneView: ICustomSeriesPaneView, + customPaneView: ICustomSeriesPaneView, options?: SeriesPartialOptions - ): ISeriesApi<'Custom', TData, TOptions, TPartialOptions> { + ): ISeriesApi<'Custom', HorzScaleItem, TData, TOptions, TPartialOptions> { const paneView = ensure(customPaneView); const defaults = { ...customStyleDefaults, @@ -208,33 +213,33 @@ export class ChartApi implements IChartApi, DataUpdatesConsumer { ); } - public addAreaSeries(options?: AreaSeriesPartialOptions): ISeriesApi<'Area'> { + public addAreaSeries(options?: AreaSeriesPartialOptions): ISeriesApi<'Area', HorzScaleItem> { return this._addSeriesImpl('Area', areaStyleDefaults, options); } - public addBaselineSeries(options?: BaselineSeriesPartialOptions): ISeriesApi<'Baseline'> { + public addBaselineSeries(options?: BaselineSeriesPartialOptions): ISeriesApi<'Baseline', HorzScaleItem> { return this._addSeriesImpl('Baseline', baselineStyleDefaults, options); } - public addBarSeries(options?: BarSeriesPartialOptions): ISeriesApi<'Bar'> { + public addBarSeries(options?: BarSeriesPartialOptions): ISeriesApi<'Bar', HorzScaleItem> { return this._addSeriesImpl('Bar', barStyleDefaults, options); } - public addCandlestickSeries(options: CandlestickSeriesPartialOptions = {}): ISeriesApi<'Candlestick'> { + public addCandlestickSeries(options: CandlestickSeriesPartialOptions = {}): ISeriesApi<'Candlestick', HorzScaleItem> { fillUpDownCandlesticksColors(options); return this._addSeriesImpl('Candlestick', candlestickStyleDefaults, options); } - public addHistogramSeries(options?: HistogramSeriesPartialOptions): ISeriesApi<'Histogram'> { + public addHistogramSeries(options?: HistogramSeriesPartialOptions): ISeriesApi<'Histogram', HorzScaleItem> { return this._addSeriesImpl('Histogram', histogramStyleDefaults, options); } - public addLineSeries(options?: LineSeriesPartialOptions): ISeriesApi<'Line'> { + public addLineSeries(options?: LineSeriesPartialOptions): ISeriesApi<'Line', HorzScaleItem> { return this._addSeriesImpl('Line', lineStyleDefaults, options); } - public removeSeries(seriesApi: SeriesApi): void { + public removeSeries(seriesApi: SeriesApi): void { const series = ensureDefined(this._seriesMap.get(seriesApi)); const update = this._dataLayer.removeSeries(series); @@ -247,52 +252,52 @@ export class ChartApi implements IChartApi, DataUpdatesConsumer { this._seriesMapReversed.delete(series); } - public applyNewData(series: Series, data: SeriesDataItemTypeMap[TSeriesType][]): void { + public applyNewData(series: Series, data: SeriesDataItemTypeMap[TSeriesType][]): void { this._sendUpdateToChart(this._dataLayer.setSeriesData(series, data)); } - public updateData(series: Series, data: SeriesDataItemTypeMap[TSeriesType]): void { + public updateData(series: Series, data: SeriesDataItemTypeMap[TSeriesType]): void { this._sendUpdateToChart(this._dataLayer.updateSeriesData(series, data)); } - public subscribeClick(handler: MouseEventHandler): void { + public subscribeClick(handler: MouseEventHandler): void { this._clickedDelegate.subscribe(handler); } - public unsubscribeClick(handler: MouseEventHandler): void { + public unsubscribeClick(handler: MouseEventHandler): void { this._clickedDelegate.unsubscribe(handler); } - public subscribeDblClick(handler: MouseEventHandler): void { - this._dblClickedDelegate.subscribe(handler); + public subscribeCrosshairMove(handler: MouseEventHandler): void { + this._crosshairMovedDelegate.subscribe(handler); } - public unsubscribeDblClick(handler: MouseEventHandler): void { - this._dblClickedDelegate.unsubscribe(handler); + public unsubscribeCrosshairMove(handler: MouseEventHandler): void { + this._crosshairMovedDelegate.unsubscribe(handler); } - public subscribeCrosshairMove(handler: MouseEventHandler): void { - this._crosshairMovedDelegate.subscribe(handler); + public subscribeDblClick(handler: MouseEventHandler): void { + this._dblClickedDelegate.subscribe(handler); } - public unsubscribeCrosshairMove(handler: MouseEventHandler): void { - this._crosshairMovedDelegate.unsubscribe(handler); + public unsubscribeDblClick(handler: MouseEventHandler): void { + this._dblClickedDelegate.unsubscribe(handler); } public priceScale(priceScaleId: string): IPriceScaleApi { return new PriceScaleApi(this._chartWidget, priceScaleId); } - public timeScale(): ITimeScaleApi { + public timeScale(): ITimeScaleApi { return this._timeScaleApi; } - public applyOptions(options: DeepPartial): void { + public applyOptions(options: DeepPartial>): void { this._chartWidget.applyOptions(toInternalOptions(options)); } - public options(): Readonly { - return this._chartWidget.options() as Readonly; + public options(): Readonly> { + return this._chartWidget.options() as Readonly>; } public takeScreenshot(): HTMLCanvasElement { @@ -309,21 +314,21 @@ export class ChartApi implements IChartApi, DataUpdatesConsumer { private _addSeriesImpl< TSeries extends SeriesType, - TData extends WhitespaceData = SeriesDataItemTypeMap[TSeries], + TData extends WhitespaceData = SeriesDataItemTypeMap[TSeries], TOptions extends SeriesOptionsMap[TSeries] = SeriesOptionsMap[TSeries], TPartialOptions extends SeriesPartialOptionsMap[TSeries] = SeriesPartialOptionsMap[TSeries] >( type: TSeries, styleDefaults: SeriesStyleOptionsMap[TSeries], options: SeriesPartialOptionsMap[TSeries] = {}, - customPaneView?: ICustomSeriesPaneView - ): ISeriesApi { + customPaneView?: ICustomSeriesPaneView + ): ISeriesApi { patchPriceFormat(options.priceFormat); const strictOptions = merge(clone(seriesOptionsDefaults), clone(styleDefaults), options) as SeriesOptionsMap[TSeries]; const series = this._chartWidget.model().createSeries(type, strictOptions, customPaneView); - const res = new SeriesApi(series, this, this, this); + const res = new SeriesApi(series, this, this, this, this._horzScaleBehavior); this._seriesMap.set(res, series); this._seriesMapReversed.set(series, res); @@ -334,20 +339,20 @@ export class ChartApi implements IChartApi, DataUpdatesConsumer { const model = this._chartWidget.model(); model.updateTimeScale(update.timeScale.baseIndex, update.timeScale.points, update.timeScale.firstChangedPointIndex); - update.series.forEach((value: SeriesChanges, series: Series) => series.setData(value.data, value.info)); + update.series.forEach((value: SeriesChanges, series: Series) => series.setData(value.data, value.info)); model.recalculateAllPanes(); } - private _mapSeriesToApi(series: Series): ISeriesApi { + private _mapSeriesToApi(series: Series): ISeriesApi { return ensureDefined(this._seriesMapReversed.get(series)); } - private _convertMouseParams(param: MouseEventParamsImpl): MouseEventParams { - const seriesData: MouseEventParams['seriesData'] = new Map(); - param.seriesData.forEach((plotRow: SeriesPlotRow, series: Series) => { + private _convertMouseParams(param: MouseEventParamsImpl): MouseEventParams { + const seriesData: MouseEventParams['seriesData'] = new Map(); + param.seriesData.forEach((plotRow: SeriesPlotRow, series: Series) => { const seriesType = series.seriesType(); - const data = getSeriesDataCreator(seriesType)(plotRow); + const data = getSeriesDataCreator(seriesType)(plotRow); if (seriesType !== 'Custom') { assert(isFulfilledData(data)); } else { @@ -360,7 +365,7 @@ export class ChartApi implements IChartApi, DataUpdatesConsumer { const hoveredSeries = param.hoveredSeries === undefined ? undefined : this._mapSeriesToApi(param.hoveredSeries); return { - time: param.time as Time | undefined, + time: param.originalTime as HorzScaleItem, logical: param.index as Logical | undefined, point: param.point, hoveredSeries, diff --git a/src/api/create-chart.ts b/src/api/create-chart.ts index 9a91dd27a3..0f0cf3f68e 100644 --- a/src/api/create-chart.ts +++ b/src/api/create-chart.ts @@ -1,19 +1,31 @@ import { assert } from '../helpers/assertions'; import { DeepPartial, isString } from '../helpers/strict-type-checks'; -import { ChartOptions } from '../model/chart-model'; +import { HorzScaleBehaviorTime } from '../model/horz-scale-behavior-time/horz-scale-behavior-time'; +import { TimeChartOptions } from '../model/horz-scale-behavior-time/time-based-chart-options'; +import { Time } from '../model/horz-scale-behavior-time/types'; +import { IHorzScaleBehavior } from '../model/ihorz-scale-behavior'; import { ChartApi } from './chart-api'; -import { IChartApi } from './ichart-api'; +import { IChartApiBase } from './ichart-api'; /** - * This function is the main entry point of the Lightweight Charting Library. + * This function is the main entry point of the Lightweight Charting Library. If you are using time values + * for the horizontal scale then it is recommended that you rather use the {@link createChart} function. + * + * @template HorzScaleItem - type of points on the horizontal scale + * @template THorzScaleBehavior - type of horizontal axis strategy that encapsulate all the specific behaviors of the horizontal scale type * * @param container - ID of HTML element or element itself + * @param horzScaleBehavior - Horizontal scale behavior * @param options - Any subset of options to be applied at start. * @returns An interface to the created chart */ -export function createChart(container: string | HTMLElement, options?: DeepPartial): IChartApi { +export function createChartEx>( + container: string | HTMLElement, + horzScaleBehavior: THorzScaleBehavior, + options?: DeepPartial> +): IChartApiBase { let htmlElement: HTMLElement; if (isString(container)) { const element = document.getElementById(container); @@ -23,5 +35,39 @@ export function createChart(container: string | HTMLElement, options?: DeepParti htmlElement = container; } - return new ChartApi(htmlElement, options); + const res = new ChartApi(htmlElement, horzScaleBehavior, options); + horzScaleBehavior.setOptions(res.options()); + return res; +} + +/** + * Structure describing options of the chart with time points at the horizontal scale. Series options are to be set separately + */ +export type ChartOptions = TimeChartOptions; + +/** + * The main interface of a single chart using time for horizontal scale. + */ +export interface IChartApi extends IChartApiBase