UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

622 lines (616 loc) • 25.5 kB
/** * DevExtreme (cjs/viz/series/points/symbol_point.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/ */ "use strict"; exports.default = void 0; var _extend2 = require("../../../core/utils/extend"); var _iterator = require("../../../core/utils/iterator"); var _common = require("../../../core/utils/common"); var _window = require("../../../core/utils/window"); var _label = require("./label"); var _type = require("../../../core/utils/type"); var _utils = require("../../core/utils"); const window = (0, _window.getWindow)(); const _extend = _extend2.extend; const _math = Math; const _round = _math.round; const _floor = _math.floor; const _ceil = _math.ceil; const DEFAULT_IMAGE_WIDTH = 20; const DEFAULT_IMAGE_HEIGHT = 20; const LABEL_OFFSET = 10; const CANVAS_POSITION_DEFAULT = "canvas_position_default"; function getSquareMarkerCoords(radius) { return [-radius, -radius, radius, -radius, radius, radius, -radius, radius, -radius, -radius] } function getPolygonMarkerCoords(radius) { const r = _ceil(radius); return [-r, 0, 0, -r, r, 0, 0, r, -r, 0] } function getCrossMarkerCoords(radius) { const r = _ceil(radius); const floorHalfRadius = _floor(r / 2); const ceilHalfRadius = _ceil(r / 2); return [-r, -floorHalfRadius, -floorHalfRadius, -r, 0, -ceilHalfRadius, floorHalfRadius, -r, r, -floorHalfRadius, ceilHalfRadius, 0, r, floorHalfRadius, floorHalfRadius, r, 0, ceilHalfRadius, -floorHalfRadius, r, -r, floorHalfRadius, -ceilHalfRadius, 0] } function getTriangleDownMarkerCoords(radius) { return [-radius, -radius, radius, -radius, 0, radius, -radius, -radius] } function getTriangleUpMarkerCoords(radius) { return [-radius, radius, radius, radius, 0, -radius, -radius, radius] } var _default = exports.default = { deleteLabel: function() { this._label.dispose(); this._label = null }, _hasGraphic: function() { return this.graphic }, clearVisibility: function() { const graphic = this.graphic; if (graphic && graphic.attr("visibility")) { graphic.attr({ visibility: null }) } }, isVisible: function() { return this.inVisibleArea && this.series.isVisible() }, setInvisibility: function() { const graphic = this.graphic; if (graphic && "hidden" !== graphic.attr("visibility")) { graphic.attr({ visibility: "hidden" }) } this._errorBar && this._errorBar.attr({ visibility: "hidden" }); this._label.draw(false) }, clearMarker: function() { const graphic = this.graphic; graphic && graphic.attr(this._emptySettings) }, _createLabel: function() { this._label = new _label.Label({ renderer: this.series._renderer, labelsGroup: this.series._labelsGroup, point: this }) }, _calculateVisibility: function(x, y) { const { minX: minX, maxX: maxX, minY: minY, maxY: maxY } = this._getVisibleArea(); this.inVisibleArea = minX <= x && maxX >= x && minY <= y && maxY >= y }, _updateLabelData: function() { this._label.setData(this._getLabelFormatObject()) }, _updateLabelOptions: function() { !this._label && this._createLabel(); this._label.setOptions(this._options.label) }, _checkImage: function(image) { return (0, _type.isDefined)(image) && ("string" === typeof image || (0, _type.isDefined)(image.url)) }, _fillStyle: function() { this._styles = this._options.styles }, _checkSymbol: function(oldOptions, newOptions) { const oldSymbol = oldOptions.symbol; const newSymbol = newOptions.symbol; const symbolChanged = "circle" === oldSymbol && "circle" !== newSymbol || "circle" !== oldSymbol && "circle" === newSymbol; const imageChanged = this._checkImage(oldOptions.image) !== this._checkImage(newOptions.image); return !!(symbolChanged || imageChanged) }, _populatePointShape: function(symbol, radius) { switch (symbol) { case "square": return getSquareMarkerCoords(radius); case "polygon": return getPolygonMarkerCoords(radius); case "triangle": case "triangleDown": return getTriangleDownMarkerCoords(radius); case "triangleUp": return getTriangleUpMarkerCoords(radius); case "cross": return getCrossMarkerCoords(radius) } }, hasCoords: function() { return null !== this.x && null !== this.y }, correctValue: function(correction) { const that = this; const axis = that.series.getValueAxis(); if (that.hasValue()) { that.value = that.properValue = axis.validateUnit(that.initialValue.valueOf() + correction.valueOf()); that.minValue = axis.validateUnit(correction) } }, resetCorrection: function() { this.value = this.properValue = this.initialValue; this.minValue = CANVAS_POSITION_DEFAULT }, resetValue: function() { const that = this; if (that.hasValue()) { that.value = that.properValue = that.initialValue = 0; that.minValue = 0; that._label.setDataField("value", that.value) } }, _getTranslates: function(animationEnabled) { let translateX = this.x; let translateY = this.y; if (animationEnabled) { if (this._options.rotated) { translateX = this.defaultX } else { translateY = this.defaultY } } return { x: translateX, y: translateY } }, _createImageMarker: function(renderer, settings, options) { const width = options.width || 20; const height = options.height || 20; return renderer.image(-_round(.5 * width), -_round(.5 * height), width, height, options.url ? options.url.toString() : options.toString(), "center").attr({ translateX: settings.translateX, translateY: settings.translateY, visibility: settings.visibility }) }, _createSymbolMarker: function(renderer, pointSettings) { let marker; const symbol = this._options.symbol; if ("circle" === symbol) { delete pointSettings.points; marker = renderer.circle().attr(pointSettings) } else if ("square" === symbol || "polygon" === symbol || "triangle" === symbol || "triangleDown" === symbol || "triangleUp" === symbol || "cross" === symbol) { marker = renderer.path([], "area").attr(pointSettings).sharp() } return marker }, _createMarker: function(renderer, group, image, settings) { const that = this; const marker = that._checkImage(image) ? that._createImageMarker(renderer, settings, image) : that._createSymbolMarker(renderer, settings); if (marker) { marker.data({ "chart-data-point": that }).append(group) } return marker }, _getSymbolBBox: function(x, y, r) { return { x: x - r, y: y - r, width: 2 * r, height: 2 * r } }, _getImageBBox: function(x, y) { const image = this._options.image; const width = image.width || 20; const height = image.height || 20; return { x: x - _round(width / 2), y: y - _round(height / 2), width: width, height: height } }, _getGraphicBBox: function() { const that = this; const options = that._options; const x = that.x; const y = that.y; let bBox; if (options.visible) { bBox = that._checkImage(options.image) ? that._getImageBBox(x, y) : that._getSymbolBBox(x, y, options.styles.normal.r) } else { bBox = { x: x, y: y, width: 0, height: 0 } } return bBox }, hideInsideLabel: _common.noop, _getShiftLabelCoords: function(label) { const coord = this._addLabelAlignmentAndOffset(label, this._getLabelCoords(label)); return this._checkLabelPosition(label, coord) }, _drawLabel: function() { const customVisibility = this._getCustomLabelVisibility(); const label = this._label; const isVisible = this._showForZeroValues() && this.hasValue() && false !== customVisibility && (this.series.getLabelVisibility() || customVisibility); label.draw(!!isVisible) }, correctLabelPosition: function(label) { const that = this; const coord = that._getShiftLabelCoords(label); if (!that.hideInsideLabel(label, coord)) { label.setFigureToDrawConnector(that._getLabelConnector(label.pointPosition)); label.shift(_round(coord.x), _round(coord.y)) } }, _showForZeroValues: function() { return true }, _getLabelConnector: function(pointPosition) { const bBox = this._getGraphicBBox(pointPosition); const w2 = bBox.width / 2; const h2 = bBox.height / 2; return { x: bBox.x + w2, y: bBox.y + h2, r: this._options.visible ? Math.max(w2, h2) : 0 } }, _getPositionFromLocation: function() { return { x: this.x, y: this.y } }, _isPointInVisibleArea: function(visibleArea, graphicBBox) { return visibleArea.minX <= graphicBBox.x + graphicBBox.width && visibleArea.maxX >= graphicBBox.x && visibleArea.minY <= graphicBBox.y + graphicBBox.height && visibleArea.maxY >= graphicBBox.y }, _checkLabelPosition: function(label, coord) { const that = this; const visibleArea = that._getVisibleArea(); const labelBBox = label.getBoundingRect(); const graphicBBox = that._getGraphicBBox(label.pointPosition); const fullGraphicBBox = that._getGraphicBBox(); const isInside = "inside" === label.getLayoutOptions().position; if (that._isPointInVisibleArea(visibleArea, fullGraphicBBox)) { if (!that._options.rotated) { if (visibleArea.minX > coord.x) { coord.x = visibleArea.minX } if (visibleArea.maxX < coord.x + labelBBox.width) { coord.x = visibleArea.maxX - labelBBox.width } if (visibleArea.minY > coord.y) { coord.y = isInside ? visibleArea.minY : graphicBBox.y + graphicBBox.height + 10 } if (visibleArea.maxY < coord.y + labelBBox.height) { coord.y = isInside ? visibleArea.maxY - labelBBox.height : graphicBBox.y - labelBBox.height - 10 } } else { if (visibleArea.minX > coord.x) { coord.x = isInside ? visibleArea.minX : graphicBBox.x + graphicBBox.width + 10 } if (visibleArea.maxX < coord.x + labelBBox.width) { coord.x = isInside ? visibleArea.maxX - labelBBox.width : graphicBBox.x - 10 - labelBBox.width } if (visibleArea.minY > coord.y) { coord.y = visibleArea.minY } if (visibleArea.maxY < coord.y + labelBBox.height) { coord.y = visibleArea.maxY - labelBBox.height } } } return coord }, _addLabelAlignmentAndOffset: function(label, coord) { const labelBBox = label.getBoundingRect(); const labelOptions = label.getLayoutOptions(); if (!this._options.rotated) { if ("left" === labelOptions.alignment) { coord.x += labelBBox.width / 2 } else if ("right" === labelOptions.alignment) { coord.x -= labelBBox.width / 2 } } coord.x += labelOptions.horizontalOffset; coord.y += labelOptions.verticalOffset; return coord }, _getLabelCoords: function(label) { return this._getLabelCoordOfPosition(label, this._getLabelPosition(label.pointPosition)) }, _getLabelCoordOfPosition: function(label, position) { const labelBBox = label.getBoundingRect(); const graphicBBox = this._getGraphicBBox(label.pointPosition); const centerY = graphicBBox.height / 2 - labelBBox.height / 2; const centerX = graphicBBox.width / 2 - labelBBox.width / 2; let x = graphicBBox.x; let y = graphicBBox.y; switch (position) { case "left": x -= labelBBox.width + 10; y += centerY; break; case "right": x += graphicBBox.width + 10; y += centerY; break; case "top": x += centerX; y -= labelBBox.height + 10; break; case "bottom": x += centerX; y += graphicBBox.height + 10; break; case "inside": x += centerX; y += centerY } return { x: x, y: y } }, _drawMarker: function(renderer, group, animationEnabled) { const options = this._options; const translates = this._getTranslates(animationEnabled); const style = this._getStyle(); this.graphic = this._createMarker(renderer, group, options.image, _extend({ translateX: translates.x, translateY: translates.y, points: this._populatePointShape(options.symbol, style.r) }, style)) }, _getErrorBarSettings: function() { return { visibility: "visible" } }, _getErrorBarBaseEdgeLength() { return 2 * this.getPointRadius() }, _drawErrorBar: function(renderer, group) { if (!this._options.errorBars) { return } const that = this; const options = that._options; const errorBarOptions = options.errorBars; const points = []; let settings; const pos = that._errorBarPos; let high = that._highErrorCoord; let low = that._lowErrorCoord; const displayMode = (0, _utils.normalizeEnum)(errorBarOptions.displayMode); const isHighDisplayMode = "high" === displayMode; const isLowDisplayMode = "low" === displayMode; const highErrorOnly = (isHighDisplayMode || !(0, _type.isDefined)(low)) && (0, _type.isDefined)(high) && !isLowDisplayMode; const lowErrorOnly = (isLowDisplayMode || !(0, _type.isDefined)(high)) && (0, _type.isDefined)(low) && !isHighDisplayMode; let edgeLength = errorBarOptions.edgeLength; if (edgeLength <= 1 && edgeLength > 0) { edgeLength = this._getErrorBarBaseEdgeLength() * errorBarOptions.edgeLength } edgeLength = _floor(parseInt(edgeLength) / 2); highErrorOnly && (low = that._baseErrorBarPos); lowErrorOnly && (high = that._baseErrorBarPos); if ("none" !== displayMode && (0, _type.isDefined)(high) && (0, _type.isDefined)(low) && (0, _type.isDefined)(pos)) { !lowErrorOnly && points.push([pos - edgeLength, high, pos + edgeLength, high]); points.push([pos, high, pos, low]); !highErrorOnly && points.push([pos + edgeLength, low, pos - edgeLength, low]); options.rotated && (0, _iterator.each)(points, (function(_, p) { p.reverse() })); settings = that._getErrorBarSettings(errorBarOptions); if (!that._errorBar) { that._errorBar = renderer.path(points, "line").attr(settings).append(group) } else { settings.points = points; that._errorBar.attr(settings) } } else { that._errorBar && that._errorBar.attr({ visibility: "hidden" }) } }, getTooltipParams: function() { const graphic = this.graphic; return { x: this.x, y: this.y, offset: graphic ? graphic.getBBox().height / 2 : 0 } }, setPercentValue: function(absTotal, total, leftHoleTotal, rightHoleTotal) { const that = this; const valuePercent = that.value / absTotal || 0; const minValuePercent = that.minValue / absTotal || 0; const percent = valuePercent - minValuePercent; that._label.setDataField("percent", percent); that._label.setDataField("total", total); if (that.series.isFullStackedSeries() && that.hasValue()) { if (that.leftHole) { that.leftHole /= absTotal - leftHoleTotal; that.minLeftHole /= absTotal - leftHoleTotal } if (that.rightHole) { that.rightHole /= absTotal - rightHoleTotal; that.minRightHole /= absTotal - rightHoleTotal } that.value = that.properValue = valuePercent; that.minValue = !minValuePercent ? that.minValue : minValuePercent } }, _storeTrackerR: function() { let navigator = window.navigator; const r = this._options.styles.normal.r; const minTrackerSize = (0, _window.hasProperty)("ontouchstart") || navigator.msPointerEnabled && navigator.msMaxTouchPoints || navigator.pointerEnabled && navigator.maxTouchPoints ? 20 : 6; this._options.trackerR = r < minTrackerSize ? minTrackerSize : r; return this._options.trackerR }, _translateErrorBars: function() { const options = this._options; const rotated = options.rotated; const errorBars = options.errorBars; const translator = this._getValTranslator(); if (!errorBars) { return }(0, _type.isDefined)(this.lowError) && (this._lowErrorCoord = translator.translate(this.lowError)); (0, _type.isDefined)(this.highError) && (this._highErrorCoord = translator.translate(this.highError)); this._errorBarPos = _floor(rotated ? this.vy : this.vx); this._baseErrorBarPos = "stdDeviation" === errorBars.type ? this._lowErrorCoord + (this._highErrorCoord - this._lowErrorCoord) / 2 : rotated ? this.vx : this.vy }, _translate: function() { const that = this; const valTranslator = that._getValTranslator(); const argTranslator = that._getArgTranslator(); if (that._options.rotated) { that.vx = that.x = valTranslator.translate(that.value, void 0, true); that.vy = that.y = argTranslator.translate(that.argument, void 0, true); that.minX = valTranslator.translate(that.minValue, void 0, true); that.defaultX = valTranslator.translate(CANVAS_POSITION_DEFAULT) } else { that.vy = that.y = valTranslator.translate(that.value, void 0, true); that.vx = that.x = argTranslator.translate(that.argument, void 0, true); that.minY = valTranslator.translate(that.minValue, void 0, true); that.defaultY = valTranslator.translate(CANVAS_POSITION_DEFAULT) } that._translateErrorBars(); that._calculateVisibility(that.x, that.y) }, _updateData: function(data) { this.value = this.properValue = this.initialValue = this.originalValue = data.value; this.minValue = this.initialMinValue = this.originalMinValue = (0, _type.isDefined)(data.minValue) ? data.minValue : CANVAS_POSITION_DEFAULT }, _getImageSettings: function(image) { return { href: image.url || image.toString(), width: image.width || 20, height: image.height || 20 } }, getCrosshairData: function() { const r = this._options.rotated; const value = this.properValue; const argument = this.argument; return { x: this.vx, y: this.vy, xValue: r ? value : argument, yValue: r ? argument : value, axis: this.series.axis } }, getPointRadius: function() { const style = this._getStyle(); const options = this._options; const r = style.r; let extraSpace; const symbol = options.symbol; const isSquare = "square" === symbol; const isTriangle = "triangle" === symbol || "triangleDown" === symbol || "triangleUp" === symbol; if (options.visible && !options.image && r) { extraSpace = style["stroke-width"] / 2; return (isSquare || isTriangle ? 1.4 * r : r) + extraSpace } return 0 }, _updateMarker: function(animationEnabled, style) { const that = this; const options = that._options; let settings; const image = options.image; const visibility = !that.isVisible() ? { visibility: "hidden" } : {}; if (that._checkImage(image)) { settings = _extend({}, { visibility: style.visibility }, visibility, that._getImageSettings(image)) } else { settings = _extend({}, style, visibility, { points: that._populatePointShape(options.symbol, style.r) }) } if (!animationEnabled) { settings.translateX = that.x; settings.translateY = that.y } that.graphic.attr(settings).sharp() }, _getLabelFormatObject: function() { return { argument: this.initialArgument, value: this.initialValue, originalArgument: this.originalArgument, originalValue: this.originalValue, seriesName: this.series.name, lowErrorValue: this.lowError, highErrorValue: this.highError, point: this } }, _getLabelPosition: function() { const rotated = this._options.rotated; if (this.initialValue > 0) { return rotated ? "right" : "top" } else { return rotated ? "left" : "bottom" } }, _getFormatObject: function(tooltip) { const labelFormatObject = this._label.getData(); return _extend({}, labelFormatObject, { argumentText: tooltip.formatValue(this.initialArgument, "argument"), valueText: tooltip.formatValue(this.initialValue) }, (0, _type.isDefined)(labelFormatObject.percent) ? { percentText: tooltip.formatValue(labelFormatObject.percent, "percent") } : {}, (0, _type.isDefined)(labelFormatObject.total) ? { totalText: tooltip.formatValue(labelFormatObject.total) } : {}) }, getMarkerVisibility: function() { return this._options.visible }, coordsIn: function(x, y) { const trackerRadius = this._storeTrackerR(); return x >= this.x - trackerRadius && x <= this.x + trackerRadius && y >= this.y - trackerRadius && y <= this.y + trackerRadius }, getMinValue: function(noErrorBar) { const errorBarOptions = this._options.errorBars; if (errorBarOptions && !noErrorBar) { const displayMode = errorBarOptions.displayMode; const lowValue = "high" !== displayMode && (0, _type.isDefined)(this.lowError) ? this.lowError : this.value; const highValue = "low" !== displayMode && (0, _type.isDefined)(this.highError) ? this.highError : this.value; return lowValue < highValue ? lowValue : highValue } else { return this.value } }, getMaxValue: function(noErrorBar) { const errorBarOptions = this._options.errorBars; if (errorBarOptions && !noErrorBar) { const displayMode = errorBarOptions.displayMode; const lowValue = "high" !== displayMode && (0, _type.isDefined)(this.lowError) ? this.lowError : this.value; const highValue = "low" !== displayMode && (0, _type.isDefined)(this.highError) ? this.highError : this.value; return lowValue > highValue ? lowValue : highValue } else { return this.value } } }; module.exports = exports.default; module.exports.default = exports.default;