From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 10 Feb 2020 22:57:04 +0000 (-0800) Subject: Document types for TimeScale (#7083) X-Git-Tag: v3.0.0-alpha~60 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=460ac02a9246737843756c57b99ff0c7f1c40230;p=thirdparty%2FChart.js.git Document types for TimeScale (#7083) --- diff --git a/src/core/core.adapters.js b/src/core/core.adapters.js index 8fee08562..90de41029 100644 --- a/src/core/core.adapters.js +++ b/src/core/core.adapters.js @@ -88,7 +88,7 @@ class DateAdapter { /** * Returns start of `unit` for the given `timestamp`. * @param {number} timestamp - the input timestamp - * @param {Unit} unit - the unit as string + * @param {Unit|'isoWeek'} unit - the unit as string * @param {number} [weekday] - the ISO day of the week with 1 being Monday * and 7 being Sunday (only needed if param *unit* is `isoWeek`). * @return {number} @@ -100,7 +100,7 @@ class DateAdapter { /** * Returns end of `unit` for the given `timestamp`. * @param {number} timestamp - the input timestamp - * @param {Unit} unit - the unit as string + * @param {Unit|'isoWeek'} unit - the unit as string * @return {number} */ endOf(timestamp, unit) { // eslint-disable-line no-unused-vars diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 42ca0a3ed..f0f86739f 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -327,6 +327,7 @@ class Scale extends Element { } /** + * @return {{min: number, max: number, minDefined: boolean, maxDefined: boolean}} * @private * @since 3.0 */ @@ -343,6 +344,8 @@ class Scale extends Element { } /** + * @param {boolean} canStack + * @return {{min: number, max: number}} * @private * @since 3.0 */ @@ -376,9 +379,8 @@ class Scale extends Element { /** * Get the padding needed for the scale - * @method getPadding + * @return {{top: number, left: number, bottom: number, right: number}} * @private - * @returns {object} the necessary padding */ getPadding() { const me = this; @@ -392,6 +394,7 @@ class Scale extends Element { /** * Returns the scale tick objects ({label, major}) + * @return {object[]} * @since 2.7 */ getTicks() { @@ -399,8 +402,9 @@ class Scale extends Element { } /** - * @private - */ + * @return {string[]} + * @private + */ _getLabels() { const data = this.chart.data; return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || []; @@ -417,7 +421,7 @@ class Scale extends Element { /** * @param {number} maxWidth - the max width in pixels * @param {number} maxHeight - the max height in pixels - * @param {object} margins - the space between the edge of the other scales and edge of the chart + * @param {{top: number, left: number, bottom: number, right: number}} margins - the space between the edge of the other scales and edge of the chart * This space comes from two sources: * - padding - space that's required to show the labels at the edges of the scale * - thickness of scales or legends in another orientation @@ -583,6 +587,7 @@ class Scale extends Element { } /** * Convert ticks to label strings + * @param {object[]} ticks */ generateTickLabels(ticks) { const me = this; @@ -771,14 +776,23 @@ class Scale extends Element { } // Shared Methods + /** + * @return {boolean} + */ isHorizontal() { const {axis, position} = this.options; return position === 'top' || position === 'bottom' || axis === 'x'; } + /** + * @return {boolean} + */ isFullWidth() { return this.options.fullWidth; } + /** + * @param {object[]} ticks + */ _convertTicksToLabels(ticks) { const me = this; @@ -790,6 +804,7 @@ class Scale extends Element { } /** + * @return {{ first: object, last: object, widest: object, highest: object }} * @private */ _getLabelSizes() { @@ -806,6 +821,7 @@ class Scale extends Element { /** * Returns {width, height, offset} objects for the first, last, widest, highest tick * labels where offset indicates the anchor point offset from the top in pixels. + * @return {{ first: object, last: object, widest: object, highest: object }} * @private */ _computeLabelSizes() { @@ -872,7 +888,8 @@ class Scale extends Element { /** * Used to get the label to display in the tooltip for the given value - * @param value + * @param {*} value + * @return {string} */ getLabelForValue(value) { return value; @@ -881,20 +898,26 @@ class Scale extends Element { /** * Returns the location of the given data point. Value can either be an index or a numerical value * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param value + * @param {*} value + * @return {number} */ - getPixelForValue(value) {} // eslint-disable-line no-unused-vars + getPixelForValue(value) { // eslint-disable-line no-unused-vars + return NaN; + } /** * Used to get the data value from a given pixel. This is the inverse of getPixelForValue * The coordinate (0, 0) is at the upper-left corner of the canvas - * @param pixel + * @param {number} pixel + * @return {*} */ getValueForPixel(pixel) {} // eslint-disable-line no-unused-vars /** * Returns the location of the tick at the given index * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param {number} index + * @return {number} */ getPixelForTick(index) { const me = this; @@ -910,6 +933,8 @@ class Scale extends Element { /** * Utility for getting the pixel location of a percentage of scale * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param {number} decimal + * @return {number} */ getPixelForDecimal(decimal) { const me = this; @@ -921,6 +946,10 @@ class Scale extends Element { return me._startPixel + decimal * me._length; } + /** + * @param {number} pixel + * @return {number} + */ getDecimalForPixel(pixel) { const decimal = (pixel - this._startPixel) / this._length; return this._reversePixels ? 1 - decimal : decimal; @@ -929,11 +958,15 @@ class Scale extends Element { /** * Returns the pixel for the minimum chart value * The coordinate (0, 0) is at the upper-left corner of the canvas + * @return {number} */ getBasePixel() { return this.getPixelForValue(this.getBaseValue()); } + /** + * @return {number} + */ getBaseValue() { const {min, max} = this; @@ -944,6 +977,8 @@ class Scale extends Element { /** * Returns a subset of ticks to be plotted to avoid overlapping labels. + * @param {object[]} ticks + * @return {object[]} * @private */ _autoSkip(ticks) { @@ -980,6 +1015,7 @@ class Scale extends Element { } /** + * @return {number} * @private */ _tickSize() { @@ -1003,6 +1039,7 @@ class Scale extends Element { } /** + * @return {boolean} * @private */ _isVisible() { @@ -1450,6 +1487,7 @@ class Scale extends Element { } /** + * @return {object[]} * @private */ _layers() { @@ -1485,6 +1523,7 @@ class Scale extends Element { /** * Returns visible dataset metas that are attached to this scale * @param {string} [type] - if specified, also filter by dataset type + * @return {object[]} * @private */ _getMatchingVisibleMetas(type) { @@ -1504,6 +1543,8 @@ class Scale extends Element { } /** + * @param {number} index + * @return {object} * @private */ _resolveTickFontOptions(index) { diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js index 49cab345f..d108fc804 100644 --- a/src/scales/scale.logarithmic.js +++ b/src/scales/scale.logarithmic.js @@ -149,8 +149,12 @@ class LogarithmicScale extends Scale { return ticks; } + /** + * @param {number} value + * @return {string} + */ getLabelForValue(value) { - return value === undefined ? 0 : new Intl.NumberFormat(this.options.locale).format(value); + return value === undefined ? '0' : new Intl.NumberFormat(this.options.locale).format(value); } getPixelForTick(index) { diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index 102691a87..f0d0d6204 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -7,62 +7,44 @@ import {toRadians} from '../helpers/helpers.math'; import Scale from '../core/core.scale'; import {_lookup, _lookupByKey} from '../helpers/helpers.collection'; +/** + * @typedef { import("../core/core.adapters").Unit } Unit + */ + // Integer constants are from the ES6 spec. const MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; -const INTERVALS = { - millisecond: { - common: true, - size: 1, - steps: 1000 - }, - second: { - common: true, - size: 1000, - steps: 60 - }, - minute: { - common: true, - size: 60000, - steps: 60 - }, - hour: { - common: true, - size: 3600000, - steps: 24 - }, - day: { - common: true, - size: 86400000, - steps: 30 - }, - week: { - common: false, - size: 604800000, - steps: 4 - }, - month: { - common: true, - size: 2.628e9, - steps: 12 - }, - quarter: { - common: false, - size: 7.884e9, - steps: 4 - }, - year: { - common: true, - size: 3.154e10 - } -}; +/** + * @type {Map} + */ +const INTERVALS = new Map(); +INTERVALS.set('millisecond', {common: true, size: 1, steps: 1000}); +INTERVALS.set('second', {common: true, size: 1000, steps: 60}); +INTERVALS.set('minute', {common: true, size: 60000, steps: 60}); +INTERVALS.set('hour', {common: true, size: 3600000, steps: 24}); +INTERVALS.set('day', {common: true, size: 86400000, steps: 30}); +INTERVALS.set('week', {common: false, size: 604800000, steps: 4}); +INTERVALS.set('month', {common: true, size: 2.628e9, steps: 12}); +INTERVALS.set('quarter', {common: false, size: 7.884e9, steps: 4}); +INTERVALS.set('year', {common: true, size: 3.154e10, steps: undefined}); -const UNITS = Object.keys(INTERVALS); +/** + * @type {Unit[]} + */ +const UNITS = []; +INTERVALS.forEach((v, k) => UNITS.push(k)); +/** + * @param {number} a + * @param {number} b + */ function sorter(a, b) { return a - b; } +/** + * @param {number[]} items + */ function arrayUnique(items) { const set = new Set(); let i, ilen; @@ -78,6 +60,10 @@ function arrayUnique(items) { return [...set]; } +/** + * @param {TimeScale} scale + * {*} input + */ function parse(scale, input) { if (isNullOrUndef(input)) { return null; @@ -110,6 +96,9 @@ function parse(scale, input) { return +value; } +/** + * @param {TimeScale} scale + */ function getDataTimestamps(scale) { const isSeries = scale.options.distribution === 'series'; let timestamps = scale._cache.data || []; @@ -134,6 +123,9 @@ function getDataTimestamps(scale) { return (scale._cache.data = arrayUnique(timestamps.sort(sorter))); } +/** + * @param {TimeScale} scale + */ function getLabelTimestamps(scale) { const isSeries = scale.options.distribution === 'series'; const timestamps = scale._cache.labels || []; @@ -152,6 +144,9 @@ function getLabelTimestamps(scale) { return (scale._cache.labels = isSeries ? timestamps : arrayUnique(timestamps.sort(sorter))); } +/** + * @param {TimeScale} scale + */ function getAllTimestamps(scale) { let timestamps = scale._cache.all || []; let label, data; @@ -188,6 +183,11 @@ function getAllTimestamps(scale) { * If 'series', timestamps will be positioned at the same distance from each other. In this * case, only timestamps that break the time linearity are registered, meaning that in the * best case, all timestamps are linear, the table contains only min and max. + * @param {number[]} timestamps + * @param {number} min + * @param {number} max + * @param {string} distribution + * @return {object[]} */ function buildLookupTable(timestamps, min, max, distribution) { if (distribution === 'linear' || !timestamps.length) { @@ -229,6 +229,11 @@ function buildLookupTable(timestamps, min, max, distribution) { * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') * returns the position for a timestamp equal to 42. If value is out of bounds, values at * index [0, 1] or [n - 1, n] are used for the interpolation. + * @param {object} table + * @param {string} skey + * @param {number} sval + * @param {string} tkey + * @return {object} */ function interpolate(table, skey, sval, tkey) { const {lo, hi} = _lookupByKey(table, skey, sval); @@ -246,13 +251,18 @@ function interpolate(table, skey, sval, tkey) { /** * Figures out what unit results in an appropriate number of auto-generated ticks + * @param {Unit} minUnit + * @param {number} min + * @param {number} max + * @param {number} capacity + * @return {object} */ function determineUnitForAutoTicks(minUnit, min, max, capacity) { const ilen = UNITS.length; let i, interval, factor; for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { - interval = INTERVALS[UNITS[i]]; + interval = INTERVALS.get(UNITS[i]); factor = interval.steps ? interval.steps : MAX_INTEGER; if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { @@ -265,13 +275,17 @@ function determineUnitForAutoTicks(minUnit, min, max, capacity) { /** * Figures out what unit to format a set of ticks with + * @param {TimeScale} scale + * @param {number} numTicks + * @param {Unit} minUnit + * @param {number} min + * @param {number} max + * @return {Unit} */ function determineUnitForFormatting(scale, numTicks, minUnit, min, max) { - let i, unit; - - for (i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) { - unit = UNITS[i]; - if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) { + for (let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) { + const unit = UNITS[i]; + if (INTERVALS.get(unit).common && scale._adapter.diff(max, min, unit) >= numTicks - 1) { return unit; } } @@ -279,14 +293,23 @@ function determineUnitForFormatting(scale, numTicks, minUnit, min, max) { return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; } +/** + * @param {Unit} unit + * @return {object} + */ function determineMajorUnit(unit) { for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { - if (INTERVALS[UNITS[i]].common) { + if (INTERVALS.get(UNITS[i]).common) { return UNITS[i]; } } } +/** + * @param {number[]} timestamps + * @param {Set} ticks + * @param {number} time + */ function addTick(timestamps, ticks, time) { if (!timestamps.length) { return; @@ -301,6 +324,7 @@ function addTick(timestamps, ticks, time) { * `minor` unit using the given scale time `options`. * Important: this method can return ticks outside the min and max range, it's the * responsibility of the calling code to clamp values if needed. + * @param {TimeScale} scale */ function generate(scale) { const adapter = scale._adapter; @@ -357,6 +381,12 @@ function generate(scale) { * where each value is a relative width to the scale and ranges between 0 and 1. * They add extra margins on the both sides by scaling down the original scale. * Offsets are added when the `offset` option is true. + * @param {object} table + * @param {number[]} timestamps + * @param {number} min + * @param {number} max + * @param {object} options + * @return {object} */ function computeOffsets(table, timestamps, min, max, options) { let start = 0; @@ -381,6 +411,13 @@ function computeOffsets(table, timestamps, min, max, options) { return {start: start, end: end, factor: 1 / (start + 1 + end)}; } +/** + * @param {TimeScale} scale + * @param {object[]} ticks + * @param {object} map + * @param {Unit} majorUnit + * @return {object[]} + */ function setMajorTicks(scale, ticks, map, majorUnit) { const adapter = scale._adapter; const first = +adapter.startOf(ticks[0].value, majorUnit); @@ -396,6 +433,12 @@ function setMajorTicks(scale, ticks, map, majorUnit) { return ticks; } +/** + * @param {TimeScale} scale + * @param {number[]} values + * @param {Unit|undefined} [majorUnit] + * @return {object[]} + */ function ticksFromTimestamps(scale, values, majorUnit) { const ticks = []; const map = {}; @@ -417,6 +460,9 @@ function ticksFromTimestamps(scale, values, majorUnit) { return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit); } +/** + * @param {TimeScale} scale + */ function getTimestampsForTicks(scale) { if (scale.options.ticks.source === 'labels') { return getLabelTimestamps(scale); @@ -425,12 +471,18 @@ function getTimestampsForTicks(scale) { return generate(scale); } +/** + * @param {TimeScale} scale + */ function getTimestampsForTable(scale) { return scale.options.distribution === 'series' ? getAllTimestamps(scale) : [scale.min, scale.max]; } +/** + * @param {TimeScale} scale + */ function getLabelBounds(scale) { const arr = getLabelTimestamps(scale); let min = Number.POSITIVE_INFINITY; @@ -516,6 +568,9 @@ const defaultConfig = { class TimeScale extends Scale { + /** + * @param {object} props + */ constructor(props) { super(props); @@ -524,16 +579,20 @@ class TimeScale extends Scale { const adapter = this._adapter = new adapters._date(options.adapters.date); - this._cache = {}; + this._cache = { + data: [], + labels: [], + all: [] + }; - /** @type {string | undefined} */ - this._unit = undefined; - /** @type {string | undefined} */ + /** @type {Unit} */ + this._unit = 'day'; + /** @type {Unit | undefined} */ this._majorUnit = undefined; - /** @type {object | undefined} */ - this._offsets = undefined; - /** @type {object[] | undefined} */ - this._table = undefined; + /** @type {object} */ + this._offsets = {}; + /** @type {object[]} */ + this._table = []; // Backward compatibility: before introducing adapter, `displayFormats` was // supposed to contain *all* unit/string pairs but this can't be resolved @@ -542,6 +601,11 @@ class TimeScale extends Scale { mergeIf(time.displayFormats, adapter.formats()); } + /** + * @param {*} raw + * @param {number} index + * @return {number} + */ _parse(raw, index) { // eslint-disable-line no-unused-vars if (raw === undefined) { return NaN; @@ -549,6 +613,12 @@ class TimeScale extends Scale { return parse(this, raw); } + /** + * @param {object} obj + * @param {string} axis + * @param {number} index + * @return {number} + */ _parseObject(obj, axis, index) { if (obj && obj.t) { return this._parse(obj.t, index); @@ -560,7 +630,11 @@ class TimeScale extends Scale { } _invalidateCaches() { - this._cache = {}; + this._cache = { + data: [], + labels: [], + all: [] + }; } determineDataLimits() { @@ -570,6 +644,9 @@ class TimeScale extends Scale { const unit = options.time.unit || 'day'; let {min, max, minDefined, maxDefined} = me._getUserBounds(); + /** + * @param {object} bounds + */ function _applyBounds(bounds) { if (!minDefined && !isNaN(bounds.min)) { min = Math.min(min, bounds.min); @@ -599,6 +676,9 @@ class TimeScale extends Scale { me.max = Math.max(min + 1, max); } + /** + * @return {object[]} + */ buildTicks() { const me = this; const options = me.options; @@ -635,6 +715,10 @@ class TimeScale extends Scale { return ticksFromTimestamps(me, ticks, me._majorUnit); } + /** + * @param {number} value + * @return {string} + */ getLabelForValue(value) { const me = this; const adapter = me._adapter; @@ -648,15 +732,21 @@ class TimeScale extends Scale { /** * Function to format an individual tick mark + * @param {number} time + * @param {number} index + * @param {object[]} ticks + * @param {string|undefined} [format] + * @return {string} * @private */ _tickFormatFunction(time, index, ticks, format) { const me = this; const options = me.options; const formats = options.time.displayFormats; + const unit = me._unit; const majorUnit = me._majorUnit; - const minorFormat = formats[me._unit]; - const majorFormat = formats[majorUnit]; + const minorFormat = unit && formats[unit]; + const majorFormat = majorUnit && formats[majorUnit]; const tick = ticks[index]; const major = majorUnit && majorFormat && tick && tick.major; const label = me._adapter.format(time, format ? format : major ? majorFormat : minorFormat); @@ -664,6 +754,9 @@ class TimeScale extends Scale { return formatter ? formatter(label, index, ticks) : label; } + /** + * @param {object[]} ticks + */ generateTickLabels(ticks) { let i, ilen, tick; @@ -675,6 +768,7 @@ class TimeScale extends Scale { /** * @param {number} value - Milliseconds since epoch (1 January 1970 00:00:00 UTC) + * @return {number} */ getPixelForValue(value) { const me = this; @@ -683,6 +777,10 @@ class TimeScale extends Scale { return me.getPixelForDecimal((offsets.start + pos) * offsets.factor); } + /** + * @param {number} index + * @return {number} + */ getPixelForTick(index) { const ticks = this.ticks; if (index < 0 || index > ticks.length - 1) { @@ -691,6 +789,10 @@ class TimeScale extends Scale { return this.getPixelForValue(ticks[index].value); } + /** + * @param {number} pixel + * @return {number} + */ getValueForPixel(pixel) { const me = this; const offsets = me._offsets; @@ -699,6 +801,8 @@ class TimeScale extends Scale { } /** + * @param {string} label + * @return {{w:number, h:number}} * @private */ _getLabelSize(label) { @@ -717,6 +821,8 @@ class TimeScale extends Scale { } /** + * @param {number} exampleTime + * @return {number} * @private */ _getLabelCapacity(exampleTime) {