@khanacademy/perseus-editor 2.15.7 → 2.16.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 (36) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/components/blur-input.d.ts +2 -0
  3. package/dist/components/widget-editor.d.ts +1 -1
  4. package/dist/editor-page.d.ts +4 -1
  5. package/dist/editor.d.ts +3 -1
  6. package/dist/es/index.js +170 -260
  7. package/dist/es/index.js.map +1 -1
  8. package/dist/hint-editor.d.ts +2 -0
  9. package/dist/iframe-content-renderer.d.ts +1 -0
  10. package/dist/index.js +137 -256
  11. package/dist/index.js.map +1 -1
  12. package/dist/item-editor.d.ts +6 -0
  13. package/dist/widgets/example-widget-editor.d.ts +1 -0
  14. package/dist/widgets/example-widget.d.ts +7 -0
  15. package/dist/widgets/graded-group-editor.d.ts +3 -0
  16. package/dist/widgets/group-editor.d.ts +2 -0
  17. package/dist/widgets/input-number-editor.d.ts +2 -0
  18. package/dist/widgets/simple-markdown-tester-editor.d.ts +7 -0
  19. package/dist/widgets/table-editor.d.ts +2 -0
  20. package/package.json +13 -13
  21. package/src/components/__tests__/blur-input.test.tsx +67 -0
  22. package/src/components/blur-input.tsx +7 -0
  23. package/src/editor-page.tsx +11 -25
  24. package/src/editor.tsx +37 -65
  25. package/src/hint-editor.tsx +15 -27
  26. package/src/iframe-content-renderer.tsx +19 -20
  27. package/src/item-editor.tsx +11 -18
  28. package/src/util/register-all-widgets-and-editors-for-testing.test.ts +3 -1
  29. package/src/widgets/example-widget-editor.tsx +4 -5
  30. package/src/widgets/example-widget.tsx +8 -10
  31. package/src/widgets/graded-group-editor.tsx +9 -19
  32. package/src/widgets/group-editor.tsx +5 -8
  33. package/src/widgets/input-number-editor.tsx +4 -5
  34. package/src/widgets/simple-markdown-tester-editor.tsx +4 -5
  35. package/src/widgets/table-editor.tsx +4 -5
  36. package/tsconfig-build.tsbuildinfo +1 -1
package/dist/es/index.js CHANGED
@@ -9,12 +9,11 @@ import Clickable from '@khanacademy/wonder-blocks-clickable';
9
9
  import Color from '@khanacademy/wonder-blocks-color';
10
10
  import { StyleSheet, css } from 'aphrodite';
11
11
  import $ from 'jquery';
12
- import * as ReactDOM from 'react-dom';
13
- import ReactDOM__default from 'react-dom';
14
12
  import { View } from '@khanacademy/wonder-blocks-core';
15
13
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
16
14
  import caretRight from '@phosphor-icons/core/bold/caret-right-bold.svg';
17
15
  import classNames from 'classnames';
16
+ import ReactDOM from 'react-dom';
18
17
  import { point, number, line, ray, vector } from '@khanacademy/kmath';
19
18
  import * as KAS from '@khanacademy/kas';
20
19
  import { isTruthy } from '@khanacademy/wonder-stuff-core';
@@ -26,7 +25,7 @@ import * as i18n$1 from '@khanacademy/wonder-blocks-i18n';
26
25
 
27
26
  // This file is processed by a Rollup plugin (replace) to inject the production
28
27
  const libName = "@khanacademy/perseus-editor";
29
- const libVersion = "2.15.7";
28
+ const libVersion = "2.16.0";
30
29
  addLibraryVersionToPerseusDebug(libName, libVersion);
31
30
 
32
31
  function _extends() {
@@ -20863,7 +20862,6 @@ const IMAGE_REGEX$1 = /!\[[^\]]*\]\(([^\s)]+)[^)]*\)/g;
20863
20862
  * ignores captures. If you don't need captures, use String::match
20864
20863
  */
