@insync-stageplayer/plotly-chart 0.5.31 → 0.5.35-beta.1

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 (37) hide show
  1. package/lib/PlotlyGraph/EventMenu.d.ts +13 -0
  2. package/lib/PlotlyGraph/EventMenu.d.ts.map +1 -0
  3. package/lib/PlotlyGraph/EventMenu.js +84 -0
  4. package/lib/PlotlyGraph/EventMenu.js.map +1 -0
  5. package/lib/PlotlyGraph/MarkerTracks.d.ts +17 -0
  6. package/lib/PlotlyGraph/MarkerTracks.d.ts.map +1 -0
  7. package/lib/PlotlyGraph/MarkerTracks.js +248 -0
  8. package/lib/PlotlyGraph/MarkerTracks.js.map +1 -0
  9. package/lib/PlotlyGraph/PlotlyLiveview.d.ts +4 -4
  10. package/lib/PlotlyGraph/PlotlyLiveview.js +297 -297
  11. package/lib/PlotlyGraph/PlotlyPlayback.d.ts +4 -4
  12. package/lib/PlotlyGraph/PlotlyPlayback.d.ts.map +1 -1
  13. package/lib/PlotlyGraph/PlotlyPlayback.js +524 -317
  14. package/lib/PlotlyGraph/PlotlyPlayback.js.map +1 -1
  15. package/lib/PlotlyGraph/PlotlyPlaybackOverlay.d.ts +23 -0
  16. package/lib/PlotlyGraph/PlotlyPlaybackOverlay.d.ts.map +1 -0
  17. package/lib/PlotlyGraph/PlotlyPlaybackOverlay.js +188 -0
  18. package/lib/PlotlyGraph/PlotlyPlaybackOverlay.js.map +1 -0
  19. package/lib/PlotlyGraph/index.d.ts +2 -2
  20. package/lib/PlotlyGraph/index.d.ts.map +1 -1
  21. package/lib/PlotlyGraph/index.js +2 -2
  22. package/lib/PlotlyGraph/index.js.map +1 -1
  23. package/lib/index.d.ts +1 -1
  24. package/lib/index.js +1 -1
  25. package/package.json +5 -3
  26. package/lib/Plotly.d.ts +0 -5
  27. package/lib/Plotly.d.ts.map +0 -1
  28. package/lib/Plotly.js +0 -338
  29. package/lib/Plotly.js.map +0 -1
  30. package/lib/PlotlyGraph/Plotly.d.ts +0 -5
  31. package/lib/PlotlyGraph/Plotly.d.ts.map +0 -1
  32. package/lib/PlotlyGraph/Plotly.js +0 -347
  33. package/lib/PlotlyGraph/Plotly.js.map +0 -1
  34. package/lib/PlotlyLiveview.d.ts +0 -5
  35. package/lib/PlotlyLiveview.d.ts.map +0 -1
  36. package/lib/PlotlyLiveview.js +0 -310
  37. package/lib/PlotlyLiveview.js.map +0 -1
@@ -1,28 +1,48 @@
1
- import React, { useCallback, useEffect, useState } from "react";
2
- import Plot from "react-plotly.js";
3
- import PropTypes from "prop-types";
4
- import { withResizeDetector } from "react-resize-detector";
5
- import { useTheme as useSelectedTheme, styled, } from "@insync-stageplayer/ui-components";
6
- const convertMicrosecondstoDateFormat = (inputMicroseconds) => {
7
- let convertedDate = new Date(+inputMicroseconds / 1000);
8
- return adjustWithLocalTimeZone(convertedDate);
9
- };
10
- const adjustWithLocalTimeZone = (date) => {
11
- let adjustedDate = new Date(date.getTime() + date.getTimezoneOffset() * 60 * 1000);
12
- return adjustedDate;
13
- };
14
- const convertDateToMicroseconds = (inputDate) => {
15
- const convertIntoDate = new Date(inputDate);
16
- return (convertIntoDate.getTime() * 1000 -
17
- convertIntoDate.getTimezoneOffset() * 60 * 1000 * 1000);
18
- };
19
- const getMicroSecondStartAndLength = (originalFromDate, originalStopDate, totalTime) => {
20
- const start = convertDateToMicroseconds(originalFromDate);
21
- const end = convertDateToMicroseconds(originalStopDate);
22
- let sLength = end - start;
23
- const length = sLength + (totalTime % 1000) === totalTime ? totalTime : sLength;
24
- return [start, length];
25
- };
1
+ import React, { useCallback, useEffect, useState, useRef } from "react";
2
+ import Plot from "react-plotly.js";
3
+ import PropTypes from "prop-types";
4
+ import { withResizeDetector } from "react-resize-detector";
5
+ import { useTheme as useSelectedTheme, styled, } from "@insync-stageplayer/ui-components";
6
+ const normalizeRange = 1000;
7
+ const convertMicrosecondstoDateFormat = (inputMicroseconds) => {
8
+ let convertedDate = new Date(+inputMicroseconds / 1000);
9
+ return adjustWithLocalTimeZone(convertedDate);
10
+ };
11
+ const adjustWithLocalTimeZone = (date) => {
12
+ let adjustedDate = new Date(date.getTime() + date.getTimezoneOffset() * 60 * 1000);
13
+ return adjustedDate;
14
+ };
15
+ const convertDateToMicroseconds = (inputDate) => {
16
+ const convertIntoDate = new Date(inputDate);
17
+ return (convertIntoDate.getTime() * 1000 -
18
+ convertIntoDate.getTimezoneOffset() * 60 * 1000 * 1000);
19
+ };
20
+ const getMicroSecondStartAndLength = (originalFromDate, originalStopDate, totalTime) => {
21
+ const start = convertDateToMicroseconds(originalFromDate);
22
+ const end = convertDateToMicroseconds(originalStopDate);
23
+ let sLength = end - start;
24
+ const length = sLength + (totalTime % 1000) === totalTime ? totalTime : sLength;
25
+ return [start, length];
26
+ };
27
+ const convertMicrosecondsToDate = (inputTime) => {
28
+ const date = new Date(inputTime / 1000 - 3600 * 1000);
29
+ const pad = function (num) {
30
+ return (num < 10 ? "0" : "") + num;
31
+ };
32
+ return (date.getFullYear() +
33
+ "-" +
34
+ pad(date.getMonth() + 1) +
35
+ "-" +
36
+ pad(date.getDate()) +
37
+ " " +
38
+ pad(date.getHours()) +
39
+ ":" +
40
+ pad(date.getMinutes()) +
41
+ ":" +
42
+ pad(date.getSeconds()) +
43
+ "." +
44
+ pad(date.getMilliseconds()));
45
+ };
26
46
  const StyledPlotly = styled(Plot) `
27
47
  rect.nsewdrag.cursor-ew-resize {
28
48
  cursor: zoom-in;
@@ -35,296 +55,483 @@ const StyledPlotly = styled(Plot) `
35
55
  cursor: grab;
