@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.
Files changed (175) hide show
  1. package/dist/es/index.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/package.json +9 -6
  4. package/.babelrc.js +0 -8
  5. package/.eslintrc.js +0 -12
  6. package/CHANGELOG.md +0 -1155
  7. package/src/README.md +0 -7
  8. package/src/__stories__/editor-page.stories.tsx +0 -65
  9. package/src/__stories__/editor.stories.tsx +0 -134
  10. package/src/__stories__/item-editor.stories.tsx +0 -41
  11. package/src/__stories__/tex-error-view.stories.tsx +0 -26
  12. package/src/__testdata__/input-number.testdata.ts +0 -27
  13. package/src/__tests__/editor.test.tsx +0 -142
  14. package/src/__tests__/hint-editor.test.tsx +0 -83
  15. package/src/__tests__/i18n.test.ts +0 -501
  16. package/src/__tests__/integration.test.ts +0 -5
  17. package/src/__tests__/item-extras-editor.test.tsx +0 -85
  18. package/src/__tests__/traversal.test.ts +0 -377
  19. package/src/all-editors.test.ts +0 -16
  20. package/src/all-editors.ts +0 -83
  21. package/src/all-widgets.ts +0 -7
  22. package/src/article-editor.tsx +0 -438
  23. package/src/components/__stories__/blur-input.stories.tsx +0 -22
  24. package/src/components/__stories__/section-control-button.stories.tsx +0 -26
  25. package/src/components/__tests__/blur-input.test.tsx +0 -67
  26. package/src/components/blur-input.tsx +0 -69
  27. package/src/components/checkbox.tsx +0 -194
  28. package/src/components/device-framer.tsx +0 -90
  29. package/src/components/drag-target.tsx +0 -87
  30. package/src/components/dropdown-option.tsx +0 -311
  31. package/src/components/form-wrapped-text-field.tsx +0 -245
  32. package/src/components/graph-points-count-selector.tsx +0 -31
  33. package/src/components/graph-settings.tsx +0 -627
  34. package/src/components/graph-type-selector.tsx +0 -31
  35. package/src/components/hover-behavior.ts +0 -177
  36. package/src/components/json-editor.tsx +0 -121
  37. package/src/components/link.ts +0 -173
  38. package/src/components/section-control-button.tsx +0 -53
  39. package/src/components/segment-count-selector.tsx +0 -27
  40. package/src/components/simple-button.tsx +0 -85
  41. package/src/components/sortable.tsx +0 -187
  42. package/src/components/util.ts +0 -38
  43. package/src/components/viewport-resizer.tsx +0 -72
  44. package/src/components/widget-editor.tsx +0 -222
  45. package/src/components/widget-select.tsx +0 -48
  46. package/src/diffs/__stories__/perseus-diff-wrapper.tsx +0 -11
  47. package/src/diffs/__stories__/structured-item-diff.stories.tsx +0 -855
  48. package/src/diffs/__stories__/tags-diff.stories.tsx +0 -36
  49. package/src/diffs/__stories__/text-diff.stories.tsx +0 -35
  50. package/src/diffs/__tests__/split-diff.test.ts +0 -33
  51. package/src/diffs/__tests__/string-array-diff.test.ts +0 -73
  52. package/src/diffs/__tests__/structured-item-diff.test.ts +0 -433
  53. package/src/diffs/__tests__/widget-diff-performer.test.ts +0 -43
  54. package/src/diffs/article-diff.tsx +0 -69
  55. package/src/diffs/item-diff.tsx +0 -76
  56. package/src/diffs/renderer-diff.tsx +0 -137
  57. package/src/diffs/split-diff.ts +0 -31
  58. package/src/diffs/string-array-diff.ts +0 -74
  59. package/src/diffs/structured-item-diff.tsx +0 -275
  60. package/src/diffs/tags-diff.tsx +0 -49
  61. package/src/diffs/text-diff.tsx +0 -193
  62. package/src/diffs/widget-diff-performer.ts +0 -69
  63. package/src/diffs/widget-diff.tsx +0 -290
  64. package/src/editor-page.tsx +0 -325
  65. package/src/editor.tsx +0 -1099
  66. package/src/hint-editor.tsx +0 -469
  67. package/src/i18n.ts +0 -140
  68. package/src/iframe-content-renderer.tsx +0 -188
  69. package/src/index.ts +0 -30
  70. package/src/item-editor.tsx +0 -158
  71. package/src/item-extras-editor.tsx +0 -230
  72. package/src/katex-mhchem/index.js +0 -73
  73. package/src/katex-mhchem/parser.js +0 -2659
  74. package/src/multirenderer-editor.tsx +0 -1061
  75. package/src/stateful-article-editor.tsx +0 -173
  76. package/src/stateful-editor-page.tsx +0 -88
  77. package/src/styles/perseus-editor.less +0 -1321
  78. package/src/testing-widgets.ts +0 -11
  79. package/src/tex-error-view.tsx +0 -90
  80. package/src/util/points.test.ts +0 -24
  81. package/src/util/points.ts +0 -13
  82. package/src/util/register-all-widgets-and-editors-for-testing.test.ts +0 -17
  83. package/src/util/register-all-widgets-and-editors-for-testing.ts +0 -17
  84. package/src/version.ts +0 -10
  85. package/src/widgets/__stories__/categorizer-editor.stories.tsx +0 -24
  86. package/src/widgets/__stories__/definition-editor.stories.tsx +0 -18
  87. package/src/widgets/__stories__/dropdown-editor.stories.tsx +0 -18
  88. package/src/widgets/__stories__/explanation-editor.stories.tsx +0 -18
  89. package/src/widgets/__stories__/expression-editor.stories.tsx +0 -122
  90. package/src/widgets/__stories__/image-editor.stories.tsx +0 -72
  91. package/src/widgets/__stories__/input-number-editor.stories.tsx +0 -18
  92. package/src/widgets/__stories__/interaction-editor.stories.tsx +0 -37
  93. package/src/widgets/__stories__/label-image-editor.stories.tsx +0 -99
  94. package/src/widgets/__stories__/matcher-editor.stories.tsx +0 -18
  95. package/src/widgets/__stories__/number-line-editor.stories.tsx +0 -18
  96. package/src/widgets/__stories__/numeric-input-editor.stories.tsx +0 -18
  97. package/src/widgets/__stories__/radio-editor.stories.tsx +0 -116
  98. package/src/widgets/__stories__/sorter-editor.stories.tsx +0 -18
  99. package/src/widgets/__tests__/categorizer-editor.test.tsx +0 -51
  100. package/src/widgets/__tests__/definition-editor.test.tsx +0 -41
  101. package/src/widgets/__tests__/dropdown-editor.test.tsx +0 -57
  102. package/src/widgets/__tests__/explanation-editor.test.tsx +0 -59
  103. package/src/widgets/__tests__/expression-editor.test.tsx +0 -403
  104. package/src/widgets/__tests__/input-number-editor.test.tsx +0 -128
  105. package/src/widgets/__tests__/matcher-editor.test.tsx +0 -47
  106. package/src/widgets/__tests__/number-line-editor.test.tsx +0 -144
  107. package/src/widgets/__tests__/numeric-input-editor.test.tsx +0 -177
  108. package/src/widgets/__tests__/radio-editor.test.tsx +0 -118
  109. package/src/widgets/__tests__/sorter-editor.test.tsx +0 -55
  110. package/src/widgets/categorizer-editor.tsx +0 -104
  111. package/src/widgets/cs-program-editor.tsx +0 -256
  112. package/src/widgets/definition-editor.tsx +0 -77
  113. package/src/widgets/deprecated-standin-editor.tsx +0 -29
  114. package/src/widgets/dropdown-editor.tsx +0 -211
  115. package/src/widgets/example-graphie-widget-editor.tsx +0 -70
  116. package/src/widgets/example-graphie-widget.tsx +0 -172
  117. package/src/widgets/example-widget-editor.tsx +0 -62
  118. package/src/widgets/example-widget.tsx +0 -154
  119. package/src/widgets/explanation-editor.tsx +0 -92
  120. package/src/widgets/expression-editor.tsx +0 -557
  121. package/src/widgets/graded-group-editor.tsx +0 -161
  122. package/src/widgets/graded-group-set-editor.tsx +0 -104
  123. package/src/widgets/grapher-editor.tsx +0 -155
  124. package/src/widgets/group-editor.tsx +0 -83
  125. package/src/widgets/iframe-editor.tsx +0 -202
  126. package/src/widgets/image-editor.tsx +0 -367
  127. package/src/widgets/input-number-editor.tsx +0 -288
  128. package/src/widgets/interaction-editor/constraint-editor.tsx +0 -134
  129. package/src/widgets/interaction-editor/function-editor.tsx +0 -108
  130. package/src/widgets/interaction-editor/index.ts +0 -3
  131. package/src/widgets/interaction-editor/interaction-editor.tsx +0 -749
  132. package/src/widgets/interaction-editor/label-editor.tsx +0 -87
  133. package/src/widgets/interaction-editor/line-editor.tsx +0 -125
  134. package/src/widgets/interaction-editor/mathquill-input.tsx +0 -81
  135. package/src/widgets/interaction-editor/movable-line-editor.tsx +0 -121
  136. package/src/widgets/interaction-editor/movable-point-editor.tsx +0 -89
  137. package/src/widgets/interaction-editor/parametric-editor.tsx +0 -117
  138. package/src/widgets/interaction-editor/point-editor.tsx +0 -72
  139. package/src/widgets/interaction-editor/rectangle-editor.tsx +0 -96
  140. package/src/widgets/interactive-graph-editor.tsx +0 -571
  141. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -82
  142. package/src/widgets/label-image/__stories__/behavior.stories.tsx +0 -31
  143. package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -75
  144. package/src/widgets/label-image/__stories__/question-markers.stories.tsx +0 -107
  145. package/src/widgets/label-image/__stories__/select-image.stories.tsx +0 -59
  146. package/src/widgets/label-image/answer-choices.tsx +0 -231
  147. package/src/widgets/label-image/behavior.tsx +0 -122
  148. package/src/widgets/label-image/marker.tsx +0 -290
  149. package/src/widgets/label-image/question-markers.tsx +0 -176
  150. package/src/widgets/label-image/select-image.tsx +0 -81
  151. package/src/widgets/label-image-editor.tsx +0 -252
  152. package/src/widgets/lights-puzzle-editor.tsx +0 -278
  153. package/src/widgets/matcher-editor.tsx +0 -149
  154. package/src/widgets/matrix-editor.tsx +0 -156
  155. package/src/widgets/measurer-editor.tsx +0 -253
  156. package/src/widgets/molecule-editor.tsx +0 -64
  157. package/src/widgets/number-line-editor.tsx +0 -512
  158. package/src/widgets/numeric-input-editor.tsx +0 -536
  159. package/src/widgets/orderer-editor.tsx +0 -172
  160. package/src/widgets/passage-editor.tsx +0 -117
  161. package/src/widgets/passage-ref-editor.tsx +0 -80
  162. package/src/widgets/passage-ref-target-editor.tsx +0 -49
  163. package/src/widgets/plotter-editor.tsx +0 -542
  164. package/src/widgets/radio/editor.tsx +0 -398
  165. package/src/widgets/reaction-diagram-editor.tsx +0 -131
  166. package/src/widgets/sequence-editor.tsx +0 -205
  167. package/src/widgets/simple-markdown-tester-editor.tsx +0 -86
  168. package/src/widgets/simple-markdown-tester.tsx +0 -92
  169. package/src/widgets/simulator-editor.tsx +0 -146
  170. package/src/widgets/sorter-editor.tsx +0 -103
  171. package/src/widgets/table-editor.tsx +0 -164
  172. package/src/widgets/unit-editor.tsx +0 -299
  173. package/src/widgets/video-editor.tsx +0 -73
  174. package/tsconfig-build.json +0 -22
  175. package/tsconfig-build.tsbuildinfo +0 -1
