@khanacademy/perseus 37.0.1 → 38.0.1
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/es/index.js +89 -60
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +89 -60
- package/dist/index.js.map +1 -1
- package/dist/multi-items/item-types.d.ts +4 -3
- package/dist/perseus-api.d.ts +6 -6
- package/dist/perseus-types.d.ts +31 -30
- package/dist/server-item-renderer.d.ts +6 -6
- package/dist/types.d.ts +1 -4
- package/dist/validation.types.d.ts +12 -13
- package/dist/widgets/expression/expression.d.ts +36 -36
- package/dist/widgets/grapher/grapher-types.d.ts +58 -0
- package/dist/widgets/grapher/grapher-validator.d.ts +1 -1
- package/dist/widgets/grapher/util.d.ts +16 -3
- package/dist/widgets/input-number/input-number-validator.d.ts +2 -4
- package/dist/widgets/label-image/label-image-validator.d.ts +1 -1
- package/dist/widgets/label-image/label-image.d.ts +14 -14
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -82,7 +82,7 @@ export { default as WIDGET_PROP_DENYLIST } from "./mixins/widget-prop-denylist";
|
|
|
82
82
|
export type { PerseusOptions } from "./init";
|
|
83
83
|
export type { ILogger, LogErrorOptions } from "./logging/log";
|
|
84
84
|
export type { ServerItemRenderer as ServerItemRendererComponent } from "./server-item-renderer";
|
|
85
|
-
export type { Alignment, APIOptions, APIOptionsWithDefaults, ChangeHandler, DeviceType, DomInsertCheckFn, EditorMode, FocusPath, ImageDict, ImageUploader, JiptLabelStore, JiptRenderer, PerseusDependencies, PerseusScore, Version, VideoData, VideoKind,
|
|
85
|
+
export type { Alignment, APIOptions, APIOptionsWithDefaults, ChangeHandler, DeviceType, DomInsertCheckFn, EditorMode, FocusPath, ImageDict, ImageUploader, JiptLabelStore, JiptRenderer, PerseusDependencies, PerseusScore, Version, VideoData, VideoKind, WidgetExports, SharedRendererProps, } from "./types";
|
|
86
86
|
export type { ParsedValue } from "./util";
|
|
87
87
|
export type { Hint, LegacyButtonSets, LockedFigure, LockedFigureColor, LockedFigureFillType, LockedFigureType, LockedPointType, LockedLineType, LockedVectorType, LockedEllipseType, LockedPolygonType, LockedFunctionType, LockedLabelType, PerseusGraphType, PerseusAnswerArea, PerseusExpressionWidgetOptions, Range, Size, CollinearTuple, MathFormat, InputNumberWidget, // TODO(jeremy): remove?
|
|
88
88
|
PerseusImageBackground, PerseusInputNumberWidgetOptions, PerseusInteractiveGraphWidgetOptions, PerseusItem, PerseusPhetSimulationWidgetOptions, PerseusPlotterWidgetOptions, PerseusPythonProgramWidgetOptions, PerseusRadioWidgetOptions, PerseusExampleWidgetOptions, PerseusSimpleMarkdownTesterWidgetOptions, PerseusRenderer, PerseusWidget, PerseusWidgetsMap, MultiItem, } from "./perseus-types";
|
package/dist/index.js
CHANGED
|
@@ -10084,9 +10084,6 @@ const setLabelMargins = function (span, size) {
|
|
|
10084
10084
|
// Inherited line-height values can really mess up placement.
|
|
10085
10085
|
$container.css("line-height", "normal");
|
|
10086
10086
|
|
|
10087
|
-
// TODO: Verify that this change doesn't impact other graphies
|
|
10088
|
-
// Check the full list of consuming widgets.
|
|
10089
|
-
|
|
10090
10087
|
// If the change in line-height affected the height of the element,
|
|
10091
10088
|
// then the height used for calculations should be updated.
|
|
10092
10089
|
// This can happen when the first label in the container calls this method,
|
|
@@ -21903,7 +21900,7 @@ const answerTypes = {
|
|
|
21903
21900
|
forms: "pi"
|
|
21904
21901
|
}
|
|
21905
21902
|
};
|
|
21906
|
-
function inputNumberValidator(
|
|
21903
|
+
function inputNumberValidator(userInput, rubric, strings) {
|
|
21907
21904
|
if (rubric.answerType == null) {
|
|
21908
21905
|
rubric.answerType = "number";
|
|
21909
21906
|
}
|
|
@@ -21921,7 +21918,7 @@ function inputNumberValidator(state, rubric, strings) {
|
|
|
21921
21918
|
|
|
21922
21919
|
// We may have received TeX; try to parse it before grading.
|
|
21923
21920
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
21924
|
-
const currentValue = ParseTex$1(
|
|
21921
|
+
const currentValue = ParseTex$1(userInput.currentValue);
|
|
21925
21922
|
const result = val(currentValue);
|
|
21926
21923
|
|
|
21927
21924
|
// TODO(eater): Seems silly to translate result to this invalid/points
|
|
@@ -28539,7 +28536,7 @@ const Exponential = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28539
28536
|
* coordinate, and `false` uses oldCoord as the resulting coordinate.
|
|
28540
28537
|
*/
|
|
28541
28538
|
extraCoordConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
|
|
28542
|
-
const y =
|
|
28539
|
+
const y = asymptote[0][1];
|
|
28543
28540
|
return ___default["default"].all(coords, coord => coord[1] !== y);
|
|
28544
28541
|
},
|
|
28545
28542
|
extraAsymptoteConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
|
|
@@ -28563,7 +28560,7 @@ const Exponential = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28563
28560
|
getCoefficients: function (coords, asymptote) {
|
|
28564
28561
|
const p1 = coords[0];
|
|
28565
28562
|
const p2 = coords[1];
|
|
28566
|
-
const c =
|
|
28563
|
+
const c = asymptote[0][1];
|
|
28567
28564
|
const b = Math.log((p1[1] - c) / (p2[1] - c)) / (p1[0] - p2[0]);
|
|
28568
28565
|
const a = (p1[1] - c) / Math.exp(b * p1[0]);
|
|
28569
28566
|
return [a, b, c];
|
|
@@ -28590,7 +28587,7 @@ const Logarithm = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28590
28587
|
defaultCoords: [[0.55, 0.5], [0.75, 0.75]],
|
|
28591
28588
|
defaultAsymptote: [[0.5, 0], [0.5, 1.0]],
|
|
28592
28589
|
extraCoordConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
|
|
28593
|
-
const x =
|
|
28590
|
+
const x = asymptote[0][0];
|
|
28594
28591
|
return ___default["default"].all(coords, coord => coord[0] !== x) && coords[0][1] !== coords[1][1];
|
|
28595
28592
|
},
|
|
28596
28593
|
extraAsymptoteConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
|
|
@@ -28618,10 +28615,12 @@ const Logarithm = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28618
28615
|
// logic between the two 'models'.
|
|
28619
28616
|
const flip = coord => [coord[1], coord[0]];
|
|
28620
28617
|
const inverseCoeffs = Exponential.getCoefficients(___default["default"].map(coords, flip), ___default["default"].map(asymptote, flip));
|
|
28621
|
-
|
|
28622
|
-
|
|
28623
|
-
|
|
28624
|
-
|
|
28618
|
+
if (inverseCoeffs) {
|
|
28619
|
+
const c = -inverseCoeffs[2] / inverseCoeffs[0];
|
|
28620
|
+
const b = 1 / inverseCoeffs[0];
|
|
28621
|
+
const a = 1 / inverseCoeffs[1];
|
|
28622
|
+
return [a, b, c];
|
|
28623
|
+
}
|
|
28625
28624
|
},
|
|
28626
28625
|
getFunctionForCoeffs: function (coeffs, x, asymptote) {
|
|
28627
28626
|
const a = coeffs[0],
|
|
@@ -28685,11 +28684,9 @@ const functionTypeMapping = {
|
|
|
28685
28684
|
absolute_value: AbsoluteValue
|
|
28686
28685
|
};
|
|
28687
28686
|
const allTypes = ___default["default"].keys(functionTypeMapping);
|
|
28688
|
-
function functionForType(
|
|
28689
|
-
//
|
|
28690
|
-
//
|
|
28691
|
-
// is a big amount of work/change.
|
|
28692
|
-
type) {
|
|
28687
|
+
function functionForType(type) {
|
|
28688
|
+
// @ts-expect-error: TypeScript doesn't know how to use deal with generics
|
|
28689
|
+
// and conditional types in this way.
|
|
28693
28690
|
return functionTypeMapping[type];
|
|
28694
28691
|
}
|
|
28695
28692
|
const getEquationString = props => {
|
|
@@ -28754,12 +28751,13 @@ const defaultPlotProps = (type, graph) => {
|
|
|
28754
28751
|
// widget before even reading the question; you can't lose, but you
|
|
28755
28752
|
// might get a free win.
|
|
28756
28753
|
const model = functionForType(type);
|
|
28754
|
+
const defaultAsymptote = "defaultAsymptote" in model ? model.defaultAsymptote : null;
|
|
28757
28755
|
const gridStep = [1, 1];
|
|
28758
28756
|
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type '[number, number]'.
|
|
28759
28757
|
const snapStep = Util.snapStepFromGridStep(gridStep);
|
|
28760
28758
|
return {
|
|
28761
28759
|
type,
|
|
28762
|
-
asymptote: maybePointsFromNormalized(
|
|
28760
|
+
asymptote: maybePointsFromNormalized(defaultAsymptote, graph.range, graph.step, snapStep),
|
|
28763
28761
|
coords: null
|
|
28764
28762
|
};
|
|
28765
28763
|
};
|
|
@@ -28819,8 +28817,19 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
28819
28817
|
typeToButton: typeToButton
|
|
28820
28818
|
});
|
|
28821
28819
|
|
|
28822
|
-
function
|
|
28823
|
-
if (
|
|
28820
|
+
function getCoefficientsByType(data) {
|
|
28821
|
+
if (data.type === "exponential" || data.type === "logarithm") {
|
|
28822
|
+
const grader = functionForType(data.type);
|
|
28823
|
+
return grader.getCoefficients(data.coords, data.asymptote);
|
|
28824
|
+
} else if (data.type === "linear" || data.type === "quadratic" || data.type === "absolute_value" || data.type === "sinusoid" || data.type === "tangent") {
|
|
28825
|
+
const grader = functionForType(data.type);
|
|
28826
|
+
return grader.getCoefficients(data.coords);
|
|
28827
|
+
} else {
|
|
28828
|
+
throw new perseusCore.PerseusError("Invalid grapher type", perseusCore.Errors.InvalidInput);
|
|
28829
|
+
}
|
|
28830
|
+
}
|
|
28831
|
+
function grapherValidator(userInput, rubric) {
|
|
28832
|
+
if (userInput.type !== rubric.correct.type) {
|
|
28824
28833
|
return {
|
|
28825
28834
|
type: "points",
|
|
28826
28835
|
earned: 0,
|
|
@@ -28830,7 +28839,7 @@ function grapherValidator(state, rubric) {
|
|
|
28830
28839
|
}
|
|
28831
28840
|
|
|
28832
28841
|
// We haven't moved the coords
|
|
28833
|
-
if (
|
|
28842
|
+
if (userInput.coords == null) {
|
|
28834
28843
|
return {
|
|
28835
28844
|
type: "invalid",
|
|
28836
28845
|
message: null
|
|
@@ -28838,11 +28847,9 @@ function grapherValidator(state, rubric) {
|
|
|
28838
28847
|
}
|
|
28839
28848
|
|
|
28840
28849
|
// Get new function handler for grading
|
|
28841
|
-
const grader = functionForType(
|
|
28842
|
-
const guessCoeffs =
|
|
28843
|
-
const correctCoeffs =
|
|
28844
|
-
// @ts-expect-error - TS(2339) - Property 'asymptote' does not exist on type '{ type: "absolute_value"; coords: [Coord, Coord]; }'
|
|
28845
|
-
rubric.correct.asymptote);
|
|
28850
|
+
const grader = functionForType(userInput.type);
|
|
28851
|
+
const guessCoeffs = getCoefficientsByType(userInput);
|
|
28852
|
+
const correctCoeffs = getCoefficientsByType(rubric.correct);
|
|
28846
28853
|
if (guessCoeffs == null || correctCoeffs == null) {
|
|
28847
28854
|
return {
|
|
28848
28855
|
type: "invalid",
|
|
@@ -39028,10 +39035,12 @@ function scoreMarker(marker) {
|
|
|
39028
39035
|
}
|
|
39029
39036
|
return score;
|
|
39030
39037
|
}
|
|
39031
|
-
|
|
39038
|
+
|
|
39039
|
+
// TODO(LEMS-2440): May need to pull answers out of PerseusLabelImageWidgetOptions[markers] for the rubric
|
|
39040
|
+
function labelImageValidator(userInput, rubric) {
|
|
39032
39041
|
let numAnswered = 0;
|
|
39033
39042
|
let numCorrect = 0;
|
|
39034
|
-
for (const marker of
|
|
39043
|
+
for (const marker of userInput.markers) {
|
|
39035
39044
|
const score = scoreMarker(marker);
|
|
39036
39045
|
if (score.hasAnswers) {
|
|
39037
39046
|
numAnswered++;
|
|
@@ -39042,7 +39051,7 @@ function labelImageValidator(state, rubric) {
|
|
|
39042
39051
|
}
|
|
39043
39052
|
|
|
39044
39053
|
// We expect all question markers to be answered before grading.
|
|
39045
|
-
if (numAnswered !==
|
|
39054
|
+
if (numAnswered !== userInput.markers.length) {
|
|
39046
39055
|
return {
|
|
39047
39056
|
type: "invalid",
|
|
39048
39057
|
message: null
|
|
@@ -39052,7 +39061,7 @@ function labelImageValidator(state, rubric) {
|
|
|
39052
39061
|
type: "points",
|
|
39053
39062
|
// Markers with no expected answers are graded as correct if user
|
|
39054
39063
|
// makes no answer selection.
|
|
39055
|
-
earned: numCorrect ===
|
|
39064
|
+
earned: numCorrect === userInput.markers.length ? 1 : 0,
|
|
39056
39065
|
total: 1,
|
|
39057
39066
|
message: null
|
|
39058
39067
|
};
|
|
@@ -39525,6 +39534,8 @@ class LabelImage extends React__namespace.Component {
|
|
|
39525
39534
|
markers
|
|
39526
39535
|
};
|
|
39527
39536
|
}
|
|
39537
|
+
|
|
39538
|
+
// TODO(LEMS-2544): Investigate impact on scoring; possibly pull out &/or remove rubric parameter.
|
|
39528
39539
|
showRationalesForCurrentlySelectedChoices(rubric) {
|
|
39529
39540
|
const {
|
|
39530
39541
|
markers
|
|
@@ -42409,17 +42420,23 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
42409
42420
|
const results = [];
|
|
42410
42421
|
|
|
42411
42422
|
// For convenience, extract some props into separate variables
|
|
42412
|
-
const
|
|
42413
|
-
|
|
42423
|
+
const {
|
|
42424
|
+
range,
|
|
42425
|
+
labelRange,
|
|
42426
|
+
labelStyle,
|
|
42427
|
+
labelTicks,
|
|
42428
|
+
tickStep,
|
|
42429
|
+
numDivisions
|
|
42430
|
+
} = props;
|
|
42414
42431
|
const leftLabel = labelRange[0] == null ? range[0] : labelRange[0];
|
|
42415
42432
|
const rightLabel = labelRange[1] == null ? range[1] : labelRange[1];
|
|
42416
42433
|
|
|
42417
42434
|
// Find base via GCD for non-reduced fractions
|
|
42418
42435
|
let base;
|
|
42419
|
-
if (
|
|
42436
|
+
if (labelStyle === "non-reduced") {
|
|
42420
42437
|
const fractions = [leftLabel, rightLabel];
|
|
42421
|
-
for (let i = 0; i <=
|
|
42422
|
-
const x = range[0] + i *
|
|
42438
|
+
for (let i = 0; i <= numDivisions; i++) {
|
|
42439
|
+
const x = range[0] + i * tickStep;
|
|
42423
42440
|
fractions.push(x);
|
|
42424
42441
|
}
|
|
42425
42442
|
const getDenom = x => kmath.number.toFraction(x)[1];
|
|
@@ -42428,30 +42445,42 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
42428
42445
|
} else {
|
|
42429
42446
|
base = undefined;
|
|
42430
42447
|
}
|
|
42431
|
-
|
|
42432
|
-
// Draw and save the tick marks and tick labels
|
|
42433
|
-
for (let i = 0; i <= props.numDivisions; i++) {
|
|
42434
|
-
const x = range[0] + i * props.tickStep;
|
|
42435
|
-
results.push(graphie.line([x, -0.2], [x, 0.2]));
|
|
42436
|
-
const labelTicks = props.labelTicks;
|
|
42437
|
-
if (labelTicks || props.labelStyle === "decimal ticks") {
|
|
42438
|
-
if (x === leftLabel || x === rightLabel) {
|
|
42439
|
-
results.push(graphie.style({
|
|
42440
|
-
color: KhanColors.BLUE
|
|
42441
|
-
}, () => _label(graphie, props.labelStyle, x, x, base)));
|
|
42442
|
-
} else {
|
|
42443
|
-
results.push(_label(graphie, props.labelStyle, x, x, base));
|
|
42444
|
-
}
|
|
42445
|
-
}
|
|
42446
|
-
}
|
|
42447
|
-
|
|
42448
|
-
// Render the labels' lines
|
|
42449
|
-
graphie.style({
|
|
42448
|
+
const highlightedLineStyle = {
|
|
42450
42449
|
stroke: KhanColors.BLUE,
|
|
42451
42450
|
strokeWidth: 3.5
|
|
42452
|
-
}
|
|
42453
|
-
|
|
42454
|
-
|
|
42451
|
+
};
|
|
42452
|
+
const highlightedTextStyle = {
|
|
42453
|
+
color: KhanColors.BLUE
|
|
42454
|
+
};
|
|
42455
|
+
|
|
42456
|
+
// Generate an array of tick numbers:
|
|
42457
|
+
// `Array(Math.round(numDivisions))` makes an array of null values - one for every division marker
|
|
42458
|
+
// `.keys()` gets the index values for each marker placeholder
|
|
42459
|
+
// `.map()` converts the index values into actual tick numbers
|
|
42460
|
+
// NOTE: 'numDivisions' can sometimes be a non-integer (i.e. 7.000001).
|
|
42461
|
+
// Using Math.round() to ensure that an integer is used in the Array setup.
|
|
42462
|
+
const initialTicks = [...Array(Math.round(numDivisions)).keys()].map(index => range[0] + index * tickStep);
|
|
42463
|
+
|
|
42464
|
+
// .sort() comparator
|
|
42465
|
+
const byNumericAscending = (a, b) => a - b;
|
|
42466
|
+
|
|
42467
|
+
// Ensure that any label markers and range endpoints are included in the array
|
|
42468
|
+
// Using `Set()` prevents duplication of tick numbers (and is quite performant)
|
|
42469
|
+
const allTicks = [...new Set([...initialTicks, leftLabel, rightLabel, ...range])].sort(byNumericAscending);
|
|
42470
|
+
|
|
42471
|
+
// Cycle through each tick number and add a tick line, and a label (if needed)
|
|
42472
|
+
allTicks.forEach(tick => {
|
|
42473
|
+
const tickIsHighlighted = tick === leftLabel || tick === rightLabel;
|
|
42474
|
+
const lineStyle = tickIsHighlighted ? highlightedLineStyle : null;
|
|
42475
|
+
const textStyle = tickIsHighlighted ? highlightedTextStyle : null;
|
|
42476
|
+
graphie.style(lineStyle, () => {
|
|
42477
|
+
results.push(graphie.line([tick, -0.2], [tick, 0.2]));
|
|
42478
|
+
});
|
|
42479
|
+
if (labelTicks || tickIsHighlighted || labelStyle === "decimal ticks") {
|
|
42480
|
+
graphie.style(textStyle, () => {
|
|
42481
|
+
results.push(_label(graphie, labelStyle, tick, tick, base));
|
|
42482
|
+
});
|
|
42483
|
+
}
|
|
42455
42484
|
});
|
|
42456
42485
|
return results;
|
|
42457
42486
|
});
|
|
@@ -43145,7 +43174,7 @@ class Orderer extends React__namespace.Component {
|
|
|
43145
43174
|
list.splice(index, 0, newCard);
|
|
43146
43175
|
}
|
|
43147
43176
|
this.props.onChange({
|
|
43148
|
-
// @ts-expect-error - TS2345 - Argument of type '{ current: any[]; }' is not assignable to parameter of type '{ hints?: readonly Hint[] | undefined; replace?: boolean | undefined; content?: string | undefined; widgets?:
|
|
43177
|
+
// @ts-expect-error - TS2345 - Argument of type '{ current: any[]; }' is not assignable to parameter of type '{ hints?: readonly Hint[] | undefined; replace?: boolean | undefined; content?: string | undefined; widgets?: PerseusWidgetsMap | undefined; images?: ImageDict | undefined; ... 13 more ...; plot?: any; }'.
|
|
43149
43178
|
current: list
|
|
43150
43179
|
});
|
|
43151
43180
|
this.setState({
|
|
@@ -43308,7 +43337,7 @@ class Orderer extends React__namespace.Component {
|
|
|
43308
43337
|
};
|
|
43309
43338
|
});
|
|
43310
43339
|
this.props.onChange({
|
|
43311
|
-
// @ts-expect-error - TS2345 - Argument of type '{ current: { content: string; }[]; }' is not assignable to parameter of type '{ hints?: readonly Hint[] | undefined; replace?: boolean | undefined; content?: string | undefined; widgets?:
|
|
43340
|
+
// @ts-expect-error - TS2345 - Argument of type '{ current: { content: string; }[]; }' is not assignable to parameter of type '{ hints?: readonly Hint[] | undefined; replace?: boolean | undefined; content?: string | undefined; widgets?: PerseusWidgetsMap | undefined; images?: ImageDict | undefined; ... 13 more ...; plot?: any; }'.
|
|
43312
43341
|
current: list
|
|
43313
43342
|
});
|
|
43314
43343
|
this.setState({
|
|
@@ -45156,7 +45185,7 @@ const init = function (options) {
|
|
|
45156
45185
|
|
|
45157
45186
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45158
45187
|
const libName = "@khanacademy/perseus";
|
|
45159
|
-
const libVersion = "
|
|
45188
|
+
const libVersion = "38.0.1";
|
|
45160
45189
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45161
45190
|
|
|
45162
45191
|
const apiVersion = {
|