@pie-lib/graphing-solution-set 2.16.0-beta.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 (151) hide show
  1. package/CHANGELOG.json +1 -0
  2. package/CHANGELOG.md +16 -0
  3. package/LICENSE.md +5 -0
  4. package/NEXT.CHANGELOG.json +1 -0
  5. package/lib/__tests__/graph-with-controls.test.js +191 -0
  6. package/lib/__tests__/graph.test.js +290 -0
  7. package/lib/__tests__/grid.test.js +40 -0
  8. package/lib/__tests__/labels.test.js +59 -0
  9. package/lib/__tests__/mark-label.test.js +154 -0
  10. package/lib/__tests__/toggle-bar.test.js +54 -0
  11. package/lib/__tests__/tool-menu.test.js +43 -0
  12. package/lib/__tests__/undo-redo.test.js +42 -0
  13. package/lib/__tests__/use-debounce.test.js +28 -0
  14. package/lib/__tests__/utils.js +72 -0
  15. package/lib/__tests__/utils.test.js +133 -0
  16. package/lib/axis/__tests__/arrow.test.js +68 -0
  17. package/lib/axis/__tests__/axes.test.js +214 -0
  18. package/lib/axis/arrow.js +115 -0
  19. package/lib/axis/axes.js +415 -0
  20. package/lib/axis/index.js +26 -0
  21. package/lib/bg.js +139 -0
  22. package/lib/container/actions.js +24 -0
  23. package/lib/container/index.js +166 -0
  24. package/lib/container/marks.js +27 -0
  25. package/lib/container/middleware.js +25 -0
  26. package/lib/container/reducer.js +25 -0
  27. package/lib/coordinates-label.js +109 -0
  28. package/lib/graph-with-controls.js +372 -0
  29. package/lib/graph.js +419 -0
  30. package/lib/grid-setup.js +462 -0
  31. package/lib/grid.js +176 -0
  32. package/lib/index.js +51 -0
  33. package/lib/labels.js +299 -0
  34. package/lib/mark-label.js +208 -0
  35. package/lib/toggle-bar.js +336 -0
  36. package/lib/tool-menu.js +325 -0
  37. package/lib/tools/index.js +29 -0
  38. package/lib/tools/line/__tests__/component.test.js +56 -0
  39. package/lib/tools/line/component.js +106 -0
  40. package/lib/tools/line/index.js +16 -0
  41. package/lib/tools/polygon/__tests__/component.test.js +245 -0
  42. package/lib/tools/polygon/__tests__/index.test.js +95 -0
  43. package/lib/tools/polygon/__tests__/line.test.js +43 -0
  44. package/lib/tools/polygon/__tests__/polygon.test.js +73 -0
  45. package/lib/tools/polygon/component.js +457 -0
  46. package/lib/tools/polygon/index.js +106 -0
  47. package/lib/tools/polygon/line.js +151 -0
  48. package/lib/tools/polygon/polygon.js +171 -0
  49. package/lib/tools/shared/__tests__/arrow-head.test.js +62 -0
  50. package/lib/tools/shared/arrow-head.js +75 -0
  51. package/lib/tools/shared/line/__tests__/index.test.js +291 -0
  52. package/lib/tools/shared/line/__tests__/line-path.test.js +78 -0
  53. package/lib/tools/shared/line/__tests__/with-root-edge.test.js +122 -0
  54. package/lib/tools/shared/line/index.js +637 -0
  55. package/lib/tools/shared/line/line-path.js +145 -0
  56. package/lib/tools/shared/line/with-root-edge.js +155 -0
  57. package/lib/tools/shared/point/__tests__/arrow-point.test.js +137 -0
  58. package/lib/tools/shared/point/__tests__/base-point.test.js +134 -0
  59. package/lib/tools/shared/point/arrow-point.js +113 -0
  60. package/lib/tools/shared/point/arrow.js +96 -0
  61. package/lib/tools/shared/point/base-point.js +151 -0
  62. package/lib/tools/shared/point/index.js +94 -0
  63. package/lib/tools/shared/styles.js +49 -0
  64. package/lib/tools/shared/types.js +19 -0
  65. package/lib/undo-redo.js +107 -0
  66. package/lib/use-debounce.js +32 -0
  67. package/lib/utils.js +314 -0
  68. package/package.json +50 -0
  69. package/src/__tests__/__snapshots__/graph-with-controls.test.jsx.snap +114 -0
  70. package/src/__tests__/__snapshots__/graph.test.jsx.snap +213 -0
  71. package/src/__tests__/__snapshots__/grid.test.jsx.snap +54 -0
  72. package/src/__tests__/__snapshots__/labels.test.jsx.snap +30 -0
  73. package/src/__tests__/__snapshots__/mark-label.test.jsx.snap +37 -0
  74. package/src/__tests__/__snapshots__/toggle-bar.test.jsx.snap +7 -0
  75. package/src/__tests__/__snapshots__/tool-menu.test.jsx.snap +35 -0
  76. package/src/__tests__/__snapshots__/undo-redo.test.jsx.snap +15 -0
  77. package/src/__tests__/graph-with-controls.test.jsx +131 -0
  78. package/src/__tests__/graph.test.jsx +230 -0
  79. package/src/__tests__/grid.test.jsx +20 -0
  80. package/src/__tests__/labels.test.jsx +38 -0
  81. package/src/__tests__/mark-label.test.jsx +68 -0
  82. package/src/__tests__/toggle-bar.test.jsx +36 -0
  83. package/src/__tests__/tool-menu.test.jsx +29 -0
  84. package/src/__tests__/undo-redo.test.jsx +25 -0
  85. package/src/__tests__/use-debounce.test.js +21 -0
  86. package/src/__tests__/utils.js +38 -0
  87. package/src/__tests__/utils.test.js +151 -0
  88. package/src/axis/__tests__/__snapshots__/arrow.test.jsx.snap +33 -0
  89. package/src/axis/__tests__/__snapshots__/axes.test.jsx.snap +122 -0
  90. package/src/axis/__tests__/arrow.test.jsx +39 -0
  91. package/src/axis/__tests__/axes.test.jsx +220 -0
  92. package/src/axis/arrow.jsx +62 -0
  93. package/src/axis/axes.jsx +307 -0
  94. package/src/axis/index.js +2 -0
  95. package/src/bg.jsx +96 -0
  96. package/src/container/actions.js +8 -0
  97. package/src/container/index.jsx +86 -0
  98. package/src/container/marks.js +14 -0
  99. package/src/container/middleware.js +7 -0
  100. package/src/container/reducer.js +5 -0
  101. package/src/coordinates-label.jsx +73 -0
  102. package/src/graph-with-controls.jsx +263 -0
  103. package/src/graph.jsx +334 -0
  104. package/src/grid-setup.jsx +427 -0
  105. package/src/grid.jsx +135 -0
  106. package/src/index.js +7 -0
  107. package/src/labels.jsx +214 -0
  108. package/src/mark-label.jsx +136 -0
  109. package/src/toggle-bar.jsx +242 -0
  110. package/src/tool-menu.jsx +294 -0
  111. package/src/tools/index.js +8 -0
  112. package/src/tools/line/__tests__/__snapshots__/component.test.jsx.snap +20 -0
  113. package/src/tools/line/__tests__/component.test.jsx +36 -0
  114. package/src/tools/line/component.jsx +77 -0
  115. package/src/tools/line/index.js +4 -0
  116. package/src/tools/polygon/__tests__/__snapshots__/component.test.jsx.snap +94 -0
  117. package/src/tools/polygon/__tests__/__snapshots__/line.test.jsx.snap +44 -0
  118. package/src/tools/polygon/__tests__/__snapshots__/polygon.test.jsx.snap +53 -0
  119. package/src/tools/polygon/__tests__/component.test.jsx +214 -0
  120. package/src/tools/polygon/__tests__/index.test.js +65 -0
  121. package/src/tools/polygon/__tests__/line.test.jsx +25 -0
  122. package/src/tools/polygon/__tests__/polygon.test.jsx +44 -0
  123. package/src/tools/polygon/component.jsx +336 -0
  124. package/src/tools/polygon/index.js +52 -0
  125. package/src/tools/polygon/line.jsx +78 -0
  126. package/src/tools/polygon/polygon.jsx +101 -0
  127. package/src/tools/shared/__tests__/__snapshots__/arrow-head.test.jsx.snap +32 -0
  128. package/src/tools/shared/__tests__/arrow-head.test.jsx +34 -0
  129. package/src/tools/shared/arrow-head.jsx +46 -0
  130. package/src/tools/shared/line/__tests__/__snapshots__/index.test.jsx.snap +360 -0
  131. package/src/tools/shared/line/__tests__/__snapshots__/line-path.test.jsx.snap +57 -0
  132. package/src/tools/shared/line/__tests__/__snapshots__/with-root-edge.test.jsx.snap +63 -0
  133. package/src/tools/shared/line/__tests__/index.test.jsx +247 -0
  134. package/src/tools/shared/line/__tests__/line-path.test.jsx +53 -0
  135. package/src/tools/shared/line/__tests__/with-root-edge.test.jsx +73 -0
  136. package/src/tools/shared/line/index.jsx +473 -0
  137. package/src/tools/shared/line/line-path.jsx +88 -0
  138. package/src/tools/shared/line/with-root-edge.jsx +97 -0
  139. package/src/tools/shared/point/__tests__/__snapshots__/arrow-point.test.jsx.snap +55 -0
  140. package/src/tools/shared/point/__tests__/__snapshots__/base-point.test.jsx.snap +43 -0
  141. package/src/tools/shared/point/__tests__/arrow-point.test.jsx +87 -0
  142. package/src/tools/shared/point/__tests__/base-point.test.jsx +84 -0
  143. package/src/tools/shared/point/arrow-point.jsx +60 -0
  144. package/src/tools/shared/point/arrow.jsx +40 -0
  145. package/src/tools/shared/point/base-point.jsx +86 -0
  146. package/src/tools/shared/point/index.jsx +60 -0
  147. package/src/tools/shared/styles.js +20 -0
  148. package/src/tools/shared/types.js +8 -0
  149. package/src/undo-redo.jsx +47 -0
  150. package/src/use-debounce.js +13 -0
  151. package/src/utils.js +234 -0