@@ -1,252 +0,0 @@
1
- /**
2
- * Direct image labeling widget editor.
3
- *
4
- * Label on image widget enables creating more natural, conceptual questions
5
- * that involve the use of images, and enable learners to demonstrate their
6
- * knowledge by directly interacting with the image.
7
- */
8
-
9
- import {EditorJsonify, Util} from "@khanacademy/perseus";
10
- import {StyleSheet, css} from "aphrodite";
11
- import * as React from "react";
12
-
13
- import FormWrappedTextField from "../components/form-wrapped-text-field";
14
-
15
- import AnswerChoices from "./label-image/answer-choices";
16
- import Behavior from "./label-image/behavior";
17
- import QuestionMarkers from "./label-image/question-markers";
18
- import SelectImage from "./label-image/select-image";
19
-
20
- import type {MarkerType} from "@khanacademy/perseus";
21
-
22
- type Props = {
23
- // List of answer choices to label question image with.
24
- choices: ReadonlyArray<string>;
25
- // The question image properties.
26
- imageAlt: string;
27
- imageUrl: string;
28
- imageWidth: number;
29
- imageHeight: number;
30
- // The list of label markers on the question image.
31
- markers: ReadonlyArray<MarkerType>;
32
- // Whether multiple answer choices may be selected for markers.
33
- multipleAnswers: boolean;
34
- // Whether to hide answer choices from user instructions.
35
- hideChoicesFromInstructions: boolean;
36
- // Callback for when a widget prop is changed.
37
- onChange: (options: any) => void;
38
- };
39
-
40
- class LabelImageEditor extends React.Component<Props> {
41
- _questionMarkers: QuestionMarkers | null | undefined;
42
-
43
- static defaultProps: {
44
- choices: ReadonlyArray<any>;
45
- hideChoicesFromInstructions: boolean;
46
- imageAlt: string;
47
- imageHeight: number;
48
- imageUrl: string;
49
- imageWidth: number;
50
- markers: ReadonlyArray<any>;
51
- multipleAnswers: boolean;
52
- } = {
53
- choices: [],
54
- imageAlt: "",
55
- imageUrl: "",
56
- imageWidth: 0,
57
- imageHeight: 0,
58
- markers: [],
59
- multipleAnswers: false,
60
- hideChoicesFromInstructions: false,
61
- };
62
-
63
- static widgetName = "label-image" as const;
64
-
65
- componentDidUpdate(prevProps: Props) {
66
- const coordsToMarkers: Record<string, any> = {};
67
-
68
- prevProps.markers.forEach(
69
- (marker) => (coordsToMarkers[`${marker.x}.${marker.y}`] = marker),
70
- );
71
-
72
- // Find the newly added marker indices.
73
- const newIndices = this.props.markers
74
- .map((marker, index) =>
75
- // eslint-disable-next-line no-prototype-builtins
76
- coordsToMarkers.hasOwnProperty(`${marker.x}.${marker.y}`)
77
- ? -1
78
- : index,
79
- )
80
- .filter((index) => index !== -1);
81
-
82
- // Automatically reveal their dropdowns as a prompt to the content
83
- // creator to select answers and set the ARIA label.
84
- if (newIndices.length && this._questionMarkers) {
85
- this._questionMarkers.openDropdownForMarkerIndices(newIndices);
86
- }
87
- }
88
-
89
- getSaveWarnings: () => ReadonlyArray<any | string> = () => {
90
- const {choices, imageAlt, imageUrl, markers} = this.props;
91
-
92
- const warnings: Array<string> = [];
93
-
94
- if (choices.length < 2) {
95
- warnings.push("Question requires at least two answer choices");
96
- }
97
-
98
- if (!imageUrl) {
99
- warnings.push("Image is not specified for question");
100
- } else if (!imageAlt) {
101
- warnings.push("Question image has no alt text");
102
- }
103
-
104
- if (!markers.length) {
105
- warnings.push("Question has no markers, to label answers on image");
106
- } else {
107
- let numNoAnswers = 0;
108
- let numNoLabels = 0;
109
-
110
- for (const marker of markers) {
111
- if (!marker.answers.length) {
112
- numNoAnswers++;
113
- }
114
-
115
- if (!marker.label) {
116
- numNoLabels++;
117
- }
118
- }
119
-
120
- if (numNoAnswers) {
121
- warnings.push(
122
- `Question has ${numNoAnswers} markers with no ` +
123
- "answers selected",
124
- );
125
- }
126
-
127
- if (numNoLabels) {
128
- warnings.push(
129
- `Question has ${numNoLabels} markers with no ` +
130
- "ARIA label",
131
- );
132
- }
133
- }
134
-
135
- return warnings;
136
- };
137
-
138
- serialize(): any {
139
- return EditorJsonify.serialize.call(this);
140
- }
141
-
142
- handleImageChange: (url: string) => void = (url: string) => {
143
- this.props.onChange({
144
- imageUrl: url,
145
- // Initially reset image size when URL is changed so it can be later
146
- // measured.
147
- imageWidth: 0,
148
- imageHeight: 0,
149
- });
150
-
151
- if (url) {
152
- Util.getImageSize(url, (width, height) => {
153
- this.props.onChange({
154
- imageWidth: width,
155
- imageHeight: height,
156
- });
157
- });
158
- }
159
- };
160
-
161
- handleAltChange: (alt: string) => void = (alt: string) => {
162
- this.props.onChange({imageAlt: alt});
163
- };
164
-
165
- handleChoicesChange: (choices: ReadonlyArray<string>) => void = (
166
- choices: ReadonlyArray<string>,
167
- ) => {
168
- this.props.onChange({choices});
169
- };
170
-
171
- handleMarkersChange: (markers: ReadonlyArray<MarkerType>) => void = (
172
- markers: ReadonlyArray<MarkerType>,
173
- ) => {
174
- this.props.onChange({markers});
175
- };
176
-
177
- handleBehaviorChange: (options?: any) => void = (options: any) => {
178
- this.props.onChange(options);
179
- };
180
-
181
- render(): React.ReactNode {
182
- const {
183
- choices,
184
- imageAlt,
185
- imageUrl,
186
- imageWidth,
187
- imageHeight,
188
- markers,
189
- multipleAnswers,
190
- hideChoicesFromInstructions,
191
- } = this.props;
192
-
193
- const imageSelected = imageUrl && imageWidth > 0 && imageHeight > 0;
194
-
195
- return (
196
- <div>
197
- <SelectImage onChange={this.handleImageChange} url={imageUrl} />
198
-
199
- <div className={css(styles.smallSpacer)} />
200
-
201
- {imageSelected && (
202
- <FormWrappedTextField
203
- placeholder="Alt text (for screen readers)"
204
- onChange={(e) => this.handleAltChange(e.target.value)}
205
- value={imageAlt}
206
- width="100%"
207
- />
208
- )}
209
-
210
- <div className={css(styles.largeSpacer)} />
211
-
212
- <QuestionMarkers
213
- choices={choices}
214
- imageUrl={imageSelected ? imageUrl : ""}
215
- imageWidth={imageWidth}
216
- imageHeight={imageHeight}
217
- markers={markers}
218
- onChange={this.handleMarkersChange}
219
- ref={(node) => (this._questionMarkers = node)}
220
- />
221
-
222
- <div className={css(styles.largeSpacer)} />
223
-
224
- <AnswerChoices
225
- choices={choices}
226
- onChange={this.handleChoicesChange}
227
- />
228
-
229
- <div className={css(styles.largeSpacer)} />
230
-
231
- <Behavior
232
- preferredPopoverDirection="NONE"
233
- multipleAnswers={multipleAnswers}
234
- hideChoicesFromInstructions={hideChoicesFromInstructions}
235
- onChange={this.handleBehaviorChange}
236
- />
237
- </div>
238
- );
239
- }
240
- }
241
-
242
- const styles = StyleSheet.create({
243
- largeSpacer: {
244
- height: 32,
245
- },
246
-
247
- smallSpacer: {
248
- height: 16,
249
- },
250
- });
251
-
252
- export default LabelImageEditor;
@@ -1,278 +0,0 @@
1
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
2
- /* eslint-disable react/sort-comp */
3
- import {components, Changeable, EditorJsonify} from "@khanacademy/perseus";
4
- import PropTypes from "prop-types";
5
- import * as React from "react";
6
- import _ from "underscore";
7
-
8
- const {InfoTip, NumberInput, PropCheckBox} = components;
9
-
10
- const MAX_SIZE = 8;
11
-
12
- // styling
13
- const CELL_PADDING = 5;
14
-
15
- const TABLE_STYLE = {
16
- display: "table",
17
- tableLayout: "fixed",
18
- } as const;
19
-
20
- const ROW_STYLE = {
21
- display: "table-row",
22
- } as const;
23
-
24
- const CELL_STYLE = {
25
- display: "table-cell",
26
- padding: CELL_PADDING,
27
- } as const;
28
-
29
- const BASE_TILE_STYLE = {
30
- borderRadius: 10,
31
- cursor: "pointer",
32
- } as const;
33
-
34
- const PATTERNS = {
35
- plus: () => [
36
- [false, true, false],
37
- [true, true, true],
38
- [false, true, false],
39
- ],
40
- x: () => [
41
- [true, false, true],
42
- [false, true, false],
43
- [true, false, true],
44
- ],
45
- "plus/x": (iter) => {
46
- return iter % 2 ? PATTERNS.x() : PATTERNS.plus();
47
- },
48
- } as const;
49
-
50
- /**
51
- * Clamps value to an integer in the range [min, max]
52
- */
53
- const clampToInt = function (value: number, min: number, max: number) {
54
- value = Math.floor(value);
55
- value = Math.max(value, min);
56
- value = Math.min(value, max);
57
- return value;
58
- };
59
-
60
- // Returns a copy of the tiles, with tiles flipped according to
61
- // whether or not their y, x position satisfies the predicate
62
- const flipTilesPredicate = (
63
- oldCells: any,
64
- predicate: (y?: any, x?: any) => boolean,
65
- ) => {
66
- return _.map(oldCells, (row, y) => {
67
- return _.map(row, (cell, x) => {
68
- return predicate(y, x) ? !cell : cell;
69
- });
70
- });
71
- };
72
-
73
- type TileProps = any;
74
-
75
- // A single glowy cell
76
- class Tile extends React.Component<TileProps> {
77
- static propTypes = {
78
- value: PropTypes.bool.isRequired,
79
- size: PropTypes.number.isRequired,
80
- };
81
-
82
- render(): React.ReactNode {
83
- const color = this.props.value ? "#55dd55" : "#115511";
84
- const style = _.extend({}, BASE_TILE_STYLE, {
85
- width: this.props.size,
86
- height: this.props.size,
87
- backgroundColor: color,
88
- });
89
- return <div style={style} onClick={this._flip} />;
90
- }
91
-
92
- _flip = () => {
93
- this.props.onChange(!this.props.value);
94
- };
95
- }
96
-
97
- type TileGridProps = any;
98
-
99
- // A grid of glowy cells
100
- class TileGrid extends React.Component<TileGridProps> {
101
- static propTypes = {
102
- cells: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.bool)).isRequired,
103
- size: PropTypes.number.isRequired,
104
- };
105
-
106
- render(): React.ReactNode {
107
- return (
108
- <div style={TABLE_STYLE} className="no-select">
109
- {_.map(this.props.cells, (row, y) => {
110
- return (
111
- <div key={y} style={ROW_STYLE}>
112
- {_.map(row, (cell, x) => {
113
- return (
114
- <div key={x} style={CELL_STYLE}>
115
- <Tile
116
- value={cell}
117
- size={this.props.size}
118
- onChange={_.partial(
119
- this.props.onChange,
120
- y,
121
- x,
122
- )}
123
- />
124
- </div>
125
- );
126
- })}
127
- </div>
128
- );
129
- })}
130
- </div>
131
- );
132
- }
133
- }
134
-
135
- type LightsPuzzleEditorProps = any;
136
-
137
- // The widget editor
138
- class LightsPuzzleEditor extends React.Component<LightsPuzzleEditorProps> {
139
- static propTypes = {
140
- ...Changeable.propTypes,
141
- startCells: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.bool)),
142
- flipPattern: PropTypes.string.isRequired,
143
- gradeIncompleteAsWrong: PropTypes.bool.isRequired,
144
- };
145
-
146
- static widgetName = "lights-puzzle" as const;
147
-
148
- static defaultProps: LightsPuzzleEditorProps = {
149
- startCells: [
150
- [false, false, false],
151
- [false, false, false],
152
- [false, false, false],
153
- ],
154
- flipPattern: "plus",
155
- gradeIncompleteAsWrong: false,
156
- };
157
-
158
- _height: () => number = () => {
159
- return this.props.startCells.length;
160
- };
161
-
162
- _width: () => number = () => {
163
- if (this.props.startCells.length !== 0) {
164
- return this.props.startCells[0].length;
165
- }
166
- return 0; // default to 0
167
- };
168
-
169
- change: (arg1: any, arg2: any, arg3: any) => any = (...args) => {
170
- return Changeable.change.apply(this, args);
171
- };
172
-
173
- render(): React.ReactNode {
174
- return (
175
- <div>
176
- <div>
177
- Width:
178
- <NumberInput
179
- value={this._width()}
180
- placeholder={5}
181
- onChange={this._changeWidth}
182
- />
183
- {", "}
184
- Height:
185
- <NumberInput
186
- value={this._height()}
187
- placeholder={5}
188
- onChange={this._changeHeight}
189
- />
190
- </div>
191
- <div>
192
- Flip pattern:
193
- <select
194
- value={this.props.flipPattern}
195
- // @ts-expect-error - TS2322 - Type '(arg1: ChangeEvent<HTMLInputElement>) => void' is not assignable to type 'ChangeEventHandler<HTMLSelectElement>'.
196
- onChange={this._handlePatternChange}
197
- >
198
- {_.map(_.keys(PATTERNS), (pattern, i) => {
199
- return (
200
- <option value={pattern} key={i}>
201
- {pattern}
202
- </option>
203
- );
204
- })}
205
- </select>
206
- </div>
207
- <div>
208
- Grade incomplete puzzles as wrong:{" "}
209
- <PropCheckBox
210
- gradeIncompleteAsWrong={
211
- this.props.gradeIncompleteAsWrong
212
- }
213
- onChange={this.props.onChange}
214
- />
215
- <InfoTip>
216
- By default, incomplete puzzles are graded as empty.
217
- </InfoTip>
218
- </div>
219
- <div>Starting configuration:</div>
220
- <div style={{overflowX: "auto"}}>
221
- <TileGrid
222
- cells={this.props.startCells}
223
- size={50}
224
- onChange={this._switchTile}
225
- />
226
- </div>
227
- </div>
228
- );
229
- }
230
-
231
- _handlePatternChange: (arg1: React.ChangeEvent<HTMLInputElement>) => void =
232
- (e) => {
233
- // @ts-expect-error - TS2554 - Expected 3 arguments, but got 2.
234
- this.change("flipPattern", e.target.value);
235
- };
236
-
237
- _changeWidth: (arg1: number) => void = (newWidth) => {
238
- newWidth = clampToInt(newWidth, 1, MAX_SIZE);
239
- this._truncateCells(newWidth, this._height());
240
- };
241
-
242
- _changeHeight: (arg1: number) => void = (newHeight) => {
243
- newHeight = clampToInt(newHeight, 1, MAX_SIZE);
244
- this._truncateCells(this._width(), newHeight);
245
- };
246
-
247
- _truncateCells: (arg1: number, arg2: number) => void = (
248
- newWidth,
249
- newHeight,
250
- ) => {
251
- const newCells = _.times(newHeight, (y) => {
252
- return _.times(newWidth, (x) => {
253
- // explicitly cast the result to a boolean with !!
254
- return !!(
255
- this.props.startCells[y] && this.props.startCells[y][x]
256
- );
257
- });
258
- });
259
-
260
- // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
261
- this.change({startCells: newCells});
262
- };
263
-
264
- _switchTile: (arg1: number, arg2: number) => void = (tileY, tileX) => {
265
- const newCells = flipTilesPredicate(this.props.startCells, (y, x) => {
266
- return y === tileY && x === tileX;
267
- });
268
-
269
- // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
270
- this.change({startCells: newCells});
271
- };
272
-
273
- serialize: () => any = () => {
274
- return EditorJsonify.serialize.call(this);
275
- };
276
- }
277
-
278
- export default LightsPuzzleEditor;
@@ -1,149 +0,0 @@
1
- /* eslint-disable react/forbid-prop-types, react/sort-comp */
2
- import {components} from "@khanacademy/perseus";
3
- import PropTypes from "prop-types";
4
- import * as React from "react";
5
- import _ from "underscore";
6
-
7
- const {InfoTip, PropCheckBox, TextListEditor} = components;
8
-
9
- type Props = any;
10
-
11
- class MatcherEditor extends React.Component<Props> {
12
- static propTypes = {
13
- left: PropTypes.array,
14
- right: PropTypes.array,
15
- labels: PropTypes.array,
16
- orderMatters: PropTypes.bool,
17
- padding: PropTypes.bool,
18
- };
19
-
20
- static widgetName = "matcher" as const;
21
-
22
- static defaultProps: Props = {
23
- left: ["$x$", "$y$", "$z$"],
24
- right: ["$1$", "$2$", "$3$"],
25
- labels: ["test", "label"],
26
- orderMatters: false,
27
- padding: true,
28
- };
29
-
30
- render(): React.ReactNode {
31
- return (
32
- <div className="perseus-matcher-editor">
33
- <div>
34
- {" "}
35
- Correct answer:{" "}
36
- <InfoTip>
37
- <p>
38
- Enter the correct answers here. The preview on the
39
- right will show the cards in a randomized order,
40
- which is how the student will see them.
41
- </p>
42
- </InfoTip>
43
- </div>
44
- <div className="perseus-clearfix">
45
- <TextListEditor
46
- options={this.props.left}
47
- onChange={(options, cb) => {
48
- this.props.onChange({left: options}, cb);
49
- }}
50
- layout="vertical"
51
- />
52
- <TextListEditor
53
- options={this.props.right}
54
- onChange={(options, cb) => {
55
- this.props.onChange({right: options}, cb);
56
- }}
57
- layout="vertical"
58
- />
59
- </div>
60
- <span>
61
- {" "}
62
- Labels:{" "}
63
- <InfoTip>
64
- <p>These are entirely optional.</p>
65
- </InfoTip>
66
- </span>
67
- <div>
68
- <input
69
- type="text"
70
- defaultValue={this.props.labels[0]}
71
- // eslint-disable-next-line react/jsx-no-bind
72
- onChange={this.onLabelChange.bind(this, 0)}
73
- />
74
- <input
75
- type="text"
76
- defaultValue={this.props.labels[1]}
77
- // eslint-disable-next-line react/jsx-no-bind
78
- onChange={this.onLabelChange.bind(this, 1)}
79
- />
80
- </div>
81
- <div>
82
- <PropCheckBox
83
- label="Order of the matched pairs matters:"
84
- orderMatters={this.props.orderMatters}
85
- onChange={this.props.onChange}
86
- />
87
- <InfoTip>
88
- <p>
89
- With this option enabled, only the order provided
90
- above will be treated as correct. This is useful
91
- when ordering is significant, such as in the context
92
- of a proof.
93
- </p>
94
- <p>
95
- If disabled, pairwise matching is sufficient. To
96
- make this clear, the left column becomes fixed in
97
- the provided order and only the cards in the right
98
- column can be moved.
99
- </p>
100
- </InfoTip>
101
- </div>
102
- <div>
103
- <PropCheckBox
104
- label="Padding:"
105
- padding={this.props.padding}
106
- onChange={this.props.onChange}
107
- />
108
- <InfoTip>
109
- <p>
110
- Padding is good for text, but not needed for images.
111
- </p>
112
- </InfoTip>
113
- </div>
114
- </div>
115
- );
116
- }
117
-
118
- onLabelChange: (
119
- arg1: number,
120
- arg2: React.ChangeEvent<HTMLInputElement>,
121
- ) => void = (index, e) => {
122
- const labels = _.clone(this.props.labels);
123
- labels[index] = e.target.value;
124
- this.props.onChange({labels: labels});
125
- };
126
-
127
- getSaveWarnings: () => ReadonlyArray<string> = () => {
128
- if (this.props.left.length !== this.props.right.length) {
129
- return [
130
- "The two halves of the matcher have different numbers" +
131
- " of cards.",
132
- ];
133
- }
134
- return [];
135
- };
136
-
137
- serialize: any = () => {
138
- return _.pick(
139
- this.props,
140
- "left",
141
- "right",
142
- "labels",
143
- "orderMatters",
144
- "padding",
145
- );
146
- };
147
- }
148
-
149
- export default MatcherEditor;