@amcharts/amcharts4
Version:
amCharts 4
915 lines • 32.1 kB
JavaScript
/**
* Defines Percent Chart Series.
*/
import { __extends } from "tslib";
/**
* ============================================================================
* IMPORTS
* ============================================================================
* @hidden
*/
import { Series, SeriesDataItem } from "./Series";
import { Sprite } from "../../core/Sprite";
import { Label } from "../../core/elements/Label";
import { Color } from "../../core/utils/Color";
import { Tick } from "../elements/Tick";
import { ListTemplate, ListDisposer } from "../../core/utils/List";
import { Container } from "../../core/Container";
import { ColorSet } from "../../core/utils/ColorSet";
import { registry } from "../../core/Registry";
import * as $iter from "../../core/utils/Iterator";
import * as $ease from "../../core/utils/Ease";
import * as $type from "../../core/utils/Type";
import { Disposer } from "../../core/utils/Disposer";
import { defaultRules, ResponsiveBreakpoints } from "../../core/utils/Responsive";
/**
* ============================================================================
* DATA ITEM
* ============================================================================
* @hidden
*/
//@todo: sequenced?
/**
* Defines a [[DataItem]] for [[PercentSeries]].
*
* @see {@link DataItem}
*/
var PercentSeriesDataItem = /** @class */ (function (_super) {
__extends(PercentSeriesDataItem, _super);
/**
* Constructor
*/
function PercentSeriesDataItem() {
var _this = _super.call(this) || this;
_this.className = "PercentSeriesDataItem";
_this.applyTheme();
return _this;
}
/**
* Adds an `id` attribute the the slice element and returns its id.
*
* @ignore Exclude from docs
*/
PercentSeriesDataItem.prototype.uidAttr = function () {
return this.slice.uidAttr();
};
/**
* Hide the data item (and corresponding visual elements).
*
* @param duration Duration (ms)
* @param delay Delay hiding (ms)
* @param toValue Target value for animation
* @param fields Fields to animate while hiding
*/
PercentSeriesDataItem.prototype.hide = function (duration, delay, toValue, fields) {
if (!fields) {
fields = ["value"];
}
return _super.prototype.hide.call(this, duration, delay, 0, fields);
};
/**
* Sets visibility of the Data Item.
*
* @param value Data Item
*/
PercentSeriesDataItem.prototype.setVisibility = function (value, noChangeValues) {
if (!noChangeValues) {
if (value) {
this.setWorkingValue("value", this.values["value"].value, 0, 0);
}
else {
this.setWorkingValue("value", 0, 0, 0);
}
}
_super.prototype.setVisibility.call(this, value, noChangeValues);
};
/**
* Show hidden data item (and corresponding visual elements).
*
* @param duration Duration (ms)
* @param delay Delay hiding (ms)
* @param fields Fields to animate while hiding
*/
PercentSeriesDataItem.prototype.show = function (duration, delay, fields) {
if (!fields) {
fields = ["value"];
}
return _super.prototype.show.call(this, duration, delay, fields);
};
Object.defineProperty(PercentSeriesDataItem.prototype, "category", {
/**
* @return Category
*/
get: function () {
return this.properties.category;
},
/**
* Category.
*
* @param value Category
*/
set: function (value) {
this.setProperty("category", value);
},
enumerable: true,
configurable: true
});
/**
* Creates a marker used in the legend for this slice.
*
* @ignore Exclude from docs
* @param marker Marker container
*/
PercentSeriesDataItem.prototype.createLegendMarker = function (marker) {
this.component.createLegendMarker(marker, this);
};
Object.defineProperty(PercentSeriesDataItem.prototype, "legendDataItem", {
/**
* @return Legend data item
*/
get: function () {
return this._legendDataItem;
},
/**
* A legend's data item, that corresponds to this data item.
*
* @param value Legend data item
*/
set: function (value) {
this._legendDataItem = value;
if (value.label) {
value.label.dataItem = this;
}
if (value.valueLabel) {
value.valueLabel.dataItem = this;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PercentSeriesDataItem.prototype, "tick", {
/**
* A Tick element, related to this data item. (slice)
*
* @readonly
* @return Tick element
*/
get: function () {
var _this = this;
if (!this._tick) {
var tick_1 = this.component.ticks.create();
this._tick = tick_1;
this.addSprite(tick_1);
this._disposers.push(tick_1);
tick_1.parent = this.component.ticksContainer;
this._disposers.push(new Disposer(function () {
if (_this.component) {
_this.component.ticks.removeValue(tick_1);
}
}));
tick_1.visible = this.visible;
}
return this._tick;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PercentSeriesDataItem.prototype, "label", {
/**
* A Label element, related to this data item. (slice)
*
* @readonly
* @return Label element
*/
get: function () {
var _this = this;
if (!this._label) {
var label_1 = this.component.labels.create();
this.addSprite(label_1);
this._label = label_1;
this._disposers.push(label_1);
label_1.parent = this.component.labelsContainer;
this._disposers.push(new Disposer(function () {
if (_this.component) {
_this.component.labels.removeValue(label_1);
}
}));
label_1.visible = this.visible;
}
return this._label;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PercentSeriesDataItem.prototype, "slice", {
/**
* An element, related to this data item. (slice)
*
* @readonly
* @return Slice element
*/
get: function () {
var _this = this;
if (!this._slice) {
var component_1 = this.component;
var slice_1 = component_1.slices.create();
this.addSprite(slice_1);
this._slice = slice_1;
this._disposers.push(slice_1);
slice_1.parent = component_1.slicesContainer;
this._disposers.push(new Disposer(function () {
component_1.slices.removeValue(slice_1);
}));
slice_1.visible = this.visible;
// Apply accessibility
if (component_1.itemsFocusable()) {
if (!$type.hasValue(this.component.role)) {
this.component.role = "menu";
}
if (!$type.hasValue(slice_1.role)) {
slice_1.role = "menuitem";
}
slice_1.focusable = true;
}
else {
if (!$type.hasValue(this.component.role)) {
this.component.role = "list";
}
if (!$type.hasValue(slice_1.role)) {
slice_1.role = "listitem";
}
slice_1.focusable = false;
}
// Apply screen reader label
if (slice_1.focusable) {
slice_1.events.on("focus", function (ev) {
slice_1.readerTitle = component_1.populateString(component_1.itemReaderText, _this);
}, undefined, false);
slice_1.events.on("blur", function (ev) {
slice_1.readerTitle = "";
}, undefined, false);
}
if (slice_1.hoverable) {
slice_1.events.on("over", function (ev) {
slice_1.readerTitle = component_1.populateString(component_1.itemReaderText, _this);
}, undefined, false);
slice_1.events.on("out", function (ev) {
slice_1.readerTitle = "";
}, undefined, false);
}
}
return this._slice;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PercentSeriesDataItem.prototype, "hiddenInLegend", {
/**
* @return Disabled in legend?
*/
get: function () {
return this.properties.hiddenInLegend;
},
/**
* Should dataItem (slice) be hidden in legend?
*
* @param value Visible in legend?
*/
set: function (value) {
this.setProperty("hiddenInLegend", value);
},
enumerable: true,
configurable: true
});
return PercentSeriesDataItem;
}(SeriesDataItem));
export { PercentSeriesDataItem };
/**
* ============================================================================
* MAIN CLASS
* ============================================================================
* @hidden
*/
/**
* Defines [[PercentSeries]] which is a base class for [[PieSeries]],
* [[FunnelSeries]], and [[PyramidSeries]].
*
* @see {@link IPercentSeriesEvents} for a list of available Events
* @see {@link IPercentSeriesAdapters} for a list of available Adapters
*/
var PercentSeries = /** @class */ (function (_super) {
__extends(PercentSeries, _super);
/**
* Constructor
*/
function PercentSeries() {
var _this = _super.call(this) || this;
_this.className = "PercentSeries";
_this._addAllDataItems = false;
_this.colors = new ColorSet();
_this.colors.step = 1;
_this.isMeasured = true;
_this.calculatePercent = true;
var slicesContainer = _this.createChild(Container);
slicesContainer.shouldClone = false;
slicesContainer.isMeasured = false;
_this.slicesContainer = slicesContainer;
var ticksContainer = _this.createChild(Container);
ticksContainer.shouldClone = false;
ticksContainer.isMeasured = false;
ticksContainer.layout = "none";
_this.ticksContainer = ticksContainer;
var labelsContainer = _this.createChild(Container);
labelsContainer.shouldClone = false;
labelsContainer.isMeasured = false;
labelsContainer.layout = "none";
_this.labelsContainer = labelsContainer;
_this.alignLabels = false;
_this.bulletsContainer.toFront();
// Make all slices focusable
_this.skipFocusThreshold = 50;
var defaultState = _this.defaultState;
defaultState.transitionEasing = $ease.sinOut;
// Accessibility
_this.itemReaderText = "{category}: {value.percent.formatNumber('#.#p')}";
_this.applyTheme();
return _this;
}
/**
* Creates a slice element.
*
* @return Slice
*/
PercentSeries.prototype.createSlice = function () {
return new Sprite();
};
/**
* Creates a tick element.
*
* @return Tick
*/
PercentSeries.prototype.createTick = function () {
return new Tick();
};
/**
* Sreates label element.
*
* @return label
*/
PercentSeries.prototype.createLabel = function () {
return new Label();
};
Object.defineProperty(PercentSeries.prototype, "slices", {
/**
* A list of slice elements for the series.
*
* Use its `template` to configure look and behavior of the slices. E.g.:
*
* ```TypeScript
* series.slices.template.stroke = am4core.color("#fff");
* series.slices.template.strokeWidth = 2;
* ```
* ```JavaScript
* series.slices.template.stroke = am4core.color("#fff");
* series.slices.template.strokeWidth = 2;
* ```
* ```JSON
* {
* // ...
* "series": [{
* // ...
* "slices": {
* "stroke": "#fff",
* "strokeWidth": 2
* }
* }]
* }
* ```
*
* @see {@link https://www.amcharts.com/docs/v4/concepts/list-templates/} for more information about list templates
* @return Slices
*/
get: function () {
if (!this._slices) {
var slice = this.createSlice();
slice.applyOnClones = true;
this._disposers.push(slice);
this.initSlice(slice);
this._slices = new ListTemplate(slice);
this._disposers.push(new ListDisposer(this._slices));
}
return this._slices;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PercentSeries.prototype, "ticks", {
/**
* A list of tick elements for the series. Ticks connect slice to its label.
*
* Use its `template` to configure look and behavior of the ticks. E.g.:
*
* ```TypeScript
* series.ticks.template.strokeWidth = 2;
* ```
* ```JavaScript
* series.ticks.template.strokeWidth = 2;
* ```
* ```JSON
* {
* // ...
* "series": [{
* // ...
* "ticks": {
* "strokeWidth": 2
* }
* }]
* }
* ```
*
* @see {@link https://www.amcharts.com/docs/v4/concepts/list-templates/} for more information about list templates
* @return Ticks
*/
get: function () {
if (!this._ticks) {
var tick = this.createTick();
tick.applyOnClones = true;
this._disposers.push(tick);
this.initTick(tick);
this._ticks = new ListTemplate(tick);
this._disposers.push(new ListDisposer(this._ticks));
}
return this._ticks;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PercentSeries.prototype, "labels", {
/**
* A list of slice label elements for the series.
*
* Use its `template` to configure look and behavior of the labels. E.g.:
*
* ```TypeScript
* series.labels.template.fill = am4core.color("#c00");
* series.labels.template.fontSize = 20;
* ```
* ```JavaScript
* series.labels.template.fill = am4core.color("#c00");
* series.labels.template.fontSize = 20;
* ```
* ```JSON
* {
* // ...
* "series": [{
* // ...
* "labels": {
* "stroke": "#c00",
* "fontSize": 20
* }
* }]
* }
* ```
*
* @see {@link https://www.amcharts.com/docs/v4/concepts/list-templates/} for more information about list templates
* @return Labels
*/
get: function () {
if (!this._labels) {
var label = this.createLabel();
label.applyOnClones = true;
this._disposers.push(label);
this.initLabel(label);
this._labels = new ListTemplate(label);
this._disposers.push(new ListDisposer(this._labels));
}
return this._labels;
},
enumerable: true,
configurable: true
});
/**
* Returns a new/empty DataItem of the type appropriate for this object.
*
* @see {@link DataItem}
* @return Data Item
*/
PercentSeries.prototype.createDataItem = function () {
return new PercentSeriesDataItem();
};
/**
* Creates and returns a new slice element.
*
* @param sliceType Type of the slice element
* @return Slice
*/
PercentSeries.prototype.initSlice = function (slice) {
};
PercentSeries.prototype.initLabel = function (label) {
label.text = "{category}: {value.percent.formatNumber('#.0p')}";
label.isMeasured = false;
label.padding(5, 5, 5, 5);
};
PercentSeries.prototype.initTick = function (label) {
};
/**
* Validates (processes) data items.
*
* @ignore Exclude from docs
*/
PercentSeries.prototype.validateDataItems = function () {
this.colors.reset();
if (this.patterns) {
this.patterns.reset();
}
_super.prototype.validateDataItems.call(this);
};
/**
* Validates data item's element, effectively redrawing it.
*
* @ignore Exclude from docs
* @param dataItem Data item
*/
PercentSeries.prototype.validateDataElement = function (dataItem) {
var slice = dataItem.slice;
if (slice) {
if (slice.fill == undefined) {
if (this.patterns) {
if (!$type.hasValue(slice.stroke)) {
slice.stroke = this.colors.next();
}
slice.fill = this.patterns.next();
if ($type.hasValue(slice.fillOpacity)) {
slice.fill.backgroundOpacity = slice.fillOpacity;
}
if (slice.stroke instanceof Color) {
slice.fill.stroke = slice.stroke;
slice.fill.fill = slice.stroke;
}
}
else {
slice.fill = this.colors.next();
}
}
else {
this.colors.currentStep += this.colors.step;
}
if (slice.stroke == undefined) {
slice.stroke = slice.fill;
}
}
// do this at the end, otherwise bullets won't be positioned properly
_super.prototype.validateDataElement.call(this, dataItem);
if (slice) {
dataItem.bullets.each(function (key, bullet) {
if (bullet.fill == undefined) {
bullet.fill = slice.fill;
}
if (bullet.stroke == undefined) {
bullet.stroke = slice.stroke;
}
});
}
this.updateLegendValue(dataItem);
};
/**
* Validates (processes) data.
*
* @ignore Exclude from docs
*/
PercentSeries.prototype.validateData = function () {
_super.prototype.validateData.call(this);
if (this.chart) {
this.chart.feedLegend();
}
};
/**
* Arranges slice labels according to position settings.
*
* @ignore Exclude from docs
* @param dataItems Data items
*/
PercentSeries.prototype.arrangeLabels = function (dataItems) {
for (var i = 0, len = dataItems.length; i < len; i++) {
var dataItem = dataItems[i];
var label = dataItem.label;
if (label) {
if (label.invalid) {
label.validate();
}
var lh = label.measuredHeight;
if (!label.visible) {
lh = 0;
}
if (label.pixelY - lh / 2 < -this.maxHeight / 2) {
label.y = -this.maxHeight / 2 + lh / 2;
}
var nextLabel = this.getNextLabel(i + 1, dataItems);
var bottom = label.pixelY + lh / 2;
if (nextLabel) {
if (nextLabel.invalid) {
nextLabel.validate();
}
var nextLabelHeight = nextLabel.measuredHeight;
if (!nextLabel.visible) {
nextLabelHeight = 0;
}
var nextLabelY = nextLabel.pixelY;
if (nextLabelY == null) {
nextLabelY = 0;
}
if (nextLabelY - nextLabelHeight / 2 < bottom) {
nextLabel.y = bottom + nextLabelHeight / 2;
}
}
}
}
};
PercentSeries.prototype.arrangeLabels2 = function (dataItems) {
var previousTop = this.maxHeight / 2;
for (var i = dataItems.length - 1; i >= 0; i--) {
var dataItem = dataItems[i];
var label = dataItem.label;
if (label) {
if (label.invalid) {
label.validate();
}
var lh = label.measuredHeight;
if (!label.visible) {
lh = 0;
}
//if (i == dataItems.length - 1) {
//previousTop += lh / 2;
//}
if (label.pixelY + lh / 2 > previousTop) {
label.y = previousTop - lh / 2;
previousTop = label.y - lh / 2;
}
}
}
};
/**
* Returns the next label according to `index`.
*
* @param index Current index
* @param dataItems Data items
* @return Label element
*/
PercentSeries.prototype.getNextLabel = function (index, dataItems) {
if (dataItems.length >= index) {
var nextDataItem = dataItems[index];
if (nextDataItem) {
if (nextDataItem.label) {
if (nextDataItem.visible) {
return nextDataItem.label;
}
else {
return this.getNextLabel(index + 1, dataItems);
}
}
else {
return this.getNextLabel(index + 1, dataItems);
}
}
}
};
Object.defineProperty(PercentSeries.prototype, "colors", {
/**
* @return Color set
*/
get: function () {
return this.getPropertyValue("colors");
},
/**
* A color set to be used for slices.
*
* For each new subsequent slice, the chart will assign the next color in
* this set.
*
* @param value Color set
*/
set: function (value) {
this.setPropertyValue("colors", value, true);
},
enumerable: true,
configurable: true
});
Object.defineProperty(PercentSeries.prototype, "patterns", {
/**
* @return Pattern set
*/
get: function () {
return this.getPropertyValue("patterns");
},
/**
* A [[PatternSet]] to use when creating patterned fills for slices.
*
* @since 4.7.5
* @param value Pattern set
*/
set: function (value) {
this.setPropertyValue("patterns", value, true);
},
enumerable: true,
configurable: true
});
/**
* Binds related legend data item's visual settings to this series' visual
* settings.
*
* @ignore Exclude from docs
* @param marker Container
* @param dataItem Data item
*/
PercentSeries.prototype.createLegendMarker = function (marker, dataItem) {
$iter.each(marker.children.iterator(), function (child) {
var slice = dataItem.slice;
child.defaultState.properties.fill = slice.fill;
child.defaultState.properties.stroke = slice.stroke;
child.defaultState.properties.fillOpacity = slice.fillOpacity;
child.defaultState.properties.strokeOpacity = slice.strokeOpacity;
child.fill = slice.fill;
child.stroke = slice.stroke;
child.fillOpacity = slice.fillOpacity;
child.strokeOpacity = slice.strokeOpacity;
if (child.fill == undefined) {
child.__disabled = true;
}
var legendDataItem = marker.dataItem;
legendDataItem.color = slice.fill;
legendDataItem.colorOrig = slice.fill;
child.addDisposer(slice.events.on("propertychanged", function (ev) {
if (ev.property == "fill") {
child.__disabled = false;
if (!child.isActive) {
child.fill = slice.fill;
}
child.defaultState.properties.fill = slice.fill;
legendDataItem.color = slice.fill;
legendDataItem.colorOrig = slice.fill;
}
if (ev.property == "stroke") {
if (!child.isActive) {
child.stroke = slice.stroke;
}
child.defaultState.properties.stroke = slice.stroke;
}
if (ev.property == "strokeOpacity") {
if (!child.isActive) {
child.strokeOpacity = slice.strokeOpacity;
}
child.defaultState.properties.strokeOpacity = slice.strokeOpacity;
}
}, undefined, false));
});
};
/**
* Repositions bullets when slice's size changes.
*
* @ignore Exclude from docs
* @param event Event
*/
PercentSeries.prototype.handleSliceScale = function (event) {
var _this = this;
var slice = event.target;
var dataItem = slice.dataItem;
if (dataItem && dataItem.bullets) {
$iter.each(dataItem.bullets.iterator(), function (a) {
var value = a[1];
_this.positionBullet(value);
});
}
};
/**
* Repositions bullet and labels when slice moves.
*
* @ignore Exclude from docs
* @param event Event
*/
PercentSeries.prototype.handleSliceMove = function (event) {
};
/**
* Copies all properties from another instance of [[PercentSeries]].
*
* @param source Source series
*/
PercentSeries.prototype.copyFrom = function (source) {
_super.prototype.copyFrom.call(this, source);
this.slices.template.copyFrom(source.slices.template);
this.labels.template.copyFrom(source.labels.template);
this.ticks.template.copyFrom(source.ticks.template);
this.colors = source.colors.clone();
};
Object.defineProperty(PercentSeries.prototype, "alignLabels", {
/**
* @return Align labels?
*/
get: function () {
return this.getPropertyValue("alignLabels");
},
/**
* Align labels into nice vertical columns?
*
* This will ensure that labels never overlap with each other.
*
* Arranging labels into columns makes them more readble, and better user
* experience.
*
* If set to `false` labels will be positioned at `label.radius` distance,
* and may, in some cases, overlap.
*
* @default true
* @param value Align labels?
*/
set: function (value) {
this.setAlignLabels(value);
},
enumerable: true,
configurable: true
});
/**
* @ignore
*/
PercentSeries.prototype.setAlignLabels = function (value) {
this.setPropertyValue("alignLabels", value, true);
};
Object.defineProperty(PercentSeries.prototype, "ignoreZeroValues", {
/**
* @return Ignore zero values
*/
get: function () {
return this.getPropertyValue("ignoreZeroValues");
},
/**
* If set to `true` the chart will not show slices with zero values.
*
* @default false
* @since 4.7.9
* @param value Ignore zero values
*/
set: function (value) {
this.setPropertyValue("ignoreZeroValues", value, true);
},
enumerable: true,
configurable: true
});
/**
* Updates corresponding legend data item with current values.
*
* @ignore Exclude from docs
* @param dataItem Data item
*/
PercentSeries.prototype.updateLegendValue = function (dataItem) {
if (dataItem) {
var legendDataItem = dataItem.legendDataItem;
var legendSettings = dataItem.legendSettings;
if (legendDataItem && legendSettings) {
if (legendSettings) {
if (legendSettings.labelText) {
legendDataItem.label.text = legendSettings.labelText;
}
if (legendSettings.itemLabelText) {
legendDataItem.label.text = legendSettings.itemLabelText;
}
if (legendSettings.valueText) {
legendDataItem.valueLabel.text = legendSettings.valueText;
}
if (legendSettings.itemValueText) {
legendDataItem.valueLabel.text = legendSettings.itemValueText;
}
}
}
}
};
return PercentSeries;
}(Series));
export { PercentSeries };
/**
* bboxter class in system, so that it can be instantiated using its name from
* anywhere.
*
* @ignore
*/
registry.registeredClasses["PercentSeries"] = PercentSeries;
registry.registeredClasses["PercentSeriesDataItem"] = PercentSeriesDataItem;
/**
* Add default responsive rules
*/
/**
* Disable labels and ticks.
*/
defaultRules.push({
relevant: ResponsiveBreakpoints.maybeXS,
state: function (target, stateId) {
if (target instanceof PercentSeries) {
var state = target.states.create(stateId);
var labelState = target.labels.template.states.create(stateId);
labelState.properties.disabled = true;
var tickState = target.ticks.template.states.create(stateId);
tickState.properties.disabled = true;
return state;
}
return null;
}
});
//# sourceMappingURL=PercentSeries.js.map