@khanacademy/perseus 23.0.0 → 23.1.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/es/index.js +133 -48
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +135 -47
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +2 -0
- package/dist/perseus-types.d.ts +9 -1
- package/dist/server-item-renderer.d.ts +2 -0
- package/dist/types.d.ts +2 -2
- package/dist/widgets/expression.d.ts +12 -0
- package/dist/widgets/interactive-graphs/graphs/index.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/sinusoid.d.ts +14 -0
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +6 -1
- package/dist/widgets/interactive-graphs/locked-circle.d.ts +4 -0
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +1 -1
- package/dist/widgets/interactive-graphs/types.d.ts +5 -1
- package/dist/widgets/label-image.d.ts +4 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ export type { ILogger, LogErrorOptions, ErrorKind } from "./logging/log";
|
|
|
87
87
|
export type { ServerItemRenderer as ServerItemRendererComponent } from "./server-item-renderer";
|
|
88
88
|
export type { Alignment, APIOptions, APIOptionsWithDefaults, ChangeHandler, DeviceType, DomInsertCheckFn, EditorMode, FocusPath, Hint, ImageDict, ImageUploader, JiptLabelStore, JiptRenderer, PerseusDependencies, PerseusScore, Version, VideoData, VideoKind, WidgetDict, WidgetExports, } from "./types";
|
|
89
89
|
export type { ParsedValue } from "./util";
|
|
90
|
-
export type { LockedFigure, LockedFigureColor, LockedFigureType, LockedPointType, LockedLineType, PerseusGraphType, PerseusAnswerArea, PerseusExpressionWidgetOptions, Range, Size, MathFormat, InputNumberWidget, // TODO(jeremy): remove?
|
|
90
|
+
export type { LockedFigure, LockedFigureColor, LockedFigureType, LockedPointType, LockedLineType, LockedCircleType, PerseusGraphType, PerseusAnswerArea, PerseusExpressionWidgetOptions, Range, Size, MathFormat, InputNumberWidget, // TODO(jeremy): remove?
|
|
91
91
|
PerseusImageBackground, PerseusInputNumberWidgetOptions, PerseusInteractiveGraphWidgetOptions, PerseusItem, PerseusPlotterWidgetOptions, PerseusPythonProgramWidgetOptions, PerseusRadioWidgetOptions, PerseusExampleWidgetOptions, PerseusExampleGraphieWidgetOptions, PerseusSimpleMarkdownTesterWidgetOptions, PerseusRenderer, PerseusWidget, PerseusWidgetsMap, } from "./perseus-types";
|
|
92
92
|
export type { Coord } from "./interactive2/types";
|
|
93
93
|
export type { MarkerType } from "./widgets/label-image/types";
|
package/dist/index.js
CHANGED
|
@@ -17598,16 +17598,8 @@ class SvgImage extends React__namespace.Component {
|
|
|
17598
17598
|
// Props to send to all images
|
|
17599
17599
|
const imageProps = {
|
|
17600
17600
|
alt: this.props.alt,
|
|
17601
|
-
title: this.props.title
|
|
17602
|
-
tabIndex: "0"
|
|
17601
|
+
title: this.props.title
|
|
17603
17602
|
};
|
|
17604
|
-
// If alt text is present, AND overrideAriaHidden is true,
|
|
17605
|
-
// include aria-hidden to avoid screen reader from crawling texts
|
|
17606
|
-
// both from the div meant for screen readers (see svg-image.jsx)
|
|
17607
|
-
// and alt attribute.
|
|
17608
|
-
if (this.props.alt && this.props.overrideAriaHidden) {
|
|
17609
|
-
imageProps["aria-hidden"] = true;
|
|
17610
|
-
}
|
|
17611
17603
|
const width = this.props.width && this.props.width * this.props.scale;
|
|
17612
17604
|
const height = this.props.height && this.props.height * this.props.scale;
|
|
17613
17605
|
const dimensions = {
|
|
@@ -27649,7 +27641,7 @@ class ImageWidget extends React__namespace.Component {
|
|
|
27649
27641
|
|
|
27650
27642
|
render() {
|
|
27651
27643
|
let image;
|
|
27652
|
-
|
|
27644
|
+
const alt = this.props.caption === this.props.alt ? "" : this.props.alt;
|
|
27653
27645
|
const {
|
|
27654
27646
|
apiOptions
|
|
27655
27647
|
} = this.props;
|
|
@@ -27662,29 +27654,7 @@ class ImageWidget extends React__namespace.Component {
|
|
|
27662
27654
|
} = _ref;
|
|
27663
27655
|
return /*#__PURE__*/React__namespace.createElement(SvgImage, {
|
|
27664
27656
|
src: url,
|
|
27665
|
-
alt:
|
|
27666
|
-
/* alt text is formatted in a sr-only
|
|
27667
|
-
div next to the image in addition to
|
|
27668
|
-
the alt attribute.
|
|
27669
|
-
If there is no alt text at all,
|
|
27670
|
-
we don't put an alt attribute on
|
|
27671
|
-
the image, so that screen readers
|
|
27672
|
-
know there's something they can't
|
|
27673
|
-
read there :(.
|
|
27674
|
-
NOTE: React <=0.13 (maybe later)
|
|
27675
|
-
has a bug where it won't ever
|
|
27676
|
-
remove an attribute, so if this
|
|
27677
|
-
alt node is ever defined it's
|
|
27678
|
-
not removed. This is sort of
|
|
27679
|
-
dangerous, but we usually re-key
|
|
27680
|
-
new renderers so that they're
|
|
27681
|
-
rendered from scratch anyways,
|
|
27682
|
-
so this shouldn't be a problem
|
|
27683
|
-
in practice right now, although
|
|
27684
|
-
it will exhibit weird behaviour
|
|
27685
|
-
while editing. */
|
|
27686
|
-
this.props.alt,
|
|
27687
|
-
overrideAriaHidden: true,
|
|
27657
|
+
alt: alt,
|
|
27688
27658
|
width: backgroundImage.width,
|
|
27689
27659
|
height: backgroundImage.height,
|
|
27690
27660
|
preloader: apiOptions.imagePreloader,
|
|
@@ -27701,16 +27671,6 @@ class ImageWidget extends React__namespace.Component {
|
|
|
27701
27671
|
});
|
|
27702
27672
|
});
|
|
27703
27673
|
}
|
|
27704
|
-
if (this.props.alt) {
|
|
27705
|
-
alt = /*#__PURE__*/React__namespace.createElement("span", {
|
|
27706
|
-
className: "perseus-sr-only"
|
|
27707
|
-
}, /*#__PURE__*/React__namespace.createElement(Renderer, {
|
|
27708
|
-
content: this.props.alt,
|
|
27709
|
-
apiOptions: apiOptions,
|
|
27710
|
-
linterContext: this.props.linterContext,
|
|
27711
|
-
strings: this.context.strings
|
|
27712
|
-
}));
|
|
27713
|
-
}
|
|
27714
27674
|
|
|
27715
27675
|
// For mobile we combine an image's title and caption.
|
|
27716
27676
|
if (apiOptions.isMobile) {
|
|
@@ -27777,7 +27737,7 @@ class ImageWidget extends React__namespace.Component {
|
|
|
27777
27737
|
style: {
|
|
27778
27738
|
maxWidth: backgroundImage.width
|
|
27779
27739
|
}
|
|
27780
|
-
}, image,
|
|
27740
|
+
}, image, titleAndCaption);
|
|
27781
27741
|
}
|
|
27782
27742
|
let title;
|
|
27783
27743
|
let caption;
|
|
@@ -27809,7 +27769,7 @@ class ImageWidget extends React__namespace.Component {
|
|
|
27809
27769
|
style: {
|
|
27810
27770
|
maxWidth: backgroundImage.width
|
|
27811
27771
|
}
|
|
27812
|
-
}, title, image,
|
|
27772
|
+
}, title, image, caption);
|
|
27813
27773
|
}
|
|
27814
27774
|
static validate(userInput, rubric) {
|
|
27815
27775
|
return {
|
|
@@ -28964,6 +28924,28 @@ const lockedFigureColors = {
|
|
|
28964
28924
|
};
|
|
28965
28925
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
28966
28926
|
|
|
28927
|
+
const LockedCircle = props => {
|
|
28928
|
+
const {
|
|
28929
|
+
center,
|
|
28930
|
+
radius,
|
|
28931
|
+
color,
|
|
28932
|
+
fillStyle,
|
|
28933
|
+
strokeStyle
|
|
28934
|
+
} = props;
|
|
28935
|
+
const fill = {
|
|
28936
|
+
none: 0,
|
|
28937
|
+
solid: 1,
|
|
28938
|
+
translucent: 0.4
|
|
28939
|
+
};
|
|
28940
|
+
return /*#__PURE__*/React__namespace.createElement(mafs.Circle, {
|
|
28941
|
+
center: center,
|
|
28942
|
+
radius: radius,
|
|
28943
|
+
fillOpacity: fill[fillStyle],
|
|
28944
|
+
strokeStyle: strokeStyle,
|
|
28945
|
+
color: lockedFigureColors[color]
|
|
28946
|
+
});
|
|
28947
|
+
};
|
|
28948
|
+
|
|
28967
28949
|
function pathBuilder() {
|
|
28968
28950
|
return new PathBuilder();
|
|
28969
28951
|
}
|
|
@@ -29232,6 +29214,10 @@ const GraphLockedLayer = props => {
|
|
|
29232
29214
|
key: `line-${index}`,
|
|
29233
29215
|
range: props.range
|
|
29234
29216
|
}, figure));
|
|
29217
|
+
case "circle":
|
|
29218
|
+
return /*#__PURE__*/React__namespace.createElement(LockedCircle, _extends({
|
|
29219
|
+
key: `circle-${index}`
|
|
29220
|
+
}, figure));
|
|
29235
29221
|
default:
|
|
29236
29222
|
/**
|
|
29237
29223
|
* Devlopment-time future-proofing: This should
|
|
@@ -30043,8 +30029,13 @@ function initializeGraphState(params) {
|
|
|
30043
30029
|
type: graph.type,
|
|
30044
30030
|
coords: getQuadraticCoords(graph, range, step)
|
|
30045
30031
|
};
|
|
30046
|
-
case "angle":
|
|
30047
30032
|
case "sinusoid":
|
|
30033
|
+
return {
|
|
30034
|
+
...shared,
|
|
30035
|
+
type: graph.type,
|
|
30036
|
+
coords: getSinusoidCoords(graph, range, step)
|
|
30037
|
+
};
|
|
30038
|
+
case "angle":
|
|
30048
30039
|
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
30049
30040
|
default:
|
|
30050
30041
|
throw new wonderStuffCore.UnreachableCaseError(graph);
|
|
@@ -30148,6 +30139,12 @@ function getGradableGraph(state, initialGraph) {
|
|
|
30148
30139
|
coords: state.coords
|
|
30149
30140
|
};
|
|
30150
30141
|
}
|
|
30142
|
+
if (state.type === "sinusoid" && initialGraph.type === "sinusoid") {
|
|
30143
|
+
return {
|
|
30144
|
+
...initialGraph,
|
|
30145
|
+
coords: state.coords
|
|
30146
|
+
};
|
|
30147
|
+
}
|
|
30151
30148
|
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
30152
30149
|
}
|
|
30153
30150
|
const getDefaultSegments = _ref2 => {
|
|
@@ -30227,6 +30224,11 @@ const getPolygonCoords = _ref4 => {
|
|
|
30227
30224
|
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
30228
30225
|
return coords;
|
|
30229
30226
|
};
|
|
30227
|
+
const getSinusoidCoords = (graph, range, step) => {
|
|
30228
|
+
let coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
30229
|
+
coords = normalizePoints(range, step, coords, true);
|
|
30230
|
+
return coords;
|
|
30231
|
+
};
|
|
30230
30232
|
const getQuadraticCoords = (graph, range, step) => {
|
|
30231
30233
|
let coords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
30232
30234
|
coords = normalizePoints(range, step, coords, true);
|
|
@@ -30417,6 +30419,62 @@ const getQuadraticCoefficients = coords => {
|
|
|
30417
30419
|
return [a, b, c];
|
|
30418
30420
|
};
|
|
30419
30421
|
|
|
30422
|
+
function SinusoidGraph(props) {
|
|
30423
|
+
const {
|
|
30424
|
+
dispatch,
|
|
30425
|
+
graphState
|
|
30426
|
+
} = props;
|
|
30427
|
+
|
|
30428
|
+
// Destructure the coordinates from the graph state
|
|
30429
|
+
// Note: The order of the coordinates is important:
|
|
30430
|
+
// The coords[0] is the root and the coords[1] is the first peak
|
|
30431
|
+
const {
|
|
30432
|
+
coords
|
|
30433
|
+
} = graphState;
|
|
30434
|
+
|
|
30435
|
+
// Get the coefficients for calculating the quadratic equation
|
|
30436
|
+
const coeffs = getSinusoidCoefficients(coords);
|
|
30437
|
+
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, {
|
|
30438
|
+
y: x => computeSine(x, coeffs),
|
|
30439
|
+
color: wonderBlocksTokens.color.blue
|
|
30440
|
+
}), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
30441
|
+
key: "point-" + i,
|
|
30442
|
+
point: coord,
|
|
30443
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
30444
|
+
})));
|
|
30445
|
+
}
|
|
30446
|
+
|
|
30447
|
+
// Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
|
|
30448
|
+
const computeSine = function (x,
|
|
30449
|
+
// x-coordinate
|
|
30450
|
+
sinusoidCoefficients) {
|
|
30451
|
+
// Break down the coefficients for the sine function to improve readability
|
|
30452
|
+
const {
|
|
30453
|
+
amplitude: a,
|
|
30454
|
+
angularFrequency: b,
|
|
30455
|
+
phase: c,
|
|
30456
|
+
verticalOffset: d
|
|
30457
|
+
} = sinusoidCoefficients;
|
|
30458
|
+
return a * Math.sin(b * x - c) + d;
|
|
30459
|
+
};
|
|
30460
|
+
const getSinusoidCoefficients = coords => {
|
|
30461
|
+
// It's assumed that p1 is the root and p2 is the first peak
|
|
30462
|
+
const p1 = coords[0];
|
|
30463
|
+
const p2 = coords[1];
|
|
30464
|
+
|
|
30465
|
+
// Resulting coefficients are canonical for this sine curve
|
|
30466
|
+
const amplitude = p2[1] - p1[1];
|
|
30467
|
+
const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
|
|
30468
|
+
const phase = p1[0] * angularFrequency;
|
|
30469
|
+
const verticalOffset = p1[1];
|
|
30470
|
+
return {
|
|
30471
|
+
amplitude,
|
|
30472
|
+
angularFrequency,
|
|
30473
|
+
phase,
|
|
30474
|
+
verticalOffset
|
|
30475
|
+
};
|
|
30476
|
+
};
|
|
30477
|
+
|
|
30420
30478
|
// Universal padding for the axis ticks to ensure that the labels
|
|
30421
30479
|
// have enough space to render without overlapping the tick lines
|
|
30422
30480
|
const tickPadding = 5;
|
|
@@ -31550,6 +31608,7 @@ function doMoveControlPoint(state, action) {
|
|
|
31550
31608
|
case "point":
|
|
31551
31609
|
case "polygon":
|
|
31552
31610
|
case "quadratic":
|
|
31611
|
+
case "sinusoid":
|
|
31553
31612
|
throw new Error(`Don't use moveControlPoint for ${state.type} graphs. Use movePoint instead!`);
|
|
31554
31613
|
default:
|
|
31555
31614
|
throw new wonderStuffCore.UnreachableCaseError(state);
|
|
@@ -31639,6 +31698,30 @@ function doMovePoint(state, action) {
|
|
|
31639
31698
|
})
|
|
31640
31699
|
};
|
|
31641
31700
|
}
|
|
31701
|
+
case "sinusoid":
|
|
31702
|
+
{
|
|
31703
|
+
// First, we need to verify that the new coordinates are not on the same vertical line
|
|
31704
|
+
// If they are, we don't want to move the point
|
|
31705
|
+
const destination = action.destination;
|
|
31706
|
+
const newCoords = [...state.coords];
|
|
31707
|
+
newCoords[action.index] = action.destination;
|
|
31708
|
+
if (newCoords[0][0] === newCoords[1][0]) {
|
|
31709
|
+
return state;
|
|
31710
|
+
}
|
|
31711
|
+
return {
|
|
31712
|
+
...state,
|
|
31713
|
+
hasBeenInteractedWith: true,
|
|
31714
|
+
coords: setAtIndex({
|
|
31715
|
+
array: state.coords,
|
|
31716
|
+
index: action.index,
|
|
31717
|
+
newValue: snap(state.snapStep, bound$1({
|
|
31718
|
+
snapStep: state.snapStep,
|
|
31719
|
+
range: state.range,
|
|
31720
|
+
point: destination
|
|
31721
|
+
}))
|
|
31722
|
+
})
|
|
31723
|
+
};
|
|
31724
|
+
}
|
|
31642
31725
|
case "quadratic":
|
|
31643
31726
|
{
|
|
31644
31727
|
return {
|
|
@@ -31883,6 +31966,11 @@ const renderGraph = props => {
|
|
|
31883
31966
|
graphState: state,
|
|
31884
31967
|
dispatch: dispatch
|
|
31885
31968
|
});
|
|
31969
|
+
case "sinusoid":
|
|
31970
|
+
return /*#__PURE__*/React__namespace.createElement(SinusoidGraph, {
|
|
31971
|
+
graphState: state,
|
|
31972
|
+
dispatch: dispatch
|
|
31973
|
+
});
|
|
31886
31974
|
default:
|
|
31887
31975
|
return new wonderStuffCore.UnreachableCaseError(type);
|
|
31888
31976
|
}
|
|
@@ -44096,7 +44184,7 @@ const init = function (options) {
|
|
|
44096
44184
|
|
|
44097
44185
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44098
44186
|
const libName = "@khanacademy/perseus";
|
|
44099
|
-
const libVersion = "23.
|
|
44187
|
+
const libVersion = "23.1.0";
|
|
44100
44188
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44101
44189
|
|
|
44102
44190
|
const apiVersion = {
|