20865
20864
  const allMatches = function allMatches(regex, str) {
20866
- // @ts-expect-error - TS2702 - 'RegExp' only refers to a type, but is being used as a namespace here.
20867
20865
  const result = [];
20868
20866
  // eslint-disable-next-line no-constant-condition
20869
20867
  while (true) {
@@ -20881,7 +20879,7 @@ const allMatches = function allMatches(regex, str) {
20881
20879
  * markdown.
20882
20880
  */
20883
20881
  const imageUrlsFromContent = function imageUrlsFromContent(content) {
20884
- return _.map(allMatches(IMAGE_REGEX$1, content), capture => capture[1]);
20882
+ return allMatches(IMAGE_REGEX$1, content).map(capture => capture[1]);
20885
20883
  };
20886
20884
  // eslint-disable-next-line react/no-unsafe
20887
20885
  class Editor extends React.Component {
@@ -20890,6 +20888,8 @@ class Editor extends React.Component {
20890
20888
  this.lastUserValue = void 0;
20891
20889
  this.deferredChange = void 0;
20892
20890
  this.widgetIds = void 0;
20891
+ this.underlay = /*#__PURE__*/React.createRef();
20892
+ this.textarea = /*#__PURE__*/React.createRef();
20893
20893
  this.state = {
20894
20894
  textAreaValue: this.props.content
20895
20895
  };
@@ -20923,12 +20923,10 @@ class Editor extends React.Component {
20923
20923
  }, cb, silent);
20924
20924
  };
20925
20925
  this._handleWidgetEditorRemove = id => {
20926
- // eslint-disable-next-line react/no-string-refs
20927
- const textarea = this.refs.textarea;
20926
+ const textarea = this.textarea.current;
20928
20927
  const re = new RegExp(widgetRegExp.replace("{id}", id), "gm");
20929
- // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'ReactInstance'.
20930
20928
  this.props.onChange({
20931
- content: textarea.value.replace(re, "")
20929
+ content: textarea == null ? void 0 : textarea.value.replace(re, "")
20932
20930
  });
20933
20931
  };
20934
20932
  /**
@@ -21074,11 +21072,11 @@ class Editor extends React.Component {
21074
21072
  // Tab-completion of widgets. For example, to insert an image:
21075
21073
  // type `[[im`, then tab.
21076
21074
  if (e.key === "Tab") {
21077
- // eslint-disable-next-line react/no-string-refs
21078
- const textarea = ReactDOM.findDOMNode(this.refs.textarea);
21079
-
21080
- // findDOMNode can also return Text, but we know it's an element.
21081
- // @ts-expect-error - TS2345 - Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLTextAreaElement'.
21075
+ // We're in an event handler attached to the textarea, so the ref
21076
+ // can't be empty/undefined! (which is why its safe to use the
21077
+ // non-null-assertion here. aka the `!` suffix)
21078
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
21079
+ const textarea = this.textarea.current;
21082
21080
  const word = Util.textarea.getWordBeforeCursor(textarea);
21083
21081
  const matches = word.string.toLowerCase().match(shortcutRegexp);
21084
21082
  if (matches != null) {
@@ -21212,9 +21210,6 @@ class Editor extends React.Component {
21212
21210
  };
21213
21211
  this._addWidgetToContent = (oldContent, cursorRange, widgetType) => {
21214
21212
  var _Widgets$getEditor;
21215
- // eslint-disable-next-line react/no-string-refs
21216
- const textarea = ReactDOM.findDOMNode(this.refs.textarea);
21217
-
21218
21213
  // Note: we have to use _.map here instead of Array::map
21219
21214
  // because the results of a .match might be null if no
21220
21215
  // widgets were found.
@@ -21261,23 +21256,22 @@ class Editor extends React.Component {
21261
21256
  this.props.onChange({
21262
21257
  content: newContent,
21263
21258
  widgets: newWidgets
21264
- }, function () {
21265
- Util.textarea.moveCursor(
21266
- // findDOMNode can return Text but we know this is Element
21267
- // @ts-expect-error - TS2345 - Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLTextAreaElement'.
21268
- textarea,
21259
+ }, () => {
21260
+ if (!this.textarea.current) {
21261
+ return;
21262
+ }
21263
+ Util.textarea.moveCursor(this.textarea.current,
21269
21264
  // We want to put the cursor after the widget
21270
21265
  // and after any added newlines
21271
21266
  newContent.length - postlude.length);
21272
21267
  });
21273
21268
  };
21274
21269
  this._addWidget = widgetType => {
21275
- // eslint-disable-next-line react/no-string-refs
21276
- const textarea = this.refs.textarea;
21277
- this._addWidgetToContent(this.props.content,
21278
- // @ts-expect-error - TS2339 - Property 'selectionStart' does not exist on type 'ReactInstance'. | TS2339 - Property 'selectionEnd' does not exist on type 'ReactInstance'.
21279
- [textarea.selectionStart, textarea.selectionEnd], widgetType);
21280
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
21270
+ const textarea = this.textarea.current;
21271
+ if (!textarea) {
21272
+ return;
21273
+ }
21274
+ this._addWidgetToContent(this.props.content, [textarea.selectionStart, textarea.selectionEnd], widgetType);
21281
21275
  textarea.focus();
21282
21276
  };
21283
21277
  this.addTemplate = e => {
@@ -21331,31 +21325,16 @@ class Editor extends React.Component {
21331
21325
  return warnings;
21332
21326
  };
21333
21327
  this.focus = () => {
21334
- // eslint-disable-next-line react/no-string-refs
21335
- const textarea = ReactDOM.findDOMNode(this.refs.textarea);
21328
+ const textarea = this.textarea.current;
21336
21329
  if (textarea) {
21337
- /**
21338
- * TODO(somewhatabstract, JIRA-XXXX):
21339
- * textarea should be refined with an instanceof check to
21340
- * HTMLTextAreaElement so that these props are available.
21341
- */
21342
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element | Text'.
21343
21330
  textarea.focus();
21344
21331
  }
21345
21332
  };
21346
21333
  this.focusAndMoveToEnd = () => {
21347
21334
  this.focus();
21348
- // eslint-disable-next-line react/no-string-refs
21349
- const textarea = ReactDOM.findDOMNode(this.refs.textarea);
21335
+ const textarea = this.textarea.current;
21350
21336
  if (textarea) {
21351
- /**
21352
- * TODO(somewhatabstract, JIRA-XXXX):
21353
- * textarea should be refined with an instanceof check to
21354
- * HTMLTextAreaElement so that these props are available.
21355
- */
21356
- // @ts-expect-error - TS2339 - Property 'selectionStart' does not exist on type 'Element | Text'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
21357
21337
  textarea.selectionStart = textarea.value.length;
21358
- // @ts-expect-error - TS2339 - Property 'selectionEnd' does not exist on type 'Element | Text'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
21359
21338
  textarea.selectionEnd = textarea.value.length;
21360
21339
  }
21361
21340
  };
@@ -21401,11 +21380,15 @@ class Editor extends React.Component {
21401
21380
  // this.props.onChange during that, since it calls our parent's
21402
21381
  // setState
21403
21382
  this._sizeImages(this.props);
21404
- // eslint-disable-next-line react/no-string-refs
21405
- // @ts-expect-error - TS2769 - No overload matches this call.
21406
- $(ReactDOM.findDOMNode(this.refs.textarea))
21383
+ // NOTE(jeremy): We use the non-null assertion here (!) because refs
21384
+ // are guaranteed to be up-to-date before componentDidMount or
21385
+ // componentDidUpdate fires.
21386
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
21387
+ $(this.textarea.current)
21407
21388
  // @ts-expect-error - TS2339 - Property 'on' does not exist on type 'JQueryStatic'.
21408
- .on("copy cut", this._maybeCopyWidgets).on("paste", this._maybePasteWidgets);
21389
+ .on("copy cut", this._maybeCopyWidgets)
21390
+ // @ts-expect-error - TS2769 - No overload matches this call.
21391
+ .on("paste", this._maybePasteWidgets);
21409
21392
  }
21410
21393
 
21411
21394
  // TODO(arun): This is a deprecated method, use the appropriate replacement
@@ -21418,9 +21401,7 @@ class Editor extends React.Component {
21418
21401
  }
21419
21402
  }
