@devexperts/dxcharts-lite 1.0.2 → 2.0.1
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.
- package/README.md +2 -2
- package/dist/chart/animation/canvas-animation.js +134 -1
- package/dist/chart/animation/types/animation.js +43 -1
- package/dist/chart/animation/types/color-alpha-animation.d.ts +1 -1
- package/dist/chart/animation/types/color-alpha-animation.js +60 -1
- package/dist/chart/animation/types/color-transition-animation.d.ts +1 -1
- package/dist/chart/animation/types/color-transition-animation.js +86 -1
- package/dist/chart/animation/types/viewport-movement-animation.js +63 -1
- package/dist/chart/animation/viewport-model-animation.js +20 -1
- package/dist/chart/bootstrap.d.ts +51 -48
- package/dist/chart/bootstrap.js +449 -1
- package/dist/chart/canvas/canvas-bounds-container.d.ts +0 -5
- package/dist/chart/canvas/canvas-bounds-container.js +815 -1
- package/dist/chart/canvas/canvas-chart-html.js +16 -18
- package/dist/chart/canvas/chart-elements.d.ts +1 -4
- package/dist/chart/canvas/chart-elements.js +22 -1
- package/dist/chart/canvas/cursor.handler.js +122 -1
- package/dist/chart/canvas/layout-creator.js +34 -1
- package/dist/chart/canvas/y-axis-bounds.container.d.ts +2 -2
- package/dist/chart/canvas/y-axis-bounds.container.js +67 -1
- package/dist/chart/chart-container.js +1 -1
- package/dist/chart/chart.config.d.ts +4 -5
- package/dist/chart/chart.config.js +645 -1
- package/dist/chart/chart.d.ts +62 -0
- package/dist/chart/chart.js +106 -0
- package/dist/chart/components/chart/basic-scale.d.ts +2 -2
- package/dist/chart/components/chart/basic-scale.js +31 -1
- package/dist/chart/components/chart/candle-transformer.functions.js +15 -1
- package/dist/chart/components/chart/candle-width-calculator.functions.js +2 -1
- package/dist/chart/components/chart/candle.functions.js +44 -1
- package/dist/chart/components/chart/chart-area-pan.handler.d.ts +3 -3
- package/dist/chart/components/chart/chart-area-pan.handler.js +197 -1
- package/dist/chart/components/chart/chart-base.model.js +66 -1
- package/dist/chart/components/chart/chart.component.d.ts +7 -3
- package/dist/chart/components/chart/chart.component.js +408 -1
- package/dist/chart/components/chart/chart.model.d.ts +4 -11
- package/dist/chart/components/chart/chart.model.js +954 -1
- package/dist/chart/components/chart/data-series.high-low-provider.js +34 -1
- package/dist/chart/components/chart/fake-candles.js +97 -1
- package/dist/chart/components/chart/price.formatter.js +35 -1
- package/dist/chart/components/chart/secondary-chart-colors-pool.js +68 -1
- package/dist/chart/components/cross_tool/cross-tool.component.js +85 -1
- package/dist/chart/components/cross_tool/cross-tool.drawer.js +32 -1
- package/dist/chart/components/cross_tool/cross-tool.model.js +107 -1
- package/dist/chart/components/cross_tool/types/cross-and-labels.drawer.js +137 -1
- package/dist/chart/components/cross_tool/types/none.drawer.js +3 -1
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-x.component.js +28 -1
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-y.component.js +24 -1
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop.component.js +53 -1
- package/dist/chart/components/dynamic-objects/dynamic-objects.component.d.ts +13 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.component.js +20 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.drawer.d.ts +18 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.drawer.js +23 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.model.d.ts +63 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.model.js +136 -0
- package/dist/chart/components/events/events-hit-test.drawer.js +63 -1
- package/dist/chart/components/events/events.component.js +61 -1
- package/dist/chart/components/events/events.drawer.js +195 -1
- package/dist/chart/components/events/events.model.js +69 -1
- package/dist/chart/components/grid/grid.component.d.ts +2 -2
- package/dist/chart/components/grid/grid.component.js +31 -1
- package/dist/chart/components/grid/grid.drawer.d.ts +4 -3
- package/dist/chart/components/grid/grid.drawer.js +106 -1
- package/dist/chart/components/high_low/high-low.component.js +12 -1
- package/dist/chart/components/high_low/high-low.drawer.js +126 -1
- package/dist/chart/components/highlights/highlights.component.js +46 -1
- package/dist/chart/components/highlights/highlights.drawer.js +154 -1
- package/dist/chart/components/highlights/highlights.model.js +75 -1
- package/dist/chart/components/labels_generator/numeric-axis-labels.generator.d.ts +1 -1
- package/dist/chart/components/labels_generator/numeric-axis-labels.generator.js +199 -1
- package/dist/chart/components/navigation_map/navigation-map-move.handler.d.ts +2 -2
- package/dist/chart/components/navigation_map/navigation-map-move.handler.js +108 -1
- package/dist/chart/components/navigation_map/navigation-map.component.js +112 -1
- package/dist/chart/components/navigation_map/navigation-map.drawer.js +244 -1
- package/dist/chart/components/navigation_map/navigation-map.model.js +33 -2
- package/dist/chart/components/pan/chart-pan.component.d.ts +2 -2
- package/dist/chart/components/pan/chart-pan.component.js +51 -1
- package/dist/chart/components/pane/extent/y-extent-component.d.ts +12 -18
- package/dist/chart/components/pane/extent/y-extent-component.js +120 -1
- package/dist/chart/components/pane/pane-hit-test.controller.d.ts +2 -2
- package/dist/chart/components/pane/pane-hit-test.controller.js +55 -1
- package/dist/chart/components/pane/pane-manager.component.d.ts +12 -6
- package/dist/chart/components/pane/pane-manager.component.js +149 -1
- package/dist/chart/components/pane/pane.component.d.ts +16 -32
- package/dist/chart/components/pane/pane.component.js +278 -1
- package/dist/chart/components/resizer/bar-resizer.component.js +139 -1
- package/dist/chart/components/resizer/bar-resizer.drawer.js +46 -1
- package/dist/chart/components/snapshot/snapshot.component.js +48 -1
- package/dist/chart/components/volumes/separate-volumes.component.d.ts +6 -10
- package/dist/chart/components/volumes/separate-volumes.component.js +63 -1
- package/dist/chart/components/volumes/volume-color-resolvers.functions.js +18 -1
- package/dist/chart/components/volumes/volumes.component.d.ts +6 -7
- package/dist/chart/components/volumes/volumes.component.js +94 -1
- package/dist/chart/components/volumes/volumes.drawer.d.ts +5 -12
- package/dist/chart/components/volumes/volumes.drawer.js +140 -1
- package/dist/chart/components/volumes/volumes.formatter.js +31 -1
- package/dist/chart/components/volumes/volumes.model.d.ts +2 -2
- package/dist/chart/components/volumes/volumes.model.js +43 -1
- package/dist/chart/components/watermark/water-mark.component.js +103 -1
- package/dist/chart/components/watermark/water-mark.drawer.js +187 -1
- package/dist/chart/components/x_axis/numeric-x-axis-labels.generator.js +27 -1
- package/dist/chart/components/x_axis/time/parser/time-formats-matchers.functions.js +187 -1
- package/dist/chart/components/x_axis/time/parser/time-formats-parser.functions.js +76 -1
- package/dist/chart/components/x_axis/time/parser/time-formats-validators.functions.js +79 -1
- package/dist/chart/components/x_axis/time/parser/time-formats.model.js +14 -1
- package/dist/chart/components/x_axis/time/x-axis-weights.functions.d.ts +1 -1
- package/dist/chart/components/x_axis/time/x-axis-weights.functions.js +143 -1
- package/dist/chart/components/x_axis/time/x-axis-weights.generator.js +73 -1
- package/dist/chart/components/x_axis/x-axis-draw.functions.js +49 -1
- package/dist/chart/components/x_axis/x-axis-labels.drawer.js +70 -1
- package/dist/chart/components/x_axis/x-axis-labels.generator.d.ts +2 -2
- package/dist/chart/components/x_axis/x-axis-labels.generator.js +332 -1
- package/dist/chart/components/x_axis/x-axis-labels.model.js +35 -1
- package/dist/chart/components/x_axis/x-axis-scale.handler.d.ts +2 -2
- package/dist/chart/components/x_axis/x-axis-scale.handler.js +60 -1
- package/dist/chart/components/x_axis/x-axis-time-labels.drawer.js +82 -1
- package/dist/chart/components/x_axis/x-axis.component.d.ts +2 -2
- package/dist/chart/components/x_axis/x-axis.component.js +119 -1
- package/dist/chart/components/y_axis/label-color.functions.js +53 -1
- package/dist/chart/components/y_axis/numeric-y-axis-labels.generator.d.ts +4 -4
- package/dist/chart/components/y_axis/numeric-y-axis-labels.generator.js +33 -1
- package/dist/chart/components/y_axis/price_labels/data-series-y-axis-labels.provider.d.ts +3 -3
- package/dist/chart/components/y_axis/price_labels/data-series-y-axis-labels.provider.js +67 -1
- package/dist/chart/components/y_axis/price_labels/labels-positions-calculator.js +56 -1
- package/dist/chart/components/y_axis/price_labels/last-candle-labels.provider.d.ts +4 -3
- package/dist/chart/components/y_axis/price_labels/last-candle-labels.provider.js +96 -1
- package/dist/chart/components/y_axis/price_labels/price-label.drawer.d.ts +2 -2
- package/dist/chart/components/y_axis/price_labels/price-label.drawer.js +119 -1
- package/dist/chart/components/y_axis/price_labels/y-axis-labels.model.d.ts +9 -8
- package/dist/chart/components/y_axis/price_labels/y-axis-labels.model.js +201 -1
- package/dist/chart/components/y_axis/price_labels/y-axis-price-labels.drawer.d.ts +6 -7
- package/dist/chart/components/y_axis/price_labels/y-axis-price-labels.drawer.js +58 -1
- package/dist/chart/components/y_axis/y-axis-base-labels.model.d.ts +6 -4
- package/dist/chart/components/y_axis/y-axis-base-labels.model.js +47 -1
- package/dist/chart/components/y_axis/y-axis-labels.drawer.d.ts +4 -4
- package/dist/chart/components/y_axis/y-axis-labels.drawer.js +161 -1
- package/dist/chart/components/y_axis/y-axis-scale.handler.d.ts +2 -2
- package/dist/chart/components/y_axis/y-axis-scale.handler.js +72 -1
- package/dist/chart/components/y_axis/y-axis.component.d.ts +32 -41
- package/dist/chart/components/y_axis/y-axis.component.js +220 -1
- package/dist/chart/components/y_axis/y-axis.drawer.d.ts +9 -12
- package/dist/chart/components/y_axis/y-axis.drawer.js +96 -1
- package/dist/chart/components/y_axis/y-axis.model.d.ts +10 -17
- package/dist/chart/components/y_axis/y-axis.model.js +34 -1
- package/dist/chart/drawers/chart-background.drawer.js +69 -1
- package/dist/chart/drawers/chart-type-drawers/area.drawer.js +69 -1
- package/dist/chart/drawers/chart-type-drawers/bar.drawer.js +57 -1
- package/dist/chart/drawers/chart-type-drawers/baseline.drawer.js +84 -1
- package/dist/chart/drawers/chart-type-drawers/candle.drawer.js +162 -1
- package/dist/chart/drawers/chart-type-drawers/histogram.drawer.js +57 -1
- package/dist/chart/drawers/chart-type-drawers/line.drawer.js +38 -1
- package/dist/chart/drawers/chart-type-drawers/scatter-plot.drawer.js +19 -1
- package/dist/chart/drawers/clear-canvas.drawer.js +14 -1
- package/dist/chart/drawers/composite.drawer.js +67 -1
- package/dist/chart/drawers/data-series-drawers/candle-series-wrapper.js +50 -1
- package/dist/chart/drawers/data-series-drawers/color-candle.drawer.js +35 -1
- package/dist/chart/drawers/data-series-drawers/data-series-drawers.utils.js +16 -1
- package/dist/chart/drawers/data-series-drawers/difference-cloud.drawer.js +106 -1
- package/dist/chart/drawers/data-series-drawers/histogram.drawer.js +23 -1
- package/dist/chart/drawers/data-series-drawers/linear.drawer.js +19 -1
- package/dist/chart/drawers/data-series-drawers/points.drawer.js +19 -1
- package/dist/chart/drawers/data-series-drawers/rectangular-tool.drawer.js +7 -1
- package/dist/chart/drawers/data-series-drawers/text.drawer.js +25 -1
- package/dist/chart/drawers/data-series-drawers/trend-histogram.drawer.js +35 -1
- package/dist/chart/drawers/data-series-drawers/triangle.drawer.js +22 -1
- package/dist/chart/drawers/data-series.drawer.d.ts +5 -7
- package/dist/chart/drawers/data-series.drawer.js +56 -1
- package/dist/chart/drawers/drawing-manager.d.ts +1 -1
- package/dist/chart/drawers/drawing-manager.js +177 -1
- package/dist/chart/drawers/ht-data-series.drawer.js +37 -1
- package/dist/chart/events/event-bus.js +73 -1
- package/dist/chart/events/events.js +3 -1
- package/dist/chart/inputhandlers/candle-tap.handler.js +46 -1
- package/dist/chart/inputhandlers/chart-resize.handler.js +98 -1
- package/dist/chart/inputhandlers/cross-event-producer.component.js +67 -1
- package/dist/chart/inputhandlers/hover-producer.component.d.ts +2 -2
- package/dist/chart/inputhandlers/hover-producer.component.js +228 -1
- package/dist/chart/inputhandlers/main-canvas-touch.handler.d.ts +2 -2
- package/dist/chart/inputhandlers/main-canvas-touch.handler.js +80 -1
- package/dist/chart/inputlisteners/canvas-input-listener.component.js +629 -1
- package/dist/chart/model/baseline.model.js +88 -1
- package/dist/chart/model/bounds.model.js +1 -1
- package/dist/chart/model/candle-hover.js +70 -1
- package/dist/chart/model/candle-series-high-low.provider.js +45 -1
- package/dist/chart/model/candle-series.model.d.ts +3 -3
- package/dist/chart/model/candle-series.model.js +245 -1
- package/dist/chart/model/candle.model.js +69 -1
- package/dist/chart/model/canvas.model.js +228 -1
- package/dist/chart/model/chart-base-element.js +129 -1
- package/dist/chart/model/compare-series-hover.js +25 -1
- package/dist/chart/model/data-series-view.d.ts +5 -5
- package/dist/chart/model/data-series-view.js +111 -1
- package/dist/chart/model/data-series.config.js +14 -1
- package/dist/chart/model/data-series.model.d.ts +4 -4
- package/dist/chart/model/data-series.model.js +281 -1
- package/dist/chart/model/date-time.formatter.js +151 -1
- package/dist/chart/model/hit-test-canvas.model.d.ts +1 -1
- package/dist/chart/model/hit-test-canvas.model.js +256 -1
- package/dist/chart/model/main-candle-series.model.d.ts +1 -1
- package/dist/chart/model/main-candle-series.model.js +29 -1
- package/dist/chart/model/scale.model.d.ts +3 -2
- package/dist/chart/model/scale.model.js +316 -1
- package/dist/chart/model/scaling/auto-scale.model.d.ts +2 -4
- package/dist/chart/model/scaling/auto-scale.model.js +91 -1
- package/dist/chart/model/scaling/constrait.functions.js +61 -1
- package/dist/chart/model/scaling/lock-ratio.model.js +17 -1
- package/dist/chart/model/scaling/move-chart.functions.js +22 -1
- package/dist/chart/model/scaling/viewport.model.d.ts +2 -2
- package/dist/chart/model/scaling/viewport.model.js +279 -1
- package/dist/chart/model/scaling/x-zooming.functions.js +35 -1
- package/dist/chart/model/time-zone.model.js +109 -1
- package/dist/chart/model/visual-candle.js +134 -1
- package/dist/chart/utils/__tests__/array.utils.test.js +135 -1
- package/dist/chart/utils/__tests__/math.utils.test.js +14 -1
- package/dist/chart/utils/__tests__/object.utils.test.js +11 -1
- package/dist/chart/utils/__tests__/priceIncrementsUtils.test.js +29 -1
- package/dist/chart/utils/array.utils.d.ts +1 -1
- package/dist/chart/utils/array.utils.js +269 -1
- package/dist/chart/utils/auto-period-detector.utils.js +39 -1
- package/dist/chart/utils/candles-generator-ts.utils.js +20 -1
- package/dist/chart/utils/candles-generator.utils.js +161 -1
- package/dist/chart/utils/candles.utils.js +42 -1
- package/dist/chart/utils/canvas/canvas-drawing-functions.utils.js +144 -1
- package/dist/chart/utils/canvas/canvas-font-measure-tool.utils.js +51 -1
- package/dist/chart/utils/canvas/canvas-text-functions.utils.js +156 -1
- package/dist/chart/utils/color.utils.js +31 -1
- package/dist/chart/utils/device/browser.utils.js +10 -1
- package/dist/chart/utils/device/device-detector.utils.js +24 -1
- package/dist/chart/utils/device/device-pixel-ratio.utils.js +21 -1
- package/dist/chart/utils/device/touchpad.utils.js +91 -1
- package/dist/chart/utils/dom.utils.js +15 -1
- package/dist/chart/utils/function.utils.js +16 -1
- package/dist/chart/utils/linkedList.utils.d.ts +28 -0
- package/dist/chart/utils/linkedList.utils.js +138 -0
- package/dist/chart/utils/math.utils.js +115 -1
- package/dist/chart/utils/merge.utils.js +56 -1
- package/dist/chart/utils/object.utils.js +43 -1
- package/dist/chart/utils/performance/animation-frame-cache.utils.js +77 -0
- package/dist/chart/utils/{perfomance → performance}/debounce.utils.js +7 -1
- package/dist/chart/utils/performance/memoize.utils.js +74 -0
- package/dist/chart/utils/performance/request-animation-frame-throttle.utils.js +43 -0
- package/dist/chart/utils/performance/throttle.utils.js +26 -0
- package/dist/chart/utils/price-increments.utils.d.ts +1 -0
- package/dist/chart/utils/price-increments.utils.js +127 -1
- package/dist/chart/utils/uuid.utils.js +4 -1
- package/dist/dxchart.min.js +9 -12
- package/dist/index.d.ts +3 -3
- package/dist/index.js +20 -1
- package/package.json +4 -4
- package/dist/chart/utils/perfomance/animation-frame-cache.utils.js +0 -6
- package/dist/chart/utils/perfomance/memoize.utils.js +0 -6
- package/dist/chart/utils/perfomance/request-animation-frame-throttle.utils.js +0 -6
- package/dist/chart/utils/perfomance/throttle.utils.js +0 -6
- /package/dist/chart/utils/{perfomance → performance}/animation-frame-cache.utils.d.ts +0 -0
- /package/dist/chart/utils/{perfomance → performance}/debounce.utils.d.ts +0 -0
- /package/dist/chart/utils/{perfomance → performance}/memoize.utils.d.ts +0 -0
- /package/dist/chart/utils/{perfomance → performance}/request-animation-frame-throttle.utils.d.ts +0 -0
- /package/dist/chart/utils/{perfomance → performance}/throttle.utils.d.ts +0 -0
|
@@ -3,4 +3,818 @@
|
|
|
3
3
|
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
4
4
|
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
5
|
*/
|
|
6
|
-
import{BehaviorSubject as F,Subject as E}from"rxjs";import{distinctUntilChanged as z,map as k}from"rxjs/operators";import{arrayCompare as Y,arrayRemove2 as K,flat as Z,moveInArrayMutable as H,reorderArray as G}from"../utils/array.utils";import{isMobile as W}from"../utils/device/browser.utils";import{calcTimeLabelBounds as q}from"../components/navigation_map/navigation-map.model";import{calculateSymbolHeight as $}from"../utils/canvas/canvas-font-measure-tool.utils";import{YAxisBoundsContainer as J}from"./y-axis-bounds.container";export const CHART_UUID="CHART";class s{}s.CANVAS="CANVAS",s.N_MAP="N_MAP",s.X_AXIS="X_AXIS",s.N_MAP_KNOT_L="N_MAP_KNOT_L",s.N_MAP_KNOT_R="N_MAP_KNOT_R",s.N_MAP_BTN_L="N_MAP_BTN_L",s.N_MAP_BTN_R="N_MAP_BTN_R",s.N_MAP_SLIDER_WINDOW="N_MAP_SLIDER_WINDOW",s.N_MAP_CHART="N_MAP_CHART",s.N_MAP_LABEL_R="N_MAP_LABEL_R",s.N_MAP_LABEL_L="N_MAP_LABEL_L",s.PANE_UUID=c=>"PANE_"+c,s.PANE_UUID_Y_AXIS=(c,t=0)=>"PANE_"+c+"_Y_AXIS_"+t,s.PANE_UUID_RESIZER=c=>"PANE_"+c+"_RESIZER",s.ALL_PANES="ALL_PANES",s.CHART_WITH_Y_AXIS="CHART_WITH_Y_AXIS",s.EVENTS="EVENTS",s.CHART=s.PANE_UUID(CHART_UUID),s.Y_AXIS=s.PANE_UUID_Y_AXIS(CHART_UUID);export{s as CanvasElement};export const DEFAULT_BOUNDS={x:0,y:0,pageX:0,pageY:0,width:0,height:0};const Q=20,j=35,I=15,V=1.5,M=W()?8*V:8,tt=1e-6;export class CanvasBoundsContainer{get graphsHeightRatio(){return this._graphsHeightRatio}constructor(t,e,i,h,n){this.config=t,this.eventBus=e,this.canvasModel=i,this.formatterFactory=h,this.bounds={},this.canvasOnPageLocation={x:0,y:0,pageX:0,pageY:0,width:0,height:0},this.panesOrder=[],this.panesOrderChangedSubject=new E,this.xAxisHeight=void 0,this.yAxisWidths={right:[0],left:[0]},this.leftRatio=0,this.rightRatio=0,this.boundsChangedSubject=new E,this.barResizerChangedSubject=new E,this._graphsHeightRatio={chart:1},this.graphsHeightRatioChangedSubject=new E,this.boundsChangedSubscriptions={},n.canvasResized.subscribe(r=>{let a=r;a||(a=this.canvasModel.canvas.getBoundingClientRect()),this.updateCanvasOnPageLocation(a),this.recalculateBounds()}),this.yAxisBoundsContainer=new J(this.config,this.canvasModel)}updateYAxisWidths(){const t=this.yAxisBoundsContainer.getYAxisWidths();this.setYAxisWidths(t)}addPaneBounds(t,e){if(this.panesOrder.indexOf(t)===-1){if(this.panesOrder.push(t),e!==void 0){const i=this.panesOrder.indexOf(t);H(this.panesOrder,i,e)}this.recalculatePanesHeightRatios(),this.panesOrderChangedSubject.next(this.panesOrder)}}overrideChartHeightRatios(t){const e=Object.assign(Object.assign({},this.graphsHeightRatio),t),i=Object.values(e).reduce((h,n)=>h+n,0);Math.abs(i-1)<tt?(this._graphsHeightRatio=e,this.recalculateBounds()):console.error(`Result ratio should be equal 1, but equal ${i}`)}movePaneUp(t){const e=this.panesOrder.indexOf(t);e!==-1&&(H(this.panesOrder,e,e-1),this.recalculateBounds(),this.eventBus.fireDraw(),this.panesOrderChangedSubject.next(this.panesOrder))}movePaneDown(t){const e=this.panesOrder.indexOf(t);e!==-1&&(H(this.panesOrder,e,e+1),this.recalculateBounds(),this.eventBus.fireDraw(),this.panesOrderChangedSubject.next(this.panesOrder))}reorderPanes(t){this.panesOrder=G(this.panesOrder,t),this.recalculateBounds(),this.panesOrderChangedSubject.next(this.panesOrder)}removedPaneBounds(t){K(this.panesOrder,t),delete this.graphsHeightRatio[t],delete this.bounds[s.PANE_UUID(t)],this.yAxisWidths.left.concat(this.yAxisWidths.right).forEach((e,i)=>delete this.bounds[s.PANE_UUID_Y_AXIS(t,i)]),delete this.bounds[s.PANE_UUID_RESIZER(t)],this.recalculatePanesHeightRatios(),this.panesOrderChangedSubject.next(this.panesOrder)}recalculateBounds(){const t=this.canvasOnPageLocation.width,e=this.canvasOnPageLocation.height,i=this.config.components.paneResizer.height,h=this.getBounds(s.CANVAS);h.x=0,h.y=0,h.width=t,h.height=e;const n=this.getYAxisWidth(),r=this.getNavMapBounds(h),a=this.getXAxisBounds(r,h),o=e-a.height-r.height,l=n.left.reduce((u,_)=>u+_,0),f=n.right.reduce((u,_)=>u+_,0),g=0,O=h.width-f,A=g+l,b=0,m=h.width-l-f;let p=b;this.panesOrder.forEach((u,_)=>{var N;const x=this.graphsHeightRatio[this.panesOrder[_]],B=this.config.components.paneResizer.visible;B&&(_!==0?v(this.bounds,s.PANE_UUID_RESIZER(u),0,p,h.width,i,this.canvasOnPageLocation):v(this.bounds,s.PANE_UUID_RESIZER(u),0,0,0,0,this.canvasOnPageLocation));const C=v(this.bounds,s.PANE_UUID(u),A,B?p+i:p,m,B?o*x-this.config.components.paneResizer.height:o*x,this.canvasOnPageLocation);if(this.config.components.yAxis.visible){const S=this.yAxisBoundsContainer.extentsOrder.get(u);if(S===void 0)return;const T=B?p+i:p,U=B?o*x-i:o*x;let X=A-((N=n.left[0])!==null&&N!==void 0?N:0),D=O;S.left.forEach((L,y)=>{var w;v(this.bounds,s.PANE_UUID_Y_AXIS(u,L),X,T,n.left[y],U,this.canvasOnPageLocation),X-=(w=n.left[y+1])!==null&&w!==void 0?w:0}),S.right.forEach((L,y)=>{v(this.bounds,s.PANE_UUID_Y_AXIS(u,L),D,T,n.right[y],U,this.canvasOnPageLocation),D+=n.right[y]})}else v(this.bounds,s.PANE_UUID_Y_AXIS(u),0,0,0,0,this.canvasOnPageLocation);p=C.y+C.height});const d=this.getBounds(s.ALL_PANES);d.x=A,d.y=b,d.width=m,d.height=p;const P=this.getBounds(s.CHART_WITH_Y_AXIS),R=this.getBounds(s.PANE_UUID(CHART_UUID));this.getEventsBounds(R),this.copyBounds(R,P),P.width=h.width,this.recalculateNavigationMapElementBounds(),this.updateAllBoundsPageCoordinates(),this.notifyBoundsSubscribers()}updateCanvasOnPageLocation(t){this.canvasOnPageLocation=Object.assign(Object.assign({},this.canvasOnPageLocation),{x:t.x,y:t.y,width:t.width,height:t.height})}updateAllBoundsPageCoordinates(){for(const t of Object.keys(this.bounds)){const e=this.bounds[t];e.pageX=e.x+this.canvasOnPageLocation.x,e.pageY=e.y+this.canvasOnPageLocation.y}}getEventsBounds(t){const e=this.getBounds(s.EVENTS);return this.config.components.events.visible?(e.x=0,e.y=t.y+t.height-this.config.components.events.height,e.width=t.width,e.height=this.config.components.events.height):this.applyDefaultBounds(e),e}getNavMapBounds(t){const e=this.getBounds(s.N_MAP);return this.config.components.navigationMap.visible?(e.x=0,e.y=t.height-j,e.width=t.width,e.height=j):this.applyDefaultBounds(e),e}getXAxisBounds(t,e){const i=this.getBounds(s.X_AXIS);return this.config.components.xAxis.visible?(i.x=0,i.y=e.height-this.getXAxisHeight()-t.height,i.width=e.width,i.height=this.getXAxisHeight()):this.applyDefaultBounds(i),i}getXAxisHeight(){var t,e;if(!this.xAxisHeight){const i=this.config.components.xAxis.fontSize+"px "+this.config.components.xAxis.fontFamily,h=$(i,this.canvasModel.ctx);this.xAxisHeight=h+((t=this.config.components.xAxis.padding.top)!==null&&t!==void 0?t:0)+((e=this.config.components.xAxis.padding.bottom)!==null&&e!==void 0?e:0)}return this.xAxisHeight}setYAxisWidths(t){(!Y(this.yAxisWidths.left,t.left)||!Y(this.yAxisWidths.right,t.right))&&(this.yAxisWidths=t,this.recalculateBounds())}setXAxisHeight(t){t!==this.xAxisHeight&&(this.xAxisHeight=t,this.recalculateBounds())}setPanesOrder(t){this.panesOrder=t,this.recalculateBounds()}getYAxisWidth(){return this.config.components.yAxis.visible?this.yAxisWidths:{right:[0],left:[0]}}recalculatePanesHeightRatios(){this.calculateGraphsHeightRatios(),this.recalculateBounds()}calculateGraphsHeightRatios(){let t=this.graphsHeightRatio[CHART_UUID];const e=[];e.push(...this.panesOrder.filter(g=>g!==CHART_UUID)),this.panesOrder.forEach(g=>{this.graphsHeightRatio[g]===0&&delete this.graphsHeightRatio[g]});const i=e.map(g=>this.graphsHeightRatio[g]===void 0?void 0:this.graphsHeightRatio[g]),h=i.filter(g=>g!==void 0).length,n=i.filter(g=>g===void 0).length;let r=0,a=0,o=1,l=0;n>0&&([o,l]=it(e.length),t*=o),h===0&&(t=1-l*n),a=1-t-l*n,i.forEach(g=>{g&&(a-=g*o)}),r=a/(e.length+1);const f=i.map(g=>g?g*o+r:l+r);t+=r,this._graphsHeightRatio={},this.graphsHeightRatio[CHART_UUID]=t,f.forEach((g,O)=>{const A=e[O];this.graphsHeightRatio[A]=g})}recalculateNavigationMapElementBounds(){var t,e,i,h;if(this.config.components.navigationMap.visible){const n=this.getBounds(s.N_MAP),{height:r,width:a}=this.config.components.navigationMap.knots,o=r!=null?r:0,l=W()?a*V:a!=null?a:0,f=o?n.y+(n.height-o)/2:n.y,g=(e=(t=this.config.components.navigationMap)===null||t===void 0?void 0:t.timeLabels)===null||e===void 0?void 0:e.visible,O=x=>q(this.canvasModel.ctx,x,this.formatterFactory,this.config)[0],A=Z((h=(i=this.mainCandleSeries)===null||i===void 0?void 0:i.getSeriesInViewport())!==null&&h!==void 0?h:[]),b=g&&A.length?O(A[0].candle.timestamp):0,m=g&&A.length?O(A[A.length-1].candle.timestamp):0,p=Math.max(b,m);if(g){const x=this.getBounds(s.N_MAP_LABEL_L);x.x=n.x,x.y=n.y,x.width=p,x.height=n.height;const B=this.getBounds(s.N_MAP_LABEL_R);B.x=n.x+n.width-p,B.y=n.y,B.width=p,B.height=n.height}const d=this.getBounds(s.N_MAP_BTN_L);d.x=n.x+p,d.y=n.y,d.width=I,d.height=n.height;const P=this.getBounds(s.N_MAP_BTN_R);P.x=n.x+n.width-I-p,P.y=n.y,P.width=I,P.height=n.height;const R=this.getBounds(s.N_MAP_KNOT_L);R.x=(P.x-d.x-d.width)*this.leftRatio+d.x+d.width,R.y=f,R.width=l!=null?l:M,R.height=o!=null?o:n.height;const u=this.getBounds(s.N_MAP_KNOT_R);u.x=(P.x-d.x-d.width)*this.rightRatio+d.x+d.width-M,u.y=f,u.width=l!=null?l:M,u.height=o!=null?o:n.height;const _=this.getBounds(s.N_MAP_SLIDER_WINDOW);_.x=R.x+R.width,_.y=n.y,_.width=u.x-_.x,_.height=n.height;const N=this.getBounds(s.N_MAP_CHART);N.x=d.x+d.width,N.y=n.y,N.width=P.x-N.x,N.height=n.height}}isVolumesInSeparatePane(){return this.config.components.volumes.visible&&this.config.components.volumes.showSeparately}getBounds(t){return this.bounds[t]===void 0&&(this.bounds[t]=this.copyOf(DEFAULT_BOUNDS)),this.bounds[t]}getCanvasOnPageLocation(){return this.canvasOnPageLocation}getBoundsPanes(){return this.panesOrder.reduce((t,e)=>Object.assign(Object.assign({},t),{[e]:this.bounds[s.PANE_UUID(e)]}),{})}getBoundsHitTest(t,e=DEFAULT_HIT_TEST_OPTIONS){const{extensionX:i,extensionY:h,wholePage:n}=Object.assign(Object.assign({},DEFAULT_HIT_TEST_OPTIONS),e);return n?(r,a)=>{const o=this.getBounds(t);return r>o.pageX-i&&r<o.pageX+o.width+i&&a>o.pageY-h&&a<o.pageY+o.height+h}:(r,a)=>{const o=this.getBounds(t);return r>o.x-i&&r<o.x+o.width+i&&a>o.y-h&&a<o.y+o.height+h}}static hitTestOf(t,e=DEFAULT_HIT_TEST_OPTIONS){const{extensionX:i,extensionY:h,wholePage:n}=Object.assign(Object.assign({},DEFAULT_HIT_TEST_OPTIONS),e);return n?(r,a)=>r>t.pageX-i&&r<t.pageX+t.width+i&&a>t.pageY-h&&a<t.pageY+t.height+h:(r,a)=>r>t.x-i&&r<t.x+t.width+i&&a>t.y-h&&a<t.y+t.height+h}isChartBoundsAvailable(){const t=this.getBounds(s.CANVAS);return t.width>0&&t.height>0}resizePaneVertically(t,e){const i=this.panesOrder.indexOf(t),h=this.getBounds(s.PANE_UUID_RESIZER(t));this.doResizePaneVertically(i,h.y-e),this.barResizerChangedSubject.next()}doResizePaneVertically(t,e){const i=t-1,h=this.getBounds(s.ALL_PANES).height,n=this.config.components.paneResizer.height+Q,r=h*this.graphsHeightRatio[this.panesOrder[t]],a=h*this.graphsHeightRatio[this.panesOrder[i]],o=r+e>n,l=a-e>n;if(o&&l){const f=e/h;this.graphsHeightRatio[this.panesOrder[t]]+=f,this.graphsHeightRatio[this.panesOrder[i]]-=f,this.recalculateBounds()}}notifyBoundsSubscribers(){Object.keys(this.boundsChangedSubscriptions).forEach(t=>{this.boundsChangedSubscriptions[t].next(this.getBounds(t))}),this.boundsChangedSubject.next()}observeBoundsChanged(t){let e=this.boundsChangedSubscriptions[t];return e||(e=new F(this.getBounds(t)),this.boundsChangedSubscriptions[t]=e),e.pipe(k(i=>this.copyOf(i)),z((i,h)=>this.sameBounds(i,h)))}observeAnyBoundsChanged(){return this.boundsChangedSubject.asObservable()}copyOf(t){return Object.assign({},t)}copyBounds(t,e){e.x=t.x,e.y=t.y,e.width=t.width,e.height=t.height}sameBounds(t,e){return t.x===e.x&&t.y===e.y&&t.width===e.width&&t.height===e.height}applyDefaultBounds(t){this.copyBounds(DEFAULT_BOUNDS,t)}setMainCandleSeries(t){this.mainCandleSeries=t}}const et={0:1,1:.8,2:.6,3:.5,4:.4,5:.2},it=c=>{var t;const e=(t=et[c])!==null&&t!==void 0?t:.4,i=(1-e)/c;return[e,i]};export const isInBounds=(c,t)=>c.x>t.x&&c.x<t.x+t.width&&c.y>t.y&&c.y<t.y+t.height,isInVerticalBounds=(c,t)=>c>t.y&&c<t.y+t.height;const v=(c,t,e,i,h,n,r)=>{const a=c[t];if(a)return a.x=e,a.y=i,a.pageX=e+r.x,a.pageY=i+r.y,a.width=h,a.height=n,a;const o={x:e,y:i,pageX:e+r.x,pageY:i+r.y,width:h,height:n};return c[t]=o,o};export const limitYToBounds=(c,t)=>Math.min(Math.max(c,t.y),t.y+t.height),DEFAULT_HIT_TEST_OPTIONS={extensionX:0,extensionY:0,wholePage:!1},areBoundsChanged=(c,t)=>c.width===t.width&&c.height===t.height;
|
|
6
|
+
import { BehaviorSubject, Subject } from 'rxjs';
|
|
7
|
+
import { distinctUntilChanged, map } from 'rxjs/operators';
|
|
8
|
+
import { arrayCompare, arrayRemove2, flat, moveInArrayMutable, reorderArray } from '../utils/array.utils';
|
|
9
|
+
import { isMobile } from '../utils/device/browser.utils';
|
|
10
|
+
import { calcTimeLabelBounds } from '../components/navigation_map/navigation-map.model';
|
|
11
|
+
import { calculateSymbolHeight } from '../utils/canvas/canvas-font-measure-tool.utils';
|
|
12
|
+
import { YAxisBoundsContainer } from './y-axis-bounds.container';
|
|
13
|
+
export const CHART_UUID = 'CHART';
|
|
14
|
+
class CanvasElement {
|
|
15
|
+
}
|
|
16
|
+
CanvasElement.CANVAS = 'CANVAS';
|
|
17
|
+
CanvasElement.N_MAP = 'N_MAP';
|
|
18
|
+
CanvasElement.X_AXIS = 'X_AXIS';
|
|
19
|
+
CanvasElement.N_MAP_KNOT_L = 'N_MAP_KNOT_L';
|
|
20
|
+
CanvasElement.N_MAP_KNOT_R = 'N_MAP_KNOT_R';
|
|
21
|
+
CanvasElement.N_MAP_BTN_L = 'N_MAP_BTN_L';
|
|
22
|
+
CanvasElement.N_MAP_BTN_R = 'N_MAP_BTN_R';
|
|
23
|
+
CanvasElement.N_MAP_SLIDER_WINDOW = 'N_MAP_SLIDER_WINDOW';
|
|
24
|
+
CanvasElement.N_MAP_CHART = 'N_MAP_CHART';
|
|
25
|
+
CanvasElement.N_MAP_LABEL_R = 'N_MAP_LABEL_R';
|
|
26
|
+
CanvasElement.N_MAP_LABEL_L = 'N_MAP_LABEL_L';
|
|
27
|
+
CanvasElement.PANE_UUID = (uuid) => 'PANE_' + uuid;
|
|
28
|
+
CanvasElement.PANE_UUID_Y_AXIS = (uuid, idx = 0) => 'PANE_' + uuid + '_Y_AXIS_' + idx;
|
|
29
|
+
CanvasElement.PANE_UUID_RESIZER = (uuid) => 'PANE_' + uuid + '_RESIZER';
|
|
30
|
+
CanvasElement.ALL_PANES = 'ALL_PANES';
|
|
31
|
+
CanvasElement.CHART_WITH_Y_AXIS = 'CHART_WITH_Y_AXIS';
|
|
32
|
+
CanvasElement.EVENTS = 'EVENTS';
|
|
33
|
+
/**
|
|
34
|
+
* @deprecated - use CanvasElement.PANE_UUID(CHART_UUID) instead
|
|
35
|
+
*/
|
|
36
|
+
CanvasElement.CHART = CanvasElement.PANE_UUID(CHART_UUID);
|
|
37
|
+
/**
|
|
38
|
+
* @deprecated - use CanvasElement.PANE_UUID_Y_AXIS(CHART_UUID) instead
|
|
39
|
+
*/
|
|
40
|
+
CanvasElement.Y_AXIS = CanvasElement.PANE_UUID_Y_AXIS(CHART_UUID);
|
|
41
|
+
export { CanvasElement };
|
|
42
|
+
export const DEFAULT_BOUNDS = { x: 0, y: 0, pageX: 0, pageY: 0, width: 0, height: 0 };
|
|
43
|
+
const DEFAULT_MIN_PANE_HEIGHT = 20;
|
|
44
|
+
const N_MAP_H = 35;
|
|
45
|
+
const N_MAP_BUTTON_W = 15;
|
|
46
|
+
const KNOTS_W_MOBILE_MULTIPLIER = 1.5;
|
|
47
|
+
const N_MAP_KNOT_W = isMobile() ? 8 * KNOTS_W_MOBILE_MULTIPLIER : 8;
|
|
48
|
+
/**
|
|
49
|
+
* we need to check that: heightRatios - 1 < 0.000001 after calculations between decimals
|
|
50
|
+
*/
|
|
51
|
+
const PRECISION_DIFFERENCE = 0.000001;
|
|
52
|
+
/**
|
|
53
|
+
* This component listens EVENT_DRAW and recalculates bounds of canvas chart elements.
|
|
54
|
+
* {@link getBounds} method will always give actual placement of element you want.
|
|
55
|
+
*
|
|
56
|
+
* NOTE: this class was designed exclusively for {@link getBounds} method
|
|
57
|
+
* please think twice when adding additional logic here (does it affect element bounds? or it's smth else)
|
|
58
|
+
*/
|
|
59
|
+
export class CanvasBoundsContainer {
|
|
60
|
+
get graphsHeightRatio() {
|
|
61
|
+
return this._graphsHeightRatio;
|
|
62
|
+
}
|
|
63
|
+
constructor(config, eventBus, canvasModel, formatterFactory, chartResizeHandler) {
|
|
64
|
+
this.config = config;
|
|
65
|
+
this.eventBus = eventBus;
|
|
66
|
+
this.canvasModel = canvasModel;
|
|
67
|
+
this.formatterFactory = formatterFactory;
|
|
68
|
+
// holds all canvas element bounds
|
|
69
|
+
this.bounds = {};
|
|
70
|
+
// position of canvas on the whole page
|
|
71
|
+
this.canvasOnPageLocation = { x: 0, y: 0, pageX: 0, pageY: 0, width: 0, height: 0 };
|
|
72
|
+
// holds ordered "top to bottom" array of panes UUID's (studies in past)
|
|
73
|
+
this.panesOrder = [];
|
|
74
|
+
this.panesOrderChangedSubject = new Subject();
|
|
75
|
+
// both will be calculated based on font/content size
|
|
76
|
+
this.xAxisHeight = undefined;
|
|
77
|
+
this.yAxisWidths = {
|
|
78
|
+
right: [0],
|
|
79
|
+
left: [0],
|
|
80
|
+
};
|
|
81
|
+
this.leftRatio = 0;
|
|
82
|
+
this.rightRatio = 0;
|
|
83
|
+
this.boundsChangedSubject = new Subject();
|
|
84
|
+
this.barResizerChangedSubject = new Subject();
|
|
85
|
+
this._graphsHeightRatio = { chart: 1 };
|
|
86
|
+
this.graphsHeightRatioChangedSubject = new Subject();
|
|
87
|
+
this.boundsChangedSubscriptions = {};
|
|
88
|
+
chartResizeHandler.canvasResized.subscribe(bcr => {
|
|
89
|
+
let calculatedBCR = bcr;
|
|
90
|
+
if (!calculatedBCR) {
|
|
91
|
+
calculatedBCR = this.canvasModel.canvas.getBoundingClientRect();
|
|
92
|
+
}
|
|
93
|
+
this.updateCanvasOnPageLocation(calculatedBCR);
|
|
94
|
+
this.recalculateBounds();
|
|
95
|
+
});
|
|
96
|
+
this.yAxisBoundsContainer = new YAxisBoundsContainer(this.config, this.canvasModel);
|
|
97
|
+
}
|
|
98
|
+
updateYAxisWidths() {
|
|
99
|
+
const widths = this.yAxisBoundsContainer.getYAxisWidths();
|
|
100
|
+
this.setYAxisWidths(widths);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Adds a pane to the list of panes and recalculates the height ratios of all panes.
|
|
104
|
+
* @param {string} uuid - The unique identifier of the pane to be added.
|
|
105
|
+
* @param {number} [order] - The order in which the pane should be added. If not provided, the pane will be added to the end of the list.
|
|
106
|
+
*/
|
|
107
|
+
addPaneBounds(uuid, order) {
|
|
108
|
+
if (this.panesOrder.indexOf(uuid) === -1) {
|
|
109
|
+
this.panesOrder.push(uuid);
|
|
110
|
+
if (order !== undefined) {
|
|
111
|
+
const idx = this.panesOrder.indexOf(uuid);
|
|
112
|
+
moveInArrayMutable(this.panesOrder, idx, order);
|
|
113
|
+
}
|
|
114
|
+
this.recalculatePanesHeightRatios();
|
|
115
|
+
this.panesOrderChangedSubject.next(this.panesOrder);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Overrides the height ratios of the chart with the provided height ratios.
|
|
120
|
+
* @param {Record<string, number>} heightRatios - An object containing the height ratios to be set.
|
|
121
|
+
* @returns {void}
|
|
122
|
+
* @throws {Error} If the sum of the height ratios is not equal to 1.
|
|
123
|
+
|
|
124
|
+
*/
|
|
125
|
+
overrideChartHeightRatios(heightRatios) {
|
|
126
|
+
const resultRatio = Object.assign(Object.assign({}, this.graphsHeightRatio), heightRatios);
|
|
127
|
+
const ratioSum = Object.values(resultRatio).reduce((sum, ratio) => sum + ratio, 0);
|
|
128
|
+
if (Math.abs(ratioSum - 1) < PRECISION_DIFFERENCE) {
|
|
129
|
+
this._graphsHeightRatio = resultRatio;
|
|
130
|
+
this.recalculateBounds();
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.error(`Result ratio should be equal 1, but equal ${ratioSum}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Moves a pane up in the panesOrder array.
|
|
138
|
+
* @param {string} uuid - The unique identifier of the pane to be moved.
|
|
139
|
+
* @returns {void}
|
|
140
|
+
*/
|
|
141
|
+
movePaneUp(uuid) {
|
|
142
|
+
const idx = this.panesOrder.indexOf(uuid);
|
|
143
|
+
if (idx !== -1) {
|
|
144
|
+
moveInArrayMutable(this.panesOrder, idx, idx - 1);
|
|
145
|
+
this.recalculateBounds();
|
|
146
|
+
this.eventBus.fireDraw();
|
|
147
|
+
this.panesOrderChangedSubject.next(this.panesOrder);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Moves the pane down in the panesOrder array.
|
|
152
|
+
* @param {string} uuid - The unique identifier of the pane to be moved.
|
|
153
|
+
* @returns {void}
|
|
154
|
+
*/
|
|
155
|
+
movePaneDown(uuid) {
|
|
156
|
+
const idx = this.panesOrder.indexOf(uuid);
|
|
157
|
+
if (idx !== -1) {
|
|
158
|
+
moveInArrayMutable(this.panesOrder, idx, idx + 1);
|
|
159
|
+
this.recalculateBounds();
|
|
160
|
+
this.eventBus.fireDraw();
|
|
161
|
+
this.panesOrderChangedSubject.next(this.panesOrder);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Reorders current panes according to newPanesOrder
|
|
166
|
+
* if element in newPanesOrder doesn't exist in real panes order - it will ignored
|
|
167
|
+
* Example:
|
|
168
|
+
* panesOrder: ['1', '2', '3']
|
|
169
|
+
* newPanesOrder: ['3', '1']
|
|
170
|
+
* result: ['3', '2', '1']
|
|
171
|
+
*/
|
|
172
|
+
reorderPanes(newPanesOrder) {
|
|
173
|
+
this.panesOrder = reorderArray(this.panesOrder, newPanesOrder);
|
|
174
|
+
this.recalculateBounds();
|
|
175
|
+
this.panesOrderChangedSubject.next(this.panesOrder);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Removes the bounds of a pane with the given uuid from the canvas element.
|
|
179
|
+
* @param {string} uuid - The uuid of the pane to remove.
|
|
180
|
+
* @returns {void}
|
|
181
|
+
*/
|
|
182
|
+
removedPaneBounds(uuid) {
|
|
183
|
+
arrayRemove2(this.panesOrder, uuid);
|
|
184
|
+
delete this.graphsHeightRatio[uuid];
|
|
185
|
+
delete this.bounds[CanvasElement.PANE_UUID(uuid)];
|
|
186
|
+
this.yAxisWidths.left
|
|
187
|
+
.concat(this.yAxisWidths.right)
|
|
188
|
+
.forEach((_, idx) => delete this.bounds[CanvasElement.PANE_UUID_Y_AXIS(uuid, idx)]);
|
|
189
|
+
delete this.bounds[CanvasElement.PANE_UUID_RESIZER(uuid)];
|
|
190
|
+
this.recalculatePanesHeightRatios();
|
|
191
|
+
this.panesOrderChangedSubject.next(this.panesOrder);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Recalculates the bounds of the chart elements based on the current configuration and canvas size.
|
|
195
|
+
* The function updates the bounds of the canvas, the main chart, the panes, the y-axis, and the chart with y-axis.
|
|
196
|
+
* It also updates the navigation map element bounds, all bounds page coordinates, and notifies the bounds subscribers.
|
|
197
|
+
* @function
|
|
198
|
+
* @name recalculateBounds
|
|
199
|
+
* @memberof ChartWidget
|
|
200
|
+
* @returns {void}
|
|
201
|
+
*/
|
|
202
|
+
recalculateBounds() {
|
|
203
|
+
const canvasW = this.canvasOnPageLocation.width;
|
|
204
|
+
const canvasH = this.canvasOnPageLocation.height;
|
|
205
|
+
const paneResizerHeight = this.config.components.paneResizer.height;
|
|
206
|
+
// whole canvas bounds
|
|
207
|
+
const canvas = this.getBounds(CanvasElement.CANVAS);
|
|
208
|
+
canvas.x = 0;
|
|
209
|
+
canvas.y = 0;
|
|
210
|
+
canvas.width = canvasW;
|
|
211
|
+
canvas.height = canvasH;
|
|
212
|
+
const yAxisWidths = this.yAxisWidths;
|
|
213
|
+
const nMap = this.getNavMapBounds(canvas);
|
|
214
|
+
const xAxis = this.getXAxisBounds(nMap, canvas);
|
|
215
|
+
const chartHeight = canvasH - xAxis.height - nMap.height;
|
|
216
|
+
const totalYAxisWidthLeft = yAxisWidths.left.reduce((sum, width) => sum + width, 0);
|
|
217
|
+
const totalYAxisWidthRight = yAxisWidths.right.reduce((sum, width) => sum + width, 0);
|
|
218
|
+
// main chart
|
|
219
|
+
const yAxisXLeft = 0;
|
|
220
|
+
const yAxisXRight = canvas.width - totalYAxisWidthRight;
|
|
221
|
+
const paneXStart = yAxisXLeft + totalYAxisWidthLeft;
|
|
222
|
+
const initialY = 0;
|
|
223
|
+
const chartWidth = canvas.width - totalYAxisWidthLeft - totalYAxisWidthRight;
|
|
224
|
+
let nextY = initialY;
|
|
225
|
+
// panes
|
|
226
|
+
this.panesOrder.forEach((uuid, index) => {
|
|
227
|
+
var _a;
|
|
228
|
+
const paneHeightRatio = this.graphsHeightRatio[this.panesOrder[index]];
|
|
229
|
+
const resizerVisible = this.config.components.paneResizer.visible;
|
|
230
|
+
if (resizerVisible) {
|
|
231
|
+
if (index !== 0) {
|
|
232
|
+
upsertBounds(this.bounds, CanvasElement.PANE_UUID_RESIZER(uuid), 0, nextY, canvas.width, paneResizerHeight, this.canvasOnPageLocation);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
upsertBounds(this.bounds, CanvasElement.PANE_UUID_RESIZER(uuid), 0, 0, 0, 0, this.canvasOnPageLocation);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const paneBounds = upsertBounds(this.bounds, CanvasElement.PANE_UUID(uuid), paneXStart, resizerVisible ? nextY + paneResizerHeight : nextY, chartWidth, resizerVisible
|
|
239
|
+
? chartHeight * paneHeightRatio - this.config.components.paneResizer.height
|
|
240
|
+
: chartHeight * paneHeightRatio, this.canvasOnPageLocation);
|
|
241
|
+
// y axis
|
|
242
|
+
if (this.config.components.yAxis.visible) {
|
|
243
|
+
const extents = this.yAxisBoundsContainer.extentsOrder.get(uuid);
|
|
244
|
+
if (extents === undefined) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const y = resizerVisible ? nextY + paneResizerHeight : nextY;
|
|
248
|
+
const height = resizerVisible
|
|
249
|
+
? chartHeight * paneHeightRatio - paneResizerHeight
|
|
250
|
+
: chartHeight * paneHeightRatio;
|
|
251
|
+
let startXLeft = paneXStart - ((_a = yAxisWidths.left[0]) !== null && _a !== void 0 ? _a : 0); // we need to provide right to left order on the left y-axis side
|
|
252
|
+
let startXRight = yAxisXRight;
|
|
253
|
+
extents.left.forEach((extentIdx, i) => {
|
|
254
|
+
var _a;
|
|
255
|
+
upsertBounds(this.bounds, CanvasElement.PANE_UUID_Y_AXIS(uuid, extentIdx), startXLeft, y, yAxisWidths.left[i], height, this.canvasOnPageLocation);
|
|
256
|
+
startXLeft -= (_a = yAxisWidths.left[i + 1]) !== null && _a !== void 0 ? _a : 0;
|
|
257
|
+
});
|
|
258
|
+
extents.right.forEach((extentIdx, i) => {
|
|
259
|
+
upsertBounds(this.bounds, CanvasElement.PANE_UUID_Y_AXIS(uuid, extentIdx), startXRight, y, yAxisWidths.right[i], height, this.canvasOnPageLocation);
|
|
260
|
+
startXRight += yAxisWidths.right[i];
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
upsertBounds(this.bounds, CanvasElement.PANE_UUID_Y_AXIS(uuid), 0, 0, 0, 0, this.canvasOnPageLocation);
|
|
265
|
+
}
|
|
266
|
+
nextY = paneBounds.y + paneBounds.height;
|
|
267
|
+
});
|
|
268
|
+
const allPanesBounds = this.getBounds(CanvasElement.ALL_PANES);
|
|
269
|
+
allPanesBounds.x = paneXStart;
|
|
270
|
+
allPanesBounds.y = initialY;
|
|
271
|
+
allPanesBounds.width = chartWidth;
|
|
272
|
+
allPanesBounds.height = nextY;
|
|
273
|
+
// chart with Y axis
|
|
274
|
+
const chartWithYAxis = this.getBounds(CanvasElement.CHART_WITH_Y_AXIS);
|
|
275
|
+
const chartBounds = this.getBounds(CanvasElement.PANE_UUID(CHART_UUID));
|
|
276
|
+
this.getEventsBounds(chartBounds);
|
|
277
|
+
this.copyBounds(chartBounds, chartWithYAxis);
|
|
278
|
+
chartWithYAxis.width = canvas.width;
|
|
279
|
+
this.recalculateNavigationMapElementBounds();
|
|
280
|
+
this.updateAllBoundsPageCoordinates();
|
|
281
|
+
this.notifyBoundsSubscribers();
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Updates the canvasOnPageLocation property with the provided PickedDOMRect object.
|
|
285
|
+
* @param {PickedDOMRect} bcr - The PickedDOMRect object containing the new values for x, y, width, and height.
|
|
286
|
+
* @private
|
|
287
|
+
*/
|
|
288
|
+
updateCanvasOnPageLocation(bcr) {
|
|
289
|
+
this.canvasOnPageLocation = Object.assign(Object.assign({}, this.canvasOnPageLocation), { x: bcr.x, y: bcr.y, width: bcr.width, height: bcr.height });
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Updates the page coordinates of all bounds.
|
|
293
|
+
* @private
|
|
294
|
+
*/
|
|
295
|
+
updateAllBoundsPageCoordinates() {
|
|
296
|
+
for (const name of Object.keys(this.bounds)) {
|
|
297
|
+
const bound = this.bounds[name];
|
|
298
|
+
bound.pageX = bound.x + this.canvasOnPageLocation.x;
|
|
299
|
+
bound.pageY = bound.y + this.canvasOnPageLocation.y;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Returns the bounds of the events component.
|
|
304
|
+
* @private
|
|
305
|
+
* @param {Bounds} chartPane - The bounds of the chart pane.
|
|
306
|
+
* @returns {Bounds} - The bounds of the events component.
|
|
307
|
+
*/
|
|
308
|
+
getEventsBounds(chartPane) {
|
|
309
|
+
const events = this.getBounds(CanvasElement.EVENTS);
|
|
310
|
+
if (this.config.components.events.visible) {
|
|
311
|
+
events.x = 0;
|
|
312
|
+
events.y = chartPane.y + chartPane.height - this.config.components.events.height;
|
|
313
|
+
events.width = chartPane.width;
|
|
314
|
+
events.height = this.config.components.events.height;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
this.applyDefaultBounds(events);
|
|
318
|
+
}
|
|
319
|
+
return events;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Returns the bounds of the navigation map element.
|
|
323
|
+
* @param {Bounds} canvas - The bounds of the canvas element.
|
|
324
|
+
* @returns {Bounds} - The bounds of the navigation map element.
|
|
325
|
+
* @private
|
|
326
|
+
*/
|
|
327
|
+
getNavMapBounds(canvas) {
|
|
328
|
+
const nMap = this.getBounds(CanvasElement.N_MAP);
|
|
329
|
+
if (this.config.components.navigationMap.visible) {
|
|
330
|
+
nMap.x = 0;
|
|
331
|
+
nMap.y = canvas.height - N_MAP_H;
|
|
332
|
+
nMap.width = canvas.width;
|
|
333
|
+
nMap.height = N_MAP_H;
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
this.applyDefaultBounds(nMap);
|
|
337
|
+
}
|
|
338
|
+
return nMap;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Returns the bounds of the X axis based on the provided parameters.
|
|
342
|
+
* @private
|
|
343
|
+
* @param {Bounds} nMap - The bounds of the nMap.
|
|
344
|
+
* @param {Bounds} canvas - The bounds of the canvas.
|
|
345
|
+
* @returns {Bounds} - The bounds of the X axis.
|
|
346
|
+
*/
|
|
347
|
+
getXAxisBounds(nMap, canvas) {
|
|
348
|
+
const xAxis = this.getBounds(CanvasElement.X_AXIS);
|
|
349
|
+
if (this.config.components.xAxis.visible) {
|
|
350
|
+
xAxis.x = 0;
|
|
351
|
+
xAxis.y = canvas.height - this.getXAxisHeight() - nMap.height;
|
|
352
|
+
xAxis.width = canvas.width;
|
|
353
|
+
xAxis.height = this.getXAxisHeight();
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
this.applyDefaultBounds(xAxis);
|
|
357
|
+
}
|
|
358
|
+
return xAxis;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Calculates the height of the X axis based on the font size, font family and padding values.
|
|
362
|
+
* If the height has already been calculated, it returns the cached value.
|
|
363
|
+
* @returns {number} The height of the X axis.
|
|
364
|
+
*/
|
|
365
|
+
getXAxisHeight() {
|
|
366
|
+
var _a, _b;
|
|
367
|
+
if (!this.xAxisHeight) {
|
|
368
|
+
const font = this.config.components.xAxis.fontSize + 'px ' + this.config.components.xAxis.fontFamily;
|
|
369
|
+
const fontHeight = calculateSymbolHeight(font, this.canvasModel.ctx);
|
|
370
|
+
this.xAxisHeight =
|
|
371
|
+
fontHeight +
|
|
372
|
+
((_a = this.config.components.xAxis.padding.top) !== null && _a !== void 0 ? _a : 0) +
|
|
373
|
+
((_b = this.config.components.xAxis.padding.bottom) !== null && _b !== void 0 ? _b : 0);
|
|
374
|
+
}
|
|
375
|
+
return this.xAxisHeight;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Sets the width of the Y axis.
|
|
379
|
+
*
|
|
380
|
+
* @param {YAxisWidths} yAxisWidths - The width of the Y axis.
|
|
381
|
+
* @returns {void}
|
|
382
|
+
*/
|
|
383
|
+
setYAxisWidths(yAxisWidths) {
|
|
384
|
+
if (!arrayCompare(this.yAxisWidths.left, yAxisWidths.left) ||
|
|
385
|
+
!arrayCompare(this.yAxisWidths.right, yAxisWidths.right)) {
|
|
386
|
+
this.yAxisWidths = yAxisWidths;
|
|
387
|
+
this.recalculateBounds();
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
*
|
|
392
|
+
* Sets the height of the X axis.
|
|
393
|
+
* @param {number} xAxisHeight - The height of the X axis.
|
|
394
|
+
* @returns {void}
|
|
395
|
+
*/
|
|
396
|
+
setXAxisHeight(xAxisHeight) {
|
|
397
|
+
if (xAxisHeight !== this.xAxisHeight) {
|
|
398
|
+
this.xAxisHeight = xAxisHeight;
|
|
399
|
+
this.recalculateBounds();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Sets the order of the panes.
|
|
404
|
+
* @param {string[]} panesOrder - An array of strings representing the order of the panes.
|
|
405
|
+
* @returns {void}
|
|
406
|
+
*/
|
|
407
|
+
setPanesOrder(panesOrder) {
|
|
408
|
+
this.panesOrder = panesOrder;
|
|
409
|
+
this.recalculateBounds();
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Recalculates the height ratios of the panes by calling the calculateGraphsHeightRatios() and recalculateBounds() methods.
|
|
413
|
+
*/
|
|
414
|
+
recalculatePanesHeightRatios() {
|
|
415
|
+
this.calculateGraphsHeightRatios();
|
|
416
|
+
this.recalculateBounds();
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Calculates the height ratios of the graphs in the chart.
|
|
420
|
+
* It first gets the height ratio of the main chart and then calculates the height ratios of the other graphs.
|
|
421
|
+
* It then calculates the free space available for the other graphs and distributes it among them based on their previous height ratios.
|
|
422
|
+
* If there are new graphs added, it calculates their height ratios based on the number of new graphs and the total number of graphs.
|
|
423
|
+
* @private
|
|
424
|
+
*/
|
|
425
|
+
calculateGraphsHeightRatios() {
|
|
426
|
+
let chartRatio = this.graphsHeightRatio[CHART_UUID];
|
|
427
|
+
// NOTE: pec stands for panesExceptMainChart
|
|
428
|
+
const pec = [];
|
|
429
|
+
pec.push(...this.panesOrder.filter(p => p !== CHART_UUID));
|
|
430
|
+
this.panesOrder.forEach(pane => {
|
|
431
|
+
if (this.graphsHeightRatio[pane] === 0) {
|
|
432
|
+
delete this.graphsHeightRatio[pane];
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
const pecRatios = pec.map(graph => this.graphsHeightRatio[graph] === undefined ? undefined : this.graphsHeightRatio[graph]);
|
|
436
|
+
const oldPecNumber = pecRatios.filter(ratio => ratio !== undefined).length;
|
|
437
|
+
const newPecNumber = pecRatios.filter(ratio => ratio === undefined).length;
|
|
438
|
+
let freeRatioForPec = 0;
|
|
439
|
+
let freeRatio = 0;
|
|
440
|
+
let ratioForOldPec = 1;
|
|
441
|
+
let ratioForNewPec = 0;
|
|
442
|
+
if (newPecNumber > 0) {
|
|
443
|
+
[ratioForOldPec, ratioForNewPec] = getHeightRatios(pec.length);
|
|
444
|
+
chartRatio *= ratioForOldPec;
|
|
445
|
+
}
|
|
446
|
+
if (oldPecNumber === 0) {
|
|
447
|
+
chartRatio = 1 - ratioForNewPec * newPecNumber;
|
|
448
|
+
}
|
|
449
|
+
freeRatio = 1 - chartRatio - ratioForNewPec * newPecNumber;
|
|
450
|
+
pecRatios.forEach(ratio => {
|
|
451
|
+
if (ratio) {
|
|
452
|
+
freeRatio -= ratio * ratioForOldPec;
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
freeRatioForPec = freeRatio / (pec.length + 1);
|
|
456
|
+
const proportions = pecRatios.map(ratio => ratio ? ratio * ratioForOldPec + freeRatioForPec : ratioForNewPec + freeRatioForPec);
|
|
457
|
+
chartRatio += freeRatioForPec;
|
|
458
|
+
this._graphsHeightRatio = {};
|
|
459
|
+
this.graphsHeightRatio[CHART_UUID] = chartRatio;
|
|
460
|
+
proportions.forEach((ratio, index) => {
|
|
461
|
+
const name = pec[index];
|
|
462
|
+
this.graphsHeightRatio[name] = ratio;
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Recalculates the bounds of the navigation map elements based on the current configuration and viewport.
|
|
467
|
+
* If the navigation map is visible, it calculates the bounds of the following elements:
|
|
468
|
+
* - Navigation map
|
|
469
|
+
* - Time labels
|
|
470
|
+
* - Left and right buttons
|
|
471
|
+
* - Knots
|
|
472
|
+
* - Slider
|
|
473
|
+
* - Chart
|
|
474
|
+
*
|
|
475
|
+
* The bounds of each element are calculated based on the current viewport and configuration values.
|
|
476
|
+
* If the time labels are visible, it calculates the width of the left and right time labels based on the first and last visible candle timestamps.
|
|
477
|
+
* The width of the time labels is used to calculate the position and size of the left and right buttons, as well as the position of the knots.
|
|
478
|
+
* The position and size of the knots are calculated based on the left and right ratios, which represent the position of the knots relative to the left and right buttons.
|
|
479
|
+
* The position and size of the slider and chart are calculated based on the position and size of the knots and buttons.
|
|
480
|
+
*/
|
|
481
|
+
recalculateNavigationMapElementBounds() {
|
|
482
|
+
var _a, _b, _c, _d;
|
|
483
|
+
if (this.config.components.navigationMap.visible) {
|
|
484
|
+
const nMap = this.getBounds(CanvasElement.N_MAP);
|
|
485
|
+
const { height, width } = this.config.components.navigationMap.knots;
|
|
486
|
+
const knotHeightFromConfig = height !== null && height !== void 0 ? height : 0;
|
|
487
|
+
const knotWidthFromConfig = isMobile() ? width * KNOTS_W_MOBILE_MULTIPLIER : width !== null && width !== void 0 ? width : 0;
|
|
488
|
+
const knotY = !knotHeightFromConfig ? nMap.y : nMap.y + (nMap.height - knotHeightFromConfig) / 2;
|
|
489
|
+
// time labels
|
|
490
|
+
const timeLabelsVisible = (_b = (_a = this.config.components.navigationMap) === null || _a === void 0 ? void 0 : _a.timeLabels) === null || _b === void 0 ? void 0 : _b.visible;
|
|
491
|
+
const calcLabelBounds = (timestamp) => {
|
|
492
|
+
return calcTimeLabelBounds(this.canvasModel.ctx, timestamp, this.formatterFactory, this.config)[0];
|
|
493
|
+
};
|
|
494
|
+
const candleSource = flat((_d = (_c = this.mainCandleSeries) === null || _c === void 0 ? void 0 : _c.getSeriesInViewport()) !== null && _d !== void 0 ? _d : []);
|
|
495
|
+
const leftTimeLabelWidth = timeLabelsVisible && candleSource.length ? calcLabelBounds(candleSource[0].candle.timestamp) : 0;
|
|
496
|
+
const rightTimeLabelWidth = timeLabelsVisible && candleSource.length
|
|
497
|
+
? calcLabelBounds(candleSource[candleSource.length - 1].candle.timestamp)
|
|
498
|
+
: 0;
|
|
499
|
+
const timeLabelWidth = Math.max(leftTimeLabelWidth, rightTimeLabelWidth);
|
|
500
|
+
if (timeLabelsVisible) {
|
|
501
|
+
const nMapLabelL = this.getBounds(CanvasElement.N_MAP_LABEL_L);
|
|
502
|
+
nMapLabelL.x = nMap.x;
|
|
503
|
+
nMapLabelL.y = nMap.y;
|
|
504
|
+
nMapLabelL.width = timeLabelWidth;
|
|
505
|
+
nMapLabelL.height = nMap.height;
|
|
506
|
+
const nMapLabelR = this.getBounds(CanvasElement.N_MAP_LABEL_R);
|
|
507
|
+
nMapLabelR.x = nMap.x + nMap.width - timeLabelWidth;
|
|
508
|
+
nMapLabelR.y = nMap.y;
|
|
509
|
+
nMapLabelR.width = timeLabelWidth;
|
|
510
|
+
nMapLabelR.height = nMap.height;
|
|
511
|
+
}
|
|
512
|
+
// buttons left and right
|
|
513
|
+
const nMapBtnL = this.getBounds(CanvasElement.N_MAP_BTN_L);
|
|
514
|
+
nMapBtnL.x = nMap.x + timeLabelWidth;
|
|
515
|
+
nMapBtnL.y = nMap.y;
|
|
516
|
+
nMapBtnL.width = N_MAP_BUTTON_W;
|
|
517
|
+
nMapBtnL.height = nMap.height;
|
|
518
|
+
const nMapBtnR = this.getBounds(CanvasElement.N_MAP_BTN_R);
|
|
519
|
+
nMapBtnR.x = nMap.x + nMap.width - N_MAP_BUTTON_W - timeLabelWidth;
|
|
520
|
+
nMapBtnR.y = nMap.y;
|
|
521
|
+
nMapBtnR.width = N_MAP_BUTTON_W;
|
|
522
|
+
nMapBtnR.height = nMap.height;
|
|
523
|
+
// knots
|
|
524
|
+
// Left drag button
|
|
525
|
+
const knotL = this.getBounds(CanvasElement.N_MAP_KNOT_L);
|
|
526
|
+
knotL.x = (nMapBtnR.x - nMapBtnL.x - nMapBtnL.width) * this.leftRatio + nMapBtnL.x + nMapBtnL.width;
|
|
527
|
+
knotL.y = knotY;
|
|
528
|
+
knotL.width = knotWidthFromConfig !== null && knotWidthFromConfig !== void 0 ? knotWidthFromConfig : N_MAP_KNOT_W;
|
|
529
|
+
knotL.height = knotHeightFromConfig !== null && knotHeightFromConfig !== void 0 ? knotHeightFromConfig : nMap.height;
|
|
530
|
+
// Right drag button
|
|
531
|
+
const knotR = this.getBounds(CanvasElement.N_MAP_KNOT_R);
|
|
532
|
+
knotR.x =
|
|
533
|
+
(nMapBtnR.x - nMapBtnL.x - nMapBtnL.width) * this.rightRatio +
|
|
534
|
+
nMapBtnL.x +
|
|
535
|
+
nMapBtnL.width -
|
|
536
|
+
N_MAP_KNOT_W;
|
|
537
|
+
knotR.y = knotY;
|
|
538
|
+
knotR.width = knotWidthFromConfig !== null && knotWidthFromConfig !== void 0 ? knotWidthFromConfig : N_MAP_KNOT_W;
|
|
539
|
+
knotR.height = knotHeightFromConfig !== null && knotHeightFromConfig !== void 0 ? knotHeightFromConfig : nMap.height;
|
|
540
|
+
// slider
|
|
541
|
+
const slider = this.getBounds(CanvasElement.N_MAP_SLIDER_WINDOW);
|
|
542
|
+
slider.x = knotL.x + knotL.width;
|
|
543
|
+
slider.y = nMap.y;
|
|
544
|
+
slider.width = knotR.x - slider.x;
|
|
545
|
+
slider.height = nMap.height;
|
|
546
|
+
// chart
|
|
547
|
+
const nMapChart = this.getBounds(CanvasElement.N_MAP_CHART);
|
|
548
|
+
nMapChart.x = nMapBtnL.x + nMapBtnL.width;
|
|
549
|
+
nMapChart.y = nMap.y;
|
|
550
|
+
nMapChart.width = nMapBtnR.x - nMapChart.x;
|
|
551
|
+
nMapChart.height = nMap.height;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Checks if the volumes are set to be visible and if they should be shown in a separate pane
|
|
556
|
+
*
|
|
557
|
+
* @returns {boolean} - Returns true if the volumes are set to be visible and if they should be shown in a separate pane, otherwise returns false
|
|
558
|
+
*/
|
|
559
|
+
isVolumesInSeparatePane() {
|
|
560
|
+
return this.config.components.volumes.visible && this.config.components.volumes.showSeparately;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Gets current canvas element bounds.
|
|
564
|
+
* @param {string} el - CanvasElement.ELEMENT_NAME
|
|
565
|
+
* @return {Bounds} bounds of element
|
|
566
|
+
*/
|
|
567
|
+
getBounds(el) {
|
|
568
|
+
if (this.bounds[el] === undefined) {
|
|
569
|
+
this.bounds[el] = this.copyOf(DEFAULT_BOUNDS);
|
|
570
|
+
}
|
|
571
|
+
return this.bounds[el];
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Returns the position of CANVAS on whole page.
|
|
575
|
+
* Use it to calculate relative coordinates for mouse movement hit test - crosstool for example.
|
|
576
|
+
*/
|
|
577
|
+
getCanvasOnPageLocation() {
|
|
578
|
+
return this.canvasOnPageLocation;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Gets current panes bounds.
|
|
582
|
+
*/
|
|
583
|
+
getBoundsPanes() {
|
|
584
|
+
return this.panesOrder.reduce((acc, uuid) => (Object.assign(Object.assign({}, acc), { [uuid]: this.bounds[CanvasElement.PANE_UUID(uuid)] })), {});
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Gets hit-test fn for canvas element.
|
|
588
|
+
* @param {string} el - CanvasElement.ELEMENT_NAME
|
|
589
|
+
* @param {boolean} reverse - reverses the hit test condition
|
|
590
|
+
* @param {number} extensionX - extended hitBoundsTest in horizontal direction
|
|
591
|
+
* @param {number} extensionY - extended hitBoundsTest in vertical direction
|
|
592
|
+
* @param wholePage
|
|
593
|
+
* @return {HitBoundsTest} hit-test fn
|
|
594
|
+
*/
|
|
595
|
+
getBoundsHitTest(el, options = DEFAULT_HIT_TEST_OPTIONS) {
|
|
596
|
+
const { extensionX, extensionY, wholePage } = Object.assign(Object.assign({}, DEFAULT_HIT_TEST_OPTIONS), options);
|
|
597
|
+
if (wholePage) {
|
|
598
|
+
return (x, y) => {
|
|
599
|
+
const bounds = this.getBounds(el);
|
|
600
|
+
const hit = x > bounds.pageX - extensionX &&
|
|
601
|
+
x < bounds.pageX + bounds.width + extensionX &&
|
|
602
|
+
y > bounds.pageY - extensionY &&
|
|
603
|
+
y < bounds.pageY + bounds.height + extensionY;
|
|
604
|
+
return hit;
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
return (x, y) => {
|
|
609
|
+
const bounds = this.getBounds(el);
|
|
610
|
+
const hit = x > bounds.x - extensionX &&
|
|
611
|
+
x < bounds.x + bounds.width + extensionX &&
|
|
612
|
+
y > bounds.y - extensionY &&
|
|
613
|
+
y < bounds.y + bounds.height + extensionY;
|
|
614
|
+
return hit;
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Returns a function that tests if a given point is inside a given bounds object.
|
|
620
|
+
* @param {Bounds} bounds - The bounds object to test against.
|
|
621
|
+
* @param {Object} options - An object containing options for the hit test.
|
|
622
|
+
* @param {number} [options.extensionX=0] - The amount of extension in the x-axis.
|
|
623
|
+
* @param {number} [options.extensionY=0] - The amount of extension in the y-axis.
|
|
624
|
+
* @param {boolean} [options.wholePage=false] - Whether to test against the whole page or just the bounds object.
|
|
625
|
+
* @returns {HitBoundsTest} - A function that takes in x and y coordinates and returns true if the point is inside the bounds object.
|
|
626
|
+
*/
|
|
627
|
+
static hitTestOf(bounds, options = DEFAULT_HIT_TEST_OPTIONS) {
|
|
628
|
+
const { extensionX, extensionY, wholePage } = Object.assign(Object.assign({}, DEFAULT_HIT_TEST_OPTIONS), options);
|
|
629
|
+
if (!wholePage) {
|
|
630
|
+
return (x, y) => {
|
|
631
|
+
const hit = x > bounds.x - extensionX &&
|
|
632
|
+
x < bounds.x + bounds.width + extensionX &&
|
|
633
|
+
y > bounds.y - extensionY &&
|
|
634
|
+
y < bounds.y + bounds.height + extensionY;
|
|
635
|
+
return hit;
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
return (x, y) => {
|
|
640
|
+
const hit = x > bounds.pageX - extensionX &&
|
|
641
|
+
x < bounds.pageX + bounds.width + extensionX &&
|
|
642
|
+
y > bounds.pageY - extensionY &&
|
|
643
|
+
y < bounds.pageY + bounds.height + extensionY;
|
|
644
|
+
return hit;
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* This method indicates whether it's possible to render the chart, in particular if its width and height > 0.
|
|
650
|
+
*/
|
|
651
|
+
isChartBoundsAvailable() {
|
|
652
|
+
const canvasBounds = this.getBounds(CanvasElement.CANVAS);
|
|
653
|
+
return canvasBounds.width > 0 && canvasBounds.height > 0;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Resizes a pane vertically.
|
|
657
|
+
* @param {string} uuid - The unique identifier of the pane.
|
|
658
|
+
* @param {number} y - The amount of pixels to resize the pane by.
|
|
659
|
+
* @returns {void}
|
|
660
|
+
*/
|
|
661
|
+
resizePaneVertically(uuid, y) {
|
|
662
|
+
const idx = this.panesOrder.indexOf(uuid);
|
|
663
|
+
const bounds = this.getBounds(CanvasElement.PANE_UUID_RESIZER(uuid));
|
|
664
|
+
this.doResizePaneVertically(idx, bounds.y - y);
|
|
665
|
+
this.barResizerChangedSubject.next();
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Resizes a pane vertically based on the provided index and delta in pixels.
|
|
669
|
+
* @param {number} idx - The index of the pane to be resized.
|
|
670
|
+
* @param {number} yDeltaPixels - The delta in pixels to resize the pane.
|
|
671
|
+
* @returns {void}
|
|
672
|
+
*/
|
|
673
|
+
doResizePaneVertically(idx, yDeltaPixels) {
|
|
674
|
+
const prevPaneIdx = idx - 1;
|
|
675
|
+
const allPanesHeight = this.getBounds(CanvasElement.ALL_PANES).height;
|
|
676
|
+
const minAllowedPaneHeight = this.config.components.paneResizer.height + DEFAULT_MIN_PANE_HEIGHT;
|
|
677
|
+
const resultPaneHeight = allPanesHeight * this.graphsHeightRatio[this.panesOrder[idx]];
|
|
678
|
+
const dependResultPaneHeight = allPanesHeight * this.graphsHeightRatio[this.panesOrder[prevPaneIdx]];
|
|
679
|
+
// check if changes fit allowed minimal pane height
|
|
680
|
+
const fitPane = resultPaneHeight + yDeltaPixels > minAllowedPaneHeight;
|
|
681
|
+
const fitDependPane = dependResultPaneHeight - yDeltaPixels > minAllowedPaneHeight;
|
|
682
|
+
if (fitPane && fitDependPane) {
|
|
683
|
+
// convert pixels to percent
|
|
684
|
+
const yDeltaPercent = yDeltaPixels / allPanesHeight;
|
|
685
|
+
this.graphsHeightRatio[this.panesOrder[idx]] += yDeltaPercent;
|
|
686
|
+
this.graphsHeightRatio[this.panesOrder[prevPaneIdx]] -= yDeltaPercent;
|
|
687
|
+
this.recalculateBounds();
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Notifies all subscribers about bounds changes
|
|
692
|
+
* @function
|
|
693
|
+
* @name notifyBoundsSubscribers
|
|
694
|
+
* @returns {void}
|
|
695
|
+
*/
|
|
696
|
+
notifyBoundsSubscribers() {
|
|
697
|
+
Object.keys(this.boundsChangedSubscriptions).forEach(el => {
|
|
698
|
+
const subject = this.boundsChangedSubscriptions[el];
|
|
699
|
+
subject.next(this.getBounds(el));
|
|
700
|
+
});
|
|
701
|
+
this.boundsChangedSubject.next();
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Subscribe to element bounds changes.
|
|
705
|
+
* Multiple subscriptions will share the same subject.
|
|
706
|
+
* @param el - element name
|
|
707
|
+
*/
|
|
708
|
+
observeBoundsChanged(el) {
|
|
709
|
+
let sub = this.boundsChangedSubscriptions[el];
|
|
710
|
+
if (!sub) {
|
|
711
|
+
sub = new BehaviorSubject(this.getBounds(el));
|
|
712
|
+
this.boundsChangedSubscriptions[el] = sub;
|
|
713
|
+
}
|
|
714
|
+
return sub.pipe(map(b => this.copyOf(b)), distinctUntilChanged((b1, b2) => this.sameBounds(b1, b2)));
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Returns an observable that emits when the bounds of the object change.
|
|
718
|
+
* @returns {Observable} An observable that emits when the bounds of the object change.
|
|
719
|
+
*/
|
|
720
|
+
observeAnyBoundsChanged() {
|
|
721
|
+
return this.boundsChangedSubject.asObservable();
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Creates a copy of the provided bounds object.
|
|
725
|
+
*
|
|
726
|
+
* @private
|
|
727
|
+
* @param {Bounds} bounds - The bounds object to be copied.
|
|
728
|
+
* @returns {Bounds} - A new bounds object with the same properties as the original.
|
|
729
|
+
*/
|
|
730
|
+
copyOf(bounds) {
|
|
731
|
+
return Object.assign({}, bounds);
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Copies the values of the `from` object to the `to` object.
|
|
735
|
+
* @param {Bounds} from - The object to copy the values from.
|
|
736
|
+
* @param {Bounds} to - The object to copy the values to.
|
|
737
|
+
*/
|
|
738
|
+
copyBounds(from, to) {
|
|
739
|
+
to.x = from.x;
|
|
740
|
+
to.y = from.y;
|
|
741
|
+
to.width = from.width;
|
|
742
|
+
to.height = from.height;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Checks if two Bounds objects have the same x, y, width and height values.
|
|
746
|
+
* @param {Bounds} b1 - The first Bounds object to compare.
|
|
747
|
+
* @param {Bounds} b2 - The second Bounds object to compare.
|
|
748
|
+
* @returns {boolean} - Returns true if both Bounds objects have the same x, y, width and height values, otherwise returns false.
|
|
749
|
+
*/
|
|
750
|
+
sameBounds(b1, b2) {
|
|
751
|
+
return b1.x === b2.x && b1.y === b2.y && b1.width === b2.width && b1.height === b2.height;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Applies default bounds to the provided bounds object.
|
|
755
|
+
* @param {Bounds} bounds - The bounds object to apply default bounds to.
|
|
756
|
+
* @returns {void}
|
|
757
|
+
* @private
|
|
758
|
+
*/
|
|
759
|
+
applyDefaultBounds(bounds) {
|
|
760
|
+
this.copyBounds(DEFAULT_BOUNDS, bounds);
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Sets the main candle series for the CanvasBoundsContainer.
|
|
764
|
+
* @param {CandleSeriesModel} candleSeries - The candle series to be set as the main candle series.
|
|
765
|
+
* @returns {void}
|
|
766
|
+
* @deprecated This method should be removed as candleSeries is not a part of CanvasBoundsContainer.
|
|
767
|
+
*/
|
|
768
|
+
setMainCandleSeries(candleSeries) {
|
|
769
|
+
this.mainCandleSeries = candleSeries;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
// paneCounter=chartHeightRatio: 0=1, 1=0.8, 2=0.6, 3=0.5, 4=0.4, 5=0.4
|
|
773
|
+
const DEFAULT_RATIOS = {
|
|
774
|
+
0: 1,
|
|
775
|
+
1: 0.8,
|
|
776
|
+
2: 0.6,
|
|
777
|
+
3: 0.5,
|
|
778
|
+
4: 0.4,
|
|
779
|
+
5: 0.2,
|
|
780
|
+
};
|
|
781
|
+
// NOTE: pec stands for panes except main chart
|
|
782
|
+
const getHeightRatios = (pecLength) => {
|
|
783
|
+
var _a;
|
|
784
|
+
const chartHeightRatio = (_a = DEFAULT_RATIOS[pecLength]) !== null && _a !== void 0 ? _a : 0.4;
|
|
785
|
+
const singlePecHeightRatio = (1 - chartHeightRatio) / pecLength;
|
|
786
|
+
return [chartHeightRatio, singlePecHeightRatio];
|
|
787
|
+
};
|
|
788
|
+
export const isInBounds = (point, bounds) => point.x > bounds.x && point.x < bounds.x + bounds.width && point.y > bounds.y && point.y < bounds.y + bounds.height;
|
|
789
|
+
export const isInVerticalBounds = (y, bounds) => y > bounds.y && y < bounds.y + bounds.height;
|
|
790
|
+
const upsertBounds = (storage, uuid, x, y, width, height, canvasOnPageLocation) => {
|
|
791
|
+
const existing = storage[uuid];
|
|
792
|
+
if (existing) {
|
|
793
|
+
existing.x = x;
|
|
794
|
+
existing.y = y;
|
|
795
|
+
existing.pageX = x + canvasOnPageLocation.x;
|
|
796
|
+
existing.pageY = y + canvasOnPageLocation.y;
|
|
797
|
+
existing.width = width;
|
|
798
|
+
existing.height = height;
|
|
799
|
+
return existing;
|
|
800
|
+
}
|
|
801
|
+
const newly = {
|
|
802
|
+
x,
|
|
803
|
+
y,
|
|
804
|
+
pageX: x + canvasOnPageLocation.x,
|
|
805
|
+
pageY: y + canvasOnPageLocation.y,
|
|
806
|
+
width,
|
|
807
|
+
height,
|
|
808
|
+
};
|
|
809
|
+
storage[uuid] = newly;
|
|
810
|
+
return newly;
|
|
811
|
+
};
|
|
812
|
+
export const limitYToBounds = (y, bounds) => Math.min(Math.max(y, bounds.y), bounds.y + bounds.height);
|
|
813
|
+
export const DEFAULT_HIT_TEST_OPTIONS = {
|
|
814
|
+
extensionX: 0,
|
|
815
|
+
extensionY: 0,
|
|
816
|
+
wholePage: false,
|
|
817
|
+
};
|
|
818
|
+
export const areBoundsChanged = (prev, next) => {
|
|
819
|
+
return prev.width === next.width && prev.height === next.height;
|
|
820
|
+
};
|