@khanacademy/perseus 24.0.0 → 24.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.css +29 -1
- package/dist/es/index.js +395 -42
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +29 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +403 -47
- package/dist/index.js.map +1 -1
- package/dist/util/svg.d.ts +5 -0
- package/dist/widgets/interactive-graph.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/use-draggable.d.ts +20 -3
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +2 -0
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +2 -0
- package/dist/widgets/interactive-graphs/protractor.d.ts +5 -0
- package/dist/widgets/interactive-graphs/utils.d.ts +7 -1
- package/package.json +2 -1
package/dist/es/index.css
CHANGED
|
@@ -3518,7 +3518,7 @@ body.mobile .framework-perseus table.perseus-widget-table-of-values.non-markdown
|
|
|
3518
3518
|
font-size: 18px;
|
|
3519
3519
|
}
|
|
3520
3520
|
}
|
|
3521
|
-
.perseus-widget-editor-content .
|
|
3521
|
+
.perseus-widget-editor-content .perseus-editor-accordion h2 {
|
|
3522
3522
|
padding-top: 0;
|
|
3523
3523
|
}
|
|
3524
3524
|
.keypad-input {
|
|
@@ -4143,6 +4143,34 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
|
|
|
4143
4143
|
}
|
|
4144
4144
|
|
|
4145
4145
|
|
|
4146
|
+
.MafsView .protractor-rotation-handle {
|
|
4147
|
+
cursor: grab;
|
|
4148
|
+
/* `pointer-events: all` is needed to make the hit target the size of the
|
|
4149
|
+
* entire <g> element and not just its visible subelements */
|
|
4150
|
+
pointer-events: all;
|
|
4151
|
+
transition: transform 0.1s ease-out;
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
.MafsView .protractor-rotation-handle:hover {
|
|
4155
|
+
transform: scale(1.3);
|
|
4156
|
+
}
|
|
4157
|
+
|
|
4158
|
+
.MafsView .protractor-rotation-handle-arrow-arc {
|
|
4159
|
+
fill: none;
|
|
4160
|
+
stroke: var(--mafs-blue);
|
|
4161
|
+
stroke-width: 8;
|
|
4162
|
+
/* `strokeLinecap: square` prevents a hairline crack from appearing
|
|
4163
|
+
* between the arc and the arrowheads, by making the arc extend a bit
|
|
4164
|
+
* beyond its start and end coordinates.
|
|
4165
|
+
*/
|
|
4166
|
+
stroke-linecap: square;
|
|
4167
|
+
}
|
|
4168
|
+
|
|
4169
|
+
.MafsView .protractor-rotation-handle-arrowhead {
|
|
4170
|
+
fill: var(--mafs-blue);
|
|
4171
|
+
stroke: none;
|
|
4172
|
+
}
|
|
4173
|
+
|
|
4146
4174
|
.MafsView {
|
|
4147
4175
|
display: block;
|
|
4148
4176
|
background: var(--mafs-bg);
|
package/dist/es/index.js
CHANGED
|
@@ -29,7 +29,8 @@ 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, Ellipse, Line as Line$4, Point as Point$2, Polygon,
|
|
32
|
+
import { vec, Ellipse, Line as Line$4, Point as Point$2, Polygon, useTransformContext, Text as Text$1, Plot as Plot$3, Coordinates, Mafs } from 'mafs';
|
|
33
|
+
import { useDrag } from '@use-gesture/react';
|
|
33
34
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
34
35
|
import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
35
36
|
import questionIcon from '@phosphor-icons/core/regular/question.svg';
|
|
@@ -13085,7 +13086,7 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
13085
13086
|
}(),
|
|
13086
13087
|
protractor: function (center) {
|
|
13087
13088
|
// @ts-expect-error - TS2350 - Only a void function can be called with the 'new' keyword.
|
|
13088
|
-
return new Protractor(this, center);
|
|
13089
|
+
return new Protractor$1(this, center);
|
|
13089
13090
|
},
|
|
13090
13091
|
ruler: function (options) {
|
|
13091
13092
|
// @ts-expect-error - TS2350 - Only a void function can be called with the 'new' keyword.
|
|
@@ -13093,7 +13094,7 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
13093
13094
|
},
|
|
13094
13095
|
addPoints: vector$1.add
|
|
13095
13096
|
});
|
|
13096
|
-
function Protractor(graph, center) {
|
|
13097
|
+
function Protractor$1(graph, center) {
|
|
13097
13098
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
13098
13099
|
this.set = graph.raphael.set();
|
|
13099
13100
|
|
|
@@ -28557,7 +28558,24 @@ class PathBuilder {
|
|
|
28557
28558
|
move(x, y) {
|
|
28558
28559
|
this.path.push({
|
|
28559
28560
|
action: "M",
|
|
28560
|
-
|
|
28561
|
+
args: [x, y]
|
|
28562
|
+
});
|
|
28563
|
+
return this;
|
|
28564
|
+
}
|
|
28565
|
+
line(x, y) {
|
|
28566
|
+
this.path.push({
|
|
28567
|
+
action: "L",
|
|
28568
|
+
args: [x, y]
|
|
28569
|
+
});
|
|
28570
|
+
return this;
|
|
28571
|
+
}
|
|
28572
|
+
circularArc(radius, toX, toY, {
|
|
28573
|
+
sweep = false,
|
|
28574
|
+
largeArc = false
|
|
28575
|
+
} = {}) {
|
|
28576
|
+
this.path.push({
|
|
28577
|
+
action: "A",
|
|
28578
|
+
args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
|
|
28561
28579
|
});
|
|
28562
28580
|
return this;
|
|
28563
28581
|
}
|
|
@@ -28566,7 +28584,7 @@ class PathBuilder {
|
|
|
28566
28584
|
curve(control1X, control1Y, control2X, control2Y, endX, endY) {
|
|
28567
28585
|
this.path.push({
|
|
28568
28586
|
action: "C",
|
|
28569
|
-
|
|
28587
|
+
args: [control1X, control1Y, control2X, control2Y, endX, endY]
|
|
28570
28588
|
});
|
|
28571
28589
|
return this;
|
|
28572
28590
|
}
|
|
@@ -28578,12 +28596,38 @@ class PathBuilder {
|
|
|
28578
28596
|
}
|
|
28579
28597
|
}
|
|
28580
28598
|
function commandToString(command) {
|
|
28581
|
-
return `${command.action}${command.
|
|
28599
|
+
return `${command.action}${command.args.join(" ")}`;
|
|
28582
28600
|
}
|
|
28583
28601
|
function scaleCommandBy(scaleFactor) {
|
|
28584
|
-
return command =>
|
|
28585
|
-
|
|
28586
|
-
|
|
28602
|
+
return command => {
|
|
28603
|
+
switch (command.action) {
|
|
28604
|
+
case "A":
|
|
28605
|
+
// TODO(benchristel): do we want to refactor this switch to
|
|
28606
|
+
// use polymorphism?
|
|
28607
|
+
// Arc command
|
|
28608
|
+
return _extends({}, command, {
|
|
28609
|
+
args: [command.args[0] * scaleFactor,
|
|
28610
|
+
// x radius
|
|
28611
|
+
command.args[1] * scaleFactor,
|
|
28612
|
+
// y radius
|
|
28613
|
+
command.args[2],
|
|
28614
|
+
// rotation
|
|
28615
|
+
command.args[3],
|
|
28616
|
+
// largeArc flag
|
|
28617
|
+
command.args[4],
|
|
28618
|
+
// sweep flag
|
|
28619
|
+
command.args[5] * scaleFactor,
|
|
28620
|
+
// end x
|
|
28621
|
+
command.args[6] * scaleFactor // end y
|
|
28622
|
+
]
|
|
28623
|
+
});
|
|
28624
|
+
|
|
28625
|
+
default:
|
|
28626
|
+
return _extends({}, command, {
|
|
28627
|
+
args: command.args.map(c => c * scaleFactor)
|
|
28628
|
+
});
|
|
28629
|
+
}
|
|
28630
|
+
};
|
|
28587
28631
|
}
|
|
28588
28632
|
|
|
28589
28633
|
// We use the pathBuilder here to scale up the SVG path coordinates used
|
|
@@ -28971,8 +29015,169 @@ function snap(snapStep, point) {
|
|
|
28971
29015
|
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
28972
29016
|
}
|
|
28973
29017
|
|
|
28974
|
-
|
|
28975
|
-
|
|
29018
|
+
// Returns the closest point to the given `point` that is within the graph
|
|
29019
|
+
// bounds given in `state`.
|
|
29020
|
+
function bound$1({
|
|
29021
|
+
snapStep,
|
|
29022
|
+
range,
|
|
29023
|
+
point
|
|
29024
|
+
}) {
|
|
29025
|
+
const [requestedX, requestedY] = point;
|
|
29026
|
+
const [snapX, snapY] = snapStep;
|
|
29027
|
+
const [[minX, maxX], [minY, maxY]] = range;
|
|
29028
|
+
return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
|
|
29029
|
+
}
|
|
29030
|
+
function clamp(value, min, max) {
|
|
29031
|
+
if (value < min) {
|
|
29032
|
+
return min;
|
|
29033
|
+
}
|
|
29034
|
+
if (value > max) {
|
|
29035
|
+
return max;
|
|
29036
|
+
}
|
|
29037
|
+
return value;
|
|
29038
|
+
}
|
|
29039
|
+
|
|
29040
|
+
var isProduction = process.env.NODE_ENV === 'production';
|
|
29041
|
+
var prefix = 'Invariant failed';
|
|
29042
|
+
function invariant(condition, message) {
|
|
29043
|
+
if (condition) {
|
|
29044
|
+
return;
|
|
29045
|
+
}
|
|
29046
|
+
if (isProduction) {
|
|
29047
|
+
throw new Error(prefix);
|
|
29048
|
+
}
|
|
29049
|
+
var provided = typeof message === 'function' ? message() : message;
|
|
29050
|
+
var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
|
|
29051
|
+
throw new Error(value);
|
|
29052
|
+
}
|
|
29053
|
+
|
|
29054
|
+
/**
|
|
29055
|
+
* Code in this file is derived from
|
|
29056
|
+
* https://github.com/stevenpetryk/mafs/blob/4520319379a2cc2df8148d8baaef1f85db117103/src/interaction/useMovable.tsx#L20-L83
|
|
29057
|
+
* and copied here under the terms of the MIT license.
|
|
29058
|
+
*
|
|
29059
|
+
* Copyright 2021 Steven Petryk
|
|
29060
|
+
*
|
|
29061
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
29062
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
29063
|
+
* in the Software without restriction, including without limitation the rights
|
|
29064
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
29065
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
29066
|
+
* furnished to do so, subject to the following conditions:
|
|
29067
|
+
*
|
|
29068
|
+
* The above copyright notice and this permission notice shall be included in
|
|
29069
|
+
* all copies or substantial portions of the Software.
|
|
29070
|
+
*/
|
|
29071
|
+
|
|
29072
|
+
function useDraggable(args) {
|
|
29073
|
+
const {
|
|
29074
|
+
gestureTarget: target,
|
|
29075
|
+
onMove,
|
|
29076
|
+
point,
|
|
29077
|
+
constrain
|
|
29078
|
+
} = args;
|
|
29079
|
+
const [dragging, setDragging] = React.useState(false);
|
|
29080
|
+
const {
|
|
29081
|
+
xSpan,
|
|
29082
|
+
ySpan
|
|
29083
|
+
} = useSpanContext();
|
|
29084
|
+
const {
|
|
29085
|
+
viewTransform,
|
|
29086
|
+
userTransform
|
|
29087
|
+
} = useTransformContext();
|
|
29088
|
+
const inverseViewTransform = vec.matrixInvert(viewTransform);
|
|
29089
|
+
invariant(inverseViewTransform, "The view transform must be invertible.");
|
|
29090
|
+
const inverseTransform = React.useMemo(() => getInverseTransform(userTransform), [userTransform]);
|
|
29091
|
+
const pickup = React.useRef([0, 0]);
|
|
29092
|
+
useDrag(state => {
|
|
29093
|
+
const {
|
|
29094
|
+
type,
|
|
29095
|
+
event
|
|
29096
|
+
} = state;
|
|
29097
|
+
event == null || event.stopPropagation();
|
|
29098
|
+
const isKeyboard = type.includes("key");
|
|
29099
|
+
if (isKeyboard) {
|
|
29100
|
+
event == null || event.preventDefault();
|
|
29101
|
+
const {
|
|
29102
|
+
direction: yDownDirection,
|
|
29103
|
+
altKey,
|
|
29104
|
+
metaKey,
|
|
29105
|
+
shiftKey
|
|
29106
|
+
} = state;
|
|
29107
|
+
const direction = [yDownDirection[0], -yDownDirection[1]];
|
|
29108
|
+
const span = Math.abs(direction[0]) ? xSpan : ySpan;
|
|
29109
|
+
let divisions = 50;
|
|
29110
|
+
if (altKey || metaKey) {
|
|
29111
|
+
divisions = 200;
|
|
29112
|
+
}
|
|
29113
|
+
if (shiftKey) {
|
|
29114
|
+
divisions = 10;
|
|
29115
|
+
}
|
|
29116
|
+
const min = span / (divisions * 2);
|
|
29117
|
+
const tests = range(span / divisions, span / 2, span / divisions);
|
|
29118
|
+
for (const dx of tests) {
|
|
29119
|
+
// Transform the test back into the point's coordinate system
|
|
29120
|
+
const testMovement = vec.scale(direction, dx);
|
|
29121
|
+
const testPoint = constrain(vec.transform(vec.add(vec.transform(point, userTransform), testMovement), inverseTransform));
|
|
29122
|
+
if (vec.dist(testPoint, point) > min) {
|
|
29123
|
+
onMove(testPoint);
|
|
29124
|
+
break;
|
|
29125
|
+
}
|
|
29126
|
+
}
|
|
29127
|
+
} else {
|
|
29128
|
+
const {
|
|
29129
|
+
last,
|
|
29130
|
+
movement: pixelMovement,
|
|
29131
|
+
first
|
|
29132
|
+
} = state;
|
|
29133
|
+
setDragging(!last);
|
|
29134
|
+
if (first) {
|
|
29135
|
+
pickup.current = vec.transform(point, userTransform);
|
|
29136
|
+
}
|
|
29137
|
+
if (vec.mag(pixelMovement) === 0) {
|
|
29138
|
+
return;
|
|
29139
|
+
}
|
|
29140
|
+
const movement = vec.transform(pixelMovement, inverseViewTransform);
|
|
29141
|
+
onMove(constrain(vec.transform(vec.add(pickup.current, movement), inverseTransform)));
|
|
29142
|
+
}
|
|
29143
|
+
}, {
|
|
29144
|
+
target,
|
|
29145
|
+
eventOptions: {
|
|
29146
|
+
passive: false
|
|
29147
|
+
}
|
|
29148
|
+
});
|
|
29149
|
+
return {
|
|
29150
|
+
dragging
|
|
29151
|
+
};
|
|
29152
|
+
}
|
|
29153
|
+
function getInverseTransform(transform) {
|
|
29154
|
+
const invert = vec.matrixInvert(transform);
|
|
29155
|
+
invariant(invert !== null, "Could not invert transform matrix. A parent transformation matrix might be degenerative (mapping 2D space to a line).");
|
|
29156
|
+
return invert;
|
|
29157
|
+
}
|
|
29158
|
+
function useSpanContext() {
|
|
29159
|
+
const {
|
|
29160
|
+
range: [[xMin, xMax], [yMin, yMax]]
|
|
29161
|
+
} = useGraphConfig();
|
|
29162
|
+
const xSpan = xMax - xMin;
|
|
29163
|
+
const ySpan = yMax - yMin;
|
|
29164
|
+
return {
|
|
29165
|
+
xSpan,
|
|
29166
|
+
ySpan
|
|
29167
|
+
};
|
|
29168
|
+
}
|
|
29169
|
+
function range(min, max, step = 1) {
|
|
29170
|
+
const result = [];
|
|
29171
|
+
for (let i = min; i < max - step / 2; i += step) {
|
|
29172
|
+
result.push(i);
|
|
29173
|
+
}
|
|
29174
|
+
const computedMax = result[result.length - 1] + step;
|
|
29175
|
+
if (Math.abs(max - computedMax) < step / 1e-6) {
|
|
29176
|
+
result.push(max);
|
|
29177
|
+
} else {
|
|
29178
|
+
result.push(computedMax);
|
|
29179
|
+
}
|
|
29180
|
+
return result;
|
|
28976
29181
|
}
|
|
28977
29182
|
|
|
28978
29183
|
// The hitbox size of 48px by 48px is preserved from the legacy interactive
|
|
@@ -30995,6 +31200,154 @@ const LegacyGrid = ({
|
|
|
30995
31200
|
return null;
|
|
30996
31201
|
};
|
|
30997
31202
|
|
|
31203
|
+
const protractorImage = "https://ka-perseus-graphie.s3.amazonaws.com/e9d032f2ab8b95979f674fbfa67056442ba1ff6a.png";
|
|
31204
|
+
|
|
31205
|
+
// The vector from the center of the protractor to the top left corner of the
|
|
31206
|
+
// protractor image, in pixels. Used for positioning.
|
|
31207
|
+
const centerToTopLeft = [-180, -170];
|
|
31208
|
+
|
|
31209
|
+
// The vector from the center of the protractor to the center of the rotation
|
|
31210
|
+
// handle.
|
|
31211
|
+
const centerToRotationHandle = [-176, -15];
|
|
31212
|
+
function Protractor() {
|
|
31213
|
+
const {
|
|
31214
|
+
range,
|
|
31215
|
+
snapStep
|
|
31216
|
+
} = useGraphConfig();
|
|
31217
|
+
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
31218
|
+
// Position the protractor in the center of the graph (horizontally), and
|
|
31219
|
+
// 95% of the way to the bottom of the graph (vertically).
|
|
31220
|
+
const initialCenter = [lerp(xMin, xMax, 0.5), lerp(yMin, yMax, 0.05)];
|
|
31221
|
+
const [center, setCenter] = useState(initialCenter);
|
|
31222
|
+
const [rotationHandleOffset, setRotationHandleOffset] = useState(centerToRotationHandle);
|
|
31223
|
+
const draggableRef = useRef(null);
|
|
31224
|
+
useDraggable({
|
|
31225
|
+
gestureTarget: draggableRef,
|
|
31226
|
+
onMove: setCenter,
|
|
31227
|
+
point: center,
|
|
31228
|
+
constrain: point => bound$1({
|
|
31229
|
+
snapStep,
|
|
31230
|
+
range,
|
|
31231
|
+
point
|
|
31232
|
+
})
|
|
31233
|
+
});
|
|
31234
|
+
const rotationHandleRef = useRef(null);
|
|
31235
|
+
useDraggablePx({
|
|
31236
|
+
gestureTarget: rotationHandleRef,
|
|
31237
|
+
onMove: setRotationHandleOffset,
|
|
31238
|
+
point: rotationHandleOffset,
|
|
31239
|
+
constrain: constrainToCircle
|
|
31240
|
+
});
|
|
31241
|
+
const [centerPx] = useTransformVectorsToPixels(center);
|
|
31242
|
+
const topLeftPx = vec.add(centerPx, centerToTopLeft);
|
|
31243
|
+
const angle = calculateAngleInDegrees(rotationHandleOffset) - calculateAngleInDegrees(centerToRotationHandle);
|
|
31244
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
31245
|
+
ref: draggableRef,
|
|
31246
|
+
transform: `translate(${topLeftPx[0]}, ${topLeftPx[1]}), rotate(${angle})`,
|
|
31247
|
+
style: {
|
|
31248
|
+
transformOrigin: `${-centerToTopLeft[0]}px ${-centerToTopLeft[1]}px`
|
|
31249
|
+
}
|
|
31250
|
+
}, /*#__PURE__*/React.createElement("image", {
|
|
31251
|
+
href: protractorImage
|
|
31252
|
+
}), /*#__PURE__*/React.createElement("g", {
|
|
31253
|
+
transform: `translate(5, ${-centerToTopLeft[1]})`,
|
|
31254
|
+
ref: rotationHandleRef
|
|
31255
|
+
}, /*#__PURE__*/React.createElement(RotationArrow, null)));
|
|
31256
|
+
}
|
|
31257
|
+
function RotationArrow() {
|
|
31258
|
+
const radius = 175;
|
|
31259
|
+
const angleDeg = 10;
|
|
31260
|
+
const angleRad = degreesToRadians(angleDeg);
|
|
31261
|
+
const endX = radius * (1 - Math.cos(angleRad));
|
|
31262
|
+
const endY = radius * -Math.sin(angleRad);
|
|
31263
|
+
const rotationArrow = pathBuilder().move(0, 0).circularArc(radius, endX, endY, {
|
|
31264
|
+
sweep: true
|
|
31265
|
+
}).build();
|
|
31266
|
+
const arrowhead = pathBuilder().move(-8, 0).line(0, 10).line(8, 0).build();
|
|
31267
|
+
const targetRadius = TARGET_SIZE / 2;
|
|
31268
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
31269
|
+
className: "protractor-rotation-handle"
|
|
31270
|
+
}, /*#__PURE__*/React.createElement("path", {
|
|
31271
|
+
className: "protractor-rotation-handle-arrow-arc",
|
|
31272
|
+
d: rotationArrow
|
|
31273
|
+
}), /*#__PURE__*/React.createElement("path", {
|
|
31274
|
+
className: "protractor-rotation-handle-arrowhead",
|
|
31275
|
+
d: arrowhead
|
|
31276
|
+
}), /*#__PURE__*/React.createElement("path", {
|
|
31277
|
+
className: "protractor-rotation-handle-arrowhead",
|
|
31278
|
+
d: arrowhead,
|
|
31279
|
+
transform: `translate(${endX}, ${endY}), rotate(${180 + angleDeg})`
|
|
31280
|
+
}), /*#__PURE__*/React.createElement("ellipse", {
|
|
31281
|
+
cx: "0px",
|
|
31282
|
+
cy: "-15px",
|
|
31283
|
+
rx: targetRadius,
|
|
31284
|
+
ry: targetRadius,
|
|
31285
|
+
fill: "none"
|
|
31286
|
+
}));
|
|
31287
|
+
}
|
|
31288
|
+
const protractorRadius = vec.mag(centerToRotationHandle);
|
|
31289
|
+
function constrainToCircle(edgePoint) {
|
|
31290
|
+
return vec.withMag(edgePoint, protractorRadius);
|
|
31291
|
+
}
|
|
31292
|
+
|
|
31293
|
+
/* The implementation of useDraggablePx is derived from
|
|
31294
|
+
* https://github.com/stevenpetryk/mafs/blob/4520319379a2cc2df8148d8baaef1f85db117103/src/interaction/useMovable.tsx#L20-L83
|
|
31295
|
+
* and copied here under the terms of the MIT license.
|
|
31296
|
+
*
|
|
31297
|
+
* Copyright 2021 Steven Petryk
|
|
31298
|
+
*
|
|
31299
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
31300
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
31301
|
+
* in the Software without restriction, including without limitation the rights
|
|
31302
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
31303
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
31304
|
+
* furnished to do so, subject to the following conditions:
|
|
31305
|
+
*
|
|
31306
|
+
* The above copyright notice and this permission notice shall be included in
|
|
31307
|
+
* all copies or substantial portions of the Software.
|
|
31308
|
+
*/
|
|
31309
|
+
function useDraggablePx(args) {
|
|
31310
|
+
const {
|
|
31311
|
+
gestureTarget: target,
|
|
31312
|
+
onMove,
|
|
31313
|
+
point,
|
|
31314
|
+
constrain = p => p
|
|
31315
|
+
} = args;
|
|
31316
|
+
const pickupPx = React.useRef([0, 0]);
|
|
31317
|
+
useDrag(state => {
|
|
31318
|
+
const {
|
|
31319
|
+
event,
|
|
31320
|
+
first,
|
|
31321
|
+
movement: pixelMovement
|
|
31322
|
+
} = state;
|
|
31323
|
+
event == null || event.stopPropagation();
|
|
31324
|
+
if (first) {
|
|
31325
|
+
pickupPx.current = point;
|
|
31326
|
+
}
|
|
31327
|
+
if (vec.mag(pixelMovement) === 0) {
|
|
31328
|
+
return;
|
|
31329
|
+
}
|
|
31330
|
+
onMove == null || onMove(constrain(vec.add(pickupPx.current, pixelMovement)));
|
|
31331
|
+
}, {
|
|
31332
|
+
target,
|
|
31333
|
+
eventOptions: {
|
|
31334
|
+
passive: false
|
|
31335
|
+
}
|
|
31336
|
+
});
|
|
31337
|
+
}
|
|
31338
|
+
|
|
31339
|
+
// [L]inear Int[erp]olation: gets the weighted average of two values `a` and
|
|
31340
|
+
// `b`, with the given `fraction` specifying how much weight `a` and `b` get:
|
|
31341
|
+
// - if `fraction` is 0, `lerp` returns `a`.
|
|
31342
|
+
// - if `fraction` is 0.5, `lerp` returns the average of `a` and `b`.
|
|
31343
|
+
// - if `fraction` is 1, `lerp` returns `b`.
|
|
31344
|
+
function lerp(a, b, fraction) {
|
|
31345
|
+
return (b - a) * fraction + a;
|
|
31346
|
+
}
|
|
31347
|
+
function degreesToRadians(degrees) {
|
|
31348
|
+
return degrees / 180 * Math.PI;
|
|
31349
|
+
}
|
|
31350
|
+
|
|
30998
31351
|
function initializeGraphState(params) {
|
|
30999
31352
|
var _graph$snapTo;
|
|
31000
31353
|
const {
|
|
@@ -31677,19 +32030,6 @@ function boundAndSnapToSides(destinationPoint, {
|
|
|
31677
32030
|
return vector$1.add(coordsCopy[rel(-1)], offset);
|
|
31678
32031
|
}
|
|
31679
32032
|
|
|
31680
|
-
// Returns the closest point to the given `point` that is within the graph
|
|
31681
|
-
// bounds given in `state`.
|
|
31682
|
-
function bound$1({
|
|
31683
|
-
snapStep,
|
|
31684
|
-
range,
|
|
31685
|
-
point
|
|
31686
|
-
}) {
|
|
31687
|
-
const [requestedX, requestedY] = point;
|
|
31688
|
-
const [snapX, snapY] = snapStep;
|
|
31689
|
-
const [[minX, maxX], [minY, maxY]] = range;
|
|
31690
|
-
return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
|
|
31691
|
-
}
|
|
31692
|
-
|
|
31693
32033
|
// Returns the vector from the given point to the top-right corner of the graph when snapped to the grid
|
|
31694
32034
|
function maxMove({
|
|
31695
32035
|
snapStep,
|
|
@@ -31717,15 +32057,6 @@ function minMove({
|
|
|
31717
32057
|
});
|
|
31718
32058
|
return vec.sub(bottomLeft, point);
|
|
31719
32059
|
}
|
|
31720
|
-
function clamp(value, min, max) {
|
|
31721
|
-
if (value < min) {
|
|
31722
|
-
return min;
|
|
31723
|
-
}
|
|
31724
|
-
if (value > max) {
|
|
31725
|
-
return max;
|
|
31726
|
-
}
|
|
31727
|
-
return value;
|
|
31728
|
-
}
|
|
31729
32060
|
const coordsOverlap = coords => coords.some((coord, i) => coords.some((c, j) => i !== j && vector$1.equal(coord, c)));
|
|
31730
32061
|
function updateAtIndex(args) {
|
|
31731
32062
|
const {
|
|
@@ -31936,7 +32267,7 @@ const MafsGraph = props => {
|
|
|
31936
32267
|
}), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
|
|
31937
32268
|
lockedFigures: props.lockedFigures,
|
|
31938
32269
|
range: state.range
|
|
31939
|
-
}), renderGraph({
|
|
32270
|
+
}), "/* Protractor */", props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
|
|
31940
32271
|
state,
|
|
31941
32272
|
dispatch
|
|
31942
32273
|
})))));
|
|
@@ -33244,14 +33575,9 @@ class InteractiveGraph extends React.Component {
|
|
|
33244
33575
|
}
|
|
33245
33576
|
render() {
|
|
33246
33577
|
var _this$props$apiOption;
|
|
33247
|
-
if (this.props.graph.type === "polygon" && this.props.graph.numSides === "unlimited") {
|
|
33248
|
-
return /*#__PURE__*/React.createElement(LegacyInteractiveGraph, _extends({
|
|
33249
|
-
ref: this.legacyGraphRef
|
|
33250
|
-
}, this.props));
|
|
33251
|
-
}
|
|
33252
|
-
|
|
33253
33578
|
// Mafs shim
|
|
33254
|
-
|
|
33579
|
+
const mafsFlags = (_this$props$apiOption = this.props.apiOptions) == null || (_this$props$apiOption = _this$props$apiOption.flags) == null ? void 0 : _this$props$apiOption["mafs"];
|
|
33580
|
+
if (shouldUseMafs(mafsFlags, this.props.graph)) {
|
|
33255
33581
|
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
33256
33582
|
const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
|
|
33257
33583
|
const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
|
|
@@ -33812,6 +34138,8 @@ class InteractiveGraph extends React.Component {
|
|
|
33812
34138
|
return props.graph;
|
|
33813
34139
|
}
|
|
33814
34140
|
}
|
|
34141
|
+
|
|
34142
|
+
// exported for testing
|
|
33815
34143
|
InteractiveGraph.defaultProps = {
|
|
33816
34144
|
labels: ["x", "y"],
|
|
33817
34145
|
range: [[-10, 10], [-10, 10]],
|
|
@@ -33827,6 +34155,31 @@ InteractiveGraph.defaultProps = {
|
|
|
33827
34155
|
type: "linear"
|
|
33828
34156
|
}
|
|
33829
34157
|
};
|
|
34158
|
+
function shouldUseMafs(mafsFlags, graph) {
|
|
34159
|
+
if (typeof mafsFlags === "boolean" || typeof mafsFlags === "undefined") {
|
|
34160
|
+
return false;
|
|
34161
|
+
}
|
|
34162
|
+
switch (graph.type) {
|
|
34163
|
+
case "point":
|
|
34164
|
+
if (graph.numPoints === UNLIMITED) {
|
|
34165
|
+
// TODO(benchristel): add a feature flag for the "unlimited"
|
|
34166
|
+
// case once we've implemented point graphs with unlimited
|
|
34167
|
+
// points
|
|
34168
|
+
return false;
|
|
34169
|
+
}
|
|
34170
|
+
return Boolean(mafsFlags["point"]);
|
|
34171
|
+
case "polygon":
|
|
34172
|
+
if (graph.numSides === UNLIMITED) {
|
|
34173
|
+
// TODO(benchristel): add a feature flag for the "unlimited"
|
|
34174
|
+
// case once we've implemented polygon graphs with unlimited
|
|
34175
|
+
// sides
|
|
34176
|
+
return false;
|
|
34177
|
+
}
|
|
34178
|
+
return Boolean(mafsFlags["polygon"]);
|
|
34179
|
+
default:
|
|
34180
|
+
return Boolean(mafsFlags[graph.type]);
|
|
34181
|
+
}
|
|
34182
|
+
}
|
|
33830
34183
|
var InteractiveGraph$1 = {
|
|
33831
34184
|
name: "interactive-graph",
|
|
33832
34185
|
displayName: "Interactive graph",
|
|
@@ -44042,7 +44395,7 @@ const init = function init(options) {
|
|
|
44042
44395
|
|
|
44043
44396
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44044
44397
|
const libName = "@khanacademy/perseus";
|
|
44045
|
-
const libVersion = "24.
|
|
44398
|
+
const libVersion = "24.1.0";
|
|
44046
44399
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44047
44400
|
|
|
44048
44401
|
const apiVersion = {
|