@khanacademy/perseus 19.1.4 → 19.1.6
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/graphie.d.ts +9 -1
- package/dist/es/index.js +255 -122
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +261 -126
- package/dist/index.js.map +1 -1
- package/dist/interactive2/interactive-util.d.ts +1 -1
- package/dist/interactive2/movable-point-options.d.ts +5 -4
- package/dist/interactive2/movable.d.ts +90 -13
- package/dist/interactive2/types.d.ts +6 -0
- package/dist/util/graphie.d.ts +5 -0
- package/dist/widgets/expression.d.ts +4 -4
- package/package.json +11 -8
- package/.babelrc.js +0 -8
- package/.eslintrc.js +0 -12
- package/CHANGELOG.md +0 -1422
- 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 -392
- 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 -43
- package/src/interactive2/__tests__/movable-point.test.ts +0 -144
- package/src/interactive2/__tests__/movable.test.ts +0 -157
- 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 -212
- 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.ts +0 -279
- package/src/interactive2/objective_.ts +0 -79
- package/src/interactive2/types.ts +0 -2
- 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 -1566
- package/src/util/interactive.ts +0 -4172
- 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,779 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
2
|
-
/* eslint-disable react/no-unsafe */
|
|
3
|
-
import {CircularSpinner} from "@khanacademy/wonder-blocks-progress-spinner";
|
|
4
|
-
import classNames from "classnames";
|
|
5
|
-
import $ from "jquery";
|
|
6
|
-
import * as React from "react";
|
|
7
|
-
import _ from "underscore";
|
|
8
|
-
|
|
9
|
-
import {getDependencies} from "../dependencies";
|
|
10
|
-
import {Errors, Log} from "../logging/log";
|
|
11
|
-
import {PerseusError} from "../perseus-error";
|
|
12
|
-
import Util from "../util";
|
|
13
|
-
import * as Zoom from "../zoom";
|
|
14
|
-
|
|
15
|
-
import FixedToResponsive from "./fixed-to-responsive";
|
|
16
|
-
import Graphie from "./graphie";
|
|
17
|
-
import ImageLoader from "./image-loader";
|
|
18
|
-
|
|
19
|
-
import type {ImageProps} from "./image-loader";
|
|
20
|
-
import type {Coord} from "../interactive2/types";
|
|
21
|
-
import type {Size} from "../perseus-types";
|
|
22
|
-
import type {Alignment, Dimensions} from "../types";
|
|
23
|
-
|
|
24
|
-
// Minimum image width to make an image appear as zoomable.
|
|
25
|
-
const ZOOMABLE_THRESHOLD = 700;
|
|
26
|
-
|
|
27
|
-
// The global cache of label data. Its format is:
|
|
28
|
-
// {
|
|
29
|
-
// hash (e.g. "c21435944d2cf0c8f39d9059cb35836aa701d04a"): {
|
|
30
|
-
// loaded: a boolean of whether the data has been loaded or not
|
|
31
|
-
// dataCallbacks: a list of callbacks to call with the data when the data
|
|
32
|
-
// is loaded
|
|
33
|
-
// data: the other data for this hash
|
|
34
|
-
// },
|
|
35
|
-
// ...
|
|
36
|
-
// }
|
|
37
|
-
const labelDataCache: Record<string, any> = {};
|
|
38
|
-
|
|
39
|
-
// Write our own JSONP handler because all the other ones don't do things we
|
|
40
|
-
// need.
|
|
41
|
-
const doJSONP = function (url: string, options) {
|
|
42
|
-
options = {
|
|
43
|
-
callbackName: "callback",
|
|
44
|
-
success: $.noop,
|
|
45
|
-
error: $.noop,
|
|
46
|
-
...options,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Create the script
|
|
50
|
-
const script = document.createElement("script");
|
|
51
|
-
script.setAttribute("async", "");
|
|
52
|
-
script.setAttribute("src", url);
|
|
53
|
-
|
|
54
|
-
// A cleanup function to run when we're done.
|
|
55
|
-
function cleanup() {
|
|
56
|
-
document.head && document.head.removeChild(script);
|
|
57
|
-
delete window[options.callbackName];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Add the global callback.
|
|
61
|
-
// @ts-expect-error - TS2740 - Type '() => void' is missing the following properties from type 'Window': clientInformation, closed, customElements, devicePixelRatio, and 206 more.
|
|
62
|
-
window[options.callbackName] = function (...args) {
|
|
63
|
-
cleanup();
|
|
64
|
-
options.success.apply(null, args);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Add the error handler.
|
|
68
|
-
script.addEventListener("error", function (...args) {
|
|
69
|
-
cleanup();
|
|
70
|
-
options.error.apply(null, args);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Insert the script to start the download.
|
|
74
|
-
document.head && document.head.appendChild(script);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// For offline exercises in the mobile app, we download the graphie data
|
|
78
|
-
// (svgs and localized data files) and serve them from the local file
|
|
79
|
-
// system (with file://). We replace urls that start with `web+graphie`
|
|
80
|
-
// in the perseus json with this `file+graphie` prefix to indicate that
|
|
81
|
-
// they should have the `file://` protocol instead of `https://`.
|
|
82
|
-
const svgLocalLabelsRegex = /^file\+graphie:/;
|
|
83
|
-
const hashRegex = /\/([^/]+)$/;
|
|
84
|
-
|
|
85
|
-
function isImageProbablyPhotograph(imageUrl) {
|
|
86
|
-
// TODO(david): Do an inventory to refine this heuristic. For example, what
|
|
87
|
-
// % of .png images are illustrations?
|
|
88
|
-
return /\.(jpg|jpeg)$/i.test(imageUrl);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function getLocale() {
|
|
92
|
-
const {JIPT, kaLocale} = getDependencies();
|
|
93
|
-
return JIPT.useJIPT ? "en-pt" : kaLocale;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function shouldUseLocalizedData() {
|
|
97
|
-
return getLocale() !== "en";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// A regex to split at the last / of a URL, separating the base part from the
|
|
101
|
-
// hash. This is used to create the localized label data URLs.
|
|
102
|
-
const splitHashRegex = /\/(?=[^/]+$)/;
|
|
103
|
-
|
|
104
|
-
function getLocalizedDataUrl(url: string) {
|
|
105
|
-
// For local (cached) graphie images, they are already localized.
|
|
106
|
-
if (svgLocalLabelsRegex.test(url)) {
|
|
107
|
-
return Util.getDataUrl(url);
|
|
108
|
-
}
|
|
109
|
-
const [base, hash] = Util.getBaseUrl(url).split(splitHashRegex);
|
|
110
|
-
return `${base}/${getLocale()}/${hash}-data.json`;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Get the hash from the url, which is just the filename
|
|
114
|
-
function getUrlHash(url: string) {
|
|
115
|
-
const match = url.match(hashRegex);
|
|
116
|
-
if (match == null) {
|
|
117
|
-
throw new PerseusError("not a valid URL", Errors.InvalidInput);
|
|
118
|
-
}
|
|
119
|
-
return match && match[1];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function defaultPreloader(dimensions: Dimensions) {
|
|
123
|
-
return (
|
|
124
|
-
<span
|
|
125
|
-
style={{
|
|
126
|
-
top: 0,
|
|
127
|
-
left: 0,
|
|
128
|
-
width: "100%",
|
|
129
|
-
height: "100%",
|
|
130
|
-
position: "absolute",
|
|
131
|
-
minWidth: "20px",
|
|
132
|
-
display: "flex",
|
|
133
|
-
justifyContent: "center",
|
|
134
|
-
alignContent: "center",
|
|
135
|
-
}}
|
|
136
|
-
>
|
|
137
|
-
<CircularSpinner size="medium" />
|
|
138
|
-
</span>
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
type Props = {
|
|
143
|
-
allowFullBleed?: boolean;
|
|
144
|
-
alt: string;
|
|
145
|
-
constrainHeight?: boolean;
|
|
146
|
-
extraGraphie?: {
|
|
147
|
-
box: Size;
|
|
148
|
-
range: [Coord, Coord];
|
|
149
|
-
labels: ReadonlyArray<any>;
|
|
150
|
-
};
|
|
151
|
-
height?: number;
|
|
152
|
-
// When the DOM updates to replace the preloader with the image, or
|
|
153
|
-
// vice-versa, we trigger this callback.
|
|
154
|
-
onUpdate: () => void;
|
|
155
|
-
// If alt is provided, DO NOT set aria-hidden=true unless this override flag
|
|
156
|
-
// is set.
|
|
157
|
-
overrideAriaHidden?: boolean;
|
|
158
|
-
preloader?: (dimensions: Dimensions) => React.ReactNode;
|
|
159
|
-
// By default, this component attempts to be responsive whenever
|
|
160
|
-
// possible (specifically, when width and height are passed in).
|
|
161
|
-
// You can expliclty force unresponsive behavior by *either*
|
|
162
|
-
// not passing in width/height *or* setting this prop to false.
|
|
163
|
-
// The difference is that forcing via this prop will result in
|
|
164
|
-
// explicit width and height styles being set on the rendered
|
|
165
|
-
// component.
|
|
166
|
-
responsive: boolean;
|
|
167
|
-
scale: number;
|
|
168
|
-
src: string;
|
|
169
|
-
title?: string;
|
|
170
|
-
trackInteraction?: () => void;
|
|
171
|
-
width?: number;
|
|
172
|
-
// Whether clicking this image will allow it to be fully zoomed in to
|
|
173
|
-
// its original size on click, and allow the user to scroll in that
|
|
174
|
-
// state. This also does some hacky viewport meta tag changing to
|
|
175
|
-
// ensure this works on mobile devices, so I (david@) don't recommend
|
|
176
|
-
// enabling this on desktop yet.
|
|
177
|
-
zoomToFullSizeOnMobile?: boolean;
|
|
178
|
-
// If provided, use AssetContext.Consumer, see renderer.jsx.
|
|
179
|
-
// If not, it defaults to a no-op.
|
|
180
|
-
setAssetStatus: (assetKey: string, loaded: boolean) => void;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
type DefaultProps = {
|
|
184
|
-
constrainHeight: NonNullable<Props["constrainHeight"]>;
|
|
185
|
-
onUpdate: NonNullable<Props["onUpdate"]>;
|
|
186
|
-
responsive: NonNullable<Props["responsive"]>;
|
|
187
|
-
scale: NonNullable<Props["scale"]>;
|
|
188
|
-
setAssetStatus: NonNullable<Props["setAssetStatus"]>;
|
|
189
|
-
src: NonNullable<Props["src"]>;
|
|
190
|
-
zoomToFullSizeOnMobile: NonNullable<Props["zoomToFullSizeOnMobile"]>;
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
type Label = {
|
|
194
|
-
coordinates: ReadonlyArray<any>;
|
|
195
|
-
content: string;
|
|
196
|
-
alignment: Alignment;
|
|
197
|
-
typesetAsMath: boolean;
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
type LabelsRenderedMap = {
|
|
201
|
-
[label: string]: boolean;
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
type State = {
|
|
205
|
-
// For labeled SVGs, when both dataLoaded and imageLoaded are true,
|
|
206
|
-
// indicates that loading has completed.
|
|
207
|
-
dataLoaded: boolean;
|
|
208
|
-
imageDimensions: [number, number] | null | undefined;
|
|
209
|
-
// Used to indicate when a non-labeled SVG image is finished loading
|
|
210
|
-
// and for labeled SVGs, indicates that it's safe to set up the graphie
|
|
211
|
-
// containing the labels.
|
|
212
|
-
imageLoaded: boolean;
|
|
213
|
-
// Some graphies have labels which are rendered after the main image is
|
|
214
|
-
// loaded. This object keeps track of whether those labels have been
|
|
215
|
-
// rendered.
|
|
216
|
-
labelsRendered: LabelsRenderedMap;
|
|
217
|
-
labelDataIsLocalized: boolean;
|
|
218
|
-
labels: ReadonlyArray<Label>;
|
|
219
|
-
range: [Coord, Coord];
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
class SvgImage extends React.Component<Props, State> {
|
|
223
|
-
_isMounted: boolean;
|
|
224
|
-
|
|
225
|
-
static defaultProps: DefaultProps = {
|
|
226
|
-
constrainHeight: false,
|
|
227
|
-
onUpdate: () => {},
|
|
228
|
-
responsive: true,
|
|
229
|
-
src: "",
|
|
230
|
-
scale: 1,
|
|
231
|
-
zoomToFullSizeOnMobile: false,
|
|
232
|
-
setAssetStatus: (src: string, status: boolean) => {},
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
constructor(props: Props) {
|
|
236
|
-
super(props);
|
|
237
|
-
props.setAssetStatus(props.src, false);
|
|
238
|
-
|
|
239
|
-
this._isMounted = false;
|
|
240
|
-
|
|
241
|
-
this.state = {
|
|
242
|
-
imageLoaded: false,
|
|
243
|
-
imageDimensions: null,
|
|
244
|
-
dataLoaded: false,
|
|
245
|
-
labelDataIsLocalized: false,
|
|
246
|
-
labels: [],
|
|
247
|
-
labelsRendered: {},
|
|
248
|
-
range: [[0, 0], [0, 0] as Coord],
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
componentDidMount() {
|
|
253
|
-
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
254
|
-
// this.isMounted() but is still considered an anti-pattern.
|
|
255
|
-
this._isMounted = true;
|
|
256
|
-
|
|
257
|
-
if (Util.isLabeledSVG(this.props.src)) {
|
|
258
|
-
this.loadResources();
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
|
263
|
-
if (this.props.src !== nextProps.src) {
|
|
264
|
-
this.setState({
|
|
265
|
-
imageLoaded: false,
|
|
266
|
-
dataLoaded: false,
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
|
|
272
|
-
// If the props changed, we definitely need to update
|
|
273
|
-
if (!_.isEqual(this.props, nextProps)) {
|
|
274
|
-
return true;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const wasLoaded = this.isLoadedInState(this.state);
|
|
278
|
-
const nextLoaded = this.isLoadedInState(nextState);
|
|
279
|
-
|
|
280
|
-
return wasLoaded !== nextLoaded;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
componentDidUpdate(prevProps: Props, prevState: State) {
|
|
284
|
-
const wasLoaded = this.isLoadedInState(prevState);
|
|
285
|
-
const isLoaded = this.isLoadedInState(this.state);
|
|
286
|
-
|
|
287
|
-
if (Util.isLabeledSVG(this.props.src) && !isLoaded) {
|
|
288
|
-
this.loadResources();
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (!wasLoaded && isLoaded) {
|
|
292
|
-
this.props.setAssetStatus(this.props.src, true);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
componentWillUnmount() {
|
|
297
|
-
this._isMounted = false;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Check if all of the resources are loaded in a given state
|
|
301
|
-
isLoadedInState(state: State): boolean {
|
|
302
|
-
return Util.isLabeledSVG(this.props.src)
|
|
303
|
-
? state.imageLoaded && state.dataLoaded
|
|
304
|
-
: state.imageLoaded;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
loadResources() {
|
|
308
|
-
const hash = getUrlHash(this.props.src);
|
|
309
|
-
|
|
310
|
-
// We can't make multiple jsonp calls to the same file because their
|
|
311
|
-
// callbacks will collide with each other. Instead, we cache the data
|
|
312
|
-
// and only make the jsonp calls once.
|
|
313
|
-
if (labelDataCache[hash]) {
|
|
314
|
-
if (labelDataCache[hash].loaded) {
|
|
315
|
-
const {data, localized} = labelDataCache[hash];
|
|
316
|
-
this.onDataLoaded(data, localized);
|
|
317
|
-
} else {
|
|
318
|
-
labelDataCache[hash].dataCallbacks.push(this.onDataLoaded);
|
|
319
|
-
}
|
|
320
|
-
} else {
|
|
321
|
-
const cacheData = {
|
|
322
|
-
loaded: false,
|
|
323
|
-
dataCallbacks: [this.onDataLoaded],
|
|
324
|
-
data: null,
|
|
325
|
-
localized: shouldUseLocalizedData(),
|
|
326
|
-
} as const;
|
|
327
|
-
|
|
328
|
-
labelDataCache[hash] = cacheData;
|
|
329
|
-
|
|
330
|
-
const retrieveData = (
|
|
331
|
-
url: string,
|
|
332
|
-
errorCallback: (x?: any, status?: any, error?: any) => void,
|
|
333
|
-
) => {
|
|
334
|
-
doJSONP(url, {
|
|
335
|
-
callbackName: "svgData" + hash,
|
|
336
|
-
success: (data) => {
|
|
337
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'data' because it is a read-only property.
|
|
338
|
-
cacheData.data = data;
|
|
339
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'loaded' because it is a read-only property.
|
|
340
|
-
cacheData.loaded = true;
|
|
341
|
-
|
|
342
|
-
_.each(cacheData.dataCallbacks, (callback) => {
|
|
343
|
-
// @ts-expect-error - TS2345 - Argument of type 'null' is not assignable to parameter of type '{ labels: readonly any[]; range: readonly any[]; }'.
|
|
344
|
-
callback(cacheData.data, cacheData.localized);
|
|
345
|
-
});
|
|
346
|
-
},
|
|
347
|
-
error: errorCallback,
|
|
348
|
-
});
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
if (shouldUseLocalizedData()) {
|
|
352
|
-
retrieveData(
|
|
353
|
-
getLocalizedDataUrl(this.props.src),
|
|
354
|
-
(x, status, error) => {
|
|
355
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'localized' because it is a read-only property.
|
|
356
|
-
cacheData.localized = false;
|
|
357
|
-
|
|
358
|
-
// If there is isn't any localized data, fall back to
|
|
359
|
-
// the original, unlocalized data
|
|
360
|
-
retrieveData(
|
|
361
|
-
Util.getDataUrl(this.props.src),
|
|
362
|
-
(x, status, error) => {
|
|
363
|
-
Log.error(
|
|
364
|
-
"Data load failed for svg-image",
|
|
365
|
-
Errors.Service,
|
|
366
|
-
{
|
|
367
|
-
cause: error,
|
|
368
|
-
loggedMetadata: {
|
|
369
|
-
dataUrl: Util.getDataUrl(
|
|
370
|
-
this.props.src,
|
|
371
|
-
),
|
|
372
|
-
status,
|
|
373
|
-
},
|
|
374
|
-
},
|
|
375
|
-
);
|
|
376
|
-
},
|
|
377
|
-
);
|
|
378
|
-
},
|
|
379
|
-
);
|
|
380
|
-
} else {
|
|
381
|
-
retrieveData(
|
|
382
|
-
Util.getDataUrl(this.props.src),
|
|
383
|
-
(x, status, error) => {
|
|
384
|
-
Log.error(
|
|
385
|
-
"Data load failed for svg-image",
|
|
386
|
-
Errors.Service,
|
|
387
|
-
{
|
|
388
|
-
cause: error,
|
|
389
|
-
loggedMetadata: {
|
|
390
|
-
dataUrl: Util.getDataUrl(this.props.src),
|
|
391
|
-
status,
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
);
|
|
395
|
-
},
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
onDataLoaded: (
|
|
402
|
-
data: {
|
|
403
|
-
labels: ReadonlyArray<any>;
|
|
404
|
-
range: ReadonlyArray<any>;
|
|
405
|
-
},
|
|
406
|
-
localized: boolean,
|
|
407
|
-
) => void = (
|
|
408
|
-
data: {
|
|
409
|
-
labels: ReadonlyArray<any>;
|
|
410
|
-
range: [Coord, Coord];
|
|
411
|
-
},
|
|
412
|
-
localized: boolean,
|
|
413
|
-
) => {
|
|
414
|
-
if (this._isMounted && data.labels && data.range) {
|
|
415
|
-
const labelsRendered: LabelsRenderedMap = data.labels.reduce<
|
|
416
|
-
Record<string, any>
|
|
417
|
-
>(
|
|
418
|
-
(dict: LabelsRenderedMap, label) => ({
|
|
419
|
-
...dict,
|
|
420
|
-
[label.content]: false,
|
|
421
|
-
}),
|
|
422
|
-
{},
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
this.setState({
|
|
426
|
-
dataLoaded: true,
|
|
427
|
-
labelDataIsLocalized: localized,
|
|
428
|
-
labelsRendered,
|
|
429
|
-
labels: data.labels,
|
|
430
|
-
range: data.range,
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
sizeProvided(): boolean {
|
|
436
|
-
return this.props.width != null && this.props.height != null;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
onImageLoad: () => void = () => {
|
|
440
|
-
// Only need to do this if rendering a Graphie
|
|
441
|
-
if (this.sizeProvided()) {
|
|
442
|
-
// If width and height are provided, we don't need to calculate the
|
|
443
|
-
// size ourselves
|
|
444
|
-
this.setState({
|
|
445
|
-
imageLoaded: true,
|
|
446
|
-
});
|
|
447
|
-
} else {
|
|
448
|
-
Util.getImageSize(this.props.src, (width, height) => {
|
|
449
|
-
if (this._isMounted) {
|
|
450
|
-
this.setState({
|
|
451
|
-
imageLoaded: true,
|
|
452
|
-
imageDimensions: [width, height],
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
setupGraphie: (graphie?: any, options?: any) => void = (
|
|
460
|
-
graphie: any,
|
|
461
|
-
options: any,
|
|
462
|
-
) => {
|
|
463
|
-
_.map(options.labels, (labelData) => {
|
|
464
|
-
const {JIPT} = getDependencies();
|
|
465
|
-
if (JIPT.useJIPT && this.state.labelDataIsLocalized) {
|
|
466
|
-
// If we're using JIPT translation and we got proper JIPT tags,
|
|
467
|
-
// render the labels as plain text (so JIPT can find them) and
|
|
468
|
-
// add some extra properties to the element so we can properly
|
|
469
|
-
// re-render the label once it is replaced with translated
|
|
470
|
-
// text.
|
|
471
|
-
const elem = graphie.label(
|
|
472
|
-
labelData.coordinates,
|
|
473
|
-
labelData.content,
|
|
474
|
-
labelData.alignment,
|
|
475
|
-
false,
|
|
476
|
-
);
|
|
477
|
-
|
|
478
|
-
getDependencies().svgImageJiptLabels.addLabel(
|
|
479
|
-
elem,
|
|
480
|
-
labelData.typesetAsMath,
|
|
481
|
-
);
|
|
482
|
-
} else if (labelData.coordinates) {
|
|
483
|
-
// Create labels from the data
|
|
484
|
-
// TODO(charlie): Some erroneous labels are being sent down
|
|
485
|
-
// without coordinates. They don't seem to have any content, so
|
|
486
|
-
// it seems fine to just ignore them (rather than error), but
|
|
487
|
-
// we should figure out why this is happening.
|
|
488
|
-
const label = graphie.label(
|
|
489
|
-
labelData.coordinates,
|
|
490
|
-
labelData.content,
|
|
491
|
-
labelData.alignment,
|
|
492
|
-
labelData.typesetAsMath,
|
|
493
|
-
{"font-size": 100 * this.props.scale + "%"},
|
|
494
|
-
);
|
|
495
|
-
|
|
496
|
-
// Convert absolute positioning css from pixels to percentages
|
|
497
|
-
// TODO(alex): Dynamically resize font-size as well. This
|
|
498
|
-
// almost certainly means listening to throttled window resize
|
|
499
|
-
// events.
|
|
500
|
-
const labelStyle = label[0].style;
|
|
501
|
-
let labelTop = this._tryGetPixels(labelStyle.top);
|
|
502
|
-
let labelLeft = this._tryGetPixels(labelStyle.left);
|
|
503
|
-
if (labelTop === null || labelLeft === null) {
|
|
504
|
-
// Graphie labels are supposed to have an explicit position,
|
|
505
|
-
// but to be on the safe side, let's fall back to using
|
|
506
|
-
// jQuery's position(). The reason we're not always using
|
|
507
|
-
// this is that in the presence of CSS transforms, it will
|
|
508
|
-
// give the rendered position, which may be scaled and
|
|
509
|
-
// not equal to the explicitly specified one.
|
|
510
|
-
const labelPosition = label.position();
|
|
511
|
-
labelTop = labelPosition.top;
|
|
512
|
-
labelLeft = labelPosition.left;
|
|
513
|
-
}
|
|
514
|
-
const svgHeight = (this.props.height || 0) * this.props.scale;
|
|
515
|
-
const svgWidth = (this.props.width || 0) * this.props.scale;
|
|
516
|
-
label.css({
|
|
517
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
518
|
-
top: (labelTop / svgHeight) * 100 + "%",
|
|
519
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
520
|
-
left: (labelLeft / svgWidth) * 100 + "%",
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
// Add back the styles to each of the labels
|
|
524
|
-
_.each(labelData.style, (styleValue, styleName) => {
|
|
525
|
-
label.css(styleName, styleValue);
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
this.setState({
|
|
529
|
-
labelsRendered: {
|
|
530
|
-
...this.state.labelsRendered,
|
|
531
|
-
[labelData.content]: true,
|
|
532
|
-
},
|
|
533
|
-
});
|
|
534
|
-
});
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
// Try to parse a CSS value as pixels. Returns null if the parameter string
|
|
538
|
-
// does not contain a number followed by "px".
|
|
539
|
-
_tryGetPixels(value: string): null | number {
|
|
540
|
-
value = value || "";
|
|
541
|
-
// While this doesn't check that there are no other alphabetical
|
|
542
|
-
// characters prior to "px", that should be taken care of by the DOM,
|
|
543
|
-
// which won't accept invalid units.
|
|
544
|
-
if (!value.endsWith("px")) {
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
// parseFloat() ignores trailing non-numerical characters.
|
|
548
|
-
return parseFloat(value) || null;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
_handleZoomClick: (e: React.SyntheticEvent) => void = (
|
|
552
|
-
e: React.SyntheticEvent,
|
|
553
|
-
) => {
|
|
554
|
-
const $image = $(e.target);
|
|
555
|
-
|
|
556
|
-
// It's possible that the image is already displayed at its
|
|
557
|
-
// full size, but we don't really know that until we get a chance
|
|
558
|
-
// to measure it (just now, after the user clicks). We only zoom
|
|
559
|
-
// if there's more image to be shown.
|
|
560
|
-
//
|
|
561
|
-
// TODO(kevindangoor) If the window is narrow and the image is
|
|
562
|
-
// already displayed as wide as possible, we may want to do
|
|
563
|
-
// nothing in that case as well. Figuring this out correctly
|
|
564
|
-
// likely required accounting for the image alignment and margins.
|
|
565
|
-
if (
|
|
566
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
567
|
-
$image.width() < this.props.width ||
|
|
568
|
-
this.props.zoomToFullSizeOnMobile
|
|
569
|
-
) {
|
|
570
|
-
Zoom.ZoomService.handleZoomClick(
|
|
571
|
-
e,
|
|
572
|
-
this.props.zoomToFullSizeOnMobile,
|
|
573
|
-
);
|
|
574
|
-
}
|
|
575
|
-
this.props.trackInteraction && this.props.trackInteraction();
|
|
576
|
-
};
|
|
577
|
-
|
|
578
|
-
handleUpdate: (status: string) => void = (status: string) => {
|
|
579
|
-
this.props.onUpdate();
|
|
580
|
-
// NOTE: Labeled SVG images use this.onImageLoad to set imageLoaded
|
|
581
|
-
// to true and we only use the imageLoaded state when isLabeledSVG
|
|
582
|
-
// is true setting imageLoaded true here shouldn't cause issues.
|
|
583
|
-
if (!Util.isLabeledSVG(this.props.src) && status === "loaded") {
|
|
584
|
-
this.setState({imageLoaded: true});
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
render():
|
|
589
|
-
| React.ReactElement<React.ComponentProps<"div">>
|
|
590
|
-
| React.ReactNode {
|
|
591
|
-
const imageSrc = this.props.src;
|
|
592
|
-
|
|
593
|
-
// Props to send to all images
|
|
594
|
-
const imageProps: ImageProps = {
|
|
595
|
-
alt: this.props.alt,
|
|
596
|
-
title: this.props.title,
|
|
597
|
-
tabIndex: "0",
|
|
598
|
-
};
|
|
599
|
-
// If alt text is present, AND overrideAriaHidden is true,
|
|
600
|
-
// include aria-hidden to avoid screen reader from crawling texts
|
|
601
|
-
// both from the div meant for screen readers (see svg-image.jsx)
|
|
602
|
-
// and alt attribute.
|
|
603
|
-
if (this.props.alt && this.props.overrideAriaHidden) {
|
|
604
|
-
imageProps["aria-hidden"] = true;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
const width = this.props.width && this.props.width * this.props.scale;
|
|
608
|
-
const height =
|
|
609
|
-
this.props.height && this.props.height * this.props.scale;
|
|
610
|
-
const dimensions = {width, height} as const;
|
|
611
|
-
|
|
612
|
-
// To make an image responsive, we need to know what its width and
|
|
613
|
-
// height are in advance (before inserting it into the DOM) so that we
|
|
614
|
-
// can ensure it doesn't grow past those limits. We don't always have
|
|
615
|
-
// this information, especially in places where <Renderer /> is used
|
|
616
|
-
// to render inline Markdown images within a widget. See Radio, Sorter,
|
|
617
|
-
// Matcher, etc.
|
|
618
|
-
// TODO(alex): Make all of those image rendering locations aware of
|
|
619
|
-
// width+height so that they too can render responsively.
|
|
620
|
-
const responsive = this.props.responsive && !!(width && height);
|
|
621
|
-
|
|
622
|
-
// An additional <Graphie /> may be inserted after the image/graphie
|
|
623
|
-
// pair. Only used by the image widget, for its legacy labels support.
|
|
624
|
-
// Note that since the image widget always provides width and height
|
|
625
|
-
// data, extraGraphie can be ignored for unresponsive images.
|
|
626
|
-
// TODO(alex): Convert all existing uses of that to web+graphie. This
|
|
627
|
-
// is tricky because web+graphie doesn't support labels on non-graphie
|
|
628
|
-
// images.
|
|
629
|
-
let extraGraphie;
|
|
630
|
-
if (this.props.extraGraphie && this.props.extraGraphie.labels.length) {
|
|
631
|
-
extraGraphie = (
|
|
632
|
-
<Graphie
|
|
633
|
-
box={this.props.extraGraphie.box}
|
|
634
|
-
range={this.props.extraGraphie.range}
|
|
635
|
-
options={{labels: this.props.extraGraphie.labels}}
|
|
636
|
-
responsive={true}
|
|
637
|
-
addMouseLayer={false}
|
|
638
|
-
setup={this.setupGraphie}
|
|
639
|
-
/>
|
|
640
|
-
);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// If preloader is undefined, we use the default. If it's
|
|
644
|
-
// null, there will be no preloader in use.
|
|
645
|
-
const preloaderBaseFunc =
|
|
646
|
-
this.props.preloader === undefined
|
|
647
|
-
? defaultPreloader
|
|
648
|
-
: this.props.preloader;
|
|
649
|
-
|
|
650
|
-
const preloader = preloaderBaseFunc
|
|
651
|
-
? () => preloaderBaseFunc(dimensions)
|
|
652
|
-
: null;
|
|
653
|
-
|
|
654
|
-
// Just use a normal image if a normal image is provided
|
|
655
|
-
if (!Util.isLabeledSVG(imageSrc)) {
|
|
656
|
-
if (responsive) {
|
|
657
|
-
const wrapperClasses = classNames({
|
|
658
|
-
zoomable: (width || 0) > ZOOMABLE_THRESHOLD,
|
|
659
|
-
"svg-image": true,
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
imageProps.onClick = this._handleZoomClick;
|
|
663
|
-
|
|
664
|
-
return (
|
|
665
|
-
<FixedToResponsive
|
|
666
|
-
className={wrapperClasses}
|
|
667
|
-
width={width}
|
|
668
|
-
height={height}
|
|
669
|
-
constrainHeight={this.props.constrainHeight}
|
|
670
|
-
allowFullBleed={
|
|
671
|
-
this.props.allowFullBleed &&
|
|
672
|
-
isImageProbablyPhotograph(imageSrc)
|
|
673
|
-
}
|
|
674
|
-
>
|
|
675
|
-
<ImageLoader
|
|
676
|
-
src={imageSrc}
|
|
677
|
-
imgProps={imageProps}
|
|
678
|
-
// @ts-expect-error - TS2322 - Type '(() => Element) | null' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>> | null | undefined'.
|
|
679
|
-
preloader={preloader}
|
|
680
|
-
onUpdate={this.handleUpdate}
|
|
681
|
-
/>
|
|
682
|
-
{extraGraphie}
|
|
683
|
-
</FixedToResponsive>
|
|
684
|
-
);
|
|
685
|
-
}
|
|
686
|
-
imageProps.style = dimensions;
|
|
687
|
-
return (
|
|
688
|
-
<ImageLoader
|
|
689
|
-
src={imageSrc}
|
|
690
|
-
// @ts-expect-error - TS2322 - Type '(() => Element) | null' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>> | null | undefined'.
|
|
691
|
-
preloader={preloader}
|
|
692
|
-
imgProps={imageProps}
|
|
693
|
-
onUpdate={this.handleUpdate}
|
|
694
|
-
/>
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
const imageUrl = Util.getSvgUrl(imageSrc);
|
|
699
|
-
|
|
700
|
-
let graphie;
|
|
701
|
-
// Since we only want to do the graphie setup once, we only render the
|
|
702
|
-
// graphie once everything is loaded
|
|
703
|
-
if (this.isLoadedInState(this.state)) {
|
|
704
|
-
// Use the provided width and height to size the graphie if
|
|
705
|
-
// possible, otherwise use our own calculated size
|
|
706
|
-
let box;
|
|
707
|
-
if (this.sizeProvided()) {
|
|
708
|
-
box = [width, height];
|
|
709
|
-
} else if (this.state.imageDimensions) {
|
|
710
|
-
box = [
|
|
711
|
-
this.state.imageDimensions[0] * this.props.scale,
|
|
712
|
-
this.state.imageDimensions[1] * this.props.scale,
|
|
713
|
-
];
|
|
714
|
-
} else {
|
|
715
|
-
throw new PerseusError(
|
|
716
|
-
"svg-image has no dimensions",
|
|
717
|
-
Errors.InvalidInput,
|
|
718
|
-
{
|
|
719
|
-
metadata: {src: this.props.src},
|
|
720
|
-
},
|
|
721
|
-
);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
// TODO: the "40" scale factor was introduced in D14974 but is not
|
|
725
|
-
// documented where it came from.
|
|
726
|
-
graphie = (
|
|
727
|
-
<Graphie
|
|
728
|
-
// eslint-disable-next-line react/no-string-refs
|
|
729
|
-
ref="graphie"
|
|
730
|
-
box={box}
|
|
731
|
-
scale={[40 * this.props.scale, 40 * this.props.scale]}
|
|
732
|
-
range={this.state.range}
|
|
733
|
-
options={_.pick(this.state, "labels")}
|
|
734
|
-
responsive={responsive}
|
|
735
|
-
addMouseLayer={false}
|
|
736
|
-
setup={this.setupGraphie}
|
|
737
|
-
/>
|
|
738
|
-
);
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
if (responsive) {
|
|
742
|
-
return (
|
|
743
|
-
<FixedToResponsive
|
|
744
|
-
className="svg-image"
|
|
745
|
-
width={width}
|
|
746
|
-
height={height}
|
|
747
|
-
constrainHeight={this.props.constrainHeight}
|
|
748
|
-
>
|
|
749
|
-
<ImageLoader
|
|
750
|
-
src={imageUrl}
|
|
751
|
-
onLoad={this.onImageLoad}
|
|
752
|
-
onUpdate={this.handleUpdate}
|
|
753
|
-
// @ts-expect-error - TS2322 - Type '(() => Element) | null' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>> | null | undefined'.
|
|
754
|
-
preloader={preloader}
|
|
755
|
-
imgProps={imageProps}
|
|
756
|
-
/>
|
|
757
|
-
{graphie}
|
|
758
|
-
{extraGraphie}
|
|
759
|
-
</FixedToResponsive>
|
|
760
|
-
);
|
|
761
|
-
}
|
|
762
|
-
imageProps.style = dimensions;
|
|
763
|
-
return (
|
|
764
|
-
<div className="unresponsive-svg-image" style={dimensions}>
|
|
765
|
-
<ImageLoader
|
|
766
|
-
src={imageUrl}
|
|
767
|
-
onLoad={this.onImageLoad}
|
|
768
|
-
onUpdate={this.handleUpdate}
|
|
769
|
-
// @ts-expect-error - TS2322 - Type '(() => Element) | null' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>> | null | undefined'.
|
|
770
|
-
preloader={preloader}
|
|
771
|
-
imgProps={imageProps}
|
|
772
|
-
/>
|
|
773
|
-
{graphie}
|
|
774
|
-
</div>
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
export default SvgImage;
|