@khanacademy/perseus 17.5.0 → 17.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/es/index.js +415 -618
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +424 -615
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +2 -1
- package/dist/strings.js +1 -0
- package/dist/widgets/deprecated-standin/index.d.ts +32 -0
- package/dist/widgets/label-image/answer-choices.d.ts +8 -40
- package/dist/widgets/label-image/answer-pill.d.ts +3 -1
- package/dist/widgets/label-image/marker.d.ts +10 -5
- package/dist/widgets/label-image.d.ts +1 -8
- package/dist/widgets.d.ts +9 -0
- package/package.json +34 -33
- package/src/__tests__/widgets.test.ts +20 -0
- package/src/extra-widgets.ts +2 -0
- package/src/init.ts +5 -0
- package/src/perseus-types.ts +4 -1
- package/src/util/register-all-widgets-for-testing.ts +4 -0
- package/src/widgets/__stories__/orderer.stories.tsx +5 -1
- package/src/widgets/__testdata__/orderer.testdata.ts +66 -0
- package/src/widgets/deprecated-standin/__stories__/deprecated-standin.stories.tsx +34 -0
- package/src/widgets/deprecated-standin/__tests__/__snapshots__/deprecated-standin.test.ts.snap +71 -0
- package/src/widgets/deprecated-standin/__tests__/deprecated-standin.test.ts +93 -0
- package/src/widgets/deprecated-standin/index.tsx +57 -0
- package/src/widgets/iframe.tsx +1 -1
- package/src/widgets/input-number.tsx +1 -1
- package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +29 -34
- package/src/widgets/label-image/__tests__/answer-pill.test.tsx +2 -10
- package/src/widgets/label-image/answer-choices.tsx +60 -278
- package/src/widgets/label-image/answer-pill.tsx +22 -13
- package/src/widgets/label-image/marker.tsx +103 -146
- package/src/widgets/label-image.test.ts +0 -2
- package/src/widgets/label-image.tsx +94 -195
- package/src/widgets/lights-puzzle.tsx +1 -1
- package/src/widgets/matcher.tsx +1 -1
- package/src/widgets/numeric-input.tsx +1 -1
- package/src/widgets/radio.ts +1 -1
- package/src/widgets/reaction-diagram.tsx +1 -1
- package/src/widgets/sequence.tsx +1 -1
- package/src/widgets/simulator.tsx +1 -1
- package/src/widgets/table.tsx +1 -1
- package/src/widgets/transformer.tsx +2 -1
- package/src/widgets/unit.tsx +1 -1
- package/src/widgets.ts +24 -0
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* knowledge by directly interacting with the image.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import Clickable from "@khanacademy/wonder-blocks-clickable";
|
|
10
|
+
import {View} from "@khanacademy/wonder-blocks-core";
|
|
9
11
|
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
10
|
-
import {Popover, PopoverContentCore} from "@khanacademy/wonder-blocks-popover";
|
|
11
12
|
import {StyleSheet, css} from "aphrodite";
|
|
12
13
|
import classNames from "classnames";
|
|
13
14
|
import * as React from "react";
|
|
@@ -21,7 +22,6 @@ import {typography} from "../styles/global-styles";
|
|
|
21
22
|
import mediaQueries from "../styles/media-queries";
|
|
22
23
|
|
|
23
24
|
import AnswerChoices from "./label-image/answer-choices";
|
|
24
|
-
import {AnswerPill} from "./label-image/answer-pill";
|
|
25
25
|
import {HideAnswersToggle} from "./label-image/hide-answers-toggle";
|
|
26
26
|
import Marker from "./label-image/marker";
|
|
27
27
|
|
|
@@ -96,8 +96,6 @@ type LabelImageState = {
|
|
|
96
96
|
activeMarkerIndex: number;
|
|
97
97
|
// Whether any of the markers were interacted with by the user.
|
|
98
98
|
markersInteracted: boolean;
|
|
99
|
-
// The currently focused marker index; defaults to -1, no focus.
|
|
100
|
-
focusedMarkerIndex: number;
|
|
101
99
|
// Hide answer pills.
|
|
102
100
|
hideAnswers: boolean;
|
|
103
101
|
};
|
|
@@ -109,12 +107,6 @@ export class LabelImage extends React.Component<
|
|
|
109
107
|
// The rendered markers on the question image for labeling.
|
|
110
108
|
_markers: Array<Marker | null | undefined>;
|
|
111
109
|
|
|
112
|
-
// The popup component containing the answer choices.
|
|
113
|
-
_selectedMarkerPopup: Popover | null | undefined;
|
|
114
|
-
|
|
115
|
-
// The rendered list of answer choices for the currently selected marker.
|
|
116
|
-
_answerChoices: AnswerChoices | null | undefined;
|
|
117
|
-
|
|
118
110
|
static gradeMarker(marker: InteractiveMarkerType): InteractiveMarkerScore {
|
|
119
111
|
const score = {
|
|
120
112
|
hasAnswers: false,
|
|
@@ -375,7 +367,6 @@ export class LabelImage extends React.Component<
|
|
|
375
367
|
|
|
376
368
|
this.state = {
|
|
377
369
|
activeMarkerIndex: -1,
|
|
378
|
-
focusedMarkerIndex: -1,
|
|
379
370
|
markersInteracted: false,
|
|
380
371
|
hideAnswers: false,
|
|
381
372
|
};
|
|
@@ -414,24 +405,6 @@ export class LabelImage extends React.Component<
|
|
|
414
405
|
onChange({markers: updatedMarkers}, null, true);
|
|
415
406
|
}
|
|
416
407
|
|
|
417
|
-
dismissMarkerPopup() {
|
|
418
|
-
const {activeMarkerIndex} = this.state;
|
|
419
|
-
|
|
420
|
-
// No popup should be open if there's no selected marker.
|
|
421
|
-
if (activeMarkerIndex === -1) {
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
this.setState({activeMarkerIndex: -1}, () => {
|
|
426
|
-
const marker = this._markers[activeMarkerIndex];
|
|
427
|
-
// Set focus on the just-deselected-marker, to enable to resume
|
|
428
|
-
// navigating between the markers using the keyboard.
|
|
429
|
-
if (marker) {
|
|
430
|
-
(ReactDOM.findDOMNode(marker) as HTMLElement).focus();
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
|
|
435
408
|
handleMarkerChange(index: number, marker: InteractiveMarkerType) {
|
|
436
409
|
const {markers, onChange} = this.props;
|
|
437
410
|
|
|
@@ -451,33 +424,21 @@ export class LabelImage extends React.Component<
|
|
|
451
424
|
onChange({markers: updatedMarkers});
|
|
452
425
|
}
|
|
453
426
|
|
|
454
|
-
activateMarker(index: number) {
|
|
455
|
-
|
|
427
|
+
activateMarker(index: number, opened: boolean) {
|
|
428
|
+
this.props.analytics?.onAnalyticsEvent({
|
|
429
|
+
type: "perseus:label-image:marker-interacted-with",
|
|
430
|
+
payload: null,
|
|
431
|
+
});
|
|
456
432
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
() => {
|
|
465
|
-
// A delay is required to allow answer choices to first render
|
|
466
|
-
// before trying to set focus on the first choice.
|
|
467
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API
|
|
468
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
469
|
-
setTimeout(() => {
|
|
470
|
-
if (this._answerChoices) {
|
|
471
|
-
this._answerChoices.focusAnswer();
|
|
472
|
-
}
|
|
473
|
-
}, 100);
|
|
474
|
-
},
|
|
475
|
-
);
|
|
433
|
+
const {activeMarkerIndex} = this.state;
|
|
434
|
+
// Set index of opened marker
|
|
435
|
+
if (activeMarkerIndex !== index && opened) {
|
|
436
|
+
this.setState({
|
|
437
|
+
activeMarkerIndex: index,
|
|
438
|
+
markersInteracted: true,
|
|
439
|
+
});
|
|
476
440
|
} else {
|
|
477
|
-
|
|
478
|
-
// selected marker. Close the popup and set focus back to the
|
|
479
|
-
// marker.
|
|
480
|
-
this.dismissMarkerPopup();
|
|
441
|
+
this.setState({activeMarkerIndex: -1});
|
|
481
442
|
}
|
|
482
443
|
}
|
|
483
444
|
|
|
@@ -523,7 +484,7 @@ export class LabelImage extends React.Component<
|
|
|
523
484
|
index: number,
|
|
524
485
|
selection: ReadonlyArray<boolean>,
|
|
525
486
|
) {
|
|
526
|
-
const {choices, markers
|
|
487
|
+
const {choices, markers} = this.props;
|
|
527
488
|
|
|
528
489
|
// Compile the user selected answer choices.
|
|
529
490
|
const selected = choices.filter((_, index) => selection[index]);
|
|
@@ -532,48 +493,6 @@ export class LabelImage extends React.Component<
|
|
|
532
493
|
...markers[index],
|
|
533
494
|
selected: selected.length ? selected : undefined,
|
|
534
495
|
});
|
|
535
|
-
|
|
536
|
-
if (!multipleAnswers) {
|
|
537
|
-
this.dismissMarkerPopup();
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
renderAnswerChoicesForMarker(
|
|
542
|
-
index: number,
|
|
543
|
-
marker: InteractiveMarkerType,
|
|
544
|
-
): React.ReactElement<React.ComponentProps<"div">> {
|
|
545
|
-
const {choices, multipleAnswers} = this.props;
|
|
546
|
-
|
|
547
|
-
// The user selected answer choices.
|
|
548
|
-
const selected = marker.selected;
|
|
549
|
-
|
|
550
|
-
return (
|
|
551
|
-
<div
|
|
552
|
-
className={classNames(
|
|
553
|
-
"perseus-label-image-widget-answer-choices",
|
|
554
|
-
css(styles.scrollableChoices),
|
|
555
|
-
)}
|
|
556
|
-
>
|
|
557
|
-
<AnswerChoices
|
|
558
|
-
choices={choices.map((choice) => ({
|
|
559
|
-
content: choice,
|
|
560
|
-
checked: selected ? selected.includes(choice) : false,
|
|
561
|
-
}))}
|
|
562
|
-
multipleSelect={multipleAnswers}
|
|
563
|
-
onChange={(selection) => {
|
|
564
|
-
this.props.analytics?.onAnalyticsEvent({
|
|
565
|
-
type: "perseus:label-image:choiced-interacted-with",
|
|
566
|
-
payload: null,
|
|
567
|
-
});
|
|
568
|
-
this.handleAnswerChoicesChangeForMarker(
|
|
569
|
-
index,
|
|
570
|
-
selection,
|
|
571
|
-
);
|
|
572
|
-
}}
|
|
573
|
-
ref={(node) => (this._answerChoices = node)}
|
|
574
|
-
/>
|
|
575
|
-
</div>
|
|
576
|
-
);
|
|
577
496
|
}
|
|
578
497
|
|
|
579
498
|
renderMarkers(): ReadonlyArray<React.ReactNode> {
|
|
@@ -584,43 +503,6 @@ export class LabelImage extends React.Component<
|
|
|
584
503
|
|
|
585
504
|
// Render all markers for widget.
|
|
586
505
|
return markers.map((marker, index): React.ReactElement => {
|
|
587
|
-
const score = LabelImage.gradeMarker(marker);
|
|
588
|
-
// Once the question is answered, show markers
|
|
589
|
-
// with correct answers, otherwise passthrough
|
|
590
|
-
// the correctness state.
|
|
591
|
-
const showCorrectness =
|
|
592
|
-
questionCompleted && score.hasAnswers && score.isCorrect
|
|
593
|
-
? "correct"
|
|
594
|
-
: marker.showCorrectness;
|
|
595
|
-
|
|
596
|
-
const element = (
|
|
597
|
-
<Marker
|
|
598
|
-
{...marker}
|
|
599
|
-
showCorrectness={showCorrectness}
|
|
600
|
-
showSelected={index === activeMarkerIndex}
|
|
601
|
-
showPulsate={!markersInteracted}
|
|
602
|
-
key={`${marker.x}.${marker.y}`}
|
|
603
|
-
onClick={() => {
|
|
604
|
-
this.props.analytics?.onAnalyticsEvent({
|
|
605
|
-
type: "perseus:label-image:marker-interacted-with",
|
|
606
|
-
payload: null,
|
|
607
|
-
});
|
|
608
|
-
this.activateMarker(index);
|
|
609
|
-
}}
|
|
610
|
-
onKeyDown={(e) => this.handleMarkerKeyDown(index, e)}
|
|
611
|
-
ref={(node) => (this._markers[index] = node)}
|
|
612
|
-
focused={index === this.state.focusedMarkerIndex}
|
|
613
|
-
onFocus={() => {
|
|
614
|
-
this.setState({focusedMarkerIndex: index});
|
|
615
|
-
}}
|
|
616
|
-
onBlur={() => {
|
|
617
|
-
if (index === this.state.focusedMarkerIndex) {
|
|
618
|
-
this.setState({focusedMarkerIndex: -1});
|
|
619
|
-
}
|
|
620
|
-
}}
|
|
621
|
-
/>
|
|
622
|
-
);
|
|
623
|
-
|
|
624
506
|
// Determine whether page is rendered in a narrow browser window.
|
|
625
507
|
const isNarrowPage = window.matchMedia(
|
|
626
508
|
mediaQueries.xsOrSmaller.replace("@media ", ""),
|
|
@@ -657,66 +539,98 @@ export class LabelImage extends React.Component<
|
|
|
657
539
|
}[markerPosition];
|
|
658
540
|
}
|
|
659
541
|
|
|
660
|
-
const
|
|
542
|
+
const score = LabelImage.gradeMarker(marker);
|
|
543
|
+
// Once the question is answered, show markers
|
|
544
|
+
// with correct answers, otherwise passthrough
|
|
545
|
+
// the correctness state.
|
|
546
|
+
const showCorrectness =
|
|
547
|
+
questionCompleted && score.hasAnswers && score.isCorrect
|
|
548
|
+
? "correct"
|
|
549
|
+
: marker.showCorrectness;
|
|
661
550
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
};
|
|
551
|
+
// Disable marker interaction once the question is answered correctly.
|
|
552
|
+
const disabled = showCorrectness === "correct";
|
|
665
553
|
|
|
666
554
|
const adjustPillDistance: CSSProperties = {
|
|
667
555
|
[`margin${
|
|
668
556
|
markerPosition.charAt(0).toUpperCase() +
|
|
669
557
|
markerPosition.slice(1)
|
|
670
|
-
}`]:
|
|
558
|
+
}`]: 10, // move pill further from marker
|
|
671
559
|
};
|
|
672
560
|
|
|
561
|
+
const answerChoicesActive = index === activeMarkerIndex;
|
|
562
|
+
|
|
673
563
|
const showAnswerChoice =
|
|
674
|
-
|
|
564
|
+
marker.selected &&
|
|
565
|
+
!answerChoicesActive &&
|
|
566
|
+
!this.state.hideAnswers;
|
|
675
567
|
|
|
676
568
|
return (
|
|
677
|
-
<
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
569
|
+
<View
|
|
570
|
+
style={{
|
|
571
|
+
position: "absolute",
|
|
572
|
+
left: `${marker.x}%`,
|
|
573
|
+
top: `${marker.y}%`,
|
|
574
|
+
// reset to allow child (answer pill) to control z-index
|
|
575
|
+
zIndex: "unset",
|
|
576
|
+
}}
|
|
577
|
+
>
|
|
578
|
+
<AnswerChoices
|
|
579
|
+
key={`answers-${marker.x}.${marker.y}`}
|
|
580
|
+
choices={this.props.choices.map((choice) => ({
|
|
581
|
+
content: choice,
|
|
582
|
+
checked: marker.selected
|
|
583
|
+
? marker.selected.includes(choice)
|
|
584
|
+
: false,
|
|
585
|
+
}))}
|
|
586
|
+
multipleSelect={this.props.multipleAnswers}
|
|
587
|
+
onChange={(selection) => {
|
|
588
|
+
this.props.analytics?.onAnalyticsEvent({
|
|
589
|
+
type: "perseus:label-image:choiced-interacted-with",
|
|
590
|
+
payload: null,
|
|
591
|
+
});
|
|
592
|
+
this.handleAnswerChoicesChangeForMarker(
|
|
593
|
+
index,
|
|
594
|
+
selection,
|
|
595
|
+
);
|
|
596
|
+
}}
|
|
597
|
+
onToggle={(opened) =>
|
|
598
|
+
this.activateMarker(index, opened)
|
|
599
|
+
}
|
|
600
|
+
// cannot change answer choices once question is answered
|
|
601
|
+
disabled={disabled}
|
|
602
|
+
opener={({opened, text}) => (
|
|
603
|
+
<Clickable
|
|
604
|
+
role="button"
|
|
605
|
+
key={`marker-${marker.x}.${marker.y}`}
|
|
606
|
+
aria-label={
|
|
607
|
+
// already translated in: /widgets/graded-group.tsx
|
|
608
|
+
showCorrectness === "correct"
|
|
609
|
+
? i18n._("Correct!")
|
|
610
|
+
: marker.label
|
|
611
|
+
}
|
|
685
612
|
>
|
|
686
|
-
{
|
|
687
|
-
|
|
688
|
-
|
|
613
|
+
{({hovered, focused, pressed}) => (
|
|
614
|
+
<Marker
|
|
615
|
+
{...marker}
|
|
616
|
+
showCorrectness={showCorrectness}
|
|
617
|
+
showSelected={opened}
|
|
618
|
+
showPulsate={!markersInteracted}
|
|
619
|
+
ref={(node) =>
|
|
620
|
+
(this._markers[index] = node)
|
|
621
|
+
}
|
|
622
|
+
showAnswer={showAnswerChoice}
|
|
623
|
+
answerSide={side}
|
|
624
|
+
answerStyles={adjustPillDistance}
|
|
625
|
+
analytics={this.props.analytics}
|
|
626
|
+
focused={focused || pressed}
|
|
627
|
+
hovered={hovered}
|
|
628
|
+
/>
|
|
689
629
|
)}
|
|
690
|
-
</
|
|
630
|
+
</Clickable>
|
|
691
631
|
)}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
opened={answerChoicesActive}
|
|
695
|
-
ref={(node) => (this._selectedMarkerPopup = node)}
|
|
696
|
-
showTail={false}
|
|
697
|
-
dismissEnabled
|
|
698
|
-
>
|
|
699
|
-
{element}
|
|
700
|
-
</Popover>
|
|
701
|
-
{!!marker.selected && showAnswerChoice && (
|
|
702
|
-
<AnswerPill
|
|
703
|
-
selectedAnswers={marker.selected}
|
|
704
|
-
showCorrectness={showCorrectness}
|
|
705
|
-
markerRef={
|
|
706
|
-
// this throws a warning because it's inadvisable to
|
|
707
|
-
// call ReactDOM fns in render. we aren't storing
|
|
708
|
-
// actual refs in this array, so not much I can do
|
|
709
|
-
// outside of a refactor.
|
|
710
|
-
ReactDOM.findDOMNode(
|
|
711
|
-
this._markers[index],
|
|
712
|
-
) as HTMLElement
|
|
713
|
-
}
|
|
714
|
-
side={side}
|
|
715
|
-
onClick={() => this.activateMarker(index)}
|
|
716
|
-
style={adjustPillDistance}
|
|
717
|
-
/>
|
|
718
|
-
)}
|
|
719
|
-
</div>
|
|
632
|
+
/>
|
|
633
|
+
</View>
|
|
720
634
|
);
|
|
721
635
|
});
|
|
722
636
|
}
|
|
@@ -883,21 +797,6 @@ const styles = StyleSheet.create({
|
|
|
883
797
|
imageInteractionDisabled: {
|
|
884
798
|
pointerEvents: "none",
|
|
885
799
|
},
|
|
886
|
-
|
|
887
|
-
scrollableChoices: {
|
|
888
|
-
paddingTop: 8,
|
|
889
|
-
paddingBottom: 8,
|
|
890
|
-
|
|
891
|
-
// Enable scroll for answer choices within popup on mobile phones.
|
|
892
|
-
[mediaQueries.xsOrSmaller]: {
|
|
893
|
-
maxHeight: 250,
|
|
894
|
-
overflowY: "auto",
|
|
895
|
-
},
|
|
896
|
-
},
|
|
897
|
-
|
|
898
|
-
choicesPopover: {
|
|
899
|
-
padding: 0,
|
|
900
|
-
},
|
|
901
800
|
});
|
|
902
801
|
|
|
903
802
|
const LabelImageWithDependencies = React.forwardRef<
|
|
@@ -352,7 +352,7 @@ const transformProps: (arg1: any) => any = function (editorProps) {
|
|
|
352
352
|
|
|
353
353
|
export default {
|
|
354
354
|
name: "lights-puzzle",
|
|
355
|
-
displayName: "Lights
|
|
355
|
+
displayName: "Lights puzzle (deprecated)",
|
|
356
356
|
hidden: true,
|
|
357
357
|
widget: LightsPuzzle,
|
|
358
358
|
transform: transformProps,
|
package/src/widgets/matcher.tsx
CHANGED
package/src/widgets/radio.ts
CHANGED
|
@@ -138,7 +138,7 @@ class ReactionDiagramWidget extends React.Component<any> {
|
|
|
138
138
|
|
|
139
139
|
export default {
|
|
140
140
|
name: "reaction-diagram",
|
|
141
|
-
displayName: "Chemical reaction",
|
|
141
|
+
displayName: "Chemical reaction diagram (deprecated)",
|
|
142
142
|
hidden: true,
|
|
143
143
|
widget: ReactionDiagramWidget,
|
|
144
144
|
} as WidgetExports<typeof ReactionDiagramWidget>;
|
package/src/widgets/sequence.tsx
CHANGED
|
@@ -133,7 +133,7 @@ const traverseChildWidgets = function (props: any, traverseRenderer: any): any {
|
|
|
133
133
|
|
|
134
134
|
export default {
|
|
135
135
|
name: "sequence",
|
|
136
|
-
displayName: "Graded
|
|
136
|
+
displayName: "Graded sequence (deprecated)",
|
|
137
137
|
widget: Sequence,
|
|
138
138
|
traverseChildWidgets: traverseChildWidgets,
|
|
139
139
|
tracking: "all",
|
|
@@ -822,7 +822,7 @@ const propTransform: (arg1: any) => any = (editorProps) => {
|
|
|
822
822
|
|
|
823
823
|
export default {
|
|
824
824
|
name: "simulator",
|
|
825
|
-
displayName: "Simulator",
|
|
825
|
+
displayName: "Simulator (deprecated)",
|
|
826
826
|
widget: Simulator,
|
|
827
827
|
transform: propTransform,
|
|
828
828
|
hidden: true,
|
package/src/widgets/table.tsx
CHANGED
|
@@ -384,7 +384,7 @@ const propTransform: (arg1: any) => any = (editorProps) => {
|
|
|
384
384
|
|
|
385
385
|
export default {
|
|
386
386
|
name: "table",
|
|
387
|
-
displayName: "Table
|
|
387
|
+
displayName: "Table (deprecated - use markdown table instead)",
|
|
388
388
|
accessible: true,
|
|
389
389
|
widget: Table,
|
|
390
390
|
transform: propTransform,
|
|
@@ -2930,6 +2930,7 @@ class Transformer extends React.Component<Props> {
|
|
|
2930
2930
|
|
|
2931
2931
|
export default {
|
|
2932
2932
|
name: "transformer",
|
|
2933
|
-
displayName: "Transformer",
|
|
2933
|
+
displayName: "Transformer (deprecated)",
|
|
2934
|
+
hidden: true,
|
|
2934
2935
|
widget: Transformer,
|
|
2935
2936
|
} as WidgetExports<typeof Transformer>;
|
package/src/widgets/unit.tsx
CHANGED
|
@@ -365,7 +365,7 @@ const primUnits = function (expr) {
|
|
|
365
365
|
|
|
366
366
|
export default {
|
|
367
367
|
name: "unit-input",
|
|
368
|
-
displayName: "Unit",
|
|
368
|
+
displayName: "Unit input",
|
|
369
369
|
defaultAlignment: "inline-block",
|
|
370
370
|
widget: OldUnitInput,
|
|
371
371
|
transform: (x: any): any => lens(x).del(["value"]).freeze(),
|
package/src/widgets.ts
CHANGED
|
@@ -43,6 +43,30 @@ export const registerWidgets = (widgets: ReadonlyArray<WidgetExports>) => {
|
|
|
43
43
|
validateAlignments();
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @param name - the widget that you are trying to replace
|
|
49
|
+
* @param replacementName - the name of the widget that takes its place
|
|
50
|
+
*
|
|
51
|
+
* e.g. replaceWidget("transformer", "always-correct") will make it so the
|
|
52
|
+
* transformer widget is replaced by the always correct widget
|
|
53
|
+
*/
|
|
54
|
+
export const replaceWidget = (name: string, replacementName: string) => {
|
|
55
|
+
const substituteWidget = widgets[replacementName];
|
|
56
|
+
|
|
57
|
+
if (!substituteWidget && Log) {
|
|
58
|
+
const errorMsg = `Failed to replace ${name} with ${replacementName}`;
|
|
59
|
+
Log.error(errorMsg, Errors.Internal);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
registerWidget(name, substituteWidget);
|
|
64
|
+
|
|
65
|
+
if (Log) {
|
|
66
|
+
Log.log(`INFO: Replacing widget ${name} with ${replacementName}`);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
46
70
|
export const registerEditors = (editorsToRegister: ReadonlyArray<Editor>) => {
|
|
47
71
|
editorsToRegister.forEach((editor) => {
|
|
48
72
|
if (!editor.widgetName) {
|