@khanacademy/perseus-editor 14.1.3 → 14.2.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.
Files changed (48) hide show
  1. package/dist/components/util.d.ts +0 -14
  2. package/dist/es/index.js +1399 -1373
  3. package/dist/es/index.js.map +1 -1
  4. package/dist/index.js +1457 -1427
  5. package/dist/index.js.map +1 -1
  6. package/dist/{components → widgets/interactive-graph-editor/components}/graph-points-count-selector.d.ts +1 -1
  7. package/dist/{components → widgets/interactive-graph-editor/components}/graph-type-selector.d.ts +1 -0
  8. package/dist/{components → widgets/interactive-graph-editor/components}/interactive-graph-description.d.ts +1 -1
  9. package/dist/widgets/interactive-graph-editor/interactive-graph-editor.d.ts +220 -128
  10. package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-figures-section.d.ts +1 -1
  11. package/dist/widgets/interactive-graph-editor/locked-figures/util.d.ts +9 -0
  12. package/dist/widgets/interactive-graph-editor/start-coords/start-coords-angle.d.ts +9 -0
  13. package/dist/widgets/interactive-graph-editor/start-coords/start-coords-circle.d.ts +12 -0
  14. package/dist/{components → widgets/interactive-graph-editor/start-coords}/start-coords-line.d.ts +2 -2
  15. package/dist/{components → widgets/interactive-graph-editor/start-coords}/start-coords-multiline.d.ts +2 -2
  16. package/dist/{components → widgets/interactive-graph-editor/start-coords}/start-coords-point.d.ts +2 -2
  17. package/dist/{components → widgets/interactive-graph-editor/start-coords}/start-coords-quadratic.d.ts +2 -2
  18. package/dist/{components → widgets/interactive-graph-editor/start-coords}/start-coords-settings.d.ts +2 -1
  19. package/dist/widgets/interactive-graph-editor/start-coords/start-coords-sinusoid.d.ts +9 -0
  20. package/dist/widgets/interactive-graph-editor/start-coords/types.d.ts +24 -0
  21. package/dist/widgets/interactive-graph-editor/start-coords/util.d.ts +8 -0
  22. package/package.json +2 -2
  23. package/dist/components/start-coords-angle.d.ts +0 -8
  24. package/dist/components/start-coords-circle.d.ts +0 -11
  25. package/dist/components/start-coords-sinusoid.d.ts +0 -8
  26. /package/dist/{components → widgets/interactive-graph-editor/components}/interactive-graph-correct-answer.d.ts +0 -0
  27. /package/dist/{components → widgets/interactive-graph-editor/components}/interactive-graph-settings.d.ts +0 -0
  28. /package/dist/{components → widgets/interactive-graph-editor/components}/segment-count-selector.d.ts +0 -0
  29. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/color-select.d.ts +0 -0
  30. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/color-swatch.d.ts +0 -0
  31. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/defining-point-settings.d.ts +0 -0
  32. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/ellipse-swatch.d.ts +0 -0
  33. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/labeled-row.d.ts +0 -0
  34. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/labeled-switch.d.ts +0 -0
  35. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/line-stroke-select.d.ts +0 -0
  36. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/line-swatch.d.ts +0 -0
  37. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-ellipse-settings.d.ts +0 -0
  38. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-figure-select.d.ts +0 -0
  39. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-figure-settings-actions.d.ts +0 -0
  40. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-figure-settings.d.ts +0 -0
  41. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-function-examples.d.ts +0 -0
  42. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-function-settings.d.ts +0 -0
  43. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-label-settings.d.ts +0 -0
  44. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-line-settings.d.ts +0 -0
  45. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-point-settings.d.ts +0 -0
  46. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-polygon-settings.d.ts +0 -0
  47. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/locked-vector-settings.d.ts +0 -0
  48. /package/dist/{components/graph-locked-figures → widgets/interactive-graph-editor/locked-figures}/polygon-swatch.d.ts +0 -0
package/dist/es/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { addLibraryVersionToPerseusDebug, PerseusError, Errors } from '@khanacademy/perseus-core';
2
- import { constants, components, Widgets, icons, WIDGET_PROP_DENYLIST, Util, Log, PerseusMarkdown, preprocessTex, ApiOptions, buildMapper, itemToTree, buildEmptyItemTreeForShape, shapes, ItemExtras, itemDataVersion, ClassNames, MultiItems, traverse, Categorizer as Categorizer$1, Changeable, EditorJsonify, Dependencies, PerseusExpressionAnswerFormConsidered, Expression, interactiveSizes, KhanMath, GrapherWidget, GrapherUtil, SizingUtils, PerseusI18nContext, KhanColors, getAngleCoords, getPolygonCoords, getPointCoords, getQuadraticCoords, getSinusoidCoords, getCircleCoords, getLinearSystemCoords, getSegmentCoords, getLineCoords, lockedFigureColors, lockedFigureFillStyles, InteractiveGraphWidget, globalStyles, globalConstants, MatrixWidget, makeSafeUrl, PlotterWidget, plotterPlotTypes, BaseRadio, TableWidget, widgets } from '@khanacademy/perseus';
2
+ import { constants, components, Widgets, icons, WIDGET_PROP_DENYLIST, Util, Log, PerseusMarkdown, preprocessTex, ApiOptions, buildMapper, itemToTree, buildEmptyItemTreeForShape, shapes, ItemExtras, itemDataVersion, ClassNames, MultiItems, traverse, Categorizer as Categorizer$1, Changeable, EditorJsonify, Dependencies, PerseusExpressionAnswerFormConsidered, Expression, interactiveSizes, KhanMath, GrapherWidget, GrapherUtil, SizingUtils, PerseusI18nContext, KhanColors, lockedFigureColors, lockedFigureFillStyles, getAngleCoords, getPolygonCoords, getPointCoords, getQuadraticCoords, getSinusoidCoords, getCircleCoords, getLinearSystemCoords, getSegmentCoords, getLineCoords, InteractiveGraphWidget, globalStyles, globalConstants, MatrixWidget, makeSafeUrl, PlotterWidget, plotterPlotTypes, BaseRadio, TableWidget, widgets } from '@khanacademy/perseus';
3
3
  export { widgets } from '@khanacademy/perseus';
4
4
  import * as React from 'react';
5
5
  import { useRef, useEffect, useState, useId } from 'react';
@@ -19,11 +19,12 @@ import { Checkbox as Checkbox$1, LabeledTextField, TextField, TextArea } from '@
19
19
  import ReactDOM from 'react-dom';
20
20
  import * as KAS from '@khanacademy/kas';
21
21
  import Button from '@khanacademy/wonder-blocks-button';
