@datalayer/jupyter-react 0.9.4 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/lib/app/JupyterReact.js +1 -1
  2. package/lib/app/JupyterReact.js.map +1 -1
  3. package/lib/components/environment/Environment.d.ts +2 -0
  4. package/lib/components/environment/Environment.js +7 -0
  5. package/lib/components/environment/Environment.js.map +1 -0
  6. package/lib/components/kernel/{KernelStatus.d.ts → Kernelndicator.d.ts} +7 -5
  7. package/lib/components/kernel/{KernelStatus.js → Kernelndicator.js} +12 -12
  8. package/lib/components/kernel/Kernelndicator.js.map +1 -0
  9. package/lib/components/kernel/index.d.ts +1 -1
  10. package/lib/components/kernel/index.js +1 -1
  11. package/lib/components/kernel/index.js.map +1 -1
  12. package/lib/examples/Kernels.js +2 -2
  13. package/lib/examples/Kernels.js.map +1 -1
  14. package/lib/jupyter/ipywidgets/classic/manager.js +0 -6
  15. package/lib/jupyter/ipywidgets/classic/manager.js.map +1 -1
  16. package/lib/jupyter/kernel/KernelExecutor.js +4 -27
  17. package/lib/jupyter/kernel/KernelExecutor.js.map +1 -1
  18. package/package.json +7 -7
  19. package/lib/components/kernel/KernelStatus.js.map +0 -1
  20. package/lib/jupyter/ipywidgets/plotly/Figure.d.ts +0 -587
  21. package/lib/jupyter/ipywidgets/plotly/Figure.js +0 -1746
  22. package/lib/jupyter/ipywidgets/plotly/Figure.js.map +0 -1
  23. package/lib/jupyter/ipywidgets/plotly/JupyterlabPlugin.d.ts +0 -5
  24. package/lib/jupyter/ipywidgets/plotly/JupyterlabPlugin.js +0 -17
  25. package/lib/jupyter/ipywidgets/plotly/JupyterlabPlugin.js.map +0 -1
  26. package/lib/jupyter/ipywidgets/plotly/Version.d.ts +0 -8
  27. package/lib/jupyter/ipywidgets/plotly/Version.js +0 -21
  28. package/lib/jupyter/ipywidgets/plotly/Version.js.map +0 -1
  29. package/lib/jupyter/ipywidgets/plotly/index.d.ts +0 -2
  30. package/lib/jupyter/ipywidgets/plotly/index.js +0 -9
  31. package/lib/jupyter/ipywidgets/plotly/index.js.map +0 -1
