@khanacademy/perseus 22.3.0 → 22.4.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.css +3 -1
- package/dist/es/index.js +688 -459
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +3 -1
- package/dist/index.js +709 -471
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +1 -0
- package/dist/perseus-types.d.ts +0 -1
- package/dist/server-item-renderer.d.ts +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/widgets/expression.d.ts +6 -0
- package/dist/widgets/interactive-graphs/graphs/circle.d.ts +5 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-point-view.d.ts +17 -0
- package/dist/widgets/interactive-graphs/graphs/components/segment.d.ts +27 -0
- package/dist/widgets/interactive-graphs/graphs/index.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/utils.d.ts +1 -0
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +9 -8
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +1 -1
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +13 -1
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-state.d.ts +8 -1
- package/dist/widgets/interactive-graphs/types.d.ts +2 -2
- package/dist/widgets/label-image.d.ts +2 -0
- package/package.json +2 -2
- package/dist/widgets/interactive-graphs/graphs/components/movable-line.d.ts +0 -17
- package/dist/widgets/interactive-graphs/graphs/types.d.ts +0 -6
package/dist/es/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { View, RenderStateRoot, useUniqueIdWithMock, UniqueIDProvider, Text as T
|
|
|
6
6
|
import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
|
|
7
7
|
import classNames from 'classnames';
|
|
8
8
|
import * as React from 'react';
|
|
9
|
-
import React__default, { useContext, useState, useEffect, useRef, createContext, useImperativeHandle } from 'react';
|
|
9
|
+
import React__default, { useContext, useState, useEffect, useRef, createContext, forwardRef, useImperativeHandle } from 'react';
|
|
10
10
|
import _, { debounce as debounce$1 } from 'underscore';
|
|
11
11
|
import Clickable from '@khanacademy/wonder-blocks-clickable';
|
|
12
12
|
import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder-blocks-popover';
|
|
@@ -26,10 +26,10 @@ import Button from '@khanacademy/wonder-blocks-button';
|
|
|
26
26
|
import { Strut } from '@khanacademy/wonder-blocks-layout';
|
|
27
27
|
import Banner from '@khanacademy/wonder-blocks-banner';
|
|
28
28
|
import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-blocks-dropdown';
|
|
29
|
-
import caretDown from '@phosphor-icons/core/
|
|
30
|
-
import caretUp from '@phosphor-icons/core/
|
|
29
|
+
import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
|
|
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$3, Point as Point$2, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
|
|
32
|
+
import { vec, Line as Line$3, Point as Point$2, useMovable, Text as Text$1, Polygon, Circle as Circle$1, 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';
|
|
@@ -341,7 +341,7 @@ class InnerMathInput extends React.Component {
|
|
|
341
341
|
locale
|
|
342
342
|
} = this.context;
|
|
343
343
|
// Initialize MathQuill.MathField instance
|
|
344
|
-
this.__mathField = createMathField(this.__mathFieldWrapperRef, this.props.mathInputStrings, baseConfig => _extends({}, baseConfig, {
|
|
344
|
+
this.__mathField = createMathField(this.__mathFieldWrapperRef, locale, this.props.mathInputStrings, baseConfig => _extends({}, baseConfig, {
|
|
345
345
|
handlers: {
|
|
346
346
|
edit: debounce(mathField => {
|
|
347
347
|
// This handler is guaranteed to be called on change, but
|
|
@@ -28726,6 +28726,7 @@ function Arrowhead(props) {
|
|
|
28726
28726
|
var _props$color;
|
|
28727
28727
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
28728
28728
|
return /*#__PURE__*/React.createElement("g", {
|
|
28729
|
+
className: "interactive-graph-arrowhead",
|
|
28729
28730
|
transform: `translate(${point[0]} ${point[1]}) rotate(${-props.angle})`
|
|
28730
28731
|
}, /*#__PURE__*/React.createElement("g", {
|
|
28731
28732
|
transform: "translate(-1.5)"
|
|
@@ -28740,49 +28741,7 @@ function Arrowhead(props) {
|
|
|
28740
28741
|
})));
|
|
28741
28742
|
}
|
|
28742
28743
|
|
|
28743
|
-
function
|
|
28744
|
-
const {
|
|
28745
|
-
start,
|
|
28746
|
-
end,
|
|
28747
|
-
style,
|
|
28748
|
-
className,
|
|
28749
|
-
testId
|
|
28750
|
-
} = props;
|
|
28751
|
-
return /*#__PURE__*/React.createElement("line", {
|
|
28752
|
-
x1: start[0],
|
|
28753
|
-
y1: start[1],
|
|
28754
|
-
x2: end[0],
|
|
28755
|
-
y2: end[1],
|
|
28756
|
-
style: style,
|
|
28757
|
-
className: className,
|
|
28758
|
-
"data-testid": testId
|
|
28759
|
-
});
|
|
28760
|
-
}
|
|
28761
|
-
|
|
28762
|
-
function Vector(props) {
|
|
28763
|
-
const {
|
|
28764
|
-
tail,
|
|
28765
|
-
tip,
|
|
28766
|
-
color,
|
|
28767
|
-
style
|
|
28768
|
-
} = props;
|
|
28769
|
-
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
28770
|
-
const direction = vec.sub(tip, tail);
|
|
28771
|
-
return /*#__PURE__*/React.createElement("g", {
|
|
28772
|
-
style: {
|
|
28773
|
-
stroke: color,
|
|
28774
|
-
strokeWidth: 2
|
|
28775
|
-
}
|
|
28776
|
-
}, /*#__PURE__*/React.createElement(SVGLine, {
|
|
28777
|
-
start: tailPx,
|
|
28778
|
-
end: tipPx,
|
|
28779
|
-
style: style
|
|
28780
|
-
}), /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28781
|
-
tip: tip,
|
|
28782
|
-
angle: angleDegrees(direction)
|
|
28783
|
-
}));
|
|
28784
|
-
}
|
|
28785
|
-
function angleDegrees([x, y]) {
|
|
28744
|
+
function calculateAngleInDegrees([x, y]) {
|
|
28786
28745
|
return Math.atan2(y, x) * 180 / Math.PI;
|
|
28787
28746
|
}
|
|
28788
28747
|
|
|
@@ -28824,10 +28783,57 @@ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
|
|
|
28824
28783
|
return [xMax, yAtXMax];
|
|
28825
28784
|
}
|
|
28826
28785
|
};
|
|
28827
|
-
const getLines = points =>
|
|
28828
|
-
|
|
28829
|
-
|
|
28830
|
-
|
|
28786
|
+
const getLines = points => {
|
|
28787
|
+
return points.map((point, i) => {
|
|
28788
|
+
const next = points[(i + 1) % points.length];
|
|
28789
|
+
return [point, next];
|
|
28790
|
+
});
|
|
28791
|
+
};
|
|
28792
|
+
|
|
28793
|
+
function SVGLine(props) {
|
|
28794
|
+
const {
|
|
28795
|
+
start,
|
|
28796
|
+
end,
|
|
28797
|
+
style,
|
|
28798
|
+
className,
|
|
28799
|
+
testId
|
|
28800
|
+
} = props;
|
|
28801
|
+
return /*#__PURE__*/React.createElement("line", {
|
|
28802
|
+
x1: start[0],
|
|
28803
|
+
y1: start[1],
|
|
28804
|
+
x2: end[0],
|
|
28805
|
+
y2: end[1],
|
|
28806
|
+
style: style,
|
|
28807
|
+
className: className,
|
|
28808
|
+
"data-testid": testId
|
|
28809
|
+
});
|
|
28810
|
+
}
|
|
28811
|
+
|
|
28812
|
+
function Vector(props) {
|
|
28813
|
+
const {
|
|
28814
|
+
tail,
|
|
28815
|
+
tip,
|
|
28816
|
+
color,
|
|
28817
|
+
style
|
|
28818
|
+
} = props;
|
|
28819
|
+
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
28820
|
+
const direction = vec.sub(tip, tail);
|
|
28821
|
+
const angle = calculateAngleInDegrees(direction);
|
|
28822
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
28823
|
+
style: {
|
|
28824
|
+
stroke: color,
|
|
28825
|
+
strokeWidth: 2
|
|
28826
|
+
}
|
|
28827
|
+
}, /*#__PURE__*/React.createElement(SVGLine, {
|
|
28828
|
+
start: tailPx,
|
|
28829
|
+
end: tipPx,
|
|
28830
|
+
style: style
|
|
28831
|
+
}), /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28832
|
+
angle: angle,
|
|
28833
|
+
tip: tip,
|
|
28834
|
+
color: color
|
|
28835
|
+
}));
|
|
28836
|
+
}
|
|
28831
28837
|
|
|
28832
28838
|
const LockedLine = props => {
|
|
28833
28839
|
const {
|
|
@@ -28843,27 +28849,38 @@ const LockedLine = props => {
|
|
|
28843
28849
|
let line;
|
|
28844
28850
|
if (kind === "ray") {
|
|
28845
28851
|
// Rays extend to the end of the graph in one direction.
|
|
28846
|
-
const
|
|
28852
|
+
const extendedPoint = getIntersectionOfRayWithBox(point2.coord, point1.coord, range);
|
|
28847
28853
|
line = /*#__PURE__*/React.createElement(Vector, {
|
|
28848
28854
|
tail: point1.coord,
|
|
28849
|
-
tip:
|
|
28855
|
+
tip: extendedPoint,
|
|
28850
28856
|
color: lockedFigureColors[color$1],
|
|
28851
28857
|
style: {
|
|
28852
|
-
strokeDasharray: lineStyle === "dashed" ?
|
|
28853
|
-
// TODO(lems-1930): Uncomment this line when the
|
|
28854
|
-
// dashed style is updated in Mafs.
|
|
28855
|
-
// ? "var(--mafs-line-stroke-dash-style)"
|
|
28856
|
-
"4, 3" : undefined
|
|
28858
|
+
strokeDasharray: lineStyle === "dashed" ? "var(--mafs-line-stroke-dash-style)" : undefined
|
|
28857
28859
|
}
|
|
28858
28860
|
});
|
|
28859
28861
|
} else {
|
|
28860
28862
|
const LineType = kind === "segment" ? Line$3.Segment : Line$3.ThroughPoints;
|
|
28861
|
-
|
|
28863
|
+
let arrowTip = kind === "segment" ? point2.coord : getIntersectionOfRayWithBox(point2.coord, point1.coord, range);
|
|
28864
|
+
const direction = vec.sub(point2.coord, point1.coord);
|
|
28865
|
+
let angle = calculateAngleInDegrees(direction);
|
|
28866
|
+
const startArrowHead = kind !== "segment" && /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28867
|
+
angle: angle,
|
|
28868
|
+
tip: arrowTip,
|
|
28869
|
+
color: lockedFigureColors[color$1]
|
|
28870
|
+
});
|
|
28871
|
+
arrowTip = kind === "segment" ? point1.coord : getIntersectionOfRayWithBox(point1.coord, point2.coord, range);
|
|
28872
|
+
angle = angle > 180 ? angle - 180 : angle + 180;
|
|
28873
|
+
const endArrowHead = kind !== "segment" && /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28874
|
+
angle: angle,
|
|
28875
|
+
tip: arrowTip,
|
|
28876
|
+
color: lockedFigureColors[color$1]
|
|
28877
|
+
});
|
|
28878
|
+
line = /*#__PURE__*/React.createElement(React.Fragment, null, startArrowHead, /*#__PURE__*/React.createElement(LineType, {
|
|
28862
28879
|
point1: point1.coord,
|
|
28863
28880
|
point2: point2.coord,
|
|
28864
28881
|
color: lockedFigureColors[color$1],
|
|
28865
28882
|
style: lineStyle
|
|
28866
|
-
});
|
|
28883
|
+
}), endArrowHead);
|
|
28867
28884
|
}
|
|
28868
28885
|
return /*#__PURE__*/React.createElement("g", {
|
|
28869
28886
|
className: kind === "ray" ? "locked-ray" : "locked-line"
|
|
@@ -28969,6 +28986,20 @@ function movePoint(index, destination) {
|
|
|
28969
28986
|
destination
|
|
28970
28987
|
};
|
|
28971
28988
|
}
|
|
28989
|
+
const MOVE_CENTER = "move-center";
|
|
28990
|
+
function moveCenter(destination) {
|
|
28991
|
+
return {
|
|
28992
|
+
type: MOVE_CENTER,
|
|
28993
|
+
destination
|
|
28994
|
+
};
|
|
28995
|
+
}
|
|
28996
|
+
const MOVE_RADIUS_POINT = "MOVE_RADIUS_POINT";
|
|
28997
|
+
function moveRadiusPoint(destination) {
|
|
28998
|
+
return {
|
|
28999
|
+
type: MOVE_RADIUS_POINT,
|
|
29000
|
+
destination
|
|
29001
|
+
};
|
|
29002
|
+
}
|
|
28972
29003
|
const CHANGE_SNAP_STEP = "change-snap-step";
|
|
28973
29004
|
function changeSnapStep(snapStep) {
|
|
28974
29005
|
return {
|
|
@@ -29015,6 +29046,192 @@ function snap(snapStep, point) {
|
|
|
29015
29046
|
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
29016
29047
|
}
|
|
29017
29048
|
|
|
29049
|
+
// The hitbox size of 48px by 48px is preserved from the legacy interactive
|
|
29050
|
+
// graph.
|
|
29051
|
+
const hitboxSizePx = 48;
|
|
29052
|
+
|
|
29053
|
+
// MovablePointView is a purely presentational component (i.e. it is a pure
|
|
29054
|
+
// function with no state or effects) that renders the SVG for a movable point
|
|
29055
|
+
// on an interactive graph.
|
|
29056
|
+
//
|
|
29057
|
+
// It has two modes for managing tabbing / focus: "controlled" (where the caller
|
|
29058
|
+
// manages the display of the focus ring, and the point itself cannot be
|
|
29059
|
+
// focused) and "uncontrolled" (where the point is focusable, and the browser
|
|
29060
|
+
// manages the focus state and styling). For context on why we did this, see
|
|
29061
|
+
// the description of https://github.com/Khan/perseus/pull/1240
|
|
29062
|
+
const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
29063
|
+
var _Object$entries$find$, _Object$entries$find;
|
|
29064
|
+
const {
|
|
29065
|
+
range,
|
|
29066
|
+
markings,
|
|
29067
|
+
showTooltips
|
|
29068
|
+
} = useGraphConfig();
|
|
29069
|
+
const {
|
|
29070
|
+
point,
|
|
29071
|
+
color: color$1 = color.blue,
|
|
29072
|
+
dragging,
|
|
29073
|
+
focusBehavior
|
|
29074
|
+
} = props;
|
|
29075
|
+
|
|
29076
|
+
// WB Tooltip requires a color name for the background color.
|
|
29077
|
+
// Since the color in props is a hex value, a reverse lookup is needed.
|
|
29078
|
+
const wbColorName = (_Object$entries$find$ = (_Object$entries$find = Object.entries(color).find(([_, value]) => value === color$1)) == null ? void 0 : _Object$entries$find[0]) != null ? _Object$entries$find$ : "blue";
|
|
29079
|
+
const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""} ${focusClass(focusBehavior)}`;
|
|
29080
|
+
const [[x, y]] = useTransformVectorsToPixels(point);
|
|
29081
|
+
const [xMin, xMax] = range[0];
|
|
29082
|
+
const [yMin, yMax] = range[1];
|
|
29083
|
+
const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
|
|
29084
|
+
const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
|
|
29085
|
+
const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
|
|
29086
|
+
const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
|
|
29087
|
+
const showHairlines = dragging && markings !== "none";
|
|
29088
|
+
const hairlines = /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("line", {
|
|
29089
|
+
x1: verticalStartX,
|
|
29090
|
+
y1: y,
|
|
29091
|
+
x2: verticalEndX,
|
|
29092
|
+
y2: y,
|
|
29093
|
+
stroke: color$1
|
|
29094
|
+
}), /*#__PURE__*/React.createElement("line", {
|
|
29095
|
+
x1: x,
|
|
29096
|
+
y1: horizontalStartY,
|
|
29097
|
+
x2: x,
|
|
29098
|
+
y2: horizontalEndY,
|
|
29099
|
+
stroke: color$1
|
|
29100
|
+
}));
|
|
29101
|
+
const svgForPoint = /*#__PURE__*/React.createElement("g", {
|
|
29102
|
+
ref: hitboxRef,
|
|
29103
|
+
className: pointClasses,
|
|
29104
|
+
style: {
|
|
29105
|
+
"--movable-point-color": color$1
|
|
29106
|
+
},
|
|
29107
|
+
"data-testid": "movable-point",
|
|
29108
|
+
tabIndex: tabIndex(focusBehavior)
|
|
29109
|
+
}, /*#__PURE__*/React.createElement("circle", {
|
|
29110
|
+
className: "movable-point-hitbox",
|
|
29111
|
+
r: hitboxSizePx / 2,
|
|
29112
|
+
cx: x,
|
|
29113
|
+
cy: y
|
|
29114
|
+
}), /*#__PURE__*/React.createElement("circle", {
|
|
29115
|
+
className: "movable-point-halo",
|
|
29116
|
+
cx: x,
|
|
29117
|
+
cy: y
|
|
29118
|
+
}), /*#__PURE__*/React.createElement("circle", {
|
|
29119
|
+
className: "movable-point-ring",
|
|
29120
|
+
cx: x,
|
|
29121
|
+
cy: y
|
|
29122
|
+
}), /*#__PURE__*/React.createElement("circle", {
|
|
29123
|
+
className: "movable-point-focus-outline",
|
|
29124
|
+
cx: x,
|
|
29125
|
+
cy: y
|
|
29126
|
+
}), /*#__PURE__*/React.createElement("circle", {
|
|
29127
|
+
className: "movable-point-center",
|
|
29128
|
+
cx: x,
|
|
29129
|
+
cy: y,
|
|
29130
|
+
style: {
|
|
29131
|
+
fill: color$1
|
|
29132
|
+
},
|
|
29133
|
+
"data-testid": "movable-point__center"
|
|
29134
|
+
}));
|
|
29135
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, showHairlines && hairlines, showTooltips ? /*#__PURE__*/React.createElement(Tooltip$1, {
|
|
29136
|
+
autoUpdate: true,
|
|
29137
|
+
backgroundColor: wbColorName,
|
|
29138
|
+
content: `(${point[0]}, ${point[1]})`,
|
|
29139
|
+
contentStyle: {
|
|
29140
|
+
color: "white"
|
|
29141
|
+
}
|
|
29142
|
+
}, svgForPoint) : svgForPoint);
|
|
29143
|
+
});
|
|
29144
|
+
function focusClass(config) {
|
|
29145
|
+
if (config.type === "controlled" && config.showFocusRing) {
|
|
29146
|
+
return "movable-point--focus";
|
|
29147
|
+
}
|
|
29148
|
+
return "";
|
|
29149
|
+
}
|
|
29150
|
+
function tabIndex(config) {
|
|
29151
|
+
if (config.type === "uncontrolled") {
|
|
29152
|
+
return config.tabIndex;
|
|
29153
|
+
}
|
|
29154
|
+
return undefined;
|
|
29155
|
+
}
|
|
29156
|
+
|
|
29157
|
+
const Segment = props => {
|
|
29158
|
+
const {
|
|
29159
|
+
onMoveLine,
|
|
29160
|
+
color,
|
|
29161
|
+
points: [start, end],
|
|
29162
|
+
extend
|
|
29163
|
+
} = props;
|
|
29164
|
+
|
|
29165
|
+
// We use separate focusableHandle elements, instead of letting the movable
|
|
29166
|
+
// points themselves be focusable, to allow the tab order of the points to
|
|
29167
|
+
// be different from the rendering order. We had to solve for the following
|
|
29168
|
+
// constraints:
|
|
29169
|
+
// - SVG has no equivalent of z-index, so the order of elements in the
|
|
29170
|
+
// document determines the order in which they're painted. We want the
|
|
29171
|
+
// movable line segment to render behind its endpoints (it looks weird
|
|
29172
|
+
// otherwise) so we have to render the line first.
|
|
29173
|
+
// - There isn't a browser-native way to customize tab order, other than
|
|
29174
|
+
// setting tabindex > 0. But that bumps elements to the front of the
|
|
29175
|
+
// tab order for the entire page, which is not what we want.
|
|
29176
|
+
const {
|
|
29177
|
+
visiblePoint: visiblePoint1,
|
|
29178
|
+
focusableHandle: focusableHandle1
|
|
29179
|
+
} = useControlPoint(start, color, p => props.onMovePoint(0, p));
|
|
29180
|
+
const {
|
|
29181
|
+
visiblePoint: visiblePoint2,
|
|
29182
|
+
focusableHandle: focusableHandle2
|
|
29183
|
+
} = useControlPoint(end, color, p => props.onMovePoint(1, p));
|
|
29184
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, focusableHandle1, /*#__PURE__*/React.createElement(MovableLine, {
|
|
29185
|
+
start: start,
|
|
29186
|
+
end: end,
|
|
29187
|
+
stroke: color,
|
|
29188
|
+
extend: extend,
|
|
29189
|
+
onMove: onMoveLine
|
|
29190
|
+
}), focusableHandle2, visiblePoint1, visiblePoint2);
|
|
29191
|
+
};
|
|
29192
|
+
function useControlPoint(point, color, onMovePoint) {
|
|
29193
|
+
const {
|
|
29194
|
+
snapStep
|
|
29195
|
+
} = useGraphConfig();
|
|
29196
|
+
const [focused, setFocused] = useState(false);
|
|
29197
|
+
const keyboardHandleRef = useRef(null);
|
|
29198
|
+
useMovable({
|
|
29199
|
+
gestureTarget: keyboardHandleRef,
|
|
29200
|
+
point,
|
|
29201
|
+
onMove: onMovePoint,
|
|
29202
|
+
constrain: p => snap(snapStep, p)
|
|
29203
|
+
});
|
|
29204
|
+
const visiblePointRef = useRef(null);
|
|
29205
|
+
const {
|
|
29206
|
+
dragging
|
|
29207
|
+
} = useMovable({
|
|
29208
|
+
gestureTarget: visiblePointRef,
|
|
29209
|
+
point,
|
|
29210
|
+
onMove: onMovePoint,
|
|
29211
|
+
constrain: p => snap(snapStep, p)
|
|
29212
|
+
});
|
|
29213
|
+
const focusableHandle = /*#__PURE__*/React.createElement("g", {
|
|
29214
|
+
"data-testid": "movable-point__focusable-handle",
|
|
29215
|
+
tabIndex: 0,
|
|
29216
|
+
ref: keyboardHandleRef,
|
|
29217
|
+
onFocus: () => setFocused(true),
|
|
29218
|
+
onBlur: () => setFocused(false)
|
|
29219
|
+
});
|
|
29220
|
+
const visiblePoint = /*#__PURE__*/React.createElement(MovablePointView, {
|
|
29221
|
+
point: point,
|
|
29222
|
+
dragging: dragging,
|
|
29223
|
+
color: color,
|
|
29224
|
+
ref: visiblePointRef,
|
|
29225
|
+
focusBehavior: {
|
|
29226
|
+
type: "controlled",
|
|
29227
|
+
showFocusRing: focused
|
|
29228
|
+
}
|
|
29229
|
+
});
|
|
29230
|
+
return {
|
|
29231
|
+
focusableHandle,
|
|
29232
|
+
visiblePoint
|
|
29233
|
+
};
|
|
29234
|
+
}
|
|
29018
29235
|
const defaultStroke = "var(--movable-line-stroke-color)";
|
|
29019
29236
|
const MovableLine = props => {
|
|
29020
29237
|
const {
|
|
@@ -29027,12 +29244,13 @@ const MovableLine = props => {
|
|
|
29027
29244
|
const midpoint = vec.midpoint(start, end);
|
|
29028
29245
|
const [startPtPx, endPtPx] = useTransformVectorsToPixels(start, end);
|
|
29029
29246
|
const {
|
|
29247
|
+
range,
|
|
29030
29248
|
graphDimensionsInPixels
|
|
29031
29249
|
} = useGraphConfig();
|
|
29032
29250
|
let startExtend = undefined;
|
|
29033
29251
|
let endExtend = undefined;
|
|
29034
29252
|
if (extend) {
|
|
29035
|
-
const trimmedRange = trimRange(
|
|
29253
|
+
const trimmedRange = trimRange(range, graphDimensionsInPixels);
|
|
29036
29254
|
startExtend = extend.start ? getIntersectionOfRayWithBox(start, end, trimmedRange) : undefined;
|
|
29037
29255
|
endExtend = extend.end ? getIntersectionOfRayWithBox(end, start, trimmedRange) : undefined;
|
|
29038
29256
|
}
|
|
@@ -29111,101 +29329,6 @@ function size(interval) {
|
|
|
29111
29329
|
return interval[1] - interval[0];
|
|
29112
29330
|
}
|
|
29113
29331
|
|
|
29114
|
-
// The hitbox size of 48px by 48px is preserved from the legacy interactive
|
|
29115
|
-
// graph.
|
|
29116
|
-
const hitboxSizePx = 48;
|
|
29117
|
-
const StyledMovablePoint = props => {
|
|
29118
|
-
var _Object$entries$find$, _Object$entries$find;
|
|
29119
|
-
const {
|
|
29120
|
-
range,
|
|
29121
|
-
snapStep,
|
|
29122
|
-
markings,
|
|
29123
|
-
showTooltips
|
|
29124
|
-
} = useGraphConfig();
|
|
29125
|
-
const hitboxRef = useRef(null);
|
|
29126
|
-
const {
|
|
29127
|
-
point,
|
|
29128
|
-
onMove,
|
|
29129
|
-
color: color$1 = color.blue
|
|
29130
|
-
} = props;
|
|
29131
|
-
|
|
29132
|
-
// WB Tooltip requires a color name for the background color.
|
|
29133
|
-
// Since the color in props is a hex value, a reverse lookup is needed.
|
|
29134
|
-
const wbColorName = (_Object$entries$find$ = (_Object$entries$find = Object.entries(color).find(([_, value]) => value === color$1)) == null ? void 0 : _Object$entries$find[0]) != null ? _Object$entries$find$ : "blue";
|
|
29135
|
-
const {
|
|
29136
|
-
dragging
|
|
29137
|
-
} = useMovable({
|
|
29138
|
-
gestureTarget: hitboxRef,
|
|
29139
|
-
point,
|
|
29140
|
-
onMove,
|
|
29141
|
-
constrain: p => snap(snapStep, p)
|
|
29142
|
-
});
|
|
29143
|
-
const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""}`;
|
|
29144
|
-
const [[x, y]] = useTransformVectorsToPixels(point);
|
|
29145
|
-
const [xMin, xMax] = range[0];
|
|
29146
|
-
const [yMin, yMax] = range[1];
|
|
29147
|
-
const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
|
|
29148
|
-
const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
|
|
29149
|
-
const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
|
|
29150
|
-
const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
|
|
29151
|
-
const showHairlines = dragging && markings !== "none";
|
|
29152
|
-
const hairlines = /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("line", {
|
|
29153
|
-
x1: verticalStartX,
|
|
29154
|
-
y1: y,
|
|
29155
|
-
x2: verticalEndX,
|
|
29156
|
-
y2: y,
|
|
29157
|
-
stroke: color$1
|
|
29158
|
-
}), /*#__PURE__*/React.createElement("line", {
|
|
29159
|
-
x1: x,
|
|
29160
|
-
y1: horizontalStartY,
|
|
29161
|
-
x2: x,
|
|
29162
|
-
y2: horizontalEndY,
|
|
29163
|
-
stroke: color$1
|
|
29164
|
-
}));
|
|
29165
|
-
const svgForPoint = /*#__PURE__*/React.createElement("g", {
|
|
29166
|
-
ref: hitboxRef,
|
|
29167
|
-
className: pointClasses,
|
|
29168
|
-
tabIndex: 0,
|
|
29169
|
-
style: {
|
|
29170
|
-
"--movable-point-color": color$1
|
|
29171
|
-
},
|
|
29172
|
-
"data-testid": "movable-point"
|
|
29173
|
-
}, /*#__PURE__*/React.createElement("circle", {
|
|
29174
|
-
className: "movable-point-hitbox",
|
|
29175
|
-
r: hitboxSizePx / 2,
|
|
29176
|
-
cx: x,
|
|
29177
|
-
cy: y
|
|
29178
|
-
}), /*#__PURE__*/React.createElement("circle", {
|
|
29179
|
-
className: "movable-point-halo",
|
|
29180
|
-
cx: x,
|
|
29181
|
-
cy: y
|
|
29182
|
-
}), /*#__PURE__*/React.createElement("circle", {
|
|
29183
|
-
className: "movable-point-ring",
|
|
29184
|
-
cx: x,
|
|
29185
|
-
cy: y
|
|
29186
|
-
}), /*#__PURE__*/React.createElement("circle", {
|
|
29187
|
-
className: "movable-point-focus-outline",
|
|
29188
|
-
cx: x,
|
|
29189
|
-
cy: y
|
|
29190
|
-
}), /*#__PURE__*/React.createElement("circle", {
|
|
29191
|
-
className: "movable-point-center",
|
|
29192
|
-
cx: x,
|
|
29193
|
-
cy: y,
|
|
29194
|
-
style: {
|
|
29195
|
-
fill: color$1
|
|
29196
|
-
},
|
|
29197
|
-
"data-testid": "movable-point__center"
|
|
29198
|
-
}));
|
|
29199
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, showHairlines && hairlines, showTooltips ? /*#__PURE__*/React.createElement(Tooltip$1, {
|
|
29200
|
-
autoUpdate: true,
|
|
29201
|
-
backgroundColor: wbColorName,
|
|
29202
|
-
content: `(${point[0]}, ${point[1]})`,
|
|
29203
|
-
contentStyle: {
|
|
29204
|
-
color: "white"
|
|
29205
|
-
}
|
|
29206
|
-
}, svgForPoint) : svgForPoint);
|
|
29207
|
-
};
|
|
29208
|
-
|
|
29209
29332
|
const SegmentGraph = props => {
|
|
29210
29333
|
const {
|
|
29211
29334
|
dispatch
|
|
@@ -29213,7 +29336,7 @@ const SegmentGraph = props => {
|
|
|
29213
29336
|
const {
|
|
29214
29337
|
coords: segments
|
|
29215
29338
|
} = props.graphState;
|
|
29216
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, segments == null ? void 0 : segments.map((segment, i) => /*#__PURE__*/React.createElement(
|
|
29339
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, segments == null ? void 0 : segments.map((segment, i) => /*#__PURE__*/React.createElement(Segment, {
|
|
29217
29340
|
key: i,
|
|
29218
29341
|
points: segment,
|
|
29219
29342
|
onMoveLine: delta => {
|
|
@@ -29224,27 +29347,6 @@ const SegmentGraph = props => {
|
|
|
29224
29347
|
}
|
|
29225
29348
|
})));
|
|
29226
29349
|
};
|
|
29227
|
-
const SegmentView = props => {
|
|
29228
|
-
const {
|
|
29229
|
-
onMoveLine: onMoveSegment,
|
|
29230
|
-
points: [start, end]
|
|
29231
|
-
} = props;
|
|
29232
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(MovableLine, {
|
|
29233
|
-
start: start,
|
|
29234
|
-
end: end,
|
|
29235
|
-
onMove: onMoveSegment
|
|
29236
|
-
}), /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29237
|
-
point: start,
|
|
29238
|
-
onMove: newPoint => {
|
|
29239
|
-
props.onMovePoint(0, newPoint);
|
|
29240
|
-
}
|
|
29241
|
-
}), /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29242
|
-
point: end,
|
|
29243
|
-
onMove: newPoint => {
|
|
29244
|
-
props.onMovePoint(1, newPoint);
|
|
29245
|
-
}
|
|
29246
|
-
}));
|
|
29247
|
-
};
|
|
29248
29350
|
|
|
29249
29351
|
const LinearGraph = props => {
|
|
29250
29352
|
const {
|
|
@@ -29254,49 +29356,19 @@ const LinearGraph = props => {
|
|
|
29254
29356
|
coords: lines
|
|
29255
29357
|
} = props.graphState;
|
|
29256
29358
|
const colors = ["var(--movable-line-stroke-color)", "var(--mafs-violet)"];
|
|
29257
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, lines == null ? void 0 : lines.map((line, i) => /*#__PURE__*/React.createElement(
|
|
29359
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, lines == null ? void 0 : lines.map((line, i) => /*#__PURE__*/React.createElement(Segment, {
|
|
29258
29360
|
key: i,
|
|
29259
29361
|
points: line,
|
|
29260
29362
|
onMoveLine: delta => {
|
|
29261
29363
|
dispatch(moveLine(i, delta));
|
|
29262
29364
|
},
|
|
29263
|
-
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
|
|
29264
|
-
stroke: colors[i]
|
|
29265
|
-
})));
|
|
29266
|
-
};
|
|
29267
|
-
const LineView = props => {
|
|
29268
|
-
const {
|
|
29269
|
-
onMoveLine,
|
|
29270
|
-
onMovePoint,
|
|
29271
|
-
points: [start, end],
|
|
29272
|
-
stroke
|
|
29273
|
-
} = props;
|
|
29274
|
-
const {
|
|
29275
|
-
range
|
|
29276
|
-
} = useGraphConfig();
|
|
29277
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(MovableLine, {
|
|
29278
|
-
start: start,
|
|
29279
|
-
end: end,
|
|
29280
|
-
onMove: onMoveLine,
|
|
29281
29365
|
extend: {
|
|
29282
29366
|
start: true,
|
|
29283
|
-
end: true
|
|
29284
|
-
range
|
|
29367
|
+
end: true
|
|
29285
29368
|
},
|
|
29286
|
-
|
|
29287
|
-
|
|
29288
|
-
|
|
29289
|
-
onMove: newPoint => {
|
|
29290
|
-
onMovePoint(0, newPoint);
|
|
29291
|
-
},
|
|
29292
|
-
color: stroke
|
|
29293
|
-
}), /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29294
|
-
point: end,
|
|
29295
|
-
onMove: newPoint => {
|
|
29296
|
-
onMovePoint(1, newPoint);
|
|
29297
|
-
},
|
|
29298
|
-
color: stroke
|
|
29299
|
-
}));
|
|
29369
|
+
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
|
|
29370
|
+
color: colors[i]
|
|
29371
|
+
})));
|
|
29300
29372
|
};
|
|
29301
29373
|
|
|
29302
29374
|
const RayGraph = props => {
|
|
@@ -29304,30 +29376,21 @@ const RayGraph = props => {
|
|
|
29304
29376
|
dispatch
|
|
29305
29377
|
} = props;
|
|
29306
29378
|
const {
|
|
29307
|
-
coords: lines
|
|
29308
|
-
range
|
|
29379
|
+
coords: lines
|
|
29309
29380
|
} = props.graphState;
|
|
29310
|
-
|
|
29311
|
-
// a ray only has one line
|
|
29312
|
-
const [start, end] = lines[0];
|
|
29313
29381
|
const handleMoveLine = delta => dispatch(moveLine(0, delta));
|
|
29314
|
-
const handleMovePoint = (
|
|
29315
|
-
return /*#__PURE__*/React.createElement(
|
|
29316
|
-
|
|
29317
|
-
|
|
29318
|
-
|
|
29382
|
+
const handleMovePoint = (pointIndex, newPoint) => dispatch(moveControlPoint(pointIndex, newPoint, 0));
|
|
29383
|
+
return /*#__PURE__*/React.createElement(Segment
|
|
29384
|
+
// a ray only has one line
|
|
29385
|
+
, {
|
|
29386
|
+
points: lines[0],
|
|
29387
|
+
onMoveLine: handleMoveLine,
|
|
29388
|
+
onMovePoint: handleMovePoint,
|
|
29319
29389
|
extend: {
|
|
29320
|
-
end: true,
|
|
29321
29390
|
start: false,
|
|
29322
|
-
|
|
29391
|
+
end: true
|
|
29323
29392
|
}
|
|
29324
|
-
})
|
|
29325
|
-
point: start,
|
|
29326
|
-
onMove: newPoint => handleMovePoint(newPoint, 0)
|
|
29327
|
-
}), /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29328
|
-
point: end,
|
|
29329
|
-
onMove: newPoint => handleMovePoint(newPoint, 1)
|
|
29330
|
-
}));
|
|
29393
|
+
});
|
|
29331
29394
|
};
|
|
29332
29395
|
|
|
29333
29396
|
/**
|
|
@@ -29491,8 +29554,37 @@ const linesIntersect = ([[a, b], [c, d]], [[p, q], [r, s]]) => {
|
|
|
29491
29554
|
};
|
|
29492
29555
|
const isEven = n => n % 2 === 0;
|
|
29493
29556
|
|
|
29557
|
+
const StyledMovablePoint = props => {
|
|
29558
|
+
const {
|
|
29559
|
+
snapStep
|
|
29560
|
+
} = useGraphConfig();
|
|
29561
|
+
const elementRef = useRef(null);
|
|
29562
|
+
const {
|
|
29563
|
+
point,
|
|
29564
|
+
onMove,
|
|
29565
|
+
color: color$1 = color.blue
|
|
29566
|
+
} = props;
|
|
29567
|
+
const {
|
|
29568
|
+
dragging
|
|
29569
|
+
} = useMovable({
|
|
29570
|
+
gestureTarget: elementRef,
|
|
29571
|
+
point,
|
|
29572
|
+
onMove,
|
|
29573
|
+
constrain: p => snap(snapStep, p)
|
|
29574
|
+
});
|
|
29575
|
+
return /*#__PURE__*/React.createElement(MovablePointView, {
|
|
29576
|
+
ref: elementRef,
|
|
29577
|
+
point: point,
|
|
29578
|
+
color: color$1,
|
|
29579
|
+
dragging: dragging,
|
|
29580
|
+
focusBehavior: {
|
|
29581
|
+
type: "uncontrolled",
|
|
29582
|
+
tabIndex: 0
|
|
29583
|
+
}
|
|
29584
|
+
});
|
|
29585
|
+
};
|
|
29586
|
+
|
|
29494
29587
|
const PolygonGraph = props => {
|
|
29495
|
-
var _points$length;
|
|
29496
29588
|
const [focused, setFocused] = React.useState(false);
|
|
29497
29589
|
const [hovered, setHovered] = React.useState(false);
|
|
29498
29590
|
const {
|
|
@@ -29505,17 +29597,19 @@ const PolygonGraph = props => {
|
|
|
29505
29597
|
range,
|
|
29506
29598
|
snapStep
|
|
29507
29599
|
} = props.graphState;
|
|
29600
|
+
|
|
29601
|
+
// TODO(benchristel): can the default set of points be removed here? I don't
|
|
29602
|
+
// think coords can be null.
|
|
29508
29603
|
const points = coords != null ? coords : [[0, 0]];
|
|
29509
|
-
const pointsSum = points.reduce((acc, point) => vec.add(acc, point), [0, 0]);
|
|
29510
|
-
const midpoint = pointsSum && vec.scale(pointsSum, 1 / ((_points$length = points.length) != null ? _points$length : 1));
|
|
29511
29604
|
const ref = React.useRef(null);
|
|
29605
|
+
const dragReferencePoint = points[0];
|
|
29512
29606
|
const {
|
|
29513
29607
|
dragging
|
|
29514
29608
|
} = useMovable({
|
|
29515
29609
|
gestureTarget: ref,
|
|
29516
|
-
point:
|
|
29610
|
+
point: dragReferencePoint,
|
|
29517
29611
|
onMove: newPoint => {
|
|
29518
|
-
const delta = vec.sub(newPoint,
|
|
29612
|
+
const delta = vec.sub(newPoint, dragReferencePoint);
|
|
29519
29613
|
dispatch(moveAll(delta));
|
|
29520
29614
|
},
|
|
29521
29615
|
constrain: p => snap(snapStep, p)
|
|
@@ -29574,6 +29668,272 @@ const PolygonGraph = props => {
|
|
|
29574
29668
|
})));
|
|
29575
29669
|
};
|
|
29576
29670
|
|
|
29671
|
+
function initializeGraphState(params) {
|
|
29672
|
+
const {
|
|
29673
|
+
graph,
|
|
29674
|
+
step,
|
|
29675
|
+
snapStep,
|
|
29676
|
+
range,
|
|
29677
|
+
markings
|
|
29678
|
+
} = params;
|
|
29679
|
+
const shared = {
|
|
29680
|
+
hasBeenInteractedWith: false,
|
|
29681
|
+
range,
|
|
29682
|
+
snapStep,
|
|
29683
|
+
markings
|
|
29684
|
+
};
|
|
29685
|
+
switch (graph.type) {
|
|
29686
|
+
case "segment":
|
|
29687
|
+
return _extends({}, shared, {
|
|
29688
|
+
type: "segment",
|
|
29689
|
+
coords: getDefaultSegments({
|
|
29690
|
+
graph,
|
|
29691
|
+
step,
|
|
29692
|
+
range
|
|
29693
|
+
})
|
|
29694
|
+
});
|
|
29695
|
+
case "linear":
|
|
29696
|
+
case "linear-system":
|
|
29697
|
+
case "ray":
|
|
29698
|
+
return _extends({}, shared, {
|
|
29699
|
+
type: graph.type,
|
|
29700
|
+
// Linear and ray graphs have a single tuple of points, while a
|
|
29701
|
+
// linear system has two tuples of points.
|
|
29702
|
+
coords: getLineCoords({
|
|
29703
|
+
graph,
|
|
29704
|
+
range,
|
|
29705
|
+
step
|
|
29706
|
+
})
|
|
29707
|
+
});
|
|
29708
|
+
case "polygon":
|
|
29709
|
+
return _extends({}, shared, {
|
|
29710
|
+
type: "polygon",
|
|
29711
|
+
showAngles: Boolean(graph.showAngles),
|
|
29712
|
+
showSides: Boolean(graph.showSides),
|
|
29713
|
+
coords: getPolygonCoords({
|
|
29714
|
+
graph,
|
|
29715
|
+
range,
|
|
29716
|
+
step
|
|
29717
|
+
})
|
|
29718
|
+
});
|
|
29719
|
+
case "point":
|
|
29720
|
+
return _extends({}, shared, {
|
|
29721
|
+
type: graph.type,
|
|
29722
|
+
coords: getDefaultPoints({
|
|
29723
|
+
graph,
|
|
29724
|
+
step,
|
|
29725
|
+
range
|
|
29726
|
+
})
|
|
29727
|
+
});
|
|
29728
|
+
case "circle":
|
|
29729
|
+
return _extends({}, shared, {
|
|
29730
|
+
type: graph.type,
|
|
29731
|
+
center: [0, 0],
|
|
29732
|
+
radiusPoint: [1, 0]
|
|
29733
|
+
});
|
|
29734
|
+
case "angle":
|
|
29735
|
+
case "sinusoid":
|
|
29736
|
+
case "quadratic":
|
|
29737
|
+
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
29738
|
+
default:
|
|
29739
|
+
throw new UnreachableCaseError(graph);
|
|
29740
|
+
}
|
|
29741
|
+
}
|
|
29742
|
+
const getDefaultPoints = ({
|
|
29743
|
+
graph,
|
|
29744
|
+
range,
|
|
29745
|
+
step
|
|
29746
|
+
}) => {
|
|
29747
|
+
var _graph$coords;
|
|
29748
|
+
const numPoints = graph.numPoints || 1;
|
|
29749
|
+
let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
|
|
29750
|
+
if (coords) {
|
|
29751
|
+
return coords;
|
|
29752
|
+
}
|
|
29753
|
+
switch (numPoints) {
|
|
29754
|
+
case 1:
|
|
29755
|
+
// Back in the day, one point's coords were in graph.coord
|
|
29756
|
+
coords = [graph.coord || [0, 0]];
|
|
29757
|
+
break;
|
|
29758
|
+
case 2:
|
|
29759
|
+
coords = [[-5, 0], [5, 0]];
|
|
29760
|
+
break;
|
|
29761
|
+
case 3:
|
|
29762
|
+
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
29763
|
+
break;
|
|
29764
|
+
case 4:
|
|
29765
|
+
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
29766
|
+
break;
|
|
29767
|
+
case 5:
|
|
29768
|
+
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
29769
|
+
break;
|
|
29770
|
+
case 6:
|
|
29771
|
+
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
29772
|
+
break;
|
|
29773
|
+
default:
|
|
29774
|
+
coords = [];
|
|
29775
|
+
break;
|
|
29776
|
+
}
|
|
29777
|
+
// Transform coords from their -10 to 10 space to 0 to 1
|
|
29778
|
+
// because of the old graph.coord, and also it's easier.
|
|
29779
|
+
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
29780
|
+
return normalizePoints(range, step, newCoords);
|
|
29781
|
+
};
|
|
29782
|
+
|
|
29783
|
+
// TS v4 doesn't narrow return types, while v5 does.
|
|
29784
|
+
// Instead of updating to v5, using generic type to relate input and output types.
|
|
29785
|
+
function getGradableGraph(state, initialGraph) {
|
|
29786
|
+
if (!state.hasBeenInteractedWith) {
|
|
29787
|
+
return _extends({}, initialGraph);
|
|
29788
|
+
}
|
|
29789
|
+
if (initialGraph.type === "linear-system" && state.type === "linear-system") {
|
|
29790
|
+
return _extends({}, initialGraph, {
|
|
29791
|
+
coords: state.coords
|
|
29792
|
+
});
|
|
29793
|
+
}
|
|
29794
|
+
if (state.type === "segment" && initialGraph.type === "segment") {
|
|
29795
|
+
return _extends({}, initialGraph, {
|
|
29796
|
+
coords: state.coords
|
|
29797
|
+
});
|
|
29798
|
+
}
|
|
29799
|
+
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
29800
|
+
return _extends({}, initialGraph, {
|
|
29801
|
+
coords: state.coords[0]
|
|
29802
|
+
});
|
|
29803
|
+
}
|
|
29804
|
+
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
29805
|
+
return _extends({}, initialGraph, {
|
|
29806
|
+
coords: state.coords[0]
|
|
29807
|
+
});
|
|
29808
|
+
}
|
|
29809
|
+
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
29810
|
+
return _extends({}, initialGraph, {
|
|
29811
|
+
coords: state.coords
|
|
29812
|
+
});
|
|
29813
|
+
}
|
|
29814
|
+
if (state.type === "point" && initialGraph.type === "point") {
|
|
29815
|
+
return _extends({}, initialGraph, {
|
|
29816
|
+
coords: state.coords
|
|
29817
|
+
});
|
|
29818
|
+
}
|
|
29819
|
+
if (state.type === "circle" && initialGraph.type === "circle") {
|
|
29820
|
+
return _extends({}, initialGraph, {
|
|
29821
|
+
center: state.center,
|
|
29822
|
+
radius: getRadius(state)
|
|
29823
|
+
});
|
|
29824
|
+
}
|
|
29825
|
+
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29826
|
+
}
|
|
29827
|
+
const getDefaultSegments = ({
|
|
29828
|
+
graph,
|
|
29829
|
+
range,
|
|
29830
|
+
step
|
|
29831
|
+
}) => {
|
|
29832
|
+
const ys = n => {
|
|
29833
|
+
switch (n) {
|
|
29834
|
+
case 2:
|
|
29835
|
+
return [5, -5];
|
|
29836
|
+
case 3:
|
|
29837
|
+
return [5, 0, -5];
|
|
29838
|
+
case 4:
|
|
29839
|
+
return [6, 2, -2, -6];
|
|
29840
|
+
case 5:
|
|
29841
|
+
return [6, 3, 0, -3, -6];
|
|
29842
|
+
case 6:
|
|
29843
|
+
return [5, 3, 1, -1, -3, -5];
|
|
29844
|
+
default:
|
|
29845
|
+
return [5];
|
|
29846
|
+
}
|
|
29847
|
+
};
|
|
29848
|
+
const defaultRange = [[-10, 10], [-10, 10]];
|
|
29849
|
+
return ys(graph.numSegments).map(y => {
|
|
29850
|
+
let endpoints = [[-5, y], [5, y]];
|
|
29851
|
+
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
29852
|
+
endpoints = normalizePoints(range, step, endpoints);
|
|
29853
|
+
return endpoints;
|
|
29854
|
+
});
|
|
29855
|
+
};
|
|
29856
|
+
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
29857
|
+
const getLineCoords = ({
|
|
29858
|
+
graph,
|
|
29859
|
+
range,
|
|
29860
|
+
step
|
|
29861
|
+
}) => {
|
|
29862
|
+
var _graph$coords2;
|
|
29863
|
+
return (
|
|
29864
|
+
// Return two lines for a linear system, one for a ray or linear
|
|
29865
|
+
((_graph$coords2 = graph.coords) != null ? _graph$coords2 : graph.type === "linear-system") ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
29866
|
+
);
|
|
29867
|
+
};
|
|
29868
|
+
const getPolygonCoords = ({
|
|
29869
|
+
graph,
|
|
29870
|
+
range,
|
|
29871
|
+
step
|
|
29872
|
+
}) => {
|
|
29873
|
+
var _graph$coords3;
|
|
29874
|
+
let coords = (_graph$coords3 = graph.coords) == null ? void 0 : _graph$coords3.slice();
|
|
29875
|
+
if (coords) {
|
|
29876
|
+
return coords;
|
|
29877
|
+
}
|
|
29878
|
+
const n = graph.numSides || 3;
|
|
29879
|
+
if (n === "unlimited") {
|
|
29880
|
+
coords = [];
|
|
29881
|
+
} else {
|
|
29882
|
+
const angle = 2 * Math.PI / n;
|
|
29883
|
+
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
29884
|
+
|
|
29885
|
+
// TODO(alex): Generalize this to more than just triangles so that
|
|
29886
|
+
// all polygons have whole number side lengths if snapping to sides
|
|
29887
|
+
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
29888
|
+
|
|
29889
|
+
// Generate coords of a regular polygon with n sides
|
|
29890
|
+
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
29891
|
+
}
|
|
29892
|
+
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
29893
|
+
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
29894
|
+
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
29895
|
+
return coords;
|
|
29896
|
+
};
|
|
29897
|
+
|
|
29898
|
+
/**
|
|
29899
|
+
* determine radius of a circle graph
|
|
29900
|
+
*
|
|
29901
|
+
* @param graph - the graph object containing the circle
|
|
29902
|
+
* @returns the radius of the circle
|
|
29903
|
+
*/
|
|
29904
|
+
function getRadius(graph) {
|
|
29905
|
+
const [centerX, centerY] = graph.center;
|
|
29906
|
+
const [edgeX, edgeY] = graph.radiusPoint;
|
|
29907
|
+
return Math.sqrt(Math.pow(edgeX - centerX, 2) + Math.pow(edgeY - centerY, 2));
|
|
29908
|
+
}
|
|
29909
|
+
|
|
29910
|
+
function CircleGraph(props) {
|
|
29911
|
+
const {
|
|
29912
|
+
dispatch,
|
|
29913
|
+
graphState
|
|
29914
|
+
} = props;
|
|
29915
|
+
const {
|
|
29916
|
+
center,
|
|
29917
|
+
radiusPoint
|
|
29918
|
+
} = graphState;
|
|
29919
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Circle$1, {
|
|
29920
|
+
center: center,
|
|
29921
|
+
radius: getRadius(graphState),
|
|
29922
|
+
fillOpacity: 0,
|
|
29923
|
+
color: color.blue
|
|
29924
|
+
}), /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29925
|
+
point: center,
|
|
29926
|
+
onMove: newCenter => {
|
|
29927
|
+
dispatch(moveCenter(newCenter));
|
|
29928
|
+
}
|
|
29929
|
+
}), /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29930
|
+
point: radiusPoint,
|
|
29931
|
+
onMove: newRadiusPoint => {
|
|
29932
|
+
dispatch(moveRadiusPoint(newRadiusPoint));
|
|
29933
|
+
}
|
|
29934
|
+
}));
|
|
29935
|
+
}
|
|
29936
|
+
|
|
29577
29937
|
function PointGraph(props) {
|
|
29578
29938
|
const {
|
|
29579
29939
|
dispatch
|
|
@@ -30375,6 +30735,10 @@ function interactiveGraphReducer(state, action) {
|
|
|
30375
30735
|
return doMoveAll(state, action);
|
|
30376
30736
|
case MOVE_POINT:
|
|
30377
30737
|
return doMovePoint(state, action);
|
|
30738
|
+
case MOVE_CENTER:
|
|
30739
|
+
return doMoveCenter(state, action);
|
|
30740
|
+
case MOVE_RADIUS_POINT:
|
|
30741
|
+
return doMoveRadiusPoint(state, action);
|
|
30378
30742
|
case CHANGE_SNAP_STEP:
|
|
30379
30743
|
return doChangeSnapStep(state, action);
|
|
30380
30744
|
case CHANGE_RANGE:
|
|
@@ -30510,6 +30874,65 @@ function doMovePoint(state, action) {
|
|
|
30510
30874
|
throw new Error("The movePoint action is only for point and polygon graphs");
|
|
30511
30875
|
}
|
|
30512
30876
|
}
|
|
30877
|
+
function doMoveCenter(state, action) {
|
|
30878
|
+
switch (state.type) {
|
|
30879
|
+
case "circle":
|
|
30880
|
+
{
|
|
30881
|
+
// Constrain the center of the circle to the chart range
|
|
30882
|
+
const constrainedCenter = bound$1({
|
|
30883
|
+
snapStep: state.snapStep,
|
|
30884
|
+
range: state.range,
|
|
30885
|
+
point: action.destination
|
|
30886
|
+
});
|
|
30887
|
+
|
|
30888
|
+
// Reposition the radius point based on the new center
|
|
30889
|
+
// (spread to make sure we're not going to mutate anything)
|
|
30890
|
+
const newRadiusPoint = [...vec.add(state.radiusPoint, vec.sub(constrainedCenter, state.center))];
|
|
30891
|
+
|
|
30892
|
+
// Try to position the radius handle in a visible spot
|
|
30893
|
+
// if it otherwise would be off the chart
|
|
30894
|
+
// ex: if the handle is on the right and we move the center
|
|
30895
|
+
// to the rightmost position, move the handle to the left
|
|
30896
|
+
const [xMin, xMax] = state.range[0];
|
|
30897
|
+
const [radX] = newRadiusPoint;
|
|
30898
|
+
if (radX < xMin || radX > xMax) {
|
|
30899
|
+
const xJumpDist = (radX - constrainedCenter[0]) * 2;
|
|
30900
|
+
const possibleNewX = radX - xJumpDist;
|
|
30901
|
+
if (possibleNewX >= xMin && possibleNewX <= xMax) {
|
|
30902
|
+
newRadiusPoint[0] = possibleNewX;
|
|
30903
|
+
}
|
|
30904
|
+
}
|
|
30905
|
+
return _extends({}, state, {
|
|
30906
|
+
hasBeenInteractedWith: true,
|
|
30907
|
+
center: constrainedCenter,
|
|
30908
|
+
radiusPoint: newRadiusPoint
|
|
30909
|
+
});
|
|
30910
|
+
}
|
|
30911
|
+
default:
|
|
30912
|
+
throw new Error("The doMoveCenter action is only for circle graphs");
|
|
30913
|
+
}
|
|
30914
|
+
}
|
|
30915
|
+
function doMoveRadiusPoint(state, action) {
|
|
30916
|
+
switch (state.type) {
|
|
30917
|
+
case "circle":
|
|
30918
|
+
{
|
|
30919
|
+
const [xMin, xMax] = state.range[0];
|
|
30920
|
+
const nextRadiusPoint = [
|
|
30921
|
+
// Constrain to graph range
|
|
30922
|
+
// The +0 is to convert -0 to +0
|
|
30923
|
+
Math.min(Math.max(xMin, action.destination[0] + 0), xMax), state.center[1]];
|
|
30924
|
+
if (_.isEqual(nextRadiusPoint, state.center)) {
|
|
30925
|
+
return state;
|
|
30926
|
+
}
|
|
30927
|
+
return _extends({}, state, {
|
|
30928
|
+
hasBeenInteractedWith: true,
|
|
30929
|
+
radiusPoint: nextRadiusPoint
|
|
30930
|
+
});
|
|
30931
|
+
}
|
|
30932
|
+
default:
|
|
30933
|
+
throw new Error("The doMoveRadiusPoint action is only for circle graphs");
|
|
30934
|
+
}
|
|
30935
|
+
}
|
|
30513
30936
|
function doChangeSnapStep(state, action) {
|
|
30514
30937
|
if (
|
|
30515
30938
|
// Deep equality check since these are arrays
|
|
@@ -30621,222 +31044,6 @@ function setAtIndex(args) {
|
|
|
30621
31044
|
return copy;
|
|
30622
31045
|
}
|
|
30623
31046
|
|
|
30624
|
-
function initializeGraphState(params) {
|
|
30625
|
-
const {
|
|
30626
|
-
graph,
|
|
30627
|
-
step,
|
|
30628
|
-
snapStep,
|
|
30629
|
-
range,
|
|
30630
|
-
markings
|
|
30631
|
-
} = params;
|
|
30632
|
-
const shared = {
|
|
30633
|
-
hasBeenInteractedWith: false,
|
|
30634
|
-
range,
|
|
30635
|
-
snapStep,
|
|
30636
|
-
markings
|
|
30637
|
-
};
|
|
30638
|
-
switch (graph.type) {
|
|
30639
|
-
case "segment":
|
|
30640
|
-
return _extends({}, shared, {
|
|
30641
|
-
type: "segment",
|
|
30642
|
-
coords: getDefaultSegments({
|
|
30643
|
-
graph,
|
|
30644
|
-
step,
|
|
30645
|
-
range
|
|
30646
|
-
})
|
|
30647
|
-
});
|
|
30648
|
-
case "linear":
|
|
30649
|
-
case "linear-system":
|
|
30650
|
-
case "ray":
|
|
30651
|
-
return _extends({}, shared, {
|
|
30652
|
-
type: graph.type,
|
|
30653
|
-
// Linear and ray graphs have a single tuple of points, while a
|
|
30654
|
-
// linear system has two tuples of points.
|
|
30655
|
-
coords: getLineCoords({
|
|
30656
|
-
graph,
|
|
30657
|
-
range,
|
|
30658
|
-
step
|
|
30659
|
-
})
|
|
30660
|
-
});
|
|
30661
|
-
case "polygon":
|
|
30662
|
-
return _extends({}, shared, {
|
|
30663
|
-
type: "polygon",
|
|
30664
|
-
showAngles: Boolean(graph.showAngles),
|
|
30665
|
-
showSides: Boolean(graph.showSides),
|
|
30666
|
-
coords: getPolygonCoords({
|
|
30667
|
-
graph,
|
|
30668
|
-
range,
|
|
30669
|
-
step
|
|
30670
|
-
})
|
|
30671
|
-
});
|
|
30672
|
-
case "point":
|
|
30673
|
-
return _extends({}, shared, {
|
|
30674
|
-
type: graph.type,
|
|
30675
|
-
coords: getDefaultPoints({
|
|
30676
|
-
graph,
|
|
30677
|
-
step,
|
|
30678
|
-
range
|
|
30679
|
-
})
|
|
30680
|
-
});
|
|
30681
|
-
case "angle":
|
|
30682
|
-
case "circle":
|
|
30683
|
-
case "sinusoid":
|
|
30684
|
-
case "quadratic":
|
|
30685
|
-
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
30686
|
-
default:
|
|
30687
|
-
throw new UnreachableCaseError(graph);
|
|
30688
|
-
}
|
|
30689
|
-
}
|
|
30690
|
-
const getDefaultPoints = ({
|
|
30691
|
-
graph,
|
|
30692
|
-
range,
|
|
30693
|
-
step
|
|
30694
|
-
}) => {
|
|
30695
|
-
var _graph$coords;
|
|
30696
|
-
const numPoints = graph.numPoints || 1;
|
|
30697
|
-
let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
|
|
30698
|
-
if (coords) {
|
|
30699
|
-
return coords;
|
|
30700
|
-
}
|
|
30701
|
-
switch (numPoints) {
|
|
30702
|
-
case 1:
|
|
30703
|
-
// Back in the day, one point's coords were in graph.coord
|
|
30704
|
-
coords = [graph.coord || [0, 0]];
|
|
30705
|
-
break;
|
|
30706
|
-
case 2:
|
|
30707
|
-
coords = [[-5, 0], [5, 0]];
|
|
30708
|
-
break;
|
|
30709
|
-
case 3:
|
|
30710
|
-
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
30711
|
-
break;
|
|
30712
|
-
case 4:
|
|
30713
|
-
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
30714
|
-
break;
|
|
30715
|
-
case 5:
|
|
30716
|
-
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
30717
|
-
break;
|
|
30718
|
-
case 6:
|
|
30719
|
-
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
30720
|
-
break;
|
|
30721
|
-
default:
|
|
30722
|
-
coords = [];
|
|
30723
|
-
break;
|
|
30724
|
-
}
|
|
30725
|
-
// Transform coords from their -10 to 10 space to 0 to 1
|
|
30726
|
-
// because of the old graph.coord, and also it's easier.
|
|
30727
|
-
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30728
|
-
return normalizePoints(range, step, newCoords);
|
|
30729
|
-
};
|
|
30730
|
-
|
|
30731
|
-
// TS v4 doesn't narrow return types, while v5 does.
|
|
30732
|
-
// Instead of updating to v5, using generic type to relate input and output types.
|
|
30733
|
-
function getGradableGraph(state, initialGraph) {
|
|
30734
|
-
if (!state.hasBeenInteractedWith) {
|
|
30735
|
-
return _extends({}, initialGraph);
|
|
30736
|
-
}
|
|
30737
|
-
if (initialGraph.type === "linear-system" && state.type === "linear-system") {
|
|
30738
|
-
return _extends({}, initialGraph, {
|
|
30739
|
-
coords: state.coords
|
|
30740
|
-
});
|
|
30741
|
-
}
|
|
30742
|
-
if (state.type === "segment" && initialGraph.type === "segment") {
|
|
30743
|
-
return _extends({}, initialGraph, {
|
|
30744
|
-
coords: state.coords
|
|
30745
|
-
});
|
|
30746
|
-
}
|
|
30747
|
-
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
30748
|
-
return _extends({}, initialGraph, {
|
|
30749
|
-
coords: state.coords[0]
|
|
30750
|
-
});
|
|
30751
|
-
}
|
|
30752
|
-
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
30753
|
-
return _extends({}, initialGraph, {
|
|
30754
|
-
coords: state.coords[0]
|
|
30755
|
-
});
|
|
30756
|
-
}
|
|
30757
|
-
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
30758
|
-
return _extends({}, initialGraph, {
|
|
30759
|
-
coords: state.coords
|
|
30760
|
-
});
|
|
30761
|
-
}
|
|
30762
|
-
if (state.type === "point" && initialGraph.type === "point") {
|
|
30763
|
-
return _extends({}, initialGraph, {
|
|
30764
|
-
coords: state.coords
|
|
30765
|
-
});
|
|
30766
|
-
}
|
|
30767
|
-
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
30768
|
-
}
|
|
30769
|
-
const getDefaultSegments = ({
|
|
30770
|
-
graph,
|
|
30771
|
-
range,
|
|
30772
|
-
step
|
|
30773
|
-
}) => {
|
|
30774
|
-
const ys = n => {
|
|
30775
|
-
switch (n) {
|
|
30776
|
-
case 2:
|
|
30777
|
-
return [5, -5];
|
|
30778
|
-
case 3:
|
|
30779
|
-
return [5, 0, -5];
|
|
30780
|
-
case 4:
|
|
30781
|
-
return [6, 2, -2, -6];
|
|
30782
|
-
case 5:
|
|
30783
|
-
return [6, 3, 0, -3, -6];
|
|
30784
|
-
case 6:
|
|
30785
|
-
return [5, 3, 1, -1, -3, -5];
|
|
30786
|
-
default:
|
|
30787
|
-
return [5];
|
|
30788
|
-
}
|
|
30789
|
-
};
|
|
30790
|
-
const defaultRange = [[-10, 10], [-10, 10]];
|
|
30791
|
-
return ys(graph.numSegments).map(y => {
|
|
30792
|
-
let endpoints = [[-5, y], [5, y]];
|
|
30793
|
-
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
30794
|
-
endpoints = normalizePoints(range, step, endpoints);
|
|
30795
|
-
return endpoints;
|
|
30796
|
-
});
|
|
30797
|
-
};
|
|
30798
|
-
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
30799
|
-
const getLineCoords = ({
|
|
30800
|
-
graph,
|
|
30801
|
-
range,
|
|
30802
|
-
step
|
|
30803
|
-
}) => {
|
|
30804
|
-
var _graph$coords2;
|
|
30805
|
-
return (
|
|
30806
|
-
// Return two lines for a linear system, one for a ray or linear
|
|
30807
|
-
((_graph$coords2 = graph.coords) != null ? _graph$coords2 : graph.type === "linear-system") ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
30808
|
-
);
|
|
30809
|
-
};
|
|
30810
|
-
const getPolygonCoords = ({
|
|
30811
|
-
graph,
|
|
30812
|
-
range,
|
|
30813
|
-
step
|
|
30814
|
-
}) => {
|
|
30815
|
-
var _graph$coords3;
|
|
30816
|
-
let coords = (_graph$coords3 = graph.coords) == null ? void 0 : _graph$coords3.slice();
|
|
30817
|
-
if (coords) {
|
|
30818
|
-
return coords;
|
|
30819
|
-
}
|
|
30820
|
-
const n = graph.numSides || 3;
|
|
30821
|
-
if (n === "unlimited") {
|
|
30822
|
-
coords = [];
|
|
30823
|
-
} else {
|
|
30824
|
-
const angle = 2 * Math.PI / n;
|
|
30825
|
-
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
30826
|
-
|
|
30827
|
-
// TODO(alex): Generalize this to more than just triangles so that
|
|
30828
|
-
// all polygons have whole number side lengths if snapping to sides
|
|
30829
|
-
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
30830
|
-
|
|
30831
|
-
// Generate coords of a regular polygon with n sides
|
|
30832
|
-
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
30833
|
-
}
|
|
30834
|
-
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30835
|
-
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
30836
|
-
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
30837
|
-
return coords;
|
|
30838
|
-
};
|
|
30839
|
-
|
|
30840
31047
|
const renderGraph = props => {
|
|
30841
31048
|
const {
|
|
30842
31049
|
state,
|
|
@@ -30873,20 +31080,42 @@ const renderGraph = props => {
|
|
|
30873
31080
|
dispatch: dispatch
|
|
30874
31081
|
});
|
|
30875
31082
|
case "circle":
|
|
30876
|
-
|
|
31083
|
+
return /*#__PURE__*/React.createElement(CircleGraph, {
|
|
31084
|
+
graphState: state,
|
|
31085
|
+
dispatch: dispatch
|
|
31086
|
+
});
|
|
30877
31087
|
default:
|
|
30878
31088
|
return new UnreachableCaseError(type);
|
|
30879
31089
|
}
|
|
30880
31090
|
};
|
|
31091
|
+
|
|
31092
|
+
// Rather than be tightly bound to how data was structured in
|
|
31093
|
+
// the legacy interactive graph, this lets us store state
|
|
31094
|
+
// however we want and we just transform it before handing it off
|
|
31095
|
+
// the the parent InteractiveGraph
|
|
31096
|
+
function mafsStateToInteractiveGraph(state) {
|
|
31097
|
+
if (state.graph.type === "circle") {
|
|
31098
|
+
return _extends({}, state, {
|
|
31099
|
+
graph: _extends({}, state.graph, {
|
|
31100
|
+
radius: getRadius(state.graph)
|
|
31101
|
+
})
|
|
31102
|
+
});
|
|
31103
|
+
}
|
|
31104
|
+
return _extends({}, state);
|
|
31105
|
+
}
|
|
30881
31106
|
const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
30882
31107
|
const [state, dispatch] = React.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
30883
31108
|
useImperativeHandle(ref, () => ({
|
|
30884
31109
|
getUserInput: () => getGradableGraph(state, props.graph)
|
|
30885
31110
|
}));
|
|
30886
|
-
|
|
31111
|
+
function onChange(next) {
|
|
31112
|
+
props.onChange(mafsStateToInteractiveGraph(next));
|
|
31113
|
+
}
|
|
31114
|
+
return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, props, {
|
|
30887
31115
|
state: state,
|
|
30888
|
-
dispatch: dispatch
|
|
30889
|
-
|
|
31116
|
+
dispatch: dispatch,
|
|
31117
|
+
onChange: onChange
|
|
31118
|
+
}));
|
|
30890
31119
|
});
|
|
30891
31120
|
const MafsGraph = props => {
|
|
30892
31121
|
const {
|
|
@@ -43065,7 +43294,7 @@ const init = function init(options) {
|
|
|
43065
43294
|
|
|
43066
43295
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43067
43296
|
const libName = "@khanacademy/perseus";
|
|
43068
|
-
const libVersion = "22.
|
|
43297
|
+
const libVersion = "22.4.1";
|
|
43069
43298
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43070
43299
|
|
|
43071
43300
|
const apiVersion = {
|