@khanacademy/perseus 2.1.0 → 4.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.
Files changed (1212) hide show
  1. package/.babelrc.js +1 -1
  2. package/.eslintrc.js +1 -0
  3. package/CHANGELOG.md +22 -0
  4. package/dist/__tests__/mock-asset-loading-widget.d.ts +59 -0
  5. package/dist/__tests__/mock-asset-loading-widget.js.flow +70 -0
  6. package/dist/__tests__/mock-widget.d.ts +30 -0
  7. package/dist/__tests__/mock-widget.js.flow +39 -0
  8. package/dist/__tests__/test-items/image-item.d.ts +14 -0
  9. package/dist/__tests__/test-items/image-item.js.flow +20 -0
  10. package/dist/__tests__/test-items/input-number-1-item.d.ts +9 -0
  11. package/dist/__tests__/test-items/input-number-1-item.js.flow +15 -0
  12. package/dist/__tests__/test-items/input-number-2-item.d.ts +9 -0
  13. package/dist/__tests__/test-items/input-number-2-item.js.flow +15 -0
  14. package/dist/__tests__/test-items/table-item.d.ts +9 -0
  15. package/dist/__tests__/test-items/table-item.js.flow +15 -0
  16. package/dist/a11y.d.ts +5 -0
  17. package/dist/a11y.js.flow +11 -0
  18. package/dist/all-widgets.d.ts +22 -0
  19. package/dist/all-widgets.js.flow +33 -0
  20. package/dist/article-renderer.d.ts +54 -0
  21. package/dist/article-renderer.js.flow +69 -0
  22. package/dist/asset-context.d.ts +14 -0
  23. package/dist/asset-context.js.flow +15 -0
  24. package/dist/basic-widgets.d.ts +22 -0
  25. package/dist/basic-widgets.js.flow +33 -0
  26. package/dist/components/button-group.d.ts +27 -0
  27. package/dist/components/button-group.js.flow +33 -0
  28. package/dist/components/constants.d.ts +7 -0
  29. package/dist/components/constants.js.flow +13 -0
  30. package/dist/components/fixed-to-responsive.d.ts +2 -0
  31. package/dist/components/fixed-to-responsive.js.flow +8 -0
  32. package/dist/components/graph.d.ts +44 -0
  33. package/dist/components/graph.js.flow +52 -0
  34. package/dist/components/graphie-classes.d.ts +13 -0
  35. package/dist/components/graphie-classes.js.flow +19 -0
  36. package/dist/components/graphie-movables.d.ts +16 -0
  37. package/dist/components/graphie-movables.js.flow +22 -0
  38. package/dist/components/graphie.d.ts +44 -0
  39. package/dist/components/graphie.js.flow +56 -0
  40. package/dist/components/highlighting/highlightable-content.d.ts +39 -0
  41. package/dist/components/highlighting/highlightable-content.js.flow +59 -0
  42. package/dist/components/highlighting/highlights.d.ts +30 -0
  43. package/dist/components/highlighting/highlights.js.flow +54 -0
  44. package/dist/components/highlighting/ranges.d.ts +42 -0
  45. package/dist/components/highlighting/ranges.js.flow +60 -0
  46. package/dist/components/highlighting/types.d.ts +64 -0
  47. package/dist/components/highlighting/types.js.flow +70 -0
  48. package/dist/components/highlighting/ui/highlight-renderer.d.ts +34 -0
  49. package/dist/components/highlighting/ui/highlight-renderer.js.flow +45 -0
  50. package/dist/components/highlighting/ui/highlight-set-renderer.d.ts +34 -0
  51. package/dist/components/highlighting/ui/highlight-set-renderer.js.flow +44 -0
  52. package/dist/components/highlighting/ui/highlight-tooltip.d.ts +16 -0
  53. package/dist/components/highlighting/ui/highlight-tooltip.js.flow +24 -0
  54. package/dist/components/highlighting/ui/highlighting-ui.d.ts +19 -0
  55. package/dist/components/highlighting/ui/highlighting-ui.js.flow +30 -0
  56. package/dist/components/highlighting/ui/selection-tracker.d.ts +47 -0
  57. package/dist/components/highlighting/ui/selection-tracker.js.flow +56 -0
  58. package/dist/components/highlighting/ui/types.d.ts +13 -0
  59. package/dist/components/highlighting/ui/types.js.flow +22 -0
  60. package/dist/components/highlighting/ui/util.d.ts +28 -0
  61. package/dist/components/highlighting/ui/util.js.flow +42 -0
  62. package/dist/components/highlighting/word-indexer.d.ts +28 -0
  63. package/dist/components/highlighting/word-indexer.js.flow +21 -0
  64. package/dist/components/hud.d.ts +15 -0
  65. package/dist/components/hud.js.flow +21 -0
  66. package/dist/components/icon.d.ts +56 -0
  67. package/dist/components/icon.js.flow +31 -0
  68. package/dist/components/image-loader.d.ts +52 -0
  69. package/dist/components/image-loader.js.flow +49 -0
  70. package/dist/components/info-tip/info-tip.d.ts +14 -0
  71. package/dist/components/info-tip/info-tip.js.flow +20 -0
  72. package/dist/components/info-tip.d.ts +18 -0
  73. package/dist/components/info-tip.js.flow +18 -0
  74. package/dist/components/inline-icon.d.ts +10 -0
  75. package/dist/components/inline-icon.js.flow +16 -0
  76. package/dist/components/input-with-examples.d.ts +52 -0
  77. package/dist/components/input-with-examples.js.flow +58 -0
  78. package/dist/components/lint.d.ts +23 -0
  79. package/dist/components/lint.js.flow +29 -0
  80. package/dist/components/math-input.d.ts +40 -0
  81. package/dist/components/math-input.js.flow +46 -0
  82. package/dist/components/math-output.d.ts +23 -0
  83. package/dist/components/math-output.js.flow +29 -0
  84. package/dist/components/multi-button-group.d.ts +29 -0
  85. package/dist/components/multi-button-group.js.flow +35 -0
  86. package/dist/components/number-input.d.ts +34 -0
  87. package/dist/components/number-input.js.flow +42 -0
  88. package/dist/components/prop-check-box.d.ts +2 -0
  89. package/dist/components/prop-check-box.js.flow +8 -0
  90. package/dist/components/range-input.d.ts +14 -0
  91. package/dist/components/range-input.js.flow +20 -0
  92. package/dist/components/simple-keypad-input.d.ts +19 -0
  93. package/dist/components/simple-keypad-input.js.flow +16 -0
  94. package/dist/components/sortable.d.ts +68 -0
  95. package/dist/components/sortable.js.flow +78 -0
  96. package/dist/components/stub-tag-editor.d.ts +25 -0
  97. package/dist/components/stub-tag-editor.js.flow +18 -0
  98. package/dist/components/svg-image.d.ts +74 -0
  99. package/dist/components/svg-image.js.flow +83 -0
  100. package/dist/components/tex-buttons.d.ts +20 -0
  101. package/dist/components/tex-buttons.js.flow +26 -0
  102. package/dist/components/tex.d.ts +25 -0
  103. package/dist/components/tex.js.flow +25 -0
  104. package/dist/components/text-input.d.ts +33 -0
  105. package/dist/components/text-input.js.flow +39 -0
  106. package/dist/components/text-list-editor.d.ts +16 -0
  107. package/dist/components/text-list-editor.js.flow +22 -0
  108. package/dist/components/tooltip.d.ts +32 -0
  109. package/dist/components/tooltip.js.flow +38 -0
  110. package/dist/components/visibility-observer/create-visibility-observer.d.ts +34 -0
  111. package/dist/components/visibility-observer/create-visibility-observer.js.flow +23 -0
  112. package/dist/components/visibility-observer/get-scroll-parent.d.ts +1 -0
  113. package/dist/components/visibility-observer/get-scroll-parent.js.flow +7 -0
  114. package/dist/components/visibility-observer/visibility-observer.d.ts +31 -0
  115. package/dist/components/visibility-observer/visibility-observer.js.flow +36 -0
  116. package/dist/components/zoomable-tex.d.ts +18 -0
  117. package/dist/components/zoomable-tex.js.flow +18 -0
  118. package/dist/components/zoomable.d.ts +64 -0
  119. package/dist/components/zoomable.js.flow +70 -0
  120. package/dist/components.d.ts +16 -0
  121. package/dist/components.js.flow +22 -0
  122. package/dist/definition-context.d.ts +21 -0
  123. package/dist/definition-context.js.flow +25 -0
  124. package/dist/dependencies.d.ts +3 -0
  125. package/dist/dependencies.js.flow +9 -0
  126. package/dist/error-boundary.d.ts +13 -0
  127. package/dist/error-boundary.js.flow +21 -0
  128. package/dist/es/index.js +23239 -26937
  129. package/dist/es/index.js.map +1 -1
  130. package/dist/extra-widgets.d.ts +22 -0
  131. package/dist/extra-widgets.js.flow +33 -0
  132. package/dist/hint-renderer.d.ts +23 -0
  133. package/dist/hint-renderer.js.flow +32 -0
  134. package/dist/hints-renderer.d.ts +29 -0
  135. package/dist/hints-renderer.js.flow +41 -0
  136. package/dist/icon-paths.d.ts +117 -0
  137. package/dist/icon-paths.js.flow +123 -0
  138. package/dist/index.d.ts +103 -2
  139. package/dist/index.js +13063 -16571
  140. package/dist/index.js.flow +133 -2
  141. package/dist/index.js.map +1 -1
  142. package/dist/init.d.ts +14 -0
  143. package/dist/init.js.flow +20 -0
  144. package/dist/interaction-tracker.d.ts +23 -0
  145. package/dist/interaction-tracker.js.flow +33 -0
  146. package/dist/interactive2/__tests__/movable-mock.d.ts +5 -0
  147. package/dist/interactive2/__tests__/movable-mock.js.flow +11 -0
  148. package/dist/interactive2/interactive-util.d.ts +31 -0
  149. package/dist/interactive2/interactive-util.js.flow +50 -0
  150. package/dist/interactive2/movable-helper-methods.d.ts +9 -0
  151. package/dist/interactive2/movable-helper-methods.js.flow +15 -0
  152. package/dist/interactive2/movable-line-options.d.ts +34 -0
  153. package/dist/interactive2/movable-line-options.js.flow +44 -0
  154. package/dist/interactive2/movable-line.d.ts +2 -0
  155. package/dist/interactive2/movable-line.js.flow +8 -0
  156. package/dist/interactive2/movable-point-options.d.ts +34 -0
  157. package/dist/interactive2/movable-point-options.js.flow +44 -0
  158. package/dist/interactive2/movable-point.d.ts +2 -0
  159. package/dist/interactive2/movable-point.js.flow +8 -0
  160. package/dist/interactive2/movable-polygon-options.d.ts +40 -0
  161. package/dist/interactive2/movable-polygon-options.js.flow +47 -0
  162. package/dist/interactive2/movable-polygon.d.ts +2 -0
  163. package/dist/interactive2/movable-polygon.js.flow +8 -0
  164. package/dist/interactive2/movable.d.ts +2 -0
  165. package/dist/interactive2/movable.js.flow +8 -0
  166. package/dist/interactive2/objective_.d.ts +13 -0
  167. package/dist/interactive2/objective_.js.flow +25 -0
  168. package/dist/interactive2/types.d.ts +2 -0
  169. package/dist/interactive2/types.js.flow +8 -0
  170. package/dist/interactive2/wrapped-defaults.d.ts +5 -0
  171. package/dist/interactive2/wrapped-defaults.js.flow +11 -0
  172. package/dist/interactive2/wrapped-ellipse.d.ts +3 -0
  173. package/dist/interactive2/wrapped-ellipse.js.flow +14 -0
  174. package/dist/interactive2/wrapped-line.d.ts +3 -0
  175. package/dist/interactive2/wrapped-line.js.flow +14 -0
  176. package/dist/interactive2/wrapped-path.d.ts +3 -0
  177. package/dist/interactive2/wrapped-path.js.flow +13 -0
  178. package/dist/interactive2.d.ts +10 -0
  179. package/dist/interactive2.js.flow +16 -0
  180. package/dist/item-renderer.d.ts +117 -0
  181. package/dist/item-renderer.js.flow +136 -0
  182. package/dist/item-version.d.ts +4 -0
  183. package/dist/item-version.js.flow +11 -0
  184. package/dist/jipt-paragraphs.d.ts +8 -0
  185. package/dist/jipt-paragraphs.js.flow +14 -0
  186. package/dist/keypad-context.d.ts +20 -0
  187. package/dist/keypad-context.js.flow +18 -0
  188. package/dist/loading-context.d.ts +14 -0
  189. package/dist/loading-context.js.flow +12 -0
  190. package/dist/logging/log.d.ts +55 -0
  191. package/dist/logging/log.js.flow +62 -0
  192. package/dist/mixins/changeable.d.ts +30 -0
  193. package/dist/mixins/changeable.js.flow +33 -0
  194. package/dist/mixins/editor-jsonify.d.ts +4 -0
  195. package/dist/mixins/editor-jsonify.js.flow +10 -0
  196. package/dist/mixins/provide-keypad.d.ts +36 -0
  197. package/dist/mixins/provide-keypad.js.flow +33 -0
  198. package/dist/mixins/widget-jsonify-deprecated.d.ts +5 -0
  199. package/dist/mixins/widget-jsonify-deprecated.js.flow +11 -0
  200. package/dist/mixins/widget-prop-denylist.d.ts +14 -0
  201. package/dist/mixins/widget-prop-denylist.js.flow +20 -0
  202. package/dist/multi-items/item-types.d.ts +32 -0
  203. package/dist/multi-items/items.d.ts +53 -0
  204. package/dist/multi-items/items.js.flow +66 -0
  205. package/dist/multi-items/multi-renderer.d.ts +152 -0
  206. package/dist/multi-items/multi-renderer.js.flow +213 -0
  207. package/dist/multi-items/prop-type-builders.d.ts +20 -0
  208. package/dist/multi-items/prop-type-builders.js.flow +22 -0
  209. package/dist/multi-items/shape-types.d.ts +47 -0
  210. package/dist/multi-items/shape-types.js.flow +60 -0
  211. package/dist/multi-items/shapes.d.ts +23 -0
  212. package/dist/multi-items/shapes.js.flow +25 -0
  213. package/dist/multi-items/tree-types.d.ts +34 -0
  214. package/dist/multi-items/tree-types.js.flow +45 -0
  215. package/dist/multi-items/trees.d.ts +146 -0
  216. package/dist/multi-items/trees.js.flow +147 -0
  217. package/dist/multi-items.d.ts +73 -0
  218. package/dist/multi-items.js.flow +36 -0
  219. package/dist/perseus-api.d.ts +128 -0
  220. package/dist/perseus-api.js.flow +139 -0
  221. package/dist/perseus-env.d.ts +3 -0
  222. package/dist/perseus-env.js.flow +6 -0
  223. package/dist/perseus-error.d.ts +11 -0
  224. package/dist/perseus-error.js.flow +16 -0
  225. package/dist/perseus-markdown.d.ts +11 -0
  226. package/dist/perseus-markdown.js.flow +20 -0
  227. package/dist/perseus-types.d.ts +776 -0
  228. package/dist/perseus-types.js.flow +1032 -0
  229. package/dist/question-paragraph.d.ts +11 -0
  230. package/dist/question-paragraph.js.flow +17 -0
  231. package/dist/renderability.d.ts +10 -0
  232. package/dist/renderability.js.flow +19 -0
  233. package/dist/renderer.d.ts +254 -0
  234. package/dist/renderer.js.flow +327 -0
  235. package/dist/server-item-renderer.d.ts +113 -0
  236. package/dist/server-item-renderer.js.flow +136 -0
  237. package/dist/sigfigs.d.ts +32 -0
  238. package/dist/sigfigs.js.flow +41 -0
  239. package/dist/styles/constants.d.ts +56 -0
  240. package/dist/styles/constants.js.flow +62 -0
  241. package/dist/styles/global-constants.d.ts +342 -0
  242. package/dist/styles/global-constants.js.flow +348 -0
  243. package/dist/styles/global-styles.d.ts +47 -0
  244. package/dist/styles/global-styles.js.flow +53 -0
  245. package/dist/styles/media-queries.d.ts +29 -0
  246. package/dist/styles/media-queries.js.flow +35 -0
  247. package/dist/styles/shared.d.ts +3 -0
  248. package/dist/styles/shared.js.flow +9 -0
  249. package/dist/tex-wrangler.d.ts +7 -0
  250. package/dist/tex-wrangler.js.flow +13 -0
  251. package/dist/translation-linter.d.ts +21 -0
  252. package/dist/translation-linter.js.flow +32 -0
  253. package/dist/traversal.d.ts +15 -0
  254. package/dist/traversal.js.flow +26 -0
  255. package/dist/types.d.ts +262 -0
  256. package/dist/types.js.flow +337 -0
  257. package/dist/util/a11y.d.ts +3 -0
  258. package/dist/util/a11y.js.flow +9 -0
  259. package/dist/util/answer-types.d.ts +34 -0
  260. package/dist/util/answer-types.js.flow +49 -0
  261. package/dist/util/colors.d.ts +89 -0
  262. package/dist/util/colors.js.flow +95 -0
  263. package/dist/util/fix-passage-refs.d.ts +2 -0
  264. package/dist/util/fix-passage-refs.js.flow +8 -0
  265. package/dist/util/graph-utils.d.ts +12 -0
  266. package/dist/util/graph-utils.js.flow +9 -0
  267. package/dist/util/graphie.d.ts +2 -0
  268. package/dist/util/graphie.js.flow +8 -0
  269. package/dist/util/interactive.d.ts +9 -0
  270. package/dist/util/interactive.js.flow +9 -0
  271. package/dist/util/katex-preprocess.d.ts +6 -0
  272. package/dist/util/katex-preprocess.js.flow +12 -0
  273. package/dist/util/math.d.ts +28 -0
  274. package/dist/util/math.js.flow +62 -0
  275. package/dist/util/mobile-native-utils.d.ts +7 -0
  276. package/dist/util/mobile-native-utils.js.flow +12 -0
  277. package/dist/util/react-render.d.ts +2 -0
  278. package/dist/util/react-render.js.flow +12 -0
  279. package/dist/util/register-all-widgets-for-testing.d.ts +1 -0
  280. package/dist/util/register-all-widgets-for-testing.js.flow +7 -0
  281. package/dist/util/scroll-utils.d.ts +10 -0
  282. package/dist/util/scroll-utils.js.flow +19 -0
  283. package/dist/util/sizing-utils.d.ts +9 -0
  284. package/dist/util/sizing-utils.js.flow +20 -0
  285. package/dist/util/tex.d.ts +6 -0
  286. package/dist/util/tex.js.flow +17 -0
  287. package/dist/util/url-utils.d.ts +6 -0
  288. package/dist/util/url-utils.js.flow +11 -0
  289. package/dist/util.d.ts +247 -0
  290. package/dist/util.js.flow +320 -0
  291. package/dist/version.d.ts +11 -0
  292. package/dist/version.js.flow +17 -0
  293. package/dist/widget-container.d.ts +27 -0
  294. package/dist/widget-container.js.flow +33 -0
  295. package/dist/widgets/__tests__/renderQuestion.d.ts +13 -0
  296. package/dist/widgets/__tests__/renderQuestion.js.flow +28 -0
  297. package/dist/widgets/categorizer.d.ts +55 -0
  298. package/dist/widgets/categorizer.js.flow +70 -0
  299. package/dist/widgets/cs-program.d.ts +48 -0
  300. package/dist/widgets/cs-program.js.flow +54 -0
  301. package/dist/widgets/definition.d.ts +42 -0
  302. package/dist/widgets/definition.js.flow +57 -0
  303. package/dist/widgets/dropdown.d.ts +51 -0
  304. package/dist/widgets/dropdown.js.flow +63 -0
  305. package/dist/widgets/explanation.d.ts +49 -0
  306. package/dist/widgets/explanation.js.flow +58 -0
  307. package/dist/widgets/expression.d.ts +90 -0
  308. package/dist/widgets/expression.js.flow +115 -0
  309. package/dist/widgets/graded-group-answer-bar.d.ts +13 -0
  310. package/dist/widgets/graded-group-answer-bar.js.flow +24 -0
  311. package/dist/widgets/graded-group-set.d.ts +52 -0
  312. package/dist/widgets/graded-group-set.js.flow +65 -0
  313. package/dist/widgets/graded-group.d.ts +66 -0
  314. package/dist/widgets/graded-group.js.flow +78 -0
  315. package/dist/widgets/grapher/util.d.ts +19 -0
  316. package/dist/widgets/grapher/util.js.flow +38 -0
  317. package/dist/widgets/grapher.d.ts +63 -0
  318. package/dist/widgets/grapher.js.flow +93 -0
  319. package/dist/widgets/group.d.ts +51 -0
  320. package/dist/widgets/group.js.flow +60 -0
  321. package/dist/widgets/iframe.d.ts +52 -0
  322. package/dist/widgets/iframe.js.flow +53 -0
  323. package/dist/widgets/image.d.ts +59 -0
  324. package/dist/widgets/image.js.flow +73 -0
  325. package/dist/widgets/input-number.d.ts +75 -0
  326. package/dist/widgets/input-number.js.flow +94 -0
  327. package/dist/widgets/interaction/arrow-picker.d.ts +12 -0
  328. package/dist/widgets/interaction/arrow-picker.js.flow +21 -0
  329. package/dist/widgets/interaction/color-picker.d.ts +14 -0
  330. package/dist/widgets/interaction/color-picker.js.flow +23 -0
  331. package/dist/widgets/interaction/dash-picker.d.ts +12 -0
  332. package/dist/widgets/interaction/dash-picker.js.flow +21 -0
  333. package/dist/widgets/interaction/element-container.d.ts +21 -0
  334. package/dist/widgets/interaction/element-container.js.flow +33 -0
  335. package/dist/widgets/interaction.d.ts +51 -0
  336. package/dist/widgets/interaction.js.flow +60 -0
  337. package/dist/widgets/interactive-graph.d.ts +180 -0
  338. package/dist/widgets/interactive-graph.js.flow +242 -0
  339. package/dist/widgets/label-image/answer-choices.d.ts +51 -0
  340. package/dist/widgets/label-image/answer-choices.js.flow +55 -0
  341. package/dist/widgets/label-image/marker.d.ts +28 -0
  342. package/dist/widgets/label-image/marker.js.flow +33 -0
  343. package/dist/widgets/label-image/types.d.ts +14 -0
  344. package/dist/widgets/label-image/types.js.flow +23 -0
  345. package/dist/widgets/label-image.d.ts +113 -0
  346. package/dist/widgets/label-image.js.flow +142 -0
  347. package/dist/widgets/lights-puzzle.d.ts +47 -0
  348. package/dist/widgets/lights-puzzle.js.flow +58 -0
  349. package/dist/widgets/matcher.d.ts +82 -0
  350. package/dist/widgets/matcher.js.flow +101 -0
  351. package/dist/widgets/matrix.d.ts +65 -0
  352. package/dist/widgets/matrix.js.flow +80 -0
  353. package/dist/widgets/measurer.d.ts +21 -0
  354. package/dist/widgets/measurer.js.flow +30 -0
  355. package/dist/widgets/molecule/molecule-drawing.d.ts +5 -0
  356. package/dist/widgets/molecule/molecule-drawing.js.flow +11 -0
  357. package/dist/widgets/molecule/molecule-layout.d.ts +82 -0
  358. package/dist/widgets/molecule/molecule-layout.js.flow +96 -0
  359. package/dist/widgets/molecule/smiles-parser.d.ts +19 -0
  360. package/dist/widgets/molecule/smiles-parser.js.flow +26 -0
  361. package/dist/widgets/molecule.d.ts +51 -0
  362. package/dist/widgets/molecule.js.flow +60 -0
  363. package/dist/widgets/number-line.d.ts +90 -0
  364. package/dist/widgets/number-line.js.flow +102 -0
  365. package/dist/widgets/numeric-input.d.ts +80 -0
  366. package/dist/widgets/numeric-input.js.flow +109 -0
  367. package/dist/widgets/orderer.d.ts +69 -0
  368. package/dist/widgets/orderer.js.flow +81 -0
  369. package/dist/widgets/passage/passage-markdown.d.ts +22 -0
  370. package/dist/widgets/passage/passage-markdown.js.flow +33 -0
  371. package/dist/widgets/passage-ref-target.d.ts +41 -0
  372. package/dist/widgets/passage-ref-target.js.flow +52 -0
  373. package/dist/widgets/passage-ref.d.ts +65 -0
  374. package/dist/widgets/passage-ref.js.flow +85 -0
  375. package/dist/widgets/passage.d.ts +125 -0
  376. package/dist/widgets/passage.js.flow +157 -0
  377. package/dist/widgets/plotter.d.ts +68 -0
  378. package/dist/widgets/plotter.js.flow +78 -0
  379. package/dist/widgets/radio/base-radio.d.ts +38 -0
  380. package/dist/widgets/radio/base-radio.js.flow +44 -0
  381. package/dist/widgets/radio/choice-a11y-text.d.ts +2 -0
  382. package/dist/widgets/radio/choice-a11y-text.js.flow +14 -0
  383. package/dist/widgets/radio/choice-icon/choice-icon.d.ts +16 -0
  384. package/dist/widgets/radio/choice-icon/choice-icon.js.flow +22 -0
  385. package/dist/widgets/radio/choice-icon/cross-out-line.d.ts +9 -0
  386. package/dist/widgets/radio/choice-icon/cross-out-line.js.flow +16 -0
  387. package/dist/widgets/radio/choice-icon/shared-styles.d.ts +5 -0
  388. package/dist/widgets/radio/choice-icon/shared-styles.js.flow +11 -0
  389. package/dist/widgets/radio/choice-none-above.d.ts +6 -0
  390. package/dist/widgets/radio/choice-none-above.js.flow +16 -0
  391. package/dist/widgets/radio/choice.d.ts +23 -0
  392. package/dist/widgets/radio/choice.js.flow +32 -0
  393. package/dist/widgets/radio/choice.jsx.fixture.d.ts +1 -0
  394. package/dist/widgets/radio/choice.jsx.fixture.js.flow +7 -0
  395. package/dist/widgets/radio/focus-ring.d.ts +12 -0
  396. package/dist/widgets/radio/focus-ring.js.flow +15 -0
  397. package/dist/widgets/radio/option-status.d.ts +17 -0
  398. package/dist/widgets/radio/option-status.js.flow +16 -0
  399. package/dist/widgets/radio/util.d.ts +6 -0
  400. package/dist/widgets/radio/util.js.flow +12 -0
  401. package/dist/widgets/radio/widget.d.ts +62 -0
  402. package/dist/widgets/radio/widget.js.flow +80 -0
  403. package/dist/widgets/radio.d.ts +22 -0
  404. package/dist/widgets/radio.js.flow +31 -0
  405. package/dist/widgets/reaction-diagram.d.ts +37 -0
  406. package/dist/widgets/reaction-diagram.js.flow +46 -0
  407. package/dist/widgets/sequence.d.ts +79 -0
  408. package/dist/widgets/sequence.js.flow +100 -0
  409. package/dist/widgets/simulator.d.ts +89 -0
  410. package/dist/widgets/simulator.js.flow +106 -0
  411. package/dist/widgets/sorter.d.ts +82 -0
  412. package/dist/widgets/sorter.js.flow +101 -0
  413. package/dist/widgets/table.d.ts +56 -0
  414. package/dist/widgets/table.js.flow +67 -0
  415. package/dist/widgets/transformer.d.ts +94 -0
  416. package/dist/widgets/transformer.js.flow +127 -0
  417. package/dist/widgets/unit.d.ts +77 -0
  418. package/dist/widgets/unit.js.flow +93 -0
  419. package/dist/widgets/video-transcript-link.d.ts +12 -0
  420. package/dist/widgets/video-transcript-link.js.flow +15 -0
  421. package/dist/widgets/video.d.ts +48 -0
  422. package/dist/widgets/video.js.flow +57 -0
  423. package/dist/widgets.d.ts +73 -0
  424. package/dist/widgets.js.flow +108 -0
  425. package/dist/zoom.d.ts +1 -0
  426. package/dist/zoom.js.flow +7 -0
  427. package/package.json +8 -8
  428. package/src/__stories__/hints-renderer.stories.tsx +58 -0
  429. package/src/__stories__/item-renderer.stories.tsx +29 -0
  430. package/src/__stories__/server-item-renderer.stories.tsx +29 -0
  431. package/src/__testdata__/graphie.testdata.ts +51 -0
  432. package/src/__testdata__/item-renderer.testdata.ts +160 -0
  433. package/src/__testdata__/renderer.testdata.ts +122 -0
  434. package/src/__tests__/a11y.test.ts +303 -0
  435. package/src/__tests__/item-renderer.test.tsx +891 -0
  436. package/src/__tests__/jipt-paragraphs.test.ts +32 -0
  437. package/src/__tests__/mock-asset-loading-widget.tsx +56 -0
  438. package/src/__tests__/mock-widget.tsx +33 -0
  439. package/src/__tests__/perseus-markdown.test.ts +764 -0
  440. package/src/__tests__/renderability.test.ts +480 -0
  441. package/src/__tests__/renderer-api.test.tsx +328 -0
  442. package/src/__tests__/renderer.test.tsx +1802 -0
  443. package/src/__tests__/server-item-renderer.test.tsx +518 -0
  444. package/src/__tests__/test-items/image-item.ts +24 -0
  445. package/src/__tests__/test-items/input-number-1-item.ts +26 -0
  446. package/src/__tests__/test-items/input-number-2-item.ts +38 -0
  447. package/src/__tests__/test-items/table-item.ts +29 -0
  448. package/src/__tests__/tex-wrangler.test.ts +82 -0
  449. package/src/__tests__/util.test.ts +40 -0
  450. package/src/a11y.ts +45 -0
  451. package/src/all-widgets.ts +9 -0
  452. package/src/article-renderer.tsx +215 -0
  453. package/src/asset-context.ts +30 -0
  454. package/src/basic-widgets.ts +16 -0
  455. package/src/components/__stories__/button-group.stories.tsx +59 -0
  456. package/src/components/__stories__/fixed-to-responsive.stories.tsx +143 -0
  457. package/src/components/__stories__/graph.stories.tsx +30 -0
  458. package/src/components/__stories__/graphie.stories.tsx +35 -0
  459. package/src/components/__stories__/hud.stories.tsx +39 -0
  460. package/src/components/__stories__/icon.stories.tsx +63 -0
  461. package/src/components/__stories__/image-loader.stories.tsx +62 -0
  462. package/src/components/__stories__/info-tip.stories.tsx +35 -0
  463. package/src/components/__stories__/inline-icon.stories.tsx +44 -0
  464. package/src/components/__stories__/input-with-examples.stories.tsx +57 -0
  465. package/src/components/__stories__/lint.stories.tsx +51 -0
  466. package/src/components/__stories__/math-input.stories.tsx +34 -0
  467. package/src/components/__stories__/math-output.stories.tsx +25 -0
  468. package/src/components/__stories__/multi-button-group.stories.tsx +70 -0
  469. package/src/components/__stories__/number-input.stories.tsx +43 -0
  470. package/src/components/__stories__/prop-check-box.stories.tsx +65 -0
  471. package/src/components/__stories__/range-input.stories.tsx +31 -0
  472. package/src/components/__stories__/simple-keypad-input.stories.tsx +29 -0
  473. package/src/components/__stories__/sortable.stories.tsx +77 -0
  474. package/src/components/__stories__/stub-tag-editor.stories.tsx +49 -0
  475. package/src/components/__stories__/svg-image.stories.tsx +82 -0
  476. package/src/components/__stories__/tex.stories.tsx +25 -0
  477. package/src/components/__stories__/text-buttons.stories.tsx +66 -0
  478. package/src/components/__stories__/text-input.stories.tsx +39 -0
  479. package/src/components/__stories__/text-list-editor.stories.tsx +34 -0
  480. package/src/components/__stories__/zoomable-tex.stories.tsx +39 -0
  481. package/src/components/__stories__/zoomable.stories.tsx +43 -0
  482. package/src/components/__testdata__/sorter.testdata.ts +19 -0
  483. package/src/components/__tests__/number-input.test.tsx +128 -0
  484. package/src/components/__tests__/sortable.test.tsx +234 -0
  485. package/src/components/__tests__/sorter.test.tsx +111 -0
  486. package/src/components/__tests__/zoomable.test.tsx +399 -0
  487. package/src/components/button-group.tsx +130 -0
  488. package/src/components/constants.ts +14 -0
  489. package/src/components/fixed-to-responsive.tsx +165 -0
  490. package/src/components/graph.tsx +421 -0
  491. package/src/components/graphie-classes.ts +153 -0
  492. package/src/components/graphie-movables.ts +338 -0
  493. package/src/components/graphie.tsx +357 -0
  494. package/src/components/highlighting/highlightable-content.tsx +243 -0
  495. package/src/components/highlighting/highlights.ts +207 -0
  496. package/src/components/highlighting/ranges.ts +304 -0
  497. package/src/components/highlighting/types.ts +73 -0
  498. package/src/components/highlighting/ui/highlight-renderer.tsx +175 -0
  499. package/src/components/highlighting/ui/highlight-set-renderer.tsx +188 -0
  500. package/src/components/highlighting/ui/highlight-tooltip.tsx +141 -0
  501. package/src/components/highlighting/ui/highlighting-ui.tsx +153 -0
  502. package/src/components/highlighting/ui/selection-tracker.tsx +189 -0
  503. package/src/components/highlighting/ui/types.ts +21 -0
  504. package/src/components/highlighting/ui/util.ts +165 -0
  505. package/src/components/highlighting/word-indexer.tsx +156 -0
  506. package/src/components/hud.tsx +178 -0
  507. package/src/components/icon.tsx +140 -0
  508. package/src/components/image-loader.tsx +176 -0
  509. package/src/components/info-tip/info-tip.tsx +144 -0
  510. package/src/components/info-tip.tsx +40 -0
  511. package/src/components/inline-icon.tsx +77 -0
  512. package/src/components/input-with-examples.tsx +249 -0
  513. package/src/components/lint.tsx +453 -0
  514. package/src/components/math-input.tsx +299 -0
  515. package/src/components/math-output.tsx +128 -0
  516. package/src/components/multi-button-group.tsx +135 -0
  517. package/src/components/number-input.tsx +250 -0
  518. package/src/components/prop-check-box.tsx +80 -0
  519. package/src/components/range-input.tsx +60 -0
  520. package/src/components/simple-keypad-input.tsx +88 -0
  521. package/src/components/sortable.tsx +914 -0
  522. package/src/components/stub-tag-editor.tsx +53 -0
  523. package/src/components/svg-image.tsx +777 -0
  524. package/src/components/tex-buttons.tsx +321 -0
  525. package/src/components/tex.tsx +67 -0
  526. package/src/components/text-input.tsx +128 -0
  527. package/src/components/text-list-editor.tsx +169 -0
  528. package/src/components/tooltip.tsx +392 -0
  529. package/src/components/visibility-observer/__stories__/visibility-observer.stories.tsx +143 -0
  530. package/src/components/visibility-observer/create-visibility-observer.ts +108 -0
  531. package/src/components/visibility-observer/get-scroll-parent.ts +46 -0
  532. package/src/components/visibility-observer/visibility-observer.tsx +124 -0
  533. package/src/components/zoomable-tex.tsx +78 -0
  534. package/src/components/zoomable.tsx +300 -0
  535. package/src/components.ts +16 -0
  536. package/src/definition-context.tsx +60 -0
  537. package/src/dependencies.ts +21 -0
  538. package/src/error-boundary.tsx +50 -0
  539. package/src/extra-widgets.ts +75 -0
  540. package/src/hint-renderer.tsx +149 -0
  541. package/src/hints-renderer.tsx +377 -0
  542. package/src/icon-paths.ts +119 -0
  543. package/src/index.ts +158 -0
  544. package/src/init.ts +54 -0
  545. package/src/interaction-tracker.ts +61 -0
  546. package/src/interactive2/__tests__/movable-mock.ts +43 -0
  547. package/src/interactive2/__tests__/movable-point.test.ts +145 -0
  548. package/src/interactive2/interactive-util.ts +155 -0
  549. package/src/interactive2/movable-helper-methods.ts +161 -0
  550. package/src/interactive2/movable-line-options.ts +419 -0
  551. package/src/interactive2/movable-line.ts +318 -0
  552. package/src/interactive2/movable-point-options.ts +212 -0
  553. package/src/interactive2/movable-point.tsx +593 -0
  554. package/src/interactive2/movable-polygon-options.ts +390 -0
  555. package/src/interactive2/movable-polygon.ts +368 -0
  556. package/src/interactive2/movable.ts +275 -0
  557. package/src/interactive2/objective_.ts +79 -0
  558. package/src/interactive2/types.ts +2 -0
  559. package/src/interactive2/wrapped-defaults.ts +87 -0
  560. package/src/interactive2/wrapped-ellipse.ts +94 -0
  561. package/src/interactive2/wrapped-line.ts +116 -0
  562. package/src/interactive2/wrapped-path.ts +51 -0
  563. package/src/interactive2.ts +73 -0
  564. package/src/item-renderer.tsx +514 -0
  565. package/src/item-version.ts +10 -0
  566. package/src/jipt-paragraphs.ts +49 -0
  567. package/src/jquery.mobile.vmouse.js +0 -1
  568. package/src/keypad-context.ts +34 -0
  569. package/src/loading-context.ts +21 -0
  570. package/src/logging/log.test.ts +39 -0
  571. package/src/logging/log.ts +97 -0
  572. package/src/mixins/__tests__/widget-prop-denylist.test.ts +21 -0
  573. package/src/mixins/changeable.ts +133 -0
  574. package/src/mixins/editor-jsonify.ts +13 -0
  575. package/src/mixins/provide-keypad.tsx +144 -0
  576. package/src/mixins/widget-jsonify-deprecated.ts +23 -0
  577. package/src/mixins/widget-prop-denylist.ts +48 -0
  578. package/src/multi-items/__stories__/multi-renderer.stories.tsx +87 -0
  579. package/src/multi-items/__testdata__/multi-renderer.testdata.ts +172 -0
  580. package/src/multi-items/__tests__/items.test.ts +250 -0
  581. package/src/multi-items/__tests__/multi-renderer.test.tsx +910 -0
  582. package/src/multi-items/__tests__/prop-type-builders.test.ts +133 -0
  583. package/src/multi-items/__tests__/shapes.test.ts +43 -0
  584. package/src/multi-items/__tests__/trees.test.ts +325 -0
  585. package/src/multi-items/item-types.js.flow +55 -0
  586. package/src/multi-items/item-types.ts +56 -0
  587. package/src/multi-items/items.ts +176 -0
  588. package/src/multi-items/multi-renderer.tsx +564 -0
  589. package/src/multi-items/prop-type-builders.ts +106 -0
  590. package/src/multi-items/shape-types.ts +52 -0
  591. package/src/multi-items/shapes.ts +52 -0
  592. package/src/multi-items/tree-types.ts +47 -0
  593. package/src/multi-items/trees.ts +360 -0
  594. package/src/multi-items.ts +75 -0
  595. package/src/perseus-api.tsx +188 -0
  596. package/src/perseus-env.ts +27 -0
  597. package/src/perseus-error.ts +17 -0
  598. package/src/perseus-markdown.tsx +407 -0
  599. package/src/perseus-types.ts +1474 -0
  600. package/src/question-paragraph.tsx +29 -0
  601. package/src/renderability.ts +109 -0
  602. package/src/renderer.tsx +1992 -0
  603. package/src/server-item-renderer.tsx +465 -0
  604. package/src/sigfigs.ts +490 -0
  605. package/src/styles/constants.ts +94 -0
  606. package/src/styles/global-constants.ts +343 -0
  607. package/src/styles/global-styles.ts +306 -0
  608. package/src/styles/media-queries.ts +47 -0
  609. package/src/styles/shared.ts +130 -0
  610. package/src/tex-wrangler.ts +182 -0
  611. package/src/translation-linter.ts +126 -0
  612. package/src/traversal.ts +138 -0
  613. package/src/types.ts +468 -0
  614. package/src/util/a11y.ts +23 -0
  615. package/src/util/answer-types.test.ts +114 -0
  616. package/src/util/answer-types.ts +876 -0
  617. package/src/util/colors.ts +93 -0
  618. package/src/util/fix-passage-refs.ts +121 -0
  619. package/src/util/graph-utils.ts +15 -0
  620. package/src/util/graphie.ts +1617 -0
  621. package/src/util/interactive.ts +4191 -0
  622. package/src/util/katex-preprocess.ts +14 -0
  623. package/src/util/math.ts +289 -0
  624. package/src/util/mobile-native-utils.test.ts +22 -0
  625. package/src/util/mobile-native-utils.ts +12 -0
  626. package/src/util/react-render.tsx +27 -0
  627. package/src/util/register-all-widgets-for-testing.ts +11 -0
  628. package/src/util/scroll-utils.ts +155 -0
  629. package/src/util/sizing-utils.ts +46 -0
  630. package/src/util/tex.ts +208 -0
  631. package/src/util/url-utils.ts +34 -0
  632. package/src/util.ts +1003 -0
  633. package/src/version.ts +11 -0
  634. package/src/widget-container.tsx +175 -0
  635. package/src/widgets/__stories__/categorizer.stories.tsx +14 -0
  636. package/src/widgets/__stories__/cs-program.stories.tsx +14 -0
  637. package/src/widgets/__stories__/definition.stories.tsx +111 -0
  638. package/src/widgets/__stories__/dropdown.stories.tsx +14 -0
  639. package/src/widgets/__stories__/explanation.stories.tsx +18 -0
  640. package/src/widgets/__stories__/expression.stories.tsx +29 -0
  641. package/src/widgets/__stories__/graded-group-set.stories.tsx +31 -0
  642. package/src/widgets/__stories__/graded-group.stories.tsx +31 -0
  643. package/src/widgets/__stories__/grapher.stories.tsx +60 -0
  644. package/src/widgets/__stories__/group.stories.tsx +14 -0
  645. package/src/widgets/__stories__/iframe.stories.tsx +14 -0
  646. package/src/widgets/__stories__/image.stories.tsx +61 -0
  647. package/src/widgets/__stories__/input-number.stories.tsx +110 -0
  648. package/src/widgets/__stories__/interaction.stories.tsx +14 -0
  649. package/src/widgets/__stories__/interactive-graph.stories.tsx +60 -0
  650. package/src/widgets/__stories__/matcher.stories.tsx +14 -0
  651. package/src/widgets/__stories__/matrix.stories.tsx +14 -0
  652. package/src/widgets/__stories__/number-line.stories.tsx +14 -0
  653. package/src/widgets/__stories__/numeric-input.stories.tsx +107 -0
  654. package/src/widgets/__stories__/on-screen-keypad.stories.tsx +120 -0
  655. package/src/widgets/__stories__/orderer.stories.tsx +14 -0
  656. package/src/widgets/__stories__/passage-ref.stories.tsx +22 -0
  657. package/src/widgets/__stories__/passage.stories.tsx +32 -0
  658. package/src/widgets/__stories__/radio.stories.tsx +88 -0
  659. package/src/widgets/__stories__/sorter.stories.tsx +14 -0
  660. package/src/widgets/__stories__/transformer.stories.tsx +14 -0
  661. package/src/widgets/__stories__/unit.stories.tsx +30 -0
  662. package/src/widgets/__stories__/video-transcript-link.stories.tsx +27 -0
  663. package/src/widgets/__stories__/video.stories.tsx +14 -0
  664. package/src/widgets/__testdata__/base-radio.testdata.ts +21 -0
  665. package/src/widgets/__testdata__/categorizer.testdata.ts +49 -0
  666. package/src/widgets/__testdata__/cs-program.testdata.ts +27 -0
  667. package/src/widgets/__testdata__/dropdown.testdata.ts +33 -0
  668. package/src/widgets/__testdata__/explanation.testdata.ts +55 -0
  669. package/src/widgets/__testdata__/expression.testdata.ts +94 -0
  670. package/src/widgets/__testdata__/graded-group-set.testdata.ts +356 -0
  671. package/src/widgets/__testdata__/graded-group.testdata.ts +77 -0
  672. package/src/widgets/__testdata__/grapher.testdata.ts +374 -0
  673. package/src/widgets/__testdata__/group.testdata.ts +155 -0
  674. package/src/widgets/__testdata__/iframe.testdata.ts +35 -0
  675. package/src/widgets/__testdata__/image.testdata.ts +36 -0
  676. package/src/widgets/__testdata__/input-number.testdata.ts +72 -0
  677. package/src/widgets/__testdata__/interaction.testdata.ts +262 -0
  678. package/src/widgets/__testdata__/interactive-graph.testdata.ts +644 -0
  679. package/src/widgets/__testdata__/matcher.testdata.ts +33 -0
  680. package/src/widgets/__testdata__/matrix.testdata.ts +28 -0
  681. package/src/widgets/__testdata__/number-line.testdata.ts +30 -0
  682. package/src/widgets/__testdata__/numeric-input.testdata.ts +266 -0
  683. package/src/widgets/__testdata__/orderer.testdata.ts +57 -0
  684. package/src/widgets/__testdata__/passage-ref.testdata.ts +101 -0
  685. package/src/widgets/__testdata__/passage.testdata.ts +95 -0
  686. package/src/widgets/__testdata__/radio.testdata.ts +261 -0
  687. package/src/widgets/__testdata__/sorter.testdata.ts +19 -0
  688. package/src/widgets/__testdata__/transformer.testdata.ts +108 -0
  689. package/src/widgets/__testdata__/video.testdata.ts +21 -0
  690. package/src/widgets/__tests__/__snapshots__/input-number.test.ts.snap +497 -0
  691. package/src/widgets/__tests__/__snapshots__/unit.test.tsx.snap +20 -0
  692. package/src/widgets/__tests__/categorizer.test.ts +203 -0
  693. package/src/widgets/__tests__/cs-program.test.ts +43 -0
  694. package/src/widgets/__tests__/definition.test.ts +136 -0
  695. package/src/widgets/__tests__/dropdown.test.ts +105 -0
  696. package/src/widgets/__tests__/explanation.test.ts +198 -0
  697. package/src/widgets/__tests__/expression.test.ts +431 -0
  698. package/src/widgets/__tests__/graded-group-set-jipt.test.ts +30 -0
  699. package/src/widgets/__tests__/graded-group-set.test.ts +288 -0
  700. package/src/widgets/__tests__/graded-group.test.ts +232 -0
  701. package/src/widgets/__tests__/grapher.cypress.ts +576 -0
  702. package/src/widgets/__tests__/grapher.test.ts +33 -0
  703. package/src/widgets/__tests__/group.test.tsx +512 -0
  704. package/src/widgets/__tests__/iframe.test.ts +45 -0
  705. package/src/widgets/__tests__/image.test.ts +62 -0
  706. package/src/widgets/__tests__/input-number.test.ts +354 -0
  707. package/src/widgets/__tests__/interaction.test.ts +31 -0
  708. package/src/widgets/__tests__/interactive-graph.cypress.ts +815 -0
  709. package/src/widgets/__tests__/interactive-graph.test.ts +106 -0
  710. package/src/widgets/__tests__/matcher.test.tsx +152 -0
  711. package/src/widgets/__tests__/matrix.test.ts +82 -0
  712. package/src/widgets/__tests__/number-line.test.ts +79 -0
  713. package/src/widgets/__tests__/numeric-input.test.ts +502 -0
  714. package/src/widgets/__tests__/orderer.test.ts +73 -0
  715. package/src/widgets/__tests__/passage-ref.test.ts +168 -0
  716. package/src/widgets/__tests__/passage.test.tsx +224 -0
  717. package/src/widgets/__tests__/radio/base-radio.test.tsx +470 -0
  718. package/src/widgets/__tests__/radio/choice-icon.test.tsx +90 -0
  719. package/src/widgets/__tests__/radio/choice.test.tsx +310 -0
  720. package/src/widgets/__tests__/radio/focus-ring.test.tsx +23 -0
  721. package/src/widgets/__tests__/radio/option-status.test.tsx +77 -0
  722. package/src/widgets/__tests__/radio.test.ts +807 -0
  723. package/src/widgets/__tests__/renderQuestion.tsx +77 -0
  724. package/src/widgets/__tests__/unit.test.tsx +168 -0
  725. package/src/widgets/__tests__/video.test.ts +53 -0
  726. package/src/widgets/categorizer.tsx +343 -0
  727. package/src/widgets/cs-program.tsx +227 -0
  728. package/src/widgets/definition.tsx +121 -0
  729. package/src/widgets/dropdown.tsx +142 -0
  730. package/src/widgets/explanation.tsx +317 -0
  731. package/src/widgets/expression.tsx +647 -0
  732. package/src/widgets/graded-group-answer-bar.tsx +142 -0
  733. package/src/widgets/graded-group-set.tsx +296 -0
  734. package/src/widgets/graded-group.tsx +466 -0
  735. package/src/widgets/grapher/util.tsx +803 -0
  736. package/src/widgets/grapher.tsx +674 -0
  737. package/src/widgets/group.tsx +196 -0
  738. package/src/widgets/iframe.tsx +195 -0
  739. package/src/widgets/image.tsx +309 -0
  740. package/src/widgets/input-number.tsx +413 -0
  741. package/src/widgets/interaction/arrow-picker.tsx +38 -0
  742. package/src/widgets/interaction/color-picker.tsx +69 -0
  743. package/src/widgets/interaction/dash-picker.tsx +45 -0
  744. package/src/widgets/interaction/element-container.tsx +101 -0
  745. package/src/widgets/interaction.tsx +826 -0
  746. package/src/widgets/interactive-graph.tsx +2921 -0
  747. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +98 -0
  748. package/src/widgets/label-image/__stories__/marker.stories.tsx +181 -0
  749. package/src/widgets/label-image/answer-choices.tsx +300 -0
  750. package/src/widgets/label-image/marker.tsx +364 -0
  751. package/src/widgets/label-image/types.ts +26 -0
  752. package/src/widgets/label-image.test.ts +840 -0
  753. package/src/widgets/label-image.tsx +849 -0
  754. package/src/widgets/lights-puzzle.tsx +359 -0
  755. package/src/widgets/matcher.tsx +278 -0
  756. package/src/widgets/matrix.tsx +637 -0
  757. package/src/widgets/measurer.tsx +217 -0
  758. package/src/widgets/molecule/__tests__/layout.test.ts +162 -0
  759. package/src/widgets/molecule/__tests__/parser.test.ts +155 -0
  760. package/src/widgets/molecule/molecule-drawing.ts +173 -0
  761. package/src/widgets/molecule/molecule-layout.ts +362 -0
  762. package/src/widgets/molecule/smiles-parser.ts +296 -0
  763. package/src/widgets/molecule.tsx +172 -0
  764. package/src/widgets/number-line.tsx +864 -0
  765. package/src/widgets/numeric-input.tsx +563 -0
  766. package/src/widgets/orderer.tsx +788 -0
  767. package/src/widgets/passage/__tests__/passage-markdown.test.ts +376 -0
  768. package/src/widgets/passage/passage-markdown.tsx +475 -0
  769. package/src/widgets/passage-ref-target.tsx +73 -0
  770. package/src/widgets/passage-ref.tsx +215 -0
  771. package/src/widgets/passage.tsx +584 -0
  772. package/src/widgets/plotter.tsx +1204 -0
  773. package/src/widgets/radio/__stories__/base-radio.stories.tsx +204 -0
  774. package/src/widgets/radio/__stories__/choice-icon.stories.tsx +168 -0
  775. package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +40 -0
  776. package/src/widgets/radio/__stories__/choice.stories.tsx +137 -0
  777. package/src/widgets/radio/__stories__/focus-ring.stories.tsx +43 -0
  778. package/src/widgets/radio/__stories__/option-status.stories.tsx +105 -0
  779. package/src/widgets/radio/base-radio.tsx +509 -0
  780. package/src/widgets/radio/choice-a11y-text.ts +61 -0
  781. package/src/widgets/radio/choice-icon/choice-icon.tsx +217 -0
  782. package/src/widgets/radio/choice-icon/cross-out-line.tsx +53 -0
  783. package/src/widgets/radio/choice-icon/shared-styles.ts +15 -0
  784. package/src/widgets/radio/choice-none-above.tsx +56 -0
  785. package/src/widgets/radio/choice.jsx.fixture.ts +45 -0
  786. package/src/widgets/radio/choice.tsx +358 -0
  787. package/src/widgets/radio/focus-ring.tsx +57 -0
  788. package/src/widgets/radio/option-status.tsx +100 -0
  789. package/src/widgets/radio/util.test.ts +18 -0
  790. package/src/widgets/radio/util.ts +31 -0
  791. package/src/widgets/radio/widget.tsx +521 -0
  792. package/src/widgets/radio.ts +157 -0
  793. package/src/widgets/reaction-diagram.tsx +144 -0
  794. package/src/widgets/sequence.tsx +137 -0
  795. package/src/widgets/simulator.tsx +831 -0
  796. package/src/widgets/sorter.tsx +154 -0
  797. package/src/widgets/table.tsx +393 -0
  798. package/src/widgets/transformer.tsx +2951 -0
  799. package/src/widgets/unit.tsx +374 -0
  800. package/src/widgets/video-transcript-link.test.tsx +213 -0
  801. package/src/widgets/video-transcript-link.tsx +84 -0
  802. package/src/widgets/video.tsx +124 -0
  803. package/src/widgets.ts +444 -0
  804. package/src/zoom.ts +558 -0
  805. package/tsconfig.json +25 -0
  806. package/tsconfig.tsbuildinfo +1 -0
  807. package/src/__stories__/hints-renderer.stories.jsx +0 -60
  808. package/src/__stories__/item-renderer.stories.jsx +0 -27
  809. package/src/__stories__/server-item-renderer.stories.jsx +0 -27
  810. package/src/__testdata__/graphie_testdata.js +0 -53
  811. package/src/__testdata__/item-renderer_testdata.js +0 -161
  812. package/src/__testdata__/renderer_testdata.js +0 -123
  813. package/src/__tests__/a11y_test.jsx +0 -304
  814. package/src/__tests__/item-renderer_test.jsx +0 -892
  815. package/src/__tests__/jipt-paragraphs_test.js +0 -34
  816. package/src/__tests__/mock-asset-loading-widget.jsx +0 -57
  817. package/src/__tests__/mock-widget.jsx +0 -35
  818. package/src/__tests__/perseus-markdown_test.jsx +0 -747
  819. package/src/__tests__/renderability_test.jsx +0 -481
  820. package/src/__tests__/renderer-api_test.jsx +0 -309
  821. package/src/__tests__/renderer_test.jsx +0 -1792
  822. package/src/__tests__/server-item-renderer_test.jsx +0 -516
  823. package/src/__tests__/test-items/image-item.js +0 -25
  824. package/src/__tests__/test-items/input-number-1-item.js +0 -27
  825. package/src/__tests__/test-items/input-number-2-item.js +0 -39
  826. package/src/__tests__/test-items/table-item.js +0 -30
  827. package/src/__tests__/tex-wrangler_test.js +0 -83
  828. package/src/__tests__/util_test.js +0 -41
  829. package/src/a11y.js +0 -46
  830. package/src/all-widgets.js +0 -9
  831. package/src/article-renderer.jsx +0 -214
  832. package/src/asset-context.js +0 -30
  833. package/src/basic-widgets.js +0 -13
  834. package/src/components/__stories__/button-group.stories.jsx +0 -61
  835. package/src/components/__stories__/fixed-to-responsive.stories.jsx +0 -132
  836. package/src/components/__stories__/graph.stories.jsx +0 -27
  837. package/src/components/__stories__/graphie.stories.jsx +0 -32
  838. package/src/components/__stories__/hud.stories.jsx +0 -36
  839. package/src/components/__stories__/icon.stories.jsx +0 -56
  840. package/src/components/__stories__/image-loader.stories.jsx +0 -60
  841. package/src/components/__stories__/info-tip.stories.jsx +0 -33
  842. package/src/components/__stories__/inline-icon.stories.jsx +0 -39
  843. package/src/components/__stories__/input-with-examples.stories.jsx +0 -52
  844. package/src/components/__stories__/lint.stories.jsx +0 -40
  845. package/src/components/__stories__/math-input.stories.jsx +0 -29
  846. package/src/components/__stories__/math-output.stories.jsx +0 -24
  847. package/src/components/__stories__/multi-button-group.stories.jsx +0 -69
  848. package/src/components/__stories__/number-input.stories.jsx +0 -42
  849. package/src/components/__stories__/prop-check-box.stories.jsx +0 -62
  850. package/src/components/__stories__/range-input.stories.jsx +0 -28
  851. package/src/components/__stories__/simple-keypad-input.stories.jsx +0 -28
  852. package/src/components/__stories__/sortable.stories.jsx +0 -73
  853. package/src/components/__stories__/stub-tag-editor.stories.jsx +0 -44
  854. package/src/components/__stories__/svg-image.stories.jsx +0 -71
  855. package/src/components/__stories__/tex.stories.jsx +0 -24
  856. package/src/components/__stories__/text-buttons.stories.jsx +0 -51
  857. package/src/components/__stories__/text-input.stories.jsx +0 -34
  858. package/src/components/__stories__/text-list-editor.stories.jsx +0 -33
  859. package/src/components/__stories__/zoomable-tex.stories.jsx +0 -39
  860. package/src/components/__stories__/zoomable.stories.jsx +0 -40
  861. package/src/components/__testdata__/sorter_testdata.js +0 -20
  862. package/src/components/__tests__/number-input_test.jsx +0 -112
  863. package/src/components/__tests__/sortable_test.jsx +0 -234
  864. package/src/components/__tests__/sorter_test.jsx +0 -111
  865. package/src/components/__tests__/zoomable_test.jsx +0 -388
  866. package/src/components/button-group.jsx +0 -131
  867. package/src/components/constants.js +0 -15
  868. package/src/components/fixed-to-responsive.jsx +0 -165
  869. package/src/components/graph.jsx +0 -413
  870. package/src/components/graphie-classes.jsx +0 -148
  871. package/src/components/graphie-movables.jsx +0 -342
  872. package/src/components/graphie.jsx +0 -353
  873. package/src/components/highlighting/highlightable-content.jsx +0 -245
  874. package/src/components/highlighting/highlights.js +0 -208
  875. package/src/components/highlighting/ranges.js +0 -299
  876. package/src/components/highlighting/types.js +0 -73
  877. package/src/components/highlighting/ui/highlight-renderer.jsx +0 -179
  878. package/src/components/highlighting/ui/highlight-set-renderer.jsx +0 -190
  879. package/src/components/highlighting/ui/highlight-tooltip.jsx +0 -143
  880. package/src/components/highlighting/ui/highlighting-ui.jsx +0 -163
  881. package/src/components/highlighting/ui/selection-tracker.jsx +0 -184
  882. package/src/components/highlighting/ui/types.js +0 -21
  883. package/src/components/highlighting/ui/util.js +0 -166
  884. package/src/components/highlighting/word-indexer.jsx +0 -155
  885. package/src/components/hud.jsx +0 -179
  886. package/src/components/icon.jsx +0 -149
  887. package/src/components/image-loader.jsx +0 -175
  888. package/src/components/info-tip/info-tip.jsx +0 -146
  889. package/src/components/info-tip.jsx +0 -38
  890. package/src/components/inline-icon.jsx +0 -81
  891. package/src/components/input-with-examples.jsx +0 -248
  892. package/src/components/lint.jsx +0 -455
  893. package/src/components/math-input.jsx +0 -293
  894. package/src/components/math-output.jsx +0 -128
  895. package/src/components/multi-button-group.jsx +0 -137
  896. package/src/components/number-input.jsx +0 -253
  897. package/src/components/prop-check-box.jsx +0 -80
  898. package/src/components/range-input.jsx +0 -61
  899. package/src/components/simple-keypad-input.jsx +0 -88
  900. package/src/components/sortable.jsx +0 -872
  901. package/src/components/stub-tag-editor.jsx +0 -54
  902. package/src/components/svg-image.jsx +0 -765
  903. package/src/components/tex-buttons.jsx +0 -320
  904. package/src/components/tex.jsx +0 -70
  905. package/src/components/text-input.jsx +0 -133
  906. package/src/components/text-list-editor.jsx +0 -157
  907. package/src/components/tooltip.jsx +0 -392
  908. package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +0 -140
  909. package/src/components/visibility-observer/get-scroll-parent.js +0 -45
  910. package/src/components/visibility-observer/visibility-observer.js +0 -109
  911. package/src/components/visibility-observer/visibility-observer.jsx +0 -123
  912. package/src/components/zoomable-tex.jsx +0 -70
  913. package/src/components/zoomable.jsx +0 -294
  914. package/src/components.js +0 -17
  915. package/src/definition-context.js +0 -59
  916. package/src/dependencies.js +0 -22
  917. package/src/error-boundary.jsx +0 -48
  918. package/src/extra-widgets.js +0 -75
  919. package/src/hint-renderer.jsx +0 -155
  920. package/src/hints-renderer.jsx +0 -367
  921. package/src/icon-paths.js +0 -120
  922. package/src/index.js +0 -159
  923. package/src/init.js +0 -49
  924. package/src/interaction-tracker.js +0 -60
  925. package/src/interactive2/__tests__/movable-mock.js +0 -48
  926. package/src/interactive2/__tests__/movable-point_test.js +0 -146
  927. package/src/interactive2/interactive-util.js +0 -152
  928. package/src/interactive2/movable-helper-methods.js +0 -160
  929. package/src/interactive2/movable-line-options.js +0 -392
  930. package/src/interactive2/movable-line.js +0 -322
  931. package/src/interactive2/movable-point-options.js +0 -190
  932. package/src/interactive2/movable-point.jsx +0 -597
  933. package/src/interactive2/movable-polygon-options.js +0 -351
  934. package/src/interactive2/movable-polygon.js +0 -368
  935. package/src/interactive2/movable.js +0 -273
  936. package/src/interactive2/objective_.js +0 -79
  937. package/src/interactive2/types.js +0 -3
  938. package/src/interactive2/wrapped-defaults.js +0 -86
  939. package/src/interactive2/wrapped-ellipse.js +0 -86
  940. package/src/interactive2/wrapped-line.js +0 -109
  941. package/src/interactive2/wrapped-path.js +0 -47
  942. package/src/interactive2.js +0 -91
  943. package/src/item-renderer.jsx +0 -514
  944. package/src/item-version.js +0 -12
  945. package/src/jipt-paragraphs.js +0 -52
  946. package/src/keypad-context.js +0 -33
  947. package/src/loading-context.js +0 -20
  948. package/src/logging/log.js +0 -107
  949. package/src/logging/log_test.js +0 -41
  950. package/src/mixins/__tests__/widget-prop-denylist_test.js +0 -23
  951. package/src/mixins/changeable.jsx +0 -115
  952. package/src/mixins/editor-jsonify.jsx +0 -13
  953. package/src/mixins/provide-keypad.jsx +0 -131
  954. package/src/mixins/widget-jsonify-deprecated.jsx +0 -23
  955. package/src/mixins/widget-prop-denylist.js +0 -49
  956. package/src/multi-items/__stories__/multi-renderer.stories.js +0 -88
  957. package/src/multi-items/__testdata__/multi-renderer_testdata.js +0 -174
  958. package/src/multi-items/__tests__/items_test.js +0 -245
  959. package/src/multi-items/__tests__/multi-renderer_test.js +0 -903
  960. package/src/multi-items/__tests__/prop-type-builders_test.js +0 -134
  961. package/src/multi-items/__tests__/shapes_test.js +0 -44
  962. package/src/multi-items/__tests__/trees_test.js +0 -316
  963. package/src/multi-items/items.js +0 -176
  964. package/src/multi-items/multi-renderer.jsx +0 -543
  965. package/src/multi-items/prop-type-builders.js +0 -105
  966. package/src/multi-items/shape-types.js +0 -47
  967. package/src/multi-items/shapes.js +0 -53
  968. package/src/multi-items/tree-types.js +0 -42
  969. package/src/multi-items/trees.js +0 -345
  970. package/src/multi-items.js +0 -76
  971. package/src/perseus-api.jsx +0 -221
  972. package/src/perseus-env.js +0 -26
  973. package/src/perseus-error.js +0 -19
  974. package/src/perseus-markdown.jsx +0 -408
  975. package/src/perseus-types.js +0 -1471
  976. package/src/question-paragraph.jsx +0 -30
  977. package/src/renderability.jsx +0 -108
  978. package/src/renderer.jsx +0 -1937
  979. package/src/server-item-renderer.jsx +0 -467
  980. package/src/sigfigs.jsx +0 -492
  981. package/src/styles/constants.js +0 -96
  982. package/src/styles/global-constants.js +0 -344
  983. package/src/styles/global-styles.js +0 -307
  984. package/src/styles/media-queries.js +0 -48
  985. package/src/styles/shared.js +0 -132
  986. package/src/tex-wrangler.js +0 -176
  987. package/src/translation-linter.js +0 -125
  988. package/src/traversal.jsx +0 -141
  989. package/src/types.js +0 -488
  990. package/src/util/a11y.js +0 -24
  991. package/src/util/answer-types.js +0 -861
  992. package/src/util/answer-types_test.js +0 -115
  993. package/src/util/colors.js +0 -94
  994. package/src/util/fix-passage-refs.jsx +0 -122
  995. package/src/util/graph-utils.js +0 -16
  996. package/src/util/graphie.js +0 -1586
  997. package/src/util/interactive.js +0 -4078
  998. package/src/util/katex-preprocess.js +0 -16
  999. package/src/util/math.js +0 -289
  1000. package/src/util/mobile-native-utils.js +0 -14
  1001. package/src/util/mobile-native-utils_test.js +0 -24
  1002. package/src/util/react-render.js +0 -27
  1003. package/src/util/register-all-widgets-for-testing.js +0 -12
  1004. package/src/util/scroll-utils.js +0 -162
  1005. package/src/util/sizing-utils.js +0 -46
  1006. package/src/util/tex.js +0 -203
  1007. package/src/util/url-utils.js +0 -36
  1008. package/src/util.js +0 -984
  1009. package/src/version.js +0 -11
  1010. package/src/widget-container.jsx +0 -178
  1011. package/src/widgets/__stories__/categorizer.stories.jsx +0 -15
  1012. package/src/widgets/__stories__/cs-program.stories.jsx +0 -15
  1013. package/src/widgets/__stories__/definition.stories.jsx +0 -108
  1014. package/src/widgets/__stories__/dropdown.stories.jsx +0 -15
  1015. package/src/widgets/__stories__/explanation.stories.jsx +0 -19
  1016. package/src/widgets/__stories__/expression.stories.jsx +0 -26
  1017. package/src/widgets/__stories__/graded-group-set.stories.jsx +0 -32
  1018. package/src/widgets/__stories__/graded-group.stories.jsx +0 -32
  1019. package/src/widgets/__stories__/grapher.stories.jsx +0 -47
  1020. package/src/widgets/__stories__/group.stories.jsx +0 -15
  1021. package/src/widgets/__stories__/iframe.stories.jsx +0 -15
  1022. package/src/widgets/__stories__/image.stories.jsx +0 -62
  1023. package/src/widgets/__stories__/input-number.stories.jsx +0 -102
  1024. package/src/widgets/__stories__/interaction.stories.jsx +0 -15
  1025. package/src/widgets/__stories__/interactive-graph.stories.jsx +0 -61
  1026. package/src/widgets/__stories__/matcher.stories.jsx +0 -15
  1027. package/src/widgets/__stories__/matrix.stories.jsx +0 -15
  1028. package/src/widgets/__stories__/number-line.stories.jsx +0 -15
  1029. package/src/widgets/__stories__/numeric-input.stories.jsx +0 -106
  1030. package/src/widgets/__stories__/on-screen-keypad.stories.jsx +0 -118
  1031. package/src/widgets/__stories__/orderer.stories.jsx +0 -15
  1032. package/src/widgets/__stories__/passage-ref.stories.jsx +0 -23
  1033. package/src/widgets/__stories__/passage.stories.jsx +0 -27
  1034. package/src/widgets/__stories__/radio.stories.jsx +0 -90
  1035. package/src/widgets/__stories__/sorter.stories.jsx +0 -15
  1036. package/src/widgets/__stories__/transformer.stories.jsx +0 -15
  1037. package/src/widgets/__stories__/unit.stories.jsx +0 -45
  1038. package/src/widgets/__stories__/video-transcript-link.stories.jsx +0 -24
  1039. package/src/widgets/__stories__/video.stories.jsx +0 -15
  1040. package/src/widgets/__testdata__/base-radio_testdata.js +0 -22
  1041. package/src/widgets/__testdata__/categorizer_testdata.js +0 -50
  1042. package/src/widgets/__testdata__/cs-program_testdata.js +0 -28
  1043. package/src/widgets/__testdata__/dropdown_testdata.js +0 -34
  1044. package/src/widgets/__testdata__/explanation_testdata.js +0 -56
  1045. package/src/widgets/__testdata__/expression_testdata.js +0 -95
  1046. package/src/widgets/__testdata__/graded-group-set_testdata.js +0 -358
  1047. package/src/widgets/__testdata__/graded-group_testdata.js +0 -79
  1048. package/src/widgets/__testdata__/grapher_testdata.js +0 -376
  1049. package/src/widgets/__testdata__/group_testdata.js +0 -156
  1050. package/src/widgets/__testdata__/iframe_testdata.js +0 -36
  1051. package/src/widgets/__testdata__/image_testdata.js +0 -38
  1052. package/src/widgets/__testdata__/input-number_testdata.js +0 -73
  1053. package/src/widgets/__testdata__/interaction_testdata.js +0 -264
  1054. package/src/widgets/__testdata__/interactive-graph_testdata.js +0 -646
  1055. package/src/widgets/__testdata__/matcher_testdata.js +0 -34
  1056. package/src/widgets/__testdata__/matrix_testdata.js +0 -29
  1057. package/src/widgets/__testdata__/number-line_testdata.js +0 -31
  1058. package/src/widgets/__testdata__/numeric-input_testdata.js +0 -266
  1059. package/src/widgets/__testdata__/orderer_testdata.js +0 -58
  1060. package/src/widgets/__testdata__/passage-ref_testdata.js +0 -103
  1061. package/src/widgets/__testdata__/passage_testdata.js +0 -96
  1062. package/src/widgets/__testdata__/radio_testdata.js +0 -262
  1063. package/src/widgets/__testdata__/sorter_testdata.js +0 -20
  1064. package/src/widgets/__testdata__/transformer_testdata.js +0 -109
  1065. package/src/widgets/__testdata__/video_testdata.js +0 -22
  1066. package/src/widgets/__tests__/__snapshots__/expression_test.jsx.snap +0 -38
  1067. package/src/widgets/__tests__/__snapshots__/input-number_test.jsx.snap +0 -978
  1068. package/src/widgets/__tests__/categorizer_test.jsx +0 -205
  1069. package/src/widgets/__tests__/cs-program_test.jsx +0 -45
  1070. package/src/widgets/__tests__/definition_test.js +0 -137
  1071. package/src/widgets/__tests__/dropdown_test.jsx +0 -106
  1072. package/src/widgets/__tests__/explanation_test.js +0 -199
  1073. package/src/widgets/__tests__/expression_test.jsx +0 -439
  1074. package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +0 -32
  1075. package/src/widgets/__tests__/graded-group-set_test.jsx +0 -290
  1076. package/src/widgets/__tests__/graded-group_test.jsx +0 -233
  1077. package/src/widgets/__tests__/grapher.cypress.jsx +0 -546
  1078. package/src/widgets/__tests__/grapher_test.jsx +0 -35
  1079. package/src/widgets/__tests__/group_test.jsx +0 -512
  1080. package/src/widgets/__tests__/iframe_test.jsx +0 -47
  1081. package/src/widgets/__tests__/image_test.jsx +0 -65
  1082. package/src/widgets/__tests__/input-number_test.jsx +0 -360
  1083. package/src/widgets/__tests__/interaction_test.jsx +0 -33
  1084. package/src/widgets/__tests__/interactive-graph.cypress.jsx +0 -777
  1085. package/src/widgets/__tests__/interactive-graph_test.jsx +0 -105
  1086. package/src/widgets/__tests__/matcher_test.jsx +0 -157
  1087. package/src/widgets/__tests__/matrix_test.jsx +0 -84
  1088. package/src/widgets/__tests__/number-line_test.jsx +0 -81
  1089. package/src/widgets/__tests__/numeric-input_test.jsx +0 -514
  1090. package/src/widgets/__tests__/orderer_test.jsx +0 -75
  1091. package/src/widgets/__tests__/passage-ref_test.jsx +0 -166
  1092. package/src/widgets/__tests__/passage_test.jsx +0 -212
  1093. package/src/widgets/__tests__/radio/base-radio_test.jsx +0 -443
  1094. package/src/widgets/__tests__/radio/choice-icon_test.jsx +0 -91
  1095. package/src/widgets/__tests__/radio/choice_test.jsx +0 -311
  1096. package/src/widgets/__tests__/radio/focus-ring_test.jsx +0 -23
  1097. package/src/widgets/__tests__/radio/option-status_test.jsx +0 -65
  1098. package/src/widgets/__tests__/radio_test.jsx +0 -810
  1099. package/src/widgets/__tests__/renderQuestion.jsx +0 -66
  1100. package/src/widgets/__tests__/unit_test.jsx +0 -144
  1101. package/src/widgets/__tests__/video_test.jsx +0 -55
  1102. package/src/widgets/categorizer.jsx +0 -343
  1103. package/src/widgets/cs-program.jsx +0 -227
  1104. package/src/widgets/definition.jsx +0 -121
  1105. package/src/widgets/dropdown.jsx +0 -141
  1106. package/src/widgets/explanation.jsx +0 -315
  1107. package/src/widgets/expression.jsx +0 -652
  1108. package/src/widgets/graded-group-answer-bar.jsx +0 -152
  1109. package/src/widgets/graded-group-set.jsx +0 -296
  1110. package/src/widgets/graded-group.jsx +0 -453
  1111. package/src/widgets/grapher/util.jsx +0 -806
  1112. package/src/widgets/grapher.jsx +0 -663
  1113. package/src/widgets/group.jsx +0 -195
  1114. package/src/widgets/iframe.jsx +0 -198
  1115. package/src/widgets/image.jsx +0 -310
  1116. package/src/widgets/input-number.jsx +0 -412
  1117. package/src/widgets/interaction/arrow-picker.jsx +0 -39
  1118. package/src/widgets/interaction/color-picker.jsx +0 -69
  1119. package/src/widgets/interaction/dash-picker.jsx +0 -46
  1120. package/src/widgets/interaction/element-container.jsx +0 -97
  1121. package/src/widgets/interaction.jsx +0 -742
  1122. package/src/widgets/interactive-graph.jsx +0 -2820
  1123. package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +0 -93
  1124. package/src/widgets/label-image/__stories__/marker.stories.jsx +0 -172
  1125. package/src/widgets/label-image/answer-choices.jsx +0 -311
  1126. package/src/widgets/label-image/marker.jsx +0 -365
  1127. package/src/widgets/label-image/types.js +0 -33
  1128. package/src/widgets/label-image.jsx +0 -841
  1129. package/src/widgets/label-image_test.js +0 -840
  1130. package/src/widgets/lights-puzzle.jsx +0 -356
  1131. package/src/widgets/matcher.jsx +0 -274
  1132. package/src/widgets/matrix.jsx +0 -632
  1133. package/src/widgets/measurer.jsx +0 -215
  1134. package/src/widgets/molecule/__tests__/layout_test.jsx +0 -159
  1135. package/src/widgets/molecule/__tests__/parser_test.jsx +0 -142
  1136. package/src/widgets/molecule/molecule-drawing.jsx +0 -175
  1137. package/src/widgets/molecule/molecule-layout.jsx +0 -367
  1138. package/src/widgets/molecule/smiles-parser.jsx +0 -300
  1139. package/src/widgets/molecule.jsx +0 -172
  1140. package/src/widgets/number-line.jsx +0 -858
  1141. package/src/widgets/numeric-input.jsx +0 -553
  1142. package/src/widgets/orderer.jsx +0 -756
  1143. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -373
  1144. package/src/widgets/passage/passage-markdown.jsx +0 -472
  1145. package/src/widgets/passage-ref-target.jsx +0 -73
  1146. package/src/widgets/passage-ref.jsx +0 -214
  1147. package/src/widgets/passage.jsx +0 -576
  1148. package/src/widgets/plotter.jsx +0 -1198
  1149. package/src/widgets/radio/__stories__/base-radio.stories.jsx +0 -189
  1150. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +0 -166
  1151. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +0 -41
  1152. package/src/widgets/radio/__stories__/choice.stories.jsx +0 -138
  1153. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +0 -44
  1154. package/src/widgets/radio/__stories__/option-status.stories.jsx +0 -104
  1155. package/src/widgets/radio/base-radio.jsx +0 -494
  1156. package/src/widgets/radio/choice-a11y-text.js +0 -62
  1157. package/src/widgets/radio/choice-icon/choice-icon.jsx +0 -214
  1158. package/src/widgets/radio/choice-icon/cross-out-line.jsx +0 -52
  1159. package/src/widgets/radio/choice-icon/shared-styles.js +0 -16
  1160. package/src/widgets/radio/choice-none-above.jsx +0 -63
  1161. package/src/widgets/radio/choice.jsx +0 -382
  1162. package/src/widgets/radio/choice.jsx.fixture.js +0 -46
  1163. package/src/widgets/radio/focus-ring.jsx +0 -58
  1164. package/src/widgets/radio/option-status.jsx +0 -98
  1165. package/src/widgets/radio/util.js +0 -32
  1166. package/src/widgets/radio/util_test.js +0 -19
  1167. package/src/widgets/radio/widget.jsx +0 -515
  1168. package/src/widgets/radio.jsx +0 -155
  1169. package/src/widgets/reaction-diagram.jsx +0 -137
  1170. package/src/widgets/sequence.jsx +0 -143
  1171. package/src/widgets/simulator.jsx +0 -806
  1172. package/src/widgets/sorter.jsx +0 -149
  1173. package/src/widgets/table.jsx +0 -397
  1174. package/src/widgets/transformer.jsx +0 -2876
  1175. package/src/widgets/unit.jsx +0 -372
  1176. package/src/widgets/video-transcript-link.jsx +0 -84
  1177. package/src/widgets/video-transcript-link_test.js +0 -211
  1178. package/src/widgets/video.jsx +0 -126
  1179. package/src/widgets.js +0 -431
  1180. package/src/zoom.js +0 -539
  1181. /package/{src/multi-items/item-types.js → dist/multi-items/item-types.js.flow} +0 -0
  1182. /package/dist/{strings.js → strings} +0 -0
  1183. /package/src/__tests__/__snapshots__/{item-renderer_test.jsx.snap → item-renderer.test.tsx.snap} +0 -0
  1184. /package/src/__tests__/__snapshots__/{perseus-markdown_test.jsx.snap → perseus-markdown.test.ts.snap} +0 -0
  1185. /package/src/__tests__/__snapshots__/{renderer_test.jsx.snap → renderer.test.tsx.snap} +0 -0
  1186. /package/src/__tests__/__snapshots__/{server-item-renderer_test.jsx.snap → server-item-renderer.test.tsx.snap} +0 -0
  1187. /package/src/components/__tests__/__snapshots__/{sortable_test.jsx.snap → sortable.test.tsx.snap} +0 -0
  1188. /package/src/components/__tests__/__snapshots__/{sorter_test.jsx.snap → sorter.test.tsx.snap} +0 -0
  1189. /package/src/multi-items/__tests__/__snapshots__/{multi-renderer_test.js.snap → multi-renderer.test.tsx.snap} +0 -0
  1190. /package/src/widgets/__tests__/__snapshots__/{categorizer_test.jsx.snap → categorizer.test.ts.snap} +0 -0
  1191. /package/src/widgets/__tests__/__snapshots__/{cs-program_test.jsx.snap → cs-program.test.ts.snap} +0 -0
  1192. /package/src/widgets/__tests__/__snapshots__/{definition_test.js.snap → definition.test.ts.snap} +0 -0
  1193. /package/src/widgets/__tests__/__snapshots__/{dropdown_test.jsx.snap → dropdown.test.ts.snap} +0 -0
  1194. /package/src/widgets/__tests__/__snapshots__/{explanation_test.js.snap → explanation.test.ts.snap} +0 -0
  1195. /package/src/widgets/__tests__/__snapshots__/{graded-group-set-jipt_test.jsx.snap → graded-group-set-jipt.test.ts.snap} +0 -0
  1196. /package/src/widgets/__tests__/__snapshots__/{graded-group-set_test.jsx.snap → graded-group-set.test.ts.snap} +0 -0
  1197. /package/src/widgets/__tests__/__snapshots__/{graded-group_test.jsx.snap → graded-group.test.ts.snap} +0 -0
  1198. /package/src/widgets/__tests__/__snapshots__/{grapher_test.jsx.snap → grapher.test.ts.snap} +0 -0
  1199. /package/src/widgets/__tests__/__snapshots__/{group_test.jsx.snap → group.test.tsx.snap} +0 -0
  1200. /package/src/widgets/__tests__/__snapshots__/{iframe_test.jsx.snap → iframe.test.ts.snap} +0 -0
  1201. /package/src/widgets/__tests__/__snapshots__/{image_test.jsx.snap → image.test.ts.snap} +0 -0
  1202. /package/src/widgets/__tests__/__snapshots__/{interaction_test.jsx.snap → interaction.test.ts.snap} +0 -0
  1203. /package/src/widgets/__tests__/__snapshots__/{interactive-graph_test.jsx.snap → interactive-graph.test.ts.snap} +0 -0
  1204. /package/src/widgets/__tests__/__snapshots__/{matcher_test.jsx.snap → matcher.test.tsx.snap} +0 -0
  1205. /package/src/widgets/__tests__/__snapshots__/{matrix_test.jsx.snap → matrix.test.ts.snap} +0 -0
  1206. /package/src/widgets/__tests__/__snapshots__/{number-line_test.jsx.snap → number-line.test.ts.snap} +0 -0
  1207. /package/src/widgets/__tests__/__snapshots__/{numeric-input_test.jsx.snap → numeric-input.test.ts.snap} +0 -0
  1208. /package/src/widgets/__tests__/__snapshots__/{orderer_test.jsx.snap → orderer.test.ts.snap} +0 -0
  1209. /package/src/widgets/__tests__/__snapshots__/{passage-ref_test.jsx.snap → passage-ref.test.ts.snap} +0 -0
  1210. /package/src/widgets/__tests__/__snapshots__/{passage_test.jsx.snap → passage.test.tsx.snap} +0 -0
  1211. /package/src/widgets/__tests__/__snapshots__/{radio_test.jsx.snap → radio.test.ts.snap} +0 -0
  1212. /package/src/widgets/__tests__/__snapshots__/{video_test.jsx.snap → video.test.ts.snap} +0 -0
