@tradingaction/core 2.0.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.
Files changed (99) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +5 -0
  3. package/lib/CanvasContainer.d.ts +19 -0
  4. package/lib/CanvasContainer.js +28 -0
  5. package/lib/CanvasContainer.js.map +1 -0
  6. package/lib/Chart.d.ts +32 -0
  7. package/lib/Chart.js +57 -0
  8. package/lib/Chart.js.map +1 -0
  9. package/lib/ChartCanvas.d.ts +235 -0
  10. package/lib/ChartCanvas.js +810 -0
  11. package/lib/ChartCanvas.js.map +1 -0
  12. package/lib/EventCapture.d.ts +131 -0
  13. package/lib/EventCapture.js +489 -0
  14. package/lib/EventCapture.js.map +1 -0
  15. package/lib/GenericChartComponent.d.ts +21 -0
  16. package/lib/GenericChartComponent.js +75 -0
  17. package/lib/GenericChartComponent.js.map +1 -0
  18. package/lib/GenericComponent.d.ts +81 -0
  19. package/lib/GenericComponent.js +355 -0
  20. package/lib/GenericComponent.js.map +1 -0
  21. package/lib/MoreProps.d.ts +16 -0
  22. package/lib/MoreProps.js +2 -0
  23. package/lib/MoreProps.js.map +1 -0
  24. package/lib/index.d.ts +7 -0
  25. package/lib/index.js +8 -0
  26. package/lib/index.js.map +1 -0
  27. package/lib/useEvent.d.ts +1 -0
  28. package/lib/useEvent.js +13 -0
  29. package/lib/useEvent.js.map +1 -0
  30. package/lib/utils/ChartDataUtil.d.ts +49 -0
  31. package/lib/utils/ChartDataUtil.js +205 -0
  32. package/lib/utils/ChartDataUtil.js.map +1 -0
  33. package/lib/utils/PureComponent.d.ts +4 -0
  34. package/lib/utils/PureComponent.js +10 -0
  35. package/lib/utils/PureComponent.js.map +1 -0
  36. package/lib/utils/accumulatingWindow.d.ts +15 -0
  37. package/lib/utils/accumulatingWindow.js +98 -0
  38. package/lib/utils/accumulatingWindow.js.map +1 -0
  39. package/lib/utils/barWidth.d.ts +15 -0
  40. package/lib/utils/barWidth.js +27 -0
  41. package/lib/utils/barWidth.js.map +1 -0
  42. package/lib/utils/closestItem.d.ts +5 -0
  43. package/lib/utils/closestItem.js +45 -0
  44. package/lib/utils/closestItem.js.map +1 -0
  45. package/lib/utils/evaluator.d.ts +7 -0
  46. package/lib/utils/evaluator.js +94 -0
  47. package/lib/utils/evaluator.js.map +1 -0
  48. package/lib/utils/identity.d.ts +1 -0
  49. package/lib/utils/identity.js +2 -0
  50. package/lib/utils/identity.js.map +1 -0
  51. package/lib/utils/index.d.ts +46 -0
  52. package/lib/utils/index.js +126 -0
  53. package/lib/utils/index.js.map +1 -0
  54. package/lib/utils/noop.d.ts +1 -0
  55. package/lib/utils/noop.js +3 -0
  56. package/lib/utils/noop.js.map +1 -0
  57. package/lib/utils/shallowEqual.d.ts +1 -0
  58. package/lib/utils/shallowEqual.js +22 -0
  59. package/lib/utils/shallowEqual.js.map +1 -0
  60. package/lib/utils/slidingWindow.d.ts +19 -0
  61. package/lib/utils/slidingWindow.js +109 -0
  62. package/lib/utils/slidingWindow.js.map +1 -0
  63. package/lib/utils/strokeDasharray.d.ts +3 -0
  64. package/lib/utils/strokeDasharray.js +37 -0
  65. package/lib/utils/strokeDasharray.js.map +1 -0
  66. package/lib/utils/zipper.d.ts +7 -0
  67. package/lib/utils/zipper.js +36 -0
  68. package/lib/utils/zipper.js.map +1 -0
  69. package/lib/zoom/index.d.ts +1 -0
  70. package/lib/zoom/index.js +2 -0
  71. package/lib/zoom/index.js.map +1 -0
  72. package/lib/zoom/zoomBehavior.d.ts +10 -0
  73. package/lib/zoom/zoomBehavior.js +18 -0
  74. package/lib/zoom/zoomBehavior.js.map +1 -0
  75. package/package.json +52 -0
  76. package/src/CanvasContainer.tsx +44 -0
  77. package/src/Chart.tsx +114 -0
  78. package/src/ChartCanvas.tsx +1336 -0
  79. package/src/EventCapture.tsx +709 -0
  80. package/src/GenericChartComponent.tsx +98 -0
  81. package/src/GenericComponent.tsx +454 -0
  82. package/src/MoreProps.ts +17 -0
  83. package/src/index.ts +7 -0
  84. package/src/useEvent.ts +14 -0
  85. package/src/utils/ChartDataUtil.ts +297 -0
  86. package/src/utils/PureComponent.tsx +12 -0
  87. package/src/utils/accumulatingWindow.ts +118 -0
  88. package/src/utils/barWidth.ts +44 -0
  89. package/src/utils/closestItem.ts +60 -0
  90. package/src/utils/evaluator.ts +163 -0
  91. package/src/utils/identity.ts +1 -0
  92. package/src/utils/index.ts +153 -0
  93. package/src/utils/noop.ts +2 -0
  94. package/src/utils/shallowEqual.ts +25 -0
  95. package/src/utils/slidingWindow.ts +140 -0
  96. package/src/utils/strokeDasharray.ts +52 -0
  97. package/src/utils/zipper.ts +45 -0
  98. package/src/zoom/index.ts +1 -0
  99. package/src/zoom/zoomBehavior.ts +34 -0
