@khanacademy/perseus 19.1.3 → 19.1.5
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 +21 -0
- package/dist/components/graphie.d.ts +9 -1
- package/dist/es/index.js +275 -174
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +289 -158
- package/dist/index.js.map +1 -1
- package/dist/interactive2/interactive-util.d.ts +2 -2
- package/dist/interactive2/movable-point-options.d.ts +5 -4
- package/dist/interactive2/movable.d.ts +98 -2
- package/dist/interactive2/types.d.ts +6 -0
- package/dist/util/graphie.d.ts +6 -1
- package/dist/widgets/expression.d.ts +4 -4
- package/package.json +3 -3
- package/src/components/graphie.tsx +9 -1
- package/src/interactive2/__tests__/movable-mock.ts +43 -30
- 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-options.ts +31 -19
- package/src/interactive2/movable-point.tsx +1 -9
- package/src/interactive2/movable-polygon.ts +1 -9
- package/src/interactive2/movable.test.ts +249 -0
- package/src/interactive2/movable.ts +313 -150
- package/src/interactive2/types.ts +15 -0
- package/src/interactive2.ts +1 -1
- package/src/util/graphie.ts +13 -1
- package/src/util/interactive.ts +4 -2
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -14,11 +14,11 @@ declare const InteractiveUtil: {
|
|
|
14
14
|
/**
|
|
15
15
|
* Turn a function or an array of functions into an array of functions
|
|
16
16
|
*/
|
|
17
|
-
readonly arrayify: (funcOrArray:
|
|
17
|
+
readonly arrayify: (funcOrArray: ((...args: ReadonlyArray<unknown>) => unknown) | readonly unknown[]) => ReadonlyArray<unknown>;
|
|
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,4 +1,5 @@
|
|
|
1
|
-
import type { Coord } from "./types";
|
|
1
|
+
import type { Constraint, Coord } from "./types";
|
|
2
|
+
import type { Interval } from "../util/interval";
|
|
2
3
|
declare const _default: {
|
|
3
4
|
add: {
|
|
4
5
|
readonly constrain: () => void;
|
|
@@ -17,9 +18,9 @@ declare const _default: {
|
|
|
17
18
|
standard: null;
|
|
18
19
|
};
|
|
19
20
|
constraints: {
|
|
20
|
-
readonly fixed: () =>
|
|
21
|
-
readonly snap: (snap
|
|
22
|
-
readonly bound: (range
|
|
21
|
+
readonly fixed: () => Constraint;
|
|
22
|
+
readonly snap: (snap?: number | null | undefined) => Constraint;
|
|
23
|
+
readonly bound: (range?: [Interval, Interval] | undefined, snap?: number | Coord | undefined, optionalPaddingPx?: number | undefined) => Constraint;
|
|
23
24
|
};
|
|
24
25
|
onMove: {
|
|
25
26
|
standard: null;
|
|
@@ -1,2 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { Constraint, ConstraintCallbacks, 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?: {
|
|
9
|
+
toFront(): void;
|
|
10
|
+
toBack(): void;
|
|
11
|
+
remove(): void;
|
|
12
|
+
getMouseTarget(): Element;
|
|
13
|
+
} | null;
|
|
14
|
+
cursor?: null;
|
|
15
|
+
id: string;
|
|
16
|
+
add?: (() => void)[];
|
|
17
|
+
modify?: ((state: State, prevState?: State) => void)[];
|
|
18
|
+
draw?: ((state: State, prevState?: State) => void)[];
|
|
19
|
+
remove?: (() => void)[];
|
|
20
|
+
onMoveStart?: ((position: Coord, positionAgain: Coord) => void)[];
|
|
21
|
+
onMove?: ((end: Coord, start: Coord) => void)[];
|
|
22
|
+
onMoveEnd?: ((end: Coord, start: Coord) => void)[];
|
|
23
|
+
onClick?: ((position: Coord, start: Coord) => void)[];
|
|
24
|
+
constraints?: Constraint[];
|
|
25
|
+
}
|
|
26
|
+
export declare class Movable<Options extends Record<string, any>> {
|
|
27
|
+
graphie: Graphie;
|
|
28
|
+
state: State;
|
|
29
|
+
prevState: State | undefined;
|
|
30
|
+
_listenerMap: Record<string, number>;
|
|
31
|
+
constructor(graphie: Graphie, options: Options);
|
|
32
|
+
modify(options: Options): void;
|
|
33
|
+
_createDefaultState(): {
|
|
34
|
+
cursor: null;
|
|
35
|
+
id: string;
|
|
36
|
+
add: never[];
|
|
37
|
+
modify: never[];
|
|
38
|
+
draw: never[];
|
|
39
|
+
remove: never[];
|
|
40
|
+
onMoveStart: never[];
|
|
41
|
+
onMove: never[];
|
|
42
|
+
onMoveEnd: never[];
|
|
43
|
+
onClick: never[];
|
|
44
|
+
};
|
|
45
|
+
update(options: Options): void;
|
|
46
|
+
cloneState(): State;
|
|
47
|
+
/**
|
|
48
|
+
* Fire an onSomething type event to all functions in listeners
|
|
49
|
+
*/
|
|
50
|
+
_fireEvent<F extends (...args: any[]) => any>(listeners: F[] | undefined, ...args: Parameters<F>): void;
|
|
51
|
+
/**
|
|
52
|
+
* Call all draw functions, and update our prevState for the next
|
|
53
|
+
* draw function
|
|
54
|
+
*/
|
|
55
|
+
draw(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Simulates a mouse grab event on the movable object.
|
|
58
|
+
*/
|
|
59
|
+
grab(coord: Coord): void;
|
|
60
|
+
_applyConstraints(current: Coord, previous: Coord, extraOptions?: ConstraintCallbacks): any;
|
|
61
|
+
toBack(): void;
|
|
62
|
+
toFront(): void;
|
|
63
|
+
/**
|
|
64
|
+
* Add a listener to any event: startMove, constraints, onMove, onMoveEnd,
|
|
65
|
+
* etc. If a listener is already bound to the given eventName and id, then
|
|
66
|
+
* it is overwritten by func.
|
|
67
|
+
*
|
|
68
|
+
* eventName: the string name of the event to listen to. one of:
|
|
69
|
+
* "onMoveStart", "onMove", "onMoveEnd", "draw", "remove"
|
|
70
|
+
*
|
|
71
|
+
* id: a string id that can be used to remove this event at a later time
|
|
72
|
+
* note: adding multiple listeners with the same id is undefined behavior
|
|
73
|
+
*
|
|
74
|
+
* func: the function to call when the event happens, which is called
|
|
75
|
+
* with the event's standard parameters [usually (coord, prevCoord) or
|
|
76
|
+
* (state, prevState)]
|
|
77
|
+
*/
|
|
78
|
+
listen(eventName: string, id: string, func: () => unknown): void;
|
|
79
|
+
/**
|
|
80
|
+
* Remove a previously added listener, by the id specified in the
|
|
81
|
+
* corresponding listen() call
|
|
82
|
+
*
|
|
83
|
+
* If the given id has not been registered already, this is a no-op
|
|
84
|
+
*/
|
|
85
|
+
unlisten(eventName: string, id: string): void;
|
|
86
|
+
remove(): void;
|
|
87
|
+
cursor(): null | undefined;
|
|
88
|
+
added(): boolean | undefined;
|
|
89
|
+
isHovering(): boolean | undefined;
|
|
90
|
+
isMouseOver(): boolean | undefined;
|
|
91
|
+
isDragging(): boolean | undefined;
|
|
92
|
+
mouseTarget(): {
|
|
93
|
+
toFront(): void;
|
|
94
|
+
toBack(): void;
|
|
95
|
+
remove(): void;
|
|
96
|
+
getMouseTarget(): Element;
|
|
97
|
+
} | null | undefined;
|
|
98
|
+
}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
import type { Movable } from "./movable";
|
|
1
2
|
export type Coord = [number, number];
|
|
2
3
|
export type Line = [Coord, Coord];
|
|
4
|
+
export type Constraint = (this: Movable<any>, current: Coord | boolean, previous: Coord, options: ConstraintCallbacks) => Coord | boolean | undefined;
|
|
5
|
+
export type ConstraintCallbacks = {
|
|
6
|
+
onSkipRemaining(): void;
|
|
7
|
+
onOutOfBounds?: () => void;
|
|
8
|
+
};
|
package/dist/util/graphie.d.ts
CHANGED
|
@@ -40,9 +40,10 @@ export declare class Graphie {
|
|
|
40
40
|
dimensions?: Coord;
|
|
41
41
|
xpixels?: number;
|
|
42
42
|
ypixels?: number;
|
|
43
|
+
isDragging?: boolean;
|
|
43
44
|
constructor(el: Element);
|
|
44
45
|
init(options: {
|
|
45
|
-
range
|
|
46
|
+
range: [Interval, Interval];
|
|
46
47
|
scale?: number | Coord;
|
|
47
48
|
isMobile?: boolean;
|
|
48
49
|
}): this;
|
|
@@ -102,6 +103,10 @@ export declare class Graphie {
|
|
|
102
103
|
unscalePoint: (point: Coord) => any;
|
|
103
104
|
unscaleVector: (point: Coord) => any;
|
|
104
105
|
private processAttributes;
|
|
106
|
+
getMouseCoord(event: Readonly<{
|
|
107
|
+
pageX?: number;
|
|
108
|
+
pageY?: number;
|
|
109
|
+
}>): Coord;
|
|
105
110
|
}
|
|
106
111
|
type RangeSpecifier = [Interval, Interval] | Coord | number;
|
|
107
112
|
export declare function normalizeRange(range: RangeSpecifier): [Interval, Interval];
|
|
@@ -212,10 +212,10 @@ declare const _default: Readonly<{
|
|
|
212
212
|
onFocus: (blurPath: import("../types").FocusPath) => void;
|
|
213
213
|
times: boolean;
|
|
214
214
|
value: string;
|
|
215
|
-
}, "
|
|
216
|
-
value?: string | undefined;
|
|
215
|
+
}, "onChange" | "trackInteraction" | "static" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
|
|
217
216
|
onFocus?: ((blurPath: import("../types").FocusPath) => void) | undefined;
|
|
218
217
|
onBlur?: ((blurPath: import("../types").FocusPath) => void) | undefined;
|
|
218
|
+
value?: string | undefined;
|
|
219
219
|
linterContext?: import("@khanacademy/perseus-linter").LinterContextProps | undefined;
|
|
220
220
|
apiOptions?: Readonly<Readonly<{
|
|
221
221
|
isArticle?: boolean | undefined;
|
|
@@ -401,10 +401,10 @@ declare const _default: Readonly<{
|
|
|
401
401
|
onFocus: (blurPath: import("../types").FocusPath) => void;
|
|
402
402
|
times: boolean;
|
|
403
403
|
value: string;
|
|
404
|
-
}, "
|
|
405
|
-
value?: string | undefined;
|
|
404
|
+
}, "onChange" | "trackInteraction" | "static" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
|
|
406
405
|
onFocus?: ((blurPath: import("../types").FocusPath) => void) | undefined;
|
|
407
406
|
onBlur?: ((blurPath: import("../types").FocusPath) => void) | undefined;
|
|
407
|
+
value?: string | undefined;
|
|
408
408
|
linterContext?: import("@khanacademy/perseus-linter").LinterContextProps | undefined;
|
|
409
409
|
apiOptions?: Readonly<Readonly<{
|
|
410
410
|
isArticle?: boolean | undefined;
|
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.5",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@khanacademy/kas": "^0.3.7",
|
|
26
|
-
"@khanacademy/kmath": "^0.1.
|
|
27
|
-
"@khanacademy/math-input": "^17.0.
|
|
26
|
+
"@khanacademy/kmath": "^0.1.11",
|
|
27
|
+
"@khanacademy/math-input": "^17.0.4",
|
|
28
28
|
"@khanacademy/perseus-core": "1.4.1",
|
|
29
29
|
"@khanacademy/perseus-linter": "^0.3.10",
|
|
30
30
|
"@khanacademy/pure-markdown": "^0.2.13",
|
|
@@ -62,6 +62,9 @@ type DefaultProps = {
|
|
|
62
62
|
|
|
63
63
|
interface Movable {
|
|
64
64
|
remove(): void;
|
|
65
|
+
props: unknown;
|
|
66
|
+
modify(graphie: GraphieDrawingContext): "reordered" | undefined;
|
|
67
|
+
toFront(): void;
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
class Graphie extends React.Component<Props> {
|
|
@@ -231,7 +234,12 @@ class Graphie extends React.Component<Props> {
|
|
|
231
234
|
// @ts-expect-error - TS2322 - Type '(children: readonly any[], options: any) => ReactElement<any, string | JSXElementConstructor<any>> | readonly ReactElement<any, string | JSXElementConstructor<any>>[]' is not assignable to type '(children: readonly any[], arg2: any) => ReactElement<any, string | JSXElementConstructor<any>>'.
|
|
232
235
|
_renderMovables: (
|
|
233
236
|
children: ReadonlyArray<any>,
|
|
234
|
-
arg2:
|
|
237
|
+
arg2: {
|
|
238
|
+
nextKey: number;
|
|
239
|
+
graphie: GraphieDrawingContext;
|
|
240
|
+
oldMovables: Record<string, Movable>;
|
|
241
|
+
newMovables: Record<string, Movable>;
|
|
242
|
+
},
|
|
235
243
|
) => React.ReactElement = (children, options) => {
|
|
236
244
|
// Each leaf of `children` is a movable descriptor created by a call to
|
|
237
245
|
// some `GraphieMovable`, such as `MovablePoint`.
|
|
@@ -4,39 +4,52 @@
|
|
|
4
4
|
|
|
5
5
|
import _ from "underscore";
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import {Graphie} from "../../util/graphie";
|
|
8
|
+
import {Movable} from "../movable";
|
|
9
|
+
|
|
10
|
+
import type {Coord} from "@khanacademy/perseus";
|
|
8
11
|
|
|
9
12
|
const createMock = function (): any {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
);
|
|
24
|
-
movable.move = function (...args) {
|
|
25
|
-
const startPoint = _.first(args);
|
|
13
|
+
return new MovableMock(new Graphie(document.createElement("div")), {});
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
class MovableMock extends Movable<Record<string, never>> {
|
|
17
|
+
modify(options: any) {
|
|
18
|
+
// TODO(benchristel): what is mouseTarget? It seems to be unused.
|
|
19
|
+
super.modify(_.omit(options, "mouseTarget"));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
move(...args) {
|
|
23
|
+
const [startPoint, ...rest] = args;
|
|
26
24
|
// TODO(jack): Move these into onMoveStart, onMove, and onMoveEnd
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
this.state.isMouseOver = true;
|
|
26
|
+
this.state.isHovering = true;
|
|
27
|
+
this.onMoveStart(startPoint, startPoint);
|
|
28
|
+
rest.forEach((point, i) => {
|
|
29
|
+
this.state.isDragging = true;
|
|
30
|
+
this.onMove(point, args[i]);
|
|
33
31
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
this.onMoveEnd(_.last(args), startPoint);
|
|
33
|
+
this.state.isDragging = false;
|
|
34
|
+
this.state.isMouseOver = false;
|
|
35
|
+
this.state.isHovering = false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onMoveStart(coord: Coord, prevCoord: Coord) {
|
|
39
|
+
this._fireEvent(this.state.onMoveStart, coord, prevCoord);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
onMove(coord: Coord, prevCoord: Coord) {
|
|
43
|
+
this._fireEvent(this.state.onMove, coord, prevCoord);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
onMoveEnd(coord: Coord, prevCoord: Coord) {
|
|
47
|
+
this._fireEvent(this.state.onMoveEnd, coord, prevCoord);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
onClick(coord: Coord, prevCoord: Coord) {
|
|
51
|
+
this._fireEvent(this.state.onClick, coord, prevCoord);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
41
54
|
|
|
42
55
|
export default createMock;
|
|
@@ -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
|
|
@@ -7,7 +7,8 @@ import _ from "underscore";
|
|
|
7
7
|
|
|
8
8
|
import WrappedEllipse from "./wrapped-ellipse";
|
|
9
9
|
|
|
10
|
-
import type {Coord} from "./types";
|
|
10
|
+
import type {Constraint, ConstraintCallbacks, Coord} from "./types";
|
|
11
|
+
import type {Interval} from "../util/interval";
|
|
11
12
|
|
|
12
13
|
const add = {
|
|
13
14
|
constrain: function () {
|
|
@@ -119,51 +120,62 @@ const remove = {
|
|
|
119
120
|
remove.standard = remove.basic;
|
|
120
121
|
|
|
121
122
|
const constraints = {
|
|
122
|
-
fixed: function ():
|
|
123
|
+
fixed: function (): Constraint {
|
|
123
124
|
return function () {
|
|
124
125
|
return false;
|
|
125
126
|
};
|
|
126
127
|
},
|
|
127
128
|
|
|
128
|
-
snap: function (snap
|
|
129
|
-
return function (coord: Coord)
|
|
129
|
+
snap: function (snap?: number | null): Constraint {
|
|
130
|
+
return function (coord: Coord) {
|
|
130
131
|
if (snap === null) {
|
|
131
|
-
// NOTE(kevinb): this should probably return
|
|
132
|
+
// TODO(benchristel), NOTE(kevinb): this should probably return
|
|
133
|
+
// the original point
|
|
132
134
|
return true;
|
|
133
135
|
}
|
|
134
|
-
// @ts-expect-error -
|
|
135
|
-
|
|
136
|
-
return kpoint.roundTo(coord, snap);
|
|
136
|
+
// @ts-expect-error - TS2339: Property 'snap' does not exist on type 'Graphie'.
|
|
137
|
+
return kpoint.roundTo(coord, snap || this.graphie.snap);
|
|
137
138
|
};
|
|
138
139
|
},
|
|
139
140
|
|
|
140
141
|
bound: function (
|
|
141
|
-
range
|
|
142
|
-
snap
|
|
143
|
-
|
|
144
|
-
):
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
142
|
+
range?: [Interval, Interval],
|
|
143
|
+
snap?: Coord | number,
|
|
144
|
+
optionalPaddingPx?: number,
|
|
145
|
+
): Constraint {
|
|
146
|
+
const paddingPx = (() => {
|
|
147
|
+
if (optionalPaddingPx === undefined) {
|
|
148
|
+
if (range === undefined) {
|
|
149
|
+
return 10;
|
|
150
|
+
} else {
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
148
153
|
} else {
|
|
149
|
-
|
|
154
|
+
return optionalPaddingPx;
|
|
150
155
|
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
})();
|
|
157
|
+
|
|
158
|
+
return function (
|
|
159
|
+
coord: Coord,
|
|
160
|
+
prev: Coord,
|
|
161
|
+
options: ConstraintCallbacks,
|
|
162
|
+
) {
|
|
154
163
|
const graphie = this.graphie;
|
|
155
164
|
range = range || graphie.range;
|
|
156
165
|
|
|
157
166
|
if (snap === undefined) {
|
|
167
|
+
// @ts-expect-error - Property 'snap' does not exist on type 'Graphie'.
|
|
158
168
|
snap = graphie.snap;
|
|
159
169
|
}
|
|
160
170
|
|
|
161
171
|
let lower = graphie.unscalePoint([
|
|
162
172
|
paddingPx,
|
|
173
|
+
// @ts-expect-error - 'graphie.ypixels' is possibly 'undefined'.
|
|
163
174
|
graphie.ypixels - paddingPx,
|
|
164
175
|
]);
|
|
165
176
|
|
|
166
177
|
let upper = graphie.unscalePoint([
|
|
178
|
+
// @ts-expect-error - 'graphie.ypixels' is possibly 'undefined'.
|
|
167
179
|
graphie.xpixels - paddingPx,
|
|
168
180
|
paddingPx,
|
|
169
181
|
]);
|
|
@@ -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
|
|