@khanacademy/perseus 3.0.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 (1210) hide show
  1. package/.babelrc.js +1 -1
  2. package/.eslintrc.js +1 -0
  3. package/CHANGELOG.md +16 -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 +10880 -14498
  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 +12443 -15871
  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__/categorizer.test.ts +203 -0
  691. package/src/widgets/__tests__/cs-program.test.ts +43 -0
  692. package/src/widgets/__tests__/definition.test.ts +136 -0
  693. package/src/widgets/__tests__/dropdown.test.ts +105 -0
  694. package/src/widgets/__tests__/explanation.test.ts +198 -0
  695. package/src/widgets/__tests__/expression.test.ts +431 -0
  696. package/src/widgets/__tests__/graded-group-set-jipt.test.ts +30 -0
  697. package/src/widgets/__tests__/graded-group-set.test.ts +288 -0
  698. package/src/widgets/__tests__/graded-group.test.ts +232 -0
  699. package/src/widgets/__tests__/grapher.cypress.ts +576 -0
  700. package/src/widgets/__tests__/grapher.test.ts +33 -0
  701. package/src/widgets/__tests__/group.test.tsx +512 -0
  702. package/src/widgets/__tests__/iframe.test.ts +45 -0
  703. package/src/widgets/__tests__/image.test.ts +62 -0
  704. package/src/widgets/__tests__/input-number.test.ts +354 -0
  705. package/src/widgets/__tests__/interaction.test.ts +31 -0
  706. package/src/widgets/__tests__/interactive-graph.cypress.ts +815 -0
  707. package/src/widgets/__tests__/interactive-graph.test.ts +106 -0
  708. package/src/widgets/__tests__/matcher.test.tsx +152 -0
  709. package/src/widgets/__tests__/matrix.test.ts +82 -0
  710. package/src/widgets/__tests__/number-line.test.ts +79 -0
  711. package/src/widgets/__tests__/numeric-input.test.ts +502 -0
  712. package/src/widgets/__tests__/orderer.test.ts +73 -0
  713. package/src/widgets/__tests__/passage-ref.test.ts +168 -0
  714. package/src/widgets/__tests__/passage.test.tsx +224 -0
  715. package/src/widgets/__tests__/radio/base-radio.test.tsx +470 -0
  716. package/src/widgets/__tests__/radio/choice-icon.test.tsx +90 -0
  717. package/src/widgets/__tests__/radio/choice.test.tsx +310 -0
  718. package/src/widgets/__tests__/radio/focus-ring.test.tsx +23 -0
  719. package/src/widgets/__tests__/radio/option-status.test.tsx +77 -0
  720. package/src/widgets/__tests__/radio.test.ts +807 -0
  721. package/src/widgets/__tests__/renderQuestion.tsx +77 -0
  722. package/src/widgets/__tests__/unit.test.tsx +168 -0
  723. package/src/widgets/__tests__/video.test.ts +53 -0
  724. package/src/widgets/categorizer.tsx +343 -0
  725. package/src/widgets/cs-program.tsx +227 -0
  726. package/src/widgets/definition.tsx +121 -0
  727. package/src/widgets/dropdown.tsx +142 -0
  728. package/src/widgets/explanation.tsx +317 -0
  729. package/src/widgets/expression.tsx +647 -0
  730. package/src/widgets/graded-group-answer-bar.tsx +142 -0
  731. package/src/widgets/graded-group-set.tsx +296 -0
  732. package/src/widgets/graded-group.tsx +466 -0
  733. package/src/widgets/grapher/util.tsx +803 -0
  734. package/src/widgets/grapher.tsx +674 -0
  735. package/src/widgets/group.tsx +196 -0
  736. package/src/widgets/iframe.tsx +195 -0
  737. package/src/widgets/image.tsx +309 -0
  738. package/src/widgets/input-number.tsx +413 -0
  739. package/src/widgets/interaction/arrow-picker.tsx +38 -0
  740. package/src/widgets/interaction/color-picker.tsx +69 -0
  741. package/src/widgets/interaction/dash-picker.tsx +45 -0
  742. package/src/widgets/interaction/element-container.tsx +101 -0
  743. package/src/widgets/interaction.tsx +826 -0
  744. package/src/widgets/interactive-graph.tsx +2921 -0
  745. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +98 -0
  746. package/src/widgets/label-image/__stories__/marker.stories.tsx +181 -0
  747. package/src/widgets/label-image/answer-choices.tsx +300 -0
  748. package/src/widgets/label-image/marker.tsx +364 -0
  749. package/src/widgets/label-image/types.ts +26 -0
  750. package/src/widgets/label-image.test.ts +840 -0
  751. package/src/widgets/label-image.tsx +849 -0
  752. package/src/widgets/lights-puzzle.tsx +359 -0
  753. package/src/widgets/matcher.tsx +278 -0
  754. package/src/widgets/matrix.tsx +637 -0
  755. package/src/widgets/measurer.tsx +217 -0
  756. package/src/widgets/molecule/__tests__/layout.test.ts +162 -0
  757. package/src/widgets/molecule/__tests__/parser.test.ts +155 -0
  758. package/src/widgets/molecule/molecule-drawing.ts +173 -0
  759. package/src/widgets/molecule/molecule-layout.ts +362 -0
  760. package/src/widgets/molecule/smiles-parser.ts +296 -0
  761. package/src/widgets/molecule.tsx +172 -0
  762. package/src/widgets/number-line.tsx +864 -0
  763. package/src/widgets/numeric-input.tsx +563 -0
  764. package/src/widgets/orderer.tsx +788 -0
  765. package/src/widgets/passage/__tests__/passage-markdown.test.ts +376 -0
  766. package/src/widgets/passage/passage-markdown.tsx +475 -0
  767. package/src/widgets/passage-ref-target.tsx +73 -0
  768. package/src/widgets/passage-ref.tsx +215 -0
  769. package/src/widgets/passage.tsx +584 -0
  770. package/src/widgets/plotter.tsx +1204 -0
  771. package/src/widgets/radio/__stories__/base-radio.stories.tsx +204 -0
  772. package/src/widgets/radio/__stories__/choice-icon.stories.tsx +168 -0
  773. package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +40 -0
  774. package/src/widgets/radio/__stories__/choice.stories.tsx +137 -0
  775. package/src/widgets/radio/__stories__/focus-ring.stories.tsx +43 -0
  776. package/src/widgets/radio/__stories__/option-status.stories.tsx +105 -0
  777. package/src/widgets/radio/base-radio.tsx +509 -0
  778. package/src/widgets/radio/choice-a11y-text.ts +61 -0
  779. package/src/widgets/radio/choice-icon/choice-icon.tsx +217 -0
  780. package/src/widgets/radio/choice-icon/cross-out-line.tsx +53 -0
  781. package/src/widgets/radio/choice-icon/shared-styles.ts +15 -0
  782. package/src/widgets/radio/choice-none-above.tsx +56 -0
  783. package/src/widgets/radio/choice.jsx.fixture.ts +45 -0
  784. package/src/widgets/radio/choice.tsx +358 -0
  785. package/src/widgets/radio/focus-ring.tsx +57 -0
  786. package/src/widgets/radio/option-status.tsx +100 -0
  787. package/src/widgets/radio/util.test.ts +18 -0
  788. package/src/widgets/radio/util.ts +31 -0
  789. package/src/widgets/radio/widget.tsx +521 -0
  790. package/src/widgets/radio.ts +157 -0
  791. package/src/widgets/reaction-diagram.tsx +144 -0
  792. package/src/widgets/sequence.tsx +137 -0
  793. package/src/widgets/simulator.tsx +831 -0
  794. package/src/widgets/sorter.tsx +154 -0
  795. package/src/widgets/table.tsx +393 -0
  796. package/src/widgets/transformer.tsx +2951 -0
  797. package/src/widgets/unit.tsx +374 -0
  798. package/src/widgets/video-transcript-link.test.tsx +213 -0
  799. package/src/widgets/video-transcript-link.tsx +84 -0
  800. package/src/widgets/video.tsx +124 -0
  801. package/src/widgets.ts +444 -0
  802. package/src/zoom.ts +558 -0
  803. package/tsconfig.json +25 -0
  804. package/tsconfig.tsbuildinfo +1 -0
  805. package/src/__stories__/hints-renderer.stories.jsx +0 -60
  806. package/src/__stories__/item-renderer.stories.jsx +0 -27
  807. package/src/__stories__/server-item-renderer.stories.jsx +0 -27
  808. package/src/__testdata__/graphie_testdata.js +0 -53
  809. package/src/__testdata__/item-renderer_testdata.js +0 -161
  810. package/src/__testdata__/renderer_testdata.js +0 -123
  811. package/src/__tests__/a11y_test.jsx +0 -304
  812. package/src/__tests__/item-renderer_test.jsx +0 -892
  813. package/src/__tests__/jipt-paragraphs_test.js +0 -34
  814. package/src/__tests__/mock-asset-loading-widget.jsx +0 -57
  815. package/src/__tests__/mock-widget.jsx +0 -35
  816. package/src/__tests__/perseus-markdown_test.jsx +0 -747
  817. package/src/__tests__/renderability_test.jsx +0 -481
  818. package/src/__tests__/renderer-api_test.jsx +0 -309
  819. package/src/__tests__/renderer_test.jsx +0 -1792
  820. package/src/__tests__/server-item-renderer_test.jsx +0 -516
  821. package/src/__tests__/test-items/image-item.js +0 -25
  822. package/src/__tests__/test-items/input-number-1-item.js +0 -27
  823. package/src/__tests__/test-items/input-number-2-item.js +0 -39
  824. package/src/__tests__/test-items/table-item.js +0 -30
  825. package/src/__tests__/tex-wrangler_test.js +0 -83
  826. package/src/__tests__/util_test.js +0 -41
  827. package/src/a11y.js +0 -46
  828. package/src/all-widgets.js +0 -9
  829. package/src/article-renderer.jsx +0 -214
  830. package/src/asset-context.js +0 -30
  831. package/src/basic-widgets.js +0 -13
  832. package/src/components/__stories__/button-group.stories.jsx +0 -61
  833. package/src/components/__stories__/fixed-to-responsive.stories.jsx +0 -132
  834. package/src/components/__stories__/graph.stories.jsx +0 -27
  835. package/src/components/__stories__/graphie.stories.jsx +0 -32
  836. package/src/components/__stories__/hud.stories.jsx +0 -36
  837. package/src/components/__stories__/icon.stories.jsx +0 -56
  838. package/src/components/__stories__/image-loader.stories.jsx +0 -60
  839. package/src/components/__stories__/info-tip.stories.jsx +0 -33
  840. package/src/components/__stories__/inline-icon.stories.jsx +0 -39
  841. package/src/components/__stories__/input-with-examples.stories.jsx +0 -52
  842. package/src/components/__stories__/lint.stories.jsx +0 -40
  843. package/src/components/__stories__/math-input.stories.jsx +0 -29
  844. package/src/components/__stories__/math-output.stories.jsx +0 -24
  845. package/src/components/__stories__/multi-button-group.stories.jsx +0 -69
  846. package/src/components/__stories__/number-input.stories.jsx +0 -42
  847. package/src/components/__stories__/prop-check-box.stories.jsx +0 -62
  848. package/src/components/__stories__/range-input.stories.jsx +0 -28
  849. package/src/components/__stories__/simple-keypad-input.stories.jsx +0 -28
  850. package/src/components/__stories__/sortable.stories.jsx +0 -73
  851. package/src/components/__stories__/stub-tag-editor.stories.jsx +0 -44
  852. package/src/components/__stories__/svg-image.stories.jsx +0 -71
  853. package/src/components/__stories__/tex.stories.jsx +0 -24
  854. package/src/components/__stories__/text-buttons.stories.jsx +0 -51
  855. package/src/components/__stories__/text-input.stories.jsx +0 -34
  856. package/src/components/__stories__/text-list-editor.stories.jsx +0 -33
  857. package/src/components/__stories__/zoomable-tex.stories.jsx +0 -39
  858. package/src/components/__stories__/zoomable.stories.jsx +0 -40
  859. package/src/components/__testdata__/sorter_testdata.js +0 -20
  860. package/src/components/__tests__/number-input_test.jsx +0 -112
  861. package/src/components/__tests__/sortable_test.jsx +0 -234
  862. package/src/components/__tests__/sorter_test.jsx +0 -111
  863. package/src/components/__tests__/zoomable_test.jsx +0 -388
  864. package/src/components/button-group.jsx +0 -131
  865. package/src/components/constants.js +0 -15
  866. package/src/components/fixed-to-responsive.jsx +0 -165
  867. package/src/components/graph.jsx +0 -413
  868. package/src/components/graphie-classes.jsx +0 -148
  869. package/src/components/graphie-movables.jsx +0 -342
  870. package/src/components/graphie.jsx +0 -353
  871. package/src/components/highlighting/highlightable-content.jsx +0 -245
  872. package/src/components/highlighting/highlights.js +0 -208
  873. package/src/components/highlighting/ranges.js +0 -299
  874. package/src/components/highlighting/types.js +0 -73
  875. package/src/components/highlighting/ui/highlight-renderer.jsx +0 -179
  876. package/src/components/highlighting/ui/highlight-set-renderer.jsx +0 -190
  877. package/src/components/highlighting/ui/highlight-tooltip.jsx +0 -143
  878. package/src/components/highlighting/ui/highlighting-ui.jsx +0 -163
  879. package/src/components/highlighting/ui/selection-tracker.jsx +0 -184
  880. package/src/components/highlighting/ui/types.js +0 -21
  881. package/src/components/highlighting/ui/util.js +0 -166
  882. package/src/components/highlighting/word-indexer.jsx +0 -155
  883. package/src/components/hud.jsx +0 -179
  884. package/src/components/icon.jsx +0 -149
  885. package/src/components/image-loader.jsx +0 -175
  886. package/src/components/info-tip/info-tip.jsx +0 -146
  887. package/src/components/info-tip.jsx +0 -38
  888. package/src/components/inline-icon.jsx +0 -81
  889. package/src/components/input-with-examples.jsx +0 -248
  890. package/src/components/lint.jsx +0 -455
  891. package/src/components/math-input.jsx +0 -293
  892. package/src/components/math-output.jsx +0 -128
  893. package/src/components/multi-button-group.jsx +0 -137
  894. package/src/components/number-input.jsx +0 -253
  895. package/src/components/prop-check-box.jsx +0 -80
  896. package/src/components/range-input.jsx +0 -61
  897. package/src/components/simple-keypad-input.jsx +0 -88
  898. package/src/components/sortable.jsx +0 -872
  899. package/src/components/stub-tag-editor.jsx +0 -54
  900. package/src/components/svg-image.jsx +0 -765
  901. package/src/components/tex-buttons.jsx +0 -320
  902. package/src/components/tex.jsx +0 -70
  903. package/src/components/text-input.jsx +0 -133
  904. package/src/components/text-list-editor.jsx +0 -157
  905. package/src/components/tooltip.jsx +0 -392
  906. package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +0 -140
  907. package/src/components/visibility-observer/get-scroll-parent.js +0 -45
  908. package/src/components/visibility-observer/visibility-observer.js +0 -109
  909. package/src/components/visibility-observer/visibility-observer.jsx +0 -123
  910. package/src/components/zoomable-tex.jsx +0 -70
  911. package/src/components/zoomable.jsx +0 -294
  912. package/src/components.js +0 -17
  913. package/src/definition-context.js +0 -59
  914. package/src/dependencies.js +0 -22
  915. package/src/error-boundary.jsx +0 -48
  916. package/src/extra-widgets.js +0 -75
  917. package/src/hint-renderer.jsx +0 -152
  918. package/src/hints-renderer.jsx +0 -367
  919. package/src/icon-paths.js +0 -120
  920. package/src/index.js +0 -159
  921. package/src/init.js +0 -49
  922. package/src/interaction-tracker.js +0 -60
  923. package/src/interactive2/__tests__/movable-mock.js +0 -48
  924. package/src/interactive2/__tests__/movable-point_test.js +0 -146
  925. package/src/interactive2/interactive-util.js +0 -152
  926. package/src/interactive2/movable-helper-methods.js +0 -160
  927. package/src/interactive2/movable-line-options.js +0 -392
  928. package/src/interactive2/movable-line.js +0 -322
  929. package/src/interactive2/movable-point-options.js +0 -190
  930. package/src/interactive2/movable-point.jsx +0 -597
  931. package/src/interactive2/movable-polygon-options.js +0 -351
  932. package/src/interactive2/movable-polygon.js +0 -368
  933. package/src/interactive2/movable.js +0 -273
  934. package/src/interactive2/objective_.js +0 -79
  935. package/src/interactive2/types.js +0 -3
  936. package/src/interactive2/wrapped-defaults.js +0 -86
  937. package/src/interactive2/wrapped-ellipse.js +0 -86
  938. package/src/interactive2/wrapped-line.js +0 -109
  939. package/src/interactive2/wrapped-path.js +0 -47
  940. package/src/interactive2.js +0 -91
  941. package/src/item-renderer.jsx +0 -514
  942. package/src/item-version.js +0 -12
  943. package/src/jipt-paragraphs.js +0 -52
  944. package/src/keypad-context.js +0 -33
  945. package/src/loading-context.js +0 -20
  946. package/src/logging/log.js +0 -107
  947. package/src/logging/log_test.js +0 -41
  948. package/src/mixins/__tests__/widget-prop-denylist_test.js +0 -23
  949. package/src/mixins/changeable.jsx +0 -115
  950. package/src/mixins/editor-jsonify.jsx +0 -13
  951. package/src/mixins/provide-keypad.jsx +0 -131
  952. package/src/mixins/widget-jsonify-deprecated.jsx +0 -23
  953. package/src/mixins/widget-prop-denylist.js +0 -49
  954. package/src/multi-items/__stories__/multi-renderer.stories.js +0 -88
  955. package/src/multi-items/__testdata__/multi-renderer_testdata.js +0 -174
  956. package/src/multi-items/__tests__/items_test.js +0 -245
  957. package/src/multi-items/__tests__/multi-renderer_test.js +0 -903
  958. package/src/multi-items/__tests__/prop-type-builders_test.js +0 -134
  959. package/src/multi-items/__tests__/shapes_test.js +0 -44
  960. package/src/multi-items/__tests__/trees_test.js +0 -316
  961. package/src/multi-items/items.js +0 -176
  962. package/src/multi-items/multi-renderer.jsx +0 -543
  963. package/src/multi-items/prop-type-builders.js +0 -105
  964. package/src/multi-items/shape-types.js +0 -47
  965. package/src/multi-items/shapes.js +0 -53
  966. package/src/multi-items/tree-types.js +0 -42
  967. package/src/multi-items/trees.js +0 -345
  968. package/src/multi-items.js +0 -76
  969. package/src/perseus-api.jsx +0 -214
  970. package/src/perseus-env.js +0 -26
  971. package/src/perseus-error.js +0 -19
  972. package/src/perseus-markdown.jsx +0 -408
  973. package/src/perseus-types.js +0 -1471
  974. package/src/question-paragraph.jsx +0 -30
  975. package/src/renderability.jsx +0 -108
  976. package/src/renderer.jsx +0 -1937
  977. package/src/server-item-renderer.jsx +0 -467
  978. package/src/sigfigs.jsx +0 -492
  979. package/src/styles/constants.js +0 -96
  980. package/src/styles/global-constants.js +0 -344
  981. package/src/styles/global-styles.js +0 -307
  982. package/src/styles/media-queries.js +0 -48
  983. package/src/styles/shared.js +0 -132
  984. package/src/tex-wrangler.js +0 -176
  985. package/src/translation-linter.js +0 -125
  986. package/src/traversal.jsx +0 -141
  987. package/src/types.js +0 -482
  988. package/src/util/a11y.js +0 -24
  989. package/src/util/answer-types.js +0 -861
  990. package/src/util/answer-types_test.js +0 -115
  991. package/src/util/colors.js +0 -94
  992. package/src/util/fix-passage-refs.jsx +0 -122
  993. package/src/util/graph-utils.js +0 -16
  994. package/src/util/graphie.js +0 -1586
  995. package/src/util/interactive.js +0 -4078
  996. package/src/util/katex-preprocess.js +0 -16
  997. package/src/util/math.js +0 -289
  998. package/src/util/mobile-native-utils.js +0 -14
  999. package/src/util/mobile-native-utils_test.js +0 -24
  1000. package/src/util/react-render.js +0 -27
  1001. package/src/util/register-all-widgets-for-testing.js +0 -12
  1002. package/src/util/scroll-utils.js +0 -162
  1003. package/src/util/sizing-utils.js +0 -46
  1004. package/src/util/tex.js +0 -203
  1005. package/src/util/url-utils.js +0 -36
  1006. package/src/util.js +0 -984
  1007. package/src/version.js +0 -11
  1008. package/src/widget-container.jsx +0 -178
  1009. package/src/widgets/__stories__/categorizer.stories.jsx +0 -15
  1010. package/src/widgets/__stories__/cs-program.stories.jsx +0 -15
  1011. package/src/widgets/__stories__/definition.stories.jsx +0 -108
  1012. package/src/widgets/__stories__/dropdown.stories.jsx +0 -15
  1013. package/src/widgets/__stories__/explanation.stories.jsx +0 -19
  1014. package/src/widgets/__stories__/expression.stories.jsx +0 -26
  1015. package/src/widgets/__stories__/graded-group-set.stories.jsx +0 -32
  1016. package/src/widgets/__stories__/graded-group.stories.jsx +0 -32
  1017. package/src/widgets/__stories__/grapher.stories.jsx +0 -47
  1018. package/src/widgets/__stories__/group.stories.jsx +0 -15
  1019. package/src/widgets/__stories__/iframe.stories.jsx +0 -15
  1020. package/src/widgets/__stories__/image.stories.jsx +0 -62
  1021. package/src/widgets/__stories__/input-number.stories.jsx +0 -102
  1022. package/src/widgets/__stories__/interaction.stories.jsx +0 -15
  1023. package/src/widgets/__stories__/interactive-graph.stories.jsx +0 -61
  1024. package/src/widgets/__stories__/matcher.stories.jsx +0 -15
  1025. package/src/widgets/__stories__/matrix.stories.jsx +0 -15
  1026. package/src/widgets/__stories__/number-line.stories.jsx +0 -15
  1027. package/src/widgets/__stories__/numeric-input.stories.jsx +0 -106
  1028. package/src/widgets/__stories__/on-screen-keypad.stories.jsx +0 -118
  1029. package/src/widgets/__stories__/orderer.stories.jsx +0 -15
  1030. package/src/widgets/__stories__/passage-ref.stories.jsx +0 -23
  1031. package/src/widgets/__stories__/passage.stories.jsx +0 -27
  1032. package/src/widgets/__stories__/radio.stories.jsx +0 -90
  1033. package/src/widgets/__stories__/sorter.stories.jsx +0 -15
  1034. package/src/widgets/__stories__/transformer.stories.jsx +0 -15
  1035. package/src/widgets/__stories__/unit.stories.jsx +0 -29
  1036. package/src/widgets/__stories__/video-transcript-link.stories.jsx +0 -24
  1037. package/src/widgets/__stories__/video.stories.jsx +0 -15
  1038. package/src/widgets/__testdata__/base-radio_testdata.js +0 -22
  1039. package/src/widgets/__testdata__/categorizer_testdata.js +0 -50
  1040. package/src/widgets/__testdata__/cs-program_testdata.js +0 -28
  1041. package/src/widgets/__testdata__/dropdown_testdata.js +0 -34
  1042. package/src/widgets/__testdata__/explanation_testdata.js +0 -56
  1043. package/src/widgets/__testdata__/expression_testdata.js +0 -95
  1044. package/src/widgets/__testdata__/graded-group-set_testdata.js +0 -358
  1045. package/src/widgets/__testdata__/graded-group_testdata.js +0 -79
  1046. package/src/widgets/__testdata__/grapher_testdata.js +0 -376
  1047. package/src/widgets/__testdata__/group_testdata.js +0 -156
  1048. package/src/widgets/__testdata__/iframe_testdata.js +0 -36
  1049. package/src/widgets/__testdata__/image_testdata.js +0 -38
  1050. package/src/widgets/__testdata__/input-number_testdata.js +0 -73
  1051. package/src/widgets/__testdata__/interaction_testdata.js +0 -264
  1052. package/src/widgets/__testdata__/interactive-graph_testdata.js +0 -646
  1053. package/src/widgets/__testdata__/matcher_testdata.js +0 -34
  1054. package/src/widgets/__testdata__/matrix_testdata.js +0 -29
  1055. package/src/widgets/__testdata__/number-line_testdata.js +0 -31
  1056. package/src/widgets/__testdata__/numeric-input_testdata.js +0 -266
  1057. package/src/widgets/__testdata__/orderer_testdata.js +0 -58
  1058. package/src/widgets/__testdata__/passage-ref_testdata.js +0 -103
  1059. package/src/widgets/__testdata__/passage_testdata.js +0 -96
  1060. package/src/widgets/__testdata__/radio_testdata.js +0 -262
  1061. package/src/widgets/__testdata__/sorter_testdata.js +0 -20
  1062. package/src/widgets/__testdata__/transformer_testdata.js +0 -109
  1063. package/src/widgets/__testdata__/video_testdata.js +0 -22
  1064. package/src/widgets/__tests__/categorizer_test.jsx +0 -205
  1065. package/src/widgets/__tests__/cs-program_test.jsx +0 -45
  1066. package/src/widgets/__tests__/definition_test.js +0 -137
  1067. package/src/widgets/__tests__/dropdown_test.jsx +0 -106
  1068. package/src/widgets/__tests__/explanation_test.js +0 -199
  1069. package/src/widgets/__tests__/expression_test.jsx +0 -429
  1070. package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +0 -32
  1071. package/src/widgets/__tests__/graded-group-set_test.jsx +0 -290
  1072. package/src/widgets/__tests__/graded-group_test.jsx +0 -233
  1073. package/src/widgets/__tests__/grapher.cypress.jsx +0 -546
  1074. package/src/widgets/__tests__/grapher_test.jsx +0 -35
  1075. package/src/widgets/__tests__/group_test.jsx +0 -512
  1076. package/src/widgets/__tests__/iframe_test.jsx +0 -47
  1077. package/src/widgets/__tests__/image_test.jsx +0 -65
  1078. package/src/widgets/__tests__/input-number_test.jsx +0 -352
  1079. package/src/widgets/__tests__/interaction_test.jsx +0 -33
  1080. package/src/widgets/__tests__/interactive-graph.cypress.jsx +0 -777
  1081. package/src/widgets/__tests__/interactive-graph_test.jsx +0 -105
  1082. package/src/widgets/__tests__/matcher_test.jsx +0 -157
  1083. package/src/widgets/__tests__/matrix_test.jsx +0 -84
  1084. package/src/widgets/__tests__/number-line_test.jsx +0 -81
  1085. package/src/widgets/__tests__/numeric-input_test.jsx +0 -503
  1086. package/src/widgets/__tests__/orderer_test.jsx +0 -75
  1087. package/src/widgets/__tests__/passage-ref_test.jsx +0 -166
  1088. package/src/widgets/__tests__/passage_test.jsx +0 -212
  1089. package/src/widgets/__tests__/radio/base-radio_test.jsx +0 -443
  1090. package/src/widgets/__tests__/radio/choice-icon_test.jsx +0 -91
  1091. package/src/widgets/__tests__/radio/choice_test.jsx +0 -311
  1092. package/src/widgets/__tests__/radio/focus-ring_test.jsx +0 -23
  1093. package/src/widgets/__tests__/radio/option-status_test.jsx +0 -65
  1094. package/src/widgets/__tests__/radio_test.jsx +0 -810
  1095. package/src/widgets/__tests__/renderQuestion.jsx +0 -66
  1096. package/src/widgets/__tests__/unit_test.jsx +0 -169
  1097. package/src/widgets/__tests__/video_test.jsx +0 -55
  1098. package/src/widgets/categorizer.jsx +0 -343
  1099. package/src/widgets/cs-program.jsx +0 -227
  1100. package/src/widgets/definition.jsx +0 -121
  1101. package/src/widgets/dropdown.jsx +0 -141
  1102. package/src/widgets/explanation.jsx +0 -315
  1103. package/src/widgets/expression.jsx +0 -629
  1104. package/src/widgets/graded-group-answer-bar.jsx +0 -152
  1105. package/src/widgets/graded-group-set.jsx +0 -296
  1106. package/src/widgets/graded-group.jsx +0 -453
  1107. package/src/widgets/grapher/util.jsx +0 -806
  1108. package/src/widgets/grapher.jsx +0 -663
  1109. package/src/widgets/group.jsx +0 -195
  1110. package/src/widgets/iframe.jsx +0 -198
  1111. package/src/widgets/image.jsx +0 -310
  1112. package/src/widgets/input-number.jsx +0 -402
  1113. package/src/widgets/interaction/arrow-picker.jsx +0 -39
  1114. package/src/widgets/interaction/color-picker.jsx +0 -69
  1115. package/src/widgets/interaction/dash-picker.jsx +0 -46
  1116. package/src/widgets/interaction/element-container.jsx +0 -97
  1117. package/src/widgets/interaction.jsx +0 -742
  1118. package/src/widgets/interactive-graph.jsx +0 -2820
  1119. package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +0 -93
  1120. package/src/widgets/label-image/__stories__/marker.stories.jsx +0 -172
  1121. package/src/widgets/label-image/answer-choices.jsx +0 -311
  1122. package/src/widgets/label-image/marker.jsx +0 -365
  1123. package/src/widgets/label-image/types.js +0 -33
  1124. package/src/widgets/label-image.jsx +0 -841
  1125. package/src/widgets/label-image_test.js +0 -840
  1126. package/src/widgets/lights-puzzle.jsx +0 -356
  1127. package/src/widgets/matcher.jsx +0 -274
  1128. package/src/widgets/matrix.jsx +0 -616
  1129. package/src/widgets/measurer.jsx +0 -215
  1130. package/src/widgets/molecule/__tests__/layout_test.jsx +0 -159
  1131. package/src/widgets/molecule/__tests__/parser_test.jsx +0 -142
  1132. package/src/widgets/molecule/molecule-drawing.jsx +0 -175
  1133. package/src/widgets/molecule/molecule-layout.jsx +0 -367
  1134. package/src/widgets/molecule/smiles-parser.jsx +0 -300
  1135. package/src/widgets/molecule.jsx +0 -172
  1136. package/src/widgets/number-line.jsx +0 -855
  1137. package/src/widgets/numeric-input.jsx +0 -546
  1138. package/src/widgets/orderer.jsx +0 -756
  1139. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -373
  1140. package/src/widgets/passage/passage-markdown.jsx +0 -472
  1141. package/src/widgets/passage-ref-target.jsx +0 -73
  1142. package/src/widgets/passage-ref.jsx +0 -214
  1143. package/src/widgets/passage.jsx +0 -576
  1144. package/src/widgets/plotter.jsx +0 -1198
  1145. package/src/widgets/radio/__stories__/base-radio.stories.jsx +0 -189
  1146. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +0 -166
  1147. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +0 -41
  1148. package/src/widgets/radio/__stories__/choice.stories.jsx +0 -138
  1149. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +0 -44
  1150. package/src/widgets/radio/__stories__/option-status.stories.jsx +0 -104
  1151. package/src/widgets/radio/base-radio.jsx +0 -494
  1152. package/src/widgets/radio/choice-a11y-text.js +0 -62
  1153. package/src/widgets/radio/choice-icon/choice-icon.jsx +0 -214
  1154. package/src/widgets/radio/choice-icon/cross-out-line.jsx +0 -52
  1155. package/src/widgets/radio/choice-icon/shared-styles.js +0 -16
  1156. package/src/widgets/radio/choice-none-above.jsx +0 -63
  1157. package/src/widgets/radio/choice.jsx +0 -373
  1158. package/src/widgets/radio/choice.jsx.fixture.js +0 -46
  1159. package/src/widgets/radio/focus-ring.jsx +0 -58
  1160. package/src/widgets/radio/option-status.jsx +0 -98
  1161. package/src/widgets/radio/util.js +0 -32
  1162. package/src/widgets/radio/util_test.js +0 -19
  1163. package/src/widgets/radio/widget.jsx +0 -515
  1164. package/src/widgets/radio.jsx +0 -155
  1165. package/src/widgets/reaction-diagram.jsx +0 -137
  1166. package/src/widgets/sequence.jsx +0 -143
  1167. package/src/widgets/simulator.jsx +0 -802
  1168. package/src/widgets/sorter.jsx +0 -149
  1169. package/src/widgets/table.jsx +0 -389
  1170. package/src/widgets/transformer.jsx +0 -2872
  1171. package/src/widgets/unit.jsx +0 -357
  1172. package/src/widgets/video-transcript-link.jsx +0 -84
  1173. package/src/widgets/video-transcript-link_test.js +0 -211
  1174. package/src/widgets/video.jsx +0 -126
  1175. package/src/widgets.js +0 -431
  1176. package/src/zoom.js +0 -539
  1177. /package/{src/multi-items/item-types.js → dist/multi-items/item-types.js.flow} +0 -0
  1178. /package/dist/{strings.js → strings} +0 -0
  1179. /package/src/__tests__/__snapshots__/{item-renderer_test.jsx.snap → item-renderer.test.tsx.snap} +0 -0
  1180. /package/src/__tests__/__snapshots__/{perseus-markdown_test.jsx.snap → perseus-markdown.test.ts.snap} +0 -0
  1181. /package/src/__tests__/__snapshots__/{renderer_test.jsx.snap → renderer.test.tsx.snap} +0 -0
  1182. /package/src/__tests__/__snapshots__/{server-item-renderer_test.jsx.snap → server-item-renderer.test.tsx.snap} +0 -0
  1183. /package/src/components/__tests__/__snapshots__/{sortable_test.jsx.snap → sortable.test.tsx.snap} +0 -0
  1184. /package/src/components/__tests__/__snapshots__/{sorter_test.jsx.snap → sorter.test.tsx.snap} +0 -0
  1185. /package/src/multi-items/__tests__/__snapshots__/{multi-renderer_test.js.snap → multi-renderer.test.tsx.snap} +0 -0
  1186. /package/src/widgets/__tests__/__snapshots__/{categorizer_test.jsx.snap → categorizer.test.ts.snap} +0 -0
  1187. /package/src/widgets/__tests__/__snapshots__/{cs-program_test.jsx.snap → cs-program.test.ts.snap} +0 -0
  1188. /package/src/widgets/__tests__/__snapshots__/{definition_test.js.snap → definition.test.ts.snap} +0 -0
  1189. /package/src/widgets/__tests__/__snapshots__/{dropdown_test.jsx.snap → dropdown.test.ts.snap} +0 -0
  1190. /package/src/widgets/__tests__/__snapshots__/{explanation_test.js.snap → explanation.test.ts.snap} +0 -0
  1191. /package/src/widgets/__tests__/__snapshots__/{graded-group-set-jipt_test.jsx.snap → graded-group-set-jipt.test.ts.snap} +0 -0
  1192. /package/src/widgets/__tests__/__snapshots__/{graded-group-set_test.jsx.snap → graded-group-set.test.ts.snap} +0 -0
  1193. /package/src/widgets/__tests__/__snapshots__/{graded-group_test.jsx.snap → graded-group.test.ts.snap} +0 -0
  1194. /package/src/widgets/__tests__/__snapshots__/{grapher_test.jsx.snap → grapher.test.ts.snap} +0 -0
  1195. /package/src/widgets/__tests__/__snapshots__/{group_test.jsx.snap → group.test.tsx.snap} +0 -0
  1196. /package/src/widgets/__tests__/__snapshots__/{iframe_test.jsx.snap → iframe.test.ts.snap} +0 -0
  1197. /package/src/widgets/__tests__/__snapshots__/{image_test.jsx.snap → image.test.ts.snap} +0 -0
  1198. /package/src/widgets/__tests__/__snapshots__/{input-number_test.jsx.snap → input-number.test.ts.snap} +0 -0
  1199. /package/src/widgets/__tests__/__snapshots__/{interaction_test.jsx.snap → interaction.test.ts.snap} +0 -0
  1200. /package/src/widgets/__tests__/__snapshots__/{interactive-graph_test.jsx.snap → interactive-graph.test.ts.snap} +0 -0
  1201. /package/src/widgets/__tests__/__snapshots__/{matcher_test.jsx.snap → matcher.test.tsx.snap} +0 -0
  1202. /package/src/widgets/__tests__/__snapshots__/{matrix_test.jsx.snap → matrix.test.ts.snap} +0 -0
  1203. /package/src/widgets/__tests__/__snapshots__/{number-line_test.jsx.snap → number-line.test.ts.snap} +0 -0
  1204. /package/src/widgets/__tests__/__snapshots__/{numeric-input_test.jsx.snap → numeric-input.test.ts.snap} +0 -0
  1205. /package/src/widgets/__tests__/__snapshots__/{orderer_test.jsx.snap → orderer.test.ts.snap} +0 -0
  1206. /package/src/widgets/__tests__/__snapshots__/{passage-ref_test.jsx.snap → passage-ref.test.ts.snap} +0 -0
  1207. /package/src/widgets/__tests__/__snapshots__/{passage_test.jsx.snap → passage.test.tsx.snap} +0 -0
  1208. /package/src/widgets/__tests__/__snapshots__/{radio_test.jsx.snap → radio.test.ts.snap} +0 -0
  1209. /package/src/widgets/__tests__/__snapshots__/{unit_test.jsx.snap → unit.test.tsx.snap} +0 -0
  1210. /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>);