@@ -0,0 +1,810 @@
1
+ import { extent as d3Extent, max, min } from "d3-array";
2
+ import * as React from "react";
3
+ import { clearCanvas, functor, head, identity, isDefined, isNotDefined, last, shallowEqual } from "./utils";
4
+ import { mouseBasedZoomAnchor } from "./zoom";
5
+ import { getChartConfigWithUpdatedYScales, getCurrentCharts, getCurrentItem, getNewChartConfig, } from "./utils/ChartDataUtil";
6
+ import { EventCapture } from "./EventCapture";
7
+ import { CanvasContainer } from "./CanvasContainer";
8
+ import evaluator from "./utils/evaluator";
9
+ const CANDIDATES_FOR_RESET = ["seriesName"];
10
+ const shouldResetChart = (thisProps, nextProps) => {
11
+ return !CANDIDATES_FOR_RESET.every((key) => {
12
+ const result = shallowEqual(thisProps[key], nextProps[key]);
13
+ return result;
14
+ });
15
+ };
16
+ const getCursorStyle = () => {
17
+ const tooltipStyle = `
18
+ .react-financial-charts-grabbing-cursor {
19
+ pointer-events: all;
20
+ cursor: -moz-grabbing;
21
+ cursor: -webkit-grabbing;
22
+ cursor: grabbing;
23
+ }
24
+ .react-financial-charts-crosshair-cursor {
25
+ pointer-events: all;
26
+ cursor: crosshair;
27
+ }
28
+ .react-financial-charts-tooltip-hover {
29
+ pointer-events: all;
30
+ cursor: pointer;
31
+ }
32
+ .react-financial-charts-avoid-interaction {
33
+ pointer-events: none;
34
+ }
35
+ .react-financial-charts-enable-interaction {
36
+ pointer-events: all;
37
+ }
38
+ .react-financial-charts-tooltip {
39
+ pointer-events: all;
40
+ cursor: pointer;
41
+ }
42
+ .react-financial-charts-default-cursor {
43
+ cursor: default;
44
+ }
45
+ .react-financial-charts-move-cursor {
46
+ cursor: move;
47
+ }
48
+ .react-financial-charts-pointer-cursor {
49
+ cursor: pointer;
50
+ }
51
+ .react-financial-charts-ns-resize-cursor {
52
+ cursor: ns-resize;
53
+ }
54
+ .react-financial-charts-ew-resize-cursor {
55
+ cursor: ew-resize;
56
+ }`;
57
+ return React.createElement("style", { type: "text/css" }, tooltipStyle);
58
+ };
59
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
60
+ const noop = () => { };
61
+ export const chartCanvasContextDefaultValue = {
62
+ amIOnTop: () => false,
63
+ chartConfigs: [],
64
+ chartId: 0,
65
+ ratio: 0,
66
+ displayXAccessor: () => 0,
67
+ fullData: [],
68
+ getMutableState: () => ({}),
69
+ height: 0,
70
+ margin: { top: 0, right: 0, bottom: 0, left: 0 },
71
+ plotData: [],
72
+ setCursorClass: noop,
73
+ subscribe: noop,
74
+ unsubscribe: noop,
75
+ redraw: noop,
76
+ width: 0,
77
+ xAccessor: () => 0,
78
+ xScale: noop,
79
+ };
80
+ export const ChartCanvasContext = React.createContext(chartCanvasContextDefaultValue);
81
+ const getDimensions = (props) => {
82
+ const { margin, height, width } = props;
83
+ return {
84
+ height: height - margin.top - margin.bottom,
85
+ width: width - margin.left - margin.right,
86
+ };
87
+ };
88
+ const getXScaleDirection = (flipXScale) => {
89
+ return flipXScale ? -1 : 1;
90
+ };
91
+ const calculateFullData = (props) => {
92
+ const { data: fullData, plotFull, xScale, clamp, pointsPerPxThreshold, flipXScale, xAccessor, displayXAccessor, minPointsPerPxThreshold, } = props;
93
+ const useWholeData = plotFull !== undefined ? plotFull : xAccessor === identity;
94
+ const { filterData } = evaluator({
95
+ xScale,
96
+ useWholeData,
97
+ clamp,
98
+ pointsPerPxThreshold,
99
+ minPointsPerPxThreshold,
100
+ flipXScale,
101
+ });
102
+ return {
103
+ xAccessor,
104
+ displayXAccessor: displayXAccessor !== null && displayXAccessor !== void 0 ? displayXAccessor : xAccessor,
105
+ xScale: xScale.copy(),
106
+ fullData,
107
+ filterData,
108
+ };
109
+ };
110
+ const resetChart = (props) => {
111
+ const state = calculateState(props);
112
+ const { xAccessor, displayXAccessor, fullData, plotData: initialPlotData, xScale } = state;
113
+ const { postCalculator, children } = props;
114
+ const plotData = postCalculator !== undefined ? postCalculator(initialPlotData) : initialPlotData;
115
+ const dimensions = getDimensions(props);
116
+ const chartConfigs = getChartConfigWithUpdatedYScales(getNewChartConfig(dimensions, children), { plotData, xAccessor, displayXAccessor, fullData }, xScale.domain());
117
+ return Object.assign(Object.assign({}, state), { xScale,
118
+ plotData,
119
+ chartConfigs });
120
+ };
121
+ const updateChart = (newState, initialXScale, props, lastItemWasVisible, initialChartConfig) => {
122
+ const { fullData, xScale, xAccessor, displayXAccessor, filterData } = newState;
123
+ const lastItem = last(fullData);
124
+ const lastXItem = xAccessor(lastItem);
125
+ const [start, end] = initialXScale.domain();
126
+ const { postCalculator, children, padding, flipXScale, maintainPointsPerPixelOnResize } = props;
127
+ const direction = getXScaleDirection(flipXScale);
128
+ const dimensions = getDimensions(props);
129
+ const updatedXScale = setXRange(xScale, dimensions, padding, direction);
130
+ let initialPlotData;
131
+ if (!lastItemWasVisible || end >= lastXItem) {
132
+ // resize comes here...
133
+ // get plotData between [start, end] and do not change the domain
134
+ const [rangeStart, rangeEnd] = initialXScale.range();
135
+ const [newRangeStart, newRangeEnd] = updatedXScale.range();
136
+ const newDomainExtent = ((newRangeEnd - newRangeStart) / (rangeEnd - rangeStart)) * (end.valueOf() - start.valueOf());
137
+ const newStart = maintainPointsPerPixelOnResize ? end.valueOf() - newDomainExtent : start;
138
+ const lastItemX = initialXScale(lastXItem);
139
+ const response = filterData(fullData, [newStart, end], xAccessor, updatedXScale, {
140
+ fallbackStart: start,
141
+ fallbackEnd: { lastItem, lastItemX },
142
+ });
143
+ initialPlotData = response.plotData;
144
+ updatedXScale.domain(response.domain);
145
+ }
146
+ else if (lastItemWasVisible && end < lastXItem) {
147
+ // this is when a new item is added and last item was visible
148
+ // so slide over and show the new item also
149
+ // get plotData between [xAccessor(l) - (end - start), xAccessor(l)] and DO change the domain
150
+ const dx = initialXScale(lastXItem) - initialXScale.range()[1];
151
+ const [newStart, newEnd] = initialXScale
152
+ .range()
153
+ .map((x) => x + dx)
154
+ .map((x) => initialXScale.invert(x));
155
+ const response = filterData(fullData, [newStart, newEnd], xAccessor, updatedXScale);
156
+ initialPlotData = response.plotData;
157
+ updatedXScale.domain(response.domain); // if last item was visible, then shift
158
+ }
159
+ const plotData = postCalculator(initialPlotData);
160
+ const chartConfigs = getChartConfigWithUpdatedYScales(getNewChartConfig(dimensions, children, initialChartConfig), { plotData, xAccessor, displayXAccessor, fullData }, updatedXScale.domain());
161
+ return {
162
+ xScale: updatedXScale,
163
+ xAccessor,
164
+ chartConfigs,
165
+ plotData,
166
+ fullData,
167
+ filterData,
168
+ };
169
+ };
170
+ const calculateState = (props) => {
171
+ const { xAccessor: inputXAccessor, xExtents: xExtentsProp, data, padding, flipXScale } = props;
172
+ const direction = getXScaleDirection(flipXScale);
173
+ const dimensions = getDimensions(props);
174
+ const extent = typeof xExtentsProp === "function"
175
+ ? xExtentsProp(data)
176
+ : d3Extent(xExtentsProp.map((d) => functor(d)).map((each) => each(data, inputXAccessor)));
177
+ const { xAccessor, displayXAccessor, xScale, fullData, filterData } = calculateFullData(props);
178
+ const updatedXScale = setXRange(xScale, dimensions, padding, direction);
179
+ const { plotData, domain } = filterData(fullData, extent, inputXAccessor, updatedXScale);
180
+ return {
181
+ plotData,
182
+ xScale: updatedXScale.domain(domain),
183
+ xAccessor,
184
+ displayXAccessor,
185
+ fullData,
186
+ filterData,
187
+ };
188
+ };
189
+ const setXRange = (xScale, dimensions, padding, direction = 1) => {
190
+ if (xScale.rangeRoundPoints) {
191
+ if (isNaN(padding)) {
192
+ throw new Error("padding has to be a number for ordinal scale");
193
+ }
194
+ xScale.rangeRoundPoints([0, dimensions.width], padding);
195
+ }
196
+ else if (xScale.padding) {
197
+ if (isNaN(padding)) {
198
+ throw new Error("padding has to be a number for ordinal scale");
199
+ }
200
+ xScale.range([0, dimensions.width]);
201
+ xScale.padding(padding / 2);
202
+ }
203
+ else {
204
+ const { left, right } = isNaN(padding) ? padding : { left: padding, right: padding };
205
+ if (direction > 0) {
206
+ xScale.range([left, dimensions.width - right]);
207
+ }
208
+ else {
209
+ xScale.range([dimensions.width - right, left]);
210
+ }
211
+ }
212
+ return xScale;
213
+ };
214
+ const pinchCoordinates = (pinch) => {
215
+ const { touch1Pos, touch2Pos } = pinch;
216
+ return {
217
+ topLeft: [Math.min(touch1Pos[0], touch2Pos[0]), Math.min(touch1Pos[1], touch2Pos[1])],
218
+ bottomRight: [Math.max(touch1Pos[0], touch2Pos[0]), Math.max(touch1Pos[1], touch2Pos[1])],
219
+ };
220
+ };
221
+ const isInteractionEnabled = (xScale, xAccessor, data) => {
222
+ const interaction = !isNaN(xScale(xAccessor(head(data)))) && isDefined(xScale.invert);
223
+ return interaction;
224
+ };
225
+ export class ChartCanvas extends React.Component {
226
+ constructor(props) {
227
+ super(props);
228
+ this.canvasContainerRef = React.createRef();
229
+ this.eventCaptureRef = React.createRef();
230
+ this.lastSubscriptionId = 0;
231
+ this.mutableState = { mouseXY: [0, 0], currentCharts: [], currentItem: null };
232
+ this.panInProgress = false;
233
+ this.subscriptions = [];
234
+ this.getMutableState = () => {
235
+ return this.mutableState;
236
+ };
237
+ this.getCanvasContexts = () => {
238
+ var _a;
239
+ return (_a = this.canvasContainerRef.current) === null || _a === void 0 ? void 0 : _a.getCanvasContexts();
240
+ };
241
+ this.generateSubscriptionId = () => {
242
+ this.lastSubscriptionId++;
243
+ return this.lastSubscriptionId;
244
+ };
245
+ this.subscribe = (id, rest) => {
246
+ const { getPanConditions = functor({
247
+ draggable: false,
248
+ panEnabled: true,
249
+ }), } = rest;
250
+ this.subscriptions = this.subscriptions.concat(Object.assign(Object.assign({ id }, rest), { getPanConditions }));
251
+ };
252
+ this.unsubscribe = (id) => {
253
+ this.subscriptions = this.subscriptions.filter((each) => each.id !== id);
254
+ };
255
+ this.getAllPanConditions = () => {
256
+ return this.subscriptions.map((each) => each.getPanConditions());
257
+ };
258
+ this.setCursorClass = (className) => {
259
+ var _a;
260
+ (_a = this.eventCaptureRef.current) === null || _a === void 0 ? void 0 : _a.setCursorClass(className);
261
+ };
262
+ this.amIOnTop = (id) => {
263
+ const dragableComponents = this.subscriptions.filter((each) => each.getPanConditions().draggable);
264
+ return dragableComponents.length > 0 && last(dragableComponents).id === id;
265
+ };
266
+ this.handleContextMenu = (mouseXY, e) => {
267
+ const { xAccessor, chartConfigs, plotData, xScale } = this.state;
268
+ const currentCharts = getCurrentCharts(chartConfigs, mouseXY);
269
+ const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData);
270
+ this.triggerEvent("contextmenu", {
271
+ mouseXY,
272
+ currentItem,
273
+ currentCharts,
274
+ }, e);
275
+ };
276
+ this.calculateStateForDomain = (newDomain) => {
277
+ const { xAccessor, displayXAccessor, xScale: initialXScale, chartConfigs: initialChartConfig, plotData: initialPlotData, } = this.state;
278
+ const { filterData, fullData } = this.state;
279
+ const { postCalculator = ChartCanvas.defaultProps.postCalculator } = this.props;
280
+ const { plotData: beforePlotData, domain } = filterData(fullData, newDomain, xAccessor, initialXScale, {
281
+ currentPlotData: initialPlotData,
282
+ currentDomain: initialXScale.domain(),
283
+ });
284
+ const plotData = postCalculator(beforePlotData);
285
+ const updatedScale = initialXScale.copy().domain(domain);
286
+ const chartConfigs = getChartConfigWithUpdatedYScales(initialChartConfig, { plotData, xAccessor, displayXAccessor, fullData }, updatedScale.domain());
287
+ return {
288
+ xScale: updatedScale,
289
+ plotData,
290
+ chartConfigs,
291
+ };
292
+ };
293
+ this.pinchZoomHelper = (initialPinch, finalPinch) => {
294
+ const { xScale: initialPinchXScale } = initialPinch;
295
+ const { xScale: initialXScale, chartConfigs: initialChartConfig, plotData: initialPlotData, xAccessor, displayXAccessor, filterData, fullData, } = this.state;
296
+ const { postCalculator = ChartCanvas.defaultProps.postCalculator } = this.props;
297
+ const { topLeft: iTL, bottomRight: iBR } = pinchCoordinates(initialPinch);
298
+ const { topLeft: fTL, bottomRight: fBR } = pinchCoordinates(finalPinch);
299
+ const e = initialPinchXScale.range()[1];
300
+ const xDash = Math.round(-(iBR[0] * fTL[0] - iTL[0] * fBR[0]) / (iTL[0] - iBR[0]));
301
+ const yDash = Math.round(e + ((e - iBR[0]) * (e - fTL[0]) - (e - iTL[0]) * (e - fBR[0])) / (e - iTL[0] - (e - iBR[0])));
302
+ const x = Math.round((-xDash * iTL[0]) / (-xDash + fTL[0]));
303
+ const y = Math.round(e - ((yDash - e) * (e - iTL[0])) / (yDash + (e - fTL[0])));
304
+ const newDomain = [x, y].map(initialPinchXScale.invert);
305
+ const { plotData: beforePlotData, domain } = filterData(fullData, newDomain, xAccessor, initialPinchXScale, {
306
+ currentPlotData: initialPlotData,
307
+ currentDomain: initialXScale.domain(),
308
+ });
309
+ const plotData = postCalculator(beforePlotData);
310
+ const updatedScale = initialXScale.copy().domain(domain);
311
+ const mouseXY = finalPinch.touch1Pos;
312
+ const chartConfigs = getChartConfigWithUpdatedYScales(initialChartConfig, { plotData, xAccessor, displayXAccessor, fullData }, updatedScale.domain());
313
+ const currentItem = getCurrentItem(updatedScale, xAccessor, mouseXY, plotData);
314
+ return {
315
+ chartConfigs,
316
+ xScale: updatedScale,
317
+ plotData,
318
+ mouseXY,
319
+ currentItem,
320
+ xAccessor,
321
+ fullData,
322
+ };
323
+ };
324
+ this.handlePinchZoom = (initialPinch, finalPinch, e) => {
325
+ if (!this.waitingForPinchZoomAnimationFrame) {
326
+ this.waitingForPinchZoomAnimationFrame = true;
327
+ const state = this.pinchZoomHelper(initialPinch, finalPinch);
328
+ this.triggerEvent("pinchzoom", state, e);
329
+ this.finalPinch = finalPinch;
330
+ requestAnimationFrame(() => {
331
+ this.clearBothCanvas();
332
+ this.draw({ trigger: "pinchzoom" });
333
+ this.waitingForPinchZoomAnimationFrame = false;
334
+ });
335
+ }
336
+ };
337
+ this.handlePinchZoomEnd = (initialPinch, e) => {
338
+ const { xAccessor = ChartCanvas.defaultProps.xAccessor } = this.state;
339
+ if (this.finalPinch) {
340
+ const state = this.pinchZoomHelper(initialPinch, this.finalPinch);
341
+ const { xScale, fullData } = state;
342
+ this.triggerEvent("pinchzoom", state, e);
343
+ this.finalPinch = undefined;
344
+ this.clearThreeCanvas();
345
+ const firstItem = head(fullData);
346
+ const scale_start = head(xScale.domain());
347
+ const data_start = xAccessor(firstItem);
348
+ const lastItem = last(fullData);
349
+ const scale_end = last(xScale.domain());
350
+ const data_end = xAccessor(lastItem);
351
+ const { onLoadAfter, onLoadBefore } = this.props;
352
+ this.setState(state, () => {
353
+ if (scale_start < data_start) {
354
+ if (onLoadBefore !== undefined) {
355
+ onLoadBefore(scale_start, data_start);
356
+ }
357
+ }
358
+ if (data_end < scale_end) {
359
+ if (onLoadAfter !== undefined) {
360
+ onLoadAfter(data_end, scale_end);
361
+ }
362
+ }
363
+ });
364
+ }
365
+ };
366
+ this.handleZoom = (zoomDirection, mouseXY, e) => {
367
+ if (this.panInProgress) {
368
+ return;
369
+ }
370
+ const { xAccessor, xScale: initialXScale, plotData: initialPlotData, fullData } = this.state;
371
+ const { zoomMultiplier = ChartCanvas.defaultProps.zoomMultiplier, zoomAnchor = ChartCanvas.defaultProps.zoomAnchor, } = this.props;
372
+ const item = zoomAnchor({
373
+ xScale: initialXScale,
374
+ xAccessor: xAccessor,
375
+ mouseXY,
376
+ plotData: initialPlotData,
377
+ });
378
+ const cx = initialXScale(item);
379
+ const c = zoomDirection > 0 ? 1 * zoomMultiplier : 1 / zoomMultiplier;
380
+ const newDomain = initialXScale
381
+ .range()
382
+ .map((x) => cx + (x - cx) * c)
383
+ .map((x) => initialXScale.invert(x));
384
+ const { xScale, plotData, chartConfigs } = this.calculateStateForDomain(newDomain);
385
+ const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData);
386
+ const currentCharts = getCurrentCharts(chartConfigs, mouseXY);
387
+ this.clearThreeCanvas();
388
+ const firstItem = head(fullData);
389
+ const scale_start = head(xScale.domain());
390
+ const data_start = xAccessor(firstItem);
391
+ const lastItem = last(fullData);
392
+ const scale_end = last(xScale.domain());
393
+ const data_end = xAccessor(lastItem);
394
+ this.mutableState = {
395
+ mouseXY,
396
+ currentItem,
397
+ currentCharts,
398
+ };
399
+ this.triggerEvent("zoom", {
400
+ xScale,
401
+ plotData,
402
+ chartConfigs,
403
+ mouseXY,
404
+ currentCharts,
405
+ currentItem,
406
+ show: true,
407
+ }, e);
408
+ const { onLoadAfter, onLoadBefore } = this.props;
409
+ this.setState({
410
+ xScale,
411
+ plotData,
412
+ chartConfigs,
413
+ }, () => {
414
+ if (scale_start < data_start) {
415
+ if (onLoadBefore !== undefined) {
416
+ onLoadBefore(scale_start, data_start);
417
+ }
418
+ }
419
+ if (data_end < scale_end) {
420
+ if (onLoadAfter !== undefined) {
421
+ onLoadAfter(data_end, scale_end);
422
+ }
423
+ }
424
+ });
425
+ };
426
+ this.xAxisZoom = (newDomain) => {
427
+ const { xScale, plotData, chartConfigs } = this.calculateStateForDomain(newDomain);
428
+ this.clearThreeCanvas();
429
+ const { xAccessor, fullData } = this.state;
430
+ const firstItem = head(fullData);
431
+ const scale_start = head(xScale.domain());
432
+ const data_start = xAccessor(firstItem);
433
+ const lastItem = last(fullData);
434
+ const scale_end = last(xScale.domain());
435
+ const data_end = xAccessor(lastItem);
436
+ const { onLoadAfter, onLoadBefore } = this.props;
437
+ this.setState({
438
+ xScale,
439
+ plotData,
440
+ chartConfigs,
441
+ }, () => {
442
+ if (scale_start < data_start) {
443
+ if (onLoadBefore !== undefined) {
444
+ onLoadBefore(scale_start, data_start);
445
+ }
446
+ }
447
+ if (data_end < scale_end) {
448
+ if (onLoadAfter !== undefined) {
449
+ onLoadAfter(data_end, scale_end);
450
+ }
451
+ }
452
+ });
453
+ };
454
+ this.yAxisZoom = (chartId, newDomain) => {
455
+ this.clearThreeCanvas();
456
+ const { chartConfigs: initialChartConfig } = this.state;
457
+ const chartConfigs = initialChartConfig.map((each) => {
458
+ if (each.id === chartId) {
459
+ const { yScale } = each;
460
+ return Object.assign(Object.assign({}, each), { yScale: yScale.copy().domain(newDomain), yPanEnabled: true });
461
+ }
462
+ else {
463
+ return each;
464
+ }
465
+ });
466
+ this.setState({
467
+ chartConfigs,
468
+ });
469
+ };
470
+ this.draw = (props) => {
471
+ this.subscriptions.forEach((each) => {
472
+ if (isDefined(each.draw)) {
473
+ each.draw(props);
474
+ }
475
+ });
476
+ };
477
+ this.redraw = () => {
478
+ this.clearThreeCanvas();
479
+ this.draw({ force: true });
480
+ };
481
+ this.panHelper = (mouseXY, initialXScale, { dx, dy }, chartsToPan) => {
482
+ const { xAccessor, displayXAccessor, chartConfigs: initialChartConfig, filterData, fullData } = this.state;
483
+ const { postCalculator = ChartCanvas.defaultProps.postCalculator } = this.props;
484
+ const newDomain = initialXScale
485
+ .range()
486
+ .map((x) => x - dx)
487
+ .map((x) => initialXScale.invert(x));
488
+ const { plotData: beforePlotData, domain } = filterData(fullData, newDomain, xAccessor, initialXScale, {
489
+ currentPlotData: this.hackyWayToStopPanBeyondBounds__plotData,
490
+ currentDomain: this.hackyWayToStopPanBeyondBounds__domain,
491
+ ignoreThresholds: true,
492
+ });
493
+ const updatedScale = initialXScale.copy().domain(domain);
494
+ const plotData = postCalculator(beforePlotData);
495
+ const currentItem = getCurrentItem(updatedScale, xAccessor, mouseXY, plotData);
496
+ const chartConfigs = getChartConfigWithUpdatedYScales(initialChartConfig, { plotData, xAccessor, displayXAccessor, fullData }, updatedScale.domain(), dy, chartsToPan);
497
+ const currentCharts = getCurrentCharts(chartConfigs, mouseXY);
498
+ return {
499
+ xScale: updatedScale,
500
+ plotData,
501
+ chartConfigs,
502
+ mouseXY,
503
+ currentCharts,
504
+ currentItem,
505
+ };
506
+ };
507
+ this.handlePan = (mousePosition, panStartXScale, dxdy, chartsToPan, e) => {
508
+ var _a, _b;
509
+ if (this.waitingForPanAnimationFrame) {
510
+ return;
511
+ }
512
+ this.waitingForPanAnimationFrame = true;
513
+ this.hackyWayToStopPanBeyondBounds__plotData =
514
+ (_a = this.hackyWayToStopPanBeyondBounds__plotData) !== null && _a !== void 0 ? _a : this.state.plotData;
515
+ this.hackyWayToStopPanBeyondBounds__domain =
516
+ (_b = this.hackyWayToStopPanBeyondBounds__domain) !== null && _b !== void 0 ? _b : this.state.xScale.domain();
517
+ const newState = this.panHelper(mousePosition, panStartXScale, dxdy, chartsToPan);
518
+ this.hackyWayToStopPanBeyondBounds__plotData = newState.plotData;
519
+ this.hackyWayToStopPanBeyondBounds__domain = newState.xScale.domain();
520
+ this.panInProgress = true;
521
+ this.triggerEvent("pan", newState, e);
522
+ this.mutableState = {
523
+ mouseXY: newState.mouseXY,
524
+ currentItem: newState.currentItem,
525
+ currentCharts: newState.currentCharts,
526
+ };
527
+ requestAnimationFrame(() => {
528
+ this.waitingForPanAnimationFrame = false;
529
+ this.clearBothCanvas();
530
+ this.draw({ trigger: "pan" });
531
+ });
532
+ };
533
+ this.handlePanEnd = (mousePosition, panStartXScale, dxdy, chartsToPan, e) => {
534
+ const state = this.panHelper(mousePosition, panStartXScale, dxdy, chartsToPan);
535
+ this.hackyWayToStopPanBeyondBounds__plotData = null;
536
+ this.hackyWayToStopPanBeyondBounds__domain = null;
537
+ this.panInProgress = false;
538
+ const { xScale, plotData, chartConfigs } = state;
539
+ this.triggerEvent("panend", state, e);
540
+ requestAnimationFrame(() => {
541
+ const { xAccessor, fullData } = this.state;
542
+ const firstItem = head(fullData);
543
+ const scale_start = head(xScale.domain());
544
+ const data_start = xAccessor(firstItem);
545
+ const lastItem = last(fullData);
546
+ const scale_end = last(xScale.domain());
547
+ const data_end = xAccessor(lastItem);
548
+ const { onLoadAfter, onLoadBefore } = this.props;
549
+ this.clearThreeCanvas();
550
+ this.setState({
551
+ xScale,
552
+ plotData,
553
+ chartConfigs,
554
+ }, () => {
555
+ if (scale_start < data_start) {
556
+ if (onLoadBefore !== undefined) {
557
+ onLoadBefore(scale_start, data_start);
558
+ }
559
+ }
560
+ if (data_end < scale_end) {
561
+ if (onLoadAfter !== undefined) {
562
+ onLoadAfter(data_end, scale_end);
563
+ }
564
+ }
565
+ });
566
+ });
567
+ };
568
+ this.handleMouseDown = (_, __, e) => {
569
+ this.triggerEvent("mousedown", this.mutableState, e);
570
+ };
571
+ this.handleMouseEnter = (e) => {
572
+ this.triggerEvent("mouseenter", {
573
+ show: true,
574
+ }, e);
575
+ };
576
+ this.handleMouseMove = (mouseXY, _, e) => {
577
+ if (this.waitingForMouseMoveAnimationFrame) {
578
+ return;
579
+ }
580
+ this.waitingForMouseMoveAnimationFrame = true;
581
+ const { chartConfigs, plotData, xScale, xAccessor } = this.state;
582
+ const currentCharts = getCurrentCharts(chartConfigs, mouseXY);
583
+ const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData);
584
+ this.triggerEvent("mousemove", {
585
+ show: true,
586
+ mouseXY,
587
+ // prevMouseXY is used in interactive components
588
+ prevMouseXY: this.prevMouseXY,
589
+ currentItem,
590
+ currentCharts,
591
+ }, e);
592
+ this.prevMouseXY = mouseXY;
593
+ this.mutableState = {
594
+ mouseXY,
595
+ currentItem,
596
+ currentCharts,
597
+ };
598
+ requestAnimationFrame(() => {
599
+ this.clearMouseCanvas();
600
+ this.draw({ trigger: "mousemove" });
601
+ this.waitingForMouseMoveAnimationFrame = false;
602
+ });
603
+ };
604
+ this.handleMouseLeave = (e) => {
605
+ this.triggerEvent("mouseleave", { show: false }, e);
606
+ this.clearMouseCanvas();
607
+ this.draw({ trigger: "mouseleave" });
608
+ };
609
+ this.handleDragStart = ({ startPos }, e) => {
610
+ this.triggerEvent("dragstart", { startPos }, e);
611
+ };
612
+ this.handleDrag = ({ startPos, mouseXY }, e) => {
613
+ const { chartConfigs, plotData, xScale, xAccessor } = this.state;
614
+ const currentCharts = getCurrentCharts(chartConfigs, mouseXY);
615
+ const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData);
616
+ this.triggerEvent("drag", {
617
+ startPos,
618
+ mouseXY,
619
+ currentItem,
620
+ currentCharts,
621
+ }, e);
622
+ this.mutableState = {
623
+ mouseXY,
624
+ currentItem,
625
+ currentCharts,
626
+ };
627
+ requestAnimationFrame(() => {
628
+ this.clearMouseCanvas();
629
+ this.draw({ trigger: "drag" });
630
+ });
631
+ };
632
+ this.handleDragEnd = ({ mouseXY }, e) => {
633
+ this.triggerEvent("dragend", { mouseXY }, e);
634
+ requestAnimationFrame(() => {
635
+ this.clearMouseCanvas();
636
+ this.draw({ trigger: "dragend" });
637
+ });
638
+ };
639
+ this.handleClick = (_, e) => {
640
+ this.triggerEvent("click", this.mutableState, e);
641
+ requestAnimationFrame(() => {
642
+ this.clearMouseCanvas();
643
+ this.draw({ trigger: "click" });
644
+ });
645
+ };
646
+ this.handleDoubleClick = (_, e) => {
647
+ this.triggerEvent("dblclick", {}, e);
648
+ };
649
+ this.resetYDomain = (chartId) => {
650
+ const { chartConfigs } = this.state;
651
+ let changed = false;
652
+ const newChartConfig = chartConfigs.map((each) => {
653
+ if ((isNotDefined(chartId) || each.id === chartId) &&
654
+ !shallowEqual(each.yScale.domain(), each.realYDomain)) {
655
+ changed = true;
656
+ return Object.assign(Object.assign({}, each), { yScale: each.yScale.domain(each.realYDomain), yPanEnabled: false });
657
+ }
658
+ return each;
659
+ });
660
+ if (changed) {
661
+ this.clearThreeCanvas();
662
+ this.setState({
663
+ chartConfigs: newChartConfig,
664
+ });
665
+ }
666
+ };
667
+ this.state = resetChart(props);
668
+ }
669
+ static getDerivedStateFromProps(props, state) {
670
+ var _a;
671
+ const { chartConfigs: initialChartConfig, plotData, xAccessor, xScale } = state;
672
+ const interaction = isInteractionEnabled(xScale, xAccessor, plotData);
673
+ const shouldReset = shouldResetChart(state.lastProps || {}, props);
674
+ let newState;
675
+ if (!interaction || shouldReset || !shallowEqual((_a = state.lastProps) === null || _a === void 0 ? void 0 : _a.xExtents, props.xExtents)) {
676
+ // do reset
677
+ newState = resetChart(props);
678
+ }
679
+ else {
680
+ const [start, end] = xScale.domain();
681
+ const prevLastItem = last(state.fullData);
682
+ const calculatedState = calculateFullData(props);
683
+ const { xAccessor } = calculatedState;
684
+ const previousX = xAccessor(prevLastItem);
685
+ const lastItemWasVisible = previousX <= end && previousX >= start;
686
+ newState = updateChart(calculatedState, xScale, props, lastItemWasVisible, initialChartConfig);
687
+ }
688
+ return Object.assign(Object.assign({}, newState), { lastProps: props, propIteration: (state.propIteration || 0) + 1 });
689
+ }
690
+ getSnapshotBeforeUpdate(prevProps, prevState) {
691
+ // propIteration is incremented when the props change to differentiate between state updates
692
+ // and prop updates
693
+ if (prevState.propIteration !== this.state.propIteration && !this.panInProgress) {
694
+ this.clearThreeCanvas();
695
+ }
696
+ return null;
697
+ }
698
+ componentDidUpdate(prevProps) {
699
+ if (prevProps.data !== this.props.data) {
700
+ this.triggerEvent("dataupdated", {
701
+ chartConfigs: this.state.chartConfigs,
702
+ xScale: this.state.xScale,
703
+ plotData: this.state.plotData,
704
+ });
705
+ }
706
+ }
707
+ clearBothCanvas() {
708
+ const canvases = this.getCanvasContexts();
709
+ if (canvases && canvases.axes && canvases.mouseCoord) {
710
+ clearCanvas([canvases.axes, canvases.mouseCoord], this.props.ratio);
711
+ }
712
+ }
713
+ clearMouseCanvas() {
714
+ const canvases = this.getCanvasContexts();
715
+ if (canvases && canvases.mouseCoord) {
716
+ clearCanvas([canvases.mouseCoord], this.props.ratio);
717
+ }
718
+ }
719
+ clearThreeCanvas() {
720
+ const canvases = this.getCanvasContexts();
721
+ if (canvases && canvases.axes && canvases.mouseCoord && canvases.bg) {
722
+ clearCanvas([canvases.axes, canvases.mouseCoord, canvases.bg], this.props.ratio);
723
+ }
724
+ }
725
+ cancelDrag() {
726
+ var _a;
727
+ (_a = this.eventCaptureRef.current) === null || _a === void 0 ? void 0 : _a.cancelDrag();
728
+ this.triggerEvent("dragcancel");
729
+ }
730
+ triggerEvent(type, props, e) {
731
+ this.subscriptions.forEach((each) => {
732
+ const state = Object.assign(Object.assign({}, this.state), { subscriptions: this.subscriptions });
733
+ each.listener(type, props, state, e);
734
+ });
735
+ }
736
+ // TODO: Memoize this
737
+ getContextValues() {
738
+ const dimensions = getDimensions(this.props);
739
+ return {
740
+ chartId: -1,
741
+ fullData: this.state.fullData,
742
+ plotData: this.state.plotData,
743
+ width: dimensions.width,
744
+ height: dimensions.height,
745
+ chartConfigs: this.state.chartConfigs,
746
+ xScale: this.state.xScale,
747
+ xAccessor: this.state.xAccessor,
748
+ displayXAccessor: this.state.displayXAccessor,
749
+ margin: this.props.margin,
750
+ ratio: this.props.ratio,
751
+ xAxisZoom: this.xAxisZoom,
752
+ yAxisZoom: this.yAxisZoom,
753
+ getCanvasContexts: this.getCanvasContexts,
754
+ redraw: this.redraw,
755
+ subscribe: this.subscribe,
756
+ unsubscribe: this.unsubscribe,
757
+ generateSubscriptionId: this.generateSubscriptionId,
758
+ getMutableState: this.getMutableState,
759
+ amIOnTop: this.amIOnTop,
760
+ setCursorClass: this.setCursorClass,
761
+ };
762
+ }
763
+ shouldComponentUpdate() {
764
+ return !this.panInProgress;
765
+ }
766
+ render() {
767
+ const { disableInteraction, disablePan, disableZoom, useCrossHairStyleCursor, onClick, onDoubleClick, height, width, margin = ChartCanvas.defaultProps.margin, className, zIndex = ChartCanvas.defaultProps.zIndex, defaultFocus, ratio, mouseMoveEvent, } = this.props;
768
+ const { plotData, xScale, xAccessor, chartConfigs } = this.state;
769
+ const dimensions = getDimensions(this.props);
770
+ const interaction = isInteractionEnabled(xScale, xAccessor, plotData);
771
+ const cursorStyle = useCrossHairStyleCursor && interaction;
772
+ const cursor = getCursorStyle();
773
+ return (React.createElement(ChartCanvasContext.Provider, { value: this.getContextValues() },
774
+ React.createElement("div", { style: { position: "relative", width, height }, className: className, onClick: onClick, onDoubleClick: onDoubleClick },
775
+ React.createElement(CanvasContainer, { ref: this.canvasContainerRef, ratio: ratio, width: width, height: height, style: { height, zIndex, width } }),
776
+ React.createElement("svg", { className: className, width: width, height: height, style: { position: "absolute", zIndex: zIndex + 5 } },
777
+ cursor,
778
+ React.createElement("defs", null,
779
+ React.createElement("clipPath", { id: "chart-area-clip" },
780
+ React.createElement("rect", { x: "0", y: "0", width: dimensions.width, height: dimensions.height })),
781
+ chartConfigs.map((each, idx) => (React.createElement("clipPath", { key: idx, id: `chart-area-clip-${each.id}` },
782
+ React.createElement("rect", { x: "0", y: "0", width: each.width, height: each.height }))))),
783
+ React.createElement("g", { transform: `translate(${margin.left + 0.5}, ${margin.top + 0.5})` },
784
+ React.createElement(EventCapture, { ref: this.eventCaptureRef, useCrossHairStyleCursor: cursorStyle, mouseMove: mouseMoveEvent && interaction, zoom: !disableZoom && interaction, pan: !disablePan && interaction, width: dimensions.width, height: dimensions.height, chartConfig: chartConfigs, xScale: xScale, xAccessor: xAccessor, focus: defaultFocus, disableInteraction: disableInteraction, getAllPanConditions: this.getAllPanConditions, onContextMenu: this.handleContextMenu, onClick: this.handleClick, onDoubleClick: this.handleDoubleClick, onMouseDown: this.handleMouseDown, onMouseMove: this.handleMouseMove, onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, onDragStart: this.handleDragStart, onDrag: this.handleDrag, onDragComplete: this.handleDragEnd, onZoom: this.handleZoom, onPinchZoom: this.handlePinchZoom, onPinchZoomEnd: this.handlePinchZoomEnd, onPan: this.handlePan, onPanEnd: this.handlePanEnd }),
785
+ React.createElement("g", { className: "react-financial-charts-avoid-interaction" }, this.props.children))))));
786
+ }
787
+ }
788
+ ChartCanvas.defaultProps = {
789
+ clamp: false,
790
+ className: "react-financial-charts",
791
+ defaultFocus: true,
792
+ disablePan: false,
793
+ disableInteraction: false,
794
+ disableZoom: false,
795
+ flipXScale: false,
796
+ maintainPointsPerPixelOnResize: true,
797
+ margin: { top: 0, right: 40, bottom: 40, left: 0 },
798
+ minPointsPerPxThreshold: 1 / 100,
799
+ mouseMoveEvent: true,
800
+ postCalculator: identity,
801
+ padding: 0,
802
+ pointsPerPxThreshold: 2,
803
+ useCrossHairStyleCursor: true,
804
+ xAccessor: identity,
805
+ xExtents: [min, max],
806
+ zIndex: 1,
807
+ zoomAnchor: mouseBasedZoomAnchor,
808
+ zoomMultiplier: 1.1,
809
+ };
810
+ //# sourceMappingURL=ChartCanvas.js.map