@khanacademy/perseus 17.7.0 → 18.0.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.
- package/CHANGELOG.md +37 -0
- package/dist/es/index.css +0 -4
- package/dist/es/index.js +1416 -1020
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +0 -4
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1315 -953
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +4 -3
- package/dist/renderer.d.ts +6 -6
- package/dist/server-item-renderer.d.ts +2 -4
- package/dist/strings.js +1 -1
- package/dist/types.d.ts +19 -2
- package/dist/util/drawing-transform.d.ts +21 -0
- package/dist/util/extract-perseus-data.d.ts +26 -0
- package/dist/util/graph-bounds.d.ts +9 -0
- package/dist/util/graphie.d.ts +2 -0
- package/dist/util/interval.d.ts +1 -0
- package/dist/util/parse-perseus-json.d.ts +10 -0
- package/dist/util/tex.d.ts +1 -3
- package/dist/widgets/deprecated-standin/index.d.ts +4 -4
- package/dist/widgets/passage.d.ts +3 -3
- package/dist/widgets.d.ts +16 -6
- package/package.json +2 -1
- package/src/__tests__/extract-perseus-data.test.ts +1027 -0
- package/src/index.ts +6 -1
- package/src/init.ts +1 -1
- package/src/perseus-types.ts +4 -3
- package/src/renderer.tsx +12 -9
- package/src/server-item-renderer.tsx +2 -4
- package/src/styles/widgets/image.less +0 -3
- package/src/types.ts +19 -16
- package/src/util/drawing-transform.test.ts +67 -0
- package/src/util/drawing-transform.ts +102 -0
- package/src/util/extract-perseus-data.ts +539 -0
- package/src/util/graph-bounds.ts +23 -0
- package/src/util/graphie.ts +1167 -1258
- package/src/util/interactive.ts +2 -2
- package/src/util/interval.ts +4 -0
- package/src/util/parse-perseus-json.test.ts +10 -0
- package/src/util/parse-perseus-json.ts +13 -0
- package/src/util/tex.ts +11 -84
- package/src/widgets/__stories__/image.stories.tsx +12 -26
- package/src/widgets/__tests__/__snapshots__/grapher.test.ts.snap +72 -288
- package/src/widgets/__tests__/__snapshots__/interaction.test.ts.snap +5 -20
- package/src/widgets/__tests__/__snapshots__/interactive-graph.test.ts.snap +32 -128
- package/src/widgets/__tests__/__snapshots__/number-line.test.ts.snap +22 -88
- package/src/widgets/__tests__/__snapshots__/plotter.test.tsx.snap +12 -48
- package/src/widgets/deprecated-standin/__stories__/deprecated-standin.stories.tsx +8 -12
- package/src/widgets/deprecated-standin/__tests__/__snapshots__/deprecated-standin.test.ts.snap +6 -7
- package/src/widgets/deprecated-standin/index.tsx +8 -7
- package/src/widgets/interactive-graph.tsx +3 -2
- package/src/widgets/passage.tsx +5 -7
- package/src/widgets.ts +34 -10
- package/tsconfig-build.tsbuildinfo +1 -1
package/src/util/interactive.ts
CHANGED
|
@@ -31,7 +31,7 @@ import {Errors} from "../logging/log";
|
|
|
31
31
|
import {PerseusError} from "../perseus-error";
|
|
32
32
|
|
|
33
33
|
import KhanColors from "./colors";
|
|
34
|
-
import GraphUtils from "./graphie";
|
|
34
|
+
import GraphUtils, {polar} from "./graphie";
|
|
35
35
|
import KhanMath from "./math";
|
|
36
36
|
|
|
37
37
|
import type {Coord} from "../interactive2/types";
|
|
@@ -4024,7 +4024,7 @@ _.extend(MovableAngle.prototype, {
|
|
|
4024
4024
|
Math.round((angle - snapOffset) / snap) * snap +
|
|
4025
4025
|
snapOffset;
|
|
4026
4026
|
const distance = GraphUtils.getDistance(newPoint, vertex);
|
|
4027
|
-
return addPoints(vertex,
|
|
4027
|
+
return addPoints(vertex, polar(distance, angle));
|
|
4028
4028
|
}
|
|
4029
4029
|
return true;
|
|
4030
4030
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import parsePerseusJSON from "./parse-perseus-json";
|
|
2
|
+
|
|
3
|
+
describe("parsePerseusJSON", () => {
|
|
4
|
+
it("should parse JSON", () => {
|
|
5
|
+
const result = parsePerseusJSON(
|
|
6
|
+
`{ "question": { "content": "idk why I'm testing this" }}`,
|
|
7
|
+
);
|
|
8
|
+
expect(result.question.content).toBe("idk why I'm testing this");
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type {PerseusItem} from "../perseus-types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper to parse Perseus JSON
|
|
5
|
+
* Why not just use JSON.parse? We want:
|
|
6
|
+
* - To make sure types are correct
|
|
7
|
+
* - To give us a central place to validate/transform output if needed
|
|
8
|
+
* @param {string} input - the stringified Perseus JSON
|
|
9
|
+
* @returns {PerseusItem} the parsed PerseusItem object
|
|
10
|
+
*/
|
|
11
|
+
export default function parsePerseusJSON(input: string): PerseusItem {
|
|
12
|
+
return JSON.parse(input);
|
|
13
|
+
}
|
package/src/util/tex.ts
CHANGED
|
@@ -6,8 +6,6 @@ import {getDependencies} from "../dependencies";
|
|
|
6
6
|
import KhanMath from "./math";
|
|
7
7
|
import reactRender from "./react-render";
|
|
8
8
|
|
|
9
|
-
declare const MathJax: any;
|
|
10
|
-
|
|
11
9
|
function findChildOrAdd(elem: any, className: string) {
|
|
12
10
|
const $child = $(elem).find("." + className);
|
|
13
11
|
if ($child.length === 0) {
|
|
@@ -16,27 +14,6 @@ function findChildOrAdd(elem: any, className: string) {
|
|
|
16
14
|
return $child;
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
function doCallback(
|
|
20
|
-
elem: HTMLElement,
|
|
21
|
-
callback: (() => unknown) | (() => void),
|
|
22
|
-
) {
|
|
23
|
-
let tries = 0;
|
|
24
|
-
(function check() {
|
|
25
|
-
const height = elem.scrollHeight;
|
|
26
|
-
// Heuristic to guess if the font has kicked in
|
|
27
|
-
// so we have box metrics (magic number ick,
|
|
28
|
-
// but this seems to work mostly-consistently)
|
|
29
|
-
if (height > 18 || tries >= 10) {
|
|
30
|
-
callback();
|
|
31
|
-
} else {
|
|
32
|
-
tries++;
|
|
33
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API
|
|
34
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35
|
-
setTimeout(check, 100);
|
|
36
|
-
}
|
|
37
|
-
})();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
17
|
export default {
|
|
41
18
|
// Process a node and add math inside of it. This attempts to use KaTeX to
|
|
42
19
|
// format the math, and if that fails it falls back to MathJax.
|
|
@@ -55,7 +32,7 @@ export default {
|
|
|
55
32
|
// processed
|
|
56
33
|
processMath: async function (
|
|
57
34
|
elem: HTMLElement,
|
|
58
|
-
text: string,
|
|
35
|
+
text: string | number,
|
|
59
36
|
force?: boolean,
|
|
60
37
|
callback?: () => unknown,
|
|
61
38
|
) {
|
|
@@ -63,29 +40,14 @@ export default {
|
|
|
63
40
|
|
|
64
41
|
// Only process if it hasn't been done before, or it is forced
|
|
65
42
|
if ($elem.attr("data-math-formula") == null || force) {
|
|
66
|
-
const $
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const script: HTMLElement | undefined = $mathjaxHolder.find(
|
|
75
|
-
"script[type='math/tex']",
|
|
76
|
-
)[0];
|
|
77
|
-
|
|
78
|
-
// If text wasn't provided, we look in two places
|
|
79
|
-
if (text == null) {
|
|
80
|
-
if ($elem.attr("data-math-formula")) {
|
|
81
|
-
// The old typeset formula
|
|
82
|
-
// @ts-expect-error - TS2322 - Type 'string | undefined' is not assignable to type 'string'.
|
|
83
|
-
text = $elem.attr("data-math-formula");
|
|
84
|
-
} else if (script) {
|
|
85
|
-
// The contents of the <script> tag
|
|
86
|
-
// @ts-expect-error - TS2339 - Property 'text' does not exist on type 'HTMLElement'.
|
|
87
|
-
text = script.text || script.textContent;
|
|
88
|
-
}
|
|
43
|
+
const $texHolder = findChildOrAdd($elem, "tex-holder");
|
|
44
|
+
|
|
45
|
+
// If text wasn't provided, we use the cached text.
|
|
46
|
+
// TODO(benchristel): I'm not sure if text can ever be null. It's
|
|
47
|
+
// possible we don't need this check.
|
|
48
|
+
if (text == null && $elem.attr("data-math-formula")) {
|
|
49
|
+
// @ts-expect-error - TS2322 - Type 'string | undefined' is not assignable to type 'string'.
|
|
50
|
+
text = $elem.attr("data-math-formula");
|
|
89
51
|
}
|
|
90
52
|
|
|
91
53
|
text = text != null ? text + "" : "";
|
|
@@ -102,45 +64,10 @@ export default {
|
|
|
102
64
|
reactRender(
|
|
103
65
|
React.createElement(TeX, {
|
|
104
66
|
children: text,
|
|
105
|
-
onRender:
|
|
106
|
-
if (callback) {
|
|
107
|
-
doCallback(elem, callback);
|
|
108
|
-
}
|
|
109
|
-
},
|
|
67
|
+
onRender: callback,
|
|
110
68
|
}),
|
|
111
|
-
$
|
|
69
|
+
$texHolder[0],
|
|
112
70
|
);
|
|
113
71
|
}
|
|
114
72
|
},
|
|
115
|
-
|
|
116
|
-
// Function to restore a node to a non-math-processed state
|
|
117
|
-
cleanupMath: function (elem: HTMLElement): HTMLElement {
|
|
118
|
-
const $elem = $(elem);
|
|
119
|
-
|
|
120
|
-
// Only mess with it if it's been processed before
|
|
121
|
-
if ($elem.attr("data-math-formula")) {
|
|
122
|
-
// Remove MathJax remnants
|
|
123
|
-
if (typeof MathJax !== "undefined") {
|
|
124
|
-
const jax = MathJax.Hub.getJaxFor($elem.find("script")[0]);
|
|
125
|
-
if (jax) {
|
|
126
|
-
const e = jax.SourceElement();
|
|
127
|
-
if (e.previousSibling && e.previousSibling.className) {
|
|
128
|
-
jax.Remove();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
134
|
-
$elem.text($elem.attr("data-math-formula"));
|
|
135
|
-
$elem.attr("data-math-formula", null);
|
|
136
|
-
$elem.attr("data-math-type", null);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return elem;
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
// Function to retrieve the formula of a typeset math node
|
|
143
|
-
retrieveMathFormula: function (elem: HTMLElement): any {
|
|
144
|
-
return $(elem).attr("data-math-formula");
|
|
145
|
-
},
|
|
146
73
|
};
|
|
@@ -28,18 +28,11 @@ export const Question1 = (args: StoryArgs): React.ReactElement => {
|
|
|
28
28
|
...question.widgets,
|
|
29
29
|
"image 1": {
|
|
30
30
|
...question.widgets["image 1"],
|
|
31
|
-
options:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
imageOptions != null
|
|
37
|
-
? {
|
|
38
|
-
...imageOptions,
|
|
39
|
-
title: args.title,
|
|
40
|
-
caption: args.caption,
|
|
41
|
-
}
|
|
42
|
-
: null,
|
|
31
|
+
options: {
|
|
32
|
+
...imageOptions,
|
|
33
|
+
title: args.title,
|
|
34
|
+
caption: args.caption,
|
|
35
|
+
},
|
|
43
36
|
},
|
|
44
37
|
},
|
|
45
38
|
} as const;
|
|
@@ -64,20 +57,13 @@ export const Question2 = (args: StoryArgs): React.ReactElement => {
|
|
|
64
57
|
...question.widgets,
|
|
65
58
|
"image 1": {
|
|
66
59
|
...question.widgets["image 1"],
|
|
67
|
-
options:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
...imageOptions,
|
|
75
|
-
alignment: "full-width",
|
|
76
|
-
title: args.title,
|
|
77
|
-
caption:
|
|
78
|
-
"There is neither happiness nor unhappiness in this world; there is only the comparison of one state with another. Only a man who has felt ultimate despair is capable of feeling ultimate bliss. It is necessary to have wished for death in order to know how good it is to live.....the sum of all human wisdom will be contained in these two words: Wait and Hope",
|
|
79
|
-
}
|
|
80
|
-
: null,
|
|
60
|
+
options: {
|
|
61
|
+
...imageOptions,
|
|
62
|
+
alignment: "full-width",
|
|
63
|
+
title: args.title,
|
|
64
|
+
caption:
|
|
65
|
+
"There is neither happiness nor unhappiness in this world; there is only the comparison of one state with another. Only a man who has felt ultimate despair is capable of feeling ultimate bliss. It is necessary to have wished for death in order to know how good it is to live.....the sum of all human wisdom will be contained in these two words: Wait and Hope",
|
|
66
|
+
},
|
|
81
67
|
},
|
|
82
68
|
},
|
|
83
69
|
} as const;
|