@khanacademy/perseus 15.0.0 → 16.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.
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export { default as itemVersion } from "./item-version";
10
10
  * Renderers
11
11
  */
12
12
  export { default as ArticleRenderer } from "./article-renderer";
13
+ export { default as ItemRenderer } from "./item-renderer";
13
14
  export { default as ServerItemRenderer } from "./server-item-renderer";
14
15
  export { default as HintsRenderer } from "./hints-renderer";
15
16
  export { default as HintRenderer } from "./hint-renderer";
package/dist/index.js CHANGED
@@ -10260,7 +10260,7 @@ const pluck = function (table, subKey) {
10260
10260
  * });
10261
10261
  * {a: 2, b: 3}
10262
10262
  */
10263
- const mapObject$2 = function (obj, lambda) {
10263
+ const mapObject$3 = function (obj, lambda) {
10264
10264
  const result = {};
10265
10265
  ___default["default"].each(___default["default"].keys(obj), function (key) {
10266
10266
  // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
@@ -10286,7 +10286,7 @@ const mapObjectFromArray$1 = function (arr, lambda) {
10286
10286
  };
10287
10287
  var objective_ = {
10288
10288
  pluck: pluck,
10289
- mapObject: mapObject$2,
10289
+ mapObject: mapObject$3,
10290
10290
  mapObjectFromArray: mapObjectFromArray$1
10291
10291
  };
10292
10292
 
@@ -18932,7 +18932,7 @@ class WidgetContainer extends React__namespace.Component {
18932
18932
  }
18933
18933
 
18934
18934
  const {
18935
- mapObject: mapObject$1,
18935
+ mapObject: mapObject$2,
18936
18936
  mapObjectFromArray
18937
18937
  } = objective_;
18938
18938
  const rContainsNonWhitespace = /\S/;
@@ -19159,7 +19159,7 @@ class Renderer extends React__namespace.Component {
19159
19159
  // @ts-expect-error - TS2322 - Type '(props: Props) => Partial<Record<string, CategorizerWidget | CSProgramWidget | DefinitionWidget | DropdownWidget | ... 35 more ... | VideoWidget>>' is not assignable to type '(props: Props) => { [key: string]: PerseusWidget; }'.
19160
19160
  _getAllWidgetsInfo = props => {
19161
19161
  // @ts-expect-error - TS2345 - Argument of type '{ [key: string]: PerseusWidget; }' is not assignable to parameter of type 'Partial<Record<string, CategorizerWidget>>'.
19162
- return mapObject$1(props.widgets, (widgetInfo, widgetId) => {
19162
+ return mapObject$2(props.widgets, (widgetInfo, widgetId) => {
19163
19163
  if (!widgetInfo.type || !widgetInfo.alignment) {
19164
19164
  const newValues = {};
19165
19165
  if (!widgetInfo.type) {
@@ -19175,7 +19175,7 @@ class Renderer extends React__namespace.Component {
19175
19175
  };
19176
19176
  _getAllWidgetsStartProps = (allWidgetInfo, props) => {
19177
19177
  // @ts-expect-error - TS2345 - Argument of type '{ [key: string]: PerseusWidget; }' is not assignable to parameter of type 'Partial<Record<string, CategorizerWidget>>'.
19178
- return mapObject$1(allWidgetInfo, widgetInfo => {
19178
+ return mapObject$2(allWidgetInfo, widgetInfo => {
19179
19179
  return getRendererPropsForWidgetInfo(widgetInfo, props.problemNum);
19180
19180
  });
19181
19181
  };
@@ -19295,7 +19295,7 @@ class Renderer extends React__namespace.Component {
19295
19295
  * state from that instead of the current widget props.
19296
19296
  */
19297
19297
  getSerializedState = widgetProps => {
19298
- return mapObject$1(widgetProps || this.state.widgetProps, (props, widgetId) => {
19298
+ return mapObject$2(widgetProps || this.state.widgetProps, (props, widgetId) => {
19299
19299
  const widget = this.getWidgetInstance(widgetId);
19300
19300
  if (widget && widget.getSerializedState) {
19301
19301
  return widget.getSerializedState();
@@ -19331,7 +19331,7 @@ class Renderer extends React__namespace.Component {
19331
19331
  }
19332
19332
  };
19333
19333
  this.setState({
19334
- widgetProps: mapObject$1(serializedState, (props, widgetId) => {
19334
+ widgetProps: mapObject$2(serializedState, (props, widgetId) => {
19335
19335
  const widget = this.getWidgetInstance(widgetId);
19336
19336
  if (widget && widget.restoreSerializedState) {
19337
19337
  // Note that we probably can't call
@@ -44216,7 +44216,7 @@ const init = function (options) {
44216
44216
 
44217
44217
  // This file is processed by a Rollup plugin (replace) to inject the production
44218
44218
  const libName = "@khanacademy/perseus";
44219
- const libVersion = "15.0.0";
44219
+ const libVersion = "16.0.0";
44220
44220
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
44221
44221
 
44222
44222
  const apiVersion = {
@@ -44726,6 +44726,454 @@ const styles$4 = aphrodite.StyleSheet.create({
44726
44726
  }
44727
44727
  });
44728
44728
 
44729
+ /* eslint-disable @khanacademy/ts-no-error-suppressions */
44730
+
44731
+ // This represents the shape of any component that uses this ProvideKeypad
44732
+ // mixin.
44733
+ // NOTE: This is not a real component. It's a collection of methods used to
44734
+ // create and manage a Keypad instances.
44735
+ // TODO(LP-10789): replace this with a React Context object to pass information
44736
+ // between Perseus and the Keypad.
44737
+ const ProvideKeypad = {
44738
+ getInitialState() {
44739
+ const _this = this;
44740
+ let keypadElement = null;
44741
+ if (_this.props.apiOptions && _this.props.apiOptions.customKeypad && _this.props.apiOptions.nativeKeypadProxy) {
44742
+ keypadElement = _this.props.apiOptions.nativeKeypadProxy(
44743
+ // @ts-expect-error - TS2339 - Property 'blur' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'. | TS2339 - Property 'blur' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'.
44744
+ () => _this.blur && _this.blur());
44745
+ }
44746
+ return {
44747
+ keypadElement
44748
+ };
44749
+ },
44750
+ componentDidMount() {
44751
+ const _this = this;
44752
+ const apiOptions = _this.props.apiOptions;
44753
+ if (apiOptions && apiOptions.customKeypad && !apiOptions.nativeKeypadProxy) {
44754
+ // TODO(charlie): Render this and the wrapped component in the same
44755
+ // React tree. We may also want to add this keypad asynchronously
44756
+ // or on-demand in the future.
44757
+ _this._keypadContainer = document.createElement("div");
44758
+ document.body?.appendChild(_this._keypadContainer);
44759
+ render( /*#__PURE__*/React__namespace.createElement(mathInput.MobileKeypad, {
44760
+ onElementMounted: element => {
44761
+ // NOTE(kevinb): The reason why this setState works is
44762
+ // b/c we're calling it manually from item-renderer.jsx
44763
+ // and we're manually setting the 'this' by using 'call'
44764
+ // @ts-expect-error - TS2339 - Property 'setState' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'.
44765
+ _this.setState({
44766
+ keypadElement: element
44767
+ });
44768
+ },
44769
+ onDismiss: () => {
44770
+ // @ts-expect-error - TS2339 - Property 'blur' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'. | TS2339 - Property 'blur' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'.
44771
+ _this.blur && _this.blur();
44772
+ }
44773
+ // @ts-expect-error - TS2339 - Property 'props' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'.
44774
+ ,
44775
+ style: _this.props.keypadStyle,
44776
+ onAnalyticsEvent: async () => {
44777
+ // Intentionally left empty. This component is
44778
+ // deprecated and so we don't want/need to instrument
44779
+ // it.
44780
+ }
44781
+ }), _this._keypadContainer);
44782
+ }
44783
+ },
44784
+ componentWillUnmount() {
44785
+ const _this = this;
44786
+ if (_this._keypadContainer) {
44787
+ ReactDOM__default["default"].unmountComponentAtNode(_this._keypadContainer);
44788
+ if (_this._keypadContainer.parentNode) {
44789
+ // Note ChildNode.remove() isn't available in older Android
44790
+ // webviews.
44791
+ _this._keypadContainer.parentNode.removeChild(_this._keypadContainer);
44792
+ }
44793
+ _this._keypadContainer = null;
44794
+ }
44795
+ },
44796
+ keypadElement() {
44797
+ const _this = this;
44798
+ return _this.state.keypadElement;
44799
+ }
44800
+ };
44801
+
44802
+ const {
44803
+ mapObject: mapObject$1
44804
+ } = objective_;
44805
+ /**
44806
+ * @depreciated use ServerItemRenderer
44807
+ */
44808
+ class ItemRenderer extends React__namespace.Component {
44809
+ // @ts-expect-error - TS2564 - Property 'questionRenderer' has no initializer and is not definitely assigned in the constructor.
44810
+
44811
+ static defaultProps = {
44812
+ apiOptions: ApiOptions.defaults,
44813
+ // defaults are set in `this.update()`
44814
+ controlPeripherals: true,
44815
+ hintsAreaSelector: "#hintsarea",
44816
+ initialHintsVisible: 0,
44817
+ workAreaSelector: "#workarea",
44818
+ reviewMode: false,
44819
+ linterContext: PerseusLinter__namespace.linterContextDefault
44820
+ };
44821
+ constructor(props) {
44822
+ super(props);
44823
+ this.state = {
44824
+ // NOTE: This value is used in provide-keypad.jsx which contains a
44825
+ // "mixin" that this file calls methods on.
44826
+ ...ProvideKeypad.getInitialState.call(this),
44827
+ hintsVisible: props.initialHintsVisible || 0,
44828
+ questionCompleted: false,
44829
+ questionHighlightedWidgets: []
44830
+ };
44831
+ }
44832
+ componentDidMount() {
44833
+ ProvideKeypad.componentDidMount.call(this);
44834
+ if (this.props.controlPeripherals && this.props.apiOptions.setDrawingAreaAvailable) {
44835
+ this.props.apiOptions.setDrawingAreaAvailable(true);
44836
+ }
44837
+ this._currentFocus = null;
44838
+ this.update();
44839
+ }
44840
+ UNSAFE_componentWillReceiveProps(nextProps) {
44841
+ this.setState({
44842
+ questionHighlightedWidgets: []
44843
+ });
44844
+ }
44845
+ componentDidUpdate() {
44846
+ this.update();
44847
+ }
44848
+ componentWillUnmount() {
44849
+ ProvideKeypad.componentWillUnmount.call(this);
44850
+ const workArea = document.querySelector(this.props.workAreaSelector);
44851
+ if (workArea != null) {
44852
+ ReactDOM__namespace.unmountComponentAtNode(workArea);
44853
+ }
44854
+ const hintsArea = document.querySelector(this.props.hintsAreaSelector);
44855
+ if (hintsArea != null) {
44856
+ ReactDOM__namespace.unmountComponentAtNode(hintsArea);
44857
+ }
44858
+ if (this.props.controlPeripherals) {
44859
+ const answerArea = this.props.item.answerArea || {};
44860
+ if (answerArea.calculator) {
44861
+ $__default["default"]("#calculator").hide();
44862
+ }
44863
+ if (answerArea.periodicTable) {
44864
+ $__default["default"](".periodic-table-info-box").hide();
44865
+ }
44866
+ if (answerArea.zTable) {
44867
+ $__default["default"](".z-table-info-box").hide();
44868
+ }
44869
+ if (answerArea.tTable) {
44870
+ $__default["default"](".t-table-info-box").hide();
44871
+ }
44872
+ if (answerArea.chi2Table) {
44873
+ $__default["default"](".chi2-table-info-box").hide();
44874
+ }
44875
+ }
44876
+ }
44877
+ keypadElement() {
44878
+ return ProvideKeypad.keypadElement.call(this);
44879
+ }
44880
+ update() {
44881
+ const apiOptions = {
44882
+ ...ApiOptions.defaults,
44883
+ ...this.props.apiOptions,
44884
+ onFocusChange: this._handleFocusChange
44885
+ };
44886
+ const workArea = document.querySelector(this.props.workAreaSelector);
44887
+ const hintsArea = document.querySelector(this.props.hintsAreaSelector);
44888
+ if (!workArea || !hintsArea) {
44889
+ return;
44890
+ }
44891
+
44892
+ // Since the item renderer works by rendering things into three divs
44893
+ // that have completely different places in the DOM, we have to do this
44894
+ // strangeness instead of relying on React's normal render() method.
44895
+ // TODO(alpert): Figure out how to clean this up somehow
44896
+ render( /*#__PURE__*/React__namespace.createElement(DependenciesContext.Provider, {
44897
+ value: this.props.dependencies
44898
+ }, /*#__PURE__*/React__namespace.createElement(Renderer, _extends({
44899
+ ref: node => {
44900
+ if (!node) {
44901
+ return;
44902
+ }
44903
+ this.questionRenderer = node;
44904
+
44905
+ // NOTE(jeremy): Why don't we just pass this into the
44906
+ // renderer as a prop?
44907
+ const {
44908
+ answerableCallback
44909
+ } = apiOptions;
44910
+ if (answerableCallback) {
44911
+ const isAnswerable = this.questionRenderer.emptyWidgets().length === 0;
44912
+ answerableCallback(isAnswerable);
44913
+ }
44914
+ },
44915
+ keypadElement: this.keypadElement(),
44916
+ problemNum: this.props.problemNum,
44917
+ onInteractWithWidget: this.handleInteractWithWidget,
44918
+ highlightedWidgets: this.state.questionHighlightedWidgets,
44919
+ apiOptions: apiOptions,
44920
+ questionCompleted: this.state.questionCompleted,
44921
+ reviewMode: this.props.reviewMode,
44922
+ savedState: this.props.savedState,
44923
+ linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "question")
44924
+ }, this.props.item.question, {
44925
+ legacyPerseusLint: this.props.legacyPerseusLint
44926
+ }))),
44927
+ // @ts-expect-error - TS2345 - Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.
44928
+ workArea);
44929
+ render( /*#__PURE__*/React__namespace.createElement(DependenciesContext.Provider, {
44930
+ value: this.props.dependencies
44931
+ }, /*#__PURE__*/React__namespace.createElement(HintsRenderer, {
44932
+ ref: node => this.hintsRenderer = node,
44933
+ hints: this.props.item.hints,
44934
+ hintsVisible: this.state.hintsVisible,
44935
+ apiOptions: apiOptions,
44936
+ linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "hints")
44937
+ })),
44938
+ // @ts-expect-error - TS2345 - Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.
44939
+ hintsArea);
44940
+ const answerArea = this.props.item.answerArea || {};
44941
+ if (this.props.controlPeripherals) {
44942
+ $__default["default"]("#calculator").toggle(answerArea.calculator || false);
44943
+ $__default["default"](".periodic-table-info-box").toggle(answerArea.periodicTable || false);
44944
+ $__default["default"](".z-table-info-box").toggle(answerArea.zTable || false);
44945
+ $__default["default"](".t-table-info-box").toggle(answerArea.tTable || false);
44946
+ $__default["default"](".chi2-table-info-box").toggle(answerArea.chi2Table || false);
44947
+ } else {
44948
+ if (answerArea.calculator) {
44949
+ this.props.onShowCalculator && this.props.onShowCalculator();
44950
+ }
44951
+ if (answerArea.periodicTable) {
44952
+ this.props.onShowPeriodicTable && this.props.onShowPeriodicTable({
44953
+ showKey: answerArea.periodicTableWithKey
44954
+ });
44955
+ }
44956
+ if (answerArea.zTable) {
44957
+ this.props.onShowZTable && this.props.onShowZTable();
44958
+ }
44959
+ if (answerArea.tTable) {
44960
+ this.props.onShowTTable && this.props.onShowTTable();
44961
+ }
44962
+ if (answerArea.chi2Table) {
44963
+ this.props.onShowChi2Table && this.props.onShowChi2Table();
44964
+ }
44965
+ }
44966
+ }
44967
+ _handleFocusChange = (newFocus, oldFocus) => {
44968
+ if (newFocus != null) {
44969
+ this._setCurrentFocus(newFocus);
44970
+ } else {
44971
+ this._onRendererBlur(oldFocus);
44972
+ }
44973
+ };
44974
+
44975
+ // Sets the current focus path and element and send an onChangeFocus event
44976
+ // back to our parent.
44977
+ _setCurrentFocus(newFocus) {
44978
+ const keypadElement = this.keypadElement();
44979
+ // By the time this happens, newFocus cannot be a prefix of
44980
+ // prevFocused, since we must have either been called from
44981
+ // an onFocusChange within a renderer, which is only called when
44982
+ // this is not a prefix, or between the question and answer areas,
44983
+ // which can never prefix each other.
44984
+ const prevFocus = this._currentFocus;
44985
+ this._currentFocus = newFocus;
44986
+
44987
+ // Determine whether the newly focused path represents an input.
44988
+ const inputPaths = this.getInputPaths();
44989
+ const didFocusInput = this._currentFocus && inputPaths.some(inputPath => {
44990
+ return Util.inputPathsEqual(inputPath, this._currentFocus);
44991
+ });
44992
+ if (this.props.apiOptions.onFocusChange != null) {
44993
+ this.props.apiOptions.onFocusChange(this._currentFocus, prevFocus, didFocusInput && keypadElement && keypadElement.getDOMNode());
44994
+ }
44995
+ if (keypadElement) {
44996
+ if (didFocusInput) {
44997
+ keypadElement.activate();
44998
+ } else {
44999
+ keypadElement.dismiss();
45000
+ }
45001
+ }
45002
+ }
45003
+ _onRendererBlur(blurPath) {
45004
+ const blurringFocusPath = this._currentFocus;
45005
+
45006
+ // Failsafe: abort if ID is different, because focus probably happened
45007
+ // before blur.
45008
+ if (!Util.inputPathsEqual(blurPath, blurringFocusPath)) {
45009
+ return;
45010
+ }
45011
+
45012
+ // Wait until after any new focus events fire this tick before
45013
+ // declaring that nothing is focused, since if there were a focus change
45014
+ // across Renderers (e.g., from the HintsRenderer to the
45015
+ // QuestionRenderer), we could receive the blur before the focus.
45016
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API
45017
+ // eslint-disable-next-line no-restricted-syntax
45018
+ setTimeout(() => {
45019
+ if (Util.inputPathsEqual(this._currentFocus, blurringFocusPath)) {
45020
+ this._setCurrentFocus(null);
45021
+ }
45022
+ });
45023
+ }
45024
+
45025
+ /**
45026
+ * Accepts a question area widgetId, or an answer area widgetId of
45027
+ * the form "answer-input-number 1", or the string "answer-area"
45028
+ * for the whole answer area (if the answer area is a single widget).
45029
+ */
45030
+ _setWidgetProps(widgetId, newProps, callback) {
45031
+ this.questionRenderer._setWidgetProps(widgetId, newProps, callback);
45032
+ }
45033
+
45034
+ /**
45035
+ * Sets the input value into the widget found at `path`. The `focus`
45036
+ * function provided is called when all affected widgets have been
45037
+ * re-rendered with the new value.
45038
+ */
45039
+ setInputValue(path, newValue, focus) {
45040
+ return this.questionRenderer.setInputValue(path, newValue, focus);
45041
+ }
45042
+ focusPath(path) {
45043
+ return this.questionRenderer.focusPath(path);
45044
+ }
45045
+ blurPath(path) {
45046
+ return this.questionRenderer.blurPath(path);
45047
+ }
45048
+ getDOMNodeForPath(path) {
45049
+ return this.questionRenderer.getDOMNodeForPath(path);
45050
+ }
45051
+ getGrammarTypeForPath(path) {
45052
+ return this.questionRenderer.getGrammarTypeForPath(path);
45053
+ }
45054
+ getInputPaths() {
45055
+ const questionAreaInputPaths = this.questionRenderer.getInputPaths();
45056
+ return questionAreaInputPaths;
45057
+ }
45058
+
45059
+ // NOTE: this must remain as an arrow function because we pass it Renderer
45060
+ handleInteractWithWidget = widgetId => {
45061
+ const withRemoved = ___default["default"].difference(this.state.questionHighlightedWidgets, [widgetId]);
45062
+ this.setState({
45063
+ questionCompleted: false,
45064
+ questionHighlightedWidgets: withRemoved
45065
+ });
45066
+ if (this.props.apiOptions.interactionCallback) {
45067
+ this.props.apiOptions.interactionCallback();
45068
+ }
45069
+ };
45070
+ focus() {
45071
+ return this.questionRenderer.focus();
45072
+ }
45073
+ blur() {
45074
+ if (this._currentFocus) {
45075
+ this.blurPath(this._currentFocus);
45076
+ }
45077
+ }
45078
+ showHint() {
45079
+ if (this.state.hintsVisible < this.getNumHints()) {
45080
+ this.setState({
45081
+ hintsVisible: this.state.hintsVisible + 1
45082
+ });
45083
+ }
45084
+ }
45085
+ getNumHints() {
45086
+ return this.props.item.hints.length;
45087
+ }
45088
+
45089
+ /**
45090
+ * Grades the item.
45091
+ *
45092
+ * Returns a KE-style score of {
45093
+ * empty: bool,
45094
+ * correct: bool,
45095
+ * message: string|null,
45096
+ * guess: Array
45097
+ * }
45098
+ */
45099
+ scoreInput() {
45100
+ const guessAndScore = this.questionRenderer.guessAndScore();
45101
+ const guess = guessAndScore[0];
45102
+ const score = guessAndScore[1];
45103
+
45104
+ // Continue to include an empty guess for the now defunct answer area.
45105
+ // TODO(alex): Check whether we rely on the format here for
45106
+ // analyzing ProblemLogs. If not, remove this layer.
45107
+ const maxCompatGuess = [guess, []];
45108
+ const keScore = Util.keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
45109
+ const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
45110
+ this.setState({
45111
+ questionCompleted: keScore.correct,
45112
+ questionHighlightedWidgets: emptyQuestionAreaWidgets
45113
+ });
45114
+ return keScore;
45115
+ }
45116
+
45117
+ /**
45118
+ * Returns an array of all widget IDs in the order they occur in
45119
+ * the question content.
45120
+ */
45121
+ getWidgetIds() {
45122
+ return this.questionRenderer.getWidgetIds();
45123
+ }
45124
+
45125
+ /**
45126
+ * Returns an object mapping from widget ID to KE-style score.
45127
+ * The keys of this object are the values of the array returned
45128
+ * from `getWidgetIds`.
45129
+ */
45130
+ scoreWidgets() {
45131
+ const qScore = this.questionRenderer.scoreWidgets();
45132
+ const qGuess = this.questionRenderer.getUserInputForWidgets();
45133
+ const state = this.questionRenderer.getSerializedState();
45134
+ // @ts-expect-error - TS2322 - Type 'Partial<Record<string, KEScore>>' is not assignable to type '{ [id: string]: KEScore; }'. | TS2345 - Argument of type '{ [widgetId: string]: PerseusScore; }' is not assignable to parameter of type 'Partial<Record<string, { type: "invalid"; message?: string | null | undefined; suppressAlmostThere?: boolean | null | undefined; }>>'.
45135
+ return mapObject$1(qScore, (score, id) => {
45136
+ return Util.keScoreFromPerseusScore(score, qGuess[id], state[id]);
45137
+ });
45138
+ }
45139
+
45140
+ /**
45141
+ * Get a representation of the current state of the item.
45142
+ */
45143
+ getSerializedState() {
45144
+ return {
45145
+ question: this.questionRenderer.getSerializedState(),
45146
+ hints: this.hintsRenderer?.getSerializedState()
45147
+ };
45148
+ }
45149
+
45150
+ // TODO(kevinb): make the type for this state better
45151
+ restoreSerializedState(state, callback) {
45152
+ // We need to wait for both the question renderer and the hints
45153
+ // renderer to finish restoring their states.
45154
+ let numCallbacks = 2;
45155
+ const fireCallback = () => {
45156
+ --numCallbacks;
45157
+ if (callback && numCallbacks === 0) {
45158
+ callback();
45159
+ }
45160
+ };
45161
+ this.questionRenderer.restoreSerializedState(state.question, fireCallback);
45162
+ this.hintsRenderer?.restoreSerializedState(state.hints, fireCallback);
45163
+ }
45164
+ showRationalesForCurrentlySelectedChoices() {
45165
+ this.questionRenderer.showRationalesForCurrentlySelectedChoices();
45166
+ }
45167
+ deselectIncorrectSelectedChoices() {
45168
+ this.questionRenderer.deselectIncorrectSelectedChoices();
45169
+ }
45170
+ render() {
45171
+ return /*#__PURE__*/React__namespace.createElement(DependenciesContext.Provider, {
45172
+ value: this.props.dependencies
45173
+ }, /*#__PURE__*/React__namespace.createElement("div", null));
45174
+ }
45175
+ }
45176
+
44729
45177
  /**
44730
45178
  * A React context for signaling when all of the components in a Perseus
44731
45179
  * renderer is fully rendered. This means that all images and math have
@@ -47009,6 +47457,7 @@ exports.HintRenderer = HintRenderer;
47009
47457
  exports.HintsRenderer = HintsRenderer;
47010
47458
  exports.InputNumber = InputNumber$1;
47011
47459
  exports.InteractiveGraphWidget = InteractiveGraph$1;
47460
+ exports.ItemRenderer = ItemRenderer;
47012
47461
  exports.JiptParagraphs = JiptParagraphs;
47013
47462
  exports.KhanColors = KhanColors;
47014
47463
  exports.KhanMath = KhanMath;