devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
458 lines (456 loc) • 16.4 kB
JavaScript
/**
* DevExtreme (esm/viz/gauges/base_indicators.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/
*/
import {
noop
} from "../../core/utils/common";
import {
each
} from "../../core/utils/iterator";
const _isFinite = isFinite;
const _Number = Number;
const _round = Math.round;
import {
formatValue,
getSampleText
} from "./base_gauge";
const _formatValue = formatValue;
const _getSampleText = getSampleText;
import {
patchFontOptions as _patchFontOptions,
extractColor
} from "../core/utils";
import {
extend
} from "../../core/utils/extend";
import Class from "../../core/class";
export const BaseElement = Class.inherit({
ctor: function(parameters) {
const that = this;
each(parameters, (function(name, value) {
that["_" + name] = value
}));
that._init()
},
dispose: function() {
const that = this;
that._dispose();
each(that, (function(name) {
that[name] = null
}));
return that
},
getOffset: function() {
return _Number(this._options.offset) || 0
}
});
export const BaseIndicator = BaseElement.inherit({
_init: function() {
this._rootElement = this._createRoot().linkOn(this._owner, {
name: "value-indicator",
after: "core"
});
this._trackerElement = this._createTracker()
},
_dispose: function() {
this._rootElement.linkOff()
},
_setupAnimation: function() {
const that = this;
if (that._options.animation) {
that._animation = {
step: function(pos) {
that._actualValue = that._animation.start + that._animation.delta * pos;
that._actualPosition = that._translator.translate(that._actualValue);
that._move()
},
duration: that._options.animation.duration > 0 ? _Number(that._options.animation.duration) : 0,
easing: that._options.animation.easing
}
}
},
_runAnimation: function(value) {
const animation = this._animation;
animation.start = this._actualValue;
animation.delta = value - this._actualValue;
this._rootElement.animate({
_: 0
}, {
step: animation.step,
duration: animation.duration,
easing: animation.easing
})
},
_createRoot: function() {
return this._renderer.g().attr({
class: this._className
})
},
_createTracker: function() {
return this._renderer.path([], "area")
},
_getTrackerSettings: noop,
clean: function() {
this._animation && this._rootElement.stopAnimation();
this._rootElement.linkRemove().clear();
this._clear();
this._tracker.detach(this._trackerElement);
this._options = this.enabled = this._animation = null;
return this
},
render: function(options) {
const that = this;
that.type = options.type;
that._options = options;
that._actualValue = that._currentValue = that._translator.adjust(that._options.currentValue);
that.enabled = that._isEnabled();
if (that.enabled) {
that._setupAnimation();
that._rootElement.attr({
fill: extractColor(that._options.color)
}).linkAppend();
that._tracker.attach(that._trackerElement, that, that._trackerInfo)
}
return that
},
resize: function(layout) {
const that = this;
that._rootElement.clear();
that._clear();
that.visible = that._isVisible(layout);
if (that.visible) {
extend(that._options, layout);
that._actualPosition = that._translator.translate(that._actualValue);
that._render();
that._trackerElement.attr(that._getTrackerSettings());
that._move()
}
return that
},
value: function(arg, _noAnimation) {
const that = this;
let val;
const rootElement = this._rootElement;
let visibility = null;
if (void 0 === arg) {
return that._currentValue
}
if (null === arg) {
visibility = "hidden";
that._currentValue = arg
} else {
val = that._translator.adjust(arg);
if (that._currentValue !== val && _isFinite(val)) {
that._currentValue = val;
if (that.visible) {
if (that._animation && !_noAnimation) {
that._runAnimation(val)
} else {
that._actualValue = val;
that._actualPosition = that._translator.translate(val);
that._move()
}
}
}
}
rootElement.attr({
visibility: visibility
});
return that
},
_isEnabled: null,
_isVisible: null,
_render: null,
_clear: null,
_move: null
});
const COEFFICIENTS_MAP = {};
COEFFICIENTS_MAP["right-bottom"] = COEFFICIENTS_MAP.rb = [0, -1, -1, 0, 0, 1, 1, 0];
COEFFICIENTS_MAP["bottom-right"] = COEFFICIENTS_MAP.br = [-1, 0, 0, -1, 1, 0, 0, 1];
COEFFICIENTS_MAP["left-bottom"] = COEFFICIENTS_MAP.lb = [0, -1, 1, 0, 0, 1, -1, 0];
COEFFICIENTS_MAP["bottom-left"] = COEFFICIENTS_MAP.bl = [1, 0, 0, -1, -1, 0, 0, 1];
COEFFICIENTS_MAP["left-top"] = COEFFICIENTS_MAP.lt = [0, 1, 1, 0, 0, -1, -1, 0];
COEFFICIENTS_MAP["top-left"] = COEFFICIENTS_MAP.tl = [1, 0, 0, 1, -1, 0, 0, -1];
COEFFICIENTS_MAP["right-top"] = COEFFICIENTS_MAP.rt = [0, 1, -1, 0, 0, -1, 1, 0];
COEFFICIENTS_MAP["top-right"] = COEFFICIENTS_MAP.tr = [-1, 0, 0, 1, 1, 0, 0, -1];
function getTextCloudInfo(options) {
let x = options.x;
let y = options.y;
const type = COEFFICIENTS_MAP[options.type];
const cloudWidth = options.cloudWidth;
const cloudHeight = options.cloudHeight;
let tailWidth;
let tailHeight;
const cx = x;
const cy = y;
tailWidth = tailHeight = options.tailLength;
if (1 & type[0]) {
tailHeight = Math.min(tailHeight, cloudHeight / 3)
} else {
tailWidth = Math.min(tailWidth, cloudWidth / 3)
}
return {
cx: _round(cx + type[0] * tailWidth + (type[0] + type[2]) * cloudWidth / 2),
cy: _round(cy + type[1] * tailHeight + (type[1] + type[3]) * cloudHeight / 2),
points: [_round(x), _round(y), _round(x += type[0] * (cloudWidth + tailWidth)), _round(y += type[1] * (cloudHeight + tailHeight)), _round(x += type[2] * cloudWidth), _round(y += type[3] * cloudHeight), _round(x += type[4] * cloudWidth), _round(y += type[5] * cloudHeight), _round(x += type[6] * (cloudWidth - tailWidth)), _round(y += type[7] * (cloudHeight - tailHeight))]
}
}
export const BaseTextCloudMarker = BaseIndicator.inherit({
_move: function() {
const options = this._options;
const textCloudOptions = this._getTextCloudOptions();
const text = _formatValue(this._actualValue, options.text);
this._text.attr({
text: text
});
const bBox = this._text.getBBox();
const x = textCloudOptions.x;
const y = textCloudOptions.y;
const cloudWidth = (bBox.width || text.length * this._textUnitWidth) + 2 * options.horizontalOffset;
const cloudHeight = (bBox.height || this._textHeight) + 2 * options.verticalOffset;
const info = getTextCloudInfo({
x: x,
y: y,
cloudWidth: cloudWidth,
cloudHeight: cloudHeight,
tailLength: options.arrowLength,
type: this._correctCloudType(textCloudOptions.type, {
x: x,
y: y
}, {
width: cloudWidth,
height: cloudHeight
})
});
this._text.attr({
x: info.cx,
y: info.cy + this._textVerticalOffset
});
this._cloud.attr({
points: info.points
});
this._trackerElement && this._trackerElement.attr({
points: info.points
})
},
_measureText: function() {
const that = this;
let root;
let text;
let bBox;
let sampleText;
if (!that._textVerticalOffset) {
root = that._createRoot().append(that._owner);
sampleText = _getSampleText(that._translator, that._options.text);
text = that._renderer.text(sampleText, 0, 0).attr({
align: "center"
}).css(_patchFontOptions(that._options.text.font)).append(root);
bBox = text.getBBox();
root.remove();
that._textVerticalOffset = -bBox.y - bBox.height / 2;
that._textWidth = bBox.width;
that._textHeight = bBox.height;
that._textUnitWidth = that._textWidth / sampleText.length;
that._textFullWidth = that._textWidth + 2 * that._options.horizontalOffset;
that._textFullHeight = that._textHeight + 2 * that._options.verticalOffset
}
},
_render: function() {
this._measureText();
this._cloud = this._cloud || this._renderer.path([], "area").append(this._rootElement);
this._text = this._text || this._renderer.text().append(this._rootElement);
this._text.attr({
align: "center"
}).css(_patchFontOptions(this._options.text.font))
},
_clear: function() {
delete this._cloud;
delete this._text
},
getTooltipParameters: function() {
const position = this._getTextCloudOptions();
return {
x: position.x,
y: position.y,
value: this._currentValue,
color: this._options.color
}
},
_correctCloudType: type => type
});
export const BaseRangeBar = BaseIndicator.inherit({
_measureText: function() {
const that = this;
let root;
let text;
let bBox;
that._hasText = that._isTextVisible();
if (that._hasText && !that._textVerticalOffset) {
root = that._createRoot().append(that._owner);
text = that._renderer.text(_getSampleText(that._translator, that._options.text), 0, 0).attr({
class: "dxg-text",
align: "center"
}).css(_patchFontOptions(that._options.text.font)).append(root);
bBox = text.getBBox();
root.remove();
that._textVerticalOffset = -bBox.y - bBox.height / 2;
that._textWidth = bBox.width;
that._textHeight = bBox.height
}
},
_move: function() {
const that = this;
that._updateBarItemsPositions();
if (that._hasText) {
that._text.attr({
text: _formatValue(that._actualValue, that._options.text)
});
that._updateTextPosition();
that._updateLinePosition()
}
},
_updateBarItems: function() {
const that = this;
const options = that._options;
let spaceColor;
const translator = that._translator;
that._setBarSides();
that._startPosition = translator.translate(translator.getDomainStart());
that._endPosition = translator.translate(translator.getDomainEnd());
that._basePosition = translator.translate(options.baseValue);
that._space = that._getSpace();
const backgroundColor = options.backgroundColor || "none";
if ("none" !== backgroundColor && that._space > 0) {
spaceColor = options.containerBackgroundColor || "none"
} else {
that._space = 0;
spaceColor = "none"
}
that._backItem1.attr({
fill: backgroundColor
});
that._backItem2.attr({
fill: backgroundColor
});
that._spaceItem1.attr({
fill: spaceColor
});
that._spaceItem2.attr({
fill: spaceColor
})
},
_getSpace: function() {
return 0
},
_updateTextItems: function() {
const that = this;
if (that._hasText) {
that._line = that._line || that._renderer.path([], "line").attr({
class: "dxg-main-bar",
"stroke-linecap": "square"
}).append(that._rootElement);
that._text = that._text || that._renderer.text("", 0, 0).attr({
class: "dxg-text"
}).append(that._rootElement);
that._text.attr({
align: that._getTextAlign()
}).css(that._getFontOptions());
that._setTextItemsSides()
} else {
if (that._line) {
that._line.remove();
delete that._line
}
if (that._text) {
that._text.remove();
delete that._text
}
}
},
_isTextVisible: function() {
return false
},
_getTextAlign: function() {
return "center"
},
_getFontOptions: function() {
const options = this._options;
let font = options.text.font;
if (!font || !font.color) {
font = extend({}, font, {
color: options.color
})
}
return _patchFontOptions(font)
},
_updateBarItemsPositions: function() {
const positions = this._getPositions();
this._backItem1.attr(this._buildItemSettings(positions.start, positions.back1));
this._backItem2.attr(this._buildItemSettings(positions.back2, positions.end));
this._spaceItem1.attr(this._buildItemSettings(positions.back1, positions.main1));
this._spaceItem2.attr(this._buildItemSettings(positions.main2, positions.back2));
this._mainItem.attr(this._buildItemSettings(positions.main1, positions.main2));
this._trackerElement && this._trackerElement.attr(this._buildItemSettings(positions.main1, positions.main2))
},
_render: function() {
const that = this;
that._measureText();
if (!that._backItem1) {
that._backItem1 = that._createBarItem();
that._backItem1.attr({
class: "dxg-back-bar"
})
}
if (!that._backItem2) {
that._backItem2 = that._createBarItem();
that._backItem2.attr({
class: "dxg-back-bar"
})
}
if (!that._spaceItem1) {
that._spaceItem1 = that._createBarItem();
that._spaceItem1.attr({
class: "dxg-space-bar"
})
}
if (!that._spaceItem2) {
that._spaceItem2 = that._createBarItem();
that._spaceItem2.attr({
class: "dxg-space-bar"
})
}
if (!that._mainItem) {
that._mainItem = that._createBarItem();
that._mainItem.attr({
class: "dxg-main-bar"
})
}
that._updateBarItems();
that._updateTextItems()
},
_clear: function() {
delete this._backItem1;
delete this._backItem2;
delete this._spaceItem1;
delete this._spaceItem2;
delete this._mainItem;
delete this._hasText;
delete this._line;
delete this._text
},
getTooltipParameters: function() {
const position = this._getTooltipPosition();
return {
x: position.x,
y: position.y,
value: this._currentValue,
color: this._options.color,
offset: 0
}
}
});