36
56
  pointer-events: none !important;
37
57
  }
38
- `;
39
- export const PlotlyPlayback = withResizeDetector(({ xRange, data, metadata, onStopSelect, onDrag, xScaleTickFormat, scrubbing, yAxisLabel, displaySecondYAxis, displayArrow, totalTime, }) => {
40
- var _a;
41
- const [plotData, setPlotData] = useState([]);
42
- const [nowLine, setNowline] = useState((_a = sessionStorage.getItem("nl")) !== null && _a !== void 0 ? _a : 0);
43
- const [layout, setLayout] = useState(null);
44
- const selectedTheme = useSelectedTheme();
45
- const [yAxisLabelLeft, setYAxisLabelLeft] = useState("");
46
- const [yAxisLabelRight, setYAxisLabelRight] = useState("");
47
- const updateLayout = useCallback(() => {
48
- let annotations = [
49
- {
50
- xref: "paper",
51
- yref: "paper",
52
- x: nowLine,
53
- xanchor: "right",
54
- y: 1,
55
- yanchor: "bottom",
56
- text: "",
57
- showarrow: displayArrow,
58
- arrowhead: 0,
59
- ax: 0,
60
- ay: 1000,
61
- arrowcolor: "red",
62
- },
63
- ];
64
- const convertXRangeFrom = xScaleTickFormat.includes(":")
65
- ? convertMicrosecondstoDateFormat(xRange.from > 0
66
- ? xRange.length === totalTime
67
- ? 0
68
- : xRange.from + xRange.length > totalTime
69
- ? totalTime - xRange.length
70
- : xRange.from
71
- : 0)
72
- : xRange.from / 1e6;
73
- const convertXRangeTo = xScaleTickFormat.includes(":")
74
- ? convertMicrosecondstoDateFormat(xRange.from > 0
75
- ? xRange.length === totalTime
76
- ? xRange.length
77
- : xRange.from + xRange.length > totalTime
78
- ? totalTime
79
- : xRange.from + xRange.length
80
- : xRange.length)
81
- : (xRange.from + xRange.length) / 1e6;
82
- let defaultLayout = {
83
- // this hovermode setting creates a hover-over legend for all the signals at the same time. It also adds a spike line
84
- hovermode: "x unified",
85
- yaxis: {
86
- fixedrange: true,
87
- rangemode: "",
88
- hoverformat: ".2f",
89
- range: [],
90
- title: yAxisLabelLeft,
91
- showline: true,
92
- showticklabels: false,
93
- linewidth: 2,
94
- linecolor: selectedTheme.colors.accent,
95
- tickformat: null,
96
- },
97
- xaxis: {
98
- rangemode: "",
99
- showline: false,
100
- linewidth: 2,
101
- linecolor: selectedTheme.colors.accent,
102
- fixedrange: false,
103
- range: [convertXRangeFrom, convertXRangeTo],
104
- tickformat: xScaleTickFormat.includes(":") ? "%H:%M:%S" : null,
105
- },
106
- showlegend: false,
107
- autosize: true,
108
- margin: { l: 25, r: 10, t: 20, b: 20 },
109
- paper_bgcolor: selectedTheme.colors.background,
110
- plot_bgcolor: selectedTheme.colors.background,
111
- font: { color: selectedTheme.colors.foreground },
112
- };
113
- // Check if a second y-axis needs to be added
114
- if (displaySecondYAxis && metadata.length == 2) {
115
- const yAxis2 = {
116
- fixedrange: true,
117
- title: yAxisLabelRight,
118
- anchor: "x",
119
- overlaying: "y",
120
- side: "right",
121
- };
122
- defaultLayout = Object.assign(Object.assign({}, defaultLayout), { yaxis2: yAxis2 });
123
- }
124
- // when scrubbing plotly is in charge, this to prevent the xRange to update and mess while
125
- // still scrubbing in plotly
126
- if (!scrubbing) {
127
- setLayout(Object.assign(Object.assign({}, defaultLayout), { annotations }));
128
- }
129
- }, [
130
- selectedTheme,
131
- xRange,
132
- xScaleTickFormat,
133
- yAxisLabelLeft,
134
- yAxisLabelRight,
135
- displaySecondYAxis,
136
- displayArrow,
137
- scrubbing,
138
- metadata,
139
- nowLine,
140
- ]);
141
- const onRelayout = (e) => {
142
- if (e["xaxis.range[0]"] && e["xaxis.range[1]"]) {
143
- let selection;
144
- if (xScaleTickFormat.includes(":")) {
145
- const [from, length] = getMicroSecondStartAndLength(e["xaxis.range[0]"], e["xaxis.range[1]"], totalTime);
146
- selection = {
147
- from: from,
148
- length: length,
149
- };
150
- }
151
- else {
152
- selection = {
153
- from: e["xaxis.range[0]"] * 1e6,
154
- length: (e["xaxis.range[1]"] - e["xaxis.range[0]"]) * 1e6,
155
- };
156
- }
157
- onStopSelect({ selection });
158
- }
159
- };
160
- const onRelayouting = (e) => {
161
- if (e["xaxis.range[0]"] && e["xaxis.range[1]"]) {
162
- let seekTime;
163
- if (xScaleTickFormat.includes(":")) {
164
- const [from, length] = getMicroSecondStartAndLength(e["xaxis.range[0]"], e["xaxis.range[1]"], totalTime);
165
- const possibleSeekTime = length * nowLine + from;
166
- seekTime =
167
- possibleSeekTime > totalTime
168
- ? totalTime
169
- : possibleSeekTime < 0
170
- ? 0
171
- : possibleSeekTime;
172
- }
173
- else {
174
- seekTime =
175
- ((e["xaxis.range[1]"] - e["xaxis.range[0]"]) / 2 +
176
- e["xaxis.range[0]"]) *
177
- 1e6;
178
- }
179
- onDrag(seekTime);
180
- }
181
- };
182
- useEffect(() => {
183
- updateLayout();
184
- const plotDataArr = [];
185
- data.forEach((signal, idx) => {
186
- const x = signal.map((item) => {
187
- if (xScaleTickFormat.includes(":")) {
188
- const xDateFormatted = convertMicrosecondstoDateFormat(+item.x);
189
- return xDateFormatted;
190
- }
191
- else {
192
- const xDateFormatted = +item.x / 1e6;
193
- return xDateFormatted;
194
- }
195
- });
196
- const yVal = signal.map((item) => {
197
- return item.y;
198
- });
199
- const { min, max } = metadata[idx];
200
- const range = max - min;
201
- const y = yVal.map((value) => {
202
- return (value - min) / range;
203
- });
204
- const signalData = {
205
- x,
206
- y,
207
- hovertemplate: "%{customdata:.2f}",
208
- customdata: yVal.map((point) => `${point}`),
209
- type: "scattergl",
210
- mode: metadata[idx].frequency * (xRange.length / 1000 / 1000) > 250
211
- ? "lines"
212
- : "lines+markers",
213
- name: metadata[idx].name,
214
- marker: {
215
- color: metadata[idx].color,
216
- size: 5,
217
- line: {
218
- color: metadata[idx].color,
219
- width: 1,
220
- },
221
- },
222
- };
223
- plotDataArr.push(signalData);
224
- });
225
- // Check if a second y-axis needs to be added
226
- if (displaySecondYAxis && metadata.length == 2) {
227
- plotDataArr[1] = Object.assign(Object.assign({}, plotDataArr[1]), { yaxis: "y2" });
228
- }
229
- // Setting y-axis labels if needed and meeting the requirements
230
- if (metadata.length == 1) {
231
- setYAxisLabelLeft(yAxisLabel ? metadata[0].name : "");
232
- }
233
- else if (metadata.length == 2 && displaySecondYAxis) {
234
- setYAxisLabelLeft(yAxisLabel ? metadata[0].name : "");
235
- setYAxisLabelRight(yAxisLabel ? metadata[1].name : "");
236
- }
237
- else {
238
- setYAxisLabelLeft("");
239
- setYAxisLabelRight("");
240
- }
241
- setPlotData(plotDataArr);
242
- }, [
243
- data,
244
- metadata,
245
- updateLayout,
246
- xScaleTickFormat,
247
- xRange,
248
- scrubbing,
249
- yAxisLabel,
250
- displaySecondYAxis,
251
- ]);
252
- useEffect(() => {
253
- if (!scrubbing) {
254
- if (xRange.from < 0) {
255
- let d = (xRange.length / 2 + xRange.from) / xRange.length;
256
- setNowline(d);
257
- }
258
- else {
259
- if (xRange.length === totalTime) {
260
- setNowline(0.5 + xRange.from / totalTime);
261
- }
262
- else if (xRange.from + xRange.length > totalTime) {
263
- setNowline(0.5 + (xRange.from - (totalTime - xRange.length)) / xRange.length);
264
- }
265
- else {
266
- setNowline(0.5);
267
- }
268
- }
269
- }
270
- sessionStorage.setItem("nl", nowLine);
271
- }, [plotData, xRange]);
272
- const shouldNormalize = React.useMemo(() => {
273
- const units = metadata.reduce((acc, curr) => {
274
- if (curr != undefined && acc.indexOf(curr === null || curr === void 0 ? void 0 : curr.unit) < 0) {
275
- return [...acc, curr.unit];
276
- }
277
- return acc;
278
- }, []);
279
- return units.length > 1;
280
- }, [metadata]);
281
- return (React.createElement(StyledPlotly, { useResizeHandler: true, className: "plot-div", data: plotData, layout: layout, style: { width: "100%", height: "100%" }, config: {
282
- displaylogo: false,
283
- displayModeBar: false,
284
- responsive: true,
285
- }, onRelayout: (e) => onRelayout(e), onRelayouting: (e) => onRelayouting(e) }));
286
- });
287
- PlotlyPlayback.defaultProps = {
288
- xRange: null,
289
- data: [],
290
- metadata: [],
291
- onStopSelect: () => { },
292
- onDrag: () => { },
293
- xScaleTickFormat: "SS",
294
- scrubbing: false,
295
- yAxisLabel: false,
296
- displaySecondYAxis: false,
297
- displayArrow: true,
298
- selectedTheme: null,
299
- totalTime: null,
300
- };
301
- const dataElement = PropTypes.shape({
302
- x: PropTypes.number,
303
- y: PropTypes.number,
304
- });
305
- const metadataElement = PropTypes.shape({
306
- name: PropTypes.string,
307
- color: PropTypes.string,
308
- });
309
- PlotlyPlayback.propTypes = {
310
- xRange: PropTypes.shape({
311
- from: PropTypes.number,
312
- length: PropTypes.number,
313
- }),
314
- data: PropTypes.arrayOf(PropTypes.arrayOf(dataElement), PropTypes.arrayOf(dataElement)),
315
- metadata: PropTypes.arrayOf(metadataElement),
316
- onStopSelect: PropTypes.func,
317
- onDrag: PropTypes.func,
318
- xScaleTickFormat: PropTypes.string,
319
- scrubbing: PropTypes.bool,
320
- yAxisLabel: PropTypes.bool,
321
- displaySecondYAxis: PropTypes.bool,
322
- displayArrow: PropTypes.bool,
323
- selectedTheme: PropTypes.shape({
324
- colors: PropTypes.shape({
325
- background: PropTypes.string,
326
- }),
327
- }),
328
- totalTime: PropTypes.number,
329
- };
58
+ `;
59
+ export const PlotlyPlayback = withResizeDetector(({ xRange, data, metadata, onStopSelect, onDrag, xScaleTickFormat, scrubbing, yAxisLabel, displaySecondYAxis, displayArrow, totalTime, startPositionCurrentTimeIndicator, onRenderedWidth, onRenderedLeft, onRenderedTop, onRenderedHeight, onRenderedScrubbar, percentageHeight, }) => {
60
+ var _a;
61
+ const [plotData, setPlotData] = useState([]);
62
+ const [nowLine, setNowline] = useState((_a = sessionStorage.getItem("nl")) !== null && _a !== void 0 ? _a : 0);
63
+ const [layout, setLayout] = useState(null);
64
+ const selectedTheme = useSelectedTheme();
65
+ const [yAxisLabelLeft, setYAxisLabelLeft] = useState("");
66
+ const [yAxisLabelRight, setYAxisLabelRight] = useState("");
67
+ const [yAxisLabelLeftColor, setYAxisLabelLeftColor] = useState("");
68
+ const [yAxisLabelRightColor, setYAxisLabelRightColor] = useState("");
69
+ const plotlyRef = useRef();
70
+ const updateLayout = useCallback(() => {
71
+ // let plotlytimeindicator = [
72
+ const nowline_color = "rgba(255, 0, 0, 1)";
73
+ const annotations = [
74
+ {
75
+ xref: "paper",
76
+ yref: "paper",
77
+ x: nowLine,
78
+ xanchor: "right",
79
+ y: 1,
80
+ yanchor: "bottom",
81
+ text: "",
82
+ showarrow: displayArrow,
83
+ arrowhead: 0,
84
+ ax: 0,
85
+ ay: 4000,
86
+ arrowcolor: nowline_color,
87
+ },
88
+ ];
89
+ const shapes = [
90
+ {
91
+ type: "rect",
92
+ xref: "paper",
93
+ yref: "paper",
94
+ layer: "below",
95
+ x0: 0,
96
+ y0: 0,
97
+ x1: 1,
98
+ y1: -0.19,
99
+ fillcolor: selectedTheme.colors.scrubBarColor,
100
+ line: {
101
+ color: selectedTheme.colors.scrubBarColor,
102
+ },
103
+ },
104
+ ];
105
+ const convertXRangeFrom = xScaleTickFormat.includes(":")
106
+ ? convertMicrosecondstoDateFormat(xRange.from > 0 || selectedTheme.name !== "noldus"
107
+ ? xRange.length === totalTime
108
+ ? xRange.from
109
+ : xRange.from + xRange.length > totalTime &&
110
+ selectedTheme.name === "noldus"
111
+ ? totalTime - xRange.length
112
+ : xRange.from
113
+ : 0)
114
+ : xRange.from / 1e6;
115
+ const convertXRangeTo = xScaleTickFormat.includes(":")
116
+ ? convertMicrosecondstoDateFormat(xRange.from > 0 || selectedTheme.name !== "noldus"
117
+ ? xRange.length === totalTime
118
+ ? xRange.length + xRange.from
119
+ : xRange.from + xRange.length > totalTime &&
120
+ selectedTheme.name === "noldus"
121
+ ? totalTime
122
+ : xRange.from + xRange.length
123
+ : xRange.length)
124
+ : (xRange.from + xRange.length) / 1e6;
125
+ const minY = yScaleRange[0].min;
126
+ const maxY = yScaleRange[0].max;
127
+ // only add y label when set, otherwise will give a lot of extra margin
128
+ const yAxisWithLabel = yAxisLabel && yAxisLabelLeft !== ""
129
+ ? {
130
+ title: yAxisLabelLeft,
131
+ automargin: "width",
132
+ titlefont: {
133
+ color: yAxisLabelLeftColor,
134
+ },
135
+ }
136
+ : {
137
+ // a bit of margin for the y axis otherwise
138
+ anchor: "free",
139
+ shift: 5,
140
+ };
141
+ const sharedYaxis = Object.assign(Object.assign({}, yAxisWithLabel), { fixedrange: true, rangemode: "", showline: true, showticklabels: selectedTheme.name === "noldus" ? false : true, linewidth: 2, linecolor: selectedTheme.colors.accent, tickformat: null });
142
+ // Noldus doesn't need the precision, where MARIN wants it to be as precise as possible
143
+ // MARIN wants it's own normalization when working with different units (e.g kn and m)
144
+ const yAxis = selectedTheme.name === "noldus"
145
+ ? Object.assign(Object.assign({}, sharedYaxis), { hoverformat: ".2f" }) : Object.assign(Object.assign({}, sharedYaxis), { range: [minY, maxY], showline: shouldNormalize ? false : true, showticklabels: shouldNormalize ? false : true });
146
+ let defaultLayout = {
147
+ // this hovermode setting creates a hover-over legend for all the signals at the same time. It also adds a spike line
148
+ hovermode: "x unified",
149
+ yaxis: yAxis,
150
+ xaxis: {
151
+ rangemode: "",
152
+ showline: true,
153
+ linewidth: 2,
154
+ linecolor: selectedTheme.colors.accent,
155
+ fixedrange: false,
156
+ range: [convertXRangeFrom, convertXRangeTo],
157
+ tickformat: xScaleTickFormat.includes(":") ? "%H:%M:%S" : null,
158
+ // dtick: 5, // removing this allows for automatic per zoom level
159
+ ticks: "outside top",
160
+ ticklen: 15,
161
+ tickcolor: selectedTheme.colors.dtickColor,
162
+ ticklabelposition: "inside",
163
+ minor: {
164
+ ticks: "inside top",
165
+ ticklen: 9,
166
+ tickmode: "auto",
167
+ nticks: 10,
168
+ tickcolor: selectedTheme.colors.dtickMinorColor,
169
+ },
170
+ },
171
+ showlegend: false,
172
+ autosize: true,
173
+ margin: { l: 25, r: 10, t: 20, b: 20 },
174
+ paper_bgcolor: selectedTheme.colors.background,
175
+ plot_bgcolor: selectedTheme.colors.background,
176
+ font: { color: selectedTheme.colors.foreground },
177
+ };
178
+ // only add y label when set, setting it empty otherwise will give extra margin
179
+ const yAxis2WithLabel = yAxisLabel && yAxisLabelRight !== ""
180
+ ? {
181
+ title: yAxisLabelRight,
182
+ titlefont: {
183
+ color: yAxisLabelRightColor,
184
+ },
185
+ }
186
+ : {};
187
+ // Check if a second y-axis needs to be added
188
+ if (displaySecondYAxis && metadata.length == 2) {
189
+ const yAxis2 = Object.assign(Object.assign({}, yAxis2WithLabel), { fixedrange: true, range: [yScaleRange[1].min, yScaleRange[1].max], anchor: "x", overlaying: "y", side: "right", automargin: "width" });
190
+ defaultLayout = Object.assign(Object.assign({}, defaultLayout), { yaxis2: yAxis2 });
191
+ }
192
+ // when scrubbing plotly is in charge, this to prevent the xRange to update and mess while
193
+ // still scrubbing in plotly
194
+ if (!scrubbing) {
195
+ setLayout(Object.assign(Object.assign({}, defaultLayout), { shapes, annotations }));
196
+ }
197
+ }, [
198
+ selectedTheme,
199
+ xRange,
200
+ xScaleTickFormat,
201
+ yAxisLabel,
202
+ yAxisLabelLeft,
203
+ yAxisLabelRight,
204
+ displaySecondYAxis,
205
+ displayArrow,
206
+ scrubbing,
207
+ metadata,
208
+ nowLine,
209
+ totalTime,
210
+ yAxisLabelLeftColor,
211
+ yAxisLabelRightColor,
212
+ ]);
213
+ const onRelayout = (e) => {
214
+ if (e["xaxis.range[0]"] && e["xaxis.range[1]"]) {
215
+ let selection;
216
+ if (xScaleTickFormat.includes(":")) {
217
+ const [from, length] = getMicroSecondStartAndLength(e["xaxis.range[0]"], e["xaxis.range[1]"], totalTime);
218
+ selection = {
219
+ from: from,
220
+ length: length,
221
+ };
222
+ }
223
+ else {
224
+ selection = {
225
+ from: e["xaxis.range[0]"] * 1e6,
226
+ length: (e["xaxis.range[1]"] - e["xaxis.range[0]"]) * 1e6,
227
+ };
228
+ }
229
+ if (selectedTheme.name === "noldus") {
230
+ const [from, length] = getMicroSecondStartAndLength(e["xaxis.range[0]"], e["xaxis.range[1]"], totalTime);
231
+ // fix if we go below zero time
232
+ if (selection.from < 0) {
233
+ selection.from = 0; // daniel fix 3 mar 2025
234
+ sessionStorage.setItem("startSelection", 0);
235
+ sessionStorage.setItem("endSelection", (e["xaxis.range[1]"] - e["xaxis.range[0]"]) * 1e6); // no date format error?
236
+ }
237
+ // fix if we go over end time
238
+ // console.log("DANIEL 1 totaltime="+totalTime+" RE="+e["xaxis.range[1]"]+" NL="+(from+length));
239
+ if (from + length > totalTime) {
240
+ var newenddate = convertMicrosecondsToDate(totalTime);
241
+ var newstartdate = convertMicrosecondsToDate(totalTime - length);
242
+ //console.log("DANIEL 3 out of range fix new start="+newstartdate+" new end="+newenddate+" len="+length);
243
+ sessionStorage.setItem("endSelection", newenddate);
244
+ sessionStorage.setItem("startSelection", newstartdate);
245
+ }
246
+ }
247
+ onStopSelect({ selection });
248
+ }
249
+ };
250
+ const onRelayouting = (e) => {
251
+ if (e["xaxis.range[0]"] && e["xaxis.range[1]"]) {
252
+ let seekTime;
253
+ if (xScaleTickFormat.includes(":")) {
254
+ const [from, length] = getMicroSecondStartAndLength(e["xaxis.range[0]"], e["xaxis.range[1]"], totalTime);
255
+ if (startPositionCurrentTimeIndicator === "0") {
256
+ if (selectedTheme.name === "noldus") {
257
+ // noldus below zero fix
258
+ if (from < 0) {
259
+ // daniel fix 19feb2025
260
+ sessionStorage.setItem("startSelection", convertMicrosecondsToDate(0));
261
+ sessionStorage.setItem("endSelection", convertMicrosecondsToDate(length));
262
+ onDrag(0);
263
+ }
264
+ else {
265
+ sessionStorage.setItem("startSelection", convertMicrosecondsToDate(from));
266
+ sessionStorage.setItem("endSelection", convertMicrosecondsToDate(from + length));
267
+ onDrag(from);
268
+ }
269
+ // noldus out of end range fix
270
+ if (from + length > totalTime) {
271
+ sessionStorage.setItem("endSelection", convertMicrosecondsToDate(totalTime));
272
+ sessionStorage.setItem("startSelection", convertMicrosecondsToDate(totalTime - length));
273
+ //onDrag(totalTime-length);
274
+ }
275
+ }
276
+ }
277
+ else {
278
+ const possibleSeekTime = length * nowLine + from;
279
+ seekTime =
280
+ possibleSeekTime > totalTime
281
+ ? totalTime
282
+ : possibleSeekTime < 0
283
+ ? 0
284
+ : possibleSeekTime;
285
+ sessionStorage.setItem("startSelection", from); // is this correct for marin should not also be date format?
286
+ sessionStorage.setItem("endSelection", length); // is this correct for marin should not also be date format, length wrong?
287
+ onDrag(seekTime);
288
+ }
289
+ }
290
+ else {
291
+ // set on drag
292
+ if (startPositionCurrentTimeIndicator === "0") {
293
+ seekTime = e["xaxis.range[0]"] * 1e6;
294
+ sessionStorage.setItem("startSelection", e["xaxis.range[0]"] * 1e6);
295
+ sessionStorage.setItem("endSelection", e["xaxis.range[1]"] * 1e6);
296
+ }
297
+ else {
298
+ seekTime =
299
+ ((e["xaxis.range[1]"] - e["xaxis.range[0]"]) / 2 +
300
+ e["xaxis.range[0]"]) *
301
+ 1e6;
302
+ sessionStorage.setItem("startSelection", e["xaxis.range[0]"] * 1e6);
303
+ sessionStorage.setItem("endSelection", e["xaxis.range[1]"] * 1e6);
304
+ }
305
+ onDrag(seekTime);
306
+ }
307
+ }
308
+ };
309
+ // MARIN data should be normalized if the unit of the different signals are not the same
310
+ const shouldNormalize = React.useMemo(() => {
311
+ const units = metadata.reduce((acc, curr) => {
312
+ if (acc.indexOf(curr.unit) < 0) {
313
+ return [...acc, curr.unit];
314
+ }
315
+ return acc;
316
+ }, []);
317
+ return displaySecondYAxis ? units.length > 2 : units.length > 1;
318
+ }, [metadata, displaySecondYAxis]);
319
+ // Getting the range for the y-axis
320
+ const yScaleRange = React.useMemo(() => {
321
+ if (!shouldNormalize && metadata) {
322
+ if (displaySecondYAxis && metadata.length == 2) {
323
+ return [
324
+ { min: metadata[0].min, max: metadata[0].max },
325
+ { min: metadata[1].min, max: metadata[1].max },
326
+ ];
327
+ }
328
+ else {
329
+ const min = metadata.reduce((acc, l) => Math.min(acc, l.min), Infinity);
330
+ const max = metadata.reduce((acc, l) => Math.max(acc, l.max), -Infinity);
331
+ return [{ min, max }];
332
+ }
333
+ }
334
+ return [{ min: 0, max: 1 }];
335
+ }, [shouldNormalize, metadata, displaySecondYAxis]);
336
+ useEffect(() => {
337
+ const plotDataArr = [];
338
+ data.forEach((signal, idx) => {
339
+ // For Noldus we need to filter out the x from the objects, MARIN directly returns a Plotly optimized format
340
+ const x = selectedTheme.name === "noldus"
341
+ ? signal.map((item) => {
342
+ if (xScaleTickFormat.includes(":")) {
343
+ const xDateFormatted = convertMicrosecondstoDateFormat(+item.x);
344
+ return xDateFormatted;
345
+ }
346
+ else {
347
+ const xValue = +item.x / 1e6;
348
+ return xValue;
349
+ }
350
+ })
351
+ : signal[0].map((item) => {
352
+ if (xScaleTickFormat.includes(":")) {
353
+ const xDateFormatted = convertMicrosecondstoDateFormat(+item);
354
+ return xDateFormatted;
355
+ }
356
+ else {
357
+ const xValue = +item / 1e6;
358
+ return xValue;
359
+ }
360
+ });
361
+ // For Noldus we need to filter out the y from the objects, MARIN directly returns a Plotly optimized format
362
+ const yVal = selectedTheme.name === "noldus"
363
+ ? signal.map((item) => {
364
+ return item.y;
365
+ })
366
+ : signal[1];
367
+ const y = handleYValue(yVal, idx);
368
+ const sharedSignalData = {
369
+ x,
370
+ y,
371
+ type: "scattergl",
372
+ mode: metadata[idx].frequency * (xRange.length / 1000 / 1000) > 250
373
+ ? "lines"
374
+ : "lines+markers",
375
+ name: metadata[idx].name,
376
+ marker: {
377
+ color: metadata[idx].color,
378
+ size: 5,
379
+ line: {
380
+ color: metadata[idx].color,
381
+ width: 1,
382
+ },
383
+ },
384
+ };
385
+ const signalData = getSignalData(sharedSignalData, yVal);
386
+ plotDataArr.push(signalData);
387
+ });
388
+ // Check if a second y-axis needs to be added
389
+ if (displaySecondYAxis && metadata.length == 2) {
390
+ plotDataArr[1] = Object.assign(Object.assign({}, plotDataArr[1]), { yaxis: "y2" });
391
+ }
392
+ // Setting y-axis labels if needed and meeting the requirements
393
+ if (metadata.length == 1) {
394
+ setYAxisLabelLeft(yAxisLabel ? metadata[0].name : "");
395
+ setYAxisLabelLeftColor(yAxisLabel ? selectedTheme.colors.foreground : "");
396
+ }
397
+ else if (metadata.length == 2 && displaySecondYAxis) {
398
+ setYAxisLabelLeft(yAxisLabel ? metadata[0].name : "");
399
+ setYAxisLabelLeftColor(yAxisLabel ? metadata[0].color : "");
400
+ setYAxisLabelRight(yAxisLabel ? metadata[1].name : "");
401
+ setYAxisLabelRightColor(yAxisLabel ? metadata[1].color : "");
402
+ }
403
+ else {
404
+ setYAxisLabelLeft("");
405
+ setYAxisLabelRight("");
406
+ setYAxisLabelLeftColor("");
407
+ setYAxisLabelRightColor("");
408
+ }
409
+ setPlotData(plotDataArr);
410
+ }, [
411
+ data,
412
+ metadata,
413
+ xScaleTickFormat,
414
+ xRange.length,
415
+ yAxisLabel,
416
+ displaySecondYAxis,
417
+ selectedTheme.colors.foreground,
418
+ selectedTheme.name,
419
+ ]);
420
+ useEffect(() => {
421
+ updateLayout();
422
+ }, [updateLayout]);
423
+ useEffect(() => {
424
+ if (!scrubbing) {
425
+ setNowline(xRange.nowline);
426
+ }
427
+ sessionStorage.setItem("nl", xRange.nowline);
428
+ }, [scrubbing, xRange.nowline]);
429
+ const afterPlot = () => {
430
+ //console.log(plotlyRef.current);
431
+ //console.log(plotlyRef.current.el);
432
+ // TODO: get from plotlyRef instead of document
433
+ var layer = document.getElementsByClassName("axesclip")[0].firstChild;
434
+ const yAxisSignalLeft = layer.x.baseVal.value;
435
+ const yAxisSignalWidth = layer.width.baseVal.value;
436
+ const xAxisSignalTop = layer.y.baseVal.value;
437
+ const xAxisSignalHeight = layer.height.baseVal.value;
438
+ onRenderedLeft(yAxisSignalLeft);
439
+ onRenderedWidth(yAxisSignalWidth);
440
+ onRenderedTop(xAxisSignalTop);
441
+ onRenderedHeight(xAxisSignalHeight);
442
+ // TODO: get from plotlyRef instead of document
443
+ const scrubbar = document.getElementsByClassName("ewdrag")[0];
444
+ const scrubbarHeight = scrubbar.height.baseVal.value;
445
+ onRenderedScrubbar(scrubbarHeight);
446
+ };
447
+ const handleYValue = (yVal, idx) => {
448
+ // Noldus wants the Y data to be relative between 0 and 1
449
+ // MARIN data engineers always wants the data as it is
450
+ if (selectedTheme.name === "noldus" || shouldNormalize) {
451
+ const { min, max } = metadata[idx];
452
+ const range = max - min;
453
+ const y = yVal.map((value) => {
454
+ return (value - min) / range;
455
+ });
456
+ return y;
457
+ }
458
+ else {
459
+ return yVal;
460
+ }
461
+ };
462
+ const getSignalData = (sharedSignalData, yVal) => {
463
+ if (selectedTheme.name === "noldus") {
464
+ const noldusSignalData = {
465
+ hovertemplate: "%{customdata:.2f}",
466
+ customdata: yVal.map((point) => `${point}`), // Setting raw data for each point
467
+ };
468
+ return Object.assign(Object.assign({}, sharedSignalData), noldusSignalData);
469
+ }
470
+ else {
471
+ if (shouldNormalize) {
472
+ const marinSignalData = {
473
+ hovertemplate: "%{customdata}",
474
+ customdata: yVal.map((point) => `${point}`),
475
+ };
476
+ return Object.assign(Object.assign({}, sharedSignalData), marinSignalData);
477
+ }
478
+ else {
479
+ return sharedSignalData;
480
+ }
481
+ }
482
+ };
483
+ return (React.createElement(StyledPlotly, { useResizeHandler: true, className: "plot-div", data: plotData, layout: layout, style: { width: "100%", height: percentageHeight + "%" }, config: {
484
+ displaylogo: false,
485
+ displayModeBar: false,
486
+ responsive: true,
487
+ doubleClick: false,
488
+ showTips: false,
489
+ }, onRelayout: (e) => onRelayout(e), onRelayouting: (e) => onRelayouting(e), onAfterPlot: () => afterPlot(), ref: plotlyRef }));
490
+ });
491
+ PlotlyPlayback.defaultProps = {
492
+ xRange: null,
493
+ data: [],
494
+ metadata: [],
495
+ onStopSelect: () => { },
496
+ onDrag: () => { },
497
+ xScaleTickFormat: "SS",
498
+ scrubbing: false,
499
+ yAxisLabel: false,
500
+ displaySecondYAxis: false,
501
+ displayArrow: true,
502
+ selectedTheme: null,
503
+ totalTime: null,
504
+ startPositionCurrentTimeIndicator: "0.5",
505
+ };
506
+ const dataElement = PropTypes.shape({
507
+ x: PropTypes.number,
508
+ y: PropTypes.number,
509
+ });
510
+ const metadataElement = PropTypes.shape({
511
+ name: PropTypes.string,
512
+ color: PropTypes.string,
513
+ });
514
+ PlotlyPlayback.propTypes = {
515
+ xRange: PropTypes.shape({
516
+ from: PropTypes.number,
517
+ length: PropTypes.number,
518
+ }),
519
+ data: PropTypes.arrayOf(PropTypes.arrayOf(dataElement), PropTypes.arrayOf(dataElement)),
520
+ metadata: PropTypes.arrayOf(metadataElement),
521
+ onStopSelect: PropTypes.func,
522
+ onDrag: PropTypes.func,
523
+ xScaleTickFormat: PropTypes.string,
524
+ scrubbing: PropTypes.bool,
525
+ yAxisLabel: PropTypes.bool,
526
+ displaySecondYAxis: PropTypes.bool,
527
+ displayArrow: PropTypes.bool,
528
+ selectedTheme: PropTypes.shape({
529
+ colors: PropTypes.shape({
530
+ background: PropTypes.string,
531
+ }),
532
+ name: PropTypes.string,
533
+ }),
534
+ totalTime: PropTypes.number,
535
+ startPositionCurrentTimeIndicator: PropTypes.string,
536
+ };
330
537
  //# sourceMappingURL=PlotlyPlayback.js.map