Type annotations (#4789)

* add some types to XAxisModel

* some more type defs and small code tweaks while getting familiar with plots

* more type annotations and a few small tweaks

* more type annotations and small tweaks to make types show

* add mocha types

* Add karma and jasmine, too

* further simplify plot canvas creation

* further simplify plot canvas creation

* update types, avoid runtime behavior in type definition that breaks SeriesCollection

* undo the changes to MctChart, improve it later

* lint fix

Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
This commit is contained in:
Joe Pea 2022-03-29 14:39:49 -07:00 committed by GitHub
parent d30ec4c757
commit 651e61954c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 464 additions and 90 deletions

View File

@ -8,6 +8,11 @@
"@percy/cli": "1.0.0-beta.76",
"@percy/playwright": "1.0.1",
"@playwright/test": "1.19.2",
"@types/eventemitter3": "^1.0.0",
"@types/jasmine": "^3.10.3",
"@types/karma": "^6.3.2",
"@types/lodash": "^4.14.178",
"@types/mocha": "^9.1.0",
"allure-playwright": "2.0.0-beta.15",
"babel-loader": "8.2.3",
"babel-plugin-istanbul": "6.1.1",

View File

@ -365,3 +365,7 @@ class TimeContext extends EventEmitter {
}
export default TimeContext;
/**
@typedef {{start: number, end: number}} Bounds
*/

View File

@ -661,7 +661,7 @@ export default {
this.positionOverElement = {
x: event.clientX - this.chartElementBounds.left,
y: this.chartElementBounds.height
- (event.clientY - this.chartElementBounds.top)
- (event.clientY - this.chartElementBounds.top)
};
this.positionOverPlot = {

View File

@ -112,6 +112,10 @@ export default {
mounted() {
eventHelpers.extend(this);
if (!this.axisType) {
throw new Error("axis-type prop expected");
}
this.axis = this.getAxisFromConfig();
this.tickCount = 4;
@ -126,15 +130,16 @@ export default {
},
methods: {
getAxisFromConfig() {
if (!this.axisType) {
return;
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
/** @type {import('./configuration/PlotConfigurationModel').default} */
let config = configStore.get(configId);
if (!config) {
throw new Error('config is missing');
}
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
let config = configStore.get(configId);
if (config) {
return config[this.axisType];
}
return config[this.axisType];
},
/**
* Determine whether ticks should be regenerated for a given range.
@ -210,8 +215,8 @@ export default {
if (this.shouldRegenerateTicks(range, forceRegeneration)) {
let newTicks = this.getTicks();
this.tickRange = {
min: Math.min.apply(Math, newTicks),
max: Math.max.apply(Math, newTicks),
min: Math.min(...newTicks),
max: Math.max(...newTicks),
step: newTicks[1] - newTicks[0]
};

View File

@ -23,6 +23,9 @@
import eventHelpers from '../lib/eventHelpers';
export default class MCTChartAlarmLineSet {
/**
* @param {Bounds} bounds
*/
constructor(series, chart, offset, bounds) {
this.series = series;
this.chart = chart;
@ -40,6 +43,9 @@ export default class MCTChartAlarmLineSet {
}
}
/**
* @param {Bounds} bounds
*/
updateBounds(bounds) {
this.bounds = bounds;
this.getLimitPoints(this.series);
@ -106,3 +112,7 @@ export default class MCTChartAlarmLineSet {
}
}
/**
@typedef {import('@/api/time/TimeContext').Bounds} Bounds
*/

View File

@ -23,7 +23,7 @@
import MCTChartSeriesElement from './MCTChartSeriesElement';
export default class MCTChartLineLinear extends MCTChartSeriesElement {
addPoint(point, start, count) {
addPoint(point, start) {
this.buffer[start] = point.x;
this.buffer[start + 1] = point.y;
}

View File

@ -45,7 +45,7 @@ export default class MCTChartLineStepAfter extends MCTChartSeriesElement {
return 2 + ((index - 1) * 4);
}
addPoint(point, start, count) {
addPoint(point, start) {
if (start === 0 && this.count === 0) {
// First point is easy.
this.buffer[start] = point.x;

View File

@ -24,7 +24,7 @@ import MCTChartSeriesElement from './MCTChartSeriesElement';
// TODO: Is this needed? This is identical to MCTChartLineLinear. Why is it a different class?
export default class MCTChartPointSet extends MCTChartSeriesElement {
addPoint(point, start, count) {
addPoint(point, start) {
this.buffer[start] = point.x;
this.buffer[start + 1] = point.y;
}

View File

@ -22,6 +22,7 @@
import eventHelpers from '../lib/eventHelpers';
/** @abstract */
export default class MCTChartSeriesElement {
constructor(series, chart, offset) {
this.series = series;
@ -72,9 +73,11 @@ export default class MCTChartSeriesElement {
}
}
removePoint(point, index, count) {
// by default, do nothing.
}
/** @abstract */
removePoint(index) {}
/** @abstract */
addPoint(point, index) {}
remove(point, index, series) {
const vertexCount = this.vertexCountForPointAtIndex(index);
@ -155,3 +158,18 @@ export default class MCTChartSeriesElement {
}
}
/** @typedef {any} TODO */
/** @typedef {import('../configuration/PlotSeries').default} PlotSeries */
/**
@typedef {{
x: (x: number) => number
y: (y: number) => number
xVal: (point: Point, pSeries: PlotSeries) => number
yVal: (point: Point, pSeries: PlotSeries) => number
xKey: TODO
yKey: TODO
}} Offset
*/

View File

@ -21,11 +21,17 @@
*****************************************************************************/
import Model from './Model';
/**
* @template {object} T
* @template {object} O
* @extends {Model<T, O>}
*/
export default class Collection extends Model {
/** @type {Constructor} */
modelClass = Model;
initialize(options) {
super.initialize(options);
this.modelClass = Model;
if (options.models) {
this.models = options.models.map(this.modelFn, this);
} else {
@ -107,3 +113,7 @@ export default class Collection extends Model {
this.stopListening();
}
}
/** @typedef {any} TODO */
/** @typedef {new (...args: any[]) => object} Constructor */

View File

@ -19,32 +19,47 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
function ConfigStore() {
this.store = {};
}
class ConfigStore {
/** @type {Record<string, Destroyable>} */
store = {};
ConfigStore.prototype.deleteStore = function (id) {
if (this.store[id]) {
if (this.store[id].destroy) {
this.store[id].destroy();
/**
@param {string} id
*/
deleteStore(id) {
const obj = this.store[id];
if (obj) {
if (obj.destroy) {
obj.destroy();
}
delete this.store[id];
}
delete this.store[id];
}
};
ConfigStore.prototype.deleteAll = function () {
Object.keys(this.store).forEach(id => this.deleteStore(id));
};
deleteAll() {
Object.keys(this.store).forEach(id => this.deleteStore(id));
}
ConfigStore.prototype.add = function (id, config) {
this.store[id] = config;
};
/**
@param {string} id
@param {any} config
*/
add(id, config) {
this.store[id] = config;
}
ConfigStore.prototype.get = function (id) {
return this.store[id];
};
/**
@param {string} id
*/
get(id) {
return this.store[id];
}
}
const STORE = new ConfigStore();
export default STORE;
/** @typedef {{destroy?(): void}} Destroyable */

View File

@ -42,7 +42,10 @@ export default class LegendModel extends Model {
}
}
defaults(options) {
/**
* @override
*/
defaultModel(options) {
return {
position: 'top',
expandByDefault: false,

View File

@ -20,11 +20,18 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import EventEmitter from 'EventEmitter';
import EventEmitter from 'eventemitter3';
import eventHelpers from "../lib/eventHelpers";
import _ from 'lodash';
/**
* @template {object} T
* @template {object} O
*/
export default class Model extends EventEmitter {
/**
* @param {ModelOptions<T, O>} options
*/
constructor(options) {
super();
@ -35,10 +42,14 @@ export default class Model extends EventEmitter {
options = {};
}
// FIXME: this.id is defined as a method further below, but here it is
// assigned a possibly-undefined value. Is this code unused?
this.id = options.id;
/** @type {ModelType<T>} */
this.model = options.model;
this.collection = options.collection;
const defaults = this.defaults(options);
const defaults = this.defaultModel(options);
if (!this.model) {
this.model = options.model = defaults;
} else {
@ -46,14 +57,23 @@ export default class Model extends EventEmitter {
}
this.initialize(options);
/** @type {keyof ModelType<T> } */
this.idAttr = 'id';
}
defaults(options) {
/**
* @param {ModelOptions<T, O>} options
* @returns {ModelType<T>}
*/
defaultModel(options) {
return {};
}
initialize(model) {
/**
* @param {ModelOptions<T, O>} options
*/
initialize(options) {
}
@ -69,14 +89,29 @@ export default class Model extends EventEmitter {
return this.get(this.idAttr);
}
/**
* @template {keyof ModelType<T>} K
* @param {K} attribute
* @returns {ModelType<T>[K]}
*/
get(attribute) {
return this.model[attribute];
}
/**
* @template {keyof ModelType<T>} K
* @param {K} attribute
* @returns boolean
*/
has(attribute) {
return _.has(this.model, attribute);
}
/**
* @template {keyof ModelType<T>} K
* @param {K} attribute
* @param {ModelType<T>[K]} value
*/
set(attribute, value) {
const oldValue = this.model[attribute];
this.model[attribute] = value;
@ -84,6 +119,10 @@ export default class Model extends EventEmitter {
this.emit('change:' + attribute, value, oldValue, this);
}
/**
* @template {keyof ModelType<T>} K
* @param {K} attribute
*/
unset(attribute) {
const oldValue = this.model[attribute];
delete this.model[attribute];
@ -91,3 +130,26 @@ export default class Model extends EventEmitter {
this.emit('change:' + attribute, undefined, oldValue, this);
}
}
/** @typedef {any} TODO */
/** @typedef {TODO} OpenMCT */
/**
@template {object} T
@typedef {{
id?: string
} & T} ModelType
*/
/**
@template {object} T
@template {object} O
@typedef {{
model?: ModelType<T>
models?: T[]
openmct: OpenMCT
id?: string
[k: string]: unknown
} & O} ModelOptions
*/

View File

@ -26,20 +26,30 @@ import SeriesCollection from "./SeriesCollection";
import XAxisModel from "./XAxisModel";
import YAxisModel from "./YAxisModel";
import LegendModel from "./LegendModel";
/**
* PlotConfiguration model stores the configuration of a plot and some
* limited state. The indiidual parts of the plot configuration model
* handle setting defaults and updating in response to various changes.
*
* @extends {Model<PlotConfigModelType, PlotConfigModelOptions>}
*/
export default class PlotConfigurationModel extends Model {
/**
* Initializes all sub models and then passes references to submodels
* to those that need it.
*
* @override
* @param {import('./Model').ModelOptions<PlotConfigModelType, PlotConfigModelOptions>} options
*/
initialize(options) {
this.openmct = options.openmct;
// This is a type assertion for TypeScript, this error is never thrown in practice.
if (!options.model) {
throw new Error('Not a collection model.');
}
this.xAxis = new XAxisModel({
model: options.model.xAxis,
plot: this,
@ -76,6 +86,8 @@ export default class PlotConfigurationModel extends Model {
}
/**
* Retrieve the persisted series config for a given identifier.
* @param {import('./PlotSeries').Identifier} identifier
* @returns {import('./PlotSeries').PlotSeriesModelType=}
*/
getPersistedSeriesConfig(identifier) {
const domainObject = this.get('domainObject');
@ -123,15 +135,48 @@ export default class PlotConfigurationModel extends Model {
/**
* Return defaults, which are extracted from the passed in domain
* object.
* @override
* @param {import('./Model').ModelOptions<PlotConfigModelType, PlotConfigModelOptions>} options
*/
defaults(options) {
defaultModel(options) {
return {
series: [],
domainObject: options.domainObject,
xAxis: {
},
yAxis: _.cloneDeep(_.get(options.domainObject, 'configuration.yAxis', {})),
legend: _.cloneDeep(_.get(options.domainObject, 'configuration.legend', {}))
xAxis: {},
yAxis: _.cloneDeep(options.domainObject.configuration?.yAxis ?? {}),
legend: _.cloneDeep(options.domainObject.configuration?.legend ?? {})
};
}
}
/** @typedef {any} TODO */
/** @typedef {import('./PlotSeries').default} PlotSeries */
/**
@typedef {{
configuration: {
series: import('./PlotSeries').PlotSeriesModelType[]
}
}} SomeDomainObject_NeedsName
*/
/**
@typedef {{
xAxis: import('./XAxisModel').XAxisModelType
yAxis: import('./YAxisModel').YAxisModelType
legend: TODO
series: PlotSeries[]
domainObject: SomeDomainObject_NeedsName
}} PlotConfigModelType
*/
/** @typedef {TODO} SomeOtherDomainObject */
/**
TODO: Is SomeOtherDomainObject the same domain object as with SomeDomainObject_NeedsName?
@typedef {{
plot: import('./PlotConfigurationModel').default
domainObject: SomeOtherDomainObject
}} PlotConfigModelOptions
*/

View File

@ -59,9 +59,15 @@ import configStore from "../configuration/ConfigStore";
* `metadata`: the Open MCT Telemetry Metadata Manager for the associated
* telemetry point.
* `formats`: the Open MCT format map for this telemetry point.
*
* @extends {Model<PlotSeriesModelType, PlotSeriesModelOptions>}
*/
export default class PlotSeries extends Model {
/**
@param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
*/
constructor(options) {
super(options);
this.listenTo(this, 'change:xKey', this.onXKeyChange, this);
@ -76,8 +82,10 @@ export default class PlotSeries extends Model {
/**
* Set defaults for telemetry series.
* @param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
* @override
*/
defaults(options) {
defaultModel(options) {
this.metadata = options
.openmct
.telemetry
@ -109,13 +117,21 @@ export default class PlotSeries extends Model {
/**
* Remove real-time subscription when destroyed.
* @override
*/
onDestroy(model) {
destroy() {
super.destroy();
if (this.unsubscribe) {
this.unsubscribe();
}
}
/**
* Set defaults for telemetry series.
* @override
* @param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
*/
initialize(options) {
this.openmct = options.openmct;
this.domainObject = options.domainObject;
@ -136,9 +152,11 @@ export default class PlotSeries extends Model {
});
this.openmct.time.on('bounds', this.updateLimits);
this.on('destroy', this.onDestroy, this);
}
/**
* @param {Bounds} bounds
*/
updateLimits(bounds) {
this.emit('limitBounds', bounds);
}
@ -188,7 +206,7 @@ export default class PlotSeries extends Model {
return this.openmct
.telemetry
.request(this.domainObject, options)
.then(function (points) {
.then((points) => {
const data = this.getSeriesData();
const newPoints = _(data)
.concat(points)
@ -196,7 +214,7 @@ export default class PlotSeries extends Model {
.uniq(true, point => [this.getXVal(point), this.getYVal(point)].join())
.value();
this.reset(newPoints);
}.bind(this))
})
.catch((error) => {
console.warn('Error fetching data', error);
});
@ -501,8 +519,55 @@ export default class PlotSeries extends Model {
/**
* Update the series data with the given value.
* @returns {Array<{
cos: number
sin: number
mctLimitState: {
cssClass: string
high: number
low: {sin: number, cos: number}
name: string
}
utc: number
wavelength: number
yesterday: number
}>}
*/
getSeriesData() {
return configStore.get(this.dataStoreId) || [];
}
}
/** @typedef {any} TODO */
/** @typedef {{key: string, namespace: string}} Identifier */
/**
@typedef {{
identifier: Identifier
name: string
unit: string
xKey: string
yKey: string
markers: boolean
markerShape: keyof typeof MARKER_SHAPES
markerSize: number
alarmMarkers: boolean
limitLines: boolean
interpolate: boolean
stats: TODO
}} PlotSeriesModelType
*/
/**
@typedef {{
model: PlotSeriesModelType
collection: import('./SeriesCollection').default
persistedConfig: PlotSeriesModelType
filters: TODO
}} PlotSeriesModelOptions
*/
/**
@typedef {import('@/api/time/TimeContext').Bounds} Bounds
*/

View File

@ -26,8 +26,14 @@ import Collection from "./Collection";
import Color from "@/ui/color/Color";
import ColorPalette from "@/ui/color/ColorPalette";
/**
* @extends {Collection<SeriesCollectionModelType, SeriesCollectionOptions>}
*/
export default class SeriesCollection extends Collection {
/**
@override
@param {import('./Model').ModelOptions<SeriesCollectionModelType, SeriesCollectionOptions>} options
*/
initialize(options) {
super.initialize(options);
this.modelClass = PlotSeries;
@ -83,11 +89,15 @@ export default class SeriesCollection extends Collection {
// Clone to prevent accidental mutation by ref.
seriesConfig = JSON.parse(JSON.stringify(seriesConfig));
if (!seriesConfig) {
throw "not possible";
}
this.add(new PlotSeries({
model: seriesConfig,
domainObject: domainObject,
collection: this,
openmct: this.openmct,
collection: this,
persistedConfig: this.plot
.getPersistedSeriesConfig(domainObject.identifier),
filters: filters
@ -163,3 +173,13 @@ export default class SeriesCollection extends Collection {
})[0];
}
}
/**
@typedef {PlotSeries} SeriesCollectionModelType
*/
/**
@typedef {{
plot: import('./PlotConfigurationModel').default
}} SeriesCollectionOptions
*/

View File

@ -20,22 +20,38 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Model from "./Model";
/**
* TODO: doc strings.
*/
* @extends {Model<XAxisModelType, XAxisModelOptions>}
*/
export default class XAxisModel extends Model {
// Despite providing template types to the Model class, we still need to
// re-define the type of the following initialize() method's options arg. Tracking
// issue for this: https://github.com/microsoft/TypeScript/issues/32082
// When they fix it, we can remove the `@param` we have here.
/**
* @override
* @param {import('./Model').ModelOptions<XAxisModelType, XAxisModelOptions>} options
*/
initialize(options) {
this.plot = options.plot;
// This is a type assertion for TypeScript, this error is not thrown in practice.
if (!options.model) {
throw new Error('Not a collection model.');
}
this.set('label', options.model.name || '');
this.on('change:range', function (newValue, oldValue, model) {
if (!model.get('frozen')) {
model.set('displayRange', newValue);
this.on('change:range', (newValue) => {
if (!this.get('frozen')) {
this.set('displayRange', newValue);
}
});
this.on('change:frozen', ((frozen, oldValue, model) => {
this.on('change:frozen', ((frozen) => {
if (!frozen) {
model.set('range', this.get('range'));
this.set('range', this.get('range'));
}
}));
@ -45,6 +61,10 @@ export default class XAxisModel extends Model {
this.listenTo(this, 'change:key', this.changeKey, this);
}
/**
* @param {string} newKey
*/
changeKey(newKey) {
const series = this.plot.series.first();
if (series) {
@ -68,12 +88,17 @@ export default class XAxisModel extends Model {
plotSeries.reset();
});
}
defaults(options) {
/**
* @param {import('./Model').ModelOptions<XAxisModelType, XAxisModelOptions>} options
* @override
*/
defaultModel(options) {
const bounds = options.openmct.time.bounds();
const timeSystem = options.openmct.time.timeSystem();
const format = options.openmct.telemetry.getFormatter(timeSystem.timeFormat);
return {
/** @type {XAxisModelType} */
const defaultModel = {
name: timeSystem.name,
key: timeSystem.key,
format: format.format.bind(format),
@ -83,5 +108,42 @@ export default class XAxisModel extends Model {
},
frozen: false
};
return defaultModel;
}
}
/** @typedef {any} TODO */
/** @typedef {TODO} OpenMCT */
/**
@typedef {{
min: number
max: number
}} NumberRange
*/
/**
@typedef {import("./Model").ModelType<{
range: NumberRange
displayRange: NumberRange
frozen: boolean
label: string
format: (n: number) => string
values: Array<TODO>
}>} AxisModelType
*/
/**
@typedef {AxisModelType & {
name: string
key: string
}} XAxisModelType
*/
/**
@typedef {{
plot: import('./PlotConfigurationModel').default
}} XAxisModelOptions
*/

View File

@ -23,27 +23,32 @@ import _ from 'lodash';
import Model from './Model';
/**
* YAxis model
*
* TODO: docstrings.
*
* has the following Model properties:
*
* `autoscale`: boolean, whether or not to autoscale.
* `autoscalePadding`: float, percent of padding to display in plots.
* `displayRange`: the current display range for the x Axis.
* `format`: the formatter for the axis.
* `frozen`: boolean, if true, displayRange will not be updated automatically.
* Used to temporarily disable automatic updates during user interaction.
* `label`: label to display on axis.
* `stats`: Min and Max Values of data, automatically updated by observing
* plot series.
* `values`: for enumerated types, an array of possible display values.
* `range`: the user-configured range to use for display, when autoscale is
* disabled.
*
*/
* YAxis model
*
* TODO: docstrings.
*
* has the following Model properties:
*
* `autoscale`: boolean, whether or not to autoscale.
* `autoscalePadding`: float, percent of padding to display in plots.
* `displayRange`: the current display range for the x Axis.
* `format`: the formatter for the axis.
* `frozen`: boolean, if true, displayRange will not be updated automatically.
* Used to temporarily disable automatic updates during user interaction.
* `label`: label to display on axis.
* `stats`: Min and Max Values of data, automatically updated by observing
* plot series.
* `values`: for enumerated types, an array of possible display values.
* `range`: the user-configured range to use for display, when autoscale is
* disabled.
*
* @extends {Model<YAxisModelType, YAxisModelOptions>}
*/
export default class YAxisModel extends Model {
/**
* @override
* @param {import('./Model').ModelOptions<YAxisModelType, YAxisModelOptions>} options
*/
initialize(options) {
this.plot = options.plot;
this.listenTo(this, 'change:stats', this.calculateAutoscaleExtents, this);
@ -53,6 +58,9 @@ export default class YAxisModel extends Model {
this.listenTo(this, 'change:range', this.updateDisplayRange, this);
this.updateDisplayRange(this.get('range'));
}
/**
* @param {import('./SeriesCollection').default} seriesCollection
*/
listenToSeriesCollection(seriesCollection) {
this.seriesCollection = seriesCollection;
this.listenTo(this.seriesCollection, 'add', (series => {
@ -138,6 +146,9 @@ export default class YAxisModel extends Model {
}
}, this);
}
/**
* @param {import('./PlotSeries').default} series
*/
trackSeries(series) {
this.listenTo(series, 'change:stats', seriesStats => {
if (!seriesStats) {
@ -163,12 +174,13 @@ export default class YAxisModel extends Model {
}
}
/**
* Update yAxis format, values, and label from known series.
*/
updateFromSeries(series) {
* Update yAxis format, values, and label from known series.
* @param {import('./SeriesCollection').default} seriesCollection
*/
updateFromSeries(seriesCollection) {
const plotModel = this.plot.get('domainObject');
const label = _.get(plotModel, 'configuration.yAxis.label');
const sampleSeries = series.first();
const sampleSeries = seriesCollection.first();
if (!sampleSeries) {
if (!label) {
this.unset('label');
@ -183,7 +195,7 @@ export default class YAxisModel extends Model {
this.set('format', yFormat.format.bind(yFormat));
this.set('values', yMetadata.values);
if (!label) {
const labelName = series.map(function (s) {
const labelName = seriesCollection.map(function (s) {
return s.metadata ? s.metadata.value(s.get('yKey')).name : '';
}).reduce(function (a, b) {
if (a === undefined) {
@ -203,7 +215,7 @@ export default class YAxisModel extends Model {
return;
}
const labelUnits = series.map(function (s) {
const labelUnits = seriesCollection.map(function (s) {
return s.metadata ? s.metadata.value(s.get('yKey')).units : '';
}).reduce(function (a, b) {
if (a === undefined) {
@ -224,7 +236,13 @@ export default class YAxisModel extends Model {
}
}
}
defaults(options) {
/**
* @override
* @param {import('./Model').ModelOptions<YAxisModelType, YAxisModelOptions>} options
* @returns {YAxisModelType}
*/
defaultModel(options) {
// @ts-ignore incomplete YAxisModelType object used for default value.
return {
frozen: false,
autoscale: true,
@ -232,3 +250,20 @@ export default class YAxisModel extends Model {
};
}
}
/** @typedef {any} TODO */
/**
@typedef {import('./XAxisModel').AxisModelType & {
autoscale: boolean
autoscalePadding: number
stats: import('./XAxisModel').NumberRange
values: Array<TODO>
}} YAxisModelType
*/
/**
@typedef {{
plot: import('./PlotConfigurationModel').default
}} YAxisModelOptions
*/

View File

@ -110,6 +110,7 @@ DrawWebGL.prototype.onContextLost = function (event) {
this.emit('error');
this.isContextLost = true;
this.destroy();
// TODO re-initialize and re-draw on context restored
};
DrawWebGL.prototype.initContext = function () {

View File

@ -90,3 +90,10 @@ const helperFunctions = {
};
export default helperFunctions;
/**
@typedef {{
listenTo: (object: any, event: any, callback: any, context: any) => void
stopListening: (object: any, event: any, callback: any, context: any) => void
}} EventHelpers
*/

View File

@ -7,7 +7,14 @@
"allowJs": true,
"checkJs": false,
"strict": true,
"esModuleInterop": true,
"noImplicitOverride": true,
"module": "esnext",
"moduleResolution": "node"
"moduleResolution": "node",
"paths": {
// matches the alias in webpack config, so that types for those imports are visible.
"@/*": ["src/*"]
}
}
}