21420
21403
  componentDidUpdate(prevProps) {
21421
- // TODO(alpert): Maybe fix React so this isn't necessary
21422
- // eslint-disable-next-line react/no-string-refs
21423
- const textarea = ReactDOM.findDOMNode(this.refs.textarea);
21404
+ const textarea = this.textarea.current;
21424
21405
 
21425
21406
  // Slightly unorthodox method to ensure that programmatic text changes
21426
21407
  // are in the browser's undo stack.
@@ -21429,24 +21410,14 @@ class Editor extends React.Component {
21429
21410
  // will return false. However at least in Firefox setting `value` on a
21430
21411
  // textbox clears the undo stack so we don't get unexpected undo
21431
21412
  // behavior.
21432
- if (this.lastUserValue !== null && textarea) {
21433
- /**
21434
- * TODO(somewhatabstract, JIRA-XXXX):
21435
- * textarea should be refined with an instanceof check to
21436
- * HTMLTextAreaElement so that these props are available.
21437
- */
21438
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element | Text'.
21413
+ if (this.lastUserValue != null && textarea) {
21439
21414
  textarea.focus();
21440
- // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element | Text'.
21441
21415
  textarea.value = this.lastUserValue;
21442
- // @ts-expect-error - TS2339 - Property 'selectionStart' does not exist on type 'Element | Text'.
21443
21416
  textarea.selectionStart = 0;
21444
- // @ts-expect-error - TS2339 - Property 'select' does not exist on type 'Element | Text'.
21445
- textarea.select(0, prevProps.content.length);
21417
+ textarea.setSelectionRange(0, prevProps.content.length);
21446
21418
  if (document.execCommand("insertText", false, this.props.content) === false) {
21447
21419
  // This command is not implemented. Fall back to setting `value`
21448
21420
  // directly.
21449
- // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element | Text'.
21450
21421
  textarea.value = this.props.content;
21451
21422
  }
21452
21423
  this.lastUserValue = null;
@@ -21551,10 +21522,7 @@ class Editor extends React.Component {
21551
21522
  onChange: this._addWidget
21552
21523
  });
21553
21524
  const insertTemplateString = "Insert template\u2026";
21554
- templatesDropDown =
21555
- /*#__PURE__*/
21556
- // @ts-expect-error - TS2322 - Type '(e: SyntheticEvent<HTMLTextAreaElement, Event>) => void' is not assignable to type 'ChangeEventHandler<HTMLSelectElement>'.
21557
- React.createElement("select", {
21525
+ templatesDropDown = /*#__PURE__*/React.createElement("select", {
21558
21526
  onChange: this.addTemplate
21559
21527
  }, /*#__PURE__*/React.createElement("option", {
21560
21528
  value: ""
@@ -21593,14 +21561,11 @@ class Editor extends React.Component {
21593
21561
  key: "end"
21594
21562
  }));
21595
21563
  const completeTextarea = [/*#__PURE__*/React.createElement("div", {
21596
- className: "perseus-textarea-underlay"
21597
- // eslint-disable-next-line react/no-string-refs
21598
- ,
21599
- ref: "underlay",
21564
+ className: "perseus-textarea-underlay",
21565
+ ref: this.underlay,
21600
21566
  key: "underlay"
21601
21567
  }, underlayPieces), /*#__PURE__*/React.createElement("textarea", {
21602
- // eslint-disable-next-line react/no-string-refs
21603
- ref: "textarea",
21568
+ ref: this.textarea,
21604
21569
  key: "textarea",
21605
21570
  onChange: this.handleChange,
21606
21571
  onKeyDown: this._handleKeyDown,
@@ -21702,6 +21667,7 @@ class IframeContentRenderer extends React.Component {
21702
21667
  constructor(...args) {
21703
21668
  super(...args);
21704
21669
  this._frame = void 0;
21670
+ this.container = /*#__PURE__*/React.createRef();
21705
21671
  // @ts-expect-error - TS2564 - Property '_isMounted' has no initializer and is not definitely assigned in the constructor.
21706
21672
  this._isMounted = void 0;
21707
21673
  this._lastData = void 0;
@@ -21722,10 +21688,8 @@ class IframeContentRenderer extends React.Component {
21722
21688
  };
21723
21689
  updateIframeHeight[this.iframeID] = height => {
21724
21690
  this._lastHeight = height;
21725
- if (this._isMounted && this.props.seamless) {
21726
- // eslint-disable-next-line react/no-string-refs
21727
- // @ts-expect-error - TS2339 - Property 'style' does not exist on type 'ReactInstance'.
21728
- this.refs.container.style.height = height + "px";
21691
+ if (this._isMounted && this.props.seamless && this.container.current) {
21692
+ this.container.current.style.height = height + "px";
21729
21693
  }
21730
21694
  };
21731
21695
  }
@@ -21733,14 +21697,12 @@ class IframeContentRenderer extends React.Component {
21733
21697
  return nextProps.datasetValue !== this.props.datasetValue || nextProps.seamless !== this.props.seamless;
21734
21698
  }
21735
21699
  componentDidUpdate(prevProps) {
21736
- if (!this.props.seamless) {
21737
- // eslint-disable-next-line react/no-string-refs
21738
- // @ts-expect-error - TS2339 - Property 'style' does not exist on type 'ReactInstance'.
21739
- this.refs.container.style.height = "100%";
21740
- } else {
21741
- // eslint-disable-next-line react/no-string-refs
21742
- // @ts-expect-error - TS2339 - Property 'style' does not exist on type 'ReactInstance'.
21743
- this.refs.container.style.height = this._lastHeight + "px";
21700
+ if (this.container.current) {
21701
+ if (!this.props.seamless) {
21702
+ this.container.current.style.height = "100%";
21703
+ } else {
21704
+ this.container.current.style.height = this._lastHeight + "px";
21705
+ }
21744
21706
  }
21745
21707
  if (prevProps.datasetValue !== this.props.datasetValue) {
21746
21708
  // Not just a change in seamless
@@ -21753,11 +21715,11 @@ class IframeContentRenderer extends React.Component {
21753
21715
  this._isMounted = false;
21754
21716
  }
21755
21717
  _prepareFrame() {
21718
+ var _this$container$curre2;
21756
21719
  // Don't initialize the iframe until the page has loaded
21757
21720
  if (this._frame) {
21758
- // eslint-disable-next-line react/no-string-refs
21759
- // @ts-expect-error - TS2339 - Property 'removeChild' does not exist on type 'ReactInstance'.
21760
- this.refs.container.removeChild(this._frame);
21721
+ var _this$container$curre;
21722
+ (_this$container$curre = this.container.current) == null || _this$container$curre.removeChild(this._frame);
21761
21723
  }
21762
21724
  const frame = document.createElement("iframe");
21763
21725
  frame.style.width = "100%";
@@ -21779,10 +21741,7 @@ class IframeContentRenderer extends React.Component {
21779
21741
  // component inside the iframe
21780
21742
  frame.dataset.lintGutter = "true";
21781
21743
  }
21782
-
21783
- // eslint-disable-next-line react/no-string-refs
21784
- // @ts-expect-error - TS2339 - Property 'appendChild' does not exist on type 'ReactInstance'.
21785
- this.refs.container.appendChild(frame);
21744
+ (_this$container$curre2 = this.container.current) == null || _this$container$curre2.appendChild(frame);
21786
21745
  this._frame = frame;
21787
21746
  }
21788
21747
  sendNewData(data) {
@@ -21799,9 +21758,8 @@ class IframeContentRenderer extends React.Component {
21799
21758
  }
21800
21759
  }
21801
21760
  render() {
21802
- // eslint-disable-next-line react/no-string-refs
21803
21761
  return /*#__PURE__*/React.createElement("div", {
21804
- ref: "container",
21762
+ ref: this.container,
21805
21763
  style: {
21806
21764
  width: "100%",
21807
21765
  height: "100%"
@@ -23524,25 +23482,23 @@ const {
23524
23482
  class HintEditor extends React.Component {
23525
23483
  constructor(...args) {
23526
23484
  super(...args);
23485
+ this.editor = /*#__PURE__*/React.createRef();
23527
23486
  this.handleChange = e => {
23528
23487
  this.props.onChange({
23529
23488
  replace: e.target.checked
23530
23489
  });
23531
23490
  };
23532
23491
  this.focus = () => {
23533
- // eslint-disable-next-line react/no-string-refs
23534
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
23535
- this.refs.editor.focus();
23492
+ var _this$editor$current;
23493
+ (_this$editor$current = this.editor.current) == null || _this$editor$current.focus();
23536
23494
  };
23537
23495
  this.getSaveWarnings = () => {
23538
- // eslint-disable-next-line react/no-string-refs
23539
- // @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
23540
- return this.refs.editor.getSaveWarnings();
23496
+ var _this$editor$current2;
23497
+ return (_this$editor$current2 = this.editor.current) == null ? void 0 : _this$editor$current2.getSaveWarnings();
23541
23498
  };
23542
23499
  this.serialize = options => {
23543
- // eslint-disable-next-line react/no-string-refs
23544
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
23545
- return this.refs.editor.serialize(options);
23500
+ var _this$editor$current3;
23501
+ return (_this$editor$current3 = this.editor.current) == null ? void 0 : _this$editor$current3.serialize(options);
23546
23502
  };
23547
23503
  }
23548
23504
  render() {
@@ -23550,10 +23506,8 @@ class HintEditor extends React.Component {
23550
23506
  className: "perseus-hint-editor " + this.props.className
23551
23507
  }, this.props.showTitle && /*#__PURE__*/React.createElement("div", {
23552
23508
  className: "pod-title"
23553
- }, "Hint"), /*#__PURE__*/React.createElement(Editor
23554
- // eslint-disable-next-line react/no-string-refs
23555
- , {
23556
- ref: "editor"
23509
+ }, "Hint"), /*#__PURE__*/React.createElement(Editor, {
23510
+ ref: this.editor
23557
23511
  // Using the AssessmentItem content ID as the key
23558
23512
  // ensures that when the user navigates to another
23559
23513
  // item in the Sidebar, the question editor is
@@ -23605,12 +23559,12 @@ HintEditor.defaultProps = {
23605
23559
  class CombinedHintEditor extends React.Component {
23606
23560
  constructor(...args) {
23607
23561
  super(...args);
23562
+ this.editor = /*#__PURE__*/React.createRef();
23563
+ this.frame = /*#__PURE__*/React.createRef();
23608
23564
  this.updatePreview = () => {
23565
+ var _this$frame$current;
23609
23566
  const shouldBold = this.props.isLast && !/\*\*/.test(this.props.hint.content);
23610
-
23611
- // eslint-disable-next-line react/no-string-refs
23612
- // @ts-expect-error - TS2339 - Property 'sendNewData' does not exist on type 'ReactInstance'.
23613
- this.refs.frame.sendNewData({
23567
+ (_this$frame$current = this.frame.current) == null || _this$frame$current.sendNewData({
23614
23568
  type: "hint",
23615
23569
  data: {
23616
23570
  hint: this.props.hint,
@@ -23626,19 +23580,16 @@ class CombinedHintEditor extends React.Component {
23626
23580
  });
23627
23581
  };
23628
23582
  this.getSaveWarnings = () => {
23629
- // eslint-disable-next-line react/no-string-refs
23630
- // @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
23631
- return this.refs.editor.getSaveWarnings();
23583
+ var _this$editor$current4;
23584
+ return (_this$editor$current4 = this.editor.current) == null ? void 0 : _this$editor$current4.getSaveWarnings();
23632
23585
  };
23633
23586
  this.serialize = options => {
23634
- // eslint-disable-next-line react/no-string-refs
23635
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
23636
- return this.refs.editor.serialize(options);
23587
+ var _this$editor$current5;
23588
+ return (_this$editor$current5 = this.editor.current) == null ? void 0 : _this$editor$current5.serialize(options);
23637
23589
  };
23638
23590
  this.focus = () => {
23639
- // eslint-disable-next-line react/no-string-refs
23640
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
23641
- this.refs.editor.focus();
23591
+ var _this$editor$current6;
23592
+ (_this$editor$current6 = this.editor.current) == null || _this$editor$current6.focus();
23642
23593
  };
23643
23594
  }
23644
23595
  componentDidMount() {
@@ -23653,10 +23604,8 @@ class CombinedHintEditor extends React.Component {
23653
23604
  className: "perseus-combined-hint-editor " + "perseus-editor-row"
23654
23605
  }, /*#__PURE__*/React.createElement("div", {
23655
23606
  className: "perseus-editor-left-cell"
23656
- }, /*#__PURE__*/React.createElement(HintEditor
23657
- // eslint-disable-next-line react/no-string-refs
23658
- , {
23659
- ref: "editor",
23607
+ }, /*#__PURE__*/React.createElement(HintEditor, {
23608
+ ref: this.editor,
23660
23609
  itemId: this.props.itemId,
23661
23610
  isFirst: this.props.isFirst,
23662
23611
  isLast: this.props.isLast,
@@ -23674,10 +23623,8 @@ class CombinedHintEditor extends React.Component {
23674
23623
  }, /*#__PURE__*/React.createElement(DeviceFramer, {
23675
23624
  deviceType: this.props.deviceType,
23676
23625
  nochrome: true
23677
- }, /*#__PURE__*/React.createElement(IframeContentRenderer
23678
- // eslint-disable-next-line react/no-string-refs
23679
- , {
23680
- ref: "frame",
23626
+ }, /*#__PURE__*/React.createElement(IframeContentRenderer, {
23627
+ ref: this.frame,
23681
23628
  datasetKey: "mobile",
23682
23629
  datasetValue: isMobile,
23683
23630
  seamless: true,
@@ -24000,15 +23947,17 @@ const ITEM_DATA_VERSION = itemDataVersion;
24000
23947
  class ItemEditor extends React.Component {
24001
23948
  constructor(...args) {
24002
23949
  super(...args);
23950
+ this.frame = /*#__PURE__*/React.createRef();
23951
+ this.questionEditor = /*#__PURE__*/React.createRef();
23952
+ this.itemExtrasEditor = /*#__PURE__*/React.createRef();
24003
23953
  // Notify the parent that the question or answer area has been updated.
24004
23954
  this.updateProps = (newProps, cb, silent) => {
24005
23955
  const props = _(this.props).pick("question", "answerArea");
24006
23956
  this.props.onChange(_(props).extend(newProps), cb, silent);
24007
23957
  };
24008
23958
  this.triggerPreviewUpdate = newData => {
24009
- // eslint-disable-next-line react/no-string-refs
24010
- // @ts-expect-error - TS2339 - Property 'sendNewData' does not exist on type 'ReactInstance'.
24011
- this.refs.frame.sendNewData(newData);
23959
+ var _this$frame$current;
23960
+ (_this$frame$current = this.frame.current) == null || _this$frame$current.sendNewData(newData);
24012
23961
  };
24013
23962
  this.handleEditorChange = (newProps, cb, silent) => {
24014
23963
  const question = _.extend({}, this.props.question, newProps);
@@ -24023,18 +23972,14 @@ class ItemEditor extends React.Component {
24023
23972
  }, cb, silent);
24024
23973
  };
24025
23974
  this.getSaveWarnings = () => {
24026
- // eslint-disable-next-line react/no-string-refs
24027
- // @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
24028
- return this.refs.questionEditor.getSaveWarnings();
23975
+ var _this$questionEditor$;
23976
+ return (_this$questionEditor$ = this.questionEditor.current) == null ? void 0 : _this$questionEditor$.getSaveWarnings();
24029
23977
  };
24030
23978
  this.serialize = options => {
23979
+ var _this$questionEditor$2, _this$itemExtrasEdito;
24031
23980
  return {
24032
- // eslint-disable-next-line react/no-string-refs
24033
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
24034
- question: this.refs.questionEditor.serialize(options),
24035
- // eslint-disable-next-line react/no-string-refs
24036
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
24037
- answerArea: this.refs.itemExtrasEditor.serialize(options),
23981
+ question: (_this$questionEditor$2 = this.questionEditor.current) == null ? void 0 : _this$questionEditor$2.serialize(options),
23982
+ answerArea: (_this$itemExtrasEdito = this.itemExtrasEditor.current) == null ? void 0 : _this$itemExtrasEdito.serialize(),
24038
23983
  itemDataVersion: ITEM_DATA_VERSION
24039
23984
  };
24040
23985
  };
@@ -24049,10 +23994,8 @@ class ItemEditor extends React.Component {
24049
23994
  className: "perseus-editor-left-cell"
24050
23995
  }, /*#__PURE__*/React.createElement("div", {
24051
23996
  className: "pod-title"
24052
- }, "Question"), /*#__PURE__*/React.createElement(Editor
24053
- // eslint-disable-next-line react/no-string-refs
24054
- , _extends({
24055
- ref: "questionEditor"
23997
+ }, "Question"), /*#__PURE__*/React.createElement(Editor, _extends({
23998
+ ref: this.questionEditor
24056
23999
  // Using the AssessmentItem content ID as the key
24057
24000
  // ensures that when the user navigates to another
24058
24001
  // item in the Sidebar, the question editor is
@@ -24072,10 +24015,8 @@ class ItemEditor extends React.Component {
24072
24015
  }, /*#__PURE__*/React.createElement(DeviceFramer, {
24073
24016
  deviceType: this.props.deviceType,
24074
24017
  nochrome: true
24075
- }, /*#__PURE__*/React.createElement(IframeContentRenderer
24076
- // eslint-disable-next-line react/no-string-refs
24077
- , {
24078
- ref: "frame",
24018
+ }, /*#__PURE__*/React.createElement(IframeContentRenderer, {
24019
+ ref: this.frame,
24079
24020
  key: this.props.deviceType,
24080
24021
  datasetKey: "mobile",
24081
24022
  datasetValue: isMobile,
@@ -24093,10 +24034,8 @@ class ItemEditor extends React.Component {
24093
24034
  className: "perseus-editor-left-cell"
24094
24035
  }, /*#__PURE__*/React.createElement("div", {
24095
24036
  className: "pod-title"
24096
- }, "Question extras"), /*#__PURE__*/React.createElement(ItemExtrasEditor
24097
- // eslint-disable-next-line react/no-string-refs
24098
- , _extends({
24099
- ref: "itemExtrasEditor",
24037
+ }, "Question extras"), /*#__PURE__*/React.createElement(ItemExtrasEditor, _extends({
24038
+ ref: this.itemExtrasEditor,
24100
24039
  onChange: this.handleItemExtrasChange
24101
24040
  }, this.props.answerArea))), /*#__PURE__*/React.createElement("div", {
24102
24041
  className: "perseus-editor-right-cell"
@@ -24118,9 +24057,9 @@ class EditorPage extends React.Component {
24118
24057
  constructor(props) {
24119
24058
  super(props);
24120
24059
  this._isMounted = void 0;
24121
- // @ts-expect-error - TS2564 - Property 'rendererMountNode' has no initializer and is not definitely assigned in the constructor.
24122
- this.rendererMountNode = void 0;
24123
24060
  this.renderer = void 0;
24061
+ this.itemEditor = /*#__PURE__*/React.createRef();
24062
+ this.hintsEditor = /*#__PURE__*/React.createRef();
24124
24063
  this.toggleJsonMode = () => {
24125
24064
  this.setState({
24126
24065
  json: this.serialize({
@@ -24156,7 +24095,6 @@ class EditorPage extends React.Component {
24156
24095
  // TODO(scottgrant): This is a hack to remove the deprecated call to
24157
24096
  // this.isMounted() but is still considered an anti-pattern.
24158
24097
  this._isMounted = true;
24159
- this.rendererMountNode = document.createElement("div");
24160
24098
  this.updateRenderer();
24161
24099
  }
24162
24100
  componentDidUpdate() {
@@ -24175,6 +24113,7 @@ class EditorPage extends React.Component {
24175
24113
  this._isMounted = false;
24176
24114
  }
24177
24115
  updateRenderer() {
24116
+ var _this$itemEditor$curr, _this$itemEditor$curr2;
24178
24117
  // Some widgets (namely the image widget) like to call onChange before
24179
24118
  // anything has actually been mounted, which causes problems here. We
24180
24119
  // just ensure don't update until we've mounted
@@ -24187,10 +24126,7 @@ class EditorPage extends React.Component {
24187
24126
  customKeypad: touch,
24188
24127
  isMobile: touch
24189
24128
  });
24190
-
24191
- // eslint-disable-next-line react/no-string-refs
24192
- // @ts-expect-error - TS2339 - Property 'triggerPreviewUpdate' does not exist on type 'ReactInstance'.
24193
- this.refs.itemEditor.triggerPreviewUpdate({
24129
+ (_this$itemEditor$curr = this.itemEditor.current) == null || _this$itemEditor$curr.triggerPreviewUpdate({
24194
24130
  type: "question",
24195
24131
  data: _({
24196
24132
  item: this.serialize(),
@@ -24204,9 +24140,7 @@ class EditorPage extends React.Component {
24204
24140
  paths: this.props.contentPaths || []
24205
24141
  },
24206
24142
  reviewMode: true,
24207
- // eslint-disable-next-line react/no-string-refs
24208
- // @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
24209
- legacyPerseusLint: this.refs.itemEditor.getSaveWarnings()
24143
+ legacyPerseusLint: (_this$itemEditor$curr2 = this.itemEditor.current) == null ? void 0 : _this$itemEditor$curr2.getSaveWarnings()
24210
24144
  }).extend(_(this.props).pick("workAreaSelector", "solutionAreaSelector", "hintsAreaSelector", "problemNum"))
24211
24145
  });
24212
24146
  }
@@ -24214,24 +24148,18 @@ class EditorPage extends React.Component {
24214
24148
  return _extends({}, ApiOptions.defaults, this.props.apiOptions);
24215
24149
  }
24216
24150
  getSaveWarnings() {
24217
- // eslint-disable-next-line react/no-string-refs
24218
- // @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
24219
- const issues1 = this.refs.itemEditor.getSaveWarnings();
24220
- // eslint-disable-next-line react/no-string-refs
24221
- // @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
24222
- const issues2 = this.refs.hintsEditor.getSaveWarnings();
24151
+ var _this$itemEditor$curr3, _this$hintsEditor$cur;
24152
+ const issues1 = (_this$itemEditor$curr3 = this.itemEditor.current) == null ? void 0 : _this$itemEditor$curr3.getSaveWarnings();
24153
+ const issues2 = (_this$hintsEditor$cur = this.hintsEditor.current) == null ? void 0 : _this$hintsEditor$cur.getSaveWarnings();
24223
24154
  return issues1.concat(issues2);
24224
24155
  }
24225
24156
  serialize(options) {
24157
+ var _this$itemEditor$curr4, _this$hintsEditor$cur2;
24226
24158
  if (this.props.jsonMode) {
24227
24159
  return this.state.json;
24228
24160
  }
24229
- // eslint-disable-next-line react/no-string-refs
24230
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
24231
- return _.extend(this.refs.itemEditor.serialize(options), {
24232
- // eslint-disable-next-line react/no-string-refs
24233
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
24234
- hints: this.refs.hintsEditor.serialize(options)
24161
+ return _.extend((_this$itemEditor$curr4 = this.itemEditor.current) == null ? void 0 : _this$itemEditor$curr4.serialize(options), {
24162
+ hints: (_this$hintsEditor$cur2 = this.hintsEditor.current) == null ? void 0 : _this$hintsEditor$cur2.serialize(options)
24235
24163
  });
24236
24164
  }
24237
24165
  scorePreview() {
@@ -24277,10 +24205,8 @@ class EditorPage extends React.Component {
24277
24205
  multiLine: true,
24278
24206
  value: this.state.json,
24279
24207
  onChange: this.changeJSON
24280
- })), (!this.props.developerMode || !this.props.jsonMode) && /*#__PURE__*/React.createElement(ItemEditor
24281
- // eslint-disable-next-line react/no-string-refs
24282
- , {
24283
- ref: "itemEditor",
24208
+ })), (!this.props.developerMode || !this.props.jsonMode) && /*#__PURE__*/React.createElement(ItemEditor, {
24209
+ ref: this.itemEditor,
24284
24210
  itemId: this.props.itemId,
24285
24211
  question: this.props.question,
24286
24212
  answerArea: this.props.answerArea,
@@ -24291,10 +24217,8 @@ class EditorPage extends React.Component {
24291
24217
  deviceType: this.props.previewDevice,
24292
24218
  apiOptions: deviceBasedApiOptions,
24293
24219
  previewURL: this.props.previewURL
24294
- }), (!this.props.developerMode || !this.props.jsonMode) && /*#__PURE__*/React.createElement(CombinedHintsEditor
24295
- // eslint-disable-next-line react/no-string-refs
24296
- , {
24297
- ref: "hintsEditor",
24220
+ }), (!this.props.developerMode || !this.props.jsonMode) && /*#__PURE__*/React.createElement(CombinedHintsEditor, {
24221
+ ref: this.hintsEditor,
24298
24222
  itemId: this.props.itemId,
24299
24223
  hints: this.props.hints,
24300
24224
  imageUploader: this.props.imageUploader,
@@ -25207,7 +25131,7 @@ const withStickiness = WrappedComponent => {
25207
25131
  sticky: false
25208
25132
  };
25209
25133
  this.updateStickiness = () => {
25210
- const domNode = ReactDOM__default.findDOMNode(this);
25134
+ const domNode = ReactDOM.findDOMNode(this);
25211
25135
  // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'offsetHeight' does not exist on type 'Element | Text'.
25212
25136
  const height = domNode.offsetHeight;
25213
25137
  const windowHeight = window.innerHeight;
@@ -25940,6 +25864,7 @@ CategorizerEditor.defaultProps = {
25940
25864
  class BlurInput extends React.Component {
25941
25865
  constructor(props) {
25942
25866
  super(props);
25867
+ this.input = /*#__PURE__*/React.createRef();
25943
25868
  this.handleChange = e => {
25944
25869
  this.setState({
25945
25870
  value: e.target.value
@@ -25959,8 +25884,13 @@ class BlurInput extends React.Component {
25959
25884
  value: nextProps.value
25960
25885
  });
25961
25886
  }
25887
+ focus() {
25888
+ var _this$input$current;
25889
+ (_this$input$current = this.input.current) == null || _this$input$current.focus();
25890
+ }
25962
25891
  render() {
25963
25892
  return /*#__PURE__*/React.createElement("input", {
25893
+ ref: this.input,
25964
25894
  className: this.props.className,
25965
25895
  style: this.props.style,
25966
25896
  type: "text",
@@ -26273,7 +26203,7 @@ class DropdownEditor extends React.Component {
26273
26203
  };
26274
26204
  this.focus = i => {
26275
26205
  // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
26276
- ReactDOM__default.findDOMNode(this.refs["editor" + i]).focus(); // eslint-disable-line react/no-string-refs
26206
+ ReactDOM.findDOMNode(this.refs["editor" + i]).focus(); // eslint-disable-line react/no-string-refs
26277
26207
  return true;
26278
26208
  };
26279
26209
  this.serialize = () => {
@@ -26548,6 +26478,7 @@ ExampleGraphieWidgetEditor.defaultProps = {
26548
26478
  class ExampleWidgetEditor extends React.Component {
26549
26479
  constructor(..._args) {
26550
26480
  super(..._args);
26481
+ this.input = /*#__PURE__*/React.createRef();
26551
26482
  this.handleAnswerChange = event => {
26552
26483
  // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
26553
26484
  this.change({
@@ -26558,9 +26489,8 @@ class ExampleWidgetEditor extends React.Component {
26558
26489
  return Changeable.change.apply(this, args);
26559
26490
  };
26560
26491
  this.focus = () => {
26561
- // eslint-disable-next-line react/no-string-refs
26562
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
26563
- this.refs.input.focus();
26492
+ var _this$input$current;
26493
+ (_this$input$current = this.input.current) == null || _this$input$current.focus();
26564
26494
  return true;
26565
26495
  };
26566
26496
  this.serialize = () => {
@@ -26570,10 +26500,8 @@ class ExampleWidgetEditor extends React.Component {
26570
26500
  render() {
26571
26501
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", null, "Correct answer:", /*#__PURE__*/React.createElement("input", {
26572
26502
  value: this.props.correct,
26573
- onChange: this.handleAnswerChange
26574
- // eslint-disable-next-line react/no-string-refs
26575
- ,
26576
- ref: "input"
26503
+ onChange: this.handleAnswerChange,
26504
+ ref: this.input
26577
26505
  })));
26578
26506
  }
26579
26507
  }
@@ -26737,7 +26665,7 @@ const SortableArea = createReactClass({
26737
26665
  this._dragItems = this._dragItems || [];
26738
26666
  const items =
26739
26667
  // @ts-expect-error - TS2531 - Object is possibly 'null'
26740
- ReactDOM__default.findDOMNode(this).querySelectorAll("[draggable=true]");
26668
+ ReactDOM.findDOMNode(this).querySelectorAll("[draggable=true]");
26741
26669
  const oldItems = [];
26742
26670
  const newItems = [];
26743
26671
  for (let i = 0; i < this._dragItems.length; i++) {
@@ -27205,6 +27133,8 @@ const {
27205
27133
  class GradedGroupEditor extends React.Component {
27206
27134
  constructor(..._args) {
27207
27135
  super(..._args);
27136
+ this.editor = /*#__PURE__*/React.createRef();
27137
+ this.hintEditor = /*#__PURE__*/React.createRef();
27208
27138
  this.change = (...args) => {
27209
27139
  return Changeable.change.apply(this, args);
27210
27140
  };
@@ -27215,9 +27145,8 @@ class GradedGroupEditor extends React.Component {
27215
27145
  this.props.onChange({
27216
27146
  hint
27217
27147
  }, () => {
27218
- // eslint-disable-next-line react/no-string-refs
27219
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
27220
- this.refs["hint-editor"].focus();
27148
+ var _this$hintEditor$curr;
27149
+ (_this$hintEditor$curr = this.hintEditor.current) == null || _this$hintEditor$curr.focus();
27221
27150
  });
27222
27151
  };
27223
27152
  this.handleRemoveHint = e => {
@@ -27226,20 +27155,15 @@ class GradedGroupEditor extends React.Component {
27226
27155
  });
27227
27156
  };
27228
27157
  this.getSaveWarnings = () => {
27229
- // eslint-disable-next-line react/no-string-refs
27230
- // @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
27231
- return this.refs.editor.getSaveWarnings();
27158
+ var _this$editor$current;
27159
+ return (_this$editor$current = this.editor.current) == null ? void 0 : _this$editor$current.getSaveWarnings();
27232
27160
  };
27233
27161
  this.serialize = () => {
27162
+ var _this$editor$current2, _this$hintEditor$curr2;
27234
27163
  return _extends({
27235
27164
  title: this.props.title
27236
- }, this.refs.editor.serialize(), {
27237
- hint:
27238
- // eslint-disable-next-line react/no-string-refs
27239
- this.refs["hint-editor"] &&
27240
- // eslint-disable-next-line react/no-string-refs
27241
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
27242
- this.refs["hint-editor"].serialize()
27165
+ }, (_this$editor$current2 = this.editor.current) == null ? void 0 : _this$editor$current2.serialize(), {
27166
+ hint: (_this$hintEditor$curr2 = this.hintEditor.current) == null ? void 0 : _this$hintEditor$curr2.serialize()
27243
27167
  });
27244
27168
  };
27245
27169
  }
@@ -27256,10 +27180,8 @@ class GradedGroupEditor extends React.Component {
27256
27180
  // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
27257
27181
  ,
27258
27182
  onChange: this.change("title")
27259
- }))), /*#__PURE__*/React.createElement(Editor
27260
- // eslint-disable-next-line react/no-string-refs
27261
- , {
27262
- ref: "editor",
27183
+ }))), /*#__PURE__*/React.createElement(Editor, {
27184
+ ref: this.editor,
27263
27185
  content: this.props.content,
27264
27186
  widgets: this.props.widgets,
27265
27187
  apiOptions: this.props.apiOptions,
@@ -27280,10 +27202,8 @@ class GradedGroupEditor extends React.Component {
27280
27202
  className: "perseus-hint-editor"
27281
27203
  }, /*#__PURE__*/React.createElement("div", {
27282
27204
  className: css(styles$b.hintsTitle)
27283
- }, "Hint"), /*#__PURE__*/React.createElement(Editor
27284
- // eslint-disable-next-line react/no-string-refs
27285
- , {
27286
- ref: "hint-editor",
27205
+ }, "Hint"), /*#__PURE__*/React.createElement(Editor, {
27206
+ ref: this.hintEditor,
27287
27207
  content: this.props.hint ? this.props.hint.content : "",
27288
27208
  widgets: this.props.hint ? this.props.hint.widgets : {},
27289
27209
  apiOptions: this.props.apiOptions,
@@ -27509,7 +27429,7 @@ const GraphSettings = createReactClass({
27509
27429
  };
27510
27430
 
27511
27431
  // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
27512
- const url = ReactDOM__default.findDOMNode(this.refs["bg-url"]).value; // eslint-disable-line react/no-string-refs
27432
+ const url = ReactDOM.findDOMNode(this.refs["bg-url"]).value; // eslint-disable-line react/no-string-refs
27513
27433
  if (url) {
27514
27434
  Util.getImageSize(url, (width, height) => {
27515
27435
  if (this._isMounted) {
@@ -27985,6 +27905,7 @@ GrapherEditor.defaultProps = {
27985
27905
  class GroupEditor extends React.Component {
27986
27906
  constructor(..._args) {
27987
27907
  super(..._args);
27908
+ this.editor = /*#__PURE__*/React.createRef();
27988
27909
  this._renderMetadataEditor = () => {
27989
27910
  const GroupMetadataEditor = this.props.apiOptions.GroupMetadataEditor;
27990
27911
  return /*#__PURE__*/React.createElement(GroupMetadataEditor, {
@@ -27998,14 +27919,12 @@ class GroupEditor extends React.Component {
27998
27919
  return Changeable.change.apply(this, args);
27999
27920
  };
28000
27921
  this.getSaveWarnings = () => {
28001
- // eslint-disable-next-line react/no-string-refs
28002
- // @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
28003
- return this.refs.editor.getSaveWarnings();
27922
+ var _this$editor$current;
27923
+ return (_this$editor$current = this.editor.current) == null ? void 0 : _this$editor$current.getSaveWarnings();
28004
27924
  };
28005
27925
  this.serialize = () => {
28006
- // eslint-disable-next-line react/no-string-refs
28007
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
28008
- return _.extend({}, this.refs.editor.serialize(), {
27926
+ var _this$editor$current2;
27927
+ return _.extend({}, (_this$editor$current2 = this.editor.current) == null ? void 0 : _this$editor$current2.serialize(), {
28009
27928
  metadata: this.props.metadata
28010
27929
  });
28011
27930
  };
@@ -28013,10 +27932,8 @@ class GroupEditor extends React.Component {
28013
27932
  render() {
28014
27933
  return /*#__PURE__*/React.createElement("div", {
28015
27934
  className: "perseus-group-editor"
28016
- }, /*#__PURE__*/React.createElement("div", null, this._renderMetadataEditor()), /*#__PURE__*/React.createElement(Editor
28017
- // eslint-disable-next-line react/no-string-refs
28018
- , {
28019
- ref: "editor",
27935
+ }, /*#__PURE__*/React.createElement("div", null, this._renderMetadataEditor()), /*#__PURE__*/React.createElement(Editor, {
27936
+ ref: this.editor,
28020
27937
  content: this.props.content,
28021
27938
  widgets: this.props.widgets,
28022
27939
  apiOptions: this.props.apiOptions,
@@ -28472,6 +28389,7 @@ const answerTypes = {
28472
28389
  class InputNumberEditor extends React.Component {
28473
28390
  constructor(...args) {
28474
28391
  super(...args);
28392
+ this.input = /*#__PURE__*/React.createRef();
28475
28393
  this.handleAnswerChange = str => {
28476
28394
  const value = Util.firstNumericalParse(str) || 0;
28477
28395
  this.props.onChange({
@@ -28479,8 +28397,8 @@ class InputNumberEditor extends React.Component {
28479
28397
  });
28480
28398
  };
28481
28399
  this.focus = () => {
28482
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
28483
- ReactDOM__default.findDOMNode(this.refs.input).focus(); // eslint-disable-line react/no-string-refs
28400
+ var _this$input$current;
28401
+ (_this$input$current = this.input.current) == null || _this$input$current.focus();
28484
28402
  return true;
28485
28403
  };
28486
28404
  this.serialize = () => ({
@@ -28502,10 +28420,8 @@ class InputNumberEditor extends React.Component {
28502
28420
  }, this);
28503
28421
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", null, "Correct answer:", " ", /*#__PURE__*/React.createElement(BlurInput, {
28504
28422
  value: "" + this.props.value,
28505
- onChange: this.handleAnswerChange
28506
- // eslint-disable-next-line react/no-string-refs
28507
- ,
28508
- ref: "input"
28423
+ onChange: this.handleAnswerChange,
28424
+ ref: this.input
28509
28425
  }))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", null, "Unsimplified answers", " ", /*#__PURE__*/React.createElement("select", {
28510
28426
  value: this.props.simplify,
28511
28427
  onChange: e => {
@@ -30812,7 +30728,7 @@ const {
30812
30728
  colors: colors$3
30813
30729
  } = globalStyles;
30814
30730
  const findAndFocusElement = component => {
30815
- const DOMNode = ReactDOM__default.findDOMNode(component);
30731
+ const DOMNode = ReactDOM.findDOMNode(component);
30816
30732
  const button = DOMNode;
30817
30733
  // @ts-expect-error - TS2774 - This condition will always return true since this function is always defined. Did you mean to call it instead?
30818
30734
  if (button.focus) {
@@ -33188,7 +33104,7 @@ class PlotterEditor extends React.Component {
33188
33104
  }
33189
33105
  if (categories) {
33190
33106
  // eslint-disable-next-line react/no-string-refs
33191
- const node = ReactDOM__default.findDOMNode(this.refs.categories);
33107
+ const node = ReactDOM.findDOMNode(this.refs.categories);
33192
33108
  // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
33193
33109
  node.value = categories.join(", ");
33194
33110
  }
@@ -33237,7 +33153,7 @@ class PlotterEditor extends React.Component {
33237
33153
  });
33238
33154
 
33239
33155
  // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
33240
- ReactDOM__default.findDOMNode(this.refs.maxY).value = maxY; // eslint-disable-line react/no-string-refs
33156
+ ReactDOM.findDOMNode(this.refs.maxY).value = maxY; // eslint-disable-line react/no-string-refs
33241
33157
  };
33242
33158
  this.changeMax = e => {
33243
33159
  this.props.onChange({
@@ -33271,7 +33187,7 @@ class PlotterEditor extends React.Component {
33271
33187
  this.changeCategories(categories);
33272
33188
 
33273
33189
  // eslint-disable-next-line react/no-string-refs
33274
- const node = ReactDOM__default.findDOMNode(this.refs.categories);
33190
+ const node = ReactDOM.findDOMNode(this.refs.categories);
33275
33191
 
33276
33192
  // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
33277
33193
  node.value = categories.join(", ");
@@ -33964,13 +33880,13 @@ class TextArea extends React.Component {
33964
33880
  class SimpleMarkdownTesterEditor extends React.Component {
33965
33881
  constructor(..._args) {
33966
33882
  super(..._args);
33883
+ this.input = /*#__PURE__*/React.createRef();
33967
33884
  this.change = (...args) => {
33968
33885
  return Changeable.change.apply(this, args);
33969
33886
  };
33970
33887
  this.focus = () => {
33971
- // eslint-disable-next-line react/no-string-refs
33972
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
33973
- this.refs.input.focus();
33888
+ var _this$input$current;
33889
+ (_this$input$current = this.input.current) == null || _this$input$current.focus();
33974
33890
  return true;
33975
33891
  };
33976
33892
  this.serialize = () => {
@@ -33982,10 +33898,8 @@ class SimpleMarkdownTesterEditor extends React.Component {
33982
33898
  value: this.props.value
33983
33899
  // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
33984
33900
  ,
33985
- onChange: this.change("value")
33986
- // eslint-disable-next-line react/no-string-refs
33987
- ,
33988
- ref: "input"
33901
+ onChange: this.change("value"),
33902
+ ref: this.input
33989
33903
  }))));
33990
33904
  }
33991
33905
  }
@@ -34136,9 +34050,10 @@ const Table = TableWidget.widget;
34136
34050
  class TableEditor extends React.Component {
34137
34051
  constructor(...args) {
34138
34052
  super(...args);
34053
+ this.numberOfColumns = /*#__PURE__*/React.createRef();
34139
34054
  this.focus = () => {
34140
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
34141
- ReactDOM__default.findDOMNode(this.refs.numberOfColumns).focus(); // eslint-disable-line react/no-string-refs
34055
+ var _this$numberOfColumns;
34056
+ (_this$numberOfColumns = this.numberOfColumns.current) == null || _this$numberOfColumns.focus();
34142
34057
  };
34143
34058
  this.onSizeInput = (numRawRows, numRawColumns) => {
34144
34059
  let rows = +numRawRows || 0;
@@ -34194,10 +34109,8 @@ class TableEditor extends React.Component {
34194
34109
  });
34195
34110
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
34196
34111
  className: "perseus-widget-row"
34197
- }, /*#__PURE__*/React.createElement("label", null, "Number of columns:", " ", /*#__PURE__*/React.createElement(NumberInput$1
34198
- // eslint-disable-next-line react/no-string-refs
34199
- , {
34200
- ref: "numberOfColumns",
34112
+ }, /*#__PURE__*/React.createElement("label", null, "Number of columns:", " ", /*#__PURE__*/React.createElement(NumberInput$1, {
34113
+ ref: this.numberOfColumns,
34201
34114
  value: this.props.columns,
34202
34115
  onChange: val => {
34203
34116
  if (val) {
@@ -35298,10 +35211,10 @@ var AllEditors = [CategorizerEditor, CSProgramEditor, DefinitionEditor, Dropdown
35298
35211
  class TextInput extends React.Component {
35299
35212
  constructor(...args) {
35300
35213
  super(...args);
35214
+ this.input = /*#__PURE__*/React.createRef();
35301
35215
  this.focus = () => {
35302
- // eslint-disable-next-line react/no-string-refs
35303
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
35304
- this.refs.input.focus();
35216
+ var _this$input$current;
35217
+ (_this$input$current = this.input.current) == null || _this$input$current.focus();
35305
35218
  return true;
35306
35219
  };
35307
35220
  this.changeValue = e => {
@@ -35312,8 +35225,7 @@ class TextInput extends React.Component {
35312
35225
  }
35313
35226
  render() {
35314
35227
  return /*#__PURE__*/React.createElement("input", {
35315
- // eslint-disable-next-line react/no-string-refs
35316
- ref: "input",
35228
+ ref: this.input,
35317
35229
  value: this.props.value || "",
35318
35230
  onChange: this.changeValue
35319
35231
  });
@@ -35328,6 +35240,7 @@ class TextInput extends React.Component {
35328
35240
  class ExampleWidget extends React.Component {
35329
35241
  constructor(..._args) {
35330
35242
  super(..._args);
35243
+ this.input = /*#__PURE__*/React.createRef();
35331
35244
  /**
35332
35245
  * Tell our parent to update our props.
35333
35246
  */
@@ -35343,9 +35256,8 @@ class ExampleWidget extends React.Component {
35343
35256
  * focused on page load.
35344
35257
  */
35345
35258
  this.focus = () => {
35346
- // eslint-disable-next-line react/no-string-refs
35347
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
35348
- this.refs.input.focus();
35259
+ var _this$input$current2;
35260
+ (_this$input$current2 = this.input.current) == null || _this$input$current2.focus();
35349
35261
  return true;
35350
35262
  };
35351
35263
  /**
@@ -35394,10 +35306,8 @@ class ExampleWidget extends React.Component {
35394
35306
  };
35395
35307
  }
35396
35308
  render() {
35397
- return /*#__PURE__*/React.createElement(TextInput
35398
- // eslint-disable-next-line react/no-string-refs
35399
- , {
35400
- ref: "input",
35309
+ return /*#__PURE__*/React.createElement(TextInput, {
35310
+ ref: this.input,
35401
35311
  value: this.props.value
35402
35312
  // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
35403
35313
  ,