@spider-analyzer/timeline 4.0.2 → 5.0.0

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 (76) hide show
  1. package/CHANGELOG.md +70 -1
  2. package/README.md +275 -637
  3. package/dist/index.d.mts +132 -0
  4. package/dist/index.d.ts +132 -0
  5. package/dist/index.js +2913 -22
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +2906 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/dist/timeline-EX2XZ6IP.css +139 -0
  10. package/package.json +48 -15
  11. package/src/Cursor.jsx +5 -13
  12. package/src/TimeLine.tsx +994 -0
  13. package/src/TimeLineResizer.jsx +2 -8
  14. package/src/ToolTip.jsx +7 -7
  15. package/src/cursorElements/CursorIcon.jsx +6 -29
  16. package/src/cursorElements/CursorSelection.jsx +4 -19
  17. package/src/cursorElements/DragOverlay.jsx +2 -12
  18. package/src/cursorElements/LeftHandle.jsx +3 -19
  19. package/src/cursorElements/LeftToolTip.jsx +2 -7
  20. package/src/cursorElements/RightHandle.jsx +3 -19
  21. package/src/cursorElements/RightToolTip.jsx +4 -13
  22. package/src/cursorElements/ZoomIn.jsx +5 -25
  23. package/src/cursorElements/ZoomOut.jsx +4 -21
  24. package/src/cursorElements/utils.js +1 -1
  25. package/src/index.js +6 -0
  26. package/src/index.ts +158 -0
  27. package/src/moment-shim.ts +169 -0
  28. package/src/styles.ts +15 -0
  29. package/src/time.ts +52 -0
  30. package/src/timeLineElements/Button.jsx +5 -30
  31. package/src/timeLineElements/HistoToolTip.jsx +3 -17
  32. package/src/timeLineElements/Histogram.jsx +4 -16
  33. package/src/timeLineElements/Legend.jsx +2 -16
  34. package/src/timeLineElements/QualityLine.jsx +4 -11
  35. package/src/timeLineElements/Tools.jsx +1 -1
  36. package/src/timeLineElements/XAxis.jsx +5 -8
  37. package/src/timeLineElements/XGrid.jsx +3 -7
  38. package/src/timeLineElements/YAxis.jsx +4 -7
  39. package/src/timeLineElements/YGrid.jsx +2 -6
  40. package/src/timeLineElements/axesStyles.jsx +0 -49
  41. package/src/timeline.css +139 -0
  42. package/src/utils.ts +60 -0
  43. package/.babelrc +0 -8
  44. package/.gitlab-ci.yml +0 -27
  45. package/Makefile +0 -20
  46. package/dist/Cursor.js +0 -290
  47. package/dist/TimeLine.js +0 -1173
  48. package/dist/TimeLineResizer.js +0 -70
  49. package/dist/ToolTip.js +0 -43
  50. package/dist/cursorElements/CursorIcon.js +0 -98
  51. package/dist/cursorElements/CursorSelection.js +0 -179
  52. package/dist/cursorElements/DragOverlay.js +0 -168
  53. package/dist/cursorElements/LeftHandle.js +0 -95
  54. package/dist/cursorElements/LeftToolTip.js +0 -70
  55. package/dist/cursorElements/RightHandle.js +0 -95
  56. package/dist/cursorElements/RightToolTip.js +0 -75
  57. package/dist/cursorElements/ZoomIn.js +0 -93
  58. package/dist/cursorElements/ZoomOut.js +0 -67
  59. package/dist/cursorElements/commonStyles.js +0 -28
  60. package/dist/cursorElements/handleHistoHovering.js +0 -79
  61. package/dist/cursorElements/utils.js +0 -30
  62. package/dist/theme.js +0 -59
  63. package/dist/timeLineElements/Button.js +0 -101
  64. package/dist/timeLineElements/HistoToolTip.js +0 -78
  65. package/dist/timeLineElements/Histogram.js +0 -110
  66. package/dist/timeLineElements/Legend.js +0 -70
  67. package/dist/timeLineElements/QualityLine.js +0 -81
  68. package/dist/timeLineElements/Tools.js +0 -115
  69. package/dist/timeLineElements/XAxis.js +0 -76
  70. package/dist/timeLineElements/XGrid.js +0 -47
  71. package/dist/timeLineElements/YAxis.js +0 -60
  72. package/dist/timeLineElements/YGrid.js +0 -46
  73. package/dist/timeLineElements/axesStyles.js +0 -57
  74. package/src/TimeLine.jsx +0 -1158
  75. package/src/cursorElements/commonStyles.js +0 -21
  76. /package/dist/{tipDark.css → tipDark-BQEJ43KY.css} +0 -0
