UNPKG

@nativescript-community/ui-chart

Version:

A powerful chart / graph plugin, supporting line, bar, pie, radar, bubble, and candlestick charts as well as scaling, panning and animations.

386 lines (385 loc) 16.2 kB
import { ComponentBase } from './ComponentBase'; import { LegendEntry } from './LegendEntry'; import { Utils } from '../utils/Utils'; import { ColorTemplate } from '../utils/ColorTemplate'; export var LegendForm; (function (LegendForm) { /** * Avoid drawing a form */ LegendForm[LegendForm["NONE"] = 0] = "NONE"; /** * Do not draw the a form, but leave space for it */ LegendForm[LegendForm["EMPTY"] = 1] = "EMPTY"; /** * Use default (default dataset's form to the legend's form) */ LegendForm[LegendForm["DEFAULT"] = 2] = "DEFAULT"; /** * Draw a square */ LegendForm[LegendForm["SQUARE"] = 3] = "SQUARE"; /** * Draw a circle */ LegendForm[LegendForm["CIRCLE"] = 4] = "CIRCLE"; /** * Draw a horizontal line */ LegendForm[LegendForm["LINE"] = 5] = "LINE"; })(LegendForm || (LegendForm = {})); export var LegendHorizontalAlignment; (function (LegendHorizontalAlignment) { LegendHorizontalAlignment[LegendHorizontalAlignment["LEFT"] = 0] = "LEFT"; LegendHorizontalAlignment[LegendHorizontalAlignment["CENTER"] = 1] = "CENTER"; LegendHorizontalAlignment[LegendHorizontalAlignment["RIGHT"] = 2] = "RIGHT"; })(LegendHorizontalAlignment || (LegendHorizontalAlignment = {})); export var LegendVerticalAlignment; (function (LegendVerticalAlignment) { LegendVerticalAlignment[LegendVerticalAlignment["TOP"] = 0] = "TOP"; LegendVerticalAlignment[LegendVerticalAlignment["CENTER"] = 1] = "CENTER"; LegendVerticalAlignment[LegendVerticalAlignment["BOTTOM"] = 2] = "BOTTOM"; })(LegendVerticalAlignment || (LegendVerticalAlignment = {})); export var LegendOrientation; (function (LegendOrientation) { LegendOrientation[LegendOrientation["HORIZONTAL"] = 0] = "HORIZONTAL"; LegendOrientation[LegendOrientation["VERTICAL"] = 1] = "VERTICAL"; })(LegendOrientation || (LegendOrientation = {})); export var LegendDirection; (function (LegendDirection) { LegendDirection[LegendDirection["LEFT_TO_RIGHT"] = 0] = "LEFT_TO_RIGHT"; LegendDirection[LegendDirection["RIGHT_TO_LEFT"] = 1] = "RIGHT_TO_LEFT"; })(LegendDirection || (LegendDirection = {})); /** * Class representing the legend of the chart. The legend will contain one entry * per color and DataSet. Multiple colors in one DataSet are grouped together. * The legend object is NOT available before setting data to the chart. * */ export class Legend extends ComponentBase { /** * Constructor. Provide entries for the legend. * * @param entries */ constructor(entries) { super(); /** * The legend entries array */ this.entries = []; /** * Are the legend labels/colors a custom value or auto calculated? If false, * then it's auto, if true, then custom. default false (automatic legend) */ this.mIsLegendCustom = false; this.horizontalAlignment = LegendHorizontalAlignment.LEFT; this.verticalAlignment = LegendVerticalAlignment.BOTTOM; this.orientation = LegendOrientation.HORIZONTAL; this.drawInside = false; /** * the text direction for the legend */ this.direction = LegendDirection.LEFT_TO_RIGHT; /** * the shape/form the legend colors are drawn in */ this.form = LegendForm.SQUARE; /** * the size of the legend forms/shapes */ this.formSize = 8; /** * the size of the legend forms/shapes */ this.formLineWidth = 3; /** * Line dash path effect used for shapes that consist of lines. */ this.formLineDashEffect = null; /** * the space between the legend entries on a horizontal axis, default 6f */ this.xEntrySpace = 6; /** * the space between the legend entries on a vertical axis, default 5f */ this.yEntrySpace = 0; /** * the space between the legend entries on a vertical axis, default 2f * private float this.mYEntrySpace = 2f; /** the space between the form and the * actual label/text */ this.formToTextSpace = 5; /** * the space that should be left between stacked forms */ this.stackSpace = 3; /** * The maximum relative size out of the whole chart view. / If the legend is * to the right/left of the chart, then this affects the width of the * legend. / If the legend is to the top/bottom of the chart, then this * affects the height of the legend. / If the legend is the center of the * piechart, then this defines the size of the rectangular bounds out of the * size of the "hole". / default: 0.95f (95%) */ this.maxSizePercent = 0.95; /** * the total width of the legend (needed width space) */ this.mNeededWidth = 0; /** * the total height of the legend (needed height space) */ this.mNeededHeight = 0; this.mTextHeightMax = 0; this.mTextWidthMax = 0; /** * Should the legend word wrap? / this is currently supported only for: * BelowChartLeft, BelowChartRight, BelowChartCenter. / note that word * wrapping a legend takes a toll on performance. / you may want to set * maxSizePercent when word wrapping, to set the point where the text wraps. * / default: false */ this.wordWrapEnabled = false; this.calculatedLabelSizes = []; this.calculatedLabelBreakPoints = []; this.calculatedLineSizes = []; this.enabled = false; this.textSize = 10; this.xOffset = 5; this.yOffset = 5; if (entries) { this.entries = entries; } } /** * returns the maximum length in pixels across all legend labels + formsize * + formtotextspace * * @param p the paint object used for rendering the text * @return */ getMaximumEntryWidth(p) { let max = 0; let maxFormSize = 0; const formToTextSpace = this.formToTextSpace; for (const entry of this.entries) { const formSize = isNaN(entry.formSize) ? this.formSize : entry.formSize; if (formSize > maxFormSize) maxFormSize = formSize; const label = entry.label; if (!label) continue; const length = Utils.calcTextWidth(p, label); if (length > max) max = length; } return max + maxFormSize + formToTextSpace; } /** * returns the maximum height in pixels across all legend labels * * @param p the paint object used for rendering the text * @return */ getMaximumEntryHeight(p) { let max = 0; for (const entry of this.entries) { const label = entry.label; if (!label) continue; const length = Utils.calcTextHeight(p, label); if (length > max) max = length; } return max; } /** * Entries that will be appended to the end of the auto calculated * entries after calculating the legend. * (if the legend has already been calculated, you will need to call notifyDataSetChanged() * to let the changes take effect) */ setExtra(colors, labels) { const entries = []; for (let i = 0; i < Math.min(colors.length, labels.length); i++) { const entry = new LegendEntry(labels[i], colors[i]); // entry.formColor = colors[i]; // entry.label = labels[i]; if (entry.formColor === ColorTemplate.COLOR_SKIP || !entry.formColor) entry.form = LegendForm.NONE; else if (entry.formColor === ColorTemplate.COLOR_NONE) entry.form = LegendForm.EMPTY; entries.push(entry); } this.extraEntries = entries; } /** * Sets a custom legend's entries array. * * A null label will start a group. * This will disable the feature that automatically calculates the legend * entries from the datasets. * Call resetCustom() to re-enable automatic calculation (and then * notifyDataSetChanged() is needed to auto-calculate the legend again) */ setCustom(entries) { this.entries = entries; this.mIsLegendCustom = true; } /** * Calling this will disable the custom legend entries (set by * setCustom(...)). Instead, the entries will again be calculated * automatically (after notifyDataSetChanged() is called). */ resetCustom() { this.mIsLegendCustom = false; } /** * @return true if a custom legend entries has been set default * false (automatic legend) */ isLegendCustom() { return this.mIsLegendCustom; } /** * Calculates the dimensions of the Legend. This includes the maximum width * and height of a single entry, as well as the total width and height of * the Legend. * * @param labelpaint */ calculateDimensions(labelpaint, viewPortHandler) { const defaultFormSize = this.formSize; const stackSpace = this.stackSpace; const formToTextSpace = this.formToTextSpace; const xEntrySpace = this.xEntrySpace; const yEntrySpace = this.yEntrySpace; const wordWrapEnabled = this.wordWrapEnabled; const entries = this.entries; const entryCount = entries.length; this.mTextWidthMax = this.getMaximumEntryWidth(labelpaint); this.mTextHeightMax = this.getMaximumEntryHeight(labelpaint); switch (this.orientation) { case LegendOrientation.VERTICAL: { let maxWidth = 0, maxHeight = 0, width = 0; const labelLineHeight = Utils.getLineHeight(labelpaint); let wasStacked = false; for (let i = 0; i < entryCount; i++) { const e = entries[i]; const drawingForm = e.form !== LegendForm.NONE; const formSize = isNaN(e.formSize) ? defaultFormSize : e.formSize; const label = e.label; if (!wasStacked) width = 0; if (drawingForm) { if (wasStacked) width += stackSpace; width += formSize; } // grouped forms have null labels if (label) { // make a step to the left if (drawingForm && !wasStacked) width += formToTextSpace; else if (wasStacked) { maxWidth = Math.max(maxWidth, width); maxHeight += labelLineHeight + yEntrySpace; width = 0; wasStacked = false; } width += Utils.calcTextWidth(labelpaint, label); if (i < entryCount - 1) maxHeight += labelLineHeight + yEntrySpace; } else { wasStacked = true; width += formSize; if (i < entryCount - 1) width += stackSpace; } maxWidth = Math.max(maxWidth, width); } this.mNeededWidth = maxWidth; this.mNeededHeight = maxHeight; break; } case LegendOrientation.HORIZONTAL: { const labelLineHeight = Utils.getLineHeight(labelpaint); const labelLineSpacing = Utils.getLineSpacing(labelpaint) + yEntrySpace; const contentWidth = viewPortHandler.contentRect.width() * this.maxSizePercent; // Start calculating layout let maxLineWidth = 0; let currentLineWidth = 0; let requiredWidth = 0; let stackedStartIndex = -1; this.calculatedLabelBreakPoints = []; this.calculatedLabelSizes = []; this.calculatedLineSizes = []; for (let i = 0; i < entryCount; i++) { const e = entries[i]; const drawingForm = e.form !== LegendForm.NONE; const formSize = isNaN(e.formSize) ? defaultFormSize : e.formSize; const label = e.label; this.calculatedLabelBreakPoints.push(false); if (stackedStartIndex === -1) { // we are not stacking, so required width is for this label // only requiredWidth = 0; } else { // add the spacing appropriate for stacked labels/forms requiredWidth += stackSpace; } // grouped forms have null labels if (label) { this.calculatedLabelSizes.push(Utils.calcTextSize(labelpaint, label)); requiredWidth += drawingForm ? formToTextSpace + formSize : 0; requiredWidth += this.calculatedLabelSizes[i].width; } else { this.calculatedLabelSizes.push({ x: 0, y: 0 }); requiredWidth += drawingForm ? formSize : 0; if (stackedStartIndex === -1) { // mark this index as we might want to break here later stackedStartIndex = i; } } if (label || i === entryCount - 1) { const requiredSpacing = currentLineWidth === 0 ? 0 : xEntrySpace; if (!wordWrapEnabled || // No word wrapping, it must fit. // The line is empty, it must fit currentLineWidth === 0 || // It simply fits contentWidth - currentLineWidth >= requiredSpacing + requiredWidth) { // Expand current line currentLineWidth += requiredSpacing + requiredWidth; } else { // It doesn't fit, we need to wrap a line // Add current line size to array this.calculatedLineSizes.push({ x: currentLineWidth, y: labelLineHeight }); maxLineWidth = Math.max(maxLineWidth, currentLineWidth); // Start a new line this.calculatedLabelBreakPoints[stackedStartIndex > -1 ? stackedStartIndex : i] = true; currentLineWidth = requiredWidth; } if (i === entryCount - 1) { // Add last line size to array this.calculatedLineSizes.push({ x: currentLineWidth, y: labelLineHeight }); maxLineWidth = Math.max(maxLineWidth, currentLineWidth); } } stackedStartIndex = label ? -1 : stackedStartIndex; } this.mNeededWidth = maxLineWidth; this.mNeededHeight = labelLineHeight * this.calculatedLineSizes.length + labelLineSpacing * (this.calculatedLineSizes.length === 0 ? 0 : this.calculatedLineSizes.length - 1); break; } } this.mNeededHeight += this.yOffset; this.mNeededWidth += this.xOffset; } } //# sourceMappingURL=Legend.js.map