@khanacademy/perseus 4.0.2 → 6.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 (249) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/components/fixed-to-responsive.d.ts +27 -1
  3. package/dist/components/fixed-to-responsive.js.flow +27 -1
  4. package/dist/components/graph.d.ts +42 -26
  5. package/dist/components/graph.js.flow +42 -28
  6. package/dist/components/graphie.d.ts +34 -22
  7. package/dist/components/graphie.js.flow +37 -24
  8. package/dist/components/highlighting/highlightable-content.d.ts +4 -4
  9. package/dist/components/highlighting/highlightable-content.js.flow +4 -7
  10. package/dist/components/highlighting/ui/highlight-renderer.d.ts +6 -6
  11. package/dist/components/highlighting/ui/highlight-renderer.js.flow +6 -8
  12. package/dist/components/highlighting/ui/highlight-set-renderer.d.ts +5 -5
  13. package/dist/components/highlighting/ui/highlight-set-renderer.js.flow +5 -8
  14. package/dist/components/highlighting/ui/highlight-tooltip.d.ts +2 -2
  15. package/dist/components/highlighting/ui/highlight-tooltip.js.flow +2 -4
  16. package/dist/components/highlighting/ui/highlighting-ui.d.ts +2 -2
  17. package/dist/components/highlighting/ui/highlighting-ui.js.flow +2 -2
  18. package/dist/components/highlighting/ui/selection-tracker.d.ts +5 -5
  19. package/dist/components/highlighting/ui/selection-tracker.js.flow +5 -7
  20. package/dist/components/highlighting/word-indexer.d.ts +2 -2
  21. package/dist/components/highlighting/word-indexer.js.flow +2 -2
  22. package/dist/components/hud.d.ts +8 -14
  23. package/dist/components/hud.js.flow +7 -14
  24. package/dist/components/inline-icon.d.ts +27 -3
  25. package/dist/components/inline-icon.js.flow +26 -3
  26. package/dist/components/lint.d.ts +41 -13
  27. package/dist/components/lint.js.flow +42 -13
  28. package/dist/components/math-output.d.ts +15 -11
  29. package/dist/components/math-output.js.flow +15 -11
  30. package/dist/components/simple-keypad-input.d.ts +1 -1
  31. package/dist/components/simple-keypad-input.js.flow +1 -1
  32. package/dist/components/sortable.d.ts +17 -9
  33. package/dist/components/sortable.js.flow +22 -9
  34. package/dist/components/svg-image.d.ts +5 -3
  35. package/dist/components/svg-image.js.flow +5 -3
  36. package/dist/components/tex-buttons.d.ts +2 -2
  37. package/dist/components/tex-buttons.js.flow +2 -2
  38. package/dist/components/tooltip.d.ts +45 -4
  39. package/dist/components/tooltip.js.flow +44 -31
  40. package/dist/error-boundary.d.ts +4 -4
  41. package/dist/error-boundary.js.flow +4 -6
  42. package/dist/es/index.css +0 -3
  43. package/dist/es/index.js +484 -658
  44. package/dist/es/index.js.map +1 -1
  45. package/dist/index.css +0 -3
  46. package/dist/index.js +488 -655
  47. package/dist/index.js.map +1 -1
  48. package/dist/perseus-types.d.ts +11 -10
  49. package/dist/perseus-types.js.flow +11 -10
  50. package/dist/renderer.d.ts +2 -2
  51. package/dist/renderer.js.flow +1 -3
  52. package/dist/styles/shared.d.ts +0 -1
  53. package/dist/styles/shared.js.flow +0 -1
  54. package/dist/types.d.ts +0 -10
  55. package/dist/types.js.flow +0 -13
  56. package/dist/util/a11y.d.ts +0 -1
  57. package/dist/util/a11y.js.flow +0 -1
  58. package/dist/util/interactive.d.ts +2 -0
  59. package/dist/util/interactive.js.flow +2 -0
  60. package/dist/util.d.ts +3 -3
  61. package/dist/util.js.flow +4 -3
  62. package/dist/widgets/__stories__/test-keypad-context-wrapper.d.ts +2 -2
  63. package/dist/widgets/__stories__/test-keypad-context-wrapper.js.flow +2 -4
  64. package/dist/widgets/expression.d.ts +2 -2
  65. package/dist/widgets/expression.js.flow +2 -2
  66. package/dist/widgets/grapher.d.ts +1 -1
  67. package/dist/widgets/grapher.js.flow +12 -3
  68. package/dist/widgets/interaction/element-container.d.ts +5 -4
  69. package/dist/widgets/interaction/element-container.js.flow +6 -11
  70. package/dist/widgets/label-image/marker.d.ts +4 -4
  71. package/dist/widgets/label-image/marker.js.flow +4 -6
  72. package/dist/widgets/matcher.d.ts +7 -3
  73. package/dist/widgets/matcher.js.flow +7 -3
  74. package/dist/widgets/number-line.d.ts +1 -1
  75. package/dist/widgets/number-line.js.flow +1 -1
  76. package/dist/widgets/numeric-input.d.ts +0 -2
  77. package/dist/widgets/numeric-input.js.flow +0 -4
  78. package/dist/widgets/radio/base-radio.d.ts +7 -1
  79. package/dist/widgets/radio/base-radio.js.flow +7 -1
  80. package/dist/widgets/radio/choice-icon/choice-icon.d.ts +1 -1
  81. package/dist/widgets/radio/choice-icon/choice-icon.js.flow +1 -1
  82. package/dist/widgets/radio/choice-icon/cross-out-line.d.ts +2 -2
  83. package/dist/widgets/radio/choice-icon/cross-out-line.js.flow +2 -2
  84. package/dist/widgets/radio/focus-ring.d.ts +8 -1
  85. package/dist/widgets/radio/focus-ring.js.flow +8 -1
  86. package/dist/widgets/radio/option-status.d.ts +2 -2
  87. package/dist/widgets/radio/option-status.js.flow +2 -2
  88. package/dist/widgets/video-transcript-link.d.ts +1 -1
  89. package/dist/widgets/video-transcript-link.js.flow +1 -1
  90. package/package.json +4 -4
  91. package/src/__stories__/item-renderer.stories.tsx +2 -6
  92. package/src/__stories__/server-item-renderer.stories.tsx +2 -6
  93. package/src/__tests__/__snapshots__/item-renderer.test.tsx.snap +151 -159
  94. package/src/__tests__/__snapshots__/renderer.test.tsx.snap +358 -394
  95. package/src/__tests__/__snapshots__/server-item-renderer.test.tsx.snap +169 -189
  96. package/src/components/__stories__/button-group.stories.tsx +2 -6
  97. package/src/components/__stories__/fixed-to-responsive.stories.tsx +14 -16
  98. package/src/components/__stories__/graph.stories.tsx +3 -5
  99. package/src/components/__stories__/graphie.stories.tsx +3 -5
  100. package/src/components/__stories__/hud.stories.tsx +2 -6
  101. package/src/components/__stories__/icon.stories.tsx +1 -1
  102. package/src/components/__stories__/image-loader.stories.tsx +4 -4
  103. package/src/components/__stories__/info-tip.stories.tsx +2 -6
  104. package/src/components/__stories__/inline-icon.stories.tsx +4 -8
  105. package/src/components/__stories__/input-with-examples.stories.tsx +6 -10
  106. package/src/components/__stories__/lint.stories.tsx +62 -27
  107. package/src/components/__stories__/math-input.stories.tsx +4 -8
  108. package/src/components/__stories__/math-output.stories.tsx +3 -5
  109. package/src/components/__stories__/multi-button-group.stories.tsx +2 -6
  110. package/src/components/__stories__/number-input.stories.tsx +6 -8
  111. package/src/components/__stories__/prop-check-box.stories.tsx +9 -9
  112. package/src/components/__stories__/range-input.stories.tsx +4 -6
  113. package/src/components/__stories__/simple-keypad-input.stories.tsx +2 -4
  114. package/src/components/__stories__/sortable.stories.tsx +20 -20
  115. package/src/components/__stories__/stub-tag-editor.stories.tsx +5 -9
  116. package/src/components/__stories__/svg-image.stories.tsx +10 -18
  117. package/src/components/__stories__/tex.stories.tsx +1 -3
  118. package/src/components/__stories__/text-buttons.stories.tsx +10 -22
  119. package/src/components/__stories__/text-input.stories.tsx +4 -10
  120. package/src/components/__stories__/text-list-editor.stories.tsx +1 -3
  121. package/src/components/__stories__/tooltip.stories.tsx +52 -0
  122. package/src/components/__stories__/zoomable-tex.stories.tsx +6 -4
  123. package/src/components/__stories__/zoomable.stories.tsx +1 -3
  124. package/src/components/__tests__/__snapshots__/sortable.test.tsx.snap +114 -30
  125. package/src/components/__tests__/__snapshots__/sorter.test.tsx.snap +172 -204
  126. package/src/components/__tests__/sortable.test.tsx +54 -16
  127. package/src/components/__tests__/sorter.test.tsx +12 -9
  128. package/src/components/fixed-to-responsive.tsx +43 -38
  129. package/src/components/graph.tsx +95 -81
  130. package/src/components/graphie.tsx +62 -32
  131. package/src/components/highlighting/highlightable-content.tsx +4 -7
  132. package/src/components/highlighting/ui/highlight-renderer.tsx +6 -9
  133. package/src/components/highlighting/ui/highlight-set-renderer.tsx +5 -8
  134. package/src/components/highlighting/ui/highlight-tooltip.tsx +2 -2
  135. package/src/components/highlighting/ui/highlighting-ui.tsx +2 -2
  136. package/src/components/highlighting/ui/selection-tracker.tsx +5 -8
  137. package/src/components/highlighting/word-indexer.tsx +2 -2
  138. package/src/components/hud.tsx +104 -115
  139. package/src/components/inline-icon.tsx +17 -34
  140. package/src/components/input-with-examples.tsx +4 -4
  141. package/src/components/lint.tsx +47 -41
  142. package/src/components/math-output.tsx +17 -11
  143. package/src/components/simple-keypad-input.tsx +4 -4
  144. package/src/components/sortable.tsx +134 -94
  145. package/src/components/svg-image.tsx +10 -11
  146. package/src/components/tex-buttons.tsx +2 -2
  147. package/src/components/tooltip.tsx +189 -171
  148. package/src/components/visibility-observer/__stories__/visibility-observer.stories.tsx +1 -3
  149. package/src/error-boundary.tsx +4 -7
  150. package/src/interactive2/types.ts +2 -2
  151. package/src/multi-items/__stories__/multi-renderer.stories.tsx +1 -1
  152. package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +708 -764
  153. package/src/multi-items/__tests__/multi-renderer.test.tsx +2 -3
  154. package/src/perseus-types.ts +13 -12
  155. package/src/renderer.tsx +11 -22
  156. package/src/types.ts +0 -18
  157. package/src/util/graphie.ts +3 -3
  158. package/src/util/interactive.ts +18 -5
  159. package/src/util/tex.ts +13 -77
  160. package/src/util.ts +3 -4
  161. package/src/widgets/__stories__/categorizer.stories.tsx +1 -1
  162. package/src/widgets/__stories__/cs-program.stories.tsx +1 -1
  163. package/src/widgets/__stories__/definition.stories.tsx +3 -7
  164. package/src/widgets/__stories__/dropdown.stories.tsx +1 -1
  165. package/src/widgets/__stories__/explanation.stories.tsx +2 -2
  166. package/src/widgets/__stories__/expression.stories.tsx +5 -11
  167. package/src/widgets/__stories__/graded-group-set.stories.tsx +1 -1
  168. package/src/widgets/__stories__/graded-group.stories.tsx +1 -1
  169. package/src/widgets/__stories__/grapher.stories.tsx +7 -21
  170. package/src/widgets/__stories__/group.stories.tsx +1 -1
  171. package/src/widgets/__stories__/iframe.stories.tsx +1 -1
  172. package/src/widgets/__stories__/image.stories.tsx +1 -1
  173. package/src/widgets/__stories__/input-number.stories.tsx +3 -12
  174. package/src/widgets/__stories__/interaction.stories.tsx +1 -1
  175. package/src/widgets/__stories__/interactive-graph.stories.tsx +9 -9
  176. package/src/widgets/__stories__/matcher.stories.tsx +1 -1
  177. package/src/widgets/__stories__/matrix.stories.tsx +1 -1
  178. package/src/widgets/__stories__/number-line.stories.tsx +1 -1
  179. package/src/widgets/__stories__/numeric-input.stories.tsx +3 -5
  180. package/src/widgets/__stories__/orderer.stories.tsx +1 -1
  181. package/src/widgets/__stories__/passage-ref.stories.tsx +2 -2
  182. package/src/widgets/__stories__/passage.stories.tsx +3 -9
  183. package/src/widgets/__stories__/radio.stories.tsx +3 -5
  184. package/src/widgets/__stories__/sorter.stories.tsx +1 -1
  185. package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +2 -4
  186. package/src/widgets/__stories__/transformer.stories.tsx +1 -1
  187. package/src/widgets/__stories__/unit.stories.tsx +1 -3
  188. package/src/widgets/__stories__/video-transcript-link.stories.tsx +2 -6
  189. package/src/widgets/__stories__/video.stories.tsx +1 -1
  190. package/src/widgets/__testdata__/group.testdata.ts +0 -2
  191. package/src/widgets/__tests__/__snapshots__/categorizer.test.ts.snap +717 -773
  192. package/src/widgets/__tests__/__snapshots__/cs-program.test.ts.snap +28 -36
  193. package/src/widgets/__tests__/__snapshots__/definition.test.ts.snap +52 -60
  194. package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +100 -108
  195. package/src/widgets/__tests__/__snapshots__/explanation.test.ts.snap +212 -260
  196. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt.test.ts.snap +613 -641
  197. package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +249 -261
  198. package/src/widgets/__tests__/__snapshots__/graded-group.test.ts.snap +671 -735
  199. package/src/widgets/__tests__/__snapshots__/grapher.test.ts.snap +2676 -2684
  200. package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +938 -1002
  201. package/src/widgets/__tests__/__snapshots__/iframe.test.ts.snap +28 -36
  202. package/src/widgets/__tests__/__snapshots__/image.test.ts.snap +227 -255
  203. package/src/widgets/__tests__/__snapshots__/input-number.test.ts.snap +353 -357
  204. package/src/widgets/__tests__/__snapshots__/interaction.test.ts.snap +255 -259
  205. package/src/widgets/__tests__/__snapshots__/interactive-graph.test.ts.snap +2401 -2433
  206. package/src/widgets/__tests__/__snapshots__/matcher.test.tsx.snap +600 -756
  207. package/src/widgets/__tests__/__snapshots__/matrix.test.ts.snap +420 -428
  208. package/src/widgets/__tests__/__snapshots__/number-line.test.ts.snap +762 -770
  209. package/src/widgets/__tests__/__snapshots__/numeric-input.test.ts.snap +373 -393
  210. package/src/widgets/__tests__/__snapshots__/orderer.test.ts.snap +116 -148
  211. package/src/widgets/__tests__/__snapshots__/passage-ref.test.ts.snap +289 -301
  212. package/src/widgets/__tests__/__snapshots__/passage.test.tsx.snap +1679 -1711
  213. package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +2692 -2960
  214. package/src/widgets/__tests__/__snapshots__/video.test.ts.snap +90 -98
  215. package/src/widgets/__tests__/group.test.tsx +0 -2
  216. package/src/widgets/__tests__/matcher.test.tsx +33 -9
  217. package/src/widgets/__tests__/radio/focus-ring.test.tsx +0 -1
  218. package/src/widgets/expression.tsx +12 -9
  219. package/src/widgets/grapher.tsx +3 -3
  220. package/src/widgets/image.tsx +2 -2
  221. package/src/widgets/interaction/element-container.tsx +7 -8
  222. package/src/widgets/interactive-graph.tsx +5 -5
  223. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +2 -4
  224. package/src/widgets/label-image/__stories__/marker.stories.tsx +9 -19
  225. package/src/widgets/label-image/marker.tsx +4 -4
  226. package/src/widgets/matcher.tsx +43 -24
  227. package/src/widgets/number-line.tsx +1 -1
  228. package/src/widgets/numeric-input.tsx +0 -3
  229. package/src/widgets/passage/passage-markdown.tsx +2 -2
  230. package/src/widgets/radio/__stories__/base-radio.stories.tsx +11 -19
  231. package/src/widgets/radio/__stories__/choice-icon.stories.tsx +9 -11
  232. package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +1 -1
  233. package/src/widgets/radio/__stories__/choice.stories.tsx +4 -4
  234. package/src/widgets/radio/__stories__/focus-ring.stories.tsx +1 -1
  235. package/src/widgets/radio/__stories__/option-status.stories.tsx +2 -4
  236. package/src/widgets/radio/base-radio.tsx +1 -1
  237. package/src/widgets/radio/choice-icon/choice-icon.tsx +5 -5
  238. package/src/widgets/radio/choice-icon/cross-out-line.tsx +1 -3
  239. package/src/widgets/radio/choice-none-above.tsx +2 -2
  240. package/src/widgets/radio/choice.tsx +1 -3
  241. package/src/widgets/radio/focus-ring.tsx +1 -1
  242. package/src/widgets/radio/option-status.tsx +2 -4
  243. package/src/widgets/radio/widget.tsx +0 -1
  244. package/src/widgets/reaction-diagram.tsx +2 -2
  245. package/src/widgets/simulator.tsx +4 -6
  246. package/src/widgets/video-transcript-link.tsx +1 -1
  247. package/{tsconfig.json → tsconfig-build.json} +6 -6
  248. package/tsconfig-build.tsbuildinfo +1 -0
  249. package/tsconfig.tsbuildinfo +0 -1
