@pie-lib/graphing-solution-set 2.16.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/CHANGELOG.json +1 -0
  2. package/CHANGELOG.md +16 -0
  3. package/LICENSE.md +5 -0
  4. package/NEXT.CHANGELOG.json +1 -0
  5. package/lib/__tests__/graph-with-controls.test.js +191 -0
  6. package/lib/__tests__/graph.test.js +290 -0
  7. package/lib/__tests__/grid.test.js +40 -0
  8. package/lib/__tests__/labels.test.js +59 -0
  9. package/lib/__tests__/mark-label.test.js +154 -0
  10. package/lib/__tests__/toggle-bar.test.js +54 -0
  11. package/lib/__tests__/tool-menu.test.js +43 -0
  12. package/lib/__tests__/undo-redo.test.js +42 -0
  13. package/lib/__tests__/use-debounce.test.js +28 -0
  14. package/lib/__tests__/utils.js +72 -0
  15. package/lib/__tests__/utils.test.js +133 -0
  16. package/lib/axis/__tests__/arrow.test.js +68 -0
  17. package/lib/axis/__tests__/axes.test.js +214 -0
  18. package/lib/axis/arrow.js +115 -0
  19. package/lib/axis/axes.js +415 -0
  20. package/lib/axis/index.js +26 -0
  21. package/lib/bg.js +139 -0
  22. package/lib/container/actions.js +24 -0
  23. package/lib/container/index.js +166 -0
  24. package/lib/container/marks.js +27 -0
  25. package/lib/container/middleware.js +25 -0
  26. package/lib/container/reducer.js +25 -0
  27. package/lib/coordinates-label.js +109 -0
  28. package/lib/graph-with-controls.js +372 -0
  29. package/lib/graph.js +419 -0
  30. package/lib/grid-setup.js +462 -0
  31. package/lib/grid.js +176 -0
  32. package/lib/index.js +51 -0
  33. package/lib/labels.js +299 -0
  34. package/lib/mark-label.js +208 -0
  35. package/lib/toggle-bar.js +336 -0
  36. package/lib/tool-menu.js +325 -0
  37. package/lib/tools/index.js +29 -0
  38. package/lib/tools/line/__tests__/component.test.js +56 -0
  39. package/lib/tools/line/component.js +106 -0
  40. package/lib/tools/line/index.js +16 -0
  41. package/lib/tools/polygon/__tests__/component.test.js +245 -0
  42. package/lib/tools/polygon/__tests__/index.test.js +95 -0
  43. package/lib/tools/polygon/__tests__/line.test.js +43 -0
  44. package/lib/tools/polygon/__tests__/polygon.test.js +73 -0
  45. package/lib/tools/polygon/component.js +457 -0
  46. package/lib/tools/polygon/index.js +106 -0
  47. package/lib/tools/polygon/line.js +151 -0
  48. package/lib/tools/polygon/polygon.js +171 -0
  49. package/lib/tools/shared/__tests__/arrow-head.test.js +62 -0
  50. package/lib/tools/shared/arrow-head.js +75 -0
  51. package/lib/tools/shared/line/__tests__/index.test.js +291 -0
  52. package/lib/tools/shared/line/__tests__/line-path.test.js +78 -0
  53. package/lib/tools/shared/line/__tests__/with-root-edge.test.js +122 -0
  54. package/lib/tools/shared/line/index.js +637 -0
  55. package/lib/tools/shared/line/line-path.js +145 -0
  56. package/lib/tools/shared/line/with-root-edge.js +155 -0
  57. package/lib/tools/shared/point/__tests__/arrow-point.test.js +137 -0
  58. package/lib/tools/shared/point/__tests__/base-point.test.js +134 -0
  59. package/lib/tools/shared/point/arrow-point.js +113 -0
  60. package/lib/tools/shared/point/arrow.js +96 -0
  61. package/lib/tools/shared/point/base-point.js +151 -0
  62. package/lib/tools/shared/point/index.js +94 -0
  63. package/lib/tools/shared/styles.js +49 -0
  64. package/lib/tools/shared/types.js +19 -0
  65. package/lib/undo-redo.js +107 -0
  66. package/lib/use-debounce.js +32 -0
  67. package/lib/utils.js +314 -0
  68. package/package.json +50 -0
  69. package/src/__tests__/__snapshots__/graph-with-controls.test.jsx.snap +114 -0
  70. package/src/__tests__/__snapshots__/graph.test.jsx.snap +213 -0
  71. package/src/__tests__/__snapshots__/grid.test.jsx.snap +54 -0
  72. package/src/__tests__/__snapshots__/labels.test.jsx.snap +30 -0
  73. package/src/__tests__/__snapshots__/mark-label.test.jsx.snap +37 -0
  74. package/src/__tests__/__snapshots__/toggle-bar.test.jsx.snap +7 -0
  75. package/src/__tests__/__snapshots__/tool-menu.test.jsx.snap +35 -0
  76. package/src/__tests__/__snapshots__/undo-redo.test.jsx.snap +15 -0
  77. package/src/__tests__/graph-with-controls.test.jsx +131 -0
  78. package/src/__tests__/graph.test.jsx +230 -0
  79. package/src/__tests__/grid.test.jsx +20 -0
  80. package/src/__tests__/labels.test.jsx +38 -0
  81. package/src/__tests__/mark-label.test.jsx +68 -0
  82. package/src/__tests__/toggle-bar.test.jsx +36 -0
  83. package/src/__tests__/tool-menu.test.jsx +29 -0
  84. package/src/__tests__/undo-redo.test.jsx +25 -0
  85. package/src/__tests__/use-debounce.test.js +21 -0
  86. package/src/__tests__/utils.js +38 -0
  87. package/src/__tests__/utils.test.js +151 -0
  88. package/src/axis/__tests__/__snapshots__/arrow.test.jsx.snap +33 -0
  89. package/src/axis/__tests__/__snapshots__/axes.test.jsx.snap +122 -0
  90. package/src/axis/__tests__/arrow.test.jsx +39 -0
  91. package/src/axis/__tests__/axes.test.jsx +220 -0
  92. package/src/axis/arrow.jsx +62 -0
  93. package/src/axis/axes.jsx +307 -0
  94. package/src/axis/index.js +2 -0
  95. package/src/bg.jsx +96 -0
  96. package/src/container/actions.js +8 -0
  97. package/src/container/index.jsx +86 -0
  98. package/src/container/marks.js +14 -0
  99. package/src/container/middleware.js +7 -0
  100. package/src/container/reducer.js +5 -0
  101. package/src/coordinates-label.jsx +73 -0
  102. package/src/graph-with-controls.jsx +263 -0
  103. package/src/graph.jsx +334 -0
  104. package/src/grid-setup.jsx +427 -0
  105. package/src/grid.jsx +135 -0
  106. package/src/index.js +7 -0
  107. package/src/labels.jsx +214 -0
  108. package/src/mark-label.jsx +136 -0
  109. package/src/toggle-bar.jsx +242 -0
  110. package/src/tool-menu.jsx +294 -0
  111. package/src/tools/index.js +8 -0
  112. package/src/tools/line/__tests__/__snapshots__/component.test.jsx.snap +20 -0
  113. package/src/tools/line/__tests__/component.test.jsx +36 -0
  114. package/src/tools/line/component.jsx +77 -0
  115. package/src/tools/line/index.js +4 -0
  116. package/src/tools/polygon/__tests__/__snapshots__/component.test.jsx.snap +94 -0
  117. package/src/tools/polygon/__tests__/__snapshots__/line.test.jsx.snap +44 -0
  118. package/src/tools/polygon/__tests__/__snapshots__/polygon.test.jsx.snap +53 -0
  119. package/src/tools/polygon/__tests__/component.test.jsx +214 -0
  120. package/src/tools/polygon/__tests__/index.test.js +65 -0
  121. package/src/tools/polygon/__tests__/line.test.jsx +25 -0
  122. package/src/tools/polygon/__tests__/polygon.test.jsx +44 -0
  123. package/src/tools/polygon/component.jsx +336 -0
  124. package/src/tools/polygon/index.js +52 -0
  125. package/src/tools/polygon/line.jsx +78 -0
  126. package/src/tools/polygon/polygon.jsx +101 -0
  127. package/src/tools/shared/__tests__/__snapshots__/arrow-head.test.jsx.snap +32 -0
  128. package/src/tools/shared/__tests__/arrow-head.test.jsx +34 -0
  129. package/src/tools/shared/arrow-head.jsx +46 -0
  130. package/src/tools/shared/line/__tests__/__snapshots__/index.test.jsx.snap +360 -0
  131. package/src/tools/shared/line/__tests__/__snapshots__/line-path.test.jsx.snap +57 -0
  132. package/src/tools/shared/line/__tests__/__snapshots__/with-root-edge.test.jsx.snap +63 -0
  133. package/src/tools/shared/line/__tests__/index.test.jsx +247 -0
  134. package/src/tools/shared/line/__tests__/line-path.test.jsx +53 -0
  135. package/src/tools/shared/line/__tests__/with-root-edge.test.jsx +73 -0
  136. package/src/tools/shared/line/index.jsx +473 -0
  137. package/src/tools/shared/line/line-path.jsx +88 -0
  138. package/src/tools/shared/line/with-root-edge.jsx +97 -0
  139. package/src/tools/shared/point/__tests__/__snapshots__/arrow-point.test.jsx.snap +55 -0
  140. package/src/tools/shared/point/__tests__/__snapshots__/base-point.test.jsx.snap +43 -0
  141. package/src/tools/shared/point/__tests__/arrow-point.test.jsx +87 -0
  142. package/src/tools/shared/point/__tests__/base-point.test.jsx +84 -0
  143. package/src/tools/shared/point/arrow-point.jsx +60 -0
  144. package/src/tools/shared/point/arrow.jsx +40 -0
  145. package/src/tools/shared/point/base-point.jsx +86 -0
  146. package/src/tools/shared/point/index.jsx +60 -0
  147. package/src/tools/shared/styles.js +20 -0
  148. package/src/tools/shared/types.js +8 -0
  149. package/src/undo-redo.jsx +47 -0
  150. package/src/use-debounce.js +13 -0
  151. package/src/utils.js +234 -0
