@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
package/src/grapher.js
ADDED
@@ -0,0 +1,367 @@
|
|
1
|
+
import React, { useEffect, useMemo } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import CustomPropTypes from './helpers/custom_prop_types';
|
4
|
+
import GraphBody from './components/graph_body';
|
5
|
+
import './grapher.scss';
|
6
|
+
import XAxis from './components/x_axis';
|
7
|
+
import YAxis from './components/y_axis';
|
8
|
+
import RangeSelection from './components/range_selection';
|
9
|
+
import RangeGraph from './components/range_graph';
|
10
|
+
import SeriesKey from './components/series_key';
|
11
|
+
import {Y_AXIS_WIDTH} from './helpers/axis_sizes';
|
12
|
+
import GraphTitle from './components/graph_title';
|
13
|
+
import {LINE_COLORS} from './helpers/colors';
|
14
|
+
import StateController from './state/state_controller';
|
15
|
+
import {useDraggingY, useLeftAxes, useRightAxes, useShowingSidebar, useTheme} from './state/hooks';
|
16
|
+
import Sidebar from './components/sidebar';
|
17
|
+
import SyncPool from './state/sync_pool.js';
|
18
|
+
import BOUND_CALCULATORS from './state/bound_calculators.js';
|
19
|
+
|
20
|
+
function calculateClassNamesAndStyles(props, { draggingY, theme }) {
|
21
|
+
const { fullscreen, height, width } = props;
|
22
|
+
|
23
|
+
const classNames = [
|
24
|
+
'grapher',
|
25
|
+
`grapher-${theme}`
|
26
|
+
];
|
27
|
+
|
28
|
+
const styles = {};
|
29
|
+
|
30
|
+
if (draggingY) {
|
31
|
+
classNames.push('grapher-dragging-y');
|
32
|
+
}
|
33
|
+
|
34
|
+
if (fullscreen) {
|
35
|
+
classNames.push('grapher-fullscreen');
|
36
|
+
classNames.push('grapher-fixed-height');
|
37
|
+
} else if (height) {
|
38
|
+
classNames.push('grapher-fixed-height');
|
39
|
+
if (typeof height === 'number') {
|
40
|
+
styles.height = height - 20;
|
41
|
+
} else {
|
42
|
+
styles.height = `calc(${height} - 20px)`;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
if (width) {
|
47
|
+
styles.width = width;
|
48
|
+
}
|
49
|
+
|
50
|
+
return {
|
51
|
+
styles,
|
52
|
+
classNames
|
53
|
+
};
|
54
|
+
}
|
55
|
+
|
56
|
+
export default React.memo(Grapher);
|
57
|
+
|
58
|
+
const grapherDefaultProps = {
|
59
|
+
theme: 'night',
|
60
|
+
showAxes: true,
|
61
|
+
showRangeGraph: true,
|
62
|
+
showRangeSelectors: true,
|
63
|
+
showSeriesKey: true,
|
64
|
+
showTooltips: true,
|
65
|
+
boundsSelectionEnabled: true,
|
66
|
+
customBoundsSelectors: [],
|
67
|
+
sidebarEnabled: false,
|
68
|
+
defaultShowAnnotations: true,
|
69
|
+
defaultShowOptions: true
|
70
|
+
};
|
71
|
+
|
72
|
+
function Grapher(props) {
|
73
|
+
props = {...grapherDefaultProps, ...props};
|
74
|
+
|
75
|
+
const stateController = useMemo(() => new StateController({
|
76
|
+
grapherID: props.id,
|
77
|
+
...props,
|
78
|
+
...props.stateControllerInitialization
|
79
|
+
}), []);
|
80
|
+
|
81
|
+
useEffect(() => {
|
82
|
+
if (process.env.NODE_ENV === 'development') {
|
83
|
+
window.stateController = stateController;
|
84
|
+
}
|
85
|
+
|
86
|
+
return () => {
|
87
|
+
stateController.dispose();
|
88
|
+
};
|
89
|
+
}, [stateController]);
|
90
|
+
|
91
|
+
useEffect(() => {
|
92
|
+
props.exportStateController && props.exportStateController(stateController);
|
93
|
+
}, [stateController, props.exportStateController]);
|
94
|
+
|
95
|
+
useEffect(() => {
|
96
|
+
stateController.timingFrameCount = props.timingFrameCount;
|
97
|
+
}, [stateController, props.timingFrameCount]);
|
98
|
+
|
99
|
+
useEffect(() => {
|
100
|
+
props.onRenderTime && stateController.on('render_time', props.onRenderTime);
|
101
|
+
|
102
|
+
return () => {
|
103
|
+
props.onRenderTime && stateController.off('render_time', props.onRenderTime);
|
104
|
+
};
|
105
|
+
}, [stateController, props.onRenderTime]);
|
106
|
+
|
107
|
+
useEffect(() => {
|
108
|
+
stateController.setSeries(props.series);
|
109
|
+
}, [stateController, props.series]);
|
110
|
+
|
111
|
+
useEffect(() => {
|
112
|
+
stateController.theme = props.theme;
|
113
|
+
}, [stateController, props.theme]);
|
114
|
+
|
115
|
+
const theme = useTheme(stateController);
|
116
|
+
const bigLabels = props.bigLabels || theme === 'export';
|
117
|
+
|
118
|
+
const defaultLineWidth = props.defaultLineWidth || (theme === 'export' ? 3 : undefined);
|
119
|
+
useEffect(() => {
|
120
|
+
stateController.defaultLineWidth = defaultLineWidth;
|
121
|
+
}, [stateController, defaultLineWidth]);
|
122
|
+
|
123
|
+
useEffect(() => {
|
124
|
+
stateController.percentile = props.percentile;
|
125
|
+
}, [stateController, props.percentile]);
|
126
|
+
|
127
|
+
useEffect(() => {
|
128
|
+
stateController.customBoundsSelectors = props.customBoundsSelectors;
|
129
|
+
}, [stateController, props.customBoundsSelectors]);
|
130
|
+
|
131
|
+
useEffect(() => {
|
132
|
+
stateController.annotations = props.annotations;
|
133
|
+
}, [stateController, props.annotations]);
|
134
|
+
|
135
|
+
useEffect(() => {
|
136
|
+
if (!props.onAxisChange) {
|
137
|
+
return () => {};
|
138
|
+
}
|
139
|
+
|
140
|
+
stateController.on('exported_axes_changed', props.onAxisChange);
|
141
|
+
return () => {
|
142
|
+
stateController.off('exported_axes_changed', props.onAxisChange);
|
143
|
+
};
|
144
|
+
}, [stateController, props.onAxisChange]);
|
145
|
+
|
146
|
+
useEffect(() => {
|
147
|
+
stateController.primaryRenderer.resize();
|
148
|
+
}, [props.height]);
|
149
|
+
|
150
|
+
const draggingY = useDraggingY(stateController);
|
151
|
+
|
152
|
+
const {styles, classNames} = calculateClassNamesAndStyles(props, { draggingY, theme });
|
153
|
+
|
154
|
+
const rightAxes = useRightAxes(stateController);
|
155
|
+
const leftAxes = useLeftAxes(stateController);
|
156
|
+
const showingSidebar = useShowingSidebar(stateController);
|
157
|
+
|
158
|
+
const showAxisColors = typeof props.showAxisColors === 'boolean' ? props.showAxisColors : (theme !== 'export');
|
159
|
+
const showGrid = typeof props.showGrid === 'boolean' ? props.showGrid : (theme !== 'export');
|
160
|
+
|
161
|
+
const commonYAxisProps = {
|
162
|
+
stateController,
|
163
|
+
showAxes: props.showAxes,
|
164
|
+
showGrid,
|
165
|
+
showSeriesKey: props.showSeriesKey,
|
166
|
+
bodyHeight: props.bodyHeight,
|
167
|
+
theme,
|
168
|
+
grapherID: props.id,
|
169
|
+
dragPositionYOffset: props.dragPositionYOffset,
|
170
|
+
showAxisColors,
|
171
|
+
bigLabels
|
172
|
+
};
|
173
|
+
|
174
|
+
return (
|
175
|
+
<div className={classNames.join(' ')} style={styles} data-grapher-id={props.id}>
|
176
|
+
{
|
177
|
+
props.title &&
|
178
|
+
<GraphTitle title={props.title} />
|
179
|
+
}
|
180
|
+
|
181
|
+
<div className="grapher-primary-container-outer">
|
182
|
+
{
|
183
|
+
showingSidebar &&
|
184
|
+
<Sidebar stateController={stateController} />
|
185
|
+
}
|
186
|
+
|
187
|
+
<div className="grapher-primary-container-body">
|
188
|
+
{
|
189
|
+
props.showSeriesKey &&
|
190
|
+
<SeriesKey
|
191
|
+
stateController={stateController}
|
192
|
+
draggingY={draggingY}
|
193
|
+
theme={props.theme}
|
194
|
+
grapherID={props.id}
|
195
|
+
dragPositionYOffset={props.dragPositionYOffset}
|
196
|
+
/>
|
197
|
+
}
|
198
|
+
|
199
|
+
{
|
200
|
+
props.showRangeSelectors &&
|
201
|
+
<RangeSelection
|
202
|
+
stateController={stateController}
|
203
|
+
customBoundsSelectors={props.customBoundsSelectors}
|
204
|
+
customBoundsSelectorsOnly={props.customBoundsSelectorsOnly}
|
205
|
+
sidebarEnabled={props.sidebarEnabled}
|
206
|
+
/>
|
207
|
+
}
|
208
|
+
|
209
|
+
<div className="grapher-main-row">
|
210
|
+
{
|
211
|
+
draggingY &&
|
212
|
+
<div
|
213
|
+
className="axis y-axis"
|
214
|
+
data-axis-index="new-left"
|
215
|
+
data-grapher-id={props.id}
|
216
|
+
style={{
|
217
|
+
width: Y_AXIS_WIDTH,
|
218
|
+
height: typeof props.bodyHeight === 'number' ? props.bodyHeight : undefined
|
219
|
+
}}
|
220
|
+
/>
|
221
|
+
}
|
222
|
+
|
223
|
+
{
|
224
|
+
leftAxes.map((axis, i) => {
|
225
|
+
return <YAxis
|
226
|
+
key={i}
|
227
|
+
axis={axis}
|
228
|
+
sideIndex={leftAxes.length - i - 1}
|
229
|
+
{...commonYAxisProps}
|
230
|
+
/>;
|
231
|
+
})
|
232
|
+
}
|
233
|
+
|
234
|
+
<div className="central-container">
|
235
|
+
<GraphBody
|
236
|
+
stateController={stateController}
|
237
|
+
webgl={props.webgl}
|
238
|
+
bodyHeight={props.bodyHeight}
|
239
|
+
boundsSelectionEnabled={props.boundsSelectionEnabled}
|
240
|
+
showTooltips={props.showTooltips}
|
241
|
+
tooltipOptions={props.tooltipOptions}
|
242
|
+
checkIntersection={props.checkIntersection}
|
243
|
+
draggablePoints={props.draggablePoints}
|
244
|
+
onPointDrag={props.onPointDrag}
|
245
|
+
onDraggablePointsDoubleClick={props.onDraggablePointsDoubleClick}
|
246
|
+
verticalLines={props.verticalLines}
|
247
|
+
clockStyle={props.clockStyle}
|
248
|
+
timeZone={props.timeZone}
|
249
|
+
/>
|
250
|
+
|
251
|
+
<XAxis
|
252
|
+
showGrid={showGrid}
|
253
|
+
showAxes={props.showAxes}
|
254
|
+
stateController={stateController}
|
255
|
+
bigLabels={bigLabels}
|
256
|
+
xTickUnit={props.xTickUnit}
|
257
|
+
clockStyle={props.clockStyle}
|
258
|
+
timeZone={props.timeZone}
|
259
|
+
integersOnly={props.xAxisIntegersOnly}
|
260
|
+
/>
|
261
|
+
|
262
|
+
{
|
263
|
+
props.showRangeGraph &&
|
264
|
+
<div className="range-graph-container">
|
265
|
+
<RangeGraph
|
266
|
+
stateController={stateController}
|
267
|
+
webgl={props.webgl}
|
268
|
+
checkIntersection={props.checkIntersection}
|
269
|
+
markDates={props.markRangeGraphDates}
|
270
|
+
timeZone={props.timeZone}
|
271
|
+
/>
|
272
|
+
</div>
|
273
|
+
}
|
274
|
+
</div>
|
275
|
+
|
276
|
+
{
|
277
|
+
rightAxes.map((axis, i) => {
|
278
|
+
return <YAxis
|
279
|
+
key={i}
|
280
|
+
axis={axis}
|
281
|
+
sideIndex={i}
|
282
|
+
{...commonYAxisProps}
|
283
|
+
/>;
|
284
|
+
})
|
285
|
+
}
|
286
|
+
|
287
|
+
{
|
288
|
+
draggingY &&
|
289
|
+
<div
|
290
|
+
className="axis y-axis"
|
291
|
+
data-axis-index="new-right"
|
292
|
+
data-grapher-id={props.id}
|
293
|
+
style={{
|
294
|
+
width: Y_AXIS_WIDTH,
|
295
|
+
height: typeof props.bodyHeight === 'number' ? props.bodyHeight : undefined
|
296
|
+
}}
|
297
|
+
/>
|
298
|
+
}
|
299
|
+
</div>
|
300
|
+
</div>
|
301
|
+
</div>
|
302
|
+
</div>
|
303
|
+
);
|
304
|
+
}
|
305
|
+
|
306
|
+
Grapher.propTypes = {
|
307
|
+
series: CustomPropTypes.Series.isRequired,
|
308
|
+
webgl: PropTypes.bool,
|
309
|
+
requireWASM: PropTypes.bool,
|
310
|
+
checkIntersection: PropTypes.bool,
|
311
|
+
|
312
|
+
onAxisChange: PropTypes.func,
|
313
|
+
onRenderTime: PropTypes.func,
|
314
|
+
exportStateController: PropTypes.func,
|
315
|
+
timingFrameCount: PropTypes.number,
|
316
|
+
|
317
|
+
stateControllerInitialization: PropTypes.object,
|
318
|
+
syncPool: PropTypes.instanceOf(SyncPool),
|
319
|
+
id: PropTypes.string,
|
320
|
+
dragPositionYOffset: PropTypes.number,
|
321
|
+
|
322
|
+
theme: PropTypes.oneOf(['day', 'night', 'export']),
|
323
|
+
title: PropTypes.string,
|
324
|
+
fullscreen: PropTypes.bool,
|
325
|
+
bodyHeight: PropTypes.number,
|
326
|
+
height: PropTypes.number,
|
327
|
+
width: PropTypes.number,
|
328
|
+
|
329
|
+
showAxes: PropTypes.bool,
|
330
|
+
showRangeGraph: PropTypes.bool,
|
331
|
+
showRangeSelectors: PropTypes.bool,
|
332
|
+
showSeriesKey: PropTypes.bool,
|
333
|
+
showTooltips: PropTypes.bool,
|
334
|
+
showGrid: PropTypes.bool,
|
335
|
+
showAxisColors: PropTypes.bool,
|
336
|
+
bigLabels: PropTypes.bool,
|
337
|
+
xTickUnit: PropTypes.oneOf(['year']),
|
338
|
+
xAxisIntegersOnly: PropTypes.bool,
|
339
|
+
clockStyle: PropTypes.oneOf(['12h', '24h']),
|
340
|
+
timeZone: PropTypes.string, // local, utc, or a full timezone string
|
341
|
+
markRangeGraphDates: PropTypes.bool,
|
342
|
+
|
343
|
+
boundsSelectionEnabled: PropTypes.bool,
|
344
|
+
sidebarEnabled: PropTypes.bool,
|
345
|
+
|
346
|
+
percentile: PropTypes.number,
|
347
|
+
defaultShowOptions: PropTypes.bool,
|
348
|
+
defaultShowIndividualPoints: PropTypes.bool,
|
349
|
+
defaultShowSidebar: PropTypes.bool,
|
350
|
+
defaultShowAnnotations: PropTypes.bool,
|
351
|
+
defaultLineWidth: PropTypes.number,
|
352
|
+
|
353
|
+
tooltipOptions: CustomPropTypes.TooltipOptions,
|
354
|
+
|
355
|
+
customBoundsSelectors: CustomPropTypes.CustomBoundsSelectors,
|
356
|
+
customBoundsSelectorsOnly: PropTypes.bool,
|
357
|
+
defaultBoundsCalculator: PropTypes.string,
|
358
|
+
|
359
|
+
annotations: CustomPropTypes.Annotations,
|
360
|
+
draggablePoints: CustomPropTypes.DraggablePoints,
|
361
|
+
onPointDrag: PropTypes.func,
|
362
|
+
onDraggablePointsDoubleClick: PropTypes.func,
|
363
|
+
verticalLines: CustomPropTypes.VerticalLines
|
364
|
+
};
|
365
|
+
|
366
|
+
export const AVAILABLE_COLORS = LINE_COLORS;
|
367
|
+
export const BUILT_IN_BOUND_CALCULATORS = BOUND_CALCULATORS;
|