@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/es/index.js
CHANGED
|
@@ -9967,9 +9967,6 @@ const setLabelMargins = function setLabelMargins(span, size) {
|
|
|
9967
9967
|
// Inherited line-height values can really mess up placement.
|
|
9968
9968
|
$container.css("line-height", "normal");
|
|
9969
9969
|
|
|
9970
|
-
// TODO: Verify that this change doesn't impact other graphies
|
|
9971
|
-
// Check the full list of consuming widgets.
|
|
9972
|
-
|
|
9973
9970
|
// If the change in line-height affected the height of the element,
|
|
9974
9971
|
// then the height used for calculations should be updated.
|
|
9975
9972
|
// This can happen when the first label in the container calls this method,
|
|
@@ -21679,7 +21676,7 @@ const answerTypes = {
|
|
|
21679
21676
|
forms: "pi"
|
|
21680
21677
|
}
|
|
21681
21678
|
};
|
|
21682
|
-
function inputNumberValidator(
|
|
21679
|
+
function inputNumberValidator(userInput, rubric, strings) {
|
|
21683
21680
|
if (rubric.answerType == null) {
|
|
21684
21681
|
rubric.answerType = "number";
|
|
21685
21682
|
}
|
|
@@ -21697,7 +21694,7 @@ function inputNumberValidator(state, rubric, strings) {
|
|
|
21697
21694
|
|
|
21698
21695
|
// We may have received TeX; try to parse it before grading.
|
|
21699
21696
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
21700
|
-
const currentValue = ParseTex$1(
|
|
21697
|
+
const currentValue = ParseTex$1(userInput.currentValue);
|
|
21701
21698
|
const result = val(currentValue);
|
|
21702
21699
|
|
|
21703
21700
|
// TODO(eater): Seems silly to translate result to this invalid/points
|
|
@@ -28275,7 +28272,7 @@ const Exponential = _.extend({}, PlotDefaults, {
|
|
|
28275
28272
|
* coordinate, and `false` uses oldCoord as the resulting coordinate.
|
|
28276
28273
|
*/
|
|
28277
28274
|
extraCoordConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
|
|
28278
|
-
const y =
|
|
28275
|
+
const y = asymptote[0][1];
|
|
28279
28276
|
return _.all(coords, coord => coord[1] !== y);
|
|
28280
28277
|
},
|
|
28281
28278
|
extraAsymptoteConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
|
|
@@ -28299,7 +28296,7 @@ const Exponential = _.extend({}, PlotDefaults, {
|
|
|
28299
28296
|
getCoefficients: function (coords, asymptote) {
|
|
28300
28297
|
const p1 = coords[0];
|
|
28301
28298
|
const p2 = coords[1];
|
|
28302
|
-
const c =
|
|
28299
|
+
const c = asymptote[0][1];
|
|
28303
28300
|
const b = Math.log((p1[1] - c) / (p2[1] - c)) / (p1[0] - p2[0]);
|
|
28304
28301
|
const a = (p1[1] - c) / Math.exp(b * p1[0]);
|
|
28305
28302
|
return [a, b, c];
|
|
@@ -28326,7 +28323,7 @@ const Logarithm = _.extend({}, PlotDefaults, {
|
|
|
28326
28323
|
defaultCoords: [[0.55, 0.5], [0.75, 0.75]],
|
|
28327
28324
|
defaultAsymptote: [[0.5, 0], [0.5, 1.0]],
|
|
28328
28325
|
extraCoordConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
|
|
28329
|
-
const x =
|
|
28326
|
+
const x = asymptote[0][0];
|
|
28330
28327
|
return _.all(coords, coord => coord[0] !== x) && coords[0][1] !== coords[1][1];
|
|
28331
28328
|
},
|
|
28332
28329
|
extraAsymptoteConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
|
|
@@ -28354,10 +28351,12 @@ const Logarithm = _.extend({}, PlotDefaults, {
|
|
|
28354
28351
|
// logic between the two 'models'.
|
|
28355
28352
|
const flip = coord => [coord[1], coord[0]];
|
|
28356
28353
|
const inverseCoeffs = Exponential.getCoefficients(_.map(coords, flip), _.map(asymptote, flip));
|
|
28357
|
-
|
|
28358
|
-
|
|
28359
|
-
|
|
28360
|
-
|
|
28354
|
+
if (inverseCoeffs) {
|
|
28355
|
+
const c = -inverseCoeffs[2] / inverseCoeffs[0];
|
|
28356
|
+
const b = 1 / inverseCoeffs[0];
|
|
28357
|
+
const a = 1 / inverseCoeffs[1];
|
|
28358
|
+
return [a, b, c];
|
|
28359
|
+
}
|
|
28361
28360
|
},
|
|
28362
28361
|
getFunctionForCoeffs: function (coeffs, x, asymptote) {
|
|
28363
28362
|
const a = coeffs[0],
|
|
@@ -28421,11 +28420,9 @@ const functionTypeMapping = {
|
|
|
28421
28420
|
absolute_value: AbsoluteValue
|
|
28422
28421
|
};
|
|
28423
28422
|
const allTypes = _.keys(functionTypeMapping);
|
|
28424
|
-
function functionForType(
|
|
28425
|
-
//
|
|
28426
|
-
//
|
|
28427
|
-
// is a big amount of work/change.
|
|
28428
|
-
type) {
|
|
28423
|
+
function functionForType(type) {
|
|
28424
|
+
// @ts-expect-error: TypeScript doesn't know how to use deal with generics
|
|
28425
|
+
// and conditional types in this way.
|
|
28429
28426
|
return functionTypeMapping[type];
|
|
28430
28427
|
}
|
|
28431
28428
|
const getEquationString = props => {
|
|
@@ -28490,12 +28487,13 @@ const defaultPlotProps = (type, graph) => {
|
|
|
28490
28487
|
// widget before even reading the question; you can't lose, but you
|
|
28491
28488
|
// might get a free win.
|
|
28492
28489
|
const model = functionForType(type);
|
|
28490
|
+
const defaultAsymptote = "defaultAsymptote" in model ? model.defaultAsymptote : null;
|
|
28493
28491
|
const gridStep = [1, 1];
|
|
28494
28492
|
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type '[number, number]'.
|
|
28495
28493
|
const snapStep = Util.snapStepFromGridStep(gridStep);
|
|
28496
28494
|
return {
|
|
28497
28495
|
type,
|
|
28498
|
-
asymptote: maybePointsFromNormalized(
|
|
28496
|
+
asymptote: maybePointsFromNormalized(defaultAsymptote, graph.range, graph.step, snapStep),
|
|
28499
28497
|
coords: null
|
|
28500
28498
|
};
|
|
28501
28499
|
};
|
|
@@ -28555,8 +28553,19 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
28555
28553
|
typeToButton: typeToButton
|
|
28556
28554
|
});
|
|
28557
28555
|
|
|
28558
|
-
function
|
|
28559
|
-
if (
|
|
28556
|
+
function getCoefficientsByType(data) {
|
|
28557
|
+
if (data.type === "exponential" || data.type === "logarithm") {
|
|
28558
|
+
const grader = functionForType(data.type);
|
|
28559
|
+
return grader.getCoefficients(data.coords, data.asymptote);
|
|
28560
|
+
} else if (data.type === "linear" || data.type === "quadratic" || data.type === "absolute_value" || data.type === "sinusoid" || data.type === "tangent") {
|
|
28561
|
+
const grader = functionForType(data.type);
|
|
28562
|
+
return grader.getCoefficients(data.coords);
|
|
28563
|
+
} else {
|
|
28564
|
+
throw new PerseusError("Invalid grapher type", Errors.InvalidInput);
|
|
28565
|
+
}
|
|
28566
|
+
}
|
|
28567
|
+
function grapherValidator(userInput, rubric) {
|
|
28568
|
+
if (userInput.type !== rubric.correct.type) {
|
|
28560
28569
|
return {
|
|
28561
28570
|
type: "points",
|
|
28562
28571
|
earned: 0,
|
|
@@ -28566,7 +28575,7 @@ function grapherValidator(state, rubric) {
|
|
|
28566
28575
|
}
|
|
28567
28576
|
|
|
28568
28577
|
// We haven't moved the coords
|
|
28569
|
-
if (
|
|
28578
|
+
if (userInput.coords == null) {
|
|
28570
28579
|
return {
|
|
28571
28580
|
type: "invalid",
|
|
28572
28581
|
message: null
|
|
@@ -28574,11 +28583,9 @@ function grapherValidator(state, rubric) {
|
|
|
28574
28583
|
}
|
|
28575
28584
|
|
|
28576
28585
|
// Get new function handler for grading
|
|
28577
|
-
const grader = functionForType(
|
|
28578
|
-
const guessCoeffs =
|
|
28579
|
-
const correctCoeffs =
|
|
28580
|
-
// @ts-expect-error - TS(2339) - Property 'asymptote' does not exist on type '{ type: "absolute_value"; coords: [Coord, Coord]; }'
|
|
28581
|
-
rubric.correct.asymptote);
|
|
28586
|
+
const grader = functionForType(userInput.type);
|
|
28587
|
+
const guessCoeffs = getCoefficientsByType(userInput);
|
|
28588
|
+
const correctCoeffs = getCoefficientsByType(rubric.correct);
|
|
28582
28589
|
if (guessCoeffs == null || correctCoeffs == null) {
|
|
28583
28590
|
return {
|
|
28584
28591
|
type: "invalid",
|
|
@@ -38686,10 +38693,12 @@ function scoreMarker(marker) {
|
|
|
38686
38693
|
}
|
|
38687
38694
|
return score;
|
|
38688
38695
|
}
|
|
38689
|
-
|
|
38696
|
+
|
|
38697
|
+
// TODO(LEMS-2440): May need to pull answers out of PerseusLabelImageWidgetOptions[markers] for the rubric
|
|
38698
|
+
function labelImageValidator(userInput, rubric) {
|
|
38690
38699
|
let numAnswered = 0;
|
|
38691
38700
|
let numCorrect = 0;
|
|
38692
|
-
for (const marker of
|
|
38701
|
+
for (const marker of userInput.markers) {
|
|
38693
38702
|
const score = scoreMarker(marker);
|
|
38694
38703
|
if (score.hasAnswers) {
|
|
38695
38704
|
numAnswered++;
|
|
@@ -38700,7 +38709,7 @@ function labelImageValidator(state, rubric) {
|
|
|
38700
38709
|
}
|
|
38701
38710
|
|
|
38702
38711
|
// We expect all question markers to be answered before grading.
|
|
38703
|
-
if (numAnswered !==
|
|
38712
|
+
if (numAnswered !== userInput.markers.length) {
|
|
38704
38713
|
return {
|
|
38705
38714
|
type: "invalid",
|
|
38706
38715
|
message: null
|
|
@@ -38710,7 +38719,7 @@ function labelImageValidator(state, rubric) {
|
|
|
38710
38719
|
type: "points",
|
|
38711
38720
|
// Markers with no expected answers are graded as correct if user
|
|
38712
38721
|
// makes no answer selection.
|
|
38713
|
-
earned: numCorrect ===
|
|
38722
|
+
earned: numCorrect === userInput.markers.length ? 1 : 0,
|
|
38714
38723
|
total: 1,
|
|
38715
38724
|
message: null
|
|
38716
38725
|
};
|
|
@@ -39179,6 +39188,8 @@ class LabelImage extends React.Component {
|
|
|
39179
39188
|
markers
|
|
39180
39189
|
};
|
|
39181
39190
|
}
|
|
39191
|
+
|
|
39192
|
+
// TODO(LEMS-2544): Investigate impact on scoring; possibly pull out &/or remove rubric parameter.
|
|
39182
39193
|
showRationalesForCurrentlySelectedChoices(rubric) {
|
|
39183
39194
|
const {
|
|
39184
39195
|
markers
|
|
@@ -42060,17 +42071,23 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
42060
42071
|
const results = [];
|
|
42061
42072
|
|
|
42062
42073
|
// For convenience, extract some props into separate variables
|
|
42063
|
-
const
|
|
42064
|
-
|
|
42074
|
+
const {
|
|
42075
|
+
range,
|
|
42076
|
+
labelRange,
|
|
42077
|
+
labelStyle,
|
|
42078
|
+
labelTicks,
|
|
42079
|
+
tickStep,
|
|
42080
|
+
numDivisions
|
|
42081
|
+
} = props;
|
|
42065
42082
|
const leftLabel = labelRange[0] == null ? range[0] : labelRange[0];
|
|
42066
42083
|
const rightLabel = labelRange[1] == null ? range[1] : labelRange[1];
|
|
42067
42084
|
|
|
42068
42085
|
// Find base via GCD for non-reduced fractions
|
|
42069
42086
|
let base;
|
|
42070
|
-
if (
|
|
42087
|
+
if (labelStyle === "non-reduced") {
|
|
42071
42088
|
const fractions = [leftLabel, rightLabel];
|
|
42072
|
-
for (let i = 0; i <=
|
|
42073
|
-
const x = range[0] + i *
|
|
42089
|
+
for (let i = 0; i <= numDivisions; i++) {
|
|
42090
|
+
const x = range[0] + i * tickStep;
|
|
42074
42091
|
fractions.push(x);
|
|
42075
42092
|
}
|
|
42076
42093
|
const getDenom = x => number.toFraction(x)[1];
|
|
@@ -42079,30 +42096,42 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
42079
42096
|
} else {
|
|
42080
42097
|
base = undefined;
|
|
42081
42098
|
}
|
|
42082
|
-
|
|
42083
|
-
// Draw and save the tick marks and tick labels
|
|
42084
|
-
for (let i = 0; i <= props.numDivisions; i++) {
|
|
42085
|
-
const x = range[0] + i * props.tickStep;
|
|
42086
|
-
results.push(graphie.line([x, -0.2], [x, 0.2]));
|
|
42087
|
-
const labelTicks = props.labelTicks;
|
|
42088
|
-
if (labelTicks || props.labelStyle === "decimal ticks") {
|
|
42089
|
-
if (x === leftLabel || x === rightLabel) {
|
|
42090
|
-
results.push(graphie.style({
|
|
42091
|
-
color: KhanColors.BLUE
|
|
42092
|
-
}, () => _label(graphie, props.labelStyle, x, x, base)));
|
|
42093
|
-
} else {
|
|
42094
|
-
results.push(_label(graphie, props.labelStyle, x, x, base));
|
|
42095
|
-
}
|
|
42096
|
-
}
|
|
42097
|
-
}
|
|
42098
|
-
|
|
42099
|
-
// Render the labels' lines
|
|
42100
|
-
graphie.style({
|
|
42099
|
+
const highlightedLineStyle = {
|
|
42101
42100
|
stroke: KhanColors.BLUE,
|
|
42102
42101
|
strokeWidth: 3.5
|
|
42103
|
-
}
|
|
42104
|
-
|
|
42105
|
-
|
|
42102
|
+
};
|
|
42103
|
+
const highlightedTextStyle = {
|
|
42104
|
+
color: KhanColors.BLUE
|
|
42105
|
+
};
|
|
42106
|
+
|
|
42107
|
+
// Generate an array of tick numbers:
|
|
42108
|
+
// `Array(Math.round(numDivisions))` makes an array of null values - one for every division marker
|
|
42109
|
+
// `.keys()` gets the index values for each marker placeholder
|
|
42110
|
+
// `.map()` converts the index values into actual tick numbers
|
|
42111
|
+
// NOTE: 'numDivisions' can sometimes be a non-integer (i.e. 7.000001).
|
|
42112
|
+
// Using Math.round() to ensure that an integer is used in the Array setup.
|
|
42113
|
+
const initialTicks = [...Array(Math.round(numDivisions)).keys()].map(index => range[0] + index * tickStep);
|
|
42114
|
+
|
|
42115
|
+
// .sort() comparator
|
|
42116
|
+
const byNumericAscending = (a, b) => a - b;
|
|
42117
|
+
|
|
42118
|
+
// Ensure that any label markers and range endpoints are included in the array
|
|
42119
|
+
// Using `Set()` prevents duplication of tick numbers (and is quite performant)
|
|
42120
|
+
const allTicks = [...new Set([...initialTicks, leftLabel, rightLabel, ...range])].sort(byNumericAscending);
|
|
42121
|
+
|
|
42122
|
+
// Cycle through each tick number and add a tick line, and a label (if needed)
|
|
42123
|
+
allTicks.forEach(tick => {
|
|
42124
|
+
const tickIsHighlighted = tick === leftLabel || tick === rightLabel;
|
|
42125
|
+
const lineStyle = tickIsHighlighted ? highlightedLineStyle : null;
|
|
42126
|
+
const textStyle = tickIsHighlighted ? highlightedTextStyle : null;
|
|
42127
|
+
graphie.style(lineStyle, () => {
|
|
42128
|
+
results.push(graphie.line([tick, -0.2], [tick, 0.2]));
|
|
42129
|
+
});
|
|
42130
|
+
if (labelTicks || tickIsHighlighted || labelStyle === "decimal ticks") {
|
|
42131
|
+
graphie.style(textStyle, () => {
|
|
42132
|
+
results.push(_label(graphie, labelStyle, tick, tick, base));
|
|
42133
|
+
});
|
|
42134
|
+
}
|
|
42106
42135
|
});
|
|
42107
42136
|
return results;
|
|
42108
42137
|
});
|
|
@@ -42781,7 +42810,7 @@ class Orderer extends React.Component {
|
|
|
42781
42810
|
list.splice(index, 0, newCard);
|
|
42782
42811
|
}
|
|
42783
42812
|
this.props.onChange({
|
|
42784
|
-
// @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?:
|
|
42813
|
+
// @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; }'.
|
|
42785
42814
|
current: list
|
|
42786
42815
|
});
|
|
42787
42816
|
this.setState({
|
|
@@ -42943,7 +42972,7 @@ class Orderer extends React.Component {
|
|
|
42943
42972
|
};
|
|
42944
42973
|
});
|
|
42945
42974
|
this.props.onChange({
|
|
42946
|
-
// @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?:
|
|
42975
|
+
// @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; }'.
|
|
42947
42976
|
current: list
|
|
42948
42977
|
});
|
|
42949
42978
|
this.setState({
|
|
@@ -44804,7 +44833,7 @@ const init = function init(options) {
|
|
|
44804
44833
|
|
|
44805
44834
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44806
44835
|
const libName = "@khanacademy/perseus";
|
|
44807
|
-
const libVersion = "
|
|
44836
|
+
const libVersion = "38.0.1";
|
|
44808
44837
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44809
44838
|
|
|
44810
44839
|
const apiVersion = {
|