@khanacademy/perseus 10.0.1 → 10.1.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 +15 -0
- package/dist/components/zoomable.d.ts +2 -0
- package/dist/es/index.css +26 -0
- package/dist/es/index.js +103 -114
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +26 -0
- package/dist/index.js +103 -114
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/renderer.test.tsx.snap +1 -1
- package/src/components/__stories__/zoomable.stories.tsx +9 -1
- package/src/components/zoomable-tex.tsx +2 -0
- package/src/components/zoomable.tsx +10 -3
- package/src/styles/widgets/image.less +32 -0
- package/src/widgets/__stories__/image.stories.tsx +37 -0
- package/src/widgets/__tests__/__snapshots__/categorizer.test.ts.snap +1 -1
- package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +2 -2
- package/src/widgets/__tests__/__snapshots__/image.test.ts.snap +9 -11
- package/src/widgets/__tests__/__snapshots__/matrix.test.ts.snap +1 -1
- package/src/widgets/__tests__/__snapshots__/number-line.test.ts.snap +1 -1
- package/src/widgets/image.tsx +8 -31
- package/tsconfig-build.tsbuildinfo +1 -1
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": "10.0
|
|
6
|
+
"version": "10.1.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@khanacademy/kas": "^0.3.1",
|
|
25
25
|
"@khanacademy/kmath": "^0.1.2",
|
|
26
|
-
"@khanacademy/math-input": "^10.1.
|
|
26
|
+
"@khanacademy/math-input": "^10.1.1",
|
|
27
27
|
"@khanacademy/perseus-core": "0.2.0",
|
|
28
28
|
"@khanacademy/perseus-linter": "^0.3.2",
|
|
29
29
|
"@khanacademy/pure-markdown": "^0.2.4",
|
|
@@ -230,7 +230,7 @@ exports[`renderer rendering should render block math on mobile 1`] = `
|
|
|
230
230
|
style="overflow-x: auto; overflow-y: hidden; margin-top: -10px; margin-bottom: -10px; transform: translate3d(0,0,0); padding: 10px 16px 10px 16px;"
|
|
231
231
|
>
|
|
232
232
|
<span
|
|
233
|
-
style="display: block; width: 100%; transform: scale(1, 1)
|
|
233
|
+
style="display: block; width: 100%; transform: scale(1, 1); transform-origin: 0 0; opacity: 1;"
|
|
234
234
|
>
|
|
235
235
|
<span
|
|
236
236
|
class="mock-TeX"
|
|
@@ -10,6 +10,11 @@ type Story = {
|
|
|
10
10
|
|
|
11
11
|
export default {
|
|
12
12
|
title: "Perseus/Components/Zoomable",
|
|
13
|
+
argTypes: {
|
|
14
|
+
disableEntranceAnimation: {
|
|
15
|
+
control: {type: "boolean"},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
13
18
|
} as Story;
|
|
14
19
|
|
|
15
20
|
type Bounds = {
|
|
@@ -29,7 +34,10 @@ const computeChildBounds = (
|
|
|
29
34
|
|
|
30
35
|
export const ZoomableExample = (args: StoryArgs): React.ReactElement => {
|
|
31
36
|
return (
|
|
32
|
-
<Zoomable
|
|
37
|
+
<Zoomable
|
|
38
|
+
computeChildBounds={computeChildBounds}
|
|
39
|
+
disableEntranceAnimation={!!args.disableEntranceAnimation}
|
|
40
|
+
>
|
|
33
41
|
<span>
|
|
34
42
|
Here's some zoomed-out content.
|
|
35
43
|
<br />
|
|
@@ -59,6 +59,8 @@ export default class ZoomableTeX extends React.Component<Props, State> {
|
|
|
59
59
|
<Zoomable
|
|
60
60
|
readyToMeasure={this.state.isRendered}
|
|
61
61
|
computeChildBounds={computeMathBounds}
|
|
62
|
+
animateHeight={false}
|
|
63
|
+
disableEntranceAnimation={true}
|
|
62
64
|
>
|
|
63
65
|
<AssetContext.Consumer>
|
|
64
66
|
{({setAssetStatus}) => (
|
|
@@ -14,6 +14,8 @@ type Bounds = {
|
|
|
14
14
|
type Props = {
|
|
15
15
|
children: React.ReactNode;
|
|
16
16
|
animateHeight: boolean;
|
|
17
|
+
|
|
18
|
+
disableEntranceAnimation: boolean;
|
|
17
19
|
/**
|
|
18
20
|
* Optional function that allows customizations in zooming.
|
|
19
21
|
*
|
|
@@ -35,6 +37,7 @@ type DefaultProps = {
|
|
|
35
37
|
animateHeight: Props["animateHeight"];
|
|
36
38
|
computeChildBounds: Props["computeChildBounds"];
|
|
37
39
|
readyToMeasure: Props["readyToMeasure"];
|
|
40
|
+
disableEntranceAnimation: Props["disableEntranceAnimation"];
|
|
38
41
|
};
|
|
39
42
|
|
|
40
43
|
type State = {
|
|
@@ -60,6 +63,7 @@ class Zoomable extends React.Component<Props, State> {
|
|
|
60
63
|
static defaultProps: DefaultProps = {
|
|
61
64
|
animateHeight: false,
|
|
62
65
|
readyToMeasure: true,
|
|
66
|
+
disableEntranceAnimation: false,
|
|
63
67
|
computeChildBounds: (parentNode) => {
|
|
64
68
|
const firstChild = parentNode.firstElementChild;
|
|
65
69
|
|
|
@@ -245,7 +249,7 @@ class Zoomable extends React.Component<Props, State> {
|
|
|
245
249
|
render(): React.ReactNode {
|
|
246
250
|
const {visible, scale, compactHeight, expandedHeight, zoomed} =
|
|
247
251
|
this.state;
|
|
248
|
-
const {animateHeight} = this.props;
|
|
252
|
+
const {animateHeight, disableEntranceAnimation} = this.props;
|
|
249
253
|
|
|
250
254
|
const property = animateHeight
|
|
251
255
|
? "opacity transform height"
|
|
@@ -261,7 +265,10 @@ class Zoomable extends React.Component<Props, State> {
|
|
|
261
265
|
: {};
|
|
262
266
|
|
|
263
267
|
// Do a fancy little slide as we fade the contents in the first time.
|
|
264
|
-
const
|
|
268
|
+
const shouldSlide = !disableEntranceAnimation && !visible;
|
|
269
|
+
const translateOffset = shouldSlide ? " translate(0, 8px)" : "";
|
|
270
|
+
const opacity = shouldSlide ? 0 : 1;
|
|
271
|
+
|
|
265
272
|
const scaleString = (scale ?? 1).toString();
|
|
266
273
|
const transform = zoomed
|
|
267
274
|
? `scale(1, 1) ${translateOffset}`
|
|
@@ -273,7 +280,7 @@ class Zoomable extends React.Component<Props, State> {
|
|
|
273
280
|
height: zoomed ? expandedHeight : compactHeight,
|
|
274
281
|
transform: transform,
|
|
275
282
|
transformOrigin: "0 0",
|
|
276
|
-
opacity:
|
|
283
|
+
opacity: opacity,
|
|
277
284
|
WebkitTapHighlightColor: "transparent",
|
|
278
285
|
...transitionStyle,
|
|
279
286
|
} as const;
|
|
@@ -1,7 +1,39 @@
|
|
|
1
1
|
.perseus-image-widget {
|
|
2
|
+
margin: 0;
|
|
3
|
+
|
|
2
4
|
.perseus-image-title {
|
|
3
5
|
text-align: center;
|
|
4
6
|
}
|
|
7
|
+
|
|
8
|
+
.perseus-image-caption {
|
|
9
|
+
color: rgba(33, 36, 44, 0.7);
|
|
10
|
+
max-width: 420px;
|
|
11
|
+
padding-left: 16px;
|
|
12
|
+
position: relative;
|
|
13
|
+
|
|
14
|
+
// This offsets the paragraph's padding
|
|
15
|
+
// So that the caption sits much closer
|
|
16
|
+
// to the image
|
|
17
|
+
top: -12px;
|
|
18
|
+
|
|
19
|
+
&::before {
|
|
20
|
+
content: "";
|
|
21
|
+
position: absolute;
|
|
22
|
+
top: 0;
|
|
23
|
+
bottom: 0;
|
|
24
|
+
left: 0;
|
|
25
|
+
margin-left: 4px;
|
|
26
|
+
width: 1px;
|
|
27
|
+
background-color: rgba(33, 36, 44, 0.5);
|
|
28
|
+
|
|
29
|
+
border: 1px solid rgba(33, 36, 44, 0.5);
|
|
30
|
+
border-radius: 2px;
|
|
31
|
+
}
|
|
32
|
+
.paragraph {
|
|
33
|
+
font-size: 14px;
|
|
34
|
+
padding-right: 12px;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
5
37
|
}
|
|
6
38
|
|
|
7
39
|
.perseus-image-editor {
|
|
@@ -43,6 +43,7 @@ export const Question1 = (args: StoryArgs): React.ReactElement => {
|
|
|
43
43
|
},
|
|
44
44
|
},
|
|
45
45
|
} as const;
|
|
46
|
+
|
|
46
47
|
return (
|
|
47
48
|
<RendererWithDebugUI
|
|
48
49
|
question={questionWithArgs}
|
|
@@ -51,6 +52,42 @@ export const Question1 = (args: StoryArgs): React.ReactElement => {
|
|
|
51
52
|
);
|
|
52
53
|
};
|
|
53
54
|
|
|
55
|
+
export const Question2 = (args: StoryArgs): React.ReactElement => {
|
|
56
|
+
const apiOptions: APIOptions = {
|
|
57
|
+
isMobile: args.isMobile,
|
|
58
|
+
};
|
|
59
|
+
const imageOptions = question.widgets["image 1"].options;
|
|
60
|
+
|
|
61
|
+
const questionWithCaptionAndArgs = {
|
|
62
|
+
...question,
|
|
63
|
+
widgets: {
|
|
64
|
+
...question.widgets,
|
|
65
|
+
"image 1": {
|
|
66
|
+
...question.widgets["image 1"],
|
|
67
|
+
options:
|
|
68
|
+
// `options` is nullable so we have handle it explicitly
|
|
69
|
+
// to avoid spreading `null` and generating
|
|
70
|
+
// hard-to-understand type errors about missing keys
|
|
71
|
+
// in object literal.
|
|
72
|
+
imageOptions != null
|
|
73
|
+
? {
|
|
74
|
+
...imageOptions,
|
|
75
|
+
title: args.title,
|
|
76
|
+
caption:
|
|
77
|
+
"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",
|
|
78
|
+
}
|
|
79
|
+
: null,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
} as const;
|
|
83
|
+
return (
|
|
84
|
+
<RendererWithDebugUI
|
|
85
|
+
question={questionWithCaptionAndArgs}
|
|
86
|
+
apiOptions={apiOptions}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
54
91
|
export default {
|
|
55
92
|
title: "Perseus/Widgets/Image",
|
|
56
93
|
args: {
|
|
@@ -30,7 +30,7 @@ exports[`categorizer widget should snapshot on mobile: first mobile render 1`] =
|
|
|
30
30
|
style="overflow-x: auto; overflow-y: hidden; margin-top: -10px; margin-bottom: -10px; transform: translate3d(0,0,0); padding: 10px 16px 10px 16px;"
|
|
31
31
|
>
|
|
32
32
|
<span
|
|
33
|
-
style="display: block; width: 100%; transform: scale(1, 1)
|
|
33
|
+
style="display: block; width: 100%; transform: scale(1, 1); transform-origin: 0 0; opacity: 1;"
|
|
34
34
|
>
|
|
35
35
|
<span
|
|
36
36
|
class="mock-TeX"
|
|
@@ -1144,7 +1144,7 @@ exports[`group widget should snapshot: initial render 1`] = `
|
|
|
1144
1144
|
<div
|
|
1145
1145
|
class="perseus-widget-container widget-nohighlight widget-block"
|
|
1146
1146
|
>
|
|
1147
|
-
<
|
|
1147
|
+
<figure
|
|
1148
1148
|
class="perseus-image-widget"
|
|
1149
1149
|
>
|
|
1150
1150
|
<div
|
|
@@ -1193,7 +1193,7 @@ exports[`group widget should snapshot: initial render 1`] = `
|
|
|
1193
1193
|
</div>
|
|
1194
1194
|
</div>
|
|
1195
1195
|
</span>
|
|
1196
|
-
</
|
|
1196
|
+
</figure>
|
|
1197
1197
|
</div>
|
|
1198
1198
|
</div>
|
|
1199
1199
|
</div>
|
|
@@ -24,7 +24,7 @@ exports[`image widget - isMobile %b should snapshot: first render 1`] = `
|
|
|
24
24
|
<div
|
|
25
25
|
class="perseus-widget-container widget-nohighlight widget-block"
|
|
26
26
|
>
|
|
27
|
-
<
|
|
27
|
+
<figure
|
|
28
28
|
class="perseus-image-widget"
|
|
29
29
|
>
|
|
30
30
|
<div
|
|
@@ -73,12 +73,10 @@ exports[`image widget - isMobile %b should snapshot: first render 1`] = `
|
|
|
73
73
|
</div>
|
|
74
74
|
</div>
|
|
75
75
|
</span>
|
|
76
|
-
<
|
|
76
|
+
<figcaption
|
|
77
77
|
class="perseus-image-caption has-title"
|
|
78
78
|
>
|
|
79
|
-
<div
|
|
80
|
-
class="caption_4bstk7"
|
|
81
|
-
>
|
|
79
|
+
<div>
|
|
82
80
|
<div
|
|
83
81
|
class="perseus-renderer perseus-renderer-responsive"
|
|
84
82
|
>
|
|
@@ -97,8 +95,8 @@ exports[`image widget - isMobile %b should snapshot: first render 1`] = `
|
|
|
97
95
|
</div>
|
|
98
96
|
</div>
|
|
99
97
|
</div>
|
|
100
|
-
</
|
|
101
|
-
</
|
|
98
|
+
</figcaption>
|
|
99
|
+
</figure>
|
|
102
100
|
</div>
|
|
103
101
|
</div>
|
|
104
102
|
</div>
|
|
@@ -206,7 +204,7 @@ exports[`image widget - isMobile %b should snapshot: first render 2`] = `
|
|
|
206
204
|
<div
|
|
207
205
|
class="perseus-widget-container widget-nohighlight widget-block"
|
|
208
206
|
>
|
|
209
|
-
<
|
|
207
|
+
<figure
|
|
210
208
|
class="perseus-image-widget"
|
|
211
209
|
>
|
|
212
210
|
<div
|
|
@@ -273,7 +271,7 @@ exports[`image widget - isMobile %b should snapshot: first render 2`] = `
|
|
|
273
271
|
</div>
|
|
274
272
|
</div>
|
|
275
273
|
</span>
|
|
276
|
-
<
|
|
274
|
+
<figcaption
|
|
277
275
|
class="perseus-image-caption"
|
|
278
276
|
>
|
|
279
277
|
<div
|
|
@@ -290,8 +288,8 @@ exports[`image widget - isMobile %b should snapshot: first render 2`] = `
|
|
|
290
288
|
</div>
|
|
291
289
|
</div>
|
|
292
290
|
</div>
|
|
293
|
-
</
|
|
294
|
-
</
|
|
291
|
+
</figcaption>
|
|
292
|
+
</figure>
|
|
295
293
|
</div>
|
|
296
294
|
</div>
|
|
297
295
|
</div>
|
|
@@ -42,7 +42,7 @@ exports[`matrix widget should snapshot on mobile: first mobile render 1`] = `
|
|
|
42
42
|
style="overflow-x: auto; overflow-y: hidden; margin-top: -10px; margin-bottom: -10px; transform: translate3d(0,0,0); padding: 10px 16px 10px 16px;"
|
|
43
43
|
>
|
|
44
44
|
<span
|
|
45
|
-
style="display: block; width: 100%; transform: scale(1, 1)
|
|
45
|
+
style="display: block; width: 100%; transform: scale(1, 1); transform-origin: 0 0; opacity: 1;"
|
|
46
46
|
>
|
|
47
47
|
<span
|
|
48
48
|
class="mock-TeX"
|
|
@@ -18,7 +18,7 @@ exports[`number-line widget should snapshot on mobile: first mobile render 1`] =
|
|
|
18
18
|
style="overflow-x: auto; overflow-y: hidden; margin-top: -10px; margin-bottom: -10px; transform: translate3d(0,0,0); padding: 10px 16px 10px 16px;"
|
|
19
19
|
>
|
|
20
20
|
<span
|
|
21
|
-
style="display: block; width: 100%; transform: scale(1, 1)
|
|
21
|
+
style="display: block; width: 100%; transform: scale(1, 1); transform-origin: 0 0; opacity: 1;"
|
|
22
22
|
>
|
|
23
23
|
<span
|
|
24
24
|
class="mock-TeX"
|
package/src/widgets/image.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/* eslint-disable react/sort-comp */
|
|
2
2
|
import {linterContextDefault} from "@khanacademy/perseus-linter";
|
|
3
|
-
import {StyleSheet, css} from "aphrodite";
|
|
4
3
|
import classNames from "classnames";
|
|
5
4
|
import * as React from "react";
|
|
6
5
|
import _ from "underscore";
|
|
@@ -8,8 +7,6 @@ import _ from "underscore";
|
|
|
8
7
|
import SvgImage from "../components/svg-image";
|
|
9
8
|
import * as Changeable from "../mixins/changeable";
|
|
10
9
|
import Renderer from "../renderer";
|
|
11
|
-
import {baseUnitPx} from "../styles/constants";
|
|
12
|
-
import mediaQueries from "../styles/media-queries";
|
|
13
10
|
|
|
14
11
|
import type {Range, PerseusImageWidgetOptions} from "../perseus-types";
|
|
15
12
|
import type {
|
|
@@ -200,9 +197,8 @@ class ImageWidget extends React.Component<Props> {
|
|
|
200
197
|
const minWidth = isImageFullWidth ? null : "0 !important";
|
|
201
198
|
|
|
202
199
|
titleAndCaption = (
|
|
203
|
-
<
|
|
200
|
+
<figcaption className={className}>
|
|
204
201
|
<div
|
|
205
|
-
className={css(styles.caption)}
|
|
206
202
|
style={{
|
|
207
203
|
// @ts-expect-error - TS2322 - Type 'string | null' is not assignable to type 'MinWidth<string | number> | undefined'.
|
|
208
204
|
minWidth: minWidth,
|
|
@@ -214,16 +210,16 @@ class ImageWidget extends React.Component<Props> {
|
|
|
214
210
|
linterContext={this.props.linterContext}
|
|
215
211
|
/>
|
|
216
212
|
</div>
|
|
217
|
-
</
|
|
213
|
+
</figcaption>
|
|
218
214
|
);
|
|
219
215
|
}
|
|
220
216
|
|
|
221
217
|
return (
|
|
222
|
-
<
|
|
218
|
+
<figure className="perseus-image-widget">
|
|
223
219
|
{image}
|
|
224
220
|
{alt}
|
|
225
221
|
{titleAndCaption}
|
|
226
|
-
</
|
|
222
|
+
</figure>
|
|
227
223
|
);
|
|
228
224
|
}
|
|
229
225
|
let title;
|
|
@@ -243,23 +239,23 @@ class ImageWidget extends React.Component<Props> {
|
|
|
243
239
|
|
|
244
240
|
if (this.props.caption) {
|
|
245
241
|
caption = (
|
|
246
|
-
<
|
|
242
|
+
<figcaption className="perseus-image-caption">
|
|
247
243
|
<Renderer
|
|
248
244
|
content={this.props.caption}
|
|
249
245
|
apiOptions={apiOptions}
|
|
250
246
|
linterContext={this.props.linterContext}
|
|
251
247
|
/>
|
|
252
|
-
</
|
|
248
|
+
</figcaption>
|
|
253
249
|
);
|
|
254
250
|
}
|
|
255
251
|
|
|
256
252
|
return (
|
|
257
|
-
<
|
|
253
|
+
<figure className="perseus-image-widget">
|
|
258
254
|
{title}
|
|
259
255
|
{image}
|
|
260
256
|
{alt}
|
|
261
257
|
{caption}
|
|
262
|
-
</
|
|
258
|
+
</figure>
|
|
263
259
|
);
|
|
264
260
|
}
|
|
265
261
|
|
|
@@ -273,25 +269,6 @@ class ImageWidget extends React.Component<Props> {
|
|
|
273
269
|
}
|
|
274
270
|
}
|
|
275
271
|
|
|
276
|
-
const styles = StyleSheet.create({
|
|
277
|
-
caption: {
|
|
278
|
-
display: "inline-block",
|
|
279
|
-
marginTop: baseUnitPx,
|
|
280
|
-
maxWidth: 640,
|
|
281
|
-
|
|
282
|
-
[mediaQueries.lgOrSmaller]: {
|
|
283
|
-
// TODO(david): This maxWidth is not being used because
|
|
284
|
-
// it's overriden by the 512px max-width we have on
|
|
285
|
-
// paragraphs.
|
|
286
|
-
maxWidth: 540,
|
|
287
|
-
},
|
|
288
|
-
|
|
289
|
-
[mediaQueries.smOrSmaller]: {
|
|
290
|
-
maxWidth: 450,
|
|
291
|
-
},
|
|
292
|
-
},
|
|
293
|
-
});
|
|
294
|
-
|
|
295
272
|
export default {
|
|
296
273
|
name: "image",
|
|
297
274
|
// This widget's accessibility depends on its contents: if the image has
|