From 58c9c6f55d26911771a0545cb4e411f1b3d833b1 Mon Sep 17 00:00:00 2001 From: liihuu Date: Sun, 3 Mar 2024 23:28:52 +0800 Subject: [PATCH] feat: support adding data forward and backward. #442 #472 #509 --- docs/.vitepress/components/Tag.vue | 15 ++++ docs/.vitepress/theme/index.js | 2 + docs/en-US/guide/chart-api.md | 6 +- docs/en-US/guide/instance-api.md | 46 ++++++++-- docs/guide/chart-api.md | 6 +- docs/guide/instance-api.md | 45 ++++++++-- package.json | 4 +- src/Chart.ts | 72 ++++++++-------- src/common/Action.ts | 1 + src/common/LoadDataCallback.ts | 33 ++++++++ src/common/LoadMoreCallback.ts | 4 + src/common/utils/logger.ts | 4 +- src/extension/figure/rectText.ts | 4 +- src/store/ChartStore.ts | 131 ++++++++++++++++++++++++----- src/store/TimeScaleStore.ts | 49 +++-------- tsconfig.json | 1 + 16 files changed, 305 insertions(+), 118 deletions(-) create mode 100644 docs/.vitepress/components/Tag.vue create mode 100644 src/common/LoadDataCallback.ts diff --git a/docs/.vitepress/components/Tag.vue b/docs/.vitepress/components/Tag.vue new file mode 100644 index 00000000..cc5e1344 --- /dev/null +++ b/docs/.vitepress/components/Tag.vue @@ -0,0 +1,15 @@ + + + \ No newline at end of file diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js index acba1d4a..202eb49e 100644 --- a/docs/.vitepress/theme/index.js +++ b/docs/.vitepress/theme/index.js @@ -4,6 +4,7 @@ import Theme from 'vitepress/theme' import HomeSponsor from '../components/HomeSponsor.vue' import NotFound from '../components/NotFound.vue' +import Tag from '../components/Tag.vue' import './style.css' @@ -17,6 +18,7 @@ export default { }) }, enhanceApp({ app, router, siteData }) { + app.component('Tag', Tag) // ... } } diff --git a/docs/en-US/guide/chart-api.md b/docs/en-US/guide/chart-api.md index 3d7dd2d4..33048e46 100644 --- a/docs/en-US/guide/chart-api.md +++ b/docs/en-US/guide/chart-api.md @@ -342,7 +342,7 @@ Add a overlay. ``` Get overlays for chart support. -## registerXAxis(axis) +## registerXAxis(axis) v9.8.0+ ```typescript ( axis: { @@ -360,7 +360,7 @@ Add custom x-axis. - `name` axis name - `createTicks` create ticks -## registerYAxis(axis) +## registerYAxis(axis) v9.8.0+ ```typescript ( axis: { @@ -464,7 +464,7 @@ Format date. `format`, such as 'YYYY-MM-DD HH:mm:ss'. ``` Format thousands separator. -### utils.formatFoldDecimal(value, threshold) +### utils.formatFoldDecimal(value, threshold) v9.8.0+ ```typescript (value: string | number, threshold: number) => string ``` diff --git a/docs/en-US/guide/instance-api.md b/docs/en-US/guide/instance-api.md index d0a76325..3bd9bb6d 100644 --- a/docs/en-US/guide/instance-api.md +++ b/docs/en-US/guide/instance-api.md @@ -178,6 +178,9 @@ Add new data, this method will clear the chart data, no need to call the clearDa - `dataList` is an array of K-line data. For details of the data type, please refer to [data](./datasource.md) - `more` tells the chart whether there is more historical data, can be defaulted, the default is true - `callback` success callback +::: warning Note +`callback` has been deprecated since version 9.8.0, use `subscribeAction('onDataReady', () => {})` instead. +::: ## applyMoreData(dataList, more, callback) @@ -200,6 +203,9 @@ Add more historical data. - `dataList` is an array of K-line data. For details of the data type, please refer to [data](./datasource.md) - `more` tells the chart whether there is more historical data, can be defaulted, the default is true - `callback` success callback +::: warning Note +This api has been deprecated since version 9.8.0. +::: ## updateData(data, callback) @@ -220,6 +226,9 @@ Add more historical data. Update data. Currently, only the timestamp of the last piece of data will be matched. If it is the same, it will be overwritten, and if it is different, it will be appended. - `data` single k-line data, please refer to [data](./datasource.md) for details of data type - `callback` success callback +::: warning Note +`callback` has been deprecated since version 9.8.0, use `subscribeAction('onDataReady', () => {})` instead. +::: ## getDataList() @@ -249,7 +258,28 @@ Clear the data of the chart. Generally, it is not necessary to call it manually. (cb: (timestamp: number | null) => void) => void ``` Set load more callback function. -- `cb` is a callback method, `timestamp` is the timestamp of the first piece of data. +- `cb` is a callback method, `timestamp` is the timestamp of the first piece of data +::: warning Note +This api has been deprecated since version 9.8.0, use `setLoadDataCallback` instead. +::: + + +## setLoadDataCallback(cb) v9.8.0+ +```typescript +( + cb: (params: { + type: 'forward' | 'backward' + data: Nullable + callback: (dataList: KLineData[], more?: boolean) => void + }) => void +) => void +``` +Set auto load data callback +- `cb` callback + - `params` params + - `type` forward or backward + - `data` boundary data + - `callback` used for returning data to chart ## createIndicator(value, isStack, paneOptions, callback) ```typescript @@ -345,7 +375,7 @@ Create a technical indicator, the return value is a string that identifies the w - `top` top margin, value less than 1 is a percentage - `bottom` bottom margin, value less than 1 is a percentage - `axisOptions` - - `name` is same `axis.name` in [registerYAxis(axis)](./chart-api#registeryaxis-axis) of chart api, default is 'default' + - `name` is same `axis.name` in [registerYAxis(axis)](./chart-api#registeryaxis-axis) of chart api, default is 'default' v9.8.0+ - `scrollZoomEnabled` Scroll zoom flag - `callback` success callback ::: tip Special id @@ -614,7 +644,7 @@ chart.createOverlay({ styles: { line: { style: 'solid', - dashedValue: [2, 2] + dashedValue: [2, 2], color: '#f00', size: 2 } @@ -739,7 +769,7 @@ chart.overrideOverlay({ styles: { line: { style: 'solid', - dashedValue: [2, 2] + dashedValue: [2, 2], color: '#f00', size: 2 } @@ -922,24 +952,24 @@ Execute chart action. ## subscribeAction(type, callback) ```typescript ( - type: 'onZoom' | 'onScroll' | 'onVisibleRangeChange' | 'onCrosshairChange' | 'onCandleBarClick' | 'onTooltipIconClick' | 'onPaneDrag', + type: 'onDataReady' | 'onZoom' | 'onScroll' | 'onVisibleRangeChange' | 'onCrosshairChange' | 'onCandleBarClick' | 'onTooltipIconClick' | 'onPaneDrag', callback: (data?: any) => void ) => void ``` Subscribe to chart actions. -- `type` options are 'onZoom', 'onScroll', 'onVisibleRangeChange', 'onCandleBarClick', 'onTooltipIconClick', 'onCrosshairChange' and 'onPaneDrag' +- `type` options are 'onDataReady', 'onZoom', 'onScroll', 'onVisibleRangeChange', 'onCandleBarClick', 'onTooltipIconClick', 'onCrosshairChange' and 'onPaneDrag' - `callback` is a callback method ## unsubscribeAction(type, callback) ```typescript ( - type: 'onZoom' | 'onScroll' | 'onVisibleRangeChange' | 'onCrosshairChange' | 'onCandleBarClick' | 'onTooltipIconClick' | 'onPaneDrag', + type: 'onDataReady' | 'onZoom' | 'onScroll' | 'onVisibleRangeChange' | 'onCrosshairChange' | 'onCandleBarClick' | 'onTooltipIconClick' | 'onPaneDrag', callback?: (data?: any) => void ) => void ``` Unsubscribe from chart actions. -- `type` options are 'onZoom', 'onScroll', 'onVisibleRangeChange', 'onCandleBarClick', 'onTooltipIconClick', 'onCrosshairChange' and 'onPaneDrag' +- `type` options are 'onDataReady', 'onZoom', 'onScroll', 'onVisibleRangeChange', 'onCandleBarClick', 'onTooltipIconClick', 'onCrosshairChange' and 'onPaneDrag' - `callback` is the callback method when subscribing, the default is to cancel all the current types diff --git a/docs/guide/chart-api.md b/docs/guide/chart-api.md index f6e278f9..ac7145d2 100644 --- a/docs/guide/chart-api.md +++ b/docs/guide/chart-api.md @@ -345,7 +345,7 @@ ``` 获取图表支持的覆盖物 -## registerXAxis(axis) +## registerXAxis(axis) v9.8.0+ ```typescript ( axis: { @@ -363,7 +363,7 @@ - `name` 坐标轴名字 - `createTicks` 创建分割文字 -## registerYAxis(axis) +## registerYAxis(axis) v9.8.0+ ```typescript ( axis: { @@ -467,7 +467,7 @@ ``` 格式化日期千分符。 -### utils.formatFoldDecimal(value, threshold) +### utils.formatFoldDecimal(value, threshold) v9.8.0+ ```typescript (value: string | number, threshold: number) => string ``` diff --git a/docs/guide/instance-api.md b/docs/guide/instance-api.md index e1eaacae..d78a36bb 100644 --- a/docs/guide/instance-api.md +++ b/docs/guide/instance-api.md @@ -179,6 +179,9 @@ - `dataList` 是一个K线数据数组,数据类型详情可参阅[数据源](./datasource.md) - `more` 告诉图表还有没有更多历史数据,可缺省,默认为true - `callback` 成功回调 +::: warning 注意 +参数 `callback` 自版本9.8.0开始,已废弃,请使用 `subscribeAction('onDataReady', () => {})` 代替。 +::: ## applyMoreData(dataList, more, callback) @@ -201,6 +204,9 @@ - `dataList` 是一个K线数据数组,数据类型详情可参阅[数据源](./datasource.md) - `more` 告诉图表还有没有更多历史数据,可缺省,默认为true - `callback` 成功回调 +::: warning 注意 +该方法自版本9.8.0开始,已废弃。 +::: ## updateData(data, callback) @@ -221,6 +227,9 @@ 更新数据,目前只会匹配当前最后一条数据的时间戳,相同则覆盖,不同则追加。 - `data` 单条k线数据,数据类型详情可参阅[数据源](./datasource.md) - `callback` 成功回调 +::: warning 注意 +参数 `callback` 自版本9.8.0开始,已废弃,请使用 `subscribeAction('onDataReady', () => {})` 代替。 +::: ## getDataList() @@ -251,8 +260,28 @@ ``` 设置加载更多回调函数。 - `cb` 是一个回调方法,`timestamp`为第一条数据的时间戳 +::: warning 注意 +该方法自版本9.8.0开始,已废弃,请使用 `setLoadDataCallback` 代替。 +::: +## setLoadDataCallback(cb) v9.8.0+ +```typescript +( + cb: (params: { + type: 'forward' | 'backward' + data: Nullable + callback: (dataList: KLineData[], more?: boolean) => void + }) => void +) => void +``` +设置自动加载数据回调方法 +- `cb` 回调方法 + - `params` 回调参数 + - `type` 类型,是往前加载还是往后加载 + - `data` 加载边界的数据 + - `callback` 回调方法,用于回传数据给图表 + ## createIndicator(value, isStack, paneOptions, callback) ```typescript ( @@ -347,7 +376,7 @@ - `top` 上边距,值小余1则是百分比 - `bottom` 下边距,值小余1则是百分比 - `axisOptions` - - `name` 指定的轴的名字,此参数对应图表实例方法 [registerYAxis(axis)](./chart-api#registeryaxis-axis) 中的 `axis.name`,默认为 'default' + - `name` 指定的轴的名字,此参数对应图表实例方法 [registerYAxis(axis)](./chart-api#registeryaxis-axis) 中的 `axis.name`,默认为 'default' v9.8.0+ - `scrollZoomEnabled` 轴上是否可以滚动缩放 - `callback` 指标创建完成回调方法 @@ -617,7 +646,7 @@ chart.overrideIndicator({ ```javascript chart.createOverlay({ name: 'segment', - id: 'segment_1' + id: 'segment_1', groupId: 'segment', points: [ { timestamp: 1614171282000, value: 18987 }, @@ -626,7 +655,7 @@ chart.createOverlay({ styles: { line: { style: 'solid', - dashedValue: [2, 2] + dashedValue: [2, 2], color: '#f00', size: 2 } @@ -752,7 +781,7 @@ chart.overrideOverlay({ styles: { line: { style: 'solid', - dashedValue: [2, 2] + dashedValue: [2, 2], color: '#f00', size: 2 } @@ -940,24 +969,24 @@ chart.setPaneOptions({ ## subscribeAction(type, callback) ```typescript ( - type: 'onZoom' | 'onScroll' | 'onVisibleRangeChange' | 'onCrosshairChange' | 'onCandleBarClick' | 'onTooltipIconClick' | 'onPaneDrag', + type: 'onDataReady' | 'onZoom' | 'onScroll' | 'onVisibleRangeChange' | 'onCrosshairChange' | 'onCandleBarClick' | 'onTooltipIconClick' | 'onPaneDrag', callback: (data?: any) => void ) => void ``` 订阅图表动作。 -- `type` 可选项为'onZoom','onScroll','onVisibleRangeChange','onCandleBarClick', 'onTooltipIconClick','onCrosshairChange'和'onPaneDrag' +- `type` 可选项为 'onDataReady','onZoom','onScroll','onVisibleRangeChange','onCandleBarClick', 'onTooltipIconClick','onCrosshairChange'和'onPaneDrag' - `callback` 是一个回调方法 ## unsubscribeAction(type, callback) ```typescript ( - type: 'onZoom' | 'onScroll' | 'onVisibleRangeChange' | 'onCrosshairChange' | 'onCandleBarClick' | 'onTooltipIconClick' | 'onPaneDrag', + type: 'onDataReady' | 'onZoom' | 'onScroll' | 'onVisibleRangeChange' | 'onCrosshairChange' | 'onCandleBarClick' | 'onTooltipIconClick' | 'onPaneDrag', callback?: (data?: any) => void ) => void ``` 取消订阅图表动作。 -- `type` 可选项为'onZoom','onScroll','onVisibleRangeChange','onCandleBarClick', 'onTooltipIconClick','onCrosshairChange'和'onPaneDrag' +- `type` 可选项为 'onDataReady','onZoom','onScroll','onVisibleRangeChange','onCandleBarClick', 'onTooltipIconClick','onCrosshairChange'和'onPaneDrag' - `callback` 订阅时的回调方法,缺省则取消当前类型所有 diff --git a/package.json b/package.json index 1d220e09..cb2577cb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "klinecharts", - "version": "9.7.2", + "version": "9.8.0", "description": "Lightweight k-line chart built with html5 canvas", "main": "./dist/index.cjs", "module": "./dist/index.esm.js", @@ -88,6 +88,6 @@ "rollup-plugin-progress": "^1.1.2", "tslib": "^2.5.0", "typescript": "^4.8.3", - "vitepress": "1.0.0-rc.36" + "vitepress": "1.0.0-rc.44" } } diff --git a/src/Chart.ts b/src/Chart.ts index ff96248a..c27c309a 100644 --- a/src/Chart.ts +++ b/src/Chart.ts @@ -20,10 +20,10 @@ import type Coordinate from './common/Coordinate' import type Point from './common/Point' import { UpdateLevel } from './common/Updater' import { type Styles, YAxisPosition } from './common/Styles' - import type Crosshair from './common/Crosshair' import { ActionType, type ActionCallback } from './common/Action' import type LoadMoreCallback from './common/LoadMoreCallback' +import type LoadDataCallback from './common/LoadDataCallback' import type Precision from './common/Precision' import type VisibleRange from './common/VisibleRange' @@ -32,7 +32,6 @@ import { createDom } from './common/utils/dom' import { getPixelRatio } from './common/utils/canvas' import { isString, isArray, isValid, merge, isNumber } from './common/utils/typeChecks' import { logWarn } from './common/utils/logger' -import { formatValue } from './common/utils/format' import { binarySearchNearest } from './common/utils/number' import ChartStore from './store/ChartStore' @@ -93,9 +92,18 @@ export interface Chart { clearData: () => void getDataList: () => KLineData[] applyNewData: (dataList: KLineData[], more?: boolean, callback?: () => void) => void + /** + * @deprecated + * Since v9.8.0 deprecated, since v10 removed + */ applyMoreData: (dataList: KLineData[], more?: boolean, callback?: () => void) => void updateData: (data: KLineData, callback?: () => void) => void + /** + * @deprecated + * Since v9.8.0 deprecated, since v10 removed + */ loadMore: (cb: LoadMoreCallback) => void + setLoadDataCallback: (cb: LoadDataCallback) => void createIndicator: (value: string | IndicatorCreate, isStack?: boolean, paneOptions?: PaneOptions, callback?: () => void) => Nullable overrideIndicator: (override: IndicatorCreate, paneId?: string, callback?: () => void) => void getIndicatorByPaneId: (paneId?: string, name?: string) => Nullable | Nullable> | Map> @@ -127,6 +135,7 @@ export interface Chart { export default class ChartImp implements Chart { id: string + private _container: HTMLElement private _chartContainer: HTMLElement private readonly _chartEvent: Event @@ -663,50 +672,41 @@ export default class ChartImp implements Chart { return this._chartStore.getDataList() } - applyNewData (dataList: KLineData[], more?: boolean, callback?: () => void): void { - this._chartStore.clear() - if (dataList.length === 0) { - this.adjustPaneViewport(false, true, true, true) - } else { - this.applyMoreData(dataList, more, callback) + applyNewData (data: KLineData[], more?: boolean, callback?: () => void): void { + if (isValid(callback)) { + logWarn('applyNewData', '', 'param `callback` has been deprecated since version 9.8.0, use `subscribeAction(\'onDataReady\')` instead.') } + this._chartStore.addData(data, true, more).then(() => {}).catch(() => {}).finally(() => { callback?.() }) } - applyMoreData (dataList: KLineData[], more?: boolean, callback?: () => void): void { - this._chartStore.addData(dataList, 0, more) - if (dataList.length > 0) { - this._chartStore.getIndicatorStore().calcInstance().then( - _ => { - this.adjustPaneViewport(false, true, true, true) - callback?.() - } - ).catch(_ => {}) - } + /** + * @deprecated + * Since v9.8.0 deprecated, since v10 removed + */ + applyMoreData (data: KLineData[], more?: boolean, callback?: () => void): void { + logWarn('', '', 'Api `applyMoreData` has been deprecated since version 9.8.0.') + const dataList = data.concat(this._chartStore.getDataList()) + this._chartStore.addData(dataList, false, more ?? true).then(() => {}).catch(() => {}).finally(() => { callback?.() }) } updateData (data: KLineData, callback?: () => void): void { - const dataList = this._chartStore.getDataList() - const dataCount = dataList.length - // Determine where individual data should be added - const timestamp = data.timestamp - const lastDataTimestamp = formatValue(dataList[dataCount - 1], 'timestamp', 0) as number - if (timestamp >= lastDataTimestamp) { - let pos = dataCount - if (timestamp === lastDataTimestamp) { - pos = dataCount - 1 - } - this._chartStore.addData(data, pos) - this._chartStore.getIndicatorStore().calcInstance().then( - _ => { - this.adjustPaneViewport(false, true, true, true) - callback?.() - } - ).catch(_ => {}) + if (isValid(callback)) { + logWarn('updateData', '', 'param `callback` has been deprecated since version 9.8.0, use `subscribeAction(\'onDataReady\')` instead.') } + this._chartStore.addData(data, false).then(() => {}).catch(() => {}).finally(() => { callback?.() }) } + /** + * @deprecated + * Since v9.8.0 deprecated, since v10 removed + */ loadMore (cb: LoadMoreCallback): void { - this._chartStore.getTimeScaleStore().setLoadMoreCallback(cb) + logWarn('', '', 'Api `loadMore` has been deprecated since version 9.8.0, use `setLoadDataCallback` instead.') + this._chartStore.setLoadMoreCallback(cb) + } + + setLoadDataCallback (cb: LoadDataCallback): void { + this._chartStore.setLoadDataCallback(cb) } createIndicator (value: string | IndicatorCreate, isStack?: boolean, paneOptions?: Nullable, callback?: () => void): Nullable { diff --git a/src/common/Action.ts b/src/common/Action.ts index 246f45e1..4c9f20d2 100644 --- a/src/common/Action.ts +++ b/src/common/Action.ts @@ -17,6 +17,7 @@ import { isFunction } from './utils/typeChecks' export type ActionCallback = (data?: any) => void export enum ActionType { + OnDataReady = 'onDataReady', OnZoom = 'onZoom', OnScroll = 'onScroll', OnVisibleRangeChange = 'onVisibleRangeChange', diff --git a/src/common/LoadDataCallback.ts b/src/common/LoadDataCallback.ts new file mode 100644 index 00000000..efb81781 --- /dev/null +++ b/src/common/LoadDataCallback.ts @@ -0,0 +1,33 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type Nullable from './Nullable' +import type KLineData from './KLineData' + +enum LoadDataType { + Forward = 'forward', + Backward = 'backward' +} + +interface LoadDataParams { + type: LoadDataType + data: Nullable + callback: (dataList: KLineData[], more?: boolean) => void +} + +export { LoadDataType, type LoadDataParams } + +type LoadDataCallback = (params: LoadDataParams) => void + +export default LoadDataCallback diff --git a/src/common/LoadMoreCallback.ts b/src/common/LoadMoreCallback.ts index efe26b70..ce7d24b1 100644 --- a/src/common/LoadMoreCallback.ts +++ b/src/common/LoadMoreCallback.ts @@ -14,6 +14,10 @@ import type Nullable from './Nullable' +/** + * Since v9.8.0 deprecated, since v10 removed + * @deprecated + */ type LoadMoreCallback = (timestamp: Nullable) => void export default LoadMoreCallback diff --git a/src/common/utils/logger.ts b/src/common/utils/logger.ts index 04447ebf..c1c6619c 100644 --- a/src/common/utils/logger.ts +++ b/src/common/utils/logger.ts @@ -16,8 +16,8 @@ const DEV = process.env.NODE_ENV === 'development' function log (templateText: string, tagStyle: string, messageStyle: string, api: string, invalidParam: string, append: string): void { if (DEV) { - const apiStr = api !== '' ? `Call api ${api}${invalidParam !== '' || append !== '' ? ', ' : '.'}` : '' - const invalidParamStr = invalidParam !== '' ? `invalid parameter ${invalidParam}${append !== '' ? ', ' : '.'}` : '' + const apiStr = api !== '' ? `Call api \`${api}\`${invalidParam !== '' || append !== '' ? ', ' : '.'}` : '' + const invalidParamStr = invalidParam !== '' ? `invalid parameter \`${invalidParam}\`${append !== '' ? ', ' : '.'}` : '' const appendStr = append !== '' ? append : '' console.log(templateText, tagStyle, messageStyle, apiStr, invalidParamStr, appendStr) } diff --git a/src/extension/figure/rectText.ts b/src/extension/figure/rectText.ts index 2ecfbca1..1affc2cd 100644 --- a/src/extension/figure/rectText.ts +++ b/src/extension/figure/rectText.ts @@ -30,6 +30,8 @@ const drawRectText = drawText * @deprecated * Starting from v10, it will be deleted */ -export { drawRectText } +export { + drawRectText +} export default rectText diff --git a/src/store/ChartStore.ts b/src/store/ChartStore.ts index a42a4bd2..f7b33fa9 100644 --- a/src/store/ChartStore.ts +++ b/src/store/ChartStore.ts @@ -12,11 +12,17 @@ * limitations under the License. */ +import type Nullable from '../common/Nullable' import type KLineData from '../common/KLineData' import type Precision from '../common/Precision' import type VisibleData from '../common/VisibleData' import { getDefaultStyles, type Styles } from '../common/Styles' -import { isArray, isNumber, isString, isValid, merge } from '../common/utils/typeChecks' +import { isArray, isBoolean, isNumber, isString, isValid, merge } from '../common/utils/typeChecks' +import { formatValue } from '../common/utils/format' +import type LoadDataCallback from '../common/LoadDataCallback' +import { type LoadDataParams, LoadDataType } from '../common/LoadDataCallback' +import type LoadMoreCallback from '../common/LoadMoreCallback' +import { ActionType } from '../common/Action' import { getDefaultCustomApi, type CustomApi, defaultLocale, type Options } from '../Options' @@ -39,27 +45,27 @@ export default class ChartStore { /** * Style config */ - private readonly _styles: Styles = getDefaultStyles() + private readonly _styles = getDefaultStyles() /** * Custom api */ - private readonly _customApi: CustomApi = getDefaultCustomApi() + private readonly _customApi = getDefaultCustomApi() /** * language */ - private _locale: string = defaultLocale + private _locale = defaultLocale /** * Price and volume precision */ - private _precision: Precision = { price: 2, volume: 0 } + private _precision = { price: 2, volume: 0 } /** * Thousands separator */ - private _thousandsSeparator: string = ',' + private _thousandsSeparator = ',' // Decimal fold threshold private _decimalFoldThreshold = 3 @@ -69,6 +75,33 @@ export default class ChartStore { */ private _dataList: KLineData[] = [] + /** + * Load more data callback + * Since v9.8.0 deprecated, since v10 removed + * @deprecated + */ + private _loadMoreCallback: Nullable = null + + /** + * Load data callback + */ + private _loadDataCallback: Nullable = null + + /** + * Is loading data flag + */ + private _loading = true + + /** + * Whether there are forward more flag + */ + private _forwardMore = true + + /** + * Whether there are forward more flag + */ + private _backwardMore = true + /** * Time scale store */ @@ -189,34 +222,94 @@ export default class ChartStore { return this._visibleDataList } - addData (data: KLineData | KLineData[], pos: number, more?: boolean): void { + async addData (data: KLineData | KLineData[], isFirstAdd: boolean, more?: boolean): Promise { + let success = false if (isArray(data)) { - this._timeScaleStore.setLoading(false) - this._timeScaleStore.setMore(more ?? true) - const isFirstAdd = this._dataList.length === 0 - this._dataList = data.concat(this._dataList) if (isFirstAdd) { + this.clear() + this._dataList = data + this._forwardMore = more ?? true this._timeScaleStore.resetOffsetRightDistance() + } else { + this._dataList = data + if (isBoolean(more)) { + this._forwardMore = more + } } - this._timeScaleStore.adjustVisibleRange() + this._loading = false + success = true } else { - const dataSize = this._dataList.length - if (pos >= dataSize) { + const dataCount = this._dataList.length + // Determine where individual data should be added + const timestamp = data.timestamp + const lastDataTimestamp = formatValue(this._dataList[dataCount - 1], 'timestamp', 0) as number + if (timestamp > lastDataTimestamp) { this._dataList.push(data) let lastBarRightSideDiffBarCount = this._timeScaleStore.getLastBarRightSideDiffBarCount() if (lastBarRightSideDiffBarCount < 0) { this._timeScaleStore.setLastBarRightSideDiffBarCount(--lastBarRightSideDiffBarCount) } - this._timeScaleStore.adjustVisibleRange() - } else { - this._dataList[pos] = data - this.adjustVisibleDataList() + success = true + } else if (timestamp === lastDataTimestamp) { + this._dataList[dataCount - 1] = data + success = true } } - this._tooltipStore.recalculateCrosshair(true) + if (success) { + this._timeScaleStore.adjustVisibleRange() + this._tooltipStore.recalculateCrosshair(true) + try { + await this._indicatorStore.calcInstance() + this._chart.adjustPaneViewport(false, true, true, true) + this._actionStore.execute(ActionType.OnDataReady) + } catch {} + } + } + + setLoadMoreCallback (callback: LoadMoreCallback): void { + this._loadMoreCallback = callback + } + + executeLoadMoreCallback (timestamp: Nullable): void { + if (this._forwardMore && !this._loading && isValid(this._loadMoreCallback)) { + this._loading = true + this._loadMoreCallback(timestamp) + } + } + + setLoadDataCallback (callback: LoadDataCallback): void { + this._loadDataCallback = callback + } + + executeLoadDataCallback (params: Omit): void { + if ( + !this._loading && + isValid(this._loadDataCallback) && + ( + (this._forwardMore && params.type === LoadDataType.Forward) || + (this._backwardMore && params.type === LoadDataType.Backward) + ) + ) { + const cb: ((data: KLineData[], more?: boolean) => void) = (data: KLineData[], more?: boolean) => { + let dataList: KLineData[] = [] + if (params.type === LoadDataType.Backward) { + dataList = this._dataList.concat(data) + this._backwardMore = more ?? false + } else { + dataList = data.concat(this._dataList) + this._forwardMore = more ?? false + } + this.addData(dataList, false).then(() => {}).catch(() => {}) + } + this._loading = true + this._loadDataCallback({ ...params, callback: cb }) + } } clear (): void { + this._forwardMore = true + this._backwardMore = true + this._loading = true this._dataList = [] this._visibleDataList = [] this._timeScaleStore.clear() diff --git a/src/store/TimeScaleStore.ts b/src/store/TimeScaleStore.ts index 945c4ce6..e26197fa 100644 --- a/src/store/TimeScaleStore.ts +++ b/src/store/TimeScaleStore.ts @@ -18,7 +18,6 @@ import type KLineData from '../common/KLineData' import type BarSpace from '../common/BarSpace' import type VisibleRange from '../common/VisibleRange' import { getDefaultVisibleRange } from '../common/VisibleRange' -import type LoadMoreCallback from '../common/LoadMoreCallback' import { ActionType } from '../common/Action' import { logWarn } from '../common/utils/logger' @@ -26,6 +25,7 @@ import { binarySearchNearest } from '../common/utils/number' import { isNumber, isString } from '../common/utils/typeChecks' import type ChartStore from './ChartStore' +import { LoadDataType } from '../common/LoadDataCallback' interface LeftRightSide { left: number @@ -67,21 +67,6 @@ export default class TimeScaleStore { */ private _scrollEnabled: boolean = true - /** - * Is loading data flag - */ - private _loading: boolean = true - - /** - * Load more data callback - */ - private _loadMoreCallback: Nullable = null - - /** - * Whether there are more flag - */ - private _more: boolean = true - /** * Total space of drawing area */ @@ -191,21 +176,20 @@ export default class TimeScaleStore { this._chartStore.getActionStore().execute(ActionType.OnVisibleRangeChange, this._visibleRange) this._chartStore.adjustVisibleDataList() // More processing and loading, more loading if there are callback methods and no data is being loaded - if (from === 0 && this._more && !this._loading && this._loadMoreCallback !== null) { - this._loading = true + if (from === 0) { const firstData = dataList[0] - this._loadMoreCallback(firstData?.timestamp ?? null) + this._chartStore.executeLoadMoreCallback(firstData?.timestamp ?? null) + this._chartStore.executeLoadDataCallback({ + type: LoadDataType.Forward, + data: firstData ?? null + }) + } + if (to === totalBarCount) { + this._chartStore.executeLoadDataCallback({ + type: LoadDataType.Backward, + data: dataList[totalBarCount - 1] ?? null + }) } - } - - setMore (more: boolean): this { - this._more = more - return this - } - - setLoading (loading: boolean): this { - this._loading = loading - return this } getDateTimeFormat (): Intl.DateTimeFormat { @@ -422,14 +406,7 @@ export default class TimeScaleStore { return this._scrollEnabled } - setLoadMoreCallback (callback: LoadMoreCallback): this { - this._loadMoreCallback = callback - return this - } - clear (): void { - this._more = true - this._loading = true this._visibleRange = getDefaultVisibleRange() } } diff --git a/tsconfig.json b/tsconfig.json index 7cf54c63..531195cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, + "experimentalDecorators": true, "lib": [ "dom", "es2018"