@pie-lib/charting 5.31.1-next.0 → 5.32.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,3714 @@
1
+ import React, { useState, useCallback, useRef, useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { withStyles } from '@material-ui/core/styles';
4
+ import classNames from 'classnames';
5
+ import debug from 'debug';
6
+ import cloneDeep from 'lodash/cloneDeep';
7
+ import { types, utils, gridDraggable, createGraphProps, Root } from '@pie-lib/plot';
8
+ import { AlertDialog, NumberTextFieldCustom } from '@pie-lib/config-ui';
9
+ import { GridRows, GridColumns } from '@vx/grid';
10
+ import { color } from '@pie-lib/render-ui';
11
+ import { AxisLeft, AxisBottom } from '@vx/axis';
12
+ import Checkbox from '@material-ui/core/Checkbox';
13
+ import Check from '@material-ui/icons/Check';
14
+ import Close from '@material-ui/icons/Close';
15
+ import { scaleBand, scalePoint } from '@vx/scale';
16
+ import AutosizeInput from 'react-input-autosize';
17
+ import { renderMath } from '@pie-lib/math-rendering';
18
+ import { Group } from '@vx/group';
19
+ import { Bar as Bar$4, LinePath, Circle } from '@vx/shape';
20
+ import { withStyles as withStyles$1 } from '@material-ui/core/styles/index';
21
+ import isEqual from 'lodash/isEqual';
22
+ import Button from '@material-ui/core/Button';
23
+ import Popover from '@material-ui/core/Popover';
24
+ import Paper from '@material-ui/core/Paper';
25
+ import Translator from '@pie-lib/translator';
26
+ import Typography from '@material-ui/core/Typography';
27
+ import MenuItem from '@material-ui/core/MenuItem';
28
+ import FormControl from '@material-ui/core/FormControl';
29
+ import InputLabel from '@material-ui/core/InputLabel';
30
+ import Select from '@material-ui/core/Select';
31
+ import OutlinedInput from '@material-ui/core/OutlinedInput';
32
+
33
+ function _extends() {
34
+ _extends = Object.assign || function (target) {
35
+ for (var i = 1; i < arguments.length; i++) {
36
+ var source = arguments[i];
37
+
38
+ for (var key in source) {
39
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
40
+ target[key] = source[key];
41
+ }
42
+ }
43
+ }
44
+
45
+ return target;
46
+ };
47
+
48
+ return _extends.apply(this, arguments);
49
+ }
50
+
51
+ function _objectWithoutPropertiesLoose(source, excluded) {
52
+ if (source == null) return {};
53
+ var target = {};
54
+ var sourceKeys = Object.keys(source);
55
+ var key, i;
56
+
57
+ for (i = 0; i < sourceKeys.length; i++) {
58
+ key = sourceKeys[i];
59
+ if (excluded.indexOf(key) >= 0) continue;
60
+ target[key] = source[key];
61
+ }
62
+
63
+ return target;
64
+ }
65
+
66
+ class Grid extends React.Component {
67
+ render() {
68
+ const {
69
+ classes,
70
+ className,
71
+ graphProps,
72
+ xBand,
73
+ rowTickValues,
74
+ columnTickValues
75
+ } = this.props;
76
+ const {
77
+ scale = {},
78
+ size = {},
79
+ range = {}
80
+ } = graphProps || {};
81
+ const {
82
+ step = 0,
83
+ labelStep = 0
84
+ } = range;
85
+ const highlightNonLabel = step && labelStep && step < labelStep; // if highlightNonLabel is true, we need to separate the unlabled lines in order to render them in a different color
86
+
87
+ const {
88
+ unlabeledLines,
89
+ labeledLines
90
+ } = (rowTickValues || []).reduce((acc, value) => {
91
+ if (highlightNonLabel && value % labelStep !== 0) {
92
+ acc.unlabeledLines.push(value);
93
+ } else {
94
+ acc.labeledLines.push(value);
95
+ }
96
+
97
+ return acc;
98
+ }, {
99
+ unlabeledLines: [],
100
+ labeledLines: []
101
+ });
102
+ return /*#__PURE__*/React.createElement("g", {
103
+ className: classNames(classes.grid, className)
104
+ }, /*#__PURE__*/React.createElement(GridRows, {
105
+ scale: scale.y,
106
+ width: size.width,
107
+ tickValues: unlabeledLines,
108
+ lineStyle: {
109
+ stroke: color.fadedPrimary(),
110
+ strokeWidth: 1
111
+ }
112
+ }), /*#__PURE__*/React.createElement(GridRows, {
113
+ scale: scale.y,
114
+ width: size.width,
115
+ tickValues: labeledLines,
116
+ lineStyle: {
117
+ stroke: color.visualElementsColors.GRIDLINES_COLOR,
118
+ strokeWidth: 1
119
+ }
120
+ }), /*#__PURE__*/React.createElement(GridColumns, {
121
+ scale: xBand,
122
+ height: size.height,
123
+ offset: xBand.bandwidth() / 2,
124
+ tickValues: columnTickValues
125
+ }));
126
+ }
127
+
128
+ }
129
+ Grid.propTypes = {
130
+ classes: PropTypes.object.isRequired,
131
+ className: PropTypes.string,
132
+ graphProps: types.GraphPropsType.isRequired,
133
+ xBand: PropTypes.func,
134
+ columnTickValues: PropTypes.array,
135
+ rowTickValues: PropTypes.array
136
+ };
137
+ Grid.defaultProps = {};
138
+
139
+ const styles$5 = () => ({
140
+ grid: {
141
+ stroke: color.primaryLight()
142
+ }
143
+ });
144
+
145
+ var ChartGrid = withStyles(styles$5)(Grid);
146
+
147
+ const CorrectnessIndicator = ({
148
+ scale,
149
+ x,
150
+ y,
151
+ classes,
152
+ r,
153
+ correctness,
154
+ interactive
155
+ }) => {
156
+ if (!correctness || !interactive) return null;
157
+ const cx = scale ? scale.x(x) : x;
158
+ const cy = scale ? scale.y(y) : y;
159
+ const isCorrect = correctness.value === 'correct';
160
+ const iconClass = isCorrect ? classes.correctIcon : classes.incorrectIcon; // the icon is 16px + 2px padding + 1px border, so total size is 22px
161
+
162
+ return /*#__PURE__*/React.createElement("foreignObject", {
163
+ x: cx - 11,
164
+ y: cy - 11,
165
+ width: 22,
166
+ height: 22
167
+ }, isCorrect ? /*#__PURE__*/React.createElement(Check, {
168
+ className: classNames(classes.correctnessIcon, iconClass),
169
+ title: correctness.label
170
+ }) : /*#__PURE__*/React.createElement(Close, {
171
+ className: classNames(classes.correctnessIcon, iconClass),
172
+ title: correctness.label
173
+ }));
174
+ };
175
+ const SmallCorrectPointIndicator = ({
176
+ scale,
177
+ x,
178
+ r,
179
+ correctness,
180
+ classes,
181
+ correctData,
182
+ label
183
+ }) => {
184
+ if (correctness && correctness.value === 'incorrect') {
185
+ var _correctData$find;
186
+
187
+ const correctVal = parseFloat((_correctData$find = correctData.find(d => d.label === label)) == null ? void 0 : _correctData$find.value);
188
+ if (isNaN(correctVal)) return null;
189
+ const correctPxY = scale.y(correctVal);
190
+ const yToRender = correctPxY - 7.5;
191
+ const xToRender = scale.x(x) - 7.5; // small circle has 10px font + 2px padding + 1px border, so total size is 15px
192
+
193
+ return /*#__PURE__*/React.createElement("foreignObject", {
194
+ x: xToRender,
195
+ y: yToRender,
196
+ width: 15,
197
+ height: 15
198
+ }, /*#__PURE__*/React.createElement(Check, {
199
+ className: classNames(classes.correctnessIcon, classes.correctIcon, classes.smallIcon),
200
+ title: correctness.label
201
+ }));
202
+ }
203
+
204
+ return null;
205
+ };
206
+ const TickCorrectnessIndicator = ({
207
+ classes,
208
+ correctness,
209
+ interactive
210
+ }) => {
211
+ if (!correctness || !interactive) return null;
212
+ return correctness.value === 'correct' ? /*#__PURE__*/React.createElement(Check, {
213
+ className: classNames(classes.correctnessIcon, classes.correctIcon),
214
+ title: correctness.label
215
+ }) : /*#__PURE__*/React.createElement(Close, {
216
+ className: classNames(classes.correctnessIcon, classes.incorrectIcon),
217
+ title: correctness.label
218
+ });
219
+ };
220
+
221
+ utils.tickCount;
222
+ utils.bounds;
223
+ utils.point;
224
+ const bandKey = (d, index) => `${index}-${d.label || '-'}`;
225
+ const dataToXBand = (scaleX, data, width, type) => {
226
+ switch (type) {
227
+ case 'bar':
228
+ case 'dotPlot':
229
+ case 'linePlot':
230
+ return scaleBand({
231
+ rangeRound: [0, width],
232
+ domain: data && data.map(bandKey),
233
+ padding: 0.2
234
+ });
235
+
236
+ case 'histogram':
237
+ return scaleBand({
238
+ rangeRound: [0, width],
239
+ domain: data && data.map(bandKey),
240
+ padding: 0
241
+ });
242
+
243
+ case 'lineCross':
244
+ case 'lineDot':
245
+ return scalePoint({
246
+ domain: data && data.map(bandKey),
247
+ rangeRound: [0, width]
248
+ });
249
+
250
+ default:
251
+ return scaleBand({
252
+ range: [0, width],
253
+ domain: data && data.map(bandKey),
254
+ padding: 0
255
+ });
256
+ }
257
+ };
258
+ const getTickValues = (prop = {}) => {
259
+ const tickValues = [];
260
+ let tickVal = prop.min;
261
+
262
+ while (tickVal <= prop.max) {
263
+ tickValues.push(tickVal);
264
+ tickVal = Math.round((tickVal + prop.step) * 10000) / 10000;
265
+ }
266
+
267
+ return tickValues;
268
+ };
269
+ const getDomainAndRangeByChartType = (domain, range, chartType) => {
270
+ let {
271
+ step,
272
+ labelStep,
273
+ min,
274
+ max
275
+ } = range || {};
276
+
277
+ if (!min) {
278
+ min = 0;
279
+ }
280
+
281
+ if (!max || max < 0) {
282
+ max = range.min + 1;
283
+ }
284
+
285
+ if (!step) {
286
+ step = labelStep || 1;
287
+ }
288
+
289
+ if (!labelStep || isNaN(labelStep) && step) {
290
+ labelStep = step || 1;
291
+ }
292
+
293
+ range.max = max;
294
+
295
+ switch (chartType) {
296
+ // if chart is dot plot or line plot, we should ignore step and make sure that min & max are integer values
297
+ case 'dotPlot':
298
+ case 'linePlot':
299
+ {
300
+ const intMin = Math.round(min);
301
+ const intMax = Math.round(max);
302
+ return {
303
+ domain: _extends({}, domain, {
304
+ step: 1,
305
+ labelStep: 1,
306
+ min: 0,
307
+ max: 1
308
+ }),
309
+ range: _extends({}, range, {
310
+ min: intMin,
311
+ max: intMin === intMax ? intMin + 1 : intMax,
312
+ labelStep,
313
+ step: 1
314
+ })
315
+ };
316
+ }
317
+
318
+ default:
319
+ return {
320
+ domain: _extends({}, domain, {
321
+ step: 1,
322
+ labelStep: 1,
323
+ min: 0,
324
+ max: 1
325
+ }),
326
+ range: _extends({}, range, {
327
+ labelStep,
328
+ step
329
+ })
330
+ };
331
+ }
332
+ };
333
+ const getGridLinesAndAxisByChartType = (range, chartType) => {
334
+ switch (chartType) {
335
+ case 'lineDot':
336
+ case 'lineCross':
337
+ return {
338
+ verticalLines: undefined,
339
+ horizontalLines: getTickValues(range),
340
+ leftAxis: true
341
+ };
342
+
343
+ case 'dotPlot':
344
+ case 'linePlot':
345
+ return {
346
+ verticalLines: [],
347
+ horizontalLines: [],
348
+ leftAxis: false
349
+ };
350
+
351
+ default:
352
+ return {
353
+ verticalLines: [],
354
+ horizontalLines: getTickValues(range),
355
+ leftAxis: true
356
+ };
357
+ }
358
+ };
359
+ const getRotateAngle = (fontSize, height) => {
360
+ if (height >= fontSize * 2) {
361
+ return 25;
362
+ }
363
+
364
+ return 0;
365
+ };
366
+ const getTopPadding = barWidth => {
367
+ if (barWidth < 30) {
368
+ return 50;
369
+ }
370
+
371
+ if (barWidth < 40) {
372
+ return 30;
373
+ }
374
+
375
+ if (barWidth < 60) {
376
+ return 15;
377
+ }
378
+
379
+ return 0;
380
+ }; // This function calculates the transformation scale for SVG and the icon's vertical distance from its category
381
+
382
+ const getScale = width => {
383
+ let scale, deltay;
384
+
385
+ if (width > 91) {
386
+ scale = 1.3;
387
+ deltay = -55;
388
+ } else if (width > 45) {
389
+ scale = 1.1;
390
+ deltay = -45;
391
+ } else if (width > 40) {
392
+ scale = 0.5 + (width - 34) * 0.02;
393
+ deltay = -25;
394
+ } else if (width > 30) {
395
+ scale = 0.5 + (width - 34) * 0.02;
396
+ deltay = -20;
397
+ } else {
398
+ scale = 0.5 * Math.pow(0.98, 34 - width); // 0.98 is the reduction factor. Adjust to control scaling.
399
+
400
+ deltay = -15;
401
+ }
402
+
403
+ return {
404
+ scale,
405
+ deltay
406
+ };
407
+ };
408
+ const getAdjustedX = (width, scaleValue) => {
409
+ const innerWidthOriginal = 57;
410
+ const effectiveInnerWidth = innerWidthOriginal * scaleValue;
411
+ return (width - effectiveInnerWidth) / 2;
412
+ };
413
+
414
+ const disabled = (key = 'fill') => ({
415
+ [key]: `var(--graph-disabled, ${color.disabled()})`,
416
+ pointerEvents: 'none',
417
+ border: 'none'
418
+ });
419
+ const correct = (key = 'fill') => ({
420
+ [key]: color.correct(),
421
+ pointerEvents: 'none',
422
+ border: `solid 1px ${color.correct()}`
423
+ });
424
+ const incorrect = (key = 'fill') => ({
425
+ [key]: color.incorrect(),
426
+ pointerEvents: 'none',
427
+ border: `solid 1px ${color.incorrect()}`
428
+ });
429
+
430
+ const styles$4 = theme => ({
431
+ input: {
432
+ float: 'right',
433
+ fontFamily: theme.typography.fontFamily,
434
+ fontSize: theme.typography.fontSize,
435
+ border: 'none',
436
+ '&.correct': correct('color'),
437
+ '&.incorrect': incorrect('color'),
438
+ '&.disabled': {
439
+ backgroundColor: 'transparent !important'
440
+ },
441
+ '&.error': {
442
+ border: `2px solid ${theme.palette.error.main}`
443
+ }
444
+ },
445
+ mathInput: {
446
+ pointerEvents: 'auto',
447
+ textAlign: 'center',
448
+ fontSize: theme.typography.fontSize + 2,
449
+ fontFamily: theme.typography.fontFamily,
450
+ color: color.primaryDark(),
451
+ paddingTop: theme.typography.fontSize / 2
452
+ },
453
+ disabled: _extends({}, disabled('color'), {
454
+ backgroundColor: 'transparent !important'
455
+ }),
456
+ error: {
457
+ border: `2px solid ${theme.palette.error.main}`
458
+ },
459
+ correct: _extends({}, correct('color')),
460
+ incorrect: _extends({}, incorrect('color')),
461
+ flexContainer: {
462
+ display: 'flex',
463
+ flexDirection: 'column',
464
+ alignItems: 'center'
465
+ }
466
+ });
467
+
468
+ function isFractionFormat(label) {
469
+ const trimmedLabel = (label == null ? void 0 : label.trim()) || '';
470
+ const fracRegex = new RegExp(/^[1-9]*[0-9]*\s?[1-9][0-9]*\/[1-9][0-9]*$/);
471
+ return fracRegex.test(trimmedLabel);
472
+ }
473
+
474
+ function getLabelMathFormat(label) {
475
+ const trimmedLabel = (label == null ? void 0 : label.trim()) || '';
476
+ let fraction;
477
+ let mixedNr = '';
478
+ let improperFraction = trimmedLabel.split(' ');
479
+
480
+ if (improperFraction[1] && improperFraction[1].includes('/')) {
481
+ fraction = improperFraction[1].split('/') || '';
482
+ } else {
483
+ fraction = (trimmedLabel == null ? void 0 : trimmedLabel.split('/')) || '';
484
+ }
485
+
486
+ let formattedLLabel;
487
+
488
+ if (isFractionFormat(label)) {
489
+ if (improperFraction[0] && improperFraction[1]) {
490
+ mixedNr = improperFraction[0];
491
+ }
492
+
493
+ formattedLLabel = `\\(${mixedNr}\\frac{${fraction[0]}}{${fraction[1]}}\\)`;
494
+ return formattedLLabel;
495
+ }
496
+
497
+ return undefined;
498
+ }
499
+
500
+ const MarkLabel = props => {
501
+ // eslint-disable-next-line no-unused-vars
502
+ const [input, setInput] = useState(null);
503
+
504
+ const _ref = useCallback(node => setInput(node), null);
505
+
506
+ const {
507
+ mark,
508
+ classes,
509
+ disabled,
510
+ inputRef: externalInputRef,
511
+ barWidth,
512
+ rotate,
513
+ correctness,
514
+ autoFocus,
515
+ error,
516
+ isHiddenLabel,
517
+ limitCharacters,
518
+ correctnessIndicator
519
+ } = props;
520
+ const [label, setLabel] = useState(mark.label);
521
+ const [mathLabel, setMathLabel] = useState(getLabelMathFormat(mark.label));
522
+ const [isEditing, setIsEditing] = useState(false);
523
+ let root = useRef(null);
524
+
525
+ const onChange = e => {
526
+ if (limitCharacters && e.target.value && e.target.value.length > 20) {
527
+ return;
528
+ }
529
+
530
+ setLabel(e.target.value);
531
+ };
532
+
533
+ const isMathRendering = () => {
534
+ return isEditing === false && mathLabel !== undefined;
535
+ };
536
+
537
+ const onChangeProp = e => {
538
+ setMathLabel(getLabelMathFormat(mark.label));
539
+ setIsEditing(false);
540
+ props.onChange(e.target.value);
541
+ };
542
+
543
+ let extraStyle = {};
544
+
545
+ if (rotate) {
546
+ extraStyle = {
547
+ width: 'unset',
548
+ textAlign: 'left'
549
+ };
550
+ } // useState only sets the value once, to synch props to state need useEffect
551
+
552
+
553
+ useEffect(() => {
554
+ setLabel(mark.label);
555
+ }, [mark.label]);
556
+ useEffect(() => {
557
+ renderMath(root);
558
+ }, []);
559
+ return /*#__PURE__*/React.createElement("div", {
560
+ className: classes.flexContainer
561
+ }, correctnessIndicator, isMathRendering() ? /*#__PURE__*/React.createElement("div", {
562
+ ref: r => {
563
+ root = r;
564
+ externalInputRef(r);
565
+ },
566
+ dangerouslySetInnerHTML: {
567
+ __html: getLabelMathFormat(label)
568
+ },
569
+ className: classNames(classes.mathInput, {
570
+ [classes.disabled]: disabled,
571
+ [classes.error]: error,
572
+ [classes.correct]: mark.editable && (correctness == null ? void 0 : correctness.label) === 'correct',
573
+ [classes.incorrect]: mark.editable && (correctness == null ? void 0 : correctness.label) === 'incorrect'
574
+ }),
575
+ onClick: () => setIsEditing(true),
576
+ style: {
577
+ minWidth: barWidth,
578
+ position: 'fixed',
579
+ transformOrigin: 'left',
580
+ transform: `rotate(${rotate}deg)`,
581
+ visibility: isHiddenLabel ? 'hidden' : 'unset',
582
+ marginTop: correctnessIndicator ? '24px' : '0'
583
+ }
584
+ }) : /*#__PURE__*/React.createElement(AutosizeInput, {
585
+ inputRef: r => {
586
+ _ref(r);
587
+
588
+ externalInputRef(r);
589
+ },
590
+ autoFocus: isEditing || autoFocus,
591
+ disabled: disabled,
592
+ inputClassName: classNames(classes.input, correctness && mark.editable ? correctness.label : null, disabled && 'disabled', error && 'error'),
593
+ inputStyle: _extends({
594
+ minWidth: barWidth,
595
+ textAlign: 'center',
596
+ background: 'transparent',
597
+ boxSizing: 'border-box',
598
+ paddingLeft: 0,
599
+ paddingRight: 0
600
+ }, extraStyle),
601
+ value: label,
602
+ style: {
603
+ position: 'fixed',
604
+ pointerEvents: 'auto',
605
+ top: 0,
606
+ left: 0,
607
+ minWidth: barWidth,
608
+ transformOrigin: 'left',
609
+ transform: `rotate(${rotate}deg)`,
610
+ visibility: isHiddenLabel ? 'hidden' : 'unset',
611
+ marginTop: correctnessIndicator ? '24px' : '0'
612
+ },
613
+ onChange: onChange,
614
+ onBlur: onChangeProp
615
+ }));
616
+ };
617
+ MarkLabel.propTypes = {
618
+ autoFocus: PropTypes.bool,
619
+ disabled: PropTypes.bool,
620
+ error: PropTypes.any,
621
+ onChange: PropTypes.func,
622
+ graphProps: types.GraphPropsType,
623
+ classes: PropTypes.object,
624
+ inputRef: PropTypes.func,
625
+ mark: PropTypes.object,
626
+ barWidth: PropTypes.number,
627
+ rotate: PropTypes.number,
628
+ correctness: PropTypes.shape({
629
+ value: PropTypes.string,
630
+ label: PropTypes.string
631
+ }),
632
+ isHiddenLabel: PropTypes.bool,
633
+ limitCharacters: PropTypes.bool,
634
+ correctnessIndicator: PropTypes.node
635
+ };
636
+ var MarkLabel$1 = withStyles(styles$4)(MarkLabel);
637
+
638
+ class TickComponent extends React.Component {
639
+ constructor(props) {
640
+ super(props);
641
+
642
+ this.handleAlertDialog = (open, callback) => this.setState({
643
+ dialog: {
644
+ open
645
+ }
646
+ }, callback);
647
+
648
+ this.changeCategory = (index, newLabel) => {
649
+ const {
650
+ categories,
651
+ onChangeCategory
652
+ } = this.props;
653
+ const category = categories[index];
654
+ onChangeCategory(index, _extends({}, category, {
655
+ label: newLabel
656
+ }));
657
+ };
658
+
659
+ this.changeInteractive = (index, value) => {
660
+ const {
661
+ categories,
662
+ onChangeCategory
663
+ } = this.props;
664
+ const category = categories[index];
665
+
666
+ if (!value) {
667
+ this.setState({
668
+ dialog: {
669
+ open: true,
670
+ title: 'Warning',
671
+ text: 'This will remove the correct answer value that has been defined for this category.',
672
+ onConfirm: () => this.handleAlertDialog(false, onChangeCategory(index, _extends({}, category, {
673
+ interactive: !category.interactive
674
+ }))),
675
+ onClose: () => this.handleAlertDialog(false)
676
+ }
677
+ });
678
+ } else {
679
+ onChangeCategory(index, _extends({}, category, {
680
+ interactive: !category.interactive
681
+ }));
682
+ }
683
+ };
684
+
685
+ this.changeEditable = (index, value) => {
686
+ const {
687
+ categories,
688
+ onChangeCategory
689
+ } = this.props;
690
+ const category = categories[index];
691
+
692
+ if (!value) {
693
+ this.setState({
694
+ dialog: {
695
+ open: true,
696
+ title: 'Warning',
697
+ text: 'This will remove the correct answer category name that has been defined for this category.',
698
+ onConfirm: () => this.handleAlertDialog(false, onChangeCategory(index, _extends({}, category, {
699
+ editable: !category.editable || false
700
+ }))),
701
+ onClose: () => this.handleAlertDialog(false)
702
+ }
703
+ });
704
+ } else {
705
+ onChangeCategory(index, _extends({}, category, {
706
+ editable: !category.editable || false
707
+ }));
708
+ }
709
+ };
710
+
711
+ this.splitText = (text, maxChar) => {
712
+ let chunks = [];
713
+
714
+ while ((text || '').length > 0) {
715
+ let indexToSplit;
716
+
717
+ if (text.length > maxChar) {
718
+ indexToSplit = text.lastIndexOf(' ', maxChar);
719
+
720
+ if (indexToSplit === -1) {
721
+ indexToSplit = maxChar;
722
+ }
723
+ } else {
724
+ indexToSplit = text.length;
725
+ }
726
+
727
+ chunks.push(text.substring(0, indexToSplit));
728
+ text = text.substring(indexToSplit).trim();
729
+ }
730
+
731
+ return chunks;
732
+ };
733
+
734
+ this.state = {
735
+ dialog: {
736
+ open: false
737
+ }
738
+ };
739
+ }
740
+
741
+ componentDidUpdate(prevProps) {
742
+ if (this.props.autoFocus && !prevProps.autoFocus) {
743
+ this.props.onAutoFocusUsed();
744
+ }
745
+ }
746
+
747
+ render() {
748
+ const {
749
+ classes,
750
+ categories,
751
+ xBand,
752
+ bandWidth,
753
+ barWidth,
754
+ rotate,
755
+ top,
756
+ graphProps,
757
+ defineChart,
758
+ chartingOptions,
759
+ x,
760
+ y,
761
+ formattedValue,
762
+ changeInteractiveEnabled,
763
+ changeEditableEnabled,
764
+ error,
765
+ autoFocus,
766
+ hiddenLabelRef,
767
+ showCorrectness
768
+ } = this.props;
769
+
770
+ if (!formattedValue) {
771
+ return null;
772
+ }
773
+
774
+ const {
775
+ dialog
776
+ } = this.state;
777
+ const {
778
+ changeEditable,
779
+ changeInteractive
780
+ } = chartingOptions || {};
781
+ const index = parseInt(formattedValue.split('-')[0], 10);
782
+ const category = categories[index];
783
+ const {
784
+ editable,
785
+ interactive,
786
+ label,
787
+ correctness
788
+ } = category || {};
789
+ const barX = xBand(bandKey({
790
+ label
791
+ }, index));
792
+ const longestCategory = (categories || []).reduce((a, b) => {
793
+ const lengthA = a && a.label ? a.label.length : 0;
794
+ const lengthB = b && b.label ? b.label.length : 0;
795
+ return lengthA > lengthB ? a : b;
796
+ });
797
+ const distinctMessages = error ? [...new Set(Object.values(error))].join(' ') : '';
798
+ return /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("foreignObject", {
799
+ x: bandWidth ? barX : x - barWidth / 2,
800
+ y: 18,
801
+ width: barWidth,
802
+ height: 4,
803
+ style: {
804
+ pointerEvents: 'none',
805
+ overflow: 'visible'
806
+ }
807
+ }, index === 0 && /*#__PURE__*/React.createElement(MarkLabel$1, {
808
+ isHiddenLabel: true,
809
+ inputRef: hiddenLabelRef,
810
+ disabled: true,
811
+ mark: longestCategory,
812
+ graphProps: graphProps,
813
+ barWidth: barWidth
814
+ }), /*#__PURE__*/React.createElement(MarkLabel$1, {
815
+ autoFocus: defineChart && autoFocus,
816
+ inputRef: r => this.input = r,
817
+ disabled: !defineChart && !editable,
818
+ mark: category,
819
+ graphProps: graphProps,
820
+ onChange: newLabel => this.changeCategory(index, newLabel),
821
+ barWidth: barWidth,
822
+ rotate: rotate,
823
+ correctness: correctness,
824
+ error: error && error[index],
825
+ limitCharacters: true,
826
+ correctnessIndicator: showCorrectness && correctness && /*#__PURE__*/React.createElement(TickCorrectnessIndicator, {
827
+ correctness: correctness,
828
+ interactive: interactive,
829
+ classes: classes
830
+ })
831
+ })), error && index === 0 && /*#__PURE__*/React.createElement("text", {
832
+ className: classes.error,
833
+ y: y + 23,
834
+ height: 6,
835
+ textAnchor: "start"
836
+ }, distinctMessages), defineChart && index === 0 && /*#__PURE__*/React.createElement("svg", {
837
+ x: -55,
838
+ style: {
839
+ overflow: 'visible'
840
+ }
841
+ }, changeInteractiveEnabled && /*#__PURE__*/React.createElement("text", {
842
+ y: y + 90 + top,
843
+ width: barWidth,
844
+ height: 4,
845
+ style: {
846
+ position: 'absolute',
847
+ pointerEvents: 'none',
848
+ wordBreak: 'break-word',
849
+ maxWidth: barWidth,
850
+ display: 'inline-block'
851
+ }
852
+ }, this.splitText(changeInteractive == null ? void 0 : changeInteractive.authoringLabel, 20).map((word, index) => /*#__PURE__*/React.createElement("tspan", {
853
+ key: index,
854
+ x: "0",
855
+ dy: `${index > 0 ? '1.2em' : '.6em'}`
856
+ }, word))), changeEditableEnabled && /*#__PURE__*/React.createElement("text", {
857
+ y: y + 145 + top,
858
+ width: barWidth,
859
+ height: 4,
860
+ style: {
861
+ position: 'absolute',
862
+ pointerEvents: 'none',
863
+ wordBreak: 'break-word',
864
+ maxWidth: barWidth,
865
+ display: 'inline-block'
866
+ }
867
+ }, this.splitText(changeEditable == null ? void 0 : changeEditable.authoringLabel, 20).map((word, index) => /*#__PURE__*/React.createElement("tspan", {
868
+ key: index,
869
+ x: "0",
870
+ dy: `${index > 0 ? '1.2em' : '.6em'}`
871
+ }, word)))), defineChart && changeInteractiveEnabled && /*#__PURE__*/React.createElement("foreignObject", {
872
+ x: x - 24,
873
+ y: y + 80 + top,
874
+ width: barWidth,
875
+ height: 4,
876
+ style: {
877
+ pointerEvents: 'visible',
878
+ overflow: 'visible'
879
+ }
880
+ }, /*#__PURE__*/React.createElement(Checkbox, {
881
+ className: classes.customColor,
882
+ style: {
883
+ position: 'fixed'
884
+ },
885
+ checked: interactive,
886
+ onChange: e => this.changeInteractive(index, e.target.checked)
887
+ })), defineChart && changeEditableEnabled && /*#__PURE__*/React.createElement("foreignObject", {
888
+ x: x - 24,
889
+ y: y + 130 + top,
890
+ width: barWidth,
891
+ height: 4,
892
+ style: {
893
+ pointerEvents: 'visible',
894
+ overflow: 'visible'
895
+ }
896
+ }, /*#__PURE__*/React.createElement(Checkbox, {
897
+ className: classes.customColor,
898
+ style: {
899
+ position: 'fixed'
900
+ },
901
+ checked: editable,
902
+ onChange: e => this.changeEditable(index, e.target.checked)
903
+ })), /*#__PURE__*/React.createElement("foreignObject", {
904
+ x: x - 24,
905
+ y: y + 100 + top,
906
+ width: barWidth,
907
+ height: 4,
908
+ style: {
909
+ pointerEvents: 'visible',
910
+ overflow: 'visible'
911
+ }
912
+ }, /*#__PURE__*/React.createElement(AlertDialog, {
913
+ open: dialog.open,
914
+ title: dialog.title,
915
+ text: dialog.text,
916
+ onClose: dialog.onClose,
917
+ onConfirm: dialog.onConfirm
918
+ })));
919
+ }
920
+
921
+ }
922
+ TickComponent.propTypes = {
923
+ defineChart: PropTypes.bool,
924
+ error: PropTypes.any
925
+ };
926
+ TickComponent.propTypes = {
927
+ categories: PropTypes.array,
928
+ xBand: PropTypes.func,
929
+ bandWidth: PropTypes.number,
930
+ barWidth: PropTypes.number,
931
+ rotate: PropTypes.number,
932
+ top: PropTypes.number,
933
+ x: PropTypes.number,
934
+ y: PropTypes.number,
935
+ graphProps: PropTypes.object,
936
+ formattedValue: PropTypes.string,
937
+ onChangeCategory: PropTypes.func,
938
+ onChange: PropTypes.func,
939
+ classes: PropTypes.object,
940
+ error: PropTypes.object,
941
+ defineChart: PropTypes.bool,
942
+ chartingOptions: PropTypes.object,
943
+ changeInteractiveEnabled: PropTypes.bool,
944
+ changeEditableEnabled: PropTypes.bool,
945
+ autoFocus: PropTypes.bool,
946
+ onAutoFocusUsed: PropTypes.func,
947
+ showCorrectness: PropTypes.bool
948
+ };
949
+ class RawChartAxes extends React.Component {
950
+ constructor(...args) {
951
+ super(...args);
952
+ this.state = {
953
+ height: 0,
954
+ width: 0
955
+ };
956
+ }
957
+
958
+ componentDidMount() {
959
+ if (this.hiddenLabelRef) {
960
+ const boundingClientRect = this.hiddenLabelRef.getBoundingClientRect();
961
+ this.setState({
962
+ height: Math.floor(boundingClientRect.height),
963
+ width: Math.floor(boundingClientRect.width)
964
+ });
965
+ }
966
+ }
967
+
968
+ componentDidUpdate(prevProps, prevState, snapshot) {
969
+ if (this.hiddenLabelRef) {
970
+ const width = Math.floor(this.hiddenLabelRef.getBoundingClientRect().width);
971
+
972
+ if (width !== this.state.width) {
973
+ this.setState({
974
+ width
975
+ });
976
+ }
977
+ }
978
+ }
979
+
980
+ render() {
981
+ const {
982
+ classes,
983
+ graphProps,
984
+ xBand,
985
+ leftAxis,
986
+ onChange,
987
+ onChangeCategory,
988
+ categories = [],
989
+ top,
990
+ defineChart,
991
+ chartingOptions,
992
+ changeInteractiveEnabled,
993
+ changeEditableEnabled,
994
+ theme,
995
+ autoFocus,
996
+ onAutoFocusUsed,
997
+ error,
998
+ showCorrectness
999
+ } = this.props;
1000
+ const {
1001
+ axis,
1002
+ axisLine,
1003
+ tick
1004
+ } = classes;
1005
+ const {
1006
+ scale = {},
1007
+ range = {},
1008
+ domain = {},
1009
+ size = {}
1010
+ } = graphProps || {};
1011
+ const {
1012
+ height,
1013
+ width
1014
+ } = this.state;
1015
+ const bottomScale = xBand && typeof xBand.rangeRound === 'function' && xBand.rangeRound([0, size.width]);
1016
+ const bandWidth = xBand && typeof xBand.bandwidth === 'function' && xBand.bandwidth(); // for chartType "line", bandWidth will be 0, so we have to calculate it
1017
+
1018
+ const barWidth = bandWidth || scale.x && scale.x(domain.max) / categories.length;
1019
+ const rowTickValues = getTickValues(_extends({}, range, {
1020
+ step: range.labelStep
1021
+ }));
1022
+ const fontSize = theme && theme.typography ? theme.typography.fontSize : 14; // this mostly applies for labels that are not editable
1023
+
1024
+ const rotateBecauseOfHeight = getRotateAngle(fontSize, height); // this applies for labels that are editable
1025
+
1026
+ const rotateBecauseOfWidth = width > barWidth ? 25 : 0;
1027
+
1028
+ const getTickLabelProps = value => ({
1029
+ dy: 4,
1030
+ dx: -10 - (value.toLocaleString().length || 1) * 5
1031
+ });
1032
+
1033
+ const getTickComponent = props => {
1034
+ const properties = {
1035
+ hiddenLabelRef: ref => {
1036
+ this.hiddenLabelRef = ref;
1037
+ },
1038
+ classes,
1039
+ categories,
1040
+ xBand,
1041
+ bandWidth,
1042
+ barWidth,
1043
+ rotate: rotateBecauseOfHeight || rotateBecauseOfWidth,
1044
+ top,
1045
+ defineChart,
1046
+ chartingOptions,
1047
+ autoFocus,
1048
+ onAutoFocusUsed,
1049
+ error,
1050
+ onChangeCategory,
1051
+ changeInteractiveEnabled,
1052
+ changeEditableEnabled,
1053
+ onChange,
1054
+ graphProps,
1055
+ x: props.x,
1056
+ y: props.y,
1057
+ formattedValue: props.formattedValue,
1058
+ showCorrectness
1059
+ };
1060
+ return /*#__PURE__*/React.createElement(TickComponent, properties);
1061
+ };
1062
+
1063
+ return /*#__PURE__*/React.createElement(React.Fragment, null, leftAxis && /*#__PURE__*/React.createElement(AxisLeft, {
1064
+ scale: scale.y,
1065
+ className: axis,
1066
+ axisLineClassName: axisLine,
1067
+ tickLength: 10,
1068
+ tickClassName: tick,
1069
+ tickFormat: value => value,
1070
+ tickValues: rowTickValues,
1071
+ tickLabelProps: getTickLabelProps
1072
+ }), /*#__PURE__*/React.createElement(AxisBottom, {
1073
+ axisLineClassName: axisLine,
1074
+ tickClassName: tick,
1075
+ scale: bottomScale,
1076
+ labelProps: {
1077
+ y: 60 + top
1078
+ },
1079
+ top: scale.y && scale.y(range.min),
1080
+ textLabelProps: () => ({
1081
+ textAnchor: 'middle'
1082
+ }),
1083
+ tickFormat: count => count,
1084
+ tickComponent: getTickComponent,
1085
+ autoFocus: autoFocus,
1086
+ onAutoFocusUsed: onAutoFocusUsed
1087
+ }));
1088
+ }
1089
+
1090
+ }
1091
+ RawChartAxes.propTypes = {
1092
+ bottomScale: PropTypes.func,
1093
+ classes: PropTypes.object.isRequired,
1094
+ categories: PropTypes.array,
1095
+ defineChart: PropTypes.bool,
1096
+ error: PropTypes.any,
1097
+ graphProps: types.GraphPropsType.isRequired,
1098
+ xBand: PropTypes.func,
1099
+ leftAxis: PropTypes.bool,
1100
+ onChange: PropTypes.func,
1101
+ onChangeCategory: PropTypes.func,
1102
+ top: PropTypes.number,
1103
+ theme: PropTypes.object,
1104
+ chartingOptions: PropTypes.object,
1105
+ changeInteractiveEnabled: PropTypes.bool,
1106
+ changeEditableEnabled: PropTypes.bool,
1107
+ autoFocus: PropTypes.bool,
1108
+ onAutoFocusUsed: PropTypes.func,
1109
+ showCorrectness: PropTypes.bool
1110
+ };
1111
+ const ChartAxes = withStyles(theme => ({
1112
+ axis: {
1113
+ stroke: color.primaryDark(),
1114
+ strokeWidth: 2
1115
+ },
1116
+ axisLine: {
1117
+ stroke: color.visualElementsColors.AXIS_LINE_COLOR,
1118
+ strokeWidth: 2
1119
+ },
1120
+ tick: {
1121
+ '& > line': {
1122
+ stroke: color.primaryDark(),
1123
+ strokeWidth: 2
1124
+ },
1125
+ fontFamily: theme.typography.body1.fontFamily,
1126
+ fontSize: theme.typography.fontSize,
1127
+ textAnchor: 'middle'
1128
+ },
1129
+ dottedLine: {
1130
+ stroke: color.primaryLight(),
1131
+ opacity: 0.2
1132
+ },
1133
+ error: {
1134
+ fontSize: theme.typography.fontSize - 2,
1135
+ fill: theme.palette.error.main
1136
+ },
1137
+ customColor: {
1138
+ color: `${color.tertiary()} !important`
1139
+ },
1140
+ correctnessIcon: {
1141
+ borderRadius: theme.spacing.unit * 2,
1142
+ color: color.defaults.WHITE,
1143
+ fontSize: '16px',
1144
+ width: '16px',
1145
+ height: '16px',
1146
+ padding: '2px',
1147
+ border: `1px solid ${color.defaults.WHITE}`,
1148
+ boxSizing: 'unset' // to override the default border-box in IBX
1149
+
1150
+ },
1151
+ incorrectIcon: {
1152
+ backgroundColor: color.incorrectWithIcon()
1153
+ },
1154
+ correctIcon: {
1155
+ backgroundColor: color.correct()
1156
+ },
1157
+ tickContainer: {
1158
+ display: 'flex',
1159
+ flexDirection: 'column',
1160
+ alignItems: 'center'
1161
+ }
1162
+ }), {
1163
+ withTheme: true
1164
+ })(RawChartAxes);
1165
+
1166
+ const DragIcon = ({
1167
+ width,
1168
+ scaleValue,
1169
+ color,
1170
+ classes
1171
+ }) => {
1172
+ var _getScale;
1173
+
1174
+ return /*#__PURE__*/React.createElement("svg", {
1175
+ x: getAdjustedX(width, scaleValue),
1176
+ y: (_getScale = getScale(width)) == null ? void 0 : _getScale.deltay,
1177
+ color: color,
1178
+ width: width,
1179
+ height: width,
1180
+ overflow: "visible",
1181
+ viewBox: `0 0 ${width} ${width}`,
1182
+ className: classes.svgOverflowVisible
1183
+ }, /*#__PURE__*/React.createElement("g", {
1184
+ xmlns: "http://www.w3.org/2000/svg",
1185
+ fill: "currentColor",
1186
+ stroke: "currentColor",
1187
+ transform: `scale(${scaleValue})`
1188
+ }, /*#__PURE__*/React.createElement("circle", {
1189
+ cx: "28.5",
1190
+ cy: "23.5",
1191
+ r: "22",
1192
+ fill: "white",
1193
+ stroke: "currentColor"
1194
+ }), /*#__PURE__*/React.createElement("path", {
1195
+ d: "M33.5 21.25H23.4609C22.7578 21.25 22.4062 20.4297 22.9141 19.9219L27.9141 14.9219C28.2266 14.6094 28.7344 14.6094 29.0469 14.9219L34.0469 19.9219C34.5547 20.4297 34.2031 21.25 33.5 21.25Z",
1196
+ fill: "currentColor"
1197
+ }), /*#__PURE__*/React.createElement("path", {
1198
+ d: "M23.5 25.75L33.5391 25.75C34.2422 25.75 34.5938 26.5703 34.0859 27.0781L29.0859 32.0781C28.7734 32.3906 28.2656 32.3906 27.9531 32.0781L22.9531 27.0781C22.4453 26.5703 22.7969 25.75 23.5 25.75Z",
1199
+ fill: "currentColor"
1200
+ })));
1201
+ };
1202
+
1203
+ const _excluded$3 = ["x", "y", "width", "graphProps", "classes", "className", "interactive", "defineChart", "isHovered", "correctness", "color", "isPlot"];
1204
+
1205
+ const RawDragHandle$1 = _ref => {
1206
+ var _getScale;
1207
+
1208
+ let {
1209
+ x,
1210
+ y,
1211
+ width,
1212
+ graphProps,
1213
+ classes,
1214
+ className,
1215
+ interactive,
1216
+ isHovered,
1217
+ correctness,
1218
+ isPlot
1219
+ } = _ref,
1220
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$3);
1221
+
1222
+ const {
1223
+ scale
1224
+ } = graphProps;
1225
+ const scaleValue = (_getScale = getScale(width)) == null ? void 0 : _getScale.scale;
1226
+ return /*#__PURE__*/React.createElement("svg", {
1227
+ x: x,
1228
+ y: scale.y(y) - 10,
1229
+ width: width,
1230
+ overflow: "visible",
1231
+ className: classes.svgOverflowVisible
1232
+ }, isHovered && !correctness && interactive && /*#__PURE__*/React.createElement(DragIcon, {
1233
+ width: width,
1234
+ scaleValue: scaleValue,
1235
+ color: color.defaults.BORDER_GRAY,
1236
+ classes: classes
1237
+ }), interactive && !correctness && /*#__PURE__*/React.createElement("ellipse", _extends({
1238
+ cx: width / 2,
1239
+ cy: 10,
1240
+ rx: width / 2 // the drag icon has a 22px fixed r value, so the ry value is 3 times that in order to cover all the area
1241
+ ,
1242
+ ry: 66,
1243
+ className: classNames(classes.transparentHandle, className)
1244
+ }, rest)), /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("filter", {
1245
+ id: "bottomShadow",
1246
+ x: "0",
1247
+ y: "0",
1248
+ width: "140%",
1249
+ height: "140%"
1250
+ }, /*#__PURE__*/React.createElement("feGaussianBlur", {
1251
+ in: "SourceAlpha",
1252
+ stdDeviation: "3"
1253
+ }), /*#__PURE__*/React.createElement("feOffset", {
1254
+ dx: "0",
1255
+ dy: "5",
1256
+ result: "offsetblur"
1257
+ }), /*#__PURE__*/React.createElement("feFlood", {
1258
+ floodColor: "#00000033"
1259
+ }), /*#__PURE__*/React.createElement("feComposite", {
1260
+ in2: "offsetblur",
1261
+ operator: "in"
1262
+ }), /*#__PURE__*/React.createElement("feMerge", null, /*#__PURE__*/React.createElement("feMergeNode", null), /*#__PURE__*/React.createElement("feMergeNode", {
1263
+ in: "SourceGraphic"
1264
+ })))), correctness && interactive && !isPlot && /*#__PURE__*/React.createElement("foreignObject", {
1265
+ x: width / 2 - 14,
1266
+ y: 0,
1267
+ width: 40,
1268
+ height: 40
1269
+ }, correctness.value === 'correct' ? /*#__PURE__*/React.createElement(Check, {
1270
+ className: classNames(classes.correctnessIcon, classes.correctIcon),
1271
+ title: correctness.label
1272
+ }) : /*#__PURE__*/React.createElement(Close, {
1273
+ className: classNames(classes.correctnessIcon, classes.incorrectIcon),
1274
+ title: correctness.label
1275
+ })));
1276
+ };
1277
+
1278
+ RawDragHandle$1.propTypes = {
1279
+ x: PropTypes.number.isRequired,
1280
+ y: PropTypes.number.isRequired,
1281
+ width: PropTypes.number,
1282
+ graphProps: types.GraphPropsType.isRequired,
1283
+ classes: PropTypes.object.isRequired,
1284
+ className: PropTypes.string,
1285
+ interactive: PropTypes.bool,
1286
+ isHovered: PropTypes.bool,
1287
+ correctness: PropTypes.shape({
1288
+ value: PropTypes.string,
1289
+ label: PropTypes.string
1290
+ }),
1291
+ color: PropTypes.string
1292
+ };
1293
+ const DragHandle$1 = withStyles(theme => ({
1294
+ handle: {
1295
+ height: '10px',
1296
+ fill: 'transparent',
1297
+ transition: 'fill 200ms linear, height 200ms linear',
1298
+ '&.correct': correct('fill'),
1299
+ '&.incorrect': incorrect('fill'),
1300
+ '&.non-interactive': disabled('fill')
1301
+ },
1302
+ transparentHandle: {
1303
+ fill: 'transparent',
1304
+ clipPath: 'polygon(50% 0%, 100% 0%, 100% 50%, 0% 50%, 0% 0%)'
1305
+ },
1306
+ handleContainer: {
1307
+ height: 30,
1308
+ '&:hover': {
1309
+ '& .handle': {
1310
+ fill: color.secondaryDark(),
1311
+ height: '16px'
1312
+ }
1313
+ },
1314
+ '&.non-interactive': disabled('fill'),
1315
+ '&.incorrect': incorrect('fill'),
1316
+ '&.correct': correct('fill')
1317
+ },
1318
+ svgOverflowVisible: {
1319
+ overflow: 'visible !important'
1320
+ },
1321
+ correctIcon: {
1322
+ backgroundColor: color.correct()
1323
+ },
1324
+ incorrectIcon: {
1325
+ backgroundColor: color.incorrectWithIcon()
1326
+ },
1327
+ correctnessIcon: {
1328
+ borderRadius: theme.spacing.unit * 2,
1329
+ color: color.defaults.WHITE,
1330
+ fontSize: '16px',
1331
+ padding: '2px',
1332
+ border: `4px solid ${color.defaults.WHITE}`,
1333
+ width: '16px',
1334
+ height: '16px',
1335
+ boxSizing: 'unset' // to override the default border-box in IBX
1336
+
1337
+ }
1338
+ }))(RawDragHandle$1);
1339
+ const D = gridDraggable({
1340
+ axis: 'y',
1341
+ fromDelta: (props, delta) => {
1342
+ //TODO: should be in grid-draggable, if axis is y delta.x should always be 0.
1343
+ delta.x = 0;
1344
+ const newPoint = utils.point(props).add(utils.point(delta));
1345
+ return newPoint.y;
1346
+ },
1347
+ bounds: (props, {
1348
+ domain,
1349
+ range
1350
+ }) => {
1351
+ const area = {
1352
+ left: 0,
1353
+ top: props.y,
1354
+ bottom: props.y,
1355
+ right: 0
1356
+ };
1357
+ return utils.bounds(area, domain, range);
1358
+ },
1359
+ anchorPoint: props => {
1360
+ return {
1361
+ x: props.x,
1362
+ y: props.y
1363
+ };
1364
+ }
1365
+ })(DragHandle$1);
1366
+
1367
+ const CorrectCheckIcon = ({
1368
+ dashColor
1369
+ }) => /*#__PURE__*/React.createElement("svg", {
1370
+ width: "24",
1371
+ height: "24",
1372
+ viewBox: "0 0 24 24",
1373
+ fill: "none",
1374
+ xmlns: "http://www.w3.org/2000/svg"
1375
+ }, /*#__PURE__*/React.createElement("circle", {
1376
+ cx: "12",
1377
+ cy: "12",
1378
+ r: "11",
1379
+ fill: "white",
1380
+ stroke: dashColor || '#7E8494',
1381
+ strokeWidth: "2",
1382
+ strokeDasharray: "5 5"
1383
+ }), /*#__PURE__*/React.createElement("mask", {
1384
+ id: "path-2-outside-1_3089_3799",
1385
+ maskUnits: "userSpaceOnUse",
1386
+ x: "2",
1387
+ y: "3",
1388
+ width: "19",
1389
+ height: "18",
1390
+ fill: "black"
1391
+ }, /*#__PURE__*/React.createElement("rect", {
1392
+ fill: "white",
1393
+ x: "2",
1394
+ y: "3",
1395
+ width: "19",
1396
+ height: "18"
1397
+ }), /*#__PURE__*/React.createElement("path", {
1398
+ d: "M12 20C9.125 20 6.5 18.5 5.0625 16C3.625 13.5312 3.625 10.5 5.0625 8C6.5 5.53125 9.125 4 12 4C14.8438 4 17.4688 5.53125 18.9062 8C20.3438 10.5 20.3438 13.5312 18.9062 16C17.4688 18.5 14.8438 20 12 20ZM15.5312 10.5312H15.5C15.8125 10.25 15.8125 9.78125 15.5 9.46875C15.2188 9.1875 14.75 9.1875 14.4688 9.46875L11 12.9688L9.53125 11.5C9.21875 11.1875 8.75 11.1875 8.46875 11.5C8.15625 11.7812 8.15625 12.25 8.46875 12.5312L10.4688 14.5312C10.75 14.8438 11.2188 14.8438 11.5312 14.5312L15.5312 10.5312Z"
1399
+ })), /*#__PURE__*/React.createElement("path", {
1400
+ d: "M12 20C9.125 20 6.5 18.5 5.0625 16C3.625 13.5312 3.625 10.5 5.0625 8C6.5 5.53125 9.125 4 12 4C14.8438 4 17.4688 5.53125 18.9062 8C20.3438 10.5 20.3438 13.5312 18.9062 16C17.4688 18.5 14.8438 20 12 20ZM15.5312 10.5312H15.5C15.8125 10.25 15.8125 9.78125 15.5 9.46875C15.2188 9.1875 14.75 9.1875 14.4688 9.46875L11 12.9688L9.53125 11.5C9.21875 11.1875 8.75 11.1875 8.46875 11.5C8.15625 11.7812 8.15625 12.25 8.46875 12.5312L10.4688 14.5312C10.75 14.8438 11.2188 14.8438 11.5312 14.5312L15.5312 10.5312Z",
1401
+ fill: "#0EA449"
1402
+ }), /*#__PURE__*/React.createElement("path", {
1403
+ d: "M5.0625 16L5.92942 15.5015L5.92668 15.4968L5.0625 16ZM5.0625 8L4.19831 7.4968L4.19559 7.50153L5.0625 8ZM18.9062 8L19.7732 7.50152L19.7704 7.49681L18.9062 8ZM18.9062 16L18.0421 15.4968L18.0393 15.5015L18.9062 16ZM15.5312 10.5312L16.2384 11.2384L17.9455 9.53125H15.5312V10.5312ZM15.5 10.5312L14.831 9.78796L12.894 11.5312H15.5V10.5312ZM14.4688 9.46875L13.7616 8.76164L13.7585 8.76482L14.4688 9.46875ZM11 12.9688L10.2929 13.6759L11.0032 14.3861L11.7103 13.6727L11 12.9688ZM8.46875 11.5L9.13771 12.2433L9.17684 12.2081L9.21204 12.169L8.46875 11.5ZM8.46875 12.5312L9.17586 11.8241L9.15726 11.8055L9.13771 11.788L8.46875 12.5312ZM10.4688 14.5312L11.212 13.8623L11.1945 13.8427L11.1759 13.8241L10.4688 14.5312ZM12 20V19C9.479 19 7.18657 17.6879 5.92941 15.5015L5.0625 16L4.19559 16.4985C5.81343 19.3121 8.771 21 12 21V20ZM5.0625 16L5.92668 15.4968C4.6714 13.341 4.66824 10.6918 5.92941 8.49847L5.0625 8L4.19559 7.50153C2.58176 10.3082 2.5786 13.7215 4.19832 16.5032L5.0625 16ZM5.0625 8L5.92668 8.50319C7.18712 6.33851 9.48502 5 12 5V4V3C8.76498 3 5.81288 4.72399 4.19832 7.49681L5.0625 8ZM12 4V5C14.4816 5 16.7805 6.33661 18.0421 8.50319L18.9062 8L19.7704 7.49681C18.157 4.72589 15.2059 3 12 3V4ZM18.9062 8L18.0393 8.49847C19.3005 10.6918 19.2973 13.341 18.0421 15.4968L18.9062 16L19.7704 16.5032C21.3902 13.7215 21.387 10.3082 19.7732 7.50153L18.9062 8ZM18.9062 16L18.0393 15.5015C16.7811 17.6898 14.4876 19 12 19V20V21C15.1999 21 18.1564 19.3102 19.7732 16.4985L18.9062 16ZM15.5312 10.5312V9.53125H15.5V10.5312V11.5312H15.5312V10.5312ZM15.5 10.5312L16.169 11.2745C16.9447 10.5764 16.8883 9.44281 16.2071 8.76164L15.5 9.46875L14.7929 10.1759C14.7696 10.1525 14.7344 10.0966 14.7344 10.0117C14.7344 9.92377 14.7735 9.83972 14.831 9.78796L15.5 10.5312ZM15.5 9.46875L16.2071 8.76164C15.5353 8.08987 14.4334 8.08987 13.7616 8.76164L14.4688 9.46875L15.1759 10.1759C15.1167 10.235 15.0442 10.2578 14.9844 10.2578C14.9245 10.2578 14.8521 10.235 14.7929 10.1759L15.5 9.46875ZM14.4688 9.46875L13.7585 8.76482L10.2897 12.2648L11 12.9688L11.7103 13.6727L15.179 10.1727L14.4688 9.46875ZM11 12.9688L11.7071 12.2616L10.2384 10.7929L9.53125 11.5L8.82414 12.2071L10.2929 13.6759L11 12.9688ZM9.53125 11.5L10.2384 10.7929C9.55719 10.1117 8.42362 10.0553 7.72546 10.831L8.46875 11.5L9.21204 12.169C9.16028 12.2265 9.07623 12.2656 8.98828 12.2656C8.90344 12.2656 8.84748 12.2304 8.82414 12.2071L9.53125 11.5ZM8.46875 11.5L7.79979 10.7567C7.04591 11.4352 7.04591 12.5961 7.79979 13.2745L8.46875 12.5312L9.13771 11.788C9.19301 11.8377 9.23438 11.9208 9.23438 12.0156C9.23438 12.1105 9.19301 12.1935 9.13771 12.2433L8.46875 11.5ZM8.46875 12.5312L7.76164 13.2384L9.76164 15.2384L10.4688 14.5312L11.1759 13.8241L9.17586 11.8241L8.46875 12.5312ZM10.4688 14.5312L9.72546 15.2002C10.4236 15.976 11.5572 15.9195 12.2384 15.2384L11.5312 14.5312L10.8241 13.8241C10.8475 13.8008 10.9034 13.7656 10.9883 13.7656C11.0762 13.7656 11.1603 13.8048 11.212 13.8623L10.4688 14.5312ZM11.5312 14.5312L12.2384 15.2384L16.2384 11.2384L15.5312 10.5312L14.8241 9.82414L10.8241 13.8241L11.5312 14.5312Z",
1404
+ fill: "white",
1405
+ mask: "url(#path-2-outside-1_3089_3799)"
1406
+ }));
1407
+
1408
+ const log$2 = debug('pie-lib:chart:bars');
1409
+ const histogramColors = ['#006699', '#F59B00', '#08916D', '#529EE0', '#52B7D8', '#D9A6C2', '#FFB03B', '#54A77B', '#E16032', '#4FD2D2', '#F0E442', '#E287B2'];
1410
+ const hoverHistogramColors = ['#003754', '#975616', '#00503B', '#225982', '#1F687D', '#825E6F', '#996428', '#255E44', '#8A331F', '#167A7A', '#91862D', '#894A65'];
1411
+
1412
+ const calculateFillColor = (isHovered, barColor, index, hoverHistogramColors, allowRolloverEvent) => {
1413
+ if (isHovered && barColor && allowRolloverEvent) {
1414
+ return hoverHistogramColors[index % hoverHistogramColors.length];
1415
+ }
1416
+
1417
+ if (isHovered && allowRolloverEvent) {
1418
+ return color.visualElementsColors.ROLLOVER_FILL_BAR_COLOR;
1419
+ }
1420
+
1421
+ return barColor || null;
1422
+ };
1423
+
1424
+ class RawBar extends React.Component {
1425
+ constructor(props) {
1426
+ super(props);
1427
+
1428
+ this.handleMouseMove = e => {
1429
+ // Update mouse position
1430
+ this.mouseX = e.clientX;
1431
+ this.mouseY = e.clientY; // Check if the mouse is inside the <g> element
1432
+
1433
+ const isMouseInside = this.isMouseInsideSvgElement();
1434
+ this.setState({
1435
+ isHovered: isMouseInside
1436
+ });
1437
+ };
1438
+
1439
+ this.isMouseInsideSvgElement = () => {
1440
+ const gBoundingBox = this.gRef.getBoundingClientRect(); // Check if the mouse position is within the bounding box
1441
+
1442
+ return this.mouseX >= gBoundingBox.left && this.mouseX <= gBoundingBox.right && this.mouseY >= gBoundingBox.top && this.mouseY <= gBoundingBox.bottom;
1443
+ };
1444
+
1445
+ this.handleMouseEnter = () => {
1446
+ this.setState({
1447
+ isHovered: true
1448
+ });
1449
+ };
1450
+
1451
+ this.handleMouseLeave = () => {
1452
+ this.setState({
1453
+ isHovered: false
1454
+ });
1455
+ };
1456
+
1457
+ this.setDragValue = dragValue => this.setState({
1458
+ dragValue
1459
+ });
1460
+
1461
+ this.dragStop = () => {
1462
+ const {
1463
+ label,
1464
+ onChangeCategory
1465
+ } = this.props;
1466
+ const {
1467
+ dragValue
1468
+ } = this.state;
1469
+ log$2('[dragStop]', dragValue);
1470
+
1471
+ if (dragValue !== undefined) {
1472
+ onChangeCategory({
1473
+ label,
1474
+ value: dragValue
1475
+ });
1476
+ }
1477
+
1478
+ this.setDragValue(undefined);
1479
+ };
1480
+
1481
+ this.dragValue = (existing, next) => {
1482
+ log$2('[dragValue] next:', next);
1483
+ this.setDragValue(next);
1484
+ };
1485
+
1486
+ this.state = {
1487
+ dragValue: undefined,
1488
+ isHovered: false
1489
+ };
1490
+ this.mouseX = 0;
1491
+ this.mouseY = 0;
1492
+ }
1493
+
1494
+ componentDidMount() {
1495
+ window.addEventListener('mousemove', this.handleMouseMove);
1496
+ }
1497
+
1498
+ componentWillUnmount() {
1499
+ window.removeEventListener('mousemove', this.handleMouseMove);
1500
+ }
1501
+
1502
+ render() {
1503
+ const {
1504
+ graphProps,
1505
+ value,
1506
+ label,
1507
+ classes,
1508
+ xBand,
1509
+ index,
1510
+ interactive,
1511
+ correctness,
1512
+ barColor,
1513
+ defineChart,
1514
+ correctData
1515
+ } = this.props;
1516
+ const {
1517
+ scale,
1518
+ range
1519
+ } = graphProps;
1520
+ const {
1521
+ dragValue,
1522
+ isHovered
1523
+ } = this.state;
1524
+ const allowRolloverEvent = interactive && !correctness;
1525
+ const fillColor = calculateFillColor(isHovered, barColor, index, hoverHistogramColors, allowRolloverEvent);
1526
+ const v = Number.isFinite(dragValue) ? dragValue : value;
1527
+ const barWidth = xBand.bandwidth();
1528
+ const barHeight = scale.y(range.max - v);
1529
+ const barX = xBand(bandKey({
1530
+ label
1531
+ }, index));
1532
+ const rawY = range.max - v;
1533
+ const yy = range.max - rawY;
1534
+ const correctValue = correctData ? correctData.find(d => d.label === label) : null;
1535
+ log$2('label:', label, 'barX:', barX, 'v: ', v, 'barHeight:', barHeight, 'barWidth: ', barWidth);
1536
+ const Component = interactive ? D : DragHandle$1;
1537
+ const isHistogram = !!barColor;
1538
+ return /*#__PURE__*/React.createElement("g", {
1539
+ ref: ref => this.gRef = ref,
1540
+ onMouseEnter: this.handleMouseEnter,
1541
+ onMouseLeave: this.handleMouseLeave,
1542
+ onTouchStart: this.handleMouseEnter,
1543
+ onTouchEnd: this.handleMouseLeave
1544
+ }, /*#__PURE__*/React.createElement(Bar$4, {
1545
+ x: barX,
1546
+ y: scale.y(yy),
1547
+ width: barWidth,
1548
+ height: barHeight,
1549
+ className: classes.bar,
1550
+ style: {
1551
+ fill: fillColor
1552
+ }
1553
+ }), correctness && correctness.value === 'incorrect' && (() => {
1554
+ const correctVal = parseFloat(correctValue && correctValue.value);
1555
+ if (isNaN(correctVal)) return null;
1556
+ const correctPxHeight = scale.y(range.max - correctVal);
1557
+ const actualPxHeight = barHeight;
1558
+ const diffPx = Math.abs(correctPxHeight - actualPxHeight);
1559
+ const yDiff = scale.y(correctVal);
1560
+ const indicatorBarColor = correctPxHeight > actualPxHeight ? color.borderGray() : color.defaults.WHITE;
1561
+ const yToRender = correctPxHeight > actualPxHeight ? yDiff : yDiff - diffPx;
1562
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Bar$4, {
1563
+ x: barX + 2 // add 2px for the stroke (the dashed border)
1564
+ ,
1565
+ y: yToRender,
1566
+ width: barWidth - 4 // substract 4px for the total stroke
1567
+ ,
1568
+ height: diffPx,
1569
+ className: classes.bar,
1570
+ style: {
1571
+ stroke: indicatorBarColor,
1572
+ strokeWidth: 2,
1573
+ strokeDasharray: '5,2',
1574
+ fill: 'none'
1575
+ }
1576
+ }), /*#__PURE__*/React.createElement("foreignObject", {
1577
+ x: barX + barWidth - (isHistogram ? 24 : 14),
1578
+ y: yDiff - 12,
1579
+ width: 24,
1580
+ height: 24
1581
+ }, /*#__PURE__*/React.createElement(CorrectCheckIcon, {
1582
+ dashColor: indicatorBarColor
1583
+ })));
1584
+ })(), /*#__PURE__*/React.createElement(Component, {
1585
+ x: barX,
1586
+ y: v,
1587
+ defineChart: defineChart,
1588
+ interactive: interactive,
1589
+ width: barWidth,
1590
+ onDrag: v => this.dragValue(value, v),
1591
+ onDragStop: this.dragStop,
1592
+ graphProps: graphProps,
1593
+ correctness: correctness,
1594
+ isHovered: isHovered,
1595
+ color: fillColor
1596
+ }));
1597
+ }
1598
+
1599
+ }
1600
+ RawBar.propTypes = {
1601
+ barColor: PropTypes.string,
1602
+ onChangeCategory: PropTypes.func,
1603
+ value: PropTypes.number,
1604
+ classes: PropTypes.object,
1605
+ label: PropTypes.string,
1606
+ xBand: PropTypes.func,
1607
+ index: PropTypes.number.isRequired,
1608
+ graphProps: types.GraphPropsType.isRequired,
1609
+ interactive: PropTypes.bool,
1610
+ correctness: PropTypes.shape({
1611
+ value: PropTypes.string,
1612
+ label: PropTypes.string
1613
+ }),
1614
+ correctData: PropTypes.array
1615
+ };
1616
+ const Bar$3 = withStyles$1(theme => ({
1617
+ bar: {
1618
+ fill: color.defaults.TERTIARY
1619
+ },
1620
+ correctIcon: {
1621
+ backgroundColor: color.correct(),
1622
+ borderRadius: theme.spacing.unit * 2,
1623
+ color: color.defaults.WHITE,
1624
+ fontSize: '10px',
1625
+ width: '10px',
1626
+ height: '10px',
1627
+ padding: '2px',
1628
+ border: `1px solid ${color.defaults.WHITE}`,
1629
+ boxSizing: 'unset' // to override the default border-box in IBX
1630
+
1631
+ }
1632
+ }))(RawBar);
1633
+ class Bars extends React.Component {
1634
+ render() {
1635
+ const {
1636
+ data,
1637
+ graphProps,
1638
+ xBand,
1639
+ onChangeCategory,
1640
+ defineChart,
1641
+ histogram,
1642
+ correctData
1643
+ } = this.props;
1644
+ return /*#__PURE__*/React.createElement(Group, null, (data || []).map((d, index) => /*#__PURE__*/React.createElement(Bar$3, {
1645
+ value: d.value,
1646
+ interactive: defineChart || d.interactive,
1647
+ defineChart: defineChart,
1648
+ label: d.label,
1649
+ xBand: xBand,
1650
+ index: index,
1651
+ key: `bar-${d.label}-${d.value}-${index}`,
1652
+ onChangeCategory: category => onChangeCategory(index, category),
1653
+ graphProps: graphProps,
1654
+ correctness: d.correctness,
1655
+ correctData: correctData,
1656
+ barColor: histogram && (histogramColors[index] ? histogramColors[index] : histogramColors[index % histogramColors.length])
1657
+ })));
1658
+ }
1659
+
1660
+ }
1661
+ Bars.propTypes = {
1662
+ data: PropTypes.array,
1663
+ correctData: PropTypes.array,
1664
+ onChangeCategory: PropTypes.func,
1665
+ defineChart: PropTypes.bool,
1666
+ xBand: PropTypes.func,
1667
+ graphProps: types.GraphPropsType.isRequired,
1668
+ histogram: PropTypes.bool
1669
+ };
1670
+
1671
+ let Bar$1 = class Bar extends React.Component {
1672
+ render() {
1673
+ const props = this.props;
1674
+ const {
1675
+ data,
1676
+ graphProps
1677
+ } = props;
1678
+ const {
1679
+ scale = {},
1680
+ size = {}
1681
+ } = graphProps || {};
1682
+ const xBand = dataToXBand(scale.x, data, size.width, 'bar');
1683
+ return /*#__PURE__*/React.createElement(Bars, _extends({}, props, {
1684
+ xBand: xBand
1685
+ }));
1686
+ }
1687
+
1688
+ };
1689
+ Bar$1.propTypes = {
1690
+ data: PropTypes.array,
1691
+ onChange: PropTypes.func,
1692
+ graphProps: types.GraphPropsType.isRequired
1693
+ };
1694
+ var Bar$2 = (() => ({
1695
+ type: 'bar',
1696
+ Component: Bar$1,
1697
+ name: 'Bar'
1698
+ }));
1699
+
1700
+ class Histogram extends React.Component {
1701
+ render() {
1702
+ const props = this.props;
1703
+ const {
1704
+ data,
1705
+ graphProps
1706
+ } = props;
1707
+ const {
1708
+ scale = {},
1709
+ size = {}
1710
+ } = graphProps || {};
1711
+ const xBand = dataToXBand(scale.x, data, size.width, 'histogram');
1712
+ return /*#__PURE__*/React.createElement(Bars, _extends({}, props, {
1713
+ xBand: xBand,
1714
+ histogram: true
1715
+ }));
1716
+ }
1717
+
1718
+ }
1719
+ Histogram.propTypes = {
1720
+ data: PropTypes.array,
1721
+ onChange: PropTypes.func,
1722
+ graphProps: types.GraphPropsType.isRequired
1723
+ };
1724
+ var Histogram$1 = (() => ({
1725
+ type: 'histogram',
1726
+ Component: Histogram,
1727
+ name: 'Histogram'
1728
+ }));
1729
+
1730
+ const _excluded$2 = ["x", "y", "graphProps", "classes", "className", "interactive", "CustomDraggableComponent", "correctness"];
1731
+
1732
+ class RawDragHandle extends React.Component {
1733
+ render() {
1734
+ const _this$props = this.props,
1735
+ {
1736
+ x,
1737
+ y,
1738
+ graphProps,
1739
+ classes,
1740
+ className,
1741
+ interactive,
1742
+ CustomDraggableComponent,
1743
+ correctness
1744
+ } = _this$props,
1745
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$2);
1746
+
1747
+ const {
1748
+ scale
1749
+ } = graphProps;
1750
+ return /*#__PURE__*/React.createElement(CustomDraggableComponent, _extends({
1751
+ scale: scale,
1752
+ x: x,
1753
+ y: y,
1754
+ classes: classes,
1755
+ className: classNames(className, !interactive && 'non-interactive'),
1756
+ correctness: correctness,
1757
+ interactive: interactive
1758
+ }, rest));
1759
+ }
1760
+
1761
+ }
1762
+
1763
+ RawDragHandle.propTypes = {
1764
+ x: PropTypes.number.isRequired,
1765
+ y: PropTypes.number.isRequired,
1766
+ width: PropTypes.number,
1767
+ graphProps: types.GraphPropsType.isRequired,
1768
+ classes: PropTypes.object.isRequired,
1769
+ className: PropTypes.string,
1770
+ interactive: PropTypes.bool,
1771
+ CustomDraggableComponent: PropTypes.func,
1772
+ correctness: PropTypes.shape({
1773
+ value: PropTypes.string,
1774
+ label: PropTypes.string
1775
+ })
1776
+ };
1777
+ const DragHandle = withStyles$1(theme => ({
1778
+ handle: {
1779
+ transition: 'fill 200ms linear, height 200ms linear',
1780
+ '&.non-interactive': disabled('fill')
1781
+ },
1782
+ transparentHandle: {
1783
+ height: '20px',
1784
+ fill: 'transparent',
1785
+ stroke: 'transparent'
1786
+ },
1787
+ line: {
1788
+ stroke: color.defaults.TEXT,
1789
+ transition: 'fill 200ms linear, height 200ms linear',
1790
+ '&.non-interactive': disabled('stroke')
1791
+ },
1792
+ disabledPoint: {
1793
+ fill: color.defaults.BLACK + ' !important',
1794
+ stroke: color.defaults.BLACK + ' !important'
1795
+ },
1796
+ correctIcon: {
1797
+ backgroundColor: color.correct()
1798
+ },
1799
+ incorrectIcon: {
1800
+ backgroundColor: color.incorrectWithIcon()
1801
+ },
1802
+ correctnessIcon: {
1803
+ borderRadius: theme.spacing.unit * 2,
1804
+ color: color.defaults.WHITE,
1805
+ fontSize: '16px',
1806
+ width: '16px',
1807
+ height: '16px',
1808
+ padding: '2px',
1809
+ border: `1px solid ${color.defaults.WHITE}`,
1810
+ stroke: 'initial',
1811
+ boxSizing: 'unset' // to override the default border-box in IBX
1812
+
1813
+ },
1814
+ smallIcon: {
1815
+ fontSize: '10px',
1816
+ width: '10px',
1817
+ height: '10px'
1818
+ }
1819
+ }))(RawDragHandle);
1820
+ const DraggableHandle = gridDraggable({
1821
+ axis: 'y',
1822
+ fromDelta: (props, delta) => {
1823
+ //TODO: should be in grid-draggable, if axis is y delta.x should always be 0.
1824
+ delta.x = 0;
1825
+ const newPoint = utils.point(props).add(utils.point(delta));
1826
+ return newPoint.y;
1827
+ },
1828
+ bounds: (props, {
1829
+ domain,
1830
+ range
1831
+ }) => {
1832
+ const area = {
1833
+ left: 0,
1834
+ top: props.y,
1835
+ bottom: props.y,
1836
+ right: 0
1837
+ };
1838
+ return utils.bounds(area, domain, range);
1839
+ },
1840
+ anchorPoint: props => {
1841
+ return {
1842
+ x: props.x,
1843
+ y: props.y
1844
+ };
1845
+ }
1846
+ })(DragHandle);
1847
+
1848
+ const getData = (data, domain) => {
1849
+ const {
1850
+ max
1851
+ } = domain || {};
1852
+ const length = data.length;
1853
+
1854
+ if (!max || !length) {
1855
+ return [];
1856
+ }
1857
+
1858
+ return data.map((el, i) => _extends({}, el, {
1859
+ x: length > 1 ? i * (max / (length - 1)) : 0.5,
1860
+ y: el.value
1861
+ }));
1862
+ };
1863
+
1864
+ class RawLine extends React.Component {
1865
+ constructor(props) {
1866
+ super(props);
1867
+
1868
+ this.setDragValue = line => this.setState({
1869
+ line
1870
+ });
1871
+
1872
+ this.dragStop = index => {
1873
+ const {
1874
+ onChange
1875
+ } = this.props;
1876
+ this.setState({
1877
+ dragging: false
1878
+ }, () => {
1879
+ onChange(index, this.state.line[index]);
1880
+ });
1881
+ };
1882
+
1883
+ this.dragValue = (index, existing, next) => {
1884
+ const newLine = [...this.state.line];
1885
+ newLine[index].dragValue = next;
1886
+ this.setDragValue(newLine);
1887
+ };
1888
+
1889
+ this.state = {
1890
+ dragValue: undefined,
1891
+ line: getData(props.data, props.graphProps.domain)
1892
+ };
1893
+ }
1894
+
1895
+ UNSAFE_componentWillReceiveProps(nextProps) {
1896
+ if (!isEqual(this.props.data, nextProps.data)) {
1897
+ this.setState({
1898
+ line: getData(nextProps.data, nextProps.graphProps.domain)
1899
+ });
1900
+ }
1901
+ }
1902
+
1903
+ render() {
1904
+ const {
1905
+ graphProps,
1906
+ data,
1907
+ classes,
1908
+ CustomDraggableComponent,
1909
+ defineChart,
1910
+ correctData
1911
+ } = this.props;
1912
+ const {
1913
+ line: lineState,
1914
+ dragging
1915
+ } = this.state;
1916
+ const {
1917
+ scale
1918
+ } = graphProps;
1919
+ const lineToUse = dragging ? lineState : getData(data, graphProps.domain);
1920
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(LinePath, {
1921
+ data: lineToUse,
1922
+ x: d => scale.x(d.x),
1923
+ y: d => scale.y(d.dragValue !== undefined ? d.dragValue : d.y),
1924
+ className: classes.line
1925
+ }), lineToUse && lineToUse.map((point, i) => {
1926
+ const r = 6;
1927
+ const enableDraggable = defineChart || point.interactive;
1928
+ const Component = enableDraggable ? DraggableHandle : DragHandle;
1929
+ return /*#__PURE__*/React.createElement(Component, {
1930
+ key: `point-${point.x}-${i}`,
1931
+ x: point.x,
1932
+ y: point.dragValue !== undefined ? point.dragValue : point.y,
1933
+ interactive: enableDraggable,
1934
+ r: r,
1935
+ onDragStart: () => this.setState({
1936
+ dragging: true
1937
+ }),
1938
+ onDrag: v => this.dragValue(i, point.dragValue !== undefined ? point.dragValue : point.y, v),
1939
+ onDragStop: () => this.dragStop(i),
1940
+ graphProps: graphProps,
1941
+ CustomDraggableComponent: CustomDraggableComponent,
1942
+ correctness: point.correctness,
1943
+ correctData: correctData,
1944
+ label: point.label
1945
+ });
1946
+ }));
1947
+ }
1948
+
1949
+ }
1950
+ RawLine.propTypes = {
1951
+ onChange: PropTypes.func,
1952
+ value: PropTypes.number,
1953
+ classes: PropTypes.object,
1954
+ label: PropTypes.string,
1955
+ xBand: PropTypes.func,
1956
+ index: PropTypes.number.isRequired,
1957
+ graphProps: types.GraphPropsType.isRequired,
1958
+ defineChart: PropTypes.bool,
1959
+ data: PropTypes.arrayOf(PropTypes.shape({
1960
+ label: PropTypes.string,
1961
+ value: PropTypes.number
1962
+ })),
1963
+ CustomDraggableComponent: PropTypes.func
1964
+ };
1965
+ RawLine.defaultProps = {
1966
+ index: 0
1967
+ };
1968
+ const StyledLine = withStyles$1(() => ({
1969
+ line: {
1970
+ fill: 'transparent',
1971
+ stroke: color.defaults.TERTIARY,
1972
+ strokeWidth: 3,
1973
+ transition: 'stroke 200ms ease-in, stroke-width 200ms ease-in'
1974
+ }
1975
+ }))(RawLine);
1976
+ class Line extends React.Component {
1977
+ constructor(...args) {
1978
+ super(...args);
1979
+
1980
+ this.changeLine = (index, category) => {
1981
+ const {
1982
+ onChange
1983
+ } = this.props;
1984
+ const newLine = [...this.props.data];
1985
+ const {
1986
+ dragValue,
1987
+ value
1988
+ } = category;
1989
+ newLine[index].value = dragValue >= 0 ? dragValue : value;
1990
+ onChange(newLine);
1991
+ };
1992
+ }
1993
+
1994
+ render() {
1995
+ const props = this.props;
1996
+ return /*#__PURE__*/React.createElement(Group, null, /*#__PURE__*/React.createElement(StyledLine, _extends({}, props, {
1997
+ onChange: this.changeLine
1998
+ })));
1999
+ }
2000
+
2001
+ }
2002
+ Line.propTypes = {
2003
+ data: PropTypes.array,
2004
+ onChange: PropTypes.func,
2005
+ xBand: PropTypes.func,
2006
+ graphProps: types.GraphPropsType.isRequired
2007
+ };
2008
+
2009
+ const _excluded$1 = ["scale", "x", "y", "className", "classes", "r", "correctness", "interactive", "correctData", "label"];
2010
+
2011
+ const DraggableComponent$1 = _ref => {
2012
+ let {
2013
+ scale,
2014
+ x,
2015
+ y,
2016
+ className,
2017
+ classes,
2018
+ r,
2019
+ correctness,
2020
+ interactive,
2021
+ correctData,
2022
+ label
2023
+ } = _ref,
2024
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$1);
2025
+
2026
+ const [isHovered, setIsHovered] = React.useState(false);
2027
+ const allowRolloverEvent = !correctness && interactive;
2028
+ return /*#__PURE__*/React.createElement("g", {
2029
+ onMouseEnter: () => setIsHovered(true),
2030
+ onMouseLeave: () => setIsHovered(false)
2031
+ }, /*#__PURE__*/React.createElement("circle", _extends({
2032
+ cx: scale.x(x),
2033
+ cy: scale.y(y),
2034
+ r: r * 3,
2035
+ className: classNames(classes.transparentHandle, className),
2036
+ pointerEvents: correctness ? 'none' : ''
2037
+ }, rest)), /*#__PURE__*/React.createElement("circle", _extends({
2038
+ cx: scale.x(x),
2039
+ cy: scale.y(y),
2040
+ r: r,
2041
+ className: classNames(className, classes.handle, correctness && !interactive && classes.disabledPoint)
2042
+ }, rest)), /*#__PURE__*/React.createElement(CorrectnessIndicator, {
2043
+ scale: scale,
2044
+ x: x,
2045
+ y: y,
2046
+ classes: classes,
2047
+ r: r,
2048
+ correctness: correctness,
2049
+ interactive: interactive
2050
+ }), /*#__PURE__*/React.createElement(SmallCorrectPointIndicator, {
2051
+ scale: scale,
2052
+ x: x,
2053
+ r: r,
2054
+ correctness: correctness,
2055
+ classes: classes,
2056
+ correctData: correctData,
2057
+ label: label
2058
+ }), isHovered && allowRolloverEvent && /*#__PURE__*/React.createElement("rect", {
2059
+ x: scale.x(x) - r * 2,
2060
+ y: scale.y(y) - r * 2,
2061
+ width: r * 4,
2062
+ height: r * 4,
2063
+ stroke: color.defaults.BORDER_GRAY,
2064
+ strokeWidth: "1",
2065
+ fill: "none"
2066
+ }));
2067
+ };
2068
+
2069
+ DraggableComponent$1.propTypes = {
2070
+ scale: PropTypes.object,
2071
+ x: PropTypes.number,
2072
+ y: PropTypes.number,
2073
+ r: PropTypes.number,
2074
+ className: PropTypes.string,
2075
+ classes: PropTypes.object,
2076
+ correctness: PropTypes.shape({
2077
+ value: PropTypes.string,
2078
+ label: PropTypes.string
2079
+ })
2080
+ };
2081
+ class LineDot extends React.Component {
2082
+ render() {
2083
+ const props = this.props;
2084
+ const {
2085
+ data,
2086
+ graphProps
2087
+ } = props;
2088
+ const {
2089
+ scale = {},
2090
+ size = {}
2091
+ } = graphProps || {};
2092
+ const xBand = dataToXBand(scale.x, data, size.width, 'lineDot');
2093
+ return /*#__PURE__*/React.createElement(Line, _extends({}, props, {
2094
+ xBand: xBand,
2095
+ CustomDraggableComponent: DraggableComponent$1
2096
+ }));
2097
+ }
2098
+
2099
+ }
2100
+ LineDot.propTypes = {
2101
+ data: PropTypes.array,
2102
+ onChange: PropTypes.func,
2103
+ graphProps: types.GraphPropsType.isRequired
2104
+ };
2105
+ var LineDot$1 = (() => ({
2106
+ type: 'lineDot',
2107
+ Component: LineDot,
2108
+ name: 'Line Dot'
2109
+ }));
2110
+
2111
+ const _excluded = ["classes", "className", "scale", "x", "y", "r", "correctness", "interactive", "correctData", "label"];
2112
+
2113
+ const DraggableComponent = props => {
2114
+ const {
2115
+ classes = {},
2116
+ className,
2117
+ scale,
2118
+ x,
2119
+ y,
2120
+ r,
2121
+ correctness,
2122
+ interactive,
2123
+ correctData,
2124
+ label
2125
+ } = props,
2126
+ rest = _objectWithoutPropertiesLoose(props, _excluded);
2127
+
2128
+ const [hover, setHover] = useState(false);
2129
+ const squareSize = r * 4;
2130
+ const squareHalf = squareSize / 2;
2131
+ const cx = scale.x(x);
2132
+ const cy = scale.y(y);
2133
+ return /*#__PURE__*/React.createElement(Group, {
2134
+ className: classNames(className, classes.line, correctness && !interactive && classes.disabledPoint)
2135
+ }, /*#__PURE__*/React.createElement(LinePath, {
2136
+ data: [{
2137
+ x: scale.x(x) - r,
2138
+ y: scale.y(y) + r
2139
+ }, {
2140
+ x: scale.x(x) + r,
2141
+ y: scale.y(y) - r
2142
+ }],
2143
+ key: `point-${x}-${y}-1`,
2144
+ x: d => d.x,
2145
+ y: d => d.y,
2146
+ strokeWidth: 5,
2147
+ style: {
2148
+ pointerEvents: 'none'
2149
+ }
2150
+ }), /*#__PURE__*/React.createElement(LinePath, {
2151
+ data: [{
2152
+ x: scale.x(x) - r,
2153
+ y: scale.y(y) - r
2154
+ }, {
2155
+ x: scale.x(x) + r,
2156
+ y: scale.y(y) + r
2157
+ }],
2158
+ key: `point-${x}-${y}-2`,
2159
+ x: d => d.x,
2160
+ y: d => d.y,
2161
+ strokeWidth: 5,
2162
+ style: {
2163
+ pointerEvents: 'none'
2164
+ }
2165
+ }), hover && /*#__PURE__*/React.createElement("rect", {
2166
+ x: cx - squareHalf,
2167
+ y: cy - squareHalf,
2168
+ width: squareSize,
2169
+ height: squareSize,
2170
+ stroke: color.defaults.BORDER_GRAY,
2171
+ fill: "none",
2172
+ strokeWidth: 2,
2173
+ pointerEvents: "none"
2174
+ }), /*#__PURE__*/React.createElement("circle", _extends({
2175
+ cx: cx,
2176
+ cy: cy,
2177
+ r: r * 2,
2178
+ className: classNames(classes.transparentHandle),
2179
+ onMouseEnter: () => setHover(true),
2180
+ onMouseLeave: () => setHover(false)
2181
+ }, rest)), /*#__PURE__*/React.createElement(CorrectnessIndicator, {
2182
+ scale: scale,
2183
+ x: x,
2184
+ y: y,
2185
+ classes: classes,
2186
+ r: r,
2187
+ correctness: correctness,
2188
+ interactive: interactive
2189
+ }), /*#__PURE__*/React.createElement(SmallCorrectPointIndicator, {
2190
+ scale: scale,
2191
+ x: x,
2192
+ r: r,
2193
+ correctness: correctness,
2194
+ classes: classes,
2195
+ correctData: correctData,
2196
+ label: label
2197
+ }));
2198
+ };
2199
+
2200
+ DraggableComponent.propTypes = {
2201
+ scale: PropTypes.object,
2202
+ x: PropTypes.number,
2203
+ y: PropTypes.number,
2204
+ r: PropTypes.number,
2205
+ className: PropTypes.string,
2206
+ classes: PropTypes.object,
2207
+ correctness: PropTypes.shape({
2208
+ value: PropTypes.string,
2209
+ label: PropTypes.string
2210
+ })
2211
+ };
2212
+ class LineCross extends React.Component {
2213
+ render() {
2214
+ const props = this.props;
2215
+ const {
2216
+ data,
2217
+ graphProps
2218
+ } = props;
2219
+ const {
2220
+ scale = {},
2221
+ size = {}
2222
+ } = graphProps || {};
2223
+ const xBand = dataToXBand(scale.x, data, size.width, 'lineCross');
2224
+ return /*#__PURE__*/React.createElement(Line, _extends({}, props, {
2225
+ xBand: xBand,
2226
+ CustomDraggableComponent: DraggableComponent
2227
+ }));
2228
+ }
2229
+
2230
+ }
2231
+ LineCross.propTypes = {
2232
+ data: PropTypes.array,
2233
+ onChange: PropTypes.func,
2234
+ graphProps: types.GraphPropsType.isRequired
2235
+ };
2236
+ var LineCross$1 = (() => ({
2237
+ type: 'lineCross',
2238
+ Component: LineCross,
2239
+ name: 'Line Cross'
2240
+ }));
2241
+
2242
+ const log$1 = debug('pie-lib:chart:bars');
2243
+ const ICON_SIZE = 16; // 10px icon + 2px padding on all sides + 1px border
2244
+
2245
+ class RawPlot extends React.Component {
2246
+ constructor(props) {
2247
+ super(props);
2248
+
2249
+ this.handleMouseEnter = () => {
2250
+ this.setState({
2251
+ isHovered: true
2252
+ });
2253
+ };
2254
+
2255
+ this.handleMouseLeave = () => {
2256
+ this.setState({
2257
+ isHovered: false
2258
+ });
2259
+ };
2260
+
2261
+ this.setDragValue = dragValue => this.setState({
2262
+ dragValue
2263
+ });
2264
+
2265
+ this.dragStop = () => {
2266
+ const {
2267
+ label,
2268
+ onChangeCategory
2269
+ } = this.props;
2270
+ const {
2271
+ dragValue
2272
+ } = this.state;
2273
+ log$1('[dragStop]', dragValue);
2274
+
2275
+ if (dragValue !== undefined) {
2276
+ onChangeCategory({
2277
+ label,
2278
+ value: dragValue
2279
+ });
2280
+ }
2281
+
2282
+ this.setDragValue(undefined);
2283
+ };
2284
+
2285
+ this.dragValue = (existing, next) => {
2286
+ log$1('[dragValue] next:', next);
2287
+ this.setDragValue(next);
2288
+ };
2289
+
2290
+ this.renderCorrectnessIcon = (barX, barWidth, correctVal, correctness, classes, scale) => /*#__PURE__*/React.createElement("foreignObject", {
2291
+ x: barX + barWidth / 2 - ICON_SIZE / 2,
2292
+ y: scale.y(correctVal) + ICON_SIZE,
2293
+ width: ICON_SIZE,
2294
+ height: ICON_SIZE
2295
+ }, /*#__PURE__*/React.createElement(Check, {
2296
+ className: classNames(classes.correctnessIcon, classes.correctIcon, classes.smallIcon),
2297
+ title: correctness.label
2298
+ }));
2299
+
2300
+ this.state = {
2301
+ dragValue: undefined,
2302
+ isHovered: false
2303
+ };
2304
+ }
2305
+
2306
+ render() {
2307
+ const {
2308
+ graphProps,
2309
+ value,
2310
+ label,
2311
+ classes,
2312
+ xBand,
2313
+ index,
2314
+ CustomBarElement,
2315
+ interactive,
2316
+ correctness,
2317
+ defineChart,
2318
+ correctData
2319
+ } = this.props;
2320
+ const {
2321
+ scale,
2322
+ range,
2323
+ size
2324
+ } = graphProps;
2325
+ const {
2326
+ max
2327
+ } = range || {};
2328
+ const {
2329
+ dragValue,
2330
+ isHovered
2331
+ } = this.state;
2332
+ const v = Number.isFinite(dragValue) ? dragValue : value;
2333
+ const barWidth = xBand.bandwidth();
2334
+ const barHeight = scale.y(range.max - v);
2335
+ const barX = xBand(bandKey({
2336
+ label
2337
+ }, index));
2338
+ log$1('label:', label, 'barX:', barX, 'v: ', v, 'barHeight:', barHeight, 'barWidth: ', barWidth);
2339
+ const values = [];
2340
+
2341
+ for (let i = 0; i < v; i++) {
2342
+ values.push(i);
2343
+ }
2344
+
2345
+ const pointHeight = size.height / max;
2346
+ const pointDiameter = (pointHeight > barWidth ? barWidth : pointHeight) * 0.8;
2347
+ const Component = interactive ? D : DragHandle$1;
2348
+ const allowRolloverEvent = interactive && !correctness;
2349
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("g", {
2350
+ onMouseEnter: this.handleMouseEnter,
2351
+ onMouseLeave: this.handleMouseLeave,
2352
+ onTouchStart: this.handleMouseEnter,
2353
+ onTouchEnd: this.handleMouseLeave
2354
+ }, isHovered && allowRolloverEvent && /*#__PURE__*/React.createElement("rect", {
2355
+ x: barX,
2356
+ y: scale.y(v),
2357
+ width: barWidth,
2358
+ height: values != null && values.length ? pointHeight * values.length : 0,
2359
+ stroke: color.defaults.BORDER_GRAY,
2360
+ strokeWidth: '4px',
2361
+ fill: 'transparent'
2362
+ }), values.map(index => CustomBarElement({
2363
+ index,
2364
+ pointDiameter,
2365
+ barX,
2366
+ barWidth,
2367
+ pointHeight,
2368
+ label,
2369
+ value,
2370
+ classes,
2371
+ scale
2372
+ })), correctness && correctness.value === 'incorrect' && (() => {
2373
+ const correctVal = parseFloat(correctData[index] && correctData[index].value);
2374
+ if (isNaN(correctVal)) return null;
2375
+ const selectedVal = v;
2376
+
2377
+ if (selectedVal > correctVal) {
2378
+ // selected is higher than correct: overlay the correct last segment
2379
+ const overlayValues = [];
2380
+
2381
+ for (let i = 0; i < correctVal; i++) {
2382
+ overlayValues.push(i);
2383
+ }
2384
+
2385
+ const lastIndexOfOverlay = overlayValues.length - 1;
2386
+ const lastOverlayValue = overlayValues[lastIndexOfOverlay];
2387
+ const barX = xBand(bandKey({
2388
+ label
2389
+ }, index));
2390
+ const barWidth = xBand.bandwidth();
2391
+ const pointHeight = size.height / max;
2392
+ const pointDiameter = (pointHeight > barWidth ? barWidth : pointHeight) * 0.8;
2393
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(CustomBarElement, {
2394
+ index: lastOverlayValue,
2395
+ pointDiameter: pointDiameter + 10 // increase point diameter for dotted line
2396
+ ,
2397
+ barX: barX,
2398
+ barWidth: barWidth,
2399
+ pointHeight: pointHeight,
2400
+ label: label,
2401
+ value: value,
2402
+ classes: classes,
2403
+ scale: scale,
2404
+ dottedOverline: true
2405
+ }), this.renderCorrectnessIcon(barX, barWidth, correctVal, correctness, classes, scale));
2406
+ } // selected is lower than correct, render missing segment below the correct bar
2407
+
2408
+
2409
+ const valuesToRender = [];
2410
+
2411
+ for (let i = selectedVal; i < correctVal; i++) {
2412
+ valuesToRender.push(i);
2413
+ }
2414
+
2415
+ return /*#__PURE__*/React.createElement(React.Fragment, null, valuesToRender.map(idx => CustomBarElement({
2416
+ index: idx,
2417
+ pointDiameter,
2418
+ barX,
2419
+ barWidth,
2420
+ pointHeight,
2421
+ label,
2422
+ value,
2423
+ classes,
2424
+ scale,
2425
+ dottedOverline: true
2426
+ })), this.renderCorrectnessIcon(barX, barWidth, correctVal, correctness, classes, scale));
2427
+ })(), /*#__PURE__*/React.createElement(Component, {
2428
+ x: barX,
2429
+ y: v,
2430
+ interactive: interactive,
2431
+ width: barWidth,
2432
+ onDrag: v => this.dragValue(value, v),
2433
+ onDragStop: this.dragStop,
2434
+ graphProps: graphProps,
2435
+ correctness: correctness,
2436
+ isHovered: isHovered,
2437
+ defineChart: defineChart,
2438
+ color: color.primaryDark(),
2439
+ isPlot: true
2440
+ })));
2441
+ }
2442
+
2443
+ }
2444
+ RawPlot.propTypes = {
2445
+ onChangeCategory: PropTypes.func,
2446
+ value: PropTypes.number,
2447
+ classes: PropTypes.object,
2448
+ label: PropTypes.string,
2449
+ xBand: PropTypes.func,
2450
+ index: PropTypes.number.isRequired,
2451
+ graphProps: types.GraphPropsType.isRequired,
2452
+ CustomBarElement: PropTypes.func,
2453
+ interactive: PropTypes.bool,
2454
+ correctness: PropTypes.shape({
2455
+ value: PropTypes.string,
2456
+ label: PropTypes.string
2457
+ })
2458
+ };
2459
+ const Bar = withStyles$1(theme => ({
2460
+ dot: {
2461
+ fill: color.visualElementsColors.PLOT_FILL_COLOR,
2462
+ '&.correct': correct('stroke'),
2463
+ '&.incorrect': incorrect('stroke')
2464
+ },
2465
+ dotColor: {
2466
+ fill: color.visualElementsColors.PLOT_FILL_COLOR,
2467
+ '&.correct': correct('fill'),
2468
+ '&.incorrect': incorrect('fill')
2469
+ },
2470
+ line: {
2471
+ stroke: color.visualElementsColors.PLOT_FILL_COLOR,
2472
+ '&.correct': correct('stroke'),
2473
+ '&.incorrect': incorrect('stroke')
2474
+ },
2475
+ correctIcon: {
2476
+ backgroundColor: color.correct()
2477
+ },
2478
+ incorrectIcon: {
2479
+ backgroundColor: color.incorrectWithIcon()
2480
+ },
2481
+ correctnessIcon: {
2482
+ borderRadius: theme.spacing.unit * 2,
2483
+ color: color.defaults.WHITE,
2484
+ fontSize: '16px',
2485
+ width: '16px',
2486
+ height: '16px',
2487
+ padding: '2px',
2488
+ border: `1px solid ${color.defaults.WHITE}`,
2489
+ stroke: 'initial',
2490
+ boxSizing: 'unset' // to override the default border-box in IBX
2491
+
2492
+ },
2493
+ smallIcon: {
2494
+ fontSize: '10px',
2495
+ width: '10px',
2496
+ height: '10px'
2497
+ }
2498
+ }))(RawPlot);
2499
+ class Plot extends React.Component {
2500
+ render() {
2501
+ const {
2502
+ data,
2503
+ graphProps,
2504
+ xBand,
2505
+ CustomBarElement,
2506
+ onChangeCategory,
2507
+ defineChart,
2508
+ correctData
2509
+ } = this.props;
2510
+ return /*#__PURE__*/React.createElement(Group, null, (data || []).map((d, index) => /*#__PURE__*/React.createElement(Bar, {
2511
+ value: d.value,
2512
+ label: d.label,
2513
+ interactive: defineChart || d.interactive,
2514
+ defineChart: defineChart,
2515
+ xBand: xBand,
2516
+ index: index,
2517
+ key: `bar-${d.label}-${d.value}-${index}`,
2518
+ onChangeCategory: category => onChangeCategory(index, category),
2519
+ graphProps: graphProps,
2520
+ CustomBarElement: CustomBarElement,
2521
+ correctness: d.correctness,
2522
+ correctData: correctData
2523
+ })));
2524
+ }
2525
+
2526
+ }
2527
+ Plot.propTypes = {
2528
+ data: PropTypes.array,
2529
+ onChangeCategory: PropTypes.func,
2530
+ xBand: PropTypes.func,
2531
+ graphProps: types.GraphPropsType.isRequired,
2532
+ defineChart: PropTypes.bool,
2533
+ CustomBarElement: PropTypes.func
2534
+ };
2535
+
2536
+ const CustomBarElement$1 = props => {
2537
+ const {
2538
+ index,
2539
+ pointDiameter,
2540
+ barX,
2541
+ barWidth,
2542
+ pointHeight,
2543
+ label,
2544
+ value,
2545
+ classes,
2546
+ scale,
2547
+ dottedOverline
2548
+ } = props;
2549
+ const r = pointDiameter / 2;
2550
+ const cx = barX + (barWidth - pointDiameter) / 2 + r;
2551
+ const cy = scale.y(index) - (pointHeight - pointDiameter) / 2 - r;
2552
+ const EXTRA_PADDING = 2;
2553
+ return dottedOverline ? /*#__PURE__*/React.createElement(Circle, {
2554
+ key: `point-${label}-${value}-${index}`,
2555
+ cx: cx,
2556
+ cy: cy,
2557
+ r: r + EXTRA_PADDING,
2558
+ strokeDasharray: "4,4",
2559
+ stroke: color.defaults.BORDER_GRAY,
2560
+ fill: "none"
2561
+ }) : /*#__PURE__*/React.createElement(Circle, {
2562
+ key: `point-${label}-${value}-${index}`,
2563
+ className: classes.dot,
2564
+ cx: cx,
2565
+ cy: cy,
2566
+ r: r
2567
+ });
2568
+ };
2569
+
2570
+ CustomBarElement$1.propTypes = {
2571
+ index: PropTypes.number,
2572
+ pointDiameter: PropTypes.number,
2573
+ barX: PropTypes.number,
2574
+ barWidth: PropTypes.number,
2575
+ pointHeight: PropTypes.number,
2576
+ value: PropTypes.number,
2577
+ label: PropTypes.string,
2578
+ classes: PropTypes.object,
2579
+ scale: PropTypes.object,
2580
+ dottedOverline: PropTypes.bool
2581
+ };
2582
+ class DotPlot extends React.Component {
2583
+ render() {
2584
+ const props = this.props;
2585
+ const {
2586
+ data,
2587
+ graphProps
2588
+ } = props;
2589
+ const {
2590
+ scale = {},
2591
+ size = {}
2592
+ } = graphProps || {};
2593
+ const xBand = dataToXBand(scale.x, data, size.width, 'dotPlot');
2594
+ return /*#__PURE__*/React.createElement(Plot, _extends({}, props, {
2595
+ xBand: xBand,
2596
+ CustomBarElement: CustomBarElement$1
2597
+ }));
2598
+ }
2599
+
2600
+ }
2601
+ DotPlot.propTypes = {
2602
+ data: PropTypes.array,
2603
+ onChange: PropTypes.func,
2604
+ graphProps: types.GraphPropsType.isRequired
2605
+ };
2606
+ var DotPlot$1 = (() => ({
2607
+ type: 'dotPlot',
2608
+ Component: DotPlot,
2609
+ name: 'Dot Plot'
2610
+ }));
2611
+
2612
+ const CustomBarElement = props => {
2613
+ const {
2614
+ index,
2615
+ pointDiameter,
2616
+ barX,
2617
+ barWidth,
2618
+ pointHeight,
2619
+ label,
2620
+ value,
2621
+ classes,
2622
+ scale,
2623
+ dottedOverline
2624
+ } = props;
2625
+ const x = barX + (barWidth - pointDiameter) / 2;
2626
+ const y = scale.y(index) - (pointHeight - pointDiameter) / 2;
2627
+ const EXTRA_PADDING = 2;
2628
+ return dottedOverline ? /*#__PURE__*/React.createElement("rect", {
2629
+ key: `point-${label}-${value}-${index}`,
2630
+ x: x - EXTRA_PADDING,
2631
+ y: y - pointDiameter - EXTRA_PADDING,
2632
+ width: pointDiameter + EXTRA_PADDING * 2,
2633
+ height: pointDiameter + EXTRA_PADDING * 2,
2634
+ strokeDasharray: "4,4",
2635
+ stroke: color.defaults.BORDER_GRAY,
2636
+ fill: "none"
2637
+ }) : /*#__PURE__*/React.createElement(Group, null, /*#__PURE__*/React.createElement(LinePath, {
2638
+ data: [{
2639
+ x,
2640
+ y
2641
+ }, {
2642
+ x: x + pointDiameter,
2643
+ y: y - pointDiameter
2644
+ }],
2645
+ key: `point-${label}-${value}-${index}-1`,
2646
+ className: classes.line,
2647
+ x: d => d.x,
2648
+ y: d => d.y,
2649
+ strokeWidth: pointDiameter / 5
2650
+ }), /*#__PURE__*/React.createElement(LinePath, {
2651
+ data: [{
2652
+ x,
2653
+ y: y - pointDiameter
2654
+ }, {
2655
+ x: x + pointDiameter,
2656
+ y
2657
+ }],
2658
+ key: `point-${label}-${value}-${index}-2`,
2659
+ className: classes.line,
2660
+ x: d => d.x,
2661
+ y: d => d.y,
2662
+ strokeWidth: pointDiameter / 5
2663
+ }));
2664
+ };
2665
+
2666
+ CustomBarElement.propTypes = {
2667
+ index: PropTypes.number,
2668
+ pointDiameter: PropTypes.number,
2669
+ barX: PropTypes.number,
2670
+ barWidth: PropTypes.number,
2671
+ pointHeight: PropTypes.number,
2672
+ value: PropTypes.number,
2673
+ label: PropTypes.string,
2674
+ classes: PropTypes.object,
2675
+ scale: PropTypes.object
2676
+ };
2677
+ class LinePlot extends React.Component {
2678
+ render() {
2679
+ const props = this.props;
2680
+ const {
2681
+ data,
2682
+ graphProps
2683
+ } = props;
2684
+ const {
2685
+ scale = {},
2686
+ size = {}
2687
+ } = graphProps || {};
2688
+ const xBand = dataToXBand(scale.x, data, size.width, 'linePlot');
2689
+ return /*#__PURE__*/React.createElement(Plot, _extends({}, props, {
2690
+ xBand: xBand,
2691
+ CustomBarElement: CustomBarElement
2692
+ }));
2693
+ }
2694
+
2695
+ }
2696
+ LinePlot.propTypes = {
2697
+ data: PropTypes.array,
2698
+ onChange: PropTypes.func,
2699
+ graphProps: types.GraphPropsType.isRequired
2700
+ };
2701
+ var LinePlot$1 = (() => ({
2702
+ type: 'linePlot',
2703
+ Component: LinePlot,
2704
+ name: 'Line Plot'
2705
+ }));
2706
+
2707
+ var chartTypes = {
2708
+ Bar: Bar$2,
2709
+ Histogram: Histogram$1,
2710
+ LineDot: LineDot$1,
2711
+ DotPlot: DotPlot$1,
2712
+ LinePlot: LinePlot$1,
2713
+ LineCross: LineCross$1
2714
+ };
2715
+
2716
+ const {
2717
+ translator: translator$2
2718
+ } = Translator;
2719
+ class ActionsButton extends React.Component {
2720
+ constructor(props) {
2721
+ super(props);
2722
+
2723
+ this.handleActionsClick = event => {
2724
+ this.setState({
2725
+ actionsAnchorEl: event.currentTarget
2726
+ });
2727
+ };
2728
+
2729
+ this.handleActionsClose = () => {
2730
+ this.setState({
2731
+ actionsAnchorEl: null
2732
+ });
2733
+ };
2734
+
2735
+ this.handleAddCategory = () => {
2736
+ const {
2737
+ addCategory
2738
+ } = this.props;
2739
+ addCategory();
2740
+ this.handleActionsClose();
2741
+ };
2742
+
2743
+ this.handleDeleteCategory = index => {
2744
+ const {
2745
+ deleteCategory
2746
+ } = this.props;
2747
+ deleteCategory(index);
2748
+ this.handleActionsClose();
2749
+ };
2750
+
2751
+ this.state = {
2752
+ actionsAnchorEl: null
2753
+ };
2754
+ }
2755
+
2756
+ render() {
2757
+ const {
2758
+ classes,
2759
+ categories,
2760
+ language
2761
+ } = this.props;
2762
+ return /*#__PURE__*/React.createElement("div", {
2763
+ className: classes.actions
2764
+ }, /*#__PURE__*/React.createElement("div", {
2765
+ role: "button",
2766
+ tabIndex: 0,
2767
+ className: classes.trigger,
2768
+ onClick: this.handleActionsClick
2769
+ }, "Actions"), /*#__PURE__*/React.createElement(Popover, {
2770
+ open: Boolean(this.state.actionsAnchorEl),
2771
+ anchorEl: this.state.actionsAnchorEl,
2772
+ onClose: this.handleActionsClose,
2773
+ anchorOrigin: {
2774
+ vertical: 'bottom',
2775
+ horizontal: 'left'
2776
+ },
2777
+ transformOrigin: {
2778
+ vertical: 'top',
2779
+ horizontal: 'left'
2780
+ }
2781
+ }, /*#__PURE__*/React.createElement(Paper, {
2782
+ className: classes.actionsPaper
2783
+ }, /*#__PURE__*/React.createElement(Button, {
2784
+ onClick: () => this.handleAddCategory()
2785
+ }, "+ ", translator$2.t('charting.add', {
2786
+ lng: language
2787
+ })), categories.length > 0 && categories.map((category, index) => category.deletable && !category.correctness && /*#__PURE__*/React.createElement(Button, {
2788
+ key: index,
2789
+ onClick: () => this.handleDeleteCategory(index)
2790
+ }, `${translator$2.t('charting.delete', {
2791
+ lng: language
2792
+ })} <${category.label || translator$2.t('charting.newLabel', {
2793
+ lng: language
2794
+ })}>`)))));
2795
+ }
2796
+
2797
+ }
2798
+ ActionsButton.propTypes = {
2799
+ classes: PropTypes.object.isRequired,
2800
+ addCategory: PropTypes.func.isRequired,
2801
+ deleteCategory: PropTypes.func.isRequired,
2802
+ language: PropTypes.string,
2803
+ categories: PropTypes.array
2804
+ };
2805
+
2806
+ const styles$3 = theme => ({
2807
+ actions: {
2808
+ alignSelf: 'flex-end'
2809
+ },
2810
+ trigger: {
2811
+ cursor: 'pointer',
2812
+ fontSize: theme.typography.fontSize,
2813
+ color: color.tertiary(),
2814
+ padding: theme.spacing.unit
2815
+ },
2816
+ actionsPaper: {
2817
+ padding: theme.spacing.unit,
2818
+ display: 'flex',
2819
+ flexDirection: 'column',
2820
+ gap: theme.spacing.unit,
2821
+ '& button': {
2822
+ textTransform: 'none',
2823
+ fontSize: theme.typography.fontSize,
2824
+ color: color.text(),
2825
+ justifyContent: 'flex-start'
2826
+ }
2827
+ }
2828
+ });
2829
+
2830
+ var ActionsButton$1 = withStyles(styles$3)(ActionsButton);
2831
+
2832
+ const {
2833
+ translator: translator$1
2834
+ } = Translator;
2835
+ const log = debug('pie-lib:charts:chart');
2836
+ class Chart extends React.Component {
2837
+ constructor(props) {
2838
+ super(props);
2839
+ this.state = {
2840
+ charts: [chartTypes.Bar(), chartTypes.Histogram(), chartTypes.LineDot(), chartTypes.LineCross(), chartTypes.DotPlot(), chartTypes.LinePlot()],
2841
+ autoFocus: false
2842
+ };
2843
+
2844
+ this.handleAlertDialog = (open, callback) => this.setState({
2845
+ dialog: {
2846
+ open
2847
+ }
2848
+ }, callback);
2849
+
2850
+ this.getChart = () => {
2851
+ const charts = this.props.charts || this.state.charts;
2852
+ let {
2853
+ chartType
2854
+ } = this.props;
2855
+ let ChartComponent = null;
2856
+ let chart = null;
2857
+
2858
+ if (chartType) {
2859
+ chart = charts && charts.find(chart => chart.type === chartType);
2860
+ ChartComponent = chart && chart.Component;
2861
+ } else {
2862
+ chart = charts && charts[0];
2863
+ ChartComponent = chart && chart.Component;
2864
+ chartType = chart && chart.type;
2865
+ }
2866
+
2867
+ return {
2868
+ type: chartType,
2869
+ ChartComponent
2870
+ };
2871
+ };
2872
+
2873
+ this.changeData = data => {
2874
+ const {
2875
+ onDataChange
2876
+ } = this.props;
2877
+ onDataChange(data);
2878
+ };
2879
+
2880
+ this.changeCategory = (index, newCategory) => {
2881
+ const integerIndex = parseInt(index, 10);
2882
+
2883
+ if (integerIndex >= 0) {
2884
+ const {
2885
+ data,
2886
+ onDataChange
2887
+ } = this.props;
2888
+ data[integerIndex] = _extends({}, data[integerIndex], newCategory);
2889
+ onDataChange(data);
2890
+ }
2891
+ };
2892
+
2893
+ this.addCategory = () => {
2894
+ const {
2895
+ onDataChange,
2896
+ data,
2897
+ categoryDefaultLabel,
2898
+ defineChart,
2899
+ categoryDefaults,
2900
+ language
2901
+ } = this.props;
2902
+
2903
+ if ((data || []).length > 19) {
2904
+ this.setState({
2905
+ dialog: {
2906
+ open: true,
2907
+ title: translator$1.t('common:warning', {
2908
+ lng: language
2909
+ }),
2910
+ text: translator$1.t('charting.reachedLimit_other', {
2911
+ count: 20,
2912
+ lng: language
2913
+ }),
2914
+ onConfirm: () => this.handleAlertDialog(false)
2915
+ }
2916
+ });
2917
+ } else {
2918
+ this.setState({
2919
+ autoFocus: true
2920
+ });
2921
+ onDataChange([...data, {
2922
+ inDefineChart: defineChart,
2923
+ label: categoryDefaultLabel || translator$1.t('charting.newLabel', {
2924
+ lng: language
2925
+ }),
2926
+ value: 0,
2927
+ deletable: true,
2928
+ editable: categoryDefaults ? categoryDefaults == null ? void 0 : categoryDefaults.editable : true,
2929
+ interactive: categoryDefaults ? categoryDefaults == null ? void 0 : categoryDefaults.interactive : true
2930
+ }]);
2931
+ }
2932
+ };
2933
+
2934
+ this.deleteCategory = index => {
2935
+ const {
2936
+ data,
2937
+ onDataChange
2938
+ } = this.props;
2939
+
2940
+ if (typeof index !== 'number' || index < 0) {
2941
+ return;
2942
+ }
2943
+
2944
+ if (data && data.length > 0) {
2945
+ onDataChange(data.filter((_, i) => i !== index));
2946
+ }
2947
+ };
2948
+
2949
+ this.getFilteredCategories = () => {
2950
+ const {
2951
+ data,
2952
+ defineChart
2953
+ } = this.props;
2954
+ return data ? data.map(d => _extends({}, d, {
2955
+ deletable: defineChart || d.deletable
2956
+ })) : [];
2957
+ };
2958
+
2959
+ this.resetAutoFocus = () => {
2960
+ this.setState({
2961
+ autoFocus: false
2962
+ });
2963
+ };
2964
+
2965
+ this.state = {
2966
+ dialog: {
2967
+ open: false
2968
+ },
2969
+ actionsAnchorEl: null
2970
+ };
2971
+ this.maskUid = this.generateMaskId();
2972
+ }
2973
+
2974
+ generateMaskId() {
2975
+ return 'chart-' + (Math.random() * 10000).toFixed();
2976
+ }
2977
+
2978
+ render() {
2979
+ const {
2980
+ classes,
2981
+ className,
2982
+ domain = {},
2983
+ range = {},
2984
+ chartingOptions,
2985
+ size,
2986
+ title,
2987
+ onChangeTitle,
2988
+ onChangeLabels,
2989
+ labelsPlaceholders,
2990
+ titlePlaceholder,
2991
+ addCategoryEnabled,
2992
+ changeInteractiveEnabled,
2993
+ changeEditableEnabled,
2994
+ showPixelGuides,
2995
+ error,
2996
+ mathMlOptions = {},
2997
+ language,
2998
+ labelsCharactersLimit,
2999
+ correctData
3000
+ } = this.props;
3001
+ let {
3002
+ chartType
3003
+ } = this.props;
3004
+ const {
3005
+ dialog
3006
+ } = this.state;
3007
+ const defineChart = this.props.defineChart || false;
3008
+ const {
3009
+ width,
3010
+ height
3011
+ } = size || {};
3012
+ const labels = {
3013
+ left: (range == null ? void 0 : range.label) || '',
3014
+ bottom: (domain == null ? void 0 : domain.label) || ''
3015
+ };
3016
+ const {
3017
+ ChartComponent
3018
+ } = this.getChart();
3019
+ const categories = this.getFilteredCategories();
3020
+ const correctValues = getDomainAndRangeByChartType(domain, range, chartType);
3021
+ const {
3022
+ verticalLines,
3023
+ horizontalLines,
3024
+ leftAxis
3025
+ } = getGridLinesAndAxisByChartType(correctValues.range, chartType);
3026
+ const common = {
3027
+ graphProps: createGraphProps(correctValues.domain, correctValues.range, size, () => this.rootNode)
3028
+ };
3029
+ log('[render] common:', common);
3030
+ const maskSize = {
3031
+ x: -10,
3032
+ y: -75,
3033
+ width: width + 20,
3034
+ height: height + 130
3035
+ };
3036
+ const {
3037
+ scale
3038
+ } = common.graphProps;
3039
+ const xBand = dataToXBand(scale.x, categories, width, chartType);
3040
+
3041
+ if (!ChartComponent) {
3042
+ return null;
3043
+ }
3044
+
3045
+ const bandWidth = xBand.bandwidth(); // for chartType "line", bandWidth will be 0, so we have to calculate it
3046
+
3047
+ const barWidth = bandWidth || scale.x(correctValues.domain.max) / categories.length;
3048
+ const increaseHeight = defineChart ? 160 : 60; // if there are many categories, we have to rotate their names in order to fit
3049
+ // and we have to add extra value on top of some items
3050
+
3051
+ const top = getTopPadding(barWidth);
3052
+ const rootCommon = cloneDeep(common);
3053
+ rootCommon.graphProps.size.height += top + increaseHeight;
3054
+ return /*#__PURE__*/React.createElement("div", {
3055
+ className: classNames(classes.chart, classes.chartBox, className)
3056
+ }, /*#__PURE__*/React.createElement(Root, _extends({
3057
+ title: title,
3058
+ onChangeTitle: onChangeTitle,
3059
+ disabledTitle: !defineChart,
3060
+ showTitle: true,
3061
+ showLabels: true,
3062
+ labels: labels,
3063
+ onChangeLabels: onChangeLabels,
3064
+ labelsPlaceholders: labelsPlaceholders,
3065
+ titlePlaceholder: titlePlaceholder,
3066
+ defineChart: defineChart,
3067
+ disabledLabels: !defineChart,
3068
+ isChart: true,
3069
+ showPixelGuides: showPixelGuides,
3070
+ rootRef: r => this.rootNode = r,
3071
+ mathMlOptions: mathMlOptions,
3072
+ labelsCharactersLimit: labelsCharactersLimit
3073
+ }, rootCommon), /*#__PURE__*/React.createElement(ChartGrid, _extends({}, common, {
3074
+ xBand: xBand,
3075
+ rowTickValues: horizontalLines,
3076
+ columnTickValues: verticalLines
3077
+ })), /*#__PURE__*/React.createElement(ChartAxes, _extends({
3078
+ autoFocus: this.state.autoFocus,
3079
+ onAutoFocusUsed: this.resetAutoFocus
3080
+ }, common, {
3081
+ defineChart: defineChart,
3082
+ categories: categories,
3083
+ xBand: xBand,
3084
+ leftAxis: leftAxis,
3085
+ onChange: this.changeData,
3086
+ onChangeCategory: this.changeCategory,
3087
+ chartingOptions: chartingOptions,
3088
+ changeInteractiveEnabled: changeInteractiveEnabled,
3089
+ changeEditableEnabled: changeEditableEnabled,
3090
+ top: top,
3091
+ error: error,
3092
+ showCorrectness: chartType === 'linePlot' || chartType === 'dotPlot'
3093
+ })), addCategoryEnabled ? /*#__PURE__*/React.createElement("foreignObject", {
3094
+ x: width,
3095
+ y: height - 16,
3096
+ width: width,
3097
+ height: height
3098
+ }, /*#__PURE__*/React.createElement("div", {
3099
+ xmlns: "http://www.w3.org/1999/xhtml",
3100
+ style: {
3101
+ display: 'flex',
3102
+ justifyContent: 'flex-start'
3103
+ }
3104
+ }, /*#__PURE__*/React.createElement(ActionsButton$1, {
3105
+ categories: categories,
3106
+ addCategory: this.addCategory,
3107
+ deleteCategory: this.deleteCategory,
3108
+ language: language
3109
+ }))) : null, /*#__PURE__*/React.createElement("mask", {
3110
+ id: `${this.maskUid}`
3111
+ }, /*#__PURE__*/React.createElement("rect", _extends({}, maskSize, {
3112
+ fill: "white"
3113
+ }))), /*#__PURE__*/React.createElement("g", {
3114
+ id: "marks",
3115
+ mask: `url('#${this.maskUid}')`
3116
+ }, /*#__PURE__*/React.createElement(ChartComponent, _extends({}, common, {
3117
+ data: categories,
3118
+ height: rootCommon.graphProps.size.height,
3119
+ defineChart: defineChart,
3120
+ onChange: this.changeData,
3121
+ onChangeCategory: this.changeCategory,
3122
+ correctData: correctData
3123
+ })))), /*#__PURE__*/React.createElement(AlertDialog, {
3124
+ open: dialog.open,
3125
+ title: dialog.title,
3126
+ text: dialog.text,
3127
+ onClose: dialog.onClose,
3128
+ onConfirm: dialog.onConfirm
3129
+ }));
3130
+ }
3131
+
3132
+ }
3133
+ Chart.propTypes = {
3134
+ classes: PropTypes.object.isRequired,
3135
+ className: PropTypes.string,
3136
+ chartType: PropTypes.string.isRequired,
3137
+ size: PropTypes.shape({
3138
+ width: PropTypes.number,
3139
+ height: PropTypes.number
3140
+ }),
3141
+ domain: PropTypes.shape({
3142
+ label: PropTypes.string,
3143
+ min: PropTypes.number,
3144
+ max: PropTypes.number,
3145
+ axisLabel: PropTypes.string
3146
+ }),
3147
+ data: PropTypes.arrayOf(PropTypes.shape({
3148
+ label: PropTypes.string,
3149
+ value: PropTypes.number
3150
+ })),
3151
+ range: PropTypes.shape({
3152
+ label: PropTypes.string,
3153
+ min: PropTypes.number,
3154
+ max: PropTypes.number,
3155
+ step: PropTypes.number,
3156
+ labelStep: PropTypes.number,
3157
+ axisLabel: PropTypes.string
3158
+ }),
3159
+ charts: PropTypes.array,
3160
+ labelsPlaceholders: PropTypes.object,
3161
+ title: PropTypes.string,
3162
+ titlePlaceholder: PropTypes.string,
3163
+ onDataChange: PropTypes.func,
3164
+ onChangeLabels: PropTypes.func,
3165
+ onChangeTitle: PropTypes.func,
3166
+ error: PropTypes.any,
3167
+ addCategoryEnabled: PropTypes.bool,
3168
+ showPixelGuides: PropTypes.bool,
3169
+ categoryDefaultLabel: PropTypes.string,
3170
+ categoryDefaults: PropTypes.object,
3171
+ defineChart: PropTypes.bool,
3172
+ theme: PropTypes.object,
3173
+ chartingOptions: PropTypes.object,
3174
+ changeInteractiveEnabled: PropTypes.bool,
3175
+ changeEditableEnabled: PropTypes.bool,
3176
+ language: PropTypes.string,
3177
+ mathMlOptions: PropTypes.object,
3178
+ labelsCharactersLimit: PropTypes.number,
3179
+ correctData: PropTypes.arrayOf(PropTypes.shape({
3180
+ label: PropTypes.string,
3181
+ value: PropTypes.number
3182
+ }))
3183
+ };
3184
+ Chart.defaultProps = {
3185
+ size: {
3186
+ width: 480,
3187
+ height: 480
3188
+ }
3189
+ };
3190
+
3191
+ const styles$2 = theme => ({
3192
+ graphBox: {
3193
+ transform: 'translate(60px, 35px)'
3194
+ },
3195
+ svg: {
3196
+ overflow: 'visible'
3197
+ },
3198
+ chartBox: {
3199
+ width: 'min-content'
3200
+ }
3201
+ });
3202
+
3203
+ var chart = withStyles(styles$2, {
3204
+ withTheme: true
3205
+ })(Chart);
3206
+
3207
+ const ChartType = withStyles(() => ({
3208
+ chartType: {
3209
+ width: '160px'
3210
+ },
3211
+ chartTypeLabel: {
3212
+ backgroundColor: 'transparent'
3213
+ }
3214
+ }))(({
3215
+ onChange,
3216
+ value,
3217
+ classes,
3218
+ availableChartTypes,
3219
+ chartTypeLabel
3220
+ }) => /*#__PURE__*/React.createElement("div", {
3221
+ className: classes.chartType
3222
+ }, /*#__PURE__*/React.createElement(FormControl, {
3223
+ variant: 'outlined',
3224
+ className: classes.chartType
3225
+ }, /*#__PURE__*/React.createElement(InputLabel, {
3226
+ id: "type-helper-label",
3227
+ className: classes.chartTypeLabel
3228
+ }, chartTypeLabel), /*#__PURE__*/React.createElement(Select, {
3229
+ labelId: "type-helper-label",
3230
+ value: value,
3231
+ onChange: onChange,
3232
+ input: /*#__PURE__*/React.createElement(OutlinedInput, {
3233
+ labelWidth: 75,
3234
+ name: "type"
3235
+ })
3236
+ }, (availableChartTypes == null ? void 0 : availableChartTypes.histogram) && /*#__PURE__*/React.createElement(MenuItem, {
3237
+ value: 'histogram'
3238
+ }, availableChartTypes.histogram), (availableChartTypes == null ? void 0 : availableChartTypes.bar) && /*#__PURE__*/React.createElement(MenuItem, {
3239
+ value: 'bar'
3240
+ }, availableChartTypes.bar), (availableChartTypes == null ? void 0 : availableChartTypes.lineDot) && /*#__PURE__*/React.createElement(MenuItem, {
3241
+ value: 'lineDot'
3242
+ }, availableChartTypes.lineDot), (availableChartTypes == null ? void 0 : availableChartTypes.lineCross) && /*#__PURE__*/React.createElement(MenuItem, {
3243
+ value: 'lineCross'
3244
+ }, availableChartTypes.lineCross), (availableChartTypes == null ? void 0 : availableChartTypes.dotPlot) && /*#__PURE__*/React.createElement(MenuItem, {
3245
+ value: 'dotPlot'
3246
+ }, availableChartTypes.dotPlot), (availableChartTypes == null ? void 0 : availableChartTypes.linePlot) && /*#__PURE__*/React.createElement(MenuItem, {
3247
+ value: 'linePlot'
3248
+ }, availableChartTypes.linePlot)))));
3249
+
3250
+ const resetValues = (data, updateModel, range, onChange, model) => {
3251
+ (data || []).forEach(d => {
3252
+ const d_value_scaled = Math.round(d.value * 10);
3253
+ const range_step_scaled = Math.round(range.step * 10);
3254
+ const remainder_scaled = d_value_scaled % range_step_scaled;
3255
+ const remainder = remainder_scaled / 10;
3256
+
3257
+ if (d.value > range.max || remainder !== 0) {
3258
+ d.value = 0;
3259
+ }
3260
+ });
3261
+
3262
+ if (updateModel) {
3263
+ onChange(_extends({}, model, {
3264
+ data
3265
+ }));
3266
+ }
3267
+ };
3268
+
3269
+ const ConfigureChartPanel = props => {
3270
+ const {
3271
+ classes,
3272
+ model,
3273
+ onChange,
3274
+ chartDimensions,
3275
+ gridValues = {},
3276
+ labelValues = {},
3277
+ studentNewCategoryDefaultLabel = {},
3278
+ availableChartTypes = {},
3279
+ chartTypeLabel
3280
+ } = props;
3281
+ const [alertDialog, setAlertDialog] = useState({
3282
+ open: false,
3283
+ title: '',
3284
+ text: '',
3285
+ onClose: null,
3286
+ onConfirm: null
3287
+ });
3288
+ const [open, setOpen] = useState(false);
3289
+ const [rangeKey, setRangeKey] = useState('');
3290
+ const [resetValue, setResetValue] = useState(0);
3291
+ const {
3292
+ range = {},
3293
+ correctAnswer,
3294
+ changeInteractiveEnabled,
3295
+ changeEditableEnabled
3296
+ } = model;
3297
+ const size = model.graph;
3298
+ const {
3299
+ showInConfigPanel,
3300
+ width,
3301
+ height
3302
+ } = chartDimensions || {};
3303
+ const widthConstraints = {
3304
+ min: width != null && width.min ? Math.max(50, width.min) : 50,
3305
+ max: width != null && width.max ? Math.min(700, width.max) : 700,
3306
+ step: (width == null ? void 0 : width.step) >= 1 ? Math.min(200, width.step) : 20
3307
+ };
3308
+ const heightConstraints = {
3309
+ min: height != null && height.min ? Math.max(400, height.min) : 400,
3310
+ max: height != null && height.max ? Math.min(700, height.max) : 700,
3311
+ step: (height == null ? void 0 : height.step) >= 1 ? Math.min(200, height.step) : 20
3312
+ };
3313
+ const gridOptions = gridValues && gridValues.range ? {
3314
+ customValues: gridValues.range
3315
+ } : {
3316
+ min: 0,
3317
+ max: 10000
3318
+ };
3319
+ const labelOptions = labelValues && labelValues.range ? {
3320
+ customValues: labelValues.range
3321
+ } : {
3322
+ min: 0,
3323
+ max: 10000
3324
+ };
3325
+ const stepConfig = /*#__PURE__*/React.createElement("div", {
3326
+ className: classes.rowView
3327
+ }, /*#__PURE__*/React.createElement(NumberTextFieldCustom, _extends({
3328
+ className: classes.mediumTextField,
3329
+ label: "Grid Interval",
3330
+ value: range.step,
3331
+ variant: "outlined",
3332
+ onChange: (e, v) => onRangeChanged('step', v, e)
3333
+ }, gridOptions)), /*#__PURE__*/React.createElement(NumberTextFieldCustom, _extends({
3334
+ className: classes.mediumTextField,
3335
+ label: 'Label Interval',
3336
+ value: range.labelStep,
3337
+ variant: 'outlined',
3338
+ onChange: (e, v) => onRangeChanged('labelStep', v, e)
3339
+ }, labelOptions)));
3340
+
3341
+ const handleAlertDialog = (openStatus, callback) => {
3342
+ setAlertDialog(prevState => _extends({}, prevState, {
3343
+ open: openStatus
3344
+ }), () => {
3345
+ if (callback) {
3346
+ callback();
3347
+ }
3348
+ });
3349
+ setOpen(openStatus);
3350
+ };
3351
+
3352
+ const setPropertiesToFalse = (data, property) => {
3353
+ return data.map(obj => {
3354
+ if (obj.hasOwnProperty(property)) {
3355
+ obj[property] = property == 'interactive' ? true : false;
3356
+ }
3357
+
3358
+ return obj;
3359
+ });
3360
+ };
3361
+
3362
+ const removeOutOfRangeValues = updateModel => {
3363
+ const {
3364
+ correctAnswer,
3365
+ data
3366
+ } = model;
3367
+
3368
+ if (changeInteractiveEnabled === false) {
3369
+ setPropertiesToFalse(data, 'interactive');
3370
+ }
3371
+
3372
+ if (changeEditableEnabled === false) {
3373
+ setPropertiesToFalse(data, 'editable');
3374
+ }
3375
+
3376
+ resetValues(data, updateModel, range, onChange, model);
3377
+ resetValues(correctAnswer.data, false, range, onChange, model);
3378
+ };
3379
+
3380
+ const setCategoryDefaultLabel = () => {
3381
+ const studentCategoryDefaultLabel = studentNewCategoryDefaultLabel == null ? void 0 : studentNewCategoryDefaultLabel.label;
3382
+ onChange(_extends({}, model, {
3383
+ studentCategoryDefaultLabel
3384
+ }));
3385
+ };
3386
+
3387
+ const rangeProps = chartType => {
3388
+ return chartType.includes('Plot') ? {
3389
+ min: 3,
3390
+ max: 10
3391
+ } : {
3392
+ min: 0.05,
3393
+ max: 10000
3394
+ };
3395
+ };
3396
+
3397
+ const onSizeChanged = (key, value) => {
3398
+ const graph = _extends({}, size, {
3399
+ [key]: value
3400
+ });
3401
+
3402
+ onChange(_extends({}, model, {
3403
+ graph
3404
+ }));
3405
+ };
3406
+
3407
+ const isOutOfRange = (data, range) => (data || []).find(d => d.value > range.max || d.value - range.step * Math.floor(d.value / range.step) !== 0);
3408
+
3409
+ const onRangeChanged = (key, value, e) => {
3410
+ // use reset values to restore range to initial values
3411
+ setResetValue(range[key]);
3412
+ setRangeKey(key);
3413
+ range[key] = value;
3414
+
3415
+ if (key === 'max' || key === 'step') {
3416
+ // check if current chart values are invalid for given range step/max
3417
+ const outOfRange = isOutOfRange(model.data, range) || isOutOfRange(model.correctAnswer.data, range);
3418
+
3419
+ if (outOfRange && JSON.stringify(e) !== '{}') {
3420
+ setOpen(true);
3421
+ } else {
3422
+ onChange(_extends({}, model, {
3423
+ range
3424
+ }));
3425
+ }
3426
+ } else {
3427
+ onChange(_extends({}, model, {
3428
+ range
3429
+ }));
3430
+ }
3431
+ };
3432
+
3433
+ useEffect(() => {
3434
+ removeOutOfRangeValues(true);
3435
+ setCategoryDefaultLabel();
3436
+ }, []);
3437
+ useEffect(() => {
3438
+ if (open) {
3439
+ setAlertDialog({
3440
+ open: true,
3441
+ title: 'Warning',
3442
+ text: 'This change will remove values defined for one or more categories',
3443
+ onConfirm: () => {
3444
+ removeOutOfRangeValues();
3445
+ handleAlertDialog(false, onChange(_extends({}, model, {
3446
+ range,
3447
+ correctAnswer
3448
+ })));
3449
+ },
3450
+ onClose: () => {
3451
+ range[rangeKey] = resetValue;
3452
+ handleAlertDialog(false);
3453
+ }
3454
+ });
3455
+ }
3456
+ }, [open]);
3457
+ const isValidPlot = range.step === 1 && range.labelStep === 1 && 3 <= range.max && range.max <= 10;
3458
+
3459
+ const getPlotConfiguration = () => {
3460
+ rangeProps.min = 3;
3461
+ rangeProps.max = 10;
3462
+ range.max = 10;
3463
+ range.step = 1;
3464
+ range.labelStep = 1;
3465
+ onChange(_extends({}, model, {
3466
+ range
3467
+ }));
3468
+ };
3469
+
3470
+ const onChartTypeChange = chartType => {
3471
+ if (chartType.includes('Plot')) {
3472
+ // The selected chart type does not support the current chart configuration
3473
+ if (!isValidPlot) {
3474
+ setAlertDialog({
3475
+ open: true,
3476
+ title: 'Warning',
3477
+ text: 'The selected chart type does not support the current chart configuration. Reset chart configuration?',
3478
+ onConfirm: () => {
3479
+ getPlotConfiguration();
3480
+ removeOutOfRangeValues();
3481
+ handleAlertDialog(false, onChange(_extends({}, model, {
3482
+ range,
3483
+ chartType
3484
+ })));
3485
+ },
3486
+ onClose: () => {
3487
+ handleAlertDialog(false);
3488
+ }
3489
+ });
3490
+ return;
3491
+ }
3492
+
3493
+ rangeProps.min = 3;
3494
+ rangeProps.max = 10;
3495
+ onChange(_extends({}, model, {
3496
+ chartType
3497
+ }));
3498
+ return;
3499
+ }
3500
+
3501
+ onChange(_extends({}, model, {
3502
+ chartType
3503
+ }));
3504
+ };
3505
+
3506
+ return /*#__PURE__*/React.createElement("div", {
3507
+ className: classes.wrapper
3508
+ }, /*#__PURE__*/React.createElement(Typography, {
3509
+ variant: 'subtitle1'
3510
+ }, "Configure Chart"), /*#__PURE__*/React.createElement("div", {
3511
+ className: classes.content
3512
+ }, /*#__PURE__*/React.createElement("div", {
3513
+ className: classes.rowView
3514
+ }, /*#__PURE__*/React.createElement(ChartType, {
3515
+ value: model.chartType,
3516
+ onChange: e => onChartTypeChange(e.target.value),
3517
+ availableChartTypes: availableChartTypes,
3518
+ chartTypeLabel: chartTypeLabel
3519
+ }), /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
3520
+ className: classes.mediumTextField,
3521
+ label: "Max Value",
3522
+ value: range.max,
3523
+ min: rangeProps(model.chartType).min,
3524
+ max: rangeProps(model.chartType).max,
3525
+ variant: "outlined",
3526
+ onChange: (e, v) => onRangeChanged('max', v, e)
3527
+ })), !model.chartType.includes('Plot') && stepConfig, showInConfigPanel && /*#__PURE__*/React.createElement("div", {
3528
+ className: classes.dimensions
3529
+ }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Typography, null, "Dimensions(px)")), /*#__PURE__*/React.createElement("div", {
3530
+ className: classes.columnView
3531
+ }, /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
3532
+ className: classes.textField,
3533
+ label: 'Width',
3534
+ value: size.width,
3535
+ min: widthConstraints.min,
3536
+ max: widthConstraints.max,
3537
+ step: widthConstraints.step,
3538
+ variant: 'outlined',
3539
+ onChange: (e, v) => onSizeChanged('width', v)
3540
+ }), /*#__PURE__*/React.createElement(Typography, {
3541
+ className: classes.disabled
3542
+ }, "Min 50, Max 700")), /*#__PURE__*/React.createElement("div", {
3543
+ className: classes.columnView
3544
+ }, /*#__PURE__*/React.createElement(NumberTextFieldCustom, {
3545
+ className: classes.textField,
3546
+ label: 'Height',
3547
+ value: size.height,
3548
+ min: heightConstraints.min,
3549
+ max: heightConstraints.max,
3550
+ step: heightConstraints.step,
3551
+ variant: 'outlined',
3552
+ onChange: (e, v) => onSizeChanged('height', v)
3553
+ }), /*#__PURE__*/React.createElement(Typography, {
3554
+ className: classes.disabled
3555
+ }, "Min 400, Max 700")))), /*#__PURE__*/React.createElement(AlertDialog, {
3556
+ open: alertDialog.open,
3557
+ title: alertDialog.title,
3558
+ text: alertDialog.text,
3559
+ onClose: alertDialog.onClose,
3560
+ onConfirm: alertDialog.onConfirm
3561
+ }));
3562
+ };
3563
+
3564
+ ConfigureChartPanel.propTypes = {
3565
+ classes: PropTypes.object,
3566
+ chartDimensions: PropTypes.object,
3567
+ domain: PropTypes.object,
3568
+ gridValues: PropTypes.object,
3569
+ labelValues: PropTypes.object,
3570
+ model: PropTypes.object,
3571
+ onChange: PropTypes.func,
3572
+ range: PropTypes.object,
3573
+ chartDimension: PropTypes.object,
3574
+ size: PropTypes.object,
3575
+ studentNewCategoryDefaultLabel: PropTypes.object,
3576
+ availableChartTypes: PropTypes.object,
3577
+ chartTypeLabel: PropTypes.string
3578
+ };
3579
+
3580
+ const styles$1 = theme => ({
3581
+ wrapper: {
3582
+ width: '450px'
3583
+ },
3584
+ content: {
3585
+ display: 'flex',
3586
+ flexDirection: 'column',
3587
+ width: '100%',
3588
+ marginTop: theme.spacing.unit * 3
3589
+ },
3590
+ columnView: {
3591
+ display: 'flex',
3592
+ flexDirection: 'column',
3593
+ alignItems: 'center'
3594
+ },
3595
+ rowView: {
3596
+ display: 'flex',
3597
+ justifyContent: 'space-around',
3598
+ alignItems: 'center'
3599
+ },
3600
+ textField: {
3601
+ width: '130px',
3602
+ margin: `${theme.spacing.unit}px ${theme.spacing.unit / 2}px`
3603
+ },
3604
+ mediumTextField: {
3605
+ width: '160px',
3606
+ margin: `${theme.spacing.unit}px ${theme.spacing.unit / 2}px`
3607
+ },
3608
+ largeTextField: {
3609
+ width: '230px',
3610
+ margin: `${theme.spacing.unit}px ${theme.spacing.unit / 2}px`
3611
+ },
3612
+ text: {
3613
+ fontStyle: 'italic',
3614
+ margin: `${theme.spacing.unit}px 0`
3615
+ },
3616
+ dimensions: {
3617
+ display: 'flex',
3618
+ justifyContent: 'space-between',
3619
+ alignItems: 'center',
3620
+ margin: `${theme.spacing.unit * 3}px 0`
3621
+ },
3622
+ disabled: {
3623
+ color: color.disabled()
3624
+ }
3625
+ });
3626
+
3627
+ var chartSetup = withStyles(styles$1)(ConfigureChartPanel);
3628
+
3629
+ const styles = theme => ({
3630
+ container: {
3631
+ backgroundColor: color.defaults.WHITE,
3632
+ padding: theme.spacing.unit * 2,
3633
+ width: '355px',
3634
+ boxShadow: 'inset 0px 1px 5px 0px #9297A6',
3635
+ display: 'flex',
3636
+ flexDirection: 'column',
3637
+ gap: '16px'
3638
+ },
3639
+ row: {
3640
+ display: 'flex',
3641
+ alignItems: 'center',
3642
+ gap: '12px'
3643
+ },
3644
+ title: {
3645
+ fontSize: theme.typography.h6.fontSize,
3646
+ fontWeight: '700'
3647
+ },
3648
+ smallText: {
3649
+ marginLeft: '2px'
3650
+ },
3651
+ correctIcon: {
3652
+ backgroundColor: color.correct(),
3653
+ borderRadius: theme.spacing.unit * 2,
3654
+ color: color.defaults.WHITE
3655
+ },
3656
+ incorrectIcon: {
3657
+ backgroundColor: color.incorrectWithIcon(),
3658
+ borderRadius: theme.spacing.unit * 2,
3659
+ color: color.defaults.WHITE
3660
+ },
3661
+ lastRow: {
3662
+ marginLeft: '3px',
3663
+ display: 'flex',
3664
+ alignItems: 'center',
3665
+ gap: '12px'
3666
+ }
3667
+ });
3668
+
3669
+ const {
3670
+ translator
3671
+ } = Translator;
3672
+
3673
+ const KeyLegend = ({
3674
+ classes,
3675
+ language
3676
+ }) => /*#__PURE__*/React.createElement("div", {
3677
+ className: classes.container
3678
+ }, /*#__PURE__*/React.createElement("div", {
3679
+ className: classes.title
3680
+ }, "Key"), /*#__PURE__*/React.createElement("div", {
3681
+ className: classes.row
3682
+ }, /*#__PURE__*/React.createElement(Close, {
3683
+ className: classes.incorrectIcon
3684
+ }), /*#__PURE__*/React.createElement("div", {
3685
+ className: classes.text
3686
+ }, translator.t('charting.keyLegend.incorrectAnswer', {
3687
+ lng: language
3688
+ }))), /*#__PURE__*/React.createElement("div", {
3689
+ className: classes.row
3690
+ }, /*#__PURE__*/React.createElement(Check, {
3691
+ className: classes.correctIcon
3692
+ }), /*#__PURE__*/React.createElement("div", {
3693
+ className: classes.text
3694
+ }, translator.t('charting.keyLegend.correctAnswer', {
3695
+ lng: language
3696
+ }))), /*#__PURE__*/React.createElement("div", {
3697
+ className: classes.lastRow
3698
+ }, /*#__PURE__*/React.createElement(Check, {
3699
+ className: classes.correctIcon,
3700
+ fontSize: 'small'
3701
+ }), /*#__PURE__*/React.createElement("div", {
3702
+ className: classes.smallText
3703
+ }, translator.t('charting.keyLegend.correctKeyAnswer', {
3704
+ lng: language
3705
+ }))));
3706
+
3707
+ KeyLegend.propTypes = {
3708
+ classes: PropTypes.object.isRequired,
3709
+ language: PropTypes.string
3710
+ };
3711
+ var keyLegend = withStyles(styles)(KeyLegend);
3712
+
3713
+ export { chart as Chart, ChartType, chartSetup as ConfigureChartPanel, keyLegend as KeyLegend, chartTypes };
3714
+ //# sourceMappingURL=index.js.map