@@ -1,2876 +0,0 @@
1
- /* eslint-disable react/prop-types */
2
- /* eslint-disable @babel/no-invalid-this */
3
- /* eslint-disable camelcase */
4
- /* eslint-disable react/no-did-update-set-state */
5
- /* eslint-disable react/sort-comp */
6
- /*
7
- NOTE(Pereus stabilize project): This widget is no-longer-used as of January
8
- 2022. It's not present in US content but is still present in some
9
- international content so it can't be removed quite yet. As soon as it's
10
- verified to have been removed from international content this all needs
11
- to get deleted. In the meantime- do not maintain this code.
12
-
13
- ref: https://khanacademy.slack.com/archives/CJDRXTGQ7/p1641857400011600
14
- ref: https://khanacademy.slack.com/archives/C2RFQGYKU/p1596160975061500?thread_ts=1596129758.040000&cid=C2RFQGYKU
15
- */
16
- // @flow
17
- import {
18
- number as knumber,
19
- vector as kvector,
20
- point as kpoint,
21
- ray as kray,
22
- line as kline,
23
- } from "@khanacademy/kmath";
24
- import * as i18n from "@khanacademy/wonder-blocks-i18n";
25
- import createReactClass from "create-react-class";
26
- import $ from "jquery";
27
- import * as React from "react";
28
- import ReactDOM from "react-dom";
29
- import _ from "underscore";
30
-
31
- import Graph from "../components/graph.jsx";
32
- import InlineIcon from "../components/inline-icon.jsx";
33
- import MathOutput from "../components/math-output.jsx";
34
- import NumberInput from "../components/number-input.jsx";
35
- import SimpleKeypadInput from "../components/simple-keypad-input.jsx";
36
- import {getDependencies} from "../dependencies.js";
37
- import {iconPlus, iconUndo} from "../icon-paths.js";
38
- import InteractiveUtil from "../interactive2/interactive-util.js";
39
- import {Errors} from "../logging/log.js";
40
- import {ApiOptions} from "../perseus-api.jsx";
41
- import {PerseusError} from "../perseus-error.js";
42
- import Util from "../util.js";
43
- import KhanColors from "../util/colors.js";
44
- import KhanMath from "../util/math.js";
45
-
46
- import type {Coord} from "../interactive2/types.js";
47
- import type {
48
- PerseusTransformerWidgetOptions,
49
- PerseusTransformerTransformation as TransformerTransformation,
50
- DilationTransformation,
51
- ReflectionTransformation,
52
- RotationTransformation,
53
- TranslationTransformation,
54
- } from "../perseus-types.js";
55
- import type {Path, PerseusScore, WidgetExports, WidgetProps} from "../types.js";
56
-
57
- const {deepEq, getGridStep, captureScratchpadTouchStart} = Util;
58
- const {assert} = InteractiveUtil;
59
-
60
- const ROTATE_SNAP_DEGREES = 15;
61
-
62
- const DEGREE_SIGN = "\u00B0";
63
- const RENDER_TRANSFORM_DELAY_IN_MS = 300;
64
- const ROTATE_HANDLE_DIST = 1.5;
65
- const REFLECT_ROTATE_HANDLE_DIST = 2;
66
- const REFLECT_BUTTON_SIZE = 1;
67
- const defaultBoxSize = 400;
68
- const defaultBackgroundImage = {
69
- url: null,
70
- };
71
-
72
- /* Does a pluck on keys inside objects in an object
73
- *
74
- * Ex:
75
- * tools = {
76
- * translation: {
77
- * enabled: true
78
- * },
79
- * rotation: {
80
- * enabled: false
81
- * }
82
- * };
83
- * pluckObject(tools, "enabled") returns {
84
- * translation: true
85
- * rotation: false
86
- * }
87
- */
88
- function pluckObject(object, subKey) {
89
- return _.object(
90
- _.map(object, function (value, key) {
91
- return [key, value[subKey]];
92
- }),
93
- );
94
- }
95
-
96
- const defaultGraphProps = function (setProps, boxSize) {
97
- setProps = setProps || {};
98
- const labels = setProps.labels || ["x", "y"];
99
- const range = setProps.range || [
100
- [-10, 10],
101
- [-10, 10],
102
- ];
103
- const step = setProps.step || [1, 1];
104
- const gridStep = setProps.gridStep || getGridStep(range, step, boxSize);
105
- return {
106
- box: [boxSize, boxSize],
107
- labels: labels,
108
- range: range,
109
- step: step,
110
- gridStep: gridStep,
111
- valid: true,
112
- backgroundImage: defaultBackgroundImage,
113
- markings: "grid",
114
- showProtractor: false,
115
- };
116
- };
117
-
118
- const defaultTransformerProps = {
119
- apiOptions: ApiOptions.defaults,
120
- gradeEmpty: false,
121
- graphMode: "interactive",
122
- listMode: "dynamic",
123
- graph: {},
124
- tools: {
125
- translation: {
126
- enabled: true,
127
- required: false,
128
- constraints: {},
129
- },
130
- rotation: {
131
- enabled: true,
132
- required: false,
133
- constraints: {
134
- fixed: false,
135
- },
136
- coord: [1, 6],
137
- },
138
- reflection: {
139
- enabled: true,
140
- required: false,
141
- constraints: {
142
- fixed: false,
143
- },
144
- coords: [
145
- [2, -4],
146
- [2, 2],
147
- ],
148
- },
149
- dilation: {
150
- enabled: true,
151
- required: false,
152
- constraints: {
153
- fixed: false,
154
- },
155
- coord: [6, 6],
156
- },
157
- },
158
- drawSolutionShape: true,
159
- starting: {
160
- shape: {
161
- type: "polygon-3",
162
- coords: [
163
- [2, 2],
164
- [2, 6],
165
- [7, 2],
166
- ],
167
- },
168
- transformations: [],
169
- },
170
- correct: {
171
- shape: {
172
- type: "polygon-3",
173
- coords: [
174
- [2, 2],
175
- [2, 6],
176
- [7, 2],
177
- ],
178
- },
179
- transformations: [],
180
- },
181
- };
182
-
183
- function colorForTool(tool) {
184
- return tool.constraints.fixed ? KhanColors.DYNAMIC : KhanColors.INTERACTIVE;
185
- }
186
-
187
- /* Scales a distance from the default range of
188
- * [-10, 10] to a given props.range pair
189
- *
190
- * Used for sizing various transformation tools
191
- * (rotation handle, dilation circle)
192
- */
193
- function scaleToRange(dist, range) {
194
- const spreadX = range[0][1] - range[0][0];
195
- const spreadY = range[1][1] - range[1][0];
196
-
197
- return (dist * Math.max(spreadX, spreadY)) / 20;
198
- }
199
-
200
- function dilatePointFromCenter(point, dilationCenter, scale) {
201
- const pv = kvector.subtract(point, dilationCenter);
202
- const pvScaled = kvector.scale(pv, scale);
203
- const transformedPoint = kvector.add(dilationCenter, pvScaled);
204
- return transformedPoint;
205
- }
206
-
207
- // TODO(jack): i18nize this
208
- function stringFromDecimal(number) {
209
- return String(KhanMath.roundTo(9, number));
210
- }
211
-
212
- function stringFromFraction(number) {
213
- const frac = KhanMath.toFraction(number, knumber.DEFAULT_TOLERANCE);
214
- if (frac[1] === 1) {
215
- return stringFromDecimal(number);
216
- }
217
- return stringFromDecimal(frac[0]) + "/" + stringFromDecimal(frac[1]);
218
- }
219
-
220
- function texFromPoint(point) {
221
- const {TeX} = getDependencies();
222
-
223
- return [
224
- <TeX key="(">(</TeX>,
225
- stringFromDecimal(point[0]),
226
- <TeX key=",">{", {}"}</TeX>,
227
- stringFromDecimal(point[1]),
228
- <TeX key=")">)</TeX>,
229
- ];
230
- }
231
-
232
- function texFromVector(vector) {
233
- const {TeX} = getDependencies();
234
-
235
- return [
236
- <TeX key="<">{i18n.doNotTranslate("\\langle")}</TeX>,
237
- stringFromDecimal(vector[0]),
238
- <TeX key=",">{", {}"}</TeX>,
239
- stringFromDecimal(vector[1]),
240
- <TeX key=">">{i18n.doNotTranslate("\\rangle")}</TeX>,
241
- ];
242
- }
243
-
244
- function texFromAngleDeg(angleDeg) {
245
- return stringFromDecimal(angleDeg) + DEGREE_SIGN;
246
- }
247
-
248
- function orderInsensitiveCoordsEqual(coords1, coords2) {
249
- coords1 = _.clone(coords1).sort(kpoint.compare);
250
- coords2 = _.clone(coords2).sort(kpoint.compare);
251
- return _.all(
252
- _.map(coords1, function (coord1, i) {
253
- const coord2 = coords2[i];
254
- return kpoint.equal(coord1, coord2);
255
- }),
256
- );
257
- }
258
-
259
- const inputComponentForApiOptions = (apiOptions) => {
260
- if (apiOptions.customKeypad) {
261
- return SimpleKeypadInput;
262
- }
263
- if (apiOptions.staticRender) {
264
- return MathOutput;
265
- }
266
- return NumberInput;
267
- };
268
-
269
- /* Perform operations on raw transform objects */
270
- const TransformOps = {
271
- apply: function (transform: TransformerTransformation) {
272
- // Any transformation with empty text boxes is a no-op until
273
- // filled out (these show up as nulls in transform.vector/line/etc).
274
- // TODO (jack): Merge this just into reflections now that other
275
- // transforms are always valid (after merging transformation
276
- // collapsing, which may use isValid)
277
- // $FlowFixMe[prop-missing]
278
- // $FlowFixMe[incompatible-call]
279
- if (!Transformations[transform.type].isValid(transform)) {
280
- return _.identity; // do not transform the coord
281
- }
282
- // $FlowFixMe[prop-missing]
283
- // $FlowFixMe[incompatible-call]
284
- return Transformations[transform.type].apply(transform);
285
- },
286
-
287
- append: function (transformList, newTransform) {
288
- // Append newTransform to transformList, and collapse the last
289
- // two transforms if they are collapsable
290
- const results = TransformOps._appendAndCollapseLastTwo(
291
- transformList,
292
- newTransform,
293
- );
294
- // Collapse any no-ops at the end of the transformation list
295
- return TransformOps._collapseFinalNoOps(results);
296
- },
297
-
298
- _collapseFinalNoOps: function (transforms) {
299
- // Collapse no-op transformations at the end of the list
300
- if (transforms.length && TransformOps.isNoOp(_.last(transforms))) {
301
- return _.initial(transforms);
302
- }
303
- return transforms;
304
- },
305
-
306
- _appendAndCollapseLastTwo: function (transformList, newTransform) {
307
- if (!transformList.length) {
308
- return [newTransform];
309
- }
310
- const collapsed = TransformOps.collapse(
311
- _.last(transformList),
312
- newTransform,
313
- );
314
- return _.initial(transformList).concat(collapsed);
315
- },
316
-
317
- isNoOp: function (transform) {
318
- return Transformations[transform.type].isNoOp(transform);
319
- },
320
-
321
- collapse: function (transform1, transform2) {
322
- // We can only collapse transforms that have the same type
323
- if (transform1.type !== transform2.type) {
324
- return [transform1, transform2];
325
- }
326
-
327
- // Clicking the button again removes empty transformations
328
- if (
329
- TransformOps.isEmpty(transform1) &&
330
- TransformOps.isEmpty(transform2)
331
- ) {
332
- return [];
333
- }
334
-
335
- // Don't collapse invalid transformations otherwise
336
- if (
337
- !TransformOps.isValid(transform1) ||
338
- !TransformOps.isValid(transform2)
339
- ) {
340
- return [transform1, transform2];
341
- }
342
-
343
- return TransformOps._collapseValidMonotypedTransforms(
344
- transform1,
345
- transform2,
346
- );
347
- },
348
-
349
- isValid: function (transform: TransformerTransformation): boolean {
350
- // $FlowFixMe[prop-missing]
351
- // $FlowFixMe[incompatible-call]
352
- return Transformations[transform.type].isValid(transform);
353
- },
354
-
355
- isEmpty: function (transform: TransformerTransformation): boolean {
356
- // $FlowFixMe[prop-missing]
357
- // $FlowFixMe[incompatible-call]
358
- return Transformations[transform.type].isEmpty(transform);
359
- },
360
-
361
- _collapseValidMonotypedTransforms: function (transform1, transform2) {
362
- let collapsed = Transformations[transform1.type].collapse(
363
- transform1,
364
- transform2,
365
- );
366
- if (collapsed) {
367
- // Force all answers into an array
368
- if (!_.isArray(collapsed)) {
369
- collapsed = [collapsed];
370
- }
371
- // Add types to all transforms in the answer
372
- _.each(collapsed, function (transform) {
373
- transform.type = transform1.type;
374
- });
375
- return collapsed;
376
- }
377
- // These transforms can't be collapsed together
378
- return [transform1, transform2];
379
- },
380
-
381
- toTeX: function (transform) {
382
- return Transformations[transform.type].toTeX(transform);
383
- },
384
-
385
- /* A react representation of this transform object */
386
- ListItem: createReactClass({
387
- displayName: "ListItem",
388
-
389
- render: function () {
390
- if (this.props.mode === "dynamic") {
391
- return <div>{TransformOps.toTeX(this.props.transform)}</div>;
392
- }
393
- if (this.props.mode === "interactive") {
394
- const TransformClass =
395
- Transformations[this.props.transform.type].Input;
396
- return (
397
- <TransformClass
398
- // eslint-disable-next-line react/no-string-refs
399
- ref="transform"
400
- onChange={this.handleChange}
401
- onFocus={this.props.onFocus}
402
- onBlur={this.props.onBlur}
403
- keypadElement={this.props.keypadElement}
404
- apiOptions={this.props.apiOptions}
405
- {...this.props.transform}
406
- />
407
- );
408
- }
409
- throw new PerseusError(
410
- "Invalid mode: " + this.props.mode,
411
- Errors.InvalidInput,
412
- );
413
- },
414
-
415
- value: function () {
416
- if (this.props.mode === "interactive") {
417
- return _.extend(
418
- {
419
- type: this.props.transform.type,
420
- },
421
- // eslint-disable-next-line react/no-string-refs
422
- this.refs.transform.value(),
423
- );
424
- }
425
- return this.props.transform;
426
- },
427
-
428
- handleChange: _.debounce(function (callback) {
429
- this.props.onChange(this.value(), callback);
430
- }, RENDER_TRANSFORM_DELAY_IN_MS),
431
-
432
- /* InputPath API: depending on the API call, this could involve simply
433
- * navigating to the right ref and calling the function on that
434
- * component, or threading the call down and returning the result. */
435
- _getComponentAtPath: function (path) {
436
- // eslint-disable-next-line react/no-string-refs
437
- const transform = this.refs.transform;
438
- const ref = _.head(path);
439
- return transform.refs[ref];
440
- },
441
-
442
- focus: function () {
443
- // eslint-disable-next-line react/no-string-refs
444
- const transform = this.refs.transform;
445
- const path = _.head(transform.getInputPaths());
446
- if (path) {
447
- this.focusInputPath(path);
448
- }
449
- },
450
-
451
- focusInputPath: function (path) {
452
- this._getComponentAtPath(path).focus();
453
- },
454
-
455
- blurInputPath: function (path) {
456
- this._getComponentAtPath(path).blur();
457
- },
458
-
459
- getDOMNodeForPath: function (path) {
460
- return ReactDOM.findDOMNode(this._getComponentAtPath(path));
461
- },
462
-
463
- getGrammarTypeForPath: function (path) {
464
- return "number";
465
- },
466
-
467
- setInputValue: function (path, value: string | number, cb) {
468
- // `value` comes in as a string on mobile, but we need a number
469
- // We let through the empty string so that "Clear" works -- in
470
- // that case, the transformer widget will just act as if there is
471
- // no input, which is what we want.
472
- if (value.length) {
473
- value = parseFloat(value);
474
- if (isNaN(value)) {
475
- return;
476
- }
477
- }
478
- // eslint-disable-next-line react/no-string-refs
479
- this.refs.transform.setInputValue(path, value, cb);
480
- },
481
-
482
- getInputPaths: function () {
483
- // If we're in dynamic mode, then the list items are made up of
484
- // static text.
485
- if (this.props.mode === "dynamic") {
486
- return [];
487
- }
488
- // eslint-disable-next-line react/no-string-refs
489
- return this.refs.transform.getInputPaths();
490
- },
491
- }),
492
- };
493
-
494
- const Transformations = {
495
- translation: {
496
- // I18N: As in the command, "Translate the polygon"
497
- verbName: i18n._("Translate"),
498
- nounName: i18n._("Translation"),
499
- lowerNounName: i18n._("translation"),
500
- apply: function (transform: TranslationTransformation) {
501
- return function (coord) {
502
- return kvector.add(coord, transform.vector);
503
- };
504
- },
505
- isValid: function (transform: TranslationTransformation) {
506
- return (
507
- _.isFinite(transform.vector[0]) &&
508
- _.isFinite(transform.vector[1])
509
- );
510
- },
511
- isEmpty: function (transform: TranslationTransformation) {
512
- return transform.vector[0] === null && transform.vector[1] === null;
513
- },
514
- isNoOp: function (transform) {
515
- return kvector.equal(transform.vector, [0, 0]);
516
- },
517
- collapse: function (
518
- transform1: TranslationTransformation,
519
- transform2: TranslationTransformation,
520
- ) {
521
- return {
522
- vector: kvector.add(transform1.vector, transform2.vector),
523
- };
524
- },
525
- toTeX: function (transform: TranslationTransformation) {
526
- // I18N: As in the command, "Translation by <3, 1>"
527
- return i18n.$_("Translation by %(vector)s", {
528
- vector: texFromVector(transform.vector),
529
- });
530
- },
531
- Input: class extends React.Component<$FlowFixMe, $FlowFixMe> {
532
- state = {
533
- vector: this.props.vector || [null, null],
534
- };
535
-
536
- componentDidUpdate(prevProps) {
537
- if (!deepEq(this.props, prevProps)) {
538
- this.setState({vector: this.props.vector});
539
- }
540
- }
541
-
542
- render(): React.Node {
543
- const InputComponent = inputComponentForApiOptions(
544
- this.props.apiOptions,
545
- );
546
- const {TeX} = getDependencies();
547
-
548
- const vector = [
549
- <TeX key="<">{i18n.doNotTranslate("\\langle")}</TeX>,
550
- <InputComponent
551
- key="vec_x"
552
- // eslint-disable-next-line react/no-string-refs
553
- ref="x"
554
- placeholder={0}
555
- value={this.state.vector[0]}
556
- useArrowKeys={true}
557
- onChange={(val0) => {
558
- const val1 = this.state.vector[1];
559
- this.setState({vector: [val0, val1]}, () => {
560
- this.props.onChange();
561
- });
562
- }}
563
- onFocus={_.partial(this.props.onFocus, "x")}
564
- onBlur={_.partial(this.props.onBlur, "x")}
565
- keypadElement={this.props.keypadElement}
566
- />,
567
- <TeX key=",">{", {}"}</TeX>,
568
- <InputComponent
569
- key="vec_y"
570
- // eslint-disable-next-line react/no-string-refs
571
- ref="y"
572
- placeholder={0}
573
- value={this.state.vector[1]}
574
- useArrowKeys={true}
575
- onChange={(val1) => {
576
- const val0 = this.state.vector[0];
577
- this.setState({vector: [val0, val1]}, () => {
578
- this.props.onChange();
579
- });
580
- }}
581
- onFocus={_.partial(this.props.onFocus, "y")}
582
- onBlur={_.partial(this.props.onBlur, "y")}
583
- keypadElement={this.props.keypadElement}
584
- />,
585
- <TeX key=">">{i18n.doNotTranslate("\\rangle")}</TeX>,
586
- ];
587
- return (
588
- <div>
589
- {i18n.$_("Translation by %(vector)s", {vector: vector})}
590
- </div>
591
- );
592
- }
593
-
594
- value = () => {
595
- // eslint-disable-next-line react/no-string-refs
596
- const x = this.refs.x.getValue();
597
- // eslint-disable-next-line react/no-string-refs
598
- const y = this.refs.y.getValue();
599
- return {
600
- vector: [x, y],
601
- };
602
- };
603
-
604
- /* InputPath API */
605
- setInputValue = (path, value, cb) => {
606
- const id = _.first(path);
607
- const vector = _.clone(this.state.vector);
608
- if (id === "x") {
609
- vector[0] = value;
610
- } else if (id === "y") {
611
- vector[1] = value;
612
- }
613
- this.setState({vector: vector}, () => {
614
- this.props.onChange(cb);
615
- });
616
- };
617
-
618
- getInputPaths = () => {
619
- return [["x"], ["y"]];
620
- };
621
- },
622
- },
623
-
624
- rotation: {
625
- // I18N: As in the command, "Rotate the polygon"
626
- verbName: i18n._("Rotate"),
627
- nounName: i18n._("Rotation"),
628
- lowerNounName: i18n._("rotation"),
629
- apply: function (transform: RotationTransformation) {
630
- return function (coord) {
631
- return kpoint.rotateDeg(
632
- coord,
633
- transform.angleDeg,
634
- transform.center,
635
- );
636
- };
637
- },
638
- isValid: function (transform: RotationTransformation) {
639
- return (
640
- _.isFinite(transform.angleDeg) &&
641
- _.isFinite(transform.center[0]) &&
642
- _.isFinite(transform.center[1])
643
- );
644
- },
645
- isEmpty: function (transform: RotationTransformation) {
646
- return (
647
- transform.angleDeg === null &&
648
- transform.center[0] === null &&
649
- transform.center[1] === null
650
- );
651
- },
652
- isNoOp: function (transform: RotationTransformation) {
653
- return knumber.equal(transform.angleDeg, 0);
654
- },
655
- collapse: function (
656
- transform1: RotationTransformation,
657
- transform2: RotationTransformation,
658
- ) {
659
- if (!kpoint.equal(transform1.center, transform2.center)) {
660
- return false;
661
- }
662
- return {
663
- center: transform1.center,
664
- angleDeg: transform1.angleDeg + transform2.angleDeg,
665
- };
666
- },
667
- toTeX: function (transform: RotationTransformation) {
668
- return i18n.$_("Rotation by %(degrees)s about %(point)s", {
669
- degrees: texFromAngleDeg(transform.angleDeg),
670
- point: texFromPoint(transform.center),
671
- });
672
- },
673
- Input: class extends React.Component<$FlowFixMe, $FlowFixMe> {
674
- state = {
675
- center: this.props.center || [null, null],
676
- angleDeg: this.props.angleDeg || null,
677
- };
678
-
679
- componentDidUpdate(prevProps) {
680
- if (!deepEq(this.props, prevProps)) {
681
- this.setState({
682
- center: this.props.center,
683
- angleDeg: this.props.angleDeg,
684
- });
685
- }
686
- }
687
-
688
- render(): React.Node {
689
- const InputComponent = inputComponentForApiOptions(
690
- this.props.apiOptions,
691
- );
692
- const {TeX} = getDependencies();
693
-
694
- const point = [
695
- <TeX key="(">(</TeX>,
696
- <InputComponent
697
- key="X"
698
- // eslint-disable-next-line react/no-string-refs
699
- ref="centerX"
700
- placeholder={0}
701
- value={this.state.center[0]}
702
- useArrowKeys={true}
703
- onChange={(val0) => {
704
- const val1 = this.state.center[1];
705
- this.setState({center: [val0, val1]}, () => {
706
- this.props.onChange();
707
- });
708
- }}
709
- onFocus={_.partial(this.props.onFocus, "centerX")}
710
- onBlur={_.partial(this.props.onBlur, "centerX")}
711
- keypadElement={this.props.keypadElement}
712
- />,
713
- <TeX key=",">{", {}"}</TeX>,
714
- <InputComponent
715
- key="Y"
716
- // eslint-disable-next-line react/no-string-refs
717
- ref="centerY"
718
- placeholder={0}
719
- value={this.state.center[1]}
720
- useArrowKeys={true}
721
- onChange={(val1) => {
722
- const val0 = this.state.center[0];
723
- this.setState({center: [val0, val1]}, () => {
724
- this.props.onChange();
725
- });
726
- }}
727
- onFocus={_.partial(this.props.onFocus, "centerY")}
728
- onBlur={_.partial(this.props.onBlur, "centerY")}
729
- keypadElement={this.props.keypadElement}
730
- />,
731
- <TeX key=")">)</TeX>,
732
- ];
733
- const degrees = [
734
- <InputComponent
735
- key="deg"
736
- // eslint-disable-next-line react/no-string-refs
737
- ref="angleDeg"
738
- placeholder={0}
739
- value={this.state.angleDeg}
740
- useArrowKeys={true}
741
- onChange={(val) => {
742
- this.setState({angleDeg: val}, () => {
743
- this.props.onChange();
744
- });
745
- }}
746
- onFocus={_.partial(this.props.onFocus, "angleDeg")}
747
- onBlur={_.partial(this.props.onBlur, "angleDeg")}
748
- keypadElement={this.props.keypadElement}
749
- />,
750
- DEGREE_SIGN,
751
- ];
752
- // I18N: %(point)s must come before %(degrees)s in this phrase
753
- const text = i18n.$_(
754
- "Rotation about %(point)s by %(degrees)s",
755
- {
756
- point,
757
- degrees,
758
- },
759
- );
760
-
761
- return <div>{text}</div>;
762
- }
763
-
764
- value = () => {
765
- // eslint-disable-next-line react/no-string-refs
766
- const angleDeg = this.refs.angleDeg.getValue();
767
- // eslint-disable-next-line react/no-string-refs
768
- const centerX = this.refs.centerX.getValue();
769
- // eslint-disable-next-line react/no-string-refs
770
- const centerY = this.refs.centerY.getValue();
771
- return {
772
- angleDeg: angleDeg,
773
- center: [centerX, centerY],
774
- };
775
- };
776
-
777
- /* InputPath API */
778
- setInputValue = (path, value, cb) => {
779
- const id = _.first(path);
780
- let angleDeg = _.clone(this.state.angleDeg);
781
- const center = _.clone(this.state.center);
782
- if (id === "angleDeg") {
783
- angleDeg = value;
784
- } else if (id === "centerX") {
785
- center[0] = value;
786
- } else if (id === "centerY") {
787
- center[1] = value;
788
- }
789
- this.setState({angleDeg: angleDeg, center: center}, () => {
790
- this.props.onChange(cb);
791
- });
792
- };
793
-
794
- getInputPaths = () => {
795
- return [["centerX"], ["centerY"], ["angleDeg"]];
796
- };
797
- },
798
- },
799
-
800
- reflection: {
801
- // I18N: As in the command, "Reflect the polygon"
802
- verbName: i18n._("Reflect"),
803
- nounName: i18n._("Reflection"),
804
- lowerNounName: i18n._("reflection"),
805
- apply: function (transform: ReflectionTransformation) {
806
- return function (coord) {
807
- return kpoint.reflectOverLine(coord, transform.line);
808
- };
809
- },
810
- isValid: function (transform: ReflectionTransformation) {
811
- // A bit hacky, but we'll also define reflecting over a
812
- // single point as a no-op, to avoid NaN fun.
813
- return (
814
- _.all(_.flatten(transform.line), _.isFinite) &&
815
- !kpoint.equal(transform.line[0], transform.line[1])
816
- );
817
- },
818
- isEmpty: function (transform: ReflectionTransformation) {
819
- return _.all(_.flatten(transform.line), _.isNull);
820
- },
821
- isNoOp: function (transform: ReflectionTransformation) {
822
- // Invalid transforms are implicitly no-ops, so we don't
823
- // have to catch that case here.
824
- return false;
825
- },
826
- collapse: function (
827
- transform1: ReflectionTransformation,
828
- transform2: ReflectionTransformation,
829
- ) {
830
- // $FlowFixMe[incompatible-call]
831
- if (!kline.equal(transform1.line, transform2.line)) {
832
- return false;
833
- }
834
- return [];
835
- },
836
- toTeX: function (transform) {
837
- const point1 = transform.line[0];
838
- const point2 = transform.line[1];
839
- return i18n.$_(
840
- "Reflection over the line from %(point1)s to %(point2)s",
841
- {
842
- point1: texFromPoint(point1),
843
- point2: texFromPoint(point2),
844
- },
845
- );
846
- },
847
- Input: class extends React.Component<$FlowFixMe, $FlowFixMe> {
848
- state = {
849
- line: this.props.line || [
850
- [null, null],
851
- [null, null],
852
- ],
853
- };
854
-
855
- componentDidUpdate(prevProps) {
856
- if (!deepEq(this.props, prevProps)) {
857
- this.setState({line: this.props.line});
858
- }
859
- }
860
-
861
- render(): React.Node {
862
- const InputComponent = inputComponentForApiOptions(
863
- this.props.apiOptions,
864
- );
865
- const {TeX} = getDependencies();
866
-
867
- const point1 = [
868
- <TeX key="(">(</TeX>,
869
- <InputComponent
870
- key="x1"
871
- // eslint-disable-next-line react/no-string-refs
872
- ref="x1"
873
- value={this.state.line[0][0]}
874
- useArrowKeys={true}
875
- // eslint-disable-next-line react/jsx-no-bind
876
- onChange={this.changePoint.bind(this, 0, 0)}
877
- onFocus={_.partial(this.props.onFocus, "x1")}
878
- onBlur={_.partial(this.props.onBlur, "x1")}
879
- keypadElement={this.props.keypadElement}
880
- />,
881
- <TeX key=",">{", {}"}</TeX>,
882
- <InputComponent
883
- key="y1"
884
- // eslint-disable-next-line react/no-string-refs
885
- ref="y1"
886
- value={this.state.line[0][1]}
887
- useArrowKeys={true}
888
- // eslint-disable-next-line react/jsx-no-bind
889
- onChange={this.changePoint.bind(this, 0, 1)}
890
- onFocus={_.partial(this.props.onFocus, "y1")}
891
- onBlur={_.partial(this.props.onBlur, "y1")}
892
- keypadElement={this.props.keypadElement}
893
- />,
894
- <TeX key=")">)</TeX>,
895
- ];
896
- const point2 = [
897
- <TeX key="(">(</TeX>,
898
- <InputComponent
899
- key="x2"
900
- // eslint-disable-next-line react/no-string-refs
901
- ref="x2"
902
- value={this.state.line[1][0]}
903
- useArrowKeys={true}
904
- // eslint-disable-next-line react/jsx-no-bind
905
- onChange={this.changePoint.bind(this, 1, 0)}
906
- onFocus={_.partial(this.props.onFocus, "x2")}
907
- onBlur={_.partial(this.props.onBlur, "x2")}
908
- keypadElement={this.props.keypadElement}
909
- />,
910
- <TeX key=",">{", {}"}</TeX>,
911
- <InputComponent
912
- key="y2"
913
- // eslint-disable-next-line react/no-string-refs
914
- ref="y2"
915
- value={this.state.line[1][1]}
916
- useArrowKeys={true}
917
- // eslint-disable-next-line react/jsx-no-bind
918
- onChange={this.changePoint.bind(this, 1, 1)}
919
- onFocus={_.partial(this.props.onFocus, "y2")}
920
- onBlur={_.partial(this.props.onBlur, "y2")}
921
- keypadElement={this.props.keypadElement}
922
- />,
923
- <TeX key=")">)</TeX>,
924
- ];
925
- return (
926
- <div>
927
- {i18n.$_(
928
- "Reflection over the line from " +
929
- "%(point1)s to %(point2)s",
930
- {point1, point2},
931
- )}
932
- </div>
933
- );
934
- }
935
-
936
- changePoint = (i, j, val, cb) => {
937
- const line = _.map(this.state.line, _.clone);
938
- line[i][j] = val;
939
- this.setState({line: line}, () => {
940
- this.props.onChange(cb);
941
- });
942
- };
943
-
944
- value = () => {
945
- // eslint-disable-next-line react/no-string-refs
946
- const x1 = this.refs.x1.getValue();
947
- // eslint-disable-next-line react/no-string-refs
948
- const y1 = this.refs.y1.getValue();
949
- // eslint-disable-next-line react/no-string-refs
950
- const x2 = this.refs.x2.getValue();
951
- // eslint-disable-next-line react/no-string-refs
952
- const y2 = this.refs.y2.getValue();
953
- return {
954
- line: [
955
- [x1, y1],
956
- [x2, y2],
957
- ],
958
- };
959
- };
960
-
961
- /* InputPath API */
962
- setInputValue = (path, value, cb) => {
963
- const id = _.first(path);
964
- let j;
965
- if (id[0] === "x") {
966
- j = 0;
967
- } else if (id[0] === "y") {
968
- j = 1;
969
- }
970
- let i;
971
- if (id[1] === "1") {
972
- i = 0;
973
- } else if (id[1] === "2") {
974
- i = 1;
975
- }
976
- this.changePoint(i, j, value, cb);
977
- };
978
-
979
- getInputPaths = () => {
980
- return [["x1"], ["y1"], ["x2"], ["y2"]];
981
- };
982
- },
983
- },
984
-
985
- dilation: {
986
- // I18N: As in the command, "Dilate the polygon"
987
- verbName: i18n._("Dilate"),
988
- nounName: i18n._("Dilation"),
989
- lowerNounName: i18n._("dilation"),
990
- apply: function (transform: DilationTransformation) {
991
- return function (coord) {
992
- return dilatePointFromCenter(
993
- coord,
994
- transform.center,
995
- transform.scale,
996
- );
997
- };
998
- },
999
- isValid: function (transform: DilationTransformation) {
1000
- return (
1001
- _.isFinite(transform.scale) &&
1002
- _.isFinite(transform.center[0]) &&
1003
- _.isFinite(transform.center[1])
1004
- );
1005
- },
1006
- isEmpty: function (transform: DilationTransformation) {
1007
- return (
1008
- transform.scale === null &&
1009
- transform.center[0] === null &&
1010
- transform.center[1] === null
1011
- );
1012
- },
1013
- isNoOp: function (transform: DilationTransformation) {
1014
- return knumber.equal(transform.scale, 1);
1015
- },
1016
- collapse: function (
1017
- transform1: DilationTransformation,
1018
- transform2: DilationTransformation,
1019
- ) {
1020
- if (!kpoint.equal(transform1.center, transform2.center)) {
1021
- return false;
1022
- }
1023
- return {
1024
- center: transform1.center,
1025
- scale: transform1.scale * transform2.scale,
1026
- };
1027
- },
1028
- toTeX: function (transform: DilationTransformation) {
1029
- const scaleString = stringFromFraction(transform.scale);
1030
- return i18n.$_("Dilation of scale %(scale)s about %(point)s", {
1031
- scale: scaleString,
1032
- point: texFromPoint(transform.center),
1033
- });
1034
- },
1035
- Input: class extends React.Component<$FlowFixMe, $FlowFixMe> {
1036
- state = {
1037
- center: this.props.center || [null, null],
1038
- scale: this.props.scale || null,
1039
- };
1040
-
1041
- componentDidUpdate(prevProps) {
1042
- if (!deepEq(this.props, prevProps)) {
1043
- this.setState({
1044
- center: this.props.center,
1045
- scale: this.props.scale,
1046
- });
1047
- }
1048
- }
1049
-
1050
- render(): React.Node {
1051
- const InputComponent = inputComponentForApiOptions(
1052
- this.props.apiOptions,
1053
- );
1054
- const {TeX} = getDependencies();
1055
-
1056
- const point = [
1057
- <TeX key="(">(</TeX>,
1058
- <InputComponent
1059
- key="point_x"
1060
- // eslint-disable-next-line react/no-string-refs
1061
- ref="x"
1062
- placeholder={0}
1063
- value={this.state.center[0]}
1064
- useArrowKeys={true}
1065
- onChange={(val0) => {
1066
- const val1 = this.state.center[1];
1067
- this.setState({center: [val0, val1]}, () => {
1068
- this.props.onChange();
1069
- });
1070
- }}
1071
- onFocus={_.partial(this.props.onFocus, "x")}
1072
- onBlur={_.partial(this.props.onBlur, "x")}
1073
- keypadElement={this.props.keypadElement}
1074
- />,
1075
- <TeX key=",">{", {}"}</TeX>,
1076
- <InputComponent
1077
- key="point_y"
1078
- // eslint-disable-next-line react/no-string-refs
1079
- ref="y"
1080
- placeholder={0}
1081
- value={this.state.center[1]}
1082
- useArrowKeys={true}
1083
- onChange={(val1) => {
1084
- const val0 = this.state.center[0];
1085
- this.setState({center: [val0, val1]}, () => {
1086
- this.props.onChange();
1087
- });
1088
- }}
1089
- onFocus={_.partial(this.props.onFocus, "y")}
1090
- onBlur={_.partial(this.props.onBlur, "y")}
1091
- keypadElement={this.props.keypadElement}
1092
- />,
1093
- <TeX key=")">)</TeX>,
1094
- ];
1095
- const scale = (
1096
- <InputComponent
1097
- // eslint-disable-next-line react/no-string-refs
1098
- ref="scale"
1099
- placeholder={1}
1100
- value={this.state.scale}
1101
- useArrowKeys={true}
1102
- onChange={(val) => {
1103
- this.setState({scale: val}, () => {
1104
- this.props.onChange();
1105
- });
1106
- }}
1107
- onFocus={_.partial(this.props.onFocus, "scale")}
1108
- onBlur={_.partial(this.props.onBlur, "scale")}
1109
- keypadElement={this.props.keypadElement}
1110
- />
1111
- );
1112
- return (
1113
- <div>
1114
- {i18n.$_("Dilation about %(point)s by %(scale)s", {
1115
- point,
1116
- scale,
1117
- })}
1118
- </div>
1119
- );
1120
- }
1121
-
1122
- value = () => {
1123
- // eslint-disable-next-line react/no-string-refs
1124
- const scale = this.refs.scale.getValue();
1125
- // eslint-disable-next-line react/no-string-refs
1126
- const x = this.refs.x.getValue();
1127
- // eslint-disable-next-line react/no-string-refs
1128
- const y = this.refs.y.getValue();
1129
- return {
1130
- scale: scale,
1131
- center: [x, y],
1132
- };
1133
- };
1134
-
1135
- /* InputPath API */
1136
- setInputValue = (path, value, cb) => {
1137
- const id = _.first(path);
1138
- let scale = this.state.scale;
1139
- const center = _.clone(this.state.center);
1140
- if (id === "x") {
1141
- center[0] = value;
1142
- } else if (id === "y") {
1143
- center[1] = value;
1144
- } else if (id === "scale") {
1145
- scale = value;
1146
- }
1147
- this.setState({scale: scale, center: center}, () => {
1148
- this.props.onChange(cb);
1149
- });
1150
- };
1151
-
1152
- getInputPaths = () => {
1153
- return [["x"], ["y"], ["scale"]];
1154
- };
1155
- },
1156
- },
1157
- };
1158
-
1159
- /* Various functions to deal with different shape types */
1160
- const ShapeTypes = {
1161
- getPointCountForType: function (type) {
1162
- const splitType = type.split("-");
1163
- if (splitType[0] === "polygon") {
1164
- return splitType[1] || 3;
1165
- }
1166
- if (splitType[0] === "line" || splitType[0] === "lineSegment") {
1167
- return 2;
1168
- }
1169
- if (splitType[0] === "angle") {
1170
- return 3;
1171
- }
1172
- if (splitType[0] === "circle") {
1173
- return 2;
1174
- }
1175
- if (splitType[0] === "point") {
1176
- return 1;
1177
- }
1178
- },
1179
-
1180
- addMovableShape: function (graphie, options) {
1181
- if (options.editable && options.translatable) {
1182
- throw new PerseusError(
1183
- "It doesn't make sense to have a movable shape " +
1184
- "where you can stretch the points and translate them " +
1185
- "simultaneously. options: " +
1186
- JSON.stringify(options),
1187
- Errors.InvalidInput,
1188
- );
1189
- }
1190
-
1191
- const points = _.map(options.shape.coords, function (coord) {
1192
- let isMoving = false;
1193
- let previousCoord = coord;
1194
-
1195
- const onMove = function (x, y) {
1196
- if (!isMoving) {
1197
- previousCoord = currentPoint.coord;
1198
- isMoving = true;
1199
- }
1200
-
1201
- let moveVector = kvector.subtract([x, y], currentPoint.coord);
1202
-
1203
- // Translate from (x, y) semantics to (dX, dY) semantics
1204
- // This is more useful for translations on multiple points,
1205
- // where we care about how the points moved, not where any
1206
- // individual point ended up
1207
- if (options.onMove) {
1208
- moveVector = options.onMove(moveVector[0], moveVector[1]);
1209
- }
1210
-
1211
- // Perform a translation on all points in this shape when
1212
- // any point moves
1213
- if (options.translatable) {
1214
- _.each(points, function (point) {
1215
- // The point itself will be updated by the
1216
- // movablePoint class, so only translate the other
1217
- // points
1218
- if (point !== currentPoint) {
1219
- point.setCoord(
1220
- kvector.add(point.coord, moveVector),
1221
- );
1222
- }
1223
- });
1224
- }
1225
-
1226
- // Update our shape and our currentPoint
1227
- // Without this, some shapes (circles, angles) appear
1228
- // "bouncy" as they are updated with currentPoint at the
1229
- // current mouse coordinate (oldCoord), rather than newCoord
1230
- const oldCoord = currentPoint.coord;
1231
- const newCoord = kvector.add(currentPoint.coord, moveVector);
1232
- // Temporarily change our coordinate so that
1233
- // shape.update() sees the new coordinate
1234
- currentPoint.coord = newCoord;
1235
- shape.update();
1236
- // ...But don't break onMove, which assumes it
1237
- // is the only thing changing our coord
1238
- currentPoint.coord = oldCoord;
1239
- return newCoord;
1240
- };
1241
-
1242
- const onMoveEnd = function () {
1243
- // onMove isn't guaranteed to be called before onMoveEnd, so
1244
- // we have to take into account that we may not have moved and
1245
- // set previousCoord.
1246
- // $FlowFixMe[prop-missing]
1247
- if (options.onMoveEnd && isMoving) {
1248
- isMoving = false;
1249
- // We don't use the supplied x and y parameters here
1250
- // because MovablePoint's onMoveEnd semantics suck.
1251
- // It returns the mouseX, mouseY without processing them
1252
- // through onMove, leaving us with weird fractional moves
1253
- const change = kvector.subtract(
1254
- currentPoint.coord,
1255
- previousCoord,
1256
- );
1257
- options.onMoveEnd(change[0], change[1]);
1258
- }
1259
- shape.update();
1260
- };
1261
-
1262
- const currentPoint = graphie.addMovablePoint({
1263
- coord: coord,
1264
- normalStyle: options.normalPointStyle,
1265
- highlightStyle: options.highlightPointStyle,
1266
- constraints: {
1267
- fixed: !options.translatable && !options.editable,
1268
- },
1269
- visible: options.showPoints,
1270
- // $FlowFixMe[prop-missing]
1271
- snapX: (options.snap && options.snap[0]) || 0,
1272
- // $FlowFixMe[prop-missing]
1273
- snapY: (options.snap && options.snap[1]) || 0,
1274
- bounded: false, // Don't bound it when placing it on the graph
1275
- onMove: onMove,
1276
- onMoveEnd: onMoveEnd,
1277
- });
1278
-
1279
- // Bound it when moving
1280
- // We can't set this earlier, because doing so would mean any
1281
- // points outside of the graph would be moved into a moved into
1282
- // a position that doesn't preserve the shape
1283
- currentPoint.bounded = true;
1284
-
1285
- return currentPoint;
1286
- });
1287
-
1288
- const shape = ShapeTypes.addShape(graphie, options, points);
1289
- const removeShapeWithoutPoints = shape.remove;
1290
- shape.remove = function () {
1291
- removeShapeWithoutPoints.apply(shape);
1292
- _.invoke(points, "remove");
1293
- };
1294
- return shape;
1295
- },
1296
-
1297
- addShape: function (graphie, options, points) {
1298
- points = points || options.shape.coords;
1299
-
1300
- const types = ShapeTypes._typesOf(options.shape);
1301
- const typeOptions =
1302
- options.shape.options || ShapeTypes.defaultOptions(types);
1303
-
1304
- const shapes = ShapeTypes._mapTypes(
1305
- types,
1306
- points,
1307
- function (type, points, i) {
1308
- const shapeOptions = _.extend({}, options, typeOptions[i]);
1309
- return ShapeTypes._addType(graphie, type, points, shapeOptions);
1310
- },
1311
- );
1312
-
1313
- const updateFuncs = _.filter(_.pluck(shapes, "update"), _.identity);
1314
- const update = function () {
1315
- _.invoke(updateFuncs, "call");
1316
- };
1317
-
1318
- const removeFuncs = _.filter(_.pluck(shapes, "remove"), _.identity);
1319
- const remove = function () {
1320
- _.invoke(removeFuncs, "call");
1321
- };
1322
-
1323
- const getOptions = function () {
1324
- return _.map(shapes, function (shape) {
1325
- if (shape.getOptions) {
1326
- return shape.getOptions();
1327
- }
1328
- return {};
1329
- });
1330
- };
1331
-
1332
- const toJSON = function () {
1333
- const coords = _.map(points, function (pt) {
1334
- if (_.isArray(pt)) {
1335
- return pt;
1336
- }
1337
- return pt.coord;
1338
- });
1339
- return {
1340
- type: types,
1341
- coords: coords,
1342
- options: getOptions(),
1343
- };
1344
- };
1345
-
1346
- return {
1347
- type: types,
1348
- points: points,
1349
- update: update,
1350
- remove: remove,
1351
- toJSON: toJSON,
1352
- getOptions: getOptions,
1353
- };
1354
- },
1355
-
1356
- equal: function (shape1, shape2) {
1357
- const types1 = ShapeTypes._typesOf(shape1);
1358
- const types2 = ShapeTypes._typesOf(shape2);
1359
- if (types1.length !== types2.length) {
1360
- return false;
1361
- }
1362
- const shapes1 = ShapeTypes._mapTypes(
1363
- types1,
1364
- shape1.coords,
1365
- ShapeTypes._combine,
1366
- );
1367
- const shapes2 = ShapeTypes._mapTypes(
1368
- types2,
1369
- shape2.coords,
1370
- ShapeTypes._combine,
1371
- );
1372
- return _.all(
1373
- _.map(shapes1, function (partialShape1, i) {
1374
- const partialShape2 = shapes2[i];
1375
- if (partialShape1.type !== partialShape2.type) {
1376
- return false;
1377
- }
1378
- return ShapeTypes._forType(partialShape1.type).equal(
1379
- partialShape1.coords,
1380
- partialShape2.coords,
1381
- );
1382
- }),
1383
- );
1384
- },
1385
-
1386
- _typesOf: function (shape) {
1387
- let types = shape.type;
1388
- if (!_.isArray(types)) {
1389
- types = [types];
1390
- }
1391
- return _.map(types, function (type) {
1392
- if (type === "polygon") {
1393
- return "polygon-3";
1394
- }
1395
- return type;
1396
- });
1397
- },
1398
-
1399
- defaultOptions: function (types) {
1400
- return _.map(types, function (type) {
1401
- const typeDefaultOptions = ShapeTypes._forType(type).defaultOptions;
1402
- return _.extend({}, typeDefaultOptions);
1403
- });
1404
- },
1405
-
1406
- _forType: function (type) {
1407
- const baseType = type.split("-")[0];
1408
- return ShapeTypes[baseType];
1409
- },
1410
-
1411
- _mapTypes: function (types, points, func, context) {
1412
- return _.map(types, function (type, i) {
1413
- const pointCount = ShapeTypes.getPointCountForType(type);
1414
- const currentPoints = _.first(points, pointCount);
1415
- points = _.rest(points, pointCount);
1416
- // $FlowFixMe[extra-arg]
1417
- return func.call(context, type, currentPoints, i);
1418
- });
1419
- },
1420
-
1421
- _addType: function (graphie, type, points, options) {
1422
- const lineCoords = _.isArray(points[0])
1423
- ? {
1424
- coordA: points[0],
1425
- coordZ: points[1],
1426
- }
1427
- : {
1428
- pointA: points[0],
1429
- pointZ: points[1],
1430
- };
1431
-
1432
- type = type.split("-")[0];
1433
- if (type === "polygon") {
1434
- const polygon = graphie.addMovablePolygon(
1435
- _.extend({}, options, {
1436
- fixed: !options.editable,
1437
- snapX: (options.snap && options.snap[0]) || 0,
1438
- snapY: (options.snap && options.snap[1]) || 0,
1439
- points: points,
1440
- constrainToGraph: false,
1441
- }),
1442
- );
1443
- return {
1444
- update: polygon.transform.bind(polygon),
1445
- remove: polygon.remove.bind(polygon),
1446
- };
1447
- }
1448
- if (type === "line" || type === "lineSegment") {
1449
- const line = graphie.addMovableLineSegment(
1450
- _.extend({}, options, lineCoords, {
1451
- movePointsWithLine: true,
1452
- fixed: true,
1453
- constraints: {
1454
- fixed: true,
1455
- },
1456
- extendLine: type === "line",
1457
- }),
1458
- );
1459
-
1460
- // TODO(jack): Hide points on uneditable lines when translation
1461
- // is a vector.
1462
- // We can't just remove the points yet, because they are the
1463
- // translation handle for the line.
1464
- return {
1465
- update: line.transform.bind(line, true),
1466
- remove: line.remove.bind(line),
1467
- };
1468
- }
1469
- if (type === "angle") {
1470
- // If this angle is editable, we want to be able to make angles
1471
- // both larger and smaller than 180 degrees.
1472
- // If this angle is not editable, it should always maintain
1473
- // it's angle measure, even if it is reflected (causing the
1474
- // clockwise-ness of the points to change)
1475
- const shouldChangeReflexivity = options.editable ? null : false;
1476
-
1477
- const angle = graphie.addMovableAngle({
1478
- angleLabel: "$deg0",
1479
- fixed: true,
1480
- points: points,
1481
- normalStyle: options.normalStyle,
1482
- reflex: options.reflex,
1483
- });
1484
-
1485
- // Hide non-vertex points on uneditable angles
1486
- if (!_.isArray(points[0]) && !options.editable) {
1487
- points[0].remove();
1488
- points[2].remove();
1489
- }
1490
- return {
1491
- update: angle.update.bind(angle, shouldChangeReflexivity),
1492
- remove: angle.remove.bind(angle),
1493
- getOptions: function () {
1494
- return {
1495
- reflex: angle.isReflex(),
1496
- };
1497
- },
1498
- };
1499
- }
1500
- if (type === "circle") {
1501
- let perimeter = {
1502
- // temporary object for the first removal
1503
- remove: _.identity,
1504
- };
1505
- const redrawPerim = function () {
1506
- const coord0 = points[0].coord || points[0];
1507
- const coord1 = points[1].coord || points[1];
1508
- const radius = kpoint.distanceToPoint(coord0, coord1);
1509
- perimeter.remove();
1510
- perimeter = graphie.circle(
1511
- coord0,
1512
- radius,
1513
- _.extend(
1514
- {
1515
- stroke: KhanColors.DYNAMIC,
1516
- "stroke-width": 2,
1517
- },
1518
- options.normalStyle,
1519
- ),
1520
- );
1521
- };
1522
-
1523
- redrawPerim();
1524
- if (points[1].remove && !options.editable) {
1525
- points[1].remove();
1526
- }
1527
-
1528
- return {
1529
- update: redrawPerim,
1530
- remove: function () {
1531
- // Not _.bind because the remove function changes
1532
- // when the perimeter is redrawn
1533
- perimeter.remove();
1534
- },
1535
- };
1536
- }
1537
- if (type === "point") {
1538
- // do nothing
1539
- return {
1540
- update: null,
1541
- remove: null,
1542
- };
1543
- }
1544
- throw new PerseusError(
1545
- "Invalid shape type " + type,
1546
- Errors.InvalidInput,
1547
- );
1548
- },
1549
-
1550
- _combine: function (type, coords) {
1551
- return {
1552
- type: type,
1553
- coords: coords,
1554
- };
1555
- },
1556
-
1557
- polygon: {
1558
- equal: orderInsensitiveCoordsEqual,
1559
- },
1560
-
1561
- line: {
1562
- equal: kline.equal,
1563
- },
1564
-
1565
- lineSegment: {
1566
- equal: orderInsensitiveCoordsEqual,
1567
- },
1568
-
1569
- angle: {
1570
- equal: function (points1, points2) {
1571
- if (!kpoint.equal(points1[1], points2[1])) {
1572
- return false;
1573
- }
1574
-
1575
- const line1_0 = [points1[1], points1[0]];
1576
- const line1_2 = [points1[1], points1[2]];
1577
- const line2_0 = [points2[1], points2[0]];
1578
- const line2_2 = [points2[1], points2[2]];
1579
-
1580
- const equalUnflipped =
1581
- kray.equal(line1_0, line2_0) && kray.equal(line1_2, line2_2);
1582
- const equalFlipped =
1583
- kray.equal(line1_0, line2_2) && kray.equal(line1_2, line2_0);
1584
-
1585
- return equalUnflipped || equalFlipped;
1586
- },
1587
-
1588
- defaultOptions: {
1589
- reflex: false,
1590
- },
1591
- },
1592
-
1593
- circle: {
1594
- equal: function (points1, points2) {
1595
- const radius1 = kpoint.distanceToPoint(points1[0], points1[1]);
1596
- const radius2 = kpoint.distanceToPoint(points2[0], points2[1]);
1597
- return (
1598
- kpoint.equal(points1[0], points2[0]) &&
1599
- knumber.equal(radius1, radius2)
1600
- );
1601
- },
1602
- },
1603
-
1604
- point: {
1605
- equal: kpoint.equal,
1606
- },
1607
- };
1608
-
1609
- const TransformationListItem = TransformOps.ListItem;
1610
-
1611
- class TransformationList extends React.Component<$FlowFixMe, $FlowFixMe> {
1612
- render(): React.Node {
1613
- if (this.props.mode === "static") {
1614
- return <span />; // don't render anything
1615
- }
1616
-
1617
- const transformationList = _.map(
1618
- this.props.transformations,
1619
- function (transform, i) {
1620
- return (
1621
- <TransformationListItem
1622
- ref={"transformation" + i}
1623
- key={"transformation" + i}
1624
- transform={transform}
1625
- mode={this.props.mode}
1626
- onChange={this.handleChange}
1627
- onFocus={_.partial(this.props.onFocus, "" + i)}
1628
- onBlur={_.partial(this.props.onBlur, "" + i)}
1629
- keypadElement={this.props.keypadElement}
1630
- apiOptions={this.props.apiOptions}
1631
- />
1632
- );
1633
- },
1634
- this,
1635
- );
1636
-
1637
- return (
1638
- <div className="perseus-transformation-list">
1639
- {transformationList}
1640
- </div>
1641
- );
1642
- }
1643
-
1644
- _transformationRefs = () => {
1645
- return _.times(this.props.transformations.length, (i) => {
1646
- // eslint-disable-next-line react/no-string-refs
1647
- return this.refs["transformation" + i];
1648
- });
1649
- };
1650
-
1651
- value = () => {
1652
- return _.invoke(this._transformationRefs(), "value");
1653
- };
1654
-
1655
- handleChange = (changed, callback) => {
1656
- this.props.onChange(this.value(), callback);
1657
- };
1658
-
1659
- focusLast = () => {
1660
- const transformationRefs = this._transformationRefs();
1661
- if (transformationRefs.length !== 0) {
1662
- _.last(transformationRefs).focus();
1663
- }
1664
- };
1665
- }
1666
-
1667
- class ToolButton extends React.Component<$FlowFixMe, $FlowFixMe> {
1668
- render(): React.Node {
1669
- const classes = this.props.toggled
1670
- ? "simple-button exercise-orange toggled highlighted-tool-button"
1671
- : "simple-button";
1672
-
1673
- return (
1674
- <button
1675
- type="button"
1676
- className={classes}
1677
- disabled={this.props.disabled}
1678
- onClick={this.props.onClick}
1679
- onTouchStart={captureScratchpadTouchStart}
1680
- >
1681
- {this.props.children}
1682
- </button>
1683
- );
1684
- }
1685
- }
1686
-
1687
- class ToolsBar extends React.Component<$FlowFixMe, $FlowFixMe> {
1688
- state = {
1689
- selected: null,
1690
- };
1691
-
1692
- render(): React.Node {
1693
- const tools = _.map(
1694
- Transformations,
1695
- function (tool, type) {
1696
- if (this.props.enabled[type]) {
1697
- return (
1698
- <ToolButton
1699
- key={type}
1700
- disabled={this.props.apiOptions.readOnly}
1701
- toggled={this.state.selected === type}
1702
- // eslint-disable-next-line react/jsx-no-bind
1703
- onClick={this.changeSelected.bind(this, type)}
1704
- >
1705
- {tool.verbName}
1706
- </ToolButton>
1707
- );
1708
- }
1709
- },
1710
- this,
1711
- );
1712
-
1713
- return (
1714
- <div className="transformer-tools-bar">
1715
- <span className="simple-button-group">{tools}</span>
1716
- <button
1717
- className="transformer-undo-button simple-button"
1718
- type="button"
1719
- disabled={this.props.apiOptions.readOnly}
1720
- onClick={this.props.onUndoClick}
1721
- onTouchStart={captureScratchpadTouchStart}
1722
- >
1723
- <InlineIcon {...iconUndo} />
1724
- {" " + i18n._("Undo")}
1725
- </button>
1726
- <div className="clear" />
1727
- </div>
1728
- );
1729
- }
1730
-
1731
- changeSelected = (tool) => {
1732
- this.props.removeTool(this.state.selected);
1733
-
1734
- if (!tool || tool === this.state.selected) {
1735
- this.setState({
1736
- selected: null,
1737
- });
1738
- } else {
1739
- this.props.addTool(tool);
1740
- this.setState({
1741
- selected: tool,
1742
- });
1743
- }
1744
- };
1745
- }
1746
-
1747
- class AddTransformBar extends React.Component<$FlowFixMe, $FlowFixMe> {
1748
- render(): React.Node {
1749
- const tools = _.map(
1750
- Transformations,
1751
- function (tool, type) {
1752
- if (this.props.enabled[type]) {
1753
- return (
1754
- <ToolButton
1755
- key={type}
1756
- toggled={false}
1757
- disabled={this.props.apiOptions.readOnly}
1758
- // eslint-disable-next-line react/jsx-no-bind
1759
- onClick={this.changeSelected.bind(this, type)}
1760
- >
1761
- <InlineIcon {...iconPlus} /> {tool.nounName}
1762
- </ToolButton>
1763
- );
1764
- }
1765
- },
1766
- this,
1767
- );
1768
-
1769
- return (
1770
- <div className="transformer-tools-bar">
1771
- {tools}
1772
- <button
1773
- className="transformer-undo-button simple-button"
1774
- type="button"
1775
- onClick={this.props.onUndoClick}
1776
- disabled={this.props.apiOptions.readOnly}
1777
- onTouchStart={captureScratchpadTouchStart}
1778
- >
1779
- <InlineIcon {...iconUndo} />
1780
- {" " + i18n._("Undo")}
1781
- </button>
1782
- <div className="clear" />
1783
- </div>
1784
- );
1785
- }
1786
-
1787
- changeSelected = (tool) => {
1788
- if (tool) {
1789
- this.props.addTool(tool);
1790
- }
1791
- };
1792
- }
1793
-
1794
- type Graphie = $FlowFixMe;
1795
- type Tool = $FlowFixMe;
1796
- type ToolCleanup = $FlowFixMe;
1797
-
1798
- type Rubric = PerseusTransformerWidgetOptions;
1799
- type RenderProps = PerseusTransformerWidgetOptions; // there's no transform in the exports
1800
- type UserInput = $FlowFixMe;
1801
- type Props = {|
1802
- ...WidgetProps<RenderProps, Rubric>,
1803
- transformations: $ReadOnlyArray<TransformerTransformation>,
1804
- keypadElement: $FlowFixMe,
1805
- |};
1806
-
1807
- class Transformer extends React.Component<Props> {
1808
- tools: {|[string]: Tool|};
1809
- currentTool: ?Tool;
1810
- transformations: $ReadOnlyArray<TransformerTransformation>;
1811
- shape: ?$FlowFixMe; // This is not the same as TransformerShape
1812
- dilationCircle: ?$FlowFixMe;
1813
- rotatePoint: ?$FlowFixMe;
1814
- rotateHandle: ?$FlowFixMe;
1815
-
1816
- static defaultProps: $FlowFixMe = _.defaults(
1817
- {
1818
- transformations: [],
1819
- },
1820
- defaultTransformerProps,
1821
- );
1822
-
1823
- render(): React.Node {
1824
- // Fill in any missing value in this.props.graph
1825
- // this can happen because the graph json doesn't include
1826
- // box, for example
1827
- const graph = _.extend(
1828
- defaultGraphProps(this.props.graph, defaultBoxSize),
1829
- this.props.graph,
1830
- );
1831
-
1832
- const interactiveToolsMode = this.props.graphMode === "interactive";
1833
-
1834
- const ToolsBarClass = interactiveToolsMode ? ToolsBar : AddTransformBar;
1835
-
1836
- // This style is applied inline because it is dependent on the
1837
- // size of the graph as set by the graph.box prop, and this also
1838
- // lets us specify it in the same place the graph's width is
1839
- // specified.
1840
- const toolsBar = (
1841
- <div style={{width: graph.box[0]}}>
1842
- <ToolsBarClass
1843
- // eslint-disable-next-line react/no-string-refs
1844
- ref="toolsBar"
1845
- enabled={pluckObject(this.props.tools, "enabled")}
1846
- apiOptions={this.props.apiOptions}
1847
- addTool={this.addTool}
1848
- removeTool={this.removeTool}
1849
- onUndoClick={this.handleUndoClick}
1850
- />
1851
- </div>
1852
- );
1853
-
1854
- return (
1855
- <div className={"perseus-widget " + "perseus-widget-transformer"}>
1856
- <Graph
1857
- // eslint-disable-next-line react/no-string-refs
1858
- ref="graph"
1859
- box={graph.box}
1860
- range={graph.range}
1861
- labels={graph.labels}
1862
- step={graph.step}
1863
- gridStep={graph.gridStep}
1864
- markings={graph.markings}
1865
- backgroundImage={graph.backgroundImage}
1866
- showProtractor={graph.showProtractor}
1867
- onGraphieUpdated={this.setupGraphie}
1868
- setDrawingAreaAvailable={
1869
- this.props.apiOptions.setDrawingAreaAvailable
1870
- }
1871
- />
1872
-
1873
- {!interactiveToolsMode && i18n._("Add transformations below:")}
1874
-
1875
- {this.props.graphMode === "static" && [
1876
- <br key="static-br" />,
1877
- <em key="static-nomove">
1878
- {" "}
1879
- {i18n._(
1880
- "Note: For this question, the shape will not move.",
1881
- )}{" "}
1882
- </em>,
1883
- ]}
1884
-
1885
- {interactiveToolsMode && toolsBar}
1886
-
1887
- <TransformationList
1888
- // eslint-disable-next-line react/no-string-refs
1889
- ref="transformationList"
1890
- mode={this.props.listMode}
1891
- transformations={this.props.transformations}
1892
- onChange={this.setTransformationProps}
1893
- onFocus={this._handleFocus}
1894
- onBlur={this._handleBlur}
1895
- keypadElement={this.props.keypadElement}
1896
- apiOptions={this.props.apiOptions}
1897
- />
1898
-
1899
- {!interactiveToolsMode && toolsBar}
1900
- </div>
1901
- );
1902
- }
1903
-
1904
- componentDidMount() {
1905
- this.setupGraphie(this.graphie());
1906
- }
1907
-
1908
- componentDidUpdate(prevProps: Props) {
1909
- if (this.shouldSetupGraphie(this.props, prevProps)) {
1910
- // eslint-disable-next-line react/no-string-refs
1911
- this.refs.graph.reset();
1912
- } else if (!deepEq(this.props.transformations, this.transformations)) {
1913
- this.setTransformations(this.props.transformations);
1914
- }
1915
- }
1916
-
1917
- shouldSetupGraphie: (Props, Props) => boolean = (nextProps, prevProps) => {
1918
- if (!deepEq(prevProps.starting, nextProps.starting)) {
1919
- return true;
1920
- }
1921
- if (prevProps.graphMode !== nextProps.graphMode) {
1922
- return true;
1923
- }
1924
- if (prevProps.listMode !== nextProps.listMode) {
1925
- return true;
1926
- }
1927
- if (prevProps.drawSolutionShape !== nextProps.drawSolutionShape) {
1928
- return true;
1929
- }
1930
- if (
1931
- nextProps.drawSolutionShape &&
1932
- !deepEq(prevProps.correct.shape, nextProps.correct.shape)
1933
- ) {
1934
- return true;
1935
- }
1936
- if (!deepEq(this.tools, nextProps.tools)) {
1937
- return true;
1938
- }
1939
- return false;
1940
- };
1941
-
1942
- graphie: () => Graphie = () => {
1943
- // eslint-disable-next-line react/no-string-refs
1944
- return this.refs.graph.graphie();
1945
- };
1946
-
1947
- setupGraphie: (Graphie) => void = (graphie) => {
1948
- // A background image of our solution:
1949
- if (
1950
- this.props.drawSolutionShape &&
1951
- this.props.correct.shape &&
1952
- this.props.correct.shape.coords
1953
- ) {
1954
- ShapeTypes.addShape(graphie, {
1955
- fixed: true,
1956
- shape: this.props.correct.shape,
1957
- normalStyle: {
1958
- stroke: KhanColors.GRAY,
1959
- "stroke-dasharray": "",
1960
- "stroke-width": 2,
1961
- },
1962
- });
1963
- }
1964
-
1965
- this.currentTool = null;
1966
- // eslint-disable-next-line react/no-string-refs
1967
- this.refs.toolsBar.changeSelected(null);
1968
- this.addTransformerShape(
1969
- this.props.starting.shape,
1970
- /* translatable */ false,
1971
- );
1972
- this.setTransformations(this.props.transformations);
1973
-
1974
- // Save a copy of our tools so that we can check future
1975
- // this.props.tools changes against them
1976
- // This seems weird, but gives us an easy way to tell whether
1977
- // props changes were self-inflicted (for which a graphie reset
1978
- // is not required, and is in fact a bad idea right now because
1979
- // of resetting the size of the dilation tool).
1980
- // TODO (jack): A deepClone method would be nice here
1981
- this.tools = {
1982
- translation: _.clone(this.props.tools.translation),
1983
- rotation: _.clone(this.props.tools.rotation),
1984
- reflection: _.clone(this.props.tools.reflection),
1985
- dilation: _.clone(this.props.tools.dilation),
1986
- };
1987
- };
1988
-
1989
- /* Applies all transformations in `transformations`
1990
- * to the starting shape, and updates this.transformations
1991
- * to reflect this
1992
- *
1993
- * Usually called with this.props.transformations
1994
- */
1995
- setTransformations: ($ReadOnlyArray<TransformerTransformation>) => void = (
1996
- transformations,
1997
- ) => {
1998
- this.resetCoords();
1999
- this.transformations = _.clone(transformations);
2000
- _.each(this.transformations, this.applyTransform);
2001
- };
2002
-
2003
- // the polygon that we transform
2004
- addTransformerShape: ($FlowFixMe, boolean) => void = (
2005
- shape,
2006
- translatable,
2007
- ) => {
2008
- const self = this;
2009
- const graphie = this.graphie();
2010
-
2011
- this.shape = ShapeTypes.addMovableShape(graphie, {
2012
- shape: shape,
2013
- editable: false,
2014
- showPoints: this.props.graphMode !== "static",
2015
- translatable: translatable,
2016
- onMove: function (dX, dY) {
2017
- dX = KhanMath.roundToNearest(graphie.snap[0], dX);
2018
- dY = KhanMath.roundToNearest(graphie.snap[1], dY);
2019
- // NOTE(kevinb): object is missing .constraints property
2020
- // $FlowFixMe[prop-missing]
2021
- self.addTransform({
2022
- type: "translation",
2023
- vector: [dX, dY],
2024
- });
2025
- return [dX, dY];
2026
- },
2027
- normalPointStyle: {
2028
- fill: translatable
2029
- ? KhanColors.INTERACTIVE
2030
- : KhanColors.DYNAMIC,
2031
- stroke: translatable
2032
- ? KhanColors.INTERACTIVE
2033
- : KhanColors.DYNAMIC,
2034
- },
2035
- highlightPointStyle: {
2036
- fill: KhanColors.INTERACTING,
2037
- stroke: KhanColors.INTERACTING,
2038
- },
2039
- });
2040
- };
2041
-
2042
- addTool: (TransformerTransformation["type"]) => void = (toolId) => {
2043
- const self = this;
2044
-
2045
- if (this.props.graphMode === "interactive") {
2046
- if (toolId === "translation") {
2047
- this.currentTool = this.addTranslationTool();
2048
- } else if (toolId === "rotation") {
2049
- this.currentTool = this.addRotationTool();
2050
- } else if (toolId === "reflection") {
2051
- this.currentTool = this.addReflectionTool();
2052
- } else if (toolId === "dilation") {
2053
- this.currentTool = this.addDilationTool();
2054
- } else {
2055
- throw new PerseusError(
2056
- "Invalid tool id: " + toolId,
2057
- Errors.InvalidInput,
2058
- );
2059
- }
2060
- } else {
2061
- let transform: TransformerTransformation;
2062
- if (toolId === "translation") {
2063
- // NOTE(kevinb): object is missing .constraints property
2064
- // $FlowFixMe[prop-missing]
2065
- transform = {
2066
- type: "translation",
2067
- // NOTE(kevib): these should be numbers
2068
- // $FlowFixMe[incompatible-type]
2069
- vector: [null, null],
2070
- };
2071
- } else if (toolId === "rotation") {
2072
- // NOTE(kevinb): object is missing .constraints property
2073
- // $FlowFixMe[prop-missing]
2074
- transform = {
2075
- type: "rotation",
2076
- // NOTE(kevib): these should be numbers
2077
- // $FlowFixMe[incompatible-type]
2078
- center: [null, null],
2079
- // NOTE(kevib): this should be a number
2080
- // $FlowFixMe[incompatible-type]
2081
- angleDeg: null,
2082
- };
2083
- } else if (toolId === "reflection") {
2084
- // Reflections with nulls in them won't be applied until
2085
- // fills in the blanks
2086
- transform = {
2087
- type: "reflection",
2088
- line: [
2089
- // NOTE(kevib): these should be numbers
2090
- // $FlowFixMe[incompatible-type]
2091
- [null, null],
2092
- // NOTE(kevib): these should be numbers
2093
- // $FlowFixMe[incompatible-type]
2094
- [null, null],
2095
- ],
2096
- };
2097
- } else if (toolId === "dilation") {
2098
- // NOTE(kevinb): object is missing .constraints property
2099
- // $FlowFixMe[prop-missing]
2100
- transform = {
2101
- type: "dilation",
2102
- // NOTE(kevib): these should be numbers
2103
- // $FlowFixMe[incompatible-type]
2104
- center: [null, null],
2105
- // NOTE(kevib): this should be a number
2106
- // $FlowFixMe[incompatible-type]
2107
- scale: null,
2108
- };
2109
- } else {
2110
- throw new PerseusError(
2111
- "Invalid tool id: " + toolId,
2112
- Errors.InvalidInput,
2113
- );
2114
- }
2115
-
2116
- // $FlowFixMe[prop-missing]
2117
- this.doTransform(transform, function () {
2118
- self.refs.transformationList.focusLast();
2119
- });
2120
- }
2121
- };
2122
-
2123
- removeTool: (string) => void = (toolId) => {
2124
- if (this.currentTool) {
2125
- this.currentTool.remove();
2126
- }
2127
- this.currentTool = null;
2128
- };
2129
-
2130
- addTranslationTool: () => $FlowFixMe = () => {
2131
- const self = this;
2132
- this.shape?.remove();
2133
- this.addTransformerShape(this.shape?.toJSON(), /* translatable */ true);
2134
-
2135
- return {
2136
- remove: function () {
2137
- self.shape?.remove();
2138
- self.addTransformerShape(
2139
- self.shape?.toJSON(),
2140
- /* translatable */ false,
2141
- );
2142
- },
2143
- };
2144
- };
2145
-
2146
- // Snaps a coord to this.graphie()'s snap
2147
- snapCoord: (Coord) => Coord = (coord) => {
2148
- const graphie = this.graphie();
2149
- return _.map(coord, function (val, dim) {
2150
- return KhanMath.roundToNearest(graphie.snap[dim], val);
2151
- });
2152
- };
2153
-
2154
- // Normalize the coords into something that fits the new 45 degree
2155
- // reflection line.
2156
- normalizeReflectionCoords: ([Coord, Coord]) => [Coord, Coord] = (
2157
- messyCoords,
2158
- ) => {
2159
- // $FlowFixMe[incompatible-call]
2160
- const midpoint = this.snapCoord(kline.midpoint(messyCoords));
2161
- const origDirectionPolar = kvector.polarDegFromCart(
2162
- kvector.subtract(messyCoords[0], messyCoords[1]),
2163
- );
2164
-
2165
- const direction = kvector.cartFromPolarDeg(
2166
- 1,
2167
- KhanMath.roundToNearest(45, origDirectionPolar[1]),
2168
- );
2169
- const coords = _.map(
2170
- [-1, 1],
2171
- function (directionCoefficient) {
2172
- const coord = kvector.add(
2173
- midpoint,
2174
- kvector.scale(
2175
- direction,
2176
- directionCoefficient *
2177
- this.scaleToCurrentRange(
2178
- REFLECT_ROTATE_HANDLE_DIST,
2179
- ),
2180
- ),
2181
- );
2182
- return this.snapCoord(coord);
2183
- },
2184
- this,
2185
- );
2186
- return coords;
2187
- };
2188
-
2189
- addReflectionTool: () => ToolCleanup = () => {
2190
- const options = this.props.tools.reflection;
2191
- if (!options.enabled) {
2192
- return;
2193
- }
2194
- const self = this;
2195
- // eslint-disable-next-line react/no-string-refs
2196
- const graphie = this.refs.graph.graphie();
2197
-
2198
- const updateReflectionTool = function () {
2199
- self.changeTool("reflection", {
2200
- coords: _.pluck(reflectPoints, "coord"),
2201
- });
2202
- };
2203
-
2204
- const coords = this.normalizeReflectionCoords(options.coords);
2205
-
2206
- // The points defining the line of reflection; hidden from the
2207
- // user.
2208
- const reflectPoints = _.map(
2209
- coords,
2210
- function (coord) {
2211
- return graphie.addMovablePoint({
2212
- coord: coord,
2213
- visible: false,
2214
- });
2215
- },
2216
- this,
2217
- );
2218
-
2219
- // the line of reflection
2220
- // TODO(jack): graphie.style here is a hack to prevent the dashed
2221
- // style from leaking into the rest of the shapes. Remove when
2222
- // graphie.addMovableLineSegment doesn't leak styles anymore.
2223
- let reflectLine;
2224
- const normalColor = colorForTool(options);
2225
- graphie.style({}, function () {
2226
- reflectLine = graphie.addMovableLineSegment({
2227
- fixed: options.constraints.fixed,
2228
- constraints: options.constraints,
2229
- pointA: reflectPoints[0],
2230
- pointZ: reflectPoints[1],
2231
- snapX: graphie.snap[0],
2232
- snapY: graphie.snap[1],
2233
- extendLine: true,
2234
- normalStyle: {
2235
- stroke: normalColor,
2236
- "stroke-width": 2,
2237
- "stroke-dasharray": "- ",
2238
- },
2239
- highlightStyle: {
2240
- stroke: KhanColors.INTERACTING,
2241
- "stroke-width": 2,
2242
- "stroke-dasharray": "- ", // TODO(jack) solid doesn't
2243
- // work here, but would be
2244
- // nicer
2245
- },
2246
- movePointsWithLine: true,
2247
- onMoveEnd: updateReflectionTool,
2248
- });
2249
- });
2250
-
2251
- // the "button" point in the center of the line of reflection
2252
- const reflectButton = graphie.addReflectButton({
2253
- fixed: options.constraints.fixed,
2254
- line: reflectLine,
2255
- size: this.scaleToCurrentRange(REFLECT_BUTTON_SIZE),
2256
- onClick: function () {
2257
- self.doTransform({
2258
- type: "reflection",
2259
- line: _.pluck(reflectPoints, "coord"),
2260
- });
2261
- if (reflectRotateHandle) {
2262
- // flip the rotation handle
2263
- reflectRotateHandle.setCoord(
2264
- kvector.add(
2265
- reflectButton.coord,
2266
- kvector.subtract(
2267
- reflectButton.coord,
2268
- reflectRotateHandle.coord,
2269
- ),
2270
- ),
2271
- );
2272
- reflectRotateHandle.update();
2273
- }
2274
- },
2275
- normalStyle: {
2276
- stroke: normalColor,
2277
- "stroke-width": 2,
2278
- fill: normalColor,
2279
- },
2280
- highlightStyle: {
2281
- stroke: KhanColors.INTERACTING,
2282
- "stroke-width": 3,
2283
- fill: KhanColors.INTERACTING,
2284
- },
2285
- onMoveEnd: updateReflectionTool,
2286
- });
2287
-
2288
- let reflectRotateHandle = null;
2289
- if (!options.constraints.fixed) {
2290
- // The rotation handle for rotating the line of reflection
2291
- const initRotateHandleAngle =
2292
- kvector.polarDegFromCart(
2293
- kvector.subtract(
2294
- reflectPoints[1].coord,
2295
- reflectPoints[0].coord,
2296
- ),
2297
- )[1] + 90; // 90 degrees off of the line
2298
- reflectRotateHandle = graphie.addRotateHandle({
2299
- center: reflectButton,
2300
- radius: this.scaleToCurrentRange(REFLECT_ROTATE_HANDLE_DIST),
2301
- angleDeg: initRotateHandleAngle,
2302
- width: this.scaleToCurrentRange(0.24),
2303
- hoverWidth: this.scaleToCurrentRange(0.4),
2304
- lengthAngle: 17,
2305
- onMove: function (newAngle) {
2306
- return KhanMath.roundToNearest(45, newAngle);
2307
- },
2308
- onMoveEnd: updateReflectionTool,
2309
- });
2310
- }
2311
-
2312
- // Move the reflectButton and reflectRotateHandle with the line
2313
- $(reflectLine).on("move", function () {
2314
- reflectButton.update();
2315
- $(reflectButton).trigger("move"); // update the rotation handle,
2316
- // which watches for this in util/interactive.js.
2317
- });
2318
-
2319
- // Update the line and reflect button when the reflectRotateHandle is
2320
- // rotated
2321
- if (reflectRotateHandle) {
2322
- $(reflectRotateHandle).on("move", function () {
2323
- const rotateHandleApprox = self.snapCoord(
2324
- // $FlowFixMe[incompatible-use]
2325
- reflectRotateHandle.coord,
2326
- );
2327
-
2328
- const rotateVector = kvector.subtract(
2329
- rotateHandleApprox,
2330
- reflectButton.coord,
2331
- );
2332
-
2333
- const flipped = reflectButton.isFlipped() ? 1 : 0;
2334
- reflectPoints[flipped].setCoord(
2335
- kvector.add(
2336
- reflectButton.coord,
2337
- kvector.rotateDeg(rotateVector, 90),
2338
- ),
2339
- );
2340
- reflectPoints[1 - flipped].setCoord(
2341
- kvector.add(
2342
- reflectButton.coord,
2343
- kvector.rotateDeg(rotateVector, -90),
2344
- ),
2345
- );
2346
-
2347
- reflectLine.transform(true);
2348
- reflectButton.update();
2349
- });
2350
- }
2351
-
2352
- return {
2353
- remove: function () {
2354
- reflectButton.remove();
2355
- if (reflectRotateHandle) {
2356
- reflectRotateHandle.remove();
2357
- }
2358
- reflectLine.remove();
2359
- reflectPoints[0].remove();
2360
- reflectPoints[1].remove();
2361
- },
2362
- };
2363
- };
2364
-
2365
- /* Scales a distance from the default range of
2366
- * [-10, 10] to the current this.props.graph.range
2367
- *
2368
- * Used for sizing various transformation tools
2369
- * (rotation handle, dilation circle)
2370
- */
2371
- scaleToCurrentRange: (number) => number = (dist) => {
2372
- // eslint-disable-next-line react/no-string-refs
2373
- return scaleToRange(dist, this.refs.graph.props.range);
2374
- };
2375
-
2376
- addRotationTool: () => ToolCleanup = () => {
2377
- const options = this.props.tools.rotation;
2378
- if (!options.enabled) {
2379
- return;
2380
- }
2381
- const self = this;
2382
- // eslint-disable-next-line react/no-string-refs
2383
- const graphie = this.refs.graph.graphie();
2384
-
2385
- const pointColor = colorForTool(options);
2386
- // The center of our rotation, which can be moved to change the
2387
- // center of rotation
2388
- this.rotatePoint = graphie.addMovablePoint({
2389
- constraints: options.constraints,
2390
- coord: options.coord,
2391
- snapX: graphie.snap[0],
2392
- snapY: graphie.snap[1],
2393
- normalStyle: {
2394
- // ugh, this seems to be a global and
2395
- "stroke-dasharray": "", // is set to dash above
2396
- stroke: pointColor,
2397
- fill: pointColor,
2398
- },
2399
- highlightStyle: {
2400
- "stroke-dasharray": "",
2401
- stroke: KhanColors.INTERACTING,
2402
- fill: KhanColors.INTERACTING,
2403
- },
2404
- });
2405
-
2406
- // The point that we move around the center of rotation to actually
2407
- // cause rotations
2408
- this.rotateHandle = graphie.addRotateHandle({
2409
- center: this.rotatePoint,
2410
- radius: this.scaleToCurrentRange(ROTATE_HANDLE_DIST),
2411
- width: this.scaleToCurrentRange(0.24),
2412
- hoverWidth: this.scaleToCurrentRange(0.4),
2413
- onMove: function (newAngle, oldAngle) {
2414
- const transform = self.getRotationTransformFromAngle(
2415
- // $FlowFixMe[incompatible-use]
2416
- self.rotatePoint.coord,
2417
- newAngle - oldAngle,
2418
- );
2419
-
2420
- // Rotate polygon with rotateHandle
2421
- self.doTransform(transform);
2422
-
2423
- // $FlowFixMe[prop-missing]
2424
- return oldAngle + transform.angleDeg;
2425
- },
2426
- });
2427
-
2428
- // Update tools.rotation.coord
2429
- // $FlowFixMe[incompatible-use]
2430
- this.rotatePoint.onMoveEnd = (x, y) => {
2431
- self.changeTool("rotation", {
2432
- coord: [x, y],
2433
- });
2434
- };
2435
-
2436
- return {
2437
- remove: function () {
2438
- self.rotateHandle?.remove();
2439
- self.rotatePoint?.remove();
2440
- },
2441
- };
2442
- };
2443
-
2444
- addDilationTool: () => ToolCleanup = () => {
2445
- const options = this.props.tools.dilation;
2446
- if (!options.enabled) {
2447
- return;
2448
- }
2449
- const self = this;
2450
- // eslint-disable-next-line react/no-string-refs
2451
- const graphie = this.refs.graph.graphie();
2452
-
2453
- const pointColor = colorForTool(options);
2454
- // the circle for causing dilation transforms
2455
- self.dilationCircle = graphie.addCircleGraph({
2456
- centerConstraints: options.constraints,
2457
- center: options.coord,
2458
- radius: self.scaleToCurrentRange(2),
2459
- snapX: graphie.snap[0],
2460
- snapY: graphie.snap[1],
2461
- minRadius: self.scaleToCurrentRange(1),
2462
- snapRadius: self.scaleToCurrentRange(0.5),
2463
- onResize: function (newRadius, oldRadius) {
2464
- // NOTE(kevinb): object is missing .constraints property
2465
- // $FlowFixMe[prop-missing]
2466
- self.doTransform({
2467
- type: "dilation",
2468
- // NOTE(kevinb): It's hard to convince that this.dilationCircle
2469
- // will still be defined here.
2470
- // $FlowFixMe[incompatible-use]
2471
- center: self.dilationCircle.centerPoint.coord,
2472
- scale: newRadius / oldRadius,
2473
- });
2474
- },
2475
- circleNormalStyle: {
2476
- stroke: pointColor,
2477
- "stroke-width": 2,
2478
- "stroke-dasharray": "- ",
2479
- "fill-opacity": 0,
2480
- },
2481
- circleHighlightStyle: {
2482
- stroke: KhanColors.INTERACTING,
2483
- "stroke-width": 2,
2484
- "stroke-dasharray": "",
2485
- fill: KhanColors.INTERACTING,
2486
- "fill-opacity": 0.05,
2487
- },
2488
- centerNormalStyle: {
2489
- stroke: pointColor,
2490
- fill: pointColor,
2491
- "stroke-width": 2,
2492
- "stroke-dasharray": "",
2493
- },
2494
- centerHighlightStyle: {
2495
- stroke: pointColor,
2496
- fill: pointColor,
2497
- "stroke-width": 2,
2498
- "stroke-dasharray": "",
2499
- },
2500
- });
2501
-
2502
- // $FlowFixMe[incompatible-use]
2503
- const origOnMoveEnd = this.dilationCircle.centerPoint.onMoveEnd;
2504
- // $FlowFixMe[incompatible-use]
2505
- this.dilationCircle.centerPoint.onMoveEnd = (...args) => {
2506
- if (origOnMoveEnd) {
2507
- origOnMoveEnd(args);
2508
- }
2509
- self.changeTool("dilation", {
2510
- // $FlowFixMe[incompatible-use]
2511
- coord: self.dilationCircle.centerPoint.coord,
2512
- });
2513
- };
2514
-
2515
- return {
2516
- remove: function () {
2517
- self.dilationCircle?.remove();
2518
- },
2519
- };
2520
- };
2521
-
2522
- // returns a transformation object representing a rotation
2523
- // rounds the angle to the nearest 15 degrees
2524
- getRotationTransformFromAngle: (
2525
- Coord,
2526
- number,
2527
- ) => TransformerTransformation = (center, angleChanged) => {
2528
- angleChanged = (angleChanged + 360) % 360;
2529
- if (angleChanged > 180) {
2530
- angleChanged -= 360;
2531
- }
2532
- const roundedAngle =
2533
- Math.round(angleChanged / ROTATE_SNAP_DEGREES) *
2534
- ROTATE_SNAP_DEGREES;
2535
-
2536
- // NOTE(kevinb): return object is missing .constraints property
2537
- // $FlowFixMe[prop-missing]
2538
- return {
2539
- type: "rotation",
2540
- center: center,
2541
- angleDeg: roundedAngle,
2542
- };
2543
- };
2544
-
2545
- // apply and save a transform
2546
- doTransform: (TransformerTransformation, ?($FlowFixMe) => mixed) => void = (
2547
- transform,
2548
- callback,
2549
- ) => {
2550
- this.applyTransform(transform);
2551
- this.addTransform(transform, callback);
2552
- };
2553
-
2554
- // apply a transform to our polygon (without modifying our transformation
2555
- // list)
2556
- applyTransform: (TransformerTransformation) => void = (transform) => {
2557
- if (this.props.graphMode !== "static") {
2558
- const transformFunc = TransformOps.apply(transform);
2559
- // $FlowFixMe[incompatible-call]
2560
- this.applyCoordTransformation(transformFunc);
2561
- }
2562
- };
2563
-
2564
- // transform our polygon by transforming each point using a given function
2565
- applyCoordTransformation: (TransformerTransformation) => void = (
2566
- pointTransform,
2567
- ) => {
2568
- // $FlowFixMe[incompatible-use]
2569
- _.each(this.shape.points, function (point) {
2570
- // $FlowFixMe[prop-missing]
2571
- const newCoord = pointTransform(point.coord);
2572
- point.setCoord(newCoord);
2573
- });
2574
- this.shape?.update();
2575
- };
2576
-
2577
- resetCoords: () => void = () => {
2578
- const startCoords = this.props.starting.shape.coords;
2579
- // $FlowFixMe[incompatible-use]
2580
- _.each(this.shape.points, function (point, i) {
2581
- point.setCoord(startCoords[i]);
2582
- });
2583
- this.shape?.update();
2584
- };
2585
-
2586
- // Remove the last transformation
2587
- handleUndoClick: () => void = () => {
2588
- // eslint-disable-next-line react/no-string-refs
2589
- this.refs.toolsBar.changeSelected(null);
2590
- if (this.props.transformations.length) {
2591
- this.props.onChange({
2592
- transformations: _.initial(this.props.transformations),
2593
- });
2594
- }
2595
- };
2596
-
2597
- setTransformationProps: (
2598
- $ReadOnlyArray<TransformerTransformation>,
2599
- ($FlowFixMe) => mixed,
2600
- ) => void = (newTransfomationList, callback) => {
2601
- this.props.onChange(
2602
- {
2603
- transformations: newTransfomationList,
2604
- },
2605
- callback,
2606
- );
2607
- };
2608
-
2609
- // add a transformation to our props list of transformation
2610
- addTransform: (TransformerTransformation, ?($FlowFixMe) => mixed) => void =
2611
- (transform, callback) => {
2612
- this.transformations = TransformOps.append(
2613
- this.transformations,
2614
- transform,
2615
- );
2616
- this.props.onChange(
2617
- {
2618
- transformations: _.clone(this.transformations),
2619
- },
2620
- callback,
2621
- );
2622
- };
2623
-
2624
- changeTool: (Tool, $FlowFixMe) => void = (tool, changes) => {
2625
- const newTools = _.clone(this.props.tools);
2626
- newTools[tool] = _.extend({}, this.props.tools[tool], changes);
2627
- this.tools[tool] = _.clone(newTools[tool]);
2628
- this.props.onChange({
2629
- tools: newTools,
2630
- });
2631
- this.props.trackInteraction();
2632
- };
2633
-
2634
- simpleValidate: (Rubric) => PerseusScore = (rubric) => {
2635
- return Transformer.validate(this.getUserInput(), rubric);
2636
- };
2637
-
2638
- /**
2639
- * Calculate where the coordinates would be if they were
2640
- * moved, even if we're in formal mode with no movement
2641
- * (and thus the actual movablepoints may not have moved
2642
- */
2643
- getCoords: () => Coord = () => {
2644
- const startCoords = this.props.starting.shape.coords;
2645
- const transforms = this.props.transformations;
2646
- return _.reduce(
2647
- transforms,
2648
- function (coords, transform) {
2649
- return _.map(coords, TransformOps.apply(transform));
2650
- },
2651
- startCoords,
2652
- );
2653
- };
2654
-
2655
- getEditorJSON: () => $FlowFixMe = () => {
2656
- const json = _.pick(
2657
- this.props,
2658
- "grading",
2659
- "starting",
2660
- "graphMode",
2661
- "listMode",
2662
- "tools",
2663
- "drawSolutionShape",
2664
- "gradeEmpty",
2665
- );
2666
- // eslint-disable-next-line react/no-string-refs
2667
- json.graph = this.refs.graph.toJSON();
2668
- json.version = 1.2; // Give us some safety to change the format
2669
- // when we realize that I wrote
2670
- // a horrible json spec for this widget
2671
-
2672
- json.answer = this.getUserInput();
2673
- return json;
2674
- };
2675
-
2676
- getUserInput: UserInput = () => {
2677
- return {
2678
- transformations: this.props.transformations,
2679
- // This doesn't call this.shape.toJSON() because that doesn't
2680
- // handle coordinates in formal mode without movement, since
2681
- // the movablepoints never move
2682
- shape: {
2683
- // $FlowFixMe[incompatible-use]
2684
- type: this.shape.type,
2685
- coords: this.getCoords(),
2686
- options: this.shape?.getOptions(),
2687
- },
2688
- };
2689
- };
2690
-
2691
- /* InputPath API */
2692
-
2693
- _handleFocus: (...$ReadOnlyArray<string>) => void = (...path) => {
2694
- this.props.onFocus(path);
2695
- };
2696
-
2697
- _handleBlur: (...$ReadOnlyArray<string>) => void = (...path) => {
2698
- this.props.onBlur(path);
2699
- };
2700
-
2701
- _getTransformationForID: (string) => $FlowFixMe = (transformationID) => {
2702
- // Returns the 'transformation' component corresponding to a given ID
2703
- const refPath = [
2704
- "transformationList",
2705
- "transformation" + transformationID,
2706
- ];
2707
-
2708
- // Follow the path of references
2709
- let component = this;
2710
- _.each(refPath, (ref) => {
2711
- component = component.refs[ref];
2712
- });
2713
- return component;
2714
- };
2715
-
2716
- getInputPaths: () => $ReadOnlyArray<Path> = () => {
2717
- // If we're in static mode, then there is no transformation list, and,
2718
- // as a result, no input paths.
2719
- if (this.props.listMode === "static") {
2720
- return [];
2721
- }
2722
-
2723
- let inputPaths = [];
2724
- _.each(this.props.transformations, (transformation, i) => {
2725
- transformation = this._getTransformationForID(i);
2726
- const innerPaths = transformation.getInputPaths();
2727
- const fullPaths = _.map(innerPaths, (innerPath) => {
2728
- return ["" + i].concat(innerPath);
2729
- });
2730
- inputPaths = inputPaths.concat(fullPaths);
2731
- });
2732
- return inputPaths;
2733
- };
2734
-
2735
- _passToInner: (
2736
- string,
2737
- $ReadOnlyArray<string>,
2738
- ?$FlowFixMe,
2739
- ?($FlowFixMe) => mixed,
2740
- ) => $FlowFixMe = function (functionName, path, ...args) {
2741
- if (!path || !path.length) {
2742
- return;
2743
- }
2744
-
2745
- // First argument tells us which transformation will receive the call;
2746
- // remaining arguments are used within that transformation to identify
2747
- // a specific input.
2748
- const innerPath = _.rest(path);
2749
-
2750
- // Pass arguments down to appropriate 'transformation' component
2751
- const transformationID = _.head(path);
2752
- const caller = this._getTransformationForID(transformationID);
2753
- return caller[functionName](innerPath, ...args);
2754
- };
2755
-
2756
- focus: () => boolean = () => {
2757
- // Just focus the first showing input
2758
- const inputs = this.getInputPaths();
2759
- if (inputs.length > 0) {
2760
- this.focusInputPath(inputs[0]);
2761
- return true;
2762
- }
2763
- return false;
2764
- };
2765
-
2766
- focusInputPath: (Path) => boolean = (path) => {
2767
- // Since the transformer exposes the input API, it needs to be robust
2768
- // to empty paths. We don't expect this to happen, as entire-widget
2769
- // focusing is typically done through the focus() method, which already
2770
- // handles the empty path case properly, but it's better to be safe
2771
- // here.
2772
- if (path.length === 0) {
2773
- return false;
2774
- }
2775
-
2776
- assert(path.length >= 2);
2777
- return this._passToInner("focusInputPath", path);
2778
- };
2779
-
2780
- blurInputPath: (Path) => boolean = (path) => {
2781
- // Since the transformer exposes the input API, it needs to be robust
2782
- // to empty paths (which indicate a blurring of the entire widget,
2783
- // e.g., when switching from interacting with the transformer to
2784
- // interacting with some other widget).
2785
- if (path.length === 0) {
2786
- return false;
2787
- }
2788
-
2789
- assert(path.length >= 2);
2790
- return this._passToInner("blurInputPath", path);
2791
- };
2792
-
2793
- setInputValue: (Path, $FlowFixMe, ($FlowFixMe) => mixed) => $FlowFixMe = (
2794
- path,
2795
- value,
2796
- cb,
2797
- ) => {
2798
- assert(path.length >= 2);
2799
- return this._passToInner("setInputValue", path, value, cb);
2800
- };
2801
-
2802
- getDOMNodeForPath: (Path) => $FlowFixMe = (path) => {
2803
- assert(path.length >= 2);
2804
- return this._passToInner("getDOMNodeForPath", path);
2805
- };
2806
-
2807
- getGrammarTypeForPath: (Path) => $FlowFixMe = (path) => {
2808
- assert(path.length >= 2);
2809
- return this._passToInner("getGrammarTypeForPath", path);
2810
- };
2811
-
2812
- static validate(guess: UserInput, rubric: Rubric): PerseusScore {
2813
- // Check for any required transformations
2814
- for (const type in Transformations) {
2815
- if (rubric.tools[type].required) {
2816
- const isUsed = _.any(
2817
- _.map(guess.transformations, function (transform) {
2818
- // Required transformations must appear in the
2819
- // transformation list, and must not be no-ops
2820
- return (
2821
- transform.type === type &&
2822
- !TransformOps.isEmpty(transform) &&
2823
- !TransformOps.isNoOp(transform)
2824
- );
2825
- }),
2826
- );
2827
-
2828
- if (!isUsed) {
2829
- return {
2830
- type: "invalid",
2831
- message: i18n._(
2832
- "Your transformation must use a " + "%(type)s.",
2833
- {
2834
- type: Transformations[type].lowerNounName,
2835
- },
2836
- ),
2837
- };
2838
- }
2839
- }
2840
- }
2841
-
2842
- // Compare shapes
2843
- if (ShapeTypes.equal(guess.shape, rubric.correct.shape)) {
2844
- return {
2845
- type: "points",
2846
- earned: 1,
2847
- total: 1,
2848
- message: null,
2849
- };
2850
- }
2851
- if (
2852
- !rubric.gradeEmpty &&
2853
- deepEq(guess.shape.coords, rubric.starting.shape.coords)
2854
- ) {
2855
- return {
2856
- type: "invalid",
2857
- message: i18n._(
2858
- "Use the interactive graph to define a " +
2859
- "correct transformation.",
2860
- ),
2861
- };
2862
- }
2863
- return {
2864
- type: "points",
2865
- earned: 0,
2866
- total: 1,
2867
- message: null,
2868
- };
2869
- }
2870
- }
2871
-
2872
- export default ({
2873
- name: "transformer",
2874
- displayName: "Transformer",
2875
- widget: Transformer,
2876
- }: WidgetExports<typeof Transformer>);