@@ -1,9 +1,8 @@
1
- /* eslint-disable @babel/no-invalid-this, react/forbid-prop-types, react/no-unsafe, react/sort-comp */
1
+ /* eslint-disable @babel/no-invalid-this, react/no-unsafe, react/sort-comp */
2
2
  import * as PerseusLinter from "@khanacademy/perseus-linter";
3
3
  import {CircularSpinner} from "@khanacademy/wonder-blocks-progress-spinner";
4
4
  import {StyleSheet, css} from "aphrodite";
5
5
  import $ from "jquery";
6
- import PropTypes from "prop-types";
7
6
  import * as React from "react";
8
7
  import ReactDOM from "react-dom";
9
8
  import _ from "underscore";
@@ -11,17 +10,21 @@ import _ from "underscore";
11
10
  import {getDependencies} from "../dependencies";
12
11
  import {ClassNames as ApiClassNames} from "../perseus-api";
13
12
  import Renderer from "../renderer";
14
- import Util from "../util";
13
+ import Util, {Position} from "../util";
15
14
 
16
15
  import type {LinterContextProps} from "../types";
17
16
 
18
- const HORIZONTAL = "horizontal";
19
- const VERTICAL = "vertical";
17
+ export enum Layout {
18
+ HORIZONTAL = "horizontal",
19
+ VERTICAL = "vertical",
20
+ }
20
21
 
