@devexperts/dxcharts-lite
Version:
163 lines (162 loc) • 8.13 kB
JavaScript
/*
* Copyright (C) 2019 - 2025 Devexperts Solutions IE Limited
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { Subject } from 'rxjs';
import { distinctUntilChanged, skip, startWith, filter } from 'rxjs/operators';
import { DynamicDrawerType } from '../../drawers/drawing-manager';
import { ChartBaseElement } from '../../model/chart-base-element';
import { DragNDropYComponent } from '../dran-n-drop_helper/drag-n-drop-y.component';
import { BarResizerDrawer } from './bar-resizer.drawer';
import { isMobile } from '../../utils/device/browser.utils';
export const RESIZER_HIT_TEST_EXTENSION = 8;
/**
* Bar separator between panes.
* Used to resize the areas height or just draw a fixed line.
* Supports hover animation.
*
* @doc-tags chart-core,resizer
*/
export class BarResizerComponent extends ChartBaseElement {
constructor(id, boundsProvider, hitTest, dragTickCb, dragPredicate, chartPanComponent, canvasModel, drawingManager, canvasInputListener, canvasAnimation, config, canvasBoundsContainer, hitTestCanvasModel) {
super();
this.id = id;
this.boundsProvider = boundsProvider;
this.hitTest = hitTest;
this.dragTickCb = dragTickCb;
this.dragPredicate = dragPredicate;
this.chartPanComponent = chartPanComponent;
this.canvasModel = canvasModel;
this.drawingManager = drawingManager;
this.canvasInputListener = canvasInputListener;
this.canvasAnimation = canvasAnimation;
this.config = config;
this.canvasBoundsContainer = canvasBoundsContainer;
this.hitTestCanvasModel = hitTestCanvasModel;
this.initialY = 0;
this.resizeEvent$ = new Subject();
this.onYDragStartMobile = () => {
this.config.components.crossTool.type = 'none';
this.onYDragStart();
};
this.onYDragStart = () => {
this.initialY = this.boundsProvider().y;
// Stop redrawing hit test
this.hitTestCanvasModel.hitTestDrawersPredicateSubject.next(false);
this.chartPanComponent.deactivatePanHandlers();
};
this.onYDragEndMobile = () => {
this.config.components.crossTool.type = 'cross-and-labels';
this.onYDragEnd();
};
this.onYDragEnd = () => {
this.initialY = this.boundsProvider().y;
this.canvasBoundsContainer.graphsHeightRatioChangedSubject.next(this.canvasBoundsContainer.graphsHeightRatio);
// Continue redrawing hit test
this.hitTestCanvasModel.hitTestDrawersPredicateSubject.next(true);
this.chartPanComponent.activateChartPanHandlers();
};
this.onYDragTick = (dragInfo) => {
const { delta: yDelta, draggedPixels } = dragInfo;
// in case cursor moves outside the bar - do not generate "tick"
if (Math.abs(this.initialY - this.boundsProvider().y + draggedPixels) >= 0) {
this.dragTickCb(yDelta);
this.resizeEvent$.next();
}
};
this.animationId = `${this.id}_RESIZER`;
}
/**
* This method activates the pane resizer component.
* It calls the parent class's doActivate method and then checks if the fixedMode property is set to false in the config object.
* If it is false, it creates a new DragNDropYComponent and adds it to the component list.
* It also adds a subscription to the canvasInputListener to observe mouse enter events and
* handle hover animations if the animation is enabled in the config object.
* Finally, it creates a new BarResizerDrawer and adds it to the drawing manager as a DynamicDrawerType.paneResizer with the current id.
* It also adds a subscription to remove the drawer when the component is deactivated.
* @protected
* @returns {void}
*/
doActivate() {
super.doActivate();
const fixedMode = this.config.components.paneResizer.fixedMode;
if (!fixedMode) {
const dragNDropYComponent = new DragNDropYComponent(this.hitTest, {
onDragTick: this.onYDragTick,
onDragStart: isMobile() ? this.onYDragStartMobile : this.onYDragStart,
onDragEnd: isMobile() ? this.onYDragEndMobile : this.onYDragEnd,
}, this.canvasInputListener, this.chartPanComponent, {
dragPredicate: this.dragPredicate,
});
this.addChildEntity(dragNDropYComponent);
if (this.config.animation.paneResizer.enabled) {
this.addRxSubscription(this.canvasInputListener
.observeMouseEnter(this.hitTest, true)
.pipe(
// set initial pipe state to false, so animation will play for the first time only for appearing
startWith(false), filter(this.dragPredicate), distinctUntilChanged(), skip(1))
.subscribe(enter => {
if (enter) {
this.handleHoverAnimation('appearing');
}
else {
this.handleHoverAnimation('fading');
}
}));
}
}
const barResizerDrawer = new BarResizerDrawer(this.config, this.boundsProvider, this.canvasModel, this.canvasAnimation, this.animationId);
this.drawingManager.addDrawer(barResizerDrawer, DynamicDrawerType.paneResizer(this.id));
this.addSubscription(() => this.drawingManager.removeDrawerByName(DynamicDrawerType.paneResizer(this.id)));
}
/**
* This method is called when the component is being deactivated.
* It calls the parent method doDeactivate() and then completes the resizeEvent$ observable.
* @protected
* @returns {void}
*/
doDeactivate() {
super.doDeactivate();
this.resizeEvent$.complete();
}
/**
* This function handles the hover animation of a pane resizer. It takes a type parameter which can be either 'fading' or 'appearing'.
* If the background mode is enabled, it gets the color alpha animation from the canvasAnimation and starts the color alpha animation
* with the given animationId and the color and type provided in the parameter. If the background mode is not enabled, it gets the color transition
* animation from the canvasAnimation and starts the color transition animation with the given animationId, startColor, endColor and type
* provided in the parameter and the duration from the config. If there is an animation in progress, it reverts it.
*
* @param {string} type - The type of animation, can be either 'fading' or 'appearing'.
* @returns {void}
*/
handleHoverAnimation(type) {
let animation;
if (this.config.animation.paneResizer.bgMode) {
animation = this.canvasAnimation.getColorAlphaAnimation(this.animationId);
if (!animation || !animation.animationInProgress) {
this.canvasAnimation.startColorAlphaAnimation(this.animationId, [
{
color: this.config.colors.paneResizer.bgHoverColor,
type,
},
]);
}
}
else {
animation = this.canvasAnimation.getColorTransitionAnimation(this.animationId);
if (!animation || !animation.animationInProgress) {
this.canvasAnimation.startColorTransitionAnimation(this.animationId, [
{
startColor: this.config.colors.paneResizer.bgColor,
endColor: this.config.colors.paneResizer.bgHoverColor,
type,
},
], this.config.animation.paneResizer.duration);
}
}
if (animation && animation.animationInProgress) {
animation.revert();
}
}
}