@khanacademy/perseus 54.0.0 → 55.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +18 -0
  2. package/dist/components/number-input.d.ts +9 -10
  3. package/dist/components/range-input.d.ts +5 -6
  4. package/dist/components/simple-keypad-input.d.ts +5 -0
  5. package/dist/components/stub-tag-editor.d.ts +3 -4
  6. package/dist/components/text-list-editor.d.ts +3 -4
  7. package/dist/es/index.css +16 -6
  8. package/dist/es/index.js +919 -1066
  9. package/dist/es/index.js.map +1 -1
  10. package/dist/es/strings.js +40 -10
  11. package/dist/es/strings.js.map +1 -1
  12. package/dist/index.css +16 -6
  13. package/dist/index.js +1055 -1170
  14. package/dist/index.js.map +1 -1
  15. package/dist/mixins/changeable.d.ts +1 -2
  16. package/dist/perseus-api.d.ts +1 -56
  17. package/dist/shared-utils/add-library-version-to-perseus-debug.d.ts +9 -0
  18. package/dist/strings.d.ts +34 -4
  19. package/dist/strings.js +142 -97
  20. package/dist/strings.js.map +1 -1
  21. package/dist/types.d.ts +3 -1
  22. package/dist/util.d.ts +0 -5
  23. package/dist/widget-ai-utils/numeric-input/prompt-utils.d.ts +1 -1
  24. package/dist/widgets/categorizer/categorizer.d.ts +1 -1
  25. package/dist/widgets/dropdown/dropdown.d.ts +5 -4
  26. package/dist/widgets/expression/expression.d.ts +0 -2
  27. package/dist/widgets/interactive-graph.d.ts +4 -2
  28. package/dist/widgets/interactive-graphs/graphs/circle.d.ts +7 -0
  29. package/dist/widgets/interactive-graphs/graphs/components/movable-line.d.ts +6 -0
  30. package/dist/widgets/interactive-graphs/graphs/quadratic.d.ts +8 -0
  31. package/dist/widgets/interactive-graphs/graphs/sinusoid.d.ts +7 -0
  32. package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +1 -1
  33. package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +6 -1
  34. package/dist/widgets/numeric-input/index.d.ts +1 -1
  35. package/dist/widgets/numeric-input/input-with-examples.d.ts +8 -0
  36. package/dist/widgets/numeric-input/numeric-input.class.d.ts +66 -0
  37. package/dist/widgets/numeric-input/numeric-input.d.ts +123 -77
  38. package/dist/widgets/numeric-input/utils.d.ts +25 -0
  39. package/dist/widgets/radio/radio.d.ts +1 -1
  40. package/dist/widgets/sorter/sorter.d.ts +1 -1
  41. package/package.json +120 -124
  42. package/dist/components/input-with-examples.d.ts +0 -51
  43. package/dist/components/tooltip.d.ts +0 -85
@@ -1,7 +1,14 @@
1
+ import { vec } from "mafs";
1
2
  import { type I18nContextType } from "../../../components/i18n-context";
2
3
  import type { SinusoidGraphState, Dispatch, InteractiveGraphElementSuite } from "../types";
3
4
  import type { NamedSineCoefficient } from "@khanacademy/kmath";
4
5
  import type { Coord } from "@khanacademy/perseus-core";
5
6
  export declare function renderSinusoidGraph(state: SinusoidGraphState, dispatch: Dispatch, i18n: I18nContextType): InteractiveGraphElementSuite;
7
+ export declare const getSinusoidKeyboardConstraint: (coords: ReadonlyArray<Coord>, snapStep: vec.Vector2, pointIndex: number) => {
8
+ up: vec.Vector2;
9
+ down: vec.Vector2;
10
+ left: vec.Vector2;
11
+ right: vec.Vector2;
12
+ };
6
13
  export declare const computeSine: (x: number, sinusoidCoefficients: NamedSineCoefficient) => number;
7
14
  export declare const getSinusoidCoefficients: (coords: ReadonlyArray<Coord>) => NamedSineCoefficient | undefined;