21
- const STATIC = "static";
22
- const DRAGGING = "dragging";
23
- const ANIMATING = "animating";
24
- const DISABLED = "disabled";
22
+ enum ItemState {
23
+ STATIC = "static",
24
+ DRAGGING = "dragging",
25
+ ANIMATING = "animating",
26
+ DISABLED = "disabled",
27
+ }
25
28
 
26
29
  // Augment the given position with the scroll position of the offset parent.
27
30
  const addOffsetParentScroll = ($el: any, position: any) => {
@@ -38,20 +41,21 @@ const addOffsetParentScroll = ($el: any, position: any) => {
38
41
  };
39
42
  };
40
43
 
41
- // A placeholder that appears in the sortable whenever an item is dragged.
42
- class Placeholder extends React.Component<any> {
43
- static propTypes = {
44
- layout: PropTypes.oneOf([HORIZONTAL, VERTICAL]),
45
- width: PropTypes.number.isRequired,
46
- height: PropTypes.number.isRequired,
47
- };
44
+ type PlaceholderProps = {
45
+ layout: Layout;
46
+ width: number;
47
+ height: number;
48
+ margin?: string;
49
+ };
48
50
 
51
+ // A placeholder that appears in the sortable whenever an item is dragged.
52
+ class Placeholder extends React.Component<PlaceholderProps> {
49
53
  render(): React.ReactNode {
50
54
  const {layout} = this.props;
51
55
  const className = css(
52
56
  styles.card,
53
57
  styles.placeholder,
54
- layout === HORIZONTAL && styles.horizontalCard,
58
+ layout === Layout.HORIZONTAL && styles.horizontalCard,
55
59
  );
56
60
  const style: any = {
57
61
  width: this.props.width,
@@ -66,8 +70,35 @@ class Placeholder extends React.Component<any> {
66
70
  }
67
71
  }
68
72
 
69
- type DraggableProps = any;
70
- type DraggableState = any;
73
+ type DraggableProps = {
74
+ content: string;
75
+ endPosition: {left: number; top: number} | Record<string, never>;
76
+ includePadding: boolean;
77
+ layout: Layout;
78
+ width?: number;
79
+ height?: number;
80
+ margin?: string;
81
+ onAnimationEnd: () => void;
82
+ onMouseDown: () => void;
83
+ onMouseMove: () => void;
84
+ onMouseUp: () => void;
85
+ onRender: () => void;
86
+ state: ItemState;
87
+ linterContext: LinterContextProps;
88
+ };
89
+
90
+ type DefaultDraggableProps = {
91
+ includePadding: DraggableProps["includePadding"];
92
+ type: DraggableProps["state"];
93
+ linterContext: DraggableProps["linterContext"];
94
+ };
95
+
96
+ type DraggableState = {
97
+ startPosition: Position;
98
+ startMouse: Position;
99
+ mouse: Position;
100
+ dragging?: boolean;
101
+ };
71
102
 
72
103
  // A draggable item in the sortable. Can be in one of four states:
73
104
  // Static: The item is not being interacted with.
@@ -85,23 +116,9 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
85
116
  // @ts-expect-error [FEI-5003] - TS2564 - Property '_mounted' has no initializer and is not definitely assigned in the constructor.
86
117
  _mounted: boolean;
87
118
 
88
- static propTypes = {
89
- content: PropTypes.string.isRequired,
90
- endPosition: PropTypes.object.isRequired,
91
- includePadding: PropTypes.bool,
92
- layout: PropTypes.oneOf([HORIZONTAL, VERTICAL]),
93
- onAnimationEnd: PropTypes.func.isRequired,
94
- onMouseDown: PropTypes.func.isRequired,
95
- onMouseMove: PropTypes.func.isRequired,
96
- onMouseUp: PropTypes.func.isRequired,
97
- onRender: PropTypes.func.isRequired,
98
- type: PropTypes.oneOf([STATIC, DRAGGING, ANIMATING, DISABLED]),
99
- linterContext: PerseusLinter.linterContextProps,
100
- };
101
-
102
- static defaultProps = {
119
+ static defaultProps: DefaultDraggableProps = {
103
120
  includePadding: true,
104
- type: STATIC,
121
+ type: ItemState.STATIC,
105
122
  linterContext: PerseusLinter.linterContextDefault,
106
123
  };
107
124
 
@@ -109,6 +126,7 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
109
126
  startPosition: {left: 0, top: 0},
110
127
  startMouse: {left: 0, top: 0},
111
128
  mouse: {left: 0, top: 0},
129
+ dragging: false,
112
130
  };
113
131
 
114
132
  componentDidMount() {
@@ -163,7 +181,7 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
163
181
  };
164
182
 
165
183
  render(): React.ReactNode {
166
- const {includePadding, layout, type} = this.props;
184
+ const {includePadding, layout, state: type} = this.props;
167
185
 
168
186
  // We need to keep backwards compatbility with rules specified directly
169
187
  // in CSS. Hence the hacky tacking on of manual classNames.
@@ -172,10 +190,10 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
172
190
  css(
173
191
  styles.card,
174
192
  styles.draggable,
175
- layout === HORIZONTAL && styles.horizontalCard,
176
- layout === VERTICAL && styles.verticalCard,
177
- type === DRAGGING && styles.dragging,
178
- type === DISABLED && styles.disabled,
193
+ layout === Layout.HORIZONTAL && styles.horizontalCard,
194
+ layout === Layout.VERTICAL && styles.verticalCard,
195
+ type === ItemState.DRAGGING && styles.dragging,
196
+ type === ItemState.DISABLED && styles.disabled,
179
197
  !includePadding && styles.unpaddedCard,
180
198
  ) +
181
199
  " " +
@@ -190,7 +208,10 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
190
208
  position: "static",
191
209
  };
192
210
 
193
- if (this.props.type === DRAGGING || this.props.type === ANIMATING) {
211
+ if (
212
+ this.props.state === ItemState.DRAGGING ||
213
+ this.props.state === ItemState.ANIMATING
214
+ ) {
194
215
  _.extend(style, {position: "absolute"}, this.getCurrentPosition());
195
216
  }
196
217
 
@@ -226,11 +247,11 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
226
247
  }
227
248
 
228
249
  componentDidUpdate(prevProps: DraggableProps) {
229
- if (this.props.type === prevProps.type) {
250
+ if (this.props.state === prevProps.state) {
230
251
  return;
231
252
  }
232
253
 
233
- if (this.props.type === ANIMATING) {
254
+ if (this.props.state === ItemState.ANIMATING) {
234
255
  // Start animating
235
256
  const current = this.getCurrentPosition();
236
257
  const duration =
@@ -254,7 +275,7 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
254
275
  // Animating -> Static
255
276
  complete: this.props.onAnimationEnd,
256
277
  });
257
- } else if (this.props.type === STATIC) {
278
+ } else if (this.props.state === ItemState.STATIC) {
258
279
  // Ensure that any animations are done
259
280
  // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call. | TS2339 - Property 'finish' does not exist on type 'JQueryStatic'.
260
281
  $(ReactDOM.findDOMNode(this)).finish();
@@ -274,7 +295,7 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
274
295
  };
275
296
 
276
297
  onMouseDown = (event) => {
277
- if (this.props.type !== STATIC) {
298
+ if (this.props.state !== ItemState.STATIC) {
278
299
  return;
279
300
  }
280
301
 
@@ -329,7 +350,7 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
329
350
 
330
351
  onMouseMove: (arg1: any) => void = (event) => {
331
352
  const notDragging =
332
- this.props.type !== DRAGGING || !this.state.dragging;
353
+ this.props.state !== ItemState.DRAGGING || !this.state.dragging;
333
354
 
334
355
  if (notDragging) {
335
356
  return;
@@ -355,7 +376,7 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
355
376
 
356
377
  onMouseUp: (arg1: any) => void = (event) => {
357
378
  const notDragging =
358
- this.props.type !== DRAGGING || !this.state.dragging;
379
+ this.props.state !== ItemState.DRAGGING || !this.state.dragging;
359
380
 
360
381
  if (notDragging) {
361
382
  return;
@@ -372,7 +393,7 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
372
393
  }
373
394
  };
374
395
  }
375
- type SortableLayoutKind = "horizontal" | "vertical";
396
+
376
397
  export type SortableOption = string;
377
398
 
378
399
  type SortableProps = {
@@ -383,7 +404,7 @@ type SortableProps = {
383
404
  }
384
405
  | Record<any, any>;
385
406
  disabled: boolean;
386
- layout: SortableLayoutKind;
407
+ layout: Layout;
387
408
  margin: number;
388
409
  onChange: (arg1: any) => void;
389
410
  onMeasure: (arg1: {
@@ -392,8 +413,8 @@ type SortableProps = {
392
413
  }) => void;
393
414
  padding: boolean;
394
415
  linterContext: LinterContextProps;
395
- waitForKatexLoad: boolean;
396
416
  options: ReadonlyArray<SortableOption>;
417
+ waitForTexRendererToLoad: boolean;
397
418
  };
398
419
 
399
420
  type DefaultProps = {
@@ -405,26 +426,25 @@ type DefaultProps = {
405
426
  onMeasure: SortableProps["onMeasure"];
406
427
  padding: SortableProps["padding"];
407
428
  linterContext: SortableProps["linterContext"];
408
- waitForKatexLoad: SortableProps["waitForKatexLoad"];
429
+ waitForTexRendererToLoad: SortableProps["waitForTexRendererToLoad"];
409
430
  };
410
431
 
411
- type ItemState = "disabled" | "static" | "dragging" | "animating";
412
432
  type SortableItem = {
413
433
  option: SortableOption;
414
434
  key: number;
415
- type: ItemState;
416
- endPosition: any;
435
+ state: ItemState;
436
+ endPosition: Position | Record<string, never>;
417
437
  width: number;
418
438
  height: number;
419
439
  };
420
440
 
421
441
  type SortableState = {
422
442
  items: ReadonlyArray<SortableItem>;
423
- katex: null | any;
443
+ texRendererLoaded: boolean;
424
444
  };
425
445
  class Sortable extends React.Component<SortableProps, SortableState> {
426
446
  static defaultProps: DefaultProps = {
427
- layout: HORIZONTAL,
447
+ layout: Layout.HORIZONTAL,
428
448
  padding: true,
429
449
  disabled: false,
430
450
  constraints: {},
@@ -432,7 +452,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
432
452
  margin: 5,
433
453
  onChange: function () {},
434
454
  linterContext: PerseusLinter.linterContextDefault,
435
- waitForKatexLoad: true,
455
+ waitForTexRendererToLoad: true,
436
456
  };
437
457
 
438
458
  constructor(props: SortableProps) {
@@ -440,7 +460,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
440
460
  // Don't call this.setState() here!
441
461
  this.state = {
442
462
  items: Sortable.itemsFromProps(this.props),
443
- katex: null,
463
+ texRendererLoaded: false,
444
464
  };
445
465
  }
446
466
 
@@ -481,24 +501,18 @@ class Sortable extends React.Component<SortableProps, SortableState> {
481
501
  }
482
502
  }
483
503
 
484
- componentDidMount() {
485
- getDependencies()
486
- .getKaTeX()
487
- .then((katex) => {
488
- this.setState({katex});
489
- });
490
- }
491
-
492
504
  static itemsFromProps(props: {
493
505
  disabled: boolean;
494
506
  options: ReadonlyArray<SortableOption>;
495
507
  }): ReadonlyArray<SortableItem> {
496
- const type: ItemState = props.disabled ? DISABLED : STATIC;
508
+ const state: ItemState = props.disabled
509
+ ? ItemState.DISABLED
510
+ : ItemState.STATIC;
497
511
  return props.options.map((option: SortableOption, i) => {
498
512
  return {
499
513
  option: option,
500
514
  key: i,
501
- type: type,
515
+ state,
502
516
  endPosition: {},
503
517
  width: 0,
504
518
  height: 0,
@@ -525,8 +539,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
525
539
  // explictly set on Draggables - this prevents them from changing size
526
540
  // or shape while being dragged.
527
541
 
528
- // @ts-expect-error [FEI-5003] - TS2740 - Type 'readonly SortableItem[]' is missing the following properties from type 'SortableItem': option, key, type, endPosition, and 2 more.
529
- let items: SortableItem = _.clone(this.state.items);
542
+ let items: ReadonlyArray<SortableItem> = [...this.state.items];
530
543
 
531
544
  // Fetches a jQuery list of elements for each item
532
545
  const $items = _.map(
@@ -548,7 +561,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
548
561
  if (constraints?.width) {
549
562
  // Items must be at least as wide as the specified constraint
550
563
  syncWidth = _.max(widths.concat(constraints.width));
551
- } else if (layout === VERTICAL) {
564
+ } else if (layout === Layout.VERTICAL) {
552
565
  // Sync widths to get a clean column
553
566
  syncWidth = _.max(widths);
554
567
  }
@@ -557,20 +570,18 @@ class Sortable extends React.Component<SortableProps, SortableState> {
557
570
  if (constraints?.height) {
558
571
  // Items must be at least as high as the specified constraint
559
572
  syncHeight = _.max(heights.concat(constraints.height));
560
- } else if (layout === HORIZONTAL) {
573
+ } else if (layout === Layout.HORIZONTAL) {
561
574
  // Sync widths to get a clean row
562
575
  syncHeight = _.max(heights);
563
576
  }
564
577
 
565
- // @ts-expect-error [FEI-5003] - TS2740 - Type 'any[]' is missing the following properties from type 'SortableItem': option, key, type, endPosition, and 2 more.
566
578
  items = _.map(items, function (item, i) {
567
579
  item.width = syncWidth || widths[i];
568
580
  item.height = syncHeight || heights[i];
569
581
  return item;
570
582
  });
571
583
 
572
- // @ts-expect-error [FEI-5003] - TS2740 - Type 'SortableItem' is missing the following properties from type 'readonly SortableItem[]': length, concat, join, slice, and 18 more.
573
- this.setState({items: items}, () => {
584
+ this.setState({items}, () => {
574
585
  this.props.onMeasure &&
575
586
  this.props.onMeasure({widths: widths, heights: heights});
576
587
  });
@@ -584,15 +595,38 @@ class Sortable extends React.Component<SortableProps, SortableState> {
584
595
  }, 20);
585
596
 
586
597
  render(): React.ReactNode {
587
- // We don't render the sortable until KaTeX has fully loaded, in case
588
- // the draggables are rendering KaTeX content. This is un-optimal as
589
- // we end up loading KaTeX even when we may not need it, however it
590
- // helps to ensure that the dimensions of the draggables (and thus the
591
- // sortable) will be correct when they render, if their contents are
592
- // KaTeX-derived.
593
- if (this.props.waitForKatexLoad && !this.state.katex) {
594
- return <CircularSpinner />;
598
+ // To minimize layout shift, we display a spinner until our math
599
+ // renderer is ready to render the math inside the sortable. To
600
+ // do this, we:
601
+ // - render a dummy TeX component to force the math renderer to load
602
+ // - display a spinner until the TeX component calls its onRender
603
+ // callback, signifying that the math is rendered (from which we can
604
+ // infer that the math renderer has loaded)
605
+ //
606
+ // If we didn't do this, the user might see a sortable with empty
607
+ // cells on first render, and then the math would pop in a few moments
608
+ // later once the rendering library loaded.
609
+ if (
610
+ this.props.waitForTexRendererToLoad &&
611
+ !this.state.texRendererLoaded
612
+ ) {
613
+ const {TeX} = getDependencies();
614
+ return (
615
+ <>
616
+ <CircularSpinner />
617
+ <div style={{display: "none"}}>
618
+ <TeX
619
+ onRender={() =>
620
+ this.setState({texRendererLoaded: true})
621
+ }
622
+ >
623
+ 1
624
+ </TeX>
625
+ </div>
626
+ </>
627
+ );
595
628
  }
629
+
596
630
  const cards: Array<
597
631
  | React.ReactElement<React.ComponentProps<typeof Placeholder>>
598
632
  | React.ReactElement<React.ComponentProps<typeof Draggable>>
@@ -603,23 +637,26 @@ class Sortable extends React.Component<SortableProps, SortableState> {
603
637
  // in CSS. See sortable.less for details.
604
638
  const className = css(styles.sortable) + " perseus-sortable";
605
639
 
606
- const syncWidth = this.props.constraints?.width || layout === VERTICAL;
640
+ const syncWidth =
641
+ this.props.constraints?.width || layout === Layout.VERTICAL;
607
642
  const syncHeight =
608
- this.props.constraints?.height || layout === HORIZONTAL;
643
+ this.props.constraints?.height || layout === Layout.HORIZONTAL;
609
644
 
610
645
  _.each(
611
646
  this.state.items,
612
647
  function (item, i, items) {
613
648
  const isLast = i === items.length - 1;
614
- const isStatic = item.type === STATIC || item.type === DISABLED;
649
+ const isStatic =
650
+ item.state === ItemState.STATIC ||
651
+ item.state === ItemState.DISABLED;
615
652
  let margin;
616
653
 
617
654
  // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
618
- if (this.props.layout === HORIZONTAL) {
655
+ if (this.props.layout === Layout.HORIZONTAL) {
619
656
  // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
620
657
  margin = "0 " + this.props.margin + "px 0 0"; // right
621
658
  // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
622
- } else if (this.props.layout === VERTICAL) {
659
+ } else if (this.props.layout === Layout.VERTICAL) {
623
660
  // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
624
661
  margin = "0 0 " + this.props.margin + "px 0"; // bottom
625
662
  }
@@ -628,7 +665,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
628
665
  <Draggable
629
666
  content={item.option}
630
667
  key={item.key}
631
- type={item.type}
668
+ state={item.state}
632
669
  // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call.
633
670
  ref={item.key}
634
671
  width={syncWidth ? item.width : undefined}
@@ -673,7 +710,10 @@ class Sortable extends React.Component<SortableProps, SortableState> {
673
710
  />,
674
711
  );
675
712
 
676
- if (item.type === DRAGGING || item.type === ANIMATING) {
713
+ if (
714
+ item.state === ItemState.DRAGGING ||
715
+ item.state === ItemState.ANIMATING
716
+ ) {
677
717
  cards.push(
678
718
  <Placeholder
679
719
  key={"placeholder_" + item.key}
@@ -696,7 +736,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
696
736
  // Static -> Dragging
697
737
  const items = _.map(this.state.items, function (item) {
698
738
  if (item.key === key) {
699
- item.type = DRAGGING;
739
+ item.state = ItemState.DRAGGING;
700
740
  }
701
741
  return item;
702
742
  });
@@ -757,7 +797,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
757
797
  // @ts-expect-error [FEI-5003] - TS2551 - Property 'splice' does not exist on type 'readonly SortableItem[]'. Did you mean 'slice'?
758
798
  items.splice(currentIndex, 1);
759
799
 
760
- if (this.props.layout === HORIZONTAL) {
800
+ if (this.props.layout === Layout.HORIZONTAL) {
761
801
  // @ts-expect-error [FEI-5003] - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'. | TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
762
802
  const midWidth = $draggable.offset().left - $sortable.offset().left;
763
803
  let sumWidth = 0;
@@ -801,7 +841,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
801
841
  this.state.items,
802
842
  function (item) {
803
843
  if (item.key === key) {
804
- item.type = ANIMATING;
844
+ item.state = ItemState.ANIMATING;
805
845
  const $placeholder = $(
806
846
  // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call.
807
847
  ReactDOM.findDOMNode(
@@ -838,7 +878,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
838
878
  // Animating -> Static
839
879
  const items = _.map(this.state.items, function (item) {
840
880
  if (item.key === key) {
841
- item.type = STATIC;
881
+ item.state = ItemState.STATIC;
842
882
  }
843
883
  return item;
844
884
  });
@@ -6,6 +6,7 @@ import * as React from "react";
6
6
  import _ from "underscore";
7
7
 
8
8
  import {getDependencies} from "../dependencies";
9
+ import {Coord} from "../interactive2/types";
9
10
  import {Errors, Log} from "../logging/log";
10
11
  import {PerseusError} from "../perseus-error";
11
12
  import Util from "../util";
@@ -15,6 +16,7 @@ import FixedToResponsive from "./fixed-to-responsive";
15
16
  import Graphie from "./graphie";
16
17
  import ImageLoader from "./image-loader";
17
18
 
19
+ import type {Size} from "../perseus-types";
18
20
  import type {Alignment, Dimensions} from "../types";
19
21
  import type {ImageProps} from "./image-loader";
20
22
 
@@ -141,8 +143,8 @@ type Props = {
141
143
  alt: string;
142
144
  constrainHeight?: boolean;
143
145
  extraGraphie?: {
144
- box: ReadonlyArray<any>;
145
- range: ReadonlyArray<any>;
146
+ box: Size;
147
+ range: [Coord, Coord];
146
148
  labels: ReadonlyArray<any>;
147
149
  };
148
150
  height?: number;
@@ -203,7 +205,7 @@ type State = {
203
205
  labelsRendered: LabelsRenderedMap;
204
206
  labelDataIsLocalized: boolean;
205
207
  labels: ReadonlyArray<Label>;
206
- range: ReadonlyArray<any>;
208
+ range: [Coord, Coord];
207
209
  };
208
210
 
209
211
  class SvgImage extends React.Component<Props, State> {
@@ -240,10 +242,7 @@ class SvgImage extends React.Component<Props, State> {
240
242
  labelDataIsLocalized: false,
241
243
  labels: [],
242
244
  labelsRendered: {},
243
- range: [
244
- [0, 0],
245
- [0, 0],
246
- ],
245
+ range: [[0, 0], [0, 0] as Coord],
247
246
  };
248
247
  }
249
248
 
@@ -405,7 +404,7 @@ class SvgImage extends React.Component<Props, State> {
405
404
  ) => void = (
406
405
  data: {
407
406
  labels: ReadonlyArray<any>;
408
- range: ReadonlyArray<any>;
407
+ range: [Coord, Coord];
409
408
  },
410
409
  localized: boolean,
411
410
  ) => {
@@ -719,14 +718,14 @@ class SvgImage extends React.Component<Props, State> {
719
718
  );
720
719
  }
721
720
 
722
- const scale = [40 * this.props.scale, 40 * this.props.scale];
723
-
721
+ // TODO: the "40" scale factor was introduced in D14974 but is not
722
+ // documented where it came from.
724
723
  graphie = (
725
724
  <Graphie
726
725
  // eslint-disable-next-line react/no-string-refs
727
726
  ref="graphie"
728
727
  box={box}
729
- scale={scale}
728
+ scale={[40 * this.props.scale, 40 * this.props.scale]}
730
729
  range={this.state.range}
731
730
  options={_.pick(this.state, "labels")}
732
731
  responsive={responsive}
@@ -253,7 +253,7 @@ const buttonSets: ButtonSets = {
253
253
 
254
254
  export type ButtonSetsType = ReadonlyArray<keyof typeof buttonSets>;
255
255
 
256
- type TexButtonProps = {
256
+ type Props = {
257
257
  sets: ButtonSetsType;
258
258
  onInsert: (arg1: Inserter) => void;
259
259
  className?: string;
@@ -264,7 +264,7 @@ const buttonSetsPropType = PropTypes.arrayOf(
264
264
  PropTypes.oneOf(_(buttonSets).keys()),
265
265
  );
266
266
 
267
- class TexButtons extends React.Component<TexButtonProps> {
267
+ class TexButtons extends React.Component<Props> {
268
268
  static buttonSets: typeof buttonSets = buttonSets;
269
269
  static buttonSetsType: any = buttonSetsPropType;
270
270