@khanacademy/perseus 19.1.0 → 19.1.2
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 +18 -0
- package/dist/components/graph.d.ts +7 -6
- package/dist/components/graphie.d.ts +8 -1
- package/dist/es/index.js +36 -38
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +35 -36
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +3 -3
- package/dist/util/graphie.d.ts +13 -6
- package/dist/widgets/measurer.d.ts +4 -4
- package/package.json +3 -3
- package/src/__tests__/widgets.test.ts +2 -3
- package/src/components/graph.tsx +25 -20
- package/src/components/graphie.tsx +19 -14
- package/src/perseus-types.ts +3 -3
- package/src/util/graphie.test.ts +1 -15
- package/src/util/graphie.ts +14 -24
- package/src/util/interactive.ts +2 -2
- package/src/widgets/measurer.tsx +8 -2
- package/src/widgets/plotter.tsx +2 -0
- package/src/widgets.ts +5 -3
- package/tsconfig-build.tsbuildinfo +1 -1
package/dist/perseus-types.d.ts
CHANGED
|
@@ -505,16 +505,16 @@ export type PerseusInteractionGraph = {
|
|
|
505
505
|
box: Size;
|
|
506
506
|
labels: ReadonlyArray<string>;
|
|
507
507
|
range: [Range, Range];
|
|
508
|
-
gridStep:
|
|
508
|
+
gridStep: [number, number];
|
|
509
509
|
markings: string;
|
|
510
|
-
snapStep?:
|
|
510
|
+
snapStep?: [number, number];
|
|
511
511
|
valid?: boolean | string;
|
|
512
512
|
backgroundImage?: PerseusImageBackground;
|
|
513
513
|
showProtractor?: boolean;
|
|
514
514
|
showRuler?: boolean;
|
|
515
515
|
rulerLabel?: string;
|
|
516
516
|
rulerTicks?: number;
|
|
517
|
-
tickStep:
|
|
517
|
+
tickStep: [number, number];
|
|
518
518
|
};
|
|
519
519
|
export type PerseusInteractionElement = {
|
|
520
520
|
type: "function";
|
package/dist/util/graphie.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ interface LabelMethod {
|
|
|
31
31
|
}
|
|
32
32
|
export declare class Graphie {
|
|
33
33
|
#private;
|
|
34
|
-
el:
|
|
34
|
+
el: Element;
|
|
35
35
|
raphael?: any;
|
|
36
36
|
isMobile: boolean;
|
|
37
37
|
currentStyle: any;
|
|
@@ -40,7 +40,7 @@ export declare class Graphie {
|
|
|
40
40
|
dimensions?: Coord;
|
|
41
41
|
xpixels?: number;
|
|
42
42
|
ypixels?: number;
|
|
43
|
-
constructor(el:
|
|
43
|
+
constructor(el: Element);
|
|
44
44
|
init(options: {
|
|
45
45
|
range?: [Interval, Interval];
|
|
46
46
|
scale?: number | Coord;
|
|
@@ -51,7 +51,7 @@ export declare class Graphie {
|
|
|
51
51
|
grid?: boolean;
|
|
52
52
|
gridRange?: [Interval, Interval] | Coord;
|
|
53
53
|
scale: Coord | number;
|
|
54
|
-
axes?:
|
|
54
|
+
axes?: boolean;
|
|
55
55
|
axisArrows?: "<->" | "->" | true | "";
|
|
56
56
|
axisOpacity?: number;
|
|
57
57
|
axisCenter?: Coord;
|
|
@@ -73,7 +73,7 @@ export declare class Graphie {
|
|
|
73
73
|
}): void;
|
|
74
74
|
private drawingTransform;
|
|
75
75
|
private bounds;
|
|
76
|
-
style(attrs: any, fn:
|
|
76
|
+
style<T>(attrs: any, fn?: (this: Graphie) => T): T | undefined;
|
|
77
77
|
grid(xr: Interval, yr: Interval, style?: StyleParams): unknown;
|
|
78
78
|
arc(center: Coord, radius: Coord, startAngle: number, endAngle: number, sector: boolean, style?: StyleParams): RaphaelElement;
|
|
79
79
|
circle(center: Coord, radius: number, style?: StyleParams): any;
|
|
@@ -92,7 +92,7 @@ export declare class Graphie {
|
|
|
92
92
|
plotParametric(fn: (t: number) => Coord, range: Interval, style?: StyleParams): RaphaelElement;
|
|
93
93
|
plot(fn: (x: number) => number, range: Interval, style?: StyleParams): RaphaelElement;
|
|
94
94
|
svgPath: (points: (Coord | true)[], alreadyScaled?: boolean) => string;
|
|
95
|
-
svgParabolaPath: (a:
|
|
95
|
+
svgParabolaPath: (a: number, b: number, c: number) => string;
|
|
96
96
|
svgSinusoidPath: (a: number, b: number, c: number, d: number) => string;
|
|
97
97
|
private withStyle;
|
|
98
98
|
private postprocessDrawingResult;
|
|
@@ -106,5 +106,12 @@ export declare class Graphie {
|
|
|
106
106
|
type RangeSpecifier = [Interval, Interval] | Coord | number;
|
|
107
107
|
export declare function normalizeRange(range: RangeSpecifier): [Interval, Interval];
|
|
108
108
|
export declare function normalizeRange(range: RangeSpecifier | undefined): [Interval, Interval] | undefined;
|
|
109
|
-
declare const GraphUtils:
|
|
109
|
+
declare const GraphUtils: {
|
|
110
|
+
Graphie: typeof Graphie;
|
|
111
|
+
createGraphie: (el: Element) => Graphie;
|
|
112
|
+
unscaledSvgPath: (points: (Coord | true)[]) => string;
|
|
113
|
+
getDistance: (point1: Coord, point2: Coord) => number;
|
|
114
|
+
findAngle: (point1: Coord, point2: Coord, vertex?: Coord) => any;
|
|
115
|
+
graphs: {};
|
|
116
|
+
};
|
|
110
117
|
export default GraphUtils;
|
|
@@ -4,10 +4,10 @@ declare const _default: Readonly<{
|
|
|
4
4
|
getWidget?: (() => any) | undefined;
|
|
5
5
|
accessible?: boolean | ((props: any) => boolean) | undefined;
|
|
6
6
|
hidden?: boolean | undefined;
|
|
7
|
-
version?: import("
|
|
8
|
-
supportedAlignments?: readonly import("
|
|
9
|
-
defaultAlignment?: import("
|
|
10
|
-
getDefaultAlignment?: (() => import("
|
|
7
|
+
version?: import("@khanacademy/perseus").Version | undefined;
|
|
8
|
+
supportedAlignments?: readonly import("@khanacademy/perseus").Alignment[] | undefined;
|
|
9
|
+
defaultAlignment?: import("@khanacademy/perseus").Alignment | undefined;
|
|
10
|
+
getDefaultAlignment?: (() => import("@khanacademy/perseus").Alignment) | undefined;
|
|
11
11
|
isLintable?: boolean | undefined;
|
|
12
12
|
tracking?: import("../types").Tracking | undefined;
|
|
13
13
|
traverseChildWidgets?: any;
|
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.2",
|
|
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.10",
|
|
27
|
+
"@khanacademy/math-input": "^17.0.1",
|
|
28
28
|
"@khanacademy/perseus-core": "1.4.1",
|
|
29
29
|
"@khanacademy/perseus-linter": "^0.3.10",
|
|
30
30
|
"@khanacademy/pure-markdown": "^0.2.13",
|
|
@@ -85,8 +85,7 @@ describe("replaceWidget", () => {
|
|
|
85
85
|
expect(Widgets.getWidget("transformer")?.name).toBe("Radio");
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
it("
|
|
89
|
-
Widgets.replaceWidget("radio", "dog-cat");
|
|
90
|
-
expect(Widgets.getWidget("radio")?.name).toBe("Radio");
|
|
88
|
+
it("Throws when the replacement isn't available", () => {
|
|
89
|
+
expect(() => Widgets.replaceWidget("radio", "dog-cat")).toThrow();
|
|
91
90
|
});
|
|
92
91
|
});
|
package/src/components/graph.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
2
2
|
/* eslint-disable react/no-unsafe */
|
|
3
|
+
import {vector as kvector} from "@khanacademy/kmath";
|
|
3
4
|
import $ from "jquery";
|
|
4
5
|
import * as React from "react";
|
|
5
6
|
import ReactDOM from "react-dom";
|
|
@@ -14,6 +15,7 @@ import SvgImage from "./svg-image";
|
|
|
14
15
|
|
|
15
16
|
import type {Coord} from "../interactive2/types";
|
|
16
17
|
import type {PerseusImageBackground} from "../perseus-types";
|
|
18
|
+
import type {GridDimensions} from "../util";
|
|
17
19
|
|
|
18
20
|
const defaultBackgroundImage = {
|
|
19
21
|
url: null,
|
|
@@ -43,12 +45,12 @@ function numSteps(range: any, step: any) {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
type Props = {
|
|
46
|
-
box:
|
|
48
|
+
box: [number, number];
|
|
47
49
|
labels: ReadonlyArray<string>;
|
|
48
|
-
range:
|
|
49
|
-
step:
|
|
50
|
-
gridStep:
|
|
51
|
-
snapStep:
|
|
50
|
+
range: [Coord, Coord];
|
|
51
|
+
step: [number, number];
|
|
52
|
+
gridStep: [number, number];
|
|
53
|
+
snapStep: [number, number];
|
|
52
54
|
markings: string;
|
|
53
55
|
backgroundImage: PerseusImageBackground;
|
|
54
56
|
showProtractor: boolean;
|
|
@@ -201,23 +203,26 @@ class Graph extends React.Component<Props> {
|
|
|
201
203
|
Util.unescapeMathMode(label),
|
|
202
204
|
);
|
|
203
205
|
const range = this.props.range;
|
|
206
|
+
// @ts-expect-error - TS2345: Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLElement'.
|
|
204
207
|
const graphie = (this._graphie = GraphUtils.createGraphie(graphieDiv));
|
|
205
208
|
|
|
206
|
-
const gridConfig =
|
|
209
|
+
const gridConfig: [GridDimensions, GridDimensions] =
|
|
210
|
+
this._getGridConfig();
|
|
211
|
+
// @ts-expect-error - TS2339: Property 'snap' does not exist on type 'Graphie'.
|
|
207
212
|
graphie.snap = this.props.snapStep;
|
|
208
213
|
|
|
209
214
|
if (this.props.markings === "graph") {
|
|
210
215
|
graphie.graphInit({
|
|
211
216
|
range: range,
|
|
212
|
-
scale:
|
|
217
|
+
scale: kvector.map(gridConfig, (g) => g.scale),
|
|
213
218
|
axisArrows: "<->",
|
|
214
219
|
labelFormat: function (s) {
|
|
215
220
|
return "\\small{" + s + "}";
|
|
216
221
|
},
|
|
217
222
|
gridStep: this.props.gridStep,
|
|
218
|
-
tickStep:
|
|
223
|
+
tickStep: kvector.map(gridConfig, (g) => g.tickStep),
|
|
219
224
|
labelStep: 1,
|
|
220
|
-
unityLabels:
|
|
225
|
+
unityLabels: kvector.map(gridConfig, (g) => g.unityLabel),
|
|
221
226
|
isMobile: this.props.isMobile,
|
|
222
227
|
});
|
|
223
228
|
graphie.label(
|
|
@@ -233,7 +238,7 @@ class Graph extends React.Component<Props> {
|
|
|
233
238
|
} else if (this.props.markings === "grid") {
|
|
234
239
|
graphie.graphInit({
|
|
235
240
|
range: range,
|
|
236
|
-
scale:
|
|
241
|
+
scale: kvector.map(gridConfig, (g) => g.scale),
|
|
237
242
|
gridStep: this.props.gridStep,
|
|
238
243
|
axes: false,
|
|
239
244
|
ticks: false,
|
|
@@ -243,7 +248,7 @@ class Graph extends React.Component<Props> {
|
|
|
243
248
|
} else if (this.props.markings === "none") {
|
|
244
249
|
graphie.init({
|
|
245
250
|
range: range,
|
|
246
|
-
scale:
|
|
251
|
+
scale: kvector.map(gridConfig, (g) => g.scale),
|
|
247
252
|
isMobile: this.props.isMobile,
|
|
248
253
|
});
|
|
249
254
|
}
|
|
@@ -303,6 +308,7 @@ class Graph extends React.Component<Props> {
|
|
|
303
308
|
: null;
|
|
304
309
|
/* eslint-enable indent */
|
|
305
310
|
|
|
311
|
+
// @ts-expect-error - Property 'addMouseLayer' does not exist on type 'Graphie'.
|
|
306
312
|
graphie.addMouseLayer({
|
|
307
313
|
onClick: this.props.onClick,
|
|
308
314
|
onMouseDown: onMouseDown,
|
|
@@ -329,16 +335,15 @@ class Graph extends React.Component<Props> {
|
|
|
329
335
|
}
|
|
330
336
|
};
|
|
331
337
|
|
|
332
|
-
_getGridConfig: () =>
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return Util.gridDimensionConfig(
|
|
338
|
+
_getGridConfig: () => [GridDimensions, GridDimensions] = () => {
|
|
339
|
+
return kvector.map(this.props.step, (step, i) =>
|
|
340
|
+
Util.gridDimensionConfig(
|
|
336
341
|
step,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
+
this.props.range[i],
|
|
343
|
+
this.props.box[i],
|
|
344
|
+
this.props.gridStep[i],
|
|
345
|
+
),
|
|
346
|
+
);
|
|
342
347
|
};
|
|
343
348
|
|
|
344
349
|
_updateProtractor: () => void = () => {
|
|
@@ -7,6 +7,7 @@ import InteractiveUtil from "../interactive2/interactive-util";
|
|
|
7
7
|
import {Errors, Log} from "../logging/log";
|
|
8
8
|
import Util from "../util";
|
|
9
9
|
import GraphUtils from "../util/graph-utils";
|
|
10
|
+
import {Graphie as GraphieDrawingContext} from "../util/graphie";
|
|
10
11
|
|
|
11
12
|
import GraphieClasses from "./graphie-classes";
|
|
12
13
|
import Movables from "./graphie-movables";
|
|
@@ -59,8 +60,17 @@ type DefaultProps = {
|
|
|
59
60
|
addMouseLayer: Props["addMouseLayer"];
|
|
60
61
|
};
|
|
61
62
|
|
|
63
|
+
interface Movable {
|
|
64
|
+
remove(): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
62
67
|
class Graphie extends React.Component<Props> {
|
|
63
68
|
graphieDivRef = React.createRef<HTMLDivElement>();
|
|
69
|
+
_graphie: GraphieDrawingContext = new GraphieDrawingContext(
|
|
70
|
+
document.createElement("div"),
|
|
71
|
+
);
|
|
72
|
+
_movables: Record<string, Movable> = {};
|
|
73
|
+
movables: Record<string, Movable> = {};
|
|
64
74
|
|
|
65
75
|
static defaultProps: DefaultProps = {
|
|
66
76
|
range: [
|
|
@@ -120,7 +130,6 @@ class Graphie extends React.Component<Props> {
|
|
|
120
130
|
* Use it for good and not evil.
|
|
121
131
|
*/
|
|
122
132
|
getGraphie: () => any = () => {
|
|
123
|
-
// @ts-expect-error - TS2339 - Property '_graphie' does not exist on type 'Graphie'.
|
|
124
133
|
return this._graphie;
|
|
125
134
|
};
|
|
126
135
|
|
|
@@ -151,10 +160,11 @@ class Graphie extends React.Component<Props> {
|
|
|
151
160
|
];
|
|
152
161
|
};
|
|
153
162
|
|
|
154
|
-
|
|
163
|
+
// @ts-expect-error - TS2322: Type '() => number[]' is not assignable to type '() => Coord'.
|
|
164
|
+
_scale: () => Coord = () => {
|
|
155
165
|
const box = this._box();
|
|
156
166
|
const range = this._range();
|
|
157
|
-
return
|
|
167
|
+
return box.map((pixelDim, i) => {
|
|
158
168
|
const unitDim = range[i][1] - range[i][0];
|
|
159
169
|
return pixelDim / unitDim;
|
|
160
170
|
});
|
|
@@ -164,9 +174,10 @@ class Graphie extends React.Component<Props> {
|
|
|
164
174
|
this._removeMovables();
|
|
165
175
|
|
|
166
176
|
const graphieDiv = ReactDOM.findDOMNode(this.graphieDivRef.current);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
177
|
+
if (graphieDiv == null || graphieDiv instanceof Text) {
|
|
178
|
+
throw new Error("No graphie container div found");
|
|
179
|
+
}
|
|
180
|
+
graphieDiv.innerHTML = "";
|
|
170
181
|
const graphie = (this._graphie = createGraphie(graphieDiv));
|
|
171
182
|
|
|
172
183
|
// This has to be called before addMouseLayer. You can re-init
|
|
@@ -178,6 +189,7 @@ class Graphie extends React.Component<Props> {
|
|
|
178
189
|
});
|
|
179
190
|
// Only add the mouselayer if we actually want one.
|
|
180
191
|
if (this.props.addMouseLayer) {
|
|
192
|
+
// @ts-expect-error - TS2339: Property 'addMouseLayer' does not exist on type 'Graphie'.
|
|
181
193
|
graphie.addMouseLayer({
|
|
182
194
|
onClick: this.props.onClick,
|
|
183
195
|
onMouseDown: this.props.onMouseDown,
|
|
@@ -187,13 +199,13 @@ class Graphie extends React.Component<Props> {
|
|
|
187
199
|
});
|
|
188
200
|
}
|
|
189
201
|
|
|
202
|
+
// @ts-expect-error - TS2339: Property 'snap' does not exist on type 'Graphie'.
|
|
190
203
|
graphie.snap = this.props.options.snapStep || [1, 1];
|
|
191
204
|
|
|
192
205
|
if (this.props.responsive) {
|
|
193
206
|
// Overwrite fixed styles set in init()
|
|
194
207
|
// TODO(alex): Either make this component always responsive by
|
|
195
208
|
// itself, or always wrap it in other components so that it is.
|
|
196
|
-
// @ts-expect-error - TS2769 - No overload matches this call. | TS2554 - Expected 2 arguments, but got 1.
|
|
197
209
|
$(graphieDiv).css({width: "100%", height: "100%"});
|
|
198
210
|
graphie.raphael.setSize("100%", "100%");
|
|
199
211
|
}
|
|
@@ -212,9 +224,7 @@ class Graphie extends React.Component<Props> {
|
|
|
212
224
|
|
|
213
225
|
_removeMovables: () => void = () => {
|
|
214
226
|
// _.invoke works even when this._movables is undefined
|
|
215
|
-
// @ts-expect-error - TS2339 - Property '_movables' does not exist on type 'Graphie'.
|
|
216
227
|
_.invoke(this._movables, "remove");
|
|
217
|
-
// @ts-expect-error - TS2339 - Property '_movables' does not exist on type 'Graphie'.
|
|
218
228
|
this._movables = {};
|
|
219
229
|
};
|
|
220
230
|
|
|
@@ -333,7 +343,6 @@ class Graphie extends React.Component<Props> {
|
|
|
333
343
|
}
|
|
334
344
|
|
|
335
345
|
if (ref) {
|
|
336
|
-
// @ts-expect-error - TS2339 - Property 'movables' does not exist on type 'Graphie'.
|
|
337
346
|
this.movables[ref] = newMovables[key];
|
|
338
347
|
}
|
|
339
348
|
|
|
@@ -343,15 +352,11 @@ class Graphie extends React.Component<Props> {
|
|
|
343
352
|
|
|
344
353
|
// Sort of like react diffing, but for movables
|
|
345
354
|
_updateMovables: () => void = () => {
|
|
346
|
-
// @ts-expect-error - TS2339 - Property '_graphie' does not exist on type 'Graphie'.
|
|
347
355
|
const graphie = this._graphie;
|
|
348
356
|
|
|
349
|
-
// @ts-expect-error - TS2339 - Property '_movables' does not exist on type 'Graphie'.
|
|
350
357
|
const oldMovables = this._movables;
|
|
351
358
|
const newMovables: Record<string, any> = {};
|
|
352
|
-
// @ts-expect-error - TS2339 - Property '_movables' does not exist on type 'Graphie'.
|
|
353
359
|
this._movables = newMovables;
|
|
354
|
-
// @ts-expect-error - TS2339 - Property 'movables' does not exist on type 'Graphie'.
|
|
355
360
|
this.movables = {};
|
|
356
361
|
|
|
357
362
|
// @ts-expect-error - TS2345 - Argument of type 'ReactNode' is not assignable to parameter of type 'readonly any[]'.
|
package/src/perseus-types.ts
CHANGED
|
@@ -1036,11 +1036,11 @@ export type PerseusInteractionGraph = {
|
|
|
1036
1036
|
// The Axis ranges. e.g. [[-10, 10], [-10, 10]]
|
|
1037
1037
|
range: [Range, Range];
|
|
1038
1038
|
// The steps in the grid. default [1, 1]
|
|
1039
|
-
gridStep:
|
|
1039
|
+
gridStep: [number, number];
|
|
1040
1040
|
// What to show on the graph. "graph", "grid", or "none"
|
|
1041
1041
|
markings: string;
|
|
1042
1042
|
// The snap steps. default [0.5, 0.5]
|
|
1043
|
-
snapStep?:
|
|
1043
|
+
snapStep?: [number, number];
|
|
1044
1044
|
// Whether the grid is valid or not. Do the numbers all make sense?
|
|
1045
1045
|
// NOTE(jeremy) The editor for this widget sometimes stores the graph
|
|
1046
1046
|
// editor validation error message into this field. It seems innocuous
|
|
@@ -1058,7 +1058,7 @@ export type PerseusInteractionGraph = {
|
|
|
1058
1058
|
// How many ticks to show on the ruler. e.g. 1, 2, 4, 8, 10, 16
|
|
1059
1059
|
rulerTicks?: number;
|
|
1060
1060
|
// This controls the number (and position) of the tick marks for the X and Y axis. e.g. [1, 1]
|
|
1061
|
-
tickStep:
|
|
1061
|
+
tickStep: [number, number];
|
|
1062
1062
|
};
|
|
1063
1063
|
|
|
1064
1064
|
export type PerseusInteractionElement =
|
package/src/util/graphie.test.ts
CHANGED
|
@@ -40,7 +40,7 @@ function createFakeRaphaelSet() {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function createAndInitGraphie(): Graphie {
|
|
43
|
-
const graphie = GraphUtils.createGraphie();
|
|
43
|
+
const graphie = GraphUtils.createGraphie(document.createElement("div"));
|
|
44
44
|
graphie.raphael = createMockRaphael();
|
|
45
45
|
|
|
46
46
|
// The graph is 50px by 50px
|
|
@@ -1244,20 +1244,6 @@ describe("Graphie drawing tools", () => {
|
|
|
1244
1244
|
});
|
|
1245
1245
|
});
|
|
1246
1246
|
|
|
1247
|
-
describe("GraphUtils", () => {
|
|
1248
|
-
describe("snapCoord", () => {
|
|
1249
|
-
it("snaps to the nearest integer coordinates", () => {
|
|
1250
|
-
expect(GraphUtils.snapCoord([0.1, 0.9], [1, 1])).toEqual([0, 1]);
|
|
1251
|
-
});
|
|
1252
|
-
|
|
1253
|
-
it("snaps to the nearest half", () => {
|
|
1254
|
-
expect(GraphUtils.snapCoord([0.4, 2.2], [0.5, 0.5])).toEqual([
|
|
1255
|
-
0.5, 2,
|
|
1256
|
-
]);
|
|
1257
|
-
});
|
|
1258
|
-
});
|
|
1259
|
-
});
|
|
1260
|
-
|
|
1261
1247
|
describe("normalizeRange", () => {
|
|
1262
1248
|
it("does nothing to a range specified as [[xMin, xMax], [yMin, yMax]]", () => {
|
|
1263
1249
|
expect(
|
package/src/util/graphie.ts
CHANGED
|
@@ -91,7 +91,7 @@ interface LabelMethod {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
export class Graphie {
|
|
94
|
-
el:
|
|
94
|
+
el: Element;
|
|
95
95
|
#bounds?: GraphBounds;
|
|
96
96
|
#drawingTransform?: DrawingTransform;
|
|
97
97
|
raphael?: any;
|
|
@@ -111,7 +111,7 @@ export class Graphie {
|
|
|
111
111
|
xpixels?: number;
|
|
112
112
|
ypixels?: number;
|
|
113
113
|
|
|
114
|
-
constructor(el:
|
|
114
|
+
constructor(el: Element) {
|
|
115
115
|
this.el = el;
|
|
116
116
|
$(el).css("position", "relative");
|
|
117
117
|
this.raphael = Raphael(el);
|
|
@@ -180,7 +180,7 @@ export class Graphie {
|
|
|
180
180
|
grid?: boolean;
|
|
181
181
|
gridRange?: [Interval, Interval] | Coord;
|
|
182
182
|
scale: Coord | number;
|
|
183
|
-
axes?:
|
|
183
|
+
axes?: boolean;
|
|
184
184
|
axisArrows?: "<->" | "->" | true | "";
|
|
185
185
|
axisOpacity?: number;
|
|
186
186
|
axisCenter?: Coord;
|
|
@@ -269,8 +269,8 @@ export class Graphie {
|
|
|
269
269
|
unityLabels = [unityLabels, unityLabels];
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
const minusIgnorer = function (lf:
|
|
273
|
-
return function (a) {
|
|
272
|
+
const minusIgnorer = function (lf: (a: number) => string) {
|
|
273
|
+
return function (a: number) {
|
|
274
274
|
return (lf(a) + "").replace(/-(\d)/g, "\\llap{-}$1");
|
|
275
275
|
};
|
|
276
276
|
};
|
|
@@ -580,7 +580,7 @@ export class Graphie {
|
|
|
580
580
|
return this.#bounds;
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
-
style(attrs: any, fn:
|
|
583
|
+
style<T>(attrs: any, fn?: (this: Graphie) => T): T | undefined {
|
|
584
584
|
const processed = this.processAttributes(attrs);
|
|
585
585
|
|
|
586
586
|
if (typeof fn === "function") {
|
|
@@ -983,7 +983,7 @@ export class Graphie {
|
|
|
983
983
|
};
|
|
984
984
|
|
|
985
985
|
// @ts-expect-error - TS2339 - Property 'processText' does not exist on type 'JQuery<HTMLElement>'.
|
|
986
|
-
$span.processText = function (text:
|
|
986
|
+
$span.processText = function (text: string) {
|
|
987
987
|
$span.html(text);
|
|
988
988
|
const width = span.scrollWidth;
|
|
989
989
|
const height = span.scrollHeight;
|
|
@@ -1098,7 +1098,7 @@ export class Graphie {
|
|
|
1098
1098
|
.join("");
|
|
1099
1099
|
};
|
|
1100
1100
|
|
|
1101
|
-
svgParabolaPath = (a:
|
|
1101
|
+
svgParabolaPath = (a: number, b: number, c: number) => {
|
|
1102
1102
|
const computeParabola = function (x) {
|
|
1103
1103
|
return (a * x + b) * x + c;
|
|
1104
1104
|
};
|
|
@@ -1346,7 +1346,7 @@ export class Graphie {
|
|
|
1346
1346
|
return this.drawingTransform().unscaleVector(point);
|
|
1347
1347
|
};
|
|
1348
1348
|
|
|
1349
|
-
private processAttributes(attrs: any) {
|
|
1349
|
+
private processAttributes(attrs: Record<string, any> | undefined) {
|
|
1350
1350
|
const transformers = {
|
|
1351
1351
|
scale: (scale) => {
|
|
1352
1352
|
this.drawingTransform().setScale(scale);
|
|
@@ -1515,14 +1515,14 @@ const setLabelMargins = function (span: HTMLElement, size: Coord): void {
|
|
|
1515
1515
|
}
|
|
1516
1516
|
};
|
|
1517
1517
|
|
|
1518
|
-
const GraphUtils
|
|
1518
|
+
const GraphUtils = {
|
|
1519
1519
|
Graphie,
|
|
1520
1520
|
|
|
1521
|
-
createGraphie: function (el:
|
|
1521
|
+
createGraphie: function (el: Element): Graphie {
|
|
1522
1522
|
return new Graphie(el);
|
|
1523
1523
|
},
|
|
1524
1524
|
|
|
1525
|
-
unscaledSvgPath: function (points) {
|
|
1525
|
+
unscaledSvgPath: function (points: (Coord | true)[]): string {
|
|
1526
1526
|
// If this is an empty closed path, return "" instead of "z", which
|
|
1527
1527
|
// would give an error
|
|
1528
1528
|
if (points[0] === true) {
|
|
@@ -1538,22 +1538,12 @@ const GraphUtils: any = {
|
|
|
1538
1538
|
.join("");
|
|
1539
1539
|
},
|
|
1540
1540
|
|
|
1541
|
-
getDistance: function (point1, point2) {
|
|
1541
|
+
getDistance: function (point1: Coord, point2: Coord): number {
|
|
1542
1542
|
return kpoint.distanceToPoint(point1, point2);
|
|
1543
1543
|
},
|
|
1544
1544
|
|
|
1545
|
-
/**
|
|
1546
|
-
* Round the given coordinates to a given snap value
|
|
1547
|
-
* (e.g., nearest 0.2 increment)
|
|
1548
|
-
*/
|
|
1549
|
-
snapCoord: function (coord, snap) {
|
|
1550
|
-
return coord.map(function (val, i) {
|
|
1551
|
-
return KhanMath.roundToNearest(snap[i], val);
|
|
1552
|
-
});
|
|
1553
|
-
},
|
|
1554
|
-
|
|
1555
1545
|
// Find the angle in degrees between two or three points
|
|
1556
|
-
findAngle: function (point1, point2, vertex) {
|
|
1546
|
+
findAngle: function (point1: Coord, point2: Coord, vertex?: Coord) {
|
|
1557
1547
|
if (vertex === undefined) {
|
|
1558
1548
|
const x = point1[0] - point2[0];
|
|
1559
1549
|
const y = point1[1] - point2[1];
|
package/src/util/interactive.ts
CHANGED
|
@@ -3982,8 +3982,8 @@ _.extend(MovableAngle.prototype, {
|
|
|
3982
3982
|
|
|
3983
3983
|
// Drag ray control points to move each ray individually
|
|
3984
3984
|
_.each([0, 2], function (i) {
|
|
3985
|
-
points[i].onMove = function (x:
|
|
3986
|
-
const newPoint = [x, y];
|
|
3985
|
+
points[i].onMove = function (x: number, y: number) {
|
|
3986
|
+
const newPoint: Coord = [x, y];
|
|
3987
3987
|
const vertex = points[1].coord;
|
|
3988
3988
|
|
|
3989
3989
|
// @ts-expect-error - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'never[]'.
|
package/src/widgets/measurer.tsx
CHANGED
|
@@ -11,6 +11,8 @@ import {ApiOptions} from "../perseus-api";
|
|
|
11
11
|
import GraphUtils from "../util/graph-utils";
|
|
12
12
|
|
|
13
13
|
import type {WidgetExports} from "../types";
|
|
14
|
+
import type {Interval} from "../util/interval";
|
|
15
|
+
import type {Coord} from "@khanacademy/perseus";
|
|
14
16
|
|
|
15
17
|
const defaultImage = {
|
|
16
18
|
url: null,
|
|
@@ -121,10 +123,11 @@ const Measurer: any = createReactClass({
|
|
|
121
123
|
const graphieDiv = ReactDOM.findDOMNode(this.refs.graphieDiv);
|
|
122
124
|
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'empty' does not exist on type 'JQueryStatic'.
|
|
123
125
|
$(graphieDiv).empty();
|
|
126
|
+
// @ts-expect-error - Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLElement'.
|
|
124
127
|
const graphie = (this.graphie = GraphUtils.createGraphie(graphieDiv));
|
|
125
128
|
|
|
126
|
-
const scale = [40, 40];
|
|
127
|
-
const range = [
|
|
129
|
+
const scale: Coord = [40, 40];
|
|
130
|
+
const range: [Interval, Interval] = [
|
|
128
131
|
[0, this.props.box[0] / scale[0]],
|
|
129
132
|
[0, this.props.box[1] / scale[1]],
|
|
130
133
|
];
|
|
@@ -132,6 +135,7 @@ const Measurer: any = createReactClass({
|
|
|
132
135
|
range: range,
|
|
133
136
|
scale: scale,
|
|
134
137
|
});
|
|
138
|
+
// @ts-expect-error - Property 'addMouseLayer' does not exist on type 'Graphie'.
|
|
135
139
|
graphie.addMouseLayer({
|
|
136
140
|
allowScratchpad: true,
|
|
137
141
|
setDrawingAreaAvailable:
|
|
@@ -143,6 +147,7 @@ const Measurer: any = createReactClass({
|
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
if (this.props.showProtractor) {
|
|
150
|
+
// @ts-expect-error - Property 'protractor' does not exist on type 'Graphie'.
|
|
146
151
|
this.protractor = graphie.protractor([
|
|
147
152
|
this.props.protractorX,
|
|
148
153
|
this.props.protractorY,
|
|
@@ -154,6 +159,7 @@ const Measurer: any = createReactClass({
|
|
|
154
159
|
}
|
|
155
160
|
|
|
156
161
|
if (this.props.showRuler) {
|
|
162
|
+
// @ts-expect-error - Property 'ruler' does not exist on type 'Graphie'.
|
|
157
163
|
this.ruler = graphie.ruler({
|
|
158
164
|
center: [
|
|
159
165
|
(range[0][0] + range[0][1]) / 2,
|
package/src/widgets/plotter.tsx
CHANGED
|
@@ -145,6 +145,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
145
145
|
const graphieDiv = ReactDOM.findDOMNode(self.refs.graphieDiv);
|
|
146
146
|
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'empty' does not exist on type 'JQueryStatic'.
|
|
147
147
|
$(graphieDiv).empty();
|
|
148
|
+
// @ts-expect-error - Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLElement'.
|
|
148
149
|
const graphie = GraphUtils.createGraphie(graphieDiv);
|
|
149
150
|
|
|
150
151
|
// TODO(jakesandlund): It's not the react way to hang
|
|
@@ -278,6 +279,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
278
279
|
scale: c.scale,
|
|
279
280
|
isMobile: this.props.apiOptions.isMobile,
|
|
280
281
|
});
|
|
282
|
+
// @ts-expect-error - Property 'addMouseLayer' does not exist on type 'Graphie'.
|
|
281
283
|
graphie.addMouseLayer({
|
|
282
284
|
allowScratchpad: true,
|
|
283
285
|
setDrawingAreaAvailable:
|
package/src/widgets.ts
CHANGED
|
@@ -54,10 +54,12 @@ export const registerWidgets = (widgets: ReadonlyArray<WidgetExports>) => {
|
|
|
54
54
|
export const replaceWidget = (name: string, replacementName: string) => {
|
|
55
55
|
const substituteWidget = widgets[replacementName];
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
// If the replacement widget isn't found, we need to throw. Otherwise after
|
|
58
|
+
// removing the deprecated widget, we'll have data asking for a widget type
|
|
59
|
+
// that doesn't exist at all.
|
|
60
|
+
if (!substituteWidget) {
|
|
58
61
|
const errorMsg = `Failed to replace ${name} with ${replacementName}`;
|
|
59
|
-
|
|
60
|
-
return;
|
|
62
|
+
throw new PerseusError(errorMsg, Errors.Internal);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
registerWidget(name, substituteWidget);
|