@@ -5,7 +5,7 @@ export type LockedFunctionOptions = {
5
5
  color?: LockedFigureColor;
6
6
  strokeStyle?: LockedLineStyle;
7
7
  directionalAxis?: "x" | "y";
8
- domain?: [min: number | null, max: number | null];
8
+ domain?: [min: number, max: number];
9
9
  labels?: LockedFigureLabelOptions[];
10
10
  ariaLabel?: string;
11
11
  };
@@ -6,7 +6,12 @@ export type StatefulMafsGraphProps = {
6
6
  box: [number, number];
7
7
  backgroundImage?: InteractiveGraphProps["backgroundImage"];
8
8
  graph: PerseusGraphType;
9
- correct: PerseusGraphType;
9
+ /**
10
+ * The correct answer for this widget. Will be undefined if the graph is
11
+ * being provided answerless data (e.g. because the learner has not yet
12
+ * submitted their guess).
13
+ */
14
+ correct?: PerseusGraphType;
10
15
  lockedFigures?: InteractiveGraphProps["lockedFigures"];
11
16
  range: InteractiveGraphProps["range"];
12
17
  snapStep: [x: number, y: number];
@@ -1 +1 @@
1
- export { default } from "./numeric-input";
1
+ export { default } from "./numeric-input.class";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The InputWithExamples component is a child component of the NumericInput
3
+ * and InputNumber components. It is responsible for rendering the UI elements
4
+ * for the desktop versions of these widgets, and displays a tooltip with
5
+ * examples of how to input the selected answer forms.
6
+ */
7
+ declare const InputWithExamples: any;
8
+ export default InputWithExamples;
@@ -0,0 +1,66 @@
1
+ import * as React from "react";
2
+ import type { FocusPath, Widget, WidgetProps } from "../../types";
3
+ import type { NumericInputPromptJSON } from "../../widget-ai-utils/numeric-input/prompt-utils";
4
+ import type { PerseusNumericInputWidgetOptions, PerseusNumericInputAnswerForm } from "@khanacademy/perseus-core";
5
+ import type { PerseusNumericInputRubric, PerseusNumericInputUserInput } from "@khanacademy/perseus-score";
6
+ type ExternalProps = WidgetProps<PerseusNumericInputWidgetOptions, PerseusNumericInputRubric>;
7
+ export type NumericInputProps = ExternalProps & {
8
+ size: NonNullable<ExternalProps["size"]>;
9
+ rightAlign: NonNullable<ExternalProps["rightAlign"]>;
10
+ apiOptions: NonNullable<ExternalProps["apiOptions"]>;
11
+ coefficient: NonNullable<ExternalProps["coefficient"]>;
12
+ answerForms: ReadonlyArray<PerseusNumericInputAnswerForm>;
13
+ labelText: string;
14
+ linterContext: NonNullable<ExternalProps["linterContext"]>;
15
+ currentValue: string;
16
+ };
17
+ type DefaultProps = Pick<NumericInputProps, "currentValue" | "size" | "rightAlign" | "apiOptions" | "coefficient" | "answerForms" | "labelText" | "linterContext">;
18
+ type RenderProps = {
19
+ answerForms: ReadonlyArray<PerseusNumericInputAnswerForm>;
20
+ labelText?: string;
21
+ size: string;
22
+ coefficient: boolean;
23
+ rightAlign?: boolean;
24
+ static: boolean;
25
+ };
26
+ /**
27
+ * The NumericInput widget is a numeric input field that supports a variety of
28
+ * answer forms, including integers, decimals, fractions, and mixed numbers.
29
+ *
30
+ * [Jan 2025] We're currenly migrating from class-based components to functional
31
+ * components. While we cannot fully migrate this component yet, we can start
32
+ * by using the functional component for the rendering the UI of the widget.
33
+ */
34
+ export declare class NumericInput extends React.Component<NumericInputProps> implements Widget {
35
+ inputRef: React.RefObject<any>;
36
+ static defaultProps: DefaultProps;
37
+ static getUserInputFromProps(props: NumericInputProps): PerseusNumericInputUserInput;
38
+ focus: () => boolean;
39
+ focusInputPath: () => void;
40
+ blurInputPath: () => void;
41
+ getInputPaths: () => ReadonlyArray<ReadonlyArray<string>>;
42
+ /**
43
+ * Sets the value of the input at the given path.
44
+ */
45
+ setInputValue: (path: FocusPath, newValue: string, cb?: () => unknown | null | undefined) => void;
46
+ /**
47
+ * Returns the value the user has currently input for this widget.
48
+ */
49
+ getUserInput(): PerseusNumericInputUserInput;
50
+ /**
51
+ * Returns the JSON representation of the prompt for this widget.
52
+ * This is used by the AI to determine the prompt for the widget.
53
+ */
54
+ getPromptJSON(): NumericInputPromptJSON;
55
+ render(): React.ReactNode;
56
+ }
57
+ declare const _default: {
58
+ name: string;
59
+ displayName: string;
60
+ accessible: true;
61
+ widget: typeof NumericInput;
62
+ transform: (widgetOptions: PerseusNumericInputWidgetOptions) => RenderProps;
63
+ isLintable: true;
64
+ getOneCorrectAnswerFromRubric(rubric: PerseusNumericInputRubric): string | null | undefined;
65
+ };
66
+ export default _default;
@@ -1,80 +1,126 @@
1
1
  import * as React from "react";
2
- import { PerseusI18nContext } from "../../components/i18n-context";
3
- import InputWithExamples from "../../components/input-with-examples";
4
- import SimpleKeypadInput from "../../components/simple-keypad-input";
5
- import type { FocusPath, Widget, WidgetProps } from "../../types";
6
- import type { NumericInputPromptJSON } from "../../widget-ai-utils/numeric-input/prompt-utils";
7
- import type { PerseusNumericInputWidgetOptions, PerseusNumericInputAnswerForm } from "@khanacademy/perseus-core";
8
- import type { PerseusNumericInputRubric, PerseusNumericInputUserInput } from "@khanacademy/perseus-score";
9
- type ExternalProps = WidgetProps<PerseusNumericInputWidgetOptions, PerseusNumericInputRubric>;
10
- type Props = ExternalProps & {
11
- size: NonNullable<ExternalProps["size"]>;
12
- rightAlign: NonNullable<ExternalProps["rightAlign"]>;
13
- apiOptions: NonNullable<ExternalProps["apiOptions"]>;
14
- coefficient: NonNullable<ExternalProps["coefficient"]>;
15
- answerForms: NonNullable<ExternalProps["answerForms"]>;
16
- labelText: string;
17
- linterContext: NonNullable<ExternalProps["linterContext"]>;
18
- currentValue: string;
19
- };
20
- type DefaultProps = {
21
- currentValue: Props["currentValue"];
22
- size: Props["size"];
23
- rightAlign: Props["rightAlign"];
24
- apiOptions: Props["apiOptions"];
25
- coefficient: Props["coefficient"];
26
- answerForms: Props["answerForms"];
27
- labelText: Props["labelText"];
28
- linterContext: Props["linterContext"];
29
- };
30
- type State = {
31
- previousValues: ReadonlyArray<string>;
32
- isFocused: boolean;
33
- };
34
- export declare class NumericInput extends React.Component<Props, State> implements Widget {
35
- static contextType: React.Context<import("../../components/i18n-context").I18nContextType>;
36
- context: React.ContextType<typeof PerseusI18nContext>;
37
- inputRef: SimpleKeypadInput | InputWithExamples | null | undefined;
38
- static defaultProps: DefaultProps;
39
- static getUserInputFromProps(props: Props): PerseusNumericInputUserInput;
40
- state: State;
41
- /**
42
- * Generates a string that demonstrates how to input the various supported
43
- * answer forms.
44
- */
45
- examples(): ReadonlyArray<string>;
46
- shouldShowExamples: () => boolean;
47
- focus: () => boolean;
48
- focusInputPath: () => void;
49
- blurInputPath: () => void;
50
- getInputPaths: () => ReadonlyArray<ReadonlyArray<string>>;
51
- setInputValue: (arg1: FocusPath, arg2: string, arg3?: () => unknown | null | undefined) => void;
52
- getUserInput(): PerseusNumericInputUserInput;
53
- getPromptJSON(): NumericInputPromptJSON;
54
- handleChange: (arg1: string, arg2?: () => unknown | null | undefined) => void;
55
- _handleFocus: () => void;
56
- _handleBlur: () => void;
57
- render(): React.ReactNode;
58
- }
59
- export declare const unionAnswerForms: (arg1: ReadonlyArray<ReadonlyArray<PerseusNumericInputAnswerForm>>) => ReadonlyArray<PerseusNumericInputAnswerForm>;
60
- type RenderProps = {
61
- answerForms: ReadonlyArray<{
62
- simplify: "required" | "correct" | "enforced" | null | undefined;
63
- name: "integer" | "decimal" | "proper" | "improper" | "mixed" | "pi";
2
+ /**
3
+ * The NumericInputComponent is a child component of the NumericInput class
4
+ * component. It is responsible for rendering the UI elements of the Numeric
5
+ * Input widget.
6
+ */
7
+ export declare const NumericInputComponent: React.ForwardRefExoticComponent<import("@khanacademy/perseus-core").PerseusNumericInputWidgetOptions & {
8
+ widgetId: string;
9
+ alignment: string | null | undefined;
10
+ static: boolean | null | undefined;
11
+ problemNum: number | null | undefined;
12
+ apiOptions: Readonly<Readonly<{
13
+ isArticle?: boolean;
14
+ onFocusChange?: (newFocusPath: import("../..").FocusPath, oldFocusPath: import("../..").FocusPath, keypadHeight?: number, focusedElement?: HTMLElement) => unknown;
15
+ GroupMetadataEditor?: React.ComponentType<any>;
16
+ showAlignmentOptions?: boolean;
17
+ readOnly?: boolean;
18
+ answerableCallback?: (arg1: boolean) => unknown;
19
+ getAnotherHint?: () => unknown;
20
+ interactionCallback?: (widgetData: {
21
+ [widgetId: string]: any;
22
+ }) => void;
23
+ groupAnnotator?: (groupNumber: number, widgetId: string) => React.ReactNode;
24
+ imagePlaceholder?: React.ReactNode;
25
+ widgetPlaceholder?: React.ReactNode;
26
+ baseElements?: {
27
+ Link: React.ComponentType<any>;
28
+ };
29
+ imagePreloader?: (dimensions: import("../../types").Dimensions) => React.ReactNode;
30
+ trackInteraction?: (args: {
31
+ type: string;
32
+ id: string;
33
+ correct?: boolean;
34
+ } & Partial<import("../../types").TrackingGradedGroupExtraArguments> & Partial<{
35
+ visible: number;
36
+ }>) => void;
37
+ customKeypad?: boolean;
38
+ nativeKeypadProxy?: (blur: () => void) => import("@khanacademy/math-input").KeypadAPI;
39
+ isMobile?: boolean;
40
+ setDrawingAreaAvailable?: (arg1: boolean) => unknown;
41
+ hintProgressColor?: string;
42
+ canScrollPage?: boolean;
43
+ crossOutEnabled?: boolean;
44
+ editorChangeDelay?: number;
45
+ onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
46
+ }> & {
47
+ baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
48
+ canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
49
+ crossOutEnabled: NonNullable<import("../..").APIOptions["crossOutEnabled"]>;
50
+ editorChangeDelay: NonNullable<import("../..").APIOptions["editorChangeDelay"]>;
51
+ groupAnnotator: NonNullable<import("../..").APIOptions["groupAnnotator"]>;
52
+ isArticle: NonNullable<import("../..").APIOptions["isArticle"]>;
53
+ isMobile: NonNullable<import("../..").APIOptions["isMobile"]>;
54
+ onFocusChange: NonNullable<import("../..").APIOptions["onFocusChange"]>;
55
+ readOnly: NonNullable<import("../..").APIOptions["readOnly"]>;
56
+ setDrawingAreaAvailable: NonNullable<import("../..").APIOptions["setDrawingAreaAvailable"]>;
57
+ showAlignmentOptions: NonNullable<import("../..").APIOptions["showAlignmentOptions"]>;
64
58
  }>;
59
+ keypadElement?: any;
60
+ questionCompleted?: boolean;
61
+ onFocus: (blurPath: import("../..").FocusPath) => void;
62
+ onBlur: (blurPath: import("../..").FocusPath) => void;
63
+ findWidgets: (criterion: import("../../types").FilterCriterion) => ReadonlyArray<import("../../types").Widget>;
64
+ reviewModeRubric?: import("@khanacademy/perseus-score").PerseusNumericInputRubric | null | undefined;
65
+ reviewMode: boolean;
66
+ onChange: import("../..").ChangeHandler;
67
+ trackInteraction: (extraData?: Empty | undefined) => void;
68
+ isLastUsedWidget: boolean;
69
+ linterContext: import("@khanacademy/perseus-linter").LinterContextProps;
70
+ containerSizeClass: import("../../util/sizing-utils").SizeClass;
71
+ } & {
72
+ size: NonNullable<string>;
73
+ rightAlign: NonNullable<boolean | undefined>;
74
+ apiOptions: NonNullable<Readonly<Readonly<{
75
+ isArticle?: boolean;
76
+ onFocusChange?: (newFocusPath: import("../..").FocusPath, oldFocusPath: import("../..").FocusPath, keypadHeight?: number, focusedElement?: HTMLElement) => unknown;
77
+ GroupMetadataEditor?: React.ComponentType<any>;
78
+ showAlignmentOptions?: boolean;
79
+ readOnly?: boolean;
80
+ answerableCallback?: (arg1: boolean) => unknown;
81
+ getAnotherHint?: () => unknown;
82
+ interactionCallback?: (widgetData: {
83
+ [widgetId: string]: any;
84
+ }) => void;
85
+ groupAnnotator?: (groupNumber: number, widgetId: string) => React.ReactNode;
86
+ imagePlaceholder?: React.ReactNode;
87
+ widgetPlaceholder?: React.ReactNode;
88
+ baseElements?: {
89
+ Link: React.ComponentType<any>;
90
+ };
91
+ imagePreloader?: (dimensions: import("../../types").Dimensions) => React.ReactNode;
92
+ trackInteraction?: (args: {
93
+ type: string;
94
+ id: string;
95
+ correct?: boolean;
96
+ } & Partial<import("../../types").TrackingGradedGroupExtraArguments> & Partial<{
97
+ visible: number;
98
+ }>) => void;
99
+ customKeypad?: boolean;
100
+ nativeKeypadProxy?: (blur: () => void) => import("@khanacademy/math-input").KeypadAPI;
101
+ isMobile?: boolean;
102
+ setDrawingAreaAvailable?: (arg1: boolean) => unknown;
103
+ hintProgressColor?: string;
104
+ canScrollPage?: boolean;
105
+ crossOutEnabled?: boolean;
106
+ editorChangeDelay?: number;
107
+ onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
108
+ }> & {
109
+ baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
110
+ canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
111
+ crossOutEnabled: NonNullable<import("../..").APIOptions["crossOutEnabled"]>;
112
+ editorChangeDelay: NonNullable<import("../..").APIOptions["editorChangeDelay"]>;
113
+ groupAnnotator: NonNullable<import("../..").APIOptions["groupAnnotator"]>;
114
+ isArticle: NonNullable<import("../..").APIOptions["isArticle"]>;
115
+ isMobile: NonNullable<import("../..").APIOptions["isMobile"]>;
116
+ onFocusChange: NonNullable<import("../..").APIOptions["onFocusChange"]>;
117
+ readOnly: NonNullable<import("../..").APIOptions["readOnly"]>;
118
+ setDrawingAreaAvailable: NonNullable<import("../..").APIOptions["setDrawingAreaAvailable"]>;
119
+ showAlignmentOptions: NonNullable<import("../..").APIOptions["showAlignmentOptions"]>;
120
+ }>>;
121
+ coefficient: NonNullable<boolean>;
122
+ answerForms: ReadonlyArray<import("@khanacademy/perseus-core").PerseusNumericInputAnswerForm>;
65
123
  labelText: string;
66
- size: "normal" | "small";
67
- coefficient: boolean;
68
- rightAlign?: boolean;
69
- static: boolean;
70
- };
71
- declare const _default: {
72
- name: string;
73
- displayName: string;
74
- accessible: true;
75
- widget: typeof NumericInput;
76
- transform: (widgetOptions: PerseusNumericInputWidgetOptions) => RenderProps;
77
- isLintable: true;
78
- getOneCorrectAnswerFromRubric(rubric: PerseusNumericInputRubric): string | null | undefined;
79
- };
80
- export default _default;
124
+ linterContext: NonNullable<import("@khanacademy/perseus-linter").LinterContextProps>;
125
+ currentValue: string;
126
+ } & React.RefAttributes<unknown>>;
@@ -0,0 +1,25 @@
1
+ import type { PerseusStrings } from "../../strings";
2
+ import type { PerseusNumericInputAnswerForm } from "@khanacademy/perseus-core";
3
+ /**
4
+ * The full list of available strings for the numeric input widget,
5
+ * based on whether the Content Creator has specified that the answer must be simplified.
6
+ */
7
+ export declare const NumericExampleStrings: {
8
+ [key: string]: (form: PerseusNumericInputAnswerForm, strings: PerseusStrings) => string;
9
+ };
10
+ /**
11
+ * Generates the specific set of examples for the current question.
12
+ * This string is shown as examples to the user in a tooltip.
13
+ */
14
+ export declare const generateExamples: (answerForms: readonly PerseusNumericInputAnswerForm[], strings: PerseusStrings) => ReadonlyArray<string>;
15
+ /**
16
+ * Determines whether to show examples of how to input the various supported answer forms.
17
+ * We do not show examples if all forms are accepted or if no forms are accepted.
18
+ */
19
+ export declare const shouldShowExamples: (answerForms: readonly PerseusNumericInputAnswerForm[]) => boolean;
20
+ /**
21
+ * Takes a list of lists of answer forms, and returns a list of the forms
22
+ * in each of these lists in the same order that they're listed in the
23
+ * `formExamples` forms from above.
24
+ */
25
+ export declare const unionAnswerForms: (answerFormsList: ReadonlyArray<ReadonlyArray<PerseusNumericInputAnswerForm>>) => ReadonlyArray<PerseusNumericInputAnswerForm>;
@@ -1,7 +1,7 @@
1
- import { type PerseusRadioWidgetOptions } from "@khanacademy/perseus-core";
2
1
  import Radio from "./radio-component";
3
2
  import type { RenderProps } from "./radio-component";
4
3
  import type { PerseusStrings } from "../../strings";
4
+ import type { PerseusRadioWidgetOptions } from "@khanacademy/perseus-core";
5
5
  declare const _default: {
6
6
  name: string;
7
7
  displayName: string;
@@ -1,8 +1,8 @@
1
- import { type PerseusSorterWidgetOptions } from "@khanacademy/perseus-core";
2
1
  import * as React from "react";
3
2
  import type { SortableOption } from "../../components/sortable";
4
3
  import type { Widget, WidgetProps } from "../../types";
5
4
  import type { SorterPromptJSON } from "../../widget-ai-utils/sorter/sorter-ai-utils";
5
+ import type { PerseusSorterWidgetOptions } from "@khanacademy/perseus-core";
6
6
  import type { PerseusSorterRubric, PerseusSorterUserInput } from "@khanacademy/perseus-score";
7
7
  type RenderProps = PerseusSorterWidgetOptions;
8
8
  type Props = WidgetProps<RenderProps, PerseusSorterRubric>;