@pie-lib/plot 2.23.1-next.0 → 2.24.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 +1278 -0
- package/esm/index.js.map +1 -0
- package/package.json +12 -5
package/esm/index.js
ADDED
|
@@ -0,0 +1,1278 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { mouse, select, clientPoint } from 'd3-selection';
|
|
5
|
+
import cn from 'classnames';
|
|
6
|
+
import { color, Readable } from '@pie-lib/render-ui';
|
|
7
|
+
import EditableHtml from '@pie-lib/editable-html';
|
|
8
|
+
import invariant from 'invariant';
|
|
9
|
+
import range from 'lodash/range';
|
|
10
|
+
import Point from '@mapbox/point-geometry';
|
|
11
|
+
import head from 'lodash/head';
|
|
12
|
+
import tail from 'lodash/tail';
|
|
13
|
+
import isEqual from 'lodash/isEqual';
|
|
14
|
+
import Draggable, { DraggableCore } from 'react-draggable';
|
|
15
|
+
import debug from 'debug';
|
|
16
|
+
import isFunction from 'lodash/isFunction';
|
|
17
|
+
import { scaleLinear } from 'd3-scale';
|
|
18
|
+
|
|
19
|
+
function _extends() {
|
|
20
|
+
_extends = Object.assign || function (target) {
|
|
21
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
22
|
+
var source = arguments[i];
|
|
23
|
+
|
|
24
|
+
for (var key in source) {
|
|
25
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
26
|
+
target[key] = source[key];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return target;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return _extends.apply(this, arguments);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
38
|
+
if (source == null) return {};
|
|
39
|
+
var target = {};
|
|
40
|
+
var sourceKeys = Object.keys(source);
|
|
41
|
+
var key, i;
|
|
42
|
+
|
|
43
|
+
for (i = 0; i < sourceKeys.length; i++) {
|
|
44
|
+
key = sourceKeys[i];
|
|
45
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
46
|
+
target[key] = source[key];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return target;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const BaseDomainRangeType = {
|
|
53
|
+
min: PropTypes.number.isRequired,
|
|
54
|
+
max: PropTypes.number.isRequired,
|
|
55
|
+
step: PropTypes.number
|
|
56
|
+
};
|
|
57
|
+
const DomainType = PropTypes.shape(BaseDomainRangeType);
|
|
58
|
+
const RangeType = PropTypes.shape(BaseDomainRangeType);
|
|
59
|
+
const SizeType = PropTypes.shape({
|
|
60
|
+
width: PropTypes.number.isRequired,
|
|
61
|
+
height: PropTypes.number.isRequired
|
|
62
|
+
});
|
|
63
|
+
const PointType = PropTypes.shape({
|
|
64
|
+
x: PropTypes.number.isRequired,
|
|
65
|
+
y: PropTypes.number.isRequired
|
|
66
|
+
});
|
|
67
|
+
const ChildrenType = PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired;
|
|
68
|
+
const ScaleType = PropTypes.shape({
|
|
69
|
+
x: PropTypes.func.isRequired,
|
|
70
|
+
y: PropTypes.func.isRequired
|
|
71
|
+
});
|
|
72
|
+
const SnapType = PropTypes.shape({
|
|
73
|
+
x: PropTypes.func.isRequired,
|
|
74
|
+
y: PropTypes.func.isRequired
|
|
75
|
+
});
|
|
76
|
+
const GraphPropsType = PropTypes.shape({
|
|
77
|
+
scale: ScaleType.isRequired,
|
|
78
|
+
snap: SnapType.isRequired,
|
|
79
|
+
domain: DomainType.isRequired,
|
|
80
|
+
range: RangeType.isRequired,
|
|
81
|
+
size: SizeType.isRequired
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
var types = /*#__PURE__*/Object.freeze({
|
|
85
|
+
__proto__: null,
|
|
86
|
+
BaseDomainRangeType: BaseDomainRangeType,
|
|
87
|
+
ChildrenType: ChildrenType,
|
|
88
|
+
DomainType: DomainType,
|
|
89
|
+
GraphPropsType: GraphPropsType,
|
|
90
|
+
PointType: PointType,
|
|
91
|
+
RangeType: RangeType,
|
|
92
|
+
ScaleType: ScaleType,
|
|
93
|
+
SizeType: SizeType,
|
|
94
|
+
SnapType: SnapType
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const xy = (x, y) => ({
|
|
98
|
+
x,
|
|
99
|
+
y
|
|
100
|
+
});
|
|
101
|
+
const buildSizeArray = (size, padding) => {
|
|
102
|
+
padding = Number.isFinite(padding) ? Math.max(padding, 0) : 0;
|
|
103
|
+
invariant(padding < size, 'padding must be less than size');
|
|
104
|
+
const half = Math.round(padding * 0.5);
|
|
105
|
+
return [half, size - half];
|
|
106
|
+
};
|
|
107
|
+
const tickCount = (min, max, step) => {
|
|
108
|
+
invariant(min < max, 'min must be less than max');
|
|
109
|
+
const size = Math.abs(min - max);
|
|
110
|
+
return Math.round(size / step);
|
|
111
|
+
};
|
|
112
|
+
function getInterval(domain, ticks) {
|
|
113
|
+
const {
|
|
114
|
+
min,
|
|
115
|
+
max
|
|
116
|
+
} = domain;
|
|
117
|
+
const {
|
|
118
|
+
major,
|
|
119
|
+
minor
|
|
120
|
+
} = ticks;
|
|
121
|
+
|
|
122
|
+
if (min >= max) {
|
|
123
|
+
throw new Error(`min is > max: ${min} > ${max}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const distance = max - min;
|
|
127
|
+
const minorTicks = minor > 0 ? minor + 1 : 1;
|
|
128
|
+
const normalizedMajor = major - 1;
|
|
129
|
+
|
|
130
|
+
if (isNaN(normalizedMajor)) {
|
|
131
|
+
throw new Error('Tick Frequency must be 2 or higher');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (normalizedMajor <= 0) {
|
|
135
|
+
throw new Error('Tick Frequency must be 2 or higher');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const divider = normalizedMajor * minorTicks;
|
|
139
|
+
const raw = distance / divider;
|
|
140
|
+
return parseFloat(Number(raw).toFixed(4));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const mkRange = (min, max, interval) => {
|
|
144
|
+
const raw = range(min, max, interval);
|
|
145
|
+
/* Fix the last step due to rounding errors */
|
|
146
|
+
|
|
147
|
+
raw.splice(raw.length, 1, max);
|
|
148
|
+
return raw;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
function snapTo(min, max, interval, value) {
|
|
152
|
+
if (value >= max) {
|
|
153
|
+
return max;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (value <= min) {
|
|
157
|
+
return min;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let rng = mkRange(min, max, interval);
|
|
161
|
+
rng = rng.filter(v => {
|
|
162
|
+
return Math.abs(value - v) <= interval;
|
|
163
|
+
});
|
|
164
|
+
return rng.length && rng.reduce((prev, curr) => {
|
|
165
|
+
const currentDistance = Math.abs(curr - value);
|
|
166
|
+
const previousDistance = Math.abs(prev - value);
|
|
167
|
+
return currentDistance <= previousDistance ? curr : prev;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function buildTickModel(domain, ticks, interval, scaleFn) {
|
|
171
|
+
const rng = mkRange(domain.min, domain.max, interval);
|
|
172
|
+
return rng.map((r, index) => {
|
|
173
|
+
const isMajor = index % (ticks.minor + 1) === 0;
|
|
174
|
+
return {
|
|
175
|
+
value: r,
|
|
176
|
+
major: isMajor,
|
|
177
|
+
x: scaleFn(r)
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
const polygonToArea = points => {
|
|
182
|
+
const h = head(points);
|
|
183
|
+
const area = {
|
|
184
|
+
left: h.x,
|
|
185
|
+
top: h.y,
|
|
186
|
+
bottom: h.y,
|
|
187
|
+
right: h.x
|
|
188
|
+
};
|
|
189
|
+
return tail(points).reduce((a, p) => {
|
|
190
|
+
a.left = Math.min(a.left, p.x);
|
|
191
|
+
a.top = Math.max(a.top, p.y);
|
|
192
|
+
a.bottom = Math.min(a.bottom, p.y);
|
|
193
|
+
a.right = Math.max(a.right, p.x);
|
|
194
|
+
return a;
|
|
195
|
+
}, area);
|
|
196
|
+
};
|
|
197
|
+
const lineToArea = (from, to) => {
|
|
198
|
+
const left = Math.min(from.x, to.x);
|
|
199
|
+
const top = Math.max(from.y, to.y);
|
|
200
|
+
const bottom = Math.min(from.y, to.y);
|
|
201
|
+
const right = Math.max(from.x, to.x);
|
|
202
|
+
return {
|
|
203
|
+
left,
|
|
204
|
+
top,
|
|
205
|
+
bottom,
|
|
206
|
+
right
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
const bounds = (area, domain, range) => {
|
|
210
|
+
return {
|
|
211
|
+
left: domain.min - area.left,
|
|
212
|
+
right: Math.abs(area.right - domain.max),
|
|
213
|
+
top: Math.abs(area.top - range.max),
|
|
214
|
+
bottom: range.min - area.bottom
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
const point = o => new Point(o.x, o.y);
|
|
218
|
+
const getDelta = (from, to) => {
|
|
219
|
+
return point(to).sub(point(from));
|
|
220
|
+
};
|
|
221
|
+
const bandKey = (d, index) => `${index}-${d.label || '-'}`;
|
|
222
|
+
const isDomainRangeEqual = (graphProps, nextGraphProps) => {
|
|
223
|
+
return isEqual(graphProps.domain, nextGraphProps.domain) && isEqual(graphProps.range, nextGraphProps.range);
|
|
224
|
+
}; // findLongestWord is also used in graphing
|
|
225
|
+
|
|
226
|
+
const findLongestWord = label => {
|
|
227
|
+
let longestWord = (label || '').replace(/<[^>]+>/g, '').split(' ').sort((a, b) => b.length - a.length);
|
|
228
|
+
return longestWord[0].length;
|
|
229
|
+
}; // amountToIncreaseWidth is also used in graphing
|
|
230
|
+
|
|
231
|
+
const amountToIncreaseWidth = longestWord => {
|
|
232
|
+
if (!longestWord) {
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return longestWord * 20;
|
|
237
|
+
};
|
|
238
|
+
const extractTextFromHTML = htmlString => {
|
|
239
|
+
var _doc$body;
|
|
240
|
+
|
|
241
|
+
const parser = new DOMParser();
|
|
242
|
+
const doc = parser == null ? void 0 : parser.parseFromString(htmlString, 'text/html');
|
|
243
|
+
return (doc == null ? void 0 : (_doc$body = doc.body) == null ? void 0 : _doc$body.textContent) || '';
|
|
244
|
+
};
|
|
245
|
+
const isEmptyObject = obj => {
|
|
246
|
+
return obj && Object.keys(obj).length === 0 && obj.constructor === Object;
|
|
247
|
+
};
|
|
248
|
+
const isEmptyString = str => {
|
|
249
|
+
return typeof str === 'string' && str.trim() === '';
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
var utils = /*#__PURE__*/Object.freeze({
|
|
253
|
+
__proto__: null,
|
|
254
|
+
amountToIncreaseWidth: amountToIncreaseWidth,
|
|
255
|
+
bandKey: bandKey,
|
|
256
|
+
bounds: bounds,
|
|
257
|
+
buildSizeArray: buildSizeArray,
|
|
258
|
+
buildTickModel: buildTickModel,
|
|
259
|
+
extractTextFromHTML: extractTextFromHTML,
|
|
260
|
+
findLongestWord: findLongestWord,
|
|
261
|
+
getDelta: getDelta,
|
|
262
|
+
getInterval: getInterval,
|
|
263
|
+
isDomainRangeEqual: isDomainRangeEqual,
|
|
264
|
+
isEmptyObject: isEmptyObject,
|
|
265
|
+
isEmptyString: isEmptyString,
|
|
266
|
+
lineToArea: lineToArea,
|
|
267
|
+
point: point,
|
|
268
|
+
polygonToArea: polygonToArea,
|
|
269
|
+
snapTo: snapTo,
|
|
270
|
+
tickCount: tickCount,
|
|
271
|
+
xy: xy
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const LabelComponent = props => {
|
|
275
|
+
const {
|
|
276
|
+
classes,
|
|
277
|
+
disabledLabel,
|
|
278
|
+
graphHeight,
|
|
279
|
+
graphWidth,
|
|
280
|
+
isChartBottomLabel,
|
|
281
|
+
isDefineChartBottomLabel,
|
|
282
|
+
isChartLeftLabel,
|
|
283
|
+
isDefineChartLeftLabel,
|
|
284
|
+
placeholder,
|
|
285
|
+
text,
|
|
286
|
+
side,
|
|
287
|
+
onChange,
|
|
288
|
+
mathMlOptions = {},
|
|
289
|
+
charactersLimit,
|
|
290
|
+
titleHeight
|
|
291
|
+
} = props;
|
|
292
|
+
const [rotatedToHorizontal, setRotatedToHorizontal] = useState(false);
|
|
293
|
+
const activePlugins = ['bold', 'italic', 'underline', 'strikethrough', 'math' // 'languageCharacters'
|
|
294
|
+
];
|
|
295
|
+
const isChart = isChartBottomLabel || isChartLeftLabel || isDefineChartBottomLabel || isDefineChartLeftLabel;
|
|
296
|
+
const chartValue = side === 'left' && isDefineChartLeftLabel && graphHeight - 220;
|
|
297
|
+
const defaultStyle = {
|
|
298
|
+
width: chartValue || (side === 'left' || side === 'right' ? graphHeight - 8 : graphWidth - 8),
|
|
299
|
+
top: chartValue || isChartLeftLabel && `${graphHeight - 70}px` || side === 'left' && `${graphHeight - 8}px` || isChartBottomLabel && `${graphHeight - 60 + titleHeight}px` || side === 'bottom' && `${graphHeight - 120 + titleHeight}px` || 0,
|
|
300
|
+
left: side === 'right' && `${graphWidth - 8}px` || (isDefineChartLeftLabel || isDefineChartBottomLabel) && '40px' || isChartBottomLabel && '-10px' || 0
|
|
301
|
+
};
|
|
302
|
+
const rotatedStyle = {
|
|
303
|
+
width: graphWidth - 8,
|
|
304
|
+
top: side === 'right' && `${graphHeight - 22}px` || 0,
|
|
305
|
+
left: 0
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const rotateLabel = () => !disabledLabel && (side === 'left' || side === 'right') && setRotatedToHorizontal(true);
|
|
309
|
+
|
|
310
|
+
return /*#__PURE__*/React.createElement(Readable, {
|
|
311
|
+
false: true
|
|
312
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
313
|
+
className: cn(isChart ? classes.chartLabel : classes.axisLabel, {
|
|
314
|
+
[classes.rotateLeftLabel]: side === 'left' && !rotatedToHorizontal,
|
|
315
|
+
[classes.rotateRightLabel]: side === 'right' && !rotatedToHorizontal,
|
|
316
|
+
[classes.editLabel]: rotatedToHorizontal,
|
|
317
|
+
[classes.customBottom]: isChartBottomLabel || isDefineChartBottomLabel,
|
|
318
|
+
[classes.displayNone]: disabledLabel && !isChart && isEmptyString(extractTextFromHTML(text))
|
|
319
|
+
}),
|
|
320
|
+
style: rotatedToHorizontal ? rotatedStyle : defaultStyle,
|
|
321
|
+
onClick: rotateLabel
|
|
322
|
+
}, disabledLabel ? /*#__PURE__*/React.createElement("div", {
|
|
323
|
+
className: classes.disabledLabel,
|
|
324
|
+
dangerouslySetInnerHTML: {
|
|
325
|
+
__html: text || ''
|
|
326
|
+
}
|
|
327
|
+
}) : /*#__PURE__*/React.createElement(EditableHtml, {
|
|
328
|
+
markup: text || '',
|
|
329
|
+
onChange: onChange,
|
|
330
|
+
placeholder: !disabledLabel && placeholder,
|
|
331
|
+
toolbarOpts: {
|
|
332
|
+
position: side === 'bottom' ? 'top' : 'bottom',
|
|
333
|
+
noPadding: true,
|
|
334
|
+
noBorder: true
|
|
335
|
+
},
|
|
336
|
+
disableScrollbar: true,
|
|
337
|
+
activePlugins: activePlugins,
|
|
338
|
+
onDone: () => setRotatedToHorizontal(false),
|
|
339
|
+
mathMlOptions: mathMlOptions,
|
|
340
|
+
charactersLimit: charactersLimit
|
|
341
|
+
})));
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
LabelComponent.propTypes = {
|
|
345
|
+
classes: PropTypes.object,
|
|
346
|
+
disabledLabel: PropTypes.bool,
|
|
347
|
+
graphHeight: PropTypes.number,
|
|
348
|
+
graphWidth: PropTypes.number,
|
|
349
|
+
isChartBottomLabel: PropTypes.bool,
|
|
350
|
+
isDefineChartBottomLabel: PropTypes.bool,
|
|
351
|
+
isChartLeftLabel: PropTypes.bool,
|
|
352
|
+
isDefineChartLeftLabel: PropTypes.bool,
|
|
353
|
+
placeholder: PropTypes.string,
|
|
354
|
+
text: PropTypes.string,
|
|
355
|
+
side: PropTypes.string,
|
|
356
|
+
onChange: PropTypes.func,
|
|
357
|
+
mathMlOptions: PropTypes.object,
|
|
358
|
+
charactersLimit: PropTypes.number,
|
|
359
|
+
titleHeight: PropTypes.number
|
|
360
|
+
};
|
|
361
|
+
var Label = withStyles(theme => ({
|
|
362
|
+
label: {
|
|
363
|
+
fill: color.secondary()
|
|
364
|
+
},
|
|
365
|
+
axisLabel: {
|
|
366
|
+
fontSize: theme.typography.fontSize - 2,
|
|
367
|
+
textAlign: 'center',
|
|
368
|
+
margin: theme.spacing.unit / 2,
|
|
369
|
+
padding: `${theme.spacing.unit / 2}px 0`
|
|
370
|
+
},
|
|
371
|
+
chartLabel: {
|
|
372
|
+
fontSize: theme.typography.fontSize + 2,
|
|
373
|
+
textAlign: 'center',
|
|
374
|
+
margin: theme.spacing.unit / 2,
|
|
375
|
+
padding: `${theme.spacing.unit / 2}px 0`
|
|
376
|
+
},
|
|
377
|
+
disabledLabel: {
|
|
378
|
+
pointerEvents: 'none',
|
|
379
|
+
width: '100%'
|
|
380
|
+
},
|
|
381
|
+
editLabel: {
|
|
382
|
+
position: 'absolute',
|
|
383
|
+
backgroundColor: 'white',
|
|
384
|
+
borderRadius: '4px',
|
|
385
|
+
boxShadow: '0px 5px 8px rgba(0, 0, 0, 0.15)',
|
|
386
|
+
zIndex: 10
|
|
387
|
+
},
|
|
388
|
+
rotateLeftLabel: {
|
|
389
|
+
'-webkit-transform': 'rotate(-90deg)',
|
|
390
|
+
transformOrigin: '0 0',
|
|
391
|
+
transformStyle: 'preserve-3d',
|
|
392
|
+
position: 'absolute'
|
|
393
|
+
},
|
|
394
|
+
rotateRightLabel: {
|
|
395
|
+
'-webkit-transform': 'rotate(90deg)',
|
|
396
|
+
transformOrigin: '0 0',
|
|
397
|
+
transformStyle: 'preserve-3d',
|
|
398
|
+
position: 'absolute'
|
|
399
|
+
},
|
|
400
|
+
customBottom: {
|
|
401
|
+
position: 'absolute'
|
|
402
|
+
},
|
|
403
|
+
displayNone: {
|
|
404
|
+
display: 'none'
|
|
405
|
+
}
|
|
406
|
+
}))(LabelComponent);
|
|
407
|
+
|
|
408
|
+
class Root extends React.Component {
|
|
409
|
+
constructor(props) {
|
|
410
|
+
super(props);
|
|
411
|
+
|
|
412
|
+
this.mouseMove = g => {
|
|
413
|
+
const {
|
|
414
|
+
graphProps,
|
|
415
|
+
onMouseMove
|
|
416
|
+
} = this.props;
|
|
417
|
+
|
|
418
|
+
if (!onMouseMove) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const {
|
|
423
|
+
scale,
|
|
424
|
+
snap
|
|
425
|
+
} = graphProps;
|
|
426
|
+
const coords = mouse(g._groups[0][0]);
|
|
427
|
+
const x = scale.x.invert(coords[0]);
|
|
428
|
+
const y = scale.y.invert(coords[1]);
|
|
429
|
+
const snapped = {
|
|
430
|
+
x: snap.x(x),
|
|
431
|
+
y: snap.y(y)
|
|
432
|
+
};
|
|
433
|
+
onMouseMove(snapped);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
this.onChangeLabel = (newValue, side) => {
|
|
437
|
+
const {
|
|
438
|
+
labels,
|
|
439
|
+
onChangeLabels,
|
|
440
|
+
isChart
|
|
441
|
+
} = this.props;
|
|
442
|
+
|
|
443
|
+
if (!onChangeLabels) {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (isChart) {
|
|
448
|
+
if (side === 'left') {
|
|
449
|
+
onChangeLabels('range', newValue);
|
|
450
|
+
} else {
|
|
451
|
+
onChangeLabels('domain', newValue);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
onChangeLabels(_extends({}, labels, {
|
|
458
|
+
[side]: newValue
|
|
459
|
+
}));
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
this.measureTitleHeight = () => {
|
|
463
|
+
const titleElement = this.titleRef;
|
|
464
|
+
|
|
465
|
+
if (titleElement) {
|
|
466
|
+
const titleHeight = titleElement.clientHeight;
|
|
467
|
+
this.setState({
|
|
468
|
+
titleHeight,
|
|
469
|
+
prevTitle: this.props.title
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
this.handleKeyDown = () => {
|
|
475
|
+
setTimeout(this.measureTitleHeight, 0);
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
this.state = {
|
|
479
|
+
titleHeight: 0
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
componentDidMount() {
|
|
484
|
+
const g = select(this.g);
|
|
485
|
+
g.on('mousemove', this.mouseMove.bind(this, g));
|
|
486
|
+
this.measureTitleHeight();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
componentWillUnmount() {
|
|
490
|
+
const g = select(this.g);
|
|
491
|
+
g.on('mousemove', null);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
componentDidUpdate(prevProps) {
|
|
495
|
+
if (prevProps.title !== this.props.title) {
|
|
496
|
+
this.measureTitleHeight();
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
render() {
|
|
501
|
+
const {
|
|
502
|
+
disabledTitle,
|
|
503
|
+
disabledLabels,
|
|
504
|
+
labels,
|
|
505
|
+
labelsPlaceholders,
|
|
506
|
+
titlePlaceholder,
|
|
507
|
+
graphProps,
|
|
508
|
+
children,
|
|
509
|
+
classes,
|
|
510
|
+
defineChart,
|
|
511
|
+
onChangeTitle,
|
|
512
|
+
isChart,
|
|
513
|
+
showLabels,
|
|
514
|
+
showPixelGuides,
|
|
515
|
+
showTitle,
|
|
516
|
+
title,
|
|
517
|
+
rootRef,
|
|
518
|
+
mathMlOptions = {},
|
|
519
|
+
labelsCharactersLimit
|
|
520
|
+
} = this.props;
|
|
521
|
+
const {
|
|
522
|
+
size: {
|
|
523
|
+
width = 500,
|
|
524
|
+
height = 500
|
|
525
|
+
},
|
|
526
|
+
domain,
|
|
527
|
+
range
|
|
528
|
+
} = graphProps;
|
|
529
|
+
const topPadding = 40;
|
|
530
|
+
const leftPadding = isEmptyString(extractTextFromHTML(labels == null ? void 0 : labels.left)) && isEmptyObject(labelsPlaceholders) ? 48 : 70;
|
|
531
|
+
const rightPadding = isEmptyString(extractTextFromHTML(labels == null ? void 0 : labels.right)) && isEmptyObject(labelsPlaceholders) ? 48 : 70;
|
|
532
|
+
const finalWidth = width + leftPadding + rightPadding + (domain.padding || 0) * 2;
|
|
533
|
+
const finalHeight = height + topPadding * 2 + (range.padding || 0) * 2;
|
|
534
|
+
const activeTitlePlugins = ['bold', 'italic', 'underline', 'superscript', 'subscript', 'strikethrough', 'math' // 'languageCharacters'
|
|
535
|
+
];
|
|
536
|
+
const actualHeight = defineChart && showPixelGuides ? height - 160 : height;
|
|
537
|
+
const nbOfVerticalLines = parseInt(width / 100);
|
|
538
|
+
const nbOfHorizontalLines = parseInt(actualHeight / 100);
|
|
539
|
+
const sideGridlinesPadding = parseInt(actualHeight % 100);
|
|
540
|
+
const {
|
|
541
|
+
titleHeight
|
|
542
|
+
} = this.state;
|
|
543
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
544
|
+
className: classes.root
|
|
545
|
+
}, showPixelGuides && /*#__PURE__*/React.createElement("div", {
|
|
546
|
+
className: classes.topPixelGuides,
|
|
547
|
+
style: {
|
|
548
|
+
marginLeft: isChart ? 80 : showLabels ? 30 : 10
|
|
549
|
+
}
|
|
550
|
+
}, [...Array(nbOfVerticalLines + 1).keys()].map(value => /*#__PURE__*/React.createElement(Readable, {
|
|
551
|
+
false: true,
|
|
552
|
+
key: `top-guide-${value}`
|
|
553
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
554
|
+
className: classes.topPixelIndicator
|
|
555
|
+
}, /*#__PURE__*/React.createElement("div", null, value * 100, "px"), /*#__PURE__*/React.createElement("div", null, "|"))))), showTitle && (disabledTitle ? /*#__PURE__*/React.createElement("div", {
|
|
556
|
+
ref: r => this.titleRef = r,
|
|
557
|
+
style: _extends({}, isChart && {
|
|
558
|
+
width: finalWidth
|
|
559
|
+
}, isEmptyString(extractTextFromHTML(title)) && {
|
|
560
|
+
display: 'none'
|
|
561
|
+
}),
|
|
562
|
+
className: cn(isChart ? classes.chartTitle : classes.graphTitle, classes.disabledTitle),
|
|
563
|
+
dangerouslySetInnerHTML: {
|
|
564
|
+
__html: title || ''
|
|
565
|
+
}
|
|
566
|
+
}) : /*#__PURE__*/React.createElement("div", {
|
|
567
|
+
ref: r => this.titleRef = r
|
|
568
|
+
}, /*#__PURE__*/React.createElement(EditableHtml, {
|
|
569
|
+
style: isChart && {
|
|
570
|
+
width: finalWidth
|
|
571
|
+
},
|
|
572
|
+
className: cn({
|
|
573
|
+
[classes.rightMargin]: showPixelGuides
|
|
574
|
+
}, isChart ? classes.chartTitle : classes.graphTitle),
|
|
575
|
+
markup: title || '',
|
|
576
|
+
onChange: onChangeTitle,
|
|
577
|
+
placeholder: defineChart && titlePlaceholder || !disabledTitle && 'Click here to add a title for this graph',
|
|
578
|
+
toolbarOpts: {
|
|
579
|
+
noPadding: true,
|
|
580
|
+
noBorder: true
|
|
581
|
+
},
|
|
582
|
+
activePlugins: activeTitlePlugins,
|
|
583
|
+
disableScrollbar: true,
|
|
584
|
+
onKeyDown: this.handleKeyDown
|
|
585
|
+
}))), showLabels && !isChart && /*#__PURE__*/React.createElement(Label, {
|
|
586
|
+
side: "top",
|
|
587
|
+
text: labels.top,
|
|
588
|
+
disabledLabel: disabledLabels,
|
|
589
|
+
placeholder: labelsPlaceholders == null ? void 0 : labelsPlaceholders.top,
|
|
590
|
+
graphHeight: finalHeight,
|
|
591
|
+
graphWidth: finalWidth,
|
|
592
|
+
onChange: value => this.onChangeLabel(value, 'top'),
|
|
593
|
+
mathMlOptions: mathMlOptions,
|
|
594
|
+
charactersLimit: labelsCharactersLimit
|
|
595
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
596
|
+
className: classes.wrapper
|
|
597
|
+
}, showLabels && /*#__PURE__*/React.createElement(Label, {
|
|
598
|
+
side: "left",
|
|
599
|
+
text: labels.left,
|
|
600
|
+
disabledLabel: disabledLabels,
|
|
601
|
+
placeholder: labelsPlaceholders == null ? void 0 : labelsPlaceholders.left,
|
|
602
|
+
graphHeight: finalHeight,
|
|
603
|
+
graphWidth: finalWidth,
|
|
604
|
+
isChartLeftLabel: isChart && !defineChart,
|
|
605
|
+
isDefineChartLeftLabel: isChart && defineChart,
|
|
606
|
+
onChange: value => this.onChangeLabel(value, 'left'),
|
|
607
|
+
mathMlOptions: mathMlOptions,
|
|
608
|
+
charactersLimit: labelsCharactersLimit
|
|
609
|
+
}), /*#__PURE__*/React.createElement("svg", {
|
|
610
|
+
width: finalWidth,
|
|
611
|
+
height: finalHeight,
|
|
612
|
+
className: defineChart ? classes.defineChart : classes.chart
|
|
613
|
+
}, /*#__PURE__*/React.createElement("g", {
|
|
614
|
+
ref: r => {
|
|
615
|
+
this.g = r;
|
|
616
|
+
|
|
617
|
+
if (rootRef) {
|
|
618
|
+
rootRef(r);
|
|
619
|
+
}
|
|
620
|
+
},
|
|
621
|
+
className: classes.graphBox,
|
|
622
|
+
transform: `translate(${leftPadding + (domain.padding || 0)}, ${topPadding + (range.padding || 0)})`
|
|
623
|
+
}, children)), showLabels && !isChart && /*#__PURE__*/React.createElement(Label, {
|
|
624
|
+
side: "right",
|
|
625
|
+
text: labels.right,
|
|
626
|
+
disabledLabel: disabledLabels,
|
|
627
|
+
placeholder: labelsPlaceholders == null ? void 0 : labelsPlaceholders.right,
|
|
628
|
+
graphHeight: finalHeight,
|
|
629
|
+
graphWidth: finalWidth,
|
|
630
|
+
onChange: value => this.onChangeLabel(value, 'right'),
|
|
631
|
+
mathMlOptions: mathMlOptions,
|
|
632
|
+
charactersLimit: labelsCharactersLimit
|
|
633
|
+
}), showPixelGuides && /*#__PURE__*/React.createElement("div", {
|
|
634
|
+
className: classes.sidePixelGuides,
|
|
635
|
+
style: {
|
|
636
|
+
paddingTop: sideGridlinesPadding,
|
|
637
|
+
marginTop: 31
|
|
638
|
+
}
|
|
639
|
+
}, [...Array(nbOfHorizontalLines + 1).keys()].reverse().map(value => /*#__PURE__*/React.createElement(Readable, {
|
|
640
|
+
false: true,
|
|
641
|
+
key: `top-guide-${value}`
|
|
642
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
643
|
+
className: classes.sidePixelIndicator
|
|
644
|
+
}, "\u2501 ", value * 100, "px"))))), showLabels && /*#__PURE__*/React.createElement(Label, {
|
|
645
|
+
side: "bottom",
|
|
646
|
+
text: labels.bottom,
|
|
647
|
+
disabledLabel: disabledLabels,
|
|
648
|
+
placeholder: labelsPlaceholders == null ? void 0 : labelsPlaceholders.bottom,
|
|
649
|
+
graphHeight: finalHeight,
|
|
650
|
+
graphWidth: finalWidth,
|
|
651
|
+
titleHeight: titleHeight,
|
|
652
|
+
isChartBottomLabel: isChart && !defineChart,
|
|
653
|
+
isDefineChartBottomLabel: isChart && defineChart,
|
|
654
|
+
onChange: value => this.onChangeLabel(value, 'bottom'),
|
|
655
|
+
mathMlOptions: mathMlOptions,
|
|
656
|
+
charactersLimit: labelsCharactersLimit
|
|
657
|
+
}));
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
} // use default color theme style to avoid color contrast issues
|
|
661
|
+
|
|
662
|
+
Root.propTypes = {
|
|
663
|
+
title: PropTypes.string,
|
|
664
|
+
children: ChildrenType,
|
|
665
|
+
defineChart: PropTypes.bool,
|
|
666
|
+
disabledLabels: PropTypes.bool,
|
|
667
|
+
disabledTitle: PropTypes.bool,
|
|
668
|
+
graphProps: GraphPropsType.isRequired,
|
|
669
|
+
isChart: PropTypes.bool,
|
|
670
|
+
labels: PropTypes.object,
|
|
671
|
+
labelsPlaceholders: PropTypes.object,
|
|
672
|
+
onChangeTitle: PropTypes.func,
|
|
673
|
+
onMouseMove: PropTypes.func,
|
|
674
|
+
classes: PropTypes.object.isRequired,
|
|
675
|
+
showLabels: PropTypes.bool,
|
|
676
|
+
showTitle: PropTypes.bool,
|
|
677
|
+
showPixelGuides: PropTypes.bool,
|
|
678
|
+
rootRef: PropTypes.func,
|
|
679
|
+
onChangeLabels: PropTypes.func,
|
|
680
|
+
titlePlaceholder: PropTypes.string,
|
|
681
|
+
mathMlOptions: PropTypes.object,
|
|
682
|
+
labelsCharactersLimit: PropTypes.number
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
const styles = theme => ({
|
|
686
|
+
root: {
|
|
687
|
+
border: `solid 1px ${color.primaryLight()}`,
|
|
688
|
+
color: color.defaults.TEXT,
|
|
689
|
+
backgroundColor: theme.palette.common.white,
|
|
690
|
+
touchAction: 'none',
|
|
691
|
+
position: 'relative',
|
|
692
|
+
boxSizing: 'unset' // to override the default border-box in IBX that breaks the component width layout
|
|
693
|
+
|
|
694
|
+
},
|
|
695
|
+
wrapper: {
|
|
696
|
+
display: 'flex',
|
|
697
|
+
position: 'relative'
|
|
698
|
+
},
|
|
699
|
+
svg: {},
|
|
700
|
+
defineChart: {
|
|
701
|
+
paddingLeft: '50px',
|
|
702
|
+
overflow: 'visible'
|
|
703
|
+
},
|
|
704
|
+
chart: {
|
|
705
|
+
overflow: 'visible'
|
|
706
|
+
},
|
|
707
|
+
graphBox: {
|
|
708
|
+
cursor: 'pointer',
|
|
709
|
+
userSelect: 'none'
|
|
710
|
+
},
|
|
711
|
+
graphTitle: {
|
|
712
|
+
color: color.defaults.TEXT,
|
|
713
|
+
fontSize: theme.typography.fontSize + 2,
|
|
714
|
+
padding: `${theme.spacing.unit * 1.5}px ${theme.spacing.unit / 2}px 0`,
|
|
715
|
+
textAlign: 'center'
|
|
716
|
+
},
|
|
717
|
+
chartTitle: {
|
|
718
|
+
color: color.defaults.TEXT,
|
|
719
|
+
fontSize: theme.typography.fontSize + 4,
|
|
720
|
+
padding: `${theme.spacing.unit * 1.5}px ${theme.spacing.unit / 2}px 0`,
|
|
721
|
+
textAlign: 'center'
|
|
722
|
+
},
|
|
723
|
+
disabledTitle: {
|
|
724
|
+
pointerEvents: 'none'
|
|
725
|
+
},
|
|
726
|
+
rightMargin: {
|
|
727
|
+
marginRight: '74px'
|
|
728
|
+
},
|
|
729
|
+
topPixelGuides: {
|
|
730
|
+
display: 'flex',
|
|
731
|
+
paddingTop: '6px'
|
|
732
|
+
},
|
|
733
|
+
topPixelIndicator: {
|
|
734
|
+
display: 'flex',
|
|
735
|
+
flexDirection: 'column',
|
|
736
|
+
alignItems: 'center',
|
|
737
|
+
width: '100px',
|
|
738
|
+
pointerEvents: 'none',
|
|
739
|
+
userSelect: 'none'
|
|
740
|
+
},
|
|
741
|
+
sidePixelGuides: {
|
|
742
|
+
width: '70px',
|
|
743
|
+
display: 'flex',
|
|
744
|
+
flexDirection: 'column',
|
|
745
|
+
marginRight: '6px'
|
|
746
|
+
},
|
|
747
|
+
sidePixelIndicator: {
|
|
748
|
+
textAlign: 'right',
|
|
749
|
+
height: '20px',
|
|
750
|
+
pointerEvents: 'none',
|
|
751
|
+
userSelect: 'none',
|
|
752
|
+
'&:not(:last-child)': {
|
|
753
|
+
marginBottom: '80px'
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
var root = withStyles(styles)(Root);
|
|
759
|
+
|
|
760
|
+
class LocalDraggable extends Draggable {
|
|
761
|
+
componentWillReceiveProps(next) {
|
|
762
|
+
super.componentWillReceiveProps(next); //Remove the x/y state as these values have now been updated and will come through as props.
|
|
763
|
+
|
|
764
|
+
this.setState({
|
|
765
|
+
x: 0,
|
|
766
|
+
y: 0
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const _excluded = ["disabled"];
|
|
773
|
+
const log$1 = debug('pie-lib:plot:grid-draggable');
|
|
774
|
+
const deltaFn = (scale, snap, val) => delta => {
|
|
775
|
+
const normalized = delta + scale(0);
|
|
776
|
+
const inverted = scale.invert(normalized);
|
|
777
|
+
const fixDecimalsArithmetic = snap(val + inverted).toFixed(4) * 1000 / 1000;
|
|
778
|
+
return fixDecimalsArithmetic;
|
|
779
|
+
};
|
|
780
|
+
/**
|
|
781
|
+
* Creates a Component that is draggable, within a bounded grid.
|
|
782
|
+
* @param {*} opts
|
|
783
|
+
*/
|
|
784
|
+
|
|
785
|
+
const gridDraggable = opts => Comp => {
|
|
786
|
+
var _class;
|
|
787
|
+
|
|
788
|
+
invariant(!!opts && isFunction(opts.fromDelta) && isFunction(opts.bounds) && isFunction(opts.anchorPoint), 'You must supply an object with: { anchorPoint: Function, fromDelta: Function, bounds: Function }');
|
|
789
|
+
return _class = class GridDraggable extends React.Component {
|
|
790
|
+
constructor(...args) {
|
|
791
|
+
super(...args);
|
|
792
|
+
|
|
793
|
+
this.grid = () => {
|
|
794
|
+
const {
|
|
795
|
+
graphProps
|
|
796
|
+
} = this.props;
|
|
797
|
+
const {
|
|
798
|
+
scale,
|
|
799
|
+
domain,
|
|
800
|
+
range
|
|
801
|
+
} = graphProps;
|
|
802
|
+
return {
|
|
803
|
+
x: scale.x(domain.step) - scale.x(0),
|
|
804
|
+
y: scale.y(range.step) - scale.y(0)
|
|
805
|
+
};
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
this.onStart = e => {
|
|
809
|
+
const {
|
|
810
|
+
onDragStart
|
|
811
|
+
} = this.props;
|
|
812
|
+
|
|
813
|
+
if (document.activeElement) {
|
|
814
|
+
document.activeElement.blur();
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
this.setState({
|
|
818
|
+
startX: e.clientX,
|
|
819
|
+
startY: e.clientY
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
if (onDragStart) {
|
|
823
|
+
onDragStart();
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
this.position = () => {
|
|
828
|
+
const {
|
|
829
|
+
x,
|
|
830
|
+
y
|
|
831
|
+
} = opts.anchorPoint(this.props);
|
|
832
|
+
const {
|
|
833
|
+
graphProps
|
|
834
|
+
} = this.props;
|
|
835
|
+
const {
|
|
836
|
+
scale,
|
|
837
|
+
snap
|
|
838
|
+
} = graphProps;
|
|
839
|
+
return {
|
|
840
|
+
anchorPoint: {
|
|
841
|
+
x,
|
|
842
|
+
y
|
|
843
|
+
},
|
|
844
|
+
x: deltaFn(scale.x, snap.x, x),
|
|
845
|
+
y: deltaFn(scale.y, snap.y, y)
|
|
846
|
+
};
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
this.tiny = (key, event) => {
|
|
850
|
+
const K = key.toUpperCase();
|
|
851
|
+
const end = event[`client${K}`];
|
|
852
|
+
const start = this.state[`start${K}`];
|
|
853
|
+
const delta = Math.abs(end - start);
|
|
854
|
+
const out = delta < Math.abs(this.grid()[key]) / 10;
|
|
855
|
+
log$1('[tiny] key: ', key, 'delta: ', delta, 'out: ', out);
|
|
856
|
+
return out;
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
this.getScaledBounds = () => {
|
|
860
|
+
const bounds = opts.bounds(this.props, this.props.graphProps);
|
|
861
|
+
log$1('bounds: ', bounds);
|
|
862
|
+
const grid = this.grid();
|
|
863
|
+
const scaled = {
|
|
864
|
+
left: bounds.left / grid.interval * grid.x,
|
|
865
|
+
right: bounds.right / grid.interval * grid.x,
|
|
866
|
+
top: bounds.top / grid.interval * grid.y,
|
|
867
|
+
bottom: bounds.bottom / grid.interval * grid.y
|
|
868
|
+
};
|
|
869
|
+
log$1('[getScaledBounds]: ', scaled);
|
|
870
|
+
return scaled;
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
this.getClientPoint = (node, event) => {
|
|
874
|
+
if (!node || !event) {
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
const svg = node.ownerSVGElement || node;
|
|
879
|
+
|
|
880
|
+
if (svg && svg.createSVGPoint) {
|
|
881
|
+
let point = svg.createSVGPoint(); // Check if it's a touch event and use the first touch point
|
|
882
|
+
|
|
883
|
+
if (event.touches && event.touches.length > 0) {
|
|
884
|
+
const touch = event.touches[0];
|
|
885
|
+
point.x = touch.clientX;
|
|
886
|
+
point.y = touch.clientY;
|
|
887
|
+
} else {
|
|
888
|
+
// Fall back to mouse event properties
|
|
889
|
+
point.x = event.clientX;
|
|
890
|
+
point.y = event.clientY;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (node.getScreenCTM) {
|
|
894
|
+
point = point.matrixTransform(node.getScreenCTM().inverse());
|
|
895
|
+
return [point.x, point.y];
|
|
896
|
+
} else {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const rect = node.getBoundingClientRect();
|
|
902
|
+
|
|
903
|
+
if (rect) {
|
|
904
|
+
return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
|
|
905
|
+
} else {
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
this.skipDragOutsideOfBounds = (dd, e, graphProps) => {
|
|
911
|
+
// Ignore drag movement outside of the domain and range.
|
|
912
|
+
const rootNode = graphProps.getRootNode();
|
|
913
|
+
const clientPoint = this.getClientPoint(rootNode, e);
|
|
914
|
+
|
|
915
|
+
if (clientPoint === null) {
|
|
916
|
+
return true; // Indicate that the drag is outside of bounds
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const [rawX, rawY] = clientPoint;
|
|
920
|
+
const {
|
|
921
|
+
scale,
|
|
922
|
+
domain,
|
|
923
|
+
range
|
|
924
|
+
} = graphProps;
|
|
925
|
+
let x = scale.x.invert(rawX);
|
|
926
|
+
let y = scale.y.invert(rawY);
|
|
927
|
+
const xOutside = dd.deltaX > 0 && x < domain.min || dd.deltaX < 0 && x > domain.max;
|
|
928
|
+
const yOutside = dd.deltaY > 0 && y > range.max || dd.deltaY < 0 && y < range.min;
|
|
929
|
+
return xOutside || yOutside;
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
this.onDrag = (e, dd) => {
|
|
933
|
+
const {
|
|
934
|
+
onDrag,
|
|
935
|
+
graphProps
|
|
936
|
+
} = this.props;
|
|
937
|
+
|
|
938
|
+
if (!onDrag) {
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
const bounds = this.getScaledBounds();
|
|
943
|
+
|
|
944
|
+
if (dd.deltaX < 0 && dd.deltaX < bounds.left) {
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
if (dd.deltaX > 0 && dd.deltaX > bounds.right) {
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (dd.deltaY < 0 && dd.deltaY < bounds.top) {
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
if (dd.deltaY > 0 && dd.deltaY > bounds.bottom) {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (this.skipDragOutsideOfBounds(dd, e, graphProps)) {
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
const dragArg = this.applyDelta({
|
|
965
|
+
x: dd.deltaX,
|
|
966
|
+
y: dd.deltaY
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
if (dragArg !== undefined || dragArg !== null) {
|
|
970
|
+
onDrag(dragArg);
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
this.getDelta = point => {
|
|
975
|
+
const pos = this.position();
|
|
976
|
+
const p = {
|
|
977
|
+
x: pos.x(point.x),
|
|
978
|
+
y: pos.y(point.y)
|
|
979
|
+
};
|
|
980
|
+
return getDelta(pos.anchorPoint, p);
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
this.applyDelta = point => {
|
|
984
|
+
const delta = this.getDelta(point);
|
|
985
|
+
log$1('[applyDelta] delta:', delta);
|
|
986
|
+
return opts.fromDelta(this.props, delta);
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
this.onStop = (e, dd) => {
|
|
990
|
+
log$1('[onStop] dd:', dd);
|
|
991
|
+
const {
|
|
992
|
+
onDragStop,
|
|
993
|
+
onClick
|
|
994
|
+
} = this.props;
|
|
995
|
+
|
|
996
|
+
if (onDragStop) {
|
|
997
|
+
onDragStop();
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
log$1('[onStop] lastX/Y: ', dd.lastX, dd.lastY);
|
|
1001
|
+
const isClick = this.tiny('x', e) && this.tiny('y', e);
|
|
1002
|
+
|
|
1003
|
+
if (isClick) {
|
|
1004
|
+
if (onClick) {
|
|
1005
|
+
log$1('call onClick');
|
|
1006
|
+
this.setState({
|
|
1007
|
+
startX: null
|
|
1008
|
+
});
|
|
1009
|
+
const {
|
|
1010
|
+
graphProps
|
|
1011
|
+
} = this.props;
|
|
1012
|
+
const {
|
|
1013
|
+
scale,
|
|
1014
|
+
snap
|
|
1015
|
+
} = graphProps;
|
|
1016
|
+
const [rawX, rawY] = clientPoint(e.target, e);
|
|
1017
|
+
let x = scale.x.invert(rawX);
|
|
1018
|
+
let y = scale.y.invert(rawY);
|
|
1019
|
+
x = snap.x(x);
|
|
1020
|
+
y = snap.y(y);
|
|
1021
|
+
onClick({
|
|
1022
|
+
x,
|
|
1023
|
+
y
|
|
1024
|
+
});
|
|
1025
|
+
return false;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
this.setState({
|
|
1030
|
+
startX: null,
|
|
1031
|
+
startY: null
|
|
1032
|
+
}); // return false to prevent state updates in the underlying draggable - a move will have triggered an update already.
|
|
1033
|
+
|
|
1034
|
+
return false;
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
render() {
|
|
1039
|
+
const _this$props = this.props,
|
|
1040
|
+
{
|
|
1041
|
+
disabled
|
|
1042
|
+
} = _this$props,
|
|
1043
|
+
rest = _objectWithoutPropertiesLoose(_this$props, _excluded);
|
|
1044
|
+
|
|
1045
|
+
const grid = this.grid(); // prevent the text select icon from rendering.
|
|
1046
|
+
|
|
1047
|
+
const onMouseDown = e => e.nativeEvent.preventDefault();
|
|
1048
|
+
/**
|
|
1049
|
+
* TODO: This shouldnt be necessary, we should be able to use the r-d classnames.
|
|
1050
|
+
* But they aren't being unset. If we continue with this lib, we'll have to fix this.
|
|
1051
|
+
*/
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
const isDragging = this.state ? !!this.state.startX : false;
|
|
1055
|
+
return /*#__PURE__*/React.createElement(DraggableCore, {
|
|
1056
|
+
disabled: disabled,
|
|
1057
|
+
onMouseDown: onMouseDown,
|
|
1058
|
+
onStart: this.onStart,
|
|
1059
|
+
onDrag: this.onDrag,
|
|
1060
|
+
onStop: this.onStop,
|
|
1061
|
+
axis: opts.axis || 'both',
|
|
1062
|
+
grid: [grid.x, grid.y]
|
|
1063
|
+
}, /*#__PURE__*/React.createElement(Comp, _extends({}, rest, {
|
|
1064
|
+
disabled: disabled,
|
|
1065
|
+
isDragging: isDragging
|
|
1066
|
+
})));
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
}, _class.propTypes = {
|
|
1070
|
+
disabled: PropTypes.bool,
|
|
1071
|
+
onDragStart: PropTypes.func,
|
|
1072
|
+
onDrag: PropTypes.func,
|
|
1073
|
+
onDragStop: PropTypes.func,
|
|
1074
|
+
onClick: PropTypes.func,
|
|
1075
|
+
onMove: PropTypes.func,
|
|
1076
|
+
graphProps: GraphPropsType.isRequired
|
|
1077
|
+
}, _class;
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
const log = debug('pie-lib:plot:trig');
|
|
1081
|
+
const toDegrees = radians => radians * (180 / Math.PI);
|
|
1082
|
+
const toRadians = degrees => degrees * (Math.PI / 180);
|
|
1083
|
+
/**
|
|
1084
|
+
* return angle in radians between 2 points using counting degrees counter clockwise
|
|
1085
|
+
*
|
|
1086
|
+
* 0,0 + 1,1 = 45 in radians
|
|
1087
|
+
* 1,1 + 0,0 = 45?
|
|
1088
|
+
* @param {Point} a
|
|
1089
|
+
* @param {Point} b
|
|
1090
|
+
*/
|
|
1091
|
+
|
|
1092
|
+
const angle = (a, b) => {
|
|
1093
|
+
const vd = b.y - a.y;
|
|
1094
|
+
const hd = b.x - a.x;
|
|
1095
|
+
log(a, b, 'vd: ', vd, 'hd: ', hd);
|
|
1096
|
+
const radians = Math.atan2(vd, hd);
|
|
1097
|
+
return radians < 0 ? radians + Math.PI * 2 : radians;
|
|
1098
|
+
};
|
|
1099
|
+
const NINETY = Math.PI / 2;
|
|
1100
|
+
const ONE_EIGHTY = Math.PI;
|
|
1101
|
+
const TWO_SEVENTY = ONE_EIGHTY + NINETY;
|
|
1102
|
+
const acuteXAngle = a => {
|
|
1103
|
+
log(toDegrees(a));
|
|
1104
|
+
|
|
1105
|
+
if (a < NINETY) {
|
|
1106
|
+
return a;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (a < ONE_EIGHTY) {
|
|
1110
|
+
return Math.abs(ONE_EIGHTY - a);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
if (a < TWO_SEVENTY) {
|
|
1114
|
+
return Math.abs(ONE_EIGHTY - a);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
return Math.abs(Math.PI * 2 - a);
|
|
1118
|
+
};
|
|
1119
|
+
const acuteYAngle = a => NINETY - acuteXAngle(a);
|
|
1120
|
+
const hypotenuse = (a, alpha) => {
|
|
1121
|
+
const out = Math.abs(a / Math.sin(alpha));
|
|
1122
|
+
return out;
|
|
1123
|
+
};
|
|
1124
|
+
/**
|
|
1125
|
+
* return 2 edge points for a,b within domain + range.
|
|
1126
|
+
* - one edge is from following a -> b to the bounds
|
|
1127
|
+
* - one edge is from following b -> a to the bounds
|
|
1128
|
+
* @param {{min: number, max: number}} domain
|
|
1129
|
+
* @param {{min: number, max: number}} range
|
|
1130
|
+
* @param {{x: number, y: number}} a
|
|
1131
|
+
* @param {{x: number, y: number}} b
|
|
1132
|
+
* @returns [{x: number, y: number}, {x: number, y: number}]
|
|
1133
|
+
*/
|
|
1134
|
+
|
|
1135
|
+
const edges = (domain, range) => (a, b) => {
|
|
1136
|
+
// const xDest =
|
|
1137
|
+
const destX = a.x < b.x ? domain.max : domain.min;
|
|
1138
|
+
const destY = a.y < b.y ? range.max : range.min;
|
|
1139
|
+
const aToB = diffEdge(xy(destX, destY), a, b);
|
|
1140
|
+
const dX = b.x < a.x ? domain.max : domain.min;
|
|
1141
|
+
const dY = b.y < a.y ? range.max : range.min;
|
|
1142
|
+
const bToA = diffEdge(xy(dX, dY), b, a);
|
|
1143
|
+
return [aToB, bToA];
|
|
1144
|
+
};
|
|
1145
|
+
/** get length of side A of a triangle from H and angle Alpha */
|
|
1146
|
+
|
|
1147
|
+
const getOpposingSide = (hyp, angle) => {
|
|
1148
|
+
log('[getOpposingSide] hyp: ', hyp, 'angle:', angle);
|
|
1149
|
+
return Math.abs(hyp * Math.sin(angle));
|
|
1150
|
+
};
|
|
1151
|
+
|
|
1152
|
+
const getShortestSide = (xh, yh) => {
|
|
1153
|
+
if (Number.isFinite(xh) && Number.isFinite(yh)) {
|
|
1154
|
+
if (xh === 0 && yh > 0) {
|
|
1155
|
+
return 'y';
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
if (yh === 0 && xh > 0) {
|
|
1159
|
+
return 'x';
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
return xh < yh ? 'x' : 'y';
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (isNaN(xh) && !isNaN(yh)) {
|
|
1166
|
+
return 'y';
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
if (!isNaN(xh) && isNaN(yh)) {
|
|
1170
|
+
return 'x';
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
if (xh === Infinity) {
|
|
1174
|
+
return 'y';
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
if (yh === Infinity) {
|
|
1178
|
+
return 'x';
|
|
1179
|
+
} // eslint-disable-next-line no-console
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
console.warn('hypotenuse - which is shorter? x:', xh, 'y:', yh);
|
|
1183
|
+
};
|
|
1184
|
+
/**
|
|
1185
|
+
* return the difference between bounds and a as a Point
|
|
1186
|
+
* @param {*} bounds
|
|
1187
|
+
*/
|
|
1188
|
+
|
|
1189
|
+
|
|
1190
|
+
const diffEdge = (bounds, a, b) => {
|
|
1191
|
+
let l = log.enabled ? log.bind(log, `diffEdge: [${a.x},${a.y} -> ${b.x},${b.y}]`) : () => {};
|
|
1192
|
+
const xRadians = angle(a, b);
|
|
1193
|
+
l('x angle', toDegrees(xRadians));
|
|
1194
|
+
const yRadians = Math.abs(xRadians - toRadians(90));
|
|
1195
|
+
l('y angle', toDegrees(yRadians));
|
|
1196
|
+
const xSide = Math.abs(a.x - bounds.x);
|
|
1197
|
+
/**
|
|
1198
|
+
* Draw 2 triangles:
|
|
1199
|
+
* 1 with a horizontal line from a to the graph x edge
|
|
1200
|
+
* 1 with a vertical line from a to the graph y edge
|
|
1201
|
+
* Calculate the hypotenuse for both, whichever is shorter
|
|
1202
|
+
* indicates that we should use that triangle to get the edge point.
|
|
1203
|
+
*/
|
|
1204
|
+
|
|
1205
|
+
const xH = hypotenuse(xSide, yRadians);
|
|
1206
|
+
const ySide = Math.abs(a.y - bounds.y);
|
|
1207
|
+
const yH = hypotenuse(ySide, xRadians);
|
|
1208
|
+
l('x: side', xSide, 'h:', xH);
|
|
1209
|
+
l('y: side', ySide, 'h:', yH);
|
|
1210
|
+
const side = getShortestSide(xH, yH);
|
|
1211
|
+
|
|
1212
|
+
if (side !== 'x' && side !== 'y') {
|
|
1213
|
+
throw new Error('Cant decide which hypotenuse to use');
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
const point = side === 'x' ? new Point(xSide, getOpposingSide(xH, xRadians)) : new Point(getOpposingSide(yH, yRadians), ySide);
|
|
1217
|
+
l('point:', point);
|
|
1218
|
+
const multiplier = new Point(b.x < a.x ? -1 : 1, b.y < a.y ? -1 : 1);
|
|
1219
|
+
l('multiplier:', multiplier);
|
|
1220
|
+
const out = point.multByPoint(multiplier);
|
|
1221
|
+
l('out:', out);
|
|
1222
|
+
const normalized = out.add(new Point(a.x, a.y));
|
|
1223
|
+
l('normalized:', normalized);
|
|
1224
|
+
return normalized;
|
|
1225
|
+
};
|
|
1226
|
+
|
|
1227
|
+
var trig = /*#__PURE__*/Object.freeze({
|
|
1228
|
+
__proto__: null,
|
|
1229
|
+
acuteXAngle: acuteXAngle,
|
|
1230
|
+
acuteYAngle: acuteYAngle,
|
|
1231
|
+
angle: angle,
|
|
1232
|
+
diffEdge: diffEdge,
|
|
1233
|
+
edges: edges,
|
|
1234
|
+
getOpposingSide: getOpposingSide,
|
|
1235
|
+
hypotenuse: hypotenuse,
|
|
1236
|
+
toDegrees: toDegrees,
|
|
1237
|
+
toRadians: toRadians
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
const createSnapMinAndMax = ({
|
|
1241
|
+
min,
|
|
1242
|
+
max,
|
|
1243
|
+
step
|
|
1244
|
+
}) => {
|
|
1245
|
+
// for graphing, if step is a value with decimals, we have to calculate the min & max for the grid taking in consideration that 0 has to be exactly in the middle
|
|
1246
|
+
// for example, if min: -5 & max: 5 & step: 0.75, in order to keep 0 in the middle we have to set min: -4.5 & max: 4.5
|
|
1247
|
+
return {
|
|
1248
|
+
step,
|
|
1249
|
+
min: parseInt(min / step) * step,
|
|
1250
|
+
max: parseInt(max / step) * step
|
|
1251
|
+
};
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
const create = (domain, range, size, getRootNode) => {
|
|
1255
|
+
invariant(domain.min < domain.max, 'domain: min must be less than max');
|
|
1256
|
+
invariant(range.min < range.max, 'range: min must be less than max');
|
|
1257
|
+
const domainMinMax = createSnapMinAndMax(domain);
|
|
1258
|
+
const rangeMinMax = createSnapMinAndMax(range);
|
|
1259
|
+
const scale = {
|
|
1260
|
+
x: scaleLinear().domain([domain.min, domain.max]).range([0, size.width]),
|
|
1261
|
+
y: scaleLinear().domain([range.max, range.min]).range([0, size.height])
|
|
1262
|
+
};
|
|
1263
|
+
const snap = {
|
|
1264
|
+
x: snapTo.bind(null, domainMinMax.min, domainMinMax.max, domainMinMax.step),
|
|
1265
|
+
y: snapTo.bind(null, rangeMinMax.min, rangeMinMax.max, rangeMinMax.step)
|
|
1266
|
+
};
|
|
1267
|
+
return {
|
|
1268
|
+
scale,
|
|
1269
|
+
snap,
|
|
1270
|
+
domain,
|
|
1271
|
+
range,
|
|
1272
|
+
size,
|
|
1273
|
+
getRootNode
|
|
1274
|
+
};
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
export { LocalDraggable as Draggable, root as Root, create as createGraphProps, gridDraggable, trig, types, utils };
|
|
1278
|
+
//# sourceMappingURL=index.js.map
|