@@ -0,0 +1,214 @@
1
+ import { shallow } from 'enzyme';
2
+ import React from 'react';
3
+ import { graphProps, xy } from '../../../__tests__/utils';
4
+
5
+ import { RawBaseComponent, buildLines, swap } from '../component';
6
+
7
+ const xyLabel = (x, y, index, label) => ({
8
+ ...xy(x, y, index),
9
+ label,
10
+ });
11
+
12
+ describe('buildLines', () => {
13
+ const defaultPoints = [xy(0, 0), xy(1, 1), xy(1, 0)];
14
+
15
+ const assertBuildLines = (points, closed, expected) => {
16
+ it(`builds points and lines for ${points} = ${expected}`, () => {
17
+ const result = buildLines(points);
18
+ expect(result).toMatchObject([
19
+ { from: xy(0, 0, 0), to: xy(1, 1, 1) },
20
+ { from: xy(1, 1, 1), to: xy(1, 0, 2) },
21
+ ]);
22
+ });
23
+ };
24
+
25
+ assertBuildLines(defaultPoints, true, []);
26
+ });
27
+
28
+ describe('swap', () => {
29
+ it('swaps pairs', () => {
30
+ const result = swap([xy(0, 0, 0), xy(1, 1, 1), xy(2, 2, 2)], xy(0, 0, 0), xy(3, 3, 0));
31
+ expect(result).toEqual([xy(3, 3, 0), xy(1, 1, 1), xy(2, 2, 2)]);
32
+ });
33
+ });
34
+
35
+ describe('RawBaseComponent', () => {
36
+ let w;
37
+ let onChange = jest.fn();
38
+ let onChangeProps = jest.fn();
39
+ const wrapper = (extras) => {
40
+ const defaults = {
41
+ classes: {},
42
+ className: 'className',
43
+ onChange,
44
+ onChangeProps,
45
+ onClosePolygon: jest.fn(),
46
+ graphProps: graphProps(),
47
+ points: [],
48
+ };
49
+ const props = { ...defaults, ...extras };
50
+
51
+ return shallow(<RawBaseComponent {...props} />);
52
+ };
53
+
54
+ // used to test items that have labels attached to points
55
+ const labelNode = document.createElement('foreignObject');
56
+ const points = [xyLabel(0, 0, 0, 'A'), xyLabel(2, 2, 1, 'B'), xyLabel(0, 2, 2, 'C')];
57
+ const wrapperWithLabels = (extras) =>
58
+ wrapper({
59
+ labelNode: labelNode,
60
+ points: points,
61
+ ...extras,
62
+ });
63
+
64
+ describe('snapshot', () => {
65
+ it('renders', () => {
66
+ w = wrapper();
67
+ expect(w).toMatchSnapshot();
68
+ });
69
+
70
+ it('renders with labels', () => {
71
+ w = wrapperWithLabels();
72
+ expect(w).toMatchSnapshot();
73
+ });
74
+ });
75
+
76
+ describe('logic', () => {
77
+ describe('dragPoint', () => {
78
+ it('calls onChange', () => {
79
+ onChange = jest.fn();
80
+ w = wrapper({ points: [xy(1, 1)], onChange });
81
+ w.instance().dragPoint(0, xy(1, 1), xy(2, 2));
82
+ expect(onChange).toHaveBeenCalledWith([xy(2, 2)]);
83
+ });
84
+
85
+ it('calls onChange keeping label property from point', () => {
86
+ onChange = jest.fn();
87
+ w = wrapperWithLabels(onChange);
88
+
89
+ w.instance().dragPoint(0, xy(0, 0), xy(0, 1));
90
+ expect(onChange).toHaveBeenCalledWith([{ x: 0, y: 1, label: 'A' }, points[1], points[2]]);
91
+ });
92
+ });
93
+
94
+ describe('dragLine', () => {
95
+ it('calls onChange', () => {
96
+ w = wrapper({ points: [xy(1, 1, 0), xy(2, 2, 1)], onChange });
97
+ w.instance().dragLine({ from: xy(1, 1, 0), to: xy(2, 2, 1) }, { from: xy(2, 2, 0), to: xy(4, 4, 1) });
98
+ expect(onChange).toHaveBeenCalledWith([xy(2, 2, 0), xy(4, 4, 1)]);
99
+ });
100
+
101
+ it('calls onChange keeping label property from both points', () => {
102
+ onChange = jest.fn();
103
+ w = wrapperWithLabels(onChange);
104
+
105
+ w.instance().dragLine(
106
+ { from: points[0], to: points[1] },
107
+ {
108
+ from: xy(0, 1, 0),
109
+ to: xy(2, 3, 1),
110
+ },
111
+ );
112
+ expect(onChange).toHaveBeenCalledWith([xyLabel(0, 1, 0, 'A'), xyLabel(2, 3, 1, 'B'), points[2]]);
113
+ });
114
+ });
115
+
116
+ describe('dragPoly', () => {
117
+ it('calls onChange', () => {
118
+ w = wrapper({ onChange });
119
+ const existing = [xy(1, 1)];
120
+ const next = [xy(2, 2)];
121
+ w.instance().dragPoly(existing, next);
122
+ expect(onChange).toHaveBeenCalledWith([xy(2, 2)]);
123
+ });
124
+
125
+ it('calls onChange keeping label property from all points', () => {
126
+ onChange = jest.fn();
127
+ w = wrapperWithLabels(onChange);
128
+
129
+ w.instance().dragPoly(points, [xy(0, 1, 0), xy(2, 3, 1), xy(0, 3, 2)]);
130
+ expect(onChange).toHaveBeenCalledWith([xyLabel(0, 1, 0, 'A'), xyLabel(2, 3, 1, 'B'), xyLabel(0, 3, 2, 'C')]);
131
+ });
132
+ });
133
+
134
+ describe('labelChange', () => {
135
+ it('updates "label" property for point', () => {
136
+ w = wrapperWithLabels();
137
+
138
+ w.instance().labelChange({ ...points[0], label: 'Label A' }, 0);
139
+ expect(onChangeProps).toBeCalledWith([{ ...points[0], label: 'Label A' }, points[1], points[2]]);
140
+
141
+ w.instance().labelChange({ ...points[1], label: 'Label B' }, 1);
142
+ expect(onChangeProps).toBeCalledWith([points[0], { ...points[1], label: 'Label B' }, points[2]]);
143
+ });
144
+
145
+ it('removes "label" property if the field is empty', () => {
146
+ w = wrapperWithLabels();
147
+
148
+ w.instance().labelChange({ ...points[0], label: '' }, 0);
149
+ expect(onChangeProps).toBeCalledWith([xy(0, 0, 0), points[1], points[2]]);
150
+
151
+ w.instance().labelChange({ ...points[1], label: '' }, 1);
152
+ expect(onChangeProps).toBeCalledWith([points[0], xy(2, 2, 1), points[2]]);
153
+ });
154
+ });
155
+ });
156
+
157
+ describe('close', () => {
158
+ it('calls onClosePolygon', () => {
159
+ const onClosePolygon = jest.fn();
160
+ w = wrapper({ onClosePolygon, points: [xy(1, 1), xy(2, 2), xy(3, 3)] });
161
+ w.instance().close();
162
+ expect(onClosePolygon).toHaveBeenCalled();
163
+ });
164
+ });
165
+
166
+ describe('clickPoint', () => {
167
+ let onClick = jest.fn();
168
+ let onClosePolygon = jest.fn();
169
+ beforeEach(() => {
170
+ onClosePolygon.mockClear();
171
+ onClick.mockClear();
172
+ });
173
+
174
+ const assertCallback = (isToolActive, closed, index, mock) => {
175
+ it('calls onClosePolygon', () => {
176
+ const w = wrapper({
177
+ points: [xy(1, 1), xy(2, 2), xy(3, 3)],
178
+ onClosePolygon,
179
+ onClick,
180
+ isToolActive,
181
+ closed,
182
+ });
183
+
184
+ w.instance().clickPoint(xy(1, 1, 0), index, {});
185
+ expect(mock).toHaveBeenCalled();
186
+ });
187
+ };
188
+
189
+ assertCallback(true, false, 0, onClosePolygon);
190
+ assertCallback(true, false, 1, onClick);
191
+ assertCallback(false, false, 0, onClick);
192
+ assertCallback(true, true, 0, onClick);
193
+
194
+ it('adds "label" property to a point', () => {
195
+ const onChangeProps = jest.fn();
196
+ const w = wrapperWithLabels({
197
+ labelModeEnabled: true,
198
+ onChangeProps,
199
+ points: [xy(0, 0, 0), xy(2, 2, 1), xy(0, 2, 2)],
200
+ });
201
+
202
+ w.instance().clickPoint(xy(0, 0, 0), 0, {});
203
+ expect(onChangeProps).toHaveBeenCalledWith([xyLabel(0, 0, 0, ''), xy(2, 2, 1), xy(0, 2, 2)]);
204
+ });
205
+
206
+ it('if point already has label, keeps that value', () => {
207
+ const onChangeProps = jest.fn();
208
+ const w = wrapperWithLabels({ labelModeEnabled: true, onChangeProps });
209
+
210
+ w.instance().clickPoint(points[0], 0, {});
211
+ expect(onChangeProps).toHaveBeenCalledWith(points);
212
+ });
213
+ });
214
+ });
@@ -0,0 +1,65 @@
1
+ import { addPointToArray, tool } from '../index';
2
+
3
+ const xy = (x, y) => ({ x, y });
4
+
5
+ describe('polygon', () => {
6
+ describe('addPointToArray', () => {
7
+ const assertAddPoint = (point, arr, expected) => {
8
+ it(`returns ${expected} when adding ${point} to ${arr}`, () => {
9
+ const result = addPointToArray(point, arr);
10
+ expect(result).toEqual(expected);
11
+ });
12
+ };
13
+ assertAddPoint(xy(0, 0), [], { closed: false, points: [xy(0, 0)] });
14
+ assertAddPoint(xy(0, 0), [xy(0, 0)], { closed: false, points: [xy(0, 0)] });
15
+ assertAddPoint(xy(1, 1), [xy(0, 0)], {
16
+ closed: false,
17
+ points: [xy(0, 0), xy(1, 1)],
18
+ });
19
+ assertAddPoint(xy(1, 1), [xy(0, 0)], {
20
+ closed: false,
21
+ points: [xy(0, 0), xy(1, 1)],
22
+ });
23
+ });
24
+ });
25
+
26
+ describe('tool', () => {
27
+ let t;
28
+ beforeEach(() => {
29
+ t = tool();
30
+ });
31
+
32
+ it('creates polygon type', () => {
33
+ expect(t.type).toEqual('polygon');
34
+ });
35
+
36
+ describe('complete', () => {
37
+ it('closes polygon', () => {
38
+ expect(t.complete()).toEqual({ building: false, closed: true });
39
+ });
40
+ });
41
+
42
+ describe('addPoint', () => {
43
+ it('creates new point', () => {
44
+ const mark = t.addPoint({ x: 0, y: 0 });
45
+ expect(mark).toEqual({
46
+ type: 'polygon',
47
+ points: [{ x: 0, y: 0 }],
48
+ closed: false,
49
+ building: true,
50
+ });
51
+ });
52
+
53
+ it('adds the point', () => {
54
+ const mark = t.addPoint({ x: 0, y: 0 }, { points: [{ x: 1, y: 1 }] });
55
+ expect(mark).toEqual({
56
+ building: true,
57
+ closed: false,
58
+ points: [
59
+ { x: 1, y: 1 },
60
+ { x: 0, y: 0 },
61
+ ],
62
+ });
63
+ });
64
+ });
65
+ });
@@ -0,0 +1,25 @@
1
+ import { shallow } from 'enzyme';
2
+ import React from 'react';
3
+ import { Line } from '../line';
4
+ import { graphProps } from '../../../__tests__/utils';
5
+
6
+ describe('Line', () => {
7
+ let w;
8
+ let onChange = jest.fn();
9
+ const wrapper = (extras) => {
10
+ const defaults = {
11
+ classes: {},
12
+ className: 'className',
13
+ graphProps: graphProps(),
14
+ };
15
+ const props = { ...defaults, ...extras };
16
+ return shallow(<Line {...props} />);
17
+ };
18
+ describe('snapshot', () => {
19
+ it('renders', () => {
20
+ w = wrapper();
21
+ expect(w).toMatchSnapshot();
22
+ });
23
+ });
24
+ describe('logic', () => {});
25
+ });
@@ -0,0 +1,44 @@
1
+ import { shallow } from 'enzyme';
2
+ import React from 'react';
3
+ import { Polygon, getPointString } from '../polygon';
4
+ import { graphProps } from '../../../__tests__/utils';
5
+
6
+ const xy = (x, y) => ({ x, y });
7
+
8
+ describe('Polygon', () => {
9
+ let w;
10
+ const wrapper = (extras) => {
11
+ const defaults = {
12
+ classes: {},
13
+ className: 'className',
14
+ graphProps: graphProps(),
15
+ closed: false,
16
+ };
17
+ const props = { ...defaults, ...extras };
18
+ return shallow(<Polygon {...props} />);
19
+ };
20
+
21
+ describe('snapshot', () => {
22
+ it('renders', () => {
23
+ w = wrapper({ points: [{ x: 1, y: 1 }] });
24
+ expect(w).toMatchSnapshot();
25
+ });
26
+ });
27
+ });
28
+
29
+ describe('getPointString', () => {
30
+ const assertString = (arr, expected) => {
31
+ it('creates: ', () => {
32
+ const result = getPointString(arr, {
33
+ x: jest.fn((n) => n),
34
+ y: jest.fn((n) => n),
35
+ });
36
+
37
+ expect(result).toEqual(expected);
38
+ });
39
+ };
40
+
41
+ assertString([xy(1, 1)], '1,1');
42
+ assertString([xy(1, 1), xy(2, 1)], '1,1 2,1');
43
+ assertString([xy(1, 1), xy(2, 1), xy(4, 4)], '1,1 2,1 4,4');
44
+ });
@@ -0,0 +1,336 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { withStyles } from '@material-ui/core/styles';
4
+ import { ToolPropTypeFields } from '../shared/types';
5
+ import chunk from 'lodash/chunk';
6
+ import initial from 'lodash/initial';
7
+ import debug from 'debug';
8
+ import DraggablePolygon from './polygon';
9
+ import { types } from '@pie-lib/plot';
10
+ import invariant from 'invariant';
11
+ import ReactDOM from 'react-dom';
12
+ import MarkLabel from '../../mark-label';
13
+ import isEmpty from 'lodash/isEmpty';
14
+ import { getMiddleOfTwoPoints, getRightestPoints, equalPoints } from '../../utils';
15
+
16
+ const log = debug('pie-lib:graphing-solution-set:polygon');
17
+
18
+ export const buildLines = (points, closed) => {
19
+ const expanded = points.reduce((acc, p, index) => {
20
+ acc.push({ ...p, index });
21
+
22
+ const isLast = index === points.length - 1;
23
+ const next = isLast ? 0 : index + 1;
24
+
25
+ acc.push({ ...points[next], index: next });
26
+
27
+ return acc;
28
+ }, []);
29
+
30
+ const all = chunk(expanded, 2).map(([from, to]) => {
31
+ return { from, to };
32
+ });
33
+
34
+ return closed ? all : initial(all);
35
+ };
36
+
37
+ export const swap = (arr, ...rest) => {
38
+ const pairs = chunk(rest, 2);
39
+
40
+ return pairs.reduce(
41
+ (acc, pr) => {
42
+ if (pr.length === 2) {
43
+ let [e, replacement] = pr;
44
+
45
+ invariant(Number.isFinite(e.index), 'Index must be defined');
46
+
47
+ const index = e.index;
48
+ // const i = acc.findIndex(pt => pt.x === e.x && pt.y === e.y);
49
+
50
+ if (index >= 0) {
51
+ acc.splice(index, 1, replacement);
52
+
53
+ return acc;
54
+ } else {
55
+ return acc;
56
+ }
57
+ } else {
58
+ return acc;
59
+ }
60
+ },
61
+ [...arr],
62
+ );
63
+ };
64
+
65
+ export class RawBaseComponent extends React.Component {
66
+ static propTypes = {
67
+ classes: PropTypes.object,
68
+ className: PropTypes.string,
69
+ disabled: PropTypes.bool,
70
+ correctness: PropTypes.string,
71
+ points: PropTypes.arrayOf(types.PointType),
72
+ closed: PropTypes.bool,
73
+ isSolution: PropTypes.bool,
74
+ coordinatesOnHover: PropTypes.bool,
75
+ onChange: PropTypes.func.isRequired,
76
+ onClosePolygon: PropTypes.func.isRequired,
77
+ onDragStart: PropTypes.func,
78
+ onDragStop: PropTypes.func,
79
+ onClick: PropTypes.func,
80
+ graphProps: types.GraphPropsType.isRequired,
81
+ isToolActive: PropTypes.bool,
82
+ middle: PropTypes.object,
83
+ labelNode: PropTypes.object,
84
+ labelModeEnabled: PropTypes.bool,
85
+ onChangeLabelProps: PropTypes.func,
86
+ onChangeProps: PropTypes.func,
87
+ };
88
+
89
+ static defaultProps = {
90
+ points: [],
91
+ };
92
+
93
+ dragPoint = (index, from, to) => {
94
+ log('[dragPoint] from, to:', from, to);
95
+ const { onChange, points } = this.props;
96
+ const update = [...points];
97
+ const overlapPoint = !!(points || []).find((p) => equalPoints(p, to));
98
+
99
+ if (equalPoints(from, to) || overlapPoint) {
100
+ return;
101
+ }
102
+
103
+ if (points[index].label) {
104
+ to.label = points[index].label;
105
+ }
106
+
107
+ update.splice(index, 1, to);
108
+ onChange(update);
109
+ };
110
+
111
+ dragLine = (existing, next) => {
112
+ log('[dragLine]: ', existing, next);
113
+ const { onChange } = this.props;
114
+
115
+ if (existing.from.label) {
116
+ next.from.label = existing.from.label;
117
+ }
118
+
119
+ if (existing.to.label) {
120
+ next.to.label = existing.to.label;
121
+ }
122
+
123
+ const points = swap(this.props.points, existing.from, next.from, existing.to, next.to);
124
+ onChange(points);
125
+ };
126
+
127
+ dragPoly = (existing, next) => {
128
+ log('[dragPoly] ', existing, next);
129
+ const { onChange } = this.props;
130
+
131
+ next.forEach((point, index) => {
132
+ if (existing[index].label) {
133
+ next[index].label = existing[index].label;
134
+ }
135
+ });
136
+
137
+ onChange(next);
138
+ };
139
+
140
+ close = () => {
141
+ const { points, onClosePolygon } = this.props;
142
+ log('[close] ...');
143
+
144
+ if (points.length >= 3) {
145
+ onClosePolygon();
146
+ } else {
147
+ log('[close] - nope');
148
+ }
149
+ };
150
+
151
+ labelChange = (point, index) => {
152
+ const { points, onChangeProps } = this.props;
153
+ const updatedPoint = { ...point };
154
+
155
+ if (!point.label || isEmpty(point.label)) {
156
+ delete updatedPoint.label;
157
+ }
158
+
159
+ const update = [...points];
160
+
161
+ update.splice(index, 1, updatedPoint);
162
+ onChangeProps(update);
163
+ };
164
+
165
+ clickPoint = (point, index, data) => {
166
+ const {
167
+ closed,
168
+ disabled,
169
+ onClick,
170
+ isToolActive,
171
+ labelModeEnabled,
172
+ onChangeProps,
173
+ onChangeLabelProps,
174
+ points,
175
+ } = this.props;
176
+
177
+ if (labelModeEnabled) {
178
+ if (disabled) {
179
+ return;
180
+ }
181
+
182
+ if (points && index === points.length) {
183
+ const { a, b } = getRightestPoints(points);
184
+ const middle = { label: '', ...point, ...getMiddleOfTwoPoints(a, b) };
185
+
186
+ onChangeLabelProps(middle);
187
+ } else {
188
+ const update = [...points];
189
+
190
+ update.splice(index, 1, { label: '', ...point });
191
+ onChangeProps(update);
192
+ }
193
+
194
+ if (this.input[index]) {
195
+ this.input[index].focus();
196
+ }
197
+
198
+ return;
199
+ }
200
+
201
+ if (isToolActive && !closed && index === 0) {
202
+ this.close();
203
+ return;
204
+ }
205
+
206
+ onClick(point || data);
207
+ };
208
+
209
+ // IMPORTANT, do not remove
210
+ input = {};
211
+
212
+ render() {
213
+ const {
214
+ closed,
215
+ graphProps,
216
+ onChangeLabelProps,
217
+ points,
218
+ middle,
219
+ labelNode,
220
+ labelModeEnabled,
221
+ isSolution = false,
222
+ } = this.props;
223
+ const polygonLabelIndex = (points && points.length) || 0;
224
+ if (labelNode && middle && middle.hasOwnProperty('label')) {
225
+ ReactDOM.createPortal(
226
+ <MarkLabel
227
+ inputRef={(r) => (this.input[polygonLabelIndex] = r)}
228
+ disabled={!labelModeEnabled}
229
+ mark={middle}
230
+ graphProps={graphProps}
231
+ onChange={(label) => onChangeLabelProps({ ...middle, label })}
232
+ />,
233
+ labelNode,
234
+ );
235
+ }
236
+ return (
237
+ <g>
238
+ <React.Fragment>
239
+ <DraggablePolygon
240
+ points={points}
241
+ closed={closed}
242
+ isSolution={isSolution}
243
+ graphProps={graphProps}
244
+ onClick={this.clickPoint.bind(this, middle, polygonLabelIndex)}
245
+ />
246
+ </React.Fragment>
247
+ </g>
248
+ );
249
+ }
250
+ }
251
+
252
+ export const BaseComponent = withStyles(() => ({}))(RawBaseComponent);
253
+
254
+ export default class Component extends React.Component {
255
+ static propTypes = {
256
+ ...ToolPropTypeFields,
257
+ graphProps: types.GraphPropsType.isRequired,
258
+ };
259
+
260
+ static defaultProps = {};
261
+
262
+ constructor(props) {
263
+ super(props);
264
+ this.state = {};
265
+ }
266
+
267
+ change = (points) => {
268
+ const {
269
+ mark: { middle },
270
+ } = this.props;
271
+ const mark = { ...this.state.mark, points };
272
+
273
+ if (middle) {
274
+ const { a, b } = getRightestPoints(points);
275
+
276
+ mark.middle = { ...middle, ...getMiddleOfTwoPoints(a, b) };
277
+ }
278
+
279
+ this.setState({ mark });
280
+ };
281
+
282
+ changeProps = (points) => {
283
+ const mark = { ...this.props.mark, points };
284
+
285
+ this.props.onChange(this.props.mark, mark);
286
+ };
287
+
288
+ changeLabelProps = (point) => {
289
+ const { mark, onChange } = this.props;
290
+ const middle = { ...mark.middle, ...point };
291
+
292
+ onChange(mark, { ...mark, middle });
293
+ };
294
+
295
+ closePolygon = () => {
296
+ log('[closePolygon] ...');
297
+ const { onComplete, mark } = this.props;
298
+ const update = { ...mark, closed: true };
299
+
300
+ onComplete(mark, update);
301
+ };
302
+
303
+ dragStart = () => this.setState({ mark: this.props.mark });
304
+
305
+ dragStop = () => {
306
+ const { onChange } = this.props;
307
+ const m = { ...this.state.mark };
308
+
309
+ this.setState({ mark: undefined }, () => {
310
+ onChange(this.props.mark, m);
311
+ });
312
+ };
313
+
314
+ render() {
315
+ const { coordinatesOnHover, mark, graphProps, onClick, isToolActive, labelNode, labelModeEnabled } = this.props;
316
+ const { mark: stateMark } = this.state;
317
+
318
+ return (
319
+ <BaseComponent
320
+ {...(stateMark || mark)}
321
+ coordinatesOnHover={coordinatesOnHover}
322
+ onChange={this.change}
323
+ onChangeLabelProps={this.changeLabelProps}
324
+ onChangeProps={this.changeProps}
325
+ onClosePolygon={this.closePolygon}
326
+ onDragStart={this.dragStart}
327
+ onDragStop={this.dragStop}
328
+ onClick={onClick}
329
+ graphProps={graphProps}
330
+ isToolActive={isToolActive}
331
+ labelNode={labelNode}
332
+ labelModeEnabled={labelModeEnabled}
333
+ />
334
+ );
335
+ }
336
+ }