UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

437 lines (430 loc) • 16.8 kB
/** * DevExtreme (esm/viz/vector_map/control_bar/control_bar.js) * Version: 24.2.6 * Build date: Mon Mar 17 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ const _math = Math; const _min = _math.min; const _max = _math.max; const _round = _math.round; const _floor = _math.floor; const _sqrt = _math.sqrt; import { parseScalar as _parseScalar, enumParser } from "../../core/utils"; import { createTracker, createVisibilityGroup, toggleDisplay } from "./utils"; const parseHorizontalAlignment = enumParser(["left", "center", "right"]); const parseVerticalAlignment = enumParser(["top", "bottom"]); const COMMAND_RESET = "command-reset"; const COMMAND_MOVE_UP = "command-move-up"; const COMMAND_MOVE_RIGHT = "command-move-right"; const COMMAND_MOVE_DOWN = "command-move-down"; const COMMAND_MOVE_LEFT = "command-move-left"; const COMMAND_ZOOM_IN = "command-zoom-in"; const COMMAND_ZOOM_OUT = "command-zoom-out"; const COMMAND_ZOOM_DRAG_LINE = "command-zoom-drag-line"; const COMMAND_ZOOM_DRAG = "command-zoom-drag"; const EVENT_TARGET_TYPE = "control-bar"; const FLAG_CENTERING = 1; const FLAG_ZOOMING = 2; const SIZE_OPTIONS = { bigCircleSize: 58, smallCircleSize: 28, buttonSize: 10, arrowButtonOffset: 20, incDecButtonSize: 11, incButtonOffset: 66, decButtonOffset: 227, sliderLineStartOffset: 88.5, sliderLineEndOffset: 205.5, sliderLength: 20, sliderWidth: 8, trackerGap: 4 }; const OFFSET_X = 30.5; const OFFSET_Y = 30.5; const TOTAL_WIDTH = 61; const TOTAL_HEIGHT = 274; let COMMAND_TO_TYPE_MAP = {}; COMMAND_TO_TYPE_MAP[COMMAND_RESET] = ResetCommand; COMMAND_TO_TYPE_MAP[COMMAND_MOVE_UP] = COMMAND_TO_TYPE_MAP[COMMAND_MOVE_RIGHT] = COMMAND_TO_TYPE_MAP[COMMAND_MOVE_DOWN] = COMMAND_TO_TYPE_MAP[COMMAND_MOVE_LEFT] = MoveCommand; COMMAND_TO_TYPE_MAP[COMMAND_ZOOM_IN] = COMMAND_TO_TYPE_MAP[COMMAND_ZOOM_OUT] = ZoomCommand; COMMAND_TO_TYPE_MAP[COMMAND_ZOOM_DRAG] = ZoomDragCommand; export function ControlBar(parameters) { this._params = parameters; this._createElements(parameters.renderer, parameters.container, parameters.dataKey); parameters.layoutControl.addItem(this); this._subscribeToProjection(parameters.projection); this._subscribeToTracker(parameters.tracker); this._createCallbacks(parameters.projection) } ControlBar.prototype = { constructor: ControlBar, _flags: 0, dispose: function() { this._params.layoutControl.removeItem(this); this._root.linkRemove().linkOff(); this._offProjection(); this._offTracker(); this._params = this._root = this._offProjection = this._offTracker = this._callbacks = null }, _subscribeToProjection: function(projection) { const that = this; that._offProjection = projection.on({ engine: function() { that._update() }, zoom: updateZoom, "max-zoom": function() { that._zoomPartition = projection.getZoomScalePartition(); that._sliderUnitLength = that._sliderLineLength / that._zoomPartition; updateZoom() } }); function updateZoom() { that._adjustZoom(projection.getScaledZoom()) } }, _subscribeToTracker: function(tracker) { const that = this; let isActive = false; that._offTracker = tracker.on({ start: function(arg) { isActive = "control-bar" === arg.data.name; if (isActive) { that._processStart(arg.data.index, arg) } }, move: function(arg) { if (isActive) { that._processMove(arg.data.index, arg) } }, end: function() { if (isActive) { that._processEnd(); isActive = false } } }) }, _createCallbacks: function(projection) { this._callbacks = { reset: function(isCenter, isZoom) { if (isCenter) { projection.setCenter(null) } if (isZoom) { projection.setZoom(null) } }, beginMove: function() { projection.beginMoveCenter() }, endMove: function() { projection.endMoveCenter() }, move: function(shift) { projection.moveCenter(shift) }, zoom: function(zoom) { projection.setScaledZoom(zoom) } } }, _createElements: function(renderer, container, dataKey) { this._root = renderer.g().attr({ class: "dxm-control-bar" }).linkOn(container, "control-bar"); const panControl = this._panControl = createVisibilityGroup(renderer, this._root, "dxm-pan-control"); const zoomBar = this._zoomBar = createVisibilityGroup(renderer, this._root, "dxm-zoom-bar"); const trackersPan = this._trackersPan = createTracker(renderer, this._root); const trackersZoom = this._trackersZoom = createTracker(renderer, this._root); this._createTrackersPan(renderer, dataKey, trackersPan); this._createTrackersZoom(renderer, dataKey, trackersZoom); this._createPanControl(renderer, dataKey, panControl); this._createZoomBar(renderer, dataKey, zoomBar) }, _createPanControl: function(renderer, dataKey, group) { const options = SIZE_OPTIONS; const size = options.buttonSize / 2; const offset1 = options.arrowButtonOffset - size; const offset2 = options.arrowButtonOffset; const directionOptions = { "stroke-linecap": "square", fill: "none" }; renderer.circle(0, 0, options.bigCircleSize / 2).append(group); renderer.circle(0, 0, size).attr({ fill: "none" }).append(group); renderer.path([-size, -offset1, 0, -offset2, size, -offset1], "line").attr(directionOptions).append(group); renderer.path([offset1, -size, offset2, 0, offset1, size], "line").attr(directionOptions).append(group); renderer.path([size, offset1, 0, offset2, -size, offset1], "line").attr(directionOptions).append(group); renderer.path([-offset1, size, -offset2, 0, -offset1, -size], "line").attr(directionOptions).append(group) }, _createZoomBar: function(renderer, dataKey, group) { const options = SIZE_OPTIONS; const incDecButtonSize = options.incDecButtonSize / 2; renderer.circle(0, options.incButtonOffset, options.smallCircleSize / 2).append(group); renderer.path([ [-incDecButtonSize, options.incButtonOffset, incDecButtonSize, options.incButtonOffset], [0, options.incButtonOffset - incDecButtonSize, 0, options.incButtonOffset + incDecButtonSize] ], "area").append(group); renderer.circle(0, options.decButtonOffset, options.smallCircleSize / 2).append(group); renderer.path([-incDecButtonSize, options.decButtonOffset, incDecButtonSize, options.decButtonOffset], "area").append(group); this._zoomLine = renderer.path([], "line").append(group); this._zoomDrag = renderer.rect(_floor(-options.sliderLength / 2), _floor(options.sliderLineEndOffset - options.sliderWidth / 2), options.sliderLength, options.sliderWidth).append(group); this._sliderLineLength = options.sliderLineEndOffset - options.sliderLineStartOffset }, _createTrackersPan: function(renderer, dataKey, group) { const options = SIZE_OPTIONS; const size = _round((options.arrowButtonOffset - options.trackerGap) / 2); const offset1 = options.arrowButtonOffset - size; const offset2 = _round(_sqrt(options.bigCircleSize * options.bigCircleSize / 4 - size * size)); const size2 = offset2 - offset1; renderer.rect(-size, -size, 2 * size, 2 * size).data(dataKey, { index: COMMAND_RESET, name: "control-bar" }).append(group); renderer.rect(-size, -offset2, 2 * size, size2).data(dataKey, { index: COMMAND_MOVE_UP, name: "control-bar" }).append(group); renderer.rect(offset1, -size, size2, 2 * size).data(dataKey, { index: COMMAND_MOVE_RIGHT, name: "control-bar" }).append(group); renderer.rect(-size, offset1, 2 * size, size2).data(dataKey, { index: COMMAND_MOVE_DOWN, name: "control-bar" }).append(group); renderer.rect(-offset2, -size, size2, 2 * size).data(dataKey, { index: COMMAND_MOVE_LEFT, name: "control-bar" }).append(group) }, _createTrackersZoom: function(renderer, dataKey, group) { const options = SIZE_OPTIONS; renderer.circle(0, options.incButtonOffset, options.smallCircleSize / 2).data(dataKey, { index: COMMAND_ZOOM_IN, name: "control-bar" }).append(group); renderer.circle(0, options.decButtonOffset, options.smallCircleSize / 2).data(dataKey, { index: COMMAND_ZOOM_OUT, name: "control-bar" }).append(group); renderer.rect(-2, options.sliderLineStartOffset - 2, 4, options.sliderLineEndOffset - options.sliderLineStartOffset + 4).css({ cursor: "default" }).data(dataKey, { index: COMMAND_ZOOM_DRAG_LINE, name: "control-bar" }).append(group); this._zoomDragTracker = renderer.rect(-options.sliderLength / 2, options.sliderLineEndOffset - options.sliderWidth / 2, options.sliderLength, options.sliderWidth).data(dataKey, { index: COMMAND_ZOOM_DRAG, name: "control-bar" }).append(group) }, resize: function(size) { if (this._isActive) { this._root.attr({ visibility: null !== size ? null : "hidden" }) } }, getLayoutOptions: function() { return this._isActive ? this._layoutOptions : null }, locate: function(x, y) { this._root.attr({ translateX: x + this._margin + 30.5, translateY: y + this._margin + 30.5 }) }, _update: function() { const that = this; that._isActive = that._isEnabled && that._flags && that._params.projection.isInvertible(); const groupPan = [that._panControl, that._trackersPan]; const groupZoom = [that._zoomBar, that._trackersZoom]; if (that._isActive) { that._root.linkAppend(); toggleDisplay(groupPan, that._isPanVisible); toggleDisplay(groupZoom, that._isZoomVisible) } else { that._root.linkRemove() } that._processEnd(); that.updateLayout() }, setInteraction: function(interaction) { const that = this; if (_parseScalar(interaction.centeringEnabled, true)) { that._flags |= 1 } else { that._flags &= -2 } if (_parseScalar(interaction.zoomingEnabled, true)) { that._flags |= 2 } else { that._flags &= -3 } that._update() }, setOptions: function(options) { const styleSvg = { "stroke-width": options.borderWidth, stroke: options.borderColor, fill: options.color, "fill-opacity": options.opacity }; this._isEnabled = !!_parseScalar(options.enabled, true); this._margin = options.margin || 0; this._layoutOptions = { width: 2 * this._margin + 61, height: 2 * this._margin + 274, horizontalAlignment: parseHorizontalAlignment(options.horizontalAlignment, "left"), verticalAlignment: parseVerticalAlignment(options.verticalAlignment, "top") }; this._isPanVisible = !!_parseScalar(options.panVisible, true); this._isZoomVisible = !!_parseScalar(options.zoomVisible, true); this._panControl.attr(styleSvg); this._zoomBar.attr(styleSvg); this._update() }, _adjustZoom: function(zoom) { const start = SIZE_OPTIONS.sliderLineStartOffset; const end = SIZE_OPTIONS.sliderLineEndOffset; const h = SIZE_OPTIONS.sliderWidth; this._zoomFactor = _max(_min(_round(zoom), this._zoomPartition), 0); const transform = { translateY: -_round(this._zoomFactor * this._sliderUnitLength) }; const y = end - h / 2 + transform.translateY; this._zoomLine.attr({ points: [ [0, start, 0, _max(start, y)], [0, _min(end, y + h), 0, end] ] }); this._zoomDrag.attr(transform); this._zoomDragTracker.attr(transform) }, _applyZoom: function() { this._callbacks.zoom(this._zoomFactor) }, _processStart: function(command, arg) { let commandType; if (this._isActive) { commandType = COMMAND_TO_TYPE_MAP[command]; this._command = commandType && commandType.flags & this._flags ? new commandType(this, command, arg) : null } }, _processMove: function(command, arg) { this._command && this._command.update(command, arg) }, _processEnd: function() { this._command && this._command.finish(); this._command = null } }; function disposeCommand(command) { delete command._owner; command.update = function() {}; command.finish = function() {} } function ResetCommand(owner, command) { this._owner = owner; this._command = command } ResetCommand.flags = 3; ResetCommand.prototype.update = function(command) { command !== this._command && disposeCommand(this) }; ResetCommand.prototype.finish = function() { const flags = this._owner._flags; this._owner._callbacks.reset(!!(1 & flags), !!(2 & flags)); disposeCommand(this) }; function MoveCommand(owner, command, arg) { this._command = command; let timeout = null; let dx = 0; let dy = 0; switch (this._command) { case COMMAND_MOVE_UP: dy = -10; break; case COMMAND_MOVE_RIGHT: dx = 10; break; case COMMAND_MOVE_DOWN: dy = 10; break; case COMMAND_MOVE_LEFT: dx = -10 } this._stop = function() { clearTimeout(timeout); owner._callbacks.endMove(); this._stop = owner = null; return this }; 0; owner._callbacks.beginMove(); ! function callback() { owner._callbacks.move([dx, dy]); timeout = setTimeout(callback, 100) }() } MoveCommand.flags = 1; MoveCommand.prototype.update = function(command) { this._command !== command && this.finish() }; MoveCommand.prototype.finish = function() { disposeCommand(this._stop()) }; function ZoomCommand(owner, command) { this._owner = owner; this._command = command; let timeout = null; const dZoom = this._command === COMMAND_ZOOM_IN ? 1 : -1; this._stop = function() { clearTimeout(timeout); this._stop = owner = null; return this }; ! function callback() { owner._adjustZoom(owner._zoomFactor + dZoom); timeout = setTimeout(callback, 150) }() } ZoomCommand.flags = 2; ZoomCommand.prototype.update = function(command) { this._command !== command && this.finish() }; ZoomCommand.prototype.finish = function() { this._owner._applyZoom(); disposeCommand(this._stop()) }; function ZoomDragCommand(owner, command, arg) { this._owner = owner; this._zoomFactor = owner._zoomFactor; this._pos = arg.y } ZoomDragCommand.flags = 2; ZoomDragCommand.prototype.update = function(command, arg) { const owner = this._owner; owner._adjustZoom(this._zoomFactor + owner._zoomPartition * (this._pos - arg.y) / owner._sliderLineLength) }; ZoomDragCommand.prototype.finish = function() { this._owner._applyZoom(); disposeCommand(this) };