22
- import { HeadingSmall, HeadingXSmall, LabelSmall, LabelLarge, LabelMedium, LabelXSmall, BodyMonospace } from '@khanacademy/wonder-blocks-typography';
22
+ import { HeadingSmall, HeadingXSmall, LabelLarge, LabelXSmall, BodyMonospace, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
23
23
  import { isTruthy, UnreachableCaseError } from '@khanacademy/wonder-stuff-core';
24
24
  import { useMathInputI18n, createMathField } from '@khanacademy/math-input';
25
25
  import { vector, number } from '@khanacademy/kmath';
26
- import { ActionMenu, ActionItem, SingleSelect, OptionItem } from '@khanacademy/wonder-blocks-dropdown';
26
+ import { SingleSelect, OptionItem, ActionMenu, ActionItem } from '@khanacademy/wonder-blocks-dropdown';
27
+ import Banner from '@khanacademy/wonder-blocks-banner';
27
28
  import { AccordionSection } from '@khanacademy/wonder-blocks-accordion';
28
29
  import IconButton from '@khanacademy/wonder-blocks-icon-button';
29
30
  import caretDoubleDownIcon from '@phosphor-icons/core/bold/caret-double-down-bold.svg';
@@ -39,12 +40,11 @@ import arrowFatRight from '@phosphor-icons/core/regular/arrow-fat-right.svg';
39
40
  import arrowFatUp from '@phosphor-icons/core/regular/arrow-fat-up.svg';
40
41
  import minusCircle from '@phosphor-icons/core/regular/minus-circle.svg';
41
42
  import plusCircle from '@phosphor-icons/core/regular/plus-circle.svg';
42
- import Banner from '@khanacademy/wonder-blocks-banner';
43
43
  import arrowCounterClockwise from '@phosphor-icons/core/bold/arrow-counter-clockwise-bold.svg';
44
44
 
45
45
  // This file is processed by a Rollup plugin (replace) to inject the production
46
46
  const libName = "@khanacademy/perseus-editor";
47
- const libVersion = "14.1.3";
47
+ const libVersion = "14.2.0";
48
48
  addLibraryVersionToPerseusDebug(libName, libVersion);
49
49
 
50
50
  function _extends() {
@@ -29496,39 +29496,95 @@ function invariant(condition, message) {
29496
29496
  throw new Error(value);
29497
29497
  }
29498
29498
 
29499
- // Renders the given children in a View laid out horizontally.
29500
- const LabeledRow = props => {
29501
- const {
29502
- children,
29503
- label,
29504
- labelSide = "left",
29505
- style
29506
- } = props;
29507
- return /*#__PURE__*/React.createElement("label", {
29508
- className: css(styles$J.label)
29509
- }, /*#__PURE__*/React.createElement(View, {
29510
- style: [styles$J.row, style]
29511
- }, labelSide === "start" || /*#__PURE__*/React.createElement(LabelSmall, {
29512
- style: styles$J.spaceEnd
29513
- }, label), children, labelSide === "end" && /*#__PURE__*/React.createElement(LabelSmall, {
29514
- style: styles$J.spaceStart
29515
- }, label)));
29499
+ const UNLIMITED = "unlimited";
29500
+ // Parses a string (ostensibly from some UI element that only supports string
29501
+ // types, such as a <select />) into a numeric point count or the special sigil
29502
+ // "unlimited". Note that 0 is a special case, and means "unlimited".
29503
+ const parsePointCount = points => {
29504
+ const parsed = parseInt(points, 10);
29505
+ if (isNaN(parsed)) {
29506
+ return UNLIMITED;
29507
+ }
29508
+ return parsed === 0 ? UNLIMITED : parsed;
29509
+ };
29510
+
29511
+ const GraphPointsCountSelector = ({
29512
+ numPoints: _numPoints = 1,
29513
+ onChange
29514
+ }) => {
29515
+ return /*#__PURE__*/React.createElement(SingleSelect, {
29516
+ selectedValue: `${_numPoints}`,
29517
+ onChange: newValue => {
29518
+ onChange(parsePointCount(newValue));
29519
+ }
29520
+ // Never uses placeholder, always has value
29521
+ ,
29522
+ placeholder: "",
29523
+ style: styles$J.singleSelectShort
29524
+ }, [...[...Array(7).keys()].map(n => /*#__PURE__*/React.createElement(OptionItem, {
29525
+ key: n,
29526
+ value: `${n}`,
29527
+ label: `${n} point${n > 1 ? "s" : ""}`
29528
+ })), /*#__PURE__*/React.createElement(OptionItem, {
29529
+ key: "unlimited",
29530
+ value: UNLIMITED,
29531
+ label: "unlimited"
29532
+ })]);
29516
29533
  };
29517
29534
  const styles$J = StyleSheet.create({
29518
- label: {
29519
- width: "fit-content"
29520
- },
29521
- row: {
29522
- flexDirection: "row",
29523
- marginTop: spacing.xSmall_8,
29524
- alignItems: "center",
29525
- width: "fit-content"
29526
- },
29527
- spaceStart: {
29528
- marginInlineStart: spacing.xSmall_8
29529
- },
29530
- spaceEnd: {
29531
- marginInlineEnd: spacing.xSmall_8
29535
+ singleSelectShort: {
29536
+ // Non-standard spacing, but it's the smallest we can go
29537
+ // without running into styling issues with the dropdown.
29538
+ height: 26
29539
+ }
29540
+ });
29541
+
29542
+ const GraphTypeSelector = props => {
29543
+ return /*#__PURE__*/React.createElement(SingleSelect, {
29544
+ selectedValue: props.graphType,
29545
+ onChange: props.onChange,
29546
+ placeholder: "Select an answer type",
29547
+ style: styles$I.singleSelectShort
29548
+ }, props.showNoneOption && /*#__PURE__*/React.createElement(OptionItem, {
29549
+ value: "none",
29550
+ label: "None"
29551
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29552
+ value: "linear",
29553
+ label: "Linear function"
29554
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29555
+ value: "quadratic",
29556
+ label: "Quadratic function"
29557
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29558
+ value: "sinusoid",
29559
+ label: "Sinusoid function"
29560
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29561
+ value: "circle",
29562
+ label: "Circle"
29563
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29564
+ value: "point",
29565
+ label: "Point(s)"
29566
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29567
+ value: "linear-system",
29568
+ label: "Linear System"
29569
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29570
+ value: "polygon",
29571
+ label: "Polygon"
29572
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29573
+ value: "segment",
29574
+ label: "Line Segment(s)"
29575
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29576
+ value: "ray",
29577
+ label: "Ray"
29578
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29579
+ value: "angle",
29580
+ label: "Angle"
29581
+ }));
29582
+ };
29583
+ const styles$I = StyleSheet.create({
29584
+ singleSelectShort: {
29585
+ // Non-standard spacing, but it's the smallest we can go
29586
+ // without running into styling issues with the dropdown.
29587
+ height: 26
29532
29588
  }
29533
29589
  });
29534
29590
 
@@ -29539,11 +29595,11 @@ function Heading({
29539
29595
  onToggle
29540
29596
  }) {
29541
29597
  return /*#__PURE__*/React.createElement(Clickable, {
29542
- style: [styles$I.container, !isCollapsible && styles$I.notClickable],
29598
+ style: [styles$H.container, !isCollapsible && styles$H.notClickable],
29543
29599
  disabled: !isCollapsible,
29544
29600
  onClick: () => isCollapsible && (onToggle == null ? void 0 : onToggle(!isOpen))
29545
29601
  }, () => /*#__PURE__*/React.createElement(View, {
29546
- style: styles$I.heading
29602
+ style: styles$H.heading
29547
29603
  }, /*#__PURE__*/React.createElement(LabelLarge, {
29548
29604
  style: {
29549
29605
  fontSize: 14,
@@ -29553,7 +29609,7 @@ function Heading({
29553
29609
  isExpanded: isOpen
29554
29610
  })));
29555
29611
  }
29556
- const styles$I = StyleSheet.create({
29612
+ const styles$H = StyleSheet.create({
29557
29613
  container: {
29558
29614
  marginTop: spacing.small_12,
29559
29615
  // NOTE(jeremy): This is the inverse of the @editorPadding CSS
@@ -29577,350 +29633,760 @@ const styles$I = StyleSheet.create({
29577
29633
  }
29578
29634
  });
29579
29635
 
29580
- function focusWithChromeStickyFocusBugWorkaround(element) {
29581
- // NOTE(marksandstrom) Chrome sticky focus bug.
29582
- //
29583
- // There is a bug in Chrome related to sticky positioning and
29584
- // focus: When a child of an element with sticky positioning is
29585
- // focused with the focus() method, the scrollable area
29586
- // containing that element will scroll to the top (even though
29587
- // the element is already visble on the page). For a dropdown
29588
- // this means that the area will be scrolled to the top when
29589
- // the user interacts with the dropdown (since we actively
29590
- // manage dropdown header and option focus for keyboard users).
29591
- //
29592
- // To work around this bug the `preventScroll` focus option is
29593
- // used.
29594
- //
29595
- // Using `focusOptions` is an experimental browser feature
29596
- // "that should not be used in production code". However, since
29597
- // Chrome is the only browser we support that exhibits this
29598
- // buggy behavior and using `focusOptions` works around the
29599
- // bug, it is perfectly reasonable to use the feature in this
29600
- // case. We are not relying on the effect of the
29601
- // `preventScroll` option otherwise.
29602
- //
29603
- // focusOptions documentation:
29604
- //
29605
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
29606
- //
29607
- // Note that there could be cases where we want default focus() behavior.
29608
- // One that comes to mind is a dropdown with a long list of options that
29609
- // might be rendered off screen when opened. With this workaround the
29610
- // selected option would not be scrolled into view if the option is near
29611
- // the end of the list. We're not aware of any uses of the dropdown where
29612
- // this is an issue, however, so there isn't currently a way to out of the
29613
- // `preventScroll` behavior.
29614
-
29615
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element'.
29616
- element.focus({
29617
- preventScroll: true
29618
- });
29619
- }
29620
- const DEFAULT_COLOR = "grayH";
29621
- function getDefaultFigureForType(type) {
29622
- switch (type) {
29623
- case "point":
29624
- return {
29625
- type: "point",
29626
- coord: [0, 0],
29627
- color: DEFAULT_COLOR,
29628
- filled: true
29629
- };
29630
- case "line":
29631
- return {
29632
- type: "line",
29633
- kind: "line",
29634
- points: [getDefaultFigureForType("point"), _extends({}, getDefaultFigureForType("point"), {
29635
- coord: [2, 2]
29636
- })],
29637
- color: DEFAULT_COLOR,
29638
- lineStyle: "solid",
29639
- showPoint1: false,
29640
- showPoint2: false
29641
- };
29642
- case "vector":
29643
- return {
29644
- type: "vector",
29645
- points: [[0, 0], [2, 2]],
29646
- color: DEFAULT_COLOR
29647
- };
29648
- case "ellipse":
29649
- return {
29650
- type: "ellipse",
29651
- center: [0, 0],
29652
- radius: [1, 1],
29653
- angle: 0,
29654
- color: DEFAULT_COLOR,
29655
- fillStyle: "none",
29656
- strokeStyle: "solid"
29657
- };
29658
- case "polygon":
29659
- return {
29660
- type: "polygon",
29661
- points: [[0, 2], [-1, 0], [1, 0]],
29662
- color: DEFAULT_COLOR,
29663
- showVertices: false,
29664
- fillStyle: "none",
29665
- strokeStyle: "solid"
29666
- };
29667
- case "function":
29668
- return {
29669
- type: "function",
29670
- color: DEFAULT_COLOR,
29671
- strokeStyle: "solid",
29672
- equation: "x^2",
29673
- directionalAxis: "x"
29674
- };
29675
- case "label":
29676
- return {
29677
- type: "label",
29678
- coord: [0, 0],
29679
- text: "",
29680
- color: DEFAULT_COLOR,
29681
- size: "medium"
29682
- };
29683
- default:
29684
- throw new UnreachableCaseError(type);
29685
- }
29686
- }
29687
- function degreeToRadian(degrees) {
29688
- return degrees / 180 * Math.PI;
29689
- }
29690
- function radianToDegree(radians) {
29691
- return radians / Math.PI * 180;
29692
- }
29693
- function getDefaultGraphStartCoords(graph, range, step) {
29694
- switch (graph.type) {
29695
- case "linear":
29696
- case "ray":
29697
- return getLineCoords(_extends({}, graph, {
29698
- startCoords: undefined
29699
- }), range, step);
29700
- case "segment":
29701
- return getSegmentCoords(_extends({}, graph, {
29702
- startCoords: undefined
29703
- }), range, step);
29704
- case "linear-system":
29705
- return getLinearSystemCoords(_extends({}, graph, {
29706
- startCoords: undefined
29707
- }), range, step);
29708
- case "circle":
29709
- const startCoords = getCircleCoords(_extends({}, graph, {
29710
- startCoords: undefined
29711
- }));
29712
- const radius = vector.length(vector.subtract(startCoords.radiusPoint, startCoords.center));
29713
- return {
29714
- center: startCoords.center,
29715
- radius
29716
- };
29717
- case "sinusoid":
29718
- return getSinusoidCoords(_extends({}, graph, {
29719
- startCoords: undefined
29720
- }), range, step);
29721
- case "quadratic":
29722
- return getQuadraticCoords(_extends({}, graph, {
29723
- startCoords: undefined
29724
- }), range, step);
29725
- case "point":
29726
- return getPointCoords(_extends({}, graph, {
29727
- startCoords: undefined
29728
- }), range, step);
29729
- case "polygon":
29730
- return getPolygonCoords(_extends({}, graph, {
29731
- startCoords: undefined
29732
- }), range, step);
29733
- case "angle":
29734
- return getAngleCoords({
29735
- graph: _extends({}, graph, {
29736
- startCoords: undefined
29737
- }),
29738
- range,
29739
- step
29740
- });
29741
- default:
29742
- return undefined;
29743
- }
29636
+ function InteractiveGraphCorrectAnswer(props) {
29637
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
29638
+ title: "Correct Answer",
29639
+ isOpen: true,
29640
+ isCollapsible: false
29641
+ }), /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(LabelXSmall, {
29642
+ style: {
29643
+ paddingTop: spacing.xxSmall_6,
29644
+ paddingBottom: spacing.xxSmall_6,
29645
+ color: color.offBlack64
29646
+ }
29647
+ }, "Graph the correct answer in the graph below and ensure the equation or point coordinates displayed represent the correct answer."), /*#__PURE__*/React.createElement(BodyMonospace, {
29648
+ style: {
29649
+ fontSize: 12,
29650
+ backgroundColor: "#eee",
29651
+ paddingInline: spacing.xxSmall_6,
29652
+ borderColor: "#ccc",
29653
+ borderStyle: "solid",
29654
+ borderWidth: 1
29655
+ }
29656
+ }, props.equationString)), props.children));
29744
29657
  }
29745
- const getSinusoidEquation = startCoords => {
29746
- // Get coefficients
29747
- // It's assumed that p1 is the root and p2 is the first peak
29748
- const p1 = startCoords[0];
29749
- const p2 = startCoords[1];
29750
-
29751
- // Resulting coefficients are canonical for this sine curve
29752
- const amplitude = p2[1] - p1[1];
29753
- const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
29754
- const phase = p1[0] * angularFrequency;
29755
- const verticalOffset = p1[1];
29756
- return "y = " + amplitude.toFixed(3) + "sin(" + angularFrequency.toFixed(3) + "x - " + phase.toFixed(3) + ") + " + verticalOffset.toFixed(3);
29757
- };
29758
- const getQuadraticEquation = startCoords => {
29759
- const p1 = startCoords[0];
29760
- const p2 = startCoords[1];
29761
- const p3 = startCoords[2];
29762
- const denom = (p1[0] - p2[0]) * (p1[0] - p3[0]) * (p2[0] - p3[0]);
29763
- if (denom === 0) {
29764
- // Many of the callers assume that the return value is always defined.
29765
- return "Division by zero error";
29766
- }
29767
- const a = (p3[0] * (p2[1] - p1[1]) + p2[0] * (p1[1] - p3[1]) + p1[0] * (p3[1] - p2[1])) / denom;
29768
- const b = (p3[0] * p3[0] * (p1[1] - p2[1]) + p2[0] * p2[0] * (p3[1] - p1[1]) + p1[0] * p1[0] * (p2[1] - p3[1])) / denom;
29769
- const c = (p2[0] * p3[0] * (p2[0] - p3[0]) * p1[1] + p3[0] * p1[0] * (p3[0] - p1[0]) * p2[1] + p1[0] * p2[0] * (p1[0] - p2[0]) * p3[1]) / denom;
29770
- return "y = " + a.toFixed(3) + "x^2 + " + b.toFixed(3) + "x + " + c.toFixed(3);
29771
- };
29772
- const findAngle = (point1, point2) => {
29773
- const x = point1[0] - point2[0];
29774
- const y = point1[1] - point2[1];
29775
- return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
29776
- };
29777
- const getAngleEquation = startCoords => {
29778
- const [point1, vertex, point2] = startCoords;
29779
- const angle = findAngle(point2, vertex) - findAngle(point1, vertex);
29780
- const roundedAngle = angle.toFixed(0);
29781
- return `${roundedAngle}\u00B0 angle at (${vertex[0]}, ${vertex[1]})`;
29782
- };
29783
- const shouldShowStartCoordsUI = (graph, isStatic) => {
29784
- // We don't show the start coords UI for static graphs, since
29785
- // the coords shown for this are determined by the coords on the
29786
- // "correct" graph. The start coords would not be used here.
29787
- if (isStatic) {
29788
- return false;
29789
- }
29790
- if (graph.type === "point" && graph.numPoints === "unlimited") {
29791
- return false;
29792
- }
29793
- if (graph.type === "polygon" && (graph.numSides === "unlimited" || graph.snapTo === "angles" || graph.snapTo === "sides")) {
29794
- return false;
29795
- }
29796
- return true;
29797
- };
29798
29658
 
29799
- /**
29800
- * Dropdown for selecting a locked figure to add to an interactive graph.
29801
- * Locked figures are elements (points, segmeents, etc.) that are not
29802
- * interactive, just present in the graph's background.
29803
- *
29804
- * Used in the interactive graph editor's locked figures section.
29805
- */
29806
- const LockedFigureSelect = props => {
29659
+ function InteractiveGraphDescription(props) {
29807
29660
  const {
29808
- id,
29661
+ ariaLabelValue,
29662
+ ariaDescriptionValue,
29809
29663
  onChange
29810
29664
  } = props;
29811
- const figureTypes = ["point", "line", "vector", "ellipse", "polygon", "function"];
29812
- if (props.showLabelsFlag) {
29813
- figureTypes.push("label");
29814
- }
29815
- return /*#__PURE__*/React.createElement(View, {
29816
- style: styles$H.container
29817
- }, /*#__PURE__*/React.createElement(ActionMenu, {
29818
- menuText: "Add locked figure",
29819
- style: styles$H.addElementSelect
29820
- }, figureTypes.map(figureType => /*#__PURE__*/React.createElement(ActionItem, {
29821
- key: `${id}-${figureType}`,
29822
- label: figureType,
29823
- onClick: () => onChange(figureType)
29665
+ const [isOpen, setIsOpen] = React.useState(true);
29666
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
29667
+ title: "Description",
29668
+ isCollapsible: true,
29669
+ isOpen: isOpen,
29670
+ onToggle: setIsOpen
29671
+ }), isOpen && /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(LabelXSmall, {
29672
+ style: styles$G.caption
29673
+ }, "Use these fields to describe the graph as a whole. These are used by screen readers to describe content to users who are visually impaired."), /*#__PURE__*/React.createElement(LabelLarge, {
29674
+ tag: "label"
29675
+ }, "Title", /*#__PURE__*/React.createElement(TextField, {
29676
+ value: ariaLabelValue,
29677
+ onChange: newValue => onChange({
29678
+ fullGraphAriaLabel: newValue
29679
+ }),
29680
+ style: styles$G.spaceAbove
29681
+ })), /*#__PURE__*/React.createElement(Strut, {
29682
+ size: spacing.small_12
29683
+ }), /*#__PURE__*/React.createElement(LabelLarge, {
29684
+ tag: "label"
29685
+ }, "Description", /*#__PURE__*/React.createElement(TextArea, {
29686
+ rows: 8,
29687
+ resizeType: "vertical",
29688
+ value: ariaDescriptionValue,
29689
+ onChange: newValue => onChange({
29690
+ fullGraphAriaDescription: newValue
29691
+ }),
29692
+ style: styles$G.spaceAbove
29824
29693
  }))));
29825
- };
29826
- const styles$H = StyleSheet.create({
29827
- container: {
29828
- marginTop: spacing.xSmall_8
29694
+ }
29695
+ const styles$G = StyleSheet.create({
29696
+ caption: {
29697
+ color: color.offBlack64,
29698
+ paddingTop: spacing.xxSmall_6,
29699
+ paddingBottom: spacing.xxSmall_6
29829
29700
  },
29830
- addElementSelect: {
29831
- backgroundColor: color.fadedBlue8,
29832
- borderRadius: spacing.xxxSmall_4
29701
+ spaceAbove: {
29702
+ marginTop: spacing.xxxSmall_4
29833
29703
  }
29834
29704
  });
29835
29705
 
29836
- /**
29837
- * This is a custom text field of type="number" for use in Perseus Editors.
29838
- *
29839
- * This makes it so that the text field's input number updates on scroll
29840
- * without scrolling the page.
29841
- *
29842
- * NOTE 1: Native HTML number inputs do not update the number value on scroll,
29843
- * they only scroll the page. Inputs in React do NOT work this way (explanation
29844
- * here: https://stackoverflow.com/a/68266494). By default, scrolling on a
29845
- * focused number input in React causes BOTH the input value to change AND
29846
- * the page to scroll. The behavior in this component is an improvement on
29847
- * the React behavior, but it's the opposite of the native HTML behavior.
29848
- *
29849
- * NOTE 2: Firefox seems to have a custom override for this. Even with this
29850
- * stopPropogation, Firefox matches the native HTML behavior.
29851
- */
29852
- const ScrolllessNumberTextField = props => {
29853
- const inputRef = React.useRef(null);
29854
- React.useEffect(() => {
29855
- const ref = inputRef.current;
29856
-
29857
- // stopPropogation makes it so that the page scroll event is not
29858
- // triggered when the input is focused and the user scrolls.
29859
- // The input value will still change on scroll.
29860
- const ignoreScroll = e => {
29861
- e.stopPropagation();
29862
- };
29863
- ref == null || ref.addEventListener("wheel", ignoreScroll);
29864
- return () => {
29865
- ref == null || ref.removeEventListener("wheel", ignoreScroll);
29866
- };
29867
- }, [inputRef]);
29868
- return /*#__PURE__*/React.createElement(TextField, _extends({
29869
- type: "number"
29870
- }, props, {
29871
- ref: inputRef
29872
- }));
29873
- };
29874
-
29875
- const AngleInput = props => {
29706
+ // Renders the given children in a View laid out horizontally.
29707
+ const LabeledRow = props => {
29876
29708
  const {
29877
- angle,
29878
- onChange
29709
+ children,
29710
+ label,
29711
+ labelSide = "left",
29712
+ style
29879
29713
  } = props;
29880
- const [angleInput, setAngleInput] = React.useState(radianToDegree(angle).toString());
29881
- function handleAngleChange(newValue) {
29882
- // Update the local state (update the input field value).
29883
- setAngleInput(newValue);
29884
-
29885
- // If the new value is not a number, don't update the props.
29886
- // If it's empty, keep the props the same value instead of setting to 0.
29887
- if (isNaN(+newValue) || newValue === "") {
29888
- return;
29889
- }
29890
-
29891
- // Update the graph.
29892
- onChange(degreeToRadian(newValue));
29893
- }
29894
- return /*#__PURE__*/React.createElement(LabelMedium, {
29895
- tag: "label",
29896
- style: styles$G.row
29897
- }, "angle (degrees)", /*#__PURE__*/React.createElement(Strut, {
29898
- size: spacing.xxSmall_6
29899
- }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
29900
- value: angleInput,
29901
- onChange: handleAngleChange,
29902
- style: styles$G.textField
29903
- }), /*#__PURE__*/React.createElement(Strut, {
29904
- size: spacing.xxSmall_6
29905
- }));
29714
+ return /*#__PURE__*/React.createElement("label", {
29715
+ className: css(styles$F.label)
29716
+ }, /*#__PURE__*/React.createElement(View, {
29717
+ style: [styles$F.row, style]
29718
+ }, labelSide === "start" || /*#__PURE__*/React.createElement(LabelSmall, {
29719
+ style: styles$F.spaceEnd
29720
+ }, label), children, labelSide === "end" && /*#__PURE__*/React.createElement(LabelSmall, {
29721
+ style: styles$F.spaceStart
29722
+ }, label)));
29906
29723
  };
29907
- const styles$G = StyleSheet.create({
29724
+ const styles$F = StyleSheet.create({
29725
+ label: {
29726
+ width: "fit-content"
29727
+ },
29908
29728
  row: {
29909
- display: "flex",
29910
29729
  flexDirection: "row",
29911
- alignItems: "center"
29730
+ marginTop: spacing.xSmall_8,
29731
+ alignItems: "center",
29732
+ width: "fit-content"
29912
29733
  },
29913
- textField: {
29914
- width: spacing.xxxLarge_64
29734
+ spaceStart: {
29735
+ marginInlineStart: spacing.xSmall_8
29736
+ },
29737
+ spaceEnd: {
29738
+ marginInlineEnd: spacing.xSmall_8
29915
29739
  }
29916
29740
  });
29917
29741
 
29918
- const CoordinatePairInput = props => {
29919
- const {
29920
- coord,
29921
- labels,
29922
- error,
29923
- style,
29742
+ const {
29743
+ ButtonGroup: ButtonGroup$2,
29744
+ InfoTip: InfoTip$d,
29745
+ RangeInput: RangeInput$4
29746
+ } = components;
29747
+ const defaultBackgroundImage$1 = {
29748
+ url: null,
29749
+ width: 0,
29750
+ height: 0
29751
+ };
29752
+ function numSteps(range, step) {
29753
+ return Math.floor((range[1] - range[0]) / step);
29754
+ }
29755
+ class InteractiveGraphSettings extends React.Component {
29756
+ static stateFromProps(props) {
29757
+ return {
29758
+ labelsTextbox: props.labels,
29759
+ gridStepTextbox: props.gridStep,
29760
+ snapStepTextbox: props.snapStep,
29761
+ stepTextbox: props.step,
29762
+ rangeTextbox: props.range,
29763
+ // Shallow-copied clone
29764
+ backgroundImage: _extends({}, props.backgroundImage)
29765
+ };
29766
+ }
29767
+ constructor(props) {
29768
+ super(props);
29769
+ this._isMounted = false;
29770
+ this.bgUrlRef = /*#__PURE__*/React.createRef();
29771
+ this.labelXRef = /*#__PURE__*/React.createRef();
29772
+ this.labelYRef = /*#__PURE__*/React.createRef();
29773
+ this.change = (...args) => {
29774
+ return Changeable.change.apply(this, args);
29775
+ };
29776
+ this.changeBackgroundUrl = e => {
29777
+ var _this$bgUrlRef$curren;
29778
+ // Only continue on blur or "enter"
29779
+ if (e.type === "keypress" && e.key !== "Enter") {
29780
+ return;
29781
+ }
29782
+ const setUrl = (url, width, height) => {
29783
+ // Shallow-copied clone
29784
+ const image = _extends({}, this.props.backgroundImage);
29785
+ image.url = url;
29786
+ image.width = width;
29787
+ image.height = height;
29788
+ this.setState({
29789
+ backgroundImage: image
29790
+ }, this.changeGraph);
29791
+ };
29792
+ const url = (_this$bgUrlRef$curren = this.bgUrlRef.current) == null ? void 0 : _this$bgUrlRef$curren.value;
29793
+ if (url) {
29794
+ Util.getImageSize(url, (width, height) => {
29795
+ if (this._isMounted) {
29796
+ setUrl(url, width, height);
29797
+ }
29798
+ });
29799
+ } else {
29800
+ setUrl(null, 0, 0);
29801
+ }
29802
+ };
29803
+ this.renderLabelChoices = choices => {
29804
+ return choices.map(nameAndValue => /*#__PURE__*/React.createElement("option", {
29805
+ key: nameAndValue[1],
29806
+ value: nameAndValue[1]
29807
+ }, nameAndValue[0]));
29808
+ };
29809
+ this.validRange = range => {
29810
+ const numbers = _.every(range, function (num) {
29811
+ return _.isFinite(num);
29812
+ });
29813
+ if (!numbers) {
29814
+ return "Range must be a valid number";
29815
+ }
29816
+ if (range[0] >= range[1]) {
29817
+ return "Range must have a higher number on the right";
29818
+ }
29819
+ return true;
29820
+ };
29821
+ this.validateStepValue = settings => {
29822
+ const {
29823
+ step,
29824
+ range,
29825
+ name,
29826
+ minTicks,
29827
+ maxTicks
29828
+ } = settings;
29829
+ const nSteps = numSteps(range, step);
29830
+ if (nSteps < minTicks) {
29831
+ return name + " is too large, there must be at least " + minTicks + " ticks.";
29832
+ }
29833
+ if (nSteps > maxTicks) {
29834
+ return name + " is too small, there can be at most " + maxTicks + " ticks.";
29835
+ }
29836
+ return true;
29837
+ };
29838
+ this.validSnapStep = (step, range) => {
29839
+ return this.validateStepValue({
29840
+ step: step,
29841
+ range: range,
29842
+ name: "Snap step",
29843
+ minTicks: 5,
29844
+ maxTicks: 60
29845
+ });
29846
+ };
29847
+ this.validGridStep = (step, range) => {
29848
+ return this.validateStepValue({
29849
+ step: step,
29850
+ range: range,
29851
+ name: "Grid step",
29852
+ minTicks: 3,
29853
+ maxTicks: 60
29854
+ });
29855
+ };
29856
+ this.validStep = (step, range) => {
29857
+ return this.validateStepValue({
29858
+ step: step,
29859
+ range: range,
29860
+ name: "Step",
29861
+ minTicks: 3,
29862
+ maxTicks: 20
29863
+ });
29864
+ };
29865
+ this.validBackgroundImageSize = image => {
29866
+ // Ignore empty images
29867
+ if (!image.url) {
29868
+ return true;
29869
+ }
29870
+ const validSize = image.width <= 450 && image.height <= 450;
29871
+ if (!validSize) {
29872
+ return "Image must be smaller than 450px x 450px.";
29873
+ }
29874
+ return true;
29875
+ };
29876
+ this.validateGraphSettings = (range, step, gridStep, snapStep, image) => {
29877
+ const self = this;
29878
+ let msg;
29879
+ const goodRange = _.every(range, function (range) {
29880
+ msg = self.validRange(range);
29881
+ return msg === true;
29882
+ });
29883
+ if (!goodRange) {
29884
+ return msg;
29885
+ }
29886
+ const goodStep = _.every(step, function (step, i) {
29887
+ msg = self.validStep(step, range[i]);
29888
+ return msg === true;
29889
+ });
29890
+ if (!goodStep) {
29891
+ return msg;
29892
+ }
29893
+ const goodGridStep = _.every(gridStep, function (gridStep, i) {
29894
+ msg = self.validGridStep(gridStep, range[i]);
29895
+ return msg === true;
29896
+ });
29897
+ if (!goodGridStep) {
29898
+ return msg;
29899
+ }
29900
+ const goodSnapStep = _.every(snapStep, function (snapStep, i) {
29901
+ msg = self.validSnapStep(snapStep, range[i]);
29902
+ return msg === true;
29903
+ });
29904
+ if (!goodSnapStep) {
29905
+ return msg;
29906
+ }
29907
+ const goodImageSize = this.validBackgroundImageSize(image);
29908
+ if (goodImageSize !== true) {
29909
+ msg = goodImageSize;
29910
+ return msg;
29911
+ }
29912
+ return true;
29913
+ };
29914
+ this.changeLabel = (i, e) => {
29915
+ const val = e.target.value;
29916
+ const labels = this.state.labelsTextbox.slice();
29917
+ labels[i] = val;
29918
+ this.setState({
29919
+ labelsTextbox: labels
29920
+ }, this.changeGraph);
29921
+ };
29922
+ this.changeRange = (i, values) => {
29923
+ const ranges = this.state.rangeTextbox.slice();
29924
+ ranges[i] = values;
29925
+ const step = this.state.stepTextbox.slice();
29926
+ const gridStep = this.state.gridStepTextbox.slice();
29927
+ const snapStep = this.state.snapStepTextbox.slice();
29928
+ const scale = Util.scaleFromExtent(ranges[i], this.props.box[i]);
29929
+ if (this.validRange(ranges[i]) === true) {
29930
+ step[i] = Util.tickStepFromExtent(ranges[i], this.props.box[i]);
29931
+ const gridStepValue = Util.gridStepFromTickStep(step[i], scale);
29932
+ if (gridStepValue) {
29933
+ gridStep[i] = gridStepValue;
29934
+ }
29935
+ snapStep[i] = gridStep[i] / 2;
29936
+ }
29937
+ this.setState({
29938
+ stepTextbox: step,
29939
+ gridStepTextbox: gridStep,
29940
+ snapStepTextbox: snapStep,
29941
+ rangeTextbox: ranges
29942
+ }, this.changeGraph);
29943
+ };
29944
+ this.changeStep = step => {
29945
+ this.setState({
29946
+ stepTextbox: step
29947
+ }, this.changeGraph);
29948
+ };
29949
+ this.changeSnapStep = snapStep => {
29950
+ this.setState({
29951
+ snapStepTextbox: snapStep
29952
+ }, this.changeGraph);
29953
+ };
29954
+ this.changeGridStep = gridStep => {
29955
+ this.setState({
29956
+ gridStepTextbox: gridStep,
29957
+ snapStepTextbox: _.map(gridStep, function (step) {
29958
+ return step / 2;
29959
+ })
29960
+ }, this.changeGraph);
29961
+ };
29962
+ this.changeGraph = () => {
29963
+ const labels = this.state.labelsTextbox;
29964
+ const range = _.map(this.state.rangeTextbox, function (range) {
29965
+ return _.map(range, Number);
29966
+ });
29967
+ const step = _.map(this.state.stepTextbox, Number);
29968
+ const gridStep = this.state.gridStepTextbox;
29969
+ const snapStep = this.state.snapStepTextbox;
29970
+ const image = this.state.backgroundImage;
29971
+
29972
+ // validationResult is either:
29973
+ // true -> the settings are valid
29974
+ // a string -> the settings are invalid, and the explanation
29975
+ // is contained in the string
29976
+ // TODO(aria): Refactor this to not be confusing
29977
+ const validationResult = this.validateGraphSettings(range, step, gridStep, snapStep, image);
29978
+ if (validationResult === true) {
29979
+ // either true or a string
29980
+ this.change({
29981
+ valid: true,
29982
+ labels: labels,
29983
+ range: range,
29984
+ step: step,
29985
+ gridStep: gridStep,
29986
+ snapStep: snapStep,
29987
+ backgroundImage: image
29988
+ });
29989
+ } else {
29990
+ this.change({
29991
+ valid: validationResult // a string message, not false
29992
+ });
29993
+ }
29994
+ };
29995
+ this.state = _extends({
29996
+ isExpanded: true
29997
+ }, InteractiveGraphSettings.stateFromProps(props));
29998
+ }
29999
+ componentDidMount() {
30000
+ // TODO(scottgrant): This is a hack to remove the deprecated call to
30001
+ // this.isMounted() but is still considered an anti-pattern.
30002
+ this._isMounted = true;
30003
+ this.changeGraph = _.debounce(this.changeGraph, 300);
30004
+ }
30005
+ UNSAFE_componentWillReceiveProps(nextProps) {
30006
+ // Make sure that state updates when switching
30007
+ // between different items in a multi-item editor.
30008
+ if (!_.isEqual(this.props.labels, nextProps.labels) || !_.isEqual(this.props.gridStep, nextProps.gridStep) || !_.isEqual(this.props.snapStep, nextProps.snapStep) || !_.isEqual(this.props.step, nextProps.step) || !_.isEqual(this.props.range, nextProps.range) || !_.isEqual(this.props.backgroundImage, nextProps.backgroundImage)) {
30009
+ this.setState(InteractiveGraphSettings.stateFromProps(nextProps));
30010
+ }
30011
+ }
30012
+ componentWillUnmount() {
30013
+ this._isMounted = false;
30014
+ }
30015
+ render() {
30016
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
30017
+ title: "Common Graph Settings",
30018
+ isOpen: this.state.isExpanded,
30019
+ isCollapsible: true,
30020
+ onToggle: () => this.setState({
30021
+ isExpanded: !this.state.isExpanded
30022
+ })
30023
+ }), this.state.isExpanded && /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement("div", {
30024
+ className: "graph-settings"
30025
+ }, /*#__PURE__*/React.createElement("div", {
30026
+ className: "perseus-widget-row"
30027
+ }, /*#__PURE__*/React.createElement("div", {
30028
+ className: "perseus-widget-left-col"
30029
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
30030
+ label: "x Label"
30031
+ }, /*#__PURE__*/React.createElement("input", {
30032
+ type: "text",
30033
+ className: "graph-settings-axis-label",
30034
+ ref: this.labelXRef,
30035
+ onChange: e => this.changeLabel(0, e),
30036
+ value: this.state.labelsTextbox[0] || ""
30037
+ }))), /*#__PURE__*/React.createElement("div", {
30038
+ className: "perseus-widget-right-col"
30039
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
30040
+ label: "y Label"
30041
+ }, /*#__PURE__*/React.createElement("input", {
30042
+ type: "text",
30043
+ className: "graph-settings-axis-label",
30044
+ ref: this.labelYRef,
30045
+ onChange: e => this.changeLabel(1, e),
30046
+ value: this.state.labelsTextbox[1] || ""
30047
+ })))), /*#__PURE__*/React.createElement("div", {
30048
+ className: "perseus-widget-row"
30049
+ }, /*#__PURE__*/React.createElement("div", {
30050
+ className: "perseus-widget-left-col"
30051
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
30052
+ label: "x Range"
30053
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
30054
+ value: this.state.rangeTextbox[0],
30055
+ onChange: vals => this.changeRange(0, vals)
30056
+ }))), /*#__PURE__*/React.createElement("div", {
30057
+ className: "perseus-widget-right-col"
30058
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
30059
+ label: "y Range"
30060
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
30061
+ value: this.state.rangeTextbox[1],
30062
+ onChange: vals => this.changeRange(1, vals)
30063
+ })))), /*#__PURE__*/React.createElement("div", {
30064
+ className: "perseus-widget-row"
30065
+ }, /*#__PURE__*/React.createElement("div", {
30066
+ className: "perseus-widget-left-col"
30067
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
30068
+ label: "Tick Step"
30069
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
30070
+ value: this.state.stepTextbox,
30071
+ onChange: this.changeStep
30072
+ }))), /*#__PURE__*/React.createElement("div", {
30073
+ className: "perseus-widget-right-col"
30074
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
30075
+ label: "Grid Step"
30076
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
30077
+ value: this.state.gridStepTextbox,
30078
+ onChange: this.changeGridStep
30079
+ })))), /*#__PURE__*/React.createElement("div", {
30080
+ className: "perseus-widget-row"
30081
+ }, /*#__PURE__*/React.createElement("div", {
30082
+ className: "perseus-widget-left-col"
30083
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
30084
+ label: "Snap Step"
30085
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
30086
+ value: this.state.snapStepTextbox,
30087
+ onChange: this.changeSnapStep
30088
+ })))), /*#__PURE__*/React.createElement("div", {
30089
+ className: "perseus-widget-row"
30090
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
30091
+ label: "Markings:"
30092
+ }, /*#__PURE__*/React.createElement(ButtonGroup$2, {
30093
+ value: this.props.markings,
30094
+ allowEmpty: false,
30095
+ buttons: [{
30096
+ value: "graph",
30097
+ content: "Graph"
30098
+ }, {
30099
+ value: "grid",
30100
+ content: "Grid"
30101
+ }, {
30102
+ value: "none",
30103
+ content: "None"
30104
+ }],
30105
+ onChange: this.change("markings")
30106
+ }))), /*#__PURE__*/React.createElement("div", {
30107
+ className: "perseus-widget-left-col"
30108
+ }, /*#__PURE__*/React.createElement(Checkbox$1, {
30109
+ label: "Show tooltips",
30110
+ checked: this.props.showTooltips,
30111
+ onChange: value => {
30112
+ this.change({
30113
+ showTooltips: value
30114
+ });
30115
+ }
30116
+ }))), /*#__PURE__*/React.createElement(LabeledRow, {
30117
+ label: "Background image URL:",
30118
+ style: styles$E.resetSpaceTop
30119
+ }, /*#__PURE__*/React.createElement("input", {
30120
+ type: "text",
30121
+ className: css(styles$E.backgroundUrlInput),
30122
+ ref: this.bgUrlRef,
30123
+ value: this.state.backgroundImage.url || "",
30124
+ onChange: e => {
30125
+ // Shallow-copied clone
30126
+ const image = _extends({}, this.props.backgroundImage);
30127
+ image.url = e.target.value;
30128
+ this.setState({
30129
+ backgroundImage: image
30130
+ });
30131
+ },
30132
+ onKeyPress: this.changeBackgroundUrl,
30133
+ onBlur: this.changeBackgroundUrl
30134
+ }), /*#__PURE__*/React.createElement(InfoTip$d, null, /*#__PURE__*/React.createElement("p", null, "Create an image in graphie, or use the \"Add image\" function to create a background."))), /*#__PURE__*/React.createElement(View, {
30135
+ style: styles$E.protractorSection
30136
+ }, /*#__PURE__*/React.createElement(View, {
30137
+ style: styles$E.checkboxRow
30138
+ }, /*#__PURE__*/React.createElement(Checkbox$1, {
30139
+ label: "Show protractor",
30140
+ checked: this.props.showProtractor,
30141
+ onChange: value => {
30142
+ this.change({
30143
+ showProtractor: value
30144
+ });
30145
+ },
30146
+ style: styles$E.resetSpaceTop
30147
+ })), this.props.showProtractor && /*#__PURE__*/React.createElement(Banner, {
30148
+ layout: "floating",
30149
+ text: "The protractor is not accessible. Please consider an alternate approach.",
30150
+ kind: "warning"
30151
+ }))));
30152
+ }
30153
+ }
30154
+ InteractiveGraphSettings.defaultProps = {
30155
+ box: [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall],
30156
+ labels: ["x", "y"],
30157
+ range: [[-10, 10], [-10, 10]],
30158
+ step: [1, 1],
30159
+ gridStep: [1, 1],
30160
+ snapStep: [1, 1],
30161
+ valid: true,
30162
+ backgroundImage: defaultBackgroundImage$1,
30163
+ markings: "graph",
30164
+ showProtractor: false,
30165
+ showTooltips: false
30166
+ };
30167
+ const styles$E = StyleSheet.create({
30168
+ resetSpaceTop: {
30169
+ marginTop: 0
30170
+ },
30171
+ backgroundUrlInput: {
30172
+ border: `1px solid ${color.offBlack32}`,
30173
+ borderRadius: spacing.xxxSmall_4,
30174
+ padding: spacing.xxxSmall_4
30175
+ },
30176
+ checkboxRow: {
30177
+ flexDirection: "row",
30178
+ alignItems: "center",
30179
+ justifyContent: "space-between",
30180
+ marginBottom: spacing.xSmall_8
30181
+ },
30182
+ protractorSection: {
30183
+ marginTop: spacing.xSmall_8,
30184
+ borderTop: `1px solid ${color.offBlack16}`,
30185
+ paddingTop: spacing.xSmall_8,
30186
+ paddingBottom: spacing.xSmall_8,
30187
+ borderBottom: `1px solid ${color.offBlack16}`
30188
+ }
30189
+ });
30190
+
30191
+ const SegmentCountSelector = ({
30192
+ numSegments: _numSegments = 1,
30193
+ onChange
30194
+ }) => /*#__PURE__*/React.createElement(SingleSelect, {
30195
+ key: "segment-select",
30196
+ selectedValue: `${_numSegments}`
30197
+ // Never uses placeholder, always has value
30198
+ ,
30199
+ placeholder: "",
30200
+ onChange: newValue => {
30201
+ const num = +newValue;
30202
+ onChange(num);
30203
+ },
30204
+ style: styles$D.singleSelectShort
30205
+ }, _.range(1, 7).map(n => /*#__PURE__*/React.createElement(OptionItem, {
30206
+ key: n,
30207
+ value: `${n}`,
30208
+ label: `${n} segment${n > 1 ? "s" : ""}`
30209
+ })));
30210
+ const styles$D = StyleSheet.create({
30211
+ singleSelectShort: {
30212
+ // Non-standard spacing, but it's the smallest we can go
30213
+ // without running into styling issues with the dropdown.
30214
+ height: 26
30215
+ }
30216
+ });
30217
+
30218
+ /**
30219
+ * Dropdown for selecting a locked figure to add to an interactive graph.
30220
+ * Locked figures are elements (points, segmeents, etc.) that are not
30221
+ * interactive, just present in the graph's background.
30222
+ *
30223
+ * Used in the interactive graph editor's locked figures section.
30224
+ */
30225
+ const LockedFigureSelect = props => {
30226
+ const {
30227
+ id,
30228
+ onChange
30229
+ } = props;
30230
+ const figureTypes = ["point", "line", "vector", "ellipse", "polygon", "function"];
30231
+ if (props.showLabelsFlag) {
30232
+ figureTypes.push("label");
30233
+ }
30234
+ return /*#__PURE__*/React.createElement(View, {
30235
+ style: styles$C.container
30236
+ }, /*#__PURE__*/React.createElement(ActionMenu, {
30237
+ menuText: "Add locked figure",
30238
+ style: styles$C.addElementSelect
30239
+ }, figureTypes.map(figureType => /*#__PURE__*/React.createElement(ActionItem, {
30240
+ key: `${id}-${figureType}`,
30241
+ label: figureType,
30242
+ onClick: () => onChange(figureType)
30243
+ }))));
30244
+ };
30245
+ const styles$C = StyleSheet.create({
30246
+ container: {
30247
+ marginTop: spacing.xSmall_8
30248
+ },
30249
+ addElementSelect: {
30250
+ backgroundColor: color.fadedBlue8,
30251
+ borderRadius: spacing.xxxSmall_4
30252
+ }
30253
+ });
30254
+
30255
+ /**
30256
+ * This is a custom text field of type="number" for use in Perseus Editors.
30257
+ *
30258
+ * This makes it so that the text field's input number updates on scroll
30259
+ * without scrolling the page.
30260
+ *
30261
+ * NOTE 1: Native HTML number inputs do not update the number value on scroll,
30262
+ * they only scroll the page. Inputs in React do NOT work this way (explanation
30263
+ * here: https://stackoverflow.com/a/68266494). By default, scrolling on a
30264
+ * focused number input in React causes BOTH the input value to change AND
30265
+ * the page to scroll. The behavior in this component is an improvement on
30266
+ * the React behavior, but it's the opposite of the native HTML behavior.
30267
+ *
30268
+ * NOTE 2: Firefox seems to have a custom override for this. Even with this
30269
+ * stopPropogation, Firefox matches the native HTML behavior.
30270
+ */
30271
+ const ScrolllessNumberTextField = props => {
30272
+ const inputRef = React.useRef(null);
30273
+ React.useEffect(() => {
30274
+ const ref = inputRef.current;
30275
+
30276
+ // stopPropogation makes it so that the page scroll event is not
30277
+ // triggered when the input is focused and the user scrolls.
30278
+ // The input value will still change on scroll.
30279
+ const ignoreScroll = e => {
30280
+ e.stopPropagation();
30281
+ };
30282
+ ref == null || ref.addEventListener("wheel", ignoreScroll);
30283
+ return () => {
30284
+ ref == null || ref.removeEventListener("wheel", ignoreScroll);
30285
+ };
30286
+ }, [inputRef]);
30287
+ return /*#__PURE__*/React.createElement(TextField, _extends({
30288
+ type: "number"
30289
+ }, props, {
30290
+ ref: inputRef
30291
+ }));
30292
+ };
30293
+
30294
+ function focusWithChromeStickyFocusBugWorkaround(element) {
30295
+ // NOTE(marksandstrom) Chrome sticky focus bug.
30296
+ //
30297
+ // There is a bug in Chrome related to sticky positioning and
30298
+ // focus: When a child of an element with sticky positioning is
30299
+ // focused with the focus() method, the scrollable area
30300
+ // containing that element will scroll to the top (even though
30301
+ // the element is already visble on the page). For a dropdown
30302
+ // this means that the area will be scrolled to the top when
30303
+ // the user interacts with the dropdown (since we actively
30304
+ // manage dropdown header and option focus for keyboard users).
30305
+ //
30306
+ // To work around this bug the `preventScroll` focus option is
30307
+ // used.
30308
+ //
30309
+ // Using `focusOptions` is an experimental browser feature
30310
+ // "that should not be used in production code". However, since
30311
+ // Chrome is the only browser we support that exhibits this
30312
+ // buggy behavior and using `focusOptions` works around the
30313
+ // bug, it is perfectly reasonable to use the feature in this
30314
+ // case. We are not relying on the effect of the
30315
+ // `preventScroll` option otherwise.
30316
+ //
30317
+ // focusOptions documentation:
30318
+ //
30319
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
30320
+ //
30321
+ // Note that there could be cases where we want default focus() behavior.
30322
+ // One that comes to mind is a dropdown with a long list of options that
30323
+ // might be rendered off screen when opened. With this workaround the
30324
+ // selected option would not be scrolled into view if the option is near
30325
+ // the end of the list. We're not aware of any uses of the dropdown where
30326
+ // this is an issue, however, so there isn't currently a way to out of the
30327
+ // `preventScroll` behavior.
30328
+
30329
+ // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element'.
30330
+ element.focus({
30331
+ preventScroll: true
30332
+ });
30333
+ }
30334
+ function degreeToRadian(degrees) {
30335
+ return degrees / 180 * Math.PI;
30336
+ }
30337
+ function radianToDegree(radians) {
30338
+ return radians / Math.PI * 180;
30339
+ }
30340
+
30341
+ const AngleInput = props => {
30342
+ const {
30343
+ angle,
30344
+ onChange
30345
+ } = props;
30346
+ const [angleInput, setAngleInput] = React.useState(radianToDegree(angle).toString());
30347
+ function handleAngleChange(newValue) {
30348
+ // Update the local state (update the input field value).
30349
+ setAngleInput(newValue);
30350
+
30351
+ // If the new value is not a number, don't update the props.
30352
+ // If it's empty, keep the props the same value instead of setting to 0.
30353
+ if (isNaN(+newValue) || newValue === "") {
30354
+ return;
30355
+ }
30356
+
30357
+ // Update the graph.
30358
+ onChange(degreeToRadian(newValue));
30359
+ }
30360
+ return /*#__PURE__*/React.createElement(LabelMedium, {
30361
+ tag: "label",
30362
+ style: styles$B.row
30363
+ }, "angle (degrees)", /*#__PURE__*/React.createElement(Strut, {
30364
+ size: spacing.xxSmall_6
30365
+ }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
30366
+ value: angleInput,
30367
+ onChange: handleAngleChange,
30368
+ style: styles$B.textField
30369
+ }), /*#__PURE__*/React.createElement(Strut, {
30370
+ size: spacing.xxSmall_6
30371
+ }));
30372
+ };
30373
+ const styles$B = StyleSheet.create({
30374
+ row: {
30375
+ display: "flex",
30376
+ flexDirection: "row",
30377
+ alignItems: "center"
30378
+ },
30379
+ textField: {
30380
+ width: spacing.xxxLarge_64
30381
+ }
30382
+ });
30383
+
30384
+ const CoordinatePairInput = props => {
30385
+ const {
30386
+ coord,
30387
+ labels,
30388
+ error,
30389
+ style,
29924
30390
  onChange
29925
30391
  } = props;
29926
30392
 
@@ -29953,30 +30419,30 @@ const CoordinatePairInput = props => {
29953
30419
  onChange(newCoords);
29954
30420
  }
29955
30421
  return /*#__PURE__*/React.createElement(View, {
29956
- style: [styles$F.row, style]
30422
+ style: [styles$A.row, style]
29957
30423
  }, /*#__PURE__*/React.createElement(LabelMedium, {
29958
30424
  tag: "label",
29959
- style: styles$F.row
30425
+ style: styles$A.row
29960
30426
  }, labels ? labels[0] : "x coord", /*#__PURE__*/React.createElement(Strut, {
29961
30427
  size: spacing.xxSmall_6
29962
30428
  }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
29963
30429
  value: coordState[0],
29964
30430
  onChange: newValue => handleCoordChange(newValue, 0),
29965
- style: [styles$F.textField, error ? styles$F.errorField : undefined]
30431
+ style: [styles$A.textField, error ? styles$A.errorField : undefined]
29966
30432
  })), /*#__PURE__*/React.createElement(Strut, {
29967
30433
  size: spacing.medium_16
29968
30434
  }), /*#__PURE__*/React.createElement(LabelMedium, {
29969
30435
  tag: "label",
29970
- style: styles$F.row
30436
+ style: styles$A.row
29971
30437
  }, labels ? labels[1] : "y coord", /*#__PURE__*/React.createElement(Strut, {
29972
30438
  size: spacing.xxSmall_6
29973
30439
  }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
29974
30440
  value: coordState[1],
29975
30441
  onChange: newValue => handleCoordChange(newValue, 1),
29976
- style: [styles$F.textField, error ? styles$F.errorField : undefined]
30442
+ style: [styles$A.textField, error ? styles$A.errorField : undefined]
29977
30443
  })));
