@spider-analyzer/timeline 4.0.3 → 5.0.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 (75) hide show
  1. package/CHANGELOG.md +80 -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.css +139 -0
  10. package/package.json +52 -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 -1177
  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 -1163
  75. package/src/cursorElements/commonStyles.js +0 -21
package/src/TimeLine.jsx DELETED
@@ -1,1163 +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: this.xAxis(start),
261
- x2: this.xAxis(end),
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
- if(isFunction(this.xAxis)){
297
- const {verticalScale, verticalMarks, maxHeight} = this.prepareVerticalScale({yAxis, xAxis, height, histo, margin});
298
- const items = this.prepareHistogram({histo, domain, verticalScale});
299
- this.items = items;
300
-
301
- return (
302
- <svg
303
- ref={this.timelineElement}
304
- width={width}
305
- height={height}
306
- onWheel={isActive ? this.onWheel : undefined}
307
- >
308
- <g transform={`translate(${margin.left},${margin.top})`}>
309
- {
310
- yAxis.showGrid &&
311
- <YGrid
312
- classes={classes}
313
- origin={pointZero}
314
- yAxis={verticalScale}
315
- marks={verticalMarks}
316
- xAxisWidth={histoWidth + 13}
317
- />
318
- }
319
- {
320
- xAxis.showGrid &&
321
- <XGrid
322
- classes={classes}
323
- origin={pointZero}
324
- xAxis={this.xAxis}
325
- marks={ticks}
326
- yAxisHeight={maxHeight}
327
- />
328
- }
329
- {
330
- isActive &&
331
- <Histogram
332
- classes={classes}
333
- origin={pointZero}
334
- items={items}
335
- histo={histo}
336
- verticalScale={verticalScale}
337
- metricsDefinition={metricsDefinition}
338
- barHovered={barHovered}
339
- dragging={dragging}
340
- tooltipVisible={tooltipVisible}
341
- onFormatTimeToolTips={onFormatTimeToolTips}
342
- onFormatMetricLegend={onFormatMetricLegend}
343
- HistoToolTip={HistoToolTip}
344
- />
345
- }
346
- <XAxis
347
- classes={classes}
348
- origin={pointZero}
349
- axisWidth={histoWidth + 13}
350
- min={domain && domain.min}
351
- max={domain && domain.max}
352
- xAxis={this.xAxis}
353
- marks={ticks}
354
- arrowPath={xAxis.arrowPath}
355
- yAxisHeight={maxHeight}
356
- showGrid={xAxis.showGrid}
357
- onFormatTimeLegend={onFormatTimeLegend}
358
- />
359
- <Tools
360
- classes={classes}
361
- rcToolTipPrefixCls={rcToolTipPrefixCls}
362
- origin={pointZero}
363
- histoWidth={histoWidth}
364
- isActive={isActive}
365
- tools={tools}
366
- labels={this.labels}
367
- onGoto={this.onGoto}
368
- onResetTime={onResetTime}
369
- shiftTimeLine={this.shiftTimeLine}
370
- />
371
- <YAxis
372
- classes={classes}
373
- origin={pointZero}
374
- maxHeight={maxHeight}
375
- yAxis={verticalScale}
376
- marks={verticalMarks}
377
- arrowPath={yAxis.arrowPath}
378
- showGrid={yAxis.showGrid}
379
- xAxisWidth={histoWidth + 13}
380
- onFormatMetricLegend={onFormatMetricLegend}
381
- />
382
- {showLegend &&
383
- <Legend
384
- classes={classes}
385
- origin={{
386
- x: -5,
387
- y: margin.top + 2
388
- }}
389
- metricsDefinition={metricsDefinition}
390
- />
391
- }
392
- { isActive && tools.cursor!==false &&
393
- <Cursor
394
- origin = {originCursor}
395
- rcToolTipPrefixCls={rcToolTipPrefixCls}
396
- classes={classes}
397
-
398
- startPos={this.xAxis(start)}
399
- startIsOutOfView={start.isBefore(domain.min) || start.isAfter(domain.max)}
400
-
401
- endPos={this.xAxis(stop)}
402
- endIsOutOfView={stop.isBefore(domain.min) || stop.isAfter(domain.max)}
403
-
404
- cursorIsBeforeView={start.isBefore(domain.min) && stop.isBefore(domain.min)}
405
- cursorIsAfterView={start.isAfter(domain.max) && stop.isAfter(domain.max)}
406
-
407
- height={maxHeight}
408
-
409
- overlayHeight={height - margin.top}
410
- overlayWidth={histoWidth}
411
- tooltipVisible={tooltipVisible}
412
- barHovered={barHovered}
413
- setToolTipVisible={tooltipVisible => this.setState({tooltipVisible})}
414
- setBarHovered={barHovered => this.setState({barHovered})}
415
- items={items}
416
- showHistoToolTip={showHistoToolTip}
417
- dragging={dragging}
418
- setDragging={(dragging) => this.setState({dragging})}
419
-
420
- canZoom={true}
421
- minZoom={minZoom}
422
- maxZoom={maxZoom}
423
-
424
- startText={onFormatTimeToolTips(start)}
425
- stopText={onFormatTimeToolTips(stop)}
426
-
427
- maxSize={maxTimespan}
428
-
429
- tools={tools}
430
-
431
- gotoCursorLabel={this.labels.gotoCursor}
432
- zoomInLabel={this.labels.zoomInLabel}
433
- zoomOutLabel={this.labels.zoomOutLabel}
434
-
435
- zoomIn={this.zoomIn}
436
- zoomOut={this.zoomOut}
437
- onResizeLeftCursor={this.onResizeLeftCursor}
438
- onResizeRightCursor={this.onResizeRightCursor}
439
- onEndResizeCursor={this.onEndChangeCursor}
440
- onDragCursor={this.onDragCursor}
441
- onEndDragCursor={this.onEndChangeCursor}
442
- onStartDrawCursor={this.onStartDrawCursor}
443
- onDrawCursor={this.onDrawCursor}
444
- onEndCursor={this.onEndDrawCursor}
445
- onMoveDomain={this.moveTimeLine}
446
- onMovedDomain={this.movedTimeLine}
447
- onGotoCursor={this.onGotoCursor}
448
- />
449
- }
450
- { isActive && quality && quality.items &&
451
- <QualityLine
452
- classes={classes}
453
- rcToolTipPrefixCls={rcToolTipPrefixCls}
454
- origin={pointZero}
455
- quality={quality}
456
- qualityScale={qualityScale}
457
- xAxis={this.xAxis}
458
- />
459
- }
460
- </g>
461
- </svg>
462
- )
463
- }
464
- else{
465
- return null;
466
- }
467
- }
468
-
469
- shiftDomains = (domains, {min, max}) => {
470
- //update domains if selection is outside domains
471
- let toUpdate = false;
472
- const biggestVisibleDomain = this.props.biggestVisibleDomain;
473
- const newDomains = domains.map((domain, index) => {
474
- if((min && min.isBefore(domain.min)) || (max && max.isAfter(domain.max))){
475
- toUpdate = true;
476
- if(index === domains.length - 1){
477
- //if last domain, increase domain
478
- const newDomain = {
479
- min: min && min.isBefore(domain.min) ? min : domain.min,
480
- max: max && max.isAfter(domain.max) ? max : domain.max
481
- };
482
-
483
- if(biggestVisibleDomain && moment(newDomain.min).add(biggestVisibleDomain).isSameOrBefore(newDomain.max)){
484
- if(min.isBefore(domain.min)){
485
- newDomain.max = moment(min).add(biggestVisibleDomain);
486
- }
487
- else {
488
- newDomain.min = moment(max).subtract(biggestVisibleDomain);
489
- }
490
- }
491
-
492
- return newDomain;
493
- }
494
- else{
495
- //if not last domain, shift domain
496
- if(min && min.isBefore(domain.min)){
497
- return {
498
- min,
499
- max: moment(domain.max).subtract(domain.min.diff(min))
500
- }
501
- }
502
- else{
503
- return {
504
- min: moment(domain.min).add(max.diff(domain.max)),
505
- max
506
- }
507
- }
508
-
509
- }
510
- }
511
- else{
512
- return domain;
513
- }
514
- });
515
- return toUpdate ? newDomains : domains;
516
- }
517
-
518
- onResizeLeftCursor = (delta, mouse) => {
519
- let newStart = moment(this.xAxis.invert(this.xAxis(this.state.start) + delta));
520
- let newStop = this.state.stop;
521
- let maxTimespan = false;
522
-
523
- if (newStart.isSameOrAfter(newStop)) {
524
- newStop = moment(this.xAxis.invert(this.xAxis(newStop) + delta));
525
- }
526
-
527
- if(this.props.biggestTimeSpan){
528
- const min = moment(newStop).subtract(this.props.biggestTimeSpan);
529
- if(min.isSameOrAfter(newStart)){
530
- newStart = min;
531
- maxTimespan = true;
532
- }
533
- }
534
-
535
- this.handleAutoSliding(mouse, this.onResizeLeftCursor);
536
-
537
- if ((newStop !== this.state.stop
538
- && newStop.isSameOrBefore(this.state.domain.max))
539
- || newStart.isSameOrAfter(this.state.domain.min)) {
540
- this.setState({
541
- start: newStart,
542
- stop: newStop,
543
- maxTimespan
544
- });
545
- }
546
- };
547
-
548
- onResizeRightCursor = (delta, mouse) => {
549
- let newStop = moment(this.xAxis.invert(this.xAxis(this.state.stop) + delta));
550
- let newStart = this.state.start;
551
- let maxTimespan = false;
552
-
553
- if(newStop.isSameOrBefore(newStart)){
554
- newStart = moment(this.xAxis.invert(this.xAxis(newStart) + delta));
555
- }
556
-
557
- if(this.props.biggestTimeSpan){
558
- const max = moment(newStart).add(this.props.biggestTimeSpan);
559
- if(max.isSameOrBefore(newStop)){
560
- newStop = max;
561
- maxTimespan = true;
562
- }
563
- }
564
-
565
- this.handleAutoSliding(mouse, this.onResizeRightCursor);
566
-
567
- if (newStop.isSameOrBefore(this.state.domain.max) &&
568
- newStart.isSameOrAfter(this.state.domain.min)){
569
- this.setState({
570
- start: newStart,
571
- stop: newStop,
572
- maxTimespan
573
- });
574
- }
575
- };
576
-
577
- onDragCursor = (delta, mouse) => {
578
- this.xAxis.clamp(false);
579
- const duration = this.state.stop.diff(this.state.start);
580
- let newStart = moment(this.xAxis.invert(this.xAxis(this.state.start) + delta));
581
- let newStop = moment(newStart).add(duration, 'MILLISECONDS');
582
- this.xAxis.clamp(true);
583
-
584
- const {maxDomain} = this.props;
585
- if(maxDomain.min && newStart.isBefore(maxDomain.min)){
586
- newStart = moment(maxDomain.min);
587
- }
588
- if(maxDomain.max && newStop.isAfter(maxDomain.max)){
589
- newStop = moment(maxDomain.max);
590
- }
591
-
592
- this.handleAutoSliding(mouse, this.onDragCursor);
593
-
594
- this.setState({
595
- start: newStart,
596
- stop: newStop
597
- });
598
- };
599
-
600
- onStartDrawCursor = (pos) => {
601
- this.setState({
602
- start: moment(this.xAxis.invert(pos)),
603
- stop: moment(this.xAxis.invert(pos + 1)),
604
- maxTimespan: false
605
- });
606
- };
607
-
608
- onDrawCursor = (delta, mouse) => {
609
- let newStop = moment(this.xAxis.invert(this.xAxis(this.state.stop) + delta));
610
- let newStart = this.state.start;
611
- let maxTimespan = false;
612
-
613
- if(newStop.isSameOrBefore(newStart)){
614
- newStart = moment(this.xAxis.invert(this.xAxis(newStart) + delta));
615
- }
616
-
617
- if(this.props.biggestTimeSpan){
618
- const max = moment(newStart).add(this.props.biggestTimeSpan);
619
- if(max.isSameOrBefore(newStop)){
620
- newStop = max;
621
- maxTimespan = true;
622
- }
623
- }
624
-
625
- this.handleAutoSliding(mouse, this.onDrawCursor);
626
-
627
- if (newStop.isSameOrBefore(this.state.domain.max) &&
628
- newStart.isSameOrAfter(this.state.domain.min)){
629
- this.setState({
630
- start: newStart,
631
- stop: newStop,
632
- maxTimespan
633
- });
634
- }
635
- };
636
-
637
- stopAutoMove = () => {
638
- if(this.autoMove) {
639
- this.setAutoMove(false);
640
- }
641
- if(this.isAutoMoving){
642
- this.isAutoMoving = false;
643
- this.movedTimeLine();
644
- }
645
- }
646
-
647
- onEndDrawCursor = () => {
648
- this.stopAutoMove();
649
-
650
- const {onCustomRange, selectBarOnClick} = this.props;
651
- let {start, stop, barHovered} = this.state;
652
-
653
- if(selectBarOnClick && this.items && barHovered > -1){
654
- const item = this.items[barHovered];
655
- if(start.isSameOrAfter(item.start) && stop.isSameOrBefore(item.end)){
656
- start = moment(item.start);
657
- stop = moment(item.end);
658
- this.setState({start, stop});
659
- }
660
- }
661
-
662
- onCustomRange(start, stop);
663
- };
664
-
665
- onEndChangeCursor = () => {
666
- this.stopAutoMove();
667
-
668
- const {onCustomRange} = this.props;
669
- const {start, stop} = this.state;
670
- onCustomRange(start, stop);
671
- };
672
-
673
- handleAutoSliding = (mouse, action) => {
674
- //slide domain when dragging and mouse out of histo
675
- if(mouse){
676
- const posX = mouse[0];
677
- if(posX < 0){
678
- this.setAutoMove(true, action, posX);
679
- }
680
- else if(posX >= 0 && posX <= this.state.histoWidth){
681
- this.setAutoMove(false);
682
- }
683
- else if(posX > this.state.histoWidth ){
684
- this.setAutoMove(true, action, posX - this.state.histoWidth);
685
- }
686
- }
687
- }
688
-
689
- setAutoMove = (active, action, delta) => {
690
- if(active){
691
- //prevent creating timer to often if mouse did not move so much
692
- if(!this.autoMove || (this.autoMove && Math.abs(this.lastAutoMovingDelta - delta) > 5)){
693
- clearInterval(this.autoMove);
694
- this.autoMove = setInterval(() => {
695
- action(delta);
696
- this.moveTimeLine(-delta);
697
- this.isAutoMoving = true;
698
- this.lastAutoMovingDelta = delta;
699
- },30);
700
- }
701
- }
702
- else if(!active){
703
- clearInterval(this.autoMove);
704
- this.autoMove = null;
705
- }
706
- }
707
-
708
- onWheel = (event) => {
709
- const {waitForLoad, maxZoom, minZoom} = this.state;
710
- const {onShowMessage, domains} = this.props;
711
-
712
- event.stopPropagation();
713
- const baseX = this.timelineElement.current.getBoundingClientRect().left;
714
- const x = event.clientX - baseX - this.state.margin.left;
715
-
716
- if(!waitForLoad){
717
- if(event.deltaY < -50){
718
- if(maxZoom){
719
- onShowMessage(this.labels.scrollMaxZoomMsg);
720
- }
721
- else{
722
- this.zoomOnTarget(x);
723
- }
724
- }
725
- else if(event.deltaY > 50){
726
- if(minZoom){
727
- onShowMessage(this.labels.minZoomMsg);
728
- }
729
- else if(domains.length > 1) {
730
- this.zoomOut();
731
- }
732
- else {
733
- this.zoomOutOfTarget(x);
734
- }
735
- }
736
- }
737
- };
738
-
739
- onGotoCursor = () => {
740
- const middle = moment(this.state.start).add(this.state.stop.diff(this.state.start)/2);
741
- this.xAxis.clamp(false);
742
- const currentX = this.state.histoWidth/2;
743
- const newX = this.xAxis(middle);
744
-
745
- const newDomain = this.moveTimeLineCore(currentX - newX);
746
-
747
- const domains = [...this.props.domains];
748
- //Replace current domain
749
- domains.splice(0,1,newDomain);
750
-
751
- //Check if other domains should shift
752
- const newDomains = this.shiftDomains(domains, newDomain);
753
- this.props.onUpdateDomains(newDomains);
754
- this.xAxis.clamp(true);
755
- };
756
-
757
- onGoto = (d) => {
758
- const halfTimeAgg = this.state.stop.diff(this.state.start)/2;
759
- const when = d || moment();
760
- const newStop = moment(when).add(halfTimeAgg);
761
- const newStart = moment(when).subtract(halfTimeAgg);
762
-
763
- this.props.onCustomRange(newStart, newStop);
764
- };
765
-
766
- zoomOnTarget = (x) => {
767
- const [minOrigin, maxOrigin] = this.xAxis.domain();
768
- const target = moment(this.xAxis.invert(x));
769
-
770
- const min = moment(target).subtract(0.25 * (target.diff(moment(minOrigin))));
771
- const max = moment(target).add(0.25 * (moment(maxOrigin).diff(target)));
772
-
773
- this.zoomOnDomain({
774
- min,
775
- max
776
- });
777
- };
778
-
779
- zoomOutOfTarget = (x) => {
780
- const [minOrigin, maxOrigin] = this.xAxis.domain();
781
- const target = moment(this.xAxis.invert(x));
782
-
783
- const min = moment(target).subtract(1.25 * (target.diff(moment(minOrigin))));
784
- const max = moment(target).add(1.25 * (moment(maxOrigin).diff(target)));
785
-
786
- this.zoomOutTo([{min, max}]);
787
- }
788
-
789
- checkAndCorrectDomain = ({domain}) => {
790
- if(!domain){
791
- return {
792
- domain,
793
- maxZoom: false,
794
- minZoom: false,
795
- domainHasChanged: false
796
- }
797
- }
798
-
799
- const newDomain = {...domain};
800
- const {biggestVisibleDomain, maxDomain, smallestResolution} = this.props;
801
- const {histoWidth} = this.state;
802
-
803
- let domainHasChanged = false;
804
-
805
- //Maximum zoom?
806
- const duration = newDomain.max.diff(newDomain.min);
807
- let maxZoom = false;
808
-
809
- //We stop/recap zoom in when 15px = 1min
810
- const minRes = smallestResolution.asMilliseconds();
811
- if(duration <= (histoWidth*minRes/15)) {
812
- maxZoom = true;
813
- newDomain.max = moment(newDomain.min).add(histoWidth*minRes/15, 'ms');
814
- }
815
-
816
- //Minimum zoom?
817
- let minTime, maxTime;
818
- if(maxDomain.min && newDomain.min.isBefore(maxDomain.min)){
819
- newDomain.min = moment(maxDomain.min);
820
- minTime = true;
821
- }
822
- if(maxDomain.max && newDomain.max.isAfter(maxDomain.max)){
823
- newDomain.max = moment(maxDomain.max);
824
- maxTime = true;
825
- }
826
-
827
- let minZoom = false;
828
- if(biggestVisibleDomain && newDomain.max.isSameOrAfter(moment(newDomain.min).add(biggestVisibleDomain))){
829
- minZoom = true;
830
- newDomain.min = moment(newDomain.max).subtract(biggestVisibleDomain);
831
- }
832
-
833
- if(! (newDomain.min.isSame(domain.min) && newDomain.max.isSame(domain.max)) ){
834
- domainHasChanged = true;
835
- }
836
-
837
- return {
838
- domain: domainHasChanged ? newDomain : domain,
839
- maxZoom,
840
- minZoom,
841
- maxTime,
842
- minTime,
843
- domainHasChanged
844
- }
845
- }
846
-
847
- zoomOnDomain = (targetDomain) => {
848
- const {onShowMessage, onUpdateDomains} = this.props;
849
-
850
- if(!this.state.waitForLoad) {
851
- if(! (targetDomain.min.isSame(this.state.domain.min) && targetDomain.max.isSame(this.state.domain.max))){
852
-
853
- const {maxZoom, domain} = this.checkAndCorrectDomain({domain: targetDomain});
854
- const {maxZoom: currentMaxZoom} = this.state;
855
-
856
- if(maxZoom && maxZoom !== currentMaxZoom){
857
- onShowMessage(this.labels.zoomSelectionResolutionExtended);
858
- }
859
-
860
- const domains = [domain, ...this.props.domains];
861
-
862
- this.setState({
863
- maxZoom,
864
- minZoom: false,
865
- domain
866
- });
867
- onUpdateDomains(domains);
868
- }
869
- else{
870
- onShowMessage(this.labels.zoomInWithoutChangingSelectionMsg);
871
- }
872
- }
873
- };
874
-
875
- zoomOutTo = (domains) => {
876
- const {minZoom, domain, domainHasChanged, maxTime, minTime} = this.checkAndCorrectDomain({domain: domains[0]});
877
- const {onShowMessage, onUpdateDomains} = this.props;
878
-
879
- if(minZoom){
880
- onShowMessage(this.labels.minZoomMsg);
881
- }
882
- if(minTime){
883
- onShowMessage(this.labels.minDomainMsg);
884
- }
885
- if(maxTime){
886
- onShowMessage(this.labels.maxDomainMsg);
887
- }
888
-
889
- const newDomains = domainHasChanged ? [domain, ...this.props.domains.slice(1)] : domains;
890
-
891
- this.setState({
892
- maxZoom: false,
893
- minZoom,
894
- domain
895
- });
896
- onUpdateDomains(newDomains);
897
- };
898
-
899
- zoomIn = () => {
900
- const {maxZoom, start, stop} = this.state;
901
- const {onShowMessage} = this.props;
902
-
903
- if(!maxZoom){
904
- this.zoomOnDomain({
905
- min: start,
906
- max: stop
907
- })
908
- }
909
- else{
910
- onShowMessage(this.labels.doubleClickMaxZoomMsg);
911
- }
912
- };
913
-
914
- zoomOut = () => {
915
- if(!this.state.waitForLoad && !this.state.minZoom){
916
- const domains = [...this.props.domains];
917
- const {zoomOutFactor} = this.props;
918
-
919
- if(domains.length > 1){
920
- domains.shift();
921
- }
922
- else{
923
- const [minOrigin, maxOrigin] = this.xAxis.domain();
924
- const halfDuration = moment(maxOrigin).diff(moment(minOrigin))/2;
925
- const target = moment(minOrigin).add(halfDuration);
926
-
927
- const min = moment(target).subtract(zoomOutFactor * halfDuration);
928
- const max = moment(target).add(zoomOutFactor * halfDuration);
929
-
930
- domains[0] = {min, max};
931
- }
932
- this.zoomOutTo(domains);
933
- }
934
- };
935
-
936
- moveTimeLineCore = (delta) => {
937
- const {maxDomain, onShowMessage} = this.props;
938
- let {domain: currentDomain, minTime = false, maxTime = false} = this.state;
939
-
940
- const currentSpan = currentDomain.max.diff(currentDomain.min);
941
-
942
- if((minTime && (delta > 0)) || (maxTime && (delta < 0))){
943
- return {
944
- ...currentDomain,
945
- moved: 0,
946
- minTime,
947
- maxTime
948
- }
949
- }
950
- else{
951
- let min = moment(this.xAxis.invert(-delta));
952
- let max = moment(this.xAxis.invert(this.state.histoWidth - delta));
953
- let moved = delta;
954
-
955
- if(maxDomain.min && min.isBefore(maxDomain.min)){
956
- moved = this.xAxis(maxDomain.min);
957
- min = moment(maxDomain.min);
958
- max = moment(min).add(currentSpan);
959
- minTime = true;
960
- onShowMessage(this.labels.minDomainMsg);
961
- }
962
-
963
- if(maxDomain.max && max.isAfter(maxDomain.max)){
964
- moved = this.xAxis(maxDomain.max)
965
- max = moment(maxDomain.max);
966
- min = moment(max).subtract(currentSpan);
967
- maxTime = true;
968
- onShowMessage(this.labels.maxDomainMsg);
969
- }
970
-
971
- return {min, max, moved, minTime, maxTime};
972
- }
973
- }
974
-
975
- moveTimeLine = (delta) => {
976
- this.xAxis.clamp(false);
977
- const {min, max, moved, minTime, maxTime} = this.moveTimeLineCore(delta);
978
-
979
- if(moved !== 0){
980
- this.xAxis.domain([min, max]);
981
- this.xAxis.clamp(true);
982
-
983
- const ticks = this.xAxis.ticks(_floor(this.state.histoWidth / this.props.xAxis.spaceBetweenTicks));
984
-
985
- this.movedSinceLastFetched += moved;
986
- if(Math.abs(this.movedSinceLastFetched) > 75 && this.props.fetchWhileSliding){
987
- this.movedSinceLastFetched = 0;
988
- this.getItems(this.props, {min, max});
989
- }
990
-
991
- this.setState({
992
- domain: {
993
- min,
994
- max,
995
- },
996
- minTime,
997
- maxTime,
998
- ticks
999
- });
1000
- }
1001
- };
1002
-
1003
- movedTimeLine = () => {
1004
- this.movedSinceLastFetched = 0;
1005
- const domain = this.state.domain;
1006
- const {domains: originalDomains, onUpdateDomains} = this.props;
1007
-
1008
- //Replace current domain
1009
- const domains = [domain, ...originalDomains.slice(1)];
1010
-
1011
- //Check if other domains should shift
1012
- const newDomains = this.shiftDomains(domains, domain);
1013
- onUpdateDomains(newDomains);
1014
- };
1015
-
1016
- shiftTimeLine = (delta) => {
1017
- const incr = delta/Math.abs(delta)*8;
1018
- let i = 0;
1019
- function step(){
1020
- this.moveTimeLine(incr);
1021
- i += 8;
1022
- if(i < Math.abs(delta)){
1023
- setTimeout(step.bind(this), 10);
1024
- }
1025
- else{
1026
- this.movedTimeLine();
1027
- }
1028
- }
1029
- step.bind(this)();
1030
- };
1031
-
1032
- static propTypes = {
1033
- className: PropTypes.string,
1034
- classes: PropTypes.object,
1035
- rcToolTipPrefixCls: PropTypes.string,
1036
-
1037
- timeSpan: PropTypes.shape({
1038
- start: PropTypes.instanceOf(moment).isRequired,
1039
- stop: PropTypes.instanceOf(moment).isRequired
1040
- }).isRequired,
1041
- domains: PropTypes.arrayOf(PropTypes.shape({
1042
- min: PropTypes.instanceOf(moment).isRequired,
1043
- max: PropTypes.instanceOf(moment).isRequired
1044
- })).isRequired,
1045
- maxDomain: PropTypes.shape({
1046
- min: PropTypes.instanceOf(moment),
1047
- max: PropTypes.instanceOf(moment)
1048
- }),
1049
- biggestVisibleDomain: PropTypes.object,
1050
- smallestResolution: PropTypes.object.isRequired,
1051
- biggestTimeSpan: PropTypes.object,
1052
- histo: PropTypes.shape({
1053
- items: PropTypes.arrayOf(PropTypes.shape({
1054
- time: PropTypes.instanceOf(moment).isRequired,
1055
- metrics: PropTypes.arrayOf(PropTypes.number).isRequired,
1056
- total: PropTypes.number.isRequired,
1057
- })),
1058
- intervalMs: PropTypes.number
1059
- }).isRequired,
1060
- showHistoToolTip: PropTypes.bool,
1061
- HistoToolTip: PropTypes.elementType,
1062
- quality: PropTypes.shape({
1063
- items: PropTypes.arrayOf(PropTypes.shape({
1064
- time: PropTypes.instanceOf(moment).isRequired,
1065
- quality: PropTypes.number.isRequired,
1066
- tip: PropTypes.node
1067
- })),
1068
- intervalMin: PropTypes.number
1069
- }),
1070
- qualityScale: PropTypes.func,
1071
- zoomOutFactor: PropTypes.number,
1072
- metricsDefinition: PropTypes.shape({
1073
- count: PropTypes.number.isRequired,
1074
- legends: PropTypes.arrayOf(PropTypes.string).isRequired,
1075
- colors: PropTypes.arrayOf(PropTypes.shape({
1076
- fill: PropTypes.string.isRequired,
1077
- stroke: PropTypes.string.isRequired,
1078
- text: PropTypes.string.isRequired,
1079
- })).isRequired
1080
- }).isRequired,
1081
- width: PropTypes.number,
1082
- height: PropTypes.number,
1083
- labels: PropTypes.shape({
1084
- forwardButtonTip: PropTypes.string,
1085
- backwardButtonTip: PropTypes.string,
1086
- resetButtonTip: PropTypes.string,
1087
- gotoNowButtonTip: PropTypes.string,
1088
- doubleClickMaxZoomMsg: PropTypes.string,
1089
- zoomInWithoutChangingSelectionMsg: PropTypes.string,
1090
- zoomSelectionResolutionExtended: PropTypes.string,
1091
- maxSelectionMsg: PropTypes.string,
1092
- scrollMaxZoomMsg: PropTypes.string,
1093
- minZoomMsg: PropTypes.string,
1094
- maxDomainMsg: PropTypes.string,
1095
- minDomainMsg: PropTypes.string,
1096
- gotoCursor: PropTypes.string,
1097
- zoomInLabel: PropTypes.string,
1098
- zoomOutLabel: PropTypes.string,
1099
- }),
1100
- showLegend: PropTypes.bool,
1101
- tools: PropTypes.shape({
1102
- slideForward: PropTypes.bool,
1103
- slideBackward: PropTypes.bool,
1104
- resetTimeline: PropTypes.bool,
1105
- gotoNow: PropTypes.bool,
1106
- cursor: PropTypes.bool,
1107
- zoomIn: PropTypes.bool,
1108
- zoomOut: PropTypes.bool,
1109
- }),
1110
- fetchWhileSliding: PropTypes.bool,
1111
- selectBarOnClick: PropTypes.bool,
1112
-
1113
- xAxis: PropTypes.shape({
1114
- arrowPath: PropTypes.string,
1115
- spaceBetweenTicks: PropTypes.number.isRequired,
1116
- barsBetweenTicks: PropTypes.number.isRequired,
1117
- showGrid: PropTypes.bool,
1118
- height: PropTypes.number,
1119
- }),
1120
- yAxis: PropTypes.shape({
1121
- path: PropTypes.string,
1122
- spaceBetweenTicks: PropTypes.number,
1123
- showGrid: PropTypes.bool,
1124
- }),
1125
- margin: PropTypes.shape({
1126
- left: PropTypes.number,
1127
- right: PropTypes.number,
1128
- top: PropTypes.number,
1129
- bottom: PropTypes.number,
1130
- }),
1131
-
1132
- onLoadDefaultDomain: PropTypes.func.isRequired,
1133
- onLoadHisto: PropTypes.func.isRequired,
1134
- onCustomRange: PropTypes.func.isRequired,
1135
- onShowMessage: PropTypes.func.isRequired,
1136
- onUpdateDomains: PropTypes.func.isRequired,
1137
- onResetTime: PropTypes.func.isRequired,
1138
- onFormatTimeToolTips: PropTypes.func.isRequired,
1139
- onFormatTimeLegend: PropTypes.func.isRequired,
1140
- onFormatMetricLegend: PropTypes.func.isRequired,
1141
- };
1142
-
1143
- static defaultProps = {
1144
- classes: {},
1145
- rcToolTipPrefixCls: theme.rcToolTipPrefixCls,
1146
- tools: {
1147
- slideForward: true,
1148
- slideBackward: true,
1149
- resetTimeline: true,
1150
- gotoNow: true,
1151
- cursor: true,
1152
- zoomIn: true,
1153
- zoomOut: true
1154
- },
1155
- xAxis: xAxisDefault,
1156
- yAxis: {},
1157
- margin: marginDefault,
1158
- showLegend: true,
1159
- qualityScale: defaultQualityScale,
1160
- zoomOutFactor: defaultZoomOutFactor,
1161
- HistoToolTip: HistoToolTip
1162
- }
1163
- }