@pie-lib/graphing-solution-set 2.30.1-next.0 → 2.31.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.
package/esm/index.js ADDED
@@ -0,0 +1,4113 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import isEqual from 'lodash/isEqual';
4
+ import cloneDeep from 'lodash/cloneDeep';
5
+ import { types, utils, createGraphProps, Root as Root$1, gridDraggable, trig } from '@pie-lib/plot';
6
+ import debug from 'debug';
7
+ import { Axis } from '@vx/axis';
8
+ import classNames from 'classnames';
9
+ import { withStyles } from '@material-ui/core/styles';
10
+ import { withStyles as withStyles$1, Typography, Button, ExpansionPanel, ExpansionPanelSummary, ExpansionPanelDetails } from '@material-ui/core';
11
+ import head from 'lodash/head';
12
+ import tail from 'lodash/tail';
13
+ import invariant from 'invariant';
14
+ import isEmpty from 'lodash/isEmpty';
15
+ import { color, Readable, InputContainer } from '@pie-lib/render-ui';
16
+ import * as vx from '@vx/grid';
17
+ import EditableHtml from '@pie-lib/editable-html';
18
+ import { mouse, select } from 'd3-selection';
19
+ import { connect, Provider } from 'react-redux';
20
+ import { combineReducers, createStore, applyMiddleware } from 'redux';
21
+ import undoable, { ActionCreators } from 'redux-undo';
22
+ import uniq from 'lodash/uniq';
23
+ import isString from 'lodash/isString';
24
+ import Radio from '@material-ui/core/Radio';
25
+ import Button$1 from '@material-ui/core/Button';
26
+ import Translator from '@pie-lib/translator';
27
+ import chunk from 'lodash/chunk';
28
+ import 'lodash/initial';
29
+ import { fade } from '@material-ui/core/styles/colorManipulator';
30
+ import ReactDOM from 'react-dom';
31
+ import AutosizeInput from 'react-input-autosize';
32
+ import { withStyles as withStyles$2 } from '@material-ui/core/styles/index';
33
+ import InputBase from '@material-ui/core/InputBase';
34
+ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
35
+ import ExpansionPanelSummary$1 from '@material-ui/core/ExpansionPanelSummary';
36
+ import Typography$1 from '@material-ui/core/Typography';
37
+ import ExpansionPanelDetails$1 from '@material-ui/core/ExpansionPanelDetails';
38
+ import ExpansionPanel$1 from '@material-ui/core/ExpansionPanel';
39
+ import { NumberTextFieldCustom, Toggle } from '@pie-lib/config-ui';
40
+
41
+ function _extends() {
42
+ _extends = Object.assign || function (target) {
43
+ for (var i = 1; i < arguments.length; i++) {
44
+ var source = arguments[i];
45
+
46
+ for (var key in source) {
47
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
48
+ target[key] = source[key];
49
+ }
50
+ }
51
+ }
52
+
53
+ return target;
54
+ };
55
+
56
+ return _extends.apply(this, arguments);
57
+ }
58
+
59
+ function _objectWithoutPropertiesLoose(source, excluded) {
60
+ if (source == null) return {};
61
+ var target = {};
62
+ var sourceKeys = Object.keys(source);
63
+ var key, i;
64
+
65
+ for (i = 0; i < sourceKeys.length; i++) {
66
+ key = sourceKeys[i];
67
+ if (excluded.indexOf(key) >= 0) continue;
68
+ target[key] = source[key];
69
+ }
70
+
71
+ return target;
72
+ }
73
+
74
+ const style = theme => ({
75
+ root: {
76
+ fill: `var(--arrow-color, ${theme.palette.common.black})`
77
+ }
78
+ });
79
+
80
+ let Arrow$1 = class Arrow extends React.Component {
81
+ render() {
82
+ const {
83
+ x,
84
+ y,
85
+ classes,
86
+ className,
87
+ scale
88
+ } = this.props;
89
+ const names = classNames(classes.root, className);
90
+ let direction = this.props.direction || 'left';
91
+ const xv = scale.x(x);
92
+ const yv = scale.y(y);
93
+ let transform = '';
94
+
95
+ const getTransform = (x, y, rotate) => `translate(${x}, ${y}) rotate(${rotate})`;
96
+
97
+ if (direction === 'left') {
98
+ transform = getTransform(xv - 15, yv, 0);
99
+ }
100
+
101
+ if (direction === 'right') {
102
+ transform = getTransform(xv + 15, yv, 180);
103
+ }
104
+
105
+ if (direction === 'up') {
106
+ transform = getTransform(xv, yv - 15, 90);
107
+ }
108
+
109
+ if (direction === 'down') {
110
+ transform = getTransform(xv, yv + 15, 270);
111
+ }
112
+
113
+ return /*#__PURE__*/React.createElement("path", {
114
+ d: "m 0,0 8,-5 0,10 -8,-5",
115
+ transform: transform,
116
+ className: names
117
+ });
118
+ }
119
+
120
+ };
121
+ Arrow$1.propTypes = {
122
+ y: PropTypes.number,
123
+ x: PropTypes.number,
124
+ direction: PropTypes.oneOf(['left', 'right', 'up', 'down']),
125
+ classes: PropTypes.object.isRequired,
126
+ className: PropTypes.string,
127
+ scale: types.ScaleType.isRequired
128
+ };
129
+ Arrow$1.defaultProps = {
130
+ y: 0,
131
+ x: 0,
132
+ direction: 'left'
133
+ };
134
+ var Arrow$2 = withStyles(style)(Arrow$1);
135
+
136
+ const bounds = utils.bounds;
137
+ const point = utils.point; //TODO: This can be removed?
138
+
139
+ const getAngleDeg = () => 0; //TODO: This can be removed?
140
+
141
+ const arrowDimensions = () => 0;
142
+ const getTickValues = prop => {
143
+ const tickValues = [];
144
+ let tickVal = 0;
145
+
146
+ while (tickVal >= prop.min && tickValues.indexOf(tickVal) < 0) {
147
+ tickValues.push(tickVal);
148
+ tickVal = Math.round((tickVal - prop.step) * 10000) / 10000;
149
+ }
150
+
151
+ tickVal = Math.round(prop.step * 10000) / 10000;
152
+
153
+ while (tickVal <= prop.max && tickValues.indexOf(tickVal) < 0) {
154
+ tickValues.push(tickVal);
155
+ tickVal = Math.round((tickVal + prop.step) * 10000) / 10000;
156
+ } // return only ticks that are inside the min-max interval
157
+
158
+
159
+ if (tickValues) {
160
+ return tickValues.filter(tV => tV >= prop.min && tV <= prop.max);
161
+ }
162
+
163
+ return [];
164
+ };
165
+ const countWords = label => {
166
+ if (label == null || isEmpty(label)) {
167
+ return 1;
168
+ }
169
+
170
+ const words = label.split(' ');
171
+ return words.length;
172
+ }; // findLongestWord is also used in plot
173
+
174
+ const findLongestWord = label => {
175
+ let longestWord = (label || '').replace(/<[^>]+>/g, '').split(' ').sort((a, b) => b.length - a.length);
176
+ return longestWord[0].length;
177
+ }; // amountToIncreaseWidth is also used in plot
178
+
179
+ const amountToIncreaseWidth = longestWord => {
180
+ if (!longestWord) {
181
+ return 0;
182
+ }
183
+
184
+ return longestWord * 10;
185
+ };
186
+ const polygonToArea = points => {
187
+ const h = head(points);
188
+ const area = {
189
+ left: h.x,
190
+ top: h.y,
191
+ bottom: h.y,
192
+ right: h.x
193
+ };
194
+ return tail(points).reduce((a, p) => {
195
+ a.left = Math.min(a.left, p.x);
196
+ a.top = Math.max(a.top, p.y);
197
+ a.bottom = Math.min(a.bottom, p.y);
198
+ a.right = Math.max(a.right, p.x);
199
+ return a;
200
+ }, area);
201
+ };
202
+ const getRightestPoints = points => {
203
+ const sortedPoints = cloneDeep(points);
204
+ sortedPoints.sort((a, b) => b.x - a.x);
205
+ return {
206
+ a: sortedPoints[0],
207
+ b: sortedPoints[1]
208
+ };
209
+ };
210
+ const getMiddleOfTwoPoints = (a, b) => ({
211
+ x: (a.x + b.x) / 2,
212
+ y: (a.y + b.y) / 2
213
+ });
214
+ const roundNumber = number => parseFloat(number.toFixed(4));
215
+ const sameAxes = (p1, p2) => p1 && p2 && (roundNumber(p1.x) === roundNumber(p2.x) || roundNumber(p1.y) === roundNumber(p2.y));
216
+ const equalPoints = (p1, p2) => p1 && p2 && isEqual({
217
+ x: roundNumber(p1.x),
218
+ y: roundNumber(p1.y)
219
+ }, {
220
+ x: roundNumber(p2.x),
221
+ y: roundNumber(p2.y)
222
+ });
223
+
224
+ const getDistanceBetweenTicks = (axis, size) => {
225
+ const {
226
+ min,
227
+ max,
228
+ step
229
+ } = axis;
230
+ const nbOfTicks = (max - min) / step;
231
+ return size / nbOfTicks;
232
+ };
233
+
234
+ const thinnerShapesNeeded = graphProps => {
235
+ const {
236
+ domain,
237
+ range,
238
+ size: {
239
+ width,
240
+ height
241
+ }
242
+ } = graphProps; // 14 is the default width of a point
243
+
244
+ return getDistanceBetweenTicks(domain, width) < 14 || getDistanceBetweenTicks(range, height) < 14;
245
+ };
246
+ const getAdjustedGraphLimits = graphProps => {
247
+ const {
248
+ domain,
249
+ range,
250
+ size: {
251
+ width,
252
+ height
253
+ }
254
+ } = graphProps;
255
+ const domainTicksDistance = getDistanceBetweenTicks(domain, width);
256
+ const rangeTicksDistance = getDistanceBetweenTicks(range, height); // 15 is the distance required for the arrow to extend the graph
257
+
258
+ const domainPadding = domain.step / (domainTicksDistance / 15);
259
+ const rangePadding = range.step / (rangeTicksDistance / 15);
260
+ return {
261
+ domain: {
262
+ min: domain.min - domainPadding,
263
+ max: domain.max + domainPadding
264
+ },
265
+ range: {
266
+ min: range.min - rangePadding,
267
+ max: range.max + rangePadding
268
+ }
269
+ };
270
+ };
271
+
272
+ const sortPoints = array => (array || []).sort((a, b) => a.x - b.x || a.y - b.y); // check colliniarity of 3 points (source: https://www.geeksforgeeks.org/program-check-three-points-collinear/)
273
+
274
+
275
+ const checkCollinearity = (a, b, c) => (a.x - b.x) * (c.y - b.y) === (c.x - b.x) * (a.y - b.y); // 2 lines are overlapping if all 4 points are collinear
276
+
277
+
278
+ const isSameLine = (markA, markB) => checkCollinearity(markA.from, markB.from, markB.to) && checkCollinearity(markA.to, markB.from, markB.to);
279
+
280
+ const isDuplicatedMark = (mark, marks, oldMark) => {
281
+ const {
282
+ type,
283
+ building
284
+ } = mark;
285
+
286
+ if (building) {
287
+ return false;
288
+ }
289
+
290
+ const filteredMarks = (marks || []).filter(m => m.type === type && !m.building);
291
+ const index = filteredMarks.findIndex(m => isEqual(m, oldMark));
292
+
293
+ if (index !== -1) {
294
+ filteredMarks.splice(index, 1);
295
+ }
296
+
297
+ const duplicated = filteredMarks.find(m => {
298
+ if (type === 'line') {
299
+ const {
300
+ from,
301
+ to
302
+ } = mark;
303
+ return equalPoints(from, m.from) && equalPoints(to, m.to) || equalPoints(from, m.to) && equalPoints(to, m.from) || type === 'line' && isSameLine(m, mark);
304
+ } else if (type === 'polygon') {
305
+ return isEqual(sortPoints(cloneDeep(mark.points)), sortPoints(cloneDeep(m.points)));
306
+ }
307
+ });
308
+ return !!duplicated;
309
+ };
310
+ const areArraysOfObjectsEqual = (array1, array2) => {
311
+ // Check if both arrays have the same length
312
+ if (array1.length !== array2.length) {
313
+ return false;
314
+ } // Iterate through each object in the arrays
315
+
316
+
317
+ for (let i = 0; i < array1.length; i++) {
318
+ // Get the current objects in both arrays
319
+ const obj1 = array1[i];
320
+ const obj2 = array2[i]; // Check if the objects have the same number of properties
321
+
322
+ if (Object.keys(obj1).length !== Object.keys(obj2).length) {
323
+ return false;
324
+ } // Iterate through each property in the objects
325
+
326
+
327
+ for (const key in obj1) {
328
+ // Check if the properties and their values are equal
329
+ if (obj1[key] !== obj2[key]) {
330
+ return false;
331
+ }
332
+ }
333
+ } // If all objects are equal, the arrays are the same
334
+
335
+
336
+ return true;
337
+ };
338
+
339
+ const AxisPropTypes = {
340
+ includeArrows: PropTypes.object,
341
+ graphProps: PropTypes.object
342
+ };
343
+ const AxisDefaultProps = {
344
+ includeArrows: {
345
+ left: true,
346
+ right: true,
347
+ up: true,
348
+ down: true
349
+ }
350
+ };
351
+
352
+ const axisStyles = theme => ({
353
+ line: {
354
+ stroke: '#8a92c0',
355
+ strokeWidth: 4
356
+ },
357
+ arrow: {
358
+ fill: '#8a92c0'
359
+ },
360
+ tick: {
361
+ fill: color.defaults.BLACK,
362
+ '& > line': {
363
+ stroke: '#8a92c0'
364
+ }
365
+ },
366
+ labelFontSize: {
367
+ fontSize: theme.typography.fontSize
368
+ },
369
+ axisLabelHolder: {
370
+ padding: 0,
371
+ margin: 0,
372
+ textAlign: 'center',
373
+ '* > *': {
374
+ margin: 0,
375
+ padding: 0
376
+ },
377
+ fontSize: theme.typography.fontSize
378
+ }
379
+ });
380
+
381
+ const tickLabelStyles = {
382
+ fontFamily: 'Roboto',
383
+ fontSize: '14px',
384
+ cursor: 'inherit'
385
+ };
386
+ const sharedValues = (firstNegativeX, firstNegativeY, distanceFromOriginToFirstNegativeX, distanceFromOriginToFirstNegativeY, deltaAllowance, dy) => {
387
+ let result = [];
388
+
389
+ if (firstNegativeX === firstNegativeY && distanceFromOriginToFirstNegativeX - deltaAllowance < distanceFromOriginToFirstNegativeY && distanceFromOriginToFirstNegativeY < distanceFromOriginToFirstNegativeX + deltaAllowance && distanceFromOriginToFirstNegativeX - deltaAllowance < dy && dy < distanceFromOriginToFirstNegativeX + deltaAllowance) {
390
+ result.push(firstNegativeX);
391
+ }
392
+
393
+ return result;
394
+ };
395
+ const firstNegativeValue = interval => (interval || []).find(element => element < 0);
396
+ class RawXAxis extends React.Component {
397
+ render() {
398
+ const {
399
+ includeArrows,
400
+ classes,
401
+ graphProps,
402
+ columnTicksValues,
403
+ skipValues,
404
+ distanceFromOriginToFirstNegativeY,
405
+ dy
406
+ } = this.props;
407
+ const {
408
+ scale,
409
+ domain,
410
+ size,
411
+ range
412
+ } = graphProps || {}; // Having 0 as a number in columnTicksValues does not make 0 to show up
413
+ // so we use this trick, by defining it as a string:
414
+
415
+ const tickValues = (domain.labelStep || range.labelStep) && domain.min <= 0 ? ['0', ...columnTicksValues] : columnTicksValues; // However, the '0' has to be displayed only if other tick labels (y-axis or x-axis) are displayed
416
+
417
+ const labelProps = label => {
418
+ const y = skipValues && skipValues[0] === label ? distanceFromOriginToFirstNegativeY + 4 : dy;
419
+ return _extends({}, tickLabelStyles, {
420
+ textAnchor: 'middle',
421
+ y: y,
422
+ dx: label === '0' ? -10 : 0,
423
+ dy: label === '0' ? -7 : 0
424
+ });
425
+ };
426
+
427
+ const necessaryRows = countWords(domain.axisLabel);
428
+ const longestWord = findLongestWord(domain.axisLabel);
429
+ const necessaryWidth = amountToIncreaseWidth(longestWord) + 2;
430
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Axis, {
431
+ axisLineClassName: classes.line,
432
+ scale: scale.x,
433
+ top: scale.y(0),
434
+ left: 0,
435
+ label: domain.label,
436
+ rangePadding: 8,
437
+ tickClassName: classes.tick,
438
+ tickFormat: value => value,
439
+ tickLabelProps: labelProps,
440
+ tickValues: tickValues
441
+ }), includeArrows && includeArrows.left && /*#__PURE__*/React.createElement(Arrow$2, {
442
+ direction: "left",
443
+ x: domain.min,
444
+ y: 0,
445
+ className: classes.arrow,
446
+ scale: scale
447
+ }), includeArrows && includeArrows.right && /*#__PURE__*/React.createElement(Arrow$2, {
448
+ direction: "right",
449
+ x: domain.max,
450
+ y: 0,
451
+ className: classes.arrow,
452
+ scale: scale
453
+ }), domain.axisLabel && /*#__PURE__*/React.createElement("foreignObject", {
454
+ x: size.width + 17,
455
+ y: scale.y(0) - 9,
456
+ width: necessaryWidth,
457
+ height: 20 * necessaryRows
458
+ }, /*#__PURE__*/React.createElement("div", {
459
+ dangerouslySetInnerHTML: {
460
+ __html: domain.axisLabel
461
+ },
462
+ className: classes.labelFontSize
463
+ })));
464
+ }
465
+
466
+ }
467
+ RawXAxis.propTypes = _extends({}, AxisPropTypes, {
468
+ classes: PropTypes.object,
469
+ graphProps: types.GraphPropsType.isRequired
470
+ });
471
+ RawXAxis.defaultProps = AxisDefaultProps;
472
+ const XAxis = withStyles$1(axisStyles)(RawXAxis);
473
+ class RawYAxis extends React.Component {
474
+ render() {
475
+ const {
476
+ classes,
477
+ includeArrows,
478
+ graphProps,
479
+ skipValues,
480
+ rowTickValues
481
+ } = this.props;
482
+ const {
483
+ scale,
484
+ range,
485
+ size
486
+ } = graphProps || {};
487
+ const necessaryWidth = range.axisLabel ? amountToIncreaseWidth(range.axisLabel.length) : 0;
488
+
489
+ const customTickFormat = value => skipValues && skipValues.indexOf(value) >= 0 ? '' : value;
490
+
491
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Axis, {
492
+ axisLineClassName: classes.line,
493
+ orientation: 'left',
494
+ scale: scale.y,
495
+ top: 0,
496
+ height: size.height,
497
+ left: scale.x(0),
498
+ label: range.label,
499
+ labelProps: {
500
+ 'data-pie-readable': false
501
+ },
502
+ rangePadding: 8,
503
+ tickLength: 10,
504
+ tickClassName: classes.tick,
505
+ tickFormat: customTickFormat,
506
+ tickLabelProps: value => {
507
+ let digits = value.toLocaleString().replace(/[.-]/g, '').length || 1;
508
+ return _extends({}, tickLabelStyles, {
509
+ dy: 4,
510
+ dx: -10 - digits * 9,
511
+ 'data-pie-readable': false
512
+ });
513
+ },
514
+ hideZero: true,
515
+ tickTextAnchor: 'bottom',
516
+ tickValues: rowTickValues
517
+ }), includeArrows && includeArrows.down && /*#__PURE__*/React.createElement(Arrow$2, {
518
+ direction: "down",
519
+ x: 0,
520
+ y: range.min,
521
+ className: classes.arrow,
522
+ scale: scale
523
+ }), includeArrows && includeArrows.up && /*#__PURE__*/React.createElement(Arrow$2, {
524
+ direction: "up",
525
+ x: 0,
526
+ y: range.max,
527
+ className: classes.arrow,
528
+ scale: scale
529
+ }), range.axisLabel && /*#__PURE__*/React.createElement("foreignObject", {
530
+ x: scale.x(0) - necessaryWidth / 2,
531
+ y: -33,
532
+ width: necessaryWidth,
533
+ height: "20"
534
+ }, /*#__PURE__*/React.createElement(Readable, {
535
+ false: true
536
+ }, /*#__PURE__*/React.createElement("div", {
537
+ dangerouslySetInnerHTML: {
538
+ __html: range.axisLabel
539
+ },
540
+ className: classes.axisLabelHolder
541
+ }))));
542
+ }
543
+
544
+ }
545
+ RawYAxis.propTypes = _extends({}, AxisPropTypes, {
546
+ graphProps: types.GraphPropsType.isRequired
547
+ });
548
+ RawYAxis.defaultProps = AxisDefaultProps;
549
+ const YAxis = withStyles$1(axisStyles)(RawYAxis);
550
+ class Axes extends React.Component {
551
+ constructor(...args) {
552
+ super(...args);
553
+
554
+ this.xValues = () => {
555
+ const {
556
+ graphProps
557
+ } = this.props;
558
+ const {
559
+ scale,
560
+ domain
561
+ } = graphProps || {};
562
+
563
+ if (!domain || !scale) {
564
+ return;
565
+ }
566
+
567
+ const ticks = getTickValues(_extends({}, domain, {
568
+ step: domain.labelStep
569
+ }));
570
+ const negative = firstNegativeValue(ticks);
571
+ return {
572
+ columnTicksValues: ticks,
573
+ firstNegativeX: negative,
574
+ distanceFromOriginToFirstNegativeX: Math.abs(scale.y(0) - scale.y(negative))
575
+ };
576
+ };
577
+
578
+ this.yValues = () => {
579
+ const {
580
+ graphProps
581
+ } = this.props;
582
+ const {
583
+ scale,
584
+ range
585
+ } = graphProps || {};
586
+
587
+ if (!range || !scale) {
588
+ return;
589
+ }
590
+
591
+ const ticks = getTickValues(_extends({}, range, {
592
+ step: range.labelStep
593
+ }));
594
+ const negative = firstNegativeValue(ticks);
595
+ return {
596
+ rowTickValues: ticks,
597
+ firstNegativeY: negative,
598
+ distanceFromOriginToFirstNegativeY: Math.abs(scale.x(0) - scale.x(negative))
599
+ };
600
+ };
601
+ }
602
+
603
+ render() {
604
+ const {
605
+ graphProps
606
+ } = this.props;
607
+ const {
608
+ domain,
609
+ range
610
+ } = graphProps || {};
611
+ const {
612
+ columnTicksValues,
613
+ firstNegativeX,
614
+ distanceFromOriginToFirstNegativeX
615
+ } = this.xValues();
616
+ const {
617
+ rowTickValues,
618
+ firstNegativeY,
619
+ distanceFromOriginToFirstNegativeY
620
+ } = this.yValues();
621
+ const deltaAllowance = 6;
622
+ const dy = 25;
623
+ const skipValues = sharedValues(firstNegativeX, firstNegativeY, distanceFromOriginToFirstNegativeX, distanceFromOriginToFirstNegativeY, deltaAllowance, dy); // each axis has to be displayed only if the domain & range include it
624
+
625
+ return /*#__PURE__*/React.createElement(React.Fragment, null, range.min <= 0 ? /*#__PURE__*/React.createElement(XAxis, _extends({}, this.props, {
626
+ skipValues: skipValues,
627
+ columnTicksValues: columnTicksValues,
628
+ distanceFromOriginToFirstNegativeY: distanceFromOriginToFirstNegativeY,
629
+ dy: dy
630
+ })) : null, domain.min <= 0 ? /*#__PURE__*/React.createElement(YAxis, _extends({}, this.props, {
631
+ skipValues: skipValues,
632
+ rowTickValues: rowTickValues,
633
+ distanceFromOriginToFirstNegativeX: distanceFromOriginToFirstNegativeX
634
+ })) : null);
635
+ }
636
+
637
+ }
638
+ Axes.propTypes = _extends({}, AxisPropTypes, {
639
+ classes: PropTypes.object,
640
+ graphProps: types.GraphPropsType.isRequired
641
+ });
642
+ Axes.defaultProps = AxisDefaultProps;
643
+
644
+ class Grid extends React.Component {
645
+ constructor(...args) {
646
+ super(...args);
647
+
648
+ this.getAdditionalGridProps = (rowTickValues, columnTickValues) => {
649
+ const {
650
+ graphProps: {
651
+ scale,
652
+ size: {
653
+ width,
654
+ height
655
+ },
656
+ domain,
657
+ range
658
+ }
659
+ } = this.props;
660
+ const rowTickLabelValues = getTickValues(_extends({}, range, {
661
+ step: range.labelStep
662
+ })).filter(value => rowTickValues.includes(value));
663
+ const columnTickLabelValues = getTickValues(_extends({}, domain, {
664
+ step: domain.labelStep
665
+ })).filter(value => columnTickValues.includes(value));
666
+ const minValueLength = rowTickLabelValues.length && Math.min(...rowTickLabelValues).toString().replace(/[.-]/g, '').length || 1;
667
+ const maxValueLength = rowTickLabelValues.length && Math.max(...rowTickLabelValues).toString().replace(/[.-]/g, '').length || 1;
668
+ const rowLabelLength = Math.max(minValueLength, maxValueLength) * 9 + 22;
669
+ const horizontalDistanceToZero = scale.x(0);
670
+ const verticalDistanceToZero = scale.y(0);
671
+ const columnLabelLength = 28;
672
+ const rowStrokeDasharray = `${horizontalDistanceToZero - rowLabelLength} ${rowLabelLength} ${width}`;
673
+ const columnStrokeDasharray = `${verticalDistanceToZero} ${columnLabelLength} ${height}`;
674
+ const displayAdditionalGrid = domain.labelStep > 0 && range.labelStep > 0 && rowTickLabelValues && columnTickLabelValues && rowTickLabelValues.length > 1 && columnTickLabelValues.length > 1 && (rowTickLabelValues.length !== rowTickValues.length || columnTickLabelValues.length !== columnTickValues.length);
675
+ const filteredColumnValues = columnTickLabelValues.filter(value => value >= 0 || horizontalDistanceToZero - scale.x(value) > rowLabelLength);
676
+ const filteredRowValues = rowTickLabelValues.filter(value => value >= 0 || scale.y(value) - verticalDistanceToZero > columnLabelLength);
677
+ return {
678
+ rowTickLabelValues: filteredRowValues,
679
+ columnTickLabelValues: filteredColumnValues,
680
+ rowStrokeDasharray,
681
+ columnStrokeDasharray,
682
+ displayAdditionalGrid
683
+ };
684
+ };
685
+ }
686
+
687
+ render() {
688
+ const {
689
+ graphProps
690
+ } = this.props;
691
+ const {
692
+ scale,
693
+ size: {
694
+ height,
695
+ width
696
+ },
697
+ domain,
698
+ range
699
+ } = graphProps;
700
+ const rowTickValues = getTickValues(range);
701
+ const columnTickValues = getTickValues(domain);
702
+ const {
703
+ rowTickLabelValues,
704
+ columnTickLabelValues,
705
+ rowStrokeDasharray,
706
+ columnStrokeDasharray,
707
+ displayAdditionalGrid
708
+ } = this.getAdditionalGridProps(rowTickValues, columnTickValues);
709
+ const additionalGridStroke = domain.labelStep * 2 === domain.step || range.labelStep * 2 === range.step ? '#9FA8DA' : '#7985CB';
710
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(vx.Grid, {
711
+ innerRef: r => this.grid = r,
712
+ xScale: scale.x,
713
+ yScale: scale.y,
714
+ width: width,
715
+ height: height,
716
+ stroke: "#D3D3D3",
717
+ rowTickValues: rowTickValues,
718
+ columnTickValues: columnTickValues
719
+ }), displayAdditionalGrid && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(vx.GridRows, {
720
+ scale: scale.y,
721
+ width: width,
722
+ tickValues: rowTickLabelValues,
723
+ stroke: additionalGridStroke,
724
+ strokeDasharray: rowStrokeDasharray
725
+ }), /*#__PURE__*/React.createElement(vx.GridColumns, {
726
+ scale: scale.x,
727
+ height: height,
728
+ tickValues: columnTickLabelValues,
729
+ stroke: additionalGridStroke,
730
+ strokeDasharray: columnStrokeDasharray
731
+ })));
732
+ }
733
+
734
+ }
735
+ Grid.propTypes = {
736
+ disabled: PropTypes.bool,
737
+ disabledAdditionalGrid: PropTypes.bool,
738
+ classes: PropTypes.object.isRequired,
739
+ graphProps: types.GraphPropsType.isRequired
740
+ };
741
+ var Grid$1 = withStyles(() => ({}))(Grid);
742
+
743
+ const rotations = {
744
+ left: -90,
745
+ top: 0,
746
+ bottom: 0,
747
+ right: 90
748
+ };
749
+ const getTransform = (side, width, height) => {
750
+ const t = (x, y, rotate) => `translate(${x}, ${y}), rotate(${rotate})`;
751
+
752
+ if (side === 'left') {
753
+ return t(-20, height / 2, rotations[side]);
754
+ }
755
+
756
+ if (side === 'right') {
757
+ return t(width + 30, height / 2, rotations[side]);
758
+ }
759
+
760
+ if (side === 'top') {
761
+ return t(width / 2, -20, rotations[side]);
762
+ }
763
+
764
+ if (side === 'bottom') {
765
+ return t(width / 2, height + 30, rotations[side]);
766
+ }
767
+ };
768
+
769
+ const getY = (side, height) => {
770
+ switch (side) {
771
+ case 'left':
772
+ return -36;
773
+
774
+ case 'top':
775
+ return -36;
776
+
777
+ case 'right':
778
+ return -36 - 10;
779
+
780
+ default:
781
+ return -36 + 10;
782
+ }
783
+ };
784
+
785
+ class RawLabel extends React.Component {
786
+ render() {
787
+ const {
788
+ disabledLabel,
789
+ placeholder,
790
+ text,
791
+ side,
792
+ graphProps,
793
+ classes,
794
+ onChange,
795
+ mathMlOptions = {}
796
+ } = this.props;
797
+ const {
798
+ size,
799
+ domain,
800
+ range
801
+ } = graphProps;
802
+ const totalHeight = (size.height || 500) + (range.padding || 0) * 2;
803
+ const totalWidth = (size.width || 500) + (domain.padding || 0) * 2;
804
+ const transform = getTransform(side, totalWidth, totalHeight);
805
+ const width = side === 'left' || side === 'right' ? totalHeight : totalWidth;
806
+ const height = 36;
807
+ const y = getY(side);
808
+ const finalHeight = side === 'bottom' ? height + 22 : height + 18;
809
+ const activePlugins = ['bold', 'italic', 'underline', 'strikethrough', 'math' // 'languageCharacters'
810
+ ];
811
+ return /*#__PURE__*/React.createElement("foreignObject", {
812
+ x: -(width / 2),
813
+ y: y,
814
+ width: width,
815
+ height: finalHeight,
816
+ transform: transform,
817
+ textAnchor: "middle"
818
+ }, /*#__PURE__*/React.createElement(Readable, {
819
+ false: true
820
+ }, /*#__PURE__*/React.createElement(EditableHtml, {
821
+ className: classNames({
822
+ [classes.bottomLabel]: side === 'bottom',
823
+ [classes.disabledAxisLabel]: disabledLabel
824
+ }, classes.axisLabel),
825
+ markup: text || '',
826
+ onChange: onChange,
827
+ placeholder: !disabledLabel && placeholder,
828
+ toolbarOpts: {
829
+ position: side === 'bottom' ? 'top' : 'bottom',
830
+ noPadding: true,
831
+ noBorder: true
832
+ },
833
+ activePlugins: activePlugins,
834
+ mathMlOptions: mathMlOptions
835
+ })));
836
+ }
837
+
838
+ }
839
+
840
+ RawLabel.propTypes = {
841
+ text: PropTypes.string,
842
+ side: PropTypes.string,
843
+ classes: PropTypes.object,
844
+ disabledLabel: PropTypes.bool,
845
+ placeholder: PropTypes.string,
846
+ graphProps: types.GraphPropsType.isRequired,
847
+ onChange: PropTypes.func
848
+ };
849
+ RawLabel.defaultProps = {
850
+ onChange: () => {}
851
+ };
852
+ const Label = withStyles(theme => ({
853
+ label: {
854
+ fill: color.defaults.SECONDARY
855
+ },
856
+ axisLabel: {
857
+ fontSize: theme.typography.fontSize - 2,
858
+ textAlign: 'center',
859
+ padding: '0 4px'
860
+ },
861
+ disabledAxisLabel: {
862
+ pointerEvents: 'none'
863
+ },
864
+ bottomLabel: {
865
+ marginTop: '44px'
866
+ }
867
+ }))(RawLabel);
868
+ const LabelType = {
869
+ left: PropTypes.string,
870
+ top: PropTypes.string,
871
+ bottom: PropTypes.string,
872
+ right: PropTypes.string
873
+ };
874
+ class Labels extends React.Component {
875
+ constructor(...args) {
876
+ super(...args);
877
+
878
+ this.onChangeLabel = (newValue, side) => {
879
+ const {
880
+ value,
881
+ onChange
882
+ } = this.props;
883
+
884
+ const labels = _extends({}, value, {
885
+ [side]: newValue
886
+ });
887
+
888
+ onChange(labels);
889
+ };
890
+ }
891
+
892
+ render() {
893
+ const {
894
+ disabledLabels,
895
+ placeholders = {},
896
+ value = {},
897
+ graphProps,
898
+ mathMlOptions = {}
899
+ } = this.props;
900
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Label, {
901
+ key: "left",
902
+ side: "left",
903
+ text: value.left,
904
+ disabledLabel: disabledLabels,
905
+ placeholder: placeholders.left,
906
+ graphProps: graphProps,
907
+ onChange: value => this.onChangeLabel(value, 'left'),
908
+ mathMlOptions: mathMlOptions
909
+ }), /*#__PURE__*/React.createElement(Label, {
910
+ key: "top",
911
+ side: "top",
912
+ text: value.top,
913
+ disabledLabel: disabledLabels,
914
+ placeholder: placeholders.top,
915
+ graphProps: graphProps,
916
+ onChange: value => this.onChangeLabel(value, 'top'),
917
+ mathMlOptions: mathMlOptions
918
+ }), /*#__PURE__*/React.createElement(Label, {
919
+ key: "bottom",
920
+ side: "bottom",
921
+ text: value.bottom,
922
+ disabledLabel: disabledLabels,
923
+ placeholder: placeholders.bottom,
924
+ graphProps: graphProps,
925
+ onChange: value => this.onChangeLabel(value, 'bottom'),
926
+ mathMlOptions: mathMlOptions
927
+ }), /*#__PURE__*/React.createElement(Label, {
928
+ key: "right",
929
+ side: "right",
930
+ text: value.right,
931
+ disabledLabel: disabledLabels,
932
+ placeholder: placeholders.right,
933
+ graphProps: graphProps,
934
+ onChange: value => this.onChangeLabel(value, 'right'),
935
+ mathMlOptions: mathMlOptions
936
+ }));
937
+ }
938
+
939
+ }
940
+ Labels.propTypes = {
941
+ classes: PropTypes.object,
942
+ className: PropTypes.string,
943
+ disabledLabels: PropTypes.bool,
944
+ placeholders: PropTypes.object,
945
+ value: PropTypes.shape(LabelType),
946
+ graphProps: PropTypes.object,
947
+ onChange: PropTypes.object
948
+ };
949
+ Labels.defaultProps = {};
950
+
951
+ class Bg extends React.Component {
952
+ constructor(...args) {
953
+ super(...args);
954
+
955
+ this.getRectPadding = () => {
956
+ const {
957
+ graphProps
958
+ } = this.props;
959
+ return thinnerShapesNeeded(graphProps) ? 6 : 10;
960
+ };
961
+
962
+ this.onRectClick = rect => {
963
+ const {
964
+ onClick,
965
+ graphProps
966
+ } = this.props;
967
+ const {
968
+ scale
969
+ } = graphProps;
970
+ const padding = this.getRectPadding();
971
+ const coords = mouse(rect._groups[0][0]); // decrease the padding from coordinates to indicate the correct point clicked
972
+
973
+ const x = scale.x.invert(coords[0] - padding);
974
+ const y = scale.y.invert(coords[1] - padding);
975
+ const rowTicks = getTickValues(graphProps.range);
976
+ const columnTicks = getTickValues(graphProps.domain);
977
+
978
+ const closest = (ticks, value) => {
979
+ return ticks.length && ticks.reduce((prev, curr) => {
980
+ const currentDistance = Math.abs(curr - value);
981
+ const previousDistance = Math.abs(prev - value);
982
+ return currentDistance <= previousDistance ? curr : prev;
983
+ });
984
+ };
985
+
986
+ let snapped = {};
987
+
988
+ if (columnTicks.indexOf(x) >= 0 && rowTicks.indexOf(y) >= 0) {
989
+ snapped.x = x;
990
+ snapped.y = y;
991
+ } else {
992
+ snapped.x = closest(columnTicks, x);
993
+ snapped.y = closest(rowTicks, y);
994
+ }
995
+
996
+ onClick(snapped);
997
+ };
998
+ }
999
+
1000
+ componentDidMount() {
1001
+ const rect = select(this.rect);
1002
+ rect.on('click', this.onRectClick.bind(this, rect));
1003
+ }
1004
+
1005
+ shouldComponentUpdate(nextProps) {
1006
+ return !utils.isDomainRangeEqual(this.props.graphProps, nextProps.graphProps) || this.props.width !== nextProps.width || this.props.height !== nextProps.height;
1007
+ }
1008
+
1009
+ render() {
1010
+ const {
1011
+ width,
1012
+ height
1013
+ } = this.props;
1014
+ const padding = this.getRectPadding(); // expand the size of clickable area so a small area outside the edges of the grid lines to be clickable
1015
+
1016
+ return /*#__PURE__*/React.createElement("rect", {
1017
+ ref: rect => this.rect = rect,
1018
+ transform: `translate(-${padding}, -${padding})`,
1019
+ fill: "red",
1020
+ fillOpacity: "0.0",
1021
+ width: width + padding * 2,
1022
+ height: height + padding * 2
1023
+ });
1024
+ }
1025
+
1026
+ }
1027
+ Bg.propTypes = {
1028
+ width: PropTypes.number.isRequired,
1029
+ height: PropTypes.number.isRequired,
1030
+ onClick: PropTypes.func.isRequired,
1031
+ graphProps: types.GraphPropsType.isRequired
1032
+ };
1033
+ Bg.defaultProps = {};
1034
+
1035
+ const log$1 = debug('pie-lib:graphing-solution-set:graph');
1036
+ const graphPropTypes = {
1037
+ axesSettings: PropTypes.shape(AxisPropTypes),
1038
+ backgroundMarks: PropTypes.array,
1039
+ className: PropTypes.string,
1040
+ collapsibleToolbar: PropTypes.bool,
1041
+ collapsibleToolbarTitle: PropTypes.string,
1042
+ disabledLabels: PropTypes.bool,
1043
+ disabledTitle: PropTypes.bool,
1044
+ domain: types.DomainType,
1045
+ labels: PropTypes.shape(LabelType),
1046
+ labelModeEnabled: PropTypes.bool,
1047
+ coordinatesOnHover: PropTypes.bool,
1048
+ marks: PropTypes.array,
1049
+ onChangeLabels: PropTypes.func,
1050
+ onChangeMarks: PropTypes.func,
1051
+ onChangeTitle: PropTypes.func,
1052
+ range: types.DomainType,
1053
+ size: PropTypes.shape({
1054
+ width: PropTypes.number.isRequired,
1055
+ height: PropTypes.number.isRequired
1056
+ }),
1057
+ showLabels: PropTypes.bool,
1058
+ showPixelGuides: PropTypes.bool,
1059
+ showTitle: PropTypes.bool,
1060
+ title: PropTypes.string,
1061
+ tools: PropTypes.array
1062
+ };
1063
+
1064
+ const getMaskSize = size => ({
1065
+ x: -23,
1066
+ y: -23,
1067
+ width: size.width + 46,
1068
+ height: size.height + 46
1069
+ });
1070
+
1071
+ const removeBuildingToolIfCurrentToolDiffers = ({
1072
+ marks,
1073
+ currentTool
1074
+ }) => {
1075
+ const buildingMark = marks.filter(m => m.building)[0];
1076
+ let newMarks = cloneDeep(marks);
1077
+
1078
+ if (buildingMark && currentTool && buildingMark.type !== currentTool.type) {
1079
+ const index = newMarks.findIndex(m => isEqual(m, buildingMark));
1080
+
1081
+ if (index >= 0) {
1082
+ newMarks.splice(index, 1);
1083
+ }
1084
+ }
1085
+
1086
+ return newMarks;
1087
+ };
1088
+ class Graph extends React.Component {
1089
+ constructor(props) {
1090
+ super(props);
1091
+ this.state = {};
1092
+
1093
+ this.componentDidMount = () => this.setState({
1094
+ labelNode: this.labelNode
1095
+ });
1096
+
1097
+ this.changeMark = (oldMark, newMark) => {
1098
+ const {
1099
+ onChangeMarks,
1100
+ marks,
1101
+ gssLineData
1102
+ } = this.props;
1103
+
1104
+ if ((gssLineData == null ? void 0 : gssLineData.selectedTool) === 'solutionSet') {
1105
+ return;
1106
+ }
1107
+
1108
+ let newMarks = cloneDeep(marks);
1109
+ const index = newMarks.findIndex(m => isEqual(m, oldMark));
1110
+
1111
+ if (index >= 0 && !isDuplicatedMark(newMark, marks, oldMark)) {
1112
+ newMarks.splice(index, 1, newMark);
1113
+ onChangeMarks(newMarks);
1114
+ }
1115
+ };
1116
+
1117
+ this.completeMark = markData => {
1118
+ const {
1119
+ currentTool,
1120
+ marks
1121
+ } = this.props;
1122
+ const buildingMark = marks.filter(m => m.building)[0];
1123
+ if (!buildingMark || !currentTool) return;
1124
+ const updatedMark = currentTool.complete(buildingMark, markData);
1125
+ this.updateMarks(buildingMark, updatedMark);
1126
+ };
1127
+
1128
+ this.updateMarks = (existing, update, addIfMissing = false) => {
1129
+ const {
1130
+ onChangeMarks,
1131
+ marks
1132
+ } = this.props;
1133
+ let newMarks = cloneDeep(marks);
1134
+
1135
+ if (!update || !update.building && isDuplicatedMark(update, marks)) {
1136
+ return;
1137
+ }
1138
+
1139
+ const index = newMarks.findIndex(m => isEqual(m, existing));
1140
+
1141
+ if (index >= 0) {
1142
+ newMarks.splice(index, 1, update);
1143
+ onChangeMarks(newMarks);
1144
+ } else if (addIfMissing) {
1145
+ onChangeMarks([...newMarks, update]);
1146
+ }
1147
+ };
1148
+
1149
+ this.getComponent = mark => {
1150
+ if (!mark) return null;
1151
+ const tool = (this.props.tools || []).find(t => t.type === mark.type);
1152
+ return tool && tool.Component || null;
1153
+ };
1154
+
1155
+ this.onBgClick = point => {
1156
+ const {
1157
+ x,
1158
+ y
1159
+ } = point || {};
1160
+ const {
1161
+ labelModeEnabled,
1162
+ currentTool,
1163
+ marks,
1164
+ gssLineData,
1165
+ onSolutionSetSelected
1166
+ } = this.props;
1167
+
1168
+ if (gssLineData) {
1169
+ if (gssLineData.selectedTool === 'solutionSet') {
1170
+ onSolutionSetSelected(point);
1171
+ return;
1172
+ }
1173
+
1174
+ if (gssLineData.numberOfLines === 1 && gssLineData.selectedTool === 'lineA' && marks.length === 1 && !marks[0].building) {
1175
+ return;
1176
+ }
1177
+
1178
+ if (gssLineData.numberOfLines === 2 && gssLineData.selectedTool === 'lineA' && marks.length === 2) {
1179
+ return;
1180
+ }
1181
+
1182
+ if (gssLineData.numberOfLines === 2 && gssLineData.selectedTool === 'lineA' && marks.length === 1 && !marks[0].building) {
1183
+ return;
1184
+ }
1185
+
1186
+ if (gssLineData.numberOfLines === 2 && gssLineData.selectedTool === 'lineB' && marks.length === 2 && !marks[1].building) {
1187
+ return;
1188
+ }
1189
+ }
1190
+
1191
+ if (labelModeEnabled || !currentTool || [null, undefined].includes(x) || [null, undefined].includes(y)) {
1192
+ return;
1193
+ }
1194
+
1195
+ log$1('[onBgClick] x,y: ', x, y);
1196
+ const buildingMark = marks.filter(m => m.building)[0];
1197
+ let updatedMark; // if the building mark has a different type, we just replace it
1198
+
1199
+ if (buildingMark && currentTool && buildingMark.type === currentTool.type) {
1200
+ updatedMark = currentTool.addPoint({
1201
+ x,
1202
+ y
1203
+ }, _extends({}, buildingMark));
1204
+ } else {
1205
+ updatedMark = currentTool.addPoint({
1206
+ x,
1207
+ y
1208
+ }, undefined);
1209
+ }
1210
+
1211
+ this.updateMarks(buildingMark, updatedMark, true);
1212
+ };
1213
+
1214
+ this.maskUid = this.generateMaskId();
1215
+ }
1216
+
1217
+ generateMaskId() {
1218
+ return 'graph-' + (Math.random() * 10000).toFixed();
1219
+ }
1220
+
1221
+ render() {
1222
+ const {
1223
+ axesSettings,
1224
+ currentTool,
1225
+ coordinatesOnHover,
1226
+ size,
1227
+ disabledLabels,
1228
+ disabledTitle,
1229
+ domain,
1230
+ backgroundMarks,
1231
+ range,
1232
+ title,
1233
+ labels,
1234
+ labelModeEnabled,
1235
+ labelsPlaceholders,
1236
+ showLabels,
1237
+ showPixelGuides,
1238
+ showTitle,
1239
+ titlePlaceholder,
1240
+ onChangeLabels,
1241
+ onChangeTitle,
1242
+ mathMlOptions = {},
1243
+ gssLineData,
1244
+ disabled
1245
+ } = this.props;
1246
+ let {
1247
+ marks
1248
+ } = this.props;
1249
+ const graphProps = createGraphProps(domain, range, size, () => this.rootNode);
1250
+ const maskSize = getMaskSize(size);
1251
+ let common = {
1252
+ graphProps,
1253
+ labelModeEnabled,
1254
+ gssLineData
1255
+ };
1256
+ marks = removeBuildingToolIfCurrentToolDiffers({
1257
+ marks: marks || [],
1258
+ currentTool
1259
+ });
1260
+ let solutionSet = marks.filter(mark => mark.type === 'polygon');
1261
+
1262
+ if (gssLineData && gssLineData.selectedTool === 'solutionSet') {
1263
+ gssLineData.sections.forEach(section => {
1264
+ if (solutionSet.length === 0 || !areArraysOfObjectsEqual(section, solutionSet[0].points)) {
1265
+ let polygon = {
1266
+ type: 'polygon',
1267
+ building: false,
1268
+ closed: true,
1269
+ points: section,
1270
+ isSolution: false
1271
+ };
1272
+ if (!disabled) marks.push(polygon);
1273
+ }
1274
+ });
1275
+ } //Adjusted layering of added svg elements so that polygons will be added first and lines will be added second
1276
+
1277
+
1278
+ let newMarks = [];
1279
+ newMarks.push(...marks.filter(mark => mark.type === 'polygon'));
1280
+ newMarks.push(...marks.filter(mark => mark.type === 'line'));
1281
+ return /*#__PURE__*/React.createElement(Root$1, _extends({
1282
+ rootRef: r => this.rootNode = r,
1283
+ disabledTitle: disabledTitle,
1284
+ disabledLabels: disabledLabels,
1285
+ labels: labels,
1286
+ labelsPlaceholders: labelsPlaceholders || {},
1287
+ showPixelGuides: showPixelGuides,
1288
+ showLabels: showLabels,
1289
+ showTitle: showTitle,
1290
+ title: title,
1291
+ titlePlaceholder: titlePlaceholder,
1292
+ onChangeTitle: onChangeTitle,
1293
+ onChangeLabels: onChangeLabels,
1294
+ mathMlOptions: mathMlOptions
1295
+ }, common), /*#__PURE__*/React.createElement("g", {
1296
+ transform: domain && domain.padding && domain.range ? `translate(${domain.padding}, ${range.padding})` : undefined
1297
+ }, /*#__PURE__*/React.createElement(Grid$1, common), /*#__PURE__*/React.createElement(Axes, _extends({}, axesSettings, common)), /*#__PURE__*/React.createElement(Bg, _extends({}, size, {
1298
+ onClick: this.onBgClick
1299
+ }, common)), /*#__PURE__*/React.createElement("mask", {
1300
+ id: `${this.maskUid}`
1301
+ }, /*#__PURE__*/React.createElement("rect", _extends({}, maskSize, {
1302
+ fill: "white"
1303
+ })), " "), /*#__PURE__*/React.createElement("g", {
1304
+ id: "marks",
1305
+ mask: `url('#${this.maskUid}')`
1306
+ }, (backgroundMarks || []).map((m, index) => {
1307
+ const Component = this.getComponent(m);
1308
+ const markType = m.type;
1309
+ return /*#__PURE__*/React.createElement(Component, _extends({
1310
+ key: `${markType}-${index}-bg`,
1311
+ mark: _extends({}, m, {
1312
+ disabled: true,
1313
+ isBackground: true
1314
+ }),
1315
+ labelNode: this.state.labelNode,
1316
+ onClick: this.onBgClick
1317
+ }, common));
1318
+ }), newMarks.map((m, index) => {
1319
+ const Component = this.getComponent(m);
1320
+ const markType = m.type;
1321
+ return /*#__PURE__*/React.createElement(Component, _extends({
1322
+ key: `${markType}-${index}`,
1323
+ mark: m,
1324
+ coordinatesOnHover: coordinatesOnHover,
1325
+ onChange: this.changeMark,
1326
+ onComplete: this.completeMark,
1327
+ onClick: this.onBgClick,
1328
+ onDragStart: this.startDrag,
1329
+ onDragStop: this.stopDrag,
1330
+ labelNode: this.state.labelNode,
1331
+ isToolActive: currentTool && markType === currentTool.type
1332
+ }, common));
1333
+ }), /*#__PURE__*/React.createElement("foreignObject", _extends({
1334
+ ref: labelNode => this.labelNode = labelNode,
1335
+ x: "0",
1336
+ y: "0"
1337
+ }, size, {
1338
+ style: {
1339
+ pointerEvents: 'none',
1340
+ fontSize: '14px'
1341
+ }
1342
+ })))));
1343
+ }
1344
+
1345
+ }
1346
+ Graph.propTypes = _extends({}, graphPropTypes, {
1347
+ currentTool: PropTypes.object
1348
+ });
1349
+ Graph.defaultProps = {
1350
+ onChangeMarks: () => {},
1351
+ disabledLabels: false,
1352
+ disabledTitle: false
1353
+ };
1354
+
1355
+ const marks = (state = [], action) => {
1356
+ switch (action.type) {
1357
+ case 'CHANGE_MARKS':
1358
+ if (Array.isArray(action.marks)) {
1359
+ return action.marks;
1360
+ } else {
1361
+ throw new Error('marks must be an array');
1362
+ }
1363
+
1364
+ default:
1365
+ return state;
1366
+ }
1367
+ };
1368
+
1369
+ var reducer = (() => combineReducers({
1370
+ marks: undoable(marks, {
1371
+ debug: false
1372
+ })
1373
+ }));
1374
+
1375
+ const changeMarks = marks => ({
1376
+ type: 'CHANGE_MARKS',
1377
+ marks
1378
+ });
1379
+
1380
+ class ToolMenu extends React.Component {
1381
+ constructor(...args) {
1382
+ super(...args);
1383
+
1384
+ this.onChangeRadioValue = event => {
1385
+ let {
1386
+ gssLineData,
1387
+ onChange
1388
+ } = this.props;
1389
+ let oldSelectedTool = gssLineData.selectedTool;
1390
+ gssLineData.selectedTool = event.target.value;
1391
+ onChange(gssLineData, oldSelectedTool);
1392
+ };
1393
+
1394
+ this.lineTypeChange = (line, type) => {
1395
+ let {
1396
+ gssLineData,
1397
+ onChange
1398
+ } = this.props;
1399
+ let oldSelectedTool = gssLineData.selectedTool;
1400
+ gssLineData[`line${line}`].lineType = type;
1401
+ onChange(gssLineData, oldSelectedTool);
1402
+ };
1403
+ }
1404
+
1405
+ render() {
1406
+ let {
1407
+ classes,
1408
+ gssLineData,
1409
+ disabled
1410
+ } = this.props;
1411
+ return /*#__PURE__*/React.createElement("div", {
1412
+ className: classes.selectLineRadioGroup
1413
+ }, /*#__PURE__*/React.createElement("div", {
1414
+ className: classes.radioFieldOuter
1415
+ }, /*#__PURE__*/React.createElement("div", {
1416
+ className: classes.radioFieldInner
1417
+ }, /*#__PURE__*/React.createElement(Radio, {
1418
+ name: 'select-line-radio-buttons',
1419
+ onChange: this.onChangeRadioValue,
1420
+ value: 'lineA',
1421
+ disabled: !!disabled,
1422
+ checked: gssLineData.selectedTool === 'lineA',
1423
+ className: classes.lineTypeRadio
1424
+ }), /*#__PURE__*/React.createElement(Typography, {
1425
+ className: classes.lineNameFont
1426
+ }, "Line A")), /*#__PURE__*/React.createElement("div", {
1427
+ className: classes.radioFieldButtons
1428
+ }, gssLineData.lineA.lineType === 'Solid' ? /*#__PURE__*/React.createElement(Button, {
1429
+ size: 'small',
1430
+ variant: 'contained',
1431
+ color: 'primary',
1432
+ onClick: () => this.lineTypeChange('A', 'Solid'),
1433
+ className: disabled ? classes.lineTypeButtonLeftSelectedDisabled : classes.lineTypeButtonLeftSelected
1434
+ }, "\u2714 Solid") : /*#__PURE__*/React.createElement(Button, {
1435
+ size: 'small',
1436
+ variant: 'contained',
1437
+ disabled: !!disabled,
1438
+ onClick: () => this.lineTypeChange('A', 'Solid'),
1439
+ className: disabled ? classes.lineTypeButtonLeftUnSelectedDisabled : classes.lineTypeButtonLeftUnSelected
1440
+ }, "Solid"), gssLineData.lineA.lineType === 'Dashed' ? /*#__PURE__*/React.createElement(Button, {
1441
+ size: 'small',
1442
+ variant: 'contained',
1443
+ color: 'primary',
1444
+ onClick: () => this.lineTypeChange('A', 'Dashed'),
1445
+ className: disabled ? classes.lineTypeButtonRightSelectedDisabled : classes.lineTypeButtonRightSelected
1446
+ }, "\u2714 Dashed") : /*#__PURE__*/React.createElement(Button, {
1447
+ size: 'small',
1448
+ variant: 'contained',
1449
+ disabled: !!disabled,
1450
+ onClick: () => this.lineTypeChange('A', 'Dashed'),
1451
+ className: disabled ? classes.lineTypeButtonRightUnSelectedDisabled : classes.lineTypeButtonRightUnSelected
1452
+ }, "Dashed"))), gssLineData.numberOfLines === 2 ? /*#__PURE__*/React.createElement("div", {
1453
+ className: classes.radioFieldOuter
1454
+ }, /*#__PURE__*/React.createElement("div", {
1455
+ className: classes.radioFieldInner
1456
+ }, /*#__PURE__*/React.createElement(Radio, {
1457
+ name: 'select-line-radio-buttons',
1458
+ onChange: this.onChangeRadioValue,
1459
+ value: 'lineB',
1460
+ disabled: !!disabled,
1461
+ checked: gssLineData.selectedTool === 'lineB',
1462
+ className: classes.lineTypeRadio
1463
+ }), /*#__PURE__*/React.createElement(Typography, {
1464
+ className: classes.lineNameFont
1465
+ }, "Line B")), /*#__PURE__*/React.createElement("div", {
1466
+ className: classes.radioFieldButtons
1467
+ }, gssLineData.lineB.lineType === 'Solid' ? /*#__PURE__*/React.createElement(Button, {
1468
+ size: 'small',
1469
+ variant: 'contained',
1470
+ color: 'primary',
1471
+ onClick: () => this.lineTypeChange('B', 'Solid'),
1472
+ className: disabled ? classes.lineTypeButtonLeftSelectedDisabled : classes.lineTypeButtonLeftSelected
1473
+ }, "\u2714 Solid") : /*#__PURE__*/React.createElement(Button, {
1474
+ size: 'small',
1475
+ variant: 'contained',
1476
+ disabled: !!disabled,
1477
+ onClick: () => this.lineTypeChange('B', 'Solid'),
1478
+ className: disabled ? classes.lineTypeButtonLeftUnSelectedDisabled : classes.lineTypeButtonLeftUnSelected
1479
+ }, "Solid"), gssLineData.lineB.lineType === 'Dashed' ? /*#__PURE__*/React.createElement(Button, {
1480
+ size: 'small',
1481
+ variant: 'contained',
1482
+ color: 'primary',
1483
+ onClick: () => this.lineTypeChange('B', 'Dashed'),
1484
+ className: disabled ? classes.lineTypeButtonRightSelectedDisabled : classes.lineTypeButtonRightSelected
1485
+ }, "\u2714 Dashed") : /*#__PURE__*/React.createElement(Button, {
1486
+ size: 'small',
1487
+ variant: 'contained',
1488
+ disabled: !!disabled,
1489
+ onClick: () => this.lineTypeChange('B', 'Dashed'),
1490
+ className: disabled ? classes.lineTypeButtonRightUnSelectedDisabled : classes.lineTypeButtonRightUnSelected
1491
+ }, "Dashed"))) : null, /*#__PURE__*/React.createElement("div", {
1492
+ className: classes.radioFieldOuter
1493
+ }, /*#__PURE__*/React.createElement("div", {
1494
+ className: classes.radioFieldInner
1495
+ }, /*#__PURE__*/React.createElement(Radio, {
1496
+ name: 'select-line-radio-buttons',
1497
+ onChange: this.onChangeRadioValue,
1498
+ value: 'solutionSet',
1499
+ disabled: !!disabled,
1500
+ checked: gssLineData.selectedTool === 'solutionSet',
1501
+ className: classes.lineTypeRadio
1502
+ }), /*#__PURE__*/React.createElement(Typography, {
1503
+ className: classes.lineNameFont
1504
+ }, "Solution Set"))));
1505
+ }
1506
+
1507
+ }
1508
+ ToolMenu.propTypes = {
1509
+ classes: PropTypes.object,
1510
+ gssLineData: PropTypes.object,
1511
+ disabled: PropTypes.bool,
1512
+ onChange: PropTypes.func
1513
+ };
1514
+
1515
+ const styles$7 = theme => ({
1516
+ radioButtonClass: {},
1517
+ selectLineRadioGroup: {
1518
+ display: 'flex',
1519
+ flexWrap: 'wrap',
1520
+ flexDirection: 'row'
1521
+ },
1522
+ radioFieldInner: {
1523
+ display: 'flex',
1524
+ flexWrap: 'wrap',
1525
+ flexDirection: 'row',
1526
+ alignItems: 'center'
1527
+ },
1528
+ radioFieldOuter: {
1529
+ display: 'flex',
1530
+ flexWrap: 'wrap',
1531
+ flexDirection: 'column'
1532
+ },
1533
+ radioFieldButtons: {
1534
+ display: 'flex',
1535
+ flexWrap: 'wrap',
1536
+ flexDirection: 'row',
1537
+ padding: '0 .9rem'
1538
+ },
1539
+ lineTypeText: {
1540
+ marginLeft: '.7rem'
1541
+ },
1542
+ lineNameFont: {
1543
+ fontWeight: 'bold',
1544
+ padding: '0 5px 0 0'
1545
+ },
1546
+ lineTypeButtonLeftSelected: {
1547
+ textTransform: 'none',
1548
+ border: '1px solid #3E4EB1',
1549
+ borderRadius: '0',
1550
+ backgroundColor: '#3E4EB1 !important',
1551
+ borderTopLeftRadius: '4px',
1552
+ borderBottomLeftRadius: '4px',
1553
+ color: '#FFFFFF !important'
1554
+ },
1555
+ lineTypeButtonLeftUnSelected: {
1556
+ textTransform: 'none',
1557
+ border: '1px solid #3E4EB1',
1558
+ borderRadius: '0',
1559
+ backgroundColor: '#FFFFFF !important',
1560
+ borderTopLeftRadius: '4px',
1561
+ borderBottomLeftRadius: '4px',
1562
+ color: '#3E4EB1 !important'
1563
+ },
1564
+ lineTypeButtonRightSelected: {
1565
+ textTransform: 'none',
1566
+ border: '1px solid #3E4EB1',
1567
+ borderRadius: '0',
1568
+ backgroundColor: '#3E4EB1 !important',
1569
+ borderTopRightRadius: '4px',
1570
+ borderBottomRightRadius: '4px',
1571
+ color: '#FFFFFF !important'
1572
+ },
1573
+ lineTypeButtonRightUnSelected: {
1574
+ textTransform: 'none',
1575
+ border: '1px solid #3E4EB1',
1576
+ borderRadius: '0',
1577
+ backgroundColor: '#FFFFFF !important',
1578
+ borderTopRightRadius: '4px',
1579
+ borderBottomRightRadius: '4px',
1580
+ color: '#3E4EB1 !important'
1581
+ },
1582
+ lineTypeRadio: {
1583
+ color: '#000000 !important'
1584
+ },
1585
+ lineTypeButtonLeftSelectedDisabled: {
1586
+ textTransform: 'none',
1587
+ border: '1px solid #3E4EB1',
1588
+ borderRadius: '0',
1589
+ backgroundColor: '#3E4EB1 !important',
1590
+ borderTopLeftRadius: '4px',
1591
+ borderBottomLeftRadius: '4px',
1592
+ color: '#FFFFFF !important',
1593
+ cursor: 'default',
1594
+ pointerEvents: 'none'
1595
+ },
1596
+ lineTypeButtonLeftUnSelectedDisabled: {
1597
+ textTransform: 'none',
1598
+ border: '1px solid #3E4EB1',
1599
+ borderRadius: '0',
1600
+ backgroundColor: '#FFFFFF !important',
1601
+ borderTopLeftRadius: '4px',
1602
+ borderBottomLeftRadius: '4px',
1603
+ color: '#3E4EB1 !important',
1604
+ cursor: 'default',
1605
+ pointerEvents: 'none'
1606
+ },
1607
+ lineTypeButtonRightSelectedDisabled: {
1608
+ textTransform: 'none',
1609
+ border: '1px solid #3E4EB1',
1610
+ borderRadius: '0',
1611
+ backgroundColor: '#3E4EB1 !important',
1612
+ borderTopRightRadius: '4px',
1613
+ borderBottomRightRadius: '4px',
1614
+ color: '#FFFFFF !important',
1615
+ cursor: 'default',
1616
+ pointerEvents: 'none'
1617
+ },
1618
+ lineTypeButtonRightUnSelectedDisabled: {
1619
+ textTransform: 'none',
1620
+ border: '1px solid #3E4EB1',
1621
+ borderRadius: '0',
1622
+ backgroundColor: '#FFFFFF !important',
1623
+ borderTopRightRadius: '4px',
1624
+ borderBottomRightRadius: '4px',
1625
+ color: '#3E4EB1 !important',
1626
+ cursor: 'default',
1627
+ pointerEvents: 'none'
1628
+ }
1629
+ });
1630
+
1631
+ var ToolMenu$1 = withStyles(styles$7)(ToolMenu);
1632
+
1633
+ const {
1634
+ translator
1635
+ } = Translator;
1636
+ class UndoRedo extends React.Component {
1637
+ render() {
1638
+ const {
1639
+ classes,
1640
+ className,
1641
+ onReset = false,
1642
+ language
1643
+ } = this.props;
1644
+ return /*#__PURE__*/React.createElement("div", {
1645
+ className: classNames(className)
1646
+ }, /*#__PURE__*/React.createElement(Button$1, {
1647
+ classes: {
1648
+ root: classes.button
1649
+ },
1650
+ onClick: () => onReset()
1651
+ }, translator.t('graphing.reset', {
1652
+ lng: language
1653
+ })));
1654
+ }
1655
+
1656
+ }
1657
+ UndoRedo.propTypes = {
1658
+ classes: PropTypes.object,
1659
+ className: PropTypes.string,
1660
+ onReset: PropTypes.func.isRequired,
1661
+ language: PropTypes.string
1662
+ };
1663
+ UndoRedo.defaultProps = {};
1664
+
1665
+ const styles$6 = theme => ({
1666
+ button: {
1667
+ color: color.text(),
1668
+ fontWeight: 'bold',
1669
+ marginBottom: theme.spacing.unit / 2,
1670
+ '&:not(:last-of-type)': {
1671
+ marginRight: theme.spacing.unit / 2
1672
+ }
1673
+ },
1674
+ undoRedoDiv: {
1675
+ display: 'flex',
1676
+ flexDirection: 'row'
1677
+ }
1678
+ });
1679
+
1680
+ var UndoRedo$1 = withStyles$1(styles$6)(UndoRedo);
1681
+
1682
+ const ToolPropTypeFields = {
1683
+ mark: PropTypes.any,
1684
+ onChange: PropTypes.func,
1685
+ onDragStart: PropTypes.func,
1686
+ onDragStop: PropTypes.func
1687
+ };
1688
+
1689
+ const disabled = (key = 'fill') => ({
1690
+ [key]: color.disabled(),
1691
+ pointerEvents: 'none'
1692
+ });
1693
+ const correct = (key = 'fill') => ({
1694
+ [key]: color.correct(),
1695
+ pointerEvents: 'none'
1696
+ });
1697
+ const incorrect = (key = 'fill') => ({
1698
+ [key]: color.incorrect(),
1699
+ pointerEvents: 'none'
1700
+ });
1701
+ const missing = (key = 'fill') => ({
1702
+ [key]: color.missing(),
1703
+ pointerEvents: 'none'
1704
+ });
1705
+
1706
+ const _excluded$5 = ["points", "classes", "className", "correctness", "graphProps", "closed", "isSolution"];
1707
+ const getPointString = (points, scale) => {
1708
+ return (points || []).map(p => {
1709
+ const scaledPoint = {
1710
+ x: scale.x(p.x),
1711
+ y: scale.y(p.y)
1712
+ };
1713
+ return `${scaledPoint.x},${scaledPoint.y}`;
1714
+ }).join(' ');
1715
+ };
1716
+ class RawPolygon extends React.Component {
1717
+ render() {
1718
+ const _this$props = this.props,
1719
+ {
1720
+ points,
1721
+ classes,
1722
+ className,
1723
+ correctness,
1724
+ graphProps,
1725
+ closed,
1726
+ isSolution
1727
+ } = _this$props,
1728
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$5);
1729
+
1730
+ const {
1731
+ scale
1732
+ } = graphProps;
1733
+ const pointString = getPointString(points, scale);
1734
+ const Tag = closed ? 'polygon' : 'polyline';
1735
+ return /*#__PURE__*/React.createElement(Tag, _extends({
1736
+ points: pointString,
1737
+ className: classNames(isSolution ? classes.gssSolution : classes.gssClosed, classes[correctness], className)
1738
+ }, rest));
1739
+ }
1740
+
1741
+ }
1742
+ RawPolygon.propTypes = {
1743
+ classes: PropTypes.object,
1744
+ className: PropTypes.string,
1745
+ isSolution: PropTypes.bool,
1746
+ points: PropTypes.arrayOf(types.PointType),
1747
+ graphProps: types.GraphPropsType.isRequired,
1748
+ closed: PropTypes.bool.isRequired,
1749
+ correctness: PropTypes.string
1750
+ };
1751
+ RawPolygon.defaultProps = {
1752
+ points: []
1753
+ };
1754
+ const Polygon = withStyles(theme => ({
1755
+ closed: {
1756
+ fill: fade(theme.palette.primary.light, 0.2),
1757
+ // TODO hardcoded color
1758
+ strokeWidth: 2,
1759
+ stroke: color.defaults.SECONDARY_LIGHT
1760
+ },
1761
+ open: {
1762
+ fill: fade(theme.palette.primary.light, 0.0),
1763
+ // TODO hardcoded color
1764
+ strokeWidth: 2,
1765
+ stroke: color.defaults.SECONDARY_LIGHT,
1766
+ pointerEvents: 'none'
1767
+ },
1768
+ gssClosed: {
1769
+ fill: 'transparent',
1770
+ '&:hover': {
1771
+ fill: 'rgb(0, 0, 0, 0.25)'
1772
+ }
1773
+ },
1774
+ gssSolution: {
1775
+ fill: 'rgb(60, 73, 150, 0.6)'
1776
+ },
1777
+ disabled: _extends({}, disabled('stroke')),
1778
+ correct: _extends({}, correct('stroke')),
1779
+ incorrect: _extends({}, incorrect('stroke'))
1780
+ }))(RawPolygon);
1781
+ var DraggablePolygon = gridDraggable({
1782
+ bounds: (props, {
1783
+ domain,
1784
+ range
1785
+ }) => {
1786
+ const {
1787
+ points
1788
+ } = props;
1789
+ const area = polygonToArea(points);
1790
+ return bounds(area, domain, range);
1791
+ },
1792
+ anchorPoint: props => {
1793
+ const {
1794
+ points
1795
+ } = props;
1796
+ return points[0];
1797
+ },
1798
+ fromDelta: (props, delta) => {
1799
+ const {
1800
+ points
1801
+ } = props;
1802
+ return points.map(p => point(p).add(point(delta)));
1803
+ }
1804
+ })(Polygon);
1805
+
1806
+ const useDebounce = (value, delay) => {
1807
+ const [debouncedValue, setDebouncedValue] = useState(value);
1808
+ useEffect(() => {
1809
+ const handler = setTimeout(() => {
1810
+ setDebouncedValue(value);
1811
+ }, delay);
1812
+ return () => clearTimeout(handler);
1813
+ }, [value]);
1814
+ return debouncedValue;
1815
+ };
1816
+
1817
+ const styles$5 = theme => ({
1818
+ input: {
1819
+ float: 'right',
1820
+ padding: theme.spacing.unit * 0.5,
1821
+ fontFamily: theme.typography.fontFamily,
1822
+ fontSize: theme.typography.fontSize,
1823
+ border: `solid 1px ${color.defaults.SECONDARY}`,
1824
+ borderRadius: '3px',
1825
+ color: color.defaults.PRIMARY_DARK
1826
+ },
1827
+ disabled: {
1828
+ border: `solid 1px ${color.defaults.PRIMARY_DARK}`,
1829
+ background: theme.palette.background.paper
1830
+ },
1831
+ disabledMark: {
1832
+ border: `solid 1px ${color.disabled()}`,
1833
+ background: theme.palette.background.paper,
1834
+ color: color.disabled()
1835
+ }
1836
+ });
1837
+
1838
+ const position = (graphProps, mark, rect) => {
1839
+ rect = rect || {
1840
+ width: 0,
1841
+ height: 0
1842
+ };
1843
+ const {
1844
+ scale,
1845
+ domain,
1846
+ range
1847
+ } = graphProps;
1848
+ const shift = 10;
1849
+ const rightEdge = scale.x(mark.x) + rect.width + shift;
1850
+ const bottomEdge = scale.y(mark.y) + rect.height + shift;
1851
+ const h = rightEdge >= scale.x(domain.max) ? 'left' : 'right';
1852
+ const v = bottomEdge >= scale.y(range.min) ? 'top' : 'bottom';
1853
+ return `${v}-${h}`;
1854
+ };
1855
+ const coordinates = (graphProps, mark, rect, position) => {
1856
+ const {
1857
+ scale
1858
+ } = graphProps;
1859
+ const shift = 10;
1860
+ rect = rect || {
1861
+ width: 0,
1862
+ height: 0
1863
+ };
1864
+
1865
+ switch (position) {
1866
+ case 'bottom-right':
1867
+ {
1868
+ return {
1869
+ left: scale.x(mark.x) + shift,
1870
+ top: scale.y(mark.y) + shift
1871
+ };
1872
+ }
1873
+
1874
+ case 'bottom-left':
1875
+ {
1876
+ return {
1877
+ left: scale.x(mark.x) - shift - rect.width,
1878
+ top: scale.y(mark.y) + shift
1879
+ };
1880
+ }
1881
+
1882
+ case 'top-left':
1883
+ {
1884
+ return {
1885
+ left: scale.x(mark.x) - shift - rect.width,
1886
+ top: scale.y(mark.y) - shift - rect.height
1887
+ };
1888
+ }
1889
+
1890
+ case 'top-right':
1891
+ {
1892
+ return {
1893
+ left: scale.x(mark.x) + shift,
1894
+ top: scale.y(mark.y) - shift - rect.height
1895
+ };
1896
+ }
1897
+ }
1898
+ };
1899
+ const MarkLabel = props => {
1900
+ const [input, setInput] = useState(null);
1901
+
1902
+ const _ref = useCallback(node => setInput(node));
1903
+
1904
+ const {
1905
+ mark,
1906
+ graphProps,
1907
+ classes,
1908
+ disabled,
1909
+ inputRef: externalInputRef,
1910
+ theme
1911
+ } = props;
1912
+ const [label, setLabel] = useState(mark.label);
1913
+
1914
+ const onChange = e => setLabel(e.target.value);
1915
+
1916
+ const debouncedLabel = useDebounce(label, 200); // useState only sets the value once, to synch props to state need useEffect
1917
+
1918
+ useEffect(() => {
1919
+ setLabel(mark.label);
1920
+ }, [mark.label]); // pick up the change to debouncedLabel and save it
1921
+
1922
+ useEffect(() => {
1923
+ if (typeof debouncedLabel === 'string' && debouncedLabel !== mark.label) {
1924
+ props.onChange(debouncedLabel);
1925
+ }
1926
+ }, [debouncedLabel]);
1927
+ const rect = input ? input.getBoundingClientRect() : {
1928
+ width: 0,
1929
+ height: 0
1930
+ };
1931
+ const pos = position(graphProps, mark, rect);
1932
+ const leftTop = coordinates(graphProps, mark, rect, pos);
1933
+
1934
+ const style = _extends({
1935
+ position: 'fixed',
1936
+ pointerEvents: 'auto'
1937
+ }, leftTop);
1938
+
1939
+ const disabledInput = disabled || mark.disabled;
1940
+ return /*#__PURE__*/React.createElement(AutosizeInput, {
1941
+ inputRef: r => {
1942
+ _ref(r);
1943
+
1944
+ externalInputRef(r);
1945
+ },
1946
+ disabled: disabledInput,
1947
+ inputClassName: classNames(classes.input, {
1948
+ [classes.disabled]: disabled,
1949
+ [classes.disabledMark]: mark.disabled
1950
+ }),
1951
+ value: label,
1952
+ style: style,
1953
+ onChange: onChange
1954
+ });
1955
+ };
1956
+ MarkLabel.propTypes = {
1957
+ disabled: PropTypes.bool,
1958
+ onChange: PropTypes.func,
1959
+ graphProps: types.GraphPropsType,
1960
+ classes: PropTypes.object,
1961
+ inputRef: PropTypes.func,
1962
+ mark: PropTypes.object,
1963
+ theme: PropTypes.object
1964
+ };
1965
+ var MarkLabel$1 = withStyles(styles$5, {
1966
+ withTheme: true
1967
+ })(MarkLabel);
1968
+
1969
+ const log = debug('pie-lib:graphing-solution-set:polygon');
1970
+ const swap = (arr, ...rest) => {
1971
+ const pairs = chunk(rest, 2);
1972
+ return pairs.reduce((acc, pr) => {
1973
+ if (pr.length === 2) {
1974
+ let [e, replacement] = pr;
1975
+ invariant(Number.isFinite(e.index), 'Index must be defined');
1976
+ const index = e.index; // const i = acc.findIndex(pt => pt.x === e.x && pt.y === e.y);
1977
+
1978
+ if (index >= 0) {
1979
+ acc.splice(index, 1, replacement);
1980
+ return acc;
1981
+ } else {
1982
+ return acc;
1983
+ }
1984
+ } else {
1985
+ return acc;
1986
+ }
1987
+ }, [...arr]);
1988
+ };
1989
+ class RawBaseComponent extends React.Component {
1990
+ constructor(...args) {
1991
+ super(...args);
1992
+
1993
+ this.dragPoint = (index, from, to) => {
1994
+ log('[dragPoint] from, to:', from, to);
1995
+ const {
1996
+ onChange,
1997
+ points
1998
+ } = this.props;
1999
+ const update = [...points];
2000
+ const overlapPoint = !!(points || []).find(p => equalPoints(p, to));
2001
+
2002
+ if (equalPoints(from, to) || overlapPoint) {
2003
+ return;
2004
+ }
2005
+
2006
+ if (points[index].label) {
2007
+ to.label = points[index].label;
2008
+ }
2009
+
2010
+ update.splice(index, 1, to);
2011
+ onChange(update);
2012
+ };
2013
+
2014
+ this.dragLine = (existing, next) => {
2015
+ log('[dragLine]: ', existing, next);
2016
+ const {
2017
+ onChange
2018
+ } = this.props;
2019
+
2020
+ if (existing.from.label) {
2021
+ next.from.label = existing.from.label;
2022
+ }
2023
+
2024
+ if (existing.to.label) {
2025
+ next.to.label = existing.to.label;
2026
+ }
2027
+
2028
+ const points = swap(this.props.points, existing.from, next.from, existing.to, next.to);
2029
+ onChange(points);
2030
+ };
2031
+
2032
+ this.dragPoly = (existing, next) => {
2033
+ log('[dragPoly] ', existing, next);
2034
+ const {
2035
+ onChange
2036
+ } = this.props;
2037
+ next.forEach((point, index) => {
2038
+ if (existing[index].label) {
2039
+ next[index].label = existing[index].label;
2040
+ }
2041
+ });
2042
+ onChange(next);
2043
+ };
2044
+
2045
+ this.close = () => {
2046
+ const {
2047
+ points,
2048
+ onClosePolygon
2049
+ } = this.props;
2050
+ log('[close] ...');
2051
+
2052
+ if (points.length >= 3) {
2053
+ onClosePolygon();
2054
+ } else {
2055
+ log('[close] - nope');
2056
+ }
2057
+ };
2058
+
2059
+ this.labelChange = (point, index) => {
2060
+ const {
2061
+ points,
2062
+ onChangeProps
2063
+ } = this.props;
2064
+
2065
+ const updatedPoint = _extends({}, point);
2066
+
2067
+ if (!point.label || isEmpty(point.label)) {
2068
+ delete updatedPoint.label;
2069
+ }
2070
+
2071
+ const update = [...points];
2072
+ update.splice(index, 1, updatedPoint);
2073
+ onChangeProps(update);
2074
+ };
2075
+
2076
+ this.clickPoint = (point, index, data) => {
2077
+ const {
2078
+ closed,
2079
+ disabled,
2080
+ onClick,
2081
+ isToolActive,
2082
+ labelModeEnabled,
2083
+ onChangeProps,
2084
+ onChangeLabelProps,
2085
+ points
2086
+ } = this.props;
2087
+
2088
+ if (labelModeEnabled) {
2089
+ if (disabled) {
2090
+ return;
2091
+ }
2092
+
2093
+ if (points && index === points.length) {
2094
+ const {
2095
+ a,
2096
+ b
2097
+ } = getRightestPoints(points);
2098
+
2099
+ const middle = _extends({
2100
+ label: ''
2101
+ }, point, getMiddleOfTwoPoints(a, b));
2102
+
2103
+ onChangeLabelProps(middle);
2104
+ } else {
2105
+ const update = [...points];
2106
+ update.splice(index, 1, _extends({
2107
+ label: ''
2108
+ }, point));
2109
+ onChangeProps(update);
2110
+ }
2111
+
2112
+ if (this.input[index]) {
2113
+ this.input[index].focus();
2114
+ }
2115
+
2116
+ return;
2117
+ }
2118
+
2119
+ if (isToolActive && !closed && index === 0) {
2120
+ this.close();
2121
+ return;
2122
+ }
2123
+
2124
+ onClick(point || data);
2125
+ };
2126
+
2127
+ this.input = {};
2128
+ }
2129
+
2130
+ render() {
2131
+ const {
2132
+ closed,
2133
+ graphProps,
2134
+ onChangeLabelProps,
2135
+ points,
2136
+ middle,
2137
+ labelNode,
2138
+ labelModeEnabled,
2139
+ isSolution = false
2140
+ } = this.props;
2141
+ const polygonLabelIndex = points && points.length || 0;
2142
+
2143
+ if (labelNode && middle && middle.hasOwnProperty('label')) ;
2144
+
2145
+ return /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DraggablePolygon, {
2146
+ points: points,
2147
+ closed: closed,
2148
+ isSolution: isSolution,
2149
+ graphProps: graphProps,
2150
+ onClick: this.clickPoint.bind(this, middle, polygonLabelIndex)
2151
+ })));
2152
+ }
2153
+
2154
+ }
2155
+ RawBaseComponent.propTypes = {
2156
+ classes: PropTypes.object,
2157
+ className: PropTypes.string,
2158
+ disabled: PropTypes.bool,
2159
+ correctness: PropTypes.string,
2160
+ points: PropTypes.arrayOf(types.PointType),
2161
+ closed: PropTypes.bool,
2162
+ isSolution: PropTypes.bool,
2163
+ coordinatesOnHover: PropTypes.bool,
2164
+ onChange: PropTypes.func.isRequired,
2165
+ onClosePolygon: PropTypes.func.isRequired,
2166
+ onDragStart: PropTypes.func,
2167
+ onDragStop: PropTypes.func,
2168
+ onClick: PropTypes.func,
2169
+ graphProps: types.GraphPropsType.isRequired,
2170
+ isToolActive: PropTypes.bool,
2171
+ middle: PropTypes.object,
2172
+ labelNode: PropTypes.object,
2173
+ labelModeEnabled: PropTypes.bool,
2174
+ onChangeLabelProps: PropTypes.func,
2175
+ onChangeProps: PropTypes.func
2176
+ };
2177
+ RawBaseComponent.defaultProps = {
2178
+ points: []
2179
+ };
2180
+ const BaseComponent = withStyles(() => ({}))(RawBaseComponent);
2181
+ let Component$1 = class Component extends React.Component {
2182
+ constructor(props) {
2183
+ super(props);
2184
+
2185
+ this.change = points => {
2186
+ const {
2187
+ mark: {
2188
+ middle
2189
+ }
2190
+ } = this.props;
2191
+
2192
+ const mark = _extends({}, this.state.mark, {
2193
+ points
2194
+ });
2195
+
2196
+ if (middle) {
2197
+ const {
2198
+ a,
2199
+ b
2200
+ } = getRightestPoints(points);
2201
+ mark.middle = _extends({}, middle, getMiddleOfTwoPoints(a, b));
2202
+ }
2203
+
2204
+ this.setState({
2205
+ mark
2206
+ });
2207
+ };
2208
+
2209
+ this.changeProps = points => {
2210
+ const mark = _extends({}, this.props.mark, {
2211
+ points
2212
+ });
2213
+
2214
+ this.props.onChange(this.props.mark, mark);
2215
+ };
2216
+
2217
+ this.changeLabelProps = point => {
2218
+ const {
2219
+ mark,
2220
+ onChange
2221
+ } = this.props;
2222
+
2223
+ const middle = _extends({}, mark.middle, point);
2224
+
2225
+ onChange(mark, _extends({}, mark, {
2226
+ middle
2227
+ }));
2228
+ };
2229
+
2230
+ this.closePolygon = () => {
2231
+ log('[closePolygon] ...');
2232
+ const {
2233
+ onComplete,
2234
+ mark
2235
+ } = this.props;
2236
+
2237
+ const update = _extends({}, mark, {
2238
+ closed: true
2239
+ });
2240
+
2241
+ onComplete(mark, update);
2242
+ };
2243
+
2244
+ this.dragStart = () => this.setState({
2245
+ mark: this.props.mark
2246
+ });
2247
+
2248
+ this.dragStop = () => {
2249
+ const {
2250
+ onChange
2251
+ } = this.props;
2252
+
2253
+ const m = _extends({}, this.state.mark);
2254
+
2255
+ this.setState({
2256
+ mark: undefined
2257
+ }, () => {
2258
+ onChange(this.props.mark, m);
2259
+ });
2260
+ };
2261
+
2262
+ this.state = {};
2263
+ }
2264
+
2265
+ render() {
2266
+ const {
2267
+ coordinatesOnHover,
2268
+ mark,
2269
+ graphProps,
2270
+ onClick,
2271
+ isToolActive,
2272
+ labelNode,
2273
+ labelModeEnabled
2274
+ } = this.props;
2275
+ const {
2276
+ mark: stateMark
2277
+ } = this.state;
2278
+ return /*#__PURE__*/React.createElement(BaseComponent, _extends({}, stateMark || mark, {
2279
+ coordinatesOnHover: coordinatesOnHover,
2280
+ onChange: this.change,
2281
+ onChangeLabelProps: this.changeLabelProps,
2282
+ onChangeProps: this.changeProps,
2283
+ onClosePolygon: this.closePolygon,
2284
+ onDragStart: this.dragStart,
2285
+ onDragStop: this.dragStop,
2286
+ onClick: onClick,
2287
+ graphProps: graphProps,
2288
+ isToolActive: isToolActive,
2289
+ labelNode: labelNode,
2290
+ labelModeEnabled: labelModeEnabled
2291
+ }));
2292
+ }
2293
+
2294
+ };
2295
+ Component$1.propTypes = _extends({}, ToolPropTypeFields, {
2296
+ graphProps: types.GraphPropsType.isRequired
2297
+ });
2298
+ Component$1.defaultProps = {};
2299
+
2300
+ const addPointToArray = (point, arr) => {
2301
+ arr = arr || [];
2302
+
2303
+ if (arr.length === 0) {
2304
+ return {
2305
+ points: [point],
2306
+ closed: false
2307
+ };
2308
+ } else if (arr.length === 1) {
2309
+ if (equalPoints(point, arr[0])) {
2310
+ return {
2311
+ points: arr,
2312
+ closed: false
2313
+ };
2314
+ } else {
2315
+ return {
2316
+ points: [...arr, point],
2317
+ closed: false
2318
+ };
2319
+ }
2320
+ } else if (arr.length >= 2) {
2321
+ const closed = equalPoints(point, arr[0]);
2322
+
2323
+ if (closed) {
2324
+ return {
2325
+ points: arr,
2326
+ closed
2327
+ };
2328
+ } else {
2329
+ const hasPoint = !!arr.find(p => equalPoints(p, point));
2330
+
2331
+ if (hasPoint) {
2332
+ return {
2333
+ points: arr,
2334
+ closed: false
2335
+ };
2336
+ } else {
2337
+ return {
2338
+ points: [...arr, point],
2339
+ closed: false
2340
+ };
2341
+ }
2342
+ }
2343
+ }
2344
+ };
2345
+ const tool$1 = () => ({
2346
+ type: 'polygon',
2347
+ Component: Component$1,
2348
+ complete: (data, mark) => {
2349
+ return _extends({}, mark, {
2350
+ building: false,
2351
+ closed: true
2352
+ });
2353
+ },
2354
+ addPoint: (point, mark) => {
2355
+ if (!mark) {
2356
+ return {
2357
+ type: 'polygon',
2358
+ points: [point],
2359
+ closed: false,
2360
+ building: true
2361
+ };
2362
+ } else {
2363
+ const {
2364
+ closed,
2365
+ points
2366
+ } = addPointToArray(point, mark.points);
2367
+ return _extends({}, mark, {
2368
+ closed,
2369
+ points,
2370
+ building: !closed
2371
+ });
2372
+ }
2373
+ }
2374
+ });
2375
+
2376
+ const styles$4 = theme => ({
2377
+ input: {
2378
+ fontFamily: theme.typography.fontFamily,
2379
+ fontSize: theme.typography.fontSize,
2380
+ borderRadius: '8px',
2381
+ background: theme.palette.common.white,
2382
+ color: color.defaults.BLACK
2383
+ },
2384
+ inputLabel: {
2385
+ padding: 0
2386
+ }
2387
+ });
2388
+
2389
+ const getLabelPosition = (graphProps, x, y, labelLength) => {
2390
+ const {
2391
+ scale,
2392
+ domain,
2393
+ range
2394
+ } = graphProps; // treat corner cases for maximum and minimum
2395
+
2396
+ const topShift = y === range.min ? 16 : y === range.max ? 0 : 8;
2397
+ const leftShift = 10;
2398
+ const rightEdge = scale.x(x) + labelLength + leftShift;
2399
+
2400
+ if (rightEdge >= scale.x(domain.max)) {
2401
+ return {
2402
+ left: scale.x(x) - leftShift - labelLength,
2403
+ top: scale.y(y) - topShift
2404
+ };
2405
+ }
2406
+
2407
+ return {
2408
+ left: scale.x(x) + leftShift,
2409
+ top: scale.y(y) - topShift
2410
+ };
2411
+ };
2412
+ const CoordinatesLabel = ({
2413
+ x,
2414
+ y,
2415
+ graphProps,
2416
+ classes
2417
+ }) => {
2418
+ const label = `(${roundNumber(x)}, ${roundNumber(y)})`;
2419
+ const labelLength = (label.length || 0) * 6;
2420
+ const labelPosition = getLabelPosition(graphProps, x, y, labelLength);
2421
+
2422
+ const style = _extends({
2423
+ position: 'absolute',
2424
+ pointerEvents: 'auto',
2425
+ width: labelLength,
2426
+ padding: 0
2427
+ }, labelPosition);
2428
+
2429
+ return /*#__PURE__*/React.createElement(InputBase, {
2430
+ style: style,
2431
+ classes: {
2432
+ input: classes.inputLabel
2433
+ },
2434
+ className: classes.input,
2435
+ value: label,
2436
+ inputProps: {
2437
+ ariaLabel: 'naked'
2438
+ }
2439
+ });
2440
+ };
2441
+ CoordinatesLabel.propTypes = {
2442
+ graphProps: types.GraphPropsType,
2443
+ classes: PropTypes.object,
2444
+ x: PropTypes.number,
2445
+ y: PropTypes.number
2446
+ };
2447
+ var CoordinatesLabel$1 = withStyles(styles$4)(CoordinatesLabel);
2448
+
2449
+ const _excluded$4 = ["classes", "className", "coordinatesOnHover", "x", "y", "disabled", "correctness", "graphProps", "labelNode", "style", "onClick", "onTouchStart", "onTouchEnd"];
2450
+ class RawBp extends React.Component {
2451
+ constructor(props) {
2452
+ super(props);
2453
+ this.state = {
2454
+ showCoordinates: false
2455
+ };
2456
+ }
2457
+
2458
+ render() {
2459
+ const _this$props = this.props,
2460
+ {
2461
+ classes,
2462
+ className,
2463
+ coordinatesOnHover,
2464
+ x,
2465
+ y,
2466
+ disabled,
2467
+ correctness,
2468
+ graphProps,
2469
+ labelNode,
2470
+ onClick,
2471
+ // Refactored RawBp component by isolating onTouchStart and onTouchEnd handlers to the outer circle, resolving erratic touch event behavior.
2472
+ // Remaining props are now applied only to the inner circle for improved event handling consistency.
2473
+ onTouchStart,
2474
+ onTouchEnd
2475
+ } = _this$props,
2476
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$4);
2477
+
2478
+ const {
2479
+ showCoordinates
2480
+ } = this.state;
2481
+ const {
2482
+ scale
2483
+ } = graphProps;
2484
+ const r = thinnerShapesNeeded(graphProps) ? 5 : 7;
2485
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("circle", {
2486
+ style: {
2487
+ fill: 'transparent',
2488
+ cursor: 'pointer',
2489
+ pointerEvents: 'all'
2490
+ },
2491
+ r: r * 3,
2492
+ cx: scale.x(x),
2493
+ cy: scale.y(y),
2494
+ onMouseEnter: () => this.setState({
2495
+ showCoordinates: true
2496
+ }),
2497
+ onMouseLeave: () => this.setState({
2498
+ showCoordinates: false
2499
+ }),
2500
+ onTouchStart: onTouchStart,
2501
+ onTouchEnd: onTouchEnd,
2502
+ onClick: onClick
2503
+ }), /*#__PURE__*/React.createElement("g", {
2504
+ className: classNames(classes.point, disabled && classes.disabled, classes[correctness], className),
2505
+ onMouseEnter: () => this.setState({
2506
+ showCoordinates: true
2507
+ }),
2508
+ onMouseLeave: () => this.setState({
2509
+ showCoordinates: false
2510
+ })
2511
+ }, /*#__PURE__*/React.createElement("circle", _extends({}, rest, {
2512
+ style: {
2513
+ fill: color.defaults.BLACK,
2514
+ cursor: 'pointer'
2515
+ },
2516
+ r: r,
2517
+ cx: scale.x(x),
2518
+ cy: scale.y(y)
2519
+ })), labelNode && coordinatesOnHover && showCoordinates && /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(CoordinatesLabel$1, {
2520
+ graphProps: graphProps,
2521
+ x: x,
2522
+ y: y
2523
+ }), labelNode)));
2524
+ }
2525
+
2526
+ }
2527
+ RawBp.propTypes = {
2528
+ classes: PropTypes.object,
2529
+ className: PropTypes.string,
2530
+ coordinatesOnHover: PropTypes.bool,
2531
+ correctness: PropTypes.string,
2532
+ disabled: PropTypes.bool,
2533
+ labelNode: PropTypes.object,
2534
+ x: PropTypes.number,
2535
+ y: PropTypes.number,
2536
+ graphProps: types.GraphPropsType.isRequired
2537
+ };
2538
+
2539
+ const _excluded$3 = ["classes", "className", "x", "y", "disabled", "correctness", "graphProps", "from", "to"];
2540
+ class RawArrow extends React.Component {
2541
+ render() {
2542
+ const _this$props = this.props,
2543
+ {
2544
+ classes,
2545
+ className,
2546
+ x,
2547
+ y,
2548
+ disabled,
2549
+ correctness,
2550
+ graphProps,
2551
+ from,
2552
+ to
2553
+ } = _this$props,
2554
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$3); // x & y are the initial coordinates for the arrow
2555
+ // from & to are used only to calculate the angle that the arrow should be rotated with
2556
+
2557
+
2558
+ const {
2559
+ scale
2560
+ } = graphProps;
2561
+ const angle = getAngleDeg(from.x, from.y, to.x, to.y);
2562
+ let points = '';
2563
+
2564
+ if (isEqual(from, to)) {
2565
+ points = '0,0 0,0 0,0';
2566
+ } else {
2567
+ points = `0,0 ${arrowDimensions.vector},${arrowDimensions.vector * 2}
2568
+ -${arrowDimensions.vector},${arrowDimensions.vector * 2}`;
2569
+ }
2570
+
2571
+ return /*#__PURE__*/React.createElement("g", _extends({
2572
+ className: classNames(classes.point, disabled && classes.disabled, classes[correctness], className)
2573
+ }, rest), /*#__PURE__*/React.createElement("polygon", {
2574
+ points: points,
2575
+ transform: `
2576
+ translate(${scale.x(x)}, ${scale.y(y)})
2577
+ rotate(${angle} 0 0)`
2578
+ }));
2579
+ }
2580
+
2581
+ }
2582
+ RawArrow.propTypes = {
2583
+ classes: PropTypes.object,
2584
+ className: PropTypes.string,
2585
+ correctness: PropTypes.string,
2586
+ disabled: PropTypes.bool,
2587
+ x: PropTypes.number,
2588
+ y: PropTypes.number,
2589
+ from: PropTypes.shape({
2590
+ x: PropTypes.number,
2591
+ y: PropTypes.number
2592
+ }).isRequired,
2593
+ to: PropTypes.shape({
2594
+ x: PropTypes.number,
2595
+ y: PropTypes.number
2596
+ }),
2597
+ graphProps: types.GraphPropsType.isRequired
2598
+ };
2599
+ RawArrow.defaultProps = {
2600
+ from: {},
2601
+ to: {}
2602
+ };
2603
+
2604
+ const ArrowHead = ({
2605
+ size,
2606
+ transform,
2607
+ points
2608
+ }) => /*#__PURE__*/React.createElement("polygon", {
2609
+ points: points || `0,0 ${size},${size / 2} 0,${size}`,
2610
+ transform: transform
2611
+ });
2612
+ ArrowHead.propTypes = {
2613
+ points: PropTypes.string,
2614
+ size: PropTypes.number,
2615
+ transform: PropTypes.string
2616
+ };
2617
+ ArrowHead.defaultProps = {
2618
+ points: '',
2619
+ size: 10,
2620
+ transform: ''
2621
+ };
2622
+ const genUid = () => {
2623
+ const v = (Math.random() * 1000).toFixed(0);
2624
+ return `arrow-${v}`;
2625
+ };
2626
+ const ArrowMarker = ({
2627
+ id,
2628
+ size,
2629
+ className
2630
+ }) => {
2631
+ return /*#__PURE__*/React.createElement("marker", {
2632
+ id: id,
2633
+ viewBox: `0 0 ${size} ${size}`,
2634
+ refX: size / 2,
2635
+ refY: size / 2,
2636
+ markerWidth: size,
2637
+ markerHeight: size,
2638
+ orient: "auto-start-reverse",
2639
+ className: className,
2640
+ style: {
2641
+ fill: color.defaults.BLACK
2642
+ }
2643
+ }, /*#__PURE__*/React.createElement(ArrowHead, {
2644
+ size: size
2645
+ }));
2646
+ };
2647
+ ArrowMarker.propTypes = {
2648
+ id: PropTypes.string,
2649
+ size: PropTypes.number,
2650
+ className: PropTypes.string
2651
+ };
2652
+ ArrowMarker.defaultProps = {
2653
+ size: 5
2654
+ };
2655
+
2656
+ const _excluded$2 = ["classes", "angle", "className", "x", "y", "disabled", "correctness", "graphProps"];
2657
+ class Arrow extends React.Component {
2658
+ render() {
2659
+ const _this$props = this.props,
2660
+ {
2661
+ classes,
2662
+ angle,
2663
+ className,
2664
+ x,
2665
+ y,
2666
+ disabled,
2667
+ correctness,
2668
+ graphProps
2669
+ } = _this$props,
2670
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$2);
2671
+
2672
+ const size = thinnerShapesNeeded(graphProps) ? 12 : 14;
2673
+ const {
2674
+ scale
2675
+ } = graphProps;
2676
+ const scaledX = scale.x(x);
2677
+ const scaledY = scale.y(y);
2678
+ const transform = `rotate(${-angle}, ${scaledX},${scaledY})`;
2679
+ const points = `${scaledX},${scaledY}
2680
+ ${scaledX - size},${scaledY - size / 2}
2681
+ ${scaledX - size}, ${scaledY + size / 2}`;
2682
+ return /*#__PURE__*/React.createElement("g", _extends({
2683
+ className: classNames(classes.point, disabled && classes.disabled, classes[correctness], className)
2684
+ }, rest), /*#__PURE__*/React.createElement(ArrowHead, {
2685
+ size: size,
2686
+ transform: transform,
2687
+ points: points
2688
+ }));
2689
+ }
2690
+
2691
+ }
2692
+ Arrow.propTypes = {
2693
+ classes: PropTypes.object,
2694
+ className: PropTypes.string,
2695
+ correctness: PropTypes.string,
2696
+ disabled: PropTypes.bool,
2697
+ x: PropTypes.number.isRequired,
2698
+ y: PropTypes.number.isRequired,
2699
+ angle: PropTypes.number.isRequired,
2700
+ graphProps: types.GraphPropsType.isRequired
2701
+ };
2702
+
2703
+ const opts = {
2704
+ bounds: (props, {
2705
+ domain,
2706
+ range
2707
+ }) => {
2708
+ const {
2709
+ x,
2710
+ y
2711
+ } = props;
2712
+ const area = {
2713
+ left: x,
2714
+ top: y,
2715
+ bottom: y,
2716
+ right: x
2717
+ };
2718
+ return bounds(area, domain, range);
2719
+ },
2720
+ anchorPoint: props => {
2721
+ const {
2722
+ x,
2723
+ y
2724
+ } = props;
2725
+ return {
2726
+ x,
2727
+ y
2728
+ };
2729
+ },
2730
+ fromDelta: (props, delta) => {
2731
+ return point(props).add(point(delta));
2732
+ }
2733
+ };
2734
+
2735
+ const styles$3 = () => {
2736
+ return {
2737
+ point: {
2738
+ '& circle, & polygon': {
2739
+ cursor: 'pointer',
2740
+ fill: color.defaults.SECONDARY
2741
+ }
2742
+ },
2743
+ disabled: {
2744
+ '& circle, & polygon': _extends({}, disabled())
2745
+ },
2746
+ correct: {
2747
+ '& circle, & polygon': _extends({}, correct())
2748
+ },
2749
+ incorrect: {
2750
+ '& circle, & polygon': _extends({}, incorrect())
2751
+ },
2752
+ missing: {
2753
+ '& circle, & polygon': _extends({}, missing())
2754
+ }
2755
+ };
2756
+ };
2757
+
2758
+ const BasePoint = withStyles$2(styles$3)(gridDraggable(opts)(RawBp));
2759
+ withStyles$2(styles$3)(gridDraggable(opts)(RawArrow));
2760
+ withStyles$2(styles$3)(gridDraggable(opts)(Arrow));
2761
+
2762
+ const lineTool = (type, Component) => () => ({
2763
+ type,
2764
+ Component,
2765
+ addPoint: (point, mark) => {
2766
+ if (mark && equalPoints(mark.root, point)) {
2767
+ return mark;
2768
+ }
2769
+
2770
+ if (!mark) {
2771
+ return {
2772
+ type,
2773
+ building: true,
2774
+ from: point
2775
+ };
2776
+ }
2777
+
2778
+ if (equalPoints(point, mark.from)) {
2779
+ return _extends({}, mark);
2780
+ }
2781
+
2782
+ return _extends({}, mark, {
2783
+ building: false,
2784
+ to: point
2785
+ });
2786
+ }
2787
+ });
2788
+ const lineToolComponent = Component => {
2789
+ var _class;
2790
+
2791
+ return _class = class LineToolComponent extends React.Component {
2792
+ constructor(props) {
2793
+ super(props);
2794
+
2795
+ this.startDrag = () => this.setState({
2796
+ mark: _extends({}, this.props.mark)
2797
+ });
2798
+
2799
+ this.stopDrag = () => {
2800
+ const {
2801
+ onChange,
2802
+ mark
2803
+ } = this.props;
2804
+
2805
+ const update = _extends({}, this.state.mark);
2806
+
2807
+ this.setState({
2808
+ mark: undefined
2809
+ }, () => {
2810
+ const {
2811
+ type
2812
+ } = update;
2813
+ let shouldNotChange = type && (type === 'parabola' || type === 'sine' || type === 'absolute' || type === 'exponential') && sameAxes(update.from, update.to);
2814
+
2815
+ if (!shouldNotChange && type && type === 'exponential' && update.from && update.to) {
2816
+ shouldNotChange = update.from.y === 0 || update.to.y === 0 || update.from.y * update.to.y < 0;
2817
+ }
2818
+
2819
+ if (!isEqual(mark, update) && !shouldNotChange) {
2820
+ onChange(mark, update);
2821
+ }
2822
+ });
2823
+ };
2824
+
2825
+ this.changeMark = ({
2826
+ from,
2827
+ to,
2828
+ middle
2829
+ }) => {
2830
+ let mark = _extends({}, this.state.mark, {
2831
+ from,
2832
+ to
2833
+ });
2834
+
2835
+ if (middle) {
2836
+ mark = _extends({}, mark, {
2837
+ middle
2838
+ });
2839
+ }
2840
+
2841
+ this.setState({
2842
+ mark
2843
+ });
2844
+ };
2845
+
2846
+ this.changeMarkProps = ({
2847
+ from,
2848
+ to,
2849
+ middle
2850
+ }) => {
2851
+ const {
2852
+ onChange,
2853
+ mark
2854
+ } = this.props;
2855
+
2856
+ let update = _extends({}, mark, this.state.mark);
2857
+
2858
+ if (from) {
2859
+ update = _extends({}, update, {
2860
+ from
2861
+ });
2862
+ }
2863
+
2864
+ if (to) {
2865
+ update = _extends({}, update, {
2866
+ to
2867
+ });
2868
+ }
2869
+
2870
+ if (middle) {
2871
+ update = _extends({}, update, {
2872
+ middle
2873
+ });
2874
+ }
2875
+
2876
+ if (!isEqual(mark, update)) {
2877
+ onChange(mark, update);
2878
+ }
2879
+ };
2880
+
2881
+ this.state = {};
2882
+ }
2883
+
2884
+ render() {
2885
+ const {
2886
+ graphProps,
2887
+ onClick,
2888
+ labelNode,
2889
+ labelModeEnabled,
2890
+ coordinatesOnHover,
2891
+ gssLineData
2892
+ } = this.props;
2893
+ const mark = this.state.mark ? this.state.mark : this.props.mark;
2894
+ const from = cloneDeep(mark.from);
2895
+ const to = cloneDeep(mark.to);
2896
+ const middle = cloneDeep(mark.middle); // SET DISABLED
2897
+ // if it's a background mark, we need to force disable it
2898
+
2899
+ if (from && mark.isBackground) {
2900
+ from.disabled = true;
2901
+ }
2902
+
2903
+ if (to && mark.isBackground) {
2904
+ to.disabled = true;
2905
+ }
2906
+
2907
+ if (middle && mark.isBackground) {
2908
+ middle.disabled = true;
2909
+ }
2910
+
2911
+ return /*#__PURE__*/React.createElement(Component, {
2912
+ disabled: mark.disabled,
2913
+ coordinatesOnHover: coordinatesOnHover,
2914
+ correctness: mark.correctness,
2915
+ from: from,
2916
+ to: to,
2917
+ middle: middle,
2918
+ graphProps: graphProps,
2919
+ fill: mark && mark.fill,
2920
+ gssLineData: gssLineData,
2921
+ onChange: this.changeMark,
2922
+ changeMarkProps: this.changeMarkProps,
2923
+ onClick: onClick,
2924
+ onDragStart: this.startDrag,
2925
+ onDragStop: this.stopDrag,
2926
+ labelNode: labelNode,
2927
+ labelModeEnabled: labelModeEnabled
2928
+ });
2929
+ }
2930
+
2931
+ }, _class.propTypes = _extends({}, types.ToolPropTypeFields, {
2932
+ graphProps: types.GraphPropsType.isRequired
2933
+ }), _class;
2934
+ };
2935
+
2936
+ const dragOpts = () => ({
2937
+ bounds: (props, {
2938
+ domain,
2939
+ range
2940
+ }) => {
2941
+ const area = utils.lineToArea(props.from, props.to);
2942
+ return utils.bounds(area, domain, range);
2943
+ },
2944
+ anchorPoint: props => {
2945
+ const {
2946
+ from
2947
+ } = props;
2948
+ return from;
2949
+ },
2950
+ fromDelta: (props, delta) => {
2951
+ const {
2952
+ from,
2953
+ to
2954
+ } = props;
2955
+ return {
2956
+ from: utils.point(from).add(utils.point(delta)),
2957
+ to: utils.point(to).add(utils.point(delta))
2958
+ };
2959
+ }
2960
+ });
2961
+
2962
+ const lineBase = (Comp, opts) => {
2963
+ const DraggableComp = gridDraggable(dragOpts())(Comp);
2964
+ const FromPoint = BasePoint;
2965
+ const ToPoint = BasePoint;
2966
+
2967
+ class LineBase extends React.Component {
2968
+ constructor(...args) {
2969
+ super(...args);
2970
+
2971
+ this.onChangePoint = point => {
2972
+ const {
2973
+ middle,
2974
+ onChange
2975
+ } = this.props;
2976
+ const {
2977
+ from,
2978
+ to
2979
+ } = point; // because point.from.label and point.to.label can be different
2980
+
2981
+ if (!equalPoints(from, to)) {
2982
+ if (middle) {
2983
+ point.middle = _extends({}, middle, getMiddleOfTwoPoints(from, to));
2984
+ }
2985
+
2986
+ onChange(point);
2987
+ }
2988
+ };
2989
+
2990
+ this.dragComp = ({
2991
+ from: draggedFrom,
2992
+ to: draggedTo
2993
+ }) => {
2994
+ const {
2995
+ from,
2996
+ to,
2997
+ onChange,
2998
+ middle
2999
+ } = this.props;
3000
+
3001
+ if (from.label) {
3002
+ draggedFrom.label = from.label;
3003
+ }
3004
+
3005
+ if (to.label) {
3006
+ draggedTo.label = to.label;
3007
+ }
3008
+
3009
+ const updated = {
3010
+ from: draggedFrom,
3011
+ to: draggedTo
3012
+ };
3013
+
3014
+ if (middle) {
3015
+ updated.middle = _extends({}, middle, getMiddleOfTwoPoints(draggedFrom, draggedTo));
3016
+ }
3017
+
3018
+ onChange(updated);
3019
+ };
3020
+
3021
+ this.dragFrom = draggedFrom => {
3022
+ const {
3023
+ from,
3024
+ to
3025
+ } = this.props;
3026
+
3027
+ if (from.label) {
3028
+ draggedFrom.label = from.label;
3029
+ }
3030
+
3031
+ if (!equalPoints(draggedFrom, to)) {
3032
+ this.onChangePoint({
3033
+ from: draggedFrom,
3034
+ to: to
3035
+ });
3036
+ }
3037
+ };
3038
+
3039
+ this.dragTo = draggedTo => {
3040
+ const {
3041
+ from,
3042
+ to
3043
+ } = this.props;
3044
+
3045
+ if (to.label) {
3046
+ draggedTo.label = to.label;
3047
+ }
3048
+
3049
+ if (!equalPoints(from, draggedTo)) {
3050
+ this.onChangePoint({
3051
+ from: from,
3052
+ to: draggedTo
3053
+ });
3054
+ }
3055
+ };
3056
+
3057
+ this.labelChange = (point, type) => {
3058
+ const {
3059
+ changeMarkProps
3060
+ } = this.props;
3061
+
3062
+ const update = _extends({}, point);
3063
+
3064
+ if (!point.label || isEmpty(point.label)) {
3065
+ delete update.label;
3066
+ }
3067
+
3068
+ changeMarkProps({
3069
+ [type]: update
3070
+ });
3071
+ };
3072
+
3073
+ this.clickPoint = (point, type, data) => {
3074
+ const {
3075
+ changeMarkProps,
3076
+ disabled,
3077
+ from,
3078
+ to,
3079
+ labelModeEnabled,
3080
+ onClick
3081
+ } = this.props;
3082
+
3083
+ if (!labelModeEnabled) {
3084
+ onClick(point || data);
3085
+ return;
3086
+ }
3087
+
3088
+ if (disabled) {
3089
+ return;
3090
+ }
3091
+
3092
+ if (type === 'middle' && !point && from && to) {
3093
+ point = _extends({}, point, getMiddleOfTwoPoints(from, to));
3094
+ }
3095
+
3096
+ changeMarkProps({
3097
+ from,
3098
+ to,
3099
+ [type]: _extends({
3100
+ label: ''
3101
+ }, point)
3102
+ });
3103
+
3104
+ if (this.input[type]) {
3105
+ this.input[type].focus();
3106
+ }
3107
+ };
3108
+
3109
+ this.input = {};
3110
+ }
3111
+
3112
+ render() {
3113
+ const {
3114
+ coordinatesOnHover,
3115
+ graphProps,
3116
+ fill,
3117
+ gssLineData,
3118
+ onDragStart,
3119
+ onDragStop,
3120
+ from,
3121
+ to,
3122
+ middle,
3123
+ disabled,
3124
+ correctness,
3125
+ onClick,
3126
+ labelNode,
3127
+ labelModeEnabled
3128
+ } = this.props;
3129
+ let common = {
3130
+ graphProps,
3131
+ fill,
3132
+ onDragStart,
3133
+ onDragStop,
3134
+ disabled,
3135
+ correctness,
3136
+ onClick
3137
+ };
3138
+
3139
+ if (gssLineData && gssLineData.selectedTool === 'solutionSet') {
3140
+ //removing dragging option for line if solution set is clicked.
3141
+ common = {
3142
+ graphProps,
3143
+ fill,
3144
+ disabled,
3145
+ correctness,
3146
+ onClick
3147
+ };
3148
+ }
3149
+
3150
+ const angle = to ? trig.toDegrees(trig.angle(from, to)) : 0;
3151
+ let fromLabelNode = null;
3152
+ let toLabelNode = null;
3153
+ let lineLabelNode = null;
3154
+
3155
+ if (labelNode) {
3156
+ if (from && from.hasOwnProperty('label')) {
3157
+ fromLabelNode = /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(MarkLabel$1, {
3158
+ inputRef: r => this.input.from = r,
3159
+ disabled: !labelModeEnabled,
3160
+ mark: from,
3161
+ graphProps: graphProps,
3162
+ onChange: label => this.labelChange(_extends({}, from, {
3163
+ label
3164
+ }), 'from')
3165
+ }), labelNode);
3166
+ }
3167
+
3168
+ if (to && to.hasOwnProperty('label')) {
3169
+ toLabelNode = /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(MarkLabel$1, {
3170
+ inputRef: r => this.input.to = r,
3171
+ disabled: !labelModeEnabled,
3172
+ mark: to,
3173
+ graphProps: graphProps,
3174
+ onChange: label => this.labelChange(_extends({}, to, {
3175
+ label
3176
+ }), 'to')
3177
+ }), labelNode);
3178
+ }
3179
+
3180
+ if (middle && middle.hasOwnProperty('label')) {
3181
+ lineLabelNode = /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(MarkLabel$1, {
3182
+ inputRef: r => this.input.middle = r,
3183
+ disabled: !labelModeEnabled,
3184
+ mark: middle,
3185
+ graphProps: graphProps,
3186
+ onChange: label => this.labelChange(_extends({}, middle, {
3187
+ label
3188
+ }), 'middle')
3189
+ }), labelNode);
3190
+ }
3191
+ }
3192
+
3193
+ if (gssLineData && gssLineData.selectedTool === 'solutionSet') {
3194
+ //removing dragging feature of line and point if solution set is true.
3195
+ return /*#__PURE__*/React.createElement("g", null, to && /*#__PURE__*/React.createElement(DraggableComp, _extends({
3196
+ from: from,
3197
+ to: to,
3198
+ middle: middle
3199
+ }, common)), lineLabelNode, /*#__PURE__*/React.createElement(FromPoint, _extends({
3200
+ x: from.x,
3201
+ y: from.y,
3202
+ labelNode: labelNode,
3203
+ coordinatesOnHover: coordinatesOnHover
3204
+ }, common)), fromLabelNode, to && /*#__PURE__*/React.createElement(ToPoint, _extends({
3205
+ x: to.x,
3206
+ y: to.y,
3207
+ angle: angle //angle + 45}
3208
+ ,
3209
+ labelNode: labelNode,
3210
+ coordinatesOnHover: coordinatesOnHover
3211
+ }, common)), toLabelNode);
3212
+ } else {
3213
+ return /*#__PURE__*/React.createElement("g", null, to && /*#__PURE__*/React.createElement(DraggableComp, _extends({
3214
+ from: from,
3215
+ to: to,
3216
+ middle: middle,
3217
+ onDrag: this.dragComp
3218
+ }, common, {
3219
+ onClick: data => this.clickPoint(middle, 'middle', data)
3220
+ })), lineLabelNode, /*#__PURE__*/React.createElement(FromPoint, _extends({
3221
+ x: from.x,
3222
+ y: from.y,
3223
+ labelNode: labelNode,
3224
+ coordinatesOnHover: coordinatesOnHover,
3225
+ onDrag: this.dragFrom
3226
+ }, common, {
3227
+ onClick: data => this.clickPoint(from, 'from', data)
3228
+ })), fromLabelNode, to && /*#__PURE__*/React.createElement(ToPoint, _extends({
3229
+ x: to.x,
3230
+ y: to.y,
3231
+ angle: angle //angle + 45}
3232
+ ,
3233
+ labelNode: labelNode,
3234
+ coordinatesOnHover: coordinatesOnHover,
3235
+ onDrag: this.dragTo
3236
+ }, common, {
3237
+ onClick: data => this.clickPoint(to, 'to', data)
3238
+ })), toLabelNode);
3239
+ }
3240
+ }
3241
+
3242
+ }
3243
+
3244
+ LineBase.propTypes = {
3245
+ coordinatesOnHover: PropTypes.bool,
3246
+ graphProps: types.GraphPropsType,
3247
+ from: types.PointType,
3248
+ to: types.PointType,
3249
+ middle: types.PointType,
3250
+ onChange: PropTypes.func,
3251
+ onDragStart: PropTypes.func,
3252
+ onDragStop: PropTypes.func,
3253
+ onClick: PropTypes.func,
3254
+ correctness: PropTypes.string,
3255
+ fill: PropTypes.string,
3256
+ gssLineData: PropTypes.object,
3257
+ disabled: PropTypes.bool,
3258
+ labelNode: PropTypes.object,
3259
+ labelModeEnabled: PropTypes.bool,
3260
+ changeMarkProps: PropTypes.func
3261
+ };
3262
+ return LineBase;
3263
+ };
3264
+ const styles$2 = {
3265
+ line: () => ({
3266
+ fill: 'transparent',
3267
+ stroke: color.defaults.BLACK,
3268
+ strokeWidth: 4,
3269
+ transition: 'stroke 200ms ease-in, stroke-width 200ms ease-in',
3270
+ '&:hover': {
3271
+ strokeWidth: 6,
3272
+ stroke: color.defaults.BLACK
3273
+ }
3274
+ }),
3275
+ dashedLine: () => ({
3276
+ fill: 'transparent',
3277
+ stroke: color.defaults.BLACK,
3278
+ strokeWidth: 4,
3279
+ transition: 'stroke 200ms ease-in, stroke-width 200ms ease-in',
3280
+ '&:hover': {
3281
+ strokeWidth: 6,
3282
+ stroke: color.defaults.BLACK
3283
+ },
3284
+ strokeDasharray: '3,3'
3285
+ }),
3286
+ arrow: () => ({
3287
+ fill: color.defaults.SECONDARY
3288
+ }),
3289
+ disabledArrow: () => _extends({}, disabled()),
3290
+ disabled: () => _extends({}, disabled('stroke'), {
3291
+ strokeWidth: 2
3292
+ }),
3293
+ correct: (theme, key) => _extends({}, correct(key)),
3294
+ incorrect: (theme, key) => _extends({}, incorrect(key)),
3295
+ missing: (theme, key) => _extends({}, missing(key))
3296
+ };
3297
+
3298
+ const _excluded$1 = ["className", "classes", "correctness", "disabled", "graphProps", "fill", "from", "to"];
3299
+
3300
+ const lineStyles = theme => ({
3301
+ line: styles$2.line(theme),
3302
+ dashedLine: styles$2.dashedLine(theme),
3303
+ enabledArrow: styles$2.arrow(theme),
3304
+ disabledArrow: styles$2.disabledArrow(theme),
3305
+ disabled: styles$2.disabled(theme),
3306
+ correct: styles$2.correct(theme, 'stroke'),
3307
+ correctArrow: styles$2.correct(theme),
3308
+ incorrect: styles$2.incorrect(theme, 'stroke'),
3309
+ incorrectArrow: styles$2.incorrect(theme),
3310
+ missing: styles$2.missing(theme, 'stroke'),
3311
+ missingArrow: styles$2.missing(theme)
3312
+ });
3313
+
3314
+ const ArrowedLine = props => {
3315
+ const markerId = genUid();
3316
+
3317
+ const {
3318
+ className,
3319
+ classes,
3320
+ correctness,
3321
+ disabled,
3322
+ graphProps,
3323
+ fill = 'Solid',
3324
+ from,
3325
+ to
3326
+ } = props,
3327
+ rest = _objectWithoutPropertiesLoose(props, _excluded$1);
3328
+
3329
+ const {
3330
+ scale
3331
+ } = graphProps;
3332
+ const {
3333
+ domain,
3334
+ range
3335
+ } = getAdjustedGraphLimits(graphProps);
3336
+ const [eFrom, eTo] = trig.edges(domain, range)(from, to);
3337
+ const suffix = correctness || disabled && 'disabled' || 'enabled';
3338
+ return /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement(ArrowMarker, {
3339
+ size: thinnerShapesNeeded(graphProps) ? 4 : 5,
3340
+ id: `${props.markerId || markerId}-${suffix}`,
3341
+ className: classNames(classes[`${suffix}Arrow`])
3342
+ })), /*#__PURE__*/React.createElement("line", _extends({
3343
+ x1: scale.x(eFrom.x),
3344
+ y1: scale.y(eFrom.y),
3345
+ x2: scale.x(eTo.x),
3346
+ y2: scale.y(eTo.y),
3347
+ className: classNames(fill === 'Solid' ? classes.line : classes.dashedLine, disabled && classes.disabled, classes[correctness], className),
3348
+ markerEnd: `url(#${props.markerId || markerId}-${suffix})`,
3349
+ markerStart: `url(#${props.markerId || markerId}-${suffix})`
3350
+ }, rest)));
3351
+ };
3352
+ ArrowedLine.propTypes = {
3353
+ className: PropTypes.string,
3354
+ classes: PropTypes.object,
3355
+ fill: PropTypes.string,
3356
+ correctness: PropTypes.string,
3357
+ disabled: PropTypes.bool,
3358
+ graphProps: types.GraphPropsType,
3359
+ from: types.PointType,
3360
+ to: types.PointType,
3361
+ markerId: PropTypes.string
3362
+ };
3363
+ const StyledArrowedLine = withStyles(lineStyles)(ArrowedLine);
3364
+ const Line = lineBase(StyledArrowedLine);
3365
+ const Component = lineToolComponent(Line);
3366
+
3367
+ const tool = lineTool('line', Component);
3368
+
3369
+ const allTools = ['line', 'polygon'];
3370
+ const toolsArr = [tool(), tool$1()];
3371
+
3372
+ var index = /*#__PURE__*/Object.freeze({
3373
+ __proto__: null,
3374
+ allTools: allTools,
3375
+ line: tool,
3376
+ polygon: tool$1,
3377
+ toolsArr: toolsArr
3378
+ });
3379
+
3380
+ const setToolbarAvailability = toolbarTools => toolsArr.map(tA => _extends({}, tA, {
3381
+ toolbar: !!toolbarTools.find(t => t === tA.type)
3382
+ })) || [];
3383
+ const toolIsAvailable = (tools, currentTool) => currentTool && tools && (tools.find(tool => tool.type === currentTool.type) || {}).toolbar;
3384
+ const getAvailableTool = tools => tools.find(tool => tool.toolbar);
3385
+ const filterByValidToolTypes = backgroundMarks => backgroundMarks.filter(bM => !!allTools.find(tool => tool === bM.type));
3386
+ const filterByVisibleToolTypes = (toolbarTools, marks) => marks.filter(bM => !!toolbarTools.find(tool => tool === bM.type));
3387
+
3388
+ const getDefaultCurrentTool = toolType => toolsArr.find(tool => tool.type === toolType) || null;
3389
+
3390
+ const Collapsible = ({
3391
+ classes,
3392
+ children,
3393
+ title
3394
+ }) => /*#__PURE__*/React.createElement(ExpansionPanel, {
3395
+ elevation: 0,
3396
+ className: classes.expansionPanel,
3397
+ disabledGutters: true,
3398
+ square: true
3399
+ }, /*#__PURE__*/React.createElement(ExpansionPanelSummary, {
3400
+ classes: {
3401
+ root: classes.summaryRoot,
3402
+ content: classes.summaryContent
3403
+ },
3404
+ expandIcon: /*#__PURE__*/React.createElement(ExpandMoreIcon, null)
3405
+ }, /*#__PURE__*/React.createElement(Typography, {
3406
+ variant: "subheading"
3407
+ }, title)), /*#__PURE__*/React.createElement(ExpansionPanelDetails, {
3408
+ className: classes.details
3409
+ }, children));
3410
+
3411
+ Collapsible.propTypes = {
3412
+ classes: PropTypes.object,
3413
+ children: PropTypes.array,
3414
+ title: PropTypes.string
3415
+ };
3416
+ class GraphWithControls extends React.Component {
3417
+ constructor(props) {
3418
+ super(props);
3419
+
3420
+ this.changeCurrentTool = (tool, tools) => this.setState({
3421
+ currentTool: tools.find(t => t.type === tool)
3422
+ });
3423
+
3424
+ this.toggleLabelMode = () => this.setState(state => ({
3425
+ labelModeEnabled: !state.labelModeEnabled
3426
+ }));
3427
+
3428
+ this.state = {
3429
+ currentTool: getDefaultCurrentTool(props.defaultTool),
3430
+ labelModeEnabled: false
3431
+ };
3432
+ }
3433
+
3434
+ componentDidUpdate(prevProps) {
3435
+ const {
3436
+ defaultTool
3437
+ } = this.props;
3438
+
3439
+ if (prevProps.defaultTool !== defaultTool) {
3440
+ const currentTool = getDefaultCurrentTool(defaultTool);
3441
+ this.setState({
3442
+ currentTool
3443
+ });
3444
+ }
3445
+ }
3446
+
3447
+ render() {
3448
+ let {
3449
+ currentTool,
3450
+ labelModeEnabled
3451
+ } = this.state;
3452
+ const {
3453
+ axesSettings,
3454
+ classes,
3455
+ className,
3456
+ coordinatesOnHover,
3457
+ collapsibleToolbar,
3458
+ collapsibleToolbarTitle,
3459
+ disabled,
3460
+ disabledLabels,
3461
+ disabledTitle,
3462
+ domain,
3463
+ labels,
3464
+ labelsPlaceholders,
3465
+ onChangeLabels,
3466
+ onChangeMarks,
3467
+ onChangeTitle,
3468
+ onUndo,
3469
+ onRedo,
3470
+ range,
3471
+ size,
3472
+ showLabels,
3473
+ showPixelGuides,
3474
+ showTitle,
3475
+ title,
3476
+ titlePlaceholder,
3477
+ language,
3478
+ disableToolbar = false,
3479
+ gssLineData,
3480
+ onChangeGssLineData,
3481
+ onSolutionSetSelected,
3482
+ onCustomReset
3483
+ } = this.props;
3484
+ let {
3485
+ backgroundMarks,
3486
+ marks,
3487
+ toolbarTools
3488
+ } = this.props; // make sure only valid tool types are kept (string) and without duplicates
3489
+
3490
+ toolbarTools = uniq(toolbarTools || []).filter(tT => !!isString(tT)) || []; // keep only the backgroundMarks that have valid types
3491
+
3492
+ backgroundMarks = filterByValidToolTypes(backgroundMarks || []); // keep only the marks that have types which appear in toolbar
3493
+
3494
+ marks = filterByVisibleToolTypes(toolbarTools, marks || []);
3495
+ if (gssLineData && gssLineData.lineA && marks[0] && marks[0].type === 'line') marks[0].fill = gssLineData.lineA.lineType;
3496
+ if (gssLineData && gssLineData.lineB && marks[1] && marks[1].type === 'line') marks[1].fill = gssLineData.lineB.lineType;
3497
+ const tools = setToolbarAvailability(toolbarTools); // set current tool if there's no current tool or if the existing one is no longer available
3498
+
3499
+ if (!currentTool || !toolIsAvailable(tools, currentTool)) {
3500
+ currentTool = getAvailableTool(tools);
3501
+ }
3502
+
3503
+ const gssActions = gssLineData && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ToolMenu$1, {
3504
+ numberOfLines: gssLineData.numberOfLines,
3505
+ gssLineData: gssLineData,
3506
+ onChange: onChangeGssLineData,
3507
+ disabled: !!disabled,
3508
+ language: language
3509
+ }), !disabled && /*#__PURE__*/React.createElement(UndoRedo$1, {
3510
+ className: classes.undoRedoOuterDiv,
3511
+ onUndo: onUndo,
3512
+ onRedo: onRedo,
3513
+ onReset: onCustomReset,
3514
+ language: language
3515
+ }));
3516
+ return /*#__PURE__*/React.createElement("div", {
3517
+ className: classNames(classes.graphWithControls, className)
3518
+ }, !disableToolbar && /*#__PURE__*/React.createElement("div", {
3519
+ className: classes.controls
3520
+ }, collapsibleToolbar ? /*#__PURE__*/React.createElement(Collapsible, {
3521
+ classes: classes,
3522
+ title: collapsibleToolbarTitle
3523
+ }, gssActions) : gssActions), /*#__PURE__*/React.createElement("div", {
3524
+ ref: r => this.labelNode = r
3525
+ }), /*#__PURE__*/React.createElement(Graph, {
3526
+ axesSettings: axesSettings,
3527
+ backgroundMarks: backgroundMarks,
3528
+ coordinatesOnHover: coordinatesOnHover,
3529
+ currentTool: currentTool,
3530
+ disabledLabels: disabledLabels,
3531
+ disabledTitle: disabledTitle,
3532
+ domain: domain,
3533
+ labels: labels,
3534
+ labelModeEnabled: labelModeEnabled,
3535
+ labelsPlaceholders: labelsPlaceholders,
3536
+ marks: marks,
3537
+ onChangeMarks: !disabled ? onChangeMarks : undefined,
3538
+ onChangeLabels: onChangeLabels,
3539
+ onChangeTitle: onChangeTitle,
3540
+ range: range,
3541
+ size: size,
3542
+ showLabels: showLabels,
3543
+ showPixelGuides: showPixelGuides,
3544
+ showTitle: showTitle,
3545
+ title: title,
3546
+ titlePlaceholder: titlePlaceholder,
3547
+ tools: tools,
3548
+ gssLineData: gssLineData,
3549
+ onSolutionSetSelected: onSolutionSetSelected,
3550
+ disabled: !!disabled
3551
+ }));
3552
+ }
3553
+
3554
+ }
3555
+ GraphWithControls.propTypes = _extends({}, graphPropTypes, {
3556
+ onUndo: PropTypes.func,
3557
+ onRedo: PropTypes.func,
3558
+ onReset: PropTypes.func,
3559
+ toolbarTools: PropTypes.arrayOf(PropTypes.string),
3560
+ // array of tool types that have to be displayed in the toolbar, same shape as 'allTools'
3561
+ language: PropTypes.string
3562
+ });
3563
+ GraphWithControls.defaultProps = {
3564
+ collapsibleToolbar: false,
3565
+ collapsibleToolbarTitle: '',
3566
+ disabledLabels: false,
3567
+ disabledTitle: false,
3568
+ showLabels: true,
3569
+ showTitle: true,
3570
+ toolbarTools: []
3571
+ };
3572
+
3573
+ const styles$1 = theme => ({
3574
+ graphWithControls: {
3575
+ display: 'flex',
3576
+ flexDirection: 'column',
3577
+ width: 'min-content'
3578
+ },
3579
+ controls: {
3580
+ display: 'flex',
3581
+ justifyContent: 'space-between',
3582
+ padding: 'calc(1.25rem - 12px) calc(1.25rem - 12px) 1.25rem',
3583
+ color: color.text(),
3584
+ backgroundColor: '#9FA8DA',
3585
+ '& button': {
3586
+ fontSize: '0.875rem',
3587
+ padding: '0.25rem .3rem',
3588
+ width: '5rem'
3589
+ }
3590
+ },
3591
+ expansionPanel: {
3592
+ backgroundColor: color.primaryLight(),
3593
+ width: '100%'
3594
+ },
3595
+ summaryRoot: {
3596
+ padding: `0 ${theme.spacing.unit}px`,
3597
+ minHeight: '32px !important'
3598
+ },
3599
+ summaryContent: {
3600
+ margin: '4px 0 !important'
3601
+ },
3602
+ details: {
3603
+ padding: 0,
3604
+ marginTop: theme.spacing.unit,
3605
+ display: 'flex',
3606
+ justifyContent: 'space-between'
3607
+ },
3608
+ undoRedoOuterDiv: {
3609
+ display: 'flex',
3610
+ flexDirection: 'column',
3611
+ marginTop: '.5rem'
3612
+ }
3613
+ });
3614
+
3615
+ var GraphWithControls$1 = withStyles(styles$1)(GraphWithControls);
3616
+
3617
+ let lastAction = null;
3618
+ const getLastAction = () => lastAction;
3619
+ const lastActionMiddleware = () => next => action => {
3620
+ lastAction = action;
3621
+ return next(action);
3622
+ };
3623
+
3624
+ const _excluded = ["onChangeMarks", "marks"];
3625
+
3626
+ const mapStateToProps = s => ({
3627
+ marks: s.marks.present
3628
+ });
3629
+
3630
+ const mapDispatchToProps = dispatch => ({
3631
+ onChangeMarks: m => dispatch(changeMarks(m)),
3632
+ onUndo: () => dispatch(ActionCreators.undo()),
3633
+ onRedo: () => dispatch(ActionCreators.redo()),
3634
+ onReset: () => dispatch(changeMarks([]))
3635
+ });
3636
+
3637
+ const GraphContainer = connect(mapStateToProps, mapDispatchToProps)(GraphWithControls$1);
3638
+ /**
3639
+ * The graph component entry point with undo/redo
3640
+ * Redux is an implementation detail, hide it in the react component.
3641
+ */
3642
+
3643
+ class Root extends React.Component {
3644
+ constructor(props) {
3645
+ super(props);
3646
+
3647
+ this.onStoreChange = () => {
3648
+ const {
3649
+ marks,
3650
+ onChangeMarks
3651
+ } = this.props;
3652
+ const storeState = this.store.getState();
3653
+ const lastAction = getLastAction();
3654
+ const isUndoOperation = lastAction.type.includes('UNDO') || lastAction.type.includes('REDO');
3655
+
3656
+ if (!isEqual(storeState.marks.present, marks)) {
3657
+ onChangeMarks(storeState.marks.present, isUndoOperation);
3658
+ }
3659
+ };
3660
+
3661
+ const r = reducer();
3662
+ this.store = createStore(r, {
3663
+ marks: props.marks
3664
+ }, applyMiddleware(lastActionMiddleware));
3665
+ this.store.subscribe(this.onStoreChange);
3666
+ }
3667
+
3668
+ componentDidUpdate(prevProps) {
3669
+ const {
3670
+ marks
3671
+ } = this.props;
3672
+ const storeState = this.store.getState();
3673
+
3674
+ if (isEqual(storeState.marks.present, marks)) {
3675
+ return;
3676
+ }
3677
+
3678
+ if (!isEqual(prevProps.marks, marks)) {
3679
+ this.store.dispatch(changeMarks(marks));
3680
+ }
3681
+ }
3682
+
3683
+ render() {
3684
+ // eslint-disable-next-line no-unused-vars
3685
+ const _this$props = this.props,
3686
+ {
3687
+ marks
3688
+ } = _this$props,
3689
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded);
3690
+
3691
+ const correctnessSet = marks && marks.find(m => m.correctness);
3692
+
3693
+ if (correctnessSet) {
3694
+ return /*#__PURE__*/React.createElement(GraphWithControls$1, _extends({}, rest, {
3695
+ marks: marks,
3696
+ disabled: correctnessSet
3697
+ }));
3698
+ }
3699
+
3700
+ return /*#__PURE__*/React.createElement(Provider, {
3701
+ store: this.store
3702
+ }, /*#__PURE__*/React.createElement(GraphContainer, rest));
3703
+ }
3704
+
3705
+ }
3706
+
3707
+ Root.propTypes = {
3708
+ onChangeMarks: PropTypes.func,
3709
+ marks: PropTypes.array
3710
+ };
3711
+
3712
+ const GridConfig = props => {
3713
+ const {
3714
+ classes,
3715
+ disabled,
3716
+ displayedFields,
3717
+ labelValue,
3718
+ labelValues,
3719
+ gridValue,
3720
+ gridValues,
3721
+ onChange
3722
+ } = props;
3723
+ const {
3724
+ labelStep = {},
3725
+ step = {}
3726
+ } = displayedFields;
3727
+ return /*#__PURE__*/React.createElement("div", {
3728
+ className: classes.columnView
3729
+ }, step && step.enabled && /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
3730
+ className: classes.mediumTextField,
3731
+ label: step.label || '',
3732
+ value: gridValue,
3733
+ customValues: gridValues,
3734
+ variant: "outlined",
3735
+ disabled: disabled,
3736
+ onChange: (e, v) => onChange('step', v)
3737
+ }), labelStep && labelStep.enabled && /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
3738
+ className: classes.mediumTextField,
3739
+ label: labelStep.label || '',
3740
+ value: labelValue,
3741
+ customValues: labelValues,
3742
+ variant: "outlined",
3743
+ disabled: disabled,
3744
+ onChange: (e, v) => onChange('labelStep', v)
3745
+ }));
3746
+ };
3747
+
3748
+ GridConfig.propTypes = {
3749
+ classes: PropTypes.object,
3750
+ disabled: PropTypes.bool,
3751
+ displayedFields: PropTypes.object,
3752
+ labelValue: PropTypes.number,
3753
+ labelValues: PropTypes.array,
3754
+ gridValue: PropTypes.number,
3755
+ gridValues: PropTypes.array,
3756
+ onChange: PropTypes.func
3757
+ };
3758
+
3759
+ const AxisConfig = props => {
3760
+ const {
3761
+ classes,
3762
+ disabled,
3763
+ displayedFields,
3764
+ displayHeader,
3765
+ label,
3766
+ maxValue,
3767
+ minValue,
3768
+ onChange,
3769
+ type
3770
+ } = props;
3771
+ const {
3772
+ axisLabel = {},
3773
+ min = {},
3774
+ max = {}
3775
+ } = displayedFields;
3776
+ const activePlugins = ['bold', 'italic', 'underline', 'strikethrough' // 'languageCharacters'
3777
+ ];
3778
+ return /*#__PURE__*/React.createElement("div", {
3779
+ className: classes.columnView
3780
+ }, displayHeader && /*#__PURE__*/React.createElement(Typography$1, {
3781
+ variant: "subtitle2"
3782
+ }, /*#__PURE__*/React.createElement("i", null, type === 'domain' ? 'x' : 'y'), "-axis"), min && min.enabled && /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
3783
+ className: classes.mediumTextField,
3784
+ label: min.label || '',
3785
+ value: minValue,
3786
+ min: -1e4,
3787
+ max: maxValue - 0.05,
3788
+ variant: "outlined",
3789
+ disabled: disabled,
3790
+ onChange: (e, v) => onChange('min', v)
3791
+ }), max && max.enabled && /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
3792
+ className: classes.mediumTextField,
3793
+ label: max.label || '',
3794
+ value: maxValue,
3795
+ min: minValue + 0.05,
3796
+ max: 10000,
3797
+ variant: "outlined",
3798
+ disabled: disabled,
3799
+ onChange: (e, v) => onChange('max', v)
3800
+ }), axisLabel && axisLabel.enabled && /*#__PURE__*/React.createElement(InputContainer, {
3801
+ label: axisLabel.label || '',
3802
+ className: classes.mediumTextField
3803
+ }, /*#__PURE__*/React.createElement(EditableHtml, {
3804
+ className: classes.axisLabel,
3805
+ onChange: value => onChange('axisLabel', value),
3806
+ markup: label || '',
3807
+ charactersLimit: 5,
3808
+ activePlugins: activePlugins
3809
+ })));
3810
+ };
3811
+
3812
+ AxisConfig.propTypes = {
3813
+ classes: PropTypes.object,
3814
+ disabled: PropTypes.bool,
3815
+ displayedFields: PropTypes.object,
3816
+ displayHeader: PropTypes.bool,
3817
+ label: PropTypes.string,
3818
+ maxValue: PropTypes.number,
3819
+ minValue: PropTypes.number,
3820
+ type: PropTypes.string,
3821
+ onChange: PropTypes.func
3822
+ };
3823
+
3824
+ const GridSetup = props => {
3825
+ const {
3826
+ classes,
3827
+ domain,
3828
+ displayedFields = {},
3829
+ gridValues = {},
3830
+ includeAxes,
3831
+ labelValues = {},
3832
+ onChange,
3833
+ onChangeView,
3834
+ range,
3835
+ size,
3836
+ sizeConstraints,
3837
+ standardGrid
3838
+ } = props;
3839
+ const gridProps = {
3840
+ min: 2,
3841
+ max: 41
3842
+ };
3843
+ const {
3844
+ axisLabel = {},
3845
+ dimensionsEnabled,
3846
+ includeAxesEnabled,
3847
+ labelStep = {},
3848
+ min = {},
3849
+ max = {},
3850
+ standardGridEnabled,
3851
+ step = {}
3852
+ } = displayedFields || {};
3853
+ const displayAxisType = min.enabled || max.enabled || axisLabel.enabled || step.enabled || labelStep.enabled;
3854
+ const gridConfigFields = {
3855
+ step,
3856
+ labelStep
3857
+ };
3858
+ const axisConfigFields = {
3859
+ min,
3860
+ max,
3861
+ axisLabel
3862
+ };
3863
+
3864
+ const onIncludeAxes = includeAxes => {
3865
+ const noAxesConfig = type => {
3866
+ const axis = type === 'domain' ? domain : range;
3867
+ return {
3868
+ min: 1,
3869
+ max: axis.max < gridProps.min || axis.max > gridProps.max ? 16 : axis.max,
3870
+ step: 1,
3871
+ labelStep: 0
3872
+ };
3873
+ };
3874
+
3875
+ const updatedRange = _extends({}, range, includeAxes ? {
3876
+ labelStep: 1
3877
+ } : noAxesConfig('range'));
3878
+
3879
+ const updatedDomain = _extends({}, domain, includeAxes ? {
3880
+ labelStep: 1
3881
+ } : noAxesConfig('domain'));
3882
+
3883
+ onChange({
3884
+ includeAxes,
3885
+ range: updatedRange,
3886
+ domain: updatedDomain
3887
+ });
3888
+ };
3889
+
3890
+ const onStandardGridChanged = value => {
3891
+ onChange({
3892
+ standardGrid: value,
3893
+ range: _extends({}, domain, {
3894
+ axisLabel: range.axisLabel
3895
+ }),
3896
+ graph: _extends({}, size, {
3897
+ height: size.width
3898
+ })
3899
+ });
3900
+ };
3901
+
3902
+ const onSizeChanged = (key, value) => {
3903
+ const graph = _extends({}, size, {
3904
+ [key]: value
3905
+ });
3906
+
3907
+ if (standardGrid) {
3908
+ graph.height = value;
3909
+ }
3910
+
3911
+ onChange({
3912
+ graph
3913
+ });
3914
+ };
3915
+
3916
+ const onDomainChanged = (key, value) => {
3917
+ domain[key] = value;
3918
+
3919
+ if (standardGrid && key !== 'axisLabel') {
3920
+ range[key] = value;
3921
+ }
3922
+
3923
+ onChange({
3924
+ domain,
3925
+ range
3926
+ });
3927
+ };
3928
+
3929
+ const onRangeChanged = (key, value) => {
3930
+ range[key] = value;
3931
+ onChange({
3932
+ range
3933
+ });
3934
+ };
3935
+
3936
+ const axesConfig = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
3937
+ className: classes.rowView
3938
+ }, /*#__PURE__*/React.createElement(AxisConfig, {
3939
+ classes: classes,
3940
+ displayedFields: axisConfigFields,
3941
+ displayHeader: displayAxisType,
3942
+ type: "domain",
3943
+ minValue: domain.min,
3944
+ maxValue: domain.max,
3945
+ label: domain.axisLabel,
3946
+ includeAxes: includeAxes,
3947
+ onChange: onDomainChanged
3948
+ }), /*#__PURE__*/React.createElement(AxisConfig, {
3949
+ classes: classes,
3950
+ displayedFields: axisConfigFields,
3951
+ displayHeader: displayAxisType,
3952
+ type: "range",
3953
+ minValue: range.min,
3954
+ maxValue: range.max,
3955
+ label: range.axisLabel,
3956
+ disabled: standardGrid,
3957
+ includeAxes: includeAxes,
3958
+ onChange: onRangeChanged
3959
+ })), (min.enabled || max.enabled) && /*#__PURE__*/React.createElement(Typography$1, {
3960
+ className: classes.text
3961
+ }, "If you want the axis to be visible, use a zero or negative Min Value, and a positive Max Value"), (step.enabled || labelStep.enabled) && /*#__PURE__*/React.createElement("div", {
3962
+ className: classes.rowView
3963
+ }, /*#__PURE__*/React.createElement(GridConfig, {
3964
+ classes: classes,
3965
+ displayedFields: gridConfigFields,
3966
+ gridValue: domain.step,
3967
+ labelValue: domain.labelStep,
3968
+ gridValues: gridValues.domain || [],
3969
+ labelValues: labelValues.domain || [],
3970
+ onChange: onDomainChanged
3971
+ }), /*#__PURE__*/React.createElement(GridConfig, {
3972
+ classes: classes,
3973
+ disabled: standardGrid,
3974
+ displayedFields: gridConfigFields,
3975
+ gridValue: range.step,
3976
+ labelValue: range.labelStep,
3977
+ gridValues: gridValues.range || [],
3978
+ labelValues: labelValues.range || [],
3979
+ onChange: onRangeChanged
3980
+ })), labelStep.enabled && /*#__PURE__*/React.createElement(Typography$1, {
3981
+ className: classes.text
3982
+ }, "For unnumbered gridlines, enter a label interval of 0"));
3983
+ const gridlinesConfig = max.enabled ? /*#__PURE__*/React.createElement("div", {
3984
+ className: classes.columnView
3985
+ }, /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
3986
+ className: classes.largeTextField,
3987
+ label: "Number of Vertical Gridlines",
3988
+ value: domain.max,
3989
+ min: !includeAxes && gridProps.min,
3990
+ max: !includeAxes && gridProps.max,
3991
+ variant: "outlined",
3992
+ onChange: (e, v) => onDomainChanged('max', v)
3993
+ }), /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
3994
+ className: classes.largeTextField,
3995
+ label: "Number of Horizontal Gridlines",
3996
+ value: range.max,
3997
+ min: !includeAxes && gridProps.min,
3998
+ max: !includeAxes && gridProps.max,
3999
+ variant: "outlined",
4000
+ disabled: standardGrid,
4001
+ onChange: (e, v) => onRangeChanged('max', v)
4002
+ })) : null;
4003
+ return /*#__PURE__*/React.createElement("div", {
4004
+ className: classes.wrapper
4005
+ }, /*#__PURE__*/React.createElement(ExpansionPanel$1, {
4006
+ onChange: onChangeView
4007
+ }, /*#__PURE__*/React.createElement(ExpansionPanelSummary$1, {
4008
+ expandIcon: /*#__PURE__*/React.createElement(ExpandMoreIcon, null)
4009
+ }, /*#__PURE__*/React.createElement(Typography$1, {
4010
+ variant: "subtitle1"
4011
+ }, "Customize Grid Setup")), /*#__PURE__*/React.createElement(ExpansionPanelDetails$1, null, /*#__PURE__*/React.createElement("div", {
4012
+ className: classes.content
4013
+ }, includeAxesEnabled && /*#__PURE__*/React.createElement(Toggle, {
4014
+ label: "Include axes and labels?",
4015
+ toggle: onIncludeAxes,
4016
+ checked: includeAxes
4017
+ }), standardGridEnabled && /*#__PURE__*/React.createElement(Toggle, {
4018
+ label: "Constrain to standard coordinate grid?",
4019
+ toggle: onStandardGridChanged,
4020
+ checked: standardGrid
4021
+ }), includeAxes ? axesConfig : gridlinesConfig, dimensionsEnabled && /*#__PURE__*/React.createElement("div", {
4022
+ className: classes.dimensions
4023
+ }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Typography$1, null, "Dimensions(px)"), /*#__PURE__*/React.createElement(Typography$1, {
4024
+ className: classes.disabled
4025
+ }, "Min ", sizeConstraints.min, ", Max ", sizeConstraints.max)), /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
4026
+ className: classes.textField,
4027
+ label: "Width",
4028
+ value: size.width,
4029
+ min: sizeConstraints.min,
4030
+ max: sizeConstraints.max,
4031
+ step: sizeConstraints.step,
4032
+ variant: "outlined",
4033
+ onChange: (e, v) => onSizeChanged('width', v)
4034
+ }), /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
4035
+ className: classes.textField,
4036
+ label: "Height",
4037
+ value: size.height,
4038
+ min: sizeConstraints.min,
4039
+ max: sizeConstraints.max,
4040
+ step: sizeConstraints.step,
4041
+ variant: "outlined",
4042
+ disabled: standardGrid,
4043
+ onChange: (e, v) => onSizeChanged('height', v)
4044
+ }))))));
4045
+ };
4046
+
4047
+ GridSetup.propTypes = {
4048
+ classes: PropTypes.object,
4049
+ domain: PropTypes.object,
4050
+ displayedFields: PropTypes.object,
4051
+ gridValues: PropTypes.object,
4052
+ includeAxes: PropTypes.bool,
4053
+ labelValues: PropTypes.object,
4054
+ onChange: PropTypes.func,
4055
+ onChangeView: PropTypes.func,
4056
+ range: PropTypes.object,
4057
+ size: PropTypes.object,
4058
+ sizeConstraints: PropTypes.object,
4059
+ standardGrid: PropTypes.bool
4060
+ };
4061
+
4062
+ const styles = theme => ({
4063
+ wrapper: {
4064
+ width: '450px'
4065
+ },
4066
+ content: {
4067
+ display: 'flex',
4068
+ flexDirection: 'column',
4069
+ width: '100%'
4070
+ },
4071
+ columnView: {
4072
+ display: 'flex',
4073
+ flexDirection: 'column',
4074
+ alignItems: 'center'
4075
+ },
4076
+ rowView: {
4077
+ display: 'flex',
4078
+ justifyContent: 'space-around',
4079
+ alignItems: 'center'
4080
+ },
4081
+ textField: {
4082
+ width: '130px',
4083
+ margin: `${theme.spacing.unit}px ${theme.spacing.unit / 2}px`
4084
+ },
4085
+ mediumTextField: {
4086
+ width: '160px',
4087
+ margin: `${theme.spacing.unit}px ${theme.spacing.unit / 2}px`
4088
+ },
4089
+ largeTextField: {
4090
+ width: '230px',
4091
+ margin: `${theme.spacing.unit}px ${theme.spacing.unit / 2}px`
4092
+ },
4093
+ text: {
4094
+ fontStyle: 'italic',
4095
+ margin: `${theme.spacing.unit}px 0`
4096
+ },
4097
+ dimensions: {
4098
+ display: 'flex',
4099
+ justifyContent: 'space-between',
4100
+ alignItems: 'center'
4101
+ },
4102
+ disabled: {
4103
+ color: color.disabled()
4104
+ },
4105
+ axisLabel: {
4106
+ paddingTop: theme.spacing.unit * 2
4107
+ }
4108
+ });
4109
+
4110
+ var gridSetup = withStyles(styles)(GridSetup);
4111
+
4112
+ export { Graph, Root as GraphContainer, gridSetup as GridSetup, ToolMenu$1 as ToolMenu, index as tools };
4113
+ //# sourceMappingURL=index.js.map