29978
30444
  };
29979
- const styles$F = StyleSheet.create({
30445
+ const styles$A = StyleSheet.create({
29980
30446
  row: {
29981
30447
  display: "flex",
29982
30448
  flexDirection: "row",
@@ -30009,14 +30475,14 @@ const PerseusEditorAccordion = props => {
30009
30475
  }, /*#__PURE__*/React.createElement(AccordionSection, {
30010
30476
  expanded: expanded,
30011
30477
  onToggle: onToggle,
30012
- style: [styles$E.container, containerStyle],
30013
- headerStyle: styles$E.accordionHeader,
30478
+ style: [styles$z.container, containerStyle],
30479
+ headerStyle: styles$z.accordionHeader,
30014
30480
  header: header
30015
30481
  }, /*#__PURE__*/React.createElement(View, {
30016
- style: [styles$E.accordionPanel, panelStyle]
30482
+ style: [styles$z.accordionPanel, panelStyle]
30017
30483
  }, children)));
30018
30484
  };
30019
- const styles$E = StyleSheet.create({
30485
+ const styles$z = StyleSheet.create({
30020
30486
  container: {
30021
30487
  backgroundColor: color.fadedBlue8,
30022
30488
  marginTop: spacing.xSmall_8
@@ -30047,13 +30513,13 @@ const ColorSwatch = props => {
30047
30513
  "aria-label":
30048
30514
  // Don't include an aria label for decorative circles.
30049
30515
  !decorative ? `${color$1}, ${filled ? "filled" : "open"}` : undefined,
30050
- style: [styles$D.colorSwatch, {
30516
+ style: [styles$y.colorSwatch, {
30051
30517
  border: `4px solid ${lockedFigureColors[color$1]}`,
30052
30518
  backgroundColor: filled ? lockedFigureColors[color$1] : color.white
30053
30519
  }]
30054
30520
  });
30055
30521
  };
30056
- const styles$D = StyleSheet.create({
30522
+ const styles$y = StyleSheet.create({
30057
30523
  colorSwatch: {
30058
30524
  // Add a white outline so that the color swatch is visible when
30059
30525
  // the dropdown option is highlighted with its blue background.
@@ -30072,10 +30538,10 @@ const ColorSelect = props => {
30072
30538
  onChange
30073
30539
  } = props;
30074
30540
  return /*#__PURE__*/React.createElement(View, {
30075
- style: [styles$C.row, style]
30541
+ style: [styles$x.row, style]
30076
30542
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30077
30543
  tag: "label",
30078
- style: styles$C.row
30544
+ style: styles$x.row
30079
30545
  }, "color", /*#__PURE__*/React.createElement(Strut, {
30080
30546
  size: spacing.xxSmall_6
30081
30547
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30094,7 +30560,7 @@ const ColorSelect = props => {
30094
30560
  })
30095
30561
  })))));
30096
30562
  };
30097
- const styles$C = StyleSheet.create({
30563
+ const styles$x = StyleSheet.create({
30098
30564
  row: {
30099
30565
  display: "flex",
30100
30566
  flexDirection: "row",
@@ -30113,17 +30579,17 @@ const EllipseSwatch = props => {
30113
30579
  } = props;
30114
30580
  return /*#__PURE__*/React.createElement(View, {
30115
30581
  "aria-label": `${color}, stroke ${strokeStyle}, fill ${fillStyle}`,
30116
- style: [styles$B.container, {
30582
+ style: [styles$w.container, {
30117
30583
  border: `4px ${strokeStyle} ${lockedFigureColors[color]}`
30118
30584
  }]
30119
30585
  }, /*#__PURE__*/React.createElement(View, {
30120
- style: [styles$B.innerCircle, {
30586
+ style: [styles$w.innerCircle, {
30121
30587
  backgroundColor: lockedFigureColors[color],
30122
30588
  opacity: fillStyle === "white" ? 0 : lockedFigureFillStyles[fillStyle]
30123
30589
  }]
30124
30590
  }));
30125
30591
  };
30126
- const styles$B = StyleSheet.create({
30592
+ const styles$w = StyleSheet.create({
30127
30593
  container: {
30128
30594
  // Add a white outline so that the color swatch is visible when
30129
30595
  // the dropdown option is highlighted with its blue background.
@@ -30149,7 +30615,7 @@ const LineStrokeSelect = props => {
30149
30615
  } = props;
30150
30616
  return /*#__PURE__*/React.createElement(LabelMedium, {
30151
30617
  tag: "label",
30152
- style: styles$A.lineStrokeSelect
30618
+ style: styles$v.lineStrokeSelect
30153
30619
  }, "stroke", /*#__PURE__*/React.createElement(Strut, {
30154
30620
  size: spacing.xxxSmall_4
30155
30621
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30166,7 +30632,7 @@ const LineStrokeSelect = props => {
30166
30632
  label: "dashed"
30167
30633
  })));
30168
30634
  };
30169
- const styles$A = StyleSheet.create({
30635
+ const styles$v = StyleSheet.create({
30170
30636
  lineStrokeSelect: {
30171
30637
  display: "flex",
30172
30638
  flexDirection: "row",
@@ -30188,40 +30654,40 @@ const LockedFigureSettingsActions = props => {
30188
30654
  onRemove
30189
30655
  } = props;
30190
30656
  return /*#__PURE__*/React.createElement(View, {
30191
- style: styles$z.container
30657
+ style: styles$u.container
30192
30658
  }, /*#__PURE__*/React.createElement(Button, {
30193
30659
  startIcon: trashIcon,
30194
30660
  "aria-label": `Delete locked ${figureType}`,
30195
30661
  onClick: onRemove,
30196
30662
  kind: "tertiary",
30197
- style: styles$z.deleteButton
30663
+ style: styles$u.deleteButton
30198
30664
  }, "Delete"), /*#__PURE__*/React.createElement(Spring, null), /*#__PURE__*/React.createElement(IconButton, {
30199
30665
  icon: caretDoubleUpIcon,
30200
30666
  size: "small",
30201
30667
  "aria-label": `Move locked ${figureType} to the back`,
30202
30668
  onClick: () => onMove("back"),
30203
- style: styles$z.iconButton
30669
+ style: styles$u.iconButton
30204
30670
  }), /*#__PURE__*/React.createElement(IconButton, {
30205
30671
  icon: caretUpIcon,
30206
30672
  size: "small",
30207
30673
  "aria-label": `Move locked ${figureType} backward`,
30208
30674
  onClick: () => onMove("backward"),
30209
- style: styles$z.iconButton
30675
+ style: styles$u.iconButton
30210
30676
  }), /*#__PURE__*/React.createElement(IconButton, {
30211
30677
  icon: caretDownIcon,
30212
30678
  size: "small",
30213
30679
  "aria-label": `Move locked ${figureType} forward`,
30214
30680
  onClick: () => onMove("forward"),
30215
- style: styles$z.iconButton
30681
+ style: styles$u.iconButton
30216
30682
  }), /*#__PURE__*/React.createElement(IconButton, {
30217
30683
  icon: caretDoubleDownIcon,
30218
30684
  size: "small",
30219
30685
  "aria-label": `Move locked ${figureType} to the front`,
30220
30686
  onClick: () => onMove("front"),
30221
- style: styles$z.iconButton
30687
+ style: styles$u.iconButton
30222
30688
  }));
30223
30689
  };
30224
- const styles$z = StyleSheet.create({
30690
+ const styles$u = StyleSheet.create({
30225
30691
  container: {
30226
30692
  width: "100%",
30227
30693
  flexDirection: "row",
@@ -30240,7 +30706,7 @@ const styles$z = StyleSheet.create({
30240
30706
  });
30241
30707
 
30242
30708
  const {
30243
- InfoTip: InfoTip$d
30709
+ InfoTip: InfoTip$c
30244
30710
  } = components;
30245
30711
  const LockedEllipseSettings = props => {
30246
30712
  const {
@@ -30268,7 +30734,7 @@ const LockedEllipseSettings = props => {
30268
30734
  /*#__PURE__*/
30269
30735
  // Summary: Ellipse, center, radius, color (opacity, dashed)
30270
30736
  React.createElement(View, {
30271
- style: styles$y.row
30737
+ style: styles$t.row
30272
30738
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Ellipse (${center[0]}, ${center[1]}), radius ${radius[0]}, ${radius[1]}`), /*#__PURE__*/React.createElement(Strut, {
30273
30739
  size: spacing.xSmall_8
30274
30740
  }), /*#__PURE__*/React.createElement(EllipseSwatch, {
@@ -30277,19 +30743,19 @@ const LockedEllipseSettings = props => {
30277
30743
  strokeStyle: strokeStyle
30278
30744
  }))
30279
30745
  }, /*#__PURE__*/React.createElement(View, {
30280
- style: styles$y.row
30746
+ style: styles$t.row
30281
30747
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30282
30748
  coord: center,
30283
- style: styles$y.spaceUnder,
30749
+ style: styles$t.spaceUnder,
30284
30750
  onChange: newCoords => onChangeProps({
30285
30751
  center: newCoords
30286
30752
  })
30287
30753
  }), /*#__PURE__*/React.createElement(View, {
30288
- style: styles$y.spaceUnder
30289
- }, /*#__PURE__*/React.createElement(InfoTip$d, null, "The coordinates for the center of the ellipse."))), /*#__PURE__*/React.createElement(CoordinatePairInput, {
30754
+ style: styles$t.spaceUnder
30755
+ }, /*#__PURE__*/React.createElement(InfoTip$c, null, "The coordinates for the center of the ellipse."))), /*#__PURE__*/React.createElement(CoordinatePairInput, {
30290
30756
  coord: radius,
30291
30757
  labels: ["x radius", "y radius"],
30292
- style: styles$y.spaceUnder,
30758
+ style: styles$t.spaceUnder,
30293
30759
  onChange: newCoords => onChangeProps({
30294
30760
  radius: newCoords
30295
30761
  })
@@ -30301,7 +30767,7 @@ const LockedEllipseSettings = props => {
30301
30767
  }), /*#__PURE__*/React.createElement(Strut, {
30302
30768
  size: spacing.xSmall_8
30303
30769
  }), /*#__PURE__*/React.createElement(View, {
30304
- style: [styles$y.row, styles$y.spaceUnder]
30770
+ style: [styles$t.row, styles$t.spaceUnder]
30305
30771
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30306
30772
  selectedValue: color,
30307
30773
  onChange: handleColorChange
@@ -30309,7 +30775,7 @@ const LockedEllipseSettings = props => {
30309
30775
  size: spacing.medium_16
30310
30776
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30311
30777
  tag: "label",
30312
- style: [styles$y.row, styles$y.truncatedWidth]
30778
+ style: [styles$t.row, styles$t.truncatedWidth]
30313
30779
  }, "fill", /*#__PURE__*/React.createElement(Strut, {
30314
30780
  size: spacing.xxSmall_6
30315
30781
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30335,7 +30801,7 @@ const LockedEllipseSettings = props => {
30335
30801
  onRemove: onRemove
30336
30802
  }));
30337
30803
  };
30338
- const styles$y = StyleSheet.create({
30804
+ const styles$t = StyleSheet.create({
30339
30805
  row: {
30340
30806
  display: "flex",
30341
30807
  flexDirection: "row",
@@ -30356,15 +30822,15 @@ const LineSwatch = props => {
30356
30822
  lineStyle
30357
30823
  } = props;
30358
30824
  return /*#__PURE__*/React.createElement(View, {
30359
- style: styles$x.container
30825
+ style: styles$s.container
30360
30826
  }, /*#__PURE__*/React.createElement(View, {
30361
30827
  "aria-label": `${color}, ${lineStyle}`,
30362
- style: [styles$x.lineSwatch, {
30828
+ style: [styles$s.lineSwatch, {
30363
30829
  border: `5px ${lineStyle} ${lockedFigureColors[color]}`
30364
30830
  }]
30365
30831
  }));
30366
30832
  };
30367
- const styles$x = StyleSheet.create({
30833
+ const styles$s = StyleSheet.create({
30368
30834
  container: {
30369
30835
  backgroundColor: color.white,
30370
30836
  justifyContent: "center",
@@ -30449,9 +30915,9 @@ const LockedFunctionSettings = props => {
30449
30915
  expanded: props.expanded,
30450
30916
  onToggle: props.onToggle,
30451
30917
  header: /*#__PURE__*/React.createElement(View, {
30452
- style: styles$w.row
30918
+ style: styles$r.row
30453
30919
  }, /*#__PURE__*/React.createElement(LabelLarge, {
30454
- style: styles$w.accordionHeader
30920
+ style: styles$r.accordionHeader
30455
30921
  }, lineLabel), /*#__PURE__*/React.createElement(Strut, {
30456
30922
  size: spacing.xSmall_8
30457
30923
  }), /*#__PURE__*/React.createElement(LineSwatch, {
@@ -30459,7 +30925,7 @@ const LockedFunctionSettings = props => {
30459
30925
  lineStyle: strokeStyle
30460
30926
  }))
30461
30927
  }, /*#__PURE__*/React.createElement(View, {
30462
- style: [styles$w.row, styles$w.spaceUnder]
30928
+ style: [styles$r.row, styles$r.spaceUnder]
30463
30929
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30464
30930
  selectedValue: lineColor,
30465
30931
  onChange: newValue => {
@@ -30473,14 +30939,14 @@ const LockedFunctionSettings = props => {
30473
30939
  handlePropChange("strokeStyle", newValue);
30474
30940
  }
30475
30941
  })), /*#__PURE__*/React.createElement(View, {
30476
- style: [styles$w.row, styles$w.rowSpace]
30942
+ style: [styles$r.row, styles$r.rowSpace]
30477
30943
  }, /*#__PURE__*/React.createElement(SingleSelect, {
30478
30944
  selectedValue: directionalAxis,
30479
30945
  onChange: newValue => {
30480
30946
  handlePropChange("directionalAxis", newValue);
30481
30947
  },
30482
30948
  "aria-label": "equation prefix",
30483
- style: [styles$w.dropdownLabel, styles$w.axisMenu]
30949
+ style: [styles$r.dropdownLabel, styles$r.axisMenu]
30484
30950
  // Placeholder is required, but never gets used.
30485
30951
  ,
30486
30952
  placeholder: ""
@@ -30499,17 +30965,17 @@ const LockedFunctionSettings = props => {
30499
30965
  onChange: newValue => {
30500
30966
  handlePropChange("equation", newValue);
30501
30967
  },
30502
- style: [styles$w.textField]
30968
+ style: [styles$r.textField]
30503
30969
  })), /*#__PURE__*/React.createElement(View, {
30504
- style: [styles$w.row, styles$w.rowSpace]
30970
+ style: [styles$r.row, styles$r.rowSpace]
30505
30971
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30506
30972
  tag: "label",
30507
- style: [styles$w.dropdownLabel, styles$w.domainMin]
30973
+ style: [styles$r.dropdownLabel, styles$r.domainMin]
30508
30974
  }, `${domainRangeText} min`, /*#__PURE__*/React.createElement(Strut, {
30509
30975
  size: spacing.xxSmall_6
30510
30976
  }), /*#__PURE__*/React.createElement(TextField, {
30511
30977
  type: "number",
30512
- style: styles$w.domainMinField,
30978
+ style: styles$r.domainMinField,
30513
30979
  value: domainEntries[0],
30514
30980
  onChange: newValue => {
30515
30981
  handleDomainChange(0, newValue);
@@ -30519,12 +30985,12 @@ const LockedFunctionSettings = props => {
30519
30985
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30520
30986
  tag: "label",
30521
30987
  "aria-label": `${domainRangeText} max`,
30522
- style: [styles$w.dropdownLabel, styles$w.domainMax]
30988
+ style: [styles$r.dropdownLabel, styles$r.domainMax]
30523
30989
  }, "max", /*#__PURE__*/React.createElement(Strut, {
30524
30990
  size: spacing.xxSmall_6
30525
30991
  }), /*#__PURE__*/React.createElement(TextField, {
30526
30992
  type: "number",
30527
- style: styles$w.domainMaxField,
30993
+ style: styles$r.domainMaxField,
30528
30994
  value: domainEntries[1],
30529
30995
  onChange: newValue => {
30530
30996
  handleDomainChange(1, newValue);
@@ -30532,11 +30998,11 @@ const LockedFunctionSettings = props => {
30532
30998
  }))), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
30533
30999
  header: /*#__PURE__*/React.createElement(LabelLarge, null, "Example Functions"),
30534
31000
  expanded: false,
30535
- containerStyle: styles$w.exampleWorkspace,
30536
- panelStyle: styles$w.exampleAccordionPanel
31001
+ containerStyle: styles$r.exampleWorkspace,
31002
+ panelStyle: styles$r.exampleAccordionPanel
30537
31003
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30538
31004
  tag: "label",
30539
- style: styles$w.dropdownLabel
31005
+ style: styles$r.dropdownLabel
30540
31006
  }, "Choose a category", /*#__PURE__*/React.createElement(Strut, {
30541
31007
  size: spacing.xxSmall_6
30542
31008
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30550,7 +31016,7 @@ const LockedFunctionSettings = props => {
30550
31016
  label: category
30551
31017
  });
30552
31018
  }))), exampleCategorySelected && /*#__PURE__*/React.createElement("ul", {
30553
- className: css(styles$w.exampleContainer)
31019
+ className: css(styles$r.exampleContainer)
30554
31020
  }, exampleContent.map((example, index) => /*#__PURE__*/React.createElement(ExampleItem, {
30555
31021
  category: exampleCategory,
30556
31022
  example: example,
@@ -30572,29 +31038,29 @@ const ExampleItem = props => {
30572
31038
  const exampleId = useId();
30573
31039
  return /*#__PURE__*/React.createElement("li", {
30574
31040
  key: `${category}-${index}`,
30575
- className: css(styles$w.exampleRow)
31041
+ className: css(styles$r.exampleRow)
30576
31042
  }, /*#__PURE__*/React.createElement(IconButton, {
30577
31043
  icon: autoPasteIcon,
30578
31044
  "aria-label": "paste example",
30579
31045
  "aria-describedby": exampleId,
30580
31046
  onClick: () => pasteEquationFn("equation", example),
30581
31047
  size: "medium",
30582
- style: styles$w.copyPasteButton
31048
+ style: styles$r.copyPasteButton
30583
31049
  }), /*#__PURE__*/React.createElement(IconButton, {
30584
31050
  icon: copyIcon,
30585
31051
  "aria-label": "copy example",
30586
31052
  "aria-describedby": exampleId,
30587
31053
  onClick: () => navigator.clipboard.writeText(example),
30588
31054
  size: "medium",
30589
- style: styles$w.copyPasteButton
31055
+ style: styles$r.copyPasteButton
30590
31056
  }), /*#__PURE__*/React.createElement(Strut, {
30591
31057
  size: spacing.xxxSmall_4
30592
31058
  }), /*#__PURE__*/React.createElement(View, {
30593
- style: styles$w.exampleContent,
31059
+ style: styles$r.exampleContent,
30594
31060
  id: exampleId
30595
31061
  }, example));
30596
31062
  };
30597
- const styles$w = StyleSheet.create({
31063
+ const styles$r = StyleSheet.create({
30598
31064
  accordionHeader: {
30599
31065
  textOverflow: "ellipsis",
30600
31066
  // A maximum width needs to be specified in order for the ellipsis to work.
@@ -30712,14 +31178,14 @@ function LockedLabelSettings(props) {
30712
31178
  expanded: expanded,
30713
31179
  onToggle: onToggle,
30714
31180
  header: /*#__PURE__*/React.createElement(View, {
30715
- style: [styles$v.row, styles$v.accordionHeaderContainer]
31181
+ style: [styles$q.row, styles$q.accordionHeaderContainer]
30716
31182
  }, /*#__PURE__*/React.createElement(LabelLarge, null, "Label (", coord[0], ", ", coord[1], ")"), /*#__PURE__*/React.createElement(Strut, {
30717
31183
  size: spacing.xSmall_8
30718
31184
  }), text !== "" && /*#__PURE__*/React.createElement(LabelLarge, {
30719
31185
  style: [{
30720
31186
  backgroundColor: color.white,
30721
31187
  color: lockedFigureColors[color$1]
30722
- }, styles$v.accordionHeader]
31188
+ }, styles$q.accordionHeader]
30723
31189
  }, text))
30724
31190
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30725
31191
  coord: coord,
@@ -30728,10 +31194,10 @@ function LockedLabelSettings(props) {
30728
31194
  coord: newCoords
30729
31195
  });
30730
31196
  },
30731
- style: styles$v.spaceUnder
31197
+ style: styles$q.spaceUnder
30732
31198
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30733
31199
  tag: "label",
30734
- style: [styles$v.row, styles$v.spaceUnder]
31200
+ style: [styles$q.row, styles$q.spaceUnder]
30735
31201
  }, "TeX", /*#__PURE__*/React.createElement(Strut, {
30736
31202
  size: spacing.xSmall_8
30737
31203
  }), /*#__PURE__*/React.createElement(TextField, {
@@ -30741,7 +31207,7 @@ function LockedLabelSettings(props) {
30741
31207
  text: newValue
30742
31208
  })
30743
31209
  })), /*#__PURE__*/React.createElement(View, {
30744
- style: styles$v.row
31210
+ style: styles$q.row
30745
31211
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30746
31212
  selectedValue: color$1,
30747
31213
  onChange: newColor => {
@@ -30749,12 +31215,12 @@ function LockedLabelSettings(props) {
30749
31215
  color: newColor
30750
31216
  });
30751
31217
  },
30752
- style: styles$v.spaceUnder
31218
+ style: styles$q.spaceUnder
30753
31219
  }), /*#__PURE__*/React.createElement(Strut, {
30754
31220
  size: spacing.medium_16
30755
31221
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30756
31222
  tag: "label",
30757
- style: styles$v.row
31223
+ style: styles$q.row
30758
31224
  }, "size", /*#__PURE__*/React.createElement(Strut, {
30759
31225
  size: spacing.xSmall_8
30760
31226
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30781,7 +31247,7 @@ function LockedLabelSettings(props) {
30781
31247
  onRemove: onRemove
30782
31248
  }));
30783
31249
  }
30784
- const styles$v = StyleSheet.create({
31250
+ const styles$q = StyleSheet.create({
30785
31251
  accordionHeaderContainer: {
30786
31252
  // Stop the label summary from wrapping.
30787
31253
  whiteSpace: "nowrap"
@@ -30815,7 +31281,7 @@ const LabeledSwitch = props => {
30815
31281
  const ids = useUniqueIdWithMock();
30816
31282
  const switchId = ids.get("switch");
30817
31283
  return /*#__PURE__*/React.createElement(View, {
30818
- style: [styles$u.row, style]
31284
+ style: [styles$p.row, style]
30819
31285
  }, /*#__PURE__*/React.createElement(Switch, {
30820
31286
  id: switchId,
30821
31287
  checked: checked,
@@ -30827,7 +31293,7 @@ const LabeledSwitch = props => {
30827
31293
  htmlFor: switchId
30828
31294
  }, label));
30829
31295
  };
30830
- const styles$u = StyleSheet.create({
31296
+ const styles$p = StyleSheet.create({
30831
31297
  row: {
30832
31298
  flexDirection: "row",
30833
31299
  alignItems: "center"
@@ -30861,13 +31327,13 @@ const DefiningPointSettings = props => {
30861
31327
  return /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
30862
31328
  expanded: expanded,
30863
31329
  onToggle: onToggle,
30864
- containerStyle: styles$t.container,
30865
- panelStyle: styles$t.accordionPanel,
31330
+ containerStyle: styles$o.container,
31331
+ panelStyle: styles$o.accordionPanel,
30866
31332
  header:
30867
31333
  /*#__PURE__*/
30868
31334
  // Summary: Point, coords, color (filled/open)
30869
31335
  React.createElement(View, {
30870
- style: styles$t.row
31336
+ style: styles$o.row
30871
31337
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `${label || "Point"} (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React.createElement(Strut, {
30872
31338
  size: spacing.xSmall_8
30873
31339
  }), showPoint && /*#__PURE__*/React.createElement(ColorSwatch, {
@@ -30877,7 +31343,7 @@ const DefiningPointSettings = props => {
30877
31343
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30878
31344
  coord: coord,
30879
31345
  error: !!error,
30880
- style: styles$t.spaceUnder,
31346
+ style: styles$o.spaceUnder,
30881
31347
  onChange: newCoords => {
30882
31348
  onChangeProps({
30883
31349
  coord: newCoords
@@ -30886,12 +31352,12 @@ const DefiningPointSettings = props => {
30886
31352
  }), onTogglePoint && /*#__PURE__*/React.createElement(LabeledSwitch, {
30887
31353
  label: "show point on graph",
30888
31354
  checked: !!showPoint,
30889
- style: showPoint && styles$t.spaceUnder,
31355
+ style: showPoint && styles$o.spaceUnder,
30890
31356
  onChange: onTogglePoint
30891
31357
  }), showPoint && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ColorSelect, {
30892
31358
  selectedValue: pointColor,
30893
31359
  onChange: handleColorChange,
30894
- style: styles$t.spaceUnder
31360
+ style: styles$o.spaceUnder
30895
31361
  }), /*#__PURE__*/React.createElement(LabeledSwitch, {
30896
31362
  label: "open point",
30897
31363
  checked: !filled,
@@ -30902,7 +31368,7 @@ const DefiningPointSettings = props => {
30902
31368
  }
30903
31369
  })));
30904
31370
  };
30905
- const styles$t = StyleSheet.create({
31371
+ const styles$o = StyleSheet.create({
30906
31372
  container: {
30907
31373
  marginTop: spacing.xSmall_8,
30908
31374
  marginBottom: 0,
@@ -30965,7 +31431,7 @@ const LockedLineSettings = props => {
30965
31431
  expanded: props.expanded,
30966
31432
  onToggle: props.onToggle,
30967
31433
  header: /*#__PURE__*/React.createElement(View, {
30968
- style: styles$s.row
31434
+ style: styles$n.row
30969
31435
  }, /*#__PURE__*/React.createElement(LabelLarge, null, lineLabel), /*#__PURE__*/React.createElement(Strut, {
30970
31436
  size: spacing.xSmall_8
30971
31437
  }), /*#__PURE__*/React.createElement(LineSwatch, {
@@ -30974,7 +31440,7 @@ const LockedLineSettings = props => {
30974
31440
  }))
30975
31441
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30976
31442
  tag: "label",
30977
- style: [styles$s.row, styles$s.spaceUnder]
31443
+ style: [styles$n.row, styles$n.spaceUnder]
30978
31444
  }, "kind", /*#__PURE__*/React.createElement(Strut, {
30979
31445
  size: spacing.xxxSmall_4
30980
31446
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30995,7 +31461,7 @@ const LockedLineSettings = props => {
30995
31461
  value: "segment",
30996
31462
  label: "segment"
30997
31463
  }))), /*#__PURE__*/React.createElement(View, {
30998
- style: styles$s.row
31464
+ style: styles$n.row
30999
31465
  }, /*#__PURE__*/React.createElement(ColorSelect, {
31000
31466
  selectedValue: lineColor,
31001
31467
  onChange: handleColorChange
@@ -31007,7 +31473,7 @@ const LockedLineSettings = props => {
31007
31473
  lineStyle: value
31008
31474
  })
31009
31475
  })), isInvalid && /*#__PURE__*/React.createElement(LabelMedium, {
31010
- style: styles$s.errorText
31476
+ style: styles$n.errorText
31011
31477
  }, lengthZeroStr), /*#__PURE__*/React.createElement(DefiningPointSettings, _extends({
31012
31478
  label: "Point 1",
31013
31479
  showPoint: showPoint1,
@@ -31032,7 +31498,7 @@ const LockedLineSettings = props => {
31032
31498
  onRemove: onRemove
31033
31499
  }));
31034
31500
  };
31035
- const styles$s = StyleSheet.create({
31501
+ const styles$n = StyleSheet.create({
31036
31502
  row: {
31037
31503
  display: "flex",
31038
31504
  flexDirection: "row",
@@ -31073,7 +31539,7 @@ const LockedPointSettings = props => {
31073
31539
  /*#__PURE__*/
31074
31540
  // Summary: Point, coords, color (filled/open)
31075
31541
  React.createElement(View, {
31076
- style: styles$r.row
31542
+ style: styles$m.row
31077
31543
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Point (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React.createElement(Strut, {
31078
31544
  size: spacing.xSmall_8
31079
31545
  }), /*#__PURE__*/React.createElement(ColorSwatch, {
@@ -31082,7 +31548,7 @@ const LockedPointSettings = props => {
31082
31548
  }))
31083
31549
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
31084
31550
  coord: coord,
31085
- style: styles$r.spaceUnder,
31551
+ style: styles$m.spaceUnder,
31086
31552
  onChange: newCoord => {
31087
31553
  onChangeProps({
31088
31554
  coord: newCoord
@@ -31091,7 +31557,7 @@ const LockedPointSettings = props => {
31091
31557
  }), /*#__PURE__*/React.createElement(ColorSelect, {
31092
31558
  selectedValue: pointColor,
31093
31559
  onChange: handleColorChange,
31094
- style: styles$r.spaceUnder
31560
+ style: styles$m.spaceUnder
31095
31561
  }), /*#__PURE__*/React.createElement(LabeledSwitch, {
31096
31562
  label: "open point",
31097
31563
  checked: !filled,
@@ -31106,7 +31572,7 @@ const LockedPointSettings = props => {
31106
31572
  onRemove: onRemove
31107
31573
  }));
31108
31574
  };
31109
- const styles$r = StyleSheet.create({
31575
+ const styles$m = StyleSheet.create({
31110
31576
  row: {
31111
31577
  flexDirection: "row",
31112
31578
  alignItems: "center"
@@ -31124,17 +31590,17 @@ const PolygonSwatch = props => {
31124
31590
  } = props;
31125
31591
  return /*#__PURE__*/React.createElement(View, {
31126
31592
  "aria-label": `${color}, stroke ${strokeStyle}, fill ${fillStyle}`,
31127
- style: [styles$q.container, {
31593
+ style: [styles$l.container, {
31128
31594
  border: `4px ${strokeStyle} ${lockedFigureColors[color]}`
31129
31595
  }]
31130
31596
  }, /*#__PURE__*/React.createElement(View, {
31131
- style: [styles$q.innerSquare, {
31597
+ style: [styles$l.innerSquare, {
31132
31598
  backgroundColor: lockedFigureColors[color],
31133
31599
  opacity: fillStyle === "white" ? 0 : lockedFigureFillStyles[fillStyle]
31134
31600
  }]
31135
31601
  }));
31136
31602
  };
31137
- const styles$q = StyleSheet.create({
31603
+ const styles$l = StyleSheet.create({
31138
31604
  container: {
31139
31605
  // Add a white outline so that the color swatch is visible when
31140
31606
  // the dropdown option is highlighted with its blue background.
@@ -31200,7 +31666,7 @@ const LockedPolygonSettings = props => {
31200
31666
  /*#__PURE__*/
31201
31667
  // Summary: Polygon, number of sides, style swatch
31202
31668
  React.createElement(View, {
31203
- style: styles$p.row
31669
+ style: styles$k.row
31204
31670
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Polygon, ${points.length} sides`), /*#__PURE__*/React.createElement(Strut, {
31205
31671
  size: spacing.xSmall_8
31206
31672
  }), /*#__PURE__*/React.createElement(PolygonSwatch, {
@@ -31209,7 +31675,7 @@ const LockedPolygonSettings = props => {
31209
31675
  strokeStyle: strokeStyle
31210
31676
  }))
31211
31677
  }, /*#__PURE__*/React.createElement(View, {
31212
- style: [styles$p.row, styles$p.spaceUnder]
31678
+ style: [styles$k.row, styles$k.spaceUnder]
31213
31679
  }, /*#__PURE__*/React.createElement(ColorSelect, {
31214
31680
  selectedValue: color,
31215
31681
  onChange: handleColorChange
@@ -31217,7 +31683,7 @@ const LockedPolygonSettings = props => {
31217
31683
  size: spacing.medium_16
31218
31684
  }), /*#__PURE__*/React.createElement(LabelMedium, {
31219
31685
  tag: "label",
31220
- style: [styles$p.row, styles$p.truncatedWidth]
31686
+ style: [styles$k.row, styles$k.truncatedWidth]
31221
31687
  }, "fill", /*#__PURE__*/React.createElement(Strut, {
31222
31688
  size: spacing.xxSmall_6
31223
31689
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -31243,17 +31709,17 @@ const LockedPolygonSettings = props => {
31243
31709
  onChange: newValue => onChangeProps({
31244
31710
  showVertices: newValue
31245
31711
  }),
31246
- style: styles$p.spaceUnder
31712
+ style: styles$k.spaceUnder
31247
31713
  }), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31248
31714
  header: /*#__PURE__*/React.createElement(LabelLarge, null, "Points"),
31249
31715
  expanded: true,
31250
- containerStyle: styles$p.pointAccordionContainer,
31251
- panelStyle: styles$p.pointAccordionPanel
31716
+ containerStyle: styles$k.pointAccordionContainer,
31717
+ panelStyle: styles$k.pointAccordionPanel
31252
31718
  }, points.map((point, index) => {
31253
31719
  const pointLabel = String.fromCharCode(65 + index);
31254
31720
  return /*#__PURE__*/React.createElement(View, {
31255
31721
  key: `locked-polygon-point-index-${index}`,
31256
- style: [styles$p.row, styles$p.spaceUnder]
31722
+ style: [styles$k.row, styles$k.spaceUnder]
31257
31723
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `${pointLabel}:`), /*#__PURE__*/React.createElement(Strut, {
31258
31724
  size: spacing.medium_16
31259
31725
  }), /*#__PURE__*/React.createElement(CoordinatePairInput, {
@@ -31281,10 +31747,10 @@ const LockedPolygonSettings = props => {
31281
31747
  points: newPoints
31282
31748
  });
31283
31749
  },
31284
- style: styles$p.icon
31750
+ style: styles$k.icon
31285
31751
  }));
31286
31752
  }), /*#__PURE__*/React.createElement(View, {
31287
- style: [styles$p.row, styles$p.polygonActionsContainer]
31753
+ style: [styles$k.row, styles$k.polygonActionsContainer]
31288
31754
  }, /*#__PURE__*/React.createElement(Button, {
31289
31755
  kind: "tertiary",
31290
31756
  startIcon: plusCircle,
@@ -31294,30 +31760,30 @@ const LockedPolygonSettings = props => {
31294
31760
  });
31295
31761
  }
31296
31762
  }, "Add point"), /*#__PURE__*/React.createElement(Spring, null), /*#__PURE__*/React.createElement(View, {
31297
- style: styles$p.movementButtonsContainer
31763
+ style: styles$k.movementButtonsContainer
31298
31764
  }, /*#__PURE__*/React.createElement(IconButton, {
31299
31765
  "aria-label": "Move polygon up",
31300
- style: styles$p.iconButton,
31766
+ style: styles$k.iconButton,
31301
31767
  size: "small",
31302
31768
  icon: arrowFatUp,
31303
31769
  onClick: () => handlePolygonMove("up")
31304
31770
  }), /*#__PURE__*/React.createElement(View, {
31305
- style: styles$p.row
31771
+ style: styles$k.row
31306
31772
  }, /*#__PURE__*/React.createElement(IconButton, {
31307
31773
  "aria-label": "Move polygon left",
31308
- style: styles$p.iconButton,
31774
+ style: styles$k.iconButton,
31309
31775
  size: "small",
31310
31776
  icon: arrowFatLeft,
31311
31777
  onClick: () => handlePolygonMove("left")
31312
31778
  }), /*#__PURE__*/React.createElement(IconButton, {
31313
31779
  "aria-label": "Move polygon down",
31314
- style: styles$p.iconButton,
31780
+ style: styles$k.iconButton,
31315
31781
  size: "small",
31316
31782
  icon: arrowFatDown,
31317
31783
  onClick: () => handlePolygonMove("down")
31318
31784
  }), /*#__PURE__*/React.createElement(IconButton, {
31319
31785
  "aria-label": "Move polygon right",
31320
- style: styles$p.iconButton,
31786
+ style: styles$k.iconButton,
31321
31787
  size: "small",
31322
31788
  icon: arrowFatRight,
31323
31789
  onClick: () => handlePolygonMove("right")
@@ -31327,7 +31793,7 @@ const LockedPolygonSettings = props => {
31327
31793
  onRemove: onRemove
31328
31794
  }));
31329
31795
  };
31330
- const styles$p = StyleSheet.create({
31796
+ const styles$k = StyleSheet.create({
31331
31797
  row: {
31332
31798
  display: "flex",
31333
31799
  flexDirection: "row",
@@ -31357,923 +31823,480 @@ const styles$p = StyleSheet.create({
31357
31823
  spaceUnder: {
31358
31824
  marginBottom: spacing.xSmall_8
31359
31825
  },
31360
- truncatedWidth: {
31361
- // Allow truncation, stop bleeding over the edge.
31362
- minWidth: 0
31363
- }
31364
- });
31365
-
31366
- /**
31367
- * LockedVectorSettings is a component that allows the user to edit the
31368
- * settings of specifically a locked vector on the graph.
31369
- *
31370
- * Used in the interactive graph editor's locked figures section.
31371
- */
31372
- const lengthErrorMessage = "The vector cannot have length 0.";
31373
- const LockedVectorSettings = props => {
31374
- const {
31375
- points,
31376
- color: lineColor,
31377
- onChangeProps,
31378
- onMove,
31379
- onRemove
31380
- } = props;
31381
- const [tail, tip] = points;
31382
- const lineLabel = `Vector (${tail[0]}, ${tail[1]}), (${tip[0]}, ${tip[1]})`;
31383
-
31384
- // Check if the line has length 0.
31385
- const isInvalid = vector.equal(tail, tip);
31386
- function handleChangePoint(newCoord, index) {
31387
- if (typeof newCoord !== "undefined") {
31388
- const newPoints = [...points];
31389
- newPoints[index] = [...newCoord];
31390
- onChangeProps({
31391
- points: newPoints
31392
- });
31393
- }
31394
- }
31395
- function handleColorChange(newColor) {
31396
- onChangeProps({
31397
- color: newColor
31398
- });
31399
- }
31400
- return /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31401
- expanded: props.expanded,
31402
- onToggle: props.onToggle,
31403
- header: /*#__PURE__*/React.createElement(View, {
31404
- style: styles$o.row
31405
- }, /*#__PURE__*/React.createElement(LabelLarge, null, lineLabel), /*#__PURE__*/React.createElement(Strut, {
31406
- size: spacing.xSmall_8
31407
- }), /*#__PURE__*/React.createElement(LineSwatch, {
31408
- color: lineColor,
31409
- lineStyle: "solid"
31410
- }))
31411
- }, /*#__PURE__*/React.createElement(View, {
31412
- style: [styles$o.row, styles$o.spaceUnder]
31413
- }, /*#__PURE__*/React.createElement(ColorSelect, {
31414
- selectedValue: lineColor,
31415
- onChange: handleColorChange
31416
- })), isInvalid && /*#__PURE__*/React.createElement(LabelMedium, {
31417
- style: styles$o.errorText
31418
- }, lengthErrorMessage), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31419
- expanded: true // Initial state is expanded
31420
- ,
31421
- containerStyle: styles$o.container,
31422
- panelStyle: styles$o.accordionPanel,
31423
- header: /*#__PURE__*/React.createElement(View, {
31424
- style: styles$o.row
31425
- }, /*#__PURE__*/React.createElement(LabelLarge, null, `Tail (${tail[0]}, ${tail[1]})`))
31426
- }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
31427
- coord: tail,
31428
- error: isInvalid,
31429
- onChange: newProps => {
31430
- handleChangePoint(newProps, 0);
31431
- }
31432
- })), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31433
- expanded: true // Initial state is expanded
31434
- ,
31435
- containerStyle: styles$o.container,
31436
- panelStyle: styles$o.accordionPanel,
31437
- header: /*#__PURE__*/React.createElement(View, {
31438
- style: styles$o.row
31439
- }, /*#__PURE__*/React.createElement(LabelLarge, null, `Tip (${tip[0]}, ${tip[1]})`))
31440
- }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
31441
- coord: tip,
31442
- error: isInvalid,
31443
- onChange: newProps => {
31444
- handleChangePoint(newProps, 1);
31445
- }
31446
- })), /*#__PURE__*/React.createElement(LockedFigureSettingsActions, {
31447
- figureType: props.type,
31448
- onMove: onMove,
31449
- onRemove: onRemove
31450
- }));
31451
- };
31452
- const styles$o = StyleSheet.create({
31453
- accordionPanel: {
31454
- paddingBottom: spacing.medium_16
31455
- },
31456
- container: {
31457
- marginTop: spacing.xSmall_8,
31458
- marginBottom: 0,
31459
- marginLeft: -spacing.xxxSmall_4,
31460
- marginRight: -spacing.xxxSmall_4,
31461
- backgroundColor: color.white
31462
- },
31463
- errorText: {
31464
- color: color.red,
31465
- marginTop: spacing.xSmall_8
31466
- },
31467
- row: {
31468
- flexDirection: "row",
31469
- alignItems: "center"
31470
- }
31471
- });
31472
-
31473
- /**
31474
- * LockedFigureSettings takes in a figure and renders the
31475
- * respective settings for that figure type.
31476
- *
31477
- * Used in the interactive graph editor's locked figures section.
31478
- */
31479
-
31480
- // Union this type with other locked figure types when they are added.
31481
-
31482
- const LockedFigureSettings = props => {
31483
- switch (props.type) {
31484
- case "point":
31485
- return /*#__PURE__*/React.createElement(LockedPointSettings, props);
31486
- case "line":
31487
- return /*#__PURE__*/React.createElement(LockedLineSettings, props);
31488
- case "vector":
31489
- return /*#__PURE__*/React.createElement(LockedVectorSettings, props);
31490
- case "ellipse":
31491
- return /*#__PURE__*/React.createElement(LockedEllipseSettings, props);
31492
- case "polygon":
31493
- return /*#__PURE__*/React.createElement(LockedPolygonSettings, props);
31494
- case "function":
31495
- return /*#__PURE__*/React.createElement(LockedFunctionSettings, props);
31496
- case "label":
31497
- if (props.showLabelsFlag) {
31498
- return /*#__PURE__*/React.createElement(LockedLabelSettings, props);
31499
- }
31500
- break;
31501
- }
31502
- return null;
31503
- };
31504
-
31505
- const LockedFiguresSection = props => {
31506
- var _props$figures;
31507
- // Keep track of all figures' accordions' expanded states for the
31508
- // expand/collapse all button. Set the whole array to false initially.
31509
- const collapsedStateArray = Array(((_props$figures = props.figures) != null ? _props$figures : []).length).fill(false);
31510
- const [expandedStates, setExpandedStates] = React.useState(collapsedStateArray);
31511
- const [isExpanded, setIsExpanded] = React.useState(true);
31512
- const uniqueId = useUniqueIdWithMock().get("locked-figures-section");
31513
- const {
31514
- figures,
31515
- onChange
31516
- } = props;
31517
- function addLockedFigure(newFigure) {
31518
- const lockedFigures = figures || [];
31519
- const newProps = {
31520
- lockedFigures: [...lockedFigures, getDefaultFigureForType(newFigure)]
31521
- };
31522
- onChange(newProps);
31523
- setExpandedStates([...expandedStates, true]);
31524
- }
31525
- function moveLockedFigure(index, movement) {
31526
- // Don't allow moving the first figure up or the last figure down.
31527
- if (index === 0 && (movement === "back" || movement === "backward")) {
31528
- return;
31529
- }
31530
- if (figures && index === figures.length - 1 && (movement === "front" || movement === "forward")) {
31531
- return;
31532
- }
31533
- const lockedFigures = figures || [];
31534
- const newFigures = [...lockedFigures];
31535
- const newExpandedStates = [...expandedStates];
31536
-
31537
- // First, remove the figure from its current position
31538
- // in the figures array and the expanded states array.
31539
- const [removedFigure] = newFigures.splice(index, 1);
31540
- newExpandedStates.splice(index, 1);
31541
-
31542
- // Then, add it back in the new position. Add "true" to the
31543
- // expanded states array for the new position (it must already
31544
- // be open since the button to move it is being pressed from there).
31545
- switch (movement) {
31546
- case "back":
31547
- newFigures.unshift(removedFigure);
31548
- newExpandedStates.unshift(true);
31549
- break;
31550
- case "backward":
31551
- newFigures.splice(index - 1, 0, removedFigure);
31552
- newExpandedStates.splice(index - 1, 0, true);
31553
- break;
31554
- case "forward":
31555
- newFigures.splice(index + 1, 0, removedFigure);
31556
- newExpandedStates.splice(index + 1, 0, true);
31557
- break;
31558
- case "front":
31559
- newFigures.push(removedFigure);
31560
- newExpandedStates.push(true);
31561
- break;
31562
- }
31563
- onChange({
31564
- lockedFigures: newFigures
31565
- });
31566
- setExpandedStates(newExpandedStates);
31567
- }
31568
- function removeLockedFigure(index) {
31569
- // eslint-disable-next-line no-alert
31570
- if (window.confirm("Are you sure you want to delete this figure?")) {
31571
- const lockedFigures = figures || [];
31572
- onChange({
31573
- lockedFigures: [...lockedFigures.slice(0, index), ...lockedFigures.slice(index + 1)]
31574
- });
31575
-
31576
- // Update expanded states
31577
- const newExpandedStates = [...expandedStates];
31578
- newExpandedStates.splice(index, 1);
31579
- setExpandedStates(newExpandedStates);
31580
- }
31581
- }
31582
- function changeProps(index,
31583
- // Omit the type from the figure props so it doesn't think
31584
- // we're trying to pass in the props for the wrong type.
31585
- figureProps) {
31586
- const lockedFigures = figures || [];
31587
- const newFigures = {
31588
- lockedFigures: [...lockedFigures.slice(0, index), _extends({}, lockedFigures[index], figureProps), ...lockedFigures.slice(index + 1)]
31589
- };
31590
- onChange(newFigures);
31591
- }
31592
- function toggleExpanded(newValue) {
31593
- setExpandedStates(Array(figures == null ? void 0 : figures.length).fill(newValue));
31594
- }
31595
- const allCollapsed = expandedStates.every(value => !value);
31596
- const buttonLabel = allCollapsed ? "Expand all" : "Collapse all";
31597
- const showExpandButton = !!(figures != null && figures.length);
31598
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
31599
- title: "Locked Figures",
31600
- isOpen: isExpanded,
31601
- onToggle: () => setIsExpanded(!isExpanded),
31602
- isCollapsible: true
31603
- }), isExpanded && /*#__PURE__*/React.createElement(View, null, figures == null ? void 0 : figures.map((figure, index) => {
31604
- return /*#__PURE__*/React.createElement(LockedFigureSettings, _extends({
31605
- key: `${uniqueId}-locked-${figure}-${index}`,
31606
- showLabelsFlag: props.showLabelsFlag,
31607
- expanded: expandedStates[index],
31608
- onToggle: newValue => {
31609
- const newExpanded = [...expandedStates];
31610
- newExpanded[index] = newValue;
31611
- setExpandedStates(newExpanded);
31612
- }
31613
- }, figure, {
31614
- onChangeProps: newProps => changeProps(index, newProps),
31615
- onMove: movement => moveLockedFigure(index, movement),
31616
- onRemove: () => removeLockedFigure(index)
31617
- }));
31618
- }), /*#__PURE__*/React.createElement(View, {
31619
- style: styles$n.buttonContainer
31620
- }, /*#__PURE__*/React.createElement(LockedFigureSelect, {
31621
- showLabelsFlag: props.showLabelsFlag,
31622
- id: `${uniqueId}-select`,
31623
- onChange: addLockedFigure
31624
- }), /*#__PURE__*/React.createElement(Strut, {
31625
- size: spacing.small_12
31626
- }), showExpandButton && /*#__PURE__*/React.createElement(Button, {
31627
- kind: "secondary",
31628
- onClick: () => toggleExpanded(allCollapsed),
31629
- style: styles$n.button
31630
- }, buttonLabel))));
31631
- };
31632
- const styles$n = StyleSheet.create({
31633
- buttonContainer: {
31634
- flexDirection: "row",
31635
- alignItems: "center"
31636
- },
31637
- button: {
31638
- marginTop: spacing.xSmall_8,
31639
- flexGrow: 1
31826
+ truncatedWidth: {
31827
+ // Allow truncation, stop bleeding over the edge.
31828
+ minWidth: 0
31640
31829
  }
31641
31830
  });
31642
31831
 
31643
- const UNLIMITED = "unlimited";
31644
- // Parses a string (ostensibly from some UI element that only supports string
31645
- // types, such as a <select />) into a numeric point count or the special sigil
31646
- // "unlimited". Note that 0 is a special case, and means "unlimited".
31647
- const parsePointCount = points => {
31648
- const parsed = parseInt(points, 10);
31649
- if (isNaN(parsed)) {
31650
- return UNLIMITED;
31651
- }
31652
- return parsed === 0 ? UNLIMITED : parsed;
31653
- };
31832
+ /**
31833
+ * LockedVectorSettings is a component that allows the user to edit the
31834
+ * settings of specifically a locked vector on the graph.
31835
+ *
31836
+ * Used in the interactive graph editor's locked figures section.
31837
+ */
31838
+ const lengthErrorMessage = "The vector cannot have length 0.";
31839
+ const LockedVectorSettings = props => {
31840
+ const {
31841
+ points,
31842
+ color: lineColor,
31843
+ onChangeProps,
31844
+ onMove,
31845
+ onRemove
31846
+ } = props;
31847
+ const [tail, tip] = points;
31848
+ const lineLabel = `Vector (${tail[0]}, ${tail[1]}), (${tip[0]}, ${tip[1]})`;
31654
31849
 
31655
- const GraphPointsCountSelector = ({
31656
- numPoints: _numPoints = 1,
31657
- onChange
31658
- }) => {
31659
- return /*#__PURE__*/React.createElement(SingleSelect, {
31660
- selectedValue: `${_numPoints}`,
31661
- onChange: newValue => {
31662
- onChange(parsePointCount(newValue));
31850
+ // Check if the line has length 0.
31851
+ const isInvalid = vector.equal(tail, tip);
31852
+ function handleChangePoint(newCoord, index) {
31853
+ if (typeof newCoord !== "undefined") {
31854
+ const newPoints = [...points];
31855
+ newPoints[index] = [...newCoord];
31856
+ onChangeProps({
31857
+ points: newPoints
31858
+ });
31663
31859
  }
31664
- // Never uses placeholder, always has value
31665
- ,
31666
- placeholder: "",
31667
- style: styles$m.singleSelectShort
31668
- }, [...[...Array(7).keys()].map(n => /*#__PURE__*/React.createElement(OptionItem, {
31669
- key: n,
31670
- value: `${n}`,
31671
- label: `${n} point${n > 1 ? "s" : ""}`
31672
- })), /*#__PURE__*/React.createElement(OptionItem, {
31673
- key: "unlimited",
31674
- value: UNLIMITED,
31675
- label: "unlimited"
31676
- })]);
31677
- };
31678
- const styles$m = StyleSheet.create({
31679
- singleSelectShort: {
31680
- // Non-standard spacing, but it's the smallest we can go
31681
- // without running into styling issues with the dropdown.
31682
- height: 26
31683
31860
  }
31684
- });
31685
-
31686
- const GraphTypeSelector = props => {
31687
- return /*#__PURE__*/React.createElement(SingleSelect, {
31688
- selectedValue: props.graphType,
31689
- onChange: props.onChange,
31690
- placeholder: "Select a graph type",
31691
- style: styles$l.singleSelectShort
31692
- }, /*#__PURE__*/React.createElement(OptionItem, {
31693
- value: "linear",
31694
- label: "Linear function"
31695
- }), /*#__PURE__*/React.createElement(OptionItem, {
31696
- value: "quadratic",
31697
- label: "Quadratic function"
31698
- }), /*#__PURE__*/React.createElement(OptionItem, {
31699
- value: "sinusoid",
31700
- label: "Sinusoid function"
31701
- }), /*#__PURE__*/React.createElement(OptionItem, {
31702
- value: "circle",
31703
- label: "Circle"
31704
- }), /*#__PURE__*/React.createElement(OptionItem, {
31705
- value: "point",
31706
- label: "Point(s)"
31707
- }), /*#__PURE__*/React.createElement(OptionItem, {
31708
- value: "linear-system",
31709
- label: "Linear System"
31710
- }), /*#__PURE__*/React.createElement(OptionItem, {
31711
- value: "polygon",
31712
- label: "Polygon"
31713
- }), /*#__PURE__*/React.createElement(OptionItem, {
31714
- value: "segment",
31715
- label: "Line Segment(s)"
31716
- }), /*#__PURE__*/React.createElement(OptionItem, {
31717
- value: "ray",
31718
- label: "Ray"
31719
- }), /*#__PURE__*/React.createElement(OptionItem, {
31720
- value: "angle",
31721
- label: "Angle"
31722
- }));
31723
- };
31724
- const styles$l = StyleSheet.create({
31725
- singleSelectShort: {
31726
- // Non-standard spacing, but it's the smallest we can go
31727
- // without running into styling issues with the dropdown.
31728
- height: 26
31861
+ function handleColorChange(newColor) {
31862
+ onChangeProps({
31863
+ color: newColor
31864
+ });
31729
31865
  }
31730
- });
31731
-
31732
- function InteractiveGraphCorrectAnswer(props) {
31733
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
31734
- title: "Correct Answer",
31735
- isOpen: true,
31736
- isCollapsible: false
31737
- }), /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(LabelXSmall, {
31738
- style: {
31739
- paddingTop: spacing.xxSmall_6,
31740
- paddingBottom: spacing.xxSmall_6,
31741
- color: color.offBlack64
31866
+ return /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31867
+ expanded: props.expanded,
31868
+ onToggle: props.onToggle,
31869
+ header: /*#__PURE__*/React.createElement(View, {
31870
+ style: styles$j.row
31871
+ }, /*#__PURE__*/React.createElement(LabelLarge, null, lineLabel), /*#__PURE__*/React.createElement(Strut, {
31872
+ size: spacing.xSmall_8
31873
+ }), /*#__PURE__*/React.createElement(LineSwatch, {
31874
+ color: lineColor,
31875
+ lineStyle: "solid"
31876
+ }))
31877
+ }, /*#__PURE__*/React.createElement(View, {
31878
+ style: [styles$j.row, styles$j.spaceUnder]
31879
+ }, /*#__PURE__*/React.createElement(ColorSelect, {
31880
+ selectedValue: lineColor,
31881
+ onChange: handleColorChange
31882
+ })), isInvalid && /*#__PURE__*/React.createElement(LabelMedium, {
31883
+ style: styles$j.errorText
31884
+ }, lengthErrorMessage), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31885
+ expanded: true // Initial state is expanded
31886
+ ,
31887
+ containerStyle: styles$j.container,
31888
+ panelStyle: styles$j.accordionPanel,
31889
+ header: /*#__PURE__*/React.createElement(View, {
31890
+ style: styles$j.row
31891
+ }, /*#__PURE__*/React.createElement(LabelLarge, null, `Tail (${tail[0]}, ${tail[1]})`))
31892
+ }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
31893
+ coord: tail,
31894
+ error: isInvalid,
31895
+ onChange: newProps => {
31896
+ handleChangePoint(newProps, 0);
31742
31897
  }
31743
- }, "Graph the correct answer in the graph below and ensure the equation or point coordinates displayed represent the correct answer."), /*#__PURE__*/React.createElement(BodyMonospace, {
31744
- style: {
31745
- fontSize: 12,
31746
- backgroundColor: "#eee",
31747
- paddingInline: spacing.xxSmall_6,
31748
- borderColor: "#ccc",
31749
- borderStyle: "solid",
31750
- borderWidth: 1
31898
+ })), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31899
+ expanded: true // Initial state is expanded
31900
+ ,
31901
+ containerStyle: styles$j.container,
31902
+ panelStyle: styles$j.accordionPanel,
31903
+ header: /*#__PURE__*/React.createElement(View, {
31904
+ style: styles$j.row
31905
+ }, /*#__PURE__*/React.createElement(LabelLarge, null, `Tip (${tip[0]}, ${tip[1]})`))
31906
+ }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
31907
+ coord: tip,
31908
+ error: isInvalid,
31909
+ onChange: newProps => {
31910
+ handleChangePoint(newProps, 1);
31751
31911
  }
31752
- }, props.equationString)), props.children));
31753
- }
31754
-
31755
- function InteractiveGraphDescription(props) {
31756
- const {
31757
- ariaLabelValue,
31758
- ariaDescriptionValue,
31759
- onChange
31760
- } = props;
31761
- const [isOpen, setIsOpen] = React.useState(true);
31762
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
31763
- title: "Description",
31764
- isCollapsible: true,
31765
- isOpen: isOpen,
31766
- onToggle: setIsOpen
31767
- }), isOpen && /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(LabelXSmall, {
31768
- style: styles$k.caption
31769
- }, "Use these fields to describe the graph as a whole. These are used by screen readers to describe content to users who are visually impaired."), /*#__PURE__*/React.createElement(LabelLarge, {
31770
- tag: "label"
31771
- }, "Title", /*#__PURE__*/React.createElement(TextField, {
31772
- value: ariaLabelValue,
31773
- onChange: newValue => onChange({
31774
- fullGraphAriaLabel: newValue
31775
- }),
31776
- style: styles$k.spaceAbove
31777
- })), /*#__PURE__*/React.createElement(Strut, {
31778
- size: spacing.small_12
31779
- }), /*#__PURE__*/React.createElement(LabelLarge, {
31780
- tag: "label"
31781
- }, "Description", /*#__PURE__*/React.createElement(TextArea, {
31782
- rows: 8,
31783
- resizeType: "vertical",
31784
- value: ariaDescriptionValue,
31785
- onChange: newValue => onChange({
31786
- fullGraphAriaDescription: newValue
31787
- }),
31788
- style: styles$k.spaceAbove
31789
- }))));
31790
- }
31791
- const styles$k = StyleSheet.create({
31792
- caption: {
31793
- color: color.offBlack64,
31794
- paddingTop: spacing.xxSmall_6,
31795
- paddingBottom: spacing.xxSmall_6
31912
+ })), /*#__PURE__*/React.createElement(LockedFigureSettingsActions, {
31913
+ figureType: props.type,
31914
+ onMove: onMove,
31915
+ onRemove: onRemove
31916
+ }));
31917
+ };
31918
+ const styles$j = StyleSheet.create({
31919
+ accordionPanel: {
31920
+ paddingBottom: spacing.medium_16
31796
31921
  },
31797
- spaceAbove: {
31798
- marginTop: spacing.xxxSmall_4
31922
+ container: {
31923
+ marginTop: spacing.xSmall_8,
31924
+ marginBottom: 0,
31925
+ marginLeft: -spacing.xxxSmall_4,
31926
+ marginRight: -spacing.xxxSmall_4,
31927
+ backgroundColor: color.white
31928
+ },
31929
+ errorText: {
31930
+ color: color.red,
31931
+ marginTop: spacing.xSmall_8
31932
+ },
31933
+ row: {
31934
+ flexDirection: "row",
31935
+ alignItems: "center"
31799
31936
  }
31800
31937
  });
31801
31938
 
31802
- const {
31803
- ButtonGroup: ButtonGroup$2,
31804
- InfoTip: InfoTip$c,
31805
- RangeInput: RangeInput$4
31806
- } = components;
31807
- const defaultBackgroundImage$1 = {
31808
- url: null,
31809
- width: 0,
31810
- height: 0
31811
- };
31812
- function numSteps(range, step) {
31813
- return Math.floor((range[1] - range[0]) / step);
31814
- }
31815
- class InteractiveGraphSettings extends React.Component {
31816
- static stateFromProps(props) {
31817
- return {
31818
- labelsTextbox: props.labels,
31819
- gridStepTextbox: props.gridStep,
31820
- snapStepTextbox: props.snapStep,
31821
- stepTextbox: props.step,
31822
- rangeTextbox: props.range,
31823
- // Shallow-copied clone
31824
- backgroundImage: _extends({}, props.backgroundImage)
31825
- };
31826
- }
31827
- constructor(props) {
31828
- super(props);
31829
- this._isMounted = false;
31830
- this.bgUrlRef = /*#__PURE__*/React.createRef();
31831
- this.labelXRef = /*#__PURE__*/React.createRef();
31832
- this.labelYRef = /*#__PURE__*/React.createRef();
31833
- this.change = (...args) => {
31834
- return Changeable.change.apply(this, args);
31835
- };
31836
- this.changeBackgroundUrl = e => {
31837
- var _this$bgUrlRef$curren;
31838
- // Only continue on blur or "enter"
31839
- if (e.type === "keypress" && e.key !== "Enter") {
31840
- return;
31939
+ /**
31940
+ * LockedFigureSettings takes in a figure and renders the
31941
+ * respective settings for that figure type.
31942
+ *
31943
+ * Used in the interactive graph editor's locked figures section.
31944
+ */
31945
+
31946
+ // Union this type with other locked figure types when they are added.
31947
+
31948
+ const LockedFigureSettings = props => {
31949
+ switch (props.type) {
31950
+ case "point":
31951
+ return /*#__PURE__*/React.createElement(LockedPointSettings, props);
31952
+ case "line":
31953
+ return /*#__PURE__*/React.createElement(LockedLineSettings, props);
31954
+ case "vector":
31955
+ return /*#__PURE__*/React.createElement(LockedVectorSettings, props);
31956
+ case "ellipse":
31957
+ return /*#__PURE__*/React.createElement(LockedEllipseSettings, props);
31958
+ case "polygon":
31959
+ return /*#__PURE__*/React.createElement(LockedPolygonSettings, props);
31960
+ case "function":
31961
+ return /*#__PURE__*/React.createElement(LockedFunctionSettings, props);
31962
+ case "label":
31963
+ if (props.showLabelsFlag) {
31964
+ return /*#__PURE__*/React.createElement(LockedLabelSettings, props);
31841
31965
  }
31842
- const setUrl = (url, width, height) => {
31843
- // Shallow-copied clone
31844
- const image = _extends({}, this.props.backgroundImage);
31845
- image.url = url;
31846
- image.width = width;
31847
- image.height = height;
31848
- this.setState({
31849
- backgroundImage: image
31850
- }, this.changeGraph);
31966
+ break;
31967
+ }
31968
+ return null;
31969
+ };
31970
+
31971
+ const DEFAULT_COLOR = "grayH";
31972
+ function getDefaultFigureForType(type) {
31973
+ switch (type) {
31974
+ case "point":
31975
+ return {
31976
+ type: "point",
31977
+ coord: [0, 0],
31978
+ color: DEFAULT_COLOR,
31979
+ filled: true
31851
31980
  };
31852
- const url = (_this$bgUrlRef$curren = this.bgUrlRef.current) == null ? void 0 : _this$bgUrlRef$curren.value;
31853
- if (url) {
31854
- Util.getImageSize(url, (width, height) => {
31855
- if (this._isMounted) {
31856
- setUrl(url, width, height);
31857
- }
31858
- });
31859
- } else {
31860
- setUrl(null, 0, 0);
31861
- }
31862
- };
31863
- this.renderLabelChoices = choices => {
31864
- return choices.map(nameAndValue => /*#__PURE__*/React.createElement("option", {
31865
- key: nameAndValue[1],
31866
- value: nameAndValue[1]
31867
- }, nameAndValue[0]));
31868
- };
31869
- this.validRange = range => {
31870
- const numbers = _.every(range, function (num) {
31871
- return _.isFinite(num);
31872
- });
31873
- if (!numbers) {
31874
- return "Range must be a valid number";
31875
- }
31876
- if (range[0] >= range[1]) {
31877
- return "Range must have a higher number on the right";
31878
- }
31879
- return true;
31880
- };
31881
- this.validateStepValue = settings => {
31882
- const {
31883
- step,
31884
- range,
31885
- name,
31886
- minTicks,
31887
- maxTicks
31888
- } = settings;
31889
- const nSteps = numSteps(range, step);
31890
- if (nSteps < minTicks) {
31891
- return name + " is too large, there must be at least " + minTicks + " ticks.";
31892
- }
31893
- if (nSteps > maxTicks) {
31894
- return name + " is too small, there can be at most " + maxTicks + " ticks.";
31895
- }
31896
- return true;
31897
- };
31898
- this.validSnapStep = (step, range) => {
31899
- return this.validateStepValue({
31900
- step: step,
31901
- range: range,
31902
- name: "Snap step",
31903
- minTicks: 5,
31904
- maxTicks: 60
31905
- });
31906
- };
31907
- this.validGridStep = (step, range) => {
31908
- return this.validateStepValue({
31909
- step: step,
31910
- range: range,
31911
- name: "Grid step",
31912
- minTicks: 3,
31913
- maxTicks: 60
31914
- });
31915
- };
31916
- this.validStep = (step, range) => {
31917
- return this.validateStepValue({
31918
- step: step,
31919
- range: range,
31920
- name: "Step",
31921
- minTicks: 3,
31922
- maxTicks: 20
31923
- });
31924
- };
31925
- this.validBackgroundImageSize = image => {
31926
- // Ignore empty images
31927
- if (!image.url) {
31928
- return true;
31929
- }
31930
- const validSize = image.width <= 450 && image.height <= 450;
31931
- if (!validSize) {
31932
- return "Image must be smaller than 450px x 450px.";
31933
- }
31934
- return true;
31935
- };
31936
- this.validateGraphSettings = (range, step, gridStep, snapStep, image) => {
31937
- const self = this;
31938
- let msg;
31939
- const goodRange = _.every(range, function (range) {
31940
- msg = self.validRange(range);
31941
- return msg === true;
31942
- });
31943
- if (!goodRange) {
31944
- return msg;
31945
- }
31946
- const goodStep = _.every(step, function (step, i) {
31947
- msg = self.validStep(step, range[i]);
31948
- return msg === true;
31949
- });
31950
- if (!goodStep) {
31951
- return msg;
31952
- }
31953
- const goodGridStep = _.every(gridStep, function (gridStep, i) {
31954
- msg = self.validGridStep(gridStep, range[i]);
31955
- return msg === true;
31956
- });
31957
- if (!goodGridStep) {
31958
- return msg;
31959
- }
31960
- const goodSnapStep = _.every(snapStep, function (snapStep, i) {
31961
- msg = self.validSnapStep(snapStep, range[i]);
31962
- return msg === true;
31963
- });
31964
- if (!goodSnapStep) {
31965
- return msg;
31966
- }
31967
- const goodImageSize = this.validBackgroundImageSize(image);
31968
- if (goodImageSize !== true) {
31969
- msg = goodImageSize;
31970
- return msg;
31971
- }
31972
- return true;
31973
- };
31974
- this.changeLabel = (i, e) => {
31975
- const val = e.target.value;
31976
- const labels = this.state.labelsTextbox.slice();
31977
- labels[i] = val;
31978
- this.setState({
31979
- labelsTextbox: labels
31980
- }, this.changeGraph);
31981
- };
31982
- this.changeRange = (i, values) => {
31983
- const ranges = this.state.rangeTextbox.slice();
31984
- ranges[i] = values;
31985
- const step = this.state.stepTextbox.slice();
31986
- const gridStep = this.state.gridStepTextbox.slice();
31987
- const snapStep = this.state.snapStepTextbox.slice();
31988
- const scale = Util.scaleFromExtent(ranges[i], this.props.box[i]);
31989
- if (this.validRange(ranges[i]) === true) {
31990
- step[i] = Util.tickStepFromExtent(ranges[i], this.props.box[i]);
31991
- const gridStepValue = Util.gridStepFromTickStep(step[i], scale);
31992
- if (gridStepValue) {
31993
- gridStep[i] = gridStepValue;
31994
- }
31995
- snapStep[i] = gridStep[i] / 2;
31996
- }
31997
- this.setState({
31998
- stepTextbox: step,
31999
- gridStepTextbox: gridStep,
32000
- snapStepTextbox: snapStep,
32001
- rangeTextbox: ranges
32002
- }, this.changeGraph);
32003
- };
32004
- this.changeStep = step => {
32005
- this.setState({
32006
- stepTextbox: step
32007
- }, this.changeGraph);
32008
- };
32009
- this.changeSnapStep = snapStep => {
32010
- this.setState({
32011
- snapStepTextbox: snapStep
32012
- }, this.changeGraph);
32013
- };
32014
- this.changeGridStep = gridStep => {
32015
- this.setState({
32016
- gridStepTextbox: gridStep,
32017
- snapStepTextbox: _.map(gridStep, function (step) {
32018
- return step / 2;
32019
- })
32020
- }, this.changeGraph);
32021
- };
32022
- this.changeGraph = () => {
32023
- const labels = this.state.labelsTextbox;
32024
- const range = _.map(this.state.rangeTextbox, function (range) {
32025
- return _.map(range, Number);
32026
- });
32027
- const step = _.map(this.state.stepTextbox, Number);
32028
- const gridStep = this.state.gridStepTextbox;
32029
- const snapStep = this.state.snapStepTextbox;
32030
- const image = this.state.backgroundImage;
31981
+ case "line":
31982
+ return {
31983
+ type: "line",
31984
+ kind: "line",
31985
+ points: [getDefaultFigureForType("point"), _extends({}, getDefaultFigureForType("point"), {
31986
+ coord: [2, 2]
31987
+ })],
31988
+ color: DEFAULT_COLOR,
31989
+ lineStyle: "solid",
31990
+ showPoint1: false,
31991
+ showPoint2: false
31992
+ };
31993
+ case "vector":
31994
+ return {
31995
+ type: "vector",
31996
+ points: [[0, 0], [2, 2]],
31997
+ color: DEFAULT_COLOR
31998
+ };
31999
+ case "ellipse":
32000
+ return {
32001
+ type: "ellipse",
32002
+ center: [0, 0],
32003
+ radius: [1, 1],
32004
+ angle: 0,
32005
+ color: DEFAULT_COLOR,
32006
+ fillStyle: "none",
32007
+ strokeStyle: "solid"
32008
+ };
32009
+ case "polygon":
32010
+ return {
32011
+ type: "polygon",
32012
+ points: [[0, 2], [-1, 0], [1, 0]],
32013
+ color: DEFAULT_COLOR,
32014
+ showVertices: false,
32015
+ fillStyle: "none",
32016
+ strokeStyle: "solid"
32017
+ };
32018
+ case "function":
32019
+ return {
32020
+ type: "function",
32021
+ color: DEFAULT_COLOR,
32022
+ strokeStyle: "solid",
32023
+ equation: "x^2",
32024
+ directionalAxis: "x"
32025
+ };
32026
+ case "label":
32027
+ return {
32028
+ type: "label",
32029
+ coord: [0, 0],
32030
+ text: "",
32031
+ color: DEFAULT_COLOR,
32032
+ size: "medium"
32033
+ };
32034
+ default:
32035
+ throw new UnreachableCaseError(type);
32036
+ }
32037
+ }
32031
32038
 
32032
- // validationResult is either:
32033
- // true -> the settings are valid
32034
- // a string -> the settings are invalid, and the explanation
32035
- // is contained in the string
32036
- // TODO(aria): Refactor this to not be confusing
32037
- const validationResult = this.validateGraphSettings(range, step, gridStep, snapStep, image);
32038
- if (validationResult === true) {
32039
- // either true or a string
32040
- this.change({
32041
- valid: true,
32042
- labels: labels,
32043
- range: range,
32044
- step: step,
32045
- gridStep: gridStep,
32046
- snapStep: snapStep,
32047
- backgroundImage: image
32048
- });
32049
- } else {
32050
- this.change({
32051
- valid: validationResult // a string message, not false
32052
- });
32053
- }
32039
+ const LockedFiguresSection = props => {
32040
+ var _props$figures;
32041
+ // Keep track of all figures' accordions' expanded states for the
32042
+ // expand/collapse all button. Set the whole array to false initially.
32043
+ const collapsedStateArray = Array(((_props$figures = props.figures) != null ? _props$figures : []).length).fill(false);
32044
+ const [expandedStates, setExpandedStates] = React.useState(collapsedStateArray);
32045
+ const [isExpanded, setIsExpanded] = React.useState(true);
32046
+ const uniqueId = useUniqueIdWithMock().get("locked-figures-section");
32047
+ const {
32048
+ figures,
32049
+ onChange
32050
+ } = props;
32051
+ function addLockedFigure(newFigure) {
32052
+ const lockedFigures = figures || [];
32053
+ const newProps = {
32054
+ lockedFigures: [...lockedFigures, getDefaultFigureForType(newFigure)]
32054
32055
  };
32055
- this.state = _extends({
32056
- isExpanded: true
32057
- }, InteractiveGraphSettings.stateFromProps(props));
32056
+ onChange(newProps);
32057
+ setExpandedStates([...expandedStates, true]);
32058
32058
  }
32059
- componentDidMount() {
32060
- // TODO(scottgrant): This is a hack to remove the deprecated call to
32061
- // this.isMounted() but is still considered an anti-pattern.
32062
- this._isMounted = true;
32063
- this.changeGraph = _.debounce(this.changeGraph, 300);
32059
+ function moveLockedFigure(index, movement) {
32060
+ // Don't allow moving the first figure up or the last figure down.
32061
+ if (index === 0 && (movement === "back" || movement === "backward")) {
32062
+ return;
32063
+ }
32064
+ if (figures && index === figures.length - 1 && (movement === "front" || movement === "forward")) {
32065
+ return;
32066
+ }
32067
+ const lockedFigures = figures || [];
32068
+ const newFigures = [...lockedFigures];
32069
+ const newExpandedStates = [...expandedStates];
32070
+
32071
+ // First, remove the figure from its current position
32072
+ // in the figures array and the expanded states array.
32073
+ const [removedFigure] = newFigures.splice(index, 1);
32074
+ newExpandedStates.splice(index, 1);
32075
+
32076
+ // Then, add it back in the new position. Add "true" to the
32077
+ // expanded states array for the new position (it must already
32078
+ // be open since the button to move it is being pressed from there).
32079
+ switch (movement) {
32080
+ case "back":
32081
+ newFigures.unshift(removedFigure);
32082
+ newExpandedStates.unshift(true);
32083
+ break;
32084
+ case "backward":
32085
+ newFigures.splice(index - 1, 0, removedFigure);
32086
+ newExpandedStates.splice(index - 1, 0, true);
32087
+ break;
32088
+ case "forward":
32089
+ newFigures.splice(index + 1, 0, removedFigure);
32090
+ newExpandedStates.splice(index + 1, 0, true);
32091
+ break;
32092
+ case "front":
32093
+ newFigures.push(removedFigure);
32094
+ newExpandedStates.push(true);
32095
+ break;
32096
+ }
32097
+ onChange({
32098
+ lockedFigures: newFigures
32099
+ });
32100
+ setExpandedStates(newExpandedStates);
32064
32101
  }
32065
- UNSAFE_componentWillReceiveProps(nextProps) {
32066
- // Make sure that state updates when switching
32067
- // between different items in a multi-item editor.
32068
- if (!_.isEqual(this.props.labels, nextProps.labels) || !_.isEqual(this.props.gridStep, nextProps.gridStep) || !_.isEqual(this.props.snapStep, nextProps.snapStep) || !_.isEqual(this.props.step, nextProps.step) || !_.isEqual(this.props.range, nextProps.range) || !_.isEqual(this.props.backgroundImage, nextProps.backgroundImage)) {
32069
- this.setState(InteractiveGraphSettings.stateFromProps(nextProps));
32102
+ function removeLockedFigure(index) {
32103
+ // eslint-disable-next-line no-alert
32104
+ if (window.confirm("Are you sure you want to delete this figure?")) {
32105
+ const lockedFigures = figures || [];
32106
+ onChange({
32107
+ lockedFigures: [...lockedFigures.slice(0, index), ...lockedFigures.slice(index + 1)]
32108
+ });
32109
+
32110
+ // Update expanded states
32111
+ const newExpandedStates = [...expandedStates];
32112
+ newExpandedStates.splice(index, 1);
32113
+ setExpandedStates(newExpandedStates);
32070
32114
  }
32071
32115
  }
32072
- componentWillUnmount() {
32073
- this._isMounted = false;
32116
+ function changeProps(index,
32117
+ // Omit the type from the figure props so it doesn't think
32118
+ // we're trying to pass in the props for the wrong type.
32119
+ figureProps) {
32120
+ const lockedFigures = figures || [];
32121
+ const newFigures = {
32122
+ lockedFigures: [...lockedFigures.slice(0, index), _extends({}, lockedFigures[index], figureProps), ...lockedFigures.slice(index + 1)]
32123
+ };
32124
+ onChange(newFigures);
32074
32125
  }
32075
- render() {
32076
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
32077
- title: "Common Graph Settings",
32078
- isOpen: this.state.isExpanded,
32079
- isCollapsible: true,
32080
- onToggle: () => this.setState({
32081
- isExpanded: !this.state.isExpanded
32082
- })
32083
- }), this.state.isExpanded && /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement("div", {
32084
- className: "graph-settings"
32085
- }, /*#__PURE__*/React.createElement("div", {
32086
- className: "perseus-widget-row"
32087
- }, /*#__PURE__*/React.createElement("div", {
32088
- className: "perseus-widget-left-col"
32089
- }, /*#__PURE__*/React.createElement(LabeledRow, {
32090
- label: "x Label"
32091
- }, /*#__PURE__*/React.createElement("input", {
32092
- type: "text",
32093
- className: "graph-settings-axis-label",
32094
- ref: this.labelXRef,
32095
- onChange: e => this.changeLabel(0, e),
32096
- value: this.state.labelsTextbox[0] || ""
32097
- }))), /*#__PURE__*/React.createElement("div", {
32098
- className: "perseus-widget-right-col"
32099
- }, /*#__PURE__*/React.createElement(LabeledRow, {
32100
- label: "y Label"
32101
- }, /*#__PURE__*/React.createElement("input", {
32102
- type: "text",
32103
- className: "graph-settings-axis-label",
32104
- ref: this.labelYRef,
32105
- onChange: e => this.changeLabel(1, e),
32106
- value: this.state.labelsTextbox[1] || ""
32107
- })))), /*#__PURE__*/React.createElement("div", {
32108
- className: "perseus-widget-row"
32109
- }, /*#__PURE__*/React.createElement("div", {
32110
- className: "perseus-widget-left-col"
32111
- }, /*#__PURE__*/React.createElement(LabeledRow, {
32112
- label: "x Range"
32113
- }, /*#__PURE__*/React.createElement(RangeInput$4, {
32114
- value: this.state.rangeTextbox[0],
32115
- onChange: vals => this.changeRange(0, vals)
32116
- }))), /*#__PURE__*/React.createElement("div", {
32117
- className: "perseus-widget-right-col"
32118
- }, /*#__PURE__*/React.createElement(LabeledRow, {
32119
- label: "y Range"
32120
- }, /*#__PURE__*/React.createElement(RangeInput$4, {
32121
- value: this.state.rangeTextbox[1],
32122
- onChange: vals => this.changeRange(1, vals)
32123
- })))), /*#__PURE__*/React.createElement("div", {
32124
- className: "perseus-widget-row"
32125
- }, /*#__PURE__*/React.createElement("div", {
32126
- className: "perseus-widget-left-col"
32127
- }, /*#__PURE__*/React.createElement(LabeledRow, {
32128
- label: "Tick Step"
32129
- }, /*#__PURE__*/React.createElement(RangeInput$4, {
32130
- value: this.state.stepTextbox,
32131
- onChange: this.changeStep
32132
- }))), /*#__PURE__*/React.createElement("div", {
32133
- className: "perseus-widget-right-col"
32134
- }, /*#__PURE__*/React.createElement(LabeledRow, {
32135
- label: "Grid Step"
32136
- }, /*#__PURE__*/React.createElement(RangeInput$4, {
32137
- value: this.state.gridStepTextbox,
32138
- onChange: this.changeGridStep
32139
- })))), /*#__PURE__*/React.createElement("div", {
32140
- className: "perseus-widget-row"
32141
- }, /*#__PURE__*/React.createElement("div", {
32142
- className: "perseus-widget-left-col"
32143
- }, /*#__PURE__*/React.createElement(LabeledRow, {
32144
- label: "Snap Step"
32145
- }, /*#__PURE__*/React.createElement(RangeInput$4, {
32146
- value: this.state.snapStepTextbox,
32147
- onChange: this.changeSnapStep
32148
- })))), /*#__PURE__*/React.createElement("div", {
32149
- className: "perseus-widget-row"
32150
- }, /*#__PURE__*/React.createElement(LabeledRow, {
32151
- label: "Markings:"
32152
- }, /*#__PURE__*/React.createElement(ButtonGroup$2, {
32153
- value: this.props.markings,
32154
- allowEmpty: false,
32155
- buttons: [{
32156
- value: "graph",
32157
- content: "Graph"
32158
- }, {
32159
- value: "grid",
32160
- content: "Grid"
32161
- }, {
32162
- value: "none",
32163
- content: "None"
32164
- }],
32165
- onChange: this.change("markings")
32166
- }))), /*#__PURE__*/React.createElement("div", {
32167
- className: "perseus-widget-left-col"
32168
- }, /*#__PURE__*/React.createElement(Checkbox$1, {
32169
- label: "Show tooltips",
32170
- checked: this.props.showTooltips,
32171
- onChange: value => {
32172
- this.change({
32173
- showTooltips: value
32174
- });
32175
- }
32176
- }))), /*#__PURE__*/React.createElement(LabeledRow, {
32177
- label: "Background image URL:",
32178
- style: styles$j.resetSpaceTop
32179
- }, /*#__PURE__*/React.createElement("input", {
32180
- type: "text",
32181
- className: css(styles$j.backgroundUrlInput),
32182
- ref: this.bgUrlRef,
32183
- value: this.state.backgroundImage.url || "",
32184
- onChange: e => {
32185
- // Shallow-copied clone
32186
- const image = _extends({}, this.props.backgroundImage);
32187
- image.url = e.target.value;
32188
- this.setState({
32189
- backgroundImage: image
32190
- });
32191
- },
32192
- onKeyPress: this.changeBackgroundUrl,
32193
- onBlur: this.changeBackgroundUrl
32194
- }), /*#__PURE__*/React.createElement(InfoTip$c, null, /*#__PURE__*/React.createElement("p", null, "Create an image in graphie, or use the \"Add image\" function to create a background."))), /*#__PURE__*/React.createElement(View, {
32195
- style: styles$j.protractorSection
32196
- }, /*#__PURE__*/React.createElement(View, {
32197
- style: styles$j.checkboxRow
32198
- }, /*#__PURE__*/React.createElement(Checkbox$1, {
32199
- label: "Show protractor",
32200
- checked: this.props.showProtractor,
32201
- onChange: value => {
32202
- this.change({
32203
- showProtractor: value
32204
- });
32205
- },
32206
- style: styles$j.resetSpaceTop
32207
- })), this.props.showProtractor && /*#__PURE__*/React.createElement(Banner, {
32208
- layout: "floating",
32209
- text: "The protractor is not accessible. Please consider an alternate approach.",
32210
- kind: "warning"
32211
- }))));
32126
+ function toggleExpanded(newValue) {
32127
+ setExpandedStates(Array(figures == null ? void 0 : figures.length).fill(newValue));
32212
32128
  }
32213
- }
32214
- InteractiveGraphSettings.defaultProps = {
32215
- box: [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall],
32216
- labels: ["x", "y"],
32217
- range: [[-10, 10], [-10, 10]],
32218
- step: [1, 1],
32219
- gridStep: [1, 1],
32220
- snapStep: [1, 1],
32221
- valid: true,
32222
- backgroundImage: defaultBackgroundImage$1,
32223
- markings: "graph",
32224
- showProtractor: false,
32225
- showTooltips: false
32129
+ const allCollapsed = expandedStates.every(value => !value);
32130
+ const buttonLabel = allCollapsed ? "Expand all" : "Collapse all";
32131
+ const showExpandButton = !!(figures != null && figures.length);
32132
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
32133
+ title: "Locked Figures",
32134
+ isOpen: isExpanded,
32135
+ onToggle: () => setIsExpanded(!isExpanded),
32136
+ isCollapsible: true
32137
+ }), isExpanded && /*#__PURE__*/React.createElement(View, null, figures == null ? void 0 : figures.map((figure, index) => {
32138
+ return /*#__PURE__*/React.createElement(LockedFigureSettings, _extends({
32139
+ key: `${uniqueId}-locked-${figure}-${index}`,
32140
+ showLabelsFlag: props.showLabelsFlag,
32141
+ expanded: expandedStates[index],
32142
+ onToggle: newValue => {
32143
+ const newExpanded = [...expandedStates];
32144
+ newExpanded[index] = newValue;
32145
+ setExpandedStates(newExpanded);
32146
+ }
32147
+ }, figure, {
32148
+ onChangeProps: newProps => changeProps(index, newProps),
32149
+ onMove: movement => moveLockedFigure(index, movement),
32150
+ onRemove: () => removeLockedFigure(index)
32151
+ }));
32152
+ }), /*#__PURE__*/React.createElement(View, {
32153
+ style: styles$i.buttonContainer
32154
+ }, /*#__PURE__*/React.createElement(LockedFigureSelect, {
32155
+ showLabelsFlag: props.showLabelsFlag,
32156
+ id: `${uniqueId}-select`,
32157
+ onChange: addLockedFigure
32158
+ }), /*#__PURE__*/React.createElement(Strut, {
32159
+ size: spacing.small_12
32160
+ }), showExpandButton && /*#__PURE__*/React.createElement(Button, {
32161
+ kind: "secondary",
32162
+ onClick: () => toggleExpanded(allCollapsed),
32163
+ style: styles$i.button
32164
+ }, buttonLabel))));
32226
32165
  };
32227
- const styles$j = StyleSheet.create({
32228
- resetSpaceTop: {
32229
- marginTop: 0
32230
- },
32231
- backgroundUrlInput: {
32232
- border: `1px solid ${color.offBlack32}`,
32233
- borderRadius: spacing.xxxSmall_4,
32234
- padding: spacing.xxxSmall_4
32235
- },
32236
- checkboxRow: {
32166
+ const styles$i = StyleSheet.create({
32167
+ buttonContainer: {
32237
32168
  flexDirection: "row",
32238
- alignItems: "center",
32239
- justifyContent: "space-between",
32240
- marginBottom: spacing.xSmall_8
32169
+ alignItems: "center"
32241
32170
  },
32242
- protractorSection: {
32171
+ button: {
32243
32172
  marginTop: spacing.xSmall_8,
32244
- borderTop: `1px solid ${color.offBlack16}`,
32245
- paddingTop: spacing.xSmall_8,
32246
- paddingBottom: spacing.xSmall_8,
32247
- borderBottom: `1px solid ${color.offBlack16}`
32173
+ flexGrow: 1
32248
32174
  }
32249
32175
  });
32250
32176
 
32251
- const SegmentCountSelector = ({
32252
- numSegments: _numSegments = 1,
32253
- onChange
32254
- }) => /*#__PURE__*/React.createElement(SingleSelect, {
32255
- key: "segment-select",
32256
- selectedValue: `${_numSegments}`
32257
- // Never uses placeholder, always has value
32258
- ,
32259
- placeholder: "",
32260
- onChange: newValue => {
32261
- const num = +newValue;
32262
- onChange(num);
32263
- },
32264
- style: styles$i.singleSelectShort
32265
- }, _.range(1, 7).map(n => /*#__PURE__*/React.createElement(OptionItem, {
32266
- key: n,
32267
- value: `${n}`,
32268
- label: `${n} segment${n > 1 ? "s" : ""}`
32269
- })));
32270
- const styles$i = StyleSheet.create({
32271
- singleSelectShort: {
32272
- // Non-standard spacing, but it's the smallest we can go
32273
- // without running into styling issues with the dropdown.
32274
- height: 26
32177
+ function getStartCoords(graph) {
32178
+ if ("startCoords" in graph) {
32179
+ return graph.startCoords;
32275
32180
  }
32276
- });
32181
+ return undefined;
32182
+ }
32183
+ function getDefaultGraphStartCoords(graph, range, step) {
32184
+ switch (graph.type) {
32185
+ case "linear":
32186
+ case "ray":
32187
+ return getLineCoords(_extends({}, graph, {
32188
+ startCoords: undefined
32189
+ }), range, step);
32190
+ case "segment":
32191
+ return getSegmentCoords(_extends({}, graph, {
32192
+ startCoords: undefined
32193
+ }), range, step);
32194
+ case "linear-system":
32195
+ return getLinearSystemCoords(_extends({}, graph, {
32196
+ startCoords: undefined
32197
+ }), range, step);
32198
+ case "circle":
32199
+ const startCoords = getCircleCoords(_extends({}, graph, {
32200
+ startCoords: undefined
32201
+ }));
32202
+ const radius = vector.length(vector.subtract(startCoords.radiusPoint, startCoords.center));
32203
+ return {
32204
+ center: startCoords.center,
32205
+ radius
32206
+ };
32207
+ case "sinusoid":
32208
+ return getSinusoidCoords(_extends({}, graph, {
32209
+ startCoords: undefined
32210
+ }), range, step);
32211
+ case "quadratic":
32212
+ return getQuadraticCoords(_extends({}, graph, {
32213
+ startCoords: undefined
32214
+ }), range, step);
32215
+ case "point":
32216
+ return getPointCoords(_extends({}, graph, {
32217
+ startCoords: undefined
32218
+ }), range, step);
32219
+ case "polygon":
32220
+ return getPolygonCoords(_extends({}, graph, {
32221
+ startCoords: undefined
32222
+ }), range, step);
32223
+ case "angle":
32224
+ return getAngleCoords({
32225
+ graph: _extends({}, graph, {
32226
+ startCoords: undefined
32227
+ }),
32228
+ range,
32229
+ step
32230
+ });
32231
+ default:
32232
+ return undefined;
32233
+ }
32234
+ }
32235
+ const getSinusoidEquation = startCoords => {
32236
+ // Get coefficients
32237
+ // It's assumed that p1 is the root and p2 is the first peak
32238
+ const p1 = startCoords[0];
32239
+ const p2 = startCoords[1];
32240
+
32241
+ // Resulting coefficients are canonical for this sine curve
32242
+ const amplitude = p2[1] - p1[1];
32243
+ const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
32244
+ const phase = p1[0] * angularFrequency;
32245
+ const verticalOffset = p1[1];
32246
+ return "y = " + amplitude.toFixed(3) + "sin(" + angularFrequency.toFixed(3) + "x - " + phase.toFixed(3) + ") + " + verticalOffset.toFixed(3);
32247
+ };
32248
+ const getQuadraticEquation = startCoords => {
32249
+ const p1 = startCoords[0];
32250
+ const p2 = startCoords[1];
32251
+ const p3 = startCoords[2];
32252
+ const denom = (p1[0] - p2[0]) * (p1[0] - p3[0]) * (p2[0] - p3[0]);
32253
+ if (denom === 0) {
32254
+ // Many of the callers assume that the return value is always defined.
32255
+ return "Division by zero error";
32256
+ }
32257
+ const a = (p3[0] * (p2[1] - p1[1]) + p2[0] * (p1[1] - p3[1]) + p1[0] * (p3[1] - p2[1])) / denom;
32258
+ const b = (p3[0] * p3[0] * (p1[1] - p2[1]) + p2[0] * p2[0] * (p3[1] - p1[1]) + p1[0] * p1[0] * (p2[1] - p3[1])) / denom;
32259
+ const c = (p2[0] * p3[0] * (p2[0] - p3[0]) * p1[1] + p3[0] * p1[0] * (p3[0] - p1[0]) * p2[1] + p1[0] * p2[0] * (p1[0] - p2[0]) * p3[1]) / denom;
32260
+ return "y = " + a.toFixed(3) + "x^2 + " + b.toFixed(3) + "x + " + c.toFixed(3);
32261
+ };
32262
+ const findAngle = (point1, point2) => {
32263
+ const x = point1[0] - point2[0];
32264
+ const y = point1[1] - point2[1];
32265
+ return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
32266
+ };
32267
+ const getAngleEquation = startCoords => {
32268
+ const [point1, vertex, point2] = startCoords;
32269
+ const angle = findAngle(point2, vertex) - findAngle(point1, vertex);
32270
+ const roundedAngle = angle.toFixed(0);
32271
+ return `${roundedAngle}\u00B0 angle at (${vertex[0]}, ${vertex[1]})`;
32272
+ };
32273
+ const shouldShowStartCoordsUI = (graph, isStatic) => {
32274
+ // We don't show the start coords UI for static graphs, since
32275
+ // the coords shown for this are determined by the coords on the
32276
+ // "correct" graph. The start coords would not be used here.
32277
+ if (isStatic) {
32278
+ return false;
32279
+ }
32280
+ switch (graph.type) {
32281
+ case "point":
32282
+ return graph.numPoints !== "unlimited";
32283
+ case "polygon":
32284
+ return graph.numSides !== "unlimited" && graph.snapTo !== "angles" && graph.snapTo !== "sides";
32285
+ case "none":
32286
+ return false;
32287
+ case "angle":
32288
+ case "circle":
32289
+ case "linear":
32290
+ case "linear-system":
32291
+ case "quadratic":
32292
+ case "ray":
32293
+ case "segment":
32294
+ case "sinusoid":
32295
+ return true;
32296
+ default:
32297
+ throw new UnreachableCaseError(graph);
32298
+ }
32299
+ };
32277
32300
 
32278
32301
  const StartCoordsAngle = props => {
32279
32302
  const {
@@ -32783,13 +32806,12 @@ class InteractiveGraphEditor extends React.Component {
32783
32806
  // eslint-disable-next-line react/no-string-refs
32784
32807
  const graph = this.refs.graph;
32785
32808
  if (graph) {
32786
- var _this$props$graph2;
32787
32809
  // @ts-expect-error TS2339 Property 'getUserInput' does not exist on type 'ReactInstance'. Property 'getUserInput' does not exist on type 'Component<any, {}, any>'.
32788
32810
  const correct = graph && graph.getUserInput();
32789
32811
  _.extend(json, {
32790
32812
  graph: {
32791
32813
  type: correct.type,
32792
- startCoords: (_this$props$graph2 = this.props.graph) == null ? void 0 : _this$props$graph2.startCoords
32814
+ startCoords: this.props.graph && getStartCoords(this.props.graph)
32793
32815
  },
32794
32816
  correct: correct
32795
32817
  });
@@ -32804,7 +32826,7 @@ class InteractiveGraphEditor extends React.Component {
32804
32826
  return json;
32805
32827
  }
32806
32828
  render() {
32807
- var _this$props$graph$typ, _this$props$graph3, _this$props$apiOption, _this$props$fullGraph, _this$props$fullGraph2, _this$props$correct, _this$props$correct2, _this$props$correct3, _this$props$correct4, _this$props$correct5, _this$props$correct6, _this$props$correct7, _this$props$correct8, _this$props$correct9, _this$props$correct10, _this$props$correct11, _this$props$graph9, _this$props$apiOption2, _this$props$apiOption3;
32829
+ var _this$props$graph$typ, _this$props$graph2, _this$props$apiOption, _this$props$apiOption2, _this$props$fullGraph, _this$props$fullGraph2, _this$props$correct, _this$props$correct2, _this$props$correct3, _this$props$correct4, _this$props$correct5, _this$props$correct6, _this$props$correct7, _this$props$correct8, _this$props$correct9, _this$props$correct10, _this$props$correct11, _this$props$graph8, _this$props$apiOption3, _this$props$apiOption4;
32808
32830
  let graph;
32809
32831
  let equationString;
32810
32832
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, interactiveSizes.defaultBoxSize);
@@ -32872,9 +32894,9 @@ class InteractiveGraphEditor extends React.Component {
32872
32894
  }, this.props.valid);
32873
32895
  }
32874
32896
  return /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(LabeledRow, {
32875
- label: "Type of Graph:"
32897
+ label: "Answer type:"
32876
32898
  }, /*#__PURE__*/React.createElement(GraphTypeSelector, {
32877
- graphType: (_this$props$graph$typ = (_this$props$graph3 = this.props.graph) == null ? void 0 : _this$props$graph3.type) != null ? _this$props$graph$typ : InteractiveGraph.defaultProps.graph.type,
32899
+ graphType: (_this$props$graph$typ = (_this$props$graph2 = this.props.graph) == null ? void 0 : _this$props$graph2.type) != null ? _this$props$graph$typ : InteractiveGraph.defaultProps.graph.type,
32878
32900
  onChange: type => {
32879
32901
  this.props.onChange({
32880
32902
  graph: {
@@ -32884,8 +32906,9 @@ class InteractiveGraphEditor extends React.Component {
32884
32906
  type
32885
32907
  }
32886
32908
  });
32887
- }
32888
- })), this.props.graph && ((_this$props$apiOption = this.props.apiOptions) == null || (_this$props$apiOption = _this$props$apiOption.flags) == null || (_this$props$apiOption = _this$props$apiOption.mafs) == null ? void 0 : _this$props$apiOption[this.props.graph.type]) && /*#__PURE__*/React.createElement(InteractiveGraphDescription, {
32909
+ },
32910
+ showNoneOption: (_this$props$apiOption = this.props.apiOptions) == null || (_this$props$apiOption = _this$props$apiOption.flags) == null || (_this$props$apiOption = _this$props$apiOption.mafs) == null ? void 0 : _this$props$apiOption["none"]
32911
+ })), this.props.graph && ((_this$props$apiOption2 = this.props.apiOptions) == null || (_this$props$apiOption2 = _this$props$apiOption2.flags) == null || (_this$props$apiOption2 = _this$props$apiOption2.mafs) == null ? void 0 : _this$props$apiOption2[this.props.graph.type]) && /*#__PURE__*/React.createElement(InteractiveGraphDescription, {
32889
32912
  ariaLabelValue: (_this$props$fullGraph = this.props.fullGraphAriaLabel) != null ? _this$props$fullGraph : "",
32890
32913
  ariaDescriptionValue: (_this$props$fullGraph2 = this.props.fullGraphAriaDescription) != null ? _this$props$fullGraph2 : "",
32891
32914
  onChange: this.props.onChange
@@ -32914,8 +32937,8 @@ class InteractiveGraphEditor extends React.Component {
32914
32937
  selectedValue: (_this$props$correct4 = this.props.correct) != null && _this$props$correct4.numSides ? `${this.props.correct.numSides}` : "3",
32915
32938
  placeholder: "",
32916
32939
  onChange: newValue => {
32917
- var _this$props$graph4;
32918
- invariant(((_this$props$graph4 = this.props.graph) == null ? void 0 : _this$props$graph4.type) === "polygon");
32940
+ var _this$props$graph3;
32941
+ invariant(((_this$props$graph3 = this.props.graph) == null ? void 0 : _this$props$graph3.type) === "polygon");
32919
32942
  const updates = {
32920
32943
  numSides: parsePointCount(newValue),
32921
32944
  coords: null,
@@ -32942,9 +32965,9 @@ class InteractiveGraphEditor extends React.Component {
32942
32965
  ,
32943
32966
  placeholder: "",
32944
32967
  onChange: newValue => {
32945
- var _this$props$graph5, _this$props$graph6;
32968
+ var _this$props$graph4, _this$props$graph5;
32946
32969
  invariant(this.props.correct.type === "polygon", `Expected correct answer type to be polygon, but got ${this.props.correct.type}`);
32947
- invariant(((_this$props$graph5 = this.props.graph) == null ? void 0 : _this$props$graph5.type) === "polygon", `Expected graph type to be polygon, but got ${(_this$props$graph6 = this.props.graph) == null ? void 0 : _this$props$graph6.type}`);
32970
+ invariant(((_this$props$graph4 = this.props.graph) == null ? void 0 : _this$props$graph4.type) === "polygon", `Expected graph type to be polygon, but got ${(_this$props$graph5 = this.props.graph) == null ? void 0 : _this$props$graph5.type}`);
32948
32971
  const updates = {
32949
32972
  snapTo: newValue,
32950
32973
  coords: null
@@ -32972,8 +32995,8 @@ class InteractiveGraphEditor extends React.Component {
32972
32995
  // Don't show indeterminate checkbox state
32973
32996
  !!((_this$props$correct8 = this.props.correct) != null && _this$props$correct8.showAngles),
32974
32997
  onChange: () => {
32975
- var _this$props$graph7;
32976
- if (((_this$props$graph7 = this.props.graph) == null ? void 0 : _this$props$graph7.type) === "polygon") {
32998
+ var _this$props$graph6;
32999
+ if (((_this$props$graph6 = this.props.graph) == null ? void 0 : _this$props$graph6.type) === "polygon") {
32977
33000
  invariant(this.props.correct.type === "polygon", `Expected graph type to be polygon, but got ${this.props.correct.type}`);
32978
33001
  this.props.onChange({
32979
33002
  correct: _extends({}, this.props.correct, {
@@ -32993,8 +33016,8 @@ class InteractiveGraphEditor extends React.Component {
32993
33016
  // Don't show indeterminate checkbox state
32994
33017
  !!((_this$props$correct9 = this.props.correct) != null && _this$props$correct9.showSides),
32995
33018
  onChange: () => {
32996
- var _this$props$graph8;
32997
- if (((_this$props$graph8 = this.props.graph) == null ? void 0 : _this$props$graph8.type) === "polygon" && this.props.correct.type === "polygon") {
33019
+ var _this$props$graph7;
33020
+ if (((_this$props$graph7 = this.props.graph) == null ? void 0 : _this$props$graph7.type) === "polygon" && this.props.correct.type === "polygon") {
32998
33021
  this.props.onChange({
32999
33022
  correct: _extends({}, this.props.correct, {
33000
33023
  showSides: !this.props.correct.showSides
@@ -33022,7 +33045,7 @@ class InteractiveGraphEditor extends React.Component {
33022
33045
  }
33023
33046
  });
33024
33047
  }
33025
- })), ((_this$props$graph9 = this.props.graph) == null ? void 0 : _this$props$graph9.type) && shouldShowStartCoordsUI(this.props.graph, this.props.static) && /*#__PURE__*/React.createElement(StartCoordsSettings, _extends({}, this.props.graph, {
33048
+ })), ((_this$props$graph8 = this.props.graph) == null ? void 0 : _this$props$graph8.type) && shouldShowStartCoordsUI(this.props.graph, this.props.static) && /*#__PURE__*/React.createElement(StartCoordsSettings, _extends({}, this.props.graph, {
33026
33049
  range: this.props.range,
33027
33050
  step: this.props.step,
33028
33051
  onChange: this.changeStartCoords
@@ -33106,8 +33129,8 @@ class InteractiveGraphEditor extends React.Component {
33106
33129
  })), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "Congruency requires only that the angle measures are the same. An exact match implies congruency, but also requires that the angles have the same orientation and that the vertices are in the same position."))),
33107
33130
  // Only show the "Add locked figure" dropdown if the graph
33108
33131
  // is using Mafs.
33109
- this.props.graph && ((_this$props$apiOption2 = this.props.apiOptions) == null || (_this$props$apiOption2 = _this$props$apiOption2.flags) == null || (_this$props$apiOption2 = _this$props$apiOption2.mafs) == null ? void 0 : _this$props$apiOption2[this.props.graph.type]) && /*#__PURE__*/React.createElement(LockedFiguresSection, {
33110
- showLabelsFlag: (_this$props$apiOption3 = this.props.apiOptions) == null || (_this$props$apiOption3 = _this$props$apiOption3.flags) == null || (_this$props$apiOption3 = _this$props$apiOption3.mafs) == null ? void 0 : _this$props$apiOption3["interactive-graph-locked-features-labels"],
33132
+ this.props.graph && ((_this$props$apiOption3 = this.props.apiOptions) == null || (_this$props$apiOption3 = _this$props$apiOption3.flags) == null || (_this$props$apiOption3 = _this$props$apiOption3.mafs) == null ? void 0 : _this$props$apiOption3[this.props.graph.type]) && /*#__PURE__*/React.createElement(LockedFiguresSection, {
33133
+ showLabelsFlag: (_this$props$apiOption4 = this.props.apiOptions) == null || (_this$props$apiOption4 = _this$props$apiOption4.flags) == null || (_this$props$apiOption4 = _this$props$apiOption4.mafs) == null ? void 0 : _this$props$apiOption4["interactive-graph-locked-features-labels"],
33111
33134
  figures: this.props.lockedFigures,
33112
33135
  onChange: this.props.onChange
33113
33136
  }));
@@ -33144,6 +33167,9 @@ function mergeGraphs(a, b) {
33144
33167
  case "linear-system":
33145
33168
  invariant(b.type === "linear-system");
33146
33169
  return _extends({}, a, b);
33170
+ case "none":
33171
+ invariant(b.type === "none");
33172
+ return _extends({}, a, b);
33147
33173
  case "point":
33148
33174
  invariant(b.type === "point");
33149
33175
  return _extends({}, a, b);