@@ -1,1746 +0,0 @@
1
- /*
2
- * Copyright (c) 2021-2023 Datalayer, Inc.
3
- *
4
- * MIT License
5
- */
6
- /* eslint-disable @typescript-eslint/ban-ts-comment */
7
- /* eslint-disable @typescript-eslint/no-this-alias */
8
- /* eslint-disable prefer-rest-params */
9
- /* eslint-disable no-var */
10
- /* eslint-disable no-prototype-builtins */
11
- import { DOMWidgetModel, DOMWidgetView, } from '@jupyter-widgets/base';
12
- import _ from 'lodash';
13
- import Plotly from 'plotly.js';
14
- import { MODULE_NAME, MODULE_VERSION } from './Version';
15
- // @ts-expect-error unknown global PlotlyConfig
16
- window.PlotlyConfig = { MathJaxConfig: 'local' };
17
- const semver_range = '^' + MODULE_VERSION;
18
- // Model
19
- // =====
20
- /**
21
- * A FigureModel holds a mirror copy of the state of a FigureWidget on
22
- * the Python side. There is a one-to-one relationship between JavaScript
23
- * FigureModels and Python FigureWidgets. The JavaScript FigureModel is
24
- * initialized as soon as a Python FigureWidget initialized, this happens
25
- * even before the widget is first displayed in the Notebook
26
- * @type {widgets.DOMWidgetModel}
27
- */
28
- class FigureModel extends DOMWidgetModel {
29
- defaults() {
30
- return {
31
- ...super.defaults(),
32
- // Model metadata
33
- // --------------
34
- _model_name: FigureModel.model_name,
35
- _model_module: FigureModel.model_module,
36
- _model_module_version: FigureModel.model_module_version,
37
- _view_name: FigureModel.view_name,
38
- _view_module: FigureModel.view_module,
39
- _view_module_version: FigureModel.view_module_version,
40
- // Data and Layout
41
- // ---------------
42
- // The _data and _layout properties are synchronized with the
43
- // Python side on initialization only. After initialization, these
44
- // properties are kept in sync through the use of the _py2js_*
45
- // messages
46
- _data: [],
47
- _layout: {},
48
- _config: {},
49
- // Python -> JS messages
50
- // ---------------------
51
- // Messages are implemented using trait properties. This is done so
52
- // that we can take advantage of ipywidget's binary serialization
53
- // protocol.
54
- //
55
- // Messages are sent by the Python side by assigning the message
56
- // contents to the appropriate _py2js_* property, and then immediately
57
- // setting it to None. Messages are received by the JavaScript
58
- // side by registering property change callbacks in the initialize
59
- // methods for FigureModel and FigureView. e.g. (where this is a
60
- // FigureModel):
61
- //
62
- // this.on('change:_py2js_addTraces', this.do_addTraces, this);
63
- //
64
- // Message handling methods, do_addTraces, are responsible for
65
- // performing the appropriate action if the message contents are
66
- // not null
67
- /**
68
- * @typedef {null|Object} Py2JsAddTracesMsg
69
- * @property {Array.<Object>} trace_data
70
- * Array of traces to append to the end of the figure's current traces
71
- * @property {Number} trace_edit_id
72
- * Edit ID to use when returning trace deltas using
73
- * the _js2py_traceDeltas message.
74
- * @property {Number} layout_edit_id
75
- * Edit ID to use when returning layout deltas using
76
- * the _js2py_layoutDelta message.
77
- */
78
- _py2js_addTraces: null,
79
- /**
80
- * @typedef {null|Object} Py2JsDeleteTracesMsg
81
- * @property {Array.<Number>} delete_inds
82
- * Array of indexes of traces to be deleted, in ascending order
83
- * @property {Number} trace_edit_id
84
- * Edit ID to use when returning trace deltas using
85
- * the _js2py_traceDeltas message.
86
- * @property {Number} layout_edit_id
87
- * Edit ID to use when returning layout deltas using
88
- * the _js2py_layoutDelta message.
89
- */
90
- _py2js_deleteTraces: null,
91
- /**
92
- * @typedef {null|Object} Py2JsMoveTracesMsg
93
- * @property {Array.<Number>} current_trace_inds
94
- * Array of the current indexes of traces to be moved
95
- * @property {Array.<Number>} new_trace_inds
96
- * Array of the new indexes that traces should be moved to.
97
- */
98
- _py2js_moveTraces: null,
99
- /**
100
- * @typedef {null|Object} Py2JsRestyleMsg
101
- * @property {Object} restyle_data
102
- * Restyle data as accepted by Plotly.restyle
103
- * @property {null|Array.<Number>} restyle_traces
104
- * Array of indexes of the traces that the resytle operation applies
105
- * to, or null to apply the operation to all traces
106
- * @property {Number} trace_edit_id
107
- * Edit ID to use when returning trace deltas using
108
- * the _js2py_traceDeltas message
109
- * @property {Number} layout_edit_id
110
- * Edit ID to use when returning layout deltas using
111
- * the _js2py_layoutDelta message
112
- * @property {null|String} source_view_id
113
- * view_id of the FigureView that triggered the original restyle
114
- * event (e.g. by clicking the legend), or null if the restyle was
115
- * triggered from Python
116
- */
117
- _py2js_restyle: null,
118
- /**
119
- * @typedef {null|Object} Py2JsRelayoutMsg
120
- * @property {Object} relayout_data
121
- * Relayout data as accepted by Plotly.relayout
122
- * @property {Number} layout_edit_id
123
- * Edit ID to use when returning layout deltas using
124
- * the _js2py_layoutDelta message
125
- * @property {null|String} source_view_id
126
- * view_id of the FigureView that triggered the original relayout
127
- * event (e.g. by clicking the zoom button), or null if the
128
- * relayout was triggered from Python
129
- */
130
- _py2js_relayout: null,
131
- /**
132
- * @typedef {null|Object} Py2JsUpdateMsg
133
- * @property {Object} style_data
134
- * Style data as accepted by Plotly.update
135
- * @property {Object} layout_data
136
- * Layout data as accepted by Plotly.update
137
- * @property {Array.<Number>} style_traces
138
- * Array of indexes of the traces that the update operation applies
139
- * to, or null to apply the operation to all traces
140
- * @property {Number} trace_edit_id
141
- * Edit ID to use when returning trace deltas using
142
- * the _js2py_traceDeltas message
143
- * @property {Number} layout_edit_id
144
- * Edit ID to use when returning layout deltas using
145
- * the _js2py_layoutDelta message
146
- * @property {null|String} source_view_id
147
- * view_id of the FigureView that triggered the original update
148
- * event (e.g. by clicking a button), or null if the update was
149
- * triggered from Python
150
- */
151
- _py2js_update: null,
152
- /**
153
- * @typedef {null|Object} Py2JsAnimateMsg
154
- * @property {Object} style_data
155
- * Style data as accepted by Plotly.animate
156
- * @property {Object} layout_data
157
- * Layout data as accepted by Plotly.animate
158
- * @property {Array.<Number>} style_traces
159
- * Array of indexes of the traces that the animate operation applies
160
- * to, or null to apply the operation to all traces
161
- * @property {Object} animation_opts
162
- * Animation options as accepted by Plotly.animate
163
- * @property {Number} trace_edit_id
164
- * Edit ID to use when returning trace deltas using
165
- * the _js2py_traceDeltas message
166
- * @property {Number} layout_edit_id
167
- * Edit ID to use when returning layout deltas using
168
- * the _js2py_layoutDelta message
169
- * @property {null|String} source_view_id
170
- * view_id of the FigureView that triggered the original animate
171
- * event (e.g. by clicking a button), or null if the update was
172
- * triggered from Python
173
- */
174
- _py2js_animate: null,
175
- /**
176
- * @typedef {null|Object} Py2JsRemoveLayoutPropsMsg
177
- * @property {Array.<Array.<String|Number>>} remove_props
178
- * Array of property paths to remove. Each propery path is an
179
- * array of property names or array indexes that locate a property
180
- * inside the _layout object
181
- */
182
- _py2js_removeLayoutProps: null,
183
- /**
184
- * @typedef {null|Object} Py2JsRemoveTracePropsMsg
185
- * @property {Number} remove_trace
186
- * The index of the trace from which to remove properties
187
- * @property {Array.<Array.<String|Number>>} remove_props
188
- * Array of property paths to remove. Each propery path is an
189
- * array of property names or array indexes that locate a property
190
- * inside the _data[remove_trace] object
191
- */
192
- _py2js_removeTraceProps: null,
193
- // JS -> Python messages
194
- // ---------------------
195
- // Messages are sent by the JavaScript side by assigning the
196
- // message contents to the appropriate _js2py_* property and then
197
- // calling the `touch` method on the view that triggered the
198
- // change. e.g. (where this is a FigureView):
199
- //
200
- // this.model.set('_js2py_restyle', data);
201
- // this.touch();
202
- //
203
- // The Python side is responsible for setting the property to None
204
- // after receiving the message.
205
- //
206
- // Message trigger logic is described in the corresponding
207
- // handle_plotly_* methods of FigureView
208
- /**
209
- * @typedef {null|Object} Js2PyRestyleMsg
210
- * @property {Object} style_data
211
- * Style data that was passed to Plotly.restyle
212
- * @property {Array.<Number>} style_traces
213
- * Array of indexes of the traces that the restyle operation
214
- * was applied to, or null if applied to all traces
215
- * @property {String} source_view_id
216
- * view_id of the FigureView that triggered the original restyle
217
- * event (e.g. by clicking the legend)
218
- */
219
- _js2py_restyle: null,
220
- /**
221
- * @typedef {null|Object} Js2PyRelayoutMsg
222
- * @property {Object} relayout_data
223
- * Relayout data that was passed to Plotly.relayout
224
- * @property {String} source_view_id
225
- * view_id of the FigureView that triggered the original relayout
226
- * event (e.g. by clicking the zoom button)
227
- */
228
- _js2py_relayout: null,
229
- /**
230
- * @typedef {null|Object} Js2PyUpdateMsg
231
- * @property {Object} style_data
232
- * Style data that was passed to Plotly.update
233
- * @property {Object} layout_data
234
- * Layout data that was passed to Plotly.update
235
- * @property {Array.<Number>} style_traces
236
- * Array of indexes of the traces that the update operation applied
237
- * to, or null if applied to all traces
238
- * @property {String} source_view_id
239
- * view_id of the FigureView that triggered the original relayout
240
- * event (e.g. by clicking the zoom button)
241
- */
242
- _js2py_update: null,
243
- /**
244
- * @typedef {null|Object} Js2PyLayoutDeltaMsg
245
- * @property {Object} layout_delta
246
- * The layout delta object that contains all of the properties of
247
- * _fullLayout that are not identical to those in the
248
- * FigureModel's _layout property
249
- * @property {Number} layout_edit_id
250
- * Edit ID of message that triggered the creation of layout delta
251
- */
252
- _js2py_layoutDelta: null,
253
- /**
254
- * @typedef {null|Object} Js2PyTraceDeltasMsg
255
- * @property {Array.<Object>} trace_deltas
256
- * Array of trace delta objects. Each trace delta contains the
257
- * trace's uid along with all of the properties of _fullData that
258
- * are not identical to those in the FigureModel's _data property
259
- * @property {Number} trace_edit_id
260
- * Edit ID of message that triggered the creation of trace deltas
261
- */
262
- _js2py_traceDeltas: null,
263
- /**
264
- * Object representing a collection of points for use in click, hover,
265
- * and selection events
266
- * @typedef {Object} Points
267
- * @property {Array.<Number>} trace_indexes
268
- * Array of the trace index for each point
269
- * @property {Array.<Number>} point_indexes
270
- * Array of the index of each point in its own trace
271
- * @property {null|Array.<Number>} xs
272
- * Array of the x coordinate of each point (for cartesian trace types)
273
- * or null (for non-cartesian trace types)
274
- * @property {null|Array.<Number>} ys
275
- * Array of the y coordinate of each point (for cartesian trace types)
276
- * or null (for non-cartesian trace types
277
- * @property {null|Array.<Number>} zs
278
- * Array of the z coordinate of each point (for 3D cartesian
279
- * trace types)
280
- * or null (for non-3D-cartesian trace types)
281
- */
282
- /**
283
- * Object representing the state of the input devices during a
284
- * plotly event
285
- * @typedef {Object} InputDeviceState
286
- * @property {boolean} alt - true if alt key pressed,
287
- * false otherwise
288
- * @property {boolean} ctrl - true if ctrl key pressed,
289
- * false otherwise
290
- * @property {boolean} meta - true if meta key pressed,
291
- * false otherwise
292
- * @property {boolean} shift - true if shift key pressed,
293
- * false otherwise
294
- *
295
- * @property {boolean} button
296
- * Indicates which button was pressed on the mouse to trigger the
297
- * event.
298
- * 0: Main button pressed, usually the left button or the
299
- * un-initialized state
300
- * 1: Auxiliary button pressed, usually the wheel button or
301
- * the middle button (if present)
302
- * 2: Secondary button pressed, usually the right button
303
- * 3: Fourth button, typically the Browser Back button
304
- * 4: Fifth button, typically the Browser Forward button
305
- *
306
- * @property {boolean} buttons
307
- * Indicates which buttons were pressed on the mouse when the event
308
- * is triggered.
309
- * 0 : No button or un-initialized
310
- * 1 : Primary button (usually left)
311
- * 2 : Secondary button (usually right)
312
- * 4 : Auxilary button (usually middle or mouse wheel button)
313
- * 8 : 4th button (typically the "Browser Back" button)
314
- * 16 : 5th button (typically the "Browser Forward" button)
315
- *
316
- * Combinations of buttons are represented by the sum of the codes
317
- * above. e.g. a value of 7 indicates buttons 1 (primary),
318
- * 2 (secondary), and 4 (auxilary) were pressed during the event
319
- */
320
- /**
321
- * @typedef {Object} BoxSelectorState
322
- * @property {Array.<Number>} xrange
323
- * Two element array containing the x-range of the box selection
324
- * @property {Array.<Number>} yrange
325
- * Two element array containing the y-range of the box selection
326
- */
327
- /**
328
- * @typedef {Object} LassoSelectorState
329
- * @property {Array.<Number>} xs
330
- * Array of the x-coordinates of the lasso selection region
331
- * @property {Array.<Number>} ys
332
- * Array of the y-coordinates of the lasso selection region
333
- */
334
- /**
335
- * Object representing the state of the selection tool during a
336
- * plotly_select event
337
- * @typedef {Object} Selector
338
- * @property {String} type
339
- * Selection type. One of: 'box', or 'lasso'
340
- * @property {BoxSelectorState|LassoSelectorState} selector_state
341
- */
342
- /**
343
- * @typedef {null|Object} Js2PyPointsCallbackMsg
344
- * @property {string} event_type
345
- * Name of the triggering event. One of 'plotly_click',
346
- * 'plotly_hover', 'plotly_unhover', or 'plotly_selected'
347
- * @property {null|Points} points
348
- * Points object for event
349
- * @property {null|InputDeviceState} device_state
350
- * InputDeviceState object for event
351
- * @property {null|Selector} selector
352
- * State of the selection tool for 'plotly_selected' events, null
353
- * for other event types
354
- */
355
- _js2py_pointsCallback: null,
356
- // Message tracking
357
- // ----------------
358
- /**
359
- * @type {Number}
360
- * layout_edit_id of the last layout modification operation
361
- * requested by the Python side
362
- */
363
- _last_layout_edit_id: 0,
364
- /**
365
- * @type {Number}
366
- * trace_edit_id of the last trace modification operation
367
- * requested by the Python side
368
- */
369
- _last_trace_edit_id: 0,
370
- };
371
- }
372
- /**
373
- * Initialize FigureModel. Called when the Python FigureWidget is first
374
- * constructed
375
- */
376
- initialize() {
377
- super.initialize.apply(this, arguments);
378
- this.on('change:_data', this.do_data, this);
379
- this.on('change:_layout', this.do_layout, this);
380
- this.on('change:_py2js_addTraces', this.do_addTraces, this);
381
- this.on('change:_py2js_deleteTraces', this.do_deleteTraces, this);
382
- this.on('change:_py2js_moveTraces', this.do_moveTraces, this);
383
- this.on('change:_py2js_restyle', this.do_restyle, this);
384
- this.on('change:_py2js_relayout', this.do_relayout, this);
385
- this.on('change:_py2js_update', this.do_update, this);
386
- this.on('change:_py2js_animate', this.do_animate, this);
387
- this.on('change:_py2js_removeLayoutProps', this.do_removeLayoutProps, this);
388
- this.on('change:_py2js_removeTraceProps', this.do_removeTraceProps, this);
389
- }
390
- /**
391
- * Input a trace index specification and return an Array of trace
392
- * indexes where:
393
- *
394
- * - null|undefined -> Array of all traces
395
- * - Trace index as Number -> Single element array of input index
396
- * - Array of trace indexes -> Input array unchanged
397
- *
398
- * @param {undefined|null|Number|Array.<Number>} trace_indexes
399
- * @returns {Array.<Number>}
400
- * Array of trace indexes
401
- * @private
402
- */
403
- _normalize_trace_indexes(trace_indexes) {
404
- if (trace_indexes === null || trace_indexes === undefined) {
405
- const numTraces = this.get('_data').length;
406
- trace_indexes = _.range(numTraces);
407
- }
408
- if (!Array.isArray(trace_indexes)) {
409
- // Make sure idx is an array
410
- trace_indexes = [trace_indexes];
411
- }
412
- return trace_indexes;
413
- }
414
- /**
415
- * Log changes to the _data trait
416
- *
417
- * This should only happed on FigureModel initialization
418
- */
419
- do_data() { }
420
- /**
421
- * Log changes to the _layout trait
422
- *
423
- * This should only happed on FigureModel initialization
424
- */
425
- do_layout() { }
426
- /**
427
- * Handle addTraces message
428
- */
429
- do_addTraces() {
430
- // add trace to plot
431
- /** @type {Py2JsAddTracesMsg} */
432
- const msgData = this.get('_py2js_addTraces');
433
- if (msgData !== null) {
434
- const currentTraces = this.get('_data');
435
- const newTraces = msgData.trace_data;
436
- _.forEach(newTraces, newTrace => {
437
- currentTraces.push(newTrace);
438
- });
439
- }
440
- }
441
- /**
442
- * Handle deleteTraces message
443
- */
444
- do_deleteTraces() {
445
- // remove traces from plot
446
- /** @type {Py2JsDeleteTracesMsg} */
447
- const msgData = this.get('_py2js_deleteTraces');
448
- if (msgData !== null) {
449
- const delete_inds = msgData.delete_inds;
450
- const tracesData = this.get('_data');
451
- // Remove del inds in reverse order so indexes remain valid
452
- // throughout loop
453
- delete_inds
454
- .slice()
455
- .reverse()
456
- .forEach(del_ind => {
457
- tracesData.splice(del_ind, 1);
458
- });
459
- }
460
- }
461
- /**
462
- * Handle moveTraces message
463
- */
464
- do_moveTraces() {
465
- /** @type {Py2JsMoveTracesMsg} */
466
- const msgData = this.get('_py2js_moveTraces');
467
- if (msgData !== null) {
468
- const tracesData = this.get('_data');
469
- const currentInds = msgData.current_trace_inds;
470
- const newInds = msgData.new_trace_inds;
471
- performMoveTracesLike(tracesData, currentInds, newInds);
472
- }
473
- }
474
- /**
475
- * Handle restyle message
476
- */
477
- do_restyle() {
478
- /** @type {Py2JsRestyleMsg} */
479
- const msgData = this.get('_py2js_restyle');
480
- if (msgData !== null) {
481
- const restyleData = msgData.restyle_data;
482
- const restyleTraces = this._normalize_trace_indexes(msgData.restyle_traces);
483
- performRestyleLike(this.get('_data'), restyleData, restyleTraces);
484
- }
485
- }
486
- /**
487
- * Handle relayout message
488
- */
489
- do_relayout() {
490
- /** @type {Py2JsRelayoutMsg} */
491
- const msgData = this.get('_py2js_relayout');
492
- if (msgData !== null) {
493
- performRelayoutLike(this.get('_layout'), msgData.relayout_data);
494
- }
495
- }
496
- /**
497
- * Handle update message
498
- */
499
- do_update() {
500
- /** @type {Py2JsUpdateMsg} */
501
- const msgData = this.get('_py2js_update');
502
- if (msgData !== null) {
503
- const style = msgData.style_data;
504
- const layout = msgData.layout_data;
505
- const styleTraces = this._normalize_trace_indexes(msgData.style_traces);
506
- performRestyleLike(this.get('_data'), style, styleTraces);
507
- performRelayoutLike(this.get('_layout'), layout);
508
- }
509
- }
510
- /**
511
- * Handle animate message
512
- */
513
- do_animate() {
514
- /** @type {Py2JsAnimateMsg} */
515
- const msgData = this.get('_py2js_animate');
516
- if (msgData !== null) {
517
- const styles = msgData.style_data;
518
- const layout = msgData.layout_data;
519
- const trace_indexes = this._normalize_trace_indexes(msgData.style_traces);
520
- for (let i = 0; i < styles.length; i++) {
521
- const style = styles[i];
522
- const trace_index = trace_indexes[i];
523
- const trace = this.get('_data')[trace_index];
524
- performRelayoutLike(trace, style);
525
- }
526
- performRelayoutLike(this.get('_layout'), layout);
527
- }
528
- }
529
- /**
530
- * Handle removeLayoutProps message
531
- */
532
- do_removeLayoutProps() {
533
- /** @type {Py2JsRemoveLayoutPropsMsg} */
534
- const msgData = this.get('_py2js_removeLayoutProps');
535
- if (msgData !== null) {
536
- const keyPaths = msgData.remove_props;
537
- const layout = this.get('_layout');
538
- performRemoveProps(layout, keyPaths);
539
- }
540
- }
541
- /**
542
- * Handle removeTraceProps message
543
- */
544
- do_removeTraceProps() {
545
- /** @type {Py2JsRemoveTracePropsMsg} */
546
- const msgData = this.get('_py2js_removeTraceProps');
547
- if (msgData !== null) {
548
- const keyPaths = msgData.remove_props;
549
- const traceIndex = msgData.remove_trace;
550
- const trace = this.get('_data')[traceIndex];
551
- performRemoveProps(trace, keyPaths);
552
- }
553
- }
554
- static serializers = {
555
- ...DOMWidgetModel.serializers,
556
- _data: { deserialize: py2js_deserializer, serialize: js2py_serializer },
557
- _layout: {
558
- deserialize: py2js_deserializer,
559
- serialize: js2py_serializer,
560
- },
561
- _py2js_addTraces: {
562
- deserialize: py2js_deserializer,
563
- serialize: js2py_serializer,
564
- },
565
- _py2js_deleteTraces: {
566
- deserialize: py2js_deserializer,
567
- serialize: js2py_serializer,
568
- },
569
- _py2js_moveTraces: {
570
- deserialize: py2js_deserializer,
571
- serialize: js2py_serializer,
572
- },
573
- _py2js_restyle: {
574
- deserialize: py2js_deserializer,
575
- serialize: js2py_serializer,
576
- },
577
- _py2js_relayout: {
578
- deserialize: py2js_deserializer,
579
- serialize: js2py_serializer,
580
- },
581
- _py2js_update: {
582
- deserialize: py2js_deserializer,
583
- serialize: js2py_serializer,
584
- },
585
- _py2js_animate: {
586
- deserialize: py2js_deserializer,
587
- serialize: js2py_serializer,
588
- },
589
- _py2js_removeLayoutProps: {
590
- deserialize: py2js_deserializer,
591
- serialize: js2py_serializer,
592
- },
593
- _py2js_removeTraceProps: {
594
- deserialize: py2js_deserializer,
595
- serialize: js2py_serializer,
596
- },
597
- _js2py_restyle: {
598
- deserialize: py2js_deserializer,
599
- serialize: js2py_serializer,
600
- },
601
- _js2py_relayout: {
602
- deserialize: py2js_deserializer,
603
- serialize: js2py_serializer,
604
- },
605
- _js2py_update: {
606
- deserialize: py2js_deserializer,
607
- serialize: js2py_serializer,
608
- },
609
- _js2py_layoutDelta: {
610
- deserialize: py2js_deserializer,
611
- serialize: js2py_serializer,
612
- },
613
- _js2py_traceDeltas: {
614
- deserialize: py2js_deserializer,
615
- serialize: js2py_serializer,
616
- },
617
- _js2py_pointsCallback: {
618
- deserialize: py2js_deserializer,
619
- serialize: js2py_serializer,
620
- },
621
- };
622
- static model_name = 'FigureModel';
623
- static model_module = MODULE_NAME;
624
- static model_module_version = semver_range;
625
- static view_name = 'FigureView';
626
- static view_module = MODULE_NAME;
627
- static view_module_version = semver_range;
628
- }
629
- export { FigureModel };
630
- // View
631
- // ====
632
- /**
633
- * A FigureView manages the visual presentation of a single Plotly.js
634
- * figure for a single notebook output cell. Each FigureView has a
635
- * reference to FigureModel. Multiple views may share a single model
636
- * instance, as is the case when a Python FigureWidget is displayed in
637
- * multiple notebook output cells.
638
- *
639
- * @type {widgets.DOMWidgetView}
640
- */
641
- export class FigureView extends DOMWidgetView {
642
- viewID;
643
- /**
644
- * The perform_render method is called by processLuminoMessage
645
- * after the widget's DOM element has been attached to the notebook
646
- * output cell. This happens after the initialize of the
647
- * FigureModel, and it won't happen at all if the Python FigureWidget
648
- * is never displayed in a notebook output cell
649
- */
650
- perform_render() {
651
- const that = this;
652
- // Wire up message property callbacks
653
- // ----------------------------------
654
- // Python -> JS event properties
655
- this.model.on('change:_py2js_addTraces', this.do_addTraces, this);
656
- this.model.on('change:_py2js_deleteTraces', this.do_deleteTraces, this);
657
- this.model.on('change:_py2js_moveTraces', this.do_moveTraces, this);
658
- this.model.on('change:_py2js_restyle', this.do_restyle, this);
659
- this.model.on('change:_py2js_relayout', this.do_relayout, this);
660
- this.model.on('change:_py2js_update', this.do_update, this);
661
- this.model.on('change:_py2js_animate', this.do_animate, this);
662
- // MathJax configuration
663
- // ---------------------
664
- if (window.MathJax) {
665
- window.MathJax.Hub.Config({ SVG: { font: 'STIX-Web' } });
666
- }
667
- // Get message ids
668
- // ---------------------
669
- const layout_edit_id = this.model.get('_last_layout_edit_id');
670
- const trace_edit_id = this.model.get('_last_trace_edit_id');
671
- // Set view UID
672
- // ------------
673
- this.viewID = randstr();
674
- // Initialize Plotly.js figure
675
- // ---------------------------
676
- // We must clone the model's data and layout properties so that
677
- // the model is not directly mutated by the Plotly.js library.
678
- const initialTraces = _.cloneDeep(this.model.get('_data'));
679
- const initialLayout = _.cloneDeep(this.model.get('_layout'));
680
- const config = this.model.get('_config');
681
- Plotly.newPlot(that.el, initialTraces, initialLayout, config).then(() => {
682
- // ### Send trace deltas ###
683
- // We create an array of deltas corresponding to the new
684
- // traces.
685
- that._sendTraceDeltas(trace_edit_id);
686
- // ### Send layout delta ###
687
- that._sendLayoutDelta(layout_edit_id);
688
- // Wire up plotly event callbacks
689
- that.el.on('plotly_restyle', (update) => {
690
- that.handle_plotly_restyle(update);
691
- });
692
- that.el.on('plotly_relayout', (update) => {
693
- that.handle_plotly_relayout(update);
694
- });
695
- /*
696
- (<Plotly.PlotlyHTMLElement>that.el).on("plotly_update", function (update: any) {
697
- that.handle_plotly_update(update);
698
- });
699
- */
700
- that.el.on('plotly_click', (update) => {
701
- that.handle_plotly_click(update);
702
- });
703
- that.el.on('plotly_hover', (update) => {
704
- that.handle_plotly_hover(update);
705
- });
706
- that.el.on('plotly_unhover', (update) => {
707
- that.handle_plotly_unhover(update);
708
- });
709
- that.el.on('plotly_selected', (update) => {
710
- that.handle_plotly_selected(update);
711
- });
712
- /*
713
- (<Plotly.PlotlyHTMLElement>that.el).on("plotly_deselect", function (update: any) {
714
- that.handle_plotly_deselect(update);
715
- });
716
- (<Plotly.PlotlyHTMLElement>that.el).on("plotly_doubleclick", function (update: any) {
717
- that.handle_plotly_doubleclick(update);
718
- });
719
- */
720
- // Emit event indicating that the widget has finished
721
- // rendering
722
- const event = new CustomEvent('plotlywidget-after-render', {
723
- detail: { element: that.el, viewID: that.viewID },
724
- });
725
- // Dispatch/Trigger/Fire the event
726
- document.dispatchEvent(event);
727
- });
728
- }
729
- /**
730
- * Respond to phosphorjs events
731
- */
732
- processLuminoMessage(msg) {
733
- super.processLuminoMessage.apply(this, arguments);
734
- const that = this;
735
- switch (msg.type) {
736
- case 'before-attach':
737
- // Render an initial empty figure. This establishes with
738
- // the page that the element will not be empty, avoiding
739
- // some occasions where the dynamic sizing behavior leads
740
- // to collapsed figure dimensions.
741
- var axisHidden = {
742
- showgrid: false,
743
- showline: false,
744
- tickvals: [],
745
- };
746
- Plotly.newPlot(that.el, [], {
747
- xaxis: axisHidden,
748
- yaxis: axisHidden,
749
- });
750
- window.addEventListener('resize', () => {
751
- that.autosizeFigure();
752
- });
753
- break;
754
- case 'after-attach':
755
- // Rendering actual figure in the after-attach event allows
756
- // Plotly.js to size the figure to fill the available element
757
- this.perform_render();
758
- break;
759
- case 'resize':
760
- this.autosizeFigure();
761
- break;
762
- }
763
- }
764
- autosizeFigure() {
765
- const that = this;
766
- const layout = that.model.get('_layout');
767
- if (_.isNil(layout) || _.isNil(layout.width)) {
768
- // @ts-ignore
769
- Plotly.Plots.resize(that.el).then(() => {
770
- const layout_edit_id = that.model.get('_last_layout_edit_id');
771
- that._sendLayoutDelta(layout_edit_id);
772
- });
773
- }
774
- }
775
- /**
776
- * Purge Plotly.js data structures from the notebook output display
777
- * element when the view is destroyed
778
- */
779
- destroy() {
780
- Plotly.purge(this.el);
781
- }
782
- /**
783
- * Return the figure's _fullData array merged with its data array
784
- *
785
- * The merge ensures that for any properties that el._fullData and
786
- * el.data have in common, we return the version from el.data
787
- *
788
- * Named colorscales are one example of why this is needed. The el.data
789
- * array will hold named colorscale strings (e.g. 'Viridis'), while the
790
- * el._fullData array will hold the actual colorscale array. e.g.
791
- *
792
- * el.data[0].marker.colorscale == 'Viridis' but
793
- * el._fullData[0].marker.colorscale = [[..., ...], ...]
794
- *
795
- * Performing the merge allows our FigureModel to retain the 'Viridis'
796
- * string, rather than having it overridded by the colorscale array.
797
- *
798
- */
799
- getFullData() {
800
- return _.mergeWith({}, this.el._fullData, this.el.data, fullMergeCustomizer);
801
- }
802
- /**
803
- * Return the figure's _fullLayout object merged with its layout object
804
- *
805
- * See getFullData documentation for discussion of why the merge is
806
- * necessary
807
- */
808
- getFullLayout() {
809
- return _.mergeWith({}, this.el._fullLayout, this.el.layout, fullMergeCustomizer);
810
- }
811
- /**
812
- * Build Points data structure from data supplied by the plotly_click,
813
- * plotly_hover, or plotly_select events
814
- * @param {Object} data
815
- * @returns {null|Points}
816
- */
817
- buildPointsObject(data) {
818
- let pointsObject;
819
- if (data.hasOwnProperty('points')) {
820
- // Most cartesian plots
821
- const pointObjects = data['points'];
822
- const numPoints = pointObjects.length;
823
- let hasNestedPointObjects = true;
824
- for (let i = 0; i < numPoints; i++) {
825
- hasNestedPointObjects =
826
- hasNestedPointObjects &&
827
- pointObjects[i].hasOwnProperty('pointNumbers');
828
- if (!hasNestedPointObjects) {
829
- break;
830
- }
831
- }
832
- let numPointNumbers = numPoints;
833
- if (hasNestedPointObjects) {
834
- numPointNumbers = 0;
835
- for (let i = 0; i < numPoints; i++) {
836
- numPointNumbers += pointObjects[i]['pointNumbers'].length;
837
- }
838
- }
839
- pointsObject = {
840
- trace_indexes: new Array(numPointNumbers),
841
- point_indexes: new Array(numPointNumbers),
842
- xs: new Array(numPointNumbers),
843
- ys: new Array(numPointNumbers),
844
- };
845
- if (hasNestedPointObjects) {
846
- let flatPointIndex = 0;
847
- for (var p = 0; p < numPoints; p++) {
848
- for (let i = 0; i < pointObjects[p]['pointNumbers'].length; i++, flatPointIndex++) {
849
- pointsObject['point_indexes'][flatPointIndex] =
850
- pointObjects[p]['pointNumbers'][i];
851
- // also add xs, ys and traces so that the array doesn't get truncated later
852
- pointsObject['xs'][flatPointIndex] = pointObjects[p]['x'];
853
- pointsObject['ys'][flatPointIndex] = pointObjects[p]['y'];
854
- pointsObject['trace_indexes'][flatPointIndex] =
855
- pointObjects[p]['curveNumber'];
856
- }
857
- }
858
- let single_trace = true;
859
- for (let i = 1; i < numPointNumbers; i++) {
860
- single_trace =
861
- single_trace &&
862
- pointsObject['trace_indexes'][i - 1] ===
863
- pointsObject['trace_indexes'][i];
864
- if (!single_trace) {
865
- break;
866
- }
867
- }
868
- if (single_trace) {
869
- pointsObject['point_indexes'].sort((a, b) => {
870
- return a - b;
871
- });
872
- }
873
- }
874
- else {
875
- for (var p = 0; p < numPoints; p++) {
876
- pointsObject['trace_indexes'][p] = pointObjects[p]['curveNumber'];
877
- pointsObject['point_indexes'][p] = pointObjects[p]['pointNumber'];
878
- pointsObject['xs'][p] = pointObjects[p]['x'];
879
- pointsObject['ys'][p] = pointObjects[p]['y'];
880
- }
881
- }
882
- // Add z if present
883
- const hasZ = pointObjects[0] !== undefined && pointObjects[0].hasOwnProperty('z');
884
- if (hasZ) {
885
- pointsObject['zs'] = new Array(numPoints);
886
- for (p = 0; p < numPoints; p++) {
887
- pointsObject['zs'][p] = pointObjects[p]['z'];
888
- }
889
- }
890
- return pointsObject;
891
- }
892
- else {
893
- return null;
894
- }
895
- }
896
- /**
897
- * Build InputDeviceState data structure from data supplied by the
898
- * plotly_click, plotly_hover, or plotly_select events
899
- * @param {Object} data
900
- * @returns {null|InputDeviceState}
901
- */
902
- buildInputDeviceStateObject(data) {
903
- const event = data['event'];
904
- if (event === undefined) {
905
- return null;
906
- }
907
- else {
908
- /** @type {InputDeviceState} */
909
- const inputDeviceState = {
910
- // Keyboard modifiers
911
- alt: event['altKey'],
912
- ctrl: event['ctrlKey'],
913
- meta: event['metaKey'],
914
- shift: event['shiftKey'],
915
- // Mouse buttons
916
- button: event['button'],
917
- buttons: event['buttons'],
918
- };
919
- return inputDeviceState;
920
- }
921
- }
922
- /**
923
- * Build Selector data structure from data supplied by the
924
- * plotly_select event
925
- * @param data
926
- * @returns {null|Selector}
927
- */
928
- buildSelectorObject(data) {
929
- let selectorObject;
930
- if (data.hasOwnProperty('range')) {
931
- // Box selection
932
- selectorObject = {
933
- type: 'box',
934
- selector_state: {
935
- xrange: data['range']['x'],
936
- yrange: data['range']['y'],
937
- },
938
- };
939
- }
940
- else if (data.hasOwnProperty('lassoPoints')) {
941
- // Lasso selection
942
- selectorObject = {
943
- type: 'lasso',
944
- selector_state: {
945
- xs: data['lassoPoints']['x'],
946
- ys: data['lassoPoints']['y'],
947
- },
948
- };
949
- }
950
- else {
951
- selectorObject = null;
952
- }
953
- return selectorObject;
954
- }
955
- /**
956
- * Handle ploty_restyle events emitted by the Plotly.js library
957
- * @param data
958
- */
959
- handle_plotly_restyle(data) {
960
- if (data === null || data === undefined) {
961
- // No data to report to the Python side
962
- return;
963
- }
964
- if (data[0] && data[0].hasOwnProperty('_doNotReportToPy')) {
965
- // Restyle originated on the Python side
966
- return;
967
- }
968
- // Unpack data
969
- const styleData = data[0];
970
- const styleTraces = data[1];
971
- // Construct restyle message to send to the Python side
972
- /** @type {Js2PyRestyleMsg} */
973
- const restyleMsg = {
974
- style_data: styleData,
975
- style_traces: styleTraces,
976
- source_view_id: this.viewID,
977
- };
978
- this.model.set('_js2py_restyle', restyleMsg);
979
- this.touch();
980
- }
981
- /**
982
- * Handle plotly_relayout events emitted by the Plotly.js library
983
- * @param data
984
- */
985
- handle_plotly_relayout(data) {
986
- if (data === null || data === undefined) {
987
- // No data to report to the Python side
988
- return;
989
- }
990
- if (data.hasOwnProperty('_doNotReportToPy')) {
991
- // Relayout originated on the Python side
992
- return;
993
- }
994
- /** @type {Js2PyRelayoutMsg} */
995
- const relayoutMsg = {
996
- relayout_data: data,
997
- source_view_id: this.viewID,
998
- };
999
- this.model.set('_js2py_relayout', relayoutMsg);
1000
- this.touch();
1001
- }
1002
- /**
1003
- * Handle plotly_update events emitted by the Plotly.js library
1004
- * @param data
1005
- */
1006
- handle_plotly_update(data) {
1007
- if (data === null || data === undefined) {
1008
- // No data to report to the Python side
1009
- return;
1010
- }
1011
- if (data['data'] && data['data'][0].hasOwnProperty('_doNotReportToPy')) {
1012
- // Update originated on the Python side
1013
- return;
1014
- }
1015
- /** @type {Js2PyUpdateMsg} */
1016
- const updateMsg = {
1017
- style_data: data['data'][0],
1018
- style_traces: data['data'][1],
1019
- layout_data: data['layout'],
1020
- source_view_id: this.viewID,
1021
- };
1022
- // Log message
1023
- this.model.set('_js2py_update', updateMsg);
1024
- this.touch();
1025
- }
1026
- /**
1027
- * Handle plotly_click events emitted by the Plotly.js library
1028
- * @param data
1029
- */
1030
- handle_plotly_click(data) {
1031
- this._send_points_callback_message(data, 'plotly_click');
1032
- }
1033
- /**
1034
- * Handle plotly_hover events emitted by the Plotly.js library
1035
- * @param data
1036
- */
1037
- handle_plotly_hover(data) {
1038
- this._send_points_callback_message(data, 'plotly_hover');
1039
- }
1040
- /**
1041
- * Handle plotly_unhover events emitted by the Plotly.js library
1042
- * @param data
1043
- */
1044
- handle_plotly_unhover(data) {
1045
- this._send_points_callback_message(data, 'plotly_unhover');
1046
- }
1047
- /**
1048
- * Handle plotly_selected events emitted by the Plotly.js library
1049
- * @param data
1050
- */
1051
- handle_plotly_selected(data) {
1052
- this._send_points_callback_message(data, 'plotly_selected');
1053
- }
1054
- /**
1055
- * Handle plotly_deselect events emitted by the Plotly.js library
1056
- * @param data
1057
- */
1058
- handle_plotly_deselect(data) {
1059
- data = {
1060
- points: [],
1061
- };
1062
- this._send_points_callback_message(data, 'plotly_deselect');
1063
- }
1064
- /**
1065
- * Build and send a points callback message to the Python side
1066
- *
1067
- * @param {Object} data
1068
- * data object as provided by the plotly_click, plotly_hover,
1069
- * plotly_unhover, or plotly_selected events
1070
- * @param {String} event_type
1071
- * Name of the triggering event. One of 'plotly_click',
1072
- * 'plotly_hover', 'plotly_unhover', or 'plotly_selected'
1073
- * @private
1074
- */
1075
- _send_points_callback_message(data, event_type) {
1076
- if (data === null || data === undefined) {
1077
- // No data to report to the Python side
1078
- return;
1079
- }
1080
- /** @type {Js2PyPointsCallbackMsg} */
1081
- const pointsMsg = {
1082
- event_type: event_type,
1083
- points: this.buildPointsObject(data),
1084
- device_state: this.buildInputDeviceStateObject(data),
1085
- selector: this.buildSelectorObject(data),
1086
- };
1087
- if (pointsMsg['points'] !== null && pointsMsg['points'] !== undefined) {
1088
- this.model.set('_js2py_pointsCallback', pointsMsg);
1089
- this.touch();
1090
- }
1091
- }
1092
- /**
1093
- * Stub for future handling of plotly_doubleclick
1094
- * @param data
1095
- */
1096
- handle_plotly_doubleclick(data) { }
1097
- /**
1098
- * Handle Plotly.addTraces request
1099
- */
1100
- do_addTraces() {
1101
- /** @type {Py2JsAddTracesMsg} */
1102
- const msgData = this.model.get('_py2js_addTraces');
1103
- if (msgData !== null) {
1104
- const that = this;
1105
- Plotly.addTraces(this.el, msgData.trace_data).then(() => {
1106
- // ### Send trace deltas ###
1107
- that._sendTraceDeltas(msgData.trace_edit_id);
1108
- // ### Send layout delta ###
1109
- const layout_edit_id = msgData.layout_edit_id;
1110
- that._sendLayoutDelta(layout_edit_id);
1111
- });
1112
- }
1113
- }
1114
- /**
1115
- * Handle Plotly.deleteTraces request
1116
- */
1117
- do_deleteTraces() {
1118
- /** @type {Py2JsDeleteTracesMsg} */
1119
- const msgData = this.model.get('_py2js_deleteTraces');
1120
- if (msgData !== null) {
1121
- const delete_inds = msgData.delete_inds;
1122
- const that = this;
1123
- Plotly.deleteTraces(this.el, delete_inds).then(() => {
1124
- // ### Send trace deltas ###
1125
- const trace_edit_id = msgData.trace_edit_id;
1126
- that._sendTraceDeltas(trace_edit_id);
1127
- // ### Send layout delta ###
1128
- const layout_edit_id = msgData.layout_edit_id;
1129
- that._sendLayoutDelta(layout_edit_id);
1130
- });
1131
- }
1132
- }
1133
- /**
1134
- * Handle Plotly.moveTraces request
1135
- */
1136
- do_moveTraces() {
1137
- /** @type {Py2JsMoveTracesMsg} */
1138
- const msgData = this.model.get('_py2js_moveTraces');
1139
- if (msgData !== null) {
1140
- // Unpack message
1141
- const currentInds = msgData.current_trace_inds;
1142
- const newInds = msgData.new_trace_inds;
1143
- // Check if the new trace indexes are actually different than
1144
- // the current indexes
1145
- const inds_equal = _.isEqual(currentInds, newInds);
1146
- if (!inds_equal) {
1147
- Plotly.moveTraces(this.el, currentInds, newInds);
1148
- }
1149
- }
1150
- }
1151
- /**
1152
- * Handle Plotly.restyle request
1153
- */
1154
- do_restyle() {
1155
- /** @type {Py2JsRestyleMsg} */
1156
- const msgData = this.model.get('_py2js_restyle');
1157
- if (msgData !== null) {
1158
- const restyleData = msgData.restyle_data;
1159
- const traceIndexes = this.model._normalize_trace_indexes(msgData.restyle_traces);
1160
- restyleData['_doNotReportToPy'] = true;
1161
- Plotly.restyle(this.el, restyleData, traceIndexes);
1162
- // ### Send trace deltas ###
1163
- // We create an array of deltas corresponding to the restyled
1164
- // traces.
1165
- this._sendTraceDeltas(msgData.trace_edit_id);
1166
- // ### Send layout delta ###
1167
- const layout_edit_id = msgData.layout_edit_id;
1168
- this._sendLayoutDelta(layout_edit_id);
1169
- }
1170
- }
1171
- /**
1172
- * Handle Plotly.relayout request
1173
- */
1174
- do_relayout() {
1175
- /** @type {Py2JsRelayoutMsg} */
1176
- const msgData = this.model.get('_py2js_relayout');
1177
- if (msgData !== null) {
1178
- if (msgData.source_view_id !== this.viewID) {
1179
- const relayoutData = msgData.relayout_data;
1180
- relayoutData['_doNotReportToPy'] = true;
1181
- Plotly.relayout(this.el, msgData.relayout_data);
1182
- }
1183
- // ### Send layout delta ###
1184
- const layout_edit_id = msgData.layout_edit_id;
1185
- this._sendLayoutDelta(layout_edit_id);
1186
- }
1187
- }
1188
- /**
1189
- * Handle Plotly.update request
1190
- */
1191
- do_update() {
1192
- /** @type {Py2JsUpdateMsg} */
1193
- const msgData = this.model.get('_py2js_update');
1194
- if (msgData !== null) {
1195
- const style = msgData.style_data || {};
1196
- const layout = msgData.layout_data || {};
1197
- const traceIndexes = this.model._normalize_trace_indexes(msgData.style_traces);
1198
- style['_doNotReportToPy'] = true;
1199
- Plotly.update(this.el, style, layout, traceIndexes);
1200
- // ### Send trace deltas ###
1201
- // We create an array of deltas corresponding to the updated
1202
- // traces.
1203
- this._sendTraceDeltas(msgData.trace_edit_id);
1204
- // ### Send layout delta ###
1205
- const layout_edit_id = msgData.layout_edit_id;
1206
- this._sendLayoutDelta(layout_edit_id);
1207
- }
1208
- }
1209
- /**
1210
- * Handle Plotly.animate request
1211
- */
1212
- do_animate() {
1213
- /** @type {Py2JsAnimateMsg} */
1214
- const msgData = this.model.get('_py2js_animate');
1215
- if (msgData !== null) {
1216
- // Unpack params
1217
- // var animationData = msgData[0];
1218
- const animationOpts = msgData.animation_opts;
1219
- const styles = msgData.style_data;
1220
- const layout = msgData.layout_data;
1221
- const traceIndexes = this.model._normalize_trace_indexes(msgData.style_traces);
1222
- const animationData = {
1223
- data: styles,
1224
- layout: layout,
1225
- traces: traceIndexes,
1226
- };
1227
- animationData['_doNotReportToPy'] = true;
1228
- const that = this;
1229
- // @ts-ignore
1230
- Plotly.animate(this.el, animationData, animationOpts).then(() => {
1231
- // ### Send trace deltas ###
1232
- // We create an array of deltas corresponding to the
1233
- // animated traces.
1234
- that._sendTraceDeltas(msgData.trace_edit_id);
1235
- // ### Send layout delta ###
1236
- const layout_edit_id = msgData.layout_edit_id;
1237
- that._sendLayoutDelta(layout_edit_id);
1238
- });
1239
- }
1240
- }
1241
- /**
1242
- * Construct layout delta object and send layoutDelta message to the
1243
- * Python side
1244
- *
1245
- * @param layout_edit_id
1246
- * Edit ID of message that triggered the creation of the layout delta
1247
- * @private
1248
- */
1249
- _sendLayoutDelta(layout_edit_id) {
1250
- // ### Handle layout delta ###
1251
- const layout_delta = createDeltaObject(this.getFullLayout(), this.model.get('_layout'));
1252
- /** @type{Js2PyLayoutDeltaMsg} */
1253
- const layoutDeltaMsg = {
1254
- layout_delta: layout_delta,
1255
- layout_edit_id: layout_edit_id,
1256
- };
1257
- this.model.set('_js2py_layoutDelta', layoutDeltaMsg);
1258
- this.touch();
1259
- }
1260
- /**
1261
- * Construct trace deltas array for the requested trace indexes and
1262
- * send traceDeltas message to the Python side
1263
- * Array of indexes of traces for which to compute deltas
1264
- * @param trace_edit_id
1265
- * Edit ID of message that triggered the creation of trace deltas
1266
- * @private
1267
- */
1268
- _sendTraceDeltas(trace_edit_id) {
1269
- const trace_data = this.model.get('_data');
1270
- const traceIndexes = _.range(trace_data.length);
1271
- const trace_deltas = new Array(traceIndexes.length);
1272
- const fullData = this.getFullData();
1273
- for (let i = 0; i < traceIndexes.length; i++) {
1274
- const traceInd = traceIndexes[i];
1275
- trace_deltas[i] = createDeltaObject(fullData[traceInd], trace_data[traceInd]);
1276
- }
1277
- /** @type{Js2PyTraceDeltasMsg} */
1278
- const traceDeltasMsg = {
1279
- trace_deltas: trace_deltas,
1280
- trace_edit_id: trace_edit_id,
1281
- };
1282
- this.model.set('_js2py_traceDeltas', traceDeltasMsg);
1283
- this.touch();
1284
- }
1285
- }
1286
- // Serialization
1287
- /**
1288
- * Create a mapping from numpy dtype strings to corresponding typed array
1289
- * constructors
1290
- */
1291
- const numpy_dtype_to_typedarray_type = {
1292
- int8: Int8Array,
1293
- int16: Int16Array,
1294
- int32: Int32Array,
1295
- uint8: Uint8Array,
1296
- uint16: Uint16Array,
1297
- uint32: Uint32Array,
1298
- float32: Float32Array,
1299
- float64: Float64Array,
1300
- };
1301
- function serializeTypedArray(v) {
1302
- let numpyType;
1303
- if (v instanceof Int8Array) {
1304
- numpyType = 'int8';
1305
- }
1306
- else if (v instanceof Int16Array) {
1307
- numpyType = 'int16';
1308
- }
1309
- else if (v instanceof Int32Array) {
1310
- numpyType = 'int32';
1311
- }
1312
- else if (v instanceof Uint8Array) {
1313
- numpyType = 'uint8';
1314
- }
1315
- else if (v instanceof Uint16Array) {
1316
- numpyType = 'uint16';
1317
- }
1318
- else if (v instanceof Uint32Array) {
1319
- numpyType = 'uint32';
1320
- }
1321
- else if (v instanceof Float32Array) {
1322
- numpyType = 'float32';
1323
- }
1324
- else if (v instanceof Float64Array) {
1325
- numpyType = 'float64';
1326
- }
1327
- else {
1328
- // Don't understand it, return as is
1329
- return v;
1330
- }
1331
- const res = {
1332
- dtype: numpyType,
1333
- shape: [v.length],
1334
- value: v.buffer,
1335
- };
1336
- return res;
1337
- }
1338
- /**
1339
- * ipywidget JavaScript -> Python serializer
1340
- */
1341
- function js2py_serializer(v, widgetManager) {
1342
- let res;
1343
- if (_.isTypedArray(v)) {
1344
- res = serializeTypedArray(v);
1345
- }
1346
- else if (Array.isArray(v)) {
1347
- // Serialize array elements recursively
1348
- res = new Array(v.length);
1349
- for (let i = 0; i < v.length; i++) {
1350
- res[i] = js2py_serializer(v[i]);
1351
- }
1352
- }
1353
- else if (_.isPlainObject(v)) {
1354
- // Serialize object properties recursively
1355
- res = {};
1356
- for (const p in v) {
1357
- if (v.hasOwnProperty(p)) {
1358
- res[p] = js2py_serializer(v[p]);
1359
- }
1360
- }
1361
- }
1362
- else if (v === undefined) {
1363
- // Translate undefined into '_undefined_' sentinal string. The
1364
- // Python _js_to_py deserializer will convert this into an
1365
- // Undefined object
1366
- res = '_undefined_';
1367
- }
1368
- else {
1369
- // Primitive value to transfer directly
1370
- res = v;
1371
- }
1372
- return res;
1373
- }
1374
- /**
1375
- * ipywidget Python -> Javascript deserializer
1376
- */
1377
- function py2js_deserializer(v, widgetManager) {
1378
- let res;
1379
- if (Array.isArray(v)) {
1380
- // Deserialize array elements recursively
1381
- res = new Array(v.length);
1382
- for (let i = 0; i < v.length; i++) {
1383
- res[i] = py2js_deserializer(v[i]);
1384
- }
1385
- }
1386
- else if (_.isPlainObject(v)) {
1387
- if ((_.has(v, 'value') || _.has(v, 'buffer')) &&
1388
- _.has(v, 'dtype') &&
1389
- _.has(v, 'shape')) {
1390
- // Deserialize special buffer/dtype/shape objects into typed arrays
1391
- // These objects correspond to numpy arrays on the Python side
1392
- //
1393
- // Note plotly.py<=3.1.1 called the buffer object `buffer`
1394
- // This was renamed `value` in 3.2 to work around a naming conflict
1395
- // when saving widget state to a notebook.
1396
- // @ts-ignore
1397
- const typedarray_type = numpy_dtype_to_typedarray_type[v.dtype];
1398
- const buffer = _.has(v, 'value') ? v.value.buffer : v.buffer.buffer;
1399
- res = new typedarray_type(buffer);
1400
- }
1401
- else {
1402
- // Deserialize object properties recursively
1403
- res = {};
1404
- for (const p in v) {
1405
- if (v.hasOwnProperty(p)) {
1406
- res[p] = py2js_deserializer(v[p]);
1407
- }
1408
- }
1409
- }
1410
- }
1411
- else if (v === '_undefined_') {
1412
- // Convert the _undefined_ sentinal into undefined
1413
- res = undefined;
1414
- }
1415
- else {
1416
- // Accept primitive value directly
1417
- res = v;
1418
- }
1419
- return res;
1420
- }
1421
- /**
1422
- * Return whether the input value is a typed array
1423
- * @param potentialTypedArray
1424
- * Value to examine
1425
- * @returns {boolean}
1426
- */
1427
- function isTypedArray(potentialTypedArray) {
1428
- return (ArrayBuffer.isView(potentialTypedArray) &&
1429
- !(potentialTypedArray instanceof DataView));
1430
- }
1431
- /**
1432
- * Customizer for use with lodash's mergeWith function
1433
- *
1434
- * The customizer ensures that typed arrays are not converted into standard
1435
- * arrays during the recursive merge
1436
- *
1437
- * See: https://lodash.com/docs/latest#mergeWith
1438
- */
1439
- function fullMergeCustomizer(objValue, srcValue, key) {
1440
- if (key[0] === '_') {
1441
- // Don't recurse into private properties
1442
- return null;
1443
- }
1444
- else if (isTypedArray(srcValue)) {
1445
- // Return typed arrays directly, don't recurse inside
1446
- return srcValue;
1447
- }
1448
- }
1449
- /**
1450
- * Reform a Plotly.relayout like operation on an input object
1451
- *
1452
- * @param {Object} parentObj
1453
- * The object that the relayout operation should be applied to
1454
- * @param {Object} relayoutData
1455
- * An relayout object as accepted by Plotly.relayout
1456
- *
1457
- * Examples:
1458
- * var d = {foo {bar [5, 10]}};
1459
- * performRelayoutLike(d, {'foo.bar': [0, 1]});
1460
- * d -> {foo: {bar: [0, 1]}}
1461
- *
1462
- * var d = {foo {bar [5, 10]}};
1463
- * performRelayoutLike(d, {'baz': 34});
1464
- * d -> {foo: {bar: [5, 10]}, baz: 34}
1465
- *
1466
- * var d = {foo: {bar: [5, 10]};
1467
- * performRelayoutLike(d, {'foo.baz[1]': 17});
1468
- * d -> {foo: {bar: [5, 17]}}
1469
- *
1470
- */
1471
- function performRelayoutLike(parentObj, relayoutData) {
1472
- // Perform a relayout style operation on a given parent object
1473
- for (const rawKey in relayoutData) {
1474
- if (!relayoutData.hasOwnProperty(rawKey)) {
1475
- continue;
1476
- }
1477
- // Extract value for this key
1478
- const relayoutVal = relayoutData[rawKey];
1479
- // Set property value
1480
- if (relayoutVal === null) {
1481
- _.unset(parentObj, rawKey);
1482
- }
1483
- else {
1484
- _.set(parentObj, rawKey, relayoutVal);
1485
- }
1486
- }
1487
- }
1488
- /**
1489
- * Perform a Plotly.restyle like operation on an input object array
1490
- *
1491
- * @param {Array.<Object>} parentArray
1492
- * The object that the restyle operation should be applied to
1493
- * @param {Object} restyleData
1494
- * A restyle object as accepted by Plotly.restyle
1495
- * @param {Array.<Number>} restyleTraces
1496
- * Array of indexes of the traces that the resytle operation applies to
1497
- *
1498
- * Examples:
1499
- * var d = [{foo: {bar: 1}}, {}, {}]
1500
- * performRestyleLike(d, {'foo.bar': 2}, [0])
1501
- * d -> [{foo: {bar: 2}}, {}, {}]
1502
- *
1503
- * var d = [{foo: {bar: 1}}, {}, {}]
1504
- * performRestyleLike(d, {'foo.bar': 2}, [0, 1, 2])
1505
- * d -> [{foo: {bar: 2}}, {foo: {bar: 2}}, {foo: {bar: 2}}]
1506
- *
1507
- * var d = [{foo: {bar: 1}}, {}, {}]
1508
- * performRestyleLike(d, {'foo.bar': [2, 3, 4]}, [0, 1, 2])
1509
- * d -> [{foo: {bar: 2}}, {foo: {bar: 3}}, {foo: {bar: 4}}]
1510
- *
1511
- */
1512
- function performRestyleLike(parentArray, restyleData, restyleTraces) {
1513
- // Loop over the properties of restyleData
1514
- for (const rawKey in restyleData) {
1515
- if (!restyleData.hasOwnProperty(rawKey)) {
1516
- continue;
1517
- }
1518
- // Extract value for property and normalize into a value list
1519
- let valArray = restyleData[rawKey];
1520
- if (!Array.isArray(valArray)) {
1521
- valArray = [valArray];
1522
- }
1523
- // Loop over the indexes of the traces being restyled
1524
- for (let i = 0; i < restyleTraces.length; i++) {
1525
- // Get trace object
1526
- const traceInd = restyleTraces[i];
1527
- const trace = parentArray[traceInd];
1528
- // Extract value for this trace
1529
- const singleVal = valArray[i % valArray.length];
1530
- // Set property value
1531
- if (singleVal === null) {
1532
- _.unset(trace, rawKey);
1533
- }
1534
- else if (singleVal !== undefined) {
1535
- _.set(trace, rawKey, singleVal);
1536
- }
1537
- }
1538
- }
1539
- }
1540
- /**
1541
- * Perform a Plotly.moveTraces like operation on an input object array
1542
- * @param parentArray
1543
- * The object that the moveTraces operation should be applied to
1544
- * @param currentInds
1545
- * Array of the current indexes of traces to be moved
1546
- * @param newInds
1547
- * Array of the new indexes that traces selected by currentInds should be
1548
- * moved to.
1549
- *
1550
- * Examples:
1551
- * var d = [{foo: 0}, {foo: 1}, {foo: 2}]
1552
- * performMoveTracesLike(d, [0, 1], [2, 0])
1553
- * d -> [{foo: 1}, {foo: 2}, {foo: 0}]
1554
- *
1555
- * var d = [{foo: 0}, {foo: 1}, {foo: 2}]
1556
- * performMoveTracesLike(d, [0, 2], [1, 2])
1557
- * d -> [{foo: 1}, {foo: 0}, {foo: 2}]
1558
- */
1559
- function performMoveTracesLike(parentArray, currentInds, newInds) {
1560
- // ### Remove by currentInds in reverse order ###
1561
- let movingTracesData = [];
1562
- for (let ci = currentInds.length - 1; ci >= 0; ci--) {
1563
- // Insert moving parentArray at beginning of the list
1564
- movingTracesData.splice(0, 0, parentArray[currentInds[ci]]);
1565
- parentArray.splice(currentInds[ci], 1);
1566
- }
1567
- // ### Sort newInds and movingTracesData by newInds ###
1568
- const newIndexSortedArrays = _(newInds)
1569
- .zip(movingTracesData)
1570
- .sortBy(0)
1571
- .unzip()
1572
- .value();
1573
- newInds = newIndexSortedArrays[0];
1574
- movingTracesData = newIndexSortedArrays[1];
1575
- // ### Insert by newInds in forward order ###
1576
- for (let ni = 0; ni < newInds.length; ni++) {
1577
- parentArray.splice(newInds[ni], 0, movingTracesData[ni]);
1578
- }
1579
- }
1580
- /**
1581
- * Remove nested properties from a parent object
1582
- * @param {Object} parentObj
1583
- * Parent object from which properties or nested properties should be removed
1584
- * @param {Array.<Array.<Number|String>>} keyPaths
1585
- * Array of key paths for properties that should be removed. Each key path
1586
- * is an array of properties names or array indexes that reference a
1587
- * property to be removed
1588
- *
1589
- * Examples:
1590
- * var d = {foo: [{bar: 0}, {bar: 1}], baz: 32}
1591
- * performRemoveProps(d, ['baz'])
1592
- * d -> {foo: [{bar: 0}, {bar: 1}]}
1593
- *
1594
- * var d = {foo: [{bar: 0}, {bar: 1}], baz: 32}
1595
- * performRemoveProps(d, ['foo[1].bar', 'baz'])
1596
- * d -> {foo: [{bar: 0}, {}]}
1597
- *
1598
- */
1599
- function performRemoveProps(parentObj, keyPaths) {
1600
- for (let i = 0; i < keyPaths.length; i++) {
1601
- const keyPath = keyPaths[i];
1602
- _.unset(parentObj, keyPath);
1603
- }
1604
- }
1605
- /**
1606
- * Return object that contains all properties in fullObj that are not
1607
- * identical to the corresponding properties in removeObj
1608
- *
1609
- * Properties of fullObj and removeObj may be objects or arrays of objects
1610
- *
1611
- * Returned object is a deep clone of the properties of the input objects
1612
- *
1613
- * @param {Object} fullObj
1614
- * @param {Object} removeObj
1615
- *
1616
- * Examples:
1617
- * var fullD = {foo: [{bar: 0}, {bar: 1}], baz: 32}
1618
- * var removeD = {baz: 32}
1619
- * createDeltaObject(fullD, removeD)
1620
- * -> {foo: [{bar: 0}, {bar: 1}]}
1621
- *
1622
- * var fullD = {foo: [{bar: 0}, {bar: 1}], baz: 32}
1623
- * var removeD = {baz: 45}
1624
- * createDeltaObject(fullD, removeD)
1625
- * -> {foo: [{bar: 0}, {bar: 1}], baz: 32}
1626
- *
1627
- * var fullD = {foo: [{bar: 0}, {bar: 1}], baz: 32}
1628
- * var removeD = {foo: [{bar: 0}, {bar: 1}]}
1629
- * createDeltaObject(fullD, removeD)
1630
- * -> {baz: 32}
1631
- *
1632
- */
1633
- function createDeltaObject(fullObj, removeObj) {
1634
- // Initialize result as object or array
1635
- let res;
1636
- if (Array.isArray(fullObj)) {
1637
- res = new Array(fullObj.length);
1638
- }
1639
- else {
1640
- res = {};
1641
- }
1642
- // Initialize removeObj to empty object if not specified
1643
- if (removeObj === null || removeObj === undefined) {
1644
- removeObj = {};
1645
- }
1646
- // Iterate over object properties or array indices
1647
- for (const p in fullObj) {
1648
- if (p[0] !== '_' && // Don't consider private properties
1649
- fullObj.hasOwnProperty(p) && // Exclude parent properties
1650
- fullObj[p] !== null // Exclude cases where fullObj doesn't
1651
- // have the property
1652
- ) {
1653
- // Compute object equality
1654
- var props_equal;
1655
- props_equal = _.isEqual(fullObj[p], removeObj[p]);
1656
- // Perform recursive comparison if props are not equal
1657
- if (!props_equal || p === 'uid') {
1658
- // Let uids through
1659
- // property has non-null value in fullObj that doesn't
1660
- // match the value in removeObj
1661
- const fullVal = fullObj[p];
1662
- if (removeObj.hasOwnProperty(p) && typeof fullVal === 'object') {
1663
- // Recurse over object properties
1664
- if (Array.isArray(fullVal)) {
1665
- if (fullVal.length > 0 && typeof fullVal[0] === 'object') {
1666
- // We have an object array
1667
- res[p] = new Array(fullVal.length);
1668
- for (let i = 0; i < fullVal.length; i++) {
1669
- if (!Array.isArray(removeObj[p]) || removeObj[p].length <= i) {
1670
- res[p][i] = fullVal[i];
1671
- }
1672
- else {
1673
- res[p][i] = createDeltaObject(fullVal[i], removeObj[p][i]);
1674
- }
1675
- }
1676
- }
1677
- else {
1678
- // We have a primitive array or typed array
1679
- res[p] = fullVal;
1680
- }
1681
- }
1682
- else {
1683
- // object
1684
- const full_obj = createDeltaObject(fullVal, removeObj[p]);
1685
- if (Object.keys(full_obj).length > 0) {
1686
- // new object is not empty
1687
- res[p] = full_obj;
1688
- }
1689
- }
1690
- }
1691
- else if (typeof fullVal === 'object' && !Array.isArray(fullVal)) {
1692
- // Return 'clone' of fullVal
1693
- // We don't use a standard clone method so that we keep
1694
- // the special case handling of this method
1695
- res[p] = createDeltaObject(fullVal, {});
1696
- }
1697
- else if (fullVal !== undefined && typeof fullVal !== 'function') {
1698
- // No recursion necessary, Just keep value from fullObj.
1699
- // But skip values with function type
1700
- res[p] = fullVal;
1701
- }
1702
- }
1703
- }
1704
- }
1705
- return res;
1706
- }
1707
- function randstr(existing, bits, base, _recursion) {
1708
- if (!base) {
1709
- base = 16;
1710
- }
1711
- if (bits === undefined) {
1712
- bits = 24;
1713
- }
1714
- if (bits <= 0) {
1715
- return '0';
1716
- }
1717
- let digits = Math.log(Math.pow(2, bits)) / Math.log(base);
1718
- let res = '';
1719
- let i, b, x;
1720
- for (i = 2; digits === Infinity; i *= 2) {
1721
- digits = (Math.log(Math.pow(2, bits / i)) / Math.log(base)) * i;
1722
- }
1723
- const rem = digits - Math.floor(digits);
1724
- for (i = 0; i < Math.floor(digits); i++) {
1725
- x = Math.floor(Math.random() * base).toString(base);
1726
- res = x + res;
1727
- }
1728
- if (rem) {
1729
- b = Math.pow(base, rem);
1730
- x = Math.floor(Math.random() * b).toString(base);
1731
- res = x + res;
1732
- }
1733
- const parsed = parseInt(res, base);
1734
- if ((existing && existing[res]) ||
1735
- (parsed !== Infinity && parsed >= Math.pow(2, bits))) {
1736
- if (_recursion > 10) {
1737
- console.warn('randstr failed uniqueness');
1738
- return res;
1739
- }
1740
- return randstr(existing, bits, base, (_recursion || 0) + 1);
1741
- }
1742
- else {
1743
- return res;
1744
- }
1745
- }
1746
- //# sourceMappingURL=Figure.js.map