@khanacademy/perseus-editor 4.0.0 → 4.1.1
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 +23 -0
- package/dist/es/index.js +47 -48
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +43 -50
- package/dist/index.js.map +1 -1
- package/dist/widgets/image-editor.d.ts +45 -1
- package/dist/widgets/interactive-graph-editor.d.ts +46 -0
- package/package.json +4 -4
- package/src/widgets/__stories__/image-editor.stories.tsx +72 -0
- package/src/widgets/image-editor.tsx +76 -61
- package/src/widgets/interaction-editor/mathquill-input.tsx +1 -1
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -6,17 +6,18 @@ import {
|
|
|
6
6
|
EditorJsonify,
|
|
7
7
|
Util,
|
|
8
8
|
} from "@khanacademy/perseus";
|
|
9
|
-
import createReactClass from "create-react-class";
|
|
10
9
|
import * as React from "react";
|
|
11
10
|
import _ from "underscore";
|
|
12
11
|
|
|
13
12
|
import BlurInput from "../components/blur-input";
|
|
14
13
|
import Editor from "../editor";
|
|
15
14
|
|
|
15
|
+
import type {APIOptions, Range, Size} from "@khanacademy/perseus";
|
|
16
|
+
|
|
16
17
|
const {InfoTip, InlineIcon, RangeInput} = components;
|
|
17
18
|
|
|
18
19
|
const defaultBoxSize = 400;
|
|
19
|
-
const defaultRange = [0, 10];
|
|
20
|
+
const defaultRange = [0, 10] as const;
|
|
20
21
|
const defaultBackgroundImage = {
|
|
21
22
|
url: null,
|
|
22
23
|
width: 0,
|
|
@@ -53,46 +54,63 @@ const captionAlignments = [
|
|
|
53
54
|
"above left",
|
|
54
55
|
];
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
type Props = Changeable.ChangeableProps & {
|
|
58
|
+
apiOptions: APIOptions;
|
|
59
|
+
|
|
60
|
+
title: string;
|
|
61
|
+
range: [Readonly<Range>, Readonly<Range>];
|
|
62
|
+
box: Size;
|
|
63
|
+
backgroundImage: any;
|
|
64
|
+
labels: ReadonlyArray<string>;
|
|
65
|
+
alt: string;
|
|
66
|
+
caption: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
type DefaultProps = {
|
|
70
|
+
title: NonNullable<Props["title"]>;
|
|
71
|
+
range: NonNullable<Props["range"]>;
|
|
72
|
+
box: NonNullable<Props["box"]>;
|
|
73
|
+
backgroundImage: NonNullable<Props["backgroundImage"]>;
|
|
74
|
+
labels: NonNullable<Props["labels"]>;
|
|
75
|
+
alt: NonNullable<Props["alt"]>;
|
|
76
|
+
caption: NonNullable<Props["caption"]>;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
type State = {
|
|
80
|
+
backgroundImageError?: string;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
class ImageEditor extends React.Component<Props> {
|
|
84
|
+
_isMounted = false;
|
|
85
|
+
|
|
86
|
+
static displayName = "ImageEditor";
|
|
87
|
+
static widgetName = "image";
|
|
88
|
+
|
|
89
|
+
static defaultProps: DefaultProps = {
|
|
90
|
+
title: "",
|
|
91
|
+
range: [defaultRange, defaultRange],
|
|
92
|
+
box: [defaultBoxSize, defaultBoxSize],
|
|
93
|
+
backgroundImage: defaultBackgroundImage,
|
|
94
|
+
labels: [],
|
|
95
|
+
alt: "",
|
|
96
|
+
caption: "",
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
state: State = {
|
|
100
|
+
backgroundImageError: "",
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
componentDidMount() {
|
|
68
104
|
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
69
105
|
// this.isMounted() but is still considered an anti-pattern.
|
|
70
106
|
this._isMounted = true;
|
|
71
|
-
}
|
|
107
|
+
}
|
|
72
108
|
|
|
73
|
-
componentWillUnmount
|
|
109
|
+
componentWillUnmount() {
|
|
74
110
|
this._isMounted = false;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
title: "",
|
|
80
|
-
range: [defaultRange, defaultRange],
|
|
81
|
-
box: [defaultBoxSize, defaultBoxSize],
|
|
82
|
-
backgroundImage: defaultBackgroundImage,
|
|
83
|
-
labels: [],
|
|
84
|
-
alt: "",
|
|
85
|
-
caption: "",
|
|
86
|
-
};
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
getInitialState: function () {
|
|
90
|
-
return {
|
|
91
|
-
backgroundImageError: "",
|
|
92
|
-
};
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
render: function () {
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
render() {
|
|
96
114
|
const backgroundImage = this.props.backgroundImage;
|
|
97
115
|
|
|
98
116
|
const imageSettings = (
|
|
@@ -183,9 +201,9 @@ const ImageEditor: any = createReactClass({
|
|
|
183
201
|
{backgroundImage.url && imageSettings}
|
|
184
202
|
</div>
|
|
185
203
|
);
|
|
186
|
-
}
|
|
204
|
+
}
|
|
187
205
|
|
|
188
|
-
_renderRowForLabel
|
|
206
|
+
_renderRowForLabel(label, i) {
|
|
189
207
|
return (
|
|
190
208
|
<tr key={i}>
|
|
191
209
|
<td>
|
|
@@ -233,46 +251,46 @@ const ImageEditor: any = createReactClass({
|
|
|
233
251
|
</td>
|
|
234
252
|
</tr>
|
|
235
253
|
);
|
|
236
|
-
}
|
|
254
|
+
}
|
|
237
255
|
|
|
238
256
|
change(...args) {
|
|
239
257
|
return Changeable.change.apply(this, args);
|
|
240
|
-
}
|
|
258
|
+
}
|
|
241
259
|
|
|
242
|
-
removeLabel
|
|
260
|
+
removeLabel(labelIndex, e) {
|
|
243
261
|
e.preventDefault();
|
|
244
|
-
const labels =
|
|
262
|
+
const labels = [...this.props.labels];
|
|
245
263
|
labels.splice(labelIndex, 1);
|
|
246
264
|
this.props.onChange({labels: labels});
|
|
247
|
-
}
|
|
265
|
+
}
|
|
248
266
|
|
|
249
|
-
onCoordinateChange
|
|
267
|
+
onCoordinateChange(labelIndex, newCoordinates) {
|
|
250
268
|
const labels = this.props.labels.slice();
|
|
251
269
|
labels[labelIndex] = _.extend({}, labels[labelIndex], {
|
|
252
270
|
coordinates: newCoordinates,
|
|
253
271
|
});
|
|
254
272
|
this.props.onChange({labels: labels});
|
|
255
|
-
}
|
|
273
|
+
}
|
|
256
274
|
|
|
257
|
-
onContentChange
|
|
275
|
+
onContentChange(labelIndex, e) {
|
|
258
276
|
const newContent = e.target.value;
|
|
259
277
|
const labels = this.props.labels.slice();
|
|
260
278
|
labels[labelIndex] = _.extend({}, labels[labelIndex], {
|
|
261
279
|
content: newContent,
|
|
262
280
|
});
|
|
263
281
|
this.props.onChange({labels: labels});
|
|
264
|
-
}
|
|
282
|
+
}
|
|
265
283
|
|
|
266
|
-
onAlignmentChange
|
|
284
|
+
onAlignmentChange(labelIndex, e) {
|
|
267
285
|
const newAlignment = e.target.value;
|
|
268
286
|
const labels = this.props.labels.slice();
|
|
269
287
|
labels[labelIndex] = _.extend({}, labels[labelIndex], {
|
|
270
288
|
alignment: newAlignment,
|
|
271
289
|
});
|
|
272
290
|
this.props.onChange({labels: labels});
|
|
273
|
-
}
|
|
291
|
+
}
|
|
274
292
|
|
|
275
|
-
setUrl
|
|
293
|
+
setUrl(url, width, height, silent) {
|
|
276
294
|
// Because this calls into WidgetEditor._handleWidgetChange, which
|
|
277
295
|
// checks for this widget's ref to serialize it.
|
|
278
296
|
//
|
|
@@ -295,15 +313,12 @@ const ImageEditor: any = createReactClass({
|
|
|
295
313
|
null,
|
|
296
314
|
silent,
|
|
297
315
|
);
|
|
298
|
-
}
|
|
316
|
+
}
|
|
299
317
|
|
|
300
318
|
// silently load the image when the component mounts
|
|
301
319
|
// silently update url and sizes when the image loads
|
|
302
320
|
// noisily load the image in response to the author changing it
|
|
303
|
-
onUrlChange:
|
|
304
|
-
url: string | undefined | null,
|
|
305
|
-
silent: boolean,
|
|
306
|
-
) {
|
|
321
|
+
async onUrlChange(url: string | undefined | null, silent: boolean) {
|
|
307
322
|
// Check if we've been passed something that looks like a URL
|
|
308
323
|
if (!url) {
|
|
309
324
|
this.setUrl(url, 0, 0, silent);
|
|
@@ -336,17 +351,17 @@ const ImageEditor: any = createReactClass({
|
|
|
336
351
|
)}`,
|
|
337
352
|
});
|
|
338
353
|
}
|
|
339
|
-
}
|
|
354
|
+
}
|
|
340
355
|
|
|
341
|
-
onRangeChange
|
|
356
|
+
onRangeChange(type, newRange) {
|
|
342
357
|
const range = this.props.range.slice();
|
|
343
358
|
range[type] = newRange;
|
|
344
359
|
this.props.onChange({range: range});
|
|
345
|
-
}
|
|
360
|
+
}
|
|
346
361
|
|
|
347
362
|
serialize() {
|
|
348
363
|
return EditorJsonify.serialize.call(this);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
351
366
|
|
|
352
367
|
export default ImageEditor;
|
|
@@ -46,7 +46,7 @@ export default function MathquillInput(props: Props) {
|
|
|
46
46
|
props.onChange(value);
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
|
-
upOutOf: (mathField) => {
|
|
49
|
+
upOutOf: (mathField: MathFieldInterface) => {
|
|
50
50
|
// This handler is called when the user presses the up
|
|
51
51
|
// arrow key, but there is nowhere in the expression to go
|
|
52
52
|
// up to (no numerator or exponent). For ease of use,
|