UNPKG

ag-charts-community

Version:

Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue

636 lines 27.1 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spread = (this && this.__spread) || function () { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; }; import { Group } from "../../../scene/group"; import { Line } from "../../../scene/shape/line"; import { Text } from "../../../scene/shape/text"; import { Selection } from "../../../scene/selection"; import { LinearScale } from "../../../scale/linearScale"; import { Sector } from "../../../scene/shape/sector"; import { HighlightStyle, SeriesTooltip } from "./../series"; import { Label } from "../../label"; import { PointerEvents } from "../../../scene/node"; import { normalizeAngle180, toRadians } from "../../../util/angle"; import { toFixed } from "../../../util/number"; import { Caption } from "../../../caption"; import { reactive, Observable } from "../../../util/observable"; import { PolarSeries } from "./polarSeries"; import { ChartAxisDirection } from "../../chartAxis"; import { toTooltipHtml } from "../../chart"; var PieHighlightStyle = /** @class */ (function (_super) { __extends(PieHighlightStyle, _super); function PieHighlightStyle() { return _super !== null && _super.apply(this, arguments) || this; } return PieHighlightStyle; }(HighlightStyle)); var PieNodeTag; (function (PieNodeTag) { PieNodeTag[PieNodeTag["Sector"] = 0] = "Sector"; PieNodeTag[PieNodeTag["Callout"] = 1] = "Callout"; PieNodeTag[PieNodeTag["Label"] = 2] = "Label"; })(PieNodeTag || (PieNodeTag = {})); var PieSeriesLabel = /** @class */ (function (_super) { __extends(PieSeriesLabel, _super); function PieSeriesLabel() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.offset = 3; // from the callout line _this.minAngle = 20; // in degrees return _this; } __decorate([ reactive('change') ], PieSeriesLabel.prototype, "offset", void 0); __decorate([ reactive('dataChange') ], PieSeriesLabel.prototype, "minAngle", void 0); __decorate([ reactive('dataChange') ], PieSeriesLabel.prototype, "formatter", void 0); return PieSeriesLabel; }(Label)); var PieSeriesCallout = /** @class */ (function (_super) { __extends(PieSeriesCallout, _super); function PieSeriesCallout() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.colors = [ '#874349', '#718661', '#a48f5f', '#5a7088', '#7f637a', '#5d8692' ]; _this.length = 10; _this.strokeWidth = 1; return _this; } __decorate([ reactive('change') ], PieSeriesCallout.prototype, "colors", void 0); __decorate([ reactive('change') ], PieSeriesCallout.prototype, "length", void 0); __decorate([ reactive('change') ], PieSeriesCallout.prototype, "strokeWidth", void 0); return PieSeriesCallout; }(Observable)); var PieSeriesTooltip = /** @class */ (function (_super) { __extends(PieSeriesTooltip, _super); function PieSeriesTooltip() { return _super !== null && _super.apply(this, arguments) || this; } __decorate([ reactive('change') ], PieSeriesTooltip.prototype, "renderer", void 0); return PieSeriesTooltip; }(SeriesTooltip)); export { PieSeriesTooltip }; var PieTitle = /** @class */ (function (_super) { __extends(PieTitle, _super); function PieTitle() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.showInLegend = false; return _this; } __decorate([ reactive() ], PieTitle.prototype, "showInLegend", void 0); return PieTitle; }(Caption)); export { PieTitle }; var PieSeries = /** @class */ (function (_super) { __extends(PieSeries, _super); function PieSeries() { var _this = _super.call(this) || this; _this.radiusScale = new LinearScale(); _this.groupSelection = Selection.select(_this.pickGroup).selectAll(); /** * The processed data that gets visualized. */ _this.groupSelectionData = []; _this.angleScale = (function () { var scale = new LinearScale(); // Each slice is a ratio of the whole, where all ratios add up to 1. scale.domain = [0, 1]; // Add 90 deg to start the first pie at 12 o'clock. scale.range = [-Math.PI, Math.PI].map(function (angle) { return angle + Math.PI / 2; }); return scale; })(); // When a user toggles a series item (e.g. from the legend), its boolean state is recorded here. _this.seriesItemEnabled = []; _this.label = new PieSeriesLabel(); _this.callout = new PieSeriesCallout(); _this.tooltip = new PieSeriesTooltip(); /** * The key of the numeric field to use to determine the angle (for example, * a pie slice angle). */ _this.angleKey = ''; _this.angleName = ''; _this._fills = [ '#c16068', '#a2bf8a', '#ebcc87', '#80a0c3', '#b58dae', '#85c0d1' ]; _this._strokes = [ '#874349', '#718661', '#a48f5f', '#5a7088', '#7f637a', '#5d8692' ]; _this.fillOpacity = 1; _this.strokeOpacity = 1; _this.lineDash = [0]; _this.lineDashOffset = 0; /** * The series rotation in degrees. */ _this.rotation = 0; _this.outerRadiusOffset = 0; _this.innerRadiusOffset = 0; _this.strokeWidth = 1; _this.highlightStyle = new PieHighlightStyle(); _this.addEventListener('update', _this.scheduleUpdate, _this); _this.label.addEventListener('change', _this.scheduleUpdate, _this); _this.label.addEventListener('dataChange', _this.scheduleData, _this); _this.callout.addEventListener('change', _this.scheduleLayout, _this); _this.addPropertyListener('data', function (event) { if (event.value) { event.source.seriesItemEnabled = event.value.map(function () { return true; }); } }); return _this; } Object.defineProperty(PieSeries.prototype, "title", { get: function () { return this._title; }, set: function (value) { var oldTitle = this._title; function updateLegend() { this.fireEvent({ type: 'legendChange' }); } if (oldTitle !== value) { if (oldTitle) { oldTitle.removeEventListener('change', this.scheduleUpdate, this); oldTitle.addPropertyListener('showInLegend', updateLegend, this); this.group.removeChild(oldTitle.node); } if (value) { value.node.textBaseline = 'bottom'; value.addEventListener('change', this.scheduleUpdate, this); value.addPropertyListener('showInLegend', updateLegend, this); this.group.appendChild(value.node); } this._title = value; this.scheduleUpdate(); } }, enumerable: true, configurable: true }); Object.defineProperty(PieSeries.prototype, "fills", { get: function () { return this._fills; }, set: function (values) { this._fills = values; this.scheduleUpdate(); }, enumerable: true, configurable: true }); Object.defineProperty(PieSeries.prototype, "strokes", { get: function () { return this._strokes; }, set: function (values) { this._strokes = values; this.scheduleUpdate(); }, enumerable: true, configurable: true }); PieSeries.prototype.onHighlightChange = function () { this.updateNodes(); }; PieSeries.prototype.setColors = function (fills, strokes) { this.fills = fills; this.strokes = strokes; this.callout.colors = strokes; }; PieSeries.prototype.getDomain = function (direction) { if (direction === ChartAxisDirection.X) { return this.angleScale.domain; } else { return this.radiusScale.domain; } }; PieSeries.prototype.processData = function () { var _this = this; var _a = this, angleKey = _a.angleKey, radiusKey = _a.radiusKey, seriesItemEnabled = _a.seriesItemEnabled, angleScale = _a.angleScale, groupSelectionData = _a.groupSelectionData, label = _a.label; var data = angleKey && this.data ? this.data : []; var angleData = data.map(function (datum, index) { return seriesItemEnabled[index] && Math.abs(+datum[angleKey]) || 0; }); var angleDataTotal = angleData.reduce(function (a, b) { return a + b; }, 0); // The ratios (in [0, 1] interval) used to calculate the end angle value for every pie slice. // Each slice starts where the previous one ends, so we only keep the ratios for end angles. var angleDataRatios = (function () { var sum = 0; return angleData.map(function (datum) { return sum += datum / angleDataTotal; }); })(); var labelFormatter = label.formatter; var labelKey = label.enabled && this.labelKey; var labelData = []; var radiusData = []; if (labelKey) { if (labelFormatter) { labelData = data.map(function (datum) { return labelFormatter({ value: datum[labelKey] }); }); } else { labelData = data.map(function (datum) { return String(datum[labelKey]); }); } } if (radiusKey) { var _b = this, radiusMin = _b.radiusMin, radiusMax = _b.radiusMax; var radii = data.map(function (datum) { return Math.abs(datum[radiusKey]); }); var min_1 = radiusMin !== undefined ? radiusMin : Math.min.apply(Math, __spread(radii)); var max = radiusMax !== undefined ? radiusMax : Math.max.apply(Math, __spread(radii)); var delta_1 = max - min_1; radiusData = radii.map(function (value) { return delta_1 ? (value - min_1) / delta_1 : 1; }); } groupSelectionData.length = 0; var rotation = toRadians(this.rotation); var halfPi = Math.PI / 2; var datumIndex = 0; // Simply use reduce here to pair up adjacent ratios. angleDataRatios.reduce(function (start, end) { var radius = radiusKey ? radiusData[datumIndex] : 1; var startAngle = angleScale.convert(start) + rotation; var endAngle = angleScale.convert(end) + rotation; var midAngle = (startAngle + endAngle) / 2; var span = Math.abs(endAngle - startAngle); var midCos = Math.cos(midAngle); var midSin = Math.sin(midAngle); var labelMinAngle = toRadians(label.minAngle); var labelVisible = labelKey && span > labelMinAngle; var midAngle180 = normalizeAngle180(midAngle); // Split the circle into quadrants like so: ⊗ var quadrantStart = -3 * Math.PI / 4; // same as `normalizeAngle180(toRadians(-135))` var textAlign; var textBaseline; if (midAngle180 >= quadrantStart && midAngle180 < (quadrantStart += halfPi)) { textAlign = 'center'; textBaseline = 'bottom'; } else if (midAngle180 >= quadrantStart && midAngle180 < (quadrantStart += halfPi)) { textAlign = 'left'; textBaseline = 'middle'; } else if (midAngle180 >= quadrantStart && midAngle180 < (quadrantStart += halfPi)) { textAlign = 'center'; textBaseline = 'hanging'; } else { textAlign = 'right'; textBaseline = 'middle'; } groupSelectionData.push({ series: _this, datum: data[datumIndex], itemId: datumIndex, index: datumIndex, radius: radius, startAngle: startAngle, endAngle: endAngle, midAngle: midAngle, midCos: midCos, midSin: midSin, label: labelVisible ? { text: labelData[datumIndex], textAlign: textAlign, textBaseline: textBaseline } : undefined }); datumIndex++; return end; }, 0); return true; }; PieSeries.prototype.update = function () { this.updatePending = false; var _a = this, radius = _a.radius, innerRadiusOffset = _a.innerRadiusOffset, outerRadiusOffset = _a.outerRadiusOffset, title = _a.title; this.radiusScale.range = [ innerRadiusOffset ? radius + innerRadiusOffset : 0, radius + (outerRadiusOffset || 0) ]; this.group.translationX = this.centerX; this.group.translationY = this.centerY; if (title) { var outerRadius = Math.max(0, this.radiusScale.range[1]); if (outerRadius === 0) { title.node.visible = false; } else { title.node.translationY = -radius - outerRadiusOffset - 2; title.node.visible = title.enabled; } } this.updateSelections(); this.updateNodes(); }; PieSeries.prototype.updateSelections = function () { if (!this.nodeDataPending) { return; } this.nodeDataPending = false; this.updateGroupSelection(); }; PieSeries.prototype.updateGroupSelection = function () { var updateGroups = this.groupSelection.setData(this.groupSelectionData); updateGroups.exit.remove(); var enterGroups = updateGroups.enter.append(Group); enterGroups.append(Sector).each(function (node) { return node.tag = PieNodeTag.Sector; }); enterGroups.append(Line).each(function (node) { node.tag = PieNodeTag.Callout; node.pointerEvents = PointerEvents.None; }); enterGroups.append(Text).each(function (node) { node.tag = PieNodeTag.Label; node.pointerEvents = PointerEvents.None; }); this.groupSelection = updateGroups.merge(enterGroups); }; PieSeries.prototype.updateNodes = function () { var _this = this; if (!this.chart) { return; } this.group.visible = this.visible && this.seriesItemEnabled.indexOf(true) >= 0; var _a = this, fills = _a.fills, strokes = _a.strokes, fillOpacity = _a.fillOpacity, strokeOpacity = _a.strokeOpacity, radiusScale = _a.radiusScale, callout = _a.callout, shadow = _a.shadow, highlightedDatum = _a.chart.highlightedDatum, _b = _a.highlightStyle, deprecatedFill = _b.fill, deprecatedStroke = _b.stroke, deprecatedStrokeWidth = _b.strokeWidth, _c = _b.item, _d = _c.fill, highlightedFill = _d === void 0 ? deprecatedFill : _d, _e = _c.stroke, highlightedStroke = _e === void 0 ? deprecatedStroke : _e, _f = _c.strokeWidth, highlightedDatumStrokeWidth = _f === void 0 ? deprecatedStrokeWidth : _f, angleKey = _a.angleKey, radiusKey = _a.radiusKey, formatter = _a.formatter; var centerOffsets = []; var innerRadius = radiusScale.convert(0); this.groupSelection.selectByTag(PieNodeTag.Sector).each(function (sector, datum, index) { var radius = radiusScale.convert(datum.radius); var isDatumHighlighted = !!highlightedDatum && highlightedDatum.series === _this && datum.itemId === highlightedDatum.itemId; var fill = isDatumHighlighted && highlightedFill !== undefined ? highlightedFill : fills[index % fills.length]; var stroke = isDatumHighlighted && highlightedStroke !== undefined ? highlightedStroke : strokes[index % strokes.length]; var strokeWidth = isDatumHighlighted && highlightedDatumStrokeWidth !== undefined ? highlightedDatumStrokeWidth : _this.getStrokeWidth(_this.strokeWidth); var format = undefined; if (formatter) { format = formatter({ datum: datum.datum, angleKey: angleKey, radiusKey: radiusKey, fill: fill, stroke: stroke, strokeWidth: strokeWidth, highlighted: isDatumHighlighted }); } // Bring highlighted slice's parent group to front. var parent = sector.parent && sector.parent.parent; if (isDatumHighlighted && parent) { parent.removeChild(sector.parent); parent.appendChild(sector.parent); } sector.innerRadius = Math.max(0, innerRadius); sector.outerRadius = Math.max(0, radius); sector.startAngle = datum.startAngle; sector.endAngle = datum.endAngle; sector.fill = format && format.fill || fill; sector.stroke = format && format.stroke || stroke; sector.strokeWidth = format && format.strokeWidth !== undefined ? format.strokeWidth : strokeWidth; sector.fillOpacity = fillOpacity; sector.strokeOpacity = strokeOpacity; sector.lineDash = _this.lineDash; sector.lineDashOffset = _this.lineDashOffset; sector.fillShadow = shadow; sector.lineJoin = 'round'; sector.opacity = _this.getOpacity(); centerOffsets.push(sector.centerOffset); }); var calloutColors = callout.colors, calloutLength = callout.length, calloutStrokeWidth = callout.strokeWidth; this.groupSelection.selectByTag(PieNodeTag.Callout).each(function (line, datum, index) { var radius = radiusScale.convert(datum.radius); var outerRadius = Math.max(0, radius); if (datum.label && outerRadius !== 0) { line.strokeWidth = calloutStrokeWidth; line.stroke = calloutColors[index % calloutColors.length]; line.x1 = datum.midCos * outerRadius; line.y1 = datum.midSin * outerRadius; line.x2 = datum.midCos * (outerRadius + calloutLength); line.y2 = datum.midSin * (outerRadius + calloutLength); } else { line.stroke = undefined; } }); { var _g = this.label, offset_1 = _g.offset, fontStyle_1 = _g.fontStyle, fontWeight_1 = _g.fontWeight, fontSize_1 = _g.fontSize, fontFamily_1 = _g.fontFamily, color_1 = _g.color; this.groupSelection.selectByTag(PieNodeTag.Label).each(function (text, datum, index) { var label = datum.label; var radius = radiusScale.convert(datum.radius); var outerRadius = Math.max(0, radius); if (label && outerRadius !== 0) { var labelRadius = centerOffsets[index] + outerRadius + calloutLength + offset_1; text.fontStyle = fontStyle_1; text.fontWeight = fontWeight_1; text.fontSize = fontSize_1; text.fontFamily = fontFamily_1; text.text = label.text; text.x = datum.midCos * labelRadius; text.y = datum.midSin * labelRadius; text.fill = color_1; text.textAlign = label.textAlign; text.textBaseline = label.textBaseline; } else { text.fill = undefined; } }); } }; PieSeries.prototype.fireNodeClickEvent = function (event, datum) { this.fireEvent({ type: 'nodeClick', event: event, series: this, datum: datum.datum, angleKey: this.angleKey, labelKey: this.labelKey, radiusKey: this.radiusKey }); }; PieSeries.prototype.getTooltipHtml = function (nodeDatum) { var angleKey = this.angleKey; if (!angleKey) { return ''; } var _a = this, fills = _a.fills, tooltip = _a.tooltip, angleName = _a.angleName, radiusKey = _a.radiusKey, radiusName = _a.radiusName, labelKey = _a.labelKey, labelName = _a.labelName; var tooltipRenderer = tooltip.renderer; var color = fills[nodeDatum.index % fills.length]; var datum = nodeDatum.datum; var label = labelKey ? datum[labelKey] + ": " : ''; var angleValue = datum[angleKey]; var formattedAngleValue = typeof angleValue === 'number' ? toFixed(angleValue) : angleValue.toString(); var title = this.title ? this.title.text : undefined; var content = label + formattedAngleValue; var defaults = { title: title, backgroundColor: color, content: content }; if (tooltipRenderer) { return toTooltipHtml(tooltipRenderer({ datum: datum, angleKey: angleKey, angleValue: angleValue, angleName: angleName, radiusKey: radiusKey, radiusValue: radiusKey ? datum[radiusKey] : undefined, radiusName: radiusName, labelKey: labelKey, labelName: labelName, title: title, color: color, }), defaults); } return toTooltipHtml(defaults); }; PieSeries.prototype.listSeriesItems = function (legendData) { var _this = this; var _a = this, labelKey = _a.labelKey, data = _a.data; if (data && data.length && labelKey) { var _b = this, fills_1 = _b.fills, strokes_1 = _b.strokes, id_1 = _b.id; var titleText_1 = this.title && this.title.showInLegend && this.title.text; data.forEach(function (datum, index) { var labelParts = []; titleText_1 && labelParts.push(titleText_1); labelParts.push(String(datum[labelKey])); legendData.push({ id: id_1, itemId: index, enabled: _this.seriesItemEnabled[index], label: { text: labelParts.join(' - ') }, marker: { fill: fills_1[index % fills_1.length], stroke: strokes_1[index % strokes_1.length], fillOpacity: _this.fillOpacity, strokeOpacity: _this.strokeOpacity } }); }); } }; PieSeries.prototype.toggleSeriesItem = function (itemId, enabled) { this.seriesItemEnabled[itemId] = enabled; this.scheduleData(); }; PieSeries.className = 'PieSeries'; PieSeries.type = 'pie'; __decorate([ reactive('dataChange') ], PieSeries.prototype, "angleKey", void 0); __decorate([ reactive('update') ], PieSeries.prototype, "angleName", void 0); __decorate([ reactive('dataChange') ], PieSeries.prototype, "radiusKey", void 0); __decorate([ reactive('update') ], PieSeries.prototype, "radiusName", void 0); __decorate([ reactive('dataChange') ], PieSeries.prototype, "radiusMin", void 0); __decorate([ reactive('dataChange') ], PieSeries.prototype, "radiusMax", void 0); __decorate([ reactive('dataChange') ], PieSeries.prototype, "labelKey", void 0); __decorate([ reactive('update') ], PieSeries.prototype, "labelName", void 0); __decorate([ reactive('layoutChange') ], PieSeries.prototype, "fillOpacity", void 0); __decorate([ reactive('layoutChange') ], PieSeries.prototype, "strokeOpacity", void 0); __decorate([ reactive('update') ], PieSeries.prototype, "lineDash", void 0); __decorate([ reactive('update') ], PieSeries.prototype, "lineDashOffset", void 0); __decorate([ reactive('update') ], PieSeries.prototype, "formatter", void 0); __decorate([ reactive('dataChange') ], PieSeries.prototype, "rotation", void 0); __decorate([ reactive('layoutChange') ], PieSeries.prototype, "outerRadiusOffset", void 0); __decorate([ reactive('dataChange') ], PieSeries.prototype, "innerRadiusOffset", void 0); __decorate([ reactive('layoutChange') ], PieSeries.prototype, "strokeWidth", void 0); __decorate([ reactive('layoutChange') ], PieSeries.prototype, "shadow", void 0); return PieSeries; }(PolarSeries)); export { PieSeries }; //# sourceMappingURL=pieSeries.js.map