@windborne/grapher 1.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.
- package/.eslintrc.js +85 -0
- package/.idea/codeStyles/Project.xml +19 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/grapher.iml +12 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/0.bundle.js +2 -0
- package/0.bundle.js.map +1 -0
- package/1767282193a714f63082.module.wasm +0 -0
- package/537.bundle.js +2 -0
- package/537.bundle.js.map +1 -0
- package/831.bundle.js +2 -0
- package/831.bundle.js.map +1 -0
- package/bundle.js +2 -0
- package/bundle.js.map +1 -0
- package/package.json +75 -0
- package/readme.md +129 -0
- package/src/components/annotations.js +62 -0
- package/src/components/context_menu.js +73 -0
- package/src/components/draggable_points.js +114 -0
- package/src/components/graph_body.js +292 -0
- package/src/components/graph_title.js +16 -0
- package/src/components/options.js +111 -0
- package/src/components/percentile_button.js +72 -0
- package/src/components/range_graph.js +352 -0
- package/src/components/range_selection.js +175 -0
- package/src/components/range_selection_button.js +26 -0
- package/src/components/range_selection_button_base.js +51 -0
- package/src/components/series_key.js +235 -0
- package/src/components/series_key_axis_container.js +70 -0
- package/src/components/series_key_item.js +52 -0
- package/src/components/sidebar.js +76 -0
- package/src/components/tooltip.js +244 -0
- package/src/components/vertical_lines.js +70 -0
- package/src/components/x_axis.js +124 -0
- package/src/components/y_axis.js +239 -0
- package/src/eventable.js +65 -0
- package/src/grapher.js +367 -0
- package/src/grapher.scss +914 -0
- package/src/helpers/axis_sizes.js +2 -0
- package/src/helpers/binary_search.js +67 -0
- package/src/helpers/color_to_vector.js +35 -0
- package/src/helpers/colors.js +27 -0
- package/src/helpers/custom_prop_types.js +159 -0
- package/src/helpers/flatten_simple_data.js +81 -0
- package/src/helpers/format.js +233 -0
- package/src/helpers/generator_params_equal.js +10 -0
- package/src/helpers/name_for_series.js +16 -0
- package/src/helpers/place_grid.js +257 -0
- package/src/helpers/pyodide_ready.js +13 -0
- package/src/multigrapher.js +105 -0
- package/src/renderer/background.frag +7 -0
- package/src/renderer/background.vert +7 -0
- package/src/renderer/background_program.js +48 -0
- package/src/renderer/circle.frag +26 -0
- package/src/renderer/circle.vert +12 -0
- package/src/renderer/create_gl_program.js +36 -0
- package/src/renderer/draw_area.js +159 -0
- package/src/renderer/draw_background.js +15 -0
- package/src/renderer/draw_bars.js +80 -0
- package/src/renderer/draw_line.js +69 -0
- package/src/renderer/draw_zero_line.js +24 -0
- package/src/renderer/extract_vertices.js +137 -0
- package/src/renderer/graph_body_renderer.js +293 -0
- package/src/renderer/line.frag +51 -0
- package/src/renderer/line.vert +32 -0
- package/src/renderer/line_program.js +125 -0
- package/src/renderer/paths_from.js +72 -0
- package/src/renderer/scale_bounds.js +28 -0
- package/src/renderer/size_canvas.js +59 -0
- package/src/rust/Cargo.lock +233 -0
- package/src/rust/Cargo.toml +35 -0
- package/src/rust/pkg/grapher_rs.d.ts +42 -0
- package/src/rust/pkg/grapher_rs.js +351 -0
- package/src/rust/pkg/grapher_rs_bg.d.ts +11 -0
- package/src/rust/pkg/grapher_rs_bg.wasm +0 -0
- package/src/rust/pkg/index.js +342 -0
- package/src/rust/pkg/index_bg.wasm +0 -0
- package/src/rust/pkg/package.json +14 -0
- package/src/rust/src/extract_vertices.rs +83 -0
- package/src/rust/src/get_point_number.rs +50 -0
- package/src/rust/src/lib.rs +15 -0
- package/src/rust/src/selected_space_to_render_space.rs +131 -0
- package/src/state/average_loop_times.js +15 -0
- package/src/state/bound_calculator_from_selection.js +36 -0
- package/src/state/bound_calculators.js +41 -0
- package/src/state/calculate_annotations_state.js +59 -0
- package/src/state/calculate_data_bounds.js +104 -0
- package/src/state/calculate_tooltip_state.js +241 -0
- package/src/state/data_types.js +13 -0
- package/src/state/expand_bounds.js +58 -0
- package/src/state/find_matching_axis.js +31 -0
- package/src/state/get_default_bounds_calculator.js +15 -0
- package/src/state/hooks.js +164 -0
- package/src/state/infer_type.js +74 -0
- package/src/state/merge_bounds.js +64 -0
- package/src/state/multigraph_state_controller.js +334 -0
- package/src/state/selection_from_global_bounds.js +25 -0
- package/src/state/space_conversions/condense_data_space.js +115 -0
- package/src/state/space_conversions/data_space_to_selected_space.js +328 -0
- package/src/state/space_conversions/selected_space_to_background_space.js +144 -0
- package/src/state/space_conversions/selected_space_to_render_space.js +161 -0
- package/src/state/space_conversions/simple_series_to_data_space.js +229 -0
- package/src/state/state_controller.js +1770 -0
- package/src/state/sync_pool.js +101 -0
- package/test/setup.js +15 -0
- package/test/space_conversions/data_space_to_selected_space.test.js +434 -0
- package/webpack.dev.config.js +109 -0
- package/webpack.prod.config.js +60 -0
- package/webpack.test.config.js +59 -0
@@ -0,0 +1,352 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import GraphBodyRenderer from '../renderer/graph_body_renderer';
|
4
|
+
import StateController from '../state/state_controller';
|
5
|
+
import placeGrid from '../helpers/place_grid';
|
6
|
+
import {formatX} from '../helpers/format';
|
7
|
+
|
8
|
+
export default class RangeGraph extends React.PureComponent {
|
9
|
+
|
10
|
+
constructor(props) {
|
11
|
+
super(props);
|
12
|
+
|
13
|
+
this.state = {
|
14
|
+
elementWidth: 0,
|
15
|
+
elementHeight: 0,
|
16
|
+
selectionBounds: {
|
17
|
+
minX: 0,
|
18
|
+
maxX: 0
|
19
|
+
},
|
20
|
+
globalBounds: {
|
21
|
+
minX: 0,
|
22
|
+
maxX: 0
|
23
|
+
}
|
24
|
+
};
|
25
|
+
|
26
|
+
this.onMouseMove = this.onMouseMove.bind(this);
|
27
|
+
this.stopDragging = this.stopDragging.bind(this);
|
28
|
+
this.startScroll = this.startScroll.bind(this);
|
29
|
+
this.startLeftDrag = this.startLeftDrag.bind(this);
|
30
|
+
this.startRightDrag = this.startRightDrag.bind(this);
|
31
|
+
}
|
32
|
+
|
33
|
+
componentDidMount() {
|
34
|
+
this._renderer = new GraphBodyRenderer({
|
35
|
+
stateController: this.props.stateController,
|
36
|
+
canvasElement: this.el,
|
37
|
+
webgl: this.props.webgl,
|
38
|
+
checkIntersection: this.props.checkIntersection
|
39
|
+
});
|
40
|
+
this.props.stateController.rangeGraphRenderer = this._renderer;
|
41
|
+
|
42
|
+
this._renderer.on('size_changed', (sizing) => {
|
43
|
+
this.setState(sizing);
|
44
|
+
});
|
45
|
+
|
46
|
+
this.setState({
|
47
|
+
selectionBounds: this.props.stateController._selection,
|
48
|
+
globalBounds: this.props.stateController._globalBounds
|
49
|
+
});
|
50
|
+
|
51
|
+
this.props.stateController.on('selection_changed', (selectionBounds) =>{
|
52
|
+
this.setState({ selectionBounds });
|
53
|
+
});
|
54
|
+
|
55
|
+
this.props.stateController.on('global_bounds_changed', (globalBounds) =>{
|
56
|
+
this.setState({ globalBounds: globalBounds });
|
57
|
+
});
|
58
|
+
}
|
59
|
+
|
60
|
+
componentDidUpdate(prevProps) {
|
61
|
+
if (prevProps.draggingY !== this.props.draggingY) {
|
62
|
+
this._renderer.resize();
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
componentWillUnmount() {
|
67
|
+
this._renderer.dispose();
|
68
|
+
this._renderer = null;
|
69
|
+
}
|
70
|
+
|
71
|
+
onMouseMove(event) {
|
72
|
+
if (!this._dragType) {
|
73
|
+
return;
|
74
|
+
}
|
75
|
+
|
76
|
+
let boundCalculator;
|
77
|
+
const leftX = this.el.getBoundingClientRect().left;
|
78
|
+
|
79
|
+
this.setState(({selectionBounds, globalBounds, elementWidth}) => {
|
80
|
+
const pixelX = event.clientX - leftX;
|
81
|
+
let percentage = pixelX/elementWidth;
|
82
|
+
|
83
|
+
percentage = Math.max(percentage, 0);
|
84
|
+
percentage = Math.min(percentage, 1);
|
85
|
+
|
86
|
+
let trueX = percentage * (globalBounds.maxX - globalBounds.minX) + globalBounds.minX;
|
87
|
+
|
88
|
+
if (this._dragType === 'scroll') {
|
89
|
+
const range = selectionBounds.maxX - selectionBounds.minX;
|
90
|
+
let minX = trueX - range*this._scrollAnchorPercentage;
|
91
|
+
let maxX = trueX + range*(1-this._scrollAnchorPercentage);
|
92
|
+
|
93
|
+
if (minX < globalBounds.minX) {
|
94
|
+
minX = globalBounds.minX;
|
95
|
+
maxX = minX + range;
|
96
|
+
}
|
97
|
+
|
98
|
+
if (maxX > globalBounds.maxX) {
|
99
|
+
maxX = globalBounds.maxX;
|
100
|
+
minX = maxX - range;
|
101
|
+
}
|
102
|
+
|
103
|
+
boundCalculator = () => {
|
104
|
+
return {minX, maxX};
|
105
|
+
};
|
106
|
+
|
107
|
+
return {
|
108
|
+
selectionBounds: Object.assign({}, selectionBounds, {
|
109
|
+
minX, maxX
|
110
|
+
})
|
111
|
+
};
|
112
|
+
} else if (this._dragType === 'left') {
|
113
|
+
if (trueX > selectionBounds.maxX) {
|
114
|
+
trueX = selectionBounds.maxX;
|
115
|
+
}
|
116
|
+
|
117
|
+
const boundToRight = globalBounds.maxX === selectionBounds.maxX;
|
118
|
+
|
119
|
+
boundCalculator = () => {
|
120
|
+
if (boundToRight) {
|
121
|
+
return {
|
122
|
+
minX: trueX
|
123
|
+
};
|
124
|
+
} else {
|
125
|
+
return {
|
126
|
+
minX: trueX,
|
127
|
+
maxX: selectionBounds.maxX
|
128
|
+
};
|
129
|
+
}
|
130
|
+
};
|
131
|
+
|
132
|
+
return {
|
133
|
+
selectionBounds: Object.assign({}, selectionBounds, {
|
134
|
+
minX: trueX
|
135
|
+
})
|
136
|
+
};
|
137
|
+
} else if (this._dragType === 'right') {
|
138
|
+
if (trueX < selectionBounds.minX) {
|
139
|
+
trueX = selectionBounds.minX;
|
140
|
+
}
|
141
|
+
|
142
|
+
const boundToLeft = globalBounds.minX === selectionBounds.minX;
|
143
|
+
|
144
|
+
boundCalculator = () => {
|
145
|
+
if (boundToLeft) {
|
146
|
+
return {
|
147
|
+
maxX: trueX
|
148
|
+
};
|
149
|
+
} else {
|
150
|
+
return {
|
151
|
+
minX: selectionBounds.minX,
|
152
|
+
maxX: trueX
|
153
|
+
};
|
154
|
+
}
|
155
|
+
};
|
156
|
+
|
157
|
+
return {
|
158
|
+
selectionBounds: Object.assign({}, selectionBounds, {
|
159
|
+
maxX: trueX
|
160
|
+
})
|
161
|
+
};
|
162
|
+
}
|
163
|
+
}, () => {
|
164
|
+
if (!boundCalculator) {
|
165
|
+
return;
|
166
|
+
}
|
167
|
+
|
168
|
+
boundCalculator.debounceHistory = true;
|
169
|
+
this.props.stateController.boundCalculator = boundCalculator;
|
170
|
+
});
|
171
|
+
}
|
172
|
+
|
173
|
+
addListeners() {
|
174
|
+
window.addEventListener('mousemove', this.onMouseMove);
|
175
|
+
window.addEventListener('mouseup', this.stopDragging);
|
176
|
+
}
|
177
|
+
|
178
|
+
stopDragging() {
|
179
|
+
this._dragType = null;
|
180
|
+
window.removeEventListener('mousemove', this.onMouseMove);
|
181
|
+
window.removeEventListener('mouseup', this.stopDragging);
|
182
|
+
}
|
183
|
+
|
184
|
+
startScroll(event) {
|
185
|
+
this._dragType = 'scroll';
|
186
|
+
|
187
|
+
const {selectionBounds, globalBounds, elementWidth} = this.state;
|
188
|
+
const leftX = this.el.getBoundingClientRect().left;
|
189
|
+
|
190
|
+
const pixelStartX = event.clientX - leftX;
|
191
|
+
const pixelMinX = (selectionBounds.minX - globalBounds.minX)/(globalBounds.maxX - globalBounds.minX) * elementWidth || 0;
|
192
|
+
const pixelMaxX = (selectionBounds.maxX - globalBounds.minX)/(globalBounds.maxX - globalBounds.minX) * elementWidth || 0;
|
193
|
+
|
194
|
+
this._scrollAnchorPercentage = (pixelStartX-pixelMinX)/(pixelMaxX - pixelMinX);
|
195
|
+
this.addListeners();
|
196
|
+
}
|
197
|
+
|
198
|
+
startLeftDrag() {
|
199
|
+
this._dragType = 'left';
|
200
|
+
this.addListeners();
|
201
|
+
}
|
202
|
+
|
203
|
+
startRightDrag() {
|
204
|
+
this._dragType = 'right';
|
205
|
+
this.addListeners();
|
206
|
+
}
|
207
|
+
|
208
|
+
render() {
|
209
|
+
const { globalBounds, selectionBounds, elementWidth, elementHeight } = this.state;
|
210
|
+
|
211
|
+
let pixelMinX = Math.min(Math.max((selectionBounds.minX - globalBounds.minX)/(globalBounds.maxX - globalBounds.minX), 0), 1) * elementWidth || 0;
|
212
|
+
let pixelMaxX = Math.min(Math.max((selectionBounds.maxX - globalBounds.minX)/(globalBounds.maxX - globalBounds.minX), 0), 1) * elementWidth || 0;
|
213
|
+
|
214
|
+
if (isNaN(pixelMinX) || !isFinite(pixelMinX) || selectionBounds.maxX < selectionBounds.minX) {
|
215
|
+
pixelMinX = 0;
|
216
|
+
}
|
217
|
+
|
218
|
+
if (isNaN(pixelMaxX) || !isFinite(pixelMaxX) || selectionBounds.maxX < selectionBounds.minX) {
|
219
|
+
pixelMaxX = 0;
|
220
|
+
}
|
221
|
+
|
222
|
+
const barSize = 14;
|
223
|
+
let ticks;
|
224
|
+
|
225
|
+
if (selectionBounds.dates && this.props.markDates) {
|
226
|
+
ticks = placeGrid({
|
227
|
+
min: globalBounds.minX,
|
228
|
+
max: globalBounds.maxX,
|
229
|
+
totalSize: elementWidth,
|
230
|
+
precision: 'day',
|
231
|
+
dates: selectionBounds.dates,
|
232
|
+
formatter: formatX,
|
233
|
+
expectedLabelSize: 30,
|
234
|
+
formatOptions: {
|
235
|
+
justMonthAndDay: true,
|
236
|
+
unitOverride: 'day',
|
237
|
+
timeZone: this.props.timeZone
|
238
|
+
},
|
239
|
+
skipFirst: true,
|
240
|
+
skipLast: true
|
241
|
+
});
|
242
|
+
}
|
243
|
+
|
244
|
+
return (
|
245
|
+
<div className="range-selection-graph">
|
246
|
+
<div className="graph-body graph-body-secondary">
|
247
|
+
<canvas ref={(el) => this.el = el} />
|
248
|
+
|
249
|
+
<svg>
|
250
|
+
<g>
|
251
|
+
<rect
|
252
|
+
x={0}
|
253
|
+
y={elementHeight}
|
254
|
+
width={elementWidth}
|
255
|
+
height={barSize}
|
256
|
+
className="selection-bar-track"
|
257
|
+
/>
|
258
|
+
|
259
|
+
{
|
260
|
+
ticks && ticks.map(({ pixelValue, label, size, position }, i) => {
|
261
|
+
if (isNaN(pixelValue)) {
|
262
|
+
return null;
|
263
|
+
}
|
264
|
+
|
265
|
+
const classes = ['axis-item', `axis-item-${size}`, `axis-item-${position}`];
|
266
|
+
|
267
|
+
return (
|
268
|
+
<g key={i} className={classes.join(' ')}>
|
269
|
+
<path d={`M${pixelValue},0 v${elementHeight}`} />
|
270
|
+
|
271
|
+
<text x={pixelValue + 3} y={elementHeight}>
|
272
|
+
{label}
|
273
|
+
</text>
|
274
|
+
</g>
|
275
|
+
);
|
276
|
+
})
|
277
|
+
}
|
278
|
+
|
279
|
+
<rect
|
280
|
+
x={pixelMinX}
|
281
|
+
y={elementHeight}
|
282
|
+
width={pixelMaxX - pixelMinX}
|
283
|
+
height={barSize}
|
284
|
+
className="selection-bar"
|
285
|
+
onMouseDown={this.startScroll}
|
286
|
+
/>
|
287
|
+
|
288
|
+
<path
|
289
|
+
d="M -3 3.5 L -3 9.333333333333334 M 0 3.5 L 0 9.333333333333334 M 3 3.5 L 3 9.333333333333334"
|
290
|
+
className="selection-bar-rifles"
|
291
|
+
transform={`translate(${pixelMinX + (pixelMaxX - pixelMinX)/2},${elementHeight})`}
|
292
|
+
onMouseDown={this.startScroll}
|
293
|
+
/>
|
294
|
+
</g>
|
295
|
+
|
296
|
+
<g>
|
297
|
+
<rect
|
298
|
+
x={pixelMinX}
|
299
|
+
y={0}
|
300
|
+
width={pixelMaxX - pixelMinX}
|
301
|
+
height={elementHeight}
|
302
|
+
className="target-selection"
|
303
|
+
onMouseDown={this.startScroll}
|
304
|
+
/>
|
305
|
+
|
306
|
+
<rect
|
307
|
+
x={pixelMinX}
|
308
|
+
y={0}
|
309
|
+
width={pixelMaxX - pixelMinX}
|
310
|
+
height={elementHeight + barSize}
|
311
|
+
className="target-selection-outline"
|
312
|
+
/>
|
313
|
+
</g>
|
314
|
+
|
315
|
+
<g>
|
316
|
+
<path
|
317
|
+
d="M -4.5 0.5 L 3.5 0.5 L 3.5 15.5 L -4.5 15.5 L -4.5 0.5 M -1.5 4 L -1.5 12 M 0.5 4 L 0.5 12"
|
318
|
+
className="selection-bar-handle"
|
319
|
+
transform={`translate(${pixelMinX},${(elementHeight - 15)/2})`}
|
320
|
+
onMouseDown={this.startLeftDrag}
|
321
|
+
/>
|
322
|
+
</g>
|
323
|
+
|
324
|
+
<g>
|
325
|
+
<path
|
326
|
+
d="M -4.5 0.5 L 3.5 0.5 L 3.5 15.5 L -4.5 15.5 L -4.5 0.5 M -1.5 4 L -1.5 12 M 0.5 4 L 0.5 12"
|
327
|
+
className="selection-bar-handle"
|
328
|
+
transform={`translate(${pixelMaxX},${(elementHeight - 15)/2})`}
|
329
|
+
onMouseDown={this.startRightDrag}
|
330
|
+
/>
|
331
|
+
</g>
|
332
|
+
</svg>
|
333
|
+
</div>
|
334
|
+
</div>
|
335
|
+
);
|
336
|
+
}
|
337
|
+
|
338
|
+
}
|
339
|
+
|
340
|
+
RangeGraph.defaultProps = {
|
341
|
+
width: 3,
|
342
|
+
shadowColor: 'transparent'
|
343
|
+
};
|
344
|
+
|
345
|
+
RangeGraph.propTypes = {
|
346
|
+
stateController: PropTypes.instanceOf(StateController).isRequired,
|
347
|
+
webgl: PropTypes.bool,
|
348
|
+
draggingY: PropTypes.bool,
|
349
|
+
checkIntersection: PropTypes.bool,
|
350
|
+
markDates: PropTypes.bool,
|
351
|
+
timeZone: PropTypes.string
|
352
|
+
};
|
@@ -0,0 +1,175 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import RangeSelectionButton from './range_selection_button';
|
4
|
+
import RangeSelectionButtonBase from './range_selection_button_base';
|
5
|
+
import {Y_AXIS_WIDTH} from '../helpers/axis_sizes';
|
6
|
+
import CustomPropTypes from '../helpers/custom_prop_types';
|
7
|
+
import StateController from '../state/state_controller';
|
8
|
+
import {
|
9
|
+
useBoundCalculator, useBoundHistory,
|
10
|
+
useGlobalBounds,
|
11
|
+
useLeftAxisCount,
|
12
|
+
useRightAxisCount,
|
13
|
+
useShowingOptions
|
14
|
+
} from '../state/hooks';
|
15
|
+
import BOUND_CALCULATORS from '../state/bound_calculators';
|
16
|
+
import Options from './options';
|
17
|
+
|
18
|
+
export default React.memo(RangeSelection);
|
19
|
+
|
20
|
+
function RangeSelection({stateController, customBoundsSelectors, customBoundsSelectorsOnly, sidebarEnabled}) {
|
21
|
+
const rightAxisCount = useRightAxisCount(stateController);
|
22
|
+
const leftAxisCount = useLeftAxisCount(stateController);
|
23
|
+
const showingOptions = useShowingOptions(stateController);
|
24
|
+
|
25
|
+
let marginRight = Y_AXIS_WIDTH*rightAxisCount;
|
26
|
+
if (rightAxisCount > 0) {
|
27
|
+
marginRight += 5;
|
28
|
+
}
|
29
|
+
|
30
|
+
let marginLeft = Y_AXIS_WIDTH*leftAxisCount;
|
31
|
+
if (leftAxisCount > 0) {
|
32
|
+
marginLeft += 5;
|
33
|
+
}
|
34
|
+
|
35
|
+
const { dates } = useGlobalBounds(stateController);
|
36
|
+
|
37
|
+
const currentBoundCalculator = useBoundCalculator(stateController);
|
38
|
+
const { hasPreviousBounds, hasNextBounds } = useBoundHistory(stateController);
|
39
|
+
|
40
|
+
const customBoundSelectorNames = new Set(customBoundsSelectors.map(({ label }) => label));
|
41
|
+
|
42
|
+
return (
|
43
|
+
<div className={`range-selection${dates ? '' : ' range-not-dates'}`} style={{ marginRight, marginLeft }}>
|
44
|
+
<div className="range-buttons">
|
45
|
+
{
|
46
|
+
showingOptions &&
|
47
|
+
<Options
|
48
|
+
stateController={stateController}
|
49
|
+
sidebarEnabled={sidebarEnabled}
|
50
|
+
/>
|
51
|
+
}
|
52
|
+
|
53
|
+
<RangeSelectionButtonBase
|
54
|
+
className="showing-options-button"
|
55
|
+
selected={showingOptions}
|
56
|
+
onClick={() => stateController.toggleShowingOptions()}
|
57
|
+
description="Show additional options"
|
58
|
+
>
|
59
|
+
<div className="icon-container icon-container-square">
|
60
|
+
<svg focusable="false" viewBox="0 0 512 512">
|
61
|
+
<path fill="currentColor" d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z" />
|
62
|
+
</svg>
|
63
|
+
</div>
|
64
|
+
</RangeSelectionButtonBase>
|
65
|
+
|
66
|
+
<RangeSelectionButtonBase
|
67
|
+
className="range-selection-history"
|
68
|
+
selected={false}
|
69
|
+
onClick={() => stateController.previousBounds()}
|
70
|
+
disabled={!hasPreviousBounds}
|
71
|
+
>
|
72
|
+
<div className="icon-container">
|
73
|
+
<svg focusable="false" viewBox="0 0 256 512">
|
74
|
+
<path fill="currentColor" d="M31.7 239l136-136c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9L127.9 256l96.4 96.4c9.4 9.4 9.4 24.6 0 33.9L201.7 409c-9.4 9.4-24.6 9.4-33.9 0l-136-136c-9.5-9.4-9.5-24.6-.1-34z" />
|
75
|
+
</svg>
|
76
|
+
</div>
|
77
|
+
</RangeSelectionButtonBase>
|
78
|
+
|
79
|
+
<RangeSelectionButtonBase
|
80
|
+
className="range-selection-history"
|
81
|
+
selected={false}
|
82
|
+
onClick={() => stateController.nextBounds()}
|
83
|
+
disabled={!hasNextBounds}
|
84
|
+
>
|
85
|
+
<div className="icon-container">
|
86
|
+
<svg focusable="false" viewBox="0 0 256 512">
|
87
|
+
<path fill="currentColor" d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z"/>
|
88
|
+
</svg>
|
89
|
+
</div>
|
90
|
+
</RangeSelectionButtonBase>
|
91
|
+
|
92
|
+
{
|
93
|
+
customBoundsSelectors.map(({label, calculator, datesOnly}, i) => {
|
94
|
+
return (
|
95
|
+
<RangeSelectionButton
|
96
|
+
key={i}
|
97
|
+
stateController={stateController}
|
98
|
+
currentBoundCalculator={currentBoundCalculator}
|
99
|
+
boundCalculator={calculator}
|
100
|
+
disabled={datesOnly && !dates}
|
101
|
+
>
|
102
|
+
{label}
|
103
|
+
</RangeSelectionButton>
|
104
|
+
);
|
105
|
+
})
|
106
|
+
}
|
107
|
+
|
108
|
+
{
|
109
|
+
!customBoundSelectorNames.has('1m') && !customBoundsSelectorsOnly &&
|
110
|
+
<RangeSelectionButton
|
111
|
+
stateController={stateController}
|
112
|
+
currentBoundCalculator={currentBoundCalculator}
|
113
|
+
boundCalculator={BOUND_CALCULATORS.lastMinute}
|
114
|
+
disabled={!dates}
|
115
|
+
>
|
116
|
+
1m
|
117
|
+
</RangeSelectionButton>
|
118
|
+
}
|
119
|
+
|
120
|
+
{
|
121
|
+
!customBoundSelectorNames.has('10m') && !customBoundsSelectorsOnly &&
|
122
|
+
<RangeSelectionButton
|
123
|
+
stateController={stateController}
|
124
|
+
currentBoundCalculator={currentBoundCalculator}
|
125
|
+
boundCalculator={BOUND_CALCULATORS.last10Minutes}
|
126
|
+
disabled={!dates}
|
127
|
+
>
|
128
|
+
10m
|
129
|
+
</RangeSelectionButton>
|
130
|
+
}
|
131
|
+
|
132
|
+
{
|
133
|
+
!customBoundSelectorNames.has('1h') && !customBoundsSelectorsOnly &&
|
134
|
+
<RangeSelectionButton
|
135
|
+
stateController={stateController}
|
136
|
+
currentBoundCalculator={currentBoundCalculator}
|
137
|
+
boundCalculator={BOUND_CALCULATORS.lastHour}
|
138
|
+
disabled={!dates}
|
139
|
+
>
|
140
|
+
1h
|
141
|
+
</RangeSelectionButton>
|
142
|
+
}
|
143
|
+
|
144
|
+
{
|
145
|
+
!customBoundSelectorNames.has('1d') && !customBoundsSelectorsOnly &&
|
146
|
+
<RangeSelectionButton
|
147
|
+
stateController={stateController}
|
148
|
+
currentBoundCalculator={currentBoundCalculator}
|
149
|
+
boundCalculator={BOUND_CALCULATORS.lastDay}
|
150
|
+
disabled={!dates}
|
151
|
+
>
|
152
|
+
1d
|
153
|
+
</RangeSelectionButton>
|
154
|
+
}
|
155
|
+
|
156
|
+
<RangeSelectionButton
|
157
|
+
stateController={stateController}
|
158
|
+
currentBoundCalculator={currentBoundCalculator}
|
159
|
+
boundCalculator={BOUND_CALCULATORS.all}
|
160
|
+
disabled={false}
|
161
|
+
>
|
162
|
+
All
|
163
|
+
</RangeSelectionButton>
|
164
|
+
</div>
|
165
|
+
</div>
|
166
|
+
);
|
167
|
+
|
168
|
+
}
|
169
|
+
|
170
|
+
RangeSelection.propTypes = {
|
171
|
+
stateController: PropTypes.instanceOf(StateController).isRequired,
|
172
|
+
customBoundsSelectors: CustomPropTypes.CustomBoundsSelectors.isRequired,
|
173
|
+
customBoundsSelectorsOnly: PropTypes.bool,
|
174
|
+
sidebarEnabled: PropTypes.bool
|
175
|
+
};
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import RangeSelectionButtonBase from './range_selection_button_base';
|
4
|
+
import StateController from '../state/state_controller';
|
5
|
+
|
6
|
+
export default React.memo(RangeSelectionButton);
|
7
|
+
|
8
|
+
function RangeSelectionButton({ stateController, currentBoundCalculator, boundCalculator, children, disabled }) {
|
9
|
+
return (
|
10
|
+
<RangeSelectionButtonBase
|
11
|
+
selected={currentBoundCalculator === boundCalculator}
|
12
|
+
onClick={() => stateController.boundCalculator = boundCalculator}
|
13
|
+
disabled={disabled}
|
14
|
+
>
|
15
|
+
{children}
|
16
|
+
</RangeSelectionButtonBase>
|
17
|
+
);
|
18
|
+
}
|
19
|
+
|
20
|
+
RangeSelectionButton.propTypes = {
|
21
|
+
stateController: PropTypes.instanceOf(StateController).isRequired,
|
22
|
+
boundCalculator: PropTypes.func.isRequired,
|
23
|
+
currentBoundCalculator: PropTypes.func.isRequired,
|
24
|
+
children: PropTypes.node.isRequired,
|
25
|
+
disabled: PropTypes.bool
|
26
|
+
};
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
|
4
|
+
export default React.memo(RangeSelectionButtonBase);
|
5
|
+
|
6
|
+
function RangeSelectionButtonBase({ selected, disabled, className, onClick, children, description }) {
|
7
|
+
const classes = [
|
8
|
+
'range-button'
|
9
|
+
];
|
10
|
+
|
11
|
+
if (selected) {
|
12
|
+
classes.push('range-button-selected');
|
13
|
+
}
|
14
|
+
|
15
|
+
if (disabled) {
|
16
|
+
classes.push('range-button-disabled');
|
17
|
+
}
|
18
|
+
|
19
|
+
if (className) {
|
20
|
+
classes.push(className);
|
21
|
+
}
|
22
|
+
|
23
|
+
if (description) {
|
24
|
+
classes.push('option-tooltip');
|
25
|
+
}
|
26
|
+
|
27
|
+
return (
|
28
|
+
<div
|
29
|
+
className={classes.join(' ')}
|
30
|
+
onClick={onClick && ((event) => disabled || onClick(event))}
|
31
|
+
>
|
32
|
+
{children}
|
33
|
+
|
34
|
+
{
|
35
|
+
description &&
|
36
|
+
<div className="option-tooltip-text">
|
37
|
+
{description}
|
38
|
+
</div>
|
39
|
+
}
|
40
|
+
</div>
|
41
|
+
);
|
42
|
+
}
|
43
|
+
|
44
|
+
RangeSelectionButtonBase.propTypes = {
|
45
|
+
selected: PropTypes.bool.isRequired,
|
46
|
+
onClick: PropTypes.func,
|
47
|
+
children: PropTypes.node.isRequired,
|
48
|
+
disabled: PropTypes.bool,
|
49
|
+
className: PropTypes.string,
|
50
|
+
description: PropTypes.string
|
51
|
+
};
|