@khanacademy/perseus-editor 4.1.5 → 4.1.6
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/dist/es/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +9 -6
- package/.babelrc.js +0 -8
- package/.eslintrc.js +0 -12
- package/CHANGELOG.md +0 -1155
- package/src/README.md +0 -7
- package/src/__stories__/editor-page.stories.tsx +0 -65
- package/src/__stories__/editor.stories.tsx +0 -134
- package/src/__stories__/item-editor.stories.tsx +0 -41
- package/src/__stories__/tex-error-view.stories.tsx +0 -26
- package/src/__testdata__/input-number.testdata.ts +0 -27
- package/src/__tests__/editor.test.tsx +0 -142
- package/src/__tests__/hint-editor.test.tsx +0 -83
- package/src/__tests__/i18n.test.ts +0 -501
- package/src/__tests__/integration.test.ts +0 -5
- package/src/__tests__/item-extras-editor.test.tsx +0 -85
- package/src/__tests__/traversal.test.ts +0 -377
- package/src/all-editors.test.ts +0 -16
- package/src/all-editors.ts +0 -83
- package/src/all-widgets.ts +0 -7
- package/src/article-editor.tsx +0 -438
- package/src/components/__stories__/blur-input.stories.tsx +0 -22
- package/src/components/__stories__/section-control-button.stories.tsx +0 -26
- package/src/components/__tests__/blur-input.test.tsx +0 -67
- package/src/components/blur-input.tsx +0 -69
- package/src/components/checkbox.tsx +0 -194
- package/src/components/device-framer.tsx +0 -90
- package/src/components/drag-target.tsx +0 -87
- package/src/components/dropdown-option.tsx +0 -311
- package/src/components/form-wrapped-text-field.tsx +0 -245
- package/src/components/graph-points-count-selector.tsx +0 -31
- package/src/components/graph-settings.tsx +0 -627
- package/src/components/graph-type-selector.tsx +0 -31
- package/src/components/hover-behavior.ts +0 -177
- package/src/components/json-editor.tsx +0 -121
- package/src/components/link.ts +0 -173
- package/src/components/section-control-button.tsx +0 -53
- package/src/components/segment-count-selector.tsx +0 -27
- package/src/components/simple-button.tsx +0 -85
- package/src/components/sortable.tsx +0 -187
- package/src/components/util.ts +0 -38
- package/src/components/viewport-resizer.tsx +0 -72
- package/src/components/widget-editor.tsx +0 -222
- package/src/components/widget-select.tsx +0 -48
- package/src/diffs/__stories__/perseus-diff-wrapper.tsx +0 -11
- package/src/diffs/__stories__/structured-item-diff.stories.tsx +0 -855
- package/src/diffs/__stories__/tags-diff.stories.tsx +0 -36
- package/src/diffs/__stories__/text-diff.stories.tsx +0 -35
- package/src/diffs/__tests__/split-diff.test.ts +0 -33
- package/src/diffs/__tests__/string-array-diff.test.ts +0 -73
- package/src/diffs/__tests__/structured-item-diff.test.ts +0 -433
- package/src/diffs/__tests__/widget-diff-performer.test.ts +0 -43
- package/src/diffs/article-diff.tsx +0 -69
- package/src/diffs/item-diff.tsx +0 -76
- package/src/diffs/renderer-diff.tsx +0 -137
- package/src/diffs/split-diff.ts +0 -31
- package/src/diffs/string-array-diff.ts +0 -74
- package/src/diffs/structured-item-diff.tsx +0 -275
- package/src/diffs/tags-diff.tsx +0 -49
- package/src/diffs/text-diff.tsx +0 -193
- package/src/diffs/widget-diff-performer.ts +0 -69
- package/src/diffs/widget-diff.tsx +0 -290
- package/src/editor-page.tsx +0 -325
- package/src/editor.tsx +0 -1099
- package/src/hint-editor.tsx +0 -469
- package/src/i18n.ts +0 -140
- package/src/iframe-content-renderer.tsx +0 -188
- package/src/index.ts +0 -30
- package/src/item-editor.tsx +0 -158
- package/src/item-extras-editor.tsx +0 -230
- package/src/katex-mhchem/index.js +0 -73
- package/src/katex-mhchem/parser.js +0 -2659
- package/src/multirenderer-editor.tsx +0 -1061
- package/src/stateful-article-editor.tsx +0 -173
- package/src/stateful-editor-page.tsx +0 -88
- package/src/styles/perseus-editor.less +0 -1321
- package/src/testing-widgets.ts +0 -11
- package/src/tex-error-view.tsx +0 -90
- package/src/util/points.test.ts +0 -24
- package/src/util/points.ts +0 -13
- package/src/util/register-all-widgets-and-editors-for-testing.test.ts +0 -17
- package/src/util/register-all-widgets-and-editors-for-testing.ts +0 -17
- package/src/version.ts +0 -10
- package/src/widgets/__stories__/categorizer-editor.stories.tsx +0 -24
- package/src/widgets/__stories__/definition-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/dropdown-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/explanation-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/expression-editor.stories.tsx +0 -122
- package/src/widgets/__stories__/image-editor.stories.tsx +0 -72
- package/src/widgets/__stories__/input-number-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/interaction-editor.stories.tsx +0 -37
- package/src/widgets/__stories__/label-image-editor.stories.tsx +0 -99
- package/src/widgets/__stories__/matcher-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/number-line-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/numeric-input-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/radio-editor.stories.tsx +0 -116
- package/src/widgets/__stories__/sorter-editor.stories.tsx +0 -18
- package/src/widgets/__tests__/categorizer-editor.test.tsx +0 -51
- package/src/widgets/__tests__/definition-editor.test.tsx +0 -41
- package/src/widgets/__tests__/dropdown-editor.test.tsx +0 -57
- package/src/widgets/__tests__/explanation-editor.test.tsx +0 -59
- package/src/widgets/__tests__/expression-editor.test.tsx +0 -403
- package/src/widgets/__tests__/input-number-editor.test.tsx +0 -128
- package/src/widgets/__tests__/matcher-editor.test.tsx +0 -47
- package/src/widgets/__tests__/number-line-editor.test.tsx +0 -144
- package/src/widgets/__tests__/numeric-input-editor.test.tsx +0 -177
- package/src/widgets/__tests__/radio-editor.test.tsx +0 -118
- package/src/widgets/__tests__/sorter-editor.test.tsx +0 -55
- package/src/widgets/categorizer-editor.tsx +0 -104
- package/src/widgets/cs-program-editor.tsx +0 -256
- package/src/widgets/definition-editor.tsx +0 -77
- package/src/widgets/deprecated-standin-editor.tsx +0 -29
- package/src/widgets/dropdown-editor.tsx +0 -211
- package/src/widgets/example-graphie-widget-editor.tsx +0 -70
- package/src/widgets/example-graphie-widget.tsx +0 -172
- package/src/widgets/example-widget-editor.tsx +0 -62
- package/src/widgets/example-widget.tsx +0 -154
- package/src/widgets/explanation-editor.tsx +0 -92
- package/src/widgets/expression-editor.tsx +0 -557
- package/src/widgets/graded-group-editor.tsx +0 -161
- package/src/widgets/graded-group-set-editor.tsx +0 -104
- package/src/widgets/grapher-editor.tsx +0 -155
- package/src/widgets/group-editor.tsx +0 -83
- package/src/widgets/iframe-editor.tsx +0 -202
- package/src/widgets/image-editor.tsx +0 -367
- package/src/widgets/input-number-editor.tsx +0 -288
- package/src/widgets/interaction-editor/constraint-editor.tsx +0 -134
- package/src/widgets/interaction-editor/function-editor.tsx +0 -108
- package/src/widgets/interaction-editor/index.ts +0 -3
- package/src/widgets/interaction-editor/interaction-editor.tsx +0 -749
- package/src/widgets/interaction-editor/label-editor.tsx +0 -87
- package/src/widgets/interaction-editor/line-editor.tsx +0 -125
- package/src/widgets/interaction-editor/mathquill-input.tsx +0 -81
- package/src/widgets/interaction-editor/movable-line-editor.tsx +0 -121
- package/src/widgets/interaction-editor/movable-point-editor.tsx +0 -89
- package/src/widgets/interaction-editor/parametric-editor.tsx +0 -117
- package/src/widgets/interaction-editor/point-editor.tsx +0 -72
- package/src/widgets/interaction-editor/rectangle-editor.tsx +0 -96
- package/src/widgets/interactive-graph-editor.tsx +0 -571
- package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -82
- package/src/widgets/label-image/__stories__/behavior.stories.tsx +0 -31
- package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -75
- package/src/widgets/label-image/__stories__/question-markers.stories.tsx +0 -107
- package/src/widgets/label-image/__stories__/select-image.stories.tsx +0 -59
- package/src/widgets/label-image/answer-choices.tsx +0 -231
- package/src/widgets/label-image/behavior.tsx +0 -122
- package/src/widgets/label-image/marker.tsx +0 -290
- package/src/widgets/label-image/question-markers.tsx +0 -176
- package/src/widgets/label-image/select-image.tsx +0 -81
- package/src/widgets/label-image-editor.tsx +0 -252
- package/src/widgets/lights-puzzle-editor.tsx +0 -278
- package/src/widgets/matcher-editor.tsx +0 -149
- package/src/widgets/matrix-editor.tsx +0 -156
- package/src/widgets/measurer-editor.tsx +0 -253
- package/src/widgets/molecule-editor.tsx +0 -64
- package/src/widgets/number-line-editor.tsx +0 -512
- package/src/widgets/numeric-input-editor.tsx +0 -536
- package/src/widgets/orderer-editor.tsx +0 -172
- package/src/widgets/passage-editor.tsx +0 -117
- package/src/widgets/passage-ref-editor.tsx +0 -80
- package/src/widgets/passage-ref-target-editor.tsx +0 -49
- package/src/widgets/plotter-editor.tsx +0 -542
- package/src/widgets/radio/editor.tsx +0 -398
- package/src/widgets/reaction-diagram-editor.tsx +0 -131
- package/src/widgets/sequence-editor.tsx +0 -205
- package/src/widgets/simple-markdown-tester-editor.tsx +0 -86
- package/src/widgets/simple-markdown-tester.tsx +0 -92
- package/src/widgets/simulator-editor.tsx +0 -146
- package/src/widgets/sorter-editor.tsx +0 -103
- package/src/widgets/table-editor.tsx +0 -164
- package/src/widgets/unit-editor.tsx +0 -299
- package/src/widgets/video-editor.tsx +0 -73
- package/tsconfig-build.json +0 -22
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import createReactClass from "create-react-class";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import ReactDOM from "react-dom";
|
|
5
|
-
|
|
6
|
-
const PT = PropTypes;
|
|
7
|
-
|
|
8
|
-
// Takes an array of components to sort
|
|
9
|
-
// eslint-disable-next-line react/no-unsafe
|
|
10
|
-
const SortableArea = createReactClass({
|
|
11
|
-
propTypes: {
|
|
12
|
-
className: PT.string,
|
|
13
|
-
components: PT.arrayOf(PT.node).isRequired,
|
|
14
|
-
onReorder: PT.func.isRequired,
|
|
15
|
-
style: PT.any,
|
|
16
|
-
verify: PT.func,
|
|
17
|
-
},
|
|
18
|
-
getDefaultProps: function () {
|
|
19
|
-
return {verify: () => true};
|
|
20
|
-
},
|
|
21
|
-
getInitialState: function () {
|
|
22
|
-
return {
|
|
23
|
-
// index of the component being dragged
|
|
24
|
-
dragging: null,
|
|
25
|
-
components: this.props.components,
|
|
26
|
-
};
|
|
27
|
-
},
|
|
28
|
-
// Firefox refuses to drag an element unless you set data on it. Hackily
|
|
29
|
-
// add data each time an item is dragged.
|
|
30
|
-
componentDidMount: function () {
|
|
31
|
-
this._setDragEvents();
|
|
32
|
-
},
|
|
33
|
-
// eslint-disable-next-line react/no-unsafe
|
|
34
|
-
UNSAFE_componentWillReceiveProps: function (nextProps) {
|
|
35
|
-
this.setState({components: nextProps.components});
|
|
36
|
-
},
|
|
37
|
-
componentDidUpdate: function () {
|
|
38
|
-
this._setDragEvents();
|
|
39
|
-
},
|
|
40
|
-
// Alternatively send each handler to each component individually,
|
|
41
|
-
// partially applied
|
|
42
|
-
onDragStart: function (startIndex) {
|
|
43
|
-
this.setState({dragging: startIndex});
|
|
44
|
-
},
|
|
45
|
-
onDrop: function () {
|
|
46
|
-
// tell the parent component
|
|
47
|
-
this.setState({dragging: null});
|
|
48
|
-
this.props.onReorder(this.state.components);
|
|
49
|
-
},
|
|
50
|
-
onDragEnter: function (enterIndex) {
|
|
51
|
-
// When a label is first dragged it triggers a dragEnter with itself,
|
|
52
|
-
// which we don't care about.
|
|
53
|
-
if (this.state.dragging === enterIndex) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const newComponents = this.state.components.slice();
|
|
58
|
-
|
|
59
|
-
// splice the tab out of its old position
|
|
60
|
-
const removed = newComponents.splice(this.state.dragging, 1);
|
|
61
|
-
// ... and into its new position
|
|
62
|
-
newComponents.splice(enterIndex, 0, removed[0]);
|
|
63
|
-
|
|
64
|
-
const verified = this.props.verify(newComponents);
|
|
65
|
-
if (verified) {
|
|
66
|
-
this.setState({
|
|
67
|
-
dragging: enterIndex,
|
|
68
|
-
components: newComponents,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
return verified;
|
|
72
|
-
},
|
|
73
|
-
_listenEvent: function (e) {
|
|
74
|
-
e.dataTransfer.setData("hackhackhack", "because browsers!");
|
|
75
|
-
},
|
|
76
|
-
_cancelEvent: function (e) {
|
|
77
|
-
// prevent the browser from redirecting to 'because browsers!'
|
|
78
|
-
e.preventDefault();
|
|
79
|
-
},
|
|
80
|
-
_setDragEvents: function () {
|
|
81
|
-
this._dragItems = this._dragItems || [];
|
|
82
|
-
const items =
|
|
83
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'
|
|
84
|
-
ReactDOM.findDOMNode(this).querySelectorAll("[draggable=true]");
|
|
85
|
-
|
|
86
|
-
const oldItems: Array<HTMLElement> = [];
|
|
87
|
-
const newItems: Array<HTMLElement> = [];
|
|
88
|
-
|
|
89
|
-
for (let i = 0; i < this._dragItems.length; i++) {
|
|
90
|
-
const item = this._dragItems[i];
|
|
91
|
-
if (items.indexOf(item) < 0) {
|
|
92
|
-
oldItems.push(item);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
for (let i = 0; i < items.length; i++) {
|
|
97
|
-
const item = items[i];
|
|
98
|
-
if (this._dragItems.indexOf(item) < 0) {
|
|
99
|
-
newItems.push(item);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
for (let i = 0; i < newItems.length; i++) {
|
|
104
|
-
const dragItem = newItems[i];
|
|
105
|
-
dragItem.addEventListener("dragstart", this._listenEvent);
|
|
106
|
-
dragItem.addEventListener("drop", this._cancelEvent);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
for (let i = 0; i < oldItems.length; i++) {
|
|
110
|
-
const dragItem = oldItems[i];
|
|
111
|
-
dragItem.removeEventListener("dragstart", this._listenEvent);
|
|
112
|
-
dragItem.removeEventListener("drop", this._cancelEvent);
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
render: function () {
|
|
116
|
-
const sortables = this.state.components.map((component, index) => (
|
|
117
|
-
<SortableItem
|
|
118
|
-
index={index}
|
|
119
|
-
component={component}
|
|
120
|
-
area={this}
|
|
121
|
-
key={component.key}
|
|
122
|
-
draggable={component.props.draggable}
|
|
123
|
-
dragging={index === this.state.dragging}
|
|
124
|
-
/>
|
|
125
|
-
));
|
|
126
|
-
return (
|
|
127
|
-
<ol className={this.props.className} style={this.props.style}>
|
|
128
|
-
{sortables}
|
|
129
|
-
</ol>
|
|
130
|
-
);
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// An individual sortable item
|
|
135
|
-
const SortableItem = createReactClass({
|
|
136
|
-
propTypes: {
|
|
137
|
-
area: PT.shape({
|
|
138
|
-
onDragEnter: PT.func.isRequired,
|
|
139
|
-
onDragStart: PT.func.isRequired,
|
|
140
|
-
onDrop: PT.func.isRequired,
|
|
141
|
-
}),
|
|
142
|
-
component: PT.node.isRequired,
|
|
143
|
-
dragging: PT.bool.isRequired,
|
|
144
|
-
draggable: PT.bool.isRequired,
|
|
145
|
-
index: PT.number.isRequired,
|
|
146
|
-
},
|
|
147
|
-
handleDragStart: function (e) {
|
|
148
|
-
e.nativeEvent.dataTransfer.effectAllowed = "move";
|
|
149
|
-
this.props.area.onDragStart(this.props.index);
|
|
150
|
-
},
|
|
151
|
-
handleDrop: function () {
|
|
152
|
-
this.props.area.onDrop(this.props.index);
|
|
153
|
-
},
|
|
154
|
-
handleDragEnter: function (e) {
|
|
155
|
-
const verified = this.props.area.onDragEnter(this.props.index);
|
|
156
|
-
// Ideally this would change the cursor based on whether this is a
|
|
157
|
-
// valid place to drop.
|
|
158
|
-
e.nativeEvent.dataTransfer.effectAllowed = verified ? "move" : "none";
|
|
159
|
-
},
|
|
160
|
-
handleDragOver: function (e) {
|
|
161
|
-
// allow a drop by preventing default handling
|
|
162
|
-
e.preventDefault();
|
|
163
|
-
},
|
|
164
|
-
render: function () {
|
|
165
|
-
let dragState = "sortable-disabled";
|
|
166
|
-
if (this.props.dragging) {
|
|
167
|
-
dragState = "sortable-dragging";
|
|
168
|
-
} else if (this.props.draggable) {
|
|
169
|
-
dragState = "sortable-enabled";
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return (
|
|
173
|
-
<li
|
|
174
|
-
draggable={this.props.draggable}
|
|
175
|
-
className={dragState}
|
|
176
|
-
onDragStart={this.handleDragStart}
|
|
177
|
-
onDrop={this.handleDrop}
|
|
178
|
-
onDragEnter={this.handleDragEnter}
|
|
179
|
-
onDragOver={this.handleDragOver}
|
|
180
|
-
>
|
|
181
|
-
{this.props.component}
|
|
182
|
-
</li>
|
|
183
|
-
);
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
export default SortableArea;
|
package/src/components/util.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export function focusWithChromeStickyFocusBugWorkaround(element: Element) {
|
|
2
|
-
// NOTE(marksandstrom) Chrome sticky focus bug.
|
|
3
|
-
//
|
|
4
|
-
// There is a bug in Chrome related to sticky positioning and
|
|
5
|
-
// focus: When a child of an element with sticky positioning is
|
|
6
|
-
// focused with the focus() method, the scrollable area
|
|
7
|
-
// containing that element will scroll to the top (even though
|
|
8
|
-
// the element is already visble on the page). For a dropdown
|
|
9
|
-
// this means that the area will be scrolled to the top when
|
|
10
|
-
// the user interacts with the dropdown (since we actively
|
|
11
|
-
// manage dropdown header and option focus for keyboard users).
|
|
12
|
-
//
|
|
13
|
-
// To work around this bug the `preventScroll` focus option is
|
|
14
|
-
// used.
|
|
15
|
-
//
|
|
16
|
-
// Using `focusOptions` is an experimental browser feature
|
|
17
|
-
// "that should not be used in production code". However, since
|
|
18
|
-
// Chrome is the only browser we support that exhibits this
|
|
19
|
-
// buggy behavior and using `focusOptions` works around the
|
|
20
|
-
// bug, it is perfectly reasonable to use the feature in this
|
|
21
|
-
// case. We are not relying on the effect of the
|
|
22
|
-
// `preventScroll` option otherwise.
|
|
23
|
-
//
|
|
24
|
-
// focusOptions documentation:
|
|
25
|
-
//
|
|
26
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
|
|
27
|
-
//
|
|
28
|
-
// Note that there could be cases where we want default focus() behavior.
|
|
29
|
-
// One that comes to mind is a dropdown with a long list of options that
|
|
30
|
-
// might be rendered off screen when opened. With this workaround the
|
|
31
|
-
// selected option would not be scrolled into view if the option is near
|
|
32
|
-
// the end of the list. We're not aware of any uses of the dropdown where
|
|
33
|
-
// this is an issue, however, so there isn't currently a way to out of the
|
|
34
|
-
// `preventScroll` behavior.
|
|
35
|
-
|
|
36
|
-
// @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element'.
|
|
37
|
-
element.focus({preventScroll: true});
|
|
38
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A component that displays controls for choosing a viewport size.
|
|
3
|
-
* Renders three buttons: "Phone", "Tablet", and "Desktop".
|
|
4
|
-
*/
|
|
5
|
-
import {components, constants, icons} from "@khanacademy/perseus";
|
|
6
|
-
import PropTypes from "prop-types";
|
|
7
|
-
import * as React from "react";
|
|
8
|
-
|
|
9
|
-
import type {DeviceType} from "@khanacademy/perseus";
|
|
10
|
-
|
|
11
|
-
const {ButtonGroup, InlineIcon} = components;
|
|
12
|
-
const {devices} = constants;
|
|
13
|
-
const {iconDesktop, iconMobilePhone, iconTablet} = icons;
|
|
14
|
-
|
|
15
|
-
type Props = {
|
|
16
|
-
deviceType: DeviceType;
|
|
17
|
-
onViewportSizeChanged: (arg1: DeviceType) => unknown;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
class ViewportResizer extends React.Component<Props> {
|
|
21
|
-
static propTypes = {
|
|
22
|
-
// The current device type that is selected.
|
|
23
|
-
deviceType: PropTypes.string.isRequired,
|
|
24
|
-
// A callback that is passed (width, height) as the dimensions of the
|
|
25
|
-
// viewport to resize to.
|
|
26
|
-
onViewportSizeChanged: PropTypes.func.isRequired,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
handleChange: (value: DeviceType) => void = (value: DeviceType) => {
|
|
30
|
-
this.props.onViewportSizeChanged(value);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
render(): React.ReactNode {
|
|
34
|
-
const phoneButtonContents = (
|
|
35
|
-
<span>
|
|
36
|
-
<InlineIcon {...iconMobilePhone} /> Phone
|
|
37
|
-
</span>
|
|
38
|
-
);
|
|
39
|
-
const tabletButtonContents = (
|
|
40
|
-
<span>
|
|
41
|
-
<InlineIcon {...iconTablet} /> Tablet
|
|
42
|
-
</span>
|
|
43
|
-
);
|
|
44
|
-
const desktopButtonContents = (
|
|
45
|
-
<span>
|
|
46
|
-
<InlineIcon {...iconDesktop} /> Desktop
|
|
47
|
-
</span>
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
// TODO(david): Allow input of custom viewport sizes.
|
|
51
|
-
return (
|
|
52
|
-
<span className="viewport-resizer">
|
|
53
|
-
Viewport:{" "}
|
|
54
|
-
<ButtonGroup
|
|
55
|
-
value={this.props.deviceType}
|
|
56
|
-
allowEmpty={false}
|
|
57
|
-
buttons={[
|
|
58
|
-
{value: devices.PHONE, content: phoneButtonContents},
|
|
59
|
-
{value: devices.TABLET, content: tabletButtonContents},
|
|
60
|
-
{
|
|
61
|
-
value: devices.DESKTOP,
|
|
62
|
-
content: desktopButtonContents,
|
|
63
|
-
},
|
|
64
|
-
]}
|
|
65
|
-
onChange={this.handleChange}
|
|
66
|
-
/>
|
|
67
|
-
</span>
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export default ViewportResizer;
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
2
|
-
import {
|
|
3
|
-
components,
|
|
4
|
-
icons,
|
|
5
|
-
Widgets,
|
|
6
|
-
WIDGET_PROP_DENYLIST,
|
|
7
|
-
} from "@khanacademy/perseus";
|
|
8
|
-
import * as React from "react";
|
|
9
|
-
import _ from "underscore";
|
|
10
|
-
|
|
11
|
-
import SectionControlButton from "./section-control-button";
|
|
12
|
-
|
|
13
|
-
import type {Alignment, PerseusWidget} from "@khanacademy/perseus";
|
|
14
|
-
|
|
15
|
-
const {InlineIcon} = components;
|
|
16
|
-
const {iconChevronDown, iconChevronRight, iconTrash} = icons;
|
|
17
|
-
|
|
18
|
-
type WidgetEditorProps = {
|
|
19
|
-
// Unserialized props
|
|
20
|
-
id: string;
|
|
21
|
-
onChange: (
|
|
22
|
-
widgetInfo: PerseusWidget,
|
|
23
|
-
cb?: () => unknown,
|
|
24
|
-
silent?: boolean,
|
|
25
|
-
) => unknown;
|
|
26
|
-
onRemove: () => unknown;
|
|
27
|
-
apiOptions: any;
|
|
28
|
-
} & PerseusWidget;
|
|
29
|
-
|
|
30
|
-
type WidgetEditorState = {
|
|
31
|
-
showWidget: boolean;
|
|
32
|
-
widgetInfo: PerseusWidget;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const _upgradeWidgetInfo = (props: WidgetEditorProps): PerseusWidget => {
|
|
36
|
-
// We can't call serialize here because this.refs.widget
|
|
37
|
-
// doesn't exist before this component is mounted.
|
|
38
|
-
const filteredProps = _.omit(props, WIDGET_PROP_DENYLIST);
|
|
39
|
-
// @ts-expect-error TS(2345) Type '"categorizer" | undefined' is not assignable to type '"deprecated-standin"'.
|
|
40
|
-
return Widgets.upgradeWidgetInfoToLatestVersion(filteredProps);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// This component handles upgading widget editor props via prop
|
|
44
|
-
// upgrade transforms. Widget editors will always be rendered
|
|
45
|
-
// with all available transforms applied, but the results of those
|
|
46
|
-
// transforms will not be propogated upwards until serialization.
|
|
47
|
-
// eslint-disable-next-line react/no-unsafe
|
|
48
|
-
class WidgetEditor extends React.Component<
|
|
49
|
-
WidgetEditorProps,
|
|
50
|
-
WidgetEditorState
|
|
51
|
-
> {
|
|
52
|
-
constructor(props: WidgetEditorProps) {
|
|
53
|
-
super(props);
|
|
54
|
-
this.state = {
|
|
55
|
-
showWidget: false,
|
|
56
|
-
widgetInfo: _upgradeWidgetInfo(props),
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// eslint-disable-next-line react/no-unsafe
|
|
61
|
-
UNSAFE_componentWillReceiveProps(nextProps: WidgetEditorProps) {
|
|
62
|
-
this.setState({widgetInfo: _upgradeWidgetInfo(nextProps)});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
_toggleWidget = (e: React.SyntheticEvent) => {
|
|
66
|
-
e.preventDefault();
|
|
67
|
-
this.setState({showWidget: !this.state.showWidget});
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
_handleWidgetChange = (
|
|
71
|
-
newProps: WidgetEditorProps,
|
|
72
|
-
cb: () => unknown,
|
|
73
|
-
silent: boolean,
|
|
74
|
-
) => {
|
|
75
|
-
const newWidgetInfo = Object.assign(
|
|
76
|
-
{},
|
|
77
|
-
this.state.widgetInfo,
|
|
78
|
-
) as PerseusWidget;
|
|
79
|
-
newWidgetInfo.options = Object.assign(
|
|
80
|
-
// eslint-disable-next-line react/no-string-refs
|
|
81
|
-
// @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
|
|
82
|
-
this.refs.widget.serialize(),
|
|
83
|
-
newProps,
|
|
84
|
-
);
|
|
85
|
-
this.props.onChange(newWidgetInfo, cb, silent);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
_toggleStatic = (e: Event) => {
|
|
89
|
-
e.preventDefault();
|
|
90
|
-
const newWidgetInfo = Object.assign({}, this.state.widgetInfo, {
|
|
91
|
-
static: !this.state.widgetInfo.static,
|
|
92
|
-
}) as PerseusWidget;
|
|
93
|
-
this.props.onChange(newWidgetInfo);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
_handleAlignmentChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {
|
|
97
|
-
const newAlignment = e.currentTarget.value as Alignment;
|
|
98
|
-
const newWidgetInfo = Object.assign(
|
|
99
|
-
{},
|
|
100
|
-
this.state.widgetInfo,
|
|
101
|
-
) as PerseusWidget;
|
|
102
|
-
newWidgetInfo.alignment = newAlignment;
|
|
103
|
-
this.props.onChange(newWidgetInfo);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
getSaveWarnings = () => {
|
|
107
|
-
// eslint-disable-next-line react/no-string-refs
|
|
108
|
-
// @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
|
|
109
|
-
const issuesFunc = this.refs.widget.getSaveWarnings;
|
|
110
|
-
return issuesFunc ? issuesFunc() : [];
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
serialize = () => {
|
|
114
|
-
// TODO(alex): Make this properly handle the case where we load json
|
|
115
|
-
// with a more recent widget version than this instance of Perseus
|
|
116
|
-
// knows how to handle.
|
|
117
|
-
const widgetInfo = this.state.widgetInfo;
|
|
118
|
-
return {
|
|
119
|
-
type: widgetInfo.type,
|
|
120
|
-
alignment: widgetInfo.alignment,
|
|
121
|
-
static: widgetInfo.static,
|
|
122
|
-
graded: widgetInfo.graded,
|
|
123
|
-
// eslint-disable-next-line react/no-string-refs
|
|
124
|
-
// @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
|
|
125
|
-
options: this.refs.widget.serialize(),
|
|
126
|
-
version: widgetInfo.version,
|
|
127
|
-
};
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
render(): React.ReactNode {
|
|
131
|
-
const widgetInfo = this.state.widgetInfo;
|
|
132
|
-
|
|
133
|
-
const Ed = Widgets.getEditor(widgetInfo.type);
|
|
134
|
-
let supportedAlignments: ReadonlyArray<Alignment>;
|
|
135
|
-
if (this.props.apiOptions.showAlignmentOptions) {
|
|
136
|
-
supportedAlignments = Widgets.getSupportedAlignments(
|
|
137
|
-
widgetInfo.type,
|
|
138
|
-
);
|
|
139
|
-
} else {
|
|
140
|
-
// NOTE(kevinb): "default" is not one in `validAlignments` in widgets.js.
|
|
141
|
-
supportedAlignments = ["default"];
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const supportsStaticMode = Widgets.supportsStaticMode(widgetInfo.type);
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
<div className="perseus-widget-editor">
|
|
148
|
-
<div
|
|
149
|
-
className={
|
|
150
|
-
"perseus-widget-editor-title " +
|
|
151
|
-
(this.state.showWidget ? "open" : "closed")
|
|
152
|
-
}
|
|
153
|
-
>
|
|
154
|
-
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
|
155
|
-
<a
|
|
156
|
-
className="perseus-widget-editor-title-id"
|
|
157
|
-
href="#"
|
|
158
|
-
onClick={this._toggleWidget}
|
|
159
|
-
>
|
|
160
|
-
{this.props.id}
|
|
161
|
-
{this.state.showWidget ? (
|
|
162
|
-
<InlineIcon {...iconChevronDown} />
|
|
163
|
-
) : (
|
|
164
|
-
<InlineIcon {...iconChevronRight} />
|
|
165
|
-
)}
|
|
166
|
-
</a>
|
|
167
|
-
|
|
168
|
-
{supportsStaticMode && (
|
|
169
|
-
<input
|
|
170
|
-
type="button"
|
|
171
|
-
// @ts-expect-error - TS2322 - Type '(e: Event) => void' is not assignable to type 'MouseEventHandler<HTMLInputElement>'.
|
|
172
|
-
onClick={this._toggleStatic}
|
|
173
|
-
className="simple-button--small"
|
|
174
|
-
value={
|
|
175
|
-
widgetInfo.static
|
|
176
|
-
? "Unset as static"
|
|
177
|
-
: "Set as static"
|
|
178
|
-
}
|
|
179
|
-
/>
|
|
180
|
-
)}
|
|
181
|
-
{supportedAlignments.length > 1 && (
|
|
182
|
-
<select
|
|
183
|
-
className="alignment"
|
|
184
|
-
value={widgetInfo.alignment}
|
|
185
|
-
onChange={this._handleAlignmentChange}
|
|
186
|
-
>
|
|
187
|
-
{supportedAlignments.map((alignment) => (
|
|
188
|
-
<option key={alignment}>{alignment}</option>
|
|
189
|
-
))}
|
|
190
|
-
</select>
|
|
191
|
-
)}
|
|
192
|
-
<SectionControlButton
|
|
193
|
-
icon={iconTrash}
|
|
194
|
-
onClick={() => {
|
|
195
|
-
this.props.onRemove();
|
|
196
|
-
}}
|
|
197
|
-
title="Remove image widget"
|
|
198
|
-
/>
|
|
199
|
-
</div>
|
|
200
|
-
<div
|
|
201
|
-
className={
|
|
202
|
-
"perseus-widget-editor-content " +
|
|
203
|
-
(this.state.showWidget ? "enter" : "leave")
|
|
204
|
-
}
|
|
205
|
-
>
|
|
206
|
-
{Ed && (
|
|
207
|
-
<Ed
|
|
208
|
-
// eslint-disable-next-line react/no-string-refs
|
|
209
|
-
ref="widget"
|
|
210
|
-
onChange={this._handleWidgetChange}
|
|
211
|
-
static={widgetInfo.static}
|
|
212
|
-
apiOptions={this.props.apiOptions}
|
|
213
|
-
{...widgetInfo.options}
|
|
214
|
-
/>
|
|
215
|
-
)}
|
|
216
|
-
</div>
|
|
217
|
-
</div>
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export default WidgetEditor;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import {Widgets} from "@khanacademy/perseus";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
import _ from "underscore";
|
|
4
|
-
|
|
5
|
-
type WidgetSelectProps = {
|
|
6
|
-
onChange?: (widgetType: string) => unknown;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
class WidgetSelect extends React.Component<WidgetSelectProps> {
|
|
10
|
-
shouldComponentUpdate() {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
handleChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {
|
|
15
|
-
const widgetType = e.currentTarget.value;
|
|
16
|
-
if (widgetType === "") {
|
|
17
|
-
// TODO(alpert): Not sure if change will trigger here
|
|
18
|
-
// but might as well be safe
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (this.props.onChange) {
|
|
22
|
-
this.props.onChange(widgetType);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
render(): React.ReactNode {
|
|
27
|
-
const widgets = Widgets.getPublicWidgets();
|
|
28
|
-
const orderedWidgetNames = _.sortBy(_.keys(widgets), (name) => {
|
|
29
|
-
return widgets[name].displayName;
|
|
30
|
-
});
|
|
31
|
-
const addWidgetString = "Add a widget\u2026";
|
|
32
|
-
return (
|
|
33
|
-
<select value="" onChange={this.handleChange}>
|
|
34
|
-
<option value="">{addWidgetString}</option>
|
|
35
|
-
<option disabled>--</option>
|
|
36
|
-
{_.map(orderedWidgetNames, (name) => {
|
|
37
|
-
return (
|
|
38
|
-
<option key={name} value={name}>
|
|
39
|
-
{widgets[name].displayName}
|
|
40
|
-
</option>
|
|
41
|
-
);
|
|
42
|
-
})}
|
|
43
|
-
</select>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export default WidgetSelect;
|