@khanacademy/perseus 19.1.5 → 19.1.7
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/graph.d.ts +0 -1
- package/dist/components/graphie.d.ts +0 -1
- package/dist/es/index.js +319 -212
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +338 -222
- package/dist/index.js.map +1 -1
- package/dist/interactive2/movable-point.d.ts +195 -1
- package/dist/interactive2/movable.d.ts +1 -1
- package/dist/interactive2/types.d.ts +4 -2
- package/dist/interactive2.d.ts +2 -1
- package/dist/server-item-renderer.d.ts +2 -2
- package/dist/util/graphie.d.ts +12 -2
- package/dist/widgets/expression.d.ts +6 -6
- package/package.json +11 -8
- package/.babelrc.js +0 -8
- package/.eslintrc.js +0 -12
- package/CHANGELOG.md +0 -1432
- package/src/__stories__/article-renderer.stories.tsx +0 -100
- package/src/__stories__/hints-renderer.stories.tsx +0 -55
- package/src/__stories__/server-item-renderer.stories.tsx +0 -44
- package/src/__testdata__/article-renderer.testdata.ts +0 -212
- package/src/__testdata__/graphie.testdata.ts +0 -51
- package/src/__testdata__/renderer.testdata.ts +0 -274
- package/src/__testdata__/server-item-renderer.testdata.ts +0 -171
- package/src/__tests__/__snapshots__/perseus-markdown.test.ts.snap +0 -507
- package/src/__tests__/__snapshots__/renderer.test.tsx.snap +0 -525
- package/src/__tests__/__snapshots__/server-item-renderer.test.tsx.snap +0 -285
- package/src/__tests__/a11y.test.ts +0 -303
- package/src/__tests__/article-renderer.test.tsx +0 -130
- package/src/__tests__/error-boundary.test.tsx +0 -76
- package/src/__tests__/extract-perseus-data.test.ts +0 -1027
- package/src/__tests__/init.test.ts +0 -10
- package/src/__tests__/jipt-paragraphs.test.ts +0 -32
- package/src/__tests__/mock-asset-loading-widget.tsx +0 -53
- package/src/__tests__/mock-widget.tsx +0 -33
- package/src/__tests__/perseus-markdown.test.ts +0 -764
- package/src/__tests__/renderability.test.ts +0 -480
- package/src/__tests__/renderer-api.test.tsx +0 -302
- package/src/__tests__/renderer.test.tsx +0 -1816
- package/src/__tests__/server-item-renderer.test.tsx +0 -582
- package/src/__tests__/test-items/image-item.ts +0 -24
- package/src/__tests__/test-items/input-number-1-item.ts +0 -26
- package/src/__tests__/test-items/input-number-2-item.ts +0 -38
- package/src/__tests__/test-items/table-item.ts +0 -29
- package/src/__tests__/tex-wrangler.test.ts +0 -82
- package/src/__tests__/util.test.ts +0 -40
- package/src/__tests__/widget-container.test.tsx +0 -126
- package/src/__tests__/widgets.test.ts +0 -91
- package/src/a11y.ts +0 -45
- package/src/all-widgets.ts +0 -9
- package/src/article-renderer.tsx +0 -237
- package/src/asset-context.ts +0 -30
- package/src/basic-widgets.ts +0 -16
- package/src/components/__stories__/button-group.stories.tsx +0 -55
- package/src/components/__stories__/fixed-to-responsive.stories.tsx +0 -141
- package/src/components/__stories__/graph.stories.tsx +0 -28
- package/src/components/__stories__/graphie.stories.tsx +0 -33
- package/src/components/__stories__/hud.stories.tsx +0 -35
- package/src/components/__stories__/icon.stories.tsx +0 -63
- package/src/components/__stories__/image-loader.stories.tsx +0 -63
- package/src/components/__stories__/info-tip.stories.tsx +0 -31
- package/src/components/__stories__/inline-icon.stories.tsx +0 -40
- package/src/components/__stories__/input-with-examples.stories.tsx +0 -53
- package/src/components/__stories__/lint.stories.tsx +0 -86
- package/src/components/__stories__/math-input.stories.tsx +0 -43
- package/src/components/__stories__/math-output.stories.tsx +0 -23
- package/src/components/__stories__/multi-button-group.stories.tsx +0 -66
- package/src/components/__stories__/number-input.stories.tsx +0 -41
- package/src/components/__stories__/prop-check-box.stories.tsx +0 -65
- package/src/components/__stories__/range-input.stories.tsx +0 -29
- package/src/components/__stories__/simple-keypad-input.stories.tsx +0 -27
- package/src/components/__stories__/sortable.stories.tsx +0 -77
- package/src/components/__stories__/stub-tag-editor.stories.tsx +0 -45
- package/src/components/__stories__/svg-image.stories.tsx +0 -74
- package/src/components/__stories__/tex.stories.tsx +0 -23
- package/src/components/__stories__/text-input.stories.tsx +0 -33
- package/src/components/__stories__/text-list-editor.stories.tsx +0 -32
- package/src/components/__stories__/tooltip.stories.tsx +0 -52
- package/src/components/__stories__/zoomable-tex.stories.tsx +0 -41
- package/src/components/__stories__/zoomable.stories.tsx +0 -49
- package/src/components/__testdata__/sorter.testdata.ts +0 -19
- package/src/components/__tests__/__snapshots__/sortable.test.tsx.snap +0 -163
- package/src/components/__tests__/__snapshots__/sorter.test.tsx.snap +0 -265
- package/src/components/__tests__/math-input.test.tsx +0 -147
- package/src/components/__tests__/number-input.test.tsx +0 -128
- package/src/components/__tests__/sortable.test.tsx +0 -269
- package/src/components/__tests__/sorter.test.tsx +0 -114
- package/src/components/__tests__/zoomable.test.tsx +0 -399
- package/src/components/button-group.tsx +0 -130
- package/src/components/constants.ts +0 -14
- package/src/components/fixed-to-responsive.tsx +0 -169
- package/src/components/graph.tsx +0 -444
- package/src/components/graphie-classes.ts +0 -153
- package/src/components/graphie-movables.ts +0 -338
- package/src/components/graphie.tsx +0 -400
- package/src/components/highlighting/highlightable-content.tsx +0 -240
- package/src/components/highlighting/highlights.ts +0 -207
- package/src/components/highlighting/ranges.ts +0 -304
- package/src/components/highlighting/types.ts +0 -73
- package/src/components/highlighting/ui/highlight-renderer.tsx +0 -172
- package/src/components/highlighting/ui/highlight-set-renderer.tsx +0 -185
- package/src/components/highlighting/ui/highlight-tooltip.tsx +0 -141
- package/src/components/highlighting/ui/highlighting-ui.tsx +0 -143
- package/src/components/highlighting/ui/selection-tracker.tsx +0 -186
- package/src/components/highlighting/ui/types.ts +0 -21
- package/src/components/highlighting/ui/util.ts +0 -165
- package/src/components/highlighting/word-indexer.tsx +0 -152
- package/src/components/hud.tsx +0 -167
- package/src/components/icon.tsx +0 -141
- package/src/components/image-loader.tsx +0 -177
- package/src/components/info-tip/info-tip.tsx +0 -144
- package/src/components/info-tip.tsx +0 -39
- package/src/components/inline-icon.tsx +0 -60
- package/src/components/input-with-examples.tsx +0 -249
- package/src/components/lint.tsx +0 -459
- package/src/components/math-input.tsx +0 -474
- package/src/components/math-output.tsx +0 -134
- package/src/components/multi-button-group.tsx +0 -135
- package/src/components/number-input.tsx +0 -249
- package/src/components/prop-check-box.tsx +0 -79
- package/src/components/range-input.tsx +0 -60
- package/src/components/simple-keypad-input.tsx +0 -88
- package/src/components/sortable.tsx +0 -958
- package/src/components/stub-tag-editor.tsx +0 -53
- package/src/components/svg-image.tsx +0 -779
- package/src/components/tex.tsx +0 -67
- package/src/components/text-input.tsx +0 -129
- package/src/components/text-list-editor.tsx +0 -166
- package/src/components/tooltip.tsx +0 -410
- package/src/components/visibility-observer/__stories__/visibility-observer.stories.tsx +0 -141
- package/src/components/visibility-observer/create-visibility-observer.ts +0 -108
- package/src/components/visibility-observer/get-scroll-parent.ts +0 -46
- package/src/components/visibility-observer/visibility-observer.tsx +0 -124
- package/src/components/zoomable-tex.tsx +0 -78
- package/src/components/zoomable.tsx +0 -304
- package/src/components.ts +0 -15
- package/src/definition-context.tsx +0 -59
- package/src/dependencies.ts +0 -40
- package/src/error-boundary.tsx +0 -70
- package/src/extra-widgets.ts +0 -75
- package/src/hint-renderer.tsx +0 -151
- package/src/hints-renderer.tsx +0 -372
- package/src/icon-paths.ts +0 -119
- package/src/index.ts +0 -171
- package/src/init.ts +0 -56
- package/src/interaction-tracker.ts +0 -62
- package/src/interactive2/__tests__/movable-mock.ts +0 -55
- package/src/interactive2/__tests__/movable-point.test.ts +0 -144
- package/src/interactive2/interactive-util.test.ts +0 -93
- package/src/interactive2/interactive-util.ts +0 -159
- package/src/interactive2/movable-helper-methods.ts +0 -166
- package/src/interactive2/movable-line-options.ts +0 -419
- package/src/interactive2/movable-line.ts +0 -307
- package/src/interactive2/movable-point-options.ts +0 -224
- package/src/interactive2/movable-point.tsx +0 -585
- package/src/interactive2/movable-polygon-options.ts +0 -389
- package/src/interactive2/movable-polygon.ts +0 -360
- package/src/interactive2/movable.test.ts +0 -249
- package/src/interactive2/movable.ts +0 -437
- package/src/interactive2/objective_.ts +0 -79
- package/src/interactive2/types.ts +0 -17
- package/src/interactive2/wrapped-defaults.ts +0 -87
- package/src/interactive2/wrapped-ellipse.ts +0 -94
- package/src/interactive2/wrapped-line.ts +0 -116
- package/src/interactive2/wrapped-path.ts +0 -51
- package/src/interactive2.ts +0 -70
- package/src/item-version.ts +0 -10
- package/src/jipt-paragraphs.ts +0 -49
- package/src/jquery.mobile.vmouse.js +0 -545
- package/src/loading-context.ts +0 -21
- package/src/logging/log.test.ts +0 -39
- package/src/logging/log.ts +0 -97
- package/src/mixins/__tests__/widget-prop-denylist.test.ts +0 -21
- package/src/mixins/changeable.ts +0 -133
- package/src/mixins/editor-jsonify.ts +0 -13
- package/src/mixins/widget-jsonify-deprecated.ts +0 -23
- package/src/mixins/widget-prop-denylist.ts +0 -48
- package/src/multi-items/__stories__/multi-renderer.stories.tsx +0 -83
- package/src/multi-items/__testdata__/multi-renderer.testdata.ts +0 -172
- package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +0 -1164
- package/src/multi-items/__tests__/items.test.ts +0 -240
- package/src/multi-items/__tests__/multi-renderer.test.tsx +0 -914
- package/src/multi-items/__tests__/prop-type-builders.test.ts +0 -133
- package/src/multi-items/__tests__/shapes.test.ts +0 -43
- package/src/multi-items/__tests__/trees.test.ts +0 -317
- package/src/multi-items/item-types.ts +0 -56
- package/src/multi-items/items.ts +0 -176
- package/src/multi-items/multi-renderer.tsx +0 -569
- package/src/multi-items/prop-type-builders.ts +0 -106
- package/src/multi-items/shape-types.ts +0 -52
- package/src/multi-items/shapes.ts +0 -52
- package/src/multi-items/tree-types.ts +0 -47
- package/src/multi-items/trees.ts +0 -360
- package/src/multi-items.ts +0 -75
- package/src/perseus-api.tsx +0 -188
- package/src/perseus-env.ts +0 -27
- package/src/perseus-error.ts +0 -17
- package/src/perseus-markdown.tsx +0 -405
- package/src/perseus-types.ts +0 -1366
- package/src/perseus-version.ts +0 -9
- package/src/question-paragraph.tsx +0 -34
- package/src/renderability.ts +0 -109
- package/src/renderer.tsx +0 -2000
- package/src/server-item-renderer.tsx +0 -495
- package/src/sigfigs.ts +0 -490
- package/src/styles/articles.less +0 -749
- package/src/styles/constants.ts +0 -94
- package/src/styles/global-constants.ts +0 -343
- package/src/styles/global-styles.ts +0 -306
- package/src/styles/khan-exercise.css +0 -1468
- package/src/styles/media-queries.ts +0 -47
- package/src/styles/perseus-renderer.less +0 -732
- package/src/styles/shared.ts +0 -130
- package/src/styles/util.less +0 -47
- package/src/styles/variables.boostrap.less +0 -277
- package/src/styles/variables.less +0 -56
- package/src/styles/variables.shared.less +0 -340
- package/src/styles/widgets/categorizer.less +0 -38
- package/src/styles/widgets/dropdown.less +0 -3
- package/src/styles/widgets/expression.less +0 -65
- package/src/styles/widgets/graded-group.less +0 -36
- package/src/styles/widgets/grapher.less +0 -12
- package/src/styles/widgets/group.less +0 -19
- package/src/styles/widgets/image.less +0 -54
- package/src/styles/widgets/interactive-graph.less +0 -64
- package/src/styles/widgets/label-image.less +0 -57
- package/src/styles/widgets/matcher.less +0 -8
- package/src/styles/widgets/matrix.less +0 -126
- package/src/styles/widgets/measurer.less +0 -20
- package/src/styles/widgets/orderer.less +0 -98
- package/src/styles/widgets/passage.less +0 -154
- package/src/styles/widgets/plotter.less +0 -31
- package/src/styles/widgets/radio.less +0 -58
- package/src/styles/widgets/reaction-diagram.less +0 -35
- package/src/styles/widgets/sequence.less +0 -5
- package/src/styles/widgets/sortable.less +0 -113
- package/src/styles/widgets/table.less +0 -57
- package/src/styles/widgets/unit.less +0 -15
- package/src/tex-wrangler.ts +0 -182
- package/src/translation-linter.ts +0 -126
- package/src/traversal.ts +0 -138
- package/src/types.ts +0 -513
- package/src/util/a11y.ts +0 -23
- package/src/util/answer-types.test.ts +0 -114
- package/src/util/answer-types.ts +0 -876
- package/src/util/colors.ts +0 -93
- package/src/util/drawing-transform.test.ts +0 -67
- package/src/util/drawing-transform.ts +0 -102
- package/src/util/extract-perseus-data.ts +0 -539
- package/src/util/fix-passage-refs.ts +0 -121
- package/src/util/geometry.test.ts +0 -75
- package/src/util/geometry.ts +0 -312
- package/src/util/graph-bounds.ts +0 -23
- package/src/util/graph-utils.ts +0 -15
- package/src/util/graphie.test.ts +0 -1277
- package/src/util/graphie.ts +0 -1576
- package/src/util/interactive.ts +0 -4174
- package/src/util/interval.ts +0 -4
- package/src/util/katex-preprocess.ts +0 -14
- package/src/util/math.test.ts +0 -11
- package/src/util/math.ts +0 -294
- package/src/util/mobile-native-utils.test.ts +0 -22
- package/src/util/mobile-native-utils.ts +0 -12
- package/src/util/parse-perseus-json.test.ts +0 -10
- package/src/util/parse-perseus-json.ts +0 -13
- package/src/util/react-render.tsx +0 -27
- package/src/util/register-all-widgets-for-testing.ts +0 -13
- package/src/util/scroll-utils.ts +0 -155
- package/src/util/sizing-utils.ts +0 -46
- package/src/util/tex.ts +0 -73
- package/src/util/url-utils.ts +0 -34
- package/src/util.graphie.test.ts +0 -45
- package/src/util.graphie.ts +0 -59
- package/src/util.ts +0 -966
- package/src/version.ts +0 -10
- package/src/widget-container.tsx +0 -199
- package/src/widgets/__stories__/categorizer.stories.tsx +0 -14
- package/src/widgets/__stories__/cs-program.stories.tsx +0 -14
- package/src/widgets/__stories__/definition.stories.tsx +0 -114
- package/src/widgets/__stories__/dropdown.stories.tsx +0 -14
- package/src/widgets/__stories__/explanation.stories.tsx +0 -18
- package/src/widgets/__stories__/expression.stories.tsx +0 -145
- package/src/widgets/__stories__/graded-group-set.stories.tsx +0 -31
- package/src/widgets/__stories__/graded-group.stories.tsx +0 -31
- package/src/widgets/__stories__/grapher.stories.tsx +0 -46
- package/src/widgets/__stories__/group.stories.tsx +0 -14
- package/src/widgets/__stories__/iframe.stories.tsx +0 -14
- package/src/widgets/__stories__/image.stories.tsx +0 -85
- package/src/widgets/__stories__/input-number.stories.tsx +0 -101
- package/src/widgets/__stories__/interaction.stories.tsx +0 -14
- package/src/widgets/__stories__/interactive-graph.stories.tsx +0 -60
- package/src/widgets/__stories__/label-image.stories.tsx +0 -130
- package/src/widgets/__stories__/matcher.stories.tsx +0 -14
- package/src/widgets/__stories__/matrix.stories.tsx +0 -14
- package/src/widgets/__stories__/number-line.stories.tsx +0 -14
- package/src/widgets/__stories__/numeric-input.stories.tsx +0 -105
- package/src/widgets/__stories__/orderer.stories.tsx +0 -18
- package/src/widgets/__stories__/passage-ref.stories.tsx +0 -33
- package/src/widgets/__stories__/passage.stories.tsx +0 -39
- package/src/widgets/__stories__/plotter.stories.tsx +0 -27
- package/src/widgets/__stories__/radio.stories.tsx +0 -86
- package/src/widgets/__stories__/random-widgets.stories.tsx +0 -59
- package/src/widgets/__stories__/sorter.stories.tsx +0 -14
- package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +0 -66
- package/src/widgets/__stories__/unit.stories.tsx +0 -28
- package/src/widgets/__stories__/video-transcript-link.stories.tsx +0 -23
- package/src/widgets/__stories__/video.stories.tsx +0 -22
- package/src/widgets/__testdata__/base-radio.testdata.ts +0 -21
- package/src/widgets/__testdata__/categorizer.testdata.ts +0 -89
- package/src/widgets/__testdata__/cs-program.testdata.ts +0 -27
- package/src/widgets/__testdata__/dropdown.testdata.ts +0 -71
- package/src/widgets/__testdata__/explanation.testdata.ts +0 -85
- package/src/widgets/__testdata__/expression.testdata.ts +0 -198
- package/src/widgets/__testdata__/graded-group-set.testdata.ts +0 -356
- package/src/widgets/__testdata__/graded-group.testdata.ts +0 -77
- package/src/widgets/__testdata__/grapher.testdata.ts +0 -374
- package/src/widgets/__testdata__/group.testdata.ts +0 -153
- package/src/widgets/__testdata__/iframe.testdata.ts +0 -35
- package/src/widgets/__testdata__/image.testdata.ts +0 -36
- package/src/widgets/__testdata__/input-number.testdata.ts +0 -72
- package/src/widgets/__testdata__/interaction.testdata.ts +0 -262
- package/src/widgets/__testdata__/interactive-graph-random.testdata.ts +0 -269
- package/src/widgets/__testdata__/interactive-graph.testdata.ts +0 -644
- package/src/widgets/__testdata__/label-image.testdata.ts +0 -308
- package/src/widgets/__testdata__/matcher.testdata.ts +0 -33
- package/src/widgets/__testdata__/matrix.testdata.ts +0 -28
- package/src/widgets/__testdata__/number-line.testdata.ts +0 -30
- package/src/widgets/__testdata__/numeric-input.testdata.ts +0 -265
- package/src/widgets/__testdata__/orderer.testdata.ts +0 -123
- package/src/widgets/__testdata__/passage-ref.testdata.ts +0 -101
- package/src/widgets/__testdata__/passage.testdata.ts +0 -95
- package/src/widgets/__testdata__/plotter.testdata.ts +0 -36
- package/src/widgets/__testdata__/radio.testdata.ts +0 -324
- package/src/widgets/__testdata__/randomizers.ts +0 -51
- package/src/widgets/__testdata__/sorter.testdata.ts +0 -19
- package/src/widgets/__testdata__/video.testdata.ts +0 -61
- package/src/widgets/__tests__/__snapshots__/categorizer.test.ts.snap +0 -940
- package/src/widgets/__tests__/__snapshots__/cs-program.test.ts.snap +0 -67
- package/src/widgets/__tests__/__snapshots__/definition.test.ts.snap +0 -97
- package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +0 -133
- package/src/widgets/__tests__/__snapshots__/explanation.test.ts.snap +0 -425
- package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt.test.ts.snap +0 -729
- package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +0 -339
- package/src/widgets/__tests__/__snapshots__/graded-group.test.ts.snap +0 -801
- package/src/widgets/__tests__/__snapshots__/grapher.test.ts.snap +0 -2673
- package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +0 -1211
- package/src/widgets/__tests__/__snapshots__/iframe.test.ts.snap +0 -61
- package/src/widgets/__tests__/__snapshots__/image.test.ts.snap +0 -378
- package/src/widgets/__tests__/__snapshots__/input-number.test.ts.snap +0 -495
- package/src/widgets/__tests__/__snapshots__/interaction.test.ts.snap +0 -292
- package/src/widgets/__tests__/__snapshots__/interactive-graph.test.ts.snap +0 -2889
- package/src/widgets/__tests__/__snapshots__/matcher.test.tsx.snap +0 -883
- package/src/widgets/__tests__/__snapshots__/matrix.test.ts.snap +0 -514
- package/src/widgets/__tests__/__snapshots__/number-line.test.ts.snap +0 -814
- package/src/widgets/__tests__/__snapshots__/numeric-input.test.ts.snap +0 -470
- package/src/widgets/__tests__/__snapshots__/orderer.test.ts.snap +0 -243
- package/src/widgets/__tests__/__snapshots__/passage-ref.test.ts.snap +0 -434
- package/src/widgets/__tests__/__snapshots__/passage.test.tsx.snap +0 -1956
- package/src/widgets/__tests__/__snapshots__/plotter.test.tsx.snap +0 -827
- package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +0 -4237
- package/src/widgets/__tests__/__snapshots__/unit.test.tsx.snap +0 -20
- package/src/widgets/__tests__/__snapshots__/video.test.ts.snap +0 -153
- package/src/widgets/__tests__/categorizer.test.ts +0 -203
- package/src/widgets/__tests__/cs-program.test.ts +0 -43
- package/src/widgets/__tests__/definition.test.ts +0 -136
- package/src/widgets/__tests__/dropdown.test.ts +0 -105
- package/src/widgets/__tests__/explanation.test.ts +0 -198
- package/src/widgets/__tests__/expression-mobile.test.tsx +0 -363
- package/src/widgets/__tests__/expression.test.tsx +0 -484
- package/src/widgets/__tests__/graded-group-set-jipt.test.ts +0 -30
- package/src/widgets/__tests__/graded-group-set.test.ts +0 -288
- package/src/widgets/__tests__/graded-group.test.ts +0 -232
- package/src/widgets/__tests__/grapher.cypress.ts +0 -576
- package/src/widgets/__tests__/grapher.test.ts +0 -33
- package/src/widgets/__tests__/group.test.tsx +0 -510
- package/src/widgets/__tests__/iframe.test.ts +0 -45
- package/src/widgets/__tests__/image.test.ts +0 -62
- package/src/widgets/__tests__/input-number.test.ts +0 -354
- package/src/widgets/__tests__/interaction.test.ts +0 -31
- package/src/widgets/__tests__/interactive-graph.cypress.ts +0 -815
- package/src/widgets/__tests__/interactive-graph.test.ts +0 -106
- package/src/widgets/__tests__/matcher.test.tsx +0 -176
- package/src/widgets/__tests__/matrix.test.ts +0 -82
- package/src/widgets/__tests__/number-line.test.ts +0 -79
- package/src/widgets/__tests__/numeric-input.test.ts +0 -575
- package/src/widgets/__tests__/orderer.test.ts +0 -73
- package/src/widgets/__tests__/passage-ref.test.ts +0 -168
- package/src/widgets/__tests__/passage.test.tsx +0 -224
- package/src/widgets/__tests__/plotter.test.tsx +0 -69
- package/src/widgets/__tests__/radio/base-radio.test.tsx +0 -470
- package/src/widgets/__tests__/radio/choice-icon.test.tsx +0 -90
- package/src/widgets/__tests__/radio/choice.test.tsx +0 -310
- package/src/widgets/__tests__/radio/focus-ring.test.tsx +0 -22
- package/src/widgets/__tests__/radio/option-status.test.tsx +0 -77
- package/src/widgets/__tests__/radio.test.ts +0 -807
- package/src/widgets/__tests__/randomizers.test.ts +0 -53
- package/src/widgets/__tests__/renderQuestion.tsx +0 -101
- package/src/widgets/__tests__/unit.test.tsx +0 -168
- package/src/widgets/__tests__/video.test.ts +0 -69
- package/src/widgets/categorizer.tsx +0 -344
- package/src/widgets/cs-program.tsx +0 -227
- package/src/widgets/definition.tsx +0 -121
- package/src/widgets/deprecated-standin/__stories__/deprecated-standin.stories.tsx +0 -30
- package/src/widgets/deprecated-standin/__tests__/__snapshots__/deprecated-standin.test.ts.snap +0 -70
- package/src/widgets/deprecated-standin/__tests__/deprecated-standin.test.ts +0 -93
- package/src/widgets/deprecated-standin/index.tsx +0 -58
- package/src/widgets/dropdown.tsx +0 -142
- package/src/widgets/explanation.tsx +0 -317
- package/src/widgets/expression.tsx +0 -707
- package/src/widgets/graded-group-answer-bar.tsx +0 -142
- package/src/widgets/graded-group-set.tsx +0 -338
- package/src/widgets/graded-group.tsx +0 -471
- package/src/widgets/grapher/util.tsx +0 -799
- package/src/widgets/grapher.tsx +0 -660
- package/src/widgets/group.tsx +0 -195
- package/src/widgets/iframe.tsx +0 -195
- package/src/widgets/image.tsx +0 -312
- package/src/widgets/input-number.tsx +0 -412
- package/src/widgets/interaction/arrow-picker.tsx +0 -38
- package/src/widgets/interaction/color-picker.tsx +0 -69
- package/src/widgets/interaction/dash-picker.tsx +0 -45
- package/src/widgets/interaction/element-container.tsx +0 -100
- package/src/widgets/interaction.tsx +0 -826
- package/src/widgets/interactive-graph.tsx +0 -2597
- package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -91
- package/src/widgets/label-image/__stories__/answer-pill.stories.tsx +0 -62
- package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -171
- package/src/widgets/label-image/__tests__/answer-pill.test.tsx +0 -75
- package/src/widgets/label-image/__tests__/hide-answers-toggle.test.tsx +0 -39
- package/src/widgets/label-image/answer-choices.tsx +0 -88
- package/src/widgets/label-image/answer-pill.tsx +0 -103
- package/src/widgets/label-image/hide-answers-toggle.tsx +0 -40
- package/src/widgets/label-image/marker.tsx +0 -268
- package/src/widgets/label-image/strings.ts +0 -5
- package/src/widgets/label-image/types.ts +0 -27
- package/src/widgets/label-image.test.ts +0 -928
- package/src/widgets/label-image.tsx +0 -819
- package/src/widgets/lights-puzzle.tsx +0 -359
- package/src/widgets/matcher.tsx +0 -298
- package/src/widgets/matrix.tsx +0 -634
- package/src/widgets/measurer.tsx +0 -222
- package/src/widgets/molecule/__tests__/layout.test.ts +0 -162
- package/src/widgets/molecule/__tests__/parser.test.ts +0 -155
- package/src/widgets/molecule/molecule-drawing.ts +0 -173
- package/src/widgets/molecule/molecule-layout.ts +0 -362
- package/src/widgets/molecule/smiles-parser.ts +0 -296
- package/src/widgets/molecule.tsx +0 -172
- package/src/widgets/number-line.tsx +0 -862
- package/src/widgets/numeric-input.tsx +0 -576
- package/src/widgets/orderer.tsx +0 -786
- package/src/widgets/passage/__tests__/passage-markdown.test.ts +0 -376
- package/src/widgets/passage/passage-markdown.tsx +0 -475
- package/src/widgets/passage-ref-target.tsx +0 -76
- package/src/widgets/passage-ref.tsx +0 -213
- package/src/widgets/passage.tsx +0 -582
- package/src/widgets/plotter.tsx +0 -1194
- package/src/widgets/radio/__stories__/base-radio.stories.tsx +0 -196
- package/src/widgets/radio/__stories__/choice-icon.stories.tsx +0 -166
- package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +0 -40
- package/src/widgets/radio/__stories__/choice.stories.tsx +0 -137
- package/src/widgets/radio/__stories__/focus-ring.stories.tsx +0 -43
- package/src/widgets/radio/__stories__/option-status.stories.tsx +0 -103
- package/src/widgets/radio/base-radio.tsx +0 -511
- package/src/widgets/radio/choice-a11y-text.ts +0 -61
- package/src/widgets/radio/choice-icon/choice-icon.tsx +0 -218
- package/src/widgets/radio/choice-icon/cross-out-line.tsx +0 -51
- package/src/widgets/radio/choice-icon/shared-styles.ts +0 -15
- package/src/widgets/radio/choice-none-above.tsx +0 -56
- package/src/widgets/radio/choice.jsx.fixture.ts +0 -45
- package/src/widgets/radio/choice.tsx +0 -359
- package/src/widgets/radio/focus-ring.tsx +0 -57
- package/src/widgets/radio/option-status.tsx +0 -98
- package/src/widgets/radio/util.test.ts +0 -18
- package/src/widgets/radio/util.ts +0 -31
- package/src/widgets/radio/widget.tsx +0 -520
- package/src/widgets/radio.ts +0 -157
- package/src/widgets/reaction-diagram.tsx +0 -144
- package/src/widgets/sequence.tsx +0 -142
- package/src/widgets/simulator.tsx +0 -829
- package/src/widgets/sorter.tsx +0 -155
- package/src/widgets/table.tsx +0 -393
- package/src/widgets/unit.tsx +0 -374
- package/src/widgets/video-transcript-link.test.tsx +0 -213
- package/src/widgets/video-transcript-link.tsx +0 -84
- package/src/widgets/video.tsx +0 -135
- package/src/widgets.ts +0 -489
- package/src/zoom.ts +0 -555
- package/tsconfig-build.json +0 -26
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,819 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Direct image labeling widget.
|
|
3
|
-
*
|
|
4
|
-
* Label on image widget enables creating more natural, conceptual questions
|
|
5
|
-
* that involve the use of images, and enable learners to demonstrate their
|
|
6
|
-
* knowledge by directly interacting with the image.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import Clickable from "@khanacademy/wonder-blocks-clickable";
|
|
10
|
-
import {View} from "@khanacademy/wonder-blocks-core";
|
|
11
|
-
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
12
|
-
import {StyleSheet, css} from "aphrodite";
|
|
13
|
-
import classNames from "classnames";
|
|
14
|
-
import * as React from "react";
|
|
15
|
-
import * as ReactDOM from "react-dom";
|
|
16
|
-
|
|
17
|
-
import AssetContext from "../asset-context";
|
|
18
|
-
import SvgImage from "../components/svg-image";
|
|
19
|
-
import {useDependencies} from "../dependencies";
|
|
20
|
-
import Renderer from "../renderer";
|
|
21
|
-
import {typography} from "../styles/global-styles";
|
|
22
|
-
import mediaQueries from "../styles/media-queries";
|
|
23
|
-
|
|
24
|
-
import AnswerChoices from "./label-image/answer-choices";
|
|
25
|
-
import {HideAnswersToggle} from "./label-image/hide-answers-toggle";
|
|
26
|
-
import Marker from "./label-image/marker";
|
|
27
|
-
|
|
28
|
-
import type {
|
|
29
|
-
InteractiveMarkerType,
|
|
30
|
-
InteractiveMarkerScore,
|
|
31
|
-
} from "./label-image/types";
|
|
32
|
-
import type {DependencyProps} from "../dependencies";
|
|
33
|
-
import type {ChangeableProps} from "../mixins/changeable";
|
|
34
|
-
import type {APIOptions, PerseusScore, WidgetExports} from "../types";
|
|
35
|
-
import type {PropsFor} from "@khanacademy/wonder-blocks-core";
|
|
36
|
-
import type {CSSProperties} from "aphrodite";
|
|
37
|
-
|
|
38
|
-
export type PreferredPopoverDirection =
|
|
39
|
-
| "NONE"
|
|
40
|
-
| "UP"
|
|
41
|
-
| "DOWN"
|
|
42
|
-
| "LEFT"
|
|
43
|
-
| "RIGHT";
|
|
44
|
-
|
|
45
|
-
type MarkersState = {
|
|
46
|
-
markers: ReadonlyArray<InteractiveMarkerType>;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Represents a direction vector.
|
|
51
|
-
* No diagonals allowed.
|
|
52
|
-
*/
|
|
53
|
-
type Direction =
|
|
54
|
-
| {
|
|
55
|
-
x: 0;
|
|
56
|
-
y: 1 | -1;
|
|
57
|
-
}
|
|
58
|
-
| {
|
|
59
|
-
x: 1 | -1;
|
|
60
|
-
y: 0;
|
|
61
|
-
}
|
|
62
|
-
| {
|
|
63
|
-
x: 0;
|
|
64
|
-
y: 0;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
type Point = {
|
|
68
|
-
x: number;
|
|
69
|
-
y: number;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
type LabelImageProps = ChangeableProps &
|
|
73
|
-
DependencyProps & {
|
|
74
|
-
apiOptions: APIOptions;
|
|
75
|
-
// The list of possible answer choices.
|
|
76
|
-
choices: ReadonlyArray<string>;
|
|
77
|
-
// The question image properties.
|
|
78
|
-
imageAlt: string;
|
|
79
|
-
imageUrl: string;
|
|
80
|
-
imageWidth: number;
|
|
81
|
-
imageHeight: number;
|
|
82
|
-
// The list of label markers on the question image.
|
|
83
|
-
markers: ReadonlyArray<InteractiveMarkerType>;
|
|
84
|
-
// Whether multiple answer choices may be selected for markers.
|
|
85
|
-
multipleAnswers: boolean;
|
|
86
|
-
// Whether to hide answer choices from user instructions.
|
|
87
|
-
hideChoicesFromInstructions: boolean;
|
|
88
|
-
// Whether the question has been answered by the user.
|
|
89
|
-
questionCompleted: boolean;
|
|
90
|
-
// preferred placement for popover (preference, not MUST)
|
|
91
|
-
preferredPopoverDirection?: PreferredPopoverDirection;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
type LabelImageState = {
|
|
95
|
-
// The user selected marker index, defaults to -1, no selection.
|
|
96
|
-
activeMarkerIndex: number;
|
|
97
|
-
// Whether any of the markers were interacted with by the user.
|
|
98
|
-
markersInteracted: boolean;
|
|
99
|
-
// Hide answer pills.
|
|
100
|
-
hideAnswers: boolean;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
export class LabelImage extends React.Component<
|
|
104
|
-
LabelImageProps,
|
|
105
|
-
LabelImageState
|
|
106
|
-
> {
|
|
107
|
-
// The rendered markers on the question image for labeling.
|
|
108
|
-
_markers: Array<Marker | null | undefined>;
|
|
109
|
-
|
|
110
|
-
static gradeMarker(marker: InteractiveMarkerType): InteractiveMarkerScore {
|
|
111
|
-
const score = {
|
|
112
|
-
hasAnswers: false,
|
|
113
|
-
isCorrect: false,
|
|
114
|
-
} as const;
|
|
115
|
-
|
|
116
|
-
if (marker.selected && marker.selected.length > 0) {
|
|
117
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'hasAnswers' because it is a read-only property.
|
|
118
|
-
score.hasAnswers = true;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (marker.answers.length > 0) {
|
|
122
|
-
if (
|
|
123
|
-
marker.selected &&
|
|
124
|
-
marker.selected.length === marker.answers.length
|
|
125
|
-
) {
|
|
126
|
-
// All correct answers are selected by the user.
|
|
127
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'isCorrect' because it is a read-only property.
|
|
128
|
-
score.isCorrect = marker.selected.every((choice) =>
|
|
129
|
-
marker.answers.includes(choice),
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
} else if (!marker.selected || marker.selected.length === 0) {
|
|
133
|
-
// Correct as no answers should be selected by the user.
|
|
134
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'isCorrect' because it is a read-only property.
|
|
135
|
-
score.isCorrect = true;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return score;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
static validate(
|
|
142
|
-
state: MarkersState,
|
|
143
|
-
rubric?: LabelImageProps,
|
|
144
|
-
): PerseusScore {
|
|
145
|
-
let numAnswered = 0;
|
|
146
|
-
let numCorrect = 0;
|
|
147
|
-
|
|
148
|
-
for (const marker of state.markers) {
|
|
149
|
-
const score = LabelImage.gradeMarker(marker);
|
|
150
|
-
|
|
151
|
-
if (score.hasAnswers) {
|
|
152
|
-
numAnswered++;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (score.isCorrect) {
|
|
156
|
-
numCorrect++;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// We expect all question markers to be answered before grading.
|
|
161
|
-
if (numAnswered !== state.markers.length) {
|
|
162
|
-
return {
|
|
163
|
-
type: "invalid",
|
|
164
|
-
message: null,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return {
|
|
169
|
-
type: "points",
|
|
170
|
-
// Markers with no expected answers are graded as correct if user
|
|
171
|
-
// makes no answer selection.
|
|
172
|
-
earned: numCorrect === state.markers.length ? 1 : 0,
|
|
173
|
-
total: 1,
|
|
174
|
-
message: null,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Test whether point is contained within triangle.
|
|
180
|
-
*
|
|
181
|
-
* Implementation taken from: https://stackoverflow.com/a/2049593
|
|
182
|
-
*/
|
|
183
|
-
static pointInTriangle(p: Point, a: Point, b: Point, c: Point): boolean {
|
|
184
|
-
const sign = (p1: Point, p2: Point, p3) =>
|
|
185
|
-
(p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
|
|
186
|
-
|
|
187
|
-
const b1 = sign(p, a, b) < 0;
|
|
188
|
-
const b2 = sign(p, b, c) < 0;
|
|
189
|
-
const b3 = sign(p, c, a) < 0;
|
|
190
|
-
|
|
191
|
-
return b1 === b2 && b2 === b3;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Determine the image side given a marker position (as percent of size).
|
|
196
|
-
*/
|
|
197
|
-
static imageSideForMarkerPosition(
|
|
198
|
-
x: number,
|
|
199
|
-
y: number,
|
|
200
|
-
preferredDirection: PreferredPopoverDirection | undefined,
|
|
201
|
-
): "bottom" | "left" | "right" | "top" | "center" {
|
|
202
|
-
// Special handling for preferred size
|
|
203
|
-
if (preferredDirection && preferredDirection !== "NONE") {
|
|
204
|
-
if (preferredDirection === "LEFT" && x > 20) {
|
|
205
|
-
return "right";
|
|
206
|
-
} else if (preferredDirection === "RIGHT" && x < 80) {
|
|
207
|
-
return "left";
|
|
208
|
-
} else if (preferredDirection === "UP" && y > 20) {
|
|
209
|
-
return "bottom";
|
|
210
|
-
} else if (preferredDirection === "DOWN" && y < 80) {
|
|
211
|
-
return "top";
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Special handling for when marker is positioned near the horizontal
|
|
216
|
-
// edges of the image. We want to ensure the returned side would not
|
|
217
|
-
// result in a popup rendering that may overflow outside the page.
|
|
218
|
-
if (x < 20) {
|
|
219
|
-
return "left";
|
|
220
|
-
}
|
|
221
|
-
if (x > 80) {
|
|
222
|
-
return "right";
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Points to define corners (and center) of a rectangle representing
|
|
226
|
-
// the constrained area of the image for which we want to determine the
|
|
227
|
-
// image side.
|
|
228
|
-
const tl = {x: 20, y: 0} as const;
|
|
229
|
-
const tr = {x: 80, y: 0} as const;
|
|
230
|
-
const br = {x: 80, y: 100} as const;
|
|
231
|
-
const bl = {x: 20, y: 100} as const;
|
|
232
|
-
const cp = {x: 50, y: 50} as const;
|
|
233
|
-
|
|
234
|
-
// The triangles representing the sides to test.
|
|
235
|
-
const triangles = {
|
|
236
|
-
top: [tl, tr, cp],
|
|
237
|
-
right: [cp, tr, br],
|
|
238
|
-
bottom: [bl, cp, br],
|
|
239
|
-
left: [tl, cp, bl],
|
|
240
|
-
} as const;
|
|
241
|
-
|
|
242
|
-
const p = {x, y} as const;
|
|
243
|
-
|
|
244
|
-
// Test whether marker is positioned within one of the triangles
|
|
245
|
-
// representing the sides.
|
|
246
|
-
for (const side of Object.keys(triangles)) {
|
|
247
|
-
const corners = triangles[side];
|
|
248
|
-
|
|
249
|
-
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
250
|
-
if (LabelImage.pointInTriangle(p, ...corners)) {
|
|
251
|
-
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type '"left" | "top" | "center" | "right" | "bottom"'.
|
|
252
|
-
return side;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Fallback, if side was not found for marker position.
|
|
257
|
-
return "center";
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Calculate the next marker to navigate to, from the "this marker".
|
|
262
|
-
*
|
|
263
|
-
* Given a cardinal navigation direction (in the x or y axis), return the
|
|
264
|
-
* next marker index to visit.
|
|
265
|
-
*/
|
|
266
|
-
static navigateToMarkerIndex(
|
|
267
|
-
navigateDirection: Direction,
|
|
268
|
-
markers: ReadonlyArray<InteractiveMarkerType>,
|
|
269
|
-
thisIndex: number,
|
|
270
|
-
): number {
|
|
271
|
-
const thisMarker = markers[thisIndex];
|
|
272
|
-
|
|
273
|
-
// Sort markers along the direction of navigation from "this" marker.
|
|
274
|
-
const sortedMarkers = markers
|
|
275
|
-
.map((otherMarker, index) => {
|
|
276
|
-
// Calculate distance and (normalized) vector to the marker from
|
|
277
|
-
// "this" marker.
|
|
278
|
-
const x = otherMarker.x - thisMarker.x;
|
|
279
|
-
const y = otherMarker.y - thisMarker.y;
|
|
280
|
-
|
|
281
|
-
const dist = Math.sqrt(x ** 2 + y ** 2);
|
|
282
|
-
|
|
283
|
-
return {
|
|
284
|
-
index,
|
|
285
|
-
dist,
|
|
286
|
-
dir: {
|
|
287
|
-
x: dist !== 0 ? x / dist : 0,
|
|
288
|
-
y: dist !== 0 ? y / dist : 0,
|
|
289
|
-
},
|
|
290
|
-
};
|
|
291
|
-
})
|
|
292
|
-
.filter((marker) => {
|
|
293
|
-
if (marker.index === thisIndex) {
|
|
294
|
-
return false;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Exclude any marker that has been answered correctly,
|
|
298
|
-
// as it should no longer be interacted with by the user.
|
|
299
|
-
return markers[marker.index].showCorrectness !== "correct";
|
|
300
|
-
})
|
|
301
|
-
.sort((a, b) => {
|
|
302
|
-
// Project distances to the two markers being sorted (from the
|
|
303
|
-
// "this" marker) along the direction of navigation. This will
|
|
304
|
-
// order by the "closest" marker in the direction of navigation,
|
|
305
|
-
// not the "absolute" closest marker to "this" marker.
|
|
306
|
-
// We round the calculated distances so that if markers are
|
|
307
|
-
// found to be positioned "close enough" to the "this" marker,
|
|
308
|
-
// they will be treated as coplanar with it.
|
|
309
|
-
const distA = Math.round(
|
|
310
|
-
a.dist *
|
|
311
|
-
(navigateDirection.x * a.dir.x +
|
|
312
|
-
navigateDirection.y * a.dir.y),
|
|
313
|
-
);
|
|
314
|
-
const distB = Math.round(
|
|
315
|
-
b.dist *
|
|
316
|
-
(navigateDirection.x * b.dir.x +
|
|
317
|
-
navigateDirection.y * b.dir.y),
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
let dirA;
|
|
321
|
-
let dirB;
|
|
322
|
-
// Determine whether the two markers lie ahead or behind the
|
|
323
|
-
// "this" marker. A marker that lies in the same plane as the
|
|
324
|
-
// "this" marker will be considered to be behind it.
|
|
325
|
-
// Only navigation in the "x" or "y" axis is supported, no
|
|
326
|
-
// diagonals.
|
|
327
|
-
if (navigateDirection.x > 0) {
|
|
328
|
-
dirA = a.dir.x > 0 && distA !== 0;
|
|
329
|
-
dirB = b.dir.x > 0 && distB !== 0;
|
|
330
|
-
} else if (navigateDirection.x < 0) {
|
|
331
|
-
dirA = a.dir.x < 0 && distA !== 0;
|
|
332
|
-
dirB = b.dir.x < 0 && distB !== 0;
|
|
333
|
-
} else if (navigateDirection.y > 0) {
|
|
334
|
-
dirA = a.dir.y > 0 && distA !== 0;
|
|
335
|
-
dirB = b.dir.y > 0 && distB !== 0;
|
|
336
|
-
} else if (navigateDirection.y < 0) {
|
|
337
|
-
dirA = a.dir.y < 0 && distA !== 0;
|
|
338
|
-
dirB = b.dir.y < 0 && distB !== 0;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// If one of the markers is positioned behind, sort the other
|
|
342
|
-
// marker ahead of it in the order.
|
|
343
|
-
if (dirA !== dirB) {
|
|
344
|
-
if (dirA) {
|
|
345
|
-
return -1;
|
|
346
|
-
}
|
|
347
|
-
return 1;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Both markers are either positioned ahead or behind, sort the
|
|
351
|
-
// closest (or furthest if behind, to "wrap" navigation) marker
|
|
352
|
-
// ahead in the order.
|
|
353
|
-
return distA - distB;
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
// Return the index in the provided markers array of the closest marker
|
|
357
|
-
// found along the direction of navigation, or "this" index if no other
|
|
358
|
-
// marker was found. This may happen if the user has answered all but
|
|
359
|
-
// one ("this" marker) of the markers correctly in the question.
|
|
360
|
-
return sortedMarkers.length > 0 ? sortedMarkers[0].index : thisIndex;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
constructor(props: LabelImageProps) {
|
|
364
|
-
super(props);
|
|
365
|
-
|
|
366
|
-
this._markers = [];
|
|
367
|
-
|
|
368
|
-
this.state = {
|
|
369
|
-
activeMarkerIndex: -1,
|
|
370
|
-
markersInteracted: false,
|
|
371
|
-
hideAnswers: false,
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
simpleValidate(rubric: LabelImageProps): PerseusScore {
|
|
376
|
-
return LabelImage.validate(this.getUserInput(), rubric);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
getUserInput(): MarkersState {
|
|
380
|
-
const {markers} = this.props;
|
|
381
|
-
return {markers};
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
showRationalesForCurrentlySelectedChoices(rubric: LabelImageProps) {
|
|
385
|
-
const {markers} = this.props;
|
|
386
|
-
const {onChange} = this.props;
|
|
387
|
-
|
|
388
|
-
const updatedMarkers = markers.map((marker) => {
|
|
389
|
-
const score = LabelImage.gradeMarker(marker);
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
...marker,
|
|
393
|
-
// Reveal correctness state for markers with answers.
|
|
394
|
-
showCorrectness: score.hasAnswers
|
|
395
|
-
? score.isCorrect
|
|
396
|
-
? "correct"
|
|
397
|
-
: "incorrect"
|
|
398
|
-
: undefined,
|
|
399
|
-
};
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
// Update Perseus widget state with user selected answers without
|
|
403
|
-
// triggering interaction events for listeners.
|
|
404
|
-
onChange({markers: updatedMarkers}, null, true);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
handleMarkerChange(index: number, marker: InteractiveMarkerType) {
|
|
408
|
-
const {markers, onChange} = this.props;
|
|
409
|
-
|
|
410
|
-
// Replace marker with a changed version at the specified index.
|
|
411
|
-
const updatedMarkers = [
|
|
412
|
-
...markers.slice(0, index),
|
|
413
|
-
{
|
|
414
|
-
...marker,
|
|
415
|
-
// Do not show correctness state if user changes answer
|
|
416
|
-
// selection for marker.
|
|
417
|
-
showCorrectness: undefined,
|
|
418
|
-
},
|
|
419
|
-
...markers.slice(index + 1),
|
|
420
|
-
];
|
|
421
|
-
|
|
422
|
-
// Update Perseus widget state with user selected answers.
|
|
423
|
-
onChange({markers: updatedMarkers});
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
activateMarker(index: number, opened: boolean) {
|
|
427
|
-
this.props.analytics?.onAnalyticsEvent({
|
|
428
|
-
type: "perseus:label-image:marker-interacted-with",
|
|
429
|
-
payload: null,
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
const {activeMarkerIndex} = this.state;
|
|
433
|
-
// Set index of opened marker
|
|
434
|
-
if (activeMarkerIndex !== index && opened) {
|
|
435
|
-
this.setState({
|
|
436
|
-
activeMarkerIndex: index,
|
|
437
|
-
markersInteracted: true,
|
|
438
|
-
});
|
|
439
|
-
} else {
|
|
440
|
-
this.setState({activeMarkerIndex: -1});
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
handleMarkerKeyDown(index: number, e: React.KeyboardEvent) {
|
|
445
|
-
const {markers} = this.props;
|
|
446
|
-
|
|
447
|
-
// One is the loneliest number.
|
|
448
|
-
if (markers.length < 2) {
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// Only navigation in the "x" or "y" axis is supported, no diagonals.
|
|
453
|
-
const navigateDirection = {
|
|
454
|
-
ArrowUp: {x: 0, y: -1},
|
|
455
|
-
ArrowRight: {x: 1, y: 0},
|
|
456
|
-
ArrowDown: {x: 0, y: 1},
|
|
457
|
-
ArrowLeft: {x: -1, y: 0},
|
|
458
|
-
}[e.key];
|
|
459
|
-
|
|
460
|
-
if (!navigateDirection) {
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
e.preventDefault();
|
|
465
|
-
|
|
466
|
-
// Focus on the closest marker along the direction of navigation.
|
|
467
|
-
const marker =
|
|
468
|
-
this._markers[
|
|
469
|
-
LabelImage.navigateToMarkerIndex(
|
|
470
|
-
// @ts-expect-error - TS2345 - Argument of type '{ x: number; y: number; } | { x: number; y: number; } | { x: number; y: number; } | { x: number; y: number; }' is not assignable to parameter of type 'Direction'.
|
|
471
|
-
navigateDirection,
|
|
472
|
-
markers,
|
|
473
|
-
index,
|
|
474
|
-
)
|
|
475
|
-
];
|
|
476
|
-
|
|
477
|
-
if (marker) {
|
|
478
|
-
(ReactDOM.findDOMNode(marker) as HTMLElement).focus();
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
handleAnswerChoicesChangeForMarker(
|
|
483
|
-
index: number,
|
|
484
|
-
selection: ReadonlyArray<boolean>,
|
|
485
|
-
) {
|
|
486
|
-
const {choices, markers} = this.props;
|
|
487
|
-
|
|
488
|
-
// Compile the user selected answer choices.
|
|
489
|
-
const selected = choices.filter((_, index) => selection[index]);
|
|
490
|
-
|
|
491
|
-
this.handleMarkerChange(index, {
|
|
492
|
-
...markers[index],
|
|
493
|
-
selected: selected.length ? selected : undefined,
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
renderMarkers(): ReadonlyArray<React.ReactNode> {
|
|
498
|
-
const {markers, questionCompleted, preferredPopoverDirection} =
|
|
499
|
-
this.props;
|
|
500
|
-
|
|
501
|
-
const {activeMarkerIndex, markersInteracted} = this.state;
|
|
502
|
-
|
|
503
|
-
// Render all markers for widget.
|
|
504
|
-
return markers.map((marker, index): React.ReactElement => {
|
|
505
|
-
// Determine whether page is rendered in a narrow browser window.
|
|
506
|
-
const isNarrowPage = window.matchMedia(
|
|
507
|
-
mediaQueries.xsOrSmaller.replace("@media ", ""),
|
|
508
|
-
).matches;
|
|
509
|
-
|
|
510
|
-
// Determine whether the image is wider than it is tall.
|
|
511
|
-
const isWideImage =
|
|
512
|
-
this.props.imageWidth / 2 > this.props.imageHeight;
|
|
513
|
-
|
|
514
|
-
let side: "bottom" | "left" | "right" | "top";
|
|
515
|
-
let markerPosition;
|
|
516
|
-
// Position popup closest to the center, preferring it renders
|
|
517
|
-
// entirely within the image area.
|
|
518
|
-
if (isNarrowPage || isWideImage) {
|
|
519
|
-
side = marker.y > 50 ? "top" : "bottom";
|
|
520
|
-
markerPosition = marker.y > 50 ? "bottom" : "top";
|
|
521
|
-
} else {
|
|
522
|
-
markerPosition = LabelImage.imageSideForMarkerPosition(
|
|
523
|
-
marker.x,
|
|
524
|
-
marker.y,
|
|
525
|
-
preferredPopoverDirection,
|
|
526
|
-
);
|
|
527
|
-
if (markerPosition === "center") {
|
|
528
|
-
markerPosition = "bottom";
|
|
529
|
-
}
|
|
530
|
-
// This mirrors the calculated side of where the marker is
|
|
531
|
-
// located within the image, so that popup appears closer to
|
|
532
|
-
// the image center.
|
|
533
|
-
side = {
|
|
534
|
-
left: "right",
|
|
535
|
-
top: "bottom",
|
|
536
|
-
right: "left",
|
|
537
|
-
bottom: "top",
|
|
538
|
-
}[markerPosition];
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
const score = LabelImage.gradeMarker(marker);
|
|
542
|
-
// Once the question is answered, show markers
|
|
543
|
-
// with correct answers, otherwise passthrough
|
|
544
|
-
// the correctness state.
|
|
545
|
-
const showCorrectness =
|
|
546
|
-
questionCompleted && score.hasAnswers && score.isCorrect
|
|
547
|
-
? "correct"
|
|
548
|
-
: marker.showCorrectness;
|
|
549
|
-
|
|
550
|
-
// Disable marker interaction once the question is answered correctly.
|
|
551
|
-
const disabled = showCorrectness === "correct";
|
|
552
|
-
|
|
553
|
-
const adjustPillDistance: CSSProperties = {
|
|
554
|
-
[`margin${
|
|
555
|
-
markerPosition.charAt(0).toUpperCase() +
|
|
556
|
-
markerPosition.slice(1)
|
|
557
|
-
}`]: 10, // move pill further from marker
|
|
558
|
-
};
|
|
559
|
-
|
|
560
|
-
const answerChoicesActive = index === activeMarkerIndex;
|
|
561
|
-
|
|
562
|
-
const showAnswerChoice =
|
|
563
|
-
marker.selected &&
|
|
564
|
-
!answerChoicesActive &&
|
|
565
|
-
!this.state.hideAnswers;
|
|
566
|
-
|
|
567
|
-
return (
|
|
568
|
-
<View
|
|
569
|
-
style={{
|
|
570
|
-
position: "absolute",
|
|
571
|
-
left: `${marker.x}%`,
|
|
572
|
-
top: `${marker.y}%`,
|
|
573
|
-
// reset to allow child (answer pill) to control z-index
|
|
574
|
-
zIndex: "unset",
|
|
575
|
-
}}
|
|
576
|
-
>
|
|
577
|
-
<AnswerChoices
|
|
578
|
-
key={`answers-${marker.x}.${marker.y}`}
|
|
579
|
-
choices={this.props.choices.map((choice) => ({
|
|
580
|
-
content: choice,
|
|
581
|
-
checked: marker.selected
|
|
582
|
-
? marker.selected.includes(choice)
|
|
583
|
-
: false,
|
|
584
|
-
}))}
|
|
585
|
-
multipleSelect={this.props.multipleAnswers}
|
|
586
|
-
onChange={(selection) => {
|
|
587
|
-
this.props.analytics?.onAnalyticsEvent({
|
|
588
|
-
type: "perseus:label-image:choiced-interacted-with",
|
|
589
|
-
payload: null,
|
|
590
|
-
});
|
|
591
|
-
this.handleAnswerChoicesChangeForMarker(
|
|
592
|
-
index,
|
|
593
|
-
selection,
|
|
594
|
-
);
|
|
595
|
-
}}
|
|
596
|
-
onToggle={(opened) =>
|
|
597
|
-
this.activateMarker(index, opened)
|
|
598
|
-
}
|
|
599
|
-
// cannot change answer choices once question is answered
|
|
600
|
-
disabled={disabled}
|
|
601
|
-
opener={({opened}) => (
|
|
602
|
-
<Clickable
|
|
603
|
-
role="button"
|
|
604
|
-
aria-expanded={opened}
|
|
605
|
-
key={`marker-${marker.x}.${marker.y}`}
|
|
606
|
-
>
|
|
607
|
-
{({hovered, focused, pressed}) => (
|
|
608
|
-
<Marker
|
|
609
|
-
{...marker}
|
|
610
|
-
showCorrectness={showCorrectness}
|
|
611
|
-
showSelected={opened}
|
|
612
|
-
showPulsate={!markersInteracted}
|
|
613
|
-
ref={(node) =>
|
|
614
|
-
(this._markers[index] = node)
|
|
615
|
-
}
|
|
616
|
-
showAnswer={showAnswerChoice}
|
|
617
|
-
answerSide={side}
|
|
618
|
-
answerStyles={adjustPillDistance}
|
|
619
|
-
analytics={this.props.analytics}
|
|
620
|
-
focused={focused || pressed}
|
|
621
|
-
hovered={hovered}
|
|
622
|
-
/>
|
|
623
|
-
)}
|
|
624
|
-
</Clickable>
|
|
625
|
-
)}
|
|
626
|
-
/>
|
|
627
|
-
</View>
|
|
628
|
-
);
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
renderInstructions(): React.ReactNode {
|
|
633
|
-
const {
|
|
634
|
-
apiOptions: {isMobile},
|
|
635
|
-
choices,
|
|
636
|
-
multipleAnswers,
|
|
637
|
-
hideChoicesFromInstructions: hideChoices,
|
|
638
|
-
} = this.props;
|
|
639
|
-
|
|
640
|
-
const promptString = isMobile
|
|
641
|
-
? multipleAnswers
|
|
642
|
-
? i18n._(
|
|
643
|
-
"Tap each dot on the image to select all answers that apply.",
|
|
644
|
-
)
|
|
645
|
-
: i18n._("Tap each dot on the image to select an answer.")
|
|
646
|
-
: multipleAnswers
|
|
647
|
-
? i18n._(
|
|
648
|
-
"Click each dot on the image to select all answers that apply.",
|
|
649
|
-
)
|
|
650
|
-
: i18n._("Click each dot on the image to select an answer.");
|
|
651
|
-
|
|
652
|
-
const choicesString = i18n._("Choices:");
|
|
653
|
-
|
|
654
|
-
return (
|
|
655
|
-
<div
|
|
656
|
-
className={classNames(
|
|
657
|
-
"perseus-label-image-widget-instructions",
|
|
658
|
-
css(styles.instructions),
|
|
659
|
-
)}
|
|
660
|
-
>
|
|
661
|
-
<div className={css(styles.instructionsCaption)}>
|
|
662
|
-
{promptString} {!hideChoices && choicesString}
|
|
663
|
-
</div>
|
|
664
|
-
|
|
665
|
-
{!hideChoices && (
|
|
666
|
-
<div className={css(styles.instructionsChoices)}>
|
|
667
|
-
{choices.map((choice, index) => (
|
|
668
|
-
<div
|
|
669
|
-
className={css(styles.instructionsChoice)}
|
|
670
|
-
key={index}
|
|
671
|
-
>
|
|
672
|
-
<Renderer content={choice} />
|
|
673
|
-
</div>
|
|
674
|
-
))}
|
|
675
|
-
</div>
|
|
676
|
-
)}
|
|
677
|
-
</div>
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
render(): React.ReactNode {
|
|
682
|
-
const {imageAlt, imageUrl, imageWidth, imageHeight} = this.props;
|
|
683
|
-
|
|
684
|
-
const {activeMarkerIndex} = this.state;
|
|
685
|
-
|
|
686
|
-
return (
|
|
687
|
-
<div>
|
|
688
|
-
{this.renderInstructions()}
|
|
689
|
-
<div
|
|
690
|
-
className={css(styles.markersCanvas)}
|
|
691
|
-
style={{
|
|
692
|
-
maxWidth: imageWidth,
|
|
693
|
-
maxHeight: imageHeight,
|
|
694
|
-
}}
|
|
695
|
-
>
|
|
696
|
-
<div
|
|
697
|
-
className={css(
|
|
698
|
-
styles.imageContainer,
|
|
699
|
-
// Ensure image interaction is disabled while answer
|
|
700
|
-
// choices popup is visible. This addresses specific
|
|
701
|
-
// case where user may click on question image to
|
|
702
|
-
// dismiss the popup. If the image is larger in size
|
|
703
|
-
// than its rendered in the widget, this would
|
|
704
|
-
// result in a zoom of the image.
|
|
705
|
-
activeMarkerIndex !== -1 &&
|
|
706
|
-
styles.imageInteractionDisabled,
|
|
707
|
-
)}
|
|
708
|
-
>
|
|
709
|
-
<AssetContext.Consumer>
|
|
710
|
-
{({setAssetStatus}) => (
|
|
711
|
-
<SvgImage
|
|
712
|
-
alt={imageAlt}
|
|
713
|
-
src={imageUrl}
|
|
714
|
-
width={imageWidth}
|
|
715
|
-
height={imageHeight}
|
|
716
|
-
setAssetStatus={setAssetStatus}
|
|
717
|
-
/>
|
|
718
|
-
)}
|
|
719
|
-
</AssetContext.Consumer>
|
|
720
|
-
</div>
|
|
721
|
-
{this.renderMarkers()}
|
|
722
|
-
</div>
|
|
723
|
-
<HideAnswersToggle
|
|
724
|
-
areAnswersHidden={this.state.hideAnswers}
|
|
725
|
-
onChange={(hideAnswers) => {
|
|
726
|
-
this.props.analytics?.onAnalyticsEvent({
|
|
727
|
-
type: "perseus:label-image:toggle-answers-hidden",
|
|
728
|
-
payload: null,
|
|
729
|
-
});
|
|
730
|
-
this.setState({hideAnswers});
|
|
731
|
-
}}
|
|
732
|
-
/>
|
|
733
|
-
</div>
|
|
734
|
-
);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
const styles = StyleSheet.create({
|
|
739
|
-
instructions: {
|
|
740
|
-
paddingBottom: 16,
|
|
741
|
-
},
|
|
742
|
-
|
|
743
|
-
instructionsCaption: {
|
|
744
|
-
...typography.bodyXsmallBold,
|
|
745
|
-
|
|
746
|
-
paddingBottom: 16,
|
|
747
|
-
},
|
|
748
|
-
|
|
749
|
-
instructionsChoices: {
|
|
750
|
-
display: "flex",
|
|
751
|
-
flexWrap: "wrap",
|
|
752
|
-
|
|
753
|
-
margin: "-8px 0",
|
|
754
|
-
},
|
|
755
|
-
|
|
756
|
-
instructionsChoice: {
|
|
757
|
-
display: "flex",
|
|
758
|
-
alignItems: "center",
|
|
759
|
-
|
|
760
|
-
margin: "8px 0",
|
|
761
|
-
|
|
762
|
-
":not(:last-child)": {
|
|
763
|
-
"::after": {
|
|
764
|
-
content: "''",
|
|
765
|
-
display: "inline-block",
|
|
766
|
-
position: "relative",
|
|
767
|
-
|
|
768
|
-
width: 2,
|
|
769
|
-
height: 2,
|
|
770
|
-
|
|
771
|
-
marginLeft: 5,
|
|
772
|
-
marginRight: 5,
|
|
773
|
-
|
|
774
|
-
background: "rgba(33, 36, 44, 0.32)",
|
|
775
|
-
|
|
776
|
-
borderRadius: 2,
|
|
777
|
-
},
|
|
778
|
-
},
|
|
779
|
-
},
|
|
780
|
-
|
|
781
|
-
markersCanvas: {
|
|
782
|
-
position: "relative",
|
|
783
|
-
},
|
|
784
|
-
|
|
785
|
-
imageContainer: {
|
|
786
|
-
// Remove the additional height added due to white-spacing, this ensures
|
|
787
|
-
// markers canvas will have the same height as the question image.
|
|
788
|
-
display: "flex",
|
|
789
|
-
},
|
|
790
|
-
|
|
791
|
-
imageInteractionDisabled: {
|
|
792
|
-
pointerEvents: "none",
|
|
793
|
-
},
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
const LabelImageWithDependencies = React.forwardRef<
|
|
797
|
-
LabelImage,
|
|
798
|
-
Omit<PropsFor<typeof LabelImage>, keyof ReturnType<typeof useDependencies>>
|
|
799
|
-
>((props, ref) => {
|
|
800
|
-
const deps = useDependencies();
|
|
801
|
-
return <LabelImage ref={ref} analytics={deps.analytics} {...props} />;
|
|
802
|
-
});
|
|
803
|
-
|
|
804
|
-
// HACK: Propogate "static" methods onto our wrapper component.
|
|
805
|
-
// In the future we should adjust client apps to not depend on these static
|
|
806
|
-
// methods and instead adjust Peresus to provide these facilities through
|
|
807
|
-
// instance methods on our Renderers.
|
|
808
|
-
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
809
|
-
LabelImageWithDependencies.validate = LabelImage.validate;
|
|
810
|
-
// @ts-expect-error - TS2339 - Property 'gradeMarker' does not exist on type
|
|
811
|
-
LabelImageWithDependencies.gradeMarker = LabelImage.gradeMarker;
|
|
812
|
-
|
|
813
|
-
export default {
|
|
814
|
-
name: "label-image",
|
|
815
|
-
displayName: "Label Image",
|
|
816
|
-
widget: LabelImageWithDependencies,
|
|
817
|
-
accessible: true,
|
|
818
|
-
isLintable: true,
|
|
819
|
-
} as WidgetExports<typeof LabelImageWithDependencies>;
|