UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

437 lines (430 loc) • 16.9 kB
/** * DevExtreme (cjs/__internal/viz/m_pie_chart.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"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _component_registrator = _interopRequireDefault(require("../../core/component_registrator")); var _common = require("../../core/utils/common"); var _extend2 = require("../../core/utils/extend"); var _iterator = require("../../core/utils/iterator"); var _type = require("../../core/utils/type"); var _consts = _interopRequireDefault(require("../../viz/components/consts")); var _annotations = require("../../viz/core/annotations"); var _center_template = require("../../viz/core/center_template"); var _utils = require("../../viz/core/utils"); var _range = require("../../viz/translators/range"); var _translator1d = require("../../viz/translators/translator1d"); var _m_base_chart = require("./chart_components/m_base_chart"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } const { states: states } = _consts.default; const seriesSpacing = _consts.default.pieSeriesSpacing; const OPTIONS_FOR_REFRESH_SERIES = ["startAngle", "innerRadius", "segmentsDirection", "type"]; const NORMAL_STATE = states.normalMark; const HOVER_STATE = states.hoverMark; const SELECTED_STATE = states.selectedMark; const MAX_RESOLVE_ITERATION_COUNT = 5; const LEGEND_ACTIONS = [states.resetItem, states.applyHover, states.applySelected, states.applySelected]; function shiftInColumnFunction(box, length) { return { x: box.x, y: box.y - length } } function dividePoints(series, points) { return series.getVisiblePoints().reduce(((r, point) => { const angle = (0, _utils.normalizeAngle)(point.middleAngle); (angle <= 90 || angle >= 270 ? r.right : r.left).push(point); return r }), points || { left: [], right: [] }) } function resolveOverlappedLabels(points, shiftCallback, inverseDirection, canvas) { let overlapped = false; if (inverseDirection) { points.left.reverse(); points.right.reverse() } overlapped = _m_base_chart.overlapping.resolveLabelOverlappingInOneDirection(points.left, canvas, false, false, shiftCallback); return _m_base_chart.overlapping.resolveLabelOverlappingInOneDirection(points.right, canvas, false, false, shiftCallback) || overlapped } function getLegendItemAction(points) { let state = NORMAL_STATE; points.forEach((point => { var _point$series; const seriesOptions = null === (_point$series = point.series) || void 0 === _point$series ? void 0 : _point$series.getOptions(); let pointState = point.fullState; if ("none" === (null === seriesOptions || void 0 === seriesOptions ? void 0 : seriesOptions.hoverMode)) { pointState &= ~HOVER_STATE } if ("none" === (null === seriesOptions || void 0 === seriesOptions ? void 0 : seriesOptions.selectionMode)) { pointState &= ~SELECTED_STATE } state |= pointState })); return LEGEND_ACTIONS[state] } function correctPercentValue(value) { if ((0, _type.isNumeric)(value)) { if (value > 1) { value = 1 } else if (value < 0) { value = 0 } } else { value = void 0 } return value } const pieSizeEqualizer = function() { function removeFromList(list, item) { return list.filter((li => li !== item)) } let pies = []; let timers = {}; return { queue(pie) { const group = pie.getSizeGroup(); pies = (list = pies, item = pie, removeFromList(list, item).concat(item)); var list, item; clearTimeout(timers[group]); timers[group] = setTimeout((() => { ! function(group, allPies) { const pies = allPies.filter((p => p._isVisible() && p.getSizeGroup() === group)); const minRadius = Math.min.apply(null, pies.map((p => p.getSizeGroupLayout().radius))); const minPie = pies.filter((p => p.getSizeGroupLayout().radius === minRadius)); pies.forEach((p => p.render({ force: true, sizeGroupLayout: minPie.length ? minPie[0].getSizeGroupLayout() : {} }))) }(group, pies) })) }, remove(pie) { pies = removeFromList(pies, pie); if (!pies.length) { timers = {} } } } }(); const dxPieChart = _m_base_chart.BaseChart.inherit({ _themeSection: "pie", _layoutManagerOptions() { return (0, _extend2.extend)(true, {}, this.callBase(), { piePercentage: correctPercentValue(this._themeManager.getOptions("diameter")), minPiePercentage: correctPercentValue(this._themeManager.getOptions("minDiameter")) }) }, _optionChangesMap: { diameter: "REINIT", minDiameter: "REINIT", sizeGroup: "REINIT" }, _disposeCore() { pieSizeEqualizer.remove(this); this.callBase() }, _groupSeries() { var _series$; const { series: series } = this; this._groupsData = { groups: [{ series: series, valueOptions: { valueType: "numeric" } }], argumentOptions: null === (_series$ = series[0]) || void 0 === _series$ ? void 0 : _series$.getOptions() } }, getArgumentAxis: () => null, _getValueAxis() { const translator = (new _translator1d.Translator1D).setCodomain(360, 0); return { getTranslator: () => translator, setBusinessRange(range) { translator.setDomain(range.min, range.max) } } }, _populateBusinessRange() { this.series.map((series => { const range = new _range.Range; range.addRange(series.getRangeData().val); series.getValueAxis().setBusinessRange(range); return range })) }, _specialProcessSeries() { (0, _iterator.each)(this.series, ((_, singleSeries) => { singleSeries.arrangePoints() })) }, _checkPaneName: () => true, _processSingleSeries(singleSeries) { this.callBase(singleSeries); singleSeries.arrangePoints() }, _handleSeriesDataUpdated() { let maxPointCount = 0; this.series.forEach((s => { maxPointCount = Math.max(s.getPointsCount(), maxPointCount) })); this.series.forEach((s => { s.setMaxPointsCount(maxPointCount) })); this.callBase() }, _getLegendOptions(item) { const legendItem = this.callBase(item); const { legendData: legendData } = legendItem; legendData.argument = item.argument; legendData.argumentIndex = item.argumentIndex; legendData.points = [item]; return legendItem }, _getLegendTargets() { const itemsByArgument = {}; (this.series || []).forEach((series => { series.getPoints().forEach((point => { const argument = point.argument.valueOf(); const index = series.getPointsByArg(argument).indexOf(point); const key = argument.valueOf().toString() + index; itemsByArgument[key] = itemsByArgument[key] || []; const argumentCount = itemsByArgument[key].push(point); point.index = itemsByArgument[key][argumentCount - 2] ? itemsByArgument[key][argumentCount - 2].index : Object.keys(itemsByArgument).length - 1; point.argumentIndex = index })) })); const items = []; (0, _iterator.each)(itemsByArgument, ((_, points) => { points.forEach(((point, index) => { if (0 === index) { items.push(this._getLegendOptions(point)); return } const item = items[items.length - 1]; item.legendData.points.push(point); if (!item.visible) { item.visible = point.isVisible() } })) })); return items }, _getLayoutTargets() { return [{ canvas: this._canvas }] }, _getLayoutSeries(series, drawOptions) { let layout; const canvas = this._canvas; let drawnLabels = false; layout = this.layoutManager.applyPieChartSeriesLayout(canvas, series, true); series.forEach((singleSeries => { singleSeries.correctPosition(layout, canvas); drawnLabels = singleSeries.drawLabelsWOPoints() || drawnLabels })); if (drawnLabels) { layout = this.layoutManager.applyPieChartSeriesLayout(canvas, series, drawOptions.hideLayoutLabels) } series.forEach((singleSeries => { singleSeries.hideLabels() })); this._sizeGroupLayout = { x: layout.centerX, y: layout.centerY, radius: layout.radiusOuter, drawOptions: drawOptions }; return layout }, _getLayoutSeriesForEqualPies(series, sizeGroupLayout) { const canvas = this._canvas; const layout = this.layoutManager.applyEqualPieChartLayout(series, sizeGroupLayout); series.forEach((s => { s.correctPosition(layout, canvas); s.drawLabelsWOPoints() })); this.layoutManager.correctPieLabelRadius(series, layout, canvas); return layout }, _updateSeriesDimensions(drawOptions) { const visibleSeries = this._getVisibleSeries(); const lengthVisibleSeries = visibleSeries.length; let innerRad; let delta; let layout; const { sizeGroupLayout: sizeGroupLayout } = drawOptions; if (lengthVisibleSeries) { layout = sizeGroupLayout ? this._getLayoutSeriesForEqualPies(visibleSeries, sizeGroupLayout) : this._getLayoutSeries(visibleSeries, drawOptions); delta = (layout.radiusOuter - layout.radiusInner - seriesSpacing * (lengthVisibleSeries - 1)) / lengthVisibleSeries; innerRad = layout.radiusInner; this._setGeometry(layout); visibleSeries.forEach((singleSeries => { singleSeries.correctRadius({ radiusInner: innerRad, radiusOuter: innerRad + delta }); innerRad += delta + seriesSpacing })) } }, _renderSeries(drawOptions, isRotated, isLegendInside) { this._calculateSeriesLayout(drawOptions, isRotated); if (!drawOptions.sizeGroupLayout && this.getSizeGroup()) { pieSizeEqualizer.queue(this); this._clearCanvas(); return } this._renderSeriesElements(drawOptions, isLegendInside) }, _getCenter() { return this._center }, getInnerRadius() { return this._innerRadius }, _getLegendCallBack() { const legend = this._legend; const items = this._getLegendTargets().map((i => i.legendData)); return target => { items.forEach((data => { const points = []; const callback = legend.getActionCallback({ index: data.id }); this.series.forEach((series => { const seriesPoints = series.getPointsByKeys(data.argument, data.argumentIndex); points.push.apply(points, seriesPoints) })); if (target && target.argument === data.argument && target.argumentIndex === data.argumentIndex) { points.push(target) } callback(getLegendItemAction(points)) })) } }, _locateLabels(resolveLabelOverlapping) { let iterationCount = 0; let labelsWereOverlapped; let wordWrapApplied; do { wordWrapApplied = this._adjustSeriesLabels("shift" === resolveLabelOverlapping); labelsWereOverlapped = this._resolveLabelOverlapping(resolveLabelOverlapping) } while ((labelsWereOverlapped || wordWrapApplied) && ++iterationCount < 5) }, _adjustSeriesLabels(moveLabelsFromCenter) { return this.series.reduce(((r, s) => s.adjustLabels(moveLabelsFromCenter) || r), false) }, _applyExtraSettings: _common.noop, _resolveLabelOverlappingShift() { const inverseDirection = "anticlockwise" === this.option("segmentsDirection"); const seriesByPosition = this.series.reduce(((r, s) => { (r[s.getOptions().label.position] || r.outside).push(s); return r }), { inside: [], columns: [], outside: [] }); let labelsOverlapped = false; const shiftFunction = (box, length) => (0, _utils.getVerticallyShiftedAngularCoords)(box, -length, this._center); if (seriesByPosition.inside.length > 0) { const pointsToResolve = seriesByPosition.inside.reduce(((r, singleSeries) => { const visiblePoints = singleSeries.getVisiblePoints(); return visiblePoints.reduce(((r, point) => { r.left.push(point); return r }), r) }), { left: [], right: [] }); labelsOverlapped = resolveOverlappedLabels(pointsToResolve, shiftInColumnFunction, inverseDirection, this._canvas) || labelsOverlapped } labelsOverlapped = seriesByPosition.columns.reduce(((r, singleSeries) => resolveOverlappedLabels(dividePoints(singleSeries), shiftInColumnFunction, inverseDirection, this._canvas) || r), labelsOverlapped); if (seriesByPosition.outside.length > 0) { labelsOverlapped = resolveOverlappedLabels(seriesByPosition.outside.reduce(((r, singleSeries) => dividePoints(singleSeries, r)), null), shiftFunction, inverseDirection, this._canvas) || labelsOverlapped } return labelsOverlapped }, _setGeometry(_ref) { let { centerX: x, centerY: y, radiusInner: radiusInner } = _ref; this._center = { x: x, y: y }; this._innerRadius = radiusInner }, _disposeSeries() { this.callBase.apply(this, arguments); this._abstractSeries = null }, _legendDataField: "point", _legendItemTextField: "argument", _applyPointMarkersAutoHiding: _common.noop, _renderTrackers: _common.noop, _trackerType: "PieTracker", _createScrollBar: _common.noop, _updateAxesLayout: _common.noop, _applyClipRects: _common.noop, _appendAdditionalSeriesGroups: _common.noop, _prepareToRender: _common.noop, _isLegendInside: _common.noop, _renderAxes: _common.noop, _shrinkAxes: _common.noop, _isRotated: _common.noop, _seriesPopulatedHandlerCore: _common.noop, _reinitAxes: _common.noop, _correctAxes: _common.noop, _getExtraOptions() { return { startAngle: this.option("startAngle"), innerRadius: this.option("innerRadius"), segmentsDirection: this.option("segmentsDirection"), type: this.option("type") } }, getSizeGroup() { return this._themeManager.getOptions("sizeGroup") }, getSizeGroupLayout() { return this._sizeGroupLayout || {} } }); (0, _iterator.each)(OPTIONS_FOR_REFRESH_SERIES, ((_, name) => { dxPieChart.prototype._optionChangesMap[name] = "REFRESH_SERIES_DATA_INIT" })); dxPieChart.addPlugin(_center_template.plugins.pieChart); dxPieChart.addPlugin(_annotations.plugins.core); dxPieChart.addPlugin(_annotations.plugins.pieChart); (0, _component_registrator.default)("dxPieChart", dxPieChart); var _default = exports.default = dxPieChart;