@khanacademy/perseus 19.1.3 → 19.1.4
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/CHANGELOG.md +11 -0
- package/dist/es/index.js +25 -57
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +38 -42
- package/dist/index.js.map +1 -1
- package/dist/interactive2/interactive-util.d.ts +1 -1
- package/dist/interactive2/movable.d.ts +20 -1
- package/dist/util/graphie.d.ts +1 -1
- package/package.json +2 -2
- package/src/interactive2/__tests__/movable-mock.ts +2 -1
- package/src/interactive2/__tests__/movable.test.ts +157 -0
- package/src/interactive2/interactive-util.test.ts +93 -0
- package/src/interactive2/interactive-util.ts +19 -15
- package/src/interactive2/movable-helper-methods.ts +7 -2
- package/src/interactive2/movable-line.ts +1 -12
- package/src/interactive2/movable-point.tsx +1 -9
- package/src/interactive2/movable-polygon.ts +1 -9
- package/src/interactive2/movable.ts +49 -44
- package/src/util/graphie.ts +3 -1
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -18,7 +18,7 @@ declare const InteractiveUtil: {
|
|
|
18
18
|
/**
|
|
19
19
|
* Convert all function-or-array arguments to arrays of functions
|
|
20
20
|
*/
|
|
21
|
-
readonly normalizeOptions: (
|
|
21
|
+
readonly normalizeOptions: (options: Record<any, any>) => Record<any, any>;
|
|
22
22
|
/**
|
|
23
23
|
* Get the correct vendor-prefixed `transform`.
|
|
24
24
|
*/
|
|
@@ -1,2 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Coord } from "./types";
|
|
2
|
+
import type { Graphie } from "../util/graphie";
|
|
3
|
+
export interface State {
|
|
4
|
+
added?: boolean;
|
|
5
|
+
isHovering?: boolean;
|
|
6
|
+
isMouseOver?: boolean;
|
|
7
|
+
isDragging?: boolean;
|
|
8
|
+
mouseTarget?: unknown | null;
|
|
9
|
+
cursor: unknown | null;
|
|
10
|
+
id: string;
|
|
11
|
+
add: (() => void)[];
|
|
12
|
+
modify: (() => void)[];
|
|
13
|
+
draw: (() => void)[];
|
|
14
|
+
remove: (() => void)[];
|
|
15
|
+
onMoveStart: ((position: Coord) => void)[];
|
|
16
|
+
onMove: ((end: Coord, start: Coord) => void)[];
|
|
17
|
+
onMoveEnd: ((end: Coord, start: Coord) => void)[];
|
|
18
|
+
onClick: ((position: Coord, start: Coord) => void)[];
|
|
19
|
+
}
|
|
20
|
+
declare const Movable: (graphie: Graphie, options: any) => void;
|
|
2
21
|
export default Movable;
|
package/dist/util/graphie.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Core Perseus API (includes renderers and widgets)",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "19.1.
|
|
6
|
+
"version": "19.1.4",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@khanacademy/kas": "^0.3.7",
|
|
26
26
|
"@khanacademy/kmath": "^0.1.10",
|
|
27
|
-
"@khanacademy/math-input": "^17.0.
|
|
27
|
+
"@khanacademy/math-input": "^17.0.3",
|
|
28
28
|
"@khanacademy/perseus-core": "1.4.1",
|
|
29
29
|
"@khanacademy/perseus-linter": "^0.3.10",
|
|
30
30
|
"@khanacademy/pure-markdown": "^0.2.13",
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import _ from "underscore";
|
|
6
6
|
|
|
7
|
+
import {Graphie} from "../../util/graphie";
|
|
7
8
|
import Movable from "../movable";
|
|
8
9
|
|
|
9
10
|
const createMock = function (): any {
|
|
10
|
-
const movable = new Movable(
|
|
11
|
+
const movable = new Movable(new Graphie(document.createElement("div")), {
|
|
11
12
|
mouseTarget: null,
|
|
12
13
|
});
|
|
13
14
|
movable.modify = function (options: any) {
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/* eslint-disable testing-library/prefer-user-event */
|
|
2
|
+
import {fireEvent} from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
import {Graphie} from "../../util/graphie";
|
|
5
|
+
import Movable from "../movable";
|
|
6
|
+
|
|
7
|
+
// Yay for side-effect imports!
|
|
8
|
+
// eslint-disable-next-line import/no-unassigned-import
|
|
9
|
+
import "../../util/interactive";
|
|
10
|
+
|
|
11
|
+
describe("Movable", () => {
|
|
12
|
+
it("should be constructable", () => {
|
|
13
|
+
const movable = new Movable(
|
|
14
|
+
new Graphie(document.createElement("div")),
|
|
15
|
+
{},
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
expect(movable).toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should have accessor properties", () => {
|
|
22
|
+
const movable = new Movable(
|
|
23
|
+
new Graphie(document.createElement("div")),
|
|
24
|
+
{},
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
expect(typeof movable.isHovering).toBe("function");
|
|
28
|
+
expect(typeof movable.cursor).toBe("function");
|
|
29
|
+
expect(typeof movable.added).toBe("function");
|
|
30
|
+
expect(typeof movable.isHovering).toBe("function");
|
|
31
|
+
expect(typeof movable.isMouseOver).toBe("function");
|
|
32
|
+
expect(typeof movable.isDragging).toBe("function");
|
|
33
|
+
expect(typeof movable.mouseTarget).toBe("function");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("cloneState()", () => {
|
|
37
|
+
const movable = new Movable(
|
|
38
|
+
new Graphie(document.createElement("div")),
|
|
39
|
+
{},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
expect(movable.cloneState()).toEqual(
|
|
43
|
+
expect.objectContaining({
|
|
44
|
+
id: expect.any(String),
|
|
45
|
+
add: [],
|
|
46
|
+
added: true,
|
|
47
|
+
cursor: null,
|
|
48
|
+
draw: [],
|
|
49
|
+
isDragging: false,
|
|
50
|
+
isHovering: false,
|
|
51
|
+
isMouseOver: false,
|
|
52
|
+
modify: [],
|
|
53
|
+
mouseTarget: null,
|
|
54
|
+
onClick: [],
|
|
55
|
+
onMove: [],
|
|
56
|
+
onMoveEnd: [],
|
|
57
|
+
onMoveStart: [],
|
|
58
|
+
remove: [],
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("cloneState() should copy state", () => {
|
|
64
|
+
const movable = new Movable(
|
|
65
|
+
new Graphie(document.createElement("div")),
|
|
66
|
+
{},
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(movable.cloneState()).not.toBe(movable.state);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("modify() resets movable to initial state when no params given", () => {
|
|
73
|
+
const movable = new Movable(
|
|
74
|
+
new Graphie(document.createElement("div")),
|
|
75
|
+
{},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const state = movable.cloneState();
|
|
79
|
+
movable.state.added = false;
|
|
80
|
+
|
|
81
|
+
// It seems that all usages of this function in our production code
|
|
82
|
+
// pass the original state + mutations to modify(). However, it _does_
|
|
83
|
+
// work to pass no arguments so we'll keep this Characterization test
|
|
84
|
+
// here for now.
|
|
85
|
+
movable.modify();
|
|
86
|
+
|
|
87
|
+
expect(movable.cloneState()).toEqual(state);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("modify() retains original ID", () => {
|
|
91
|
+
const movable = new Movable(
|
|
92
|
+
new Graphie(document.createElement("div")),
|
|
93
|
+
{},
|
|
94
|
+
);
|
|
95
|
+
const id = movable.state.id;
|
|
96
|
+
|
|
97
|
+
movable.modify({added: true});
|
|
98
|
+
|
|
99
|
+
expect(movable.state.id).toBe(id);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("grab() fires onMoveStart", () => {
|
|
103
|
+
const graphie = new Graphie(document.createElement("div"));
|
|
104
|
+
graphie.init({
|
|
105
|
+
range: [
|
|
106
|
+
[-10, 10],
|
|
107
|
+
[-10, 10],
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
const onMoveStart = jest.fn();
|
|
111
|
+
const movable = new Movable(graphie, {onMoveStart});
|
|
112
|
+
|
|
113
|
+
movable.grab([0, 0]);
|
|
114
|
+
|
|
115
|
+
expect(onMoveStart).toHaveBeenCalledWith([0, 0], [0, 0]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("grab() fires onMove when mouse moves", () => {
|
|
119
|
+
const graphie = new Graphie(document.createElement("div"));
|
|
120
|
+
graphie.init({
|
|
121
|
+
range: [
|
|
122
|
+
[-10, 10],
|
|
123
|
+
[-10, 10],
|
|
124
|
+
],
|
|
125
|
+
scale: [10, 10],
|
|
126
|
+
});
|
|
127
|
+
const onMove = jest.fn();
|
|
128
|
+
const movable = new Movable(graphie, {onMove});
|
|
129
|
+
|
|
130
|
+
movable.grab([0, 0]);
|
|
131
|
+
fireEvent.mouseMove(document, {clientX: 120, clientY: 80});
|
|
132
|
+
|
|
133
|
+
expect(onMove).toHaveBeenCalledWith([2, 2], [0, 0]);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("grab() fires onMoveEnd when mouse up", () => {
|
|
137
|
+
const dummyGraphie: any = {};
|
|
138
|
+
const onMoveEnd = jest.fn();
|
|
139
|
+
const movable = new Movable(dummyGraphie, {onMoveEnd});
|
|
140
|
+
|
|
141
|
+
movable.grab([0, 0]);
|
|
142
|
+
fireEvent.mouseUp(document);
|
|
143
|
+
|
|
144
|
+
expect(onMoveEnd).toHaveBeenCalledWith([0, 0], [0, 0]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("grab() fires onClick if still hovering when mouse up", () => {
|
|
148
|
+
const dummyGraphie: any = {};
|
|
149
|
+
const onClick = jest.fn();
|
|
150
|
+
const movable = new Movable(dummyGraphie, {onClick});
|
|
151
|
+
|
|
152
|
+
movable.grab([0, 0]);
|
|
153
|
+
fireEvent.mouseUp(document);
|
|
154
|
+
|
|
155
|
+
expect(onClick).toHaveBeenCalledWith([0, 0], [0, 0]);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import InteractiveUtil from "./interactive-util";
|
|
2
|
+
|
|
3
|
+
describe("normalizeOptions", () => {
|
|
4
|
+
it("wraps single functions in arrays", () => {
|
|
5
|
+
const options = {
|
|
6
|
+
add: () => {},
|
|
7
|
+
modify: () => {},
|
|
8
|
+
draw: () => {},
|
|
9
|
+
remove: () => {},
|
|
10
|
+
constraints: () => {},
|
|
11
|
+
onMoveStart: () => {},
|
|
12
|
+
onMove: () => {},
|
|
13
|
+
onMoveEnd: () => {},
|
|
14
|
+
onClick: () => {},
|
|
15
|
+
};
|
|
16
|
+
expect(InteractiveUtil.normalizeOptions(options)).toEqual({
|
|
17
|
+
add: [options.add],
|
|
18
|
+
modify: [options.modify],
|
|
19
|
+
draw: [options.draw],
|
|
20
|
+
remove: [options.remove],
|
|
21
|
+
constraints: [options.constraints],
|
|
22
|
+
onMoveStart: [options.onMoveStart],
|
|
23
|
+
onMove: [options.onMove],
|
|
24
|
+
onMoveEnd: [options.onMoveEnd],
|
|
25
|
+
onClick: [options.onClick],
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("tolerates missing properties", () => {
|
|
30
|
+
const options = {
|
|
31
|
+
add: () => {},
|
|
32
|
+
};
|
|
33
|
+
expect(InteractiveUtil.normalizeOptions(options)).toEqual({
|
|
34
|
+
add: [options.add],
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("ignores extra properties", () => {
|
|
39
|
+
const options = {
|
|
40
|
+
foo: () => {},
|
|
41
|
+
};
|
|
42
|
+
expect(InteractiveUtil.normalizeOptions(options)).toEqual({
|
|
43
|
+
foo: options.foo,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("does not modify its argument", () => {
|
|
48
|
+
const options = {
|
|
49
|
+
add: () => {},
|
|
50
|
+
};
|
|
51
|
+
const optionsCopy = {...options};
|
|
52
|
+
InteractiveUtil.normalizeOptions(options);
|
|
53
|
+
expect(options).toEqual(optionsCopy);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("leaves existing arrays of functions alone", () => {
|
|
57
|
+
const options = {
|
|
58
|
+
add: [() => {}],
|
|
59
|
+
modify: [() => {}],
|
|
60
|
+
draw: [() => {}],
|
|
61
|
+
remove: [() => {}],
|
|
62
|
+
onMoveStart: [() => {}],
|
|
63
|
+
onMove: [() => {}],
|
|
64
|
+
onMoveEnd: [() => {}],
|
|
65
|
+
onClick: [() => {}],
|
|
66
|
+
};
|
|
67
|
+
expect(InteractiveUtil.normalizeOptions(options)).toEqual(options);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("flattens arrays", () => {
|
|
71
|
+
const dummyFunction = () => {};
|
|
72
|
+
const options = {
|
|
73
|
+
add: [[dummyFunction]],
|
|
74
|
+
modify: [[dummyFunction]],
|
|
75
|
+
draw: [[dummyFunction]],
|
|
76
|
+
remove: [[dummyFunction]],
|
|
77
|
+
onMoveStart: [[dummyFunction]],
|
|
78
|
+
onMove: [[dummyFunction]],
|
|
79
|
+
onMoveEnd: [[dummyFunction]],
|
|
80
|
+
onClick: [[dummyFunction]],
|
|
81
|
+
};
|
|
82
|
+
expect(InteractiveUtil.normalizeOptions(options)).toEqual({
|
|
83
|
+
add: [dummyFunction],
|
|
84
|
+
modify: [dummyFunction],
|
|
85
|
+
draw: [dummyFunction],
|
|
86
|
+
remove: [dummyFunction],
|
|
87
|
+
onMoveStart: [dummyFunction],
|
|
88
|
+
onMove: [dummyFunction],
|
|
89
|
+
onMoveEnd: [dummyFunction],
|
|
90
|
+
onClick: [dummyFunction],
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -52,6 +52,18 @@ function computeCanUse3dTransform(): boolean {
|
|
|
52
52
|
return !!el.style[prefix];
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
const FUNCTION_ARRAY_OPTIONS = [
|
|
56
|
+
"add",
|
|
57
|
+
"modify",
|
|
58
|
+
"draw",
|
|
59
|
+
"remove",
|
|
60
|
+
"constraints",
|
|
61
|
+
"onMoveStart",
|
|
62
|
+
"onMove",
|
|
63
|
+
"onMoveEnd",
|
|
64
|
+
"onClick",
|
|
65
|
+
];
|
|
66
|
+
|
|
55
67
|
const InteractiveUtil = {
|
|
56
68
|
assert: function (isTrue: boolean, message?: string) {
|
|
57
69
|
if (!isTrue) {
|
|
@@ -106,24 +118,16 @@ const InteractiveUtil = {
|
|
|
106
118
|
/**
|
|
107
119
|
* Convert all function-or-array arguments to arrays of functions
|
|
108
120
|
*/
|
|
109
|
-
normalizeOptions: function (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
): Record<any, any> {
|
|
113
|
-
// TODO(jack): Having to clone here is annoying; this
|
|
114
|
-
// function should really just modify this.state in place
|
|
115
|
-
// (and maybe be a function on MovableHelperMethods to get access
|
|
116
|
-
// to this.state), which would also be nicer because we could
|
|
117
|
-
// normalizeOptions once in this.modify
|
|
118
|
-
const result = _.clone(options);
|
|
119
|
-
_.each(arrayOptionNames, function (eventName) {
|
|
120
|
-
const funcOrArray = options[eventName];
|
|
121
|
+
normalizeOptions: function (options: Record<any, any>): Record<any, any> {
|
|
122
|
+
const result = {...options};
|
|
123
|
+
FUNCTION_ARRAY_OPTIONS.forEach(function (eventName) {
|
|
121
124
|
// Only propagate values which were set; not present values
|
|
122
125
|
// shouldn't be added to options because we'd like them to
|
|
123
126
|
// fall through to defaults
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
if (options[eventName] !== undefined) {
|
|
128
|
+
result[eventName] = InteractiveUtil.arrayify(
|
|
129
|
+
options[eventName],
|
|
130
|
+
);
|
|
127
131
|
}
|
|
128
132
|
});
|
|
129
133
|
return result;
|
|
@@ -27,8 +27,13 @@ const MovableHelperMethods: any = {
|
|
|
27
27
|
/**
|
|
28
28
|
* Fire an onSomething type event to all functions in listeners
|
|
29
29
|
*/
|
|
30
|
-
_fireEvent: function (
|
|
31
|
-
|
|
30
|
+
_fireEvent: function <F extends (...args: any[]) => any>(
|
|
31
|
+
listeners: F[],
|
|
32
|
+
...args: Parameters<F>
|
|
33
|
+
) {
|
|
34
|
+
for (const listener of listeners) {
|
|
35
|
+
listener.call(this, ...args);
|
|
36
|
+
}
|
|
32
37
|
},
|
|
33
38
|
|
|
34
39
|
/**
|
|
@@ -14,16 +14,6 @@ import WrappedLine from "./wrapped-line";
|
|
|
14
14
|
const assert = InteractiveUtil.assert;
|
|
15
15
|
const normalizeOptions = InteractiveUtil.normalizeOptions;
|
|
16
16
|
|
|
17
|
-
const FUNCTION_ARRAY_OPTIONS = [
|
|
18
|
-
"add",
|
|
19
|
-
"draw",
|
|
20
|
-
"remove",
|
|
21
|
-
"onMoveStart",
|
|
22
|
-
"constraints",
|
|
23
|
-
"onMove",
|
|
24
|
-
"onMoveEnd",
|
|
25
|
-
];
|
|
26
|
-
|
|
27
17
|
// Default "props" and "state". Both are added to this.state and
|
|
28
18
|
// receive magic getter methods (this.cursor() etc).
|
|
29
19
|
// However, properties in DEFAULT_PROPS are updated on `modify()`,
|
|
@@ -87,7 +77,6 @@ _.extend(MovableLine.prototype, {
|
|
|
87
77
|
id: this.state.id,
|
|
88
78
|
},
|
|
89
79
|
normalizeOptions(
|
|
90
|
-
FUNCTION_ARRAY_OPTIONS,
|
|
91
80
|
// Defaults are copied from MovableLineOptions.*.standard
|
|
92
81
|
// These defaults are set here instead of DEFAULT_PROPS/STATE
|
|
93
82
|
// because they:
|
|
@@ -121,7 +110,7 @@ _.extend(MovableLine.prototype, {
|
|
|
121
110
|
const graphie = this.graphie;
|
|
122
111
|
const state = (self.state = _.extend(
|
|
123
112
|
self.state,
|
|
124
|
-
normalizeOptions(
|
|
113
|
+
normalizeOptions(options),
|
|
125
114
|
));
|
|
126
115
|
|
|
127
116
|
// Default things inside the state.normalStyle object, because
|
|
@@ -69,10 +69,6 @@ const normalizeOptions = InteractiveUtil.normalizeOptions;
|
|
|
69
69
|
|
|
70
70
|
const {processMath} = Tex;
|
|
71
71
|
|
|
72
|
-
// State parameters that should be converted into an array of
|
|
73
|
-
// functions
|
|
74
|
-
const FUNCTION_ARRAY_OPTIONS = _.keys(MovablePointOptions);
|
|
75
|
-
|
|
76
72
|
// Default "props" and "state". Both are added to this.state and
|
|
77
73
|
// receive magic getter methods (this.coord() etc).
|
|
78
74
|
// However, properties in DEFAULT_PROPS are updated on `modify()`,
|
|
@@ -134,7 +130,6 @@ _.extend(MovablePoint.prototype, {
|
|
|
134
130
|
id: this.state.id,
|
|
135
131
|
},
|
|
136
132
|
normalizeOptions(
|
|
137
|
-
FUNCTION_ARRAY_OPTIONS,
|
|
138
133
|
// Defaults are copied from MovablePointOptions.*.standard
|
|
139
134
|
// These defaults are set here instead of DEFAULT_PROPS/STATE
|
|
140
135
|
// because they:
|
|
@@ -216,10 +211,7 @@ _.extend(MovablePoint.prototype, {
|
|
|
216
211
|
update: function (options) {
|
|
217
212
|
const self = this;
|
|
218
213
|
const graphie = self.graphie;
|
|
219
|
-
const state = _.extend(
|
|
220
|
-
self.state,
|
|
221
|
-
normalizeOptions(FUNCTION_ARRAY_OPTIONS, options),
|
|
222
|
-
);
|
|
214
|
+
const state = _.extend(self.state, normalizeOptions(options));
|
|
223
215
|
|
|
224
216
|
assert(kpoint.is(state.coord));
|
|
225
217
|
|
|
@@ -15,10 +15,6 @@ import objective_ from "./objective_";
|
|
|
15
15
|
const assert = InteractiveUtil.assert;
|
|
16
16
|
const normalizeOptions = InteractiveUtil.normalizeOptions;
|
|
17
17
|
|
|
18
|
-
// State parameters that should be converted into an array of
|
|
19
|
-
// functions
|
|
20
|
-
const FUNCTION_ARRAY_OPTIONS = _.keys(MovablePolygonOptions);
|
|
21
|
-
|
|
22
18
|
// Default "props" and "state". Both are added to this.state and
|
|
23
19
|
// receive magic getter methods (this.points() etc).
|
|
24
20
|
// However, properties in DEFAULT_PROPS are updated on `modify()`,
|
|
@@ -89,7 +85,6 @@ _.extend(MovablePolygon.prototype, {
|
|
|
89
85
|
id: this.state.id,
|
|
90
86
|
},
|
|
91
87
|
normalizeOptions(
|
|
92
|
-
FUNCTION_ARRAY_OPTIONS,
|
|
93
88
|
// Defaults are copied from MovablePolygonOptions.*.standard
|
|
94
89
|
// These defaults are set here instead of DEFAULT_PROPS/STATE
|
|
95
90
|
// because they:
|
|
@@ -124,10 +119,7 @@ _.extend(MovablePolygon.prototype, {
|
|
|
124
119
|
update: function (options) {
|
|
125
120
|
const self = this;
|
|
126
121
|
const graphie = self.graphie;
|
|
127
|
-
const state = _.extend(
|
|
128
|
-
self.state,
|
|
129
|
-
normalizeOptions(FUNCTION_ARRAY_OPTIONS, options),
|
|
130
|
-
);
|
|
122
|
+
const state = _.extend(self.state, normalizeOptions(options));
|
|
131
123
|
|
|
132
124
|
// Default things inside the state.normalStyle object, because
|
|
133
125
|
// _.extend is not deep.
|
|
@@ -15,23 +15,13 @@ import _ from "underscore";
|
|
|
15
15
|
|
|
16
16
|
import InteractiveUtil from "./interactive-util";
|
|
17
17
|
|
|
18
|
+
import type {Coord} from "./types";
|
|
19
|
+
import type {Graphie} from "../util/graphie";
|
|
20
|
+
|
|
18
21
|
const normalizeOptions = InteractiveUtil.normalizeOptions;
|
|
19
22
|
|
|
20
23
|
const assert = InteractiveUtil.assert;
|
|
21
24
|
|
|
22
|
-
// state parameters that should be converted into an array of
|
|
23
|
-
// functions
|
|
24
|
-
const FUNCTION_ARRAY_OPTIONS = [
|
|
25
|
-
"add",
|
|
26
|
-
"modify",
|
|
27
|
-
"draw",
|
|
28
|
-
"remove",
|
|
29
|
-
"onMoveStart",
|
|
30
|
-
"onMove",
|
|
31
|
-
"onMoveEnd",
|
|
32
|
-
"onClick",
|
|
33
|
-
];
|
|
34
|
-
|
|
35
25
|
// Default "props" and "state". Both are added to this.state and
|
|
36
26
|
// receive magic getter methods (this.isHovering() etc).
|
|
37
27
|
// However, properties in DEFAULT_PROPS are updated on `modify()`,
|
|
@@ -49,10 +39,30 @@ const DEFAULT_STATE = {
|
|
|
49
39
|
mouseTarget: null,
|
|
50
40
|
} as const;
|
|
51
41
|
|
|
52
|
-
|
|
42
|
+
export interface State {
|
|
43
|
+
added?: boolean;
|
|
44
|
+
isHovering?: boolean;
|
|
45
|
+
isMouseOver?: boolean;
|
|
46
|
+
isDragging?: boolean;
|
|
47
|
+
// TODO(benchristel): improve types
|
|
48
|
+
mouseTarget?: unknown | null;
|
|
49
|
+
// TODO(benchristel): improve types
|
|
50
|
+
cursor: unknown | null;
|
|
51
|
+
id: string;
|
|
52
|
+
add: (() => void)[];
|
|
53
|
+
modify: (() => void)[];
|
|
54
|
+
draw: (() => void)[];
|
|
55
|
+
remove: (() => void)[];
|
|
56
|
+
onMoveStart: ((position: Coord) => void)[];
|
|
57
|
+
onMove: ((end: Coord, start: Coord) => void)[];
|
|
58
|
+
onMoveEnd: ((end: Coord, start: Coord) => void)[];
|
|
59
|
+
onClick: ((position: Coord, start: Coord) => void)[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const Movable = function (graphie: Graphie, options: any): void {
|
|
53
63
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
54
64
|
_.extend(this, {
|
|
55
|
-
graphie
|
|
65
|
+
graphie,
|
|
56
66
|
state: {
|
|
57
67
|
// Set here because this must be unique for each instance
|
|
58
68
|
id: _.uniqueId("movable"),
|
|
@@ -71,28 +81,26 @@ InteractiveUtil.createGettersFor(
|
|
|
71
81
|
InteractiveUtil.addMovableHelperMethodsTo(Movable);
|
|
72
82
|
|
|
73
83
|
_.extend(Movable.prototype, {
|
|
74
|
-
cloneState: function () {
|
|
75
|
-
return
|
|
84
|
+
cloneState: function (): State {
|
|
85
|
+
return {...this.state};
|
|
76
86
|
},
|
|
77
87
|
|
|
78
|
-
_createDefaultState: function () {
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
DEFAULT_PROPS,
|
|
95
|
-
);
|
|
88
|
+
_createDefaultState: function (): State {
|
|
89
|
+
return {
|
|
90
|
+
id: this.state.id,
|
|
91
|
+
add: [],
|
|
92
|
+
modify: [],
|
|
93
|
+
draw: [],
|
|
94
|
+
remove: [],
|
|
95
|
+
onMoveStart: [],
|
|
96
|
+
onMove: [],
|
|
97
|
+
onMoveEnd: [],
|
|
98
|
+
onClick: [],
|
|
99
|
+
|
|
100
|
+
// We only update props here, because we want things on state to
|
|
101
|
+
// be persistent, and updated appropriately in modify()
|
|
102
|
+
...DEFAULT_PROPS,
|
|
103
|
+
};
|
|
96
104
|
},
|
|
97
105
|
|
|
98
106
|
/**
|
|
@@ -101,18 +109,18 @@ _.extend(Movable.prototype, {
|
|
|
101
109
|
*
|
|
102
110
|
* Analogous to React.js's replaceProps
|
|
103
111
|
*/
|
|
104
|
-
modify: function (options) {
|
|
105
|
-
this.update(
|
|
112
|
+
modify: function (options: Partial<State>) {
|
|
113
|
+
this.update({...this._createDefaultState(), ...options});
|
|
106
114
|
},
|
|
107
115
|
|
|
108
116
|
/**
|
|
109
117
|
* Simulates a mouse grab event on the movable object.
|
|
110
118
|
*/
|
|
111
|
-
grab: function (coord) {
|
|
119
|
+
grab: function (coord: Coord) {
|
|
112
120
|
assert(kpoint.is(coord));
|
|
113
121
|
const self = this;
|
|
114
122
|
const graphie = self.graphie;
|
|
115
|
-
const state = self.state;
|
|
123
|
+
const state: State = self.state;
|
|
116
124
|
|
|
117
125
|
state.isHovering = true;
|
|
118
126
|
state.isDragging = true;
|
|
@@ -154,15 +162,12 @@ _.extend(Movable.prototype, {
|
|
|
154
162
|
*
|
|
155
163
|
* Analogous to React.js's setProps
|
|
156
164
|
*/
|
|
157
|
-
update: function (options) {
|
|
165
|
+
update: function (options: State) {
|
|
158
166
|
const self = this;
|
|
159
167
|
const graphie = self.graphie;
|
|
160
168
|
|
|
161
169
|
const prevState = self.cloneState();
|
|
162
|
-
const state = _.extend(
|
|
163
|
-
self.state,
|
|
164
|
-
normalizeOptions(FUNCTION_ARRAY_OPTIONS, options),
|
|
165
|
-
);
|
|
170
|
+
const state = _.extend(self.state, normalizeOptions(options));
|
|
166
171
|
|
|
167
172
|
// the invisible shape in front of the point that gets mouse events
|
|
168
173
|
if (state.mouseTarget && !prevState.mouseTarget) {
|
package/src/util/graphie.ts
CHANGED
|
@@ -121,13 +121,15 @@ export class Graphie {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
init(options: {
|
|
124
|
-
range
|
|
124
|
+
range: [Interval, Interval];
|
|
125
125
|
scale?: number | Coord;
|
|
126
126
|
isMobile?: boolean;
|
|
127
127
|
}) {
|
|
128
128
|
let scale = options.scale || [40, 40];
|
|
129
129
|
scale = typeof scale === "number" ? [scale, scale] : scale;
|
|
130
130
|
|
|
131
|
+
// TODO(jeremy): This can be removed once we're confident that callers
|
|
132
|
+
// obey the Typescript types.
|
|
131
133
|
if (options.range == null) {
|
|
132
134
|
throw new PerseusError(
|
|
133
135
|
"range should be specified in graph init",
|