@@ -0,0 +1,473 @@
1
+ import React from 'react';
2
+ import isEqual from 'lodash/isEqual';
3
+ import cloneDeep from 'lodash/cloneDeep';
4
+ import { BasePoint } from '../point';
5
+ import { types, utils, gridDraggable, trig } from '@pie-lib/plot';
6
+ import PropTypes from 'prop-types';
7
+ import { disabled, correct, incorrect, missing } from '../styles';
8
+ import ReactDOM from 'react-dom';
9
+ import MarkLabel from '../../../mark-label';
10
+ import isEmpty from 'lodash/isEmpty';
11
+ import { color } from '@pie-lib/render-ui';
12
+ import { getMiddleOfTwoPoints, equalPoints, sameAxes } from '../../../utils';
13
+
14
+ export const lineTool = (type, Component) => () => ({
15
+ type,
16
+ Component,
17
+ addPoint: (point, mark) => {
18
+ if (mark && equalPoints(mark.root, point)) {
19
+ return mark;
20
+ }
21
+
22
+ if (!mark) {
23
+ return {
24
+ type,
25
+ building: true,
26
+ from: point,
27
+ };
28
+ }
29
+
30
+ if (equalPoints(point, mark.from)) {
31
+ return { ...mark };
32
+ }
33
+
34
+ return { ...mark, building: false, to: point };
35
+ },
36
+ });
37
+
38
+ export const lineToolComponent = (Component) => {
39
+ return class LineToolComponent extends React.Component {
40
+ static propTypes = {
41
+ ...types.ToolPropTypeFields,
42
+ graphProps: types.GraphPropsType.isRequired,
43
+ };
44
+
45
+ constructor(props) {
46
+ super(props);
47
+ this.state = {};
48
+ }
49
+
50
+ startDrag = () => this.setState({ mark: { ...this.props.mark } });
51
+
52
+ stopDrag = () => {
53
+ const { onChange, mark } = this.props;
54
+ const update = { ...this.state.mark };
55
+
56
+ this.setState({ mark: undefined }, () => {
57
+ const { type } = update;
58
+ let shouldNotChange =
59
+ type &&
60
+ (type === 'parabola' || type === 'sine' || type === 'absolute' || type === 'exponential') &&
61
+ sameAxes(update.from, update.to);
62
+ if (!shouldNotChange && type && type === 'exponential' && update.from && update.to) {
63
+ shouldNotChange = update.from.y === 0 || update.to.y === 0 || update.from.y * update.to.y < 0;
64
+ }
65
+ if (!isEqual(mark, update) && !shouldNotChange) {
66
+ onChange(mark, update);
67
+ }
68
+ });
69
+ };
70
+
71
+ changeMark = ({ from, to, middle }) => {
72
+ let mark = { ...this.state.mark, from, to };
73
+
74
+ if (middle) {
75
+ mark = { ...mark, middle };
76
+ }
77
+
78
+ this.setState({ mark });
79
+ };
80
+
81
+ changeMarkProps = ({ from, to, middle }) => {
82
+ const { onChange, mark } = this.props;
83
+ let update = { ...mark, ...this.state.mark };
84
+
85
+ if (from) {
86
+ update = { ...update, from };
87
+ }
88
+
89
+ if (to) {
90
+ update = { ...update, to };
91
+ }
92
+
93
+ if (middle) {
94
+ update = { ...update, middle };
95
+ }
96
+
97
+ if (!isEqual(mark, update)) {
98
+ onChange(mark, update);
99
+ }
100
+ };
101
+
102
+ render() {
103
+ const { graphProps, onClick, labelNode, labelModeEnabled, coordinatesOnHover, gssLineData } = this.props;
104
+ const mark = this.state.mark ? this.state.mark : this.props.mark;
105
+
106
+ const from = cloneDeep(mark.from);
107
+ const to = cloneDeep(mark.to);
108
+ const middle = cloneDeep(mark.middle);
109
+
110
+ // SET DISABLED
111
+ // if it's a background mark, we need to force disable it
112
+ if (from && mark.isBackground) {
113
+ from.disabled = true;
114
+ }
115
+
116
+ if (to && mark.isBackground) {
117
+ to.disabled = true;
118
+ }
119
+
120
+ if (middle && mark.isBackground) {
121
+ middle.disabled = true;
122
+ }
123
+
124
+ return (
125
+ <Component
126
+ disabled={mark.disabled}
127
+ coordinatesOnHover={coordinatesOnHover}
128
+ correctness={mark.correctness}
129
+ from={from}
130
+ to={to}
131
+ middle={middle}
132
+ graphProps={graphProps}
133
+ fill={mark && mark.fill}
134
+ gssLineData={gssLineData}
135
+ onChange={this.changeMark}
136
+ changeMarkProps={this.changeMarkProps}
137
+ onClick={onClick}
138
+ onDragStart={this.startDrag}
139
+ onDragStop={this.stopDrag}
140
+ labelNode={labelNode}
141
+ labelModeEnabled={labelModeEnabled}
142
+ />
143
+ );
144
+ }
145
+ };
146
+ };
147
+
148
+ const dragOpts = () => ({
149
+ bounds: (props, { domain, range }) => {
150
+ const area = utils.lineToArea(props.from, props.to);
151
+ return utils.bounds(area, domain, range);
152
+ },
153
+ anchorPoint: (props) => {
154
+ const { from } = props;
155
+ return from;
156
+ },
157
+ fromDelta: (props, delta) => {
158
+ const { from, to } = props;
159
+ return {
160
+ from: utils.point(from).add(utils.point(delta)),
161
+ to: utils.point(to).add(utils.point(delta)),
162
+ };
163
+ },
164
+ });
165
+
166
+ export const lineBase = (Comp, opts) => {
167
+ const DraggableComp = gridDraggable(dragOpts())(Comp);
168
+
169
+ const FromPoint = opts && opts.from ? opts.from : BasePoint;
170
+ const ToPoint = opts && opts.to ? opts.to : BasePoint;
171
+
172
+ class LineBase extends React.Component {
173
+ static propTypes = {
174
+ coordinatesOnHover: PropTypes.bool,
175
+ graphProps: types.GraphPropsType,
176
+ from: types.PointType,
177
+ to: types.PointType,
178
+ middle: types.PointType,
179
+ onChange: PropTypes.func,
180
+ onDragStart: PropTypes.func,
181
+ onDragStop: PropTypes.func,
182
+ onClick: PropTypes.func,
183
+ correctness: PropTypes.string,
184
+ fill: PropTypes.string,
185
+ gssLineData: PropTypes.object,
186
+ disabled: PropTypes.bool,
187
+ labelNode: PropTypes.object,
188
+ labelModeEnabled: PropTypes.bool,
189
+ changeMarkProps: PropTypes.func,
190
+ };
191
+
192
+ onChangePoint = (point) => {
193
+ const { middle, onChange } = this.props;
194
+ const { from, to } = point;
195
+
196
+ // because point.from.label and point.to.label can be different
197
+ if (!equalPoints(from, to)) {
198
+ if (middle) {
199
+ point.middle = { ...middle, ...getMiddleOfTwoPoints(from, to) };
200
+ }
201
+
202
+ onChange(point);
203
+ }
204
+ };
205
+
206
+ dragComp = ({ from: draggedFrom, to: draggedTo }) => {
207
+ const { from, to, onChange, middle } = this.props;
208
+
209
+ if (from.label) {
210
+ draggedFrom.label = from.label;
211
+ }
212
+
213
+ if (to.label) {
214
+ draggedTo.label = to.label;
215
+ }
216
+
217
+ const updated = { from: draggedFrom, to: draggedTo };
218
+
219
+ if (middle) {
220
+ updated.middle = { ...middle, ...getMiddleOfTwoPoints(draggedFrom, draggedTo) };
221
+ }
222
+
223
+ onChange(updated);
224
+ };
225
+
226
+ dragFrom = (draggedFrom) => {
227
+ const { from, to } = this.props;
228
+
229
+ if (from.label) {
230
+ draggedFrom.label = from.label;
231
+ }
232
+
233
+ if (!equalPoints(draggedFrom, to)) {
234
+ this.onChangePoint({ from: draggedFrom, to: to });
235
+ }
236
+ };
237
+
238
+ dragTo = (draggedTo) => {
239
+ const { from, to } = this.props;
240
+
241
+ if (to.label) {
242
+ draggedTo.label = to.label;
243
+ }
244
+
245
+ if (!equalPoints(from, draggedTo)) {
246
+ this.onChangePoint({ from: from, to: draggedTo });
247
+ }
248
+ };
249
+
250
+ labelChange = (point, type) => {
251
+ const { changeMarkProps } = this.props;
252
+ const update = { ...point };
253
+
254
+ if (!point.label || isEmpty(point.label)) {
255
+ delete update.label;
256
+ }
257
+
258
+ changeMarkProps({ [type]: update });
259
+ };
260
+
261
+ clickPoint = (point, type, data) => {
262
+ const { changeMarkProps, disabled, from, to, labelModeEnabled, onClick } = this.props;
263
+
264
+ if (!labelModeEnabled) {
265
+ onClick(point || data);
266
+ return;
267
+ }
268
+
269
+ if (disabled) {
270
+ return;
271
+ }
272
+
273
+ if (type === 'middle' && !point && from && to) {
274
+ point = { ...point, ...getMiddleOfTwoPoints(from, to) };
275
+ }
276
+
277
+ changeMarkProps({ from, to, [type]: { label: '', ...point } });
278
+
279
+ if (this.input[type]) {
280
+ this.input[type].focus();
281
+ }
282
+ };
283
+
284
+ // IMPORTANT, do not remove
285
+ input = {};
286
+
287
+ render() {
288
+ const {
289
+ coordinatesOnHover,
290
+ graphProps,
291
+ fill,
292
+ gssLineData,
293
+ onDragStart,
294
+ onDragStop,
295
+ from,
296
+ to,
297
+ middle,
298
+ disabled,
299
+ correctness,
300
+ onClick,
301
+ labelNode,
302
+ labelModeEnabled,
303
+ } = this.props;
304
+ let common = { graphProps, fill, onDragStart, onDragStop, disabled, correctness, onClick };
305
+ if (gssLineData && gssLineData.selectedTool === 'solutionSet') {
306
+ //removing dragging option for line if solution set is clicked.
307
+ common = { graphProps, fill, disabled, correctness, onClick };
308
+ }
309
+ const angle = to ? trig.toDegrees(trig.angle(from, to)) : 0;
310
+
311
+ let fromLabelNode = null;
312
+ let toLabelNode = null;
313
+ let lineLabelNode = null;
314
+
315
+ if (labelNode) {
316
+ if (from && from.hasOwnProperty('label')) {
317
+ fromLabelNode = ReactDOM.createPortal(
318
+ <MarkLabel
319
+ inputRef={(r) => (this.input.from = r)}
320
+ disabled={!labelModeEnabled}
321
+ mark={from}
322
+ graphProps={graphProps}
323
+ onChange={(label) => this.labelChange({ ...from, label }, 'from')}
324
+ />,
325
+ labelNode,
326
+ );
327
+ }
328
+
329
+ if (to && to.hasOwnProperty('label')) {
330
+ toLabelNode = ReactDOM.createPortal(
331
+ <MarkLabel
332
+ inputRef={(r) => (this.input.to = r)}
333
+ disabled={!labelModeEnabled}
334
+ mark={to}
335
+ graphProps={graphProps}
336
+ onChange={(label) => this.labelChange({ ...to, label }, 'to')}
337
+ />,
338
+ labelNode,
339
+ );
340
+ }
341
+
342
+ if (middle && middle.hasOwnProperty('label')) {
343
+ lineLabelNode = ReactDOM.createPortal(
344
+ <MarkLabel
345
+ inputRef={(r) => (this.input.middle = r)}
346
+ disabled={!labelModeEnabled}
347
+ mark={middle}
348
+ graphProps={graphProps}
349
+ onChange={(label) => this.labelChange({ ...middle, label }, 'middle')}
350
+ />,
351
+ labelNode,
352
+ );
353
+ }
354
+ }
355
+ if (gssLineData && gssLineData.selectedTool === 'solutionSet') {
356
+ //removing dragging feature of line and point if solution set is true.
357
+ return (
358
+ <g>
359
+ {to && <DraggableComp from={from} to={to} middle={middle} {...common} />}
360
+ {lineLabelNode}
361
+
362
+ <FromPoint
363
+ x={from.x}
364
+ y={from.y}
365
+ labelNode={labelNode}
366
+ coordinatesOnHover={coordinatesOnHover}
367
+ {...common}
368
+ />
369
+ {fromLabelNode}
370
+
371
+ {to && (
372
+ <ToPoint
373
+ x={to.x}
374
+ y={to.y}
375
+ angle={angle} //angle + 45}
376
+ labelNode={labelNode}
377
+ coordinatesOnHover={coordinatesOnHover}
378
+ {...common}
379
+ />
380
+ )}
381
+ {toLabelNode}
382
+ </g>
383
+ );
384
+ } else {
385
+ return (
386
+ <g>
387
+ {to && (
388
+ <DraggableComp
389
+ from={from}
390
+ to={to}
391
+ middle={middle}
392
+ onDrag={this.dragComp}
393
+ {...common}
394
+ onClick={(data) => this.clickPoint(middle, 'middle', data)}
395
+ />
396
+ )}
397
+ {lineLabelNode}
398
+
399
+ <FromPoint
400
+ x={from.x}
401
+ y={from.y}
402
+ labelNode={labelNode}
403
+ coordinatesOnHover={coordinatesOnHover}
404
+ onDrag={this.dragFrom}
405
+ {...common}
406
+ onClick={(data) => this.clickPoint(from, 'from', data)}
407
+ />
408
+ {fromLabelNode}
409
+
410
+ {to && (
411
+ <ToPoint
412
+ x={to.x}
413
+ y={to.y}
414
+ angle={angle} //angle + 45}
415
+ labelNode={labelNode}
416
+ coordinatesOnHover={coordinatesOnHover}
417
+ onDrag={this.dragTo}
418
+ {...common}
419
+ onClick={(data) => this.clickPoint(to, 'to', data)}
420
+ />
421
+ )}
422
+ {toLabelNode}
423
+ </g>
424
+ );
425
+ }
426
+ }
427
+ }
428
+
429
+ return LineBase;
430
+ };
431
+
432
+ export const styles = {
433
+ line: () => ({
434
+ fill: 'transparent',
435
+ stroke: color.defaults.BLACK,
436
+ strokeWidth: 4,
437
+ transition: 'stroke 200ms ease-in, stroke-width 200ms ease-in',
438
+ '&:hover': {
439
+ strokeWidth: 6,
440
+ stroke: color.defaults.BLACK,
441
+ },
442
+ }),
443
+ dashedLine: () => ({
444
+ fill: 'transparent',
445
+ stroke: color.defaults.BLACK,
446
+ strokeWidth: 4,
447
+ transition: 'stroke 200ms ease-in, stroke-width 200ms ease-in',
448
+ '&:hover': {
449
+ strokeWidth: 6,
450
+ stroke: color.defaults.BLACK,
451
+ },
452
+ strokeDasharray: '3,3',
453
+ }),
454
+ arrow: () => ({
455
+ fill: color.defaults.SECONDARY,
456
+ }),
457
+ disabledArrow: () => ({
458
+ ...disabled(),
459
+ }),
460
+ disabled: () => ({
461
+ ...disabled('stroke'),
462
+ strokeWidth: 2,
463
+ }),
464
+ correct: (theme, key) => ({
465
+ ...correct(key),
466
+ }),
467
+ incorrect: (theme, key) => ({
468
+ ...incorrect(key),
469
+ }),
470
+ missing: (theme, key) => ({
471
+ ...missing(key),
472
+ }),
473
+ };
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { withStyles } from '@material-ui/core/styles';
4
+ import { types } from '@pie-lib/plot';
5
+ import classNames from 'classnames';
6
+ import { disabled, correct, incorrect, missing } from '../styles';
7
+ import * as vx from '@vx/shape';
8
+ import { color } from '@pie-lib/render-ui';
9
+
10
+ export class RawLinePath extends React.Component {
11
+ static propTypes = {
12
+ className: PropTypes.string,
13
+ classes: PropTypes.object,
14
+ data: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)),
15
+ graphProps: types.GraphPropsType.isRequired,
16
+ disabled: PropTypes.bool,
17
+ correctness: PropTypes.string,
18
+ from: types.PointType,
19
+ to: types.PointType,
20
+ isDragging: PropTypes.bool,
21
+ };
22
+
23
+ static defaultProps = {
24
+ from: {},
25
+ to: {},
26
+ };
27
+
28
+ render() {
29
+ /* eslint-disable no-unused-vars */
30
+ const { data, classes, className, disabled, correctness, from, to, graphProps, isDragging, ...rest } = this.props;
31
+ /* eslint-enable */
32
+
33
+ return (
34
+ <React.Fragment>
35
+ <vx.LinePath
36
+ data={data}
37
+ className={classNames(classes.drawLine, disabled && classes.disabled, classes[correctness], className)}
38
+ {...rest}
39
+ />
40
+ <vx.LinePath
41
+ data={data}
42
+ className={classNames(
43
+ classes.line,
44
+ isDragging && classes.dragging,
45
+ disabled && classes.disabled,
46
+ classes[correctness],
47
+ className,
48
+ )}
49
+ {...rest}
50
+ />
51
+ </React.Fragment>
52
+ );
53
+ }
54
+ }
55
+
56
+ const dragging = () => ({
57
+ strokeWidth: 7,
58
+ stroke: color.defaults.BLACK,
59
+ });
60
+
61
+ export const LinePath = withStyles((theme) => ({
62
+ drawLine: {
63
+ fill: 'none',
64
+ strokeWidth: 2,
65
+ stroke: color.secondaryLight(),
66
+ },
67
+ line: {
68
+ strokeWidth: 6,
69
+ fill: 'none',
70
+ transition: 'stroke-width 200ms ease-in, stroke 200ms ease-in',
71
+ stroke: 'transparent',
72
+ '&:hover': dragging(theme),
73
+ },
74
+ dragging: dragging(theme),
75
+ disabled: {
76
+ ...disabled('stroke'),
77
+ strokeWidth: 2,
78
+ },
79
+ correct: {
80
+ ...correct('stroke'),
81
+ },
82
+ incorrect: {
83
+ ...incorrect('stroke'),
84
+ },
85
+ missing: {
86
+ ...missing('stroke'),
87
+ },
88
+ }))(RawLinePath);
@@ -0,0 +1,97 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { types } from '@pie-lib/plot';
4
+ import { LinePath } from './line-path';
5
+ import { curveMonotoneX } from '@vx/curve';
6
+ import { lineBase, lineToolComponent } from './index';
7
+
8
+ const toRootEdge = (m) => {
9
+ const out = { ...m };
10
+ out.root = { ...m.from };
11
+ out.edge = m.to ? { ...m.to } : undefined;
12
+ delete out.from;
13
+ delete out.to;
14
+ return out;
15
+ };
16
+
17
+ const toFromTo = (m) => {
18
+ const out = { ...m };
19
+ out.from = { ...m.root };
20
+ out.to = m.edge ? { ...m.edge } : undefined;
21
+ delete out.root;
22
+ delete out.edge;
23
+ return out;
24
+ };
25
+
26
+ export const rootEdgeToFromToWrapper = (BaseComp) => {
27
+ const Wrapper = (props) => {
28
+ const m = toFromTo(props.mark);
29
+
30
+ const onChange = (current, next) => {
31
+ props.onChange(toRootEdge(current), toRootEdge(next));
32
+ };
33
+
34
+ return <BaseComp {...props} mark={m} onChange={onChange} />;
35
+ };
36
+
37
+ Wrapper.propTypes = {
38
+ onChange: PropTypes.func,
39
+ mark: PropTypes.object,
40
+ };
41
+
42
+ return Wrapper;
43
+ };
44
+
45
+ export const rootEdgeComponent = (RootEdgeComp) => {
46
+ const BaseComponent = lineToolComponent(RootEdgeComp);
47
+ return rootEdgeToFromToWrapper(BaseComponent);
48
+ };
49
+
50
+ const withPointsGenerationLinePath = (getPoints) => {
51
+ const LinePathComponent = (props) => {
52
+ const { graphProps, from, to, onClick, onDragStart, onDragStop, onChange, disabled, correctness, ...rest } = props;
53
+
54
+ const { dataPoints, enableCurve = true } = getPoints({
55
+ graphProps: props.graphProps,
56
+ root: from,
57
+ edge: to,
58
+ });
59
+
60
+ const raw = dataPoints.map((d) => [graphProps.scale.x(d.x), graphProps.scale.y(d.y)]);
61
+
62
+ const common = {
63
+ onClick,
64
+ graphProps,
65
+ onDragStart,
66
+ onDragStop,
67
+ onChange,
68
+ disabled,
69
+ correctness,
70
+ };
71
+
72
+ if (!enableCurve) {
73
+ return <LinePath data={raw} from={from} to={to} {...common} {...rest} />;
74
+ } else {
75
+ return <LinePath data={raw} from={from} to={to} curve={curveMonotoneX} {...common} {...rest} />;
76
+ }
77
+ };
78
+
79
+ LinePathComponent.propTypes = {
80
+ graphProps: types.GraphPropsType.isRequired,
81
+ from: types.PointType.isRequired,
82
+ to: types.PointType,
83
+ onClick: PropTypes.func,
84
+ onDragStart: PropTypes.func,
85
+ onDragStop: PropTypes.func,
86
+ onChange: PropTypes.func,
87
+ disabled: PropTypes.bool,
88
+ correctness: PropTypes.string,
89
+ };
90
+
91
+ return LinePathComponent;
92
+ };
93
+
94
+ export const withRootEdge = (getPoints) => {
95
+ const LinePathComp = withPointsGenerationLinePath(getPoints);
96
+ return lineBase(LinePathComp);
97
+ };
@@ -0,0 +1,55 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ArrowPoint snapshot renders 1`] = `
4
+ <RawArrow
5
+ className="className"
6
+ classes={
7
+ Object {
8
+ "correct": "RawArrow-correct-3",
9
+ "disabled": "RawArrow-disabled-2",
10
+ "incorrect": "RawArrow-incorrect-4",
11
+ "missing": "RawArrow-missing-5",
12
+ "point": "RawArrow-point-1",
13
+ }
14
+ }
15
+ from={
16
+ Object {
17
+ "x": 0,
18
+ "y": 0,
19
+ }
20
+ }
21
+ graphProps={
22
+ Object {
23
+ "domain": Object {
24
+ "max": 1,
25
+ "min": 0,
26
+ "step": 1,
27
+ },
28
+ "range": Object {
29
+ "max": 1,
30
+ "min": 0,
31
+ "step": 1,
32
+ },
33
+ "scale": Object {
34
+ "x": [MockFunction],
35
+ "y": [MockFunction],
36
+ },
37
+ "size": Object {
38
+ "height": 400,
39
+ "width": 400,
40
+ },
41
+ "snap": Object {
42
+ "x": [MockFunction],
43
+ "y": [MockFunction],
44
+ },
45
+ }
46
+ }
47
+ onChange={[MockFunction]}
48
+ to={
49
+ Object {
50
+ "x": 1,
51
+ "y": 1,
52
+ }
53
+ }
54
+ />
55
+ `;