@khanacademy/perseus 53.0.0 → 54.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/highlighting/types.d.ts +2 -1
- package/dist/components.d.ts +0 -1
- package/dist/es/index.js +1101 -2034
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +101 -14
- package/dist/es/strings.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1180 -2058
- package/dist/index.js.map +1 -1
- package/dist/interactive2/movable.d.ts +2 -1
- package/dist/server-item-renderer.d.ts +0 -3
- package/dist/strings.d.ts +93 -6
- package/dist/strings.js +184 -58
- package/dist/strings.js.map +1 -1
- package/dist/types.d.ts +3 -24
- package/dist/util/graphie.d.ts +1 -1
- package/dist/widget-ai-utils/radio/radio-ai-utils.d.ts +2 -1
- package/dist/widget-type-utils.d.ts +1 -0
- package/dist/widgets/categorizer/categorizer.d.ts +1 -2
- package/dist/widgets/cs-program/cs-program.d.ts +0 -2
- package/dist/widgets/dropdown/dropdown.d.ts +0 -2
- package/dist/widgets/expression/expression.d.ts +3 -10
- package/dist/widgets/iframe/iframe.d.ts +1 -1
- package/dist/widgets/interactive-graph.d.ts +2 -86
- package/dist/widgets/interactive-graphs/backgrounds/axis-labels.d.ts +4 -1
- package/dist/widgets/interactive-graphs/graphs/angle.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/circle.d.ts +6 -2
- package/dist/widgets/interactive-graphs/graphs/components/hairlines.d.ts +7 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-point-view.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/components/svg-line.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/linear-system.d.ts +2 -2
- package/dist/widgets/interactive-graphs/graphs/linear.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/point.d.ts +3 -2
- package/dist/widgets/interactive-graphs/graphs/polygon.d.ts +3 -2
- package/dist/widgets/interactive-graphs/graphs/ray.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/screenreader-text.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/segment.d.ts +2 -2
- package/dist/widgets/interactive-graphs/graphs/sinusoid.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/utils.d.ts +16 -4
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +1 -2
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +0 -2
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +3 -3
- package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +0 -2
- package/dist/widgets/label-image/label-image.d.ts +1 -3
- package/dist/widgets/number-line/number-line.d.ts +0 -2
- package/dist/widgets/numeric-input/numeric-input.d.ts +0 -2
- package/dist/widgets/orderer/orderer.d.ts +0 -2
- package/dist/widgets/sorter/sorter.d.ts +1 -2
- package/dist/widgets.d.ts +1 -2
- package/package.json +46 -46
- package/dist/components/graph.d.ts +0 -61
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +0 -4
package/dist/index.js
CHANGED
|
@@ -33,7 +33,6 @@ var Banner = require('@khanacademy/wonder-blocks-banner');
|
|
|
33
33
|
var wonderBlocksDropdown = require('@khanacademy/wonder-blocks-dropdown');
|
|
34
34
|
var caretDown = require('@phosphor-icons/core/regular/caret-down.svg');
|
|
35
35
|
var caretUp = require('@phosphor-icons/core/regular/caret-up.svg');
|
|
36
|
-
var debounce$1 = require('lodash.debounce');
|
|
37
36
|
var mafs = require('mafs');
|
|
38
37
|
var wonderBlocksIcon = require('@khanacademy/wonder-blocks-icon');
|
|
39
38
|
var questionIcon = require('@phosphor-icons/core/regular/question.svg');
|
|
@@ -82,7 +81,6 @@ var Button__default = /*#__PURE__*/_interopDefaultLegacy(Button);
|
|
|
82
81
|
var Banner__default = /*#__PURE__*/_interopDefaultLegacy(Banner);
|
|
83
82
|
var caretDown__default = /*#__PURE__*/_interopDefaultLegacy(caretDown);
|
|
84
83
|
var caretUp__default = /*#__PURE__*/_interopDefaultLegacy(caretUp);
|
|
85
|
-
var debounce__default = /*#__PURE__*/_interopDefaultLegacy(debounce$1);
|
|
86
84
|
var questionIcon__default = /*#__PURE__*/_interopDefaultLegacy(questionIcon);
|
|
87
85
|
var Switch__default = /*#__PURE__*/_interopDefaultLegacy(Switch);
|
|
88
86
|
var Pill__default = /*#__PURE__*/_interopDefaultLegacy(Pill);
|
|
@@ -368,6 +366,10 @@ const mockStrings = {
|
|
|
368
366
|
simulationLocaleWarning: "Sorry, this simulation isn't available in your language.",
|
|
369
367
|
selectAnAnswer: "Select an answer",
|
|
370
368
|
// The following strings are used for interactive graph SR descriptions.
|
|
369
|
+
srGraphInstructions: "Use the Tab key to move through the interactive elements in the graph. When an interactive element has focus, use the Arrow keys to move it.",
|
|
370
|
+
srUnlimitedGraphInstructions: "Press Shift + Enter to interact with the graph. Use the Tab key to move through the interactive elements in the graph and access the graph Action Bar. When an interactive element has focus, use the Arrow keys to move it or use the Delete key to remove it from the graph. Use the buttons in the Action Bar to add or adjust elements within the graph.",
|
|
371
|
+
xAxis: "X-axis",
|
|
372
|
+
yAxis: "Y-axis",
|
|
371
373
|
graphKeyboardPrompt: "Press Shift + Enter to interact with the graph",
|
|
372
374
|
addPoint: "Add Point",
|
|
373
375
|
removePoint: "Remove Point",
|
|
@@ -379,7 +381,7 @@ const mockStrings = {
|
|
|
379
381
|
x,
|
|
380
382
|
y
|
|
381
383
|
} = _ref27;
|
|
382
|
-
return `Point ${num} at ${x} comma ${y}
|
|
384
|
+
return `Point ${num} at ${x} comma ${y}.`;
|
|
383
385
|
},
|
|
384
386
|
srInteractiveElements: _ref28 => {
|
|
385
387
|
let {
|
|
@@ -465,25 +467,37 @@ const mockStrings = {
|
|
|
465
467
|
} = _ref37;
|
|
466
468
|
return `Line from ${point1X} comma ${point1Y} to ${point2X} comma ${point2Y}.`;
|
|
467
469
|
},
|
|
468
|
-
|
|
470
|
+
srAngleStartingSide: _ref38 => {
|
|
469
471
|
let {
|
|
470
|
-
point,
|
|
471
|
-
side,
|
|
472
472
|
x,
|
|
473
473
|
y
|
|
474
474
|
} = _ref38;
|
|
475
|
-
return `Point
|
|
475
|
+
return `Point 3, starting side at ${x} comma ${y}.`;
|
|
476
476
|
},
|
|
477
|
-
|
|
477
|
+
srAngleEndingSide: _ref39 => {
|
|
478
|
+
let {
|
|
479
|
+
x,
|
|
480
|
+
y
|
|
481
|
+
} = _ref39;
|
|
482
|
+
return `Point 2, ending side at ${x} comma ${y}.`;
|
|
483
|
+
},
|
|
484
|
+
srAngleVertex: _ref40 => {
|
|
485
|
+
let {
|
|
486
|
+
x,
|
|
487
|
+
y
|
|
488
|
+
} = _ref40;
|
|
489
|
+
return `Point 1, vertex at ${x} comma ${y}.`;
|
|
490
|
+
},
|
|
491
|
+
srAngleVertexWithAngleMeasure: _ref41 => {
|
|
478
492
|
let {
|
|
479
493
|
x,
|
|
480
494
|
y,
|
|
481
495
|
angleMeasure
|
|
482
|
-
} =
|
|
483
|
-
return `Point
|
|
496
|
+
} = _ref41;
|
|
497
|
+
return `Point 1, vertex at ${x} comma ${y}. Angle ${angleMeasure} degrees.`;
|
|
484
498
|
},
|
|
485
499
|
srAngleGraphAriaLabel: "An angle on a coordinate plane.",
|
|
486
|
-
srAngleGraphAriaDescription:
|
|
500
|
+
srAngleGraphAriaDescription: _ref42 => {
|
|
487
501
|
let {
|
|
488
502
|
angleMeasure,
|
|
489
503
|
vertexX,
|
|
@@ -492,118 +506,129 @@ const mockStrings = {
|
|
|
492
506
|
startingSideY,
|
|
493
507
|
endingSideX,
|
|
494
508
|
endingSideY
|
|
495
|
-
} =
|
|
509
|
+
} = _ref42;
|
|
496
510
|
return `The angle measure is ${angleMeasure} degrees with a vertex at ${vertexX} comma ${vertexY}, a point on the starting side at ${startingSideX} comma ${startingSideY} and a point on the ending side at ${endingSideX} comma ${endingSideY}.`;
|
|
497
511
|
},
|
|
512
|
+
srAngleInteractiveElements: _ref43 => {
|
|
513
|
+
let {
|
|
514
|
+
vertexX,
|
|
515
|
+
vertexY,
|
|
516
|
+
startingSideX,
|
|
517
|
+
startingSideY,
|
|
518
|
+
endingSideX,
|
|
519
|
+
endingSideY
|
|
520
|
+
} = _ref43;
|
|
521
|
+
return `An angle formed by 3 points. The vertex is at ${vertexX} comma ${vertexY}. The starting side point is at ${startingSideX} comma ${startingSideY}. The ending side point is at ${endingSideX} comma ${endingSideY}.`;
|
|
522
|
+
},
|
|
498
523
|
srSingleSegmentGraphAriaLabel: "A line segment on a coordinate plane.",
|
|
499
|
-
srMultipleSegmentGraphAriaLabel:
|
|
524
|
+
srMultipleSegmentGraphAriaLabel: _ref44 => {
|
|
500
525
|
let {
|
|
501
526
|
countOfSegments
|
|
502
|
-
} =
|
|
527
|
+
} = _ref44;
|
|
503
528
|
return `${countOfSegments} segments on a coordinate plane.`;
|
|
504
529
|
},
|
|
505
|
-
srMultipleSegmentIndividualLabel:
|
|
530
|
+
srMultipleSegmentIndividualLabel: _ref45 => {
|
|
506
531
|
let {
|
|
507
532
|
point1X,
|
|
508
533
|
point1Y,
|
|
509
534
|
point2X,
|
|
510
535
|
point2Y,
|
|
511
536
|
indexOfSegment
|
|
512
|
-
} =
|
|
537
|
+
} = _ref45;
|
|
513
538
|
return `Segment ${indexOfSegment}: Endpoint 1 at ${point1X} comma ${point1Y}. Endpoint 2 at ${point2X} comma ${point2Y}.`;
|
|
514
539
|
},
|
|
515
|
-
srSingleSegmentLabel:
|
|
540
|
+
srSingleSegmentLabel: _ref46 => {
|
|
516
541
|
let {
|
|
517
542
|
point1X,
|
|
518
543
|
point1Y,
|
|
519
544
|
point2X,
|
|
520
545
|
point2Y
|
|
521
|
-
} =
|
|
546
|
+
} = _ref46;
|
|
522
547
|
return `Endpoint 1 at ${point1X} comma ${point1Y}. Endpoint 2 at ${point2X} comma ${point2Y}.`;
|
|
523
548
|
},
|
|
524
|
-
srSegmentLength:
|
|
549
|
+
srSegmentLength: _ref47 => {
|
|
525
550
|
let {
|
|
526
551
|
length
|
|
527
|
-
} =
|
|
552
|
+
} = _ref47;
|
|
528
553
|
return `Segment length ${length} units.`;
|
|
529
554
|
},
|
|
530
|
-
srSingleSegmentGraphEndpointAriaLabel:
|
|
555
|
+
srSingleSegmentGraphEndpointAriaLabel: _ref48 => {
|
|
531
556
|
let {
|
|
532
557
|
endpointNumber,
|
|
533
558
|
x,
|
|
534
559
|
y
|
|
535
|
-
} =
|
|
560
|
+
} = _ref48;
|
|
536
561
|
return `Endpoint ${endpointNumber} at ${x} comma ${y}.`;
|
|
537
562
|
},
|
|
538
|
-
srMultipleSegmentGraphEndpointAriaLabel:
|
|
563
|
+
srMultipleSegmentGraphEndpointAriaLabel: _ref49 => {
|
|
539
564
|
let {
|
|
540
565
|
endpointNumber,
|
|
541
566
|
x,
|
|
542
567
|
y,
|
|
543
568
|
indexOfSegment
|
|
544
|
-
} =
|
|
569
|
+
} = _ref49;
|
|
545
570
|
return `Endpoint ${endpointNumber} on segment ${indexOfSegment} at ${x} comma ${y}.`;
|
|
546
571
|
},
|
|
547
|
-
srSegmentGrabHandle:
|
|
572
|
+
srSegmentGrabHandle: _ref50 => {
|
|
548
573
|
let {
|
|
549
574
|
point1X,
|
|
550
575
|
point1Y,
|
|
551
576
|
point2X,
|
|
552
577
|
point2Y
|
|
553
|
-
} =
|
|
578
|
+
} = _ref50;
|
|
554
579
|
return `Segment from ${point1X} comma ${point1Y} to ${point2X} comma ${point2Y}.`;
|
|
555
580
|
},
|
|
556
581
|
srLinearSystemGraph: "Two lines on a coordinate plane.",
|
|
557
|
-
srLinearSystemPoints:
|
|
582
|
+
srLinearSystemPoints: _ref51 => {
|
|
558
583
|
let {
|
|
559
584
|
lineNumber,
|
|
560
585
|
point1X,
|
|
561
586
|
point1Y,
|
|
562
587
|
point2X,
|
|
563
588
|
point2Y
|
|
564
|
-
} =
|
|
589
|
+
} = _ref51;
|
|
565
590
|
return `Line ${lineNumber} has two points, point 1 at ${point1X} comma ${point1Y} and point 2 at ${point2X} comma ${point2Y}.`;
|
|
566
591
|
},
|
|
567
|
-
srLinearSystemPoint:
|
|
592
|
+
srLinearSystemPoint: _ref52 => {
|
|
568
593
|
let {
|
|
569
594
|
lineNumber,
|
|
570
595
|
pointSequence,
|
|
571
596
|
x,
|
|
572
597
|
y
|
|
573
|
-
} =
|
|
598
|
+
} = _ref52;
|
|
574
599
|
return `Point ${pointSequence} on line ${lineNumber} at ${x} comma ${y}.`;
|
|
575
600
|
},
|
|
576
601
|
srRayGraph: "A ray on a coordinate plane.",
|
|
577
|
-
srRayPoints:
|
|
602
|
+
srRayPoints: _ref53 => {
|
|
578
603
|
let {
|
|
579
604
|
point1X,
|
|
580
605
|
point1Y,
|
|
581
606
|
point2X,
|
|
582
607
|
point2Y
|
|
583
|
-
} =
|
|
608
|
+
} = _ref53;
|
|
584
609
|
return `The endpoint is at ${point1X} comma ${point1Y} and the ray goes through point ${point2X} comma ${point2Y}.`;
|
|
585
610
|
},
|
|
586
|
-
srRayGrabHandle:
|
|
611
|
+
srRayGrabHandle: _ref54 => {
|
|
587
612
|
let {
|
|
588
613
|
point1X,
|
|
589
614
|
point1Y,
|
|
590
615
|
point2X,
|
|
591
616
|
point2Y
|
|
592
|
-
} =
|
|
617
|
+
} = _ref54;
|
|
593
618
|
return `Ray with endpoint ${point1X} comma ${point1Y} going through point ${point2X} comma ${point2Y}.`;
|
|
594
619
|
},
|
|
595
|
-
srRayEndpoint:
|
|
620
|
+
srRayEndpoint: _ref55 => {
|
|
596
621
|
let {
|
|
597
622
|
x,
|
|
598
623
|
y
|
|
599
|
-
} =
|
|
624
|
+
} = _ref55;
|
|
600
625
|
return `Endpoint at ${x} comma ${y}.`;
|
|
601
626
|
},
|
|
602
|
-
srRayTerminalPoint:
|
|
627
|
+
srRayTerminalPoint: _ref56 => {
|
|
603
628
|
let {
|
|
604
629
|
x,
|
|
605
630
|
y
|
|
606
|
-
} =
|
|
631
|
+
} = _ref56;
|
|
607
632
|
return `Through point at ${x} comma ${y}.`;
|
|
608
633
|
},
|
|
609
634
|
srQuadraticGraph: "A parabola on a 4-quadrant coordinate plane.",
|
|
@@ -612,55 +637,55 @@ const mockStrings = {
|
|
|
612
637
|
srQuadraticGraphVertexOrigin: "Vertex is at the origin.",
|
|
613
638
|
srQuadraticGraphVertexXAxis: "Vertex is on the X-axis.",
|
|
614
639
|
srQuadraticGraphVertexYAxis: "Vertex is on the Y-axis.",
|
|
615
|
-
srQuadraticGraphVertexQuadrant:
|
|
640
|
+
srQuadraticGraphVertexQuadrant: _ref57 => {
|
|
616
641
|
let {
|
|
617
642
|
quadrant
|
|
618
|
-
} =
|
|
643
|
+
} = _ref57;
|
|
619
644
|
return `Vertex is in quadrant ${quadrant}.`;
|
|
620
645
|
},
|
|
621
|
-
srQuadraticTwoXIntercepts:
|
|
646
|
+
srQuadraticTwoXIntercepts: _ref58 => {
|
|
622
647
|
let {
|
|
623
648
|
intercept1,
|
|
624
649
|
intercept2
|
|
625
|
-
} =
|
|
650
|
+
} = _ref58;
|
|
626
651
|
return `The X-intercepts are at ${intercept1} comma 0 and ${intercept2} comma 0.`;
|
|
627
652
|
},
|
|
628
|
-
srQuadraticOneXIntercept:
|
|
653
|
+
srQuadraticOneXIntercept: _ref59 => {
|
|
629
654
|
let {
|
|
630
655
|
intercept
|
|
631
|
-
} =
|
|
656
|
+
} = _ref59;
|
|
632
657
|
return `The X-intercept is at ${intercept} comma 0.`;
|
|
633
658
|
},
|
|
634
|
-
srQuadraticYIntercept:
|
|
659
|
+
srQuadraticYIntercept: _ref60 => {
|
|
635
660
|
let {
|
|
636
661
|
intercept
|
|
637
|
-
} =
|
|
662
|
+
} = _ref60;
|
|
638
663
|
return `The Y-intercept is at 0 comma ${intercept}.`;
|
|
639
664
|
},
|
|
640
|
-
srQuadraticPointOrigin:
|
|
665
|
+
srQuadraticPointOrigin: _ref61 => {
|
|
641
666
|
let {
|
|
642
667
|
pointNumber
|
|
643
|
-
} =
|
|
668
|
+
} = _ref61;
|
|
644
669
|
return `Point ${pointNumber} on parabola at the origin.`;
|
|
645
670
|
},
|
|
646
|
-
srQuadraticPointAxis:
|
|
671
|
+
srQuadraticPointAxis: _ref62 => {
|
|
647
672
|
let {
|
|
648
673
|
pointNumber,
|
|
649
674
|
x,
|
|
650
675
|
y
|
|
651
|
-
} =
|
|
676
|
+
} = _ref62;
|
|
652
677
|
return `Point ${pointNumber} on parabola at ${x} comma ${y}.`;
|
|
653
678
|
},
|
|
654
|
-
srQuadraticPointQuadrant:
|
|
679
|
+
srQuadraticPointQuadrant: _ref63 => {
|
|
655
680
|
let {
|
|
656
681
|
pointNumber,
|
|
657
682
|
x,
|
|
658
683
|
y,
|
|
659
684
|
quadrant
|
|
660
|
-
} =
|
|
685
|
+
} = _ref63;
|
|
661
686
|
return `Point ${pointNumber} on parabola in quadrant ${quadrant} at ${x} comma ${y}.`;
|
|
662
687
|
},
|
|
663
|
-
srQuadraticInteractiveElements:
|
|
688
|
+
srQuadraticInteractiveElements: _ref64 => {
|
|
664
689
|
let {
|
|
665
690
|
point1X,
|
|
666
691
|
point1Y,
|
|
@@ -668,8 +693,84 @@ const mockStrings = {
|
|
|
668
693
|
point2Y,
|
|
669
694
|
point3X,
|
|
670
695
|
point3Y
|
|
671
|
-
} =
|
|
696
|
+
} = _ref64;
|
|
672
697
|
return `Parabola with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and ${point3X} comma ${point3Y}.`;
|
|
698
|
+
},
|
|
699
|
+
srPolygonGraph: "A polygon.",
|
|
700
|
+
srPolygonGraphCoordinatePlane: "A polygon on a coordinate plane.",
|
|
701
|
+
srPolygonGraphPointsNum: _ref65 => {
|
|
702
|
+
let {
|
|
703
|
+
num
|
|
704
|
+
} = _ref65;
|
|
705
|
+
return `The polygon has ${num} points.`;
|
|
706
|
+
},
|
|
707
|
+
srPolygonGraphPointsOne: "The polygon has 1 point.",
|
|
708
|
+
srPolygonElementsNum: _ref66 => {
|
|
709
|
+
let {
|
|
710
|
+
num
|
|
711
|
+
} = _ref66;
|
|
712
|
+
return `A polygon with ${num} points.`;
|
|
713
|
+
},
|
|
714
|
+
srPolygonElementsOne: "A polygon with 1 point.",
|
|
715
|
+
srPolygonPointAngleApprox: _ref67 => {
|
|
716
|
+
let {
|
|
717
|
+
angle
|
|
718
|
+
} = _ref67;
|
|
719
|
+
return `Angle approximately equal to ${angle} degrees.`;
|
|
720
|
+
},
|
|
721
|
+
srPolygonPointAngle: _ref68 => {
|
|
722
|
+
let {
|
|
723
|
+
angle
|
|
724
|
+
} = _ref68;
|
|
725
|
+
return `Angle equal to ${angle} degrees.`;
|
|
726
|
+
},
|
|
727
|
+
srPolygonSideLength: _ref69 => {
|
|
728
|
+
let {
|
|
729
|
+
pointNum,
|
|
730
|
+
length
|
|
731
|
+
} = _ref69;
|
|
732
|
+
return `A line segment, length equal to ${length} units, connects to point ${pointNum}.`;
|
|
733
|
+
},
|
|
734
|
+
srPolygonSideLengthApprox: _ref70 => {
|
|
735
|
+
let {
|
|
736
|
+
pointNum,
|
|
737
|
+
length
|
|
738
|
+
} = _ref70;
|
|
739
|
+
return `A line segment, length approximately equal to ${length} units, connects to point ${pointNum}.`;
|
|
740
|
+
},
|
|
741
|
+
srUnlimitedPolygonEmpty: "An empty coordinate plane.",
|
|
742
|
+
srSinusoidGraph: "A sinusoid function on a coordinate plane.",
|
|
743
|
+
srSinusoidRootPoint: _ref71 => {
|
|
744
|
+
let {
|
|
745
|
+
x,
|
|
746
|
+
y
|
|
747
|
+
} = _ref71;
|
|
748
|
+
return `Midline intersection at ${x} comma ${y}.`;
|
|
749
|
+
},
|
|
750
|
+
srSinusoidPeakPoint: _ref72 => {
|
|
751
|
+
let {
|
|
752
|
+
x,
|
|
753
|
+
y
|
|
754
|
+
} = _ref72;
|
|
755
|
+
return `Extremum point at ${x} comma ${y}.`;
|
|
756
|
+
},
|
|
757
|
+
srSinusoidDescription: _ref73 => {
|
|
758
|
+
let {
|
|
759
|
+
minValue,
|
|
760
|
+
maxValue,
|
|
761
|
+
cycleStart,
|
|
762
|
+
cycleEnd
|
|
763
|
+
} = _ref73;
|
|
764
|
+
return `The graph shows a wave with a minimum value of ${minValue} and a maximum value of ${maxValue}. The wave completes a full cycle from ${cycleStart} to ${cycleEnd}.`;
|
|
765
|
+
},
|
|
766
|
+
srSinusoidInteractiveElements: _ref74 => {
|
|
767
|
+
let {
|
|
768
|
+
point1X,
|
|
769
|
+
point1Y,
|
|
770
|
+
point2X,
|
|
771
|
+
point2Y
|
|
772
|
+
} = _ref74;
|
|
773
|
+
return `Sinusoid graph with midline intersection point at ${point1X} comma ${point1Y} and extremum point at ${point2X} comma ${point2Y}.`;
|
|
673
774
|
}
|
|
674
775
|
// The above strings are used for interactive graph SR descriptions.
|
|
675
776
|
};
|
|
@@ -1592,6 +1693,7 @@ const ApiOptions = {
|
|
|
1592
1693
|
},
|
|
1593
1694
|
baseElements: {
|
|
1594
1695
|
Link: props => {
|
|
1696
|
+
// eslint-disable-next-line jsx-a11y/anchor-has-content -- TODO(LEMS-2871): Address a11y error
|
|
1595
1697
|
return /*#__PURE__*/React__namespace.createElement("a", props);
|
|
1596
1698
|
}
|
|
1597
1699
|
},
|
|
@@ -1992,7 +2094,6 @@ var Expression$1 = {
|
|
|
1992
2094
|
propUpgrades: perseusCore.expressionLogic.widgetOptionsUpgrades,
|
|
1993
2095
|
// For use by the editor
|
|
1994
2096
|
isLintable: true,
|
|
1995
|
-
getPublicWidgetOptions: perseusCore.getExpressionPublicWidgetOptions,
|
|
1996
2097
|
// TODO(LEMS-2656): remove TS suppression
|
|
1997
2098
|
// @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusExpressionRubric'.
|
|
1998
2099
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
@@ -10059,7 +10160,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
10059
10160
|
|
|
10060
10161
|
/* eslint-disable @babel/no-invalid-this */
|
|
10061
10162
|
const {
|
|
10062
|
-
clockwise: clockwise$
|
|
10163
|
+
clockwise: clockwise$2,
|
|
10063
10164
|
reverseVector: reverseVector$1
|
|
10064
10165
|
} = kmath.geometry;
|
|
10065
10166
|
function scaledDistanceFromAngle(angle) {
|
|
@@ -11297,7 +11398,7 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11297
11398
|
// Temporary objects
|
|
11298
11399
|
___default["default"].invoke(polygon.temp, "remove");
|
|
11299
11400
|
polygon.temp = [];
|
|
11300
|
-
const isClockwise = clockwise$
|
|
11401
|
+
const isClockwise = clockwise$2(polygon.coords);
|
|
11301
11402
|
|
|
11302
11403
|
// Update angle labels
|
|
11303
11404
|
if (polygon.angleLabels.length || polygon.showRightAngleMarkers.length) {
|
|
@@ -14730,7 +14831,7 @@ function sum(array) {
|
|
|
14730
14831
|
return memo + arg;
|
|
14731
14832
|
}, 0);
|
|
14732
14833
|
}
|
|
14733
|
-
function clockwise$
|
|
14834
|
+
function clockwise$1(points) {
|
|
14734
14835
|
const segments = ___default["default"].zip(points, points.slice(1).concat(points.slice(0, 1)));
|
|
14735
14836
|
const areas = ___default["default"].map(segments, function (segment) {
|
|
14736
14837
|
const p1 = segment[0];
|
|
@@ -14796,7 +14897,7 @@ const draw$1 = {
|
|
|
14796
14897
|
const graphie = this.graphie;
|
|
14797
14898
|
const self = this;
|
|
14798
14899
|
const coords = ___default["default"].invoke(state.points, "coord");
|
|
14799
|
-
const isClockwise = clockwise$
|
|
14900
|
+
const isClockwise = clockwise$1(coords);
|
|
14800
14901
|
const n = coords.length;
|
|
14801
14902
|
|
|
14802
14903
|
// graphie.labelAngle and similar methods attempt to re-use the label
|
|
@@ -15968,12 +16069,17 @@ class ImageLoader extends React__namespace.Component {
|
|
|
15968
16069
|
};
|
|
15969
16070
|
}
|
|
15970
16071
|
const staticUrl = getDependencies().staticUrl;
|
|
15971
|
-
return
|
|
15972
|
-
|
|
15973
|
-
|
|
15974
|
-
|
|
15975
|
-
|
|
15976
|
-
|
|
16072
|
+
return (
|
|
16073
|
+
/*#__PURE__*/
|
|
16074
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
16075
|
+
React__namespace.createElement("img", _extends({
|
|
16076
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- TODO(LEMS-2871): Address a11y error
|
|
16077
|
+
tabIndex: 0,
|
|
16078
|
+
src: staticUrl(src),
|
|
16079
|
+
onKeyUp: onKeyUp,
|
|
16080
|
+
onKeyDown: onKeyDown
|
|
16081
|
+
}, imgProps))
|
|
16082
|
+
);
|
|
15977
16083
|
};
|
|
15978
16084
|
render() {
|
|
15979
16085
|
switch (this.state.status) {
|
|
@@ -16542,9 +16648,28 @@ class Zoomable extends React__namespace.Component {
|
|
|
16542
16648
|
if (!this._isMounted) {
|
|
16543
16649
|
return;
|
|
16544
16650
|
}
|
|
16651
|
+
|
|
16652
|
+
// When a zoomable item is contained within a collapsed parent (such as an Explanation widget),
|
|
16653
|
+
// the parent node has zero width, which causes the scale to be miscalculated.
|
|
16654
|
+
// Therefore, we check to see if the node has a valid width, and if it doesn't,
|
|
16655
|
+
// then we traverse up the DOM tree and look for the closest "perseus-renderer"
|
|
16656
|
+
// (which should have a width).
|
|
16657
|
+
// To be cautious, we also account for the possibility that we can't find any parents with a valid width
|
|
16658
|
+
// (not that they don't exist, just that we can't find them).
|
|
16659
|
+
let parentNode = this._node;
|
|
16660
|
+
let currentNode = parentNode;
|
|
16661
|
+
while (currentNode && currentNode.offsetWidth === 0) {
|
|
16662
|
+
// When traversing up the DOM tree, we need to start at the "parentElement"
|
|
16663
|
+
// because "closest()" can match the current element,
|
|
16664
|
+
// and if it does so, then we are stuck in an infinite loop.
|
|
16665
|
+
currentNode = currentNode.parentElement?.closest(".perseus-renderer") ?? null;
|
|
16666
|
+
}
|
|
16667
|
+
if (currentNode) {
|
|
16668
|
+
parentNode = currentNode;
|
|
16669
|
+
}
|
|
16545
16670
|
const parentBounds = {
|
|
16546
|
-
width:
|
|
16547
|
-
height:
|
|
16671
|
+
width: parentNode.offsetWidth,
|
|
16672
|
+
height: parentNode.offsetHeight
|
|
16548
16673
|
};
|
|
16549
16674
|
const childBounds = this.props.computeChildBounds(this._node, parentBounds);
|
|
16550
16675
|
|
|
@@ -16628,16 +16753,20 @@ class Zoomable extends React__namespace.Component {
|
|
|
16628
16753
|
WebkitTapHighlightColor: "transparent",
|
|
16629
16754
|
...transitionStyle
|
|
16630
16755
|
};
|
|
16631
|
-
return
|
|
16632
|
-
|
|
16633
|
-
|
|
16634
|
-
|
|
16635
|
-
|
|
16636
|
-
|
|
16637
|
-
|
|
16638
|
-
|
|
16639
|
-
|
|
16640
|
-
|
|
16756
|
+
return (
|
|
16757
|
+
/*#__PURE__*/
|
|
16758
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events -- TODO(LEMS-2871): Address a11y error
|
|
16759
|
+
React__namespace.createElement("span", {
|
|
16760
|
+
onClick: this.handleClick,
|
|
16761
|
+
onClickCapture: this.handleClickIfZoomed,
|
|
16762
|
+
onTouchCancelCapture: this.stopPropagationIfZoomed,
|
|
16763
|
+
onTouchEndCapture: this.stopPropagationIfZoomed,
|
|
16764
|
+
onTouchStartCapture: this.stopPropagationIfZoomed
|
|
16765
|
+
// @ts-expect-error - TS2322 - Type '{ readonly transitionProperty: string; readonly transitionDuration: string; readonly transitionTimingFunction: string; readonly display: "block"; readonly width: "100%"; readonly height: number | ... 1 more ... | undefined; readonly transform: string; readonly transformOrigin: "0 0"; readonly opacity: 0 | 1; readonl...' is not assignable to type 'CSSProperties | undefined'.
|
|
16766
|
+
,
|
|
16767
|
+
style: style
|
|
16768
|
+
}, this.props.children)
|
|
16769
|
+
);
|
|
16641
16770
|
}
|
|
16642
16771
|
}
|
|
16643
16772
|
|
|
@@ -17860,9 +17989,6 @@ const getWidget = name => {
|
|
|
17860
17989
|
const getWidgetExport = name => {
|
|
17861
17990
|
return widgets[name] ?? null;
|
|
17862
17991
|
};
|
|
17863
|
-
const getPublicWidgetOptionsFunction = name => {
|
|
17864
|
-
return widgets[name]?.getPublicWidgetOptions ?? (i => i);
|
|
17865
|
-
};
|
|
17866
17992
|
const getEditor = name => {
|
|
17867
17993
|
return ___default["default"].has(editors, name) ? editors[name] : null;
|
|
17868
17994
|
};
|
|
@@ -17999,7 +18125,6 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
17999
18125
|
replaceDeprecatedEditors: replaceDeprecatedEditors,
|
|
18000
18126
|
getWidget: getWidget,
|
|
18001
18127
|
getWidgetExport: getWidgetExport,
|
|
18002
|
-
getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
|
|
18003
18128
|
getEditor: getEditor,
|
|
18004
18129
|
getTransform: getTransform,
|
|
18005
18130
|
getVersion: getVersion,
|
|
@@ -18075,6 +18200,7 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
18075
18200
|
"perseus-widget__definition": this.props.type === "definition"
|
|
18076
18201
|
});
|
|
18077
18202
|
const type = this.props.type;
|
|
18203
|
+
const userAgent = navigator.userAgent;
|
|
18078
18204
|
const WidgetType = getWidget(type);
|
|
18079
18205
|
if (WidgetType == null) {
|
|
18080
18206
|
// This is for the good of all people!!
|
|
@@ -18085,6 +18211,11 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
18085
18211
|
className: className
|
|
18086
18212
|
});
|
|
18087
18213
|
}
|
|
18214
|
+
let subType = "null";
|
|
18215
|
+
if (type === "interactive-graph") {
|
|
18216
|
+
const props = this.state.widgetProps;
|
|
18217
|
+
subType = props.graph?.type ?? "null";
|
|
18218
|
+
}
|
|
18088
18219
|
let alignment = this.state.widgetProps.alignment;
|
|
18089
18220
|
if (alignment === "default") {
|
|
18090
18221
|
alignment = perseusCore.CoreWidgetRegistry.getDefaultAlignment(type);
|
|
@@ -18141,12 +18272,26 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
18141
18272
|
widget_type: type,
|
|
18142
18273
|
widget_id: this.props.id
|
|
18143
18274
|
},
|
|
18144
|
-
onError:
|
|
18275
|
+
onError: error => {
|
|
18276
|
+
// TODO(LEMS-2826): Remove analytics event in LEMS-2826 in favor of ti below.
|
|
18145
18277
|
analytics.onAnalyticsEvent({
|
|
18146
18278
|
type: "perseus:widget-rendering-error",
|
|
18147
18279
|
payload: {
|
|
18280
|
+
widgetSubType: subType,
|
|
18281
|
+
widgetType: type,
|
|
18282
|
+
widgetId: this.props.id,
|
|
18283
|
+
message: error.message,
|
|
18284
|
+
userAgent: userAgent
|
|
18285
|
+
}
|
|
18286
|
+
});
|
|
18287
|
+
analytics.onAnalyticsEvent({
|
|
18288
|
+
type: "perseus:widget-rendering-error:ti",
|
|
18289
|
+
payload: {
|
|
18290
|
+
widgetSubType: subType,
|
|
18148
18291
|
widgetType: type,
|
|
18149
|
-
widgetId: this.props.id
|
|
18292
|
+
widgetId: this.props.id,
|
|
18293
|
+
message: error.message,
|
|
18294
|
+
userAgent: userAgent
|
|
18150
18295
|
}
|
|
18151
18296
|
});
|
|
18152
18297
|
}
|
|
@@ -20589,7 +20734,6 @@ var NumericInput$1 = {
|
|
|
20589
20734
|
widget: NumericInput,
|
|
20590
20735
|
transform: propsTransform,
|
|
20591
20736
|
isLintable: true,
|
|
20592
|
-
getPublicWidgetOptions: perseusCore.getNumericInputPublicWidgetOptions,
|
|
20593
20737
|
// TODO(LEMS-2656): remove TS suppression
|
|
20594
20738
|
// @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusNumericInputRubric'
|
|
20595
20739
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
@@ -21396,7 +21540,10 @@ class HighlightTooltip extends React__namespace.PureComponent {
|
|
|
21396
21540
|
// using div instead of TooltipContent because
|
|
21397
21541
|
// TooltipContent wouldn't let me overwrite
|
|
21398
21542
|
// user-select and onClick
|
|
21399
|
-
const content =
|
|
21543
|
+
const content =
|
|
21544
|
+
/*#__PURE__*/
|
|
21545
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events -- TODO(LEMS-2871): Address a11y error
|
|
21546
|
+
React__namespace.createElement("div", {
|
|
21400
21547
|
className: aphrodite.css(styles$x.tooltipLabel),
|
|
21401
21548
|
onClick: this.props.onClick
|
|
21402
21549
|
}, this.props.label);
|
|
@@ -21407,14 +21554,18 @@ class HighlightTooltip extends React__namespace.PureComponent {
|
|
|
21407
21554
|
top: `calc(${Math.round(focusRect.top)}px + 0.95em)`,
|
|
21408
21555
|
height: 0
|
|
21409
21556
|
};
|
|
21410
|
-
return
|
|
21411
|
-
|
|
21412
|
-
|
|
21413
|
-
|
|
21414
|
-
|
|
21415
|
-
|
|
21416
|
-
|
|
21417
|
-
|
|
21557
|
+
return (
|
|
21558
|
+
/*#__PURE__*/
|
|
21559
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
21560
|
+
React__namespace.createElement("div", {
|
|
21561
|
+
style: style,
|
|
21562
|
+
onMouseEnter: this.props.onMouseEnter,
|
|
21563
|
+
onMouseLeave: this.props.onMouseLeave
|
|
21564
|
+
}, /*#__PURE__*/React__namespace.createElement(Tooltip__default["default"], {
|
|
21565
|
+
content: content,
|
|
21566
|
+
opened: true
|
|
21567
|
+
}, /*#__PURE__*/React__namespace.createElement("div", null)))
|
|
21568
|
+
);
|
|
21418
21569
|
}
|
|
21419
21570
|
}
|
|
21420
21571
|
const styles$x = aphrodite.StyleSheet.create({
|
|
@@ -23377,8 +23528,8 @@ const styles$s = aphrodite.StyleSheet.create({
|
|
|
23377
23528
|
crossOutLine: {
|
|
23378
23529
|
// Center the icon within the container.
|
|
23379
23530
|
position: "absolute",
|
|
23380
|
-
top: `
|
|
23381
|
-
left: `
|
|
23531
|
+
top: `2px`,
|
|
23532
|
+
left: `2px`
|
|
23382
23533
|
}
|
|
23383
23534
|
});
|
|
23384
23535
|
|
|
@@ -23405,43 +23556,6 @@ function ChoiceInner(props) {
|
|
|
23405
23556
|
}
|
|
23406
23557
|
return /*#__PURE__*/React__namespace.createElement(InlineIcon, iconMinus);
|
|
23407
23558
|
}
|
|
23408
|
-
|
|
23409
|
-
// Handle dynamic styling of the multiple choice icon. Most
|
|
23410
|
-
// MC icon styles are constant, but we do allow the caller
|
|
23411
|
-
// to specify the selected color, and thus must control styles
|
|
23412
|
-
// related to the selected state dynamically.
|
|
23413
|
-
function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, correct) {
|
|
23414
|
-
let backgroundColor;
|
|
23415
|
-
let borderColor;
|
|
23416
|
-
let color;
|
|
23417
|
-
if (!showCorrectness && pressed) {
|
|
23418
|
-
borderColor = wonderBlocksTokens.color.blue;
|
|
23419
|
-
color = wonderBlocksTokens.color.blue;
|
|
23420
|
-
backgroundColor = "transparent";
|
|
23421
|
-
} else if (checked) {
|
|
23422
|
-
const bg = showCorrectness && correct ? wonderBlocksTokens.color.green : wonderBlocksTokens.color.blue;
|
|
23423
|
-
color = white;
|
|
23424
|
-
backgroundColor = bg;
|
|
23425
|
-
borderColor = bg;
|
|
23426
|
-
} else {
|
|
23427
|
-
borderColor = wonderBlocksTokens.color.offBlack64;
|
|
23428
|
-
color = wonderBlocksTokens.color.offBlack64;
|
|
23429
|
-
}
|
|
23430
|
-
|
|
23431
|
-
// define shape
|
|
23432
|
-
let borderRadius;
|
|
23433
|
-
if (multipleSelect) {
|
|
23434
|
-
borderRadius = 3;
|
|
23435
|
-
} else {
|
|
23436
|
-
borderRadius = CHOICE_ICON_SIZE;
|
|
23437
|
-
}
|
|
23438
|
-
return {
|
|
23439
|
-
backgroundColor,
|
|
23440
|
-
borderColor,
|
|
23441
|
-
color,
|
|
23442
|
-
borderRadius
|
|
23443
|
-
};
|
|
23444
|
-
}
|
|
23445
23559
|
const ChoiceIcon = function (props) {
|
|
23446
23560
|
const {
|
|
23447
23561
|
checked,
|
|
@@ -23453,9 +23567,41 @@ const ChoiceIcon = function (props) {
|
|
|
23453
23567
|
multipleSelect,
|
|
23454
23568
|
pos,
|
|
23455
23569
|
previouslyAnswered,
|
|
23570
|
+
// Used in "review mode"/"show rationale" to show that option was previously chosen
|
|
23456
23571
|
pressed
|
|
23457
23572
|
} = props;
|
|
23458
|
-
|
|
23573
|
+
|
|
23574
|
+
// Accounts for incorrect choice still displaying as learner tries again
|
|
23575
|
+
const choiceIsChecked = checked || showCorrectness && !correct && previouslyAnswered;
|
|
23576
|
+
|
|
23577
|
+
// Core styling
|
|
23578
|
+
const choiceStyling = [styles$r.choiceBase, multipleSelect ? styles$r.multiSelectShape : styles$r.singleSelectShape, showCorrectness ? styles$r.choiceHasIcon : styles$r.choiceHasLetter, choiceIsChecked ? styles$r.choiceIsChecked : styles$r.choiceIsUnchecked];
|
|
23579
|
+
|
|
23580
|
+
// Color styling
|
|
23581
|
+
// Handle dynamic styling of the multiple choice icon. Most
|
|
23582
|
+
// MC icon styles are constant, but we do allow the caller
|
|
23583
|
+
// to specify the selected color, and thus must control styles
|
|
23584
|
+
// related to the selected state dynamically.
|
|
23585
|
+
let crossOutColor;
|
|
23586
|
+
if (showCorrectness && correct && checked) {
|
|
23587
|
+
choiceStyling.push(styles$r.choiceCorrect);
|
|
23588
|
+
crossOutColor = wonderBlocksTokens.color.green;
|
|
23589
|
+
} else if (showCorrectness && !correct && (checked || previouslyAnswered)) {
|
|
23590
|
+
choiceStyling.push(styles$r.choiceIncorrect);
|
|
23591
|
+
crossOutColor = wonderBlocksTokens.color.red;
|
|
23592
|
+
} else if (checked) {
|
|
23593
|
+
// Show filled neutral blue color (showCorrectness is false)
|
|
23594
|
+
choiceStyling.push(styles$r.choiceNeutral);
|
|
23595
|
+
crossOutColor = wonderBlocksTokens.color.blue;
|
|
23596
|
+
} else if (pressed) {
|
|
23597
|
+
// Show outlined neutral blue color (showCorrectness is false)
|
|
23598
|
+
choiceStyling.push(styles$r.activeNeutral);
|
|
23599
|
+
crossOutColor = wonderBlocksTokens.color.blue;
|
|
23600
|
+
} else {
|
|
23601
|
+
// choice is not checked
|
|
23602
|
+
choiceStyling.push(styles$r.uncheckedColors);
|
|
23603
|
+
crossOutColor = wonderBlocksTokens.color.offBlack64;
|
|
23604
|
+
}
|
|
23459
23605
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
23460
23606
|
className: aphrodite.css(styles$t.iconWrapper)
|
|
23461
23607
|
}, /*#__PURE__*/React__namespace.createElement(FocusRing, {
|
|
@@ -23463,9 +23609,8 @@ const ChoiceIcon = function (props) {
|
|
|
23463
23609
|
visible: focused || hovered,
|
|
23464
23610
|
multipleSelect: multipleSelect
|
|
23465
23611
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
23466
|
-
style: dynamicStyles,
|
|
23467
23612
|
"data-testid": "choice-icon__library-choice-icon",
|
|
23468
|
-
className: aphrodite.css(
|
|
23613
|
+
className: aphrodite.css(...choiceStyling)
|
|
23469
23614
|
// used in BaseRadio editMode to check
|
|
23470
23615
|
// if we actually clicked on the radio icon
|
|
23471
23616
|
,
|
|
@@ -23477,7 +23622,7 @@ const ChoiceIcon = function (props) {
|
|
|
23477
23622
|
showCorrectness: showCorrectness,
|
|
23478
23623
|
correct: correct
|
|
23479
23624
|
})))), crossedOut && /*#__PURE__*/React__namespace.createElement(CrossOutLine, {
|
|
23480
|
-
color:
|
|
23625
|
+
color: crossOutColor
|
|
23481
23626
|
}));
|
|
23482
23627
|
};
|
|
23483
23628
|
const styles$r = aphrodite.StyleSheet.create({
|
|
@@ -23489,8 +23634,7 @@ const styles$r = aphrodite.StyleSheet.create({
|
|
|
23489
23634
|
alignItems: "center",
|
|
23490
23635
|
justifyContent: "center"
|
|
23491
23636
|
},
|
|
23492
|
-
|
|
23493
|
-
// Make the circle
|
|
23637
|
+
choiceBase: {
|
|
23494
23638
|
width: CHOICE_ICON_SIZE,
|
|
23495
23639
|
height: CHOICE_ICON_SIZE,
|
|
23496
23640
|
boxSizing: "border-box",
|
|
@@ -23502,30 +23646,48 @@ const styles$r = aphrodite.StyleSheet.create({
|
|
|
23502
23646
|
// "bold font family" so that characters which fall back to the default
|
|
23503
23647
|
// font get bolded too.
|
|
23504
23648
|
fontWeight: "bold",
|
|
23505
|
-
fontSize: 12,
|
|
23506
23649
|
// Center the icon wrapper.
|
|
23507
23650
|
display: "flex",
|
|
23508
23651
|
alignItems: "center",
|
|
23509
|
-
justifyContent: "center"
|
|
23510
|
-
|
|
23511
|
-
|
|
23512
|
-
|
|
23513
|
-
|
|
23514
|
-
|
|
23515
|
-
lineHeight: "1px"
|
|
23516
|
-
},
|
|
23517
|
-
circleCorrect: {
|
|
23652
|
+
justifyContent: "center"
|
|
23653
|
+
},
|
|
23654
|
+
choiceHasLetter: {
|
|
23655
|
+
fontSize: 12
|
|
23656
|
+
},
|
|
23657
|
+
choiceHasIcon: {
|
|
23518
23658
|
fontSize: CHOICE_ICON_SIZE
|
|
23519
23659
|
},
|
|
23520
|
-
|
|
23521
|
-
|
|
23522
|
-
|
|
23523
|
-
|
|
23660
|
+
choiceIsChecked: {
|
|
23661
|
+
color: wonderBlocksTokens.color.white
|
|
23662
|
+
},
|
|
23663
|
+
choiceIsUnchecked: {
|
|
23664
|
+
color: wonderBlocksTokens.color.offBlack64
|
|
23665
|
+
},
|
|
23666
|
+
choiceCorrect: {
|
|
23667
|
+
backgroundColor: wonderBlocksTokens.color.green,
|
|
23668
|
+
borderColor: wonderBlocksTokens.color.green
|
|
23524
23669
|
},
|
|
23525
|
-
|
|
23670
|
+
choiceIncorrect: {
|
|
23526
23671
|
backgroundColor: wonderBlocksTokens.color.red,
|
|
23527
|
-
borderColor: wonderBlocksTokens.color.red
|
|
23528
|
-
|
|
23672
|
+
borderColor: wonderBlocksTokens.color.red
|
|
23673
|
+
},
|
|
23674
|
+
choiceNeutral: {
|
|
23675
|
+
backgroundColor: wonderBlocksTokens.color.blue,
|
|
23676
|
+
borderColor: wonderBlocksTokens.color.blue
|
|
23677
|
+
},
|
|
23678
|
+
activeNeutral: {
|
|
23679
|
+
color: wonderBlocksTokens.color.blue,
|
|
23680
|
+
borderColor: wonderBlocksTokens.color.blue,
|
|
23681
|
+
backgroundColor: "transparent"
|
|
23682
|
+
},
|
|
23683
|
+
multiSelectShape: {
|
|
23684
|
+
borderRadius: 3
|
|
23685
|
+
},
|
|
23686
|
+
singleSelectShape: {
|
|
23687
|
+
borderRadius: CHOICE_ICON_SIZE
|
|
23688
|
+
},
|
|
23689
|
+
uncheckedColors: {
|
|
23690
|
+
borderColor: wonderBlocksTokens.color.offBlack64
|
|
23529
23691
|
}
|
|
23530
23692
|
});
|
|
23531
23693
|
|
|
@@ -24120,15 +24282,19 @@ const BaseRadio = function (_ref) {
|
|
|
24120
24282
|
// content somehow? Would changing our choice of key
|
|
24121
24283
|
// somehow break something happening inside a choice's
|
|
24122
24284
|
// child Renderers, by changing when we mount/unmount?
|
|
24123
|
-
return
|
|
24124
|
-
|
|
24125
|
-
|
|
24126
|
-
|
|
24127
|
-
|
|
24128
|
-
|
|
24129
|
-
|
|
24130
|
-
|
|
24131
|
-
|
|
24285
|
+
return (
|
|
24286
|
+
/*#__PURE__*/
|
|
24287
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
24288
|
+
React__namespace.createElement("li", {
|
|
24289
|
+
key: i,
|
|
24290
|
+
ref: e => listElem = e,
|
|
24291
|
+
className: className,
|
|
24292
|
+
onClick: clickHandler,
|
|
24293
|
+
onTouchStart: labelWrap ? undefined : captureScratchpadTouchStart$1
|
|
24294
|
+
}, /*#__PURE__*/React__namespace.createElement(Element, _extends({}, elementProps, {
|
|
24295
|
+
ref: ref
|
|
24296
|
+
})))
|
|
24297
|
+
);
|
|
24132
24298
|
})));
|
|
24133
24299
|
|
|
24134
24300
|
// Allow for horizontal scrolling if content is too wide, which may be
|
|
@@ -24962,8 +25128,7 @@ var Categorizer$1 = {
|
|
|
24962
25128
|
staticTransform: editorProps => {
|
|
24963
25129
|
return ___default["default"].pick(editorProps, "items", "categories", "values", "randomizeItems");
|
|
24964
25130
|
},
|
|
24965
|
-
isLintable: true
|
|
24966
|
-
getPublicWidgetOptions: perseusCore.getCategorizerPublicWidgetOptions
|
|
25131
|
+
isLintable: true
|
|
24967
25132
|
};
|
|
24968
25133
|
|
|
24969
25134
|
/**
|
|
@@ -25173,8 +25338,7 @@ var CSProgram$1 = {
|
|
|
25173
25338
|
name: "cs-program",
|
|
25174
25339
|
displayName: "CS Program",
|
|
25175
25340
|
widget: CSProgram,
|
|
25176
|
-
hidden: true
|
|
25177
|
-
getPublicWidgetOptions: perseusCore.getCSProgramPublicWidgetOptions
|
|
25341
|
+
hidden: true
|
|
25178
25342
|
};
|
|
25179
25343
|
|
|
25180
25344
|
const getPromptJSON$l = renderProps => {
|
|
@@ -25390,8 +25554,7 @@ var Dropdown$1 = {
|
|
|
25390
25554
|
displayName: "Drop down",
|
|
25391
25555
|
accessible: true,
|
|
25392
25556
|
widget: Dropdown,
|
|
25393
|
-
transform: optionsTransform
|
|
25394
|
-
getPublicWidgetOptions: perseusCore.getDropdownPublicWidgetOptions
|
|
25557
|
+
transform: optionsTransform
|
|
25395
25558
|
};
|
|
25396
25559
|
|
|
25397
25560
|
const getPromptJSON$j = renderProps => {
|
|
@@ -27177,12 +27340,16 @@ class Iframe extends React__namespace.Component {
|
|
|
27177
27340
|
// that we need. This makes it a bit safer in case some content
|
|
27178
27341
|
// creator "went wild".
|
|
27179
27342
|
// http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
|
|
27180
|
-
return
|
|
27181
|
-
|
|
27182
|
-
|
|
27183
|
-
|
|
27184
|
-
|
|
27185
|
-
|
|
27343
|
+
return (
|
|
27344
|
+
/*#__PURE__*/
|
|
27345
|
+
// eslint-disable-next-line jsx-a11y/iframe-has-title -- TODO(LEMS-2871): Address a11y error
|
|
27346
|
+
React__namespace.createElement("iframe", {
|
|
27347
|
+
sandbox: sandboxProperties,
|
|
27348
|
+
style: style,
|
|
27349
|
+
src: url,
|
|
27350
|
+
allowFullScreen: this.props.allowFullScreen
|
|
27351
|
+
})
|
|
27352
|
+
);
|
|
27186
27353
|
}
|
|
27187
27354
|
}
|
|
27188
27355
|
var Iframe$1 = {
|
|
@@ -27207,7 +27374,7 @@ const getPromptJSON$d = renderProps => {
|
|
|
27207
27374
|
|
|
27208
27375
|
const defaultBoxSize = 400;
|
|
27209
27376
|
const defaultRange = [0, 10];
|
|
27210
|
-
const defaultBackgroundImage$
|
|
27377
|
+
const defaultBackgroundImage$1 = {
|
|
27211
27378
|
url: null,
|
|
27212
27379
|
width: 0,
|
|
27213
27380
|
height: 0
|
|
@@ -27222,7 +27389,7 @@ class ImageWidget extends React__namespace.Component {
|
|
|
27222
27389
|
title: "",
|
|
27223
27390
|
range: [defaultRange, defaultRange],
|
|
27224
27391
|
box: [defaultBoxSize, defaultBoxSize],
|
|
27225
|
-
backgroundImage: defaultBackgroundImage$
|
|
27392
|
+
backgroundImage: defaultBackgroundImage$1,
|
|
27226
27393
|
labels: [],
|
|
27227
27394
|
alt: "",
|
|
27228
27395
|
caption: "",
|
|
@@ -27993,306 +28160,6 @@ var Interactive = {
|
|
|
27993
28160
|
hidden: true
|
|
27994
28161
|
};
|
|
27995
28162
|
|
|
27996
|
-
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
27997
|
-
const defaultBackgroundImage$1 = {
|
|
27998
|
-
url: null
|
|
27999
|
-
};
|
|
28000
|
-
|
|
28001
|
-
/* Style objects */
|
|
28002
|
-
const defaultInstructionsStyle = {
|
|
28003
|
-
fontStyle: "italic",
|
|
28004
|
-
fontWeight: "bold",
|
|
28005
|
-
fontSize: "32px",
|
|
28006
|
-
width: "100%",
|
|
28007
|
-
height: "100%",
|
|
28008
|
-
textAlign: "center",
|
|
28009
|
-
backgroundColor: "white",
|
|
28010
|
-
position: "absolute",
|
|
28011
|
-
zIndex: 1,
|
|
28012
|
-
transition: "opacity .25s ease-in-out"
|
|
28013
|
-
};
|
|
28014
|
-
const instructionsTextStyle = {
|
|
28015
|
-
position: "relative",
|
|
28016
|
-
top: "25%"
|
|
28017
|
-
};
|
|
28018
|
-
function numSteps$1(range, step) {
|
|
28019
|
-
return Math.floor((range[1] - range[0]) / step);
|
|
28020
|
-
}
|
|
28021
|
-
class Graph extends React__namespace.Component {
|
|
28022
|
-
graphieDivRef = /*#__PURE__*/React__namespace.createRef();
|
|
28023
|
-
protractor;
|
|
28024
|
-
ruler;
|
|
28025
|
-
_graphie;
|
|
28026
|
-
// @ts-expect-error - TS2564 - Property '_hasSetupGraphieThisUpdate' has no initializer and is not definitely assigned in the constructor.
|
|
28027
|
-
_hasSetupGraphieThisUpdate;
|
|
28028
|
-
// @ts-expect-error - TS2564 - Property '_shouldSetupGraphie' has no initializer and is not definitely assigned in the constructor.
|
|
28029
|
-
_shouldSetupGraphie;
|
|
28030
|
-
static defaultProps = {
|
|
28031
|
-
labels: ["x", "y"],
|
|
28032
|
-
range: [[-10, 10], [-10, 10]],
|
|
28033
|
-
step: [1, 1],
|
|
28034
|
-
gridStep: [1, 1],
|
|
28035
|
-
snapStep: [0.5, 0.5],
|
|
28036
|
-
markings: "graph",
|
|
28037
|
-
backgroundImage: defaultBackgroundImage$1,
|
|
28038
|
-
showProtractor: false,
|
|
28039
|
-
showRuler: false,
|
|
28040
|
-
rulerLabel: "",
|
|
28041
|
-
rulerTicks: 10,
|
|
28042
|
-
isMobile: false
|
|
28043
|
-
};
|
|
28044
|
-
componentDidMount() {
|
|
28045
|
-
this._setupGraphie(true);
|
|
28046
|
-
}
|
|
28047
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
28048
|
-
const potentialChanges = ["labels", "range", "step", "markings", "showProtractor", "showRuler", "rulerLabel", "rulerTicks", "gridStep", "snapStep"];
|
|
28049
|
-
const self = this;
|
|
28050
|
-
___default["default"].each(potentialChanges, function (prop) {
|
|
28051
|
-
if (!___default["default"].isEqual(self.props[prop], nextProps[prop])) {
|
|
28052
|
-
self._shouldSetupGraphie = true;
|
|
28053
|
-
}
|
|
28054
|
-
});
|
|
28055
|
-
}
|
|
28056
|
-
componentDidUpdate() {
|
|
28057
|
-
// Only setupGraphie once per componentDidUpdate().
|
|
28058
|
-
// See explanation in setupGraphie().
|
|
28059
|
-
this._hasSetupGraphieThisUpdate = false;
|
|
28060
|
-
if (this._shouldSetupGraphie) {
|
|
28061
|
-
this._setupGraphie(false);
|
|
28062
|
-
this._shouldSetupGraphie = false;
|
|
28063
|
-
}
|
|
28064
|
-
}
|
|
28065
|
-
|
|
28066
|
-
/* Reset the graphie canvas to its initial state
|
|
28067
|
-
*
|
|
28068
|
-
* Use when re-rendering the parent component and you need a blank
|
|
28069
|
-
* graphie.
|
|
28070
|
-
*/
|
|
28071
|
-
reset = () => {
|
|
28072
|
-
this._setupGraphie(false);
|
|
28073
|
-
};
|
|
28074
|
-
graphie = () => {
|
|
28075
|
-
return this._graphie;
|
|
28076
|
-
};
|
|
28077
|
-
pointsFromNormalized = (coordsList, noSnap) => {
|
|
28078
|
-
const self = this;
|
|
28079
|
-
return ___default["default"].map(coordsList, function (coords) {
|
|
28080
|
-
return ___default["default"].map(coords, function (coord, i) {
|
|
28081
|
-
const range = self.props.range[i];
|
|
28082
|
-
if (noSnap) {
|
|
28083
|
-
return range[0] + (range[1] - range[0]) * coord;
|
|
28084
|
-
}
|
|
28085
|
-
const step = self.props.step[i];
|
|
28086
|
-
const nSteps = numSteps$1(range, step);
|
|
28087
|
-
const tick = Math.round(coord * nSteps);
|
|
28088
|
-
return range[0] + step * tick;
|
|
28089
|
-
});
|
|
28090
|
-
});
|
|
28091
|
-
};
|
|
28092
|
-
_setupGraphie = initialMount => {
|
|
28093
|
-
// Only setupGraphie once per componentDidUpdate().
|
|
28094
|
-
// This prevents this component from rendering graphie
|
|
28095
|
-
// and then immediately re-render graphie because its
|
|
28096
|
-
// parent component asked it to. This will happen when
|
|
28097
|
-
// props on the parent and props on this component both
|
|
28098
|
-
// require graphie to be re-rendered.
|
|
28099
|
-
if (this._hasSetupGraphieThisUpdate) {
|
|
28100
|
-
return;
|
|
28101
|
-
}
|
|
28102
|
-
const graphieDiv = this.graphieDivRef.current;
|
|
28103
|
-
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'empty' does not exist on type 'JQueryStatic'.
|
|
28104
|
-
$__default["default"](graphieDiv).empty();
|
|
28105
|
-
|
|
28106
|
-
// Content creators may need to explicitly add the dollar signs so the
|
|
28107
|
-
// strings are picked up by our translation tools. However, these math
|
|
28108
|
-
// annotations are redundant because we already render all graph labels
|
|
28109
|
-
// in math mode. For example, a label value of `$\text{Dollars}$` will
|
|
28110
|
-
// be translatable, but we only want to pass the string
|
|
28111
|
-
// `\text{Dollars}` to the Graph widget.
|
|
28112
|
-
const labels = this.props.labels.map(label => Util.unescapeMathMode(label));
|
|
28113
|
-
const range = this.props.range;
|
|
28114
|
-
// @ts-expect-error - TS2345: Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLElement'.
|
|
28115
|
-
const graphie = this._graphie = GraphUtils.createGraphie(graphieDiv);
|
|
28116
|
-
const gridConfig = this._getGridConfig();
|
|
28117
|
-
// @ts-expect-error - TS2339: Property 'snap' does not exist on type 'Graphie'.
|
|
28118
|
-
graphie.snap = this.props.snapStep;
|
|
28119
|
-
if (this.props.markings === "graph") {
|
|
28120
|
-
graphie.graphInit({
|
|
28121
|
-
range: range,
|
|
28122
|
-
scale: kmath.vector.map(gridConfig, g => g.scale),
|
|
28123
|
-
axisArrows: "<->",
|
|
28124
|
-
labelFormat: function (s) {
|
|
28125
|
-
return "\\small{" + s + "}";
|
|
28126
|
-
},
|
|
28127
|
-
gridStep: this.props.gridStep,
|
|
28128
|
-
tickStep: kmath.vector.map(gridConfig, g => g.tickStep),
|
|
28129
|
-
labelStep: 1,
|
|
28130
|
-
unityLabels: kmath.vector.map(gridConfig, g => g.unityLabel),
|
|
28131
|
-
isMobile: this.props.isMobile
|
|
28132
|
-
});
|
|
28133
|
-
graphie.label([0, range[1][1]], labels[1], this.props.isMobile ? "below right" : "above");
|
|
28134
|
-
graphie.label([range[0][1], 0], labels[0], this.props.isMobile ? "above left" : "right");
|
|
28135
|
-
} else if (this.props.markings === "grid") {
|
|
28136
|
-
graphie.graphInit({
|
|
28137
|
-
range: range,
|
|
28138
|
-
scale: kmath.vector.map(gridConfig, g => g.scale),
|
|
28139
|
-
gridStep: this.props.gridStep,
|
|
28140
|
-
axes: false,
|
|
28141
|
-
ticks: false,
|
|
28142
|
-
labels: false,
|
|
28143
|
-
isMobile: this.props.isMobile
|
|
28144
|
-
});
|
|
28145
|
-
} else if (this.props.markings === "none") {
|
|
28146
|
-
graphie.init({
|
|
28147
|
-
range: range,
|
|
28148
|
-
scale: kmath.vector.map(gridConfig, g => g.scale),
|
|
28149
|
-
isMobile: this.props.isMobile
|
|
28150
|
-
});
|
|
28151
|
-
}
|
|
28152
|
-
|
|
28153
|
-
// Add instructions just before mouse layer
|
|
28154
|
-
const visible = 0.5;
|
|
28155
|
-
const invisible = 0.0;
|
|
28156
|
-
let $instructionsWrapper;
|
|
28157
|
-
if (this.props.instructions) {
|
|
28158
|
-
$instructionsWrapper = $__default["default"]("<div/>");
|
|
28159
|
-
___default["default"].each(defaultInstructionsStyle, function (value, key) {
|
|
28160
|
-
$instructionsWrapper.css(key, value);
|
|
28161
|
-
});
|
|
28162
|
-
$instructionsWrapper.css("opacity", visible);
|
|
28163
|
-
const $instructions = $__default["default"]("<span/>", {
|
|
28164
|
-
text: this.props.instructions
|
|
28165
|
-
});
|
|
28166
|
-
___default["default"].each(instructionsTextStyle, function (value, key) {
|
|
28167
|
-
$instructions.css(key, value);
|
|
28168
|
-
});
|
|
28169
|
-
$instructionsWrapper.append($instructions);
|
|
28170
|
-
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'append' does not exist on type 'JQueryStatic'.
|
|
28171
|
-
$__default["default"](graphieDiv).append($instructionsWrapper);
|
|
28172
|
-
} else {
|
|
28173
|
-
$instructionsWrapper = undefined;
|
|
28174
|
-
}
|
|
28175
|
-
|
|
28176
|
-
// Add some handlers for instructions text (if necessary)
|
|
28177
|
-
const onMouseDown = $instructionsWrapper || this.props.onMouseDown ? ___default["default"].bind(function (coord) {
|
|
28178
|
-
if ($instructionsWrapper) {
|
|
28179
|
-
$instructionsWrapper.remove();
|
|
28180
|
-
$instructionsWrapper = null;
|
|
28181
|
-
}
|
|
28182
|
-
// eslint-disable-next-line @babel/no-invalid-this
|
|
28183
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
28184
|
-
// eslint-disable-next-line @babel/no-invalid-this
|
|
28185
|
-
this.props.onMouseDown(coord);
|
|
28186
|
-
}, this) : undefined;
|
|
28187
|
-
const onMouseOver = $instructionsWrapper ? function () {
|
|
28188
|
-
$instructionsWrapper?.css("opacity", invisible);
|
|
28189
|
-
} : undefined;
|
|
28190
|
-
const onMouseOut = $instructionsWrapper ? function () {
|
|
28191
|
-
$instructionsWrapper?.css("opacity", visible);
|
|
28192
|
-
} : undefined;
|
|
28193
|
-
/* eslint-enable indent */
|
|
28194
|
-
|
|
28195
|
-
graphie.addMouseLayer({
|
|
28196
|
-
onClick: this.props.onClick,
|
|
28197
|
-
onMouseDown: onMouseDown,
|
|
28198
|
-
onMouseOver: onMouseOver,
|
|
28199
|
-
onMouseOut: onMouseOut,
|
|
28200
|
-
onMouseMove: this.props.onMouseMove,
|
|
28201
|
-
allowScratchpad: true,
|
|
28202
|
-
setDrawingAreaAvailable: this.props.setDrawingAreaAvailable
|
|
28203
|
-
});
|
|
28204
|
-
this._updateProtractor();
|
|
28205
|
-
this._updateRuler();
|
|
28206
|
-
|
|
28207
|
-
// We set this flag before jumping into our callback
|
|
28208
|
-
// to avoid recursing if our callback calls reset() itself
|
|
28209
|
-
this._hasSetupGraphieThisUpdate = true;
|
|
28210
|
-
if (!initialMount && this.props.onGraphieUpdated) {
|
|
28211
|
-
// Calling a parent callback in componentDidMount is bad and
|
|
28212
|
-
// results in hard-to-reason-about lifecycle problems (esp. with
|
|
28213
|
-
// refs), so we do it only on update and rely on the parent to
|
|
28214
|
-
// query for the graphie object on initial mount
|
|
28215
|
-
this.props.onGraphieUpdated(graphie);
|
|
28216
|
-
}
|
|
28217
|
-
};
|
|
28218
|
-
_getGridConfig = () => {
|
|
28219
|
-
return kmath.vector.map(this.props.step, (step, i) => Util.gridDimensionConfig(step, this.props.range[i], this.props.box[i], this.props.gridStep[i]));
|
|
28220
|
-
};
|
|
28221
|
-
_updateProtractor = () => {
|
|
28222
|
-
if (this.protractor) {
|
|
28223
|
-
this.protractor.remove();
|
|
28224
|
-
}
|
|
28225
|
-
if (this.props.showProtractor) {
|
|
28226
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
28227
|
-
const coord = this.pointsFromNormalized([[0.5, 0.05]])[0];
|
|
28228
|
-
this.protractor = this._graphie.protractor(coord);
|
|
28229
|
-
}
|
|
28230
|
-
};
|
|
28231
|
-
_updateRuler = () => {
|
|
28232
|
-
if (this.ruler) {
|
|
28233
|
-
this.ruler.remove();
|
|
28234
|
-
}
|
|
28235
|
-
if (this.props.showRuler) {
|
|
28236
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
28237
|
-
const coord = this.pointsFromNormalized([[0.5, 0.25]])[0];
|
|
28238
|
-
const extent = this._graphie.range[0][1] - this._graphie.range[0][0];
|
|
28239
|
-
this.ruler = this._graphie.ruler({
|
|
28240
|
-
center: coord,
|
|
28241
|
-
label: this.props.rulerLabel,
|
|
28242
|
-
pixelsPerUnit: this._graphie.scale[0],
|
|
28243
|
-
ticksPerUnit: this.props.rulerTicks,
|
|
28244
|
-
units: Math.round(0.8 * extent)
|
|
28245
|
-
});
|
|
28246
|
-
}
|
|
28247
|
-
};
|
|
28248
|
-
toJSON = () => {
|
|
28249
|
-
return ___default["default"].pick(this.props, "range", "step", "markings", "labels", "backgroundImage", "showProtractor", "showRuler", "rulerLabel", "rulerTicks", "gridStep", "snapStep");
|
|
28250
|
-
};
|
|
28251
|
-
render() {
|
|
28252
|
-
let image;
|
|
28253
|
-
const imageData = this.props.backgroundImage;
|
|
28254
|
-
if (imageData.url) {
|
|
28255
|
-
const scale = this.props.box[0] / interactiveSizes$1.defaultBoxSize;
|
|
28256
|
-
image = /*#__PURE__*/React__namespace.createElement(context$1.Consumer, null, _ref => {
|
|
28257
|
-
let {
|
|
28258
|
-
setAssetStatus
|
|
28259
|
-
} = _ref;
|
|
28260
|
-
return /*#__PURE__*/React__namespace.createElement(SvgImage
|
|
28261
|
-
// @ts-expect-error - TS2741 - Property 'alt' is missing in type '{ src: any; width: any; height: any; scale: number; responsive: false; }' but required in type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "children" | "height" | "width" | "title" | "alt" | "trackInteraction" | "preloader" | "allowFullBleed" | "extraGraphie" | "overrideAriaHidden">'.
|
|
28262
|
-
, {
|
|
28263
|
-
src: imageData.url,
|
|
28264
|
-
width: imageData.width,
|
|
28265
|
-
height: imageData.height,
|
|
28266
|
-
scale: scale,
|
|
28267
|
-
responsive: false,
|
|
28268
|
-
setAssetStatus: setAssetStatus
|
|
28269
|
-
});
|
|
28270
|
-
});
|
|
28271
|
-
} else {
|
|
28272
|
-
image = null;
|
|
28273
|
-
}
|
|
28274
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
28275
|
-
className: "graphie-container blank-background",
|
|
28276
|
-
style: {
|
|
28277
|
-
width: this.props.box[0],
|
|
28278
|
-
height: this.props.box[1]
|
|
28279
|
-
}
|
|
28280
|
-
// @ts-expect-error - TS2339 - Property 'onMouseOut' does not exist on type 'Graph'.
|
|
28281
|
-
,
|
|
28282
|
-
onMouseOut: this.onMouseOut
|
|
28283
|
-
// @ts-expect-error - TS2339 - Property 'onMouseOver' does not exist on type 'Graph'.
|
|
28284
|
-
,
|
|
28285
|
-
onMouseOver: this.onMouseOver
|
|
28286
|
-
// @ts-expect-error - TS2339 - Property 'onClick' does not exist on type 'Graph'.
|
|
28287
|
-
,
|
|
28288
|
-
onClick: this.onClick
|
|
28289
|
-
}, image, /*#__PURE__*/React__namespace.createElement("div", {
|
|
28290
|
-
className: "graphie",
|
|
28291
|
-
ref: this.graphieDivRef
|
|
28292
|
-
}));
|
|
28293
|
-
}
|
|
28294
|
-
}
|
|
28295
|
-
|
|
28296
28163
|
const getPromptJSON$b = (props, userInput) => {
|
|
28297
28164
|
return {
|
|
28298
28165
|
type: "interactive-graph",
|
|
@@ -28857,13 +28724,19 @@ function escapeIfUnescaped(character) {
|
|
|
28857
28724
|
}
|
|
28858
28725
|
}
|
|
28859
28726
|
|
|
28860
|
-
function AxisLabels() {
|
|
28727
|
+
function AxisLabels(_ref) {
|
|
28728
|
+
let {
|
|
28729
|
+
i18n
|
|
28730
|
+
} = _ref;
|
|
28861
28731
|
const {
|
|
28862
28732
|
range,
|
|
28863
28733
|
labels,
|
|
28864
28734
|
width,
|
|
28865
28735
|
height
|
|
28866
28736
|
} = useGraphConfig();
|
|
28737
|
+
const {
|
|
28738
|
+
strings
|
|
28739
|
+
} = i18n;
|
|
28867
28740
|
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
28868
28741
|
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
28869
28742
|
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
@@ -28881,6 +28754,7 @@ function AxisLabels() {
|
|
|
28881
28754
|
TeX
|
|
28882
28755
|
} = getDependencies();
|
|
28883
28756
|
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("span", {
|
|
28757
|
+
"aria-label": strings.xAxis,
|
|
28884
28758
|
style: {
|
|
28885
28759
|
position: "absolute",
|
|
28886
28760
|
left: x1,
|
|
@@ -28889,6 +28763,7 @@ function AxisLabels() {
|
|
|
28889
28763
|
transform: "translate(7px, -50%)"
|
|
28890
28764
|
}
|
|
28891
28765
|
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, replaceOutsideTeX(xAxisLabelText))), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
28766
|
+
"aria-label": strings.yAxis,
|
|
28892
28767
|
style: {
|
|
28893
28768
|
position: "absolute",
|
|
28894
28769
|
left: x2,
|
|
@@ -29697,7 +29572,6 @@ class RangeInput extends React__namespace.Component {
|
|
|
29697
29572
|
var components = /*#__PURE__*/Object.freeze({
|
|
29698
29573
|
__proto__: null,
|
|
29699
29574
|
ButtonGroup: ButtonGroup,
|
|
29700
|
-
Graph: Graph,
|
|
29701
29575
|
Graphie: Graphie,
|
|
29702
29576
|
HUD: HUD,
|
|
29703
29577
|
Icon: Icon,
|
|
@@ -29841,9 +29715,9 @@ const LockedFunction = props => {
|
|
|
29841
29715
|
const {
|
|
29842
29716
|
color,
|
|
29843
29717
|
strokeStyle,
|
|
29844
|
-
directionalAxis
|
|
29845
|
-
domain
|
|
29718
|
+
directionalAxis
|
|
29846
29719
|
} = props;
|
|
29720
|
+
const domain = props.domain ? [Number.isFinite(props.domain[0]) ? props.domain[0] : -Infinity, Number.isFinite(props.domain[1]) ? props.domain[1] : Infinity] : undefined;
|
|
29847
29721
|
const plotProps = {
|
|
29848
29722
|
color: perseusCore.lockedFigureColors[color],
|
|
29849
29723
|
style: strokeStyle,
|
|
@@ -29922,14 +29796,90 @@ function Vector(props) {
|
|
|
29922
29796
|
}));
|
|
29923
29797
|
}
|
|
29924
29798
|
|
|
29925
|
-
function srFormatNumber(a, locale) {
|
|
29799
|
+
function srFormatNumber(a, locale, maximumFractionDigits) {
|
|
29800
|
+
const piBasedNumber = getPiMultiple(a);
|
|
29801
|
+
if (piBasedNumber) {
|
|
29802
|
+
return piBasedNumber;
|
|
29803
|
+
}
|
|
29804
|
+
|
|
29926
29805
|
// adding zero here converts negative zero to positive zero.
|
|
29927
29806
|
return (0 + a).toLocaleString(locale, {
|
|
29928
|
-
maximumFractionDigits: 3,
|
|
29807
|
+
maximumFractionDigits: maximumFractionDigits ?? 3,
|
|
29929
29808
|
useGrouping: false // no thousands separators
|
|
29930
29809
|
});
|
|
29931
29810
|
}
|
|
29932
29811
|
|
|
29812
|
+
// Exported for testing
|
|
29813
|
+
function getPiMultiple(a) {
|
|
29814
|
+
// - Save some calculations by checking if this is an integer first.
|
|
29815
|
+
// If a is an integer, it's definitely not a multiple of π.
|
|
29816
|
+
// - 0 should not be presented as a multiple of π.
|
|
29817
|
+
// - If a is greater than 1e12, then it thinks that it's always a
|
|
29818
|
+
// multiple of π even when it's not, due to rounding errors.
|
|
29819
|
+
if (Number.isInteger(a) || a === 0 || a > 1e12) {
|
|
29820
|
+
return null;
|
|
29821
|
+
}
|
|
29822
|
+
|
|
29823
|
+
// Figure out the coefficient before the π.
|
|
29824
|
+
// Example: If a = π/2, then piCoefficient = 0.5
|
|
29825
|
+
// Example: If a = 2π, then piCoefficient = 2
|
|
29826
|
+
const piCoefficient = a / Math.PI;
|
|
29827
|
+
// Truncate the coefficient to account for floating point errors.
|
|
29828
|
+
// π calculations are only accurate up to precision of 1e-12.
|
|
29829
|
+
const truncatedCoefficient = parseFloat(piCoefficient.toFixed(12));
|
|
29830
|
+
|
|
29831
|
+
// If the coefficient is already an integer, then the number
|
|
29832
|
+
// is a multiple of π. Return it here.
|
|
29833
|
+
// Example: If a = 2π, then truncatedCoefficient = 2, so return "2π"
|
|
29834
|
+
if (Number.isInteger(truncatedCoefficient)) {
|
|
29835
|
+
// Return "π" rather than "1π".
|
|
29836
|
+
if (truncatedCoefficient === 1) {
|
|
29837
|
+
return "π";
|
|
29838
|
+
}
|
|
29839
|
+
|
|
29840
|
+
// Return "-π" rather than "-1π".
|
|
29841
|
+
if (truncatedCoefficient === -1) {
|
|
29842
|
+
return "-π";
|
|
29843
|
+
}
|
|
29844
|
+
return truncatedCoefficient + "π";
|
|
29845
|
+
}
|
|
29846
|
+
|
|
29847
|
+
// If the coefficient is not an integer, then we need to
|
|
29848
|
+
// check if it's a multiple of π/2, π/3, π/4, or π/6.
|
|
29849
|
+
// These are π-based values on a unit circle.
|
|
29850
|
+
const acceptableDivisors = [2, 3, 4, 6];
|
|
29851
|
+
|
|
29852
|
+
// Loop through the acceptable divisors and check if the
|
|
29853
|
+
// coefficient is a multiple of (1/divisor).
|
|
29854
|
+
// Example: If a = 5π/6, the coeff is 5/6, which is a multiple of 1/6.
|
|
29855
|
+
// So we return "5π/6".
|
|
29856
|
+
for (const divisor of acceptableDivisors) {
|
|
29857
|
+
// Check if the coefficient is a multiple of (1/divisor) by
|
|
29858
|
+
// multiplying the coefficient by the divisor and checking if
|
|
29859
|
+
// the result is an integer.
|
|
29860
|
+
// Example: If a = 5π/6, then piCoefficient = 5/6. We multiply
|
|
29861
|
+
// by 6 (divisor) to get 5, which is an integer.
|
|
29862
|
+
const coefficientNumerator = parseFloat(
|
|
29863
|
+
// π calculations are only accurate up to precision of 1e-12.
|
|
29864
|
+
(piCoefficient * divisor).toFixed(12));
|
|
29865
|
+
if (Number.isInteger(coefficientNumerator)) {
|
|
29866
|
+
// Handle the case where the coefficient numberator is just 1.
|
|
29867
|
+
// We don't want to write "π/6" as "1π/6"
|
|
29868
|
+
if (coefficientNumerator === 1) {
|
|
29869
|
+
return "π/" + divisor;
|
|
29870
|
+
}
|
|
29871
|
+
|
|
29872
|
+
// Handle the case where the coefficient numberator is just -1.
|
|
29873
|
+
// We don't want to write "π/6" as "-1π/6"
|
|
29874
|
+
if (coefficientNumerator === -1) {
|
|
29875
|
+
return "-π/" + divisor;
|
|
29876
|
+
}
|
|
29877
|
+
return coefficientNumerator + "π/" + divisor;
|
|
29878
|
+
}
|
|
29879
|
+
}
|
|
29880
|
+
return null;
|
|
29881
|
+
}
|
|
29882
|
+
|
|
29933
29883
|
/**
|
|
29934
29884
|
* Given a ray and a rectangular box, find the point where the ray intersects
|
|
29935
29885
|
* the edge of the box. Assumes the `initialPoint` is inside the box.
|
|
@@ -30104,6 +30054,95 @@ a, b, c) {
|
|
|
30104
30054
|
return [x1, x2];
|
|
30105
30055
|
}
|
|
30106
30056
|
|
|
30057
|
+
/**
|
|
30058
|
+
* Given a list of points, return the angle made by the point at index i
|
|
30059
|
+
* with the points at indices i - 1 and i + 1.
|
|
30060
|
+
*/
|
|
30061
|
+
function getAngleFromPoints(points, i) {
|
|
30062
|
+
// Make sure the index is valid.
|
|
30063
|
+
if (i < 0 || i >= points.length || !Number.isInteger(i)) {
|
|
30064
|
+
return null;
|
|
30065
|
+
}
|
|
30066
|
+
|
|
30067
|
+
// If there are fewer than 3 points, there is no angle.
|
|
30068
|
+
if (points.length < 3) {
|
|
30069
|
+
return null;
|
|
30070
|
+
}
|
|
30071
|
+
|
|
30072
|
+
// Get the points at the current index and the two adjacent points.
|
|
30073
|
+
const point = points.at(i);
|
|
30074
|
+
const pt1 = points.at(i - 1);
|
|
30075
|
+
const pt2 = points[(i + 1) % points.length];
|
|
30076
|
+
if (!point || !pt1 || !pt2) {
|
|
30077
|
+
return null;
|
|
30078
|
+
}
|
|
30079
|
+
|
|
30080
|
+
// Get the lengths of adjacent sides, making a triangle
|
|
30081
|
+
// we can use to calculate the angle.
|
|
30082
|
+
const a = mafs.vec.dist(point, pt1);
|
|
30083
|
+
const b = mafs.vec.dist(point, pt2);
|
|
30084
|
+
const c = mafs.vec.dist(pt1, pt2);
|
|
30085
|
+
|
|
30086
|
+
// Law of cosines
|
|
30087
|
+
const angle = Math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b));
|
|
30088
|
+
return angle;
|
|
30089
|
+
}
|
|
30090
|
+
function getSideLengthsFromPoints(points, i, isPolygonOpen) {
|
|
30091
|
+
if (i < 0 || i >= points.length || !Number.isInteger(i)) {
|
|
30092
|
+
return [];
|
|
30093
|
+
}
|
|
30094
|
+
if (points.length < 2) {
|
|
30095
|
+
return [];
|
|
30096
|
+
}
|
|
30097
|
+
const returnArray = [];
|
|
30098
|
+
const point = points[i];
|
|
30099
|
+
// If this point is the first point, then side 1 is the
|
|
30100
|
+
// last point in the list.
|
|
30101
|
+
const point1Index = i === 0 ? points.length - 1 : i - 1;
|
|
30102
|
+
const point1 = points[point1Index];
|
|
30103
|
+
// Make sure the previous point is not the same
|
|
30104
|
+
// as the current point.
|
|
30105
|
+
const side1 = i !== point1Index ? mafs.vec.dist(point, point1) : null;
|
|
30106
|
+
// The first side of the first point does not exist if
|
|
30107
|
+
// the polygon is open.
|
|
30108
|
+
if (side1 && !(isPolygonOpen && i === 0)) {
|
|
30109
|
+
returnArray.push({
|
|
30110
|
+
pointIndex: point1Index,
|
|
30111
|
+
sideLength: side1
|
|
30112
|
+
});
|
|
30113
|
+
}
|
|
30114
|
+
|
|
30115
|
+
// See if there is a side 2.
|
|
30116
|
+
const point2Index = (i + 1) % points.length;
|
|
30117
|
+
const point2 = points[point2Index];
|
|
30118
|
+
// Make sure that the next point is not the same as the
|
|
30119
|
+
// current point, and don't repeat the first point.
|
|
30120
|
+
const side2 = i !== point2Index && point2Index !== point1Index ? mafs.vec.dist(point, point2) : null;
|
|
30121
|
+
// The second side of the last point does not exist if
|
|
30122
|
+
// the polygon is open.
|
|
30123
|
+
if (side2 && !(isPolygonOpen && i === points.length - 1)) {
|
|
30124
|
+
returnArray.push({
|
|
30125
|
+
pointIndex: point2Index,
|
|
30126
|
+
sideLength: side2
|
|
30127
|
+
});
|
|
30128
|
+
}
|
|
30129
|
+
return returnArray;
|
|
30130
|
+
}
|
|
30131
|
+
|
|
30132
|
+
/**
|
|
30133
|
+
* Determine whether to determine the string with the
|
|
30134
|
+
* exact side length or the approximate side length.
|
|
30135
|
+
*/
|
|
30136
|
+
function getPolygonSideString(sideLength, pointIndex, strings, locale) {
|
|
30137
|
+
return Number.isInteger(sideLength) ? strings.srPolygonSideLength({
|
|
30138
|
+
pointNum: pointIndex + 1,
|
|
30139
|
+
length: `${sideLength}`
|
|
30140
|
+
}) : strings.srPolygonSideLengthApprox({
|
|
30141
|
+
pointNum: pointIndex + 1,
|
|
30142
|
+
length: srFormatNumber(sideLength, locale, 1)
|
|
30143
|
+
});
|
|
30144
|
+
}
|
|
30145
|
+
|
|
30107
30146
|
const {
|
|
30108
30147
|
calculateAngleInDegrees: calculateAngleInDegrees$2
|
|
30109
30148
|
} = kmath.angles;
|
|
@@ -30551,7 +30590,7 @@ const SvgDefs = () => /*#__PURE__*/React__default["default"].createElement("defs
|
|
|
30551
30590
|
})));
|
|
30552
30591
|
|
|
30553
30592
|
const {
|
|
30554
|
-
clockwise
|
|
30593
|
+
clockwise
|
|
30555
30594
|
} = kmath.geometry;
|
|
30556
30595
|
const {
|
|
30557
30596
|
getAngleFromVertex: getAngleFromVertex$1
|
|
@@ -30566,7 +30605,7 @@ const PolygonAngle = _ref => {
|
|
|
30566
30605
|
snapTo
|
|
30567
30606
|
} = _ref;
|
|
30568
30607
|
const [centerX, centerY] = centerPoint;
|
|
30569
|
-
const areClockwise = clockwise
|
|
30608
|
+
const areClockwise = clockwise([centerPoint, ...endPoints]);
|
|
30570
30609
|
const [[startX, startY], [endX, endY]] = areClockwise ? endPoints : endPoints.reverse();
|
|
30571
30610
|
const radius = 0.3;
|
|
30572
30611
|
const a = mafs.vec.dist(centerPoint, endPoints[0]);
|
|
@@ -30639,7 +30678,7 @@ const Angle = _ref2 => {
|
|
|
30639
30678
|
range
|
|
30640
30679
|
} = _ref2;
|
|
30641
30680
|
// Check if the points are clockwise or not, depending on whether we allow reflex angles
|
|
30642
|
-
const areClockwise = clockwise
|
|
30681
|
+
const areClockwise = clockwise([...coords, vertex]);
|
|
30643
30682
|
const shouldReverseCoords = areClockwise && !allowReflexAngles;
|
|
30644
30683
|
|
|
30645
30684
|
// Reverse the coordinates accordingly to ensure the angle is calculated correctly
|
|
@@ -31008,6 +31047,34 @@ function range(min, max) {
|
|
|
31008
31047
|
return result;
|
|
31009
31048
|
}
|
|
31010
31049
|
|
|
31050
|
+
function Hairlines(props) {
|
|
31051
|
+
const {
|
|
31052
|
+
point
|
|
31053
|
+
} = props;
|
|
31054
|
+
const {
|
|
31055
|
+
range
|
|
31056
|
+
} = useGraphConfig();
|
|
31057
|
+
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
31058
|
+
const [[x, y]] = useTransformVectorsToPixels(point);
|
|
31059
|
+
const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
|
|
31060
|
+
const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
|
|
31061
|
+
const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
|
|
31062
|
+
const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
|
|
31063
|
+
return /*#__PURE__*/React__namespace.createElement("g", null, /*#__PURE__*/React__namespace.createElement("line", {
|
|
31064
|
+
x1: verticalStartX,
|
|
31065
|
+
y1: y,
|
|
31066
|
+
x2: verticalEndX,
|
|
31067
|
+
y2: y,
|
|
31068
|
+
stroke: wonderBlocksTokens.color.blue
|
|
31069
|
+
}), /*#__PURE__*/React__namespace.createElement("line", {
|
|
31070
|
+
x1: x,
|
|
31071
|
+
y1: horizontalStartY,
|
|
31072
|
+
x2: x,
|
|
31073
|
+
y2: horizontalEndY,
|
|
31074
|
+
stroke: wonderBlocksTokens.color.blue
|
|
31075
|
+
}));
|
|
31076
|
+
}
|
|
31077
|
+
|
|
31011
31078
|
// The hitbox size of 48px by 48px is preserved from the legacy interactive
|
|
31012
31079
|
// graph.
|
|
31013
31080
|
const hitboxSizePx = 48;
|
|
@@ -31017,7 +31084,6 @@ const hitboxSizePx = 48;
|
|
|
31017
31084
|
// on an interactive graph.
|
|
31018
31085
|
const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
|
|
31019
31086
|
const {
|
|
31020
|
-
range,
|
|
31021
31087
|
markings,
|
|
31022
31088
|
showTooltips
|
|
31023
31089
|
} = useGraphConfig();
|
|
@@ -31025,6 +31091,7 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
|
|
|
31025
31091
|
point,
|
|
31026
31092
|
color = wonderBlocksTokens.color.blue,
|
|
31027
31093
|
dragging,
|
|
31094
|
+
focused,
|
|
31028
31095
|
cursor,
|
|
31029
31096
|
showFocusRing,
|
|
31030
31097
|
onClick = () => {}
|
|
@@ -31038,25 +31105,7 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
|
|
|
31038
31105
|
})?.[0] ?? "blue";
|
|
31039
31106
|
const pointClasses = classNames("movable-point", dragging && "movable-point--dragging", showFocusRing && "movable-point--focus");
|
|
31040
31107
|
const [[x, y]] = useTransformVectorsToPixels(point);
|
|
31041
|
-
const
|
|
31042
|
-
const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
|
|
31043
|
-
const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
|
|
31044
|
-
const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
|
|
31045
|
-
const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
|
|
31046
|
-
const showHairlines = dragging && markings !== "none";
|
|
31047
|
-
const hairlines = /*#__PURE__*/React__namespace.createElement("g", null, /*#__PURE__*/React__namespace.createElement("line", {
|
|
31048
|
-
x1: verticalStartX,
|
|
31049
|
-
y1: y,
|
|
31050
|
-
x2: verticalEndX,
|
|
31051
|
-
y2: y,
|
|
31052
|
-
stroke: color
|
|
31053
|
-
}), /*#__PURE__*/React__namespace.createElement("line", {
|
|
31054
|
-
x1: x,
|
|
31055
|
-
y1: horizontalStartY,
|
|
31056
|
-
x2: x,
|
|
31057
|
-
y2: horizontalEndY,
|
|
31058
|
-
stroke: color
|
|
31059
|
-
}));
|
|
31108
|
+
const showHairlines = (dragging || focused) && markings !== "none";
|
|
31060
31109
|
const svgForPoint = /*#__PURE__*/React__namespace.createElement("g", {
|
|
31061
31110
|
ref: hitboxRef,
|
|
31062
31111
|
className: pointClasses,
|
|
@@ -31092,7 +31141,9 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
|
|
|
31092
31141
|
},
|
|
31093
31142
|
"data-testid": "movable-point__center"
|
|
31094
31143
|
}));
|
|
31095
|
-
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, showHairlines &&
|
|
31144
|
+
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, showHairlines && /*#__PURE__*/React__namespace.createElement(Hairlines, {
|
|
31145
|
+
point: point
|
|
31146
|
+
}), showTooltips ? /*#__PURE__*/React__namespace.createElement(Tooltip__default["default"], {
|
|
31096
31147
|
autoUpdate: true,
|
|
31097
31148
|
backgroundColor: wbColorName,
|
|
31098
31149
|
content: `(${point[X]}, ${point[Y]})`,
|
|
@@ -31188,6 +31239,7 @@ function useControlPoint(params) {
|
|
|
31188
31239
|
},
|
|
31189
31240
|
point: point,
|
|
31190
31241
|
dragging: dragging,
|
|
31242
|
+
focused: focused,
|
|
31191
31243
|
color: color,
|
|
31192
31244
|
ref: visiblePointRef,
|
|
31193
31245
|
showFocusRing: focused
|
|
@@ -31405,13 +31457,13 @@ const {
|
|
|
31405
31457
|
getClockwiseAngle: getClockwiseAngle$2,
|
|
31406
31458
|
polar: polar$1
|
|
31407
31459
|
} = kmath.angles;
|
|
31408
|
-
function renderAngleGraph(state, dispatch) {
|
|
31460
|
+
function renderAngleGraph(state, dispatch, i18n) {
|
|
31409
31461
|
return {
|
|
31410
31462
|
graph: /*#__PURE__*/React__namespace.createElement(AngleGraph, {
|
|
31411
31463
|
graphState: state,
|
|
31412
31464
|
dispatch: dispatch
|
|
31413
31465
|
}),
|
|
31414
|
-
interactiveElementsDescription:
|
|
31466
|
+
interactiveElementsDescription: getAngleGraphDescription(state, i18n)
|
|
31415
31467
|
};
|
|
31416
31468
|
}
|
|
31417
31469
|
function AngleGraph(props) {
|
|
@@ -31422,6 +31474,9 @@ function AngleGraph(props) {
|
|
|
31422
31474
|
const {
|
|
31423
31475
|
graphDimensionsInPixels
|
|
31424
31476
|
} = useGraphConfig();
|
|
31477
|
+
const i18n = usePerseusI18n();
|
|
31478
|
+
const id = React__namespace.useId();
|
|
31479
|
+
const descriptionId = id + "-description";
|
|
31425
31480
|
const {
|
|
31426
31481
|
coords,
|
|
31427
31482
|
showAngles,
|
|
@@ -31471,80 +31526,106 @@ function AngleGraph(props) {
|
|
|
31471
31526
|
showAngles: showAngles || false // Whether to show the angle or not
|
|
31472
31527
|
};
|
|
31473
31528
|
|
|
31529
|
+
// Aria strings
|
|
31474
31530
|
const {
|
|
31475
|
-
|
|
31476
|
-
|
|
31477
|
-
|
|
31478
|
-
|
|
31479
|
-
|
|
31480
|
-
|
|
31481
|
-
angleMeasure,
|
|
31482
|
-
vertexX: srFormatNumber(coords[1][X], locale),
|
|
31483
|
-
vertexY: srFormatNumber(coords[1][Y], locale),
|
|
31484
|
-
startingSideX: srFormatNumber(coords[2][X], locale),
|
|
31485
|
-
startingSideY: srFormatNumber(coords[2][Y], locale),
|
|
31486
|
-
endingSideX: srFormatNumber(coords[0][X], locale),
|
|
31487
|
-
endingSideY: srFormatNumber(coords[0][Y], locale)
|
|
31488
|
-
});
|
|
31489
|
-
const formatCoordinates = (x, y) => ({
|
|
31490
|
-
x: srFormatNumber(x, locale),
|
|
31491
|
-
y: srFormatNumber(y, locale)
|
|
31492
|
-
});
|
|
31493
|
-
const formatSideAriaLabel = (side, newPoint) => {
|
|
31494
|
-
const formattedCoordinates = formatCoordinates(newPoint[X], newPoint[Y]);
|
|
31495
|
-
const label = side === "terminal" ? strings.srAngleSideAtCoordinates({
|
|
31496
|
-
point: 1,
|
|
31497
|
-
side: "terminal side",
|
|
31498
|
-
...formattedCoordinates
|
|
31499
|
-
}) : strings.srAngleSideAtCoordinates({
|
|
31500
|
-
point: 3,
|
|
31501
|
-
side: "initial side",
|
|
31502
|
-
...formattedCoordinates
|
|
31503
|
-
});
|
|
31504
|
-
return label;
|
|
31505
|
-
};
|
|
31506
|
-
const formatVertexAriaLabel = newPoint => {
|
|
31507
|
-
const formattedVertexCoordinates = formatCoordinates(newPoint[X], newPoint[Y]);
|
|
31508
|
-
const label = showAngles ? strings.srAngleVertexAtCoordinatesWithAngleMeasure({
|
|
31509
|
-
...formattedVertexCoordinates,
|
|
31510
|
-
angleMeasure: angleMeasure
|
|
31511
|
-
}) : strings.srAngleSideAtCoordinates({
|
|
31512
|
-
point: 2,
|
|
31513
|
-
side: "vertex",
|
|
31514
|
-
...formattedVertexCoordinates
|
|
31515
|
-
});
|
|
31516
|
-
return label;
|
|
31517
|
-
};
|
|
31518
|
-
const initialSideAriaLabel = formatSideAriaLabel("initial", coords[2]);
|
|
31519
|
-
const terminalSideAriaLabel = formatSideAriaLabel("terminal", coords[0]);
|
|
31520
|
-
const vertexAriaLabel = formatVertexAriaLabel(coords[1]);
|
|
31531
|
+
srAngleGraphAriaLabel,
|
|
31532
|
+
srAngleGraphAriaDescription,
|
|
31533
|
+
srAngleStartingSide,
|
|
31534
|
+
srAngleEndingSide,
|
|
31535
|
+
srAngleVertex
|
|
31536
|
+
} = describeAngleGraph(graphState, i18n);
|
|
31521
31537
|
|
|
31522
31538
|
// Render the lines, angle, and then movable points
|
|
31523
31539
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
31524
|
-
"aria-label":
|
|
31525
|
-
"aria-describedby":
|
|
31540
|
+
"aria-label": srAngleGraphAriaLabel,
|
|
31541
|
+
"aria-describedby": descriptionId
|
|
31526
31542
|
}, svgLines, /*#__PURE__*/React__namespace.createElement(Angle, angleParams), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
31527
31543
|
point: coords[1],
|
|
31528
31544
|
sequenceNumber: 1,
|
|
31529
31545
|
constrain: p => p,
|
|
31530
31546
|
onMove: destination => dispatch(actions.angle.movePoint(1, destination)),
|
|
31531
|
-
ariaLabel:
|
|
31547
|
+
ariaLabel: srAngleVertex
|
|
31532
31548
|
}), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
31533
31549
|
point: coords[0],
|
|
31534
31550
|
sequenceNumber: 2,
|
|
31535
31551
|
constrain: getAngleSideConstraint(coords[0], coords[1], snapDegrees || 1),
|
|
31536
31552
|
onMove: destination => dispatch(actions.angle.movePoint(0, destination)),
|
|
31537
|
-
ariaLabel:
|
|
31553
|
+
ariaLabel: srAngleEndingSide
|
|
31538
31554
|
}), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
31539
31555
|
point: coords[2],
|
|
31540
31556
|
sequenceNumber: 3,
|
|
31541
31557
|
constrain: getAngleSideConstraint(coords[2], coords[1], snapDegrees || 1),
|
|
31542
31558
|
onMove: destination => dispatch(actions.angle.movePoint(2, destination)),
|
|
31543
|
-
ariaLabel:
|
|
31559
|
+
ariaLabel: srAngleStartingSide
|
|
31544
31560
|
}), /*#__PURE__*/React__namespace.createElement("g", {
|
|
31545
|
-
id:
|
|
31561
|
+
id: descriptionId,
|
|
31546
31562
|
style: a11y.srOnly
|
|
31547
|
-
},
|
|
31563
|
+
}, srAngleGraphAriaDescription));
|
|
31564
|
+
}
|
|
31565
|
+
function getAngleGraphDescription(state, i18n) {
|
|
31566
|
+
const {
|
|
31567
|
+
strings,
|
|
31568
|
+
locale
|
|
31569
|
+
} = i18n;
|
|
31570
|
+
const {
|
|
31571
|
+
coords
|
|
31572
|
+
} = state;
|
|
31573
|
+
return strings.srInteractiveElements({
|
|
31574
|
+
elements: strings.srAngleInteractiveElements({
|
|
31575
|
+
vertexX: srFormatNumber(coords[1][X], locale),
|
|
31576
|
+
vertexY: srFormatNumber(coords[1][Y], locale),
|
|
31577
|
+
startingSideX: srFormatNumber(coords[2][X], locale),
|
|
31578
|
+
startingSideY: srFormatNumber(coords[2][Y], locale),
|
|
31579
|
+
endingSideX: srFormatNumber(coords[0][X], locale),
|
|
31580
|
+
endingSideY: srFormatNumber(coords[0][Y], locale)
|
|
31581
|
+
})
|
|
31582
|
+
});
|
|
31583
|
+
}
|
|
31584
|
+
function describeAngleGraph(state, i18n) {
|
|
31585
|
+
const {
|
|
31586
|
+
strings,
|
|
31587
|
+
locale
|
|
31588
|
+
} = i18n;
|
|
31589
|
+
const {
|
|
31590
|
+
coords,
|
|
31591
|
+
allowReflexAngles,
|
|
31592
|
+
showAngles
|
|
31593
|
+
} = state;
|
|
31594
|
+
const [endingSide, vertex, startingSide] = coords;
|
|
31595
|
+
const angleMeasure = srFormatNumber(getClockwiseAngle$2(coords, allowReflexAngles), locale);
|
|
31596
|
+
const srAngleGraphAriaLabel = strings.srAngleGraphAriaLabel;
|
|
31597
|
+
const srAngleGraphAriaDescription = strings.srAngleGraphAriaDescription({
|
|
31598
|
+
angleMeasure,
|
|
31599
|
+
vertexX: srFormatNumber(vertex[X], locale),
|
|
31600
|
+
vertexY: srFormatNumber(vertex[Y], locale),
|
|
31601
|
+
startingSideX: srFormatNumber(startingSide[X], locale),
|
|
31602
|
+
startingSideY: srFormatNumber(startingSide[Y], locale),
|
|
31603
|
+
endingSideX: srFormatNumber(endingSide[X], locale),
|
|
31604
|
+
endingSideY: srFormatNumber(endingSide[Y], locale)
|
|
31605
|
+
});
|
|
31606
|
+
const srAngleStartingSide = strings.srAngleStartingSide({
|
|
31607
|
+
x: srFormatNumber(startingSide[X], locale),
|
|
31608
|
+
y: srFormatNumber(startingSide[Y], locale)
|
|
31609
|
+
});
|
|
31610
|
+
const srAngleEndingSide = strings.srAngleEndingSide({
|
|
31611
|
+
x: srFormatNumber(endingSide[X], locale),
|
|
31612
|
+
y: srFormatNumber(endingSide[Y], locale)
|
|
31613
|
+
});
|
|
31614
|
+
const srAngleVertex = showAngles ? strings.srAngleVertexWithAngleMeasure({
|
|
31615
|
+
x: srFormatNumber(vertex[X], locale),
|
|
31616
|
+
y: srFormatNumber(vertex[Y], locale),
|
|
31617
|
+
angleMeasure
|
|
31618
|
+
}) : strings.srAngleVertex({
|
|
31619
|
+
x: srFormatNumber(vertex[X], locale),
|
|
31620
|
+
y: srFormatNumber(vertex[Y], locale)
|
|
31621
|
+
});
|
|
31622
|
+
return {
|
|
31623
|
+
srAngleGraphAriaLabel,
|
|
31624
|
+
srAngleGraphAriaDescription,
|
|
31625
|
+
srAngleStartingSide,
|
|
31626
|
+
srAngleEndingSide,
|
|
31627
|
+
srAngleVertex
|
|
31628
|
+
};
|
|
31548
31629
|
}
|
|
31549
31630
|
const positiveX = [1, 0];
|
|
31550
31631
|
const negativeX = [-1, 0];
|
|
@@ -31586,9 +31667,6 @@ function getAngleSideConstraint(sidePoint, vertex, snapDegrees) {
|
|
|
31586
31667
|
};
|
|
31587
31668
|
}
|
|
31588
31669
|
|
|
31589
|
-
const {
|
|
31590
|
-
clockwise
|
|
31591
|
-
} = kmath.geometry;
|
|
31592
31670
|
function getGradableGraph(state, initialGraph) {
|
|
31593
31671
|
if (!state.hasBeenInteractedWith) {
|
|
31594
31672
|
return {
|
|
@@ -31665,17 +31743,9 @@ function getGradableGraph(state, initialGraph) {
|
|
|
31665
31743
|
};
|
|
31666
31744
|
}
|
|
31667
31745
|
if (state.type === "angle" && initialGraph.type === "angle") {
|
|
31668
|
-
// We're going to reverse the coords for scoring if the angle is clockwise and
|
|
31669
|
-
// we don't allow reflex angles. This is because the angle is largely defined
|
|
31670
|
-
// by the order of the points in the coords array, and we want to maintain
|
|
31671
|
-
// the same angle scoring with the legacy graph (which had a bug).
|
|
31672
|
-
// (LEMS-2190): When we remove the legacy graph, move this logic to the scoring function.
|
|
31673
|
-
const areClockwise = clockwise([state.coords[0], state.coords[2], state.coords[1]]);
|
|
31674
|
-
const shouldReverseCoords = areClockwise && !state.allowReflexAngles;
|
|
31675
|
-
const coords = shouldReverseCoords ? state.coords.slice().reverse() : state.coords;
|
|
31676
31746
|
return {
|
|
31677
31747
|
...initialGraph,
|
|
31678
|
-
coords,
|
|
31748
|
+
coords: state.coords,
|
|
31679
31749
|
allowReflexAngles: state.allowReflexAngles
|
|
31680
31750
|
};
|
|
31681
31751
|
}
|
|
@@ -31699,17 +31769,16 @@ function getRadius(graph) {
|
|
|
31699
31769
|
return Math.sqrt(Math.pow(edgeX - centerX, 2) + Math.pow(edgeY - centerY, 2));
|
|
31700
31770
|
}
|
|
31701
31771
|
|
|
31702
|
-
function renderCircleGraph(state, dispatch) {
|
|
31772
|
+
function renderCircleGraph(state, dispatch, i18n) {
|
|
31703
31773
|
return {
|
|
31704
31774
|
graph: /*#__PURE__*/React__namespace.createElement(CircleGraph, {
|
|
31705
31775
|
graphState: state,
|
|
31706
31776
|
dispatch: dispatch
|
|
31707
31777
|
}),
|
|
31708
|
-
interactiveElementsDescription:
|
|
31709
|
-
state: state
|
|
31710
|
-
})
|
|
31778
|
+
interactiveElementsDescription: getCircleGraphDescription(state, i18n)
|
|
31711
31779
|
};
|
|
31712
31780
|
}
|
|
31781
|
+
// Exported for testing
|
|
31713
31782
|
function CircleGraph(props) {
|
|
31714
31783
|
const {
|
|
31715
31784
|
dispatch,
|
|
@@ -31802,6 +31871,7 @@ function MovableCircle(props) {
|
|
|
31802
31871
|
snapStep,
|
|
31803
31872
|
disableKeyboardInteraction
|
|
31804
31873
|
} = useGraphConfig();
|
|
31874
|
+
const [focused, setFocused] = React__namespace.useState(false);
|
|
31805
31875
|
const draggableRef = React.useRef(null);
|
|
31806
31876
|
const {
|
|
31807
31877
|
dragging
|
|
@@ -31820,7 +31890,9 @@ function MovableCircle(props) {
|
|
|
31820
31890
|
ref: draggableRef,
|
|
31821
31891
|
role: "button",
|
|
31822
31892
|
tabIndex: disableKeyboardInteraction ? -1 : 0,
|
|
31823
|
-
className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}
|
|
31893
|
+
className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`,
|
|
31894
|
+
onFocus: () => setFocused(true),
|
|
31895
|
+
onBlur: () => setFocused(false)
|
|
31824
31896
|
}, /*#__PURE__*/React__namespace.createElement("ellipse", {
|
|
31825
31897
|
className: "focus-ring",
|
|
31826
31898
|
cx: centerPx[X],
|
|
@@ -31835,19 +31907,29 @@ function MovableCircle(props) {
|
|
|
31835
31907
|
rx: radiiPx[X],
|
|
31836
31908
|
ry: radiiPx[Y]
|
|
31837
31909
|
}), /*#__PURE__*/React__namespace.createElement(DragHandle, {
|
|
31838
|
-
center: center
|
|
31910
|
+
center: center,
|
|
31911
|
+
dragging: dragging,
|
|
31912
|
+
focused: focused
|
|
31839
31913
|
}));
|
|
31840
31914
|
}
|
|
31841
31915
|
const dragHandleDimensions = [24, 14];
|
|
31842
31916
|
const dragHandleDotPositions = crossProduct([-4.4, 0, 4.4], [-2.1, 2.1]);
|
|
31843
31917
|
function DragHandle(props) {
|
|
31844
31918
|
const {
|
|
31845
|
-
center
|
|
31919
|
+
center,
|
|
31920
|
+
dragging,
|
|
31921
|
+
focused
|
|
31846
31922
|
} = props;
|
|
31847
|
-
const cornerRadius = Math.min(...dragHandleDimensions) / 2;
|
|
31848
31923
|
const [centerPx] = useTransformVectorsToPixels(center);
|
|
31924
|
+
const {
|
|
31925
|
+
markings
|
|
31926
|
+
} = useGraphConfig();
|
|
31927
|
+
const cornerRadius = Math.min(...dragHandleDimensions) / 2;
|
|
31849
31928
|
const topLeft = mafs.vec.sub(centerPx, mafs.vec.scale(dragHandleDimensions, 0.5));
|
|
31850
|
-
|
|
31929
|
+
const showHairlines = (dragging || focused) && markings !== "none";
|
|
31930
|
+
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, showHairlines && /*#__PURE__*/React__namespace.createElement(Hairlines, {
|
|
31931
|
+
point: center
|
|
31932
|
+
}), /*#__PURE__*/React__namespace.createElement("rect", {
|
|
31851
31933
|
className: "movable-circle-handle",
|
|
31852
31934
|
x: topLeft[X],
|
|
31853
31935
|
y: topLeft[Y],
|
|
@@ -31874,14 +31956,7 @@ function crossProduct(as, bs) {
|
|
|
31874
31956
|
}
|
|
31875
31957
|
return result;
|
|
31876
31958
|
}
|
|
31877
|
-
function
|
|
31878
|
-
let {
|
|
31879
|
-
state
|
|
31880
|
-
} = _ref;
|
|
31881
|
-
// The reason that CircleGraphDescription is a component (rather than a
|
|
31882
|
-
// function that returns a string) is because it needs to use a
|
|
31883
|
-
// hook: `usePerseusI18n`.
|
|
31884
|
-
const i18n = usePerseusI18n();
|
|
31959
|
+
function getCircleGraphDescription(state, i18n) {
|
|
31885
31960
|
const strings = describeCircleGraph(state, i18n);
|
|
31886
31961
|
return strings.srCircleInteractiveElement;
|
|
31887
31962
|
}
|
|
@@ -31934,15 +32009,13 @@ function describeCircleGraph(state, i18n) {
|
|
|
31934
32009
|
};
|
|
31935
32010
|
}
|
|
31936
32011
|
|
|
31937
|
-
function renderLinearGraph(state, dispatch) {
|
|
32012
|
+
function renderLinearGraph(state, dispatch, i18n) {
|
|
31938
32013
|
return {
|
|
31939
32014
|
graph: /*#__PURE__*/React__namespace.createElement(LinearGraph, {
|
|
31940
32015
|
graphState: state,
|
|
31941
32016
|
dispatch: dispatch
|
|
31942
32017
|
}),
|
|
31943
|
-
interactiveElementsDescription:
|
|
31944
|
-
state: state
|
|
31945
|
-
})
|
|
32018
|
+
interactiveElementsDescription: getLinearGraphDescription(state, i18n)
|
|
31946
32019
|
};
|
|
31947
32020
|
}
|
|
31948
32021
|
const LinearGraph = (props, key) => {
|
|
@@ -32007,14 +32080,7 @@ const LinearGraph = (props, key) => {
|
|
|
32007
32080
|
style: a11y.srOnly
|
|
32008
32081
|
}, slopeString));
|
|
32009
32082
|
};
|
|
32010
|
-
function
|
|
32011
|
-
let {
|
|
32012
|
-
state
|
|
32013
|
-
} = _ref;
|
|
32014
|
-
// The reason that LinearGraphDescription is a component (rather than a
|
|
32015
|
-
// function that returns a string) is because it needs to use a
|
|
32016
|
-
// hook: `usePerseusI18n`.
|
|
32017
|
-
const i18n = usePerseusI18n();
|
|
32083
|
+
function getLinearGraphDescription(state, i18n) {
|
|
32018
32084
|
const strings = describeLinearGraph(state, i18n);
|
|
32019
32085
|
return strings.srLinearInteractiveElement;
|
|
32020
32086
|
}
|
|
@@ -32058,15 +32124,13 @@ function describeLinearGraph(state, i18n) {
|
|
|
32058
32124
|
};
|
|
32059
32125
|
}
|
|
32060
32126
|
|
|
32061
|
-
function renderLinearSystemGraph(state, dispatch) {
|
|
32127
|
+
function renderLinearSystemGraph(state, dispatch, i18n) {
|
|
32062
32128
|
return {
|
|
32063
32129
|
graph: /*#__PURE__*/React__namespace.createElement(LinearSystemGraph, {
|
|
32064
32130
|
graphState: state,
|
|
32065
32131
|
dispatch: dispatch
|
|
32066
32132
|
}),
|
|
32067
|
-
interactiveElementsDescription:
|
|
32068
|
-
state: state
|
|
32069
|
-
})
|
|
32133
|
+
interactiveElementsDescription: getLinearSystemGraphDescription(state, i18n)
|
|
32070
32134
|
};
|
|
32071
32135
|
}
|
|
32072
32136
|
const LinearSystemGraph = props => {
|
|
@@ -32159,19 +32223,9 @@ const LinearSystemGraph = props => {
|
|
|
32159
32223
|
}, slopeDescription));
|
|
32160
32224
|
}));
|
|
32161
32225
|
};
|
|
32162
|
-
function LinearSystemGraphDescription(_ref3) {
|
|
32163
|
-
let {
|
|
32164
|
-
state
|
|
32165
|
-
} = _ref3;
|
|
32166
|
-
// The reason that LinearSystemGraphDescription is a component (rather
|
|
32167
|
-
// than a function that returns a string) is because it needs to use a
|
|
32168
|
-
// hook: `usePerseusI18n`.
|
|
32169
|
-
const i18n = usePerseusI18n();
|
|
32170
|
-
return describeLinearSystemGraph(state, i18n);
|
|
32171
|
-
}
|
|
32172
32226
|
|
|
32173
32227
|
// Exported for testing
|
|
32174
|
-
function
|
|
32228
|
+
function getLinearSystemGraphDescription(state, i18n) {
|
|
32175
32229
|
const {
|
|
32176
32230
|
strings,
|
|
32177
32231
|
locale
|
|
@@ -32197,15 +32251,13 @@ function describeLinearSystemGraph(state, i18n) {
|
|
|
32197
32251
|
});
|
|
32198
32252
|
}
|
|
32199
32253
|
|
|
32200
|
-
function renderPointGraph(state, dispatch) {
|
|
32254
|
+
function renderPointGraph(state, dispatch, i18n) {
|
|
32201
32255
|
return {
|
|
32202
32256
|
graph: /*#__PURE__*/React__namespace.createElement(PointGraph, {
|
|
32203
32257
|
graphState: state,
|
|
32204
32258
|
dispatch: dispatch
|
|
32205
32259
|
}),
|
|
32206
|
-
interactiveElementsDescription:
|
|
32207
|
-
state: state
|
|
32208
|
-
})
|
|
32260
|
+
interactiveElementsDescription: getPointGraphDescription(state, i18n)
|
|
32209
32261
|
};
|
|
32210
32262
|
}
|
|
32211
32263
|
function PointGraph(props) {
|
|
@@ -32227,7 +32279,9 @@ function PointGraph(props) {
|
|
|
32227
32279
|
if (focusedIndex != null) {
|
|
32228
32280
|
pointsRef.current[focusedIndex]?.focus();
|
|
32229
32281
|
}
|
|
32230
|
-
}, [props.graphState.focusedPointIndex,
|
|
32282
|
+
}, [props.graphState.focusedPointIndex,
|
|
32283
|
+
// Ensure that we re-focus if a point is deleted.
|
|
32284
|
+
props.graphState.coords.length, pointsRef]);
|
|
32231
32285
|
const statefulProps = {
|
|
32232
32286
|
...props,
|
|
32233
32287
|
graphConfig,
|
|
@@ -32299,18 +32353,9 @@ function UnlimitedPointGraph(statefulProps) {
|
|
|
32299
32353
|
}
|
|
32300
32354
|
})));
|
|
32301
32355
|
}
|
|
32302
|
-
function PointGraphDescription(_ref) {
|
|
32303
|
-
let {
|
|
32304
|
-
state
|
|
32305
|
-
} = _ref;
|
|
32306
|
-
// PointGraphDescription needs to `usePerseusI18n`, so it has to be a
|
|
32307
|
-
// component rather than a function that simply returns a string.
|
|
32308
|
-
const i18n = usePerseusI18n();
|
|
32309
|
-
return describePointGraph(state, i18n);
|
|
32310
|
-
}
|
|
32311
32356
|
|
|
32312
32357
|
// Exported for testing
|
|
32313
|
-
function
|
|
32358
|
+
function getPointGraphDescription(state, i18n) {
|
|
32314
32359
|
const {
|
|
32315
32360
|
strings,
|
|
32316
32361
|
locale
|
|
@@ -32318,8 +32363,8 @@ function describePointGraph(state, i18n) {
|
|
|
32318
32363
|
if (state.coords.length === 0) {
|
|
32319
32364
|
return strings.srNoInteractiveElements;
|
|
32320
32365
|
}
|
|
32321
|
-
const pointDescriptions = state.coords.map((
|
|
32322
|
-
let [x, y] =
|
|
32366
|
+
const pointDescriptions = state.coords.map((_ref, index) => {
|
|
32367
|
+
let [x, y] = _ref;
|
|
32323
32368
|
return strings.srPointAtCoordinates({
|
|
32324
32369
|
num: index + 1,
|
|
32325
32370
|
x: srFormatNumber(x, locale),
|
|
@@ -32327,17 +32372,20 @@ function describePointGraph(state, i18n) {
|
|
|
32327
32372
|
});
|
|
32328
32373
|
});
|
|
32329
32374
|
return strings.srInteractiveElements({
|
|
32330
|
-
elements: pointDescriptions.join("
|
|
32375
|
+
elements: pointDescriptions.join(" ")
|
|
32331
32376
|
});
|
|
32332
32377
|
}
|
|
32333
32378
|
|
|
32334
|
-
|
|
32379
|
+
const {
|
|
32380
|
+
convertRadiansToDegrees
|
|
32381
|
+
} = kmath.angles;
|
|
32382
|
+
function renderPolygonGraph(state, dispatch, i18n, markings) {
|
|
32335
32383
|
return {
|
|
32336
32384
|
graph: /*#__PURE__*/React__namespace.createElement(PolygonGraph, {
|
|
32337
32385
|
graphState: state,
|
|
32338
32386
|
dispatch: dispatch
|
|
32339
32387
|
}),
|
|
32340
|
-
interactiveElementsDescription:
|
|
32388
|
+
interactiveElementsDescription: getPolygonGraphDescription(state, i18n, markings)
|
|
32341
32389
|
};
|
|
32342
32390
|
}
|
|
32343
32391
|
const PolygonGraph = props => {
|
|
@@ -32396,7 +32444,9 @@ const PolygonGraph = props => {
|
|
|
32396
32444
|
if (focusedIndex != null) {
|
|
32397
32445
|
pointsRef.current[focusedIndex]?.focus();
|
|
32398
32446
|
}
|
|
32399
|
-
}, [props.graphState.focusedPointIndex,
|
|
32447
|
+
}, [props.graphState.focusedPointIndex,
|
|
32448
|
+
// Ensure that we re-focus if a point is deleted.
|
|
32449
|
+
props.graphState.coords.length, pointsRef]);
|
|
32400
32450
|
|
|
32401
32451
|
// If the unlimited polygon is rendered with 3 or more coordinates
|
|
32402
32452
|
// Close the polygon, but only on first render.
|
|
@@ -32422,7 +32472,7 @@ const PolygonGraph = props => {
|
|
|
32422
32472
|
focusVisible,
|
|
32423
32473
|
setFocusVisible
|
|
32424
32474
|
};
|
|
32425
|
-
return numSides === "unlimited" ? UnlimitedPolygonGraph
|
|
32475
|
+
return numSides === "unlimited" ? /*#__PURE__*/React__namespace.createElement(UnlimitedPolygonGraph, statefulProps) : /*#__PURE__*/React__namespace.createElement(LimitedPolygonGraph, statefulProps);
|
|
32426
32476
|
};
|
|
32427
32477
|
const LimitedPolygonGraph = statefulProps => {
|
|
32428
32478
|
const {
|
|
@@ -32447,8 +32497,39 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32447
32497
|
const {
|
|
32448
32498
|
disableKeyboardInteraction
|
|
32449
32499
|
} = graphConfig;
|
|
32500
|
+
const {
|
|
32501
|
+
strings,
|
|
32502
|
+
locale
|
|
32503
|
+
} = usePerseusI18n();
|
|
32504
|
+
const id = React__namespace.useId();
|
|
32505
|
+
const pointsOffArray = Array(points.length).fill("off");
|
|
32506
|
+
// When moving an element, set its aria-live to "polite" and the others
|
|
32507
|
+
// to "off". Otherwise, other connected elements that move at the same
|
|
32508
|
+
// time might override the currently focused element's aria live.
|
|
32509
|
+
const [ariaLives, setAriaLives] = React__namespace.useState([
|
|
32510
|
+
// First one represents the aria-live value for the polygon itself.
|
|
32511
|
+
"off",
|
|
32512
|
+
// The rest represent the points.
|
|
32513
|
+
...pointsOffArray]);
|
|
32450
32514
|
const lines = getLines(points);
|
|
32451
|
-
|
|
32515
|
+
const polygonPointsNumId = id + "-points-num";
|
|
32516
|
+
const polygonPointsId = id + "-points";
|
|
32517
|
+
|
|
32518
|
+
// Aria label srings
|
|
32519
|
+
const {
|
|
32520
|
+
srPolygonGraph,
|
|
32521
|
+
srPolygonGraphPointsNum,
|
|
32522
|
+
srPolygonGraphPoints,
|
|
32523
|
+
srPolygonElementsNum
|
|
32524
|
+
} = describePolygonGraph(statefulProps.graphState, {
|
|
32525
|
+
strings,
|
|
32526
|
+
locale
|
|
32527
|
+
}, statefulProps.graphConfig.markings);
|
|
32528
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32529
|
+
// Outer graph minimal description
|
|
32530
|
+
"aria-label": srPolygonGraph,
|
|
32531
|
+
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
32532
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
32452
32533
|
points: [...points],
|
|
32453
32534
|
color: "var(--movable-line-stroke-color)",
|
|
32454
32535
|
svgPolygonProps: {
|
|
@@ -32474,13 +32555,17 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32474
32555
|
});
|
|
32475
32556
|
}), showSides && lines.map((_ref, i) => {
|
|
32476
32557
|
let [start, end] = _ref;
|
|
32558
|
+
// Use x and y to find the position of the label
|
|
32477
32559
|
const [x, y] = mafs.vec.midpoint(start, end);
|
|
32478
|
-
const length =
|
|
32560
|
+
const length = mafs.vec.dist(start, end);
|
|
32561
|
+
// Check if the length needs to indicate
|
|
32562
|
+
// that it's an approximation.
|
|
32563
|
+
const isApprox = !Number.isInteger(length);
|
|
32479
32564
|
return /*#__PURE__*/React__namespace.createElement(TextLabel, {
|
|
32480
32565
|
key: "side-" + i,
|
|
32481
32566
|
x: x,
|
|
32482
32567
|
y: y
|
|
32483
|
-
},
|
|
32568
|
+
}, isApprox ? `≈ ${length.toFixed(snapTo === "sides" ? 0 : 1)}` : length);
|
|
32484
32569
|
}), /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
32485
32570
|
points: [...points],
|
|
32486
32571
|
color: "transparent",
|
|
@@ -32500,27 +32585,80 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32500
32585
|
setFocusVisible(hasFocusVisible(polygonRef.current));
|
|
32501
32586
|
},
|
|
32502
32587
|
// Required for lines to darken on focus
|
|
32503
|
-
onFocus: () =>
|
|
32588
|
+
onFocus: () => {
|
|
32589
|
+
setFocusVisible(hasFocusVisible(polygonRef.current));
|
|
32590
|
+
setAriaLives(() => ["polite", ...pointsOffArray]);
|
|
32591
|
+
},
|
|
32504
32592
|
// Required for line weighting to update on blur. Without this,
|
|
32505
32593
|
// the user has to hover over the shape for it to update
|
|
32506
32594
|
onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
|
|
32507
|
-
className: "movable-polygon"
|
|
32508
|
-
|
|
32509
|
-
|
|
32510
|
-
|
|
32511
|
-
|
|
32512
|
-
point: point,
|
|
32513
|
-
sequenceNumber: i + 1,
|
|
32514
|
-
onMove: destination => {
|
|
32515
|
-
const now = Date.now();
|
|
32516
|
-
const targetFPS = 40;
|
|
32517
|
-
const moveThresholdTime = 1000 / targetFPS;
|
|
32518
|
-
if (now - lastMoveTimeRef.current > moveThresholdTime) {
|
|
32519
|
-
dispatch(actions.polygon.movePoint(i, destination));
|
|
32520
|
-
lastMoveTimeRef.current = now;
|
|
32521
|
-
}
|
|
32595
|
+
className: "movable-polygon",
|
|
32596
|
+
// Accessibility-related fields
|
|
32597
|
+
role: "button",
|
|
32598
|
+
"aria-label": srPolygonGraphPoints ? `${srPolygonElementsNum} ${srPolygonGraphPoints}` : srPolygonElementsNum,
|
|
32599
|
+
"aria-live": ariaLives[0]
|
|
32522
32600
|
}
|
|
32523
|
-
}))
|
|
32601
|
+
}), points.map((point, i) => {
|
|
32602
|
+
const angleId = `${id}-angle-${i}`;
|
|
32603
|
+
const side1Id = `${id}-point-${i}-side-1`;
|
|
32604
|
+
const side2Id = `${id}-point-${i}-side-2`;
|
|
32605
|
+
|
|
32606
|
+
// Limited polygons always have an angle at each vertex.
|
|
32607
|
+
const angle = getAngleFromPoints(points, i);
|
|
32608
|
+
const angleDegree = angle ? convertRadiansToDegrees(angle) : null;
|
|
32609
|
+
|
|
32610
|
+
// Limited polygons always two sides attached to each vertex.
|
|
32611
|
+
const sidesArray = getSideLengthsFromPoints(points, i);
|
|
32612
|
+
const {
|
|
32613
|
+
pointIndex: point1Index,
|
|
32614
|
+
sideLength: side1Length
|
|
32615
|
+
} = sidesArray[0];
|
|
32616
|
+
const {
|
|
32617
|
+
pointIndex: point2Index,
|
|
32618
|
+
sideLength: side2Length
|
|
32619
|
+
} = sidesArray[1];
|
|
32620
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32621
|
+
key: "point-" + i
|
|
32622
|
+
}, /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
32623
|
+
ariaDescribedBy: `${angleId} ${side1Id} ${side2Id}`,
|
|
32624
|
+
ariaLive: ariaLives[i + 1],
|
|
32625
|
+
constrain: constrain,
|
|
32626
|
+
point: point,
|
|
32627
|
+
sequenceNumber: i + 1,
|
|
32628
|
+
onMove: destination => {
|
|
32629
|
+
const now = Date.now();
|
|
32630
|
+
const targetFPS = 40;
|
|
32631
|
+
const moveThresholdTime = 1000 / targetFPS;
|
|
32632
|
+
if (now - lastMoveTimeRef.current > moveThresholdTime) {
|
|
32633
|
+
dispatch(actions.polygon.movePoint(i, destination));
|
|
32634
|
+
lastMoveTimeRef.current = now;
|
|
32635
|
+
}
|
|
32636
|
+
},
|
|
32637
|
+
onFocus: () => {
|
|
32638
|
+
const newPointAriaLives = [...pointsOffArray];
|
|
32639
|
+
newPointAriaLives[i] = "polite";
|
|
32640
|
+
// Whole polygon is "off", and the current
|
|
32641
|
+
// point is "polite".
|
|
32642
|
+
setAriaLives(["off", ...newPointAriaLives]);
|
|
32643
|
+
}
|
|
32644
|
+
}), angleDegree && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32645
|
+
id: angleId
|
|
32646
|
+
}, Number.isInteger(angleDegree) ? strings.srPolygonPointAngle({
|
|
32647
|
+
angle: angleDegree
|
|
32648
|
+
}) : strings.srPolygonPointAngleApprox({
|
|
32649
|
+
angle: srFormatNumber(angleDegree, locale, 1)
|
|
32650
|
+
})), /*#__PURE__*/React__namespace.createElement("g", {
|
|
32651
|
+
id: side1Id
|
|
32652
|
+
}, getPolygonSideString(side1Length, point1Index, strings, locale)), /*#__PURE__*/React__namespace.createElement("g", {
|
|
32653
|
+
id: side2Id
|
|
32654
|
+
}, getPolygonSideString(side2Length, point2Index, strings, locale)));
|
|
32655
|
+
}), /*#__PURE__*/React__namespace.createElement("g", {
|
|
32656
|
+
id: polygonPointsNumId,
|
|
32657
|
+
style: a11y.srOnly
|
|
32658
|
+
}, srPolygonGraphPointsNum), srPolygonGraphPoints && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32659
|
+
id: polygonPointsId,
|
|
32660
|
+
style: a11y.srOnly
|
|
32661
|
+
}, srPolygonGraphPoints));
|
|
32524
32662
|
};
|
|
32525
32663
|
const UnlimitedPolygonGraph = statefulProps => {
|
|
32526
32664
|
const {
|
|
@@ -32535,6 +32673,19 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
32535
32673
|
coords,
|
|
32536
32674
|
closedPolygon
|
|
32537
32675
|
} = statefulProps.graphState;
|
|
32676
|
+
const {
|
|
32677
|
+
strings,
|
|
32678
|
+
locale
|
|
32679
|
+
} = usePerseusI18n();
|
|
32680
|
+
const id = React__namespace.useId();
|
|
32681
|
+
const polygonPointsNumId = id + "-points-num";
|
|
32682
|
+
const polygonPointsId = id + "-points";
|
|
32683
|
+
|
|
32684
|
+
// When moving an element, set its aria-live to "polite" and the others
|
|
32685
|
+
// to "off". Otherwise, other connected elements that move at the same
|
|
32686
|
+
// time might override the currently focused element's aria live.
|
|
32687
|
+
const pointsOffArray = Array(points.length).fill("off");
|
|
32688
|
+
const [ariaLives, setAriaLives] = React__namespace.useState(pointsOffArray);
|
|
32538
32689
|
|
|
32539
32690
|
// If the polygon is closed, return a LimitedPolygon component.
|
|
32540
32691
|
if (closedPolygon) {
|
|
@@ -32549,7 +32700,22 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
32549
32700
|
} = graphConfig;
|
|
32550
32701
|
const widthPx = graphDimensionsInPixels[0];
|
|
32551
32702
|
const heightPx = graphDimensionsInPixels[1];
|
|
32552
|
-
|
|
32703
|
+
|
|
32704
|
+
// Aria label srings
|
|
32705
|
+
const emptyGraph = coords.length === 0;
|
|
32706
|
+
const {
|
|
32707
|
+
srPolygonGraph,
|
|
32708
|
+
srPolygonGraphPointsNum,
|
|
32709
|
+
srPolygonGraphPoints
|
|
32710
|
+
} = describePolygonGraph(statefulProps.graphState, {
|
|
32711
|
+
strings,
|
|
32712
|
+
locale
|
|
32713
|
+
}, statefulProps.graphConfig.markings);
|
|
32714
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32715
|
+
// Outer graph minimal description
|
|
32716
|
+
"aria-label": emptyGraph ? strings.srUnlimitedPolygonEmpty : srPolygonGraph,
|
|
32717
|
+
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
32718
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Polyline, {
|
|
32553
32719
|
points: [...points],
|
|
32554
32720
|
color: "var(--movable-line-stroke-color)",
|
|
32555
32721
|
svgPolylineProps: {
|
|
@@ -32574,28 +32740,71 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
32574
32740
|
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
32575
32741
|
dispatch(actions.polygon.addPoint(graphCoordinates[0]));
|
|
32576
32742
|
}
|
|
32577
|
-
}), coords.map((point, i) =>
|
|
32578
|
-
|
|
32579
|
-
|
|
32580
|
-
|
|
32581
|
-
|
|
32582
|
-
|
|
32583
|
-
|
|
32584
|
-
|
|
32585
|
-
|
|
32586
|
-
|
|
32587
|
-
|
|
32588
|
-
|
|
32589
|
-
// If the point being clicked is the first point and
|
|
32590
|
-
// there's enough non-duplicated points to form
|
|
32591
|
-
// a polygon (3 or more), close the shape before
|
|
32592
|
-
// setting focus.
|
|
32593
|
-
if (i === 0 && getArrayWithoutDuplicates(coords).length >= 3) {
|
|
32594
|
-
dispatch(actions.polygon.closePolygon());
|
|
32595
|
-
}
|
|
32596
|
-
dispatch(actions.polygon.clickPoint(i));
|
|
32743
|
+
}), coords.map((point, i) => {
|
|
32744
|
+
const angleId = `${id}-angle-${i}`;
|
|
32745
|
+
let sideIds = "";
|
|
32746
|
+
|
|
32747
|
+
// This point only has an angle if it's not the first
|
|
32748
|
+
// or last point in this open polygon.
|
|
32749
|
+
const hasAngle = i > 0 && i < coords.length - 1;
|
|
32750
|
+
const angle = hasAngle ? getAngleFromPoints(points, i) : null;
|
|
32751
|
+
const angleDegree = angle ? convertRadiansToDegrees(angle) : null;
|
|
32752
|
+
const sidesArray = getSideLengthsFromPoints(points, i, true);
|
|
32753
|
+
for (let sideIndex = 0; sideIndex < sidesArray.length; sideIndex++) {
|
|
32754
|
+
sideIds += `${id}-point-${i}-side-${sideIndex} `;
|
|
32597
32755
|
}
|
|
32598
|
-
|
|
32756
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32757
|
+
key: "point-" + i
|
|
32758
|
+
}, /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
32759
|
+
ariaDescribedBy: `${angleId} ${sideIds}`,
|
|
32760
|
+
ariaLive: ariaLives[i],
|
|
32761
|
+
point: point,
|
|
32762
|
+
sequenceNumber: i + 1,
|
|
32763
|
+
onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
|
|
32764
|
+
ref: ref => {
|
|
32765
|
+
pointsRef.current[i] = ref;
|
|
32766
|
+
},
|
|
32767
|
+
onFocus: () => {
|
|
32768
|
+
dispatch(actions.polygon.focusPoint(i));
|
|
32769
|
+
const newPointAriaLives = [...pointsOffArray];
|
|
32770
|
+
newPointAriaLives[i] = "polite";
|
|
32771
|
+
// Whole polygon is "off", and the current
|
|
32772
|
+
// point is "polite".
|
|
32773
|
+
setAriaLives([...newPointAriaLives]);
|
|
32774
|
+
},
|
|
32775
|
+
onClick: () => {
|
|
32776
|
+
// If the point being clicked is the first point and
|
|
32777
|
+
// there's enough non-duplicated points to form
|
|
32778
|
+
// a polygon (3 or more), close the shape before
|
|
32779
|
+
// setting focus.
|
|
32780
|
+
if (i === 0 && getArrayWithoutDuplicates(coords).length >= 3) {
|
|
32781
|
+
dispatch(actions.polygon.closePolygon());
|
|
32782
|
+
}
|
|
32783
|
+
dispatch(actions.polygon.clickPoint(i));
|
|
32784
|
+
}
|
|
32785
|
+
}), angleDegree && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32786
|
+
id: angleId
|
|
32787
|
+
}, Number.isInteger(angleDegree) ? strings.srPolygonPointAngle({
|
|
32788
|
+
angle: angleDegree
|
|
32789
|
+
}) : strings.srPolygonPointAngleApprox({
|
|
32790
|
+
angle: srFormatNumber(angleDegree, locale, 1)
|
|
32791
|
+
})), sidesArray.map((_ref2, j) => {
|
|
32792
|
+
let {
|
|
32793
|
+
pointIndex,
|
|
32794
|
+
sideLength
|
|
32795
|
+
} = _ref2;
|
|
32796
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32797
|
+
key: `${id}-point-${i}-side-${j}`,
|
|
32798
|
+
id: `${id}-point-${i}-side-${j}`
|
|
32799
|
+
}, getPolygonSideString(sideLength, pointIndex, strings, locale));
|
|
32800
|
+
}));
|
|
32801
|
+
}), coords.length > 0 && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32802
|
+
id: polygonPointsNumId,
|
|
32803
|
+
style: a11y.srOnly
|
|
32804
|
+
}, srPolygonGraphPointsNum), srPolygonGraphPoints && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32805
|
+
id: polygonPointsId,
|
|
32806
|
+
style: a11y.srOnly
|
|
32807
|
+
}, srPolygonGraphPoints));
|
|
32599
32808
|
};
|
|
32600
32809
|
function getLines(points) {
|
|
32601
32810
|
return points.map((point, i) => {
|
|
@@ -32614,6 +32823,54 @@ const hasFocusVisible = element => {
|
|
|
32614
32823
|
return matches(":focus");
|
|
32615
32824
|
}
|
|
32616
32825
|
};
|
|
32826
|
+
function getPolygonGraphDescription(state, i18n, markings) {
|
|
32827
|
+
const strings = describePolygonGraph(state, i18n, markings);
|
|
32828
|
+
return strings.srPolygonInteractiveElements;
|
|
32829
|
+
}
|
|
32830
|
+
function describePolygonGraph(state, i18n, markings) {
|
|
32831
|
+
const {
|
|
32832
|
+
strings,
|
|
32833
|
+
locale
|
|
32834
|
+
} = i18n;
|
|
32835
|
+
const {
|
|
32836
|
+
coords
|
|
32837
|
+
} = state;
|
|
32838
|
+
const isCoordinatePlane = markings === "axes" || markings === "graph";
|
|
32839
|
+
const hasOnePoint = coords.length === 1;
|
|
32840
|
+
|
|
32841
|
+
// Figure out graph aria label based on markings.
|
|
32842
|
+
const srPolygonGraph = isCoordinatePlane ? strings.srPolygonGraphCoordinatePlane : strings.srPolygonGraph;
|
|
32843
|
+
const srPolygonGraphPointsNum = hasOnePoint ? strings.srPolygonGraphPointsOne : strings.srPolygonGraphPointsNum({
|
|
32844
|
+
num: coords.length
|
|
32845
|
+
});
|
|
32846
|
+
let srPolygonGraphPoints;
|
|
32847
|
+
// Figure out graph description based on markings.
|
|
32848
|
+
// If the graph is not on a coordinate plane, we should not include
|
|
32849
|
+
// the points' coordinates in the description.
|
|
32850
|
+
if (isCoordinatePlane) {
|
|
32851
|
+
const pointsString = coords.map((coord, i) => {
|
|
32852
|
+
return strings.srPointAtCoordinates({
|
|
32853
|
+
num: i + 1,
|
|
32854
|
+
x: srFormatNumber(coord[0], locale),
|
|
32855
|
+
y: srFormatNumber(coord[1], locale)
|
|
32856
|
+
});
|
|
32857
|
+
});
|
|
32858
|
+
srPolygonGraphPoints = pointsString.join(" ");
|
|
32859
|
+
}
|
|
32860
|
+
const srPolygonElementsNum = hasOnePoint ? strings.srPolygonElementsOne : strings.srPolygonElementsNum({
|
|
32861
|
+
num: coords.length
|
|
32862
|
+
});
|
|
32863
|
+
const srPolygonInteractiveElements = coords.length > 0 ? strings.srInteractiveElements({
|
|
32864
|
+
elements: [srPolygonElementsNum, srPolygonGraphPoints].join(" ")
|
|
32865
|
+
}) : null;
|
|
32866
|
+
return {
|
|
32867
|
+
srPolygonGraph,
|
|
32868
|
+
srPolygonGraphPointsNum,
|
|
32869
|
+
srPolygonGraphPoints,
|
|
32870
|
+
srPolygonElementsNum,
|
|
32871
|
+
srPolygonInteractiveElements
|
|
32872
|
+
};
|
|
32873
|
+
}
|
|
32617
32874
|
|
|
32618
32875
|
function renderQuadraticGraph(state, dispatch, i18n) {
|
|
32619
32876
|
return {
|
|
@@ -32772,15 +33029,13 @@ function describeQuadraticGraph(state, i18n) {
|
|
|
32772
33029
|
};
|
|
32773
33030
|
}
|
|
32774
33031
|
|
|
32775
|
-
function renderRayGraph(state, dispatch) {
|
|
33032
|
+
function renderRayGraph(state, dispatch, i18n) {
|
|
32776
33033
|
return {
|
|
32777
33034
|
graph: /*#__PURE__*/React__namespace.createElement(RayGraph, {
|
|
32778
33035
|
graphState: state,
|
|
32779
33036
|
dispatch: dispatch
|
|
32780
33037
|
}),
|
|
32781
|
-
interactiveElementsDescription:
|
|
32782
|
-
state: state
|
|
32783
|
-
})
|
|
33038
|
+
interactiveElementsDescription: getRayGraphDescription(state, i18n)
|
|
32784
33039
|
};
|
|
32785
33040
|
}
|
|
32786
33041
|
const RayGraph = props => {
|
|
@@ -32834,14 +33089,7 @@ const RayGraph = props => {
|
|
|
32834
33089
|
style: a11y.srOnly
|
|
32835
33090
|
}, srRayPoints));
|
|
32836
33091
|
};
|
|
32837
|
-
function
|
|
32838
|
-
let {
|
|
32839
|
-
state
|
|
32840
|
-
} = _ref;
|
|
32841
|
-
// The reason that RayGraphDescription is a component (rather than a
|
|
32842
|
-
// function that returns a string) is because it needs to use a
|
|
32843
|
-
// hook: `usePerseusI18n`.
|
|
32844
|
-
const i18n = usePerseusI18n();
|
|
33092
|
+
function getRayGraphDescription(state, i18n) {
|
|
32845
33093
|
const strings = describeRayGraph(state, i18n);
|
|
32846
33094
|
return strings.srRayInteractiveElement;
|
|
32847
33095
|
}
|
|
@@ -32891,15 +33139,13 @@ function describeRayGraph(state, i18n) {
|
|
|
32891
33139
|
};
|
|
32892
33140
|
}
|
|
32893
33141
|
|
|
32894
|
-
function renderSegmentGraph(state, dispatch) {
|
|
33142
|
+
function renderSegmentGraph(state, dispatch, i18n) {
|
|
32895
33143
|
return {
|
|
32896
33144
|
graph: /*#__PURE__*/React__namespace.createElement(SegmentGraph, {
|
|
32897
33145
|
graphState: state,
|
|
32898
33146
|
dispatch: dispatch
|
|
32899
33147
|
}),
|
|
32900
|
-
interactiveElementsDescription:
|
|
32901
|
-
state: state
|
|
32902
|
-
})
|
|
33148
|
+
interactiveElementsDescription: getSegmentGraphDescription(state, i18n)
|
|
32903
33149
|
};
|
|
32904
33150
|
}
|
|
32905
33151
|
const SegmentGraph = _ref => {
|
|
@@ -32993,25 +33239,15 @@ const SegmentGraph = _ref => {
|
|
|
32993
33239
|
function getLengthOfSegment(segment) {
|
|
32994
33240
|
return kmath.point.distanceToPoint(...segment);
|
|
32995
33241
|
}
|
|
32996
|
-
function SegmentGraphDescription(_ref2) {
|
|
32997
|
-
let {
|
|
32998
|
-
state
|
|
32999
|
-
} = _ref2;
|
|
33000
|
-
// The reason that SegmentGraphDescription is a component (rather than a
|
|
33001
|
-
// function that returns a string) is because it needs to use a
|
|
33002
|
-
// hook: `usePerseusI18n`.
|
|
33003
|
-
const i18n = usePerseusI18n();
|
|
33004
|
-
return describeSegmentGraph(state, i18n);
|
|
33005
|
-
}
|
|
33006
33242
|
|
|
33007
33243
|
// Exported for testing
|
|
33008
|
-
function
|
|
33244
|
+
function getSegmentGraphDescription(state, i18n) {
|
|
33009
33245
|
const {
|
|
33010
33246
|
strings,
|
|
33011
33247
|
locale
|
|
33012
33248
|
} = i18n;
|
|
33013
|
-
const segmentDescriptions = state.coords.map((
|
|
33014
|
-
let [point1, point2] =
|
|
33249
|
+
const segmentDescriptions = state.coords.map((_ref2, index) => {
|
|
33250
|
+
let [point1, point2] = _ref2;
|
|
33015
33251
|
return strings.srMultipleSegmentIndividualLabel({
|
|
33016
33252
|
point1X: srFormatNumber(point1[X], locale),
|
|
33017
33253
|
point1Y: srFormatNumber(point1[Y], locale),
|
|
@@ -33025,13 +33261,13 @@ function describeSegmentGraph(state, i18n) {
|
|
|
33025
33261
|
});
|
|
33026
33262
|
}
|
|
33027
33263
|
|
|
33028
|
-
function renderSinusoidGraph(state, dispatch) {
|
|
33264
|
+
function renderSinusoidGraph(state, dispatch, i18n) {
|
|
33029
33265
|
return {
|
|
33030
33266
|
graph: /*#__PURE__*/React__namespace.createElement(SinusoidGraph, {
|
|
33031
33267
|
graphState: state,
|
|
33032
33268
|
dispatch: dispatch
|
|
33033
33269
|
}),
|
|
33034
|
-
interactiveElementsDescription:
|
|
33270
|
+
interactiveElementsDescription: getSinusoidDescription(state, i18n)
|
|
33035
33271
|
};
|
|
33036
33272
|
}
|
|
33037
33273
|
function SinusoidGraph(props) {
|
|
@@ -33039,6 +33275,9 @@ function SinusoidGraph(props) {
|
|
|
33039
33275
|
dispatch,
|
|
33040
33276
|
graphState
|
|
33041
33277
|
} = props;
|
|
33278
|
+
const i18n = usePerseusI18n();
|
|
33279
|
+
const id = React__namespace.useId();
|
|
33280
|
+
const descriptionId = id + "-description";
|
|
33042
33281
|
|
|
33043
33282
|
// Destructure the coordinates from the graph state
|
|
33044
33283
|
// Note: The order of the coordinates is important:
|
|
@@ -33063,15 +33302,30 @@ function SinusoidGraph(props) {
|
|
|
33063
33302
|
if (coeffs !== undefined) {
|
|
33064
33303
|
coeffRef.current = coeffs;
|
|
33065
33304
|
}
|
|
33066
|
-
|
|
33305
|
+
|
|
33306
|
+
// Aria strings
|
|
33307
|
+
const {
|
|
33308
|
+
srSinusoidGraph,
|
|
33309
|
+
srSinusoidDescription,
|
|
33310
|
+
srSinusoidRootPoint,
|
|
33311
|
+
srSinusoidPeakPoint
|
|
33312
|
+
} = describeSinusoidGraph(graphState, i18n);
|
|
33313
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
33314
|
+
// Outer graph minimal description
|
|
33315
|
+
"aria-label": srSinusoidGraph,
|
|
33316
|
+
"aria-describedby": descriptionId
|
|
33317
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, {
|
|
33067
33318
|
y: x => computeSine(x, coeffRef.current),
|
|
33068
33319
|
color: wonderBlocksTokens.color.blue
|
|
33069
33320
|
}), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
33321
|
+
ariaLabel: i === 0 ? srSinusoidRootPoint : srSinusoidPeakPoint,
|
|
33070
33322
|
key: "point-" + i,
|
|
33071
33323
|
point: coord,
|
|
33072
33324
|
sequenceNumber: i + 1,
|
|
33073
33325
|
onMove: destination => dispatch(actions.sinusoid.movePoint(i, destination))
|
|
33074
|
-
}))
|
|
33326
|
+
})), /*#__PURE__*/React__namespace.createElement("g", {
|
|
33327
|
+
id: descriptionId
|
|
33328
|
+
}, srSinusoidDescription));
|
|
33075
33329
|
}
|
|
33076
33330
|
|
|
33077
33331
|
// Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
|
|
@@ -33109,6 +33363,52 @@ const getSinusoidCoefficients$1 = coords => {
|
|
|
33109
33363
|
verticalOffset
|
|
33110
33364
|
};
|
|
33111
33365
|
};
|
|
33366
|
+
function getSinusoidDescription(state, i18n) {
|
|
33367
|
+
const strings = describeSinusoidGraph(state, i18n);
|
|
33368
|
+
return strings.srSinusoidInteractiveElements;
|
|
33369
|
+
}
|
|
33370
|
+
function describeSinusoidGraph(state, i18n) {
|
|
33371
|
+
const {
|
|
33372
|
+
strings,
|
|
33373
|
+
locale
|
|
33374
|
+
} = i18n;
|
|
33375
|
+
const {
|
|
33376
|
+
coords
|
|
33377
|
+
} = state;
|
|
33378
|
+
const [root, peak] = coords;
|
|
33379
|
+
const diffX = Math.abs(peak[X] - root[X]);
|
|
33380
|
+
const diffY = Math.abs(peak[Y] - root[Y]);
|
|
33381
|
+
const srSinusoidGraph = strings.srSinusoidGraph;
|
|
33382
|
+
const srSinusoidDescription = strings.srSinusoidDescription({
|
|
33383
|
+
minValue: srFormatNumber(root[Y] - diffY, locale),
|
|
33384
|
+
maxValue: srFormatNumber(root[Y] + diffY, locale),
|
|
33385
|
+
cycleStart: srFormatNumber(root[X] - 2 * diffX, locale),
|
|
33386
|
+
cycleEnd: srFormatNumber(root[X] + 2 * diffX, locale)
|
|
33387
|
+
});
|
|
33388
|
+
const srSinusoidRootPoint = strings.srSinusoidRootPoint({
|
|
33389
|
+
x: srFormatNumber(root[X], locale),
|
|
33390
|
+
y: srFormatNumber(root[Y], locale)
|
|
33391
|
+
});
|
|
33392
|
+
const srSinusoidPeakPoint = strings.srSinusoidPeakPoint({
|
|
33393
|
+
x: srFormatNumber(peak[X], locale),
|
|
33394
|
+
y: srFormatNumber(peak[Y], locale)
|
|
33395
|
+
});
|
|
33396
|
+
const srSinusoidInteractiveElements = strings.srInteractiveElements({
|
|
33397
|
+
elements: strings.srSinusoidInteractiveElements({
|
|
33398
|
+
point1X: srFormatNumber(root[X], locale),
|
|
33399
|
+
point1Y: srFormatNumber(root[Y], locale),
|
|
33400
|
+
point2X: srFormatNumber(peak[X], locale),
|
|
33401
|
+
point2Y: srFormatNumber(peak[Y], locale)
|
|
33402
|
+
})
|
|
33403
|
+
});
|
|
33404
|
+
return {
|
|
33405
|
+
srSinusoidGraph,
|
|
33406
|
+
srSinusoidDescription,
|
|
33407
|
+
srSinusoidRootPoint,
|
|
33408
|
+
srSinusoidPeakPoint,
|
|
33409
|
+
srSinusoidInteractiveElements
|
|
33410
|
+
};
|
|
33411
|
+
}
|
|
33112
33412
|
|
|
33113
33413
|
const {
|
|
33114
33414
|
calculateAngleInDegrees,
|
|
@@ -33279,6 +33579,7 @@ const MafsGraph = props => {
|
|
|
33279
33579
|
const descriptionId = `interactive-graph-description-${uniqueId}`;
|
|
33280
33580
|
const interactiveElementsDescriptionId = `interactive-graph-interactive-elements-description-${uniqueId}`;
|
|
33281
33581
|
const unlimitedGraphKeyboardPromptId = `unlimited-graph-keyboard-prompt-${uniqueId}`;
|
|
33582
|
+
const instructionsId = `instructions-${uniqueId}`;
|
|
33282
33583
|
const graphRef = React__namespace.useRef(null);
|
|
33283
33584
|
const {
|
|
33284
33585
|
analytics
|
|
@@ -33306,6 +33607,7 @@ const MafsGraph = props => {
|
|
|
33306
33607
|
const interactionPrompt = isUnlimitedGraphState(state) && state.showKeyboardInteractionInvitation;
|
|
33307
33608
|
wonderBlocksCore.useOnMountEffect(() => {
|
|
33308
33609
|
analytics.onAnalyticsEvent({
|
|
33610
|
+
// TODO(LEMS-2827): Remove analytics event in LEMS-2827 in favor of ti below.
|
|
33309
33611
|
type: "perseus:interactive-graph-widget:rendered",
|
|
33310
33612
|
payload: {
|
|
33311
33613
|
type,
|
|
@@ -33313,6 +33615,14 @@ const MafsGraph = props => {
|
|
|
33313
33615
|
widgetId: "interactive-graph"
|
|
33314
33616
|
}
|
|
33315
33617
|
});
|
|
33618
|
+
analytics.onAnalyticsEvent({
|
|
33619
|
+
type: "perseus:widget:rendered:ti",
|
|
33620
|
+
payload: {
|
|
33621
|
+
widgetSubType: type,
|
|
33622
|
+
widgetType: "INTERACTIVE_GRAPH",
|
|
33623
|
+
widgetId: "interactive-graph"
|
|
33624
|
+
}
|
|
33625
|
+
});
|
|
33316
33626
|
});
|
|
33317
33627
|
const {
|
|
33318
33628
|
graph,
|
|
@@ -33320,7 +33630,8 @@ const MafsGraph = props => {
|
|
|
33320
33630
|
} = renderGraphElements({
|
|
33321
33631
|
state,
|
|
33322
33632
|
dispatch,
|
|
33323
|
-
i18n
|
|
33633
|
+
i18n,
|
|
33634
|
+
markings: props.markings
|
|
33324
33635
|
});
|
|
33325
33636
|
return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
|
|
33326
33637
|
value: {
|
|
@@ -33355,7 +33666,7 @@ const MafsGraph = props => {
|
|
|
33355
33666
|
handleKeyboardEvent(event, state, dispatch);
|
|
33356
33667
|
},
|
|
33357
33668
|
"aria-label": fullGraphAriaLabel,
|
|
33358
|
-
"aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId, isUnlimitedGraphState(state) && "
|
|
33669
|
+
"aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId, isUnlimitedGraphState(state) && unlimitedGraphKeyboardPromptId, state.type !== "none" && instructionsId),
|
|
33359
33670
|
ref: graphRef,
|
|
33360
33671
|
tabIndex: 0,
|
|
33361
33672
|
onFocus: event => {
|
|
@@ -33372,7 +33683,11 @@ const MafsGraph = props => {
|
|
|
33372
33683
|
id: interactiveElementsDescriptionId,
|
|
33373
33684
|
tabIndex: -1,
|
|
33374
33685
|
className: "mafs-sr-only"
|
|
33375
|
-
}, interactiveElementsDescription), /*#__PURE__*/React__namespace.createElement(
|
|
33686
|
+
}, interactiveElementsDescription), state.type !== "none" && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
33687
|
+
id: instructionsId,
|
|
33688
|
+
tabIndex: -1,
|
|
33689
|
+
className: "mafs-sr-only"
|
|
33690
|
+
}, isUnlimitedGraphState(state) ? strings.srUnlimitedGraphInstructions : strings.srGraphInstructions), /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
|
|
33376
33691
|
box: props.box,
|
|
33377
33692
|
backgroundImage: props.backgroundImage
|
|
33378
33693
|
}), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
@@ -33381,7 +33696,21 @@ const MafsGraph = props => {
|
|
|
33381
33696
|
bottom: 0,
|
|
33382
33697
|
left: 0
|
|
33383
33698
|
}
|
|
33384
|
-
}, (props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels,
|
|
33699
|
+
}, (props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, {
|
|
33700
|
+
i18n: i18n
|
|
33701
|
+
})), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View
|
|
33702
|
+
// If we have locked figures, we set aria-hidden
|
|
33703
|
+
// to false so that screen readers can read the
|
|
33704
|
+
// locked figures. If we don't have any locked
|
|
33705
|
+
// figures, we need to set aria-hidden to true so
|
|
33706
|
+
// that screen readers don't read this Mafs element
|
|
33707
|
+
// as an empty image.
|
|
33708
|
+
// Note: Adding this in a wrapping View element
|
|
33709
|
+
// so that `aria-hidden` is a valid property.
|
|
33710
|
+
// It does not work on `Mafs` or svg elements.
|
|
33711
|
+
, {
|
|
33712
|
+
"aria-hidden": props.lockedFigures && props.lockedFigures.length > 0 ? false : true
|
|
33713
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
|
|
33385
33714
|
preserveAspectRatio: false,
|
|
33386
33715
|
viewBox: {
|
|
33387
33716
|
x: state.range[X],
|
|
@@ -33401,10 +33730,10 @@ const MafsGraph = props => {
|
|
|
33401
33730
|
height: height
|
|
33402
33731
|
})),
|
|
33403
33732
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
33404
|
-
(props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes,
|
|
33733
|
+
(props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), props.lockedFigures && props.lockedFigures.length > 0 && /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
|
|
33405
33734
|
lockedFigures: props.lockedFigures,
|
|
33406
33735
|
range: state.range
|
|
33407
|
-
}))), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
|
|
33736
|
+
})))), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
|
|
33408
33737
|
lockedFigures: props.lockedFigures
|
|
33409
33738
|
}), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
33410
33739
|
style: {
|
|
@@ -33698,32 +34027,33 @@ const renderGraphElements = props => {
|
|
|
33698
34027
|
const {
|
|
33699
34028
|
state,
|
|
33700
34029
|
dispatch,
|
|
33701
|
-
i18n
|
|
34030
|
+
i18n,
|
|
34031
|
+
markings
|
|
33702
34032
|
} = props;
|
|
33703
34033
|
const {
|
|
33704
34034
|
type
|
|
33705
34035
|
} = state;
|
|
33706
34036
|
switch (type) {
|
|
33707
34037
|
case "angle":
|
|
33708
|
-
return renderAngleGraph(state, dispatch);
|
|
34038
|
+
return renderAngleGraph(state, dispatch, i18n);
|
|
33709
34039
|
case "segment":
|
|
33710
|
-
return renderSegmentGraph(state, dispatch);
|
|
34040
|
+
return renderSegmentGraph(state, dispatch, i18n);
|
|
33711
34041
|
case "linear-system":
|
|
33712
|
-
return renderLinearSystemGraph(state, dispatch);
|
|
34042
|
+
return renderLinearSystemGraph(state, dispatch, i18n);
|
|
33713
34043
|
case "linear":
|
|
33714
|
-
return renderLinearGraph(state, dispatch);
|
|
34044
|
+
return renderLinearGraph(state, dispatch, i18n);
|
|
33715
34045
|
case "ray":
|
|
33716
|
-
return renderRayGraph(state, dispatch);
|
|
34046
|
+
return renderRayGraph(state, dispatch, i18n);
|
|
33717
34047
|
case "polygon":
|
|
33718
|
-
return renderPolygonGraph(state, dispatch);
|
|
34048
|
+
return renderPolygonGraph(state, dispatch, i18n, markings);
|
|
33719
34049
|
case "point":
|
|
33720
|
-
return renderPointGraph(state, dispatch);
|
|
34050
|
+
return renderPointGraph(state, dispatch, i18n);
|
|
33721
34051
|
case "circle":
|
|
33722
|
-
return renderCircleGraph(state, dispatch);
|
|
34052
|
+
return renderCircleGraph(state, dispatch, i18n);
|
|
33723
34053
|
case "quadratic":
|
|
33724
34054
|
return renderQuadraticGraph(state, dispatch, i18n);
|
|
33725
34055
|
case "sinusoid":
|
|
33726
|
-
return renderSinusoidGraph(state, dispatch);
|
|
34056
|
+
return renderSinusoidGraph(state, dispatch, i18n);
|
|
33727
34057
|
case "none":
|
|
33728
34058
|
return {
|
|
33729
34059
|
graph: null,
|
|
@@ -34143,13 +34473,13 @@ const {
|
|
|
34143
34473
|
polar
|
|
34144
34474
|
} = kmath.angles;
|
|
34145
34475
|
const {
|
|
34146
|
-
angleMeasures
|
|
34147
|
-
ccw
|
|
34148
|
-
lawOfCosines
|
|
34476
|
+
angleMeasures,
|
|
34477
|
+
ccw,
|
|
34478
|
+
lawOfCosines,
|
|
34149
34479
|
magnitude: magnitude$1,
|
|
34150
34480
|
polygonSidesIntersect,
|
|
34151
34481
|
reverseVector,
|
|
34152
|
-
sign
|
|
34482
|
+
sign,
|
|
34153
34483
|
vector: vector$1
|
|
34154
34484
|
} = kmath.geometry;
|
|
34155
34485
|
const {
|
|
@@ -34656,7 +34986,7 @@ function doAddPoint(state, action) {
|
|
|
34656
34986
|
...state,
|
|
34657
34987
|
hasBeenInteractedWith: true,
|
|
34658
34988
|
coords: newCoords,
|
|
34659
|
-
showRemovePointButton:
|
|
34989
|
+
showRemovePointButton: true,
|
|
34660
34990
|
focusedPointIndex: newCoords.length - 1
|
|
34661
34991
|
};
|
|
34662
34992
|
}
|
|
@@ -34664,17 +34994,12 @@ function doRemovePoint(state, action) {
|
|
|
34664
34994
|
if (!isUnlimitedGraphState(state)) {
|
|
34665
34995
|
return state;
|
|
34666
34996
|
}
|
|
34667
|
-
|
|
34668
|
-
if (state.interactionMode === "mouse") {
|
|
34669
|
-
nextFocusedPointIndex = null;
|
|
34670
|
-
} else {
|
|
34671
|
-
nextFocusedPointIndex = state.coords.length > 1 ? state.coords.length - 2 : null;
|
|
34672
|
-
}
|
|
34997
|
+
const nextFocusedPointIndex = state.coords.length > 1 ? state.coords.length - 2 : null;
|
|
34673
34998
|
return {
|
|
34674
34999
|
...state,
|
|
34675
35000
|
coords: state.coords.filter((_, i) => i !== action.index),
|
|
34676
35001
|
focusedPointIndex: nextFocusedPointIndex,
|
|
34677
|
-
showRemovePointButton: false
|
|
35002
|
+
showRemovePointButton: nextFocusedPointIndex !== null ? true : false
|
|
34678
35003
|
};
|
|
34679
35004
|
}
|
|
34680
35005
|
const getDeltaVertex = (maxMoves, minMoves, delta) => {
|
|
@@ -34700,7 +35025,7 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
34700
35025
|
return [dx, dy];
|
|
34701
35026
|
};
|
|
34702
35027
|
// Less than or approximately equal
|
|
34703
|
-
function leq
|
|
35028
|
+
function leq(a, b) {
|
|
34704
35029
|
return a < b || perseusCore.approximateEqual(a, b);
|
|
34705
35030
|
}
|
|
34706
35031
|
function boundAndSnapToGrid(point, _ref) {
|
|
@@ -34851,7 +35176,7 @@ function boundAndSnapToPolygonAngle(destinationPoint, _ref6, index) {
|
|
|
34851
35176
|
});
|
|
34852
35177
|
|
|
34853
35178
|
// Gets the radian angles between the coords and maps them to degrees
|
|
34854
|
-
const angles = angleMeasures
|
|
35179
|
+
const angles = angleMeasures(coordsCopy).map(angle => angle * 180 / Math.PI);
|
|
34855
35180
|
|
|
34856
35181
|
// Gets the relative index of a point
|
|
34857
35182
|
const rel = j => {
|
|
@@ -34871,7 +35196,7 @@ function boundAndSnapToPolygonAngle(destinationPoint, _ref6, index) {
|
|
|
34871
35196
|
|
|
34872
35197
|
// Avoid degenerate triangles
|
|
34873
35198
|
if (innerAngles.some(function (angle) {
|
|
34874
|
-
return leq
|
|
35199
|
+
return leq(angle, 1);
|
|
34875
35200
|
})) {
|
|
34876
35201
|
return startingPoint;
|
|
34877
35202
|
}
|
|
@@ -34885,7 +35210,7 @@ function boundAndSnapToPolygonAngle(destinationPoint, _ref6, index) {
|
|
|
34885
35210
|
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
34886
35211
|
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
34887
35212
|
// for is then used in the polar function to determine the new point.
|
|
34888
|
-
const onLeft = sign
|
|
35213
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
34889
35214
|
|
|
34890
35215
|
// Solve for side by using the law of sines
|
|
34891
35216
|
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
@@ -34933,13 +35258,13 @@ function boundAndSnapToSides(destinationPoint, _ref7, index) {
|
|
|
34933
35258
|
});
|
|
34934
35259
|
|
|
34935
35260
|
// Avoid degenerate triangles
|
|
34936
|
-
if (leq
|
|
35261
|
+
if (leq(sides[1] + sides[2], sides[0]) || leq(sides[0] + sides[2], sides[1]) || leq(sides[0] + sides[1], sides[2])) {
|
|
34937
35262
|
return startingPoint;
|
|
34938
35263
|
}
|
|
34939
35264
|
|
|
34940
35265
|
// Solve for angle by using the law of cosines
|
|
34941
35266
|
// Angle at the first vertex of the polygon
|
|
34942
|
-
const innerAngle = lawOfCosines
|
|
35267
|
+
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
34943
35268
|
|
|
34944
35269
|
// Angle at the second vertex of the polygon
|
|
34945
35270
|
const outerAngle = getAngleFromVertex(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
@@ -34950,7 +35275,7 @@ function boundAndSnapToSides(destinationPoint, _ref7, index) {
|
|
|
34950
35275
|
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
34951
35276
|
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
34952
35277
|
// for is then used in the polar function to determine the new point.
|
|
34953
|
-
const onLeft = sign
|
|
35278
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
34954
35279
|
|
|
34955
35280
|
// Uses the length of the first side of the polygon (radial coordinate)
|
|
34956
35281
|
// and the angle between the first and second sides of the
|
|
@@ -35088,19 +35413,11 @@ const {
|
|
|
35088
35413
|
getClockwiseAngle
|
|
35089
35414
|
} = kmath.angles;
|
|
35090
35415
|
const {
|
|
35091
|
-
angleMeasures,
|
|
35092
|
-
ccw,
|
|
35093
|
-
collinear,
|
|
35094
35416
|
getLineEquation,
|
|
35095
35417
|
getLineIntersection,
|
|
35096
|
-
intersects,
|
|
35097
|
-
lawOfCosines,
|
|
35098
35418
|
magnitude,
|
|
35099
|
-
rotate,
|
|
35100
|
-
sign,
|
|
35101
35419
|
vector
|
|
35102
35420
|
} = kmath.geometry;
|
|
35103
|
-
const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
|
|
35104
35421
|
const defaultBackgroundImage = {
|
|
35105
35422
|
url: null
|
|
35106
35423
|
};
|
|
@@ -35109,31 +35426,9 @@ const UNLIMITED = "unlimited";
|
|
|
35109
35426
|
// Sample background image:
|
|
35110
35427
|
// https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
|
|
35111
35428
|
|
|
35112
|
-
// default to defaultValue if actual is null or undefined
|
|
35113
|
-
function defaultVal(actual, defaultValue) {
|
|
35114
|
-
return actual == null ? defaultValue : actual;
|
|
35115
|
-
}
|
|
35116
|
-
|
|
35117
|
-
// Less than or approximately equal
|
|
35118
|
-
function leq(a, b) {
|
|
35119
|
-
return a < b || perseusCore.approximateEqual(a, b);
|
|
35120
|
-
}
|
|
35121
|
-
function capitalize(str) {
|
|
35122
|
-
return str.replace(/(?:^|-)(.)/g, function (match, letter) {
|
|
35123
|
-
return letter.toUpperCase();
|
|
35124
|
-
});
|
|
35125
|
-
}
|
|
35126
35429
|
function numSteps(range, step) {
|
|
35127
35430
|
return Math.floor((range[1] - range[0]) / step);
|
|
35128
35431
|
}
|
|
35129
|
-
const _getShouldShowInstructions = props => {
|
|
35130
|
-
return _isClickToAddPoints(props) && (
|
|
35131
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
35132
|
-
props.graph.coords == null || props.graph.coords.length === 0);
|
|
35133
|
-
};
|
|
35134
|
-
const _isClickToAddPoints = props => {
|
|
35135
|
-
return props.graph.type === "point" && props.graph.numPoints === UNLIMITED || props.graph.type === "polygon" && props.graph.numSides === UNLIMITED;
|
|
35136
|
-
};
|
|
35137
35432
|
const makeInvalidTypeError = (functionName, graphType) => {
|
|
35138
35433
|
return new perseusCore.PerseusError(`${functionName} called but current graph type is not a '${graphType}'`, perseusCore.Errors.NotAllowed, {
|
|
35139
35434
|
metadata: {
|
|
@@ -35174,1202 +35469,7 @@ function getQuadraticCoefficients(coords) {
|
|
|
35174
35469
|
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;
|
|
35175
35470
|
return [a, b, c];
|
|
35176
35471
|
}
|
|
35177
|
-
|
|
35178
|
-
// (LEMS-2190): Move the Mafs Angle Graph coordinate reversal logic in interactive-graph-state.ts
|
|
35179
|
-
// to this file when we remove the legacy graph. This logic allows us to support bi-directional angles
|
|
35180
|
-
// for the new (non-reflexive) Mafs graphs, while maintaining the same scoring behaviour as the legacy graph.
|
|
35181
|
-
// Once the legacy graph is removed, we should move this logic directly into the validate function below.
|
|
35182
|
-
class LegacyInteractiveGraph extends React__namespace.Component {
|
|
35183
|
-
static contextType = PerseusI18nContext;
|
|
35184
|
-
angle;
|
|
35185
|
-
circle;
|
|
35186
|
-
graphie;
|
|
35187
|
-
horizHairline;
|
|
35188
|
-
line;
|
|
35189
|
-
lines;
|
|
35190
|
-
parabola;
|
|
35191
|
-
pointA;
|
|
35192
|
-
pointB;
|
|
35193
|
-
pointC;
|
|
35194
|
-
// @ts-expect-error - TS2564 - Property 'points' has no initializer and is not definitely assigned in the constructor.
|
|
35195
|
-
points;
|
|
35196
|
-
polygon;
|
|
35197
|
-
// @ts-expect-error - TS2564 - Property 'shouldResetGraphie' has no initializer and is not definitely assigned in the constructor.
|
|
35198
|
-
shouldResetGraphie;
|
|
35199
|
-
sinusoid;
|
|
35200
|
-
trashCan;
|
|
35201
|
-
vertHairline;
|
|
35202
|
-
static defaultProps = {
|
|
35203
|
-
labels: ["x", "y"],
|
|
35204
|
-
range: [[-10, 10], [-10, 10]],
|
|
35205
|
-
step: [1, 1],
|
|
35206
|
-
backgroundImage: defaultBackgroundImage,
|
|
35207
|
-
markings: "graph",
|
|
35208
|
-
showTooltips: false,
|
|
35209
|
-
showProtractor: false,
|
|
35210
|
-
graph: {
|
|
35211
|
-
type: "linear"
|
|
35212
|
-
}
|
|
35213
|
-
};
|
|
35214
|
-
state = {
|
|
35215
|
-
shouldShowInstructions: _getShouldShowInstructions(this.props)
|
|
35216
|
-
};
|
|
35217
|
-
componentDidMount() {
|
|
35218
|
-
if (this.refs.graph) {
|
|
35219
|
-
// eslint-disable-next-line react/no-string-refs
|
|
35220
|
-
// @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
|
|
35221
|
-
this.setGraphie(this.refs.graph.graphie());
|
|
35222
|
-
}
|
|
35223
|
-
}
|
|
35224
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
35225
|
-
if (this.isClickToAddPoints() !== this.isClickToAddPoints(nextProps)) {
|
|
35226
|
-
this.shouldResetGraphie = true;
|
|
35227
|
-
this.setState({
|
|
35228
|
-
shouldShowInstructions: _getShouldShowInstructions(nextProps)
|
|
35229
|
-
});
|
|
35230
|
-
}
|
|
35231
|
-
if (this.props.backgroundImage?.url !== nextProps.backgroundImage?.url || this.props.backgroundImage !== nextProps.backgroundImage || this.props.containerSizeClass !== nextProps.containerSizeClass) {
|
|
35232
|
-
this.shouldResetGraphie = true;
|
|
35233
|
-
}
|
|
35234
|
-
}
|
|
35235
|
-
componentDidUpdate(prevProps, prevState) {
|
|
35236
|
-
// TODO(jeremy): This feels underspecified. For example, if the props
|
|
35237
|
-
// change the coords of a segment graph, but not the numSegments, we'd
|
|
35238
|
-
// not count that as an update.
|
|
35239
|
-
const oldType = prevProps.graph.type;
|
|
35240
|
-
const newType = this.props.graph.type;
|
|
35241
|
-
if (oldType !== newType || prevProps.graph.type === "angle" && this.props.graph.type === "angle" && this.areAngleGraphsEqual(prevProps.graph, this.props.graph) || prevProps.graph.type === "point" && this.props.graph.type === "point" && this.arePointGraphsEqual(prevProps.graph, this.props.graph) || prevProps.graph.type === "polygon" && this.props.graph.type === "polygon" && this.arePolygonGraphsEqual(prevProps.graph, this.props.graph) || prevProps.graph.type === "segment" && this.props.graph.type === "segment" && this.areSegmentGraphsEqual(prevProps.graph, this.props.graph)) {
|
|
35242
|
-
this["remove" + capitalize(oldType) + "Controls"]();
|
|
35243
|
-
this["add" + capitalize(newType) + "Controls"]();
|
|
35244
|
-
}
|
|
35245
|
-
if (this.shouldResetGraphie) {
|
|
35246
|
-
this.resetGraphie();
|
|
35247
|
-
}
|
|
35248
|
-
}
|
|
35249
|
-
_getShouldShowInstructions = props => {
|
|
35250
|
-
props = props || this.props;
|
|
35251
|
-
return this.isClickToAddPoints(props) && (
|
|
35252
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
35253
|
-
props.graph.coords == null || props.graph.coords.length === 0);
|
|
35254
|
-
};
|
|
35255
|
-
setGraphie = newGraphie => {
|
|
35256
|
-
this.graphie = newGraphie;
|
|
35257
|
-
this.setupGraphie();
|
|
35258
|
-
};
|
|
35259
|
-
handleAddPointsMouseDown = coord => {
|
|
35260
|
-
// This function should only be called when this.isClickToAddPoints()
|
|
35261
|
-
// is true
|
|
35262
|
-
if (!this.isClickToAddPoints()) {
|
|
35263
|
-
throw new perseusCore.PerseusError("handleAddPointsClick should not be registered" + "when isClickToAddPoints() is false", perseusCore.Errors.NotAllowed);
|
|
35264
|
-
}
|
|
35265
|
-
if (!this.isCoordInTrash(coord)) {
|
|
35266
|
-
let point;
|
|
35267
|
-
if (this.props.graph.type === "point") {
|
|
35268
|
-
point = this.createPointForPointsType(coord, this.points.length);
|
|
35269
|
-
if (!point.constrain()) {
|
|
35270
|
-
point.remove();
|
|
35271
|
-
return;
|
|
35272
|
-
}
|
|
35273
|
-
this.points.push(point);
|
|
35274
|
-
|
|
35275
|
-
// interactive2 allows us to grab the point
|
|
35276
|
-
const idx = this.points.length - 1;
|
|
35277
|
-
this.points[idx].grab(coord);
|
|
35278
|
-
this.updateCoordsFromPoints();
|
|
35279
|
-
} else if (this.props.graph.type === "polygon") {
|
|
35280
|
-
if (this.polygon?.closed()) {
|
|
35281
|
-
return;
|
|
35282
|
-
}
|
|
35283
|
-
point = this.createPointForPolygonType(coord, this.points.length);
|
|
35284
|
-
this.points.push(point);
|
|
35285
|
-
const idx = this.points.length - 1;
|
|
35286
|
-
this.points[idx].grab(coord);
|
|
35287
|
-
|
|
35288
|
-
// We don't call updateCoordsFromPoints for
|
|
35289
|
-
// polygons, since the polygon won't be
|
|
35290
|
-
// closed yet.
|
|
35291
|
-
this.updatePolygon();
|
|
35292
|
-
}
|
|
35293
|
-
this.setState({
|
|
35294
|
-
shouldShowInstructions: false
|
|
35295
|
-
});
|
|
35296
|
-
}
|
|
35297
|
-
};
|
|
35298
|
-
resetGraphie = () => {
|
|
35299
|
-
this.shouldResetGraphie = false;
|
|
35300
|
-
this.parabola = null;
|
|
35301
|
-
this.sinusoid = null;
|
|
35302
|
-
// eslint-disable-next-line react/no-string-refs
|
|
35303
|
-
// @ts-expect-error - TS2339 - Property 'reset' does not exist on type 'ReactInstance'.
|
|
35304
|
-
this.refs.graph?.reset();
|
|
35305
|
-
};
|
|
35306
|
-
setupGraphie = () => {
|
|
35307
|
-
this.setTrashCanVisibility(0);
|
|
35308
|
-
if (this.isClickToAddPoints()) {
|
|
35309
|
-
this.setTrashCanVisibility(0.5);
|
|
35310
|
-
}
|
|
35311
|
-
if (this.props.apiOptions.isMobile) {
|
|
35312
|
-
this.horizHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
|
|
35313
|
-
normalStyle: {
|
|
35314
|
-
strokeWidth: 1
|
|
35315
|
-
}
|
|
35316
|
-
});
|
|
35317
|
-
this.horizHairline.attr({
|
|
35318
|
-
stroke: KhanColors.INTERACTIVE
|
|
35319
|
-
});
|
|
35320
|
-
this.horizHairline?.hide();
|
|
35321
|
-
this.vertHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
|
|
35322
|
-
normalStyle: {
|
|
35323
|
-
strokeWidth: 1
|
|
35324
|
-
}
|
|
35325
|
-
});
|
|
35326
|
-
this.vertHairline.attr({
|
|
35327
|
-
stroke: KhanColors.INTERACTIVE
|
|
35328
|
-
});
|
|
35329
|
-
this.vertHairline?.hide();
|
|
35330
|
-
}
|
|
35331
|
-
const type = this.props.graph.type;
|
|
35332
|
-
this["add" + capitalize(type) + "Controls"]();
|
|
35333
|
-
};
|
|
35334
|
-
showHairlines = point => {
|
|
35335
|
-
if (this.props.apiOptions.isMobile && this.props.markings !== "none") {
|
|
35336
|
-
// Hairlines are already initialized when the graph is loaded, so
|
|
35337
|
-
// here we just move them to the updated location and make them
|
|
35338
|
-
// visible.
|
|
35339
|
-
this.horizHairline?.moveTo([this.props.range[0][0], point[1]], [this.props.range[0][1], point[1]]);
|
|
35340
|
-
this.horizHairline?.show();
|
|
35341
|
-
this.vertHairline?.moveTo([point[0], this.props.range[1][0]], [point[0], this.props.range[1][1]]);
|
|
35342
|
-
this.vertHairline?.show();
|
|
35343
|
-
}
|
|
35344
|
-
};
|
|
35345
|
-
hideHairlines = () => {
|
|
35346
|
-
if (this.props.apiOptions.isMobile) {
|
|
35347
|
-
this.horizHairline?.hide();
|
|
35348
|
-
this.vertHairline?.hide();
|
|
35349
|
-
}
|
|
35350
|
-
};
|
|
35351
|
-
setTrashCanVisibility = opacity => {
|
|
35352
|
-
const graphie = this.graphie;
|
|
35353
|
-
if (kmath.number.equal(opacity, 0)) {
|
|
35354
|
-
if (this.trashCan) {
|
|
35355
|
-
this.trashCan.remove();
|
|
35356
|
-
this.trashCan = null;
|
|
35357
|
-
}
|
|
35358
|
-
} else if (!this.props.apiOptions.isMobile) {
|
|
35359
|
-
// Only if trash tooltips are not being used, we initialize the old
|
|
35360
|
-
// trash can area.
|
|
35361
|
-
if (!this.trashCan) {
|
|
35362
|
-
this.trashCan = graphie?.raphael?.image(TRASH_ICON_URI, graphie.xpixels - 40, graphie.ypixels - 40, 40, 40);
|
|
35363
|
-
}
|
|
35364
|
-
this.trashCan?.attr({
|
|
35365
|
-
opacity: opacity
|
|
35366
|
-
});
|
|
35367
|
-
}
|
|
35368
|
-
};
|
|
35369
|
-
isClickToAddPoints = props => {
|
|
35370
|
-
props = props || this.props;
|
|
35371
|
-
return _isClickToAddPoints(props);
|
|
35372
|
-
};
|
|
35373
|
-
areAngleGraphsEqual(prevGraph, currentGraph) {
|
|
35374
|
-
return prevGraph.allowReflexAngles !== currentGraph.allowReflexAngles || prevGraph.angleOffsetDeg !== currentGraph.angleOffsetDeg || prevGraph.snapDegrees !== currentGraph.snapDegrees;
|
|
35375
|
-
}
|
|
35376
|
-
arePointGraphsEqual(prevGraph, currentGraph) {
|
|
35377
|
-
return prevGraph.numPoints !== currentGraph.numPoints;
|
|
35378
|
-
}
|
|
35379
|
-
arePolygonGraphsEqual(prevGraph, currentGraph) {
|
|
35380
|
-
return prevGraph.numSides !== currentGraph.numSides || prevGraph.showAngles !== currentGraph.showAngles || prevGraph.showSides !== currentGraph.showSides || prevGraph.snapTo !== currentGraph.snapTo;
|
|
35381
|
-
}
|
|
35382
|
-
areSegmentGraphsEqual(prevGraph, currentGraph) {
|
|
35383
|
-
return prevGraph.numSegments !== currentGraph.numSegments;
|
|
35384
|
-
}
|
|
35385
|
-
_lineStroke = () => {
|
|
35386
|
-
// This should probably use: this.props.apiOptions.isMobile
|
|
35387
|
-
// @ts-expect-error - TS2339 - Property 'isMobile' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
|
|
35388
|
-
return this.props.isMobile ? {
|
|
35389
|
-
"stroke-width": 3
|
|
35390
|
-
} : {};
|
|
35391
|
-
};
|
|
35392
|
-
addLine = type => {
|
|
35393
|
-
const self = this;
|
|
35394
|
-
const graphie = self.graphie;
|
|
35395
|
-
const coords = InteractiveGraph.getLineCoords(self.props.graph, self.props);
|
|
35396
|
-
const points = self.points = ___default["default"].map(coords, coord => {
|
|
35397
|
-
return Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35398
|
-
coord: coord,
|
|
35399
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap()],
|
|
35400
|
-
onMove: () => {
|
|
35401
|
-
const graph = ___default["default"].extend({}, self.props.graph, {
|
|
35402
|
-
coords: ___default["default"].invoke(points, "coord")
|
|
35403
|
-
});
|
|
35404
|
-
self.onChange({
|
|
35405
|
-
graph: graph
|
|
35406
|
-
});
|
|
35407
|
-
}
|
|
35408
|
-
});
|
|
35409
|
-
});
|
|
35410
|
-
const lineConfig = {
|
|
35411
|
-
points: points,
|
|
35412
|
-
static: true,
|
|
35413
|
-
normalStyle: {
|
|
35414
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTIVE,
|
|
35415
|
-
...this._lineStroke()
|
|
35416
|
-
}
|
|
35417
|
-
};
|
|
35418
|
-
if (type === "line") {
|
|
35419
|
-
// @ts-expect-error - TS2339 - Property 'extendLine' does not exist on type '{ readonly points: any[]; readonly static: true; readonly normalStyle: { readonly "stroke-width"?: number | undefined; readonly stroke: "#71B307" | "#63D9EA"; }; }'.
|
|
35420
|
-
lineConfig.extendLine = true;
|
|
35421
|
-
} else if (type === "ray") {
|
|
35422
|
-
// @ts-expect-error - TS2339 - Property 'extendRay' does not exist on type '{ readonly points: any[]; readonly static: true; readonly normalStyle: { readonly "stroke-width"?: number | undefined; readonly stroke: "#71B307" | "#63D9EA"; }; }'.
|
|
35423
|
-
lineConfig.extendRay = true;
|
|
35424
|
-
}
|
|
35425
|
-
self.line = Interactive2.addMovableLine(graphie, lineConfig);
|
|
35426
|
-
|
|
35427
|
-
// A and B can't be in the same place
|
|
35428
|
-
points[0].listen("constraints", "isLine", coord => {
|
|
35429
|
-
return !kmath.point.equal(coord, points[1].coord());
|
|
35430
|
-
});
|
|
35431
|
-
points[1].listen("constraints", "isLine", coord => {
|
|
35432
|
-
return !kmath.point.equal(coord, points[0].coord());
|
|
35433
|
-
});
|
|
35434
|
-
};
|
|
35435
|
-
removeLine = () => {
|
|
35436
|
-
___default["default"].invoke(this.points, "remove");
|
|
35437
|
-
this.line?.remove();
|
|
35438
|
-
};
|
|
35439
|
-
addNoneControls = () => {};
|
|
35440
|
-
removeNoneControls = () => {};
|
|
35441
|
-
addLinearControls = () => {
|
|
35442
|
-
this.addLine("line");
|
|
35443
|
-
};
|
|
35444
|
-
removeLinearControls = () => {
|
|
35445
|
-
this.removeLine();
|
|
35446
|
-
};
|
|
35447
|
-
addQuadraticControls = () => {
|
|
35448
|
-
if (this.props.graph.type !== "quadratic") {
|
|
35449
|
-
throw makeInvalidTypeError("addQuadraticControls", "quadratic");
|
|
35450
|
-
}
|
|
35451
|
-
let coords = this.props.graph.coords;
|
|
35452
|
-
if (!coords) {
|
|
35453
|
-
coords = InteractiveGraph.defaultQuadraticCoords(this.props);
|
|
35454
|
-
}
|
|
35455
|
-
const onMoveHandler = () => {
|
|
35456
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
35457
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'.
|
|
35458
|
-
coords: [pointA.coord(), pointB.coord(), pointC.coord()]
|
|
35459
|
-
});
|
|
35460
|
-
this.onChange({
|
|
35461
|
-
graph
|
|
35462
|
-
});
|
|
35463
|
-
this.updateQuadratic();
|
|
35464
|
-
};
|
|
35465
|
-
|
|
35466
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35467
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35468
|
-
// to be executed synchronously, which causes this issue.
|
|
35469
|
-
let pointA = null;
|
|
35470
|
-
pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35471
|
-
coord: coords[0],
|
|
35472
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35473
|
-
return !pointA ||
|
|
35474
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35475
|
-
coord[0] !== pointB.coord()[0] &&
|
|
35476
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35477
|
-
coord[0] !== pointC.coord()[0];
|
|
35478
|
-
}],
|
|
35479
|
-
onMove: onMoveHandler
|
|
35480
|
-
});
|
|
35481
|
-
|
|
35482
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35483
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35484
|
-
// to be executed synchronously, which causes this issue.
|
|
35485
|
-
let pointB = null;
|
|
35486
|
-
pointB = this.pointB = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35487
|
-
coord: coords[1],
|
|
35488
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35489
|
-
return !pointB ||
|
|
35490
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35491
|
-
coord[0] !== pointA.coord()[0] &&
|
|
35492
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35493
|
-
coord[0] !== pointC.coord()[0];
|
|
35494
|
-
}],
|
|
35495
|
-
onMove: onMoveHandler
|
|
35496
|
-
});
|
|
35497
|
-
|
|
35498
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35499
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35500
|
-
// to be executed synchronously, which causes this issue.
|
|
35501
|
-
let pointC = null;
|
|
35502
|
-
pointC = this.pointC = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35503
|
-
coord: coords[2],
|
|
35504
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35505
|
-
return !pointC ||
|
|
35506
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35507
|
-
coord[0] !== pointA.coord()[0] &&
|
|
35508
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35509
|
-
coord[0] !== pointB.coord()[0];
|
|
35510
|
-
}],
|
|
35511
|
-
onMove: onMoveHandler
|
|
35512
|
-
});
|
|
35513
|
-
this.updateQuadratic();
|
|
35514
|
-
};
|
|
35515
|
-
updateQuadratic = () => {
|
|
35516
|
-
const coeffs = InteractiveGraph.getCurrentQuadraticCoefficients(this.props);
|
|
35517
|
-
if (!coeffs) {
|
|
35518
|
-
return;
|
|
35519
|
-
}
|
|
35520
|
-
|
|
35521
|
-
// Extract coefficients the parabola
|
|
35522
|
-
const a = coeffs[0];
|
|
35523
|
-
const b = coeffs[1];
|
|
35524
|
-
const c = coeffs[2];
|
|
35525
|
-
|
|
35526
|
-
// Plot and style
|
|
35527
|
-
if (this.parabola) {
|
|
35528
|
-
const path = this.graphie?.svgParabolaPath(a, b, c);
|
|
35529
|
-
this.parabola?.attr({
|
|
35530
|
-
path: path
|
|
35531
|
-
});
|
|
35532
|
-
} else {
|
|
35533
|
-
this.parabola = this.graphie?.parabola(a, b, c);
|
|
35534
|
-
this.parabola?.attr({
|
|
35535
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.DYNAMIC,
|
|
35536
|
-
...this._lineStroke()
|
|
35537
|
-
});
|
|
35538
|
-
this.parabola?.toBack();
|
|
35539
|
-
}
|
|
35540
|
-
};
|
|
35541
|
-
removeQuadraticControls = () => {
|
|
35542
|
-
this.pointA?.remove();
|
|
35543
|
-
this.pointB?.remove();
|
|
35544
|
-
this.pointC?.remove();
|
|
35545
|
-
if (this.parabola) {
|
|
35546
|
-
this.parabola.remove();
|
|
35547
|
-
this.parabola = null;
|
|
35548
|
-
}
|
|
35549
|
-
};
|
|
35550
|
-
addSinusoidControls = () => {
|
|
35551
|
-
if (this.props.graph.type !== "sinusoid") {
|
|
35552
|
-
throw makeInvalidTypeError("addSinusoidControls", "sinusoid");
|
|
35553
|
-
}
|
|
35554
|
-
let coords = this.props.graph.coords;
|
|
35555
|
-
if (!coords) {
|
|
35556
|
-
coords = InteractiveGraph.defaultSinusoidCoords(this.props);
|
|
35557
|
-
}
|
|
35558
|
-
const onMoveHandler = () => {
|
|
35559
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
35560
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35561
|
-
coords: [pointA.coord(), pointB.coord()]
|
|
35562
|
-
});
|
|
35563
|
-
this.onChange({
|
|
35564
|
-
graph: graph
|
|
35565
|
-
});
|
|
35566
|
-
this.updateSinusoid();
|
|
35567
|
-
};
|
|
35568
|
-
|
|
35569
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35570
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35571
|
-
// to be executed synchronously, which causes this issue.
|
|
35572
|
-
let pointA = null;
|
|
35573
|
-
pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35574
|
-
coord: coords[0],
|
|
35575
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35576
|
-
return !pointA || coord[0] !== pointB.coord()[0];
|
|
35577
|
-
}],
|
|
35578
|
-
onMove: onMoveHandler
|
|
35579
|
-
});
|
|
35580
|
-
const pointB = this.pointB = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35581
|
-
coord: coords[1],
|
|
35582
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35583
|
-
// @ts-expect-error - TS2339 - Property 'coord' does not exist on type 'never'.
|
|
35584
|
-
return !pointA || coord[0] !== pointA.coord()[0];
|
|
35585
|
-
}],
|
|
35586
|
-
onMove: onMoveHandler
|
|
35587
|
-
});
|
|
35588
|
-
this.updateSinusoid();
|
|
35589
|
-
};
|
|
35590
|
-
updateSinusoid = () => {
|
|
35591
|
-
const coeffs = InteractiveGraph.getCurrentSinusoidCoefficients(this.props);
|
|
35592
|
-
if (!coeffs) {
|
|
35593
|
-
return;
|
|
35594
|
-
}
|
|
35595
|
-
const a = coeffs[0];
|
|
35596
|
-
const b = coeffs[1];
|
|
35597
|
-
const c = coeffs[2];
|
|
35598
|
-
const d = coeffs[3];
|
|
35599
|
-
|
|
35600
|
-
// Plot and style
|
|
35601
|
-
if (this.sinusoid) {
|
|
35602
|
-
const path = this.graphie?.svgSinusoidPath(a, b, c, d);
|
|
35603
|
-
this.sinusoid?.attr({
|
|
35604
|
-
path: path
|
|
35605
|
-
});
|
|
35606
|
-
} else {
|
|
35607
|
-
this.sinusoid = this.graphie?.sinusoid(a, b, c, d);
|
|
35608
|
-
this.sinusoid?.attr({
|
|
35609
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.DYNAMIC,
|
|
35610
|
-
...this._lineStroke()
|
|
35611
|
-
});
|
|
35612
|
-
this.sinusoid?.toBack();
|
|
35613
|
-
}
|
|
35614
|
-
};
|
|
35615
|
-
removeSinusoidControls = () => {
|
|
35616
|
-
this.pointA?.remove();
|
|
35617
|
-
this.pointB?.remove();
|
|
35618
|
-
if (this.sinusoid) {
|
|
35619
|
-
this.sinusoid.remove();
|
|
35620
|
-
this.sinusoid = null;
|
|
35621
|
-
}
|
|
35622
|
-
};
|
|
35623
|
-
addCircleControls = () => {
|
|
35624
|
-
const graphie = this.graphie;
|
|
35625
|
-
const minSnap = ___default["default"].min(graphie.snap);
|
|
35626
|
-
const circle = this.circle = graphie?.addCircleGraph({
|
|
35627
|
-
// @ts-expect-error - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'.
|
|
35628
|
-
center: this.props.graph.center || [0, 0],
|
|
35629
|
-
// @ts-expect-error - TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
|
|
35630
|
-
radius: this.props.graph.radius || ___default["default"].min(this.props.step),
|
|
35631
|
-
snapX: graphie.snap[0],
|
|
35632
|
-
snapY: graphie.snap[1],
|
|
35633
|
-
minRadius: minSnap,
|
|
35634
|
-
snapRadius: minSnap
|
|
35635
|
-
});
|
|
35636
|
-
$__default["default"](circle).on("move", () => {
|
|
35637
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
35638
|
-
center: circle.center,
|
|
35639
|
-
radius: circle.radius
|
|
35640
|
-
});
|
|
35641
|
-
this.onChange({
|
|
35642
|
-
graph: graph
|
|
35643
|
-
});
|
|
35644
|
-
});
|
|
35645
|
-
};
|
|
35646
|
-
removeCircleControls = () => {
|
|
35647
|
-
this.circle?.remove();
|
|
35648
|
-
};
|
|
35649
|
-
addLinearSystemControls = () => {
|
|
35650
|
-
const graphie = this.graphie;
|
|
35651
|
-
const coords = InteractiveGraph.getLinearSystemCoords(this.props.graph, this.props);
|
|
35652
|
-
const segmentColors = [KhanColors.INTERACTIVE, KhanColors.GREEN];
|
|
35653
|
-
const points = this.points = ___default["default"].map(coords, (segmentCoords, segmentIndex) => {
|
|
35654
|
-
// NOTE(jeresig): This code attempts to access the variable
|
|
35655
|
-
// while it's still being defined, it should be refactored!
|
|
35656
|
-
// The callbacks appear to be executed synchronously, which
|
|
35657
|
-
// causes this issue.
|
|
35658
|
-
const segmentPoints = [];
|
|
35659
|
-
for (let i = 0; i < segmentCoords.length; i += 1) {
|
|
35660
|
-
const coord = segmentCoords[i];
|
|
35661
|
-
segmentPoints.push(Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35662
|
-
coord: coord,
|
|
35663
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35664
|
-
const otherSegment = segmentPoints[1 - i];
|
|
35665
|
-
if (!otherSegment) {
|
|
35666
|
-
// segment hasn't been defined yet
|
|
35667
|
-
// because we're still creating them
|
|
35668
|
-
return;
|
|
35669
|
-
}
|
|
35670
|
-
return !kmath.point.equal(coord, otherSegment.coord());
|
|
35671
|
-
}],
|
|
35672
|
-
onMove: () => {
|
|
35673
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
35674
|
-
coords: ___default["default"].map(this.points, segment => ___default["default"].invoke(segment, "coord"))
|
|
35675
|
-
});
|
|
35676
|
-
this.onChange({
|
|
35677
|
-
graph: graph
|
|
35678
|
-
});
|
|
35679
|
-
},
|
|
35680
|
-
normalStyle: {
|
|
35681
|
-
fill: segmentColors[segmentIndex]
|
|
35682
|
-
},
|
|
35683
|
-
highlightStyle: {
|
|
35684
|
-
fill: segmentColors[segmentIndex]
|
|
35685
|
-
}
|
|
35686
|
-
}));
|
|
35687
|
-
}
|
|
35688
|
-
return segmentPoints;
|
|
35689
|
-
});
|
|
35690
|
-
this.lines = ___default["default"].map(points, (segmentPoints, segmentIndex) => {
|
|
35691
|
-
return Interactive2.addMovableLine(graphie, {
|
|
35692
|
-
points: segmentPoints,
|
|
35693
|
-
static: true,
|
|
35694
|
-
extendLine: true,
|
|
35695
|
-
normalStyle: {
|
|
35696
|
-
stroke: segmentColors[segmentIndex]
|
|
35697
|
-
}
|
|
35698
|
-
});
|
|
35699
|
-
});
|
|
35700
|
-
};
|
|
35701
|
-
removeLinearSystemControls = () => {
|
|
35702
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
35703
|
-
___default["default"].invoke(this.lines, "remove");
|
|
35704
|
-
___default["default"].map(this.points, segment => ___default["default"].invoke(segment, "remove"));
|
|
35705
|
-
};
|
|
35706
|
-
isCoordInTrash = coord => {
|
|
35707
|
-
if (this.props.apiOptions.isMobile) {
|
|
35708
|
-
return false;
|
|
35709
|
-
}
|
|
35710
|
-
const graphie = this.graphie;
|
|
35711
|
-
const screenPoint = graphie?.scalePoint(coord);
|
|
35712
|
-
return screenPoint[0] >= graphie.xpixels - 40 && screenPoint[1] >= graphie.ypixels - 40;
|
|
35713
|
-
};
|
|
35714
|
-
createPointForPointsType = (coord, i) => {
|
|
35715
|
-
const self = this;
|
|
35716
|
-
const remove = () => {
|
|
35717
|
-
self.points = ___default["default"].filter(self.points, function (pt) {
|
|
35718
|
-
return pt !== point;
|
|
35719
|
-
});
|
|
35720
|
-
// update the correct answer box
|
|
35721
|
-
self.updateCoordsFromPoints();
|
|
35722
|
-
|
|
35723
|
-
// remove this movablePoint from graphie.
|
|
35724
|
-
// we wait to do this until we're not inside of
|
|
35725
|
-
// said point's onMoveEnd method so its state is
|
|
35726
|
-
// consistent throughout this method call
|
|
35727
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35728
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35729
|
-
setTimeout(point.remove.bind(point), 0); // eslint-disable-line no-restricted-syntax
|
|
35730
|
-
};
|
|
35731
|
-
|
|
35732
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35733
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35734
|
-
// to be executed synchronously, which causes this issue.
|
|
35735
|
-
let point = null;
|
|
35736
|
-
point = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35737
|
-
coord: coord,
|
|
35738
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), function (coord) {
|
|
35739
|
-
// TODO(jack): There ought to be a
|
|
35740
|
-
// MovablePoint.constraints.avoid
|
|
35741
|
-
// default that lets you do things like this
|
|
35742
|
-
return ___default["default"].all(self.points, function (pt) {
|
|
35743
|
-
return point === pt || !kmath.point.equal(coord, pt.coord());
|
|
35744
|
-
});
|
|
35745
|
-
}],
|
|
35746
|
-
onMoveStart: function () {
|
|
35747
|
-
if (self.isClickToAddPoints()) {
|
|
35748
|
-
self.setTrashCanVisibility(1);
|
|
35749
|
-
}
|
|
35750
|
-
},
|
|
35751
|
-
onMove: self.updateCoordsFromPoints,
|
|
35752
|
-
onMoveEnd: function (coord) {
|
|
35753
|
-
if (self.isClickToAddPoints()) {
|
|
35754
|
-
if (self.isCoordInTrash(coord)) {
|
|
35755
|
-
remove();
|
|
35756
|
-
}
|
|
35757
|
-
// In case we mouseup'd off the graphie and that
|
|
35758
|
-
// stopped the move (in which case, we might not
|
|
35759
|
-
// be in isCoordInTrash()
|
|
35760
|
-
self.setTrashCanVisibility(0.5);
|
|
35761
|
-
}
|
|
35762
|
-
},
|
|
35763
|
-
...(this.props.apiOptions.isMobile && self.isClickToAddPoints() ? {
|
|
35764
|
-
onRemove: remove
|
|
35765
|
-
} : {})
|
|
35766
|
-
});
|
|
35767
|
-
return point;
|
|
35768
|
-
};
|
|
35769
|
-
removePoint = point => {
|
|
35770
|
-
let index = null;
|
|
35771
|
-
this.points = ___default["default"].filter(this.points, function (pt, i) {
|
|
35772
|
-
if (pt === point) {
|
|
35773
|
-
// @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'null'.
|
|
35774
|
-
index = i;
|
|
35775
|
-
return false;
|
|
35776
|
-
}
|
|
35777
|
-
return true;
|
|
35778
|
-
});
|
|
35779
|
-
return index;
|
|
35780
|
-
};
|
|
35781
|
-
createPointForPolygonType = (coord, i) => {
|
|
35782
|
-
if (this.props.graph.type !== "polygon") {
|
|
35783
|
-
throw makeInvalidTypeError("createPointForPolygonType", "polygon");
|
|
35784
|
-
}
|
|
35785
|
-
|
|
35786
|
-
// TODO(alex): check against "grid" instead, use constants
|
|
35787
|
-
const snapToGrid = !___default["default"].contains(["angles", "sides"], this.props.graph.snapTo);
|
|
35788
|
-
|
|
35789
|
-
// Index relative to current point -> absolute index
|
|
35790
|
-
// NOTE: This does not work when isClickToAddPoints() == true,
|
|
35791
|
-
// as `i` can be changed by dragging a point to the trash
|
|
35792
|
-
// Currently this function is only called when !isClickToAddPoints()
|
|
35793
|
-
const rel = j => {
|
|
35794
|
-
return (i + j + this.points.length) % this.points.length;
|
|
35795
|
-
};
|
|
35796
|
-
const remove = () => {
|
|
35797
|
-
// remove this point from points
|
|
35798
|
-
const index = this.removePoint(point);
|
|
35799
|
-
if (this.polygon?.closed()) {
|
|
35800
|
-
// We should be checking if this.points is defined before rotating them.
|
|
35801
|
-
// @ts-expect-error - TS4104 - The type 'readonly any[]' is 'readonly' and cannot be assigned to the mutable type 'any[]'. | TS2345 - Argument of type 'number | null | undefined' is not assignable to parameter of type 'number | undefined'.
|
|
35802
|
-
this.points = rotate(this.points, index);
|
|
35803
|
-
this.polygon?.update({
|
|
35804
|
-
closed: false
|
|
35805
|
-
});
|
|
35806
|
-
}
|
|
35807
|
-
this.updatePolygon();
|
|
35808
|
-
// the polygon is now unclosed, so we need to
|
|
35809
|
-
// remove any points props
|
|
35810
|
-
this.clearCoords();
|
|
35811
|
-
|
|
35812
|
-
// remove this movablePoint from graphie.
|
|
35813
|
-
// wait to do this until we're not inside of
|
|
35814
|
-
// said point's onMoveEnd method so state is
|
|
35815
|
-
// consistent throughout the method call
|
|
35816
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35817
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35818
|
-
setTimeout(point.remove.bind(point), 0);
|
|
35819
|
-
};
|
|
35820
|
-
const setInitialMove = debounce__default["default"]((point, bool) => {
|
|
35821
|
-
point.state.isInitialMove = bool;
|
|
35822
|
-
}, 1000);
|
|
35823
|
-
const onMoveEndHandler = coord => {
|
|
35824
|
-
// If the user clicked and dragged a point over endpoint,
|
|
35825
|
-
// join the them
|
|
35826
|
-
const overlappedPoints = this.points.length > 1 && (point === this.points[0] && kmath.point.equal(coord, ___default["default"].last(this.points).coord()) || point === ___default["default"].last(this.points) && kmath.point.equal(coord, this.points[0].coord()));
|
|
35827
|
-
if (this.isClickToAddPoints()) {
|
|
35828
|
-
if (this.isCoordInTrash(coord)) {
|
|
35829
|
-
remove();
|
|
35830
|
-
} else if (overlappedPoints) {
|
|
35831
|
-
const pointToRemove = this.points.pop();
|
|
35832
|
-
if (this.points.length > 2) {
|
|
35833
|
-
this.polygon?.update({
|
|
35834
|
-
closed: true
|
|
35835
|
-
});
|
|
35836
|
-
this.updateCoordsFromPoints();
|
|
35837
|
-
} else {
|
|
35838
|
-
this.polygon?.update({
|
|
35839
|
-
closed: false
|
|
35840
|
-
});
|
|
35841
|
-
this.clearCoords();
|
|
35842
|
-
}
|
|
35843
|
-
this.updatePolygon();
|
|
35844
|
-
// remove this movablePoint from graphie.
|
|
35845
|
-
// wait to do this until we're not inside of
|
|
35846
|
-
// said point's onMoveEnd method so state is
|
|
35847
|
-
// consistent throughout the method call
|
|
35848
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35849
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35850
|
-
setTimeout(pointToRemove.remove.bind(pointToRemove), 0);
|
|
35851
|
-
} else {
|
|
35852
|
-
// If the user clicked and dragged a point over any other
|
|
35853
|
-
// existing point, fix shape
|
|
35854
|
-
const shouldRemove = ___default["default"].any(this.points, function (pt) {
|
|
35855
|
-
return pt !== point && kmath.point.equal(pt.coord(), coord);
|
|
35856
|
-
});
|
|
35857
|
-
if (shouldRemove) {
|
|
35858
|
-
this.removePoint(point);
|
|
35859
|
-
if (this.points.length < 3) {
|
|
35860
|
-
this.polygon?.update({
|
|
35861
|
-
closed: false
|
|
35862
|
-
});
|
|
35863
|
-
this.clearCoords();
|
|
35864
|
-
} else if (this.polygon?.closed()) {
|
|
35865
|
-
this.updateCoordsFromPoints();
|
|
35866
|
-
}
|
|
35867
|
-
this.updatePolygon();
|
|
35868
|
-
// remove this movablePoint from graphie.
|
|
35869
|
-
// wait to do this until we're not inside
|
|
35870
|
-
// said point's onMoveEnd method so state
|
|
35871
|
-
// is consistent throughout the method call
|
|
35872
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35873
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35874
|
-
setTimeout(point.remove.bind(point), 0);
|
|
35875
|
-
} else {
|
|
35876
|
-
// If this was
|
|
35877
|
-
// * a click on the first or last point
|
|
35878
|
-
// * and not a deletion
|
|
35879
|
-
// * and not a drag,
|
|
35880
|
-
// * and not a creation of a new point
|
|
35881
|
-
// (see !point.state.isInitialMove, below),
|
|
35882
|
-
// * and our polygon is not closed,
|
|
35883
|
-
// * and we can close it (we need at least the same
|
|
35884
|
-
// number of points as coordinates asked for in the
|
|
35885
|
-
// question),
|
|
35886
|
-
// then close it
|
|
35887
|
-
const isFirstOrLastPoint = point === ___default["default"].head(this.points) || point === ___default["default"].last(this.points);
|
|
35888
|
-
const pointNotMoved = !point.hasMoved();
|
|
35889
|
-
// NOTE (jsatk): Currently different on mobile versus
|
|
35890
|
-
// destkop
|
|
35891
|
-
const notANewPoint = !point.state.isInitialMove;
|
|
35892
|
-
const polygonNotClosed = !this.polygon?.closed();
|
|
35893
|
-
const allPointsPlotted = this.points.length > 2;
|
|
35894
|
-
const canCloseUnclosedPolygon = isFirstOrLastPoint && pointNotMoved && notANewPoint && polygonNotClosed && allPointsPlotted;
|
|
35895
|
-
if (canCloseUnclosedPolygon) {
|
|
35896
|
-
this.polygon?.update({
|
|
35897
|
-
closed: true
|
|
35898
|
-
});
|
|
35899
|
-
this.updatePolygon();
|
|
35900
|
-
|
|
35901
|
-
// We finally have a closed polygon, so save our
|
|
35902
|
-
// points to props
|
|
35903
|
-
this.updateCoordsFromPoints();
|
|
35904
|
-
}
|
|
35905
|
-
}
|
|
35906
|
-
}
|
|
35907
|
-
|
|
35908
|
-
// In case we mouseup'd off the graphie and that
|
|
35909
|
-
// stopped the move
|
|
35910
|
-
this.setTrashCanVisibility(0.5);
|
|
35911
|
-
}
|
|
35912
|
-
setInitialMove(point, false);
|
|
35913
|
-
};
|
|
35914
|
-
const graphConstraint = coord => {
|
|
35915
|
-
// These constraints are all relative to the other points, so if
|
|
35916
|
-
// we're creating the initial points and haven't added any others
|
|
35917
|
-
// to the graph, we can't enforce them.
|
|
35918
|
-
if (this.points == null || this.points.length === 0) {
|
|
35919
|
-
return true;
|
|
35920
|
-
}
|
|
35921
|
-
const coords = ___default["default"].invoke(this.points, "coord");
|
|
35922
|
-
coords[i] = coord;
|
|
35923
|
-
|
|
35924
|
-
// Check for invalid positioning, but only if we aren't adding
|
|
35925
|
-
// points one click at a time, since those added points could
|
|
35926
|
-
// have already violated these constraints
|
|
35927
|
-
if (!this.isClickToAddPoints()) {
|
|
35928
|
-
// Polygons can't have consecutive collinear points
|
|
35929
|
-
if (collinear(coords[rel(-2)], coords[rel(-1)], coords[i]) || collinear(coords[rel(-1)], coords[i], coords[rel(1)]) || collinear(coords[i], coords[rel(1)], coords[rel(2)])) {
|
|
35930
|
-
return false;
|
|
35931
|
-
}
|
|
35932
|
-
const segments = ___default["default"].zip(coords, rotate(coords));
|
|
35933
|
-
if (this.points.length > 3) {
|
|
35934
|
-
// Constrain to simple (non self-intersecting) polygon by
|
|
35935
|
-
// testing whether adjacent segments intersect any others
|
|
35936
|
-
for (let j = -1; j <= 0; j++) {
|
|
35937
|
-
const segment = segments[rel(j)];
|
|
35938
|
-
const others = ___default["default"].without(segments, segment, segments[rel(j - 1)], segments[rel(j + 1)]);
|
|
35939
|
-
for (let k = 0; k < others.length; k++) {
|
|
35940
|
-
const other = others[k];
|
|
35941
|
-
// @ts-expect-error - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'Line'.
|
|
35942
|
-
if (intersects(segment, other)) {
|
|
35943
|
-
return false;
|
|
35944
|
-
}
|
|
35945
|
-
}
|
|
35946
|
-
}
|
|
35947
|
-
}
|
|
35948
|
-
}
|
|
35949
|
-
if (
|
|
35950
|
-
// @ts-expect-error - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
|
|
35951
|
-
this.props.graph.snapTo === "angles" && this.points.length > 2) {
|
|
35952
|
-
// Snap to whole degree interior angles
|
|
35953
|
-
|
|
35954
|
-
const angles = ___default["default"].map(angleMeasures(coords), function (rad) {
|
|
35955
|
-
return rad * 180 / Math.PI;
|
|
35956
|
-
});
|
|
35957
|
-
___default["default"].each([-1, 1], function (j) {
|
|
35958
|
-
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
35959
|
-
});
|
|
35960
|
-
const getAngle = function (a, vertex, b) {
|
|
35961
|
-
const angle = GraphUtils.findAngleDeprecated(coords[rel(a)], coords[rel(b)], coords[rel(vertex)]);
|
|
35962
|
-
return (angle + 360) % 360;
|
|
35963
|
-
};
|
|
35964
|
-
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
35965
|
-
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
35966
|
-
|
|
35967
|
-
// Avoid degenerate triangles
|
|
35968
|
-
if (___default["default"].any(innerAngles, function (angle) {
|
|
35969
|
-
return leq(angle, 1);
|
|
35970
|
-
})) {
|
|
35971
|
-
return false;
|
|
35972
|
-
}
|
|
35973
|
-
const knownSide = magnitude(
|
|
35974
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
35975
|
-
vector(coords[rel(-1)], coords[rel(1)]));
|
|
35976
|
-
const onLeft = sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
35977
|
-
|
|
35978
|
-
// Solve for side by using the law of sines
|
|
35979
|
-
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
35980
|
-
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
35981
|
-
const offset = polar$2(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
35982
|
-
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
35983
|
-
}
|
|
35984
|
-
// @ts-expect-error - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
|
|
35985
|
-
if (this.props.graph.snapTo === "sides" && this.points.length > 1) {
|
|
35986
|
-
// Snap to whole unit side measures
|
|
35987
|
-
|
|
35988
|
-
const sides = ___default["default"].map([[coords[rel(-1)], coords[i]], [coords[i], coords[rel(1)]], [coords[rel(-1)], coords[rel(1)]]], function (coords) {
|
|
35989
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
35990
|
-
return magnitude(vector(...coords));
|
|
35991
|
-
});
|
|
35992
|
-
___default["default"].each([0, 1], function (j) {
|
|
35993
|
-
sides[j] = Math.round(sides[j]);
|
|
35994
|
-
});
|
|
35995
|
-
|
|
35996
|
-
// Avoid degenerate triangles
|
|
35997
|
-
if (leq(sides[1] + sides[2], sides[0]) || leq(sides[0] + sides[2], sides[1]) || leq(sides[0] + sides[1], sides[2])) {
|
|
35998
|
-
return false;
|
|
35999
|
-
}
|
|
36000
|
-
|
|
36001
|
-
// Solve for angle by using the law of cosines
|
|
36002
|
-
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
36003
|
-
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
36004
|
-
const onLeft = sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
36005
|
-
const offset = polar$2(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
36006
|
-
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
36007
|
-
}
|
|
36008
|
-
// Snap to grid (already done)
|
|
36009
|
-
return true;
|
|
36010
|
-
};
|
|
36011
|
-
const point = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
36012
|
-
coord: coord,
|
|
36013
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), snapToGrid ? Interactive2.MovablePoint.constraints.snap() : null, graphConstraint],
|
|
36014
|
-
onMoveStart: () => {
|
|
36015
|
-
if (this.isClickToAddPoints()) {
|
|
36016
|
-
this.setTrashCanVisibility(1);
|
|
36017
|
-
}
|
|
36018
|
-
},
|
|
36019
|
-
onMove: () => {
|
|
36020
|
-
if (this.polygon?.closed()) {
|
|
36021
|
-
this.updateCoordsFromPoints();
|
|
36022
|
-
}
|
|
36023
|
-
},
|
|
36024
|
-
onMoveEnd: onMoveEndHandler,
|
|
36025
|
-
...(this.props.apiOptions.isMobile && this.isClickToAddPoints() ? {
|
|
36026
|
-
onRemove: remove
|
|
36027
|
-
} : {})
|
|
36028
|
-
});
|
|
36029
|
-
point.state.isInitialMove = true;
|
|
36030
|
-
return point;
|
|
36031
|
-
};
|
|
36032
|
-
updateCoordsFromPoints = () => {
|
|
36033
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
36034
|
-
// Handle old movable points with .coord, or
|
|
36035
|
-
// Interactive2.MovablePoint's with .coord()
|
|
36036
|
-
coords: ___default["default"].map(this.points, function (point) {
|
|
36037
|
-
return ___default["default"].result(point, "coord");
|
|
36038
|
-
})
|
|
36039
|
-
});
|
|
36040
|
-
this.onChange({
|
|
36041
|
-
graph: graph
|
|
36042
|
-
});
|
|
36043
|
-
};
|
|
36044
|
-
clearCoords = () => {
|
|
36045
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
36046
|
-
coords: null
|
|
36047
|
-
});
|
|
36048
|
-
this.onChange({
|
|
36049
|
-
graph: graph
|
|
36050
|
-
});
|
|
36051
|
-
};
|
|
36052
|
-
onChange = data => {
|
|
36053
|
-
this.props.onChange(data);
|
|
36054
|
-
this.props.trackInteraction();
|
|
36055
|
-
};
|
|
36056
|
-
addPointControls = () => {
|
|
36057
|
-
if (this.props.graph.type !== "point") {
|
|
36058
|
-
throw makeInvalidTypeError("addPointControls", "point");
|
|
36059
|
-
}
|
|
36060
|
-
const coords = InteractiveGraph.getPointCoords(this.props.graph, this.props);
|
|
36061
|
-
// Clear out our old points so that newly added points don't
|
|
36062
|
-
// "collide" with them and reposition when being added
|
|
36063
|
-
// Without this, when added, each point checks whether it is on top
|
|
36064
|
-
// of a point in this.points, which (a) shouldn't matter since
|
|
36065
|
-
// we're clearing out this.points anyways, and (b) can cause problems
|
|
36066
|
-
// if each of this.points is a MovablePoint instead of an
|
|
36067
|
-
// Interactive2.MovablePoint, since one has a .coord and the other
|
|
36068
|
-
// has .coord()
|
|
36069
|
-
// TODO(jack): Figure out a better way to do this
|
|
36070
|
-
this.points = [];
|
|
36071
|
-
this.points = ___default["default"].map(coords, this.createPointForPointsType, this);
|
|
36072
|
-
};
|
|
36073
|
-
removePointControls = () => {
|
|
36074
|
-
___default["default"].invoke(this.points, "remove");
|
|
36075
|
-
};
|
|
36076
|
-
addSegmentControls = () => {
|
|
36077
|
-
if (this.props.graph.type !== "segment") {
|
|
36078
|
-
throw makeInvalidTypeError("addSegmentControls", "segment");
|
|
36079
|
-
}
|
|
36080
|
-
const self = this;
|
|
36081
|
-
const graphie = this.graphie;
|
|
36082
|
-
const coords = InteractiveGraph.getSegmentCoords(this.props.graph, this.props);
|
|
36083
|
-
const createPoint = options => Interactive2.addMaybeMobileMovablePoint(this, options);
|
|
36084
|
-
this.points = [];
|
|
36085
|
-
this.lines = ___default["default"].map(coords, function (segment, i) {
|
|
36086
|
-
const updateCoordProps = function () {
|
|
36087
|
-
const graph = ___default["default"].extend({}, self.props.graph, {
|
|
36088
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
36089
|
-
coords: ___default["default"].invoke(self.lines, "coords")
|
|
36090
|
-
});
|
|
36091
|
-
self.onChange({
|
|
36092
|
-
graph: graph
|
|
36093
|
-
});
|
|
36094
|
-
};
|
|
36095
|
-
|
|
36096
|
-
// NOTE(jeresig): This code attempts to access the variable
|
|
36097
|
-
// while it's still being defined, it should be refactored!
|
|
36098
|
-
// The callbacks appear to be executed synchronously, which
|
|
36099
|
-
// causes this issue.
|
|
36100
|
-
const points = [];
|
|
36101
|
-
for (let i = 0; i < segment.length; i += 1) {
|
|
36102
|
-
const coord = segment[i];
|
|
36103
|
-
points.push(createPoint({
|
|
36104
|
-
coord: coord,
|
|
36105
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
36106
|
-
const otherPoint = points[1 - i];
|
|
36107
|
-
if (!otherPoint) {
|
|
36108
|
-
// point hasn't been defined yet
|
|
36109
|
-
// because we're still creating them
|
|
36110
|
-
// We return false as the points aren't
|
|
36111
|
-
// going to be in the same position at
|
|
36112
|
-
// this point.
|
|
36113
|
-
return false;
|
|
36114
|
-
}
|
|
36115
|
-
return !kmath.point.equal(coord, otherPoint.coord());
|
|
36116
|
-
}],
|
|
36117
|
-
onMove: updateCoordProps
|
|
36118
|
-
}));
|
|
36119
|
-
}
|
|
36120
|
-
self.points = self.points.concat(points);
|
|
36121
|
-
const line = Interactive2.addMovableLine(graphie, {
|
|
36122
|
-
points: points,
|
|
36123
|
-
static: false,
|
|
36124
|
-
constraints: [
|
|
36125
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
36126
|
-
Interactive2.MovableLine.constraints.bound(),
|
|
36127
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
36128
|
-
Interactive2.MovableLine.constraints.snap()],
|
|
36129
|
-
onMove: [
|
|
36130
|
-
// @ts-expect-error - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
36131
|
-
Interactive2.MovableLine.onMove.updatePoints, updateCoordProps],
|
|
36132
|
-
normalStyle: {
|
|
36133
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36134
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTIVE,
|
|
36135
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36136
|
-
...this._lineStroke()
|
|
36137
|
-
},
|
|
36138
|
-
highlightStyle: {
|
|
36139
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36140
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTING,
|
|
36141
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36142
|
-
...this._lineStroke()
|
|
36143
|
-
}
|
|
36144
|
-
});
|
|
36145
|
-
___default["default"].invoke(points, "toFront");
|
|
36146
|
-
return line;
|
|
36147
|
-
}, this);
|
|
36148
|
-
};
|
|
36149
|
-
removeSegmentControls = () => {
|
|
36150
|
-
___default["default"].invoke(this.points, "remove");
|
|
36151
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
36152
|
-
___default["default"].invoke(this.lines, "remove");
|
|
36153
|
-
};
|
|
36154
|
-
addRayControls = () => {
|
|
36155
|
-
this.addLine("ray");
|
|
36156
|
-
};
|
|
36157
|
-
removeRayControls = () => {
|
|
36158
|
-
this.removeLine();
|
|
36159
|
-
};
|
|
36160
|
-
addPolygonControls = () => {
|
|
36161
|
-
this.polygon = null;
|
|
36162
|
-
const coords = InteractiveGraph.getPolygonCoords(this.props.graph, this.props);
|
|
36163
|
-
// Clear out our old points so that newly added points don't
|
|
36164
|
-
// "collide", as in `addPointControls`
|
|
36165
|
-
this.points = [];
|
|
36166
|
-
this.points = ___default["default"].map(coords, this.createPointForPolygonType, this);
|
|
36167
|
-
this.updatePolygon();
|
|
36168
|
-
};
|
|
36169
|
-
updatePolygon = () => {
|
|
36170
|
-
if (this.props.graph.type !== "polygon") {
|
|
36171
|
-
throw makeInvalidTypeError("updatePolygon", "polygon");
|
|
36172
|
-
}
|
|
36173
|
-
let closed;
|
|
36174
|
-
if (this.polygon) {
|
|
36175
|
-
closed = this.polygon.closed();
|
|
36176
|
-
} else if (this.points.length >= 3) {
|
|
36177
|
-
closed = true;
|
|
36178
|
-
} else {
|
|
36179
|
-
// There will only be fewer than 3 points in click-to-add-vertices
|
|
36180
|
-
// mode, so we don't need to explicitly check for that here.
|
|
36181
|
-
closed = false;
|
|
36182
|
-
}
|
|
36183
|
-
const graphie = this.graphie;
|
|
36184
|
-
const n = this.points.length;
|
|
36185
|
-
|
|
36186
|
-
// TODO(alex): check against "grid" instead, use constants
|
|
36187
|
-
const snapToGrid = !___default["default"].contains(["angles", "sides"], this.props.graph.snapTo);
|
|
36188
|
-
const angleLabels = ___default["default"].times(n, function (i) {
|
|
36189
|
-
if (
|
|
36190
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36191
|
-
!this.props.graph.showAngles || !closed && (i === 0 || i === n - 1)) {
|
|
36192
|
-
return "";
|
|
36193
|
-
}
|
|
36194
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36195
|
-
if (this.props.graph.snapTo === "angles") {
|
|
36196
|
-
return "$deg0";
|
|
36197
|
-
}
|
|
36198
|
-
return "$deg1";
|
|
36199
|
-
}, this);
|
|
36200
|
-
const showRightAngleMarkers = ___default["default"].times(n, function (i) {
|
|
36201
|
-
return closed || i !== 0 && i !== n - 1;
|
|
36202
|
-
}, this);
|
|
36203
|
-
const numArcs = ___default["default"].times(n, function (i) {
|
|
36204
|
-
if (
|
|
36205
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36206
|
-
this.props.graph.showAngles && (closed || i !== 0 && i !== n - 1)) {
|
|
36207
|
-
return 1;
|
|
36208
|
-
}
|
|
36209
|
-
return 0;
|
|
36210
|
-
}, this);
|
|
36211
|
-
const sideLabels = ___default["default"].times(n, function (i) {
|
|
36212
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36213
|
-
if (!this.props.graph.showSides || !closed && i === n - 1) {
|
|
36214
|
-
return "";
|
|
36215
|
-
}
|
|
36216
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36217
|
-
if (this.props.graph.snapTo === "sides") {
|
|
36218
|
-
return "$len0";
|
|
36219
|
-
}
|
|
36220
|
-
return "$len1";
|
|
36221
|
-
}, this);
|
|
36222
|
-
if (this.polygon == null) {
|
|
36223
|
-
const self = this;
|
|
36224
|
-
self.polygon = Interactive2.addMovablePolygon(graphie, {
|
|
36225
|
-
constraints: [
|
|
36226
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
36227
|
-
Interactive2.MovablePolygon.constraints.bound(), snapToGrid ?
|
|
36228
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
36229
|
-
Interactive2.MovablePolygon.constraints.snap() : null],
|
|
36230
|
-
closed: closed,
|
|
36231
|
-
points: self.points,
|
|
36232
|
-
angleLabels: angleLabels,
|
|
36233
|
-
showRightAngleMarkers: showRightAngleMarkers,
|
|
36234
|
-
numArcs: numArcs,
|
|
36235
|
-
sideLabels: sideLabels,
|
|
36236
|
-
onMove: [
|
|
36237
|
-
// @ts-expect-error - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
36238
|
-
Interactive2.MovablePolygon.onMove.updatePoints, function () {
|
|
36239
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36240
|
-
if (this.closed()) {
|
|
36241
|
-
self.updateCoordsFromPoints();
|
|
36242
|
-
}
|
|
36243
|
-
}],
|
|
36244
|
-
normalStyle: {
|
|
36245
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTIVE,
|
|
36246
|
-
...this._lineStroke()
|
|
36247
|
-
}
|
|
36248
|
-
});
|
|
36249
|
-
} else {
|
|
36250
|
-
// We only need to pass in the properties that might've changed
|
|
36251
|
-
this.polygon.update({
|
|
36252
|
-
closed: closed,
|
|
36253
|
-
points: this.points,
|
|
36254
|
-
angleLabels: angleLabels,
|
|
36255
|
-
showRightAngleMarkers: showRightAngleMarkers,
|
|
36256
|
-
numArcs: numArcs,
|
|
36257
|
-
sideLabels: sideLabels
|
|
36258
|
-
});
|
|
36259
|
-
}
|
|
36260
|
-
};
|
|
36261
|
-
removePolygonControls = () => {
|
|
36262
|
-
___default["default"].invoke(this.points, "remove");
|
|
36263
|
-
this.polygon?.remove();
|
|
36264
|
-
};
|
|
36265
|
-
addAngleControls = () => {
|
|
36266
|
-
if (this.props.graph.type !== "angle") {
|
|
36267
|
-
throw makeInvalidTypeError("addAngleControls", "angle");
|
|
36268
|
-
}
|
|
36269
|
-
const graph = this.props.graph;
|
|
36270
|
-
const graphie = this.graphie;
|
|
36271
|
-
const coords = InteractiveGraph.getAngleCoords(this.props.graph, this.props);
|
|
36272
|
-
|
|
36273
|
-
// The vertex snaps to the grid, but the rays don't...
|
|
36274
|
-
this.points = coords.map(function (coord, i) {
|
|
36275
|
-
return graphie?.addMovablePoint(___default["default"].extend({
|
|
36276
|
-
coord: coord,
|
|
36277
|
-
normalStyle: {
|
|
36278
|
-
stroke: KhanColors.INTERACTIVE,
|
|
36279
|
-
fill: KhanColors.INTERACTIVE
|
|
36280
|
-
}
|
|
36281
|
-
}, i === 1 ? {
|
|
36282
|
-
snapX: graphie.snap[0],
|
|
36283
|
-
snapY: graphie.snap[1]
|
|
36284
|
-
} : {}));
|
|
36285
|
-
});
|
|
36286
|
-
|
|
36287
|
-
// ...they snap to whole-degree angles from the vertex.
|
|
36288
|
-
this.angle = graphie?.addMovableAngle({
|
|
36289
|
-
points: this.points,
|
|
36290
|
-
snapDegrees: graph.snapDegrees || 1,
|
|
36291
|
-
snapOffsetDeg: graph.angleOffsetDeg || 0,
|
|
36292
|
-
angleLabel: graph.showAngles ? "$deg0" : "",
|
|
36293
|
-
pushOut: 2,
|
|
36294
|
-
allowReflex: defaultVal(graph.allowReflexAngles, true)
|
|
36295
|
-
});
|
|
36296
|
-
$__default["default"](this.angle).on("move", () => {
|
|
36297
|
-
this.onChange({
|
|
36298
|
-
graph: {
|
|
36299
|
-
...graph,
|
|
36300
|
-
coords: this.angle?.getClockwiseCoords()
|
|
36301
|
-
}
|
|
36302
|
-
});
|
|
36303
|
-
});
|
|
36304
|
-
};
|
|
36305
|
-
removeAngleControls = () => {
|
|
36306
|
-
___default["default"].invoke(this.points, "remove");
|
|
36307
|
-
this.angle?.remove();
|
|
36308
|
-
};
|
|
36309
|
-
toggleShowSides = () => {
|
|
36310
|
-
if (this.props.graph.type !== "polygon") {
|
|
36311
|
-
throw makeInvalidTypeError("toggleShowSides", "polygon");
|
|
36312
|
-
}
|
|
36313
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
36314
|
-
showSides: !this.props.graph.showSides
|
|
36315
|
-
});
|
|
36316
|
-
this.onChange({
|
|
36317
|
-
graph: graph
|
|
36318
|
-
});
|
|
36319
|
-
};
|
|
36320
|
-
getUserInput() {
|
|
36321
|
-
return InteractiveGraph.getUserInputFromProps(this.props);
|
|
36322
|
-
}
|
|
36323
|
-
focus = $__default["default"].noop;
|
|
36324
|
-
render() {
|
|
36325
|
-
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
36326
|
-
const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
|
|
36327
|
-
const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
|
|
36328
|
-
let instructions;
|
|
36329
|
-
// isClickToAddPoints() only applies to points and polygons
|
|
36330
|
-
if (this.isClickToAddPoints() && this.state.shouldShowInstructions) {
|
|
36331
|
-
if (this.props.graph.type === "point") {
|
|
36332
|
-
instructions = this.context.strings.addPoints;
|
|
36333
|
-
} else if (this.props.graph.type === "polygon") {
|
|
36334
|
-
instructions = this.context.strings.addVertices;
|
|
36335
|
-
}
|
|
36336
|
-
} else {
|
|
36337
|
-
instructions = undefined;
|
|
36338
|
-
}
|
|
36339
|
-
let onMouseDown;
|
|
36340
|
-
if (this.isClickToAddPoints()) {
|
|
36341
|
-
onMouseDown = this.handleAddPointsMouseDown;
|
|
36342
|
-
}
|
|
36343
|
-
const isMobile = this.props.apiOptions.isMobile;
|
|
36344
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
36345
|
-
className: "perseus-widget " + "perseus-widget-interactive-graph",
|
|
36346
|
-
style: {
|
|
36347
|
-
width: box[0],
|
|
36348
|
-
height: box[1]
|
|
36349
|
-
}
|
|
36350
|
-
}, /*#__PURE__*/React__namespace.createElement(Graph, {
|
|
36351
|
-
instructions: instructions
|
|
36352
|
-
// eslint-disable-next-line react/no-string-refs
|
|
36353
|
-
,
|
|
36354
|
-
ref: "graph",
|
|
36355
|
-
box: box,
|
|
36356
|
-
labels: this.props.labels,
|
|
36357
|
-
range: this.props.range,
|
|
36358
|
-
step: isMobile ? Util.constrainedTickStepsFromTickSteps(this.props.step, this.props.range) : this.props.step,
|
|
36359
|
-
gridStep: gridStep,
|
|
36360
|
-
snapStep: snapStep,
|
|
36361
|
-
markings: this.props.markings,
|
|
36362
|
-
backgroundImage: this.props.backgroundImage,
|
|
36363
|
-
showProtractor: this.props.showProtractor,
|
|
36364
|
-
onMouseDown: onMouseDown,
|
|
36365
|
-
onGraphieUpdated: this.setGraphie,
|
|
36366
|
-
setDrawingAreaAvailable: this.props.apiOptions.setDrawingAreaAvailable,
|
|
36367
|
-
isMobile: isMobile
|
|
36368
|
-
}));
|
|
36369
|
-
}
|
|
36370
|
-
}
|
|
36371
35472
|
class InteractiveGraph extends React__namespace.Component {
|
|
36372
|
-
legacyGraphRef = /*#__PURE__*/React__namespace.createRef();
|
|
36373
35473
|
mafsRef = /*#__PURE__*/React__namespace.createRef();
|
|
36374
35474
|
static defaultProps = {
|
|
36375
35475
|
labels: ["x", "y"],
|
|
@@ -36387,34 +35487,23 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
36387
35487
|
if (this.mafsRef.current?.getUserInput) {
|
|
36388
35488
|
return this.mafsRef.current.getUserInput();
|
|
36389
35489
|
}
|
|
36390
|
-
if (this.legacyGraphRef.current?.getUserInput) {
|
|
36391
|
-
return this.legacyGraphRef.current.getUserInput();
|
|
36392
|
-
}
|
|
36393
35490
|
throw new perseusCore.PerseusError("Cannot getUserInput from a graph that has never rendered", perseusCore.Errors.NotAllowed);
|
|
36394
35491
|
}
|
|
36395
35492
|
getPromptJSON() {
|
|
36396
35493
|
return getPromptJSON$b(this.props, this.getUserInput());
|
|
36397
35494
|
}
|
|
36398
35495
|
render() {
|
|
36399
|
-
|
|
36400
|
-
const
|
|
36401
|
-
|
|
36402
|
-
|
|
36403
|
-
|
|
36404
|
-
|
|
36405
|
-
|
|
36406
|
-
|
|
36407
|
-
|
|
36408
|
-
|
|
36409
|
-
|
|
36410
|
-
box: box,
|
|
36411
|
-
showTooltips: !!this.props.showTooltips,
|
|
36412
|
-
readOnly: this.props.apiOptions?.readOnly
|
|
36413
|
-
}));
|
|
36414
|
-
}
|
|
36415
|
-
return /*#__PURE__*/React__namespace.createElement(LegacyInteractiveGraph, _extends({
|
|
36416
|
-
ref: this.legacyGraphRef
|
|
36417
|
-
}, this.props));
|
|
35496
|
+
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
35497
|
+
const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
|
|
35498
|
+
const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
|
|
35499
|
+
return /*#__PURE__*/React__namespace.createElement(StatefulMafsGraph, _extends({}, this.props, {
|
|
35500
|
+
ref: this.mafsRef,
|
|
35501
|
+
gridStep: gridStep,
|
|
35502
|
+
snapStep: snapStep,
|
|
35503
|
+
box: box,
|
|
35504
|
+
showTooltips: !!this.props.showTooltips,
|
|
35505
|
+
readOnly: this.props.apiOptions?.readOnly
|
|
35506
|
+
}));
|
|
36418
35507
|
}
|
|
36419
35508
|
|
|
36420
35509
|
/**
|
|
@@ -36591,8 +35680,32 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
36591
35680
|
}
|
|
36592
35681
|
static getEquationString(props) {
|
|
36593
35682
|
const type = props.graph.type;
|
|
36594
|
-
|
|
36595
|
-
|
|
35683
|
+
switch (type) {
|
|
35684
|
+
case "none":
|
|
35685
|
+
return InteractiveGraph.getNoneEquationString();
|
|
35686
|
+
case "linear":
|
|
35687
|
+
return InteractiveGraph.getLinearEquationString(props);
|
|
35688
|
+
case "quadratic":
|
|
35689
|
+
return InteractiveGraph.getQuadraticEquationString(props);
|
|
35690
|
+
case "sinusoid":
|
|
35691
|
+
return InteractiveGraph.getSinusoidEquationString(props);
|
|
35692
|
+
case "circle":
|
|
35693
|
+
return InteractiveGraph.getCircleEquationString(props);
|
|
35694
|
+
case "linear-system":
|
|
35695
|
+
return InteractiveGraph.getLinearSystemEquationString(props);
|
|
35696
|
+
case "point":
|
|
35697
|
+
return InteractiveGraph.getPointEquationString(props);
|
|
35698
|
+
case "segment":
|
|
35699
|
+
return InteractiveGraph.getSegmentEquationString(props);
|
|
35700
|
+
case "ray":
|
|
35701
|
+
return InteractiveGraph.getRayEquationString(props);
|
|
35702
|
+
case "polygon":
|
|
35703
|
+
return InteractiveGraph.getPolygonEquationString(props);
|
|
35704
|
+
case "angle":
|
|
35705
|
+
return InteractiveGraph.getAngleEquationString(props);
|
|
35706
|
+
default:
|
|
35707
|
+
throw new wonderStuffCore.UnreachableCaseError(type);
|
|
35708
|
+
}
|
|
36596
35709
|
}
|
|
36597
35710
|
static pointsFromNormalized(props, coordsList, noSnap) {
|
|
36598
35711
|
// @ts-expect-error - TS2322 - Type 'number[][]' is not assignable to type 'readonly Coord[]'.
|
|
@@ -36730,28 +35843,6 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
36730
35843
|
}
|
|
36731
35844
|
}
|
|
36732
35845
|
|
|
36733
|
-
// exported for testing
|
|
36734
|
-
function shouldUseMafs(mafsFlags, graph) {
|
|
36735
|
-
if (typeof mafsFlags === "boolean" || typeof mafsFlags === "undefined") {
|
|
36736
|
-
return false;
|
|
36737
|
-
}
|
|
36738
|
-
switch (graph.type) {
|
|
36739
|
-
case "none":
|
|
36740
|
-
return true;
|
|
36741
|
-
case "point":
|
|
36742
|
-
if (graph.numPoints === UNLIMITED) {
|
|
36743
|
-
return Boolean(mafsFlags["unlimited-point"]);
|
|
36744
|
-
}
|
|
36745
|
-
return Boolean(mafsFlags["point"]);
|
|
36746
|
-
case "polygon":
|
|
36747
|
-
if (graph.numSides === UNLIMITED) {
|
|
36748
|
-
return Boolean(mafsFlags["unlimited-polygon"]);
|
|
36749
|
-
}
|
|
36750
|
-
return Boolean(mafsFlags["polygon"]);
|
|
36751
|
-
default:
|
|
36752
|
-
return Boolean(mafsFlags[graph.type]);
|
|
36753
|
-
}
|
|
36754
|
-
}
|
|
36755
35846
|
// We don't need to change any of the original props for static mode
|
|
36756
35847
|
const staticTransform$3 = ___default["default"].identity;
|
|
36757
35848
|
var InteractiveGraph$1 = {
|
|
@@ -37394,10 +36485,15 @@ class LabelImage extends React__namespace.Component {
|
|
|
37394
36485
|
});
|
|
37395
36486
|
}
|
|
37396
36487
|
activateMarker(index, opened) {
|
|
36488
|
+
// TODO(LEMS-2830): Remove analytics event in LEMS-2830 in favor of ti below.
|
|
37397
36489
|
this.props.analytics?.onAnalyticsEvent({
|
|
37398
36490
|
type: "perseus:label-image:marker-interacted-with",
|
|
37399
36491
|
payload: null
|
|
37400
36492
|
});
|
|
36493
|
+
this.props.analytics?.onAnalyticsEvent({
|
|
36494
|
+
type: "perseus:label-image:marker-interacted-with:ti",
|
|
36495
|
+
payload: null
|
|
36496
|
+
});
|
|
37401
36497
|
const {
|
|
37402
36498
|
activeMarkerIndex
|
|
37403
36499
|
} = this.state;
|
|
@@ -37541,10 +36637,15 @@ class LabelImage extends React__namespace.Component {
|
|
|
37541
36637
|
})),
|
|
37542
36638
|
multipleSelect: this.props.multipleAnswers,
|
|
37543
36639
|
onChange: selection => {
|
|
36640
|
+
// TODO(LEMS-2829): Remove analytics event in LEMS-2829 in favor of ti below.
|
|
37544
36641
|
this.props.analytics?.onAnalyticsEvent({
|
|
37545
36642
|
type: "perseus:label-image:choiced-interacted-with",
|
|
37546
36643
|
payload: null
|
|
37547
36644
|
});
|
|
36645
|
+
this.props.analytics?.onAnalyticsEvent({
|
|
36646
|
+
type: "perseus:label-image:choiced-interacted-with:ti",
|
|
36647
|
+
payload: null
|
|
36648
|
+
});
|
|
37548
36649
|
this.handleAnswerChoicesChangeForMarker(index, selection);
|
|
37549
36650
|
},
|
|
37550
36651
|
onToggle: opened => this.activateMarker(index, opened)
|
|
@@ -37649,10 +36750,15 @@ class LabelImage extends React__namespace.Component {
|
|
|
37649
36750
|
})), this.renderMarkers()), /*#__PURE__*/React__namespace.createElement(HideAnswersToggle, {
|
|
37650
36751
|
areAnswersHidden: this.state.hideAnswers,
|
|
37651
36752
|
onChange: hideAnswers => {
|
|
36753
|
+
// TODO(LEMS-2831): Remove analytics event in LEMS-2831 in favor of ti below.
|
|
37652
36754
|
this.props.analytics?.onAnalyticsEvent({
|
|
37653
36755
|
type: "perseus:label-image:toggle-answers-hidden",
|
|
37654
36756
|
payload: null
|
|
37655
36757
|
});
|
|
36758
|
+
this.props.analytics?.onAnalyticsEvent({
|
|
36759
|
+
type: "perseus:label-image:toggle-answers-hidden:ti",
|
|
36760
|
+
payload: null
|
|
36761
|
+
});
|
|
37656
36762
|
this.setState({
|
|
37657
36763
|
hideAnswers
|
|
37658
36764
|
});
|
|
@@ -37715,8 +36821,7 @@ var LabelImage$1 = {
|
|
|
37715
36821
|
displayName: "Label Image",
|
|
37716
36822
|
widget: LabelImageWithDependencies,
|
|
37717
36823
|
accessible: true,
|
|
37718
|
-
isLintable: true
|
|
37719
|
-
getPublicWidgetOptions: perseusCore.getLabelImagePublicWidgetOptions
|
|
36824
|
+
isLintable: true
|
|
37720
36825
|
};
|
|
37721
36826
|
|
|
37722
36827
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -37975,20 +37080,24 @@ class Draggable extends React__namespace.Component {
|
|
|
37975
37080
|
if (this.props.margin != null) {
|
|
37976
37081
|
style.margin = this.props.margin;
|
|
37977
37082
|
}
|
|
37978
|
-
return
|
|
37979
|
-
|
|
37980
|
-
|
|
37981
|
-
|
|
37982
|
-
|
|
37983
|
-
|
|
37984
|
-
|
|
37985
|
-
|
|
37986
|
-
|
|
37987
|
-
|
|
37988
|
-
|
|
37989
|
-
|
|
37990
|
-
|
|
37991
|
-
|
|
37083
|
+
return (
|
|
37084
|
+
/*#__PURE__*/
|
|
37085
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
37086
|
+
React__namespace.createElement("li", {
|
|
37087
|
+
className: className,
|
|
37088
|
+
style: style,
|
|
37089
|
+
onMouseDown: this.onMouseDown,
|
|
37090
|
+
onTouchStart: this.onMouseDown,
|
|
37091
|
+
onTouchMove: this.onMouseMove,
|
|
37092
|
+
onTouchEnd: this.onMouseUp,
|
|
37093
|
+
onTouchCancel: this.onMouseUp
|
|
37094
|
+
}, /*#__PURE__*/React__namespace.createElement(Renderer, {
|
|
37095
|
+
content: this.props.content,
|
|
37096
|
+
linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "draggable"),
|
|
37097
|
+
onRender: this.props.onRender,
|
|
37098
|
+
strings: this.context.strings
|
|
37099
|
+
}))
|
|
37100
|
+
);
|
|
37992
37101
|
}
|
|
37993
37102
|
}
|
|
37994
37103
|
class Sortable extends React__namespace.Component {
|
|
@@ -40624,8 +39733,7 @@ var NumberLine$1 = {
|
|
|
40624
39733
|
displayName: "Number line",
|
|
40625
39734
|
widget: NumberLine,
|
|
40626
39735
|
transform: numberLineTransform,
|
|
40627
|
-
staticTransform: staticTransform$1
|
|
40628
|
-
getPublicWidgetOptions: perseusCore.getNumberLinePublicWidgetOptions
|
|
39736
|
+
staticTransform: staticTransform$1
|
|
40629
39737
|
};
|
|
40630
39738
|
|
|
40631
39739
|
const getPromptJSON$5 = (renderProps, userInput) => {
|
|
@@ -40792,19 +39900,23 @@ class Card extends React__namespace.Component {
|
|
|
40792
39900
|
// Pull out the content to get rendered
|
|
40793
39901
|
const rendererProps = ___default["default"].pick(this.props, "content");
|
|
40794
39902
|
const onMouseDown = this.props.animating ? $__default["default"].noop : this.onMouseDown;
|
|
40795
|
-
return
|
|
40796
|
-
|
|
40797
|
-
|
|
40798
|
-
|
|
40799
|
-
|
|
40800
|
-
|
|
40801
|
-
|
|
40802
|
-
|
|
40803
|
-
|
|
40804
|
-
|
|
40805
|
-
|
|
40806
|
-
|
|
40807
|
-
|
|
39903
|
+
return (
|
|
39904
|
+
/*#__PURE__*/
|
|
39905
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
39906
|
+
React__namespace.createElement("div", {
|
|
39907
|
+
className: "card-wrap",
|
|
39908
|
+
style: style,
|
|
39909
|
+
onMouseDown: onMouseDown,
|
|
39910
|
+
onTouchStart: onMouseDown,
|
|
39911
|
+
onTouchEnd: this.onMouseUp,
|
|
39912
|
+
onTouchCancel: this.onMouseUp
|
|
39913
|
+
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
39914
|
+
className: className.join(" ")
|
|
39915
|
+
}, /*#__PURE__*/React__namespace.createElement(Renderer, _extends({}, rendererProps, {
|
|
39916
|
+
linterContext: this.props.linterContext,
|
|
39917
|
+
strings: this.context.strings
|
|
39918
|
+
}))))
|
|
39919
|
+
);
|
|
40808
39920
|
}
|
|
40809
39921
|
}
|
|
40810
39922
|
class Orderer extends React__namespace.Component {
|
|
@@ -41176,8 +40288,7 @@ var Orderer$1 = {
|
|
|
41176
40288
|
displayName: "Orderer",
|
|
41177
40289
|
hidden: true,
|
|
41178
40290
|
widget: Orderer,
|
|
41179
|
-
isLintable: true
|
|
41180
|
-
getPublicWidgetOptions: perseusCore.getOrdererPublicWidgetOptions
|
|
40291
|
+
isLintable: true
|
|
41181
40292
|
};
|
|
41182
40293
|
|
|
41183
40294
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -42403,8 +41514,7 @@ var Sorter$1 = {
|
|
|
42403
41514
|
name: "sorter",
|
|
42404
41515
|
displayName: "Sorter",
|
|
42405
41516
|
widget: Sorter,
|
|
42406
|
-
isLintable: true
|
|
42407
|
-
getPublicWidgetOptions: perseusCore.getSorterPublicWidgetOptions
|
|
41517
|
+
isLintable: true
|
|
42408
41518
|
};
|
|
42409
41519
|
|
|
42410
41520
|
const {
|
|
@@ -42801,7 +41911,7 @@ const init = function () {
|
|
|
42801
41911
|
|
|
42802
41912
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
42803
41913
|
const libName = "@khanacademy/perseus";
|
|
42804
|
-
const libVersion = "
|
|
41914
|
+
const libVersion = "54.0.0";
|
|
42805
41915
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
42806
41916
|
|
|
42807
41917
|
const apiVersion = {
|
|
@@ -44295,6 +43405,17 @@ function getWidgetTypeByWidgetId(WidgetId, widgetMap) {
|
|
|
44295
43405
|
const widget = widgetMap[WidgetId];
|
|
44296
43406
|
return widget?.type ?? null;
|
|
44297
43407
|
}
|
|
43408
|
+
function getWidgetSubTypeByWidgetId(widgetId, widgetMap) {
|
|
43409
|
+
const widget = widgetMap[widgetId];
|
|
43410
|
+
const widgetType = widget?.type ?? null;
|
|
43411
|
+
switch (widgetType) {
|
|
43412
|
+
case "interactive-graph":
|
|
43413
|
+
const graph = widget.options.graph;
|
|
43414
|
+
return graph?.type ?? null;
|
|
43415
|
+
default:
|
|
43416
|
+
return null;
|
|
43417
|
+
}
|
|
43418
|
+
}
|
|
44298
43419
|
|
|
44299
43420
|
/**
|
|
44300
43421
|
* Does the content have a specific type of widget?
|
|
@@ -45173,6 +44294,7 @@ exports.getSegmentCoords = getSegmentCoords;
|
|
|
45173
44294
|
exports.getSinusoidCoords = getSinusoidCoords;
|
|
45174
44295
|
exports.getValidWidgetIds = getValidWidgetIds;
|
|
45175
44296
|
exports.getWidgetFromWidgetMap = getWidgetFromWidgetMap;
|
|
44297
|
+
exports.getWidgetSubTypeByWidgetId = getWidgetSubTypeByWidgetId;
|
|
45176
44298
|
exports.getWidgetTypeByWidgetId = getWidgetTypeByWidgetId;
|
|
45177
44299
|
exports.getWidgetsFromWidgetMap = getWidgetsFromWidgetMap;
|
|
45178
44300
|
exports.getWidgetsMapFromItemData = getWidgetsMapFromItemData;
|