@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,2820 +0,0 @@
1
- /* eslint-disable @babel/no-invalid-this, react/no-unsafe, react/sort-comp */
2
- // @flow
3
- import {number as knumber, point as kpoint} from "@khanacademy/kmath";
4
- import * as i18n from "@khanacademy/wonder-blocks-i18n";
5
- import $ from "jquery";
6
- import debounce from "lodash.debounce";
7
- import * as React from "react";
8
- import _ from "underscore";
9
-
10
- import Graph from "../components/graph.jsx";
11
- import Interactive2 from "../interactive2.js";
12
- import WrappedLine from "../interactive2/wrapped-line.js";
13
- import {Errors} from "../logging/log.js";
14
- import {PerseusError} from "../perseus-error.js";
15
- import Util from "../util.js";
16
- import KhanColors from "../util/colors.js";
17
- import GraphUtils from "../util/graph-utils.js";
18
- import {getInteractiveBoxFromSizeClass} from "../util/sizing-utils.js";
19
-
20
- import type {Coord, Line} from "../interactive2/types.js";
21
- import type {
22
- PerseusGraphType,
23
- PerseusGraphTypeAngle,
24
- PerseusGraphTypePoint,
25
- PerseusGraphTypePolygon,
26
- PerseusGraphTypeSegment,
27
- PerseusInteractiveGraphWidgetOptions,
28
- } from "../perseus-types.js";
29
- import type {PerseusScore, WidgetExports, WidgetProps} from "../types.js";
30
-
31
- type Range = $ReadOnlyArray<number>; // This should really be a readonly tuple of [number, number]
32
- type SineCoefficient = [
33
- /* amplitude */ number,
34
- /* angularFrequency */ number,
35
- /* phase */ number,
36
- /* verticalOffset */ number,
37
- ];
38
- type QuadraticCoefficient = [/* a */ number, /* b */ number, /* c */ number];
39
-
40
- const {DeprecationMixin} = Util;
41
-
42
- const TRASH_ICON_URI =
43
- "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
44
-
45
- const defaultBackgroundImage = {
46
- url: null,
47
- };
48
-
49
- const eq = Util.eq;
50
- const deepEq = Util.deepEq;
51
-
52
- const UNLIMITED = ("unlimited": "unlimited");
53
-
54
- // Sample background image:
55
- // https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
56
-
57
- function ccw(a: Coord, b: Coord, c: Coord): number {
58
- return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
59
- }
60
-
61
- function collinear(a: Coord, b: Coord, c: Coord): boolean {
62
- return eq(ccw(a, b, c), 0);
63
- }
64
-
65
- function sign(val: number): 0 | 1 | -1 {
66
- if (eq(val, 0)) {
67
- return 0;
68
- }
69
- return val > 0 ? 1 : -1;
70
- }
71
-
72
- // default to defaultValue if actual is null or undefined
73
- function defaultVal<T>(actual: ?T, defaultValue: T): T {
74
- return actual == null ? defaultValue : actual;
75
- }
76
-
77
- // Given rect bounding points A and B, whether point C is inside the rect
78
- function pointInRect(a: Coord, b: Coord, c: Coord): boolean {
79
- return (
80
- c[0] <= Math.max(a[0], b[0]) &&
81
- c[0] >= Math.min(a[0], b[0]) &&
82
- c[1] <= Math.max(a[1], b[1]) &&
83
- c[1] >= Math.min(a[1], b[1])
84
- );
85
- }
86
-
87
- // Whether line segment AB intersects line segment CD
88
- // http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
89
- function intersects(ab: Line, cd: Line): boolean {
90
- const triplets = [
91
- [ab[0], ab[1], cd[0]],
92
- [ab[0], ab[1], cd[1]],
93
- [cd[0], cd[1], ab[0]],
94
- [cd[0], cd[1], ab[1]],
95
- ];
96
-
97
- const orientations = _.map(triplets, function (triplet) {
98
- return sign(ccw(...triplet));
99
- });
100
-
101
- if (
102
- orientations[0] !== orientations[1] &&
103
- orientations[2] !== orientations[3]
104
- ) {
105
- return true;
106
- }
107
-
108
- for (let i = 0; i < 4; i++) {
109
- if (orientations[i] === 0 && pointInRect(...triplets[i])) {
110
- return true;
111
- }
112
- }
113
-
114
- return false;
115
- }
116
-
117
- function vector(a, b) {
118
- return _.map(_.zip(a, b), function (pair) {
119
- return pair[0] - pair[1];
120
- });
121
- }
122
-
123
- function magnitude(v: $ReadOnlyArray<Coord>): number {
124
- return Math.sqrt(
125
- _.reduce(
126
- v,
127
- function (memo, el) {
128
- return memo + Math.pow(el, 2);
129
- },
130
- 0,
131
- ),
132
- );
133
- }
134
-
135
- function dotProduct(a: Coord, b: Coord): number {
136
- return _.reduce(
137
- _.zip(a, b),
138
- function (memo, pair) {
139
- return memo + pair[0] * pair[1];
140
- },
141
- 0,
142
- );
143
- }
144
-
145
- function sideLengths(coords: $ReadOnlyArray<Coord>): $ReadOnlyArray<number> {
146
- const segments = _.zip(coords, rotate(coords));
147
- return segments.map(function (segment) {
148
- return magnitude(vector(...segment));
149
- });
150
- }
151
-
152
- // Based on http://math.stackexchange.com/a/151149
153
- function angleMeasures(coords: $ReadOnlyArray<Coord>): $ReadOnlyArray<number> {
154
- const triplets = _.zip(rotate(coords, -1), coords, rotate(coords, 1));
155
-
156
- const offsets = _.map(triplets, function (triplet) {
157
- const p = vector(triplet[1], triplet[0]);
158
- const q = vector(triplet[2], triplet[1]);
159
- const raw = Math.acos(dotProduct(p, q) / (magnitude(p) * magnitude(q)));
160
- return sign(ccw(...triplet)) > 0 ? raw : -raw;
161
- });
162
-
163
- const sum = _.reduce(
164
- offsets,
165
- function (memo, arg) {
166
- return memo + arg;
167
- },
168
- 0,
169
- );
170
-
171
- return _.map(offsets, function (offset) {
172
- return sum > 0 ? Math.PI - offset : Math.PI + offset;
173
- });
174
- }
175
-
176
- // Whether two polygons are similar (or if specified, congruent)
177
- function similar(
178
- coords1: $ReadOnlyArray<Coord>,
179
- coords2: $ReadOnlyArray<Coord>,
180
- tolerance: number,
181
- ): boolean {
182
- if (coords1.length !== coords2.length) {
183
- return false;
184
- }
185
-
186
- const n = coords1.length;
187
-
188
- const angles1 = angleMeasures(coords1);
189
- const angles2 = angleMeasures(coords2);
190
-
191
- const sides1 = sideLengths(coords1);
192
- const sides2 = sideLengths(coords2);
193
-
194
- for (let i = 0; i < 2 * n; i++) {
195
- let angles = angles2.slice();
196
- let sides = sides2.slice();
197
-
198
- // Reverse angles and sides to allow matching reflected polygons
199
- if (i >= n) {
200
- angles.reverse();
201
- sides.reverse();
202
- // Since sides are calculated from two coordinates,
203
- // simply reversing results in an off by one error
204
- sides = rotate(sides, 1);
205
- }
206
-
207
- angles = rotate(angles, i);
208
- sides = rotate(sides, i);
209
-
210
- if (deepEq(angles1, angles)) {
211
- const sidePairs = _.zip(sides1, sides);
212
-
213
- const factors = _.map(sidePairs, function (pair) {
214
- return pair[0] / pair[1];
215
- });
216
-
217
- const same = _.all(factors, function (factor) {
218
- return eq(factors[0], factor);
219
- });
220
-
221
- const congruentEnough = _.all(sidePairs, function (pair) {
222
- return knumber.equal(pair[0], pair[1], tolerance);
223
- });
224
-
225
- if (same && congruentEnough) {
226
- return true;
227
- }
228
- }
229
- }
230
-
231
- return false;
232
- }
233
-
234
- // Less than or approximately equal
235
- function leq(a, b) {
236
- return a < b || eq(a, b);
237
- }
238
-
239
- // Given triangle with sides ABC return angle opposite side C in degrees
240
- function lawOfCosines(a: number, b: number, c: number): number {
241
- return (Math.acos((a * a + b * b - c * c) / (2 * a * b)) * 180) / Math.PI;
242
- }
243
-
244
- function canonicalSineCoefficients([
245
- amplitude,
246
- angularFrequency,
247
- phase,
248
- verticalOffset,
249
- ]: SineCoefficient) {
250
- // For a curve of the form f(x) = a * Sin(b * x - c) + d,
251
- // this function ensures that a, b > 0, and c is its
252
- // smallest possible positive value.
253
-
254
- // Guarantee a > 0
255
- if (amplitude < 0) {
256
- amplitude *= -1;
257
- angularFrequency *= -1;
258
- phase *= -1;
259
- }
260
-
261
- const period = 2 * Math.PI;
262
- // Guarantee b > 0
263
- if (angularFrequency < 0) {
264
- angularFrequency *= -1;
265
- phase *= -1;
266
- phase += period / 2;
267
- }
268
-
269
- // Guarantee c is smallest possible positive value
270
- while (phase > 0) {
271
- phase -= period;
272
- }
273
- while (phase < 0) {
274
- phase += period;
275
- }
276
-
277
- return [amplitude, angularFrequency, phase, verticalOffset];
278
- }
279
-
280
- // e.g. rotate([1, 2, 3]) -> [2, 3, 1]
281
- function rotate<T>(array: $ReadOnlyArray<T>, n?: number): $ReadOnlyArray<T> {
282
- n = typeof n === "undefined" ? 1 : n % array.length;
283
- return array.slice(n).concat(array.slice(0, n));
284
- }
285
-
286
- function capitalize(str) {
287
- return str.replace(/(?:^|-)(.)/g, function (match, letter) {
288
- return letter.toUpperCase();
289
- });
290
- }
291
-
292
- function getLineEquation(first: Coord, second: Coord): string {
293
- if (eq(first[0], second[0])) {
294
- return "x = " + first[0].toFixed(3);
295
- }
296
- const m = (second[1] - first[1]) / (second[0] - first[0]);
297
- const b = first[1] - m * first[0];
298
- return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
299
- }
300
-
301
- // Stolen from the wikipedia article
302
- // http://en.wikipedia.org/wiki/Line-line_intersection
303
- function getLineIntersection(
304
- // TODO(LP-10725): update these to be 2-tuples
305
- firstPoints: $ReadOnlyArray<Coord>,
306
- secondPoints: $ReadOnlyArray<Coord>,
307
- ): string {
308
- const x1 = firstPoints[0][0];
309
- const y1 = firstPoints[0][1];
310
- const x2 = firstPoints[1][0];
311
- const y2 = firstPoints[1][1];
312
- const x3 = secondPoints[0][0];
313
- const y3 = secondPoints[0][1];
314
- const x4 = secondPoints[1][0];
315
- const y4 = secondPoints[1][1];
316
-
317
- const determinant = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
318
-
319
- if (Math.abs(determinant) < 1e-9) {
320
- return "Lines are parallel";
321
- }
322
- const x =
323
- ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) /
324
- determinant;
325
- const y =
326
- ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) /
327
- determinant;
328
- return "Intersection: (" + x.toFixed(3) + ", " + y.toFixed(3) + ")";
329
- }
330
-
331
- function numSteps(range: Range, step: number) {
332
- return Math.floor((range[1] - range[0]) / step);
333
- }
334
-
335
- const deprecatedProps = {
336
- showGraph: function (props) {
337
- return {markings: props.showGraph ? "graph" : "none"};
338
- },
339
- };
340
-
341
- const _getShouldShowInstructions: (Props) => boolean = (props) => {
342
- return (
343
- _isClickToAddPoints(props) &&
344
- (props.graph.coords == null || props.graph.coords.length === 0)
345
- );
346
- };
347
-
348
- const _isClickToAddPoints: (Props) => boolean = (props) => {
349
- return (
350
- (props.graph.type === "point" && props.graph.numPoints === UNLIMITED) ||
351
- (props.graph.type === "polygon" && props.graph.numSides === UNLIMITED)
352
- );
353
- };
354
-
355
- const makeInvalidTypeError = (
356
- functionName: string,
357
- graphType: string,
358
- ): PerseusError => {
359
- return new PerseusError(
360
- `${functionName} called but current graph type is not a '${graphType}'`,
361
- Errors.NotAllowed,
362
- {metadata: {graphType}},
363
- );
364
- };
365
-
366
- type RenderProps = PerseusInteractiveGraphWidgetOptions; // There's no transform function in exports
367
- type Rubric = PerseusInteractiveGraphWidgetOptions;
368
- type Props = WidgetProps<RenderProps, Rubric>;
369
- type State = $FlowFixMe;
370
- type DefaultProps = {|
371
- labels: Props["labels"],
372
- range: Props["range"],
373
- step: Props["step"],
374
- backgroundImage: Props["backgroundImage"],
375
- markings: Props["markings"],
376
- showTooltips: Props["showTooltips"],
377
- showProtractor: Props["showProtractor"],
378
- showRuler: Props["showRuler"],
379
- rulerLabel: Props["rulerLabel"],
380
- rulerTicks: Props["rulerTicks"],
381
- graph: Props["graph"],
382
- |};
383
-
384
- class InteractiveGraph extends React.Component<Props, State> {
385
- angle: ?$FlowFixMe;
386
- circle: ?$FlowFixMe;
387
- graphie: ?$FlowFixMe;
388
- horizHairline: ?$FlowFixMe;
389
- line: ?$FlowFixMe;
390
- lines: ?$ReadOnlyArray<$ReadOnlyArray<Coord>>;
391
- parabola: ?$FlowFixMe;
392
- pointA: ?$FlowFixMe;
393
- pointB: ?$FlowFixMe;
394
- pointC: ?$FlowFixMe;
395
- // eslint-disable-next-line ft-flow/no-mutable-array
396
- points: Array<$FlowFixMe>;
397
- polygon: ?$FlowFixMe;
398
- shouldResetGraphie: boolean;
399
- sinusoid: ?$FlowFixMe;
400
- trashCan: ?$FlowFixMe;
401
- vertHairline: ?$FlowFixMe;
402
-
403
- static defaultProps: DefaultProps = {
404
- labels: ["x", "y"],
405
- range: [
406
- [-10, 10],
407
- [-10, 10],
408
- ],
409
- step: [1, 1],
410
- // $FlowFixMe[prop-missing]
411
- backgroundImage: defaultBackgroundImage,
412
- markings: "graph",
413
- showTooltips: false,
414
- showProtractor: false,
415
- showRuler: false,
416
- rulerLabel: "",
417
- rulerTicks: 10,
418
- graph: {
419
- type: "linear",
420
- },
421
- };
422
-
423
- state: State = {
424
- shouldShowInstructions: _getShouldShowInstructions(this.props),
425
- };
426
-
427
- // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
428
- UNSAFE_componentWillMount() {
429
- DeprecationMixin.UNSAFE_componentWillMount.call(this);
430
- }
431
-
432
- componentDidMount() {
433
- // eslint-disable-next-line react/no-string-refs
434
- this.setGraphie(this.refs.graph.graphie());
435
- }
436
-
437
- UNSAFE_componentWillReceiveProps(nextProps: Props) {
438
- if (this.isClickToAddPoints() !== this.isClickToAddPoints(nextProps)) {
439
- this.shouldResetGraphie = true;
440
- this.setState({
441
- shouldShowInstructions: _getShouldShowInstructions(nextProps),
442
- });
443
- }
444
-
445
- if (
446
- this.props.backgroundImage?.url !==
447
- nextProps.backgroundImage?.url ||
448
- this.props.backgroundImage !== nextProps.backgroundImage ||
449
- this.props.containerSizeClass !== nextProps.containerSizeClass
450
- ) {
451
- this.shouldResetGraphie = true;
452
- }
453
- }
454
-
455
- componentDidUpdate(prevProps: Props, prevState: State) {
456
- // TODO(jeremy): This feels underspecified. For example, if the props
457
- // change the coords of a segment graph, but not the numSegments, we'd
458
- // not count that as an update.
459
- const oldType = prevProps.graph.type;
460
- const newType = this.props.graph.type;
461
- if (
462
- oldType !== newType ||
463
- (prevProps.graph.type === "angle" &&
464
- this.props.graph.type === "angle" &&
465
- this.areAngleGraphsEqual(prevProps.graph, this.props.graph)) ||
466
- (prevProps.graph.type === "point" &&
467
- this.props.graph.type === "point" &&
468
- this.arePointGraphsEqual(prevProps.graph, this.props.graph)) ||
469
- (prevProps.graph.type === "polygon" &&
470
- this.props.graph.type === "polygon" &&
471
- this.arePolygonGraphsEqual(
472
- prevProps.graph,
473
- this.props.graph,
474
- )) ||
475
- (prevProps.graph.type === "segment" &&
476
- this.props.graph.type === "segment" &&
477
- this.areSegmentGraphsEqual(prevProps.graph, this.props.graph))
478
- ) {
479
- // $FlowFixMe[incompatible-use]
480
- this["remove" + capitalize(oldType) + "Controls"]();
481
- // $FlowFixMe[incompatible-use]
482
- this["add" + capitalize(newType) + "Controls"]();
483
- }
484
- if (this.shouldResetGraphie) {
485
- this.resetGraphie();
486
- }
487
- }
488
-
489
- _getShouldShowInstructions: (?Props) => boolean = (props) => {
490
- props = props || this.props;
491
- return (
492
- this.isClickToAddPoints(props) &&
493
- (props.graph.coords == null || props.graph.coords.length === 0)
494
- );
495
- };
496
-
497
- deprecatedProps: $FlowFixMe = deprecatedProps;
498
-
499
- setGraphie: ($FlowFixMe) => void = (newGraphie) => {
500
- this.graphie = newGraphie;
501
- this.setupGraphie();
502
- };
503
-
504
- handleAddPointsMouseDown: (Coord) => void = (coord) => {
505
- // This function should only be called when this.isClickToAddPoints()
506
- // is true
507
- if (!this.isClickToAddPoints()) {
508
- throw new PerseusError(
509
- "handleAddPointsClick should not be registered" +
510
- "when isClickToAddPoints() is false",
511
- Errors.NotAllowed,
512
- );
513
- }
514
- if (!this.isCoordInTrash(coord)) {
515
- let point;
516
-
517
- if (this.props.graph.type === "point") {
518
- point = this.createPointForPointsType(
519
- coord,
520
- this.points.length,
521
- );
522
- if (!point.constrain()) {
523
- point.remove();
524
- return;
525
- }
526
- this.points.push(point);
527
-
528
- // interactive2 allows us to grab the point
529
- const idx = this.points.length - 1;
530
- this.points[idx].grab(coord);
531
-
532
- this.updateCoordsFromPoints();
533
- } else if (this.props.graph.type === "polygon") {
534
- if (this.polygon?.closed()) {
535
- return;
536
- }
537
- point = this.createPointForPolygonType(
538
- coord,
539
- this.points.length,
540
- );
541
- this.points.push(point);
542
-
543
- const idx = this.points.length - 1;
544
- this.points[idx].grab(coord);
545
-
546
- // We don't call updateCoordsFromPoints for
547
- // polygons, since the polygon won't be
548
- // closed yet.
549
- this.updatePolygon();
550
- }
551
-
552
- this.setState({
553
- shouldShowInstructions: false,
554
- });
555
- }
556
- };
557
-
558
- resetGraphie: () => void = () => {
559
- this.shouldResetGraphie = false;
560
- this.parabola = null;
561
- this.sinusoid = null;
562
- // eslint-disable-next-line react/no-string-refs
563
- this.refs.graph.reset();
564
- };
565
-
566
- setupGraphie: () => void = () => {
567
- this.setTrashCanVisibility(0);
568
- if (this.isClickToAddPoints()) {
569
- this.setTrashCanVisibility(0.5);
570
- }
571
-
572
- if (this.props.apiOptions.isMobile) {
573
- // $FlowFixMe[invalid-constructor]
574
- this.horizHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
575
- normalStyle: {
576
- strokeWidth: 1,
577
- },
578
- });
579
- this.horizHairline.attr({
580
- stroke: KhanColors.INTERACTIVE,
581
- });
582
- this.horizHairline?.hide();
583
-
584
- // $FlowFixMe[invalid-constructor]
585
- this.vertHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
586
- normalStyle: {
587
- strokeWidth: 1,
588
- },
589
- });
590
- this.vertHairline.attr({
591
- stroke: KhanColors.INTERACTIVE,
592
- });
593
- this.vertHairline?.hide();
594
- }
595
-
596
- const type = this.props.graph.type;
597
- // $FlowFixMe[incompatible-use]
598
- this["add" + capitalize(type) + "Controls"]();
599
- };
600
-
601
- showHairlines: (Coord) => void = (point) => {
602
- if (this.props.apiOptions.isMobile && this.props.markings !== "none") {
603
- // Hairlines are already initialized when the graph is loaded, so
604
- // here we just move them to the updated location and make them
605
- // visible.
606
- this.horizHairline?.moveTo(
607
- [this.props.range[0][0], point[1]],
608
- [this.props.range[0][1], point[1]],
609
- );
610
-
611
- this.horizHairline?.show();
612
-
613
- this.vertHairline?.moveTo(
614
- [point[0], this.props.range[1][0]],
615
- [point[0], this.props.range[1][1]],
616
- );
617
-
618
- this.vertHairline?.show();
619
- }
620
- };
621
-
622
- hideHairlines: () => void = () => {
623
- if (this.props.apiOptions.isMobile) {
624
- this.horizHairline?.hide();
625
- this.vertHairline?.hide();
626
- }
627
- };
628
-
629
- setTrashCanVisibility: (number) => void = (opacity) => {
630
- const graphie = this.graphie;
631
-
632
- if (knumber.equal(opacity, 0)) {
633
- if (this.trashCan) {
634
- this.trashCan.remove();
635
- this.trashCan = null;
636
- }
637
- } else if (!this.props.apiOptions.isMobile) {
638
- // Only if trash tooltips are not being used, we initialize the old
639
- // trash can area.
640
- if (!this.trashCan) {
641
- this.trashCan = graphie?.raphael?.image(
642
- TRASH_ICON_URI,
643
- graphie.xpixels - 40,
644
- graphie.ypixels - 40,
645
- 40,
646
- 40,
647
- );
648
- }
649
- this.trashCan?.attr({
650
- opacity: opacity,
651
- });
652
- }
653
- };
654
-
655
- isClickToAddPoints: (?Props) => boolean = (props) => {
656
- props = props || this.props;
657
- return _isClickToAddPoints(props);
658
- };
659
-
660
- areAngleGraphsEqual(
661
- prevGraph: PerseusGraphTypeAngle,
662
- currentGraph: PerseusGraphTypeAngle,
663
- ): boolean {
664
- return (
665
- prevGraph.allowReflexAngles !== currentGraph.allowReflexAngles ||
666
- prevGraph.angleOffsetDeg !== currentGraph.angleOffsetDeg ||
667
- prevGraph.snapDegrees !== currentGraph.snapDegrees
668
- );
669
- }
670
-
671
- arePointGraphsEqual(
672
- prevGraph: PerseusGraphTypePoint,
673
- currentGraph: PerseusGraphTypePoint,
674
- ): boolean {
675
- return prevGraph.numPoints !== currentGraph.numPoints;
676
- }
677
-
678
- arePolygonGraphsEqual(
679
- prevGraph: PerseusGraphTypePolygon,
680
- currentGraph: PerseusGraphTypePolygon,
681
- ): boolean {
682
- return (
683
- prevGraph.numSides !== currentGraph.numSides ||
684
- prevGraph.showAngles !== currentGraph.showAngles ||
685
- prevGraph.showSides !== currentGraph.showSides ||
686
- prevGraph.snapTo !== currentGraph.snapTo
687
- );
688
- }
689
-
690
- areSegmentGraphsEqual(
691
- prevGraph: PerseusGraphTypeSegment,
692
- currentGraph: PerseusGraphTypeSegment,
693
- ): boolean {
694
- return prevGraph.numSegments !== currentGraph.numSegments;
695
- }
696
-
697
- _lineStroke: () => {|"stroke-width"?: number|} = () => {
698
- // This should probably use: this.props.apiOptions.isMobile
699
- // $FlowFixMe[prop-missing]
700
- // $FlowFixMe[incompatible-exact]
701
- return this.props.isMobile ? {"stroke-width": 3} : {};
702
- };
703
-
704
- addLine: (string) => void = (type) => {
705
- const self = this;
706
- const graphie = self.graphie;
707
- const coords = InteractiveGraph.getLineCoords(
708
- self.props.graph,
709
- self.props,
710
- );
711
-
712
- const points = (self.points = _.map(coords, (coord) => {
713
- return Interactive2.addMaybeMobileMovablePoint(this, {
714
- coord: coord,
715
- constraints: [
716
- // $FlowFixMe[prop-missing]
717
- Interactive2.MovablePoint.constraints.bound(),
718
- // $FlowFixMe[prop-missing]
719
- Interactive2.MovablePoint.constraints.snap(),
720
- ],
721
- onMove: () => {
722
- const graph = _.extend({}, self.props.graph, {
723
- coords: _.invoke(points, "coord"),
724
- });
725
- self.onChange({graph: graph});
726
- },
727
- });
728
- }));
729
-
730
- const lineConfig = {
731
- points: points,
732
- static: true,
733
- normalStyle: {
734
- stroke: this.props.apiOptions.isMobile
735
- ? KhanColors.BLUE_C
736
- : KhanColors.INTERACTIVE,
737
- ...this._lineStroke(),
738
- },
739
- };
740
-
741
- if (type === "line") {
742
- // $FlowFixMe[prop-missing]
743
- lineConfig.extendLine = true;
744
- } else if (type === "ray") {
745
- // $FlowFixMe[prop-missing]
746
- lineConfig.extendRay = true;
747
- }
748
-
749
- self.line = Interactive2.addMovableLine(graphie, lineConfig);
750
-
751
- // A and B can't be in the same place
752
- points[0].listen("constraints", "isLine", (coord) => {
753
- return !kpoint.equal(coord, points[1].coord());
754
- });
755
- points[1].listen("constraints", "isLine", (coord) => {
756
- return !kpoint.equal(coord, points[0].coord());
757
- });
758
- };
759
-
760
- removeLine: () => void = () => {
761
- _.invoke(this.points, "remove");
762
- this.line?.remove();
763
- };
764
-
765
- addLinearControls: () => void = () => {
766
- this.addLine("line");
767
- };
768
-
769
- removeLinearControls: () => void = () => {
770
- this.removeLine();
771
- };
772
-
773
- addQuadraticControls: () => void = () => {
774
- if (this.props.graph.type !== "quadratic") {
775
- throw makeInvalidTypeError("addQuadraticControls", "quadratic");
776
- }
777
-
778
- let coords = this.props.graph.coords;
779
- if (!coords) {
780
- coords = InteractiveGraph.defaultQuadraticCoords(this.props);
781
- }
782
-
783
- const onMoveHandler = () => {
784
- const graph = _.extend({}, this.props.graph, {
785
- // $FlowFixMe[incompatible-use]
786
- coords: [pointA.coord(), pointB.coord(), pointC.coord()],
787
- });
788
- this.onChange({graph});
789
- this.updateQuadratic();
790
- };
791
-
792
- // NOTE(jeresig): This code attempts to access the variable while it's
793
- // still being defined, it should be refactored! The callbacks appear
794
- // to be executed synchronously, which causes this issue.
795
- let pointA = null;
796
- pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
797
- coord: coords[0],
798
- constraints: [
799
- // $FlowFixMe[prop-missing]
800
- Interactive2.MovablePoint.constraints.bound(),
801
- // $FlowFixMe[prop-missing]
802
- Interactive2.MovablePoint.constraints.snap(),
803
- (coord) => {
804
- return (
805
- !pointA ||
806
- // $FlowFixMe[incompatible-use]
807
- (coord[0] !== pointB.coord()[0] &&
808
- // $FlowFixMe[incompatible-use]
809
- coord[0] !== pointC.coord()[0])
810
- );
811
- },
812
- ],
813
- onMove: onMoveHandler,
814
- });
815
-
816
- // NOTE(jeresig): This code attempts to access the variable while it's
817
- // still being defined, it should be refactored! The callbacks appear
818
- // to be executed synchronously, which causes this issue.
819
- let pointB = null;
820
- pointB = this.pointB = Interactive2.addMaybeMobileMovablePoint(this, {
821
- coord: coords[1],
822
- constraints: [
823
- // $FlowFixMe[prop-missing]
824
- Interactive2.MovablePoint.constraints.bound(),
825
- // $FlowFixMe[prop-missing]
826
- Interactive2.MovablePoint.constraints.snap(),
827
- (coord) => {
828
- return (
829
- !pointB ||
830
- // $FlowFixMe[incompatible-use]
831
- (coord[0] !== pointA.coord()[0] &&
832
- // $FlowFixMe[incompatible-use]
833
- coord[0] !== pointC.coord()[0])
834
- );
835
- },
836
- ],
837
- onMove: onMoveHandler,
838
- });
839
-
840
- // NOTE(jeresig): This code attempts to access the variable while it's
841
- // still being defined, it should be refactored! The callbacks appear
842
- // to be executed synchronously, which causes this issue.
843
- let pointC = null;
844
- pointC = this.pointC = Interactive2.addMaybeMobileMovablePoint(this, {
845
- coord: coords[2],
846
- constraints: [
847
- // $FlowFixMe[prop-missing]
848
- Interactive2.MovablePoint.constraints.bound(),
849
- // $FlowFixMe[prop-missing]
850
- Interactive2.MovablePoint.constraints.snap(),
851
- (coord) => {
852
- return (
853
- !pointC ||
854
- // $FlowFixMe[incompatible-use]
855
- (coord[0] !== pointA.coord()[0] &&
856
- // $FlowFixMe[incompatible-use]
857
- coord[0] !== pointB.coord()[0])
858
- );
859
- },
860
- ],
861
- onMove: onMoveHandler,
862
- });
863
-
864
- this.updateQuadratic();
865
- };
866
-
867
- updateQuadratic: () => void = () => {
868
- const coeffs = InteractiveGraph.getCurrentQuadraticCoefficients(
869
- this.props,
870
- );
871
- if (!coeffs) {
872
- return;
873
- }
874
-
875
- // Extract coefficients the parabola
876
- const a = coeffs[0];
877
- const b = coeffs[1];
878
- const c = coeffs[2];
879
-
880
- // Plot and style
881
- if (this.parabola) {
882
- const path = this.graphie?.svgParabolaPath(a, b, c);
883
- this.parabola?.attr({path: path});
884
- } else {
885
- this.parabola = this.graphie?.parabola(a, b, c);
886
- this.parabola?.attr({
887
- stroke: this.props.apiOptions.isMobile
888
- ? KhanColors.BLUE_C
889
- : KhanColors.DYNAMIC,
890
- ...this._lineStroke(),
891
- });
892
- this.parabola?.toBack();
893
- }
894
- };
895
-
896
- removeQuadraticControls: () => void = () => {
897
- this.pointA?.remove();
898
- this.pointB?.remove();
899
- this.pointC?.remove();
900
- if (this.parabola) {
901
- this.parabola.remove();
902
- this.parabola = null;
903
- }
904
- };
905
-
906
- addSinusoidControls: () => void = () => {
907
- if (this.props.graph.type !== "sinusoid") {
908
- throw makeInvalidTypeError("addSinusoidControls", "sinusoid");
909
- }
910
- let coords = this.props.graph.coords;
911
- if (!coords) {
912
- coords = InteractiveGraph.defaultSinusoidCoords(this.props);
913
- }
914
-
915
- const onMoveHandler = () => {
916
- const graph = _.extend({}, this.props.graph, {
917
- // $FlowFixMe[incompatible-use]
918
- coords: [pointA.coord(), pointB.coord()],
919
- });
920
- this.onChange({graph: graph});
921
- this.updateSinusoid();
922
- };
923
-
924
- // NOTE(jeresig): This code attempts to access the variable while it's
925
- // still being defined, it should be refactored! The callbacks appear
926
- // to be executed synchronously, which causes this issue.
927
- let pointA = null;
928
- pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
929
- coord: coords[0],
930
- constraints: [
931
- // $FlowFixMe[prop-missing]
932
- Interactive2.MovablePoint.constraints.bound(),
933
- // $FlowFixMe[prop-missing]
934
- Interactive2.MovablePoint.constraints.snap(),
935
- (coord) => {
936
- return !pointA || coord[0] !== pointB.coord()[0];
937
- },
938
- ],
939
- onMove: onMoveHandler,
940
- });
941
-
942
- const pointB = (this.pointB = Interactive2.addMaybeMobileMovablePoint(
943
- this,
944
- {
945
- coord: coords[1],
946
- constraints: [
947
- // $FlowFixMe[prop-missing]
948
- Interactive2.MovablePoint.constraints.bound(),
949
- // $FlowFixMe[prop-missing]
950
- Interactive2.MovablePoint.constraints.snap(),
951
- (coord) => {
952
- return !pointA || coord[0] !== pointA.coord()[0];
953
- },
954
- ],
955
- onMove: onMoveHandler,
956
- },
957
- ));
958
-
959
- this.updateSinusoid();
960
- };
961
-
962
- updateSinusoid: () => void = () => {
963
- const coeffs = InteractiveGraph.getCurrentSinusoidCoefficients(
964
- this.props,
965
- );
966
- if (!coeffs) {
967
- return;
968
- }
969
-
970
- const a = coeffs[0];
971
- const b = coeffs[1];
972
- const c = coeffs[2];
973
- const d = coeffs[3];
974
-
975
- // Plot and style
976
- if (this.sinusoid) {
977
- const path = this.graphie?.svgSinusoidPath(a, b, c, d);
978
- this.sinusoid?.attr({path: path});
979
- } else {
980
- this.sinusoid = this.graphie?.sinusoid(a, b, c, d);
981
- this.sinusoid?.attr({
982
- stroke: this.props.apiOptions.isMobile
983
- ? KhanColors.BLUE_C
984
- : KhanColors.DYNAMIC,
985
- ...this._lineStroke(),
986
- });
987
- this.sinusoid?.toBack();
988
- }
989
- };
990
-
991
- removeSinusoidControls: () => void = () => {
992
- this.pointA?.remove();
993
- this.pointB?.remove();
994
- if (this.sinusoid) {
995
- this.sinusoid.remove();
996
- this.sinusoid = null;
997
- }
998
- };
999
-
1000
- addCircleControls: () => void = () => {
1001
- const graphie = this.graphie;
1002
- // $FlowFixMe[incompatible-use]
1003
- const minSnap = _.min(graphie.snap);
1004
-
1005
- const circle = (this.circle = graphie?.addCircleGraph({
1006
- center: this.props.graph.center || [0, 0],
1007
- radius: this.props.graph.radius || _.min(this.props.step),
1008
- snapX: graphie.snap[0],
1009
- snapY: graphie.snap[1],
1010
- minRadius: minSnap,
1011
- snapRadius: minSnap,
1012
- }));
1013
-
1014
- $(circle).on("move", () => {
1015
- const graph = _.extend({}, this.props.graph, {
1016
- // $FlowFixMe[incompatible-use]
1017
- center: circle.center,
1018
- // $FlowFixMe[incompatible-use]
1019
- radius: circle.radius,
1020
- });
1021
- this.onChange({graph: graph});
1022
- });
1023
- };
1024
-
1025
- removeCircleControls: () => void = () => {
1026
- this.circle?.remove();
1027
- };
1028
-
1029
- addLinearSystemControls: () => void = () => {
1030
- const graphie = this.graphie;
1031
- const coords = InteractiveGraph.getLinearSystemCoords(
1032
- this.props.graph,
1033
- this.props,
1034
- );
1035
-
1036
- const segmentColors = [KhanColors.INTERACTIVE, KhanColors.GREEN];
1037
- const points = (this.points = _.map(
1038
- coords,
1039
- (segmentCoords, segmentIndex) => {
1040
- // NOTE(jeresig): This code attempts to access the variable
1041
- // while it's still being defined, it should be refactored!
1042
- // The callbacks appear to be executed synchronously, which
1043
- // causes this issue.
1044
- const segmentPoints = [];
1045
-
1046
- for (let i = 0; i < segmentCoords.length; i += 1) {
1047
- const coord = segmentCoords[i];
1048
- segmentPoints.push(
1049
- Interactive2.addMaybeMobileMovablePoint(this, {
1050
- coord: coord,
1051
- constraints: [
1052
- // $FlowFixMe[prop-missing]
1053
- Interactive2.MovablePoint.constraints.bound(),
1054
- // $FlowFixMe[prop-missing]
1055
- Interactive2.MovablePoint.constraints.snap(),
1056
- (coord) => {
1057
- const otherSegment = segmentPoints[1 - i];
1058
- if (!otherSegment) {
1059
- // segment hasn't been defined yet
1060
- // because we're still creating them
1061
- return;
1062
- }
1063
- return !kpoint.equal(
1064
- coord,
1065
- otherSegment.coord(),
1066
- );
1067
- },
1068
- ],
1069
- onMove: () => {
1070
- const graph = _.extend({}, this.props.graph, {
1071
- coords: _.map(this.points, (segment) =>
1072
- _.invoke(segment, "coord"),
1073
- ),
1074
- });
1075
- this.onChange({graph: graph});
1076
- },
1077
- normalStyle: {
1078
- fill: segmentColors[segmentIndex],
1079
- },
1080
- highlightStyle: {
1081
- fill: segmentColors[segmentIndex],
1082
- },
1083
- }),
1084
- );
1085
- }
1086
-
1087
- return segmentPoints;
1088
- },
1089
- ));
1090
-
1091
- this.lines = _.map(points, (segmentPoints, segmentIndex) => {
1092
- return Interactive2.addMovableLine(graphie, {
1093
- points: segmentPoints,
1094
- static: true,
1095
- extendLine: true,
1096
- normalStyle: {
1097
- stroke: segmentColors[segmentIndex],
1098
- },
1099
- });
1100
- });
1101
- };
1102
-
1103
- removeLinearSystemControls: () => void = () => {
1104
- _.invoke(this.lines, "remove");
1105
- _.map(this.points, (segment) => _.invoke(segment, "remove"));
1106
- };
1107
-
1108
- isCoordInTrash: (Coord) => boolean = (coord) => {
1109
- if (this.props.apiOptions.isMobile) {
1110
- return false;
1111
- }
1112
-
1113
- const graphie = this.graphie;
1114
- const screenPoint = graphie?.scalePoint(coord);
1115
- return (
1116
- // $FlowFixMe[incompatible-use]
1117
- screenPoint[0] >= graphie.xpixels - 40 &&
1118
- // $FlowFixMe[incompatible-use]
1119
- screenPoint[1] >= graphie.ypixels - 40
1120
- );
1121
- };
1122
-
1123
- createPointForPointsType: (Coord, number) => $FlowFixMe = (coord, i) => {
1124
- const self = this;
1125
-
1126
- const remove = () => {
1127
- self.points = _.filter(self.points, function (pt) {
1128
- return pt !== point;
1129
- });
1130
- // update the correct answer box
1131
- self.updateCoordsFromPoints();
1132
-
1133
- // remove this movablePoint from graphie.
1134
- // we wait to do this until we're not inside of
1135
- // said point's onMoveEnd method so its state is
1136
- // consistent throughout this method call
1137
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
1138
- // $FlowFixMe[incompatible-use]
1139
- setTimeout(point.remove.bind(point), 0); // eslint-disable-line no-restricted-syntax
1140
- };
1141
-
1142
- // NOTE(jeresig): This code attempts to access the variable while it's
1143
- // still being defined, it should be refactored! The callbacks appear
1144
- // to be executed synchronously, which causes this issue.
1145
- let point = null;
1146
- point = Interactive2.addMaybeMobileMovablePoint(this, {
1147
- coord: coord,
1148
- constraints: [
1149
- // $FlowFixMe[prop-missing]
1150
- Interactive2.MovablePoint.constraints.bound(),
1151
- // $FlowFixMe[prop-missing]
1152
- Interactive2.MovablePoint.constraints.snap(),
1153
- function (coord) {
1154
- // TODO(jack): There ought to be a
1155
- // MovablePoint.constraints.avoid
1156
- // default that lets you do things like this
1157
- return _.all(self.points, function (pt) {
1158
- return point === pt || !kpoint.equal(coord, pt.coord());
1159
- });
1160
- },
1161
- ],
1162
- onMoveStart: function () {
1163
- if (self.isClickToAddPoints()) {
1164
- self.setTrashCanVisibility(1);
1165
- }
1166
- },
1167
- onMove: self.updateCoordsFromPoints,
1168
- onMoveEnd: function (coord) {
1169
- if (self.isClickToAddPoints()) {
1170
- if (self.isCoordInTrash(coord)) {
1171
- remove();
1172
- }
1173
- // In case we mouseup'd off the graphie and that
1174
- // stopped the move (in which case, we might not
1175
- // be in isCoordInTrash()
1176
- self.setTrashCanVisibility(0.5);
1177
- }
1178
- },
1179
- ...(this.props.apiOptions.isMobile && self.isClickToAddPoints()
1180
- ? {onRemove: remove}
1181
- : {}),
1182
- });
1183
-
1184
- return point;
1185
- };
1186
-
1187
- removePoint: (Coord) => ?number = (point) => {
1188
- let index = null;
1189
- this.points = _.filter(this.points, function (pt, i) {
1190
- if (pt === point) {
1191
- index = i;
1192
- return false;
1193
- }
1194
- return true;
1195
- });
1196
- return index;
1197
- };
1198
-
1199
- createPointForPolygonType: (Coord, number) => $FlowFixMe = (coord, i) => {
1200
- if (this.props.graph.type !== "polygon") {
1201
- throw makeInvalidTypeError("createPointForPolygonType", "polygon");
1202
- }
1203
-
1204
- // TODO(alex): check against "grid" instead, use constants
1205
- const snapToGrid = !_.contains(
1206
- ["angles", "sides"],
1207
- this.props.graph.snapTo,
1208
- );
1209
-
1210
- // Index relative to current point -> absolute index
1211
- // NOTE: This does not work when isClickToAddPoints() == true,
1212
- // as `i` can be changed by dragging a point to the trash
1213
- // Currently this function is only called when !isClickToAddPoints()
1214
- const rel = (j) => {
1215
- return (i + j + this.points.length) % this.points.length;
1216
- };
1217
-
1218
- const remove = () => {
1219
- // remove this point from points
1220
- const index = this.removePoint(point);
1221
- if (this.polygon?.closed()) {
1222
- // We should be checking if this.points is defined before rotating them.
1223
- // $FlowFixMe[incompatible-call]
1224
- // $FlowFixMe[incompatible-type]
1225
- this.points = rotate(this.points, index);
1226
- this.polygon?.update({closed: false});
1227
- }
1228
- this.updatePolygon();
1229
- // the polygon is now unclosed, so we need to
1230
- // remove any points props
1231
- this.clearCoords();
1232
-
1233
- // remove this movablePoint from graphie.
1234
- // wait to do this until we're not inside of
1235
- // said point's onMoveEnd method so state is
1236
- // consistent throughout the method call
1237
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
1238
- // eslint-disable-next-line no-restricted-syntax
1239
- setTimeout(point.remove.bind(point), 0);
1240
- };
1241
-
1242
- const setInitialMove = debounce((point, bool) => {
1243
- point.state.isInitialMove = bool;
1244
- }, 1000);
1245
-
1246
- const onMoveEndHandler = (coord) => {
1247
- // If the user clicked and dragged a point over endpoint,
1248
- // join the them
1249
- const overlappedPoints =
1250
- this.points.length > 1 &&
1251
- ((point === this.points[0] &&
1252
- kpoint.equal(coord, _.last(this.points).coord())) ||
1253
- (point === _.last(this.points) &&
1254
- kpoint.equal(coord, this.points[0].coord())));
1255
-
1256
- if (this.isClickToAddPoints()) {
1257
- if (this.isCoordInTrash(coord)) {
1258
- remove();
1259
- } else if (overlappedPoints) {
1260
- const pointToRemove = this.points.pop();
1261
-
1262
- if (this.points.length > 2) {
1263
- this.polygon?.update({closed: true});
1264
- this.updateCoordsFromPoints();
1265
- } else {
1266
- this.polygon?.update({closed: false});
1267
- this.clearCoords();
1268
- }
1269
-
1270
- this.updatePolygon();
1271
- // remove this movablePoint from graphie.
1272
- // wait to do this until we're not inside of
1273
- // said point's onMoveEnd method so state is
1274
- // consistent throughout the method call
1275
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
1276
- // eslint-disable-next-line no-restricted-syntax
1277
- setTimeout(pointToRemove.remove.bind(pointToRemove), 0);
1278
- } else {
1279
- // If the user clicked and dragged a point over any other
1280
- // existing point, fix shape
1281
- const shouldRemove = _.any(this.points, function (pt) {
1282
- return pt !== point && kpoint.equal(pt.coord(), coord);
1283
- });
1284
-
1285
- if (shouldRemove) {
1286
- this.removePoint(point);
1287
-
1288
- if (this.points.length < 3) {
1289
- this.polygon?.update({closed: false});
1290
- this.clearCoords();
1291
- } else if (this.polygon?.closed()) {
1292
- this.updateCoordsFromPoints();
1293
- }
1294
-
1295
- this.updatePolygon();
1296
- // remove this movablePoint from graphie.
1297
- // wait to do this until we're not inside
1298
- // said point's onMoveEnd method so state
1299
- // is consistent throughout the method call
1300
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
1301
- // eslint-disable-next-line no-restricted-syntax
1302
- setTimeout(point.remove.bind(point), 0);
1303
- } else {
1304
- // If this was
1305
- // * a click on the first or last point
1306
- // * and not a deletion
1307
- // * and not a drag,
1308
- // * and not a creation of a new point
1309
- // (see !point.state.isInitialMove, below),
1310
- // * and our polygon is not closed,
1311
- // * and we can close it (we need at least the same
1312
- // number of points as coordinates asked for in the
1313
- // question),
1314
- // then close it
1315
- const isFirstOrLastPoint =
1316
- point === _.head(this.points) ||
1317
- point === _.last(this.points);
1318
- const pointNotMoved = !point.hasMoved();
1319
- // NOTE (jsatk): Currently different on mobile versus
1320
- // destkop
1321
- const notANewPoint = !point.state.isInitialMove;
1322
- const polygonNotClosed = !this.polygon?.closed();
1323
- const allPointsPlotted = this.points.length > 2;
1324
-
1325
- const canCloseUnclosedPolygon =
1326
- isFirstOrLastPoint &&
1327
- pointNotMoved &&
1328
- notANewPoint &&
1329
- polygonNotClosed &&
1330
- allPointsPlotted;
1331
-
1332
- if (canCloseUnclosedPolygon) {
1333
- this.polygon?.update({closed: true});
1334
- this.updatePolygon();
1335
-
1336
- // We finally have a closed polygon, so save our
1337
- // points to props
1338
- this.updateCoordsFromPoints();
1339
- }
1340
- }
1341
- }
1342
-
1343
- // In case we mouseup'd off the graphie and that
1344
- // stopped the move
1345
- this.setTrashCanVisibility(0.5);
1346
- }
1347
-
1348
- setInitialMove(point, false);
1349
- };
1350
-
1351
- const graphConstraint = (coord) => {
1352
- // These constraints are all relative to the other points, so if
1353
- // we're creating the initial points and haven't added any others
1354
- // to the graph, we can't enforce them.
1355
- if (this.points == null || this.points.length === 0) {
1356
- return true;
1357
- }
1358
-
1359
- // eslint-disable-next-line ft-flow/no-mutable-array
1360
- const coords: Array<Coord> = _.invoke(this.points, "coord");
1361
- coords[i] = coord;
1362
-
1363
- // Check for invalid positioning, but only if we aren't adding
1364
- // points one click at a time, since those added points could
1365
- // have already violated these constraints
1366
- if (!this.isClickToAddPoints()) {
1367
- // Polygons can't have consecutive collinear points
1368
- if (
1369
- collinear(coords[rel(-2)], coords[rel(-1)], coords[i]) ||
1370
- collinear(coords[rel(-1)], coords[i], coords[rel(1)]) ||
1371
- collinear(coords[i], coords[rel(1)], coords[rel(2)])
1372
- ) {
1373
- return false;
1374
- }
1375
-
1376
- const segments = _.zip(coords, rotate(coords));
1377
-
1378
- if (this.points.length > 3) {
1379
- // Constrain to simple (non self-intersecting) polygon by
1380
- // testing whether adjacent segments intersect any others
1381
- for (let j = -1; j <= 0; j++) {
1382
- const segment = segments[rel(j)];
1383
- const others = _.without(
1384
- segments,
1385
- segment,
1386
- segments[rel(j - 1)],
1387
- segments[rel(j + 1)],
1388
- );
1389
-
1390
- for (let k = 0; k < others.length; k++) {
1391
- const other = others[k];
1392
- if (intersects(segment, other)) {
1393
- return false;
1394
- }
1395
- }
1396
- }
1397
- }
1398
- }
1399
-
1400
- if (
1401
- this.props.graph.snapTo === "angles" &&
1402
- this.points.length > 2
1403
- ) {
1404
- // Snap to whole degree interior angles
1405
-
1406
- const angles = _.map(angleMeasures(coords), function (rad) {
1407
- return (rad * 180) / Math.PI;
1408
- });
1409
-
1410
- _.each([-1, 1], function (j) {
1411
- angles[rel(j)] = Math.round(angles[rel(j)]);
1412
- });
1413
-
1414
- const getAngle = function (a, vertex, b) {
1415
- const angle = GraphUtils.findAngle(
1416
- coords[rel(a)],
1417
- coords[rel(b)],
1418
- coords[rel(vertex)],
1419
- );
1420
- return (angle + 360) % 360;
1421
- };
1422
-
1423
- const innerAngles = [
1424
- angles[rel(-1)] - getAngle(-2, -1, 1),
1425
- angles[rel(1)] - getAngle(-1, 1, 2),
1426
- ];
1427
- innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
1428
-
1429
- // Avoid degenerate triangles
1430
- if (
1431
- _.any(innerAngles, function (angle) {
1432
- return leq(angle, 1);
1433
- })
1434
- ) {
1435
- return false;
1436
- }
1437
-
1438
- const knownSide = magnitude(
1439
- vector(coords[rel(-1)], coords[rel(1)]),
1440
- );
1441
-
1442
- const onLeft =
1443
- sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
1444
-
1445
- // Solve for side by using the law of sines
1446
- const side =
1447
- (Math.sin((innerAngles[1] * Math.PI) / 180) /
1448
- Math.sin((innerAngles[2] * Math.PI) / 180)) *
1449
- knownSide;
1450
-
1451
- const outerAngle = GraphUtils.findAngle(
1452
- coords[rel(1)],
1453
- coords[rel(-1)],
1454
- );
1455
-
1456
- const offset = this.graphie?.polar(
1457
- side,
1458
- outerAngle + (onLeft ? 1 : -1) * innerAngles[0],
1459
- );
1460
-
1461
- return this.graphie?.addPoints(coords[rel(-1)], offset);
1462
- }
1463
- if (this.props.graph.snapTo === "sides" && this.points.length > 1) {
1464
- // Snap to whole unit side measures
1465
-
1466
- const sides = _.map(
1467
- [
1468
- [coords[rel(-1)], coords[i]],
1469
- [coords[i], coords[rel(1)]],
1470
- [coords[rel(-1)], coords[rel(1)]],
1471
- ],
1472
- function (coords) {
1473
- return magnitude(vector(...coords));
1474
- },
1475
- );
1476
-
1477
- _.each([0, 1], function (j) {
1478
- sides[j] = Math.round(sides[j]);
1479
- });
1480
-
1481
- // Avoid degenerate triangles
1482
- if (
1483
- leq(sides[1] + sides[2], sides[0]) ||
1484
- leq(sides[0] + sides[2], sides[1]) ||
1485
- leq(sides[0] + sides[1], sides[2])
1486
- ) {
1487
- return false;
1488
- }
1489
-
1490
- // Solve for angle by using the law of cosines
1491
- const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
1492
-
1493
- const outerAngle = GraphUtils.findAngle(
1494
- coords[rel(1)],
1495
- coords[rel(-1)],
1496
- );
1497
-
1498
- const onLeft =
1499
- sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
1500
-
1501
- const offset = this.graphie?.polar(
1502
- sides[0],
1503
- outerAngle + (onLeft ? 1 : -1) * innerAngle,
1504
- );
1505
-
1506
- return this.graphie?.addPoints(coords[rel(-1)], offset);
1507
- }
1508
- // Snap to grid (already done)
1509
- return true;
1510
- };
1511
-
1512
- const point = Interactive2.addMaybeMobileMovablePoint(this, {
1513
- coord: coord,
1514
- constraints: [
1515
- // $FlowFixMe[prop-missing]
1516
- Interactive2.MovablePoint.constraints.bound(),
1517
- snapToGrid
1518
- ? // $FlowFixMe[prop-missing]
1519
- Interactive2.MovablePoint.constraints.snap()
1520
- : null,
1521
- graphConstraint,
1522
- ],
1523
- onMoveStart: () => {
1524
- if (this.isClickToAddPoints()) {
1525
- this.setTrashCanVisibility(1);
1526
- }
1527
- },
1528
- onMove: () => {
1529
- if (this.polygon?.closed()) {
1530
- this.updateCoordsFromPoints();
1531
- }
1532
- },
1533
- onMoveEnd: onMoveEndHandler,
1534
- ...(this.props.apiOptions.isMobile && this.isClickToAddPoints()
1535
- ? {onRemove: remove}
1536
- : {}),
1537
- });
1538
-
1539
- point.state.isInitialMove = true;
1540
-
1541
- return point;
1542
- };
1543
-
1544
- updateCoordsFromPoints: () => void = () => {
1545
- const graph = _.extend({}, this.props.graph, {
1546
- // Handle old movable points with .coord, or
1547
- // Interactive2.MovablePoint's with .coord()
1548
- coords: _.map(this.points, function (point) {
1549
- return _.result(point, "coord");
1550
- }),
1551
- });
1552
- this.onChange({graph: graph});
1553
- };
1554
-
1555
- clearCoords: () => void = () => {
1556
- const graph = _.extend({}, this.props.graph, {
1557
- coords: null,
1558
- });
1559
- this.onChange({graph: graph});
1560
- };
1561
-
1562
- onChange: ($FlowFixMe) => void = (data) => {
1563
- this.props.onChange(data);
1564
- this.props.trackInteraction();
1565
- };
1566
-
1567
- addPointControls: () => void = () => {
1568
- if (this.props.graph.type !== "point") {
1569
- throw makeInvalidTypeError("addPointControls", "point");
1570
- }
1571
-
1572
- const coords = InteractiveGraph.getPointCoords(
1573
- this.props.graph,
1574
- this.props,
1575
- );
1576
- // Clear out our old points so that newly added points don't
1577
- // "collide" with them and reposition when being added
1578
- // Without this, when added, each point checks whether it is on top
1579
- // of a point in this.points, which (a) shouldn't matter since
1580
- // we're clearing out this.points anyways, and (b) can cause problems
1581
- // if each of this.points is a MovablePoint instead of an
1582
- // Interactive2.MovablePoint, since one has a .coord and the other
1583
- // has .coord()
1584
- // TODO(jack): Figure out a better way to do this
1585
- this.points = [];
1586
- this.points = _.map(coords, this.createPointForPointsType, this);
1587
- };
1588
-
1589
- removePointControls: () => void = () => {
1590
- _.invoke(this.points, "remove");
1591
- };
1592
-
1593
- addSegmentControls: () => void = () => {
1594
- if (this.props.graph.type !== "segment") {
1595
- throw makeInvalidTypeError("addSegmentControls", "segment");
1596
- }
1597
-
1598
- const self = this;
1599
- const graphie = this.graphie;
1600
-
1601
- const coords = InteractiveGraph.getSegmentCoords(
1602
- this.props.graph,
1603
- this.props,
1604
- );
1605
-
1606
- const createPoint = (options) =>
1607
- Interactive2.addMaybeMobileMovablePoint(this, options);
1608
-
1609
- this.points = [];
1610
- this.lines = _.map(
1611
- coords,
1612
- function (segment, i) {
1613
- const updateCoordProps = function () {
1614
- const graph = _.extend({}, self.props.graph, {
1615
- coords: _.invoke(self.lines, "coords"),
1616
- });
1617
- self.onChange({graph: graph});
1618
- };
1619
-
1620
- // NOTE(jeresig): This code attempts to access the variable
1621
- // while it's still being defined, it should be refactored!
1622
- // The callbacks appear to be executed synchronously, which
1623
- // causes this issue.
1624
- const points = [];
1625
-
1626
- for (let i = 0; i < segment.length; i += 1) {
1627
- const coord = segment[i];
1628
- points.push(
1629
- createPoint({
1630
- coord: coord,
1631
- constraints: [
1632
- // $FlowFixMe[prop-missing]
1633
- Interactive2.MovablePoint.constraints.bound(),
1634
- // $FlowFixMe[prop-missing]
1635
- Interactive2.MovablePoint.constraints.snap(),
1636
- (coord) => {
1637
- const otherPoint = points[1 - i];
1638
- if (!otherPoint) {
1639
- // point hasn't been defined yet
1640
- // because we're still creating them
1641
- // We return false as the points aren't
1642
- // going to be in the same position at
1643
- // this point.
1644
- return false;
1645
- }
1646
- return !kpoint.equal(
1647
- coord,
1648
- otherPoint.coord(),
1649
- );
1650
- },
1651
- ],
1652
- onMove: updateCoordProps,
1653
- }),
1654
- );
1655
- }
1656
-
1657
- self.points = self.points.concat(points);
1658
- const line = Interactive2.addMovableLine(graphie, {
1659
- points: points,
1660
- static: false,
1661
- constraints: [
1662
- // $FlowFixMe[prop-missing]
1663
- Interactive2.MovableLine.constraints.bound(),
1664
- // $FlowFixMe[prop-missing]
1665
- Interactive2.MovableLine.constraints.snap(),
1666
- ],
1667
- onMove: [
1668
- // $FlowFixMe[prop-missing]
1669
- Interactive2.MovableLine.onMove.updatePoints,
1670
- updateCoordProps,
1671
- ],
1672
- normalStyle: {
1673
- stroke: this.props.apiOptions.isMobile
1674
- ? KhanColors.BLUE_C
1675
- : KhanColors.INTERACTIVE,
1676
- ...this._lineStroke(),
1677
- },
1678
- highlightStyle: {
1679
- stroke: this.props.apiOptions.isMobile
1680
- ? KhanColors.BLUE_C
1681
- : KhanColors.INTERACTING,
1682
- ...this._lineStroke(),
1683
- },
1684
- });
1685
- _.invoke(points, "toFront");
1686
-
1687
- return line;
1688
- },
1689
- this,
1690
- );
1691
- };
1692
-
1693
- removeSegmentControls: () => void = () => {
1694
- _.invoke(this.points, "remove");
1695
- _.invoke(this.lines, "remove");
1696
- };
1697
-
1698
- addRayControls: () => void = () => {
1699
- this.addLine("ray");
1700
- };
1701
-
1702
- removeRayControls: () => void = () => {
1703
- this.removeLine();
1704
- };
1705
-
1706
- addPolygonControls: () => void = () => {
1707
- this.polygon = null;
1708
- const coords = InteractiveGraph.getPolygonCoords(
1709
- this.props.graph,
1710
- this.props,
1711
- );
1712
- // Clear out our old points so that newly added points don't
1713
- // "collide", as in `addPointControls`
1714
- this.points = [];
1715
- this.points = _.map(coords, this.createPointForPolygonType, this);
1716
- this.updatePolygon();
1717
- };
1718
-
1719
- updatePolygon: () => void = () => {
1720
- if (this.props.graph.type !== "polygon") {
1721
- throw makeInvalidTypeError("updatePolygon", "polygon");
1722
- }
1723
-
1724
- let closed;
1725
- if (this.polygon) {
1726
- closed = this.polygon.closed();
1727
- } else if (this.points.length >= 3) {
1728
- closed = true;
1729
- } else {
1730
- // There will only be fewer than 3 points in click-to-add-vertices
1731
- // mode, so we don't need to explicitly check for that here.
1732
- closed = false;
1733
- }
1734
-
1735
- const graphie = this.graphie;
1736
- const n = this.points.length;
1737
-
1738
- // TODO(alex): check against "grid" instead, use constants
1739
- const snapToGrid = !_.contains(
1740
- ["angles", "sides"],
1741
- this.props.graph.snapTo,
1742
- );
1743
-
1744
- const angleLabels = _.times(
1745
- n,
1746
- function (i) {
1747
- if (
1748
- !this.props.graph.showAngles ||
1749
- (!closed && (i === 0 || i === n - 1))
1750
- ) {
1751
- return "";
1752
- }
1753
- if (this.props.graph.snapTo === "angles") {
1754
- return "$deg0";
1755
- }
1756
- return "$deg1";
1757
- },
1758
- this,
1759
- );
1760
-
1761
- const showRightAngleMarkers = _.times(
1762
- n,
1763
- function (i) {
1764
- return closed || (i !== 0 && i !== n - 1);
1765
- },
1766
- this,
1767
- );
1768
-
1769
- const numArcs = _.times(
1770
- n,
1771
- function (i) {
1772
- if (
1773
- this.props.graph.showAngles &&
1774
- (closed || (i !== 0 && i !== n - 1))
1775
- ) {
1776
- return 1;
1777
- }
1778
- return 0;
1779
- },
1780
- this,
1781
- );
1782
-
1783
- const sideLabels = _.times(
1784
- n,
1785
- function (i) {
1786
- if (!this.props.graph.showSides || (!closed && i === n - 1)) {
1787
- return "";
1788
- }
1789
- if (this.props.graph.snapTo === "sides") {
1790
- return "$len0";
1791
- }
1792
- return "$len1";
1793
- },
1794
- this,
1795
- );
1796
-
1797
- if (this.polygon == null) {
1798
- const self = this;
1799
- self.polygon = Interactive2.addMovablePolygon(graphie, {
1800
- constraints: [
1801
- // $FlowFixMe[prop-missing]
1802
- Interactive2.MovablePolygon.constraints.bound(),
1803
- snapToGrid
1804
- ? // $FlowFixMe[prop-missing]
1805
- Interactive2.MovablePolygon.constraints.snap()
1806
- : null,
1807
- ],
1808
- closed: closed,
1809
- points: self.points,
1810
- angleLabels: angleLabels,
1811
- showRightAngleMarkers: showRightAngleMarkers,
1812
- numArcs: numArcs,
1813
- sideLabels: sideLabels,
1814
- onMove: [
1815
- // $FlowFixMe[prop-missing]
1816
- Interactive2.MovablePolygon.onMove.updatePoints,
1817
- function () {
1818
- if (this.closed()) {
1819
- self.updateCoordsFromPoints();
1820
- }
1821
- },
1822
- ],
1823
- normalStyle: {
1824
- stroke: this.props.apiOptions.isMobile
1825
- ? KhanColors.BLUE_C
1826
- : KhanColors.INTERACTIVE,
1827
- ...this._lineStroke(),
1828
- },
1829
- });
1830
- } else {
1831
- // We only need to pass in the properties that might've changed
1832
- this.polygon.update({
1833
- closed: closed,
1834
- points: this.points,
1835
- angleLabels: angleLabels,
1836
- showRightAngleMarkers: showRightAngleMarkers,
1837
- numArcs: numArcs,
1838
- sideLabels: sideLabels,
1839
- });
1840
- }
1841
- };
1842
-
1843
- removePolygonControls: () => void = () => {
1844
- _.invoke(this.points, "remove");
1845
- this.polygon?.remove();
1846
- };
1847
-
1848
- addAngleControls: () => void = () => {
1849
- if (this.props.graph.type !== "angle") {
1850
- throw makeInvalidTypeError("addAngleControls", "angle");
1851
- }
1852
-
1853
- const graph = this.props.graph;
1854
- const graphie = this.graphie;
1855
-
1856
- const coords = InteractiveGraph.getAngleCoords(
1857
- this.props.graph,
1858
- this.props,
1859
- );
1860
-
1861
- // The vertex snaps to the grid, but the rays don't...
1862
- this.points = coords.map(function (coord, i) {
1863
- return graphie?.addMovablePoint(
1864
- _.extend(
1865
- {
1866
- coord: coord,
1867
- normalStyle: {
1868
- stroke: KhanColors.INTERACTIVE,
1869
- fill: KhanColors.INTERACTIVE,
1870
- },
1871
- },
1872
- i === 1
1873
- ? {
1874
- snapX: graphie.snap[0],
1875
- snapY: graphie.snap[1],
1876
- }
1877
- : {},
1878
- ),
1879
- );
1880
- });
1881
-
1882
- // ...they snap to whole-degree angles from the vertex.
1883
- this.angle = graphie?.addMovableAngle({
1884
- points: this.points,
1885
- snapDegrees: graph.snapDegrees || 1,
1886
- snapOffsetDeg: graph.angleOffsetDeg || 0,
1887
- angleLabel: graph.showAngles ? "$deg0" : "",
1888
- pushOut: 2,
1889
- allowReflex: defaultVal(graph.allowReflexAngles, true),
1890
- });
1891
-
1892
- $(this.angle).on("move", () => {
1893
- this.onChange({
1894
- graph: {...graph, coords: this.angle?.getClockwiseCoords()},
1895
- });
1896
- });
1897
- };
1898
-
1899
- removeAngleControls: () => void = () => {
1900
- _.invoke(this.points, "remove");
1901
- this.angle?.remove();
1902
- };
1903
-
1904
- toggleShowAngles: () => void = () => {
1905
- if (this.props.graph.type !== "polygon") {
1906
- throw makeInvalidTypeError("toggleShowAngles", "polygon");
1907
- }
1908
- const graph = _.extend({}, this.props.graph, {
1909
- showAngles: !this.props.graph.showAngles,
1910
- });
1911
- this.onChange({graph: graph});
1912
- };
1913
-
1914
- toggleShowSides: () => void = () => {
1915
- if (this.props.graph.type !== "polygon") {
1916
- throw makeInvalidTypeError("toggleShowSides", "polygon");
1917
- }
1918
- const graph = _.extend({}, this.props.graph, {
1919
- showSides: !this.props.graph.showSides,
1920
- });
1921
- this.onChange({graph: graph});
1922
- };
1923
-
1924
- getUserInput: () => PerseusGraphType = () => {
1925
- return InteractiveGraph.getUserInputFromProps(this.props);
1926
- };
1927
-
1928
- simpleValidate: (Rubric) => PerseusScore = (rubric) => {
1929
- return InteractiveGraph.validate(this.getUserInput(), rubric, this);
1930
- };
1931
-
1932
- focus: () => void = $.noop;
1933
-
1934
- render(): React.Node {
1935
- const box = getInteractiveBoxFromSizeClass(
1936
- this.props.containerSizeClass,
1937
- );
1938
-
1939
- let instructions;
1940
- if (this.isClickToAddPoints() && this.state.shouldShowInstructions) {
1941
- if (this.props.graph.type === "point") {
1942
- instructions = i18n._("Click to add points");
1943
- } else if (this.props.graph.type === "polygon") {
1944
- instructions = i18n._("Click to add vertices");
1945
- }
1946
- } else {
1947
- instructions = undefined;
1948
- }
1949
-
1950
- const onMouseDown = this.isClickToAddPoints()
1951
- ? this.handleAddPointsMouseDown
1952
- : null;
1953
-
1954
- const gridStep =
1955
- this.props.gridStep ||
1956
- Util.getGridStep(this.props.range, this.props.step, box[0]);
1957
- const snapStep =
1958
- this.props.snapStep || Util.snapStepFromGridStep(gridStep);
1959
-
1960
- const isMobile = this.props.apiOptions.isMobile;
1961
-
1962
- return (
1963
- <div
1964
- className={
1965
- "perseus-widget " + "perseus-widget-interactive-graph"
1966
- }
1967
- style={{
1968
- width: box[0],
1969
- height: box[1],
1970
- }}
1971
- >
1972
- <Graph
1973
- instructions={instructions}
1974
- // eslint-disable-next-line react/no-string-refs
1975
- ref="graph"
1976
- box={box}
1977
- labels={this.props.labels}
1978
- range={this.props.range}
1979
- step={
1980
- isMobile
1981
- ? Util.constrainedTickStepsFromTickSteps(
1982
- this.props.step,
1983
- this.props.range,
1984
- )
1985
- : this.props.step
1986
- }
1987
- gridStep={gridStep}
1988
- snapStep={snapStep}
1989
- markings={this.props.markings}
1990
- backgroundImage={this.props.backgroundImage}
1991
- showProtractor={this.props.showProtractor}
1992
- showRuler={this.props.showRuler}
1993
- rulerLabel={this.props.rulerLabel}
1994
- rulerTicks={this.props.rulerTicks}
1995
- onMouseDown={onMouseDown}
1996
- onGraphieUpdated={this.setGraphie}
1997
- setDrawingAreaAvailable={
1998
- this.props.apiOptions.setDrawingAreaAvailable
1999
- }
2000
- isMobile={isMobile}
2001
- />
2002
- </div>
2003
- );
2004
- }
2005
-
2006
- static getQuadraticCoefficients(
2007
- coords: $ReadOnlyArray<Coord>,
2008
- ): QuadraticCoefficient {
2009
- const p1 = coords[0];
2010
- const p2 = coords[1];
2011
- const p3 = coords[2];
2012
-
2013
- const denom = (p1[0] - p2[0]) * (p1[0] - p3[0]) * (p2[0] - p3[0]);
2014
- if (denom === 0) {
2015
- // Many of the callers assume that the return value is always defined.
2016
- // $FlowFixMe[incompatible-return]
2017
- return;
2018
- }
2019
- const a =
2020
- (p3[0] * (p2[1] - p1[1]) +
2021
- p2[0] * (p1[1] - p3[1]) +
2022
- p1[0] * (p3[1] - p2[1])) /
2023
- denom;
2024
- const b =
2025
- (p3[0] * p3[0] * (p1[1] - p2[1]) +
2026
- p2[0] * p2[0] * (p3[1] - p1[1]) +
2027
- p1[0] * p1[0] * (p2[1] - p3[1])) /
2028
- denom;
2029
- const c =
2030
- (p2[0] * p3[0] * (p2[0] - p3[0]) * p1[1] +
2031
- p3[0] * p1[0] * (p3[0] - p1[0]) * p2[1] +
2032
- p1[0] * p2[0] * (p1[0] - p2[0]) * p3[1]) /
2033
- denom;
2034
- return [a, b, c];
2035
- }
2036
-
2037
- static getSinusoidCoefficients(
2038
- coords: $ReadOnlyArray<Coord>,
2039
- ): SineCoefficient {
2040
- // It's assumed that p1 is the root and p2 is the first peak
2041
- const p1 = coords[0];
2042
- const p2 = coords[1];
2043
-
2044
- // Resulting coefficients are canonical for this sine curve
2045
- const amplitude = p2[1] - p1[1];
2046
- const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
2047
- const phase = p1[0] * angularFrequency;
2048
- const verticalOffset = p1[1];
2049
-
2050
- return [amplitude, angularFrequency, phase, verticalOffset];
2051
- }
2052
-
2053
- /**
2054
- * @param {object} graph Like props.graph or props.correct
2055
- * @param {object} props of an InteractiveGraph instance
2056
- */
2057
- static getLineCoords(
2058
- graph: PerseusGraphType,
2059
- props: Props,
2060
- ): $ReadOnlyArray<Coord> {
2061
- return (
2062
- // $FlowFixMe[incompatible-return]
2063
- graph.coords ||
2064
- InteractiveGraph.pointsFromNormalized(props, [
2065
- [0.25, 0.75],
2066
- [0.75, 0.75],
2067
- ])
2068
- );
2069
- }
2070
-
2071
- /**
2072
- * @param {object} graph Like props.graph or props.correct
2073
- * @param {object} props of an InteractiveGraph instance
2074
- */
2075
- static getPointCoords(
2076
- graph: PerseusGraphTypePoint,
2077
- props: Props,
2078
- ): $ReadOnlyArray<Coord> {
2079
- const numPoints = graph.numPoints || 1;
2080
- let coords = graph.coords;
2081
-
2082
- if (coords) {
2083
- return coords;
2084
- }
2085
- switch (numPoints) {
2086
- case 1:
2087
- // Back in the day, one point's coords were in graph.coord
2088
- coords = [graph.coord || [0, 0]];
2089
- break;
2090
- case 2:
2091
- coords = [
2092
- [-5, 0],
2093
- [5, 0],
2094
- ];
2095
- break;
2096
- case 3:
2097
- coords = [
2098
- [-5, 0],
2099
- [0, 0],
2100
- [5, 0],
2101
- ];
2102
- break;
2103
- case 4:
2104
- coords = [
2105
- [-6, 0],
2106
- [-2, 0],
2107
- [2, 0],
2108
- [6, 0],
2109
- ];
2110
- break;
2111
- case 5:
2112
- coords = [
2113
- [-6, 0],
2114
- [-3, 0],
2115
- [0, 0],
2116
- [3, 0],
2117
- [6, 0],
2118
- ];
2119
- break;
2120
- case 6:
2121
- coords = [
2122
- [-5, 0],
2123
- [-3, 0],
2124
- [-1, 0],
2125
- [1, 0],
2126
- [3, 0],
2127
- [5, 0],
2128
- ];
2129
- break;
2130
- case UNLIMITED:
2131
- coords = [];
2132
- break;
2133
- }
2134
- // Transform coords from their -10 to 10 space to 0 to 1
2135
- // because of the old graph.coord, and also it's easier.
2136
- const range = [
2137
- [-10, 10],
2138
- [-10, 10],
2139
- ];
2140
- // $FlowFixMe[incompatible-call]
2141
- const newCoords = InteractiveGraph.normalizeCoords(coords, range);
2142
-
2143
- return InteractiveGraph.pointsFromNormalized(props, newCoords);
2144
- }
2145
-
2146
- /**
2147
- * @param {object} graph Like props.graph or props.correct
2148
- * @param {object} props of an InteractiveGraph instance
2149
- */
2150
- static getLinearSystemCoords(
2151
- graph: PerseusGraphType,
2152
- props: Props,
2153
- ): $ReadOnlyArray<$ReadOnlyArray<Coord>> {
2154
- return (
2155
- // The callers assume that we're return an array of points
2156
- // $FlowFixMe[incompatible-return]
2157
- graph.coords ||
2158
- _.map(
2159
- [
2160
- [
2161
- [0.25, 0.75],
2162
- [0.75, 0.75],
2163
- ],
2164
- [
2165
- [0.25, 0.25],
2166
- [0.75, 0.25],
2167
- ],
2168
- ],
2169
- (coords) => {
2170
- return InteractiveGraph.pointsFromNormalized(props, coords);
2171
- },
2172
- )
2173
- );
2174
- }
2175
-
2176
- /**
2177
- * @param {object} graph Like props.graph or props.correct
2178
- * @param {object} props of an InteractiveGraph instance
2179
- */
2180
- static getPolygonCoords(
2181
- graph: PerseusGraphType,
2182
- props: Props,
2183
- ): $ReadOnlyArray<Coord> {
2184
- if (graph.type !== "polygon") {
2185
- throw makeInvalidTypeError("toggleShowSides", "polygon");
2186
- }
2187
-
2188
- let coords = graph.coords;
2189
- if (coords) {
2190
- return coords;
2191
- }
2192
-
2193
- const n = graph.numSides || 3;
2194
-
2195
- if (n === UNLIMITED) {
2196
- coords = [];
2197
- } else {
2198
- // $FlowFixMe[unsafe-addition]
2199
- const angle = (2 * Math.PI) / n;
2200
- // $FlowFixMe[unsafe-addition]
2201
- const offset = (1 / n - 1 / 2) * Math.PI;
2202
-
2203
- // TODO(alex): Generalize this to more than just triangles so that
2204
- // all polygons have whole number side lengths if snapping to sides
2205
- const radius =
2206
- graph.snapTo === "sides" ? (Math.sqrt(3) / 3) * 7 : 4;
2207
-
2208
- // Generate coords of a regular polygon with n sides
2209
- coords = _.times(n, function (i) {
2210
- return [
2211
- radius * Math.cos(i * angle + offset),
2212
- radius * Math.sin(i * angle + offset),
2213
- ];
2214
- });
2215
- }
2216
-
2217
- const ranges: [Range, Range] = [
2218
- [-10, 10],
2219
- [-10, 10],
2220
- ];
2221
- coords = InteractiveGraph.normalizeCoords(coords, ranges);
2222
-
2223
- const snapToGrid = !_.contains(["angles", "sides"], graph.snapTo);
2224
- coords = InteractiveGraph.pointsFromNormalized(
2225
- props,
2226
- coords,
2227
- /* noSnap */ !snapToGrid,
2228
- );
2229
-
2230
- return coords;
2231
- }
2232
-
2233
- /**
2234
- * @param {object} graph Like props.graph or props.correct
2235
- * @param {object} props of an InteractiveGraph instance
2236
- */
2237
- static getSegmentCoords(
2238
- graph: PerseusGraphTypeSegment,
2239
- props: Props,
2240
- ): $ReadOnlyArray<$ReadOnlyArray<Coord>> {
2241
- const coords = graph.coords;
2242
- if (coords) {
2243
- return coords;
2244
- }
2245
-
2246
- const n = graph.numSegments || 1;
2247
- const ys: $ReadOnlyArray<number> = {
2248
- // $FlowFixMe[unsupported-syntax]
2249
- 1: [5],
2250
- // $FlowFixMe[unsupported-syntax]
2251
- 2: [5, -5],
2252
- // $FlowFixMe[unsupported-syntax]
2253
- 3: [5, 0, -5],
2254
- // $FlowFixMe[unsupported-syntax]
2255
- 4: [6, 2, -2, -6],
2256
- // $FlowFixMe[unsupported-syntax]
2257
- 5: [6, 3, 0, -3, -6],
2258
- // $FlowFixMe[unsupported-syntax]
2259
- 6: [5, 3, 1, -1, -3, -5],
2260
- }[n];
2261
- const range = [
2262
- [-10, 10],
2263
- [-10, 10],
2264
- ];
2265
-
2266
- return ys.map(function (y) {
2267
- let segment = [
2268
- [-5, y],
2269
- [5, y],
2270
- ];
2271
- segment = InteractiveGraph.normalizeCoords(segment, range);
2272
- segment = InteractiveGraph.pointsFromNormalized(props, segment);
2273
- return segment;
2274
- });
2275
- }
2276
-
2277
- /**
2278
- * @param {object} graph Like props.graph or props.correct
2279
- * @param {object} props of an InteractiveGraph instance
2280
- */
2281
- static getAngleCoords(
2282
- graph: PerseusGraphTypeAngle,
2283
- props: Props,
2284
- ): $ReadOnlyArray<Coord> {
2285
- let coords = graph.coords;
2286
- if (coords) {
2287
- return coords;
2288
- }
2289
-
2290
- const snap = graph.snapDegrees || 1;
2291
- let angle = snap;
2292
- while (angle < 20) {
2293
- angle += snap;
2294
- }
2295
- angle = (angle * Math.PI) / 180;
2296
- const offset = ((graph.angleOffsetDeg || 0) * Math.PI) / 180;
2297
-
2298
- coords = InteractiveGraph.pointsFromNormalized(props, [
2299
- [0.85, 0.5],
2300
- [0.5, 0.5],
2301
- ]);
2302
-
2303
- const radius = magnitude(vector(...coords));
2304
-
2305
- // Adjust the lower point by angleOffsetDeg degrees
2306
- // $FlowFixMe[cannot-write]
2307
- coords[0] = [
2308
- coords[1][0] + radius * Math.cos(offset),
2309
- coords[1][1] + radius * Math.sin(offset),
2310
- ];
2311
- // Position the upper point angle radians from the
2312
- // lower point
2313
- // $FlowFixMe[cannot-write]
2314
- coords[2] = [
2315
- coords[1][0] + radius * Math.cos(angle + offset),
2316
- coords[1][1] + radius * Math.sin(angle + offset),
2317
- ];
2318
-
2319
- return coords;
2320
- }
2321
-
2322
- static normalizeCoords(
2323
- coordsList: $ReadOnlyArray<Coord>,
2324
- ranges: [Range, Range],
2325
- ): $ReadOnlyArray<Coord> {
2326
- return _.map(coordsList, function (coords) {
2327
- return _.map(coords, function (coord, i) {
2328
- const extent = ranges[i][1] - ranges[i][0];
2329
- return (coord + ranges[i][1]) / extent;
2330
- });
2331
- });
2332
- }
2333
-
2334
- static getEquationString(props: Props): string {
2335
- const type = props.graph.type;
2336
- const funcName = "get" + capitalize(type) + "EquationString";
2337
- // $FlowFixMe[incompatible-use]
2338
- return InteractiveGraph[funcName](props);
2339
- }
2340
-
2341
- static pointsFromNormalized(
2342
- props: Props,
2343
- coordsList: $ReadOnlyArray<Coord>,
2344
- noSnap?: boolean,
2345
- ): $ReadOnlyArray<Coord> {
2346
- return _.map(coordsList, function (coords) {
2347
- return _.map(coords, function (coord, i) {
2348
- const range: Range = props.range[i];
2349
- if (noSnap) {
2350
- return range[0] + (range[1] - range[0]) * coord;
2351
- }
2352
- const step = props.step[i];
2353
- const nSteps = numSteps(range, step);
2354
- const tick = Math.round(coord * nSteps);
2355
- return range[0] + step * tick;
2356
- });
2357
- });
2358
- }
2359
-
2360
- static getLinearEquationString(props: Props): string {
2361
- const coords = InteractiveGraph.getLineCoords(props.graph, props);
2362
- if (eq(coords[0][0], coords[1][0])) {
2363
- return "x = " + coords[0][0].toFixed(3);
2364
- }
2365
- const m = (coords[1][1] - coords[0][1]) / (coords[1][0] - coords[0][0]);
2366
- const b = coords[0][1] - m * coords[0][0];
2367
- if (eq(m, 0)) {
2368
- return "y = " + b.toFixed(3);
2369
- }
2370
- return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
2371
- }
2372
-
2373
- static getCurrentQuadraticCoefficients(props: Props): QuadraticCoefficient {
2374
- // TODO(alpert): Don't duplicate
2375
- const coords =
2376
- props.graph.coords ||
2377
- InteractiveGraph.defaultQuadraticCoords(props);
2378
- // $FlowFixMe[incompatible-call]
2379
- return InteractiveGraph.getQuadraticCoefficients(coords);
2380
- }
2381
-
2382
- static defaultQuadraticCoords(props: Props): $ReadOnlyArray<Coord> {
2383
- const coords = [
2384
- [0.25, 0.75],
2385
- [0.5, 0.25],
2386
- [0.75, 0.75],
2387
- ];
2388
- return InteractiveGraph.pointsFromNormalized(props, coords);
2389
- }
2390
-
2391
- static getQuadraticEquationString(props: Props): string {
2392
- const coeffs = InteractiveGraph.getCurrentQuadraticCoefficients(props);
2393
- return (
2394
- "y = " +
2395
- coeffs[0].toFixed(3) +
2396
- "x^2 + " +
2397
- coeffs[1].toFixed(3) +
2398
- "x + " +
2399
- coeffs[2].toFixed(3)
2400
- );
2401
- }
2402
-
2403
- static getCurrentSinusoidCoefficients(props: Props): SineCoefficient {
2404
- const coords =
2405
- props.graph.coords || InteractiveGraph.defaultSinusoidCoords(props);
2406
- // $FlowFixMe[incompatible-call]
2407
- return InteractiveGraph.getSinusoidCoefficients(coords);
2408
- }
2409
-
2410
- static defaultSinusoidCoords(props: Props): $ReadOnlyArray<Coord> {
2411
- const coords = [
2412
- [0.5, 0.5],
2413
- [0.65, 0.6],
2414
- ];
2415
- return InteractiveGraph.pointsFromNormalized(props, coords);
2416
- }
2417
-
2418
- static getSinusoidEquationString(props: Props): string {
2419
- const coeffs = InteractiveGraph.getCurrentSinusoidCoefficients(props);
2420
- return (
2421
- "y = " +
2422
- coeffs[0].toFixed(3) +
2423
- "sin(" +
2424
- coeffs[1].toFixed(3) +
2425
- "x - " +
2426
- coeffs[2].toFixed(3) +
2427
- ") + " +
2428
- coeffs[3].toFixed(3)
2429
- );
2430
- }
2431
-
2432
- static getCircleEquationString(props: Props): string {
2433
- const graph = props.graph;
2434
- // TODO(alpert): Don't duplicate
2435
- const center = graph.center || [0, 0];
2436
- const radius = graph.radius || 2;
2437
- return (
2438
- "center (" + center[0] + ", " + center[1] + "), radius " + radius
2439
- );
2440
- }
2441
-
2442
- static getLinearSystemEquationString(props: Props): string {
2443
- const coords = InteractiveGraph.getLinearSystemCoords(
2444
- props.graph,
2445
- props,
2446
- );
2447
-
2448
- return (
2449
- "\n" +
2450
- getLineEquation(coords[0][0], coords[0][1]) +
2451
- "\n" +
2452
- getLineEquation(coords[1][0], coords[1][1]) +
2453
- "\n" +
2454
- getLineIntersection(coords[0], coords[1])
2455
- );
2456
- }
2457
-
2458
- static getPointEquationString(props: Props): string {
2459
- if (props.graph.type !== "point") {
2460
- throw makeInvalidTypeError("getPointEquationString", "point");
2461
- }
2462
-
2463
- const coords = InteractiveGraph.getPointCoords(props.graph, props);
2464
- return coords
2465
- .map(function (coord) {
2466
- return "(" + coord[0] + ", " + coord[1] + ")";
2467
- })
2468
- .join(", ");
2469
- }
2470
-
2471
- static getSegmentEquationString(props: Props): string {
2472
- if (props.graph.type !== "segment") {
2473
- throw makeInvalidTypeError("getSegmentEquationString", "segment");
2474
- }
2475
-
2476
- const segments = InteractiveGraph.getSegmentCoords(props.graph, props);
2477
- return _.map(segments, function (segment) {
2478
- return (
2479
- "[" +
2480
- _.map(segment, function (coord) {
2481
- return "(" + coord.join(", ") + ")";
2482
- }).join(" ") +
2483
- "]"
2484
- );
2485
- }).join(" ");
2486
- }
2487
-
2488
- static getRayEquationString(props: Props): string {
2489
- if (props.graph.type !== "ray") {
2490
- throw makeInvalidTypeError("createPointForPolygonType", "ray");
2491
- }
2492
-
2493
- const coords = InteractiveGraph.getLineCoords(props.graph, props);
2494
- const a = coords[0];
2495
- const b = coords[1];
2496
- let eq = InteractiveGraph.getLinearEquationString(props);
2497
-
2498
- if (a[0] > b[0]) {
2499
- eq += " (for x <= " + a[0].toFixed(3) + ")";
2500
- } else if (a[0] < b[0]) {
2501
- eq += " (for x >= " + a[0].toFixed(3) + ")";
2502
- } else if (a[1] > b[1]) {
2503
- eq += " (for y <= " + a[1].toFixed(3) + ")";
2504
- } else {
2505
- eq += " (for y >= " + a[1].toFixed(3) + ")";
2506
- }
2507
-
2508
- return eq;
2509
- }
2510
-
2511
- static getPolygonEquationString(props: Props): string {
2512
- if (props.graph.type !== "polygon") {
2513
- throw makeInvalidTypeError("getPolygonEquationString", "polygon");
2514
- }
2515
- const coords = InteractiveGraph.getPolygonCoords(props.graph, props);
2516
- return _.map(coords, function (coord) {
2517
- return "(" + coord.join(", ") + ")";
2518
- }).join(" ");
2519
- }
2520
-
2521
- static getAngleEquationString(props: Props): string {
2522
- if (props.graph.type !== "angle") {
2523
- throw makeInvalidTypeError("getAngleEquationString", "angle");
2524
- }
2525
- const coords = InteractiveGraph.getAngleCoords(props.graph, props);
2526
- const angle = GraphUtils.findAngle(coords[2], coords[0], coords[1]);
2527
- return (
2528
- angle.toFixed(0) +
2529
- "\u00B0 angle" +
2530
- " at (" +
2531
- coords[1].join(", ") +
2532
- ")"
2533
- );
2534
- }
2535
-
2536
- static validate(
2537
- // TODO(kevinb): rename state to userInput
2538
- state: PerseusGraphType, // state === userInput
2539
- rubric: Rubric,
2540
- component: $FlowFixMe,
2541
- ): PerseusScore {
2542
- // When nothing has moved, there will neither be coords nor the
2543
- // circle's center/radius fields. When those fields are absent, skip
2544
- // all these checks; just go mark the answer as empty.
2545
- const hasValue = !!(state.coords || (state.center && state.radius));
2546
-
2547
- if (state.type === rubric.correct.type && hasValue) {
2548
- if (
2549
- state.type === "linear" &&
2550
- rubric.correct.type === "linear" &&
2551
- state.coords != null
2552
- ) {
2553
- const guess = state.coords;
2554
- const correct = rubric.correct.coords;
2555
-
2556
- // If both of the guess points are on the correct line, it's
2557
- // correct.
2558
- if (
2559
- // $FlowFixMe[incompatible-use] - Fix type so coords can't be omitted
2560
- collinear(correct[0], correct[1], guess[0]) &&
2561
- collinear(correct[0], correct[1], guess[1])
2562
- ) {
2563
- return {
2564
- type: "points",
2565
- earned: 1,
2566
- total: 1,
2567
- message: null,
2568
- };
2569
- }
2570
- } else if (
2571
- state.type === "linear-system" &&
2572
- rubric.correct.type === "linear-system" &&
2573
- state.coords != null
2574
- ) {
2575
- const guess = state.coords;
2576
- // $FlowFixMe[incompatible-cast] - Fix type so this can't be undefined
2577
- const correct = (rubric.correct.coords: $ReadOnlyArray<
2578
- $ReadOnlyArray<Coord>,
2579
- >);
2580
-
2581
- if (
2582
- (collinear(correct[0][0], correct[0][1], guess[0][0]) &&
2583
- collinear(correct[0][0], correct[0][1], guess[0][1]) &&
2584
- collinear(correct[1][0], correct[1][1], guess[1][0]) &&
2585
- collinear(correct[1][0], correct[1][1], guess[1][1])) ||
2586
- (collinear(correct[0][0], correct[0][1], guess[1][0]) &&
2587
- collinear(correct[0][0], correct[0][1], guess[1][1]) &&
2588
- collinear(correct[1][0], correct[1][1], guess[0][0]) &&
2589
- collinear(correct[1][0], correct[1][1], guess[0][1]))
2590
- ) {
2591
- return {
2592
- type: "points",
2593
- earned: 1,
2594
- total: 1,
2595
- message: null,
2596
- };
2597
- }
2598
- } else if (
2599
- state.type === "quadratic" &&
2600
- rubric.correct.type === "quadratic" &&
2601
- state.coords != null
2602
- ) {
2603
- // If the parabola coefficients match, it's correct.
2604
- const guessCoeffs = this.getQuadraticCoefficients(state.coords);
2605
- const correctCoeffs = this.getQuadraticCoefficients(
2606
- // $FlowFixMe[incompatible-call]
2607
- // $FlowFixMe[prop-missing]
2608
- rubric.correct.coords,
2609
- );
2610
- if (deepEq(guessCoeffs, correctCoeffs)) {
2611
- return {
2612
- type: "points",
2613
- earned: 1,
2614
- total: 1,
2615
- message: null,
2616
- };
2617
- }
2618
- } else if (
2619
- state.type === "sinusoid" &&
2620
- rubric.correct.type === "sinusoid" &&
2621
- state.coords != null
2622
- ) {
2623
- const guessCoeffs = this.getSinusoidCoefficients(state.coords);
2624
- const correctCoeffs = this.getSinusoidCoefficients(
2625
- // $FlowFixMe[incompatible-call]
2626
- // $FlowFixMe[prop-missing]
2627
- rubric.correct.coords,
2628
- );
2629
-
2630
- const canonicalGuessCoeffs =
2631
- canonicalSineCoefficients(guessCoeffs);
2632
- const canonicalCorrectCoeffs =
2633
- canonicalSineCoefficients(correctCoeffs);
2634
- // If the canonical coefficients match, it's correct.
2635
- if (deepEq(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
2636
- return {
2637
- type: "points",
2638
- earned: 1,
2639
- total: 1,
2640
- message: null,
2641
- };
2642
- }
2643
- } else if (
2644
- state.type === "circle" &&
2645
- rubric.correct.type === "circle"
2646
- ) {
2647
- if (
2648
- deepEq(state.center, rubric.correct.center) &&
2649
- // $FlowFixMe[prop-missing]
2650
- eq(state.radius, rubric.correct.radius)
2651
- ) {
2652
- return {
2653
- type: "points",
2654
- earned: 1,
2655
- total: 1,
2656
- message: null,
2657
- };
2658
- }
2659
- } else if (
2660
- state.type === "point" &&
2661
- rubric.correct.type === "point" &&
2662
- state.coords != null
2663
- ) {
2664
- let correct = InteractiveGraph.getPointCoords(
2665
- rubric.correct,
2666
- component,
2667
- );
2668
- const guess = state.coords?.slice();
2669
- correct = correct.slice();
2670
- // Everything's already rounded so we shouldn't need to do an
2671
- // eq() comparison but _.isEqual(0, -0) is false, so we'll use
2672
- // eq() anyway. The sort should be fine because it'll stringify
2673
- // it and -0 converted to a string is "0"
2674
- guess?.sort();
2675
- correct.sort();
2676
- if (deepEq(guess, correct)) {
2677
- return {
2678
- type: "points",
2679
- earned: 1,
2680
- total: 1,
2681
- message: null,
2682
- };
2683
- }
2684
- } else if (
2685
- state.type === "polygon" &&
2686
- rubric.correct.type === "polygon" &&
2687
- state.coords != null
2688
- ) {
2689
- const guess: Array<Coord> = state.coords?.slice(); // eslint-disable-line ft-flow/no-mutable-array
2690
- // $FlowFixMe[incompatible-type]
2691
- // $FlowFixMe[prop-missing]
2692
- const correct: Array<Coord> = rubric.correct.coords?.slice(); // eslint-disable-line ft-flow/no-mutable-array
2693
-
2694
- let match;
2695
- if (rubric.correct.match === "similar") {
2696
- match = similar(guess, correct, Number.POSITIVE_INFINITY);
2697
- } else if (rubric.correct.match === "congruent") {
2698
- match = similar(guess, correct, knumber.DEFAULT_TOLERANCE);
2699
- } else if (rubric.correct.match === "approx") {
2700
- match = similar(guess, correct, 0.1);
2701
- } else {
2702
- /* exact */
2703
- guess.sort();
2704
- correct.sort();
2705
- match = deepEq(guess, correct);
2706
- }
2707
-
2708
- if (match) {
2709
- return {
2710
- type: "points",
2711
- earned: 1,
2712
- total: 1,
2713
- message: null,
2714
- };
2715
- }
2716
- } else if (
2717
- state.type === "segment" &&
2718
- rubric.correct.type === "segment" &&
2719
- state.coords != null
2720
- ) {
2721
- let guess = state.coords.slice();
2722
- // $FlowFixMe[prop-missing]
2723
- let correct = rubric.correct.coords?.slice();
2724
- guess = _.invoke(guess, "sort").sort();
2725
- correct = _.invoke(correct, "sort").sort();
2726
- if (deepEq(guess, correct)) {
2727
- return {
2728
- type: "points",
2729
- earned: 1,
2730
- total: 1,
2731
- message: null,
2732
- };
2733
- }
2734
- } else if (
2735
- state.type === "ray" &&
2736
- rubric.correct.type === "ray" &&
2737
- state.coords != null
2738
- ) {
2739
- const guess = state.coords;
2740
- const correct = rubric.correct.coords;
2741
- if (
2742
- // $FlowFixMe[incompatible-use]
2743
- deepEq(guess[0], correct[0]) &&
2744
- // $FlowFixMe[incompatible-use]
2745
- collinear(correct[0], correct[1], guess[1])
2746
- ) {
2747
- return {
2748
- type: "points",
2749
- earned: 1,
2750
- total: 1,
2751
- message: null,
2752
- };
2753
- }
2754
- } else if (
2755
- state.type === "angle" &&
2756
- rubric.correct.type === "angle"
2757
- ) {
2758
- const guess = state.coords;
2759
- const correct = rubric.correct.coords;
2760
-
2761
- let match;
2762
- if (rubric.correct.match === "congruent") {
2763
- const angles = _.map([guess, correct], function (coords) {
2764
- const angle = GraphUtils.findAngle(
2765
- coords[2],
2766
- coords[0],
2767
- coords[1],
2768
- );
2769
- return (angle + 360) % 360;
2770
- });
2771
- match = eq(...angles);
2772
- } else {
2773
- /* exact */
2774
- match =
2775
- // $FlowFixMe[incompatible-use]
2776
- deepEq(guess[1], correct[1]) &&
2777
- // $FlowFixMe[incompatible-use]
2778
- collinear(correct[1], correct[0], guess[0]) &&
2779
- // $FlowFixMe[incompatible-use]
2780
- collinear(correct[1], correct[2], guess[2]);
2781
- }
2782
-
2783
- if (match) {
2784
- return {
2785
- type: "points",
2786
- earned: 1,
2787
- total: 1,
2788
- message: null,
2789
- };
2790
- }
2791
- }
2792
- }
2793
-
2794
- // The input wasn't correct, so check if it's a blank input or if it's
2795
- // actually just wrong
2796
- if (!hasValue || _.isEqual(state, rubric.graph)) {
2797
- // We're where we started.
2798
- return {
2799
- type: "invalid",
2800
- message: null,
2801
- };
2802
- }
2803
- return {
2804
- type: "points",
2805
- earned: 0,
2806
- total: 1,
2807
- message: null,
2808
- };
2809
- }
2810
-
2811
- static getUserInputFromProps(props: Props): PerseusGraphType {
2812
- return props.graph;
2813
- }
2814
- }
2815
-
2816
- export default ({
2817
- name: "interactive-graph",
2818
- displayName: "Interactive graph",
2819
- widget: InteractiveGraph,
2820
- }: WidgetExports<typeof InteractiveGraph>);