@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/es/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
|
|
|
29
29
|
import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
|
|
30
30
|
import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
|
|
31
31
|
import debounce$2 from 'lodash.debounce';
|
|
32
|
-
import { vec, Line as Line$4, Point as Point$2, useMovable, Text as Text$1, Polygon, Plot as Plot$3, Coordinates, Mafs } from 'mafs';
|
|
32
|
+
import { vec, Circle as Circle$1, Line as Line$4, Point as Point$2, useMovable, Text as Text$1, Polygon, Plot as Plot$3, Coordinates, Mafs } from 'mafs';
|
|
33
33
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
34
34
|
import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
35
35
|
import questionIcon from '@phosphor-icons/core/regular/question.svg';
|
|
@@ -17430,16 +17430,8 @@ class SvgImage extends React.Component {
|
|
|
17430
17430
|
// Props to send to all images
|
|
17431
17431
|
const imageProps = {
|
|
17432
17432
|
alt: this.props.alt,
|
|
17433
|
-
title: this.props.title
|
|
17434
|
-
tabIndex: "0"
|
|
17433
|
+
title: this.props.title
|
|
17435
17434
|
};
|
|
17436
|
-
// If alt text is present, AND overrideAriaHidden is true,
|
|
17437
|
-
// include aria-hidden to avoid screen reader from crawling texts
|
|
17438
|
-
// both from the div meant for screen readers (see svg-image.jsx)
|
|
17439
|
-
// and alt attribute.
|
|
17440
|
-
if (this.props.alt && this.props.overrideAriaHidden) {
|
|
17441
|
-
imageProps["aria-hidden"] = true;
|
|
17442
|
-
}
|
|
17443
17435
|
const width = this.props.width && this.props.width * this.props.scale;
|
|
17444
17436
|
const height = this.props.height && this.props.height * this.props.scale;
|
|
17445
17437
|
const dimensions = {
|
|
@@ -27361,7 +27353,7 @@ class ImageWidget extends React.Component {
|
|
|
27361
27353
|
|
|
27362
27354
|
render() {
|
|
27363
27355
|
let image;
|
|
27364
|
-
|
|
27356
|
+
const alt = this.props.caption === this.props.alt ? "" : this.props.alt;
|
|
27365
27357
|
const {
|
|
27366
27358
|
apiOptions
|
|
27367
27359
|
} = this.props;
|
|
@@ -27372,29 +27364,7 @@ class ImageWidget extends React.Component {
|
|
|
27372
27364
|
setAssetStatus
|
|
27373
27365
|
}) => /*#__PURE__*/React.createElement(SvgImage, {
|
|
27374
27366
|
src: url,
|
|
27375
|
-
alt:
|
|
27376
|
-
/* alt text is formatted in a sr-only
|
|
27377
|
-
div next to the image in addition to
|
|
27378
|
-
the alt attribute.
|
|
27379
|
-
If there is no alt text at all,
|
|
27380
|
-
we don't put an alt attribute on
|
|
27381
|
-
the image, so that screen readers
|
|
27382
|
-
know there's something they can't
|
|
27383
|
-
read there :(.
|
|
27384
|
-
NOTE: React <=0.13 (maybe later)
|
|
27385
|
-
has a bug where it won't ever
|
|
27386
|
-
remove an attribute, so if this
|
|
27387
|
-
alt node is ever defined it's
|
|
27388
|
-
not removed. This is sort of
|
|
27389
|
-
dangerous, but we usually re-key
|
|
27390
|
-
new renderers so that they're
|
|
27391
|
-
rendered from scratch anyways,
|
|
27392
|
-
so this shouldn't be a problem
|
|
27393
|
-
in practice right now, although
|
|
27394
|
-
it will exhibit weird behaviour
|
|
27395
|
-
while editing. */
|
|
27396
|
-
this.props.alt,
|
|
27397
|
-
overrideAriaHidden: true,
|
|
27367
|
+
alt: alt,
|
|
27398
27368
|
width: backgroundImage.width,
|
|
27399
27369
|
height: backgroundImage.height,
|
|
27400
27370
|
preloader: apiOptions.imagePreloader,
|
|
@@ -27410,16 +27380,6 @@ class ImageWidget extends React.Component {
|
|
|
27410
27380
|
setAssetStatus: setAssetStatus
|
|
27411
27381
|
}));
|
|
27412
27382
|
}
|
|
27413
|
-
if (this.props.alt) {
|
|
27414
|
-
alt = /*#__PURE__*/React.createElement("span", {
|
|
27415
|
-
className: "perseus-sr-only"
|
|
27416
|
-
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
27417
|
-
content: this.props.alt,
|
|
27418
|
-
apiOptions: apiOptions,
|
|
27419
|
-
linterContext: this.props.linterContext,
|
|
27420
|
-
strings: this.context.strings
|
|
27421
|
-
}));
|
|
27422
|
-
}
|
|
27423
27383
|
|
|
27424
27384
|
// For mobile we combine an image's title and caption.
|
|
27425
27385
|
if (apiOptions.isMobile) {
|
|
@@ -27486,7 +27446,7 @@ class ImageWidget extends React.Component {
|
|
|
27486
27446
|
style: {
|
|
27487
27447
|
maxWidth: backgroundImage.width
|
|
27488
27448
|
}
|
|
27489
|
-
}, image,
|
|
27449
|
+
}, image, titleAndCaption);
|
|
27490
27450
|
}
|
|
27491
27451
|
let title;
|
|
27492
27452
|
let caption;
|
|
@@ -27518,7 +27478,7 @@ class ImageWidget extends React.Component {
|
|
|
27518
27478
|
style: {
|
|
27519
27479
|
maxWidth: backgroundImage.width
|
|
27520
27480
|
}
|
|
27521
|
-
}, title, image,
|
|
27481
|
+
}, title, image, caption);
|
|
27522
27482
|
}
|
|
27523
27483
|
static validate(userInput, rubric) {
|
|
27524
27484
|
return {
|
|
@@ -28673,6 +28633,28 @@ const lockedFigureColors = {
|
|
|
28673
28633
|
};
|
|
28674
28634
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
28675
28635
|
|
|
28636
|
+
const LockedCircle = props => {
|
|
28637
|
+
const {
|
|
28638
|
+
center,
|
|
28639
|
+
radius,
|
|
28640
|
+
color,
|
|
28641
|
+
fillStyle,
|
|
28642
|
+
strokeStyle
|
|
28643
|
+
} = props;
|
|
28644
|
+
const fill = {
|
|
28645
|
+
none: 0,
|
|
28646
|
+
solid: 1,
|
|
28647
|
+
translucent: 0.4
|
|
28648
|
+
};
|
|
28649
|
+
return /*#__PURE__*/React.createElement(Circle$1, {
|
|
28650
|
+
center: center,
|
|
28651
|
+
radius: radius,
|
|
28652
|
+
fillOpacity: fill[fillStyle],
|
|
28653
|
+
strokeStyle: strokeStyle,
|
|
28654
|
+
color: lockedFigureColors[color]
|
|
28655
|
+
});
|
|
28656
|
+
};
|
|
28657
|
+
|
|
28676
28658
|
function pathBuilder() {
|
|
28677
28659
|
return new PathBuilder();
|
|
28678
28660
|
}
|
|
@@ -28942,6 +28924,10 @@ const GraphLockedLayer = props => {
|
|
|
28942
28924
|
key: `line-${index}`,
|
|
28943
28925
|
range: props.range
|
|
28944
28926
|
}, figure));
|
|
28927
|
+
case "circle":
|
|
28928
|
+
return /*#__PURE__*/React.createElement(LockedCircle, _extends({
|
|
28929
|
+
key: `circle-${index}`
|
|
28930
|
+
}, figure));
|
|
28945
28931
|
default:
|
|
28946
28932
|
/**
|
|
28947
28933
|
* Devlopment-time future-proofing: This should
|
|
@@ -29736,8 +29722,12 @@ function initializeGraphState(params) {
|
|
|
29736
29722
|
type: graph.type,
|
|
29737
29723
|
coords: getQuadraticCoords(graph, range, step)
|
|
29738
29724
|
});
|
|
29739
|
-
case "angle":
|
|
29740
29725
|
case "sinusoid":
|
|
29726
|
+
return _extends({}, shared, {
|
|
29727
|
+
type: graph.type,
|
|
29728
|
+
coords: getSinusoidCoords(graph, range, step)
|
|
29729
|
+
});
|
|
29730
|
+
case "angle":
|
|
29741
29731
|
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
29742
29732
|
default:
|
|
29743
29733
|
throw new UnreachableCaseError(graph);
|
|
@@ -29831,6 +29821,11 @@ function getGradableGraph(state, initialGraph) {
|
|
|
29831
29821
|
coords: state.coords
|
|
29832
29822
|
});
|
|
29833
29823
|
}
|
|
29824
|
+
if (state.type === "sinusoid" && initialGraph.type === "sinusoid") {
|
|
29825
|
+
return _extends({}, initialGraph, {
|
|
29826
|
+
coords: state.coords
|
|
29827
|
+
});
|
|
29828
|
+
}
|
|
29834
29829
|
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29835
29830
|
}
|
|
29836
29831
|
const getDefaultSegments = ({
|
|
@@ -29908,6 +29903,11 @@ const getPolygonCoords = ({
|
|
|
29908
29903
|
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
29909
29904
|
return coords;
|
|
29910
29905
|
};
|
|
29906
|
+
const getSinusoidCoords = (graph, range, step) => {
|
|
29907
|
+
let coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
29908
|
+
coords = normalizePoints(range, step, coords, true);
|
|
29909
|
+
return coords;
|
|
29910
|
+
};
|
|
29911
29911
|
const getQuadraticCoords = (graph, range, step) => {
|
|
29912
29912
|
let coords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
29913
29913
|
coords = normalizePoints(range, step, coords, true);
|
|
@@ -30098,6 +30098,62 @@ const getQuadraticCoefficients = coords => {
|
|
|
30098
30098
|
return [a, b, c];
|
|
30099
30099
|
};
|
|
30100
30100
|
|
|
30101
|
+
function SinusoidGraph(props) {
|
|
30102
|
+
const {
|
|
30103
|
+
dispatch,
|
|
30104
|
+
graphState
|
|
30105
|
+
} = props;
|
|
30106
|
+
|
|
30107
|
+
// Destructure the coordinates from the graph state
|
|
30108
|
+
// Note: The order of the coordinates is important:
|
|
30109
|
+
// The coords[0] is the root and the coords[1] is the first peak
|
|
30110
|
+
const {
|
|
30111
|
+
coords
|
|
30112
|
+
} = graphState;
|
|
30113
|
+
|
|
30114
|
+
// Get the coefficients for calculating the quadratic equation
|
|
30115
|
+
const coeffs = getSinusoidCoefficients(coords);
|
|
30116
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Plot$3.OfX, {
|
|
30117
|
+
y: x => computeSine(x, coeffs),
|
|
30118
|
+
color: color.blue
|
|
30119
|
+
}), coords.map((coord, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
30120
|
+
key: "point-" + i,
|
|
30121
|
+
point: coord,
|
|
30122
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
30123
|
+
})));
|
|
30124
|
+
}
|
|
30125
|
+
|
|
30126
|
+
// Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
|
|
30127
|
+
const computeSine = function computeSine(x,
|
|
30128
|
+
// x-coordinate
|
|
30129
|
+
sinusoidCoefficients) {
|
|
30130
|
+
// Break down the coefficients for the sine function to improve readability
|
|
30131
|
+
const {
|
|
30132
|
+
amplitude: a,
|
|
30133
|
+
angularFrequency: b,
|
|
30134
|
+
phase: c,
|
|
30135
|
+
verticalOffset: d
|
|
30136
|
+
} = sinusoidCoefficients;
|
|
30137
|
+
return a * Math.sin(b * x - c) + d;
|
|
30138
|
+
};
|
|
30139
|
+
const getSinusoidCoefficients = coords => {
|
|
30140
|
+
// It's assumed that p1 is the root and p2 is the first peak
|
|
30141
|
+
const p1 = coords[0];
|
|
30142
|
+
const p2 = coords[1];
|
|
30143
|
+
|
|
30144
|
+
// Resulting coefficients are canonical for this sine curve
|
|
30145
|
+
const amplitude = p2[1] - p1[1];
|
|
30146
|
+
const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
|
|
30147
|
+
const phase = p1[0] * angularFrequency;
|
|
30148
|
+
const verticalOffset = p1[1];
|
|
30149
|
+
return {
|
|
30150
|
+
amplitude,
|
|
30151
|
+
angularFrequency,
|
|
30152
|
+
phase,
|
|
30153
|
+
verticalOffset
|
|
30154
|
+
};
|
|
30155
|
+
};
|
|
30156
|
+
|
|
30101
30157
|
// Universal padding for the axis ticks to ensure that the labels
|
|
30102
30158
|
// have enough space to render without overlapping the tick lines
|
|
30103
30159
|
const tickPadding = 5;
|
|
@@ -31224,6 +31280,7 @@ function doMoveControlPoint(state, action) {
|
|
|
31224
31280
|
case "point":
|
|
31225
31281
|
case "polygon":
|
|
31226
31282
|
case "quadratic":
|
|
31283
|
+
case "sinusoid":
|
|
31227
31284
|
throw new Error(`Don't use moveControlPoint for ${state.type} graphs. Use movePoint instead!`);
|
|
31228
31285
|
default:
|
|
31229
31286
|
throw new UnreachableCaseError(state);
|
|
@@ -31310,6 +31367,29 @@ function doMovePoint(state, action) {
|
|
|
31310
31367
|
})
|
|
31311
31368
|
});
|
|
31312
31369
|
}
|
|
31370
|
+
case "sinusoid":
|
|
31371
|
+
{
|
|
31372
|
+
// First, we need to verify that the new coordinates are not on the same vertical line
|
|
31373
|
+
// If they are, we don't want to move the point
|
|
31374
|
+
const destination = action.destination;
|
|
31375
|
+
const newCoords = [...state.coords];
|
|
31376
|
+
newCoords[action.index] = action.destination;
|
|
31377
|
+
if (newCoords[0][0] === newCoords[1][0]) {
|
|
31378
|
+
return state;
|
|
31379
|
+
}
|
|
31380
|
+
return _extends({}, state, {
|
|
31381
|
+
hasBeenInteractedWith: true,
|
|
31382
|
+
coords: setAtIndex({
|
|
31383
|
+
array: state.coords,
|
|
31384
|
+
index: action.index,
|
|
31385
|
+
newValue: snap(state.snapStep, bound$1({
|
|
31386
|
+
snapStep: state.snapStep,
|
|
31387
|
+
range: state.range,
|
|
31388
|
+
point: destination
|
|
31389
|
+
}))
|
|
31390
|
+
})
|
|
31391
|
+
});
|
|
31392
|
+
}
|
|
31313
31393
|
case "quadratic":
|
|
31314
31394
|
{
|
|
31315
31395
|
return _extends({}, state, {
|
|
@@ -31544,6 +31624,11 @@ const renderGraph = props => {
|
|
|
31544
31624
|
graphState: state,
|
|
31545
31625
|
dispatch: dispatch
|
|
31546
31626
|
});
|
|
31627
|
+
case "sinusoid":
|
|
31628
|
+
return /*#__PURE__*/React.createElement(SinusoidGraph, {
|
|
31629
|
+
graphState: state,
|
|
31630
|
+
dispatch: dispatch
|
|
31631
|
+
});
|
|
31547
31632
|
default:
|
|
31548
31633
|
return new UnreachableCaseError(type);
|
|
31549
31634
|
}
|
|
@@ -43758,7 +43843,7 @@ const init = function init(options) {
|
|
|
43758
43843
|
|
|
43759
43844
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43760
43845
|
const libName = "@khanacademy/perseus";
|
|
43761
|
-
const libVersion = "23.
|
|
43846
|
+
const libVersion = "23.1.0";
|
|
43762
43847
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43763
43848
|
|
|
43764
43849
|
const apiVersion = {
|