@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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @khanacademy/perseus
2
2
 
3
+ ## 16.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#847](https://github.com/Khan/perseus/pull/847) [`45350a24`](https://github.com/Khan/perseus/commit/45350a2458cd3f816bc87af40997e426feb4b13b) Thanks [@handeyeco](https://github.com/handeyeco)! - Add back ItemRenderer temporarily
8
+
3
9
  ## 15.0.0
4
10
 
5
11
  ### Major Changes
@@ -5,12 +5,13 @@
5
5
  import * as React from "react";
6
6
  import { DependenciesContext } from "./dependencies";
7
7
  import Renderer from "./renderer";
8
+ import type { KeypadProps } from "./mixins/provide-keypad";
8
9
  import type { PerseusRenderer } from "./perseus-types";
9
10
  import type { APIOptions, PerseusDependenciesV2 } from "./types";
10
11
  import type { KeypadAPI } from "@khanacademy/math-input";
11
12
  import type { KeypadContextRendererInterface } from "@khanacademy/perseus-core";
12
13
  import type { LinterContextProps } from "@khanacademy/perseus-linter";
13
- type Props = Partial<React.ContextType<typeof DependenciesContext>> & {
14
+ type Props = Partial<React.ContextType<typeof DependenciesContext>> & KeypadProps & {
14
15
  apiOptions: APIOptions;
15
16
  json: PerseusRenderer | ReadonlyArray<PerseusRenderer>;
16
17
  /**
package/dist/es/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as KAS from '@khanacademy/kas';
2
- import { keyTranslator, CursorContext, createMathField, mathQuillInstance, getCursorContext, DesktopKeypad, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
2
+ import { keyTranslator, CursorContext, createMathField, mathQuillInstance, getCursorContext, DesktopKeypad, KeypadInput, KeypadType, KeyArray, keypadElementPropType, MobileKeypad } from '@khanacademy/math-input';
3
3
  import * as PerseusLinter from '@khanacademy/perseus-linter';
4
4
  import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
5
5
  import { View, RenderStateRoot, Text } from '@khanacademy/wonder-blocks-core';
@@ -10229,7 +10229,7 @@ const pluck = function pluck(table, subKey) {
10229
10229
  * });
10230
10230
  * {a: 2, b: 3}
10231
10231
  */
10232
- const mapObject$2 = function mapObject(obj, lambda) {
10232
+ const mapObject$3 = function mapObject(obj, lambda) {
10233
10233
  const result = {};
10234
10234
  _.each(_.keys(obj), function (key) {
10235
10235
  // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
@@ -10255,7 +10255,7 @@ const mapObjectFromArray$1 = function mapObjectFromArray(arr, lambda) {
10255
10255
  };
10256
10256
  var objective_ = {
10257
10257
  pluck: pluck,
10258
- mapObject: mapObject$2,
10258
+ mapObject: mapObject$3,
10259
10259
  mapObjectFromArray: mapObjectFromArray$1
10260
10260
  };
10261
10261
 
@@ -18864,7 +18864,7 @@ WidgetContainer.defaultProps = {
18864
18864
  };
18865
18865
 
18866
18866
  const {
18867
- mapObject: mapObject$1,
18867
+ mapObject: mapObject$2,
18868
18868
  mapObjectFromArray
18869
18869
  } = objective_;
18870
18870
  const rContainsNonWhitespace = /\S/;
@@ -18932,7 +18932,7 @@ class Renderer extends React.Component {
18932
18932
  // @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; }'.
18933
18933
  this._getAllWidgetsInfo = props => {
18934
18934
  // @ts-expect-error - TS2345 - Argument of type '{ [key: string]: PerseusWidget; }' is not assignable to parameter of type 'Partial<Record<string, CategorizerWidget>>'.
18935
- return mapObject$1(props.widgets, (widgetInfo, widgetId) => {
18935
+ return mapObject$2(props.widgets, (widgetInfo, widgetId) => {
18936
18936
  if (!widgetInfo.type || !widgetInfo.alignment) {
18937
18937
  const newValues = {};
18938
18938
  if (!widgetInfo.type) {
@@ -18948,7 +18948,7 @@ class Renderer extends React.Component {
18948
18948
  };
18949
18949
  this._getAllWidgetsStartProps = (allWidgetInfo, props) => {
18950
18950
  // @ts-expect-error - TS2345 - Argument of type '{ [key: string]: PerseusWidget; }' is not assignable to parameter of type 'Partial<Record<string, CategorizerWidget>>'.
18951
- return mapObject$1(allWidgetInfo, widgetInfo => {
18951
+ return mapObject$2(allWidgetInfo, widgetInfo => {
18952
18952
  return getRendererPropsForWidgetInfo(widgetInfo, props.problemNum);
18953
18953
  });
18954
18954
  };
@@ -19063,7 +19063,7 @@ class Renderer extends React.Component {
19063
19063
  * state from that instead of the current widget props.
19064
19064
  */
19065
19065
  this.getSerializedState = widgetProps => {
19066
- return mapObject$1(widgetProps || this.state.widgetProps, (props, widgetId) => {
19066
+ return mapObject$2(widgetProps || this.state.widgetProps, (props, widgetId) => {
19067
19067
  const widget = this.getWidgetInstance(widgetId);
19068
19068
  if (widget && widget.getSerializedState) {
19069
19069
  return widget.getSerializedState();
@@ -19099,7 +19099,7 @@ class Renderer extends React.Component {
19099
19099
  }
19100
19100
  };
19101
19101
  this.setState({
19102
- widgetProps: mapObject$1(serializedState, (props, widgetId) => {
19102
+ widgetProps: mapObject$2(serializedState, (props, widgetId) => {
19103
19103
  const widget = this.getWidgetInstance(widgetId);
19104
19104
  if (widget && widget.restoreSerializedState) {
19105
19105
  // Note that we probably can't call
@@ -44079,7 +44079,7 @@ const init = function init(options) {
44079
44079
 
44080
44080
  // This file is processed by a Rollup plugin (replace) to inject the production
44081
44081
  const libName = "@khanacademy/perseus";
44082
- const libVersion = "15.0.0";
44082
+ const libVersion = "16.0.0";
44083
44083
  addLibraryVersionToPerseusDebug(libName, libVersion);
44084
44084
 
44085
44085
  const apiVersion = {
@@ -44588,6 +44588,452 @@ const styles$4 = StyleSheet.create({
44588
44588
  }
44589
44589
  });
44590
44590
 
44591
+ /* eslint-disable @khanacademy/ts-no-error-suppressions */
44592
+
44593
+ // This represents the shape of any component that uses this ProvideKeypad
44594
+ // mixin.
44595
+ // NOTE: This is not a real component. It's a collection of methods used to
44596
+ // create and manage a Keypad instances.
44597
+ // TODO(LP-10789): replace this with a React Context object to pass information
44598
+ // between Perseus and the Keypad.
44599
+ const ProvideKeypad = {
44600
+ getInitialState() {
44601
+ const _this = this;
44602
+ let keypadElement = null;
44603
+ if (_this.props.apiOptions && _this.props.apiOptions.customKeypad && _this.props.apiOptions.nativeKeypadProxy) {
44604
+ keypadElement = _this.props.apiOptions.nativeKeypadProxy(
44605
+ // @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...'.
44606
+ () => _this.blur && _this.blur());
44607
+ }
44608
+ return {
44609
+ keypadElement
44610
+ };
44611
+ },
44612
+ componentDidMount() {
44613
+ const _this = this;
44614
+ const apiOptions = _this.props.apiOptions;
44615
+ if (apiOptions && apiOptions.customKeypad && !apiOptions.nativeKeypadProxy) {
44616
+ var _document$body;
44617
+ // TODO(charlie): Render this and the wrapped component in the same
44618
+ // React tree. We may also want to add this keypad asynchronously
44619
+ // or on-demand in the future.
44620
+ _this._keypadContainer = document.createElement("div");
44621
+ (_document$body = document.body) == null || _document$body.appendChild(_this._keypadContainer);
44622
+ render( /*#__PURE__*/React.createElement(MobileKeypad, {
44623
+ onElementMounted: element => {
44624
+ // NOTE(kevinb): The reason why this setState works is
44625
+ // b/c we're calling it manually from item-renderer.jsx
44626
+ // and we're manually setting the 'this' by using 'call'
44627
+ // @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...'.
44628
+ _this.setState({
44629
+ keypadElement: element
44630
+ });
44631
+ },
44632
+ onDismiss: () => {
44633
+ // @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...'.
44634
+ _this.blur && _this.blur();
44635
+ }
44636
+ // @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...'.
44637
+ ,
44638
+ style: _this.props.keypadStyle,
44639
+ onAnalyticsEvent: async () => {
44640
+ // Intentionally left empty. This component is
44641
+ // deprecated and so we don't want/need to instrument
44642
+ // it.
44643
+ }
44644
+ }), _this._keypadContainer);
44645
+ }
44646
+ },
44647
+ componentWillUnmount() {
44648
+ const _this = this;
44649
+ if (_this._keypadContainer) {
44650
+ ReactDOM__default.unmountComponentAtNode(_this._keypadContainer);
44651
+ if (_this._keypadContainer.parentNode) {
44652
+ // Note ChildNode.remove() isn't available in older Android
44653
+ // webviews.
44654
+ _this._keypadContainer.parentNode.removeChild(_this._keypadContainer);
44655
+ }
44656
+ _this._keypadContainer = null;
44657
+ }
44658
+ },
44659
+ keypadElement() {
44660
+ const _this = this;
44661
+ return _this.state.keypadElement;
44662
+ }
44663
+ };
44664
+
44665
+ const {
44666
+ mapObject: mapObject$1
44667
+ } = objective_;
44668
+ /**
44669
+ * @depreciated use ServerItemRenderer
44670
+ */
44671
+ class ItemRenderer extends React.Component {
44672
+ constructor(props) {
44673
+ super(props);
44674
+ // @ts-expect-error - TS2564 - Property 'questionRenderer' has no initializer and is not definitely assigned in the constructor.
44675
+ this.questionRenderer = void 0;
44676
+ this.hintsRenderer = void 0;
44677
+ this._currentFocus = void 0;
44678
+ this._handleFocusChange = (newFocus, oldFocus) => {
44679
+ if (newFocus != null) {
44680
+ this._setCurrentFocus(newFocus);
44681
+ } else {
44682
+ this._onRendererBlur(oldFocus);
44683
+ }
44684
+ };
44685
+ // NOTE: this must remain as an arrow function because we pass it Renderer
44686
+ this.handleInteractWithWidget = widgetId => {
44687
+ const withRemoved = _.difference(this.state.questionHighlightedWidgets, [widgetId]);
44688
+ this.setState({
44689
+ questionCompleted: false,
44690
+ questionHighlightedWidgets: withRemoved
44691
+ });
44692
+ if (this.props.apiOptions.interactionCallback) {
44693
+ this.props.apiOptions.interactionCallback();
44694
+ }
44695
+ };
44696
+ this.state = _extends({}, ProvideKeypad.getInitialState.call(this), {
44697
+ hintsVisible: props.initialHintsVisible || 0,
44698
+ questionCompleted: false,
44699
+ questionHighlightedWidgets: []
44700
+ });
44701
+ }
44702
+ componentDidMount() {
44703
+ ProvideKeypad.componentDidMount.call(this);
44704
+ if (this.props.controlPeripherals && this.props.apiOptions.setDrawingAreaAvailable) {
44705
+ this.props.apiOptions.setDrawingAreaAvailable(true);
44706
+ }
44707
+ this._currentFocus = null;
44708
+ this.update();
44709
+ }
44710
+ UNSAFE_componentWillReceiveProps(nextProps) {
44711
+ this.setState({
44712
+ questionHighlightedWidgets: []
44713
+ });
44714
+ }
44715
+ componentDidUpdate() {
44716
+ this.update();
44717
+ }
44718
+ componentWillUnmount() {
44719
+ ProvideKeypad.componentWillUnmount.call(this);
44720
+ const workArea = document.querySelector(this.props.workAreaSelector);
44721
+ if (workArea != null) {
44722
+ ReactDOM.unmountComponentAtNode(workArea);
44723
+ }
44724
+ const hintsArea = document.querySelector(this.props.hintsAreaSelector);
44725
+ if (hintsArea != null) {
44726
+ ReactDOM.unmountComponentAtNode(hintsArea);
44727
+ }
44728
+ if (this.props.controlPeripherals) {
44729
+ const answerArea = this.props.item.answerArea || {};
44730
+ if (answerArea.calculator) {
44731
+ $("#calculator").hide();
44732
+ }
44733
+ if (answerArea.periodicTable) {
44734
+ $(".periodic-table-info-box").hide();
44735
+ }
44736
+ if (answerArea.zTable) {
44737
+ $(".z-table-info-box").hide();
44738
+ }
44739
+ if (answerArea.tTable) {
44740
+ $(".t-table-info-box").hide();
44741
+ }
44742
+ if (answerArea.chi2Table) {
44743
+ $(".chi2-table-info-box").hide();
44744
+ }
44745
+ }
44746
+ }
44747
+ keypadElement() {
44748
+ return ProvideKeypad.keypadElement.call(this);
44749
+ }
44750
+ update() {
44751
+ const apiOptions = _extends({}, ApiOptions.defaults, this.props.apiOptions, {
44752
+ onFocusChange: this._handleFocusChange
44753
+ });
44754
+ const workArea = document.querySelector(this.props.workAreaSelector);
44755
+ const hintsArea = document.querySelector(this.props.hintsAreaSelector);
44756
+ if (!workArea || !hintsArea) {
44757
+ return;
44758
+ }
44759
+
44760
+ // Since the item renderer works by rendering things into three divs
44761
+ // that have completely different places in the DOM, we have to do this
44762
+ // strangeness instead of relying on React's normal render() method.
44763
+ // TODO(alpert): Figure out how to clean this up somehow
44764
+ render( /*#__PURE__*/React.createElement(DependenciesContext.Provider, {
44765
+ value: this.props.dependencies
44766
+ }, /*#__PURE__*/React.createElement(Renderer, _extends({
44767
+ ref: node => {
44768
+ if (!node) {
44769
+ return;
44770
+ }
44771
+ this.questionRenderer = node;
44772
+
44773
+ // NOTE(jeremy): Why don't we just pass this into the
44774
+ // renderer as a prop?
44775
+ const {
44776
+ answerableCallback
44777
+ } = apiOptions;
44778
+ if (answerableCallback) {
44779
+ const isAnswerable = this.questionRenderer.emptyWidgets().length === 0;
44780
+ answerableCallback(isAnswerable);
44781
+ }
44782
+ },
44783
+ keypadElement: this.keypadElement(),
44784
+ problemNum: this.props.problemNum,
44785
+ onInteractWithWidget: this.handleInteractWithWidget,
44786
+ highlightedWidgets: this.state.questionHighlightedWidgets,
44787
+ apiOptions: apiOptions,
44788
+ questionCompleted: this.state.questionCompleted,
44789
+ reviewMode: this.props.reviewMode,
44790
+ savedState: this.props.savedState,
44791
+ linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "question")
44792
+ }, this.props.item.question, {
44793
+ legacyPerseusLint: this.props.legacyPerseusLint
44794
+ }))),
44795
+ // @ts-expect-error - TS2345 - Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.
44796
+ workArea);
44797
+ render( /*#__PURE__*/React.createElement(DependenciesContext.Provider, {
44798
+ value: this.props.dependencies
44799
+ }, /*#__PURE__*/React.createElement(HintsRenderer, {
44800
+ ref: node => this.hintsRenderer = node,
44801
+ hints: this.props.item.hints,
44802
+ hintsVisible: this.state.hintsVisible,
44803
+ apiOptions: apiOptions,
44804
+ linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "hints")
44805
+ })),
44806
+ // @ts-expect-error - TS2345 - Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.
44807
+ hintsArea);
44808
+ const answerArea = this.props.item.answerArea || {};
44809
+ if (this.props.controlPeripherals) {
44810
+ $("#calculator").toggle(answerArea.calculator || false);
44811
+ $(".periodic-table-info-box").toggle(answerArea.periodicTable || false);
44812
+ $(".z-table-info-box").toggle(answerArea.zTable || false);
44813
+ $(".t-table-info-box").toggle(answerArea.tTable || false);
44814
+ $(".chi2-table-info-box").toggle(answerArea.chi2Table || false);
44815
+ } else {
44816
+ if (answerArea.calculator) {
44817
+ this.props.onShowCalculator && this.props.onShowCalculator();
44818
+ }
44819
+ if (answerArea.periodicTable) {
44820
+ this.props.onShowPeriodicTable && this.props.onShowPeriodicTable({
44821
+ showKey: answerArea.periodicTableWithKey
44822
+ });
44823
+ }
44824
+ if (answerArea.zTable) {
44825
+ this.props.onShowZTable && this.props.onShowZTable();
44826
+ }
44827
+ if (answerArea.tTable) {
44828
+ this.props.onShowTTable && this.props.onShowTTable();
44829
+ }
44830
+ if (answerArea.chi2Table) {
44831
+ this.props.onShowChi2Table && this.props.onShowChi2Table();
44832
+ }
44833
+ }
44834
+ }
44835
+ // Sets the current focus path and element and send an onChangeFocus event
44836
+ // back to our parent.
44837
+ _setCurrentFocus(newFocus) {
44838
+ const keypadElement = this.keypadElement();
44839
+ // By the time this happens, newFocus cannot be a prefix of
44840
+ // prevFocused, since we must have either been called from
44841
+ // an onFocusChange within a renderer, which is only called when
44842
+ // this is not a prefix, or between the question and answer areas,
44843
+ // which can never prefix each other.
44844
+ const prevFocus = this._currentFocus;
44845
+ this._currentFocus = newFocus;
44846
+
44847
+ // Determine whether the newly focused path represents an input.
44848
+ const inputPaths = this.getInputPaths();
44849
+ const didFocusInput = this._currentFocus && inputPaths.some(inputPath => {
44850
+ return Util.inputPathsEqual(inputPath, this._currentFocus);
44851
+ });
44852
+ if (this.props.apiOptions.onFocusChange != null) {
44853
+ this.props.apiOptions.onFocusChange(this._currentFocus, prevFocus, didFocusInput && keypadElement && keypadElement.getDOMNode());
44854
+ }
44855
+ if (keypadElement) {
44856
+ if (didFocusInput) {
44857
+ keypadElement.activate();
44858
+ } else {
44859
+ keypadElement.dismiss();
44860
+ }
44861
+ }
44862
+ }
44863
+ _onRendererBlur(blurPath) {
44864
+ const blurringFocusPath = this._currentFocus;
44865
+
44866
+ // Failsafe: abort if ID is different, because focus probably happened
44867
+ // before blur.
44868
+ if (!Util.inputPathsEqual(blurPath, blurringFocusPath)) {
44869
+ return;
44870
+ }
44871
+
44872
+ // Wait until after any new focus events fire this tick before
44873
+ // declaring that nothing is focused, since if there were a focus change
44874
+ // across Renderers (e.g., from the HintsRenderer to the
44875
+ // QuestionRenderer), we could receive the blur before the focus.
44876
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API
44877
+ // eslint-disable-next-line no-restricted-syntax
44878
+ setTimeout(() => {
44879
+ if (Util.inputPathsEqual(this._currentFocus, blurringFocusPath)) {
44880
+ this._setCurrentFocus(null);
44881
+ }
44882
+ });
44883
+ }
44884
+
44885
+ /**
44886
+ * Accepts a question area widgetId, or an answer area widgetId of
44887
+ * the form "answer-input-number 1", or the string "answer-area"
44888
+ * for the whole answer area (if the answer area is a single widget).
44889
+ */
44890
+ _setWidgetProps(widgetId, newProps, callback) {
44891
+ this.questionRenderer._setWidgetProps(widgetId, newProps, callback);
44892
+ }
44893
+
44894
+ /**
44895
+ * Sets the input value into the widget found at `path`. The `focus`
44896
+ * function provided is called when all affected widgets have been
44897
+ * re-rendered with the new value.
44898
+ */
44899
+ setInputValue(path, newValue, focus) {
44900
+ return this.questionRenderer.setInputValue(path, newValue, focus);
44901
+ }
44902
+ focusPath(path) {
44903
+ return this.questionRenderer.focusPath(path);
44904
+ }
44905
+ blurPath(path) {
44906
+ return this.questionRenderer.blurPath(path);
44907
+ }
44908
+ getDOMNodeForPath(path) {
44909
+ return this.questionRenderer.getDOMNodeForPath(path);
44910
+ }
44911
+ getGrammarTypeForPath(path) {
44912
+ return this.questionRenderer.getGrammarTypeForPath(path);
44913
+ }
44914
+ getInputPaths() {
44915
+ const questionAreaInputPaths = this.questionRenderer.getInputPaths();
44916
+ return questionAreaInputPaths;
44917
+ }
44918
+ focus() {
44919
+ return this.questionRenderer.focus();
44920
+ }
44921
+ blur() {
44922
+ if (this._currentFocus) {
44923
+ this.blurPath(this._currentFocus);
44924
+ }
44925
+ }
44926
+ showHint() {
44927
+ if (this.state.hintsVisible < this.getNumHints()) {
44928
+ this.setState({
44929
+ hintsVisible: this.state.hintsVisible + 1
44930
+ });
44931
+ }
44932
+ }
44933
+ getNumHints() {
44934
+ return this.props.item.hints.length;
44935
+ }
44936
+
44937
+ /**
44938
+ * Grades the item.
44939
+ *
44940
+ * Returns a KE-style score of {
44941
+ * empty: bool,
44942
+ * correct: bool,
44943
+ * message: string|null,
44944
+ * guess: Array
44945
+ * }
44946
+ */
44947
+ scoreInput() {
44948
+ const guessAndScore = this.questionRenderer.guessAndScore();
44949
+ const guess = guessAndScore[0];
44950
+ const score = guessAndScore[1];
44951
+
44952
+ // Continue to include an empty guess for the now defunct answer area.
44953
+ // TODO(alex): Check whether we rely on the format here for
44954
+ // analyzing ProblemLogs. If not, remove this layer.
44955
+ const maxCompatGuess = [guess, []];
44956
+ const keScore = Util.keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
44957
+ const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
44958
+ this.setState({
44959
+ questionCompleted: keScore.correct,
44960
+ questionHighlightedWidgets: emptyQuestionAreaWidgets
44961
+ });
44962
+ return keScore;
44963
+ }
44964
+
44965
+ /**
44966
+ * Returns an array of all widget IDs in the order they occur in
44967
+ * the question content.
44968
+ */
44969
+ getWidgetIds() {
44970
+ return this.questionRenderer.getWidgetIds();
44971
+ }
44972
+
44973
+ /**
44974
+ * Returns an object mapping from widget ID to KE-style score.
44975
+ * The keys of this object are the values of the array returned
44976
+ * from `getWidgetIds`.
44977
+ */
44978
+ scoreWidgets() {
44979
+ const qScore = this.questionRenderer.scoreWidgets();
44980
+ const qGuess = this.questionRenderer.getUserInputForWidgets();
44981
+ const state = this.questionRenderer.getSerializedState();
44982
+ // @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; }>>'.
44983
+ return mapObject$1(qScore, (score, id) => {
44984
+ return Util.keScoreFromPerseusScore(score, qGuess[id], state[id]);
44985
+ });
44986
+ }
44987
+
44988
+ /**
44989
+ * Get a representation of the current state of the item.
44990
+ */
44991
+ getSerializedState() {
44992
+ var _this$hintsRenderer;
44993
+ return {
44994
+ question: this.questionRenderer.getSerializedState(),
44995
+ hints: (_this$hintsRenderer = this.hintsRenderer) == null ? void 0 : _this$hintsRenderer.getSerializedState()
44996
+ };
44997
+ }
44998
+
44999
+ // TODO(kevinb): make the type for this state better
45000
+ restoreSerializedState(state, callback) {
45001
+ var _this$hintsRenderer2;
45002
+ // We need to wait for both the question renderer and the hints
45003
+ // renderer to finish restoring their states.
45004
+ let numCallbacks = 2;
45005
+ const fireCallback = () => {
45006
+ --numCallbacks;
45007
+ if (callback && numCallbacks === 0) {
45008
+ callback();
45009
+ }
45010
+ };
45011
+ this.questionRenderer.restoreSerializedState(state.question, fireCallback);
45012
+ (_this$hintsRenderer2 = this.hintsRenderer) == null || _this$hintsRenderer2.restoreSerializedState(state.hints, fireCallback);
45013
+ }
45014
+ showRationalesForCurrentlySelectedChoices() {
45015
+ this.questionRenderer.showRationalesForCurrentlySelectedChoices();
45016
+ }
45017
+ deselectIncorrectSelectedChoices() {
45018
+ this.questionRenderer.deselectIncorrectSelectedChoices();
45019
+ }
45020
+ render() {
45021
+ return /*#__PURE__*/React.createElement(DependenciesContext.Provider, {
45022
+ value: this.props.dependencies
45023
+ }, /*#__PURE__*/React.createElement("div", null));
45024
+ }
45025
+ }
45026
+ ItemRenderer.defaultProps = {
45027
+ apiOptions: ApiOptions.defaults,
45028
+ // defaults are set in `this.update()`
45029
+ controlPeripherals: true,
45030
+ hintsAreaSelector: "#hintsarea",
45031
+ initialHintsVisible: 0,
45032
+ workAreaSelector: "#workarea",
45033
+ reviewMode: false,
45034
+ linterContext: PerseusLinter.linterContextDefault
45035
+ };
45036
+
44591
45037
  /**
44592
45038
  * A React context for signaling when all of the components in a Perseus
44593
45039
  * renderer is fully rendered. This means that all images and math have
@@ -46861,5 +47307,5 @@ const EditorJsonify = {
46861
47307
  }
46862
47308
  };
46863
47309
 
46864
- export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Transformer$1 as TransformerWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, displaySigFigs, globalConstants, globalStyles, iconPaths as icons, init, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, traverse, violatingWidgets, allWidgets as widgets };
47310
+ export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemRenderer, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Transformer$1 as TransformerWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, displaySigFigs, globalConstants, globalStyles, iconPaths as icons, init, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, traverse, violatingWidgets, allWidgets as widgets };
46865
47311
  //# sourceMappingURL=index.js.map