package/src/TimeLine.jsx DELETED
@@ -1,1158 +0,0 @@
1
- import React, {Component} from 'react';
2
- import PropTypes from 'prop-types';
3
- import moment from 'moment-timezone';
4
- import {scaleLinear, scaleTime} from 'd3-scale';
5
- import _merge from 'lodash-es/merge';
6
- import _round from 'lodash-es/round';
7
- import _max from 'lodash-es/max';
8
- import _floor from 'lodash-es/floor';
9
- import isEqual from 'lodash-es/isEqual';
10
- import isFunction from 'lodash-es/isFunction';
11
-
12
- import Cursor from './Cursor';
13
- import Histogram from './timeLineElements/Histogram';
14
- import XAxis from './timeLineElements/XAxis';
15
- import YAxis from './timeLineElements/YAxis';
16
- import Legend from './timeLineElements/Legend';
17
- import Tools from './timeLineElements/Tools';
18
- import HistoToolTip from './timeLineElements/HistoToolTip';
19
- import theme, {defaultLabels, defaultQualityScale} from './theme';
20
-
21
- import QualityLine from './timeLineElements/QualityLine';
22
- import YGrid from './timeLineElements/YGrid';
23
- import XGrid from './timeLineElements/XGrid';
24
-
25
- const marginDefault = {
26
- left: 50,
27
- right: 45,
28
- top: 5,
29
- bottom: 5
30
- };
31
- const xAxisDefault = {
32
- height: 20,
33
- spaceBetweenTicks: 70,
34
- barsBetweenTicks: 8
35
- };
36
- const defaultZoomOutFactor = 1.25;
37
-
38
- export default class TimeLine extends Component{
39
- constructor(props){
40
- super(props);
41
-
42
- this.labels = _merge(defaultLabels, props.labels);
43
- this.xAxis = null; //time axis computation function
44
-
45
- this.state = {
46
- isActive : false,
47
- start: props.timeSpan.start,
48
- stop: props.timeSpan.stop,
49
- maxTimespan: false,
50
- waitForLoad: false,
51
- tooltipVisible: false,
52
- barHovered: null,
53
- dragging: false,
54
- ticks: [],
55
- verticalMarks: [],
56
- ...this.computeMarginAndWidth(props)
57
- };
58
-
59
- this.state = {
60
- ...this.state,
61
- ...this.checkAndCorrectDomain({domain: props.domains[0]}),
62
- }
63
-
64
- this.widthOfLastUpdate = null;
65
- this.timelineElement = React.createRef();
66
-
67
- //manage auto sliding
68
- this.autoMove = null;
69
- this.isAutoMoving = false;
70
- this.lastAutoMovingDelta = 0;
71
- this.movedSinceLastFetched = 0;
72
- }
73
-
74
- componentDidMount(){
75
- if(this.state.domain){
76
- this.getItems(this.props, this.state.domain);
77
- }
78
- else{
79
- this.props.onLoadDefaultDomain();
80
- }
81
- this.computeMarginAndWidth(this.props);
82
- }
83
-
84
- componentDidUpdate(prevProps, prevState) {
85
- //reached max selection
86
- if(!prevState.maxTimespan && this.state.maxTimespan){
87
- this.props.onShowMessage(this.labels.maxSelectionMsg);
88
- }
89
-
90
- //Change of width (big): update item
91
- if((prevProps.width !== this.props.width) || !isEqual(prevProps.margin, this.props.margin)){
92
- if(this.state.isActive){
93
- this.getItems(this.props, this.state.domain, this.widthOfLastUpdate && Math.abs(this.props.width - this.widthOfLastUpdate) > 30);
94
- this.setState(this.computeMarginAndWidth(this.props));
95
- }
96
- else{
97
- this.setState(this.computeMarginAndWidth(this.props));
98
- }
99
-
100
- if(this.state.domain){
101
-
102
- const {maxZoom, minZoom, domain, domainHasChanged, minTime, maxTime} = this.checkAndCorrectDomain({domain: this.state.domain});
103
- if(maxZoom !== this.state.maxZoom || minZoom !== this.state.minZoom){
104
-
105
- this.setState({
106
- maxZoom,
107
- minZoom,
108
- minTime,
109
- maxTime,
110
- domain: domainHasChanged ? domain: this.state.domain
111
- });
112
- if(domainHasChanged){
113
- const domains = [domain, ...this.props.domains.slice(1)];
114
- this.props.onUpdateDomains(domains);
115
- }
116
- }
117
- }
118
- }
119
-
120
- //If selection changed
121
- if(!prevProps.timeSpan.start.isSame(this.props.timeSpan.start)
122
- || !prevProps.timeSpan.stop.isSame(this.props.timeSpan.stop)){
123
-
124
- const newDomains = this.shiftDomains(this.props.domains, {
125
- min: this.props.timeSpan.start,
126
- max: this.props.timeSpan.stop
127
- });
128
-
129
- if(newDomains !== this.props.domains){
130
- this.props.onUpdateDomains(newDomains);
131
- }
132
-
133
- //Update cursor
134
- this.setState({
135
- start: this.props.timeSpan.start,
136
- stop: this.props.timeSpan.stop
137
- });
138
- }
139
-
140
- //If change in current domain, update items and state
141
- if((!prevProps.domains[0] && this.props.domains[0])
142
- || (prevProps.domains[0]
143
- && (!prevProps.domains[0].min.isSame(this.props.domains[0].min)
144
- || !prevProps.domains[0].max.isSame(this.props.domains[0].max)))){
145
-
146
- //If current zoom changes and is changing min/max zoom attribute
147
- //then update min/maxZoom and crop domain
148
- const {maxZoom, minZoom, domain, domainHasChanged, minTime, maxTime} = this.checkAndCorrectDomain({domain: this.props.domains[0]});
149
- this.setState({
150
- maxZoom,
151
- minZoom,
152
- minTime,
153
- maxTime,
154
- domain
155
- });
156
-
157
- //if domain changed, update domain, and getItems will be done later
158
- if(domainHasChanged){
159
- const domains = [domain, ...this.props.domains.slice(1)];
160
- this.props.onUpdateDomains(domains);
161
- }
162
- else{
163
- this.getItems(this.props, domain);
164
- }
165
- }
166
-
167
- //If we got new histo change indicator
168
- if(prevProps.histo !== this.props.histo){
169
- this.setState({
170
- waitForLoad: false,
171
- });
172
- }
173
-
174
- //Recompute margin on legend change
175
- if(prevProps.metricsDefinition.legends !== this.props.metricsDefinition.legends){
176
- this.computeMarginAndWidth(this.props);
177
- }
178
- }
179
-
180
- computeMarginAndWidth(props){
181
- const {margin, showLegend, metricsDefinition, width} = this.props;
182
-
183
- const localMargin = {
184
- ...marginDefault,
185
- ...margin
186
- }
187
-
188
- if(showLegend){
189
- const maxLength = _max(metricsDefinition.legends.map(s => s.length));
190
- localMargin.left = _max([5 + maxLength*9, localMargin.left]);
191
- }
192
-
193
- return {
194
- margin: localMargin,
195
- histoWidth: width - localMargin.left - localMargin.right
196
- }
197
- }
198
-
199
- getItems(props, domain, shouldReload = true){
200
- const histoWidth = this.computeMarginAndWidth(this.props).histoWidth;
201
-
202
- this.xAxis = scaleTime()
203
- .domain([domain.min, domain.max])
204
- .range([0, histoWidth]);
205
- this.xAxis.clamp(true);
206
-
207
- const ticks = this.xAxis.ticks(_floor(histoWidth / props.xAxis.spaceBetweenTicks));
208
-
209
- const intervalMs = _max([_round(moment(ticks[1]).diff(moment(ticks[0]))/props.xAxis.barsBetweenTicks), this.props.smallestResolution.asMilliseconds()]);
210
-
211
- this.setState({
212
- histoWidth,
213
- isActive: true,
214
- ticks
215
- });
216
-
217
- if(shouldReload && intervalMs){
218
- this.widthOfLastUpdate = props.width;
219
- this.setState({
220
- waitForLoad: true
221
- });
222
- props.onLoadHisto(intervalMs, domain.min, domain.max);
223
- }
224
- }
225
-
226
- prepareVerticalScale({yAxis, xAxis, height, histo, margin}){
227
- const items = histo && histo.items;
228
-
229
- const maxHeight = height - margin.bottom - margin.top - (xAxis.height || xAxisDefault.height);
230
-
231
- //Y Axis computation
232
- const maxScale = items ? _max(items.map(i => i.total)) || 0 : 0;
233
- const verticalScale = scaleLinear()
234
- .domain([0, maxScale])
235
- .range([0, maxHeight]);
236
-
237
- const verticalMarks = yAxis.spaceBetweenTicks && (maxHeight>yAxis.spaceBetweenTicks*2) ?
238
- verticalScale.ticks(_floor(maxHeight / yAxis.spaceBetweenTicks))
239
- : [maxScale];
240
-
241
- return {
242
- verticalScale,
243
- verticalMarks,
244
- maxHeight
245
- };
246
- }
247
-
248
- prepareHistogram({histo, domain, verticalScale}){
249
- if(histo && histo.items && domain){
250
- const {min, max} = domain;
251
-
252
- return histo.items
253
- .filter(item => item.total > 0
254
- && item.time.isSameOrAfter(min)
255
- && item.time.isBefore(max))
256
- .map(item => {
257
- const start = moment(item.time);
258
- const end = moment(item.time).add(histo.intervalMs);
259
- return {
260
- x1: isFunction(this.xAxis) ? this.xAxis(start) : 0,
261
- x2: isFunction(this.xAxis) ? this.xAxis(end) : 0,
262
- start,
263
- end,
264
- metrics: item.metrics,
265
- total: item.total,
266
- height: verticalScale(item.total)
267
- }
268
- });
269
- }
270
- else {
271
- return [];
272
- }
273
- }
274
-
275
- render() {
276
- const {width, height, histo, tools, quality, qualityScale, metricsDefinition,
277
- classes, rcToolTipPrefixCls, showLegend,
278
- onFormatTimeLegend, onFormatMetricLegend, onResetTime, onFormatTimeToolTips,
279
- xAxis, yAxis, showHistoToolTip, HistoToolTip
280
- } = this.props;
281
- const {isActive, domain, histoWidth, margin,
282
- start, stop, maxZoom, minZoom,
283
- maxTimespan, barHovered, tooltipVisible,
284
- ticks, dragging
285
- } = this.state;
286
- const xAxisHeight = (xAxis.height || xAxisDefault.height);
287
- const pointZero = {
288
- x: 0,
289
- y: height - margin.bottom - xAxisHeight
290
- };
291
- const originCursor = {
292
- x: 0,
293
- y: margin.top - 5
294
- }
295
-
296
- const {verticalScale, verticalMarks, maxHeight} = this.prepareVerticalScale({yAxis, xAxis, height, histo, margin});
297
- const items = this.prepareHistogram({histo, domain, verticalScale});
298
- this.items = items;
299
-
300
- return isFunction(this.xAxis) && (
301
- <svg
302
- ref={this.timelineElement}
303
- width={width}
304
- height={height}
305
- onWheel={isActive ? this.onWheel : undefined}
306
- >
307
- <g transform={`translate(${margin.left},${margin.top})`}>
308
- {
309
- yAxis.showGrid &&
310
- <YGrid
311
- classes={classes}
312
- origin={pointZero}
313
- yAxis={verticalScale}
314
- marks={verticalMarks}
315
- xAxisWidth={histoWidth + 13}
316
- />
317
- }
318
- {
319
- xAxis.showGrid &&
320
- <XGrid
321
- classes={classes}
322
- origin={pointZero}
323
- xAxis={this.xAxis}
324
- marks={ticks}
325
- yAxisHeight={maxHeight}
326
- />
327
- }
328
- {
329
- isActive &&
330
- <Histogram
331
- classes={classes}
332
- origin={pointZero}
333
- items={items}
334
- histo={histo}
335
- verticalScale={verticalScale}
336
- metricsDefinition={metricsDefinition}
337
- barHovered={barHovered}
338
- dragging={dragging}
339
- tooltipVisible={tooltipVisible}
340
- onFormatTimeToolTips={onFormatTimeToolTips}
341
- onFormatMetricLegend={onFormatMetricLegend}
342
- HistoToolTip={HistoToolTip}
343
- />
344
- }
345
- <XAxis
346
- classes={classes}
347
- origin={pointZero}
348
- axisWidth={histoWidth + 13}
349
- min={domain && domain.min}
350
- max={domain && domain.max}
351
- xAxis={this.xAxis}
352
- marks={ticks}
353
- arrowPath={xAxis.arrowPath}
354
- yAxisHeight={maxHeight}
355
- showGrid={xAxis.showGrid}
356
- onFormatTimeLegend={onFormatTimeLegend}
357
- />
358
- <Tools
359
- classes={classes}
360
- rcToolTipPrefixCls={rcToolTipPrefixCls}
361
- origin={pointZero}
362
- histoWidth={histoWidth}
363
- isActive={isActive}
364
- tools={tools}
365
- labels={this.labels}
366
- onGoto={this.onGoto}
367
- onResetTime={onResetTime}
368
- shiftTimeLine={this.shiftTimeLine}
369
- />
370
- <YAxis
371
- classes={classes}
372
- origin={pointZero}
373
- maxHeight={maxHeight}
374
- yAxis={verticalScale}
375
- marks={verticalMarks}
376
- arrowPath={yAxis.arrowPath}
377
- showGrid={yAxis.showGrid}
378
- xAxisWidth={histoWidth + 13}
379
- onFormatMetricLegend={onFormatMetricLegend}
380
- />
381
- {showLegend &&
382
- <Legend
383
- classes={classes}
384
- origin={{
385
- x: -5,
386
- y: margin.top + 2
387
- }}
388
- metricsDefinition={metricsDefinition}
389
- />
390
- }
391
- { isActive && tools.cursor!==false &&
392
- <Cursor
393
- origin = {originCursor}
394
- rcToolTipPrefixCls={rcToolTipPrefixCls}
395
- classes={classes}
396
-
397
- startPos={this.xAxis(start)}
398
- startIsOutOfView={start.isBefore(domain.min) || start.isAfter(domain.max)}
399
-
400
- endPos={this.xAxis(stop)}
401
- endIsOutOfView={stop.isBefore(domain.min) || stop.isAfter(domain.max)}
402
-
403
- cursorIsBeforeView={start.isBefore(domain.min) && stop.isBefore(domain.min)}
404
- cursorIsAfterView={start.isAfter(domain.max) && stop.isAfter(domain.max)}
405
-
406
- height={maxHeight}
407
-
408
- overlayHeight={height - margin.top}
409
- overlayWidth={histoWidth}
410
- tooltipVisible={tooltipVisible}
411
- barHovered={barHovered}
412
- setToolTipVisible={tooltipVisible => this.setState({tooltipVisible})}
413
- setBarHovered={barHovered => this.setState({barHovered})}
414
- items={items}
415
- showHistoToolTip={showHistoToolTip}
416
- dragging={dragging}
417
- setDragging={(dragging) => this.setState({dragging})}
418
-
419
- canZoom={true}
420
- minZoom={minZoom}
421
- maxZoom={maxZoom}
422
-
423
- startText={onFormatTimeToolTips(start)}
424
- stopText={onFormatTimeToolTips(stop)}
425
-
426
- maxSize={maxTimespan}
427
-
428
- tools={tools}
429
-
430
- gotoCursorLabel={this.labels.gotoCursor}
431
- zoomInLabel={this.labels.zoomInLabel}
432
- zoomOutLabel={this.labels.zoomOutLabel}
433
-
434
- zoomIn={this.zoomIn}
435
- zoomOut={this.zoomOut}
436
- onResizeLeftCursor={this.onResizeLeftCursor}
437
- onResizeRightCursor={this.onResizeRightCursor}
438
- onEndResizeCursor={this.onEndChangeCursor}
439
- onDragCursor={this.onDragCursor}
440
- onEndDragCursor={this.onEndChangeCursor}
441
- onStartDrawCursor={this.onStartDrawCursor}
442
- onDrawCursor={this.onDrawCursor}
443
- onEndCursor={this.onEndDrawCursor}
444
- onMoveDomain={this.moveTimeLine}
445
- onMovedDomain={this.movedTimeLine}
446
- onGotoCursor={this.onGotoCursor}
447
- />
448
- }
449
- { isActive && quality && quality.items &&
450
- <QualityLine
451
- classes={classes}
452
- rcToolTipPrefixCls={rcToolTipPrefixCls}
453
- origin={pointZero}
454
- quality={quality}
455
- qualityScale={qualityScale}
456
- xAxis={this.xAxis}
457
- />
458
- }
459
- </g>
460
- </svg>
461
- )
462
- }
463
-
464
- shiftDomains = (domains, {min, max}) => {
465
- //update domains if selection is outside domains
466
- let toUpdate = false;
467
- const biggestVisibleDomain = this.props.biggestVisibleDomain;
468
- const newDomains = domains.map((domain, index) => {
469
- if((min && min.isBefore(domain.min)) || (max && max.isAfter(domain.max))){
470
- toUpdate = true;
471
- if(index === domains.length - 1){
472
- //if last domain, increase domain
473
- const newDomain = {
474
- min: min && min.isBefore(domain.min) ? min : domain.min,
475
- max: max && max.isAfter(domain.max) ? max : domain.max
476
- };
477
-
478
- if(biggestVisibleDomain && moment(newDomain.min).add(biggestVisibleDomain).isSameOrBefore(newDomain.max)){
479
- if(min.isBefore(domain.min)){
480
- newDomain.max = moment(min).add(biggestVisibleDomain);
481
- }
482
- else {
483
- newDomain.min = moment(max).subtract(biggestVisibleDomain);
484
- }
485
- }
486
-
487
- return newDomain;
488
- }
489
- else{
490
- //if not last domain, shift domain
491
- if(min && min.isBefore(domain.min)){
492
- return {
493
- min,
494
- max: moment(domain.max).subtract(domain.min.diff(min))
495
- }
496
- }
497
- else{
498
- return {
499
- min: moment(domain.min).add(max.diff(domain.max)),
500
- max
501
- }
502
- }
503
-
504
- }
505
- }
506
- else{
507
- return domain;
508
- }
509
- });
510
- return toUpdate ? newDomains : domains;
511
- }
512
-
513
- onResizeLeftCursor = (delta, mouse) => {
514
- let newStart = moment(this.xAxis.invert(this.xAxis(this.state.start) + delta));
515
- let newStop = this.state.stop;
516
- let maxTimespan = false;
517
-
518
- if (newStart.isSameOrAfter(newStop)) {
519
- newStop = moment(this.xAxis.invert(this.xAxis(newStop) + delta));
520
- }
521
-
522
- if(this.props.biggestTimeSpan){
523
- const min = moment(newStop).subtract(this.props.biggestTimeSpan);
524
- if(min.isSameOrAfter(newStart)){
525
- newStart = min;
526
- maxTimespan = true;
527
- }
528
- }
529
-
530
- this.handleAutoSliding(mouse, this.onResizeLeftCursor);
531
-
532
- if ((newStop !== this.state.stop
533
- && newStop.isSameOrBefore(this.state.domain.max))
534
- || newStart.isSameOrAfter(this.state.domain.min)) {
535
- this.setState({
536
- start: newStart,
537
- stop: newStop,
538
- maxTimespan
539
- });
540
- }
541
- };
542
-
543
- onResizeRightCursor = (delta, mouse) => {
544
- let newStop = moment(this.xAxis.invert(this.xAxis(this.state.stop) + delta));
545
- let newStart = this.state.start;
546
- let maxTimespan = false;
547
-
548
- if(newStop.isSameOrBefore(newStart)){
549
- newStart = moment(this.xAxis.invert(this.xAxis(newStart) + delta));
550
- }
551
-
552
- if(this.props.biggestTimeSpan){
553
- const max = moment(newStart).add(this.props.biggestTimeSpan);
554
- if(max.isSameOrBefore(newStop)){
555
- newStop = max;
556
- maxTimespan = true;
557
- }
558
- }
559
-
560
- this.handleAutoSliding(mouse, this.onResizeRightCursor);
561
-
562
- if (newStop.isSameOrBefore(this.state.domain.max) &&
563
- newStart.isSameOrAfter(this.state.domain.min)){
564
- this.setState({
565
- start: newStart,
566
- stop: newStop,
567
- maxTimespan
568
- });
569
- }
570
- };
571
-
572
- onDragCursor = (delta, mouse) => {
573
- this.xAxis.clamp(false);
574
- const duration = this.state.stop.diff(this.state.start);
575
- let newStart = moment(this.xAxis.invert(this.xAxis(this.state.start) + delta));
576
- let newStop = moment(newStart).add(duration, 'MILLISECONDS');
577
- this.xAxis.clamp(true);
578
-
579
- const {maxDomain} = this.props;
580
- if(maxDomain.min && newStart.isBefore(maxDomain.min)){
581
- newStart = moment(maxDomain.min);
582
- }
583
- if(maxDomain.max && newStop.isAfter(maxDomain.max)){
584
- newStop = moment(maxDomain.max);
585
- }
586
-
587
- this.handleAutoSliding(mouse, this.onDragCursor);
588
-
589
- this.setState({
590
- start: newStart,
591
- stop: newStop
592
- });
593
- };
594
-
595
- onStartDrawCursor = (pos) => {
596
- this.setState({
597
- start: moment(this.xAxis.invert(pos)),
598
- stop: moment(this.xAxis.invert(pos + 1)),
599
- maxTimespan: false
600
- });
601
- };
602
-
603
- onDrawCursor = (delta, mouse) => {
604
- let newStop = moment(this.xAxis.invert(this.xAxis(this.state.stop) + delta));
605
- let newStart = this.state.start;
606
- let maxTimespan = false;
607
-
608
- if(newStop.isSameOrBefore(newStart)){
609
- newStart = moment(this.xAxis.invert(this.xAxis(newStart) + delta));
610
- }
611
-
612
- if(this.props.biggestTimeSpan){
613
- const max = moment(newStart).add(this.props.biggestTimeSpan);
614
- if(max.isSameOrBefore(newStop)){
615
- newStop = max;
616
- maxTimespan = true;
617
- }
618
- }
619
-
620
- this.handleAutoSliding(mouse, this.onDrawCursor);
621
-
622
- if (newStop.isSameOrBefore(this.state.domain.max) &&
623
- newStart.isSameOrAfter(this.state.domain.min)){
624
- this.setState({
625
- start: newStart,
626
- stop: newStop,
627
- maxTimespan
628
- });
629
- }
630
- };
631
-
632
- stopAutoMove = () => {
633
- if(this.autoMove) {
634
- this.setAutoMove(false);
635
- }
636
- if(this.isAutoMoving){
637
- this.isAutoMoving = false;
638
- this.movedTimeLine();
639
- }
640
- }
641
-
642
- onEndDrawCursor = () => {
643
- this.stopAutoMove();
644
-
645
- const {onCustomRange, selectBarOnClick} = this.props;
646
- let {start, stop, barHovered} = this.state;
647
-
648
- if(selectBarOnClick && this.items && barHovered > -1){
649
- const item = this.items[barHovered];
650
- if(start.isSameOrAfter(item.start) && stop.isSameOrBefore(item.end)){
651
- start = moment(item.start);
652
- stop = moment(item.end);
653
- this.setState({start, stop});
654
- }
655
- }
656
-
657
- onCustomRange(start, stop);
658
- };
659
-
660
- onEndChangeCursor = () => {
661
- this.stopAutoMove();
662
-
663
- const {onCustomRange} = this.props;
664
- const {start, stop} = this.state;
665
- onCustomRange(start, stop);
666
- };
667
-
668
- handleAutoSliding = (mouse, action) => {
669
- //slide domain when dragging and mouse out of histo
670
- if(mouse){
671
- const posX = mouse[0];
672
- if(posX < 0){
673
- this.setAutoMove(true, action, posX);
674
- }
675
- else if(posX >= 0 && posX <= this.state.histoWidth){
676
- this.setAutoMove(false);
677
- }
678
- else if(posX > this.state.histoWidth ){
679
- this.setAutoMove(true, action, posX - this.state.histoWidth);
680
- }
681
- }
682
- }
683
-
684
- setAutoMove = (active, action, delta) => {
685
- if(active){
686
- //prevent creating timer to often if mouse did not move so much
687
- if(!this.autoMove || (this.autoMove && Math.abs(this.lastAutoMovingDelta - delta) > 5)){
688
- clearInterval(this.autoMove);
689
- this.autoMove = setInterval(() => {
690
- action(delta);
691
- this.moveTimeLine(-delta);
692
- this.isAutoMoving = true;
693
- this.lastAutoMovingDelta = delta;
694
- },30);
695
- }
696
- }
697
- else if(!active){
698
- clearInterval(this.autoMove);
699
- this.autoMove = null;
700
- }
701
- }
702
-
703
- onWheel = (event) => {
704
- const {waitForLoad, maxZoom, minZoom} = this.state;
705
- const {onShowMessage, domains} = this.props;
706
-
707
- event.stopPropagation();
708
- const baseX = this.timelineElement.current.getBoundingClientRect().left;
709
- const x = event.clientX - baseX - this.state.margin.left;
710
-
711
- if(!waitForLoad){
712
- if(event.deltaY < -50){
713
- if(maxZoom){
714
- onShowMessage(this.labels.scrollMaxZoomMsg);
715
- }
716
- else{
717
- this.zoomOnTarget(x);
718
- }
719
- }
720
- else if(event.deltaY > 50){
721
- if(minZoom){
722
- onShowMessage(this.labels.minZoomMsg);
723
- }
724
- else if(domains.length > 1) {
725
- this.zoomOut();
726
- }
727
- else {
728
- this.zoomOutOfTarget(x);
729
- }
730
- }
731
- }
732
- };
733
-
734
- onGotoCursor = () => {
735
- const middle = moment(this.state.start).add(this.state.stop.diff(this.state.start)/2);
736
- this.xAxis.clamp(false);
737
- const currentX = this.state.histoWidth/2;
738
- const newX = this.xAxis(middle);
739
-
740
- const newDomain = this.moveTimeLineCore(currentX - newX);
741
-
742
- const domains = [...this.props.domains];
743
- //Replace current domain
744
- domains.splice(0,1,newDomain);
745
-
746
- //Check if other domains should shift
747
- const newDomains = this.shiftDomains(domains, newDomain);
748
- this.props.onUpdateDomains(newDomains);
749
- this.xAxis.clamp(true);
750
- };
751
-
752
- onGoto = (d) => {
753
- const halfTimeAgg = this.state.stop.diff(this.state.start)/2;
754
- const when = d || moment();
755
- const newStop = moment(when).add(halfTimeAgg);
756
- const newStart = moment(when).subtract(halfTimeAgg);
757
-
758
- this.props.onCustomRange(newStart, newStop);
759
- };
760
-
761
- zoomOnTarget = (x) => {
762
- const [minOrigin, maxOrigin] = this.xAxis.domain();
763
- const target = moment(this.xAxis.invert(x));
764
-
765
- const min = moment(target).subtract(0.25 * (target.diff(moment(minOrigin))));
766
- const max = moment(target).add(0.25 * (moment(maxOrigin).diff(target)));
767
-
768
- this.zoomOnDomain({
769
- min,
770
- max
771
- });
772
- };
773
-
774
- zoomOutOfTarget = (x) => {
775
- const [minOrigin, maxOrigin] = this.xAxis.domain();
776
- const target = moment(this.xAxis.invert(x));
777
-
778
- const min = moment(target).subtract(1.25 * (target.diff(moment(minOrigin))));
779
- const max = moment(target).add(1.25 * (moment(maxOrigin).diff(target)));
780
-
781
- this.zoomOutTo([{min, max}]);
782
- }
783
-
784
- checkAndCorrectDomain = ({domain}) => {
785
- if(!domain){
786
- return {
787
- domain,
788
- maxZoom: false,
789
- minZoom: false,
790
- domainHasChanged: false
791
- }
792
- }
793
-
794
- const newDomain = {...domain};
795
- const {biggestVisibleDomain, maxDomain, smallestResolution} = this.props;
796
- const {histoWidth} = this.state;
797
-
798
- let domainHasChanged = false;
799
-
800
- //Maximum zoom?
801
- const duration = newDomain.max.diff(newDomain.min);
802
- let maxZoom = false;
803
-
804
- //We stop/recap zoom in when 15px = 1min
805
- const minRes = smallestResolution.asMilliseconds();
806
- if(duration <= (histoWidth*minRes/15)) {
807
- maxZoom = true;
808
- newDomain.max = moment(newDomain.min).add(histoWidth*minRes/15, 'ms');
809
- }
810
-
811
- //Minimum zoom?
812
- let minTime, maxTime;
813
- if(maxDomain.min && newDomain.min.isBefore(maxDomain.min)){
814
- newDomain.min = moment(maxDomain.min);
815
- minTime = true;
816
- }
817
- if(maxDomain.max && newDomain.max.isAfter(maxDomain.max)){
818
- newDomain.max = moment(maxDomain.max);
819
- maxTime = true;
820
- }
821
-
822
- let minZoom = false;
823
- if(biggestVisibleDomain && newDomain.max.isSameOrAfter(moment(newDomain.min).add(biggestVisibleDomain))){
824
- minZoom = true;
825
- newDomain.min = moment(newDomain.max).subtract(biggestVisibleDomain);
826
- }
827
-
828
- if(! (newDomain.min.isSame(domain.min) && newDomain.max.isSame(domain.max)) ){
829
- domainHasChanged = true;
830
- }
831
-
832
- return {
833
- domain: domainHasChanged ? newDomain : domain,
834
- maxZoom,
835
- minZoom,
836
- maxTime,
837
- minTime,
838
- domainHasChanged
839
- }
840
- }
841
-
842
- zoomOnDomain = (targetDomain) => {
843
- const {onShowMessage, onUpdateDomains} = this.props;
844
-
845
- if(!this.state.waitForLoad) {
846
- if(! (targetDomain.min.isSame(this.state.domain.min) && targetDomain.max.isSame(this.state.domain.max))){
847
-
848
- const {maxZoom, domain} = this.checkAndCorrectDomain({domain: targetDomain});
849
- const {maxZoom: currentMaxZoom} = this.state;
850
-
851
- if(maxZoom && maxZoom !== currentMaxZoom){
852
- onShowMessage(this.labels.zoomSelectionResolutionExtended);
853
- }
854
-
855
- const domains = [domain, ...this.props.domains];
856
-
857
- this.setState({
858
- maxZoom,
859
- minZoom: false,
860
- domain
861
- });
862
- onUpdateDomains(domains);
863
- }
864
- else{
865
- onShowMessage(this.labels.zoomInWithoutChangingSelectionMsg);
866
- }
867
- }
868
- };
869
-
870
- zoomOutTo = (domains) => {
871
- const {minZoom, domain, domainHasChanged, maxTime, minTime} = this.checkAndCorrectDomain({domain: domains[0]});
872
- const {onShowMessage, onUpdateDomains} = this.props;
873
-
874
- if(minZoom){
875
- onShowMessage(this.labels.minZoomMsg);
876
- }
877
- if(minTime){
878
- onShowMessage(this.labels.minDomainMsg);
879
- }
880
- if(maxTime){
881
- onShowMessage(this.labels.maxDomainMsg);
882
- }
883
-
884
- const newDomains = domainHasChanged ? [domain, ...this.props.domains.slice(1)] : domains;
885
-
886
- this.setState({
887
- maxZoom: false,
888
- minZoom,
889
- domain
890
- });
891
- onUpdateDomains(newDomains);
892
- };
893
-
894
- zoomIn = () => {
895
- const {maxZoom, start, stop} = this.state;
896
- const {onShowMessage} = this.props;
897
-
898
- if(!maxZoom){
899
- this.zoomOnDomain({
900
- min: start,
901
- max: stop
902
- })
903
- }
904
- else{
905
- onShowMessage(this.labels.doubleClickMaxZoomMsg);
906
- }
907
- };
908
-
909
- zoomOut = () => {
910
- if(!this.state.waitForLoad && !this.state.minZoom){
911
- const domains = [...this.props.domains];
912
- const {zoomOutFactor} = this.props;
913
-
914
- if(domains.length > 1){
915
- domains.shift();
916
- }
917
- else{
918
- const [minOrigin, maxOrigin] = this.xAxis.domain();
919
- const halfDuration = moment(maxOrigin).diff(moment(minOrigin))/2;
920
- const target = moment(minOrigin).add(halfDuration);
921
-
922
- const min = moment(target).subtract(zoomOutFactor * halfDuration);
923
- const max = moment(target).add(zoomOutFactor * halfDuration);
924
-
925
- domains[0] = {min, max};
926
- }
927
- this.zoomOutTo(domains);
928
- }
929
- };
930
-
931
- moveTimeLineCore = (delta) => {
932
- const {maxDomain, onShowMessage} = this.props;
933
- let {domain: currentDomain, minTime = false, maxTime = false} = this.state;
934
-
935
- const currentSpan = currentDomain.max.diff(currentDomain.min);
936
-
937
- if((minTime && (delta > 0)) || (maxTime && (delta < 0))){
938
- return {
939
- ...currentDomain,
940
- moved: 0,
941
- minTime,
942
- maxTime
943
- }
944
- }
945
- else{
946
- let min = moment(this.xAxis.invert(-delta));
947
- let max = moment(this.xAxis.invert(this.state.histoWidth - delta));
948
- let moved = delta;
949
-
950
- if(maxDomain.min && min.isBefore(maxDomain.min)){
951
- moved = this.xAxis(maxDomain.min);
952
- min = moment(maxDomain.min);
953
- max = moment(min).add(currentSpan);
954
- minTime = true;
955
- onShowMessage(this.labels.minDomainMsg);
956
- }
957
-
958
- if(maxDomain.max && max.isAfter(maxDomain.max)){
959
- moved = this.xAxis(maxDomain.max)
960
- max = moment(maxDomain.max);
961
- min = moment(max).subtract(currentSpan);
962
- maxTime = true;
963
- onShowMessage(this.labels.maxDomainMsg);
964
- }
965
-
966
- return {min, max, moved, minTime, maxTime};
967
- }
968
- }
969
-
970
- moveTimeLine = (delta) => {
971
- this.xAxis.clamp(false);
972
- const {min, max, moved, minTime, maxTime} = this.moveTimeLineCore(delta);
973
-
974
- if(moved !== 0){
975
- this.xAxis.domain([min, max]);
976
- this.xAxis.clamp(true);
977
-
978
- const ticks = this.xAxis.ticks(_floor(this.state.histoWidth / this.props.xAxis.spaceBetweenTicks));
979
-
980
- this.movedSinceLastFetched += moved;
981
- if(Math.abs(this.movedSinceLastFetched) > 75 && this.props.fetchWhileSliding){
982
- this.movedSinceLastFetched = 0;
983
- this.getItems(this.props, {min, max});
984
- }
985
-
986
- this.setState({
987
- domain: {
988
- min,
989
- max,
990
- },
991
- minTime,
992
- maxTime,
993
- ticks
994
- });
995
- }
996
- };
997
-
998
- movedTimeLine = () => {
999
- this.movedSinceLastFetched = 0;
1000
- const domain = this.state.domain;
1001
- const {domains: originalDomains, onUpdateDomains} = this.props;
1002
-
1003
- //Replace current domain
1004
- const domains = [domain, ...originalDomains.slice(1)];
1005
-
1006
- //Check if other domains should shift
1007
- const newDomains = this.shiftDomains(domains, domain);
1008
- onUpdateDomains(newDomains);
1009
- };
1010
-
1011
- shiftTimeLine = (delta) => {
1012
- const incr = delta/Math.abs(delta)*8;
1013
- let i = 0;
1014
- function step(){
1015
- this.moveTimeLine(incr);
1016
- i += 8;
1017
- if(i < Math.abs(delta)){
1018
- setTimeout(step.bind(this), 10);
1019
- }
1020
- else{
1021
- this.movedTimeLine();
1022
- }
1023
- }
1024
- step.bind(this)();
1025
- };
1026
-
1027
- static propTypes = {
1028
- className: PropTypes.string,
1029
- classes: PropTypes.object,
1030
- rcToolTipPrefixCls: PropTypes.string,
1031
-
1032
- timeSpan: PropTypes.shape({
1033
- start: PropTypes.instanceOf(moment).isRequired,
1034
- stop: PropTypes.instanceOf(moment).isRequired
1035
- }).isRequired,
1036
- domains: PropTypes.arrayOf(PropTypes.shape({
1037
- min: PropTypes.instanceOf(moment).isRequired,
1038
- max: PropTypes.instanceOf(moment).isRequired
1039
- })).isRequired,
1040
- maxDomain: PropTypes.shape({
1041
- min: PropTypes.instanceOf(moment),
1042
- max: PropTypes.instanceOf(moment)
1043
- }),
1044
- biggestVisibleDomain: PropTypes.object,
1045
- smallestResolution: PropTypes.object.isRequired,
1046
- biggestTimeSpan: PropTypes.object,
1047
- histo: PropTypes.shape({
1048
- items: PropTypes.arrayOf(PropTypes.shape({
1049
- time: PropTypes.instanceOf(moment).isRequired,
1050
- metrics: PropTypes.arrayOf(PropTypes.number).isRequired,
1051
- total: PropTypes.number.isRequired,
1052
- })),
1053
- intervalMs: PropTypes.number
1054
- }).isRequired,
1055
- showHistoToolTip: PropTypes.bool,
1056
- HistoToolTip: PropTypes.elementType,
1057
- quality: PropTypes.shape({
1058
- items: PropTypes.arrayOf(PropTypes.shape({
1059
- time: PropTypes.instanceOf(moment).isRequired,
1060
- quality: PropTypes.number.isRequired,
1061
- tip: PropTypes.node
1062
- })),
1063
- intervalMin: PropTypes.number
1064
- }),
1065
- qualityScale: PropTypes.func,
1066
- zoomOutFactor: PropTypes.number,
1067
- metricsDefinition: PropTypes.shape({
1068
- count: PropTypes.number.isRequired,
1069
- legends: PropTypes.arrayOf(PropTypes.string).isRequired,
1070
- colors: PropTypes.arrayOf(PropTypes.shape({
1071
- fill: PropTypes.string.isRequired,
1072
- stroke: PropTypes.string.isRequired,
1073
- text: PropTypes.string.isRequired,
1074
- })).isRequired
1075
- }).isRequired,
1076
- width: PropTypes.number,
1077
- height: PropTypes.number,
1078
- labels: PropTypes.shape({
1079
- forwardButtonTip: PropTypes.string,
1080
- backwardButtonTip: PropTypes.string,
1081
- resetButtonTip: PropTypes.string,
1082
- gotoNowButtonTip: PropTypes.string,
1083
- doubleClickMaxZoomMsg: PropTypes.string,
1084
- zoomInWithoutChangingSelectionMsg: PropTypes.string,
1085
- zoomSelectionResolutionExtended: PropTypes.string,
1086
- maxSelectionMsg: PropTypes.string,
1087
- scrollMaxZoomMsg: PropTypes.string,
1088
- minZoomMsg: PropTypes.string,
1089
- maxDomainMsg: PropTypes.string,
1090
- minDomainMsg: PropTypes.string,
1091
- gotoCursor: PropTypes.string,
1092
- zoomInLabel: PropTypes.string,
1093
- zoomOutLabel: PropTypes.string,
1094
- }),
1095
- showLegend: PropTypes.bool,
1096
- tools: PropTypes.shape({
1097
- slideForward: PropTypes.bool,
1098
- slideBackward: PropTypes.bool,
1099
- resetTimeline: PropTypes.bool,
1100
- gotoNow: PropTypes.bool,
1101
- cursor: PropTypes.bool,
1102
- zoomIn: PropTypes.bool,
1103
- zoomOut: PropTypes.bool,
1104
- }),
1105
- fetchWhileSliding: PropTypes.bool,
1106
- selectBarOnClick: PropTypes.bool,
1107
-
1108
- xAxis: PropTypes.shape({
1109
- arrowPath: PropTypes.string,
1110
- spaceBetweenTicks: PropTypes.number.isRequired,
1111
- barsBetweenTicks: PropTypes.number.isRequired,
1112
- showGrid: PropTypes.bool,
1113
- height: PropTypes.number,
1114
- }),
1115
- yAxis: PropTypes.shape({
1116
- path: PropTypes.string,
1117
- spaceBetweenTicks: PropTypes.number,
1118
- showGrid: PropTypes.bool,
1119
- }),
1120
- margin: PropTypes.shape({
1121
- left: PropTypes.number,
1122
- right: PropTypes.number,
1123
- top: PropTypes.number,
1124
- bottom: PropTypes.number,
1125
- }),
1126
-
1127
- onLoadDefaultDomain: PropTypes.func.isRequired,
1128
- onLoadHisto: PropTypes.func.isRequired,
1129
- onCustomRange: PropTypes.func.isRequired,
1130
- onShowMessage: PropTypes.func.isRequired,
1131
- onUpdateDomains: PropTypes.func.isRequired,
1132
- onResetTime: PropTypes.func.isRequired,
1133
- onFormatTimeToolTips: PropTypes.func.isRequired,
1134
- onFormatTimeLegend: PropTypes.func.isRequired,
1135
- onFormatMetricLegend: PropTypes.func.isRequired,
1136
- };
1137
-
1138
- static defaultProps = {
1139
- classes: {},
1140
- rcToolTipPrefixCls: theme.rcToolTipPrefixCls,
1141
- tools: {
1142
- slideForward: true,
1143
- slideBackward: true,
1144
- resetTimeline: true,
1145
- gotoNow: true,
1146
- cursor: true,
1147
- zoomIn: true,
1148
- zoomOut: true
1149
- },
1150
- xAxis: xAxisDefault,
1151
- yAxis: {},
1152
- margin: marginDefault,
1153
- showLegend: true,
1154
- qualityScale: defaultQualityScale,
1155
- zoomOutFactor: defaultZoomOutFactor,
1156
- HistoToolTip: HistoToolTip
1157
- }
1158
- }