@khanacademy/perseus 54.1.0 → 55.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/dist/components/number-input.d.ts +9 -10
- package/dist/components/range-input.d.ts +5 -6
- package/dist/components/simple-keypad-input.d.ts +5 -0
- package/dist/components/stub-tag-editor.d.ts +3 -4
- package/dist/components/text-list-editor.d.ts +3 -4
- package/dist/es/index.css +16 -6
- package/dist/es/index.js +902 -985
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +40 -10
- package/dist/es/strings.js.map +1 -1
- package/dist/index.css +16 -6
- package/dist/index.js +1036 -1086
- package/dist/index.js.map +1 -1
- package/dist/mixins/changeable.d.ts +1 -2
- package/dist/perseus-api.d.ts +1 -56
- package/dist/shared-utils/add-library-version-to-perseus-debug.d.ts +9 -0
- package/dist/strings.d.ts +34 -4
- package/dist/strings.js +142 -97
- package/dist/strings.js.map +1 -1
- package/dist/widget-ai-utils/numeric-input/prompt-utils.d.ts +1 -1
- package/dist/widgets/dropdown/dropdown.d.ts +5 -4
- package/dist/widgets/interactive-graph.d.ts +4 -2
- package/dist/widgets/interactive-graphs/graphs/circle.d.ts +7 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-line.d.ts +6 -0
- package/dist/widgets/interactive-graphs/graphs/quadratic.d.ts +8 -0
- package/dist/widgets/interactive-graphs/graphs/sinusoid.d.ts +7 -0
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +1 -1
- package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +6 -1
- package/dist/widgets/numeric-input/index.d.ts +1 -1
- package/dist/widgets/numeric-input/input-with-examples.d.ts +8 -0
- package/dist/widgets/numeric-input/numeric-input.class.d.ts +66 -0
- package/dist/widgets/numeric-input/numeric-input.d.ts +123 -77
- package/dist/widgets/numeric-input/utils.d.ts +25 -0
- package/package.json +120 -124
- package/dist/components/input-with-examples.d.ts +0 -51
- package/dist/components/tooltip.d.ts +0 -85
package/dist/es/index.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import _extends from '@babel/runtime/helpers/extends';
|
|
1
2
|
import * as KAS from '@khanacademy/kas';
|
|
2
3
|
import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
|
|
3
|
-
import { PerseusError, Errors, expressionLogic, getDecimalSeparator, approximateDeepEqual, pluck, CoreWidgetRegistry, getUpgradedWidgetOptions, mapObject, passageRefLogic, radioLogic, shuffle, random, GrapherUtil as GrapherUtil$1, lockedFigureColors, lockedFigureFillStyles, approximateEqual, shuffleMatcher, getMatrixSize, measurerLogic,
|
|
4
|
+
import { PerseusError, Errors, expressionLogic, getDecimalSeparator, approximateDeepEqual, pluck, CoreWidgetRegistry, getUpgradedWidgetOptions, mapObject, passageRefLogic, radioLogic, shuffle, random, GrapherUtil as GrapherUtil$1, lockedFigureColors, lockedFigureFillStyles, approximateEqual, shuffleMatcher, getMatrixSize, measurerLogic, upgradeWidgetInfoToLatestVersion, getWidgetIdsFromContentByType } from '@khanacademy/perseus-core';
|
|
4
5
|
export { isFailure, isSuccess, parseAndMigratePerseusArticle, parseAndMigratePerseusItem, parsePerseusItem } from '@khanacademy/perseus-core';
|
|
5
6
|
import * as PerseusLinter from '@khanacademy/perseus-linter';
|
|
6
7
|
import { linterContextDefault, Rule } from '@khanacademy/perseus-linter';
|
|
7
8
|
import { View, RenderStateRoot, Id, useOnMountEffect, useLatestRef, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
|
|
8
|
-
import Tooltip
|
|
9
|
+
import Tooltip, { TooltipContent } from '@khanacademy/wonder-blocks-tooltip';
|
|
9
10
|
import { HeadingMedium, LabelSmall, LabelLarge, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
10
11
|
import { StyleSheet, css } from 'aphrodite';
|
|
11
12
|
import classNames$1 from 'classnames';
|
|
12
13
|
import * as React from 'react';
|
|
13
|
-
import React__default, { useContext,
|
|
14
|
+
import React__default, { useContext, forwardRef, useId, useImperativeHandle, useRef, useState, useEffect, createContext, useLayoutEffect } from 'react';
|
|
14
15
|
import * as ReactDOM from 'react-dom';
|
|
15
16
|
import ReactDOM__default from 'react-dom';
|
|
16
17
|
import _, { debounce as debounce$1 } from 'underscore';
|
|
@@ -21,12 +22,16 @@ import $ from 'jquery';
|
|
|
21
22
|
import { v4 } from 'uuid';
|
|
22
23
|
import PropTypes from 'prop-types';
|
|
23
24
|
import { KhanAnswerTypes, emptyWidgetsFunctional, scoreWidgetsFunctional, flattenScores, inputNumberAnswerTypes, scoreRadio, scoreLabelImageMarker } from '@khanacademy/perseus-score';
|
|
25
|
+
import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/objectWithoutPropertiesLoose';
|
|
26
|
+
import { KeypadContext } from '@khanacademy/keypad-context';
|
|
27
|
+
import { TextField } from '@khanacademy/wonder-blocks-form';
|
|
24
28
|
import { entries, UnreachableCaseError, keys } from '@khanacademy/wonder-stuff-core';
|
|
25
29
|
import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
|
|
26
30
|
import { point, KhanMath, number, vector as vector$3, line, geometry, angles, coefficients } from '@khanacademy/kmath';
|
|
31
|
+
import _classPrivateFieldLooseBase from '@babel/runtime/helpers/classPrivateFieldLooseBase';
|
|
32
|
+
import _classPrivateFieldLooseKey from '@babel/runtime/helpers/classPrivateFieldLooseKey';
|
|
27
33
|
import SimpleMarkdown from '@khanacademy/simple-markdown';
|
|
28
34
|
import { pureMarkdownRules } from '@khanacademy/pure-markdown';
|
|
29
|
-
import { TextField } from '@khanacademy/wonder-blocks-form';
|
|
30
35
|
import Button from '@khanacademy/wonder-blocks-button';
|
|
31
36
|
import { Strut } from '@khanacademy/wonder-blocks-layout';
|
|
32
37
|
import Banner from '@khanacademy/wonder-blocks-banner';
|
|
@@ -44,16 +49,6 @@ import IconButton from '@khanacademy/wonder-blocks-icon-button';
|
|
|
44
49
|
import cornersOutIcon from '@phosphor-icons/core/regular/corners-out.svg';
|
|
45
50
|
import Link from '@khanacademy/wonder-blocks-link';
|
|
46
51
|
|
|
47
|
-
function _extends() {
|
|
48
|
-
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
49
|
-
for (var e = 1; e < arguments.length; e++) {
|
|
50
|
-
var t = arguments[e];
|
|
51
|
-
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
52
|
-
}
|
|
53
|
-
return n;
|
|
54
|
-
}, _extends.apply(null, arguments);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
52
|
/**
|
|
58
53
|
* The translated strings that are used to render Perseus.
|
|
59
54
|
*/
|
|
@@ -262,10 +257,14 @@ const mockStrings = {
|
|
|
262
257
|
centerX,
|
|
263
258
|
centerY
|
|
264
259
|
}) => `Circle. The center point is at ${centerX} comma ${centerY}.`,
|
|
265
|
-
|
|
260
|
+
srCircleRadiusPointRight: ({
|
|
266
261
|
radiusPointX,
|
|
267
262
|
radiusPointY
|
|
268
|
-
}) => `
|
|
263
|
+
}) => `Right radius endpoint at ${radiusPointX} comma ${radiusPointY}.`,
|
|
264
|
+
srCircleRadiusPointLeft: ({
|
|
265
|
+
radiusPointX,
|
|
266
|
+
radiusPointY
|
|
267
|
+
}) => `Left radius endpoint at ${radiusPointX} comma ${radiusPointY}.`,
|
|
269
268
|
srCircleRadius: ({
|
|
270
269
|
radius
|
|
271
270
|
}) => `Circle radius is ${radius}.`,
|
|
@@ -300,13 +299,13 @@ const mockStrings = {
|
|
|
300
299
|
xIntercept,
|
|
301
300
|
yIntercept
|
|
302
301
|
}) => `The line crosses the X-axis at ${xIntercept} comma 0 and the Y-axis at 0 comma ${yIntercept}.`,
|
|
303
|
-
srLinearGraphOriginIntercept: "The line crosses the
|
|
302
|
+
srLinearGraphOriginIntercept: "The line crosses the X and Y axes at the graph's origin.",
|
|
304
303
|
srLinearGrabHandle: ({
|
|
305
304
|
point1X,
|
|
306
305
|
point1Y,
|
|
307
306
|
point2X,
|
|
308
307
|
point2Y
|
|
309
|
-
}) => `Line
|
|
308
|
+
}) => `Line going through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y}.`,
|
|
310
309
|
srAngleStartingSide: ({
|
|
311
310
|
x,
|
|
312
311
|
y
|
|
@@ -393,6 +392,18 @@ const mockStrings = {
|
|
|
393
392
|
x,
|
|
394
393
|
y
|
|
395
394
|
}) => `Point ${pointSequence} on line ${lineNumber} at ${x} comma ${y}.`,
|
|
395
|
+
srLinearSystemGrabHandle: ({
|
|
396
|
+
lineNumber,
|
|
397
|
+
point1X,
|
|
398
|
+
point1Y,
|
|
399
|
+
point2X,
|
|
400
|
+
point2Y
|
|
401
|
+
}) => `Line ${lineNumber} going through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y}.`,
|
|
402
|
+
srLinearSystemIntersection: ({
|
|
403
|
+
x,
|
|
404
|
+
y
|
|
405
|
+
}) => `Line 1 and line 2 intersect at point ${x} comma ${y}.`,
|
|
406
|
+
srLinearSystemParallel: "Line 1 and line 2 are parallel.",
|
|
396
407
|
srRayGraph: "A ray on a coordinate plane.",
|
|
397
408
|
srRayPoints: ({
|
|
398
409
|
point1X,
|
|
@@ -485,10 +496,18 @@ const mockStrings = {
|
|
|
485
496
|
x,
|
|
486
497
|
y
|
|
487
498
|
}) => `Midline intersection at ${x} comma ${y}.`,
|
|
488
|
-
|
|
499
|
+
srSinusoidMaxPoint: ({
|
|
500
|
+
x,
|
|
501
|
+
y
|
|
502
|
+
}) => `Maximum point at ${x} comma ${y}.`,
|
|
503
|
+
srSinusoidMinPoint: ({
|
|
489
504
|
x,
|
|
490
505
|
y
|
|
491
|
-
}) => `
|
|
506
|
+
}) => `Minimum point at ${x} comma ${y}.`,
|
|
507
|
+
srSinusoidFlatPoint: ({
|
|
508
|
+
x,
|
|
509
|
+
y
|
|
510
|
+
}) => `Line through point at ${x} comma ${y}.`,
|
|
492
511
|
srSinusoidDescription: ({
|
|
493
512
|
minValue,
|
|
494
513
|
maxValue,
|
|
@@ -1021,11 +1040,11 @@ const useDependencies = () => {
|
|
|
1021
1040
|
};
|
|
1022
1041
|
|
|
1023
1042
|
var dependencies = /*#__PURE__*/Object.freeze({
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1043
|
+
__proto__: null,
|
|
1044
|
+
setDependencies: setDependencies,
|
|
1045
|
+
getDependencies: getDependencies,
|
|
1046
|
+
DependenciesContext: DependenciesContext,
|
|
1047
|
+
useDependencies: useDependencies
|
|
1029
1048
|
});
|
|
1030
1049
|
|
|
1031
1050
|
/**
|
|
@@ -1129,9 +1148,9 @@ const propTypes = {
|
|
|
1129
1148
|
};
|
|
1130
1149
|
|
|
1131
1150
|
var changeable = /*#__PURE__*/Object.freeze({
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1151
|
+
__proto__: null,
|
|
1152
|
+
change: change,
|
|
1153
|
+
propTypes: propTypes
|
|
1135
1154
|
});
|
|
1136
1155
|
|
|
1137
1156
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -1691,7 +1710,7 @@ class Expression extends React.Component {
|
|
|
1691
1710
|
}, /*#__PURE__*/React.createElement(View, {
|
|
1692
1711
|
style: a11y.srOnly,
|
|
1693
1712
|
role: "alert"
|
|
1694
|
-
}, this.state.showErrorTooltip && ERROR_TITLE + " " + ERROR_MESSAGE), /*#__PURE__*/React.createElement(Tooltip
|
|
1713
|
+
}, this.state.showErrorTooltip && ERROR_TITLE + " " + ERROR_MESSAGE), /*#__PURE__*/React.createElement(Tooltip, {
|
|
1695
1714
|
forceAnchorFocusivity: false,
|
|
1696
1715
|
opened: this.state.showErrorTooltip,
|
|
1697
1716
|
title: ERROR_TITLE,
|
|
@@ -1847,6 +1866,183 @@ var Expression$1 = {
|
|
|
1847
1866
|
}
|
|
1848
1867
|
};
|
|
1849
1868
|
|
|
1869
|
+
const _excluded$4 = ["keypadElement", "onFocus", "value"];
|
|
1870
|
+
class SimpleKeypadInput extends React.Component {
|
|
1871
|
+
constructor(...args) {
|
|
1872
|
+
super(...args);
|
|
1873
|
+
this._isMounted = false;
|
|
1874
|
+
this.inputRef = /*#__PURE__*/React.createRef();
|
|
1875
|
+
}
|
|
1876
|
+
componentDidMount() {
|
|
1877
|
+
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
1878
|
+
// this.isMounted() but is still considered an anti-pattern.
|
|
1879
|
+
this._isMounted = true;
|
|
1880
|
+
}
|
|
1881
|
+
componentWillUnmount() {
|
|
1882
|
+
this._isMounted = false;
|
|
1883
|
+
}
|
|
1884
|
+
focus() {
|
|
1885
|
+
var _this$inputRef$curren;
|
|
1886
|
+
// The inputRef is a ref to a MathInput, which
|
|
1887
|
+
// also controls the keypad state during focus events.
|
|
1888
|
+
(_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.focus(this.context.setKeypadActive);
|
|
1889
|
+
}
|
|
1890
|
+
blur() {
|
|
1891
|
+
var _this$inputRef$curren2;
|
|
1892
|
+
(_this$inputRef$curren2 = this.inputRef.current) == null || _this$inputRef$curren2.blur();
|
|
1893
|
+
}
|
|
1894
|
+
getValue() {
|
|
1895
|
+
return this.props.value;
|
|
1896
|
+
}
|
|
1897
|
+
render() {
|
|
1898
|
+
const _this = this;
|
|
1899
|
+
// Intercept the `onFocus` prop, as we need to configure the keypad
|
|
1900
|
+
// before continuing with the default focus logic for Perseus inputs.
|
|
1901
|
+
// Intercept the `value` prop so as to map `null` to the empty string,
|
|
1902
|
+
// as the `KeypadInput` does not support `null` values.
|
|
1903
|
+
const _this$props = _this.props,
|
|
1904
|
+
{
|
|
1905
|
+
keypadElement,
|
|
1906
|
+
onFocus,
|
|
1907
|
+
value
|
|
1908
|
+
} = _this$props,
|
|
1909
|
+
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$4);
|
|
1910
|
+
return (
|
|
1911
|
+
/*#__PURE__*/
|
|
1912
|
+
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
1913
|
+
React.createElement(KeypadInput, _extends({
|
|
1914
|
+
ref: this.inputRef,
|
|
1915
|
+
keypadElement: keypadElement,
|
|
1916
|
+
onFocus: () => {
|
|
1917
|
+
if (keypadElement) {
|
|
1918
|
+
keypadElement.configure({
|
|
1919
|
+
keypadType: KeypadType.FRACTION
|
|
1920
|
+
}, () => {
|
|
1921
|
+
if (_this._isMounted) {
|
|
1922
|
+
onFocus == null || onFocus();
|
|
1923
|
+
}
|
|
1924
|
+
});
|
|
1925
|
+
} else {
|
|
1926
|
+
onFocus == null || onFocus();
|
|
1927
|
+
}
|
|
1928
|
+
},
|
|
1929
|
+
value: value == null ? "" : "" + value
|
|
1930
|
+
}, rest))
|
|
1931
|
+
);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
// @ts-expect-error - TS2339 - Property 'propTypes' does not exist on type 'typeof SimpleKeypadInput'.
|
|
1936
|
+
SimpleKeypadInput.contextType = KeypadContext;
|
|
1937
|
+
SimpleKeypadInput.propTypes = {
|
|
1938
|
+
keypadElement: keypadElementPropType,
|
|
1939
|
+
onFocus: PropTypes.func,
|
|
1940
|
+
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
|
1941
|
+
};
|
|
1942
|
+
|
|
1943
|
+
const getPromptJSON$s = (renderProps, userInput) => {
|
|
1944
|
+
return {
|
|
1945
|
+
type: "input-number",
|
|
1946
|
+
options: {
|
|
1947
|
+
simplify: renderProps.simplify,
|
|
1948
|
+
answerType: renderProps.answerType
|
|
1949
|
+
},
|
|
1950
|
+
userInput: {
|
|
1951
|
+
value: userInput.currentValue
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1954
|
+
};
|
|
1955
|
+
|
|
1956
|
+
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
1957
|
+
let lastId = 0;
|
|
1958
|
+
function uniqueIdForInput(prefix = "input-") {
|
|
1959
|
+
lastId++;
|
|
1960
|
+
return `${prefix}${lastId}`;
|
|
1961
|
+
}
|
|
1962
|
+
class TextInput extends React.Component {
|
|
1963
|
+
constructor(props) {
|
|
1964
|
+
super(props);
|
|
1965
|
+
this.inputRef = /*#__PURE__*/React.createRef();
|
|
1966
|
+
this.id = void 0;
|
|
1967
|
+
this._getInput = () => {
|
|
1968
|
+
if (!this.inputRef.current) {
|
|
1969
|
+
throw new PerseusError("Input ref accessed before set", Errors.Internal);
|
|
1970
|
+
}
|
|
1971
|
+
return this.inputRef.current;
|
|
1972
|
+
};
|
|
1973
|
+
this.focus = () => {
|
|
1974
|
+
this._getInput().focus();
|
|
1975
|
+
};
|
|
1976
|
+
this.blur = () => {
|
|
1977
|
+
this._getInput().blur();
|
|
1978
|
+
};
|
|
1979
|
+
this.getValue = () => {
|
|
1980
|
+
var _this$inputRef$curren;
|
|
1981
|
+
return (_this$inputRef$curren = this.inputRef.current) == null ? void 0 : _this$inputRef$curren.value;
|
|
1982
|
+
};
|
|
1983
|
+
this.getStringValue = () => {
|
|
1984
|
+
var _this$inputRef$curren2;
|
|
1985
|
+
return (_this$inputRef$curren2 = this.inputRef.current) == null ? void 0 : _this$inputRef$curren2.value.toString();
|
|
1986
|
+
};
|
|
1987
|
+
this.setSelectionRange = (selectionStart, selectionEnd) => {
|
|
1988
|
+
this._getInput().setSelectionRange(selectionStart, selectionEnd);
|
|
1989
|
+
};
|
|
1990
|
+
this.getSelectionStart = () => {
|
|
1991
|
+
return this._getInput().selectionStart;
|
|
1992
|
+
};
|
|
1993
|
+
this.getSelectionEnd = () => {
|
|
1994
|
+
return this._getInput().selectionEnd;
|
|
1995
|
+
};
|
|
1996
|
+
if (props.id) {
|
|
1997
|
+
this.id = props.id;
|
|
1998
|
+
} else {
|
|
1999
|
+
this.id = uniqueIdForInput();
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
render() {
|
|
2003
|
+
const {
|
|
2004
|
+
labelText,
|
|
2005
|
+
value,
|
|
2006
|
+
onFocus,
|
|
2007
|
+
onBlur,
|
|
2008
|
+
disabled,
|
|
2009
|
+
placeholder,
|
|
2010
|
+
onKeyDown,
|
|
2011
|
+
style
|
|
2012
|
+
} = this.props;
|
|
2013
|
+
const formattedValue = value === null ? "" : value.toString();
|
|
2014
|
+
|
|
2015
|
+
// Some of our content was saved with empty strings as the label text,
|
|
2016
|
+
// and we don't want to render an empty aria-label attribute.
|
|
2017
|
+
const ariaLabel = labelText || undefined;
|
|
2018
|
+
return /*#__PURE__*/React.createElement(TextField, {
|
|
2019
|
+
ref: this.inputRef,
|
|
2020
|
+
style: style,
|
|
2021
|
+
disabled: disabled,
|
|
2022
|
+
id: this.id,
|
|
2023
|
+
value: formattedValue,
|
|
2024
|
+
type: "text",
|
|
2025
|
+
"aria-label": ariaLabel,
|
|
2026
|
+
"aria-describedby": this.props["aria-describedby"],
|
|
2027
|
+
onChange: value => this.props.onChange(value),
|
|
2028
|
+
placeholder: placeholder,
|
|
2029
|
+
testId: "input-with-examples",
|
|
2030
|
+
onFocus: onFocus,
|
|
2031
|
+
onBlur: onBlur,
|
|
2032
|
+
onKeyDown: onKeyDown
|
|
2033
|
+
// @ts-expect-error - TS2322 - Type '{ style: StyleType; disabled: boolean | undefined; id: string; value: string; type: "text"; "aria-label": string | undefined; onChange: (value: string) => void; placeholder: string | undefined; ... 5 more ...; autoComplete: string; }' is not assignable to type 'IntrinsicAttributes & ExportProps & RefAttributes<HTMLInputElement>'.
|
|
2034
|
+
,
|
|
2035
|
+
autoCorrect: "off",
|
|
2036
|
+
autoCapitalize: "off",
|
|
2037
|
+
autoComplete: "off"
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
TextInput.defaultProps = {
|
|
2042
|
+
value: "",
|
|
2043
|
+
disabled: false
|
|
2044
|
+
};
|
|
2045
|
+
|
|
1850
2046
|
/**
|
|
1851
2047
|
* A React context for keeping track of whether an component is fully
|
|
1852
2048
|
* loaded. This is used to know when SvgImage and (Zoomable)TeX are finished
|
|
@@ -2411,9 +2607,11 @@ const Util = {
|
|
|
2411
2607
|
// TODO(LP-11481): Change the primary API we use for logging in Perseus to use
|
|
2412
2608
|
// getDependencies() internally... simplify usage to just using a "singleton"
|
|
2413
2609
|
// which get's the injected logger.
|
|
2610
|
+
|
|
2414
2611
|
/**
|
|
2415
2612
|
* Provides logging infrastructure for Perseus
|
|
2416
2613
|
*/
|
|
2614
|
+
|
|
2417
2615
|
const Log = {
|
|
2418
2616
|
// Logs a message
|
|
2419
2617
|
log: (message, extra) => {
|
|
@@ -3087,40 +3285,40 @@ const articleMaxWidthInPx = 688;
|
|
|
3087
3285
|
const articleMaxWidthTableInPx = 512;
|
|
3088
3286
|
|
|
3089
3287
|
var constants = /*#__PURE__*/Object.freeze({
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3288
|
+
__proto__: null,
|
|
3289
|
+
baseFontFamily: baseFontFamily,
|
|
3290
|
+
boldFontFamily: boldFontFamily,
|
|
3291
|
+
kaGreen: kaGreen,
|
|
3292
|
+
white: white,
|
|
3293
|
+
gray97: gray97,
|
|
3294
|
+
gray85: gray85,
|
|
3295
|
+
gray76: gray76,
|
|
3296
|
+
gray68: gray68,
|
|
3297
|
+
gray17: gray17,
|
|
3298
|
+
pureSmMin: pureSmMin$1,
|
|
3299
|
+
pureMdMin: pureMdMin$1,
|
|
3300
|
+
pureLgMin: pureLgMin$1,
|
|
3301
|
+
pureXlMin: pureXlMin$1,
|
|
3302
|
+
pureXsMax: pureXsMax$1,
|
|
3303
|
+
pureSmMax: pureSmMax$1,
|
|
3304
|
+
pureMdMax: pureMdMax$1,
|
|
3305
|
+
pureLgMax: pureLgMax$1,
|
|
3306
|
+
tableBackgroundAccent: tableBackgroundAccent,
|
|
3307
|
+
phoneMargin: phoneMargin,
|
|
3308
|
+
negativePhoneMargin: negativePhoneMargin,
|
|
3309
|
+
hintBorderWidth: hintBorderWidth,
|
|
3310
|
+
baseUnitPx: baseUnitPx,
|
|
3311
|
+
interactiveSizes: interactiveSizes$1,
|
|
3312
|
+
circleSize: circleSize$1,
|
|
3313
|
+
radioMarginWidth: radioMarginWidth$1,
|
|
3314
|
+
warningColor: warningColor,
|
|
3315
|
+
warningColorHover: warningColorHover,
|
|
3316
|
+
warningColorActive: warningColorActive,
|
|
3317
|
+
publishBlockingErrorColor: publishBlockingErrorColor,
|
|
3318
|
+
radioBorderColor: radioBorderColor$1,
|
|
3319
|
+
checkedColor: checkedColor$1,
|
|
3320
|
+
articleMaxWidthInPx: articleMaxWidthInPx,
|
|
3321
|
+
articleMaxWidthTableInPx: articleMaxWidthTableInPx
|
|
3124
3322
|
});
|
|
3125
3323
|
|
|
3126
3324
|
/**
|
|
@@ -3470,16 +3668,6 @@ const InteractiveUtil = {
|
|
|
3470
3668
|
}
|
|
3471
3669
|
};
|
|
3472
3670
|
|
|
3473
|
-
function _classPrivateFieldBase(e, t) {
|
|
3474
|
-
if (!{}.hasOwnProperty.call(e, t)) throw new TypeError("attempted to use private field on non-instance");
|
|
3475
|
-
return e;
|
|
3476
|
-
}
|
|
3477
|
-
|
|
3478
|
-
var id = 0;
|
|
3479
|
-
function _classPrivateFieldKey(e) {
|
|
3480
|
-
return "__private_" + id++ + "_" + e;
|
|
3481
|
-
}
|
|
3482
|
-
|
|
3483
3671
|
/*!
|
|
3484
3672
|
* Raphael 1.5.2 - JavaScript Vector Library
|
|
3485
3673
|
*
|
|
@@ -3536,7 +3724,7 @@ var Raphael$1 = (function () {
|
|
|
3536
3724
|
appendChild = "appendChild",
|
|
3537
3725
|
apply = "apply",
|
|
3538
3726
|
concat = "concat",
|
|
3539
|
-
supportsTouch =
|
|
3727
|
+
supportsTouch = "createTouch" in doc,
|
|
3540
3728
|
E = "",
|
|
3541
3729
|
S = " ",
|
|
3542
3730
|
Str = String,
|
|
@@ -5141,10 +5329,10 @@ var Raphael$1 = (function () {
|
|
|
5141
5329
|
});
|
|
5142
5330
|
break;
|
|
5143
5331
|
case "gradient":
|
|
5144
|
-
(
|
|
5332
|
+
({
|
|
5145
5333
|
circle: 1,
|
|
5146
5334
|
ellipse: 1
|
|
5147
|
-
}
|
|
5335
|
+
}[has](o.type) || Str(value).charAt() != "r") && _addGradientFill(node, value, o.paper);
|
|
5148
5336
|
break;
|
|
5149
5337
|
case "opacity":
|
|
5150
5338
|
if (attrs.gradient && !attrs[has]("stroke-opacity")) {
|
|
@@ -7971,8 +8159,8 @@ function polar$2(r, th) {
|
|
|
7971
8159
|
th = th * Math.PI / 180;
|
|
7972
8160
|
return [r[0] * Math.cos(th), r[1] * Math.sin(th)];
|
|
7973
8161
|
}
|
|
7974
|
-
var _bounds = /*#__PURE__*/
|
|
7975
|
-
var _drawingTransform = /*#__PURE__*/
|
|
8162
|
+
var _bounds = /*#__PURE__*/_classPrivateFieldLooseKey("bounds");
|
|
8163
|
+
var _drawingTransform = /*#__PURE__*/_classPrivateFieldLooseKey("drawingTransform");
|
|
7976
8164
|
class Graphie$1 {
|
|
7977
8165
|
constructor(el) {
|
|
7978
8166
|
this.el = void 0;
|
|
@@ -8177,8 +8365,8 @@ class Graphie$1 {
|
|
|
8177
8365
|
if (options.range == null) {
|
|
8178
8366
|
throw new PerseusError("range should be specified in graph init", Errors.Internal);
|
|
8179
8367
|
}
|
|
8180
|
-
|
|
8181
|
-
|
|
8368
|
+
_classPrivateFieldLooseBase(this, _bounds)[_bounds] = new GraphBounds(...options.range);
|
|
8369
|
+
_classPrivateFieldLooseBase(this, _drawingTransform)[_drawingTransform] = new DrawingTransform(this.raphael, scale, this.bounds());
|
|
8182
8370
|
const [w, h] = this.drawingTransform().canvasDimensions();
|
|
8183
8371
|
$(this.el).css({
|
|
8184
8372
|
width: w,
|
|
@@ -8413,16 +8601,16 @@ class Graphie$1 {
|
|
|
8413
8601
|
}
|
|
8414
8602
|
}
|
|
8415
8603
|
drawingTransform() {
|
|
8416
|
-
if (
|
|
8604
|
+
if (_classPrivateFieldLooseBase(this, _drawingTransform)[_drawingTransform] == null) {
|
|
8417
8605
|
throw new Error("Can't get drawingTransform of an uninitialized Graphie");
|
|
8418
8606
|
}
|
|
8419
|
-
return
|
|
8607
|
+
return _classPrivateFieldLooseBase(this, _drawingTransform)[_drawingTransform];
|
|
8420
8608
|
}
|
|
8421
8609
|
bounds() {
|
|
8422
|
-
if (
|
|
8610
|
+
if (_classPrivateFieldLooseBase(this, _bounds)[_bounds] == null) {
|
|
8423
8611
|
throw new Error("Can't get bounds of an uninitialized Graphie");
|
|
8424
8612
|
}
|
|
8425
|
-
return
|
|
8613
|
+
return _classPrivateFieldLooseBase(this, _bounds)[_bounds];
|
|
8426
8614
|
}
|
|
8427
8615
|
style(attrs, fn) {
|
|
8428
8616
|
const processed = this.processAttributes(attrs);
|
|
@@ -8973,8 +9161,10 @@ const labelDirections = {
|
|
|
8973
9161
|
// is equivalent to [[-5, 5], [-10, 10]].
|
|
8974
9162
|
// - A single magnitude to be used for both x and y. E.g. 10 is equivalent to
|
|
8975
9163
|
// [[-10, 10], [-10, 10]]
|
|
9164
|
+
|
|
8976
9165
|
// Overload normalizeRange to specify that it only returns undefined if its
|
|
8977
9166
|
// argument is undefined.
|
|
9167
|
+
|
|
8978
9168
|
// normalizeRange converts a RangeSpecifier into a pair of Intervals that give
|
|
8979
9169
|
// the x and y ranges, respectively
|
|
8980
9170
|
function normalizeRange(range) {
|
|
@@ -9167,7 +9357,7 @@ const GraphUtils = {
|
|
|
9167
9357
|
clickBlockList = [],
|
|
9168
9358
|
blockMouseTriggers = false,
|
|
9169
9359
|
blockTouchTriggers = false,
|
|
9170
|
-
eventCaptureSupported =
|
|
9360
|
+
eventCaptureSupported = "addEventListener" in document,
|
|
9171
9361
|
$document = $(document),
|
|
9172
9362
|
nextTouchID = 1,
|
|
9173
9363
|
lastTouchID = 0;
|
|
@@ -9862,7 +10052,6 @@ function scaledPolarRad(radius, radians) {
|
|
|
9862
10052
|
return [radius * Math.cos(radians), radius * Math.sin(radians) * -1 // SVG flips y axis
|
|
9863
10053
|
];
|
|
9864
10054
|
}
|
|
9865
|
-
|
|
9866
10055
|
function scaledPolarDeg(radius, degrees) {
|
|
9867
10056
|
const radians = degrees * Math.PI / 180;
|
|
9868
10057
|
return scaledPolarRad(radius, radians);
|
|
@@ -11680,7 +11869,6 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
11680
11869
|
if (hideArrow) {
|
|
11681
11870
|
return; // Don't draw anything!
|
|
11682
11871
|
}
|
|
11683
|
-
|
|
11684
11872
|
const handleVec = vector$3.subtract(handleCoord, rotatePoint.coord);
|
|
11685
11873
|
const handlePolar = vector$3.polarDegFromCart(handleVec);
|
|
11686
11874
|
const angle = handlePolar[1];
|
|
@@ -12644,7 +12832,6 @@ _.extend(MovableAngle.prototype, {
|
|
|
12644
12832
|
// clockwise to the first point
|
|
12645
12833
|
coords[1] // the vertex parameter is last
|
|
12646
12834
|
);
|
|
12647
|
-
|
|
12648
12835
|
const clockwiseAngle = rawAngle + 360 % 360;
|
|
12649
12836
|
return clockwiseAngle;
|
|
12650
12837
|
},
|
|
@@ -14018,7 +14205,7 @@ class MovablePoint$5 {
|
|
|
14018
14205
|
const showTrashTooltip = () => {
|
|
14019
14206
|
this._showTooltip(container => {
|
|
14020
14207
|
// eslint-disable-next-line import/no-deprecated
|
|
14021
|
-
render(
|
|
14208
|
+
render(/*#__PURE__*/React.createElement("span", {
|
|
14022
14209
|
style: {
|
|
14023
14210
|
fontSize: "2em"
|
|
14024
14211
|
}
|
|
@@ -14817,7 +15004,6 @@ const DEFAULT_PROPS = {
|
|
|
14817
15004
|
// likewise
|
|
14818
15005
|
labelStyle: null // likewise
|
|
14819
15006
|
};
|
|
14820
|
-
|
|
14821
15007
|
const DEFAULT_STATE = {
|
|
14822
15008
|
added: false,
|
|
14823
15009
|
hasMoved: false,
|
|
@@ -17468,7 +17654,7 @@ class TranslationLinter {
|
|
|
17468
17654
|
* Preprocess TeX code to convert things that MathJax doesn't know how to handle
|
|
17469
17655
|
* to things is does.
|
|
17470
17656
|
*/
|
|
17471
|
-
var preprocessTex =
|
|
17657
|
+
var preprocessTex = texCode => texCode
|
|
17472
17658
|
// Replace uses of \begin{align}...\end{align} which KaTeX doesn't
|
|
17473
17659
|
// support (yet) with \begin{aligned}...\end{aligned} which renders
|
|
17474
17660
|
// the same is supported by KaTeX. It does the same for align*.
|
|
@@ -17478,7 +17664,7 @@ var preprocessTex = (texCode => texCode
|
|
|
17478
17664
|
// "aligned").
|
|
17479
17665
|
.replace(/\{align[*]?\}/g, "{aligned}")
|
|
17480
17666
|
// Replace non-breaking spaces with regular spaces.
|
|
17481
|
-
.replace(/[\u00a0]/g, " ")
|
|
17667
|
+
.replace(/[\u00a0]/g, " ");
|
|
17482
17668
|
|
|
17483
17669
|
const {
|
|
17484
17670
|
interactiveSizes
|
|
@@ -17740,29 +17926,29 @@ const isLintable = type => {
|
|
|
17740
17926
|
};
|
|
17741
17927
|
|
|
17742
17928
|
var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
17743
|
-
|
|
17744
|
-
|
|
17745
|
-
|
|
17746
|
-
|
|
17747
|
-
|
|
17748
|
-
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
|
|
17754
|
-
|
|
17755
|
-
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
|
|
17759
|
-
|
|
17760
|
-
|
|
17761
|
-
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17929
|
+
__proto__: null,
|
|
17930
|
+
registerWidget: registerWidget,
|
|
17931
|
+
registerWidgets: registerWidgets,
|
|
17932
|
+
replaceWidget: replaceWidget,
|
|
17933
|
+
replaceDeprecatedWidgets: replaceDeprecatedWidgets,
|
|
17934
|
+
registerEditors: registerEditors,
|
|
17935
|
+
replaceEditor: replaceEditor,
|
|
17936
|
+
replaceDeprecatedEditors: replaceDeprecatedEditors,
|
|
17937
|
+
getWidget: getWidget,
|
|
17938
|
+
getWidgetExport: getWidgetExport,
|
|
17939
|
+
getEditor: getEditor,
|
|
17940
|
+
getTransform: getTransform,
|
|
17941
|
+
getVersion: getVersion,
|
|
17942
|
+
getVersionVector: getVersionVector,
|
|
17943
|
+
getPublicWidgets: getPublicWidgets,
|
|
17944
|
+
isAccessible: isAccessible,
|
|
17945
|
+
getAllWidgetTypes: getAllWidgetTypes,
|
|
17946
|
+
getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
|
|
17947
|
+
traverseChildWidgets: traverseChildWidgets$3,
|
|
17948
|
+
supportsStaticMode: supportsStaticMode,
|
|
17949
|
+
getStaticTransform: getStaticTransform,
|
|
17950
|
+
getTracking: getTracking,
|
|
17951
|
+
isLintable: isLintable
|
|
17766
17952
|
});
|
|
17767
17953
|
|
|
17768
17954
|
class WidgetContainer extends React.Component {
|
|
@@ -17805,7 +17991,6 @@ class WidgetContainer extends React.Component {
|
|
|
17805
17991
|
/* eslint-enable react/no-did-mount-set-state */
|
|
17806
17992
|
}
|
|
17807
17993
|
}
|
|
17808
|
-
|
|
17809
17994
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
17810
17995
|
if (this.props.type !== nextProps.type) {
|
|
17811
17996
|
throw new Error("WidgetContainer can't change widget type; set a different " + "key instead to recreate the container.");
|
|
@@ -19259,620 +19444,123 @@ const styles$A = {
|
|
|
19259
19444
|
}
|
|
19260
19445
|
};
|
|
19261
19446
|
|
|
19262
|
-
|
|
19263
|
-
|
|
19264
|
-
|
|
19265
|
-
|
|
19266
|
-
|
|
19267
|
-
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
19275
|
-
|
|
19447
|
+
const {
|
|
19448
|
+
captureScratchpadTouchStart: captureScratchpadTouchStart$2
|
|
19449
|
+
} = Util;
|
|
19450
|
+
// [LEMS-2411](Jan 2025) Third: This component has been moved to the NumericInput
|
|
19451
|
+
// folder as we are actively working towards removing the InputNumber widget.
|
|
19452
|
+
// This comment can be removed as part of LEMS-2411.
|
|
19453
|
+
|
|
19454
|
+
/**
|
|
19455
|
+
* The InputWithExamples component is a child component of the NumericInput
|
|
19456
|
+
* and InputNumber components. It is responsible for rendering the UI elements
|
|
19457
|
+
* for the desktop versions of these widgets, and displays a tooltip with
|
|
19458
|
+
* examples of how to input the selected answer forms.
|
|
19459
|
+
*/
|
|
19460
|
+
const InputWithExamples = /*#__PURE__*/forwardRef((props, ref) => {
|
|
19461
|
+
// Desctructure the props to set default values
|
|
19462
|
+
const {
|
|
19463
|
+
shouldShowExamples = true,
|
|
19464
|
+
onFocus = () => {},
|
|
19465
|
+
onBlur = () => {},
|
|
19466
|
+
disabled = false,
|
|
19467
|
+
linterContext = PerseusLinter.linterContextDefault,
|
|
19468
|
+
className = ""
|
|
19469
|
+
} = props;
|
|
19470
|
+
const context = React.useContext(PerseusI18nContext);
|
|
19471
|
+
const inputRef = React.useRef(null);
|
|
19472
|
+
const [inputFocused, setInputFocused] = React.useState(false);
|
|
19473
|
+
const id = useId();
|
|
19474
|
+
const ariaId = `aria-for-${id}`;
|
|
19475
|
+
useImperativeHandle(ref, () => ({
|
|
19476
|
+
current: inputRef.current,
|
|
19477
|
+
focus: () => {
|
|
19478
|
+
if (inputRef.current) {
|
|
19479
|
+
inputRef.current.focus();
|
|
19480
|
+
}
|
|
19481
|
+
},
|
|
19482
|
+
blur: () => {
|
|
19483
|
+
if (inputRef.current) {
|
|
19484
|
+
inputRef.current.blur();
|
|
19276
19485
|
}
|
|
19277
|
-
return this.inputRef.current;
|
|
19278
|
-
};
|
|
19279
|
-
this.focus = () => {
|
|
19280
|
-
this._getInput().focus();
|
|
19281
|
-
};
|
|
19282
|
-
this.blur = () => {
|
|
19283
|
-
this._getInput().blur();
|
|
19284
|
-
};
|
|
19285
|
-
this.getValue = () => {
|
|
19286
|
-
var _this$inputRef$curren;
|
|
19287
|
-
return (_this$inputRef$curren = this.inputRef.current) == null ? void 0 : _this$inputRef$curren.value;
|
|
19288
|
-
};
|
|
19289
|
-
this.getStringValue = () => {
|
|
19290
|
-
var _this$inputRef$curren2;
|
|
19291
|
-
return (_this$inputRef$curren2 = this.inputRef.current) == null ? void 0 : _this$inputRef$curren2.value.toString();
|
|
19292
|
-
};
|
|
19293
|
-
this.setSelectionRange = (selectionStart, selectionEnd) => {
|
|
19294
|
-
this._getInput().setSelectionRange(selectionStart, selectionEnd);
|
|
19295
|
-
};
|
|
19296
|
-
this.getSelectionStart = () => {
|
|
19297
|
-
return this._getInput().selectionStart;
|
|
19298
|
-
};
|
|
19299
|
-
this.getSelectionEnd = () => {
|
|
19300
|
-
return this._getInput().selectionEnd;
|
|
19301
|
-
};
|
|
19302
|
-
if (props.id) {
|
|
19303
|
-
this.id = props.id;
|
|
19304
|
-
} else {
|
|
19305
|
-
this.id = uniqueIdForInput();
|
|
19306
19486
|
}
|
|
19307
|
-
}
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
|
|
19322
|
-
|
|
19487
|
+
}));
|
|
19488
|
+
const getInputClassName = () => {
|
|
19489
|
+
let inputClassName = ClassNames.INPUT;
|
|
19490
|
+
if (inputFocused) {
|
|
19491
|
+
inputClassName += " " + ClassNames.FOCUSED;
|
|
19492
|
+
}
|
|
19493
|
+
if (className) {
|
|
19494
|
+
inputClassName += " " + className;
|
|
19495
|
+
}
|
|
19496
|
+
return inputClassName;
|
|
19497
|
+
};
|
|
19498
|
+
const handleFocus = () => {
|
|
19499
|
+
onFocus();
|
|
19500
|
+
setInputFocused(true);
|
|
19501
|
+
};
|
|
19502
|
+
const handleBlur = () => {
|
|
19503
|
+
onBlur();
|
|
19504
|
+
setInputFocused(false);
|
|
19505
|
+
};
|
|
19506
|
+
const renderInput = () => {
|
|
19507
|
+
// Generate the provided examples in simple language for screen readers.
|
|
19508
|
+
const examplesAria = props.shouldShowExamples ? `${props.examples[0]}
|
|
19509
|
+
${props.examples.slice(1).join(", or\n")}`
|
|
19510
|
+
// @ts-expect-error TS2550: Property replaceAll does not exist on type string.
|
|
19511
|
+
.replaceAll("*", "").replaceAll("$", "").replaceAll("\\ \\text{pi}", " pi").replaceAll("\\ ", " and ") : "";
|
|
19512
|
+
const inputProps = {
|
|
19513
|
+
id: id,
|
|
19514
|
+
// If we have examples, we want to provide the aria-describedby attribute
|
|
19515
|
+
"aria-describedby": props.shouldShowExamples ? ariaId : undefined,
|
|
19516
|
+
ref: inputRef,
|
|
19517
|
+
className: getInputClassName(),
|
|
19518
|
+
labelText: props.labelText,
|
|
19519
|
+
value: props.value,
|
|
19520
|
+
onFocus: handleFocus,
|
|
19521
|
+
onBlur: handleBlur,
|
|
19323
19522
|
disabled: disabled,
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
"aria-label": labelText,
|
|
19328
|
-
"aria-describedby": this.props["aria-describedby"],
|
|
19329
|
-
onChange: value => this.props.onChange(value),
|
|
19330
|
-
placeholder: placeholder,
|
|
19331
|
-
onFocus: onFocus,
|
|
19332
|
-
onBlur: onBlur,
|
|
19333
|
-
onKeyDown: onKeyDown
|
|
19334
|
-
// @ts-expect-error - TS2322 - Type '{ style: StyleType; disabled: boolean | undefined; id: string; value: string; type: "text"; "aria-label": string | undefined; onChange: (value: string) => void; placeholder: string | undefined; ... 5 more ...; autoComplete: string; }' is not assignable to type 'IntrinsicAttributes & ExportProps & RefAttributes<HTMLInputElement>'.
|
|
19335
|
-
,
|
|
19336
|
-
autoCorrect: "off",
|
|
19523
|
+
style: props.style,
|
|
19524
|
+
onChange: props.onChange,
|
|
19525
|
+
onTouchStart: captureScratchpadTouchStart$2,
|
|
19337
19526
|
autoCapitalize: "off",
|
|
19338
|
-
autoComplete: "off"
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
}
|
|
19342
|
-
TextInput.defaultProps = {
|
|
19343
|
-
value: "",
|
|
19344
|
-
disabled: false
|
|
19345
|
-
};
|
|
19346
|
-
|
|
19347
|
-
function _objectWithoutPropertiesLoose(r, e) {
|
|
19348
|
-
if (null == r) return {};
|
|
19349
|
-
var t = {};
|
|
19350
|
-
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
|
|
19351
|
-
if (-1 !== e.indexOf(n)) continue;
|
|
19352
|
-
t[n] = r[n];
|
|
19353
|
-
}
|
|
19354
|
-
return t;
|
|
19355
|
-
}
|
|
19356
|
-
|
|
19357
|
-
const _excluded$4 = ["position", "visibility", "left", "top"];
|
|
19358
|
-
const zIndex = 10;
|
|
19359
|
-
let HorizontalDirection = /*#__PURE__*/function (HorizontalDirection) {
|
|
19360
|
-
HorizontalDirection["Left"] = "left";
|
|
19361
|
-
HorizontalDirection["Right"] = "right";
|
|
19362
|
-
return HorizontalDirection;
|
|
19363
|
-
}({});
|
|
19364
|
-
let VerticalDirection = /*#__PURE__*/function (VerticalDirection) {
|
|
19365
|
-
VerticalDirection["Top"] = "top";
|
|
19366
|
-
VerticalDirection["Bottom"] = "bottom";
|
|
19367
|
-
return VerticalDirection;
|
|
19368
|
-
}({});
|
|
19369
|
-
const Triangle = props => {
|
|
19370
|
-
let borderLeft;
|
|
19371
|
-
let borderRight;
|
|
19372
|
-
let borderTop;
|
|
19373
|
-
let borderBottom;
|
|
19374
|
-
const hBorder = `${props.width}px solid transparent`;
|
|
19375
|
-
if (props.horizontalDirection === "right") {
|
|
19376
|
-
borderLeft = hBorder;
|
|
19377
|
-
} else {
|
|
19378
|
-
borderRight = hBorder;
|
|
19379
|
-
}
|
|
19380
|
-
const vBorder = `${props.height}px solid ${props.color}`;
|
|
19381
|
-
if (props.verticalDirection === "top") {
|
|
19382
|
-
borderTop = vBorder;
|
|
19383
|
-
} else {
|
|
19384
|
-
borderBottom = vBorder;
|
|
19385
|
-
}
|
|
19386
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
19387
|
-
style: {
|
|
19388
|
-
display: "block",
|
|
19389
|
-
height: 0,
|
|
19390
|
-
width: 0,
|
|
19391
|
-
position: "absolute",
|
|
19392
|
-
left: props.left,
|
|
19393
|
-
top: props.top,
|
|
19394
|
-
borderLeft: borderLeft,
|
|
19395
|
-
borderRight: borderRight,
|
|
19396
|
-
borderTop: borderTop,
|
|
19397
|
-
borderBottom: borderBottom
|
|
19398
|
-
}
|
|
19399
|
-
});
|
|
19400
|
-
};
|
|
19401
|
-
const TooltipArrow = _ref => {
|
|
19402
|
-
let {
|
|
19403
|
-
position = "relative",
|
|
19404
|
-
visibility = "visible",
|
|
19405
|
-
left = 100,
|
|
19406
|
-
top = 0
|
|
19407
|
-
} = _ref,
|
|
19408
|
-
props = _objectWithoutPropertiesLoose(_ref, _excluded$4);
|
|
19409
|
-
// TODO(aria): Think about adding a box-shadow to the triangle here
|
|
19410
|
-
// See http://css-tricks.com/triangle-with-shadow/
|
|
19411
|
-
|
|
19412
|
-
//const isRight = (this.props.horizontalDirection === "right");
|
|
19413
|
-
const isTop = props.verticalDirection === "top";
|
|
19414
|
-
const frontTopOffset = isTop ? 0 : 1;
|
|
19415
|
-
const borderTopOffset = isTop ? 0 : -1;
|
|
19416
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
19417
|
-
style: {
|
|
19418
|
-
display: "block",
|
|
19419
|
-
position: position,
|
|
19420
|
-
visibility: visibility,
|
|
19421
|
-
left: left,
|
|
19422
|
-
top: top,
|
|
19423
|
-
width: props.width + 2,
|
|
19424
|
-
height: props.height + 1,
|
|
19425
|
-
marginTop: -1,
|
|
19426
|
-
marginBottom: -2,
|
|
19427
|
-
zIndex: zIndex
|
|
19428
|
-
}
|
|
19429
|
-
}, /*#__PURE__*/React.createElement(Triangle, {
|
|
19430
|
-
horizontalDirection: props.horizontalDirection,
|
|
19431
|
-
verticalDirection: props.verticalDirection,
|
|
19432
|
-
color: props.border,
|
|
19433
|
-
left: 0,
|
|
19434
|
-
top: borderTopOffset,
|
|
19435
|
-
width: props.width + 2 // one extra for the diagonal
|
|
19436
|
-
,
|
|
19437
|
-
height: props.height + 2
|
|
19438
|
-
}), /*#__PURE__*/React.createElement(Triangle, {
|
|
19439
|
-
horizontalDirection: props.horizontalDirection,
|
|
19440
|
-
verticalDirection: props.verticalDirection,
|
|
19441
|
-
color: props.color,
|
|
19442
|
-
left: 1,
|
|
19443
|
-
top: frontTopOffset,
|
|
19444
|
-
width: props.width,
|
|
19445
|
-
height: props.height
|
|
19446
|
-
}));
|
|
19447
|
-
};
|
|
19448
|
-
const VERTICAL_CORNERS = {
|
|
19449
|
-
top: {
|
|
19450
|
-
top: "-100%"
|
|
19451
|
-
},
|
|
19452
|
-
bottom: {
|
|
19453
|
-
top: 0
|
|
19454
|
-
}
|
|
19455
|
-
};
|
|
19456
|
-
const HORIZONTAL_CORNERS = {
|
|
19457
|
-
left: {
|
|
19458
|
-
targetLeft: 0
|
|
19459
|
-
},
|
|
19460
|
-
right: {
|
|
19461
|
-
targetLeft: "100%"
|
|
19462
|
-
}
|
|
19463
|
-
};
|
|
19464
|
-
const HORIZONTAL_ALIGNMNENTS = {
|
|
19465
|
-
left: {
|
|
19466
|
-
tooltipLeft: 0,
|
|
19467
|
-
arrowLeft: arrowSize => 0
|
|
19468
|
-
},
|
|
19469
|
-
right: {
|
|
19470
|
-
tooltipLeft: "-100%",
|
|
19471
|
-
arrowLeft: arrowSize => -arrowSize - 2
|
|
19472
|
-
}
|
|
19473
|
-
};
|
|
19474
|
-
/**
|
|
19475
|
-
* DEPRECATED! Use Wonder Blocks tooltip instead.
|
|
19476
|
-
*
|
|
19477
|
-
* A generic tooltip library for React.js
|
|
19478
|
-
*
|
|
19479
|
-
* ```
|
|
19480
|
-
* import Tooltip from "./tooltip";
|
|
19481
|
-
* <Tooltip
|
|
19482
|
-
* className="class-for-tooltip-contents"
|
|
19483
|
-
* horizontalPosition={HoriziontalDirection.Left}
|
|
19484
|
-
* horizontalAlign={HoriziontalDirection.Left}
|
|
19485
|
-
* verticalPosition={VerticalDirection.Top}
|
|
19486
|
-
* arrowSize={10} // arrow size in pixels
|
|
19487
|
-
* borderColor="#ccc" // color of the border for the tooltip
|
|
19488
|
-
* show={true} // whether the tooltip should currently be visible
|
|
19489
|
-
* targetContainerStyle={targetContainerStyle}
|
|
19490
|
-
* >
|
|
19491
|
-
* <TargetElementOfTheTooltip />
|
|
19492
|
-
* <TooltipContents1 />
|
|
19493
|
-
* <TooltipContents2 />
|
|
19494
|
-
* </Tooltip>
|
|
19495
|
-
* ```
|
|
19496
|
-
*
|
|
19497
|
-
* To show/hide the tooltip, the parent component should call the
|
|
19498
|
-
* `.show()` and `.hide()` methods of the tooltip when appropriate.
|
|
19499
|
-
* (These are usually set up as handlers of events on the target element.)
|
|
19500
|
-
*
|
|
19501
|
-
* Notes:
|
|
19502
|
-
* `className` should not specify a border; that is handled by `borderColor`
|
|
19503
|
-
* so that the arrow and tooltip match
|
|
19504
|
-
*
|
|
19505
|
-
* ```
|
|
19506
|
-
* __,,--``\\
|
|
19507
|
-
* _,,-''`` \\ ,
|
|
19508
|
-
* '----------_.------'-.___|\__
|
|
19509
|
-
* _.--''`` `)__ )__ @\__
|
|
19510
|
-
* ( .. ''---/___,,E/__,E'------`
|
|
19511
|
-
* `-''`''
|
|
19512
|
-
* Here be dragons.
|
|
19513
|
-
* ```
|
|
19514
|
-
*/
|
|
19515
|
-
class Tooltip extends React.Component {
|
|
19516
|
-
constructor(...args) {
|
|
19517
|
-
super(...args);
|
|
19518
|
-
this.tooltipContainerRef = /*#__PURE__*/React.createRef();
|
|
19519
|
-
this.state = {
|
|
19520
|
-
height: null // used for offsetting "top" positioned tooltips
|
|
19527
|
+
autoComplete: "off",
|
|
19528
|
+
autoCorrect: "off",
|
|
19529
|
+
spellCheck: "false"
|
|
19521
19530
|
};
|
|
19522
|
-
|
|
19523
|
-
|
|
19524
|
-
componentDidMount() {
|
|
19525
|
-
this._updateHeight();
|
|
19526
|
-
}
|
|
19527
|
-
UNSAFE_componentWillReceiveProps() {
|
|
19528
|
-
// If the contents have changed, reset our measure of the height
|
|
19529
|
-
this.setState({
|
|
19530
|
-
height: null
|
|
19531
|
-
});
|
|
19532
|
-
}
|
|
19533
|
-
componentDidUpdate() {
|
|
19534
|
-
this._updateHeight();
|
|
19535
|
-
}
|
|
19536
|
-
_renderToolTipDiv(isTooltipAbove) {
|
|
19537
|
-
const settings = Object.assign({}, HORIZONTAL_CORNERS[this.props.horizontalPosition], HORIZONTAL_ALIGNMNENTS[this.props.horizontalAlign], VERTICAL_CORNERS[this.props.verticalPosition]);
|
|
19538
|
-
let arrowAbove;
|
|
19539
|
-
let arrowBelow;
|
|
19540
|
-
if (isTooltipAbove) {
|
|
19541
|
-
// We put an absolutely positioned arrow in the correct place
|
|
19542
|
-
arrowAbove = /*#__PURE__*/React.createElement(TooltipArrow, {
|
|
19543
|
-
verticalDirection: VerticalDirection.Top,
|
|
19544
|
-
horizontalDirection: this.props.horizontalAlign,
|
|
19545
|
-
position: "absolute",
|
|
19546
|
-
color: "white",
|
|
19547
|
-
border: this.props.borderColor,
|
|
19548
|
-
left: settings.arrowLeft(this.props.arrowSize),
|
|
19549
|
-
top: -this.props.arrowSize + 2,
|
|
19550
|
-
width: this.props.arrowSize,
|
|
19551
|
-
height: this.props.arrowSize,
|
|
19552
|
-
zIndex: zIndex
|
|
19553
|
-
});
|
|
19554
|
-
|
|
19555
|
-
// And we use a visibility: hidden arrow below to shift up the
|
|
19556
|
-
// content by the correct amount
|
|
19557
|
-
arrowBelow = /*#__PURE__*/React.createElement(TooltipArrow, {
|
|
19558
|
-
verticalDirection: VerticalDirection.Top,
|
|
19559
|
-
horizontalDirection: this.props.horizontalAlign,
|
|
19560
|
-
visibility: "hidden",
|
|
19561
|
-
color: "white",
|
|
19562
|
-
border: this.props.borderColor,
|
|
19563
|
-
left: settings.arrowLeft(this.props.arrowSize),
|
|
19564
|
-
top: -1,
|
|
19565
|
-
width: this.props.arrowSize,
|
|
19566
|
-
height: this.props.arrowSize,
|
|
19567
|
-
zIndex: zIndex
|
|
19568
|
-
});
|
|
19569
|
-
} else {
|
|
19570
|
-
arrowAbove = /*#__PURE__*/React.createElement(TooltipArrow, {
|
|
19571
|
-
verticalDirection: VerticalDirection.Bottom,
|
|
19572
|
-
horizontalDirection: this.props.horizontalAlign,
|
|
19573
|
-
color: "white",
|
|
19574
|
-
border: this.props.borderColor,
|
|
19575
|
-
left: settings.arrowLeft(this.props.arrowSize),
|
|
19576
|
-
top: -1,
|
|
19577
|
-
width: this.props.arrowSize,
|
|
19578
|
-
height: this.props.arrowSize,
|
|
19579
|
-
zIndex: zIndex
|
|
19580
|
-
});
|
|
19581
|
-
arrowBelow = null;
|
|
19582
|
-
}
|
|
19583
|
-
|
|
19584
|
-
/* A positioned div below the input to be the parent for our
|
|
19585
|
-
tooltip */
|
|
19586
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
19531
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TextInput, inputProps), /*#__PURE__*/React.createElement("span", {
|
|
19532
|
+
id: ariaId,
|
|
19587
19533
|
style: {
|
|
19588
|
-
|
|
19589
|
-
height: 0,
|
|
19590
|
-
display: this.props.show ? "block" : "none"
|
|
19534
|
+
display: "none"
|
|
19591
19535
|
}
|
|
19592
|
-
},
|
|
19593
|
-
|
|
19594
|
-
|
|
19595
|
-
|
|
19596
|
-
|
|
19597
|
-
|
|
19598
|
-
// height must start out undefined, not null, so that
|
|
19599
|
-
// we can measure the actual height with jquery.
|
|
19600
|
-
// This is used to position the tooltip with top: -100%
|
|
19601
|
-
// when in verticalPosition: "top" mode
|
|
19602
|
-
height: this.state.height || undefined,
|
|
19603
|
-
left: settings.targetLeft
|
|
19604
|
-
}
|
|
19605
|
-
}, arrowAbove, /*#__PURE__*/React.createElement("div", {
|
|
19606
|
-
className: this.props.className
|
|
19607
|
-
// eslint-disable-next-line react/no-string-refs
|
|
19608
|
-
,
|
|
19609
|
-
ref: "tooltipContent",
|
|
19610
|
-
style: {
|
|
19611
|
-
position: "relative",
|
|
19612
|
-
top: settings["top"],
|
|
19613
|
-
left: settings.tooltipLeft,
|
|
19614
|
-
border: "1px solid " + this.props.borderColor,
|
|
19615
|
-
boxShadow: "0 1px 3px " + this.props.borderColor,
|
|
19616
|
-
zIndex: zIndex - 1
|
|
19617
|
-
}
|
|
19618
|
-
}, this.props.children.slice(1)), arrowBelow));
|
|
19619
|
-
}
|
|
19620
|
-
_updateHeight() {
|
|
19621
|
-
const tooltipContainer = this.tooltipContainerRef.current;
|
|
19622
|
-
const height = (tooltipContainer == null ? void 0 : tooltipContainer.offsetHeight) || 0;
|
|
19623
|
-
if (height !== this.state.height) {
|
|
19624
|
-
this.setState({
|
|
19625
|
-
height
|
|
19626
|
-
});
|
|
19627
|
-
}
|
|
19628
|
-
}
|
|
19629
|
-
render() {
|
|
19630
|
-
const isTooltipAbove = this.props.verticalPosition === "top";
|
|
19631
|
-
|
|
19632
|
-
/* We wrap the entire output in a span so that it displays inline */
|
|
19633
|
-
return /*#__PURE__*/React.createElement("span", null, isTooltipAbove && this._renderToolTipDiv(isTooltipAbove), /*#__PURE__*/React.createElement("div", {
|
|
19634
|
-
style: this.props.targetContainerStyle
|
|
19635
|
-
}, this.props.children[0]), !isTooltipAbove && this._renderToolTipDiv());
|
|
19636
|
-
}
|
|
19637
|
-
}
|
|
19638
|
-
|
|
19639
|
-
// Sorry. // Apology-Oriented-Programming
|
|
19640
|
-
Tooltip.defaultProps = {
|
|
19641
|
-
className: "",
|
|
19642
|
-
arrowSize: 10,
|
|
19643
|
-
borderColor: "#ccc",
|
|
19644
|
-
verticalPosition: VerticalDirection.Bottom,
|
|
19645
|
-
horizontalPosition: HorizontalDirection.Left,
|
|
19646
|
-
horizontalAlign: HorizontalDirection.Left,
|
|
19647
|
-
targetContainerStyle: {}
|
|
19648
|
-
};
|
|
19649
|
-
|
|
19650
|
-
const {
|
|
19651
|
-
captureScratchpadTouchStart: captureScratchpadTouchStart$2
|
|
19652
|
-
} = Util;
|
|
19653
|
-
class InputWithExamples extends React.Component {
|
|
19654
|
-
constructor(...args) {
|
|
19655
|
-
super(...args);
|
|
19656
|
-
this.state = {
|
|
19657
|
-
focused: false,
|
|
19658
|
-
showExamples: false
|
|
19659
|
-
};
|
|
19660
|
-
this._getUniqueId = () => {
|
|
19661
|
-
return `input-with-examples-${btoa(this.props.id).replace(/=/g, "")}`;
|
|
19662
|
-
};
|
|
19663
|
-
this._getInputClassName = () => {
|
|
19664
|
-
// Otherwise, we need to add these INPUT and FOCUSED tags here.
|
|
19665
|
-
let className = ClassNames.INPUT;
|
|
19666
|
-
if (this.state.focused) {
|
|
19667
|
-
className += " " + ClassNames.FOCUSED;
|
|
19668
|
-
}
|
|
19669
|
-
if (this.props.className) {
|
|
19670
|
-
className += " " + this.props.className;
|
|
19671
|
-
}
|
|
19672
|
-
return className;
|
|
19673
|
-
};
|
|
19674
|
-
this._renderInput = () => {
|
|
19675
|
-
const id = this._getUniqueId();
|
|
19676
|
-
const ariaId = `aria-for-${id}`;
|
|
19677
|
-
// Generate text from a known set of format options that will read well in a screen reader
|
|
19678
|
-
const examplesAria = this.props.examples.length === 7 ? "" : `${this.props.examples[0]}
|
|
19679
|
-
${this.props.examples.slice(1).join(", or\n")}`
|
|
19680
|
-
// @ts-expect-error TS2550: Property replaceAll does not exist on type string.
|
|
19681
|
-
.replaceAll("*", "").replaceAll("$", "").replaceAll("\\ \\text{pi}", " pi").replaceAll("\\ ", " and ");
|
|
19682
|
-
const inputProps = {
|
|
19683
|
-
id: id,
|
|
19684
|
-
"aria-describedby": ariaId,
|
|
19685
|
-
ref: "input",
|
|
19686
|
-
className: this._getInputClassName(),
|
|
19687
|
-
labelText: this.props.labelText,
|
|
19688
|
-
value: this.props.value,
|
|
19689
|
-
onFocus: this._handleFocus,
|
|
19690
|
-
onBlur: this._handleBlur,
|
|
19691
|
-
disabled: this.props.disabled,
|
|
19692
|
-
style: this.props.style,
|
|
19693
|
-
onChange: this.props.onChange,
|
|
19694
|
-
onTouchStart: captureScratchpadTouchStart$2,
|
|
19695
|
-
autoCapitalize: "off",
|
|
19696
|
-
autoComplete: "off",
|
|
19697
|
-
autoCorrect: "off",
|
|
19698
|
-
spellCheck: "false"
|
|
19699
|
-
};
|
|
19700
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TextInput, inputProps), /*#__PURE__*/React.createElement("span", {
|
|
19701
|
-
id: ariaId,
|
|
19702
|
-
style: {
|
|
19703
|
-
display: "none"
|
|
19704
|
-
}
|
|
19705
|
-
}, examplesAria));
|
|
19706
|
-
};
|
|
19707
|
-
this._handleFocus = () => {
|
|
19708
|
-
this.props.onFocus();
|
|
19709
|
-
this.setState({
|
|
19710
|
-
focused: true,
|
|
19711
|
-
showExamples: true
|
|
19712
|
-
});
|
|
19713
|
-
};
|
|
19714
|
-
this.show = () => {
|
|
19715
|
-
this.setState({
|
|
19716
|
-
showExamples: true
|
|
19717
|
-
});
|
|
19718
|
-
};
|
|
19719
|
-
this.hide = () => {
|
|
19720
|
-
this.setState({
|
|
19721
|
-
showExamples: false
|
|
19722
|
-
});
|
|
19723
|
-
};
|
|
19724
|
-
this._handleBlur = () => {
|
|
19725
|
-
this.props.onBlur();
|
|
19726
|
-
this.setState({
|
|
19727
|
-
focused: false,
|
|
19728
|
-
showExamples: false
|
|
19729
|
-
});
|
|
19730
|
-
};
|
|
19731
|
-
this.focus = () => {
|
|
19732
|
-
// eslint-disable-next-line react/no-string-refs
|
|
19733
|
-
// @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
|
|
19734
|
-
this.refs.input.focus();
|
|
19735
|
-
};
|
|
19736
|
-
this.blur = () => {
|
|
19737
|
-
// eslint-disable-next-line react/no-string-refs
|
|
19738
|
-
// @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
|
|
19739
|
-
this.refs.input.blur();
|
|
19740
|
-
};
|
|
19741
|
-
this.handleChange = e => {
|
|
19742
|
-
this.props.onChange(e.target.value);
|
|
19743
|
-
};
|
|
19744
|
-
}
|
|
19745
|
-
render() {
|
|
19746
|
-
const input = this._renderInput();
|
|
19747
|
-
const examplesContent = this.props.examples.length <= 2 ? this.props.examples.join(" ") // A single item (with or without leading text) is not a "list"
|
|
19748
|
-
: this.props.examples // 2 or more items should display as a list
|
|
19749
|
-
.map((example, index) => {
|
|
19750
|
-
// If the first example is bold, then it is most likely a heading/leading text.
|
|
19751
|
-
// So, it shouldn't be part of the list.
|
|
19752
|
-
return index === 0 && example.startsWith("**") ? `${example}\n` : `- ${example}`;
|
|
19753
|
-
}).join("\n");
|
|
19754
|
-
const showExamples = this.props.shouldShowExamples && this.state.showExamples;
|
|
19755
|
-
return /*#__PURE__*/React.createElement(Tooltip, {
|
|
19756
|
-
className: "perseus-formats-tooltip preview-measure",
|
|
19757
|
-
horizontalPosition: HorizontalDirection.Left,
|
|
19758
|
-
horizontalAlign: HorizontalDirection.Left,
|
|
19759
|
-
verticalPosition: VerticalDirection.Bottom,
|
|
19760
|
-
arrowSize: 10,
|
|
19761
|
-
borderColor: "#ccc",
|
|
19762
|
-
show: showExamples
|
|
19763
|
-
}, input, /*#__PURE__*/React.createElement("div", {
|
|
19764
|
-
id: this._getUniqueId()
|
|
19536
|
+
}, examplesAria));
|
|
19537
|
+
};
|
|
19538
|
+
const renderTooltipContent = () => {
|
|
19539
|
+
return /*#__PURE__*/React.createElement(TooltipContent, null, /*#__PURE__*/React.createElement("div", {
|
|
19540
|
+
id: id,
|
|
19541
|
+
className: "input-with-examples-tooltip"
|
|
19765
19542
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
19766
19543
|
content: examplesContent,
|
|
19767
|
-
linterContext: PerseusLinter.pushContextStack(
|
|
19768
|
-
strings:
|
|
19544
|
+
linterContext: PerseusLinter.pushContextStack(linterContext, "input-with-examples"),
|
|
19545
|
+
strings: context.strings
|
|
19769
19546
|
})));
|
|
19770
|
-
}
|
|
19771
|
-
}
|
|
19772
|
-
InputWithExamples.contextType = PerseusI18nContext;
|
|
19773
|
-
InputWithExamples.defaultProps = {
|
|
19774
|
-
shouldShowExamples: true,
|
|
19775
|
-
onFocus: function () {},
|
|
19776
|
-
onBlur: function () {},
|
|
19777
|
-
disabled: false,
|
|
19778
|
-
linterContext: PerseusLinter.linterContextDefault,
|
|
19779
|
-
className: ""
|
|
19780
|
-
};
|
|
19781
|
-
|
|
19782
|
-
const _excluded$3 = ["keypadElement", "onFocus", "value"];
|
|
19783
|
-
class SimpleKeypadInput extends React.Component {
|
|
19784
|
-
constructor(...args) {
|
|
19785
|
-
super(...args);
|
|
19786
|
-
this._isMounted = false;
|
|
19787
|
-
}
|
|
19788
|
-
componentDidMount() {
|
|
19789
|
-
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
19790
|
-
// this.isMounted() but is still considered an anti-pattern.
|
|
19791
|
-
this._isMounted = true;
|
|
19792
|
-
}
|
|
19793
|
-
componentWillUnmount() {
|
|
19794
|
-
this._isMounted = false;
|
|
19795
|
-
}
|
|
19796
|
-
focus() {
|
|
19797
|
-
// @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
|
|
19798
|
-
this.refs.input.focus(); // eslint-disable-line react/no-string-refs
|
|
19799
|
-
}
|
|
19800
|
-
|
|
19801
|
-
blur() {
|
|
19802
|
-
var _this$refs$input;
|
|
19803
|
-
// eslint-disable-next-line react/no-string-refs
|
|
19804
|
-
// @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
|
|
19805
|
-
if (typeof ((_this$refs$input = this.refs.input) == null ? void 0 : _this$refs$input.blur) === "function") {
|
|
19806
|
-
var _this$refs$input2;
|
|
19807
|
-
// eslint-disable-next-line react/no-string-refs
|
|
19808
|
-
// @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
|
|
19809
|
-
(_this$refs$input2 = this.refs.input) == null || _this$refs$input2.blur();
|
|
19810
|
-
}
|
|
19811
|
-
}
|
|
19812
|
-
getValue() {
|
|
19813
|
-
return this.props.value;
|
|
19814
|
-
}
|
|
19815
|
-
render() {
|
|
19816
|
-
const _this = this;
|
|
19817
|
-
// Intercept the `onFocus` prop, as we need to configure the keypad
|
|
19818
|
-
// before continuing with the default focus logic for Perseus inputs.
|
|
19819
|
-
// Intercept the `value` prop so as to map `null` to the empty string,
|
|
19820
|
-
// as the `KeypadInput` does not support `null` values.
|
|
19821
|
-
const _this$props = _this.props,
|
|
19822
|
-
{
|
|
19823
|
-
keypadElement,
|
|
19824
|
-
onFocus,
|
|
19825
|
-
value
|
|
19826
|
-
} = _this$props,
|
|
19827
|
-
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
|
|
19828
|
-
return (
|
|
19829
|
-
/*#__PURE__*/
|
|
19830
|
-
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
19831
|
-
React.createElement(KeypadInput
|
|
19832
|
-
// eslint-disable-next-line react/no-string-refs
|
|
19833
|
-
, _extends({
|
|
19834
|
-
ref: "input",
|
|
19835
|
-
keypadElement: keypadElement,
|
|
19836
|
-
onFocus: () => {
|
|
19837
|
-
if (keypadElement) {
|
|
19838
|
-
keypadElement.configure({
|
|
19839
|
-
keypadType: KeypadType.FRACTION
|
|
19840
|
-
}, () => {
|
|
19841
|
-
if (_this._isMounted) {
|
|
19842
|
-
onFocus == null || onFocus();
|
|
19843
|
-
}
|
|
19844
|
-
});
|
|
19845
|
-
} else {
|
|
19846
|
-
onFocus == null || onFocus();
|
|
19847
|
-
}
|
|
19848
|
-
},
|
|
19849
|
-
value: value == null ? "" : "" + value
|
|
19850
|
-
}, rest))
|
|
19851
|
-
);
|
|
19852
|
-
}
|
|
19853
|
-
}
|
|
19854
|
-
|
|
19855
|
-
// @ts-expect-error - TS2339 - Property 'propTypes' does not exist on type 'typeof SimpleKeypadInput'.
|
|
19856
|
-
SimpleKeypadInput.propTypes = {
|
|
19857
|
-
keypadElement: keypadElementPropType,
|
|
19858
|
-
onFocus: PropTypes.func,
|
|
19859
|
-
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
|
19860
|
-
};
|
|
19861
|
-
|
|
19862
|
-
const getPromptJSON$s = (renderProps, userInput) => {
|
|
19863
|
-
return {
|
|
19864
|
-
type: "input-number",
|
|
19865
|
-
options: {
|
|
19866
|
-
simplify: renderProps.simplify,
|
|
19867
|
-
answerType: renderProps.answerType
|
|
19868
|
-
},
|
|
19869
|
-
userInput: {
|
|
19870
|
-
value: userInput.currentValue
|
|
19871
|
-
}
|
|
19872
19547
|
};
|
|
19873
|
-
|
|
19548
|
+
// Display the examples as a string when there are less than or equal to 2 examples.
|
|
19549
|
+
// Otherwise, display the examples as a list.
|
|
19550
|
+
const examplesContent = props.examples.length <= 2 ? props.examples.join(" ") : props.examples.map((example, index) => {
|
|
19551
|
+
return index === 0 && example.startsWith("**") ? `${example}\n` : `- ${example}`;
|
|
19552
|
+
}).join("\n");
|
|
19553
|
+
|
|
19554
|
+
// Display the examples when they are enabled (shouldShowExamples) and the input is focused.
|
|
19555
|
+
const showExamplesTooltip = shouldShowExamples && inputFocused;
|
|
19556
|
+
return /*#__PURE__*/React.createElement(Tooltip, {
|
|
19557
|
+
content: renderTooltipContent(),
|
|
19558
|
+
opened: showExamplesTooltip,
|
|
19559
|
+
placement: "bottom"
|
|
19560
|
+
}, renderInput());
|
|
19561
|
+
});
|
|
19874
19562
|
|
|
19875
|
-
const formExamples
|
|
19563
|
+
const formExamples = {
|
|
19876
19564
|
integer: function (options, strings) {
|
|
19877
19565
|
return strings.integerExample;
|
|
19878
19566
|
},
|
|
@@ -19975,7 +19663,7 @@ class InputNumber extends React.Component {
|
|
|
19975
19663
|
} = this.context;
|
|
19976
19664
|
const type = this.props.answerType;
|
|
19977
19665
|
const forms = inputNumberAnswerTypes[type].forms.split(/\s*,\s*/);
|
|
19978
|
-
const examples = _.map(forms, form => formExamples
|
|
19666
|
+
const examples = _.map(forms, form => formExamples[form](this.props, strings));
|
|
19979
19667
|
return [strings.yourAnswer].concat(examples);
|
|
19980
19668
|
}
|
|
19981
19669
|
render() {
|
|
@@ -20100,7 +19788,11 @@ const getPromptJSON$r = (renderProps, userInput) => {
|
|
|
20100
19788
|
};
|
|
20101
19789
|
};
|
|
20102
19790
|
|
|
20103
|
-
|
|
19791
|
+
/**
|
|
19792
|
+
* The full list of available strings for the numeric input widget,
|
|
19793
|
+
* based on whether the Content Creator has specified that the answer must be simplified.
|
|
19794
|
+
*/
|
|
19795
|
+
const NumericExampleStrings = {
|
|
20104
19796
|
integer: (form, strings) => strings.integerExample,
|
|
20105
19797
|
proper: (form, strings) => form.simplify === "optional" ? strings.properExample : strings.simplifiedProperExample,
|
|
20106
19798
|
improper: (form, strings) => form.simplify === "optional" ? strings.improperExample : strings.simplifiedImproperExample,
|
|
@@ -20108,38 +19800,200 @@ const formExamples = {
|
|
|
20108
19800
|
decimal: (form, strings) => strings.decimalExample,
|
|
20109
19801
|
pi: (form, strings) => strings.piExample
|
|
20110
19802
|
};
|
|
19803
|
+
|
|
19804
|
+
/**
|
|
19805
|
+
* Generates the specific set of examples for the current question.
|
|
19806
|
+
* This string is shown as examples to the user in a tooltip.
|
|
19807
|
+
*/
|
|
19808
|
+
const generateExamples = (answerForms, strings) => {
|
|
19809
|
+
// If the Content Creator has not specified any answer forms,
|
|
19810
|
+
// we do not need to show any examples.
|
|
19811
|
+
if (answerForms.length === 0) {
|
|
19812
|
+
return [];
|
|
19813
|
+
}
|
|
19814
|
+
|
|
19815
|
+
// Generate a list of the unique answer forms.
|
|
19816
|
+
const uniqueForms = getUniqueAnswerForms(answerForms);
|
|
19817
|
+
|
|
19818
|
+
// Generate the example strings for each unique form.
|
|
19819
|
+
const examples = uniqueForms.map(form => {
|
|
19820
|
+
return NumericExampleStrings[form.name](form, strings);
|
|
19821
|
+
});
|
|
19822
|
+
|
|
19823
|
+
// Add the "Your answer" string to the beginning of the examples list.
|
|
19824
|
+
return [strings.yourAnswer].concat(examples);
|
|
19825
|
+
};
|
|
19826
|
+
|
|
19827
|
+
/**
|
|
19828
|
+
* Determines whether to show examples of how to input the various supported answer forms.
|
|
19829
|
+
* We do not show examples if all forms are accepted or if no forms are accepted.
|
|
19830
|
+
*/
|
|
19831
|
+
const shouldShowExamples = answerForms => {
|
|
19832
|
+
// If the Content Creator has not specified any answer forms,
|
|
19833
|
+
// we do not need to show any examples.
|
|
19834
|
+
if (answerForms.length === 0) {
|
|
19835
|
+
return false;
|
|
19836
|
+
}
|
|
19837
|
+
|
|
19838
|
+
// Generate a list of the unique names of the selected answer forms.
|
|
19839
|
+
const answerFormNames = getUniqueAnswerForms(answerForms).map(form => form.name);
|
|
19840
|
+
|
|
19841
|
+
// If all forms are accepted, we do not need to show any examples.
|
|
19842
|
+
const allFormsAccepted = answerFormNames.length >= Object.keys(NumericExampleStrings).length;
|
|
19843
|
+
return !allFormsAccepted;
|
|
19844
|
+
};
|
|
19845
|
+
|
|
19846
|
+
/**
|
|
19847
|
+
* uniqueAnswerForms takes a list of answer forms and returns a list of unique
|
|
19848
|
+
* answer forms. This is useful for ensuring that we don't show duplicate examples
|
|
19849
|
+
* to the user.
|
|
19850
|
+
*/
|
|
19851
|
+
const getUniqueAnswerForms = function getUniqueAnswerForms(list) {
|
|
19852
|
+
// We use a Set to keep track of the forms we've already seen.
|
|
19853
|
+
const foundForms = new Set();
|
|
19854
|
+
return list.filter(form => {
|
|
19855
|
+
// If we've already seen this form, skip it.
|
|
19856
|
+
if (foundForms.has(form.name)) {
|
|
19857
|
+
return false;
|
|
19858
|
+
}
|
|
19859
|
+
// Otherwise, add it to the set and return true.
|
|
19860
|
+
foundForms.add(form.name);
|
|
19861
|
+
return true;
|
|
19862
|
+
});
|
|
19863
|
+
};
|
|
19864
|
+
|
|
19865
|
+
/**
|
|
19866
|
+
* Takes a list of lists of answer forms, and returns a list of the forms
|
|
19867
|
+
* in each of these lists in the same order that they're listed in the
|
|
19868
|
+
* `formExamples` forms from above.
|
|
19869
|
+
*/
|
|
19870
|
+
const unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
19871
|
+
// Pull out all of the forms from the different lists.
|
|
19872
|
+
const allForms = answerFormsList.flat();
|
|
19873
|
+
// Pull out the unique forms using getUniqueAnswerForms.
|
|
19874
|
+
const uniqueForms = getUniqueAnswerForms(allForms);
|
|
19875
|
+
// Sort them by the order they appear in the `formExamples` list.
|
|
19876
|
+
const formExampleKeys = Object.keys(NumericExampleStrings);
|
|
19877
|
+
return uniqueForms.sort((a, b) => {
|
|
19878
|
+
return formExampleKeys.indexOf(a.name) - formExampleKeys.indexOf(b.name);
|
|
19879
|
+
});
|
|
19880
|
+
};
|
|
19881
|
+
|
|
19882
|
+
/**
|
|
19883
|
+
* The NumericInputComponent is a child component of the NumericInput class
|
|
19884
|
+
* component. It is responsible for rendering the UI elements of the Numeric
|
|
19885
|
+
* Input widget.
|
|
19886
|
+
*/
|
|
19887
|
+
const NumericInputComponent = /*#__PURE__*/forwardRef((props, ref) => {
|
|
19888
|
+
const context = useContext(PerseusI18nContext);
|
|
19889
|
+
const inputRef = useRef(null);
|
|
19890
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
19891
|
+
|
|
19892
|
+
// Pass the focus and blur methods to the Numeric Input Class component
|
|
19893
|
+
useImperativeHandle(ref, () => ({
|
|
19894
|
+
current: inputRef.current,
|
|
19895
|
+
focus: () => {
|
|
19896
|
+
if (inputRef.current) {
|
|
19897
|
+
var _inputRef$current;
|
|
19898
|
+
(_inputRef$current = inputRef.current) == null || _inputRef$current.focus();
|
|
19899
|
+
setIsFocused(true);
|
|
19900
|
+
}
|
|
19901
|
+
},
|
|
19902
|
+
blur: () => {
|
|
19903
|
+
if (inputRef.current) {
|
|
19904
|
+
var _inputRef$current2;
|
|
19905
|
+
(_inputRef$current2 = inputRef.current) == null || _inputRef$current2.blur();
|
|
19906
|
+
setIsFocused(false);
|
|
19907
|
+
}
|
|
19908
|
+
}
|
|
19909
|
+
}));
|
|
19910
|
+
const handleChange = (newValue, cb) => {
|
|
19911
|
+
props.onChange({
|
|
19912
|
+
currentValue: newValue
|
|
19913
|
+
}, cb);
|
|
19914
|
+
props.trackInteraction();
|
|
19915
|
+
};
|
|
19916
|
+
const handleFocus = () => {
|
|
19917
|
+
props.onFocus([]);
|
|
19918
|
+
setIsFocused(true);
|
|
19919
|
+
};
|
|
19920
|
+
const handleBlur = () => {
|
|
19921
|
+
props.onBlur([]);
|
|
19922
|
+
setIsFocused(false);
|
|
19923
|
+
};
|
|
19924
|
+
|
|
19925
|
+
// Styles for the InputWithExamples
|
|
19926
|
+
const styles = StyleSheet.create({
|
|
19927
|
+
inputWithExamples: {
|
|
19928
|
+
borderRadius: "3px",
|
|
19929
|
+
borderWidth: isFocused ? "2px" : "1px",
|
|
19930
|
+
display: "inline-block",
|
|
19931
|
+
fontFamily: `Symbola, "Times New Roman", serif`,
|
|
19932
|
+
fontSize: "18px",
|
|
19933
|
+
height: "32px",
|
|
19934
|
+
lineHeight: "18px",
|
|
19935
|
+
padding: isFocused ? "4px" : "4px 5px",
|
|
19936
|
+
textAlign: props.rightAlign ? "right" : "left",
|
|
19937
|
+
width: props.size === "small" ? 40 : 80
|
|
19938
|
+
}
|
|
19939
|
+
});
|
|
19940
|
+
|
|
19941
|
+
// (mobile-only) If the custom keypad is enabled, use the SimpleKeypadInput component
|
|
19942
|
+
if (props.apiOptions.customKeypad) {
|
|
19943
|
+
const alignmentClass = props.rightAlign ? "perseus-input-right-align" : undefined;
|
|
19944
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
19945
|
+
className: alignmentClass
|
|
19946
|
+
}, /*#__PURE__*/React.createElement(SimpleKeypadInput, {
|
|
19947
|
+
ref: inputRef,
|
|
19948
|
+
value: props.currentValue,
|
|
19949
|
+
keypadElement: props.keypadElement,
|
|
19950
|
+
onChange: handleChange,
|
|
19951
|
+
onFocus: handleFocus,
|
|
19952
|
+
onBlur: handleBlur
|
|
19953
|
+
}));
|
|
19954
|
+
}
|
|
19955
|
+
// (desktop-only) Otherwise, use the InputWithExamples component
|
|
19956
|
+
return /*#__PURE__*/React.createElement(InputWithExamples, {
|
|
19957
|
+
ref: inputRef,
|
|
19958
|
+
value: props.currentValue,
|
|
19959
|
+
onChange: handleChange,
|
|
19960
|
+
labelText: props.labelText,
|
|
19961
|
+
examples: generateExamples(props.answerForms, context.strings),
|
|
19962
|
+
shouldShowExamples: shouldShowExamples(props.answerForms),
|
|
19963
|
+
onFocus: handleFocus,
|
|
19964
|
+
onBlur: handleBlur,
|
|
19965
|
+
id: props.widgetId,
|
|
19966
|
+
disabled: props.apiOptions.readOnly,
|
|
19967
|
+
style: styles.inputWithExamples
|
|
19968
|
+
});
|
|
19969
|
+
});
|
|
19970
|
+
|
|
19971
|
+
const _excluded$3 = ["answers"];
|
|
19972
|
+
|
|
19973
|
+
/**
|
|
19974
|
+
* The NumericInput widget is a numeric input field that supports a variety of
|
|
19975
|
+
* answer forms, including integers, decimals, fractions, and mixed numbers.
|
|
19976
|
+
*
|
|
19977
|
+
* [Jan 2025] We're currenly migrating from class-based components to functional
|
|
19978
|
+
* components. While we cannot fully migrate this component yet, we can start
|
|
19979
|
+
* by using the functional component for the rendering the UI of the widget.
|
|
19980
|
+
*/
|
|
20111
19981
|
class NumericInput extends React.Component {
|
|
20112
19982
|
constructor(...args) {
|
|
20113
19983
|
super(...args);
|
|
20114
|
-
this.inputRef =
|
|
20115
|
-
this.state = {
|
|
20116
|
-
// keeps track of the other set of values when switching
|
|
20117
|
-
// between 0 and finite solutions
|
|
20118
|
-
previousValues: [""],
|
|
20119
|
-
isFocused: false
|
|
20120
|
-
};
|
|
20121
|
-
this.shouldShowExamples = () => {
|
|
20122
|
-
var _this$props$answerFor, _this$props$answerFor2;
|
|
20123
|
-
const noFormsAccepted = ((_this$props$answerFor = this.props.answerForms) == null ? void 0 : _this$props$answerFor.length) === 0;
|
|
20124
|
-
// To check if all answer forms are accepted, we must first
|
|
20125
|
-
// find the *names* of all accepted forms, and see if they are
|
|
20126
|
-
// all present, ignoring duplicates
|
|
20127
|
-
const answerFormNames = _.uniq((_this$props$answerFor2 = this.props.answerForms) == null ? void 0 : _this$props$answerFor2.map(form => form.name));
|
|
20128
|
-
const allFormsAccepted = answerFormNames.length >= Object.keys(formExamples).length;
|
|
20129
|
-
return !noFormsAccepted && !allFormsAccepted;
|
|
20130
|
-
};
|
|
19984
|
+
this.inputRef = /*#__PURE__*/React.createRef();
|
|
20131
19985
|
this.focus = () => {
|
|
20132
|
-
var _this$inputRef;
|
|
20133
|
-
(_this$inputRef = this.inputRef) == null || _this$inputRef.focus();
|
|
19986
|
+
var _this$inputRef$curren;
|
|
19987
|
+
(_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.focus();
|
|
20134
19988
|
return true;
|
|
20135
19989
|
};
|
|
20136
19990
|
this.focusInputPath = () => {
|
|
20137
|
-
var _this$
|
|
20138
|
-
(_this$
|
|
19991
|
+
var _this$inputRef$curren2;
|
|
19992
|
+
(_this$inputRef$curren2 = this.inputRef.current) == null || _this$inputRef$curren2.focus();
|
|
20139
19993
|
};
|
|
20140
19994
|
this.blurInputPath = () => {
|
|
20141
|
-
var _this$
|
|
20142
|
-
(_this$
|
|
19995
|
+
var _this$inputRef$curren3;
|
|
19996
|
+
(_this$inputRef$curren3 = this.inputRef.current) == null || _this$inputRef$curren3.blur();
|
|
20143
19997
|
};
|
|
20144
19998
|
this.getInputPaths = () => {
|
|
20145
19999
|
// The widget itself is an input, so we return a single empty list to
|
|
@@ -20147,33 +20001,13 @@ class NumericInput extends React.Component {
|
|
|
20147
20001
|
/* c8 ignore next */
|
|
20148
20002
|
return [[]];
|
|
20149
20003
|
};
|
|
20004
|
+
/**
|
|
20005
|
+
* Sets the value of the input at the given path.
|
|
20006
|
+
*/
|
|
20150
20007
|
this.setInputValue = (path, newValue, cb) => {
|
|
20151
|
-
/* c8 ignore next */
|
|
20152
|
-
this.props.onChange({
|
|
20153
|
-
currentValue: newValue
|
|
20154
|
-
}, cb);
|
|
20155
|
-
};
|
|
20156
|
-
this.handleChange = (newValue, cb) => {
|
|
20157
20008
|
this.props.onChange({
|
|
20158
20009
|
currentValue: newValue
|
|
20159
20010
|
}, cb);
|
|
20160
|
-
this.props.trackInteraction();
|
|
20161
|
-
};
|
|
20162
|
-
this._handleFocus = () => {
|
|
20163
|
-
this.props.onFocus([]);
|
|
20164
|
-
this.setState(currentState => {
|
|
20165
|
-
return _extends({}, currentState, {
|
|
20166
|
-
isFocused: true
|
|
20167
|
-
});
|
|
20168
|
-
});
|
|
20169
|
-
};
|
|
20170
|
-
this._handleBlur = () => {
|
|
20171
|
-
this.props.onBlur([]);
|
|
20172
|
-
this.setState(currentState => {
|
|
20173
|
-
return _extends({}, currentState, {
|
|
20174
|
-
isFocused: false
|
|
20175
|
-
});
|
|
20176
|
-
});
|
|
20177
20011
|
};
|
|
20178
20012
|
}
|
|
20179
20013
|
static getUserInputFromProps(props) {
|
|
@@ -20182,97 +20016,26 @@ class NumericInput extends React.Component {
|
|
|
20182
20016
|
};
|
|
20183
20017
|
}
|
|
20184
20018
|
/**
|
|
20185
|
-
*
|
|
20186
|
-
* answer forms.
|
|
20019
|
+
* Returns the value the user has currently input for this widget.
|
|
20187
20020
|
*/
|
|
20188
|
-
examples() {
|
|
20189
|
-
var _this$props$answerFor3;
|
|
20190
|
-
// if the set of specified forms are empty, allow all forms
|
|
20191
|
-
const forms = ((_this$props$answerFor3 = this.props.answerForms) == null ? void 0 : _this$props$answerFor3.length) !== 0 ? this.props.answerForms : Object.keys(formExamples).map(name => {
|
|
20192
|
-
return {
|
|
20193
|
-
name: name,
|
|
20194
|
-
simplify: "required"
|
|
20195
|
-
};
|
|
20196
|
-
});
|
|
20197
|
-
let examples = _.map(forms, form => {
|
|
20198
|
-
return formExamples[form.name](form, this.context.strings);
|
|
20199
|
-
});
|
|
20200
|
-
// Ensure no duplicate tooltip text from simplified and unsimplified
|
|
20201
|
-
// versions of the same format
|
|
20202
|
-
examples = _.uniq(examples);
|
|
20203
|
-
return [this.context.strings.yourAnswer].concat(examples);
|
|
20204
|
-
}
|
|
20205
20021
|
getUserInput() {
|
|
20206
20022
|
return NumericInput.getUserInputFromProps(this.props);
|
|
20207
20023
|
}
|
|
20024
|
+
|
|
20025
|
+
/**
|
|
20026
|
+
* Returns the JSON representation of the prompt for this widget.
|
|
20027
|
+
* This is used by the AI to determine the prompt for the widget.
|
|
20028
|
+
*/
|
|
20208
20029
|
getPromptJSON() {
|
|
20209
20030
|
return getPromptJSON$r(this.props, this.getUserInput());
|
|
20210
20031
|
}
|
|
20211
20032
|
render() {
|
|
20212
|
-
|
|
20213
|
-
|
|
20214
|
-
|
|
20215
|
-
}
|
|
20216
|
-
|
|
20217
|
-
// To right align a custom keypad we need to wrap it.
|
|
20218
|
-
const maybeRightAlignKeypadInput = keypadInput => {
|
|
20219
|
-
return this.props.rightAlign ? /*#__PURE__*/React.createElement("div", {
|
|
20220
|
-
className: "perseus-input-right-align"
|
|
20221
|
-
}, keypadInput) : keypadInput;
|
|
20222
|
-
};
|
|
20223
|
-
if (this.props.apiOptions.customKeypad) {
|
|
20224
|
-
// TODO(charlie): Support "Review Mode".
|
|
20225
|
-
return maybeRightAlignKeypadInput( /*#__PURE__*/React.createElement(SimpleKeypadInput, {
|
|
20226
|
-
ref: ref => this.inputRef = ref,
|
|
20227
|
-
value: this.props.currentValue,
|
|
20228
|
-
keypadElement: this.props.keypadElement,
|
|
20229
|
-
onChange: this.handleChange,
|
|
20230
|
-
onFocus: this._handleFocus,
|
|
20231
|
-
onBlur: this._handleBlur
|
|
20232
|
-
}));
|
|
20233
|
-
}
|
|
20234
|
-
|
|
20235
|
-
// Note: This is _very_ similar to what `input-number.jsx` does. If
|
|
20236
|
-
// you modify this, double-check if you also need to modify that
|
|
20237
|
-
// component.
|
|
20238
|
-
const styles = StyleSheet.create({
|
|
20239
|
-
input: {
|
|
20240
|
-
borderRadius: "3px",
|
|
20241
|
-
borderWidth: this.state.isFocused ? "2px" : "1px",
|
|
20242
|
-
display: "inline-block",
|
|
20243
|
-
fontFamily: `Symbola, "Times New Roman", serif`,
|
|
20244
|
-
fontSize: "18px",
|
|
20245
|
-
height: "32px",
|
|
20246
|
-
lineHeight: "18px",
|
|
20247
|
-
padding: this.state.isFocused ? "4px" : "4px 5px",
|
|
20248
|
-
// account for added focus border thickness
|
|
20249
|
-
textAlign: this.props.rightAlign ? "right" : "left",
|
|
20250
|
-
width: this.props.size === "small" ? 40 : 80
|
|
20251
|
-
}
|
|
20252
|
-
});
|
|
20253
|
-
return /*#__PURE__*/React.createElement(InputWithExamples, {
|
|
20254
|
-
ref: ref => this.inputRef = ref,
|
|
20255
|
-
value: this.props.currentValue,
|
|
20256
|
-
onChange: this.handleChange,
|
|
20257
|
-
labelText: labelText,
|
|
20258
|
-
examples: this.examples(),
|
|
20259
|
-
shouldShowExamples: this.shouldShowExamples(),
|
|
20260
|
-
onFocus: this._handleFocus,
|
|
20261
|
-
onBlur: this._handleBlur,
|
|
20262
|
-
id: this.props.widgetId,
|
|
20263
|
-
disabled: this.props.apiOptions.readOnly,
|
|
20264
|
-
style: styles.input
|
|
20265
|
-
});
|
|
20033
|
+
return /*#__PURE__*/React.createElement(NumericInputComponent, _extends({}, this.props, {
|
|
20034
|
+
ref: this.inputRef
|
|
20035
|
+
}));
|
|
20266
20036
|
}
|
|
20267
20037
|
}
|
|
20268
|
-
|
|
20269
|
-
// TODO(thomas): Currently we receive a list of lists of acceptable answer types
|
|
20270
|
-
// and union them down into a single set. It's worth considering whether it
|
|
20271
|
-
// wouldn't make more sense to have a single set of acceptable answer types for
|
|
20272
|
-
// a given *problem* rather than for each possible [correct/wrong] *answer*.
|
|
20273
|
-
// When should two answers to a problem take different answer types?
|
|
20274
|
-
// See D27790 for more discussion.
|
|
20275
|
-
NumericInput.contextType = PerseusI18nContext;
|
|
20038
|
+
// Transforms the widget options to the props used to render the widget.
|
|
20276
20039
|
NumericInput.defaultProps = {
|
|
20277
20040
|
currentValue: "",
|
|
20278
20041
|
size: "normal",
|
|
@@ -20283,52 +20046,23 @@ NumericInput.defaultProps = {
|
|
|
20283
20046
|
labelText: "",
|
|
20284
20047
|
linterContext: linterContextDefault
|
|
20285
20048
|
};
|
|
20286
|
-
const unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
20287
|
-
// Takes a list of lists of answer forms, and returns a list of the forms
|
|
20288
|
-
// in each of these lists in the same order that they're listed in the
|
|
20289
|
-
// `formExamples` forms from above.
|
|
20290
|
-
|
|
20291
|
-
// uniqueBy takes a list of elements and a function which compares whether
|
|
20292
|
-
// two elements are equal, and returns a list of unique elements. This is
|
|
20293
|
-
// just a helper function here, but works generally.
|
|
20294
|
-
const uniqueBy = function uniqueBy(list, iteratee) {
|
|
20295
|
-
// @ts-expect-error - TS2347 - Untyped function calls may not accept type arguments.
|
|
20296
|
-
return list.reduce((uniqueList, element) => {
|
|
20297
|
-
// For each element, decide whether it's already in the list of
|
|
20298
|
-
// unique items.
|
|
20299
|
-
const inList = _.find(uniqueList, iteratee.bind(null, element));
|
|
20300
|
-
if (inList) {
|
|
20301
|
-
return uniqueList;
|
|
20302
|
-
}
|
|
20303
|
-
return uniqueList.concat([element]);
|
|
20304
|
-
}, []);
|
|
20305
|
-
};
|
|
20306
|
-
|
|
20307
|
-
// Pull out all of the forms from the different lists.
|
|
20308
|
-
const allForms = answerFormsList.flat();
|
|
20309
|
-
// Pull out the unique forms using uniqueBy.
|
|
20310
|
-
const uniqueForms = uniqueBy(allForms, _.isEqual);
|
|
20311
|
-
// Sort them by the order they appear in the `formExamples` list.
|
|
20312
|
-
const formExampleKeys = Object.keys(formExamples);
|
|
20313
|
-
return _.sortBy(uniqueForms, form => {
|
|
20314
|
-
return formExampleKeys.indexOf(form.name);
|
|
20315
|
-
});
|
|
20316
|
-
};
|
|
20317
20049
|
const propsTransform = function propsTransform(widgetOptions) {
|
|
20318
|
-
|
|
20319
|
-
|
|
20320
|
-
|
|
20321
|
-
|
|
20322
|
-
|
|
20323
|
-
//
|
|
20324
|
-
|
|
20325
|
-
return {
|
|
20326
|
-
|
|
20327
|
-
|
|
20328
|
-
|
|
20329
|
-
|
|
20330
|
-
|
|
20331
|
-
|
|
20050
|
+
// Omit the answers from the widget options since they are
|
|
20051
|
+
// not needed for rendering the widget.
|
|
20052
|
+
const _widgetOptions$answer = _extends({}, widgetOptions, {
|
|
20053
|
+
answerForms: unionAnswerForms(
|
|
20054
|
+
// Filter out the correct answers and map them to the answer forms
|
|
20055
|
+
// so that we can generate the examples for the widget.
|
|
20056
|
+
widgetOptions.answers.filter(answer => answer.status === "correct").map(answer => {
|
|
20057
|
+
return (answer.answerForms || []).map(form => {
|
|
20058
|
+
return {
|
|
20059
|
+
simplify: answer.simplify,
|
|
20060
|
+
name: form
|
|
20061
|
+
};
|
|
20062
|
+
});
|
|
20063
|
+
}))
|
|
20064
|
+
}),
|
|
20065
|
+
rendererProps = _objectWithoutPropertiesLoose(_widgetOptions$answer, _excluded$3);
|
|
20332
20066
|
return rendererProps;
|
|
20333
20067
|
};
|
|
20334
20068
|
var NumericInput$1 = {
|
|
@@ -20343,23 +20077,11 @@ var NumericInput$1 = {
|
|
|
20343
20077
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
20344
20078
|
const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
20345
20079
|
const answerStrings = correctAnswers.map(answer => {
|
|
20346
|
-
//
|
|
20347
|
-
|
|
20348
|
-
let format = "decimal";
|
|
20349
|
-
if (answer.answerForms && answer.answerForms[0]) {
|
|
20350
|
-
// NOTE(johnsullivan): This isn't exactly ideal, but
|
|
20351
|
-
// it does behave well for all the currently known
|
|
20352
|
-
// problems. See D14742 for some discussion on
|
|
20353
|
-
// alternate strategies.
|
|
20354
|
-
format = answer.answerForms[0];
|
|
20355
|
-
}
|
|
20356
|
-
|
|
20357
|
-
// @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'MathFormat | undefined'.
|
|
20080
|
+
// Either get the first answer form or default to decimal
|
|
20081
|
+
const format = answer.answerForms && answer.answerForms[0] ? answer.answerForms[0] : "decimal";
|
|
20358
20082
|
let answerString = KhanMath.toNumericString(answer.value, format);
|
|
20359
20083
|
if (answer.maxError) {
|
|
20360
|
-
answerString += " \u00B1 " +
|
|
20361
|
-
// @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'MathFormat | undefined'.
|
|
20362
|
-
KhanMath.toNumericString(answer.maxError, format);
|
|
20084
|
+
answerString += " \u00B1 " + KhanMath.toNumericString(answer.maxError, format);
|
|
20363
20085
|
}
|
|
20364
20086
|
return answerString;
|
|
20365
20087
|
});
|
|
@@ -21163,7 +20885,7 @@ class HighlightTooltip extends React.PureComponent {
|
|
|
21163
20885
|
style: style,
|
|
21164
20886
|
onMouseEnter: this.props.onMouseEnter,
|
|
21165
20887
|
onMouseLeave: this.props.onMouseLeave
|
|
21166
|
-
}, /*#__PURE__*/React.createElement(Tooltip
|
|
20888
|
+
}, /*#__PURE__*/React.createElement(Tooltip, {
|
|
21167
20889
|
content: content,
|
|
21168
20890
|
opened: true
|
|
21169
20891
|
}, /*#__PURE__*/React.createElement("div", null)))
|
|
@@ -22886,6 +22608,7 @@ const BASE_ICON_SIZE = 10;
|
|
|
22886
22608
|
// If you pass in an object you need to specify an SVG path to
|
|
22887
22609
|
// render along with the width and height of the path (to ensure
|
|
22888
22610
|
// that it gets scaled correctly).
|
|
22611
|
+
|
|
22889
22612
|
/**
|
|
22890
22613
|
* An SVG Icon
|
|
22891
22614
|
*
|
|
@@ -23971,7 +23694,6 @@ const styles$o = StyleSheet.create({
|
|
|
23971
23694
|
paddingLeft: phoneMargin
|
|
23972
23695
|
// paddingRight is handled by responsiveFieldset
|
|
23973
23696
|
},
|
|
23974
|
-
|
|
23975
23697
|
responsiveFieldset: {
|
|
23976
23698
|
paddingRight: phoneMargin,
|
|
23977
23699
|
minWidth: "auto"
|
|
@@ -24377,7 +24099,7 @@ const _choiceTransform = (widgetOptions, strings, problemNum) => {
|
|
|
24377
24099
|
// 1) we randomize the order first
|
|
24378
24100
|
_maybeRandomize(choices)));
|
|
24379
24101
|
};
|
|
24380
|
-
const transform = (widgetOptions, strings, problemNum) => {
|
|
24102
|
+
const transform$1 = (widgetOptions, strings, problemNum) => {
|
|
24381
24103
|
const choices = _choiceTransform(widgetOptions, strings, problemNum);
|
|
24382
24104
|
const numCorrect = _.reduce(widgetOptions.choices, function (memo, choice) {
|
|
24383
24105
|
return choice.correct ? memo + 1 : memo;
|
|
@@ -24403,8 +24125,8 @@ var Radio = {
|
|
|
24403
24125
|
displayName: "Radio / Multiple choice",
|
|
24404
24126
|
accessible: true,
|
|
24405
24127
|
widget: Radio$1,
|
|
24406
|
-
transform: transform,
|
|
24407
|
-
staticTransform: transform,
|
|
24128
|
+
transform: transform$1,
|
|
24129
|
+
staticTransform: transform$1,
|
|
24408
24130
|
version: radioLogic.version,
|
|
24409
24131
|
propUpgrades: radioLogic.widgetOptionsUpgrades,
|
|
24410
24132
|
isLintable: true
|
|
@@ -24975,6 +24697,7 @@ var Definition$1 = {
|
|
|
24975
24697
|
|
|
24976
24698
|
// There are no required props, but this component can receive props from any
|
|
24977
24699
|
// deprecated widget
|
|
24700
|
+
|
|
24978
24701
|
class DeprecatedStandin extends React__default.Component {
|
|
24979
24702
|
constructor(...args) {
|
|
24980
24703
|
super(...args);
|
|
@@ -25101,20 +24824,20 @@ Dropdown.defaultProps = {
|
|
|
25101
24824
|
placeholder: "",
|
|
25102
24825
|
apiOptions: ApiOptions.defaults
|
|
25103
24826
|
};
|
|
25104
|
-
|
|
24827
|
+
function transform(widgetOptions) {
|
|
25105
24828
|
return {
|
|
25106
24829
|
placeholder: widgetOptions.placeholder,
|
|
25107
24830
|
visibleLabel: widgetOptions.visibleLabel,
|
|
25108
24831
|
ariaLabel: widgetOptions.ariaLabel,
|
|
25109
24832
|
choices: widgetOptions.choices.map(choice => choice.content)
|
|
25110
24833
|
};
|
|
25111
|
-
}
|
|
24834
|
+
}
|
|
25112
24835
|
var Dropdown$1 = {
|
|
25113
24836
|
name: "dropdown",
|
|
25114
24837
|
displayName: "Drop down",
|
|
25115
24838
|
accessible: true,
|
|
25116
24839
|
widget: Dropdown,
|
|
25117
|
-
transform
|
|
24840
|
+
transform
|
|
25118
24841
|
};
|
|
25119
24842
|
|
|
25120
24843
|
const getPromptJSON$j = renderProps => {
|
|
@@ -25178,7 +24901,6 @@ class Explanation extends React.Component {
|
|
|
25178
24901
|
// adds space between the right border and any text that follows
|
|
25179
24902
|
paddingLeft: "2px" // adds space between the left border and the button text
|
|
25180
24903
|
};
|
|
25181
|
-
|
|
25182
24904
|
const labelStyle = {
|
|
25183
24905
|
fontSize: "18px",
|
|
25184
24906
|
lineHeight: "inherit",
|
|
@@ -27502,7 +27224,6 @@ class Interaction extends React.Component {
|
|
|
27502
27224
|
}
|
|
27503
27225
|
});
|
|
27504
27226
|
}
|
|
27505
|
-
|
|
27506
27227
|
if (element.type === "parametric") {
|
|
27507
27228
|
const fn = t => {
|
|
27508
27229
|
return [
|
|
@@ -27555,7 +27276,6 @@ class Interaction extends React.Component {
|
|
|
27555
27276
|
}
|
|
27556
27277
|
});
|
|
27557
27278
|
}
|
|
27558
|
-
|
|
27559
27279
|
if (element.type === "label") {
|
|
27560
27280
|
const coord = [
|
|
27561
27281
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
@@ -27900,7 +27620,6 @@ function scaleCommandBy(scaleFactor) {
|
|
|
27900
27620
|
command.args[6] * scaleFactor // end y
|
|
27901
27621
|
]
|
|
27902
27622
|
});
|
|
27903
|
-
|
|
27904
27623
|
default:
|
|
27905
27624
|
return _extends({}, command, {
|
|
27906
27625
|
args: command.args.map(c => c * scaleFactor)
|
|
@@ -28642,7 +28361,6 @@ const styles$f = StyleSheet.create({
|
|
|
28642
28361
|
userSelect: "none",
|
|
28643
28362
|
borderWidth: 0 // <button> gives us a border by default
|
|
28644
28363
|
},
|
|
28645
|
-
|
|
28646
28364
|
// We want to render the element in-place in our fixtures, so allow us to
|
|
28647
28365
|
// render in the bottom corner in production and in-place in testing.
|
|
28648
28366
|
hudFixedPosition: {
|
|
@@ -28672,7 +28390,6 @@ const styles$f = StyleSheet.create({
|
|
|
28672
28390
|
":hover": {
|
|
28673
28391
|
backgroundColor: "#a1a5a9" // in between those two grays
|
|
28674
28392
|
},
|
|
28675
|
-
|
|
28676
28393
|
":active": {
|
|
28677
28394
|
backgroundColor: gray68
|
|
28678
28395
|
}
|
|
@@ -28680,7 +28397,7 @@ const styles$f = StyleSheet.create({
|
|
|
28680
28397
|
});
|
|
28681
28398
|
|
|
28682
28399
|
const InfoTipBase = props => {
|
|
28683
|
-
return /*#__PURE__*/React.createElement(Tooltip
|
|
28400
|
+
return /*#__PURE__*/React.createElement(Tooltip, {
|
|
28684
28401
|
content: /*#__PURE__*/React.createElement(Body, {
|
|
28685
28402
|
style: styles$e.body
|
|
28686
28403
|
}, props.children),
|
|
@@ -28720,7 +28437,6 @@ class InfoTip extends React.Component {
|
|
|
28720
28437
|
});
|
|
28721
28438
|
/* eslint-enable react/no-did-mount-set-state */
|
|
28722
28439
|
}
|
|
28723
|
-
|
|
28724
28440
|
render() {
|
|
28725
28441
|
if (this.state.didMount) {
|
|
28726
28442
|
return /*#__PURE__*/React.createElement(InfoTipBase, this.props);
|
|
@@ -28952,7 +28668,6 @@ class NumberInput extends React.Component {
|
|
|
28952
28668
|
if (val !== Math.floor(val)) {
|
|
28953
28669
|
return; // bail if not an integer
|
|
28954
28670
|
}
|
|
28955
|
-
|
|
28956
28671
|
if (e.key === "ArrowUp") {
|
|
28957
28672
|
val = val + 1;
|
|
28958
28673
|
} else if (e.key === "ArrowDown") {
|
|
@@ -29086,20 +28801,20 @@ RangeInput.defaultProps = {
|
|
|
29086
28801
|
};
|
|
29087
28802
|
|
|
29088
28803
|
var components = /*#__PURE__*/Object.freeze({
|
|
29089
|
-
|
|
29090
|
-
|
|
29091
|
-
|
|
29092
|
-
|
|
29093
|
-
|
|
29094
|
-
|
|
29095
|
-
|
|
29096
|
-
|
|
29097
|
-
|
|
29098
|
-
|
|
29099
|
-
|
|
29100
|
-
|
|
29101
|
-
|
|
29102
|
-
|
|
28804
|
+
__proto__: null,
|
|
28805
|
+
ButtonGroup: ButtonGroup,
|
|
28806
|
+
Graphie: Graphie,
|
|
28807
|
+
HUD: HUD,
|
|
28808
|
+
Icon: Icon,
|
|
28809
|
+
InfoTip: InfoTip,
|
|
28810
|
+
InlineIcon: InlineIcon,
|
|
28811
|
+
MathInput: MathInput,
|
|
28812
|
+
MultiButtonGroup: MultiButtonGroup,
|
|
28813
|
+
NumberInput: NumberInput,
|
|
28814
|
+
RangeInput: RangeInput,
|
|
28815
|
+
SvgImage: SvgImage,
|
|
28816
|
+
TextInput: TextInput,
|
|
28817
|
+
TextListEditor: TextListEditor
|
|
29103
28818
|
});
|
|
29104
28819
|
|
|
29105
28820
|
/**
|
|
@@ -29228,9 +28943,9 @@ const LockedFunction = props => {
|
|
|
29228
28943
|
const {
|
|
29229
28944
|
color,
|
|
29230
28945
|
strokeStyle,
|
|
29231
|
-
directionalAxis
|
|
28946
|
+
directionalAxis,
|
|
28947
|
+
domain
|
|
29232
28948
|
} = props;
|
|
29233
|
-
const domain = props.domain ? [Number.isFinite(props.domain[0]) ? props.domain[0] : -Infinity, Number.isFinite(props.domain[1]) ? props.domain[1] : Infinity] : undefined;
|
|
29234
28949
|
const plotProps = {
|
|
29235
28950
|
color: lockedFigureColors[color],
|
|
29236
28951
|
style: strokeStyle,
|
|
@@ -30642,7 +30357,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
30642
30357
|
}));
|
|
30643
30358
|
return /*#__PURE__*/React.createElement(React.Fragment, null, showHairlines && /*#__PURE__*/React.createElement(Hairlines, {
|
|
30644
30359
|
point: point
|
|
30645
|
-
}), showTooltips ? /*#__PURE__*/React.createElement(Tooltip
|
|
30360
|
+
}), showTooltips ? /*#__PURE__*/React.createElement(Tooltip, {
|
|
30646
30361
|
autoUpdate: true,
|
|
30647
30362
|
backgroundColor: wbColorName,
|
|
30648
30363
|
content: `(${point[X]}, ${point[Y]})`,
|
|
@@ -30775,6 +30490,9 @@ const MovableLine = props => {
|
|
|
30775
30490
|
onMoveLine = () => {},
|
|
30776
30491
|
onMovePoint = () => {}
|
|
30777
30492
|
} = props;
|
|
30493
|
+
const {
|
|
30494
|
+
snapStep
|
|
30495
|
+
} = useGraphConfig();
|
|
30778
30496
|
|
|
30779
30497
|
// Aria live states for (0) point 1, (1) point 2, and (2) grab handle.
|
|
30780
30498
|
// When moving an element, set its aria live to "polite" and the others
|
|
@@ -30806,7 +30524,8 @@ const MovableLine = props => {
|
|
|
30806
30524
|
onMove: p => {
|
|
30807
30525
|
setAriaLives(["polite", "off", "off"]);
|
|
30808
30526
|
onMovePoint(0, p);
|
|
30809
|
-
}
|
|
30527
|
+
},
|
|
30528
|
+
constrain: getMovableLineKeyboardConstraint([start, end], snapStep, 0)
|
|
30810
30529
|
});
|
|
30811
30530
|
const {
|
|
30812
30531
|
visiblePoint: visiblePoint2,
|
|
@@ -30821,7 +30540,8 @@ const MovableLine = props => {
|
|
|
30821
30540
|
onMove: p => {
|
|
30822
30541
|
setAriaLives(["off", "polite", "off"]);
|
|
30823
30542
|
onMovePoint(1, p);
|
|
30824
|
-
}
|
|
30543
|
+
},
|
|
30544
|
+
constrain: getMovableLineKeyboardConstraint([start, end], snapStep, 1)
|
|
30825
30545
|
});
|
|
30826
30546
|
const line = /*#__PURE__*/React.createElement(Line$1, {
|
|
30827
30547
|
ariaLabel: ariaLabels == null ? void 0 : ariaLabels.grabHandleAriaLabel,
|
|
@@ -30927,6 +30647,34 @@ const Line$1 = props => {
|
|
|
30927
30647
|
color: stroke
|
|
30928
30648
|
}));
|
|
30929
30649
|
};
|
|
30650
|
+
const getMovableLineKeyboardConstraint = (line, snapStep, pointIndex) => {
|
|
30651
|
+
// Separate the two points into their own variables, and determine which point is being moved
|
|
30652
|
+
const coordToBeMoved = line[pointIndex];
|
|
30653
|
+
const otherPoint = line[1 - pointIndex];
|
|
30654
|
+
|
|
30655
|
+
// Create a helper function that moves the point and then checks
|
|
30656
|
+
// if it overlaps with the other point in the line after the move.
|
|
30657
|
+
const movePointWithConstraint = moveFunc => {
|
|
30658
|
+
// Move the point
|
|
30659
|
+
let movedCoord = moveFunc(coordToBeMoved);
|
|
30660
|
+
// If the moved point overlaps with the other point in the line,
|
|
30661
|
+
// move the point again.
|
|
30662
|
+
if (vec.dist(movedCoord, otherPoint) === 0) {
|
|
30663
|
+
movedCoord = moveFunc(movedCoord);
|
|
30664
|
+
}
|
|
30665
|
+
return movedCoord;
|
|
30666
|
+
};
|
|
30667
|
+
|
|
30668
|
+
// Check if the new point will overlap the other point.
|
|
30669
|
+
// If it will, we need to snap the point to the left or right an additional
|
|
30670
|
+
// snapStep to avoid overlap.
|
|
30671
|
+
return {
|
|
30672
|
+
up: movePointWithConstraint(coord => vec.add(coord, [0, snapStep[1]])),
|
|
30673
|
+
down: movePointWithConstraint(coord => vec.sub(coord, [0, snapStep[1]])),
|
|
30674
|
+
left: movePointWithConstraint(coord => vec.sub(coord, [snapStep[0], 0])),
|
|
30675
|
+
right: movePointWithConstraint(coord => vec.add(coord, [snapStep[0], 0]))
|
|
30676
|
+
};
|
|
30677
|
+
};
|
|
30930
30678
|
function trimRange(range, graphDimensionsInPixels) {
|
|
30931
30679
|
const pixelsToTrim = 4;
|
|
30932
30680
|
const [xRange, yRange] = range;
|
|
@@ -31268,7 +31016,8 @@ function CircleGraph(props) {
|
|
|
31268
31016
|
} = props;
|
|
31269
31017
|
const {
|
|
31270
31018
|
center,
|
|
31271
|
-
radiusPoint
|
|
31019
|
+
radiusPoint,
|
|
31020
|
+
snapStep
|
|
31272
31021
|
} = graphState;
|
|
31273
31022
|
const {
|
|
31274
31023
|
strings,
|
|
@@ -31331,7 +31080,8 @@ function CircleGraph(props) {
|
|
|
31331
31080
|
onMove: newRadiusPoint => {
|
|
31332
31081
|
setRadiusPointAriaLive("polite");
|
|
31333
31082
|
dispatch(actions.circle.moveRadiusPoint(newRadiusPoint));
|
|
31334
|
-
}
|
|
31083
|
+
},
|
|
31084
|
+
constrain: getCircleKeyboardConstraint(center, radiusPoint, snapStep)
|
|
31335
31085
|
}), /*#__PURE__*/React.createElement("g", {
|
|
31336
31086
|
id: radiusId,
|
|
31337
31087
|
style: a11y.srOnly
|
|
@@ -31442,6 +31192,30 @@ function getCircleGraphDescription(state, i18n) {
|
|
|
31442
31192
|
const strings = describeCircleGraph(state, i18n);
|
|
31443
31193
|
return strings.srCircleInteractiveElement;
|
|
31444
31194
|
}
|
|
31195
|
+
const getCircleKeyboardConstraint = (center, radiusPoint, snapStep) => {
|
|
31196
|
+
// Create a helper function that moves the point and then checks
|
|
31197
|
+
// if it overlaps with the center point after the move.
|
|
31198
|
+
const movePointWithConstraint = moveFunc => {
|
|
31199
|
+
// Move the point
|
|
31200
|
+
let movedCoord = moveFunc(radiusPoint);
|
|
31201
|
+
// If the moved point overlaps with the center point,
|
|
31202
|
+
// move the point again.
|
|
31203
|
+
if (vec.dist(movedCoord, center) === 0) {
|
|
31204
|
+
movedCoord = moveFunc(movedCoord);
|
|
31205
|
+
}
|
|
31206
|
+
return movedCoord;
|
|
31207
|
+
};
|
|
31208
|
+
|
|
31209
|
+
// Check if the new point overlaps the center point.
|
|
31210
|
+
// If it does, we need to snap the point to the left
|
|
31211
|
+
// or right an additional snapStep to avoid the overlap.
|
|
31212
|
+
return {
|
|
31213
|
+
up: movePointWithConstraint(coord => vec.add(coord, [0, snapStep[1]])),
|
|
31214
|
+
down: movePointWithConstraint(coord => vec.sub(coord, [0, snapStep[1]])),
|
|
31215
|
+
left: movePointWithConstraint(coord => vec.sub(coord, [snapStep[0], 0])),
|
|
31216
|
+
right: movePointWithConstraint(coord => vec.add(coord, [snapStep[0], 0]))
|
|
31217
|
+
};
|
|
31218
|
+
};
|
|
31445
31219
|
|
|
31446
31220
|
// Exported for testing
|
|
31447
31221
|
function describeCircleGraph(state, i18n) {
|
|
@@ -31454,6 +31228,7 @@ function describeCircleGraph(state, i18n) {
|
|
|
31454
31228
|
radiusPoint
|
|
31455
31229
|
} = state;
|
|
31456
31230
|
const radius = getRadius(state);
|
|
31231
|
+
const isRadiusOnRight = radiusPoint[X] >= center[X];
|
|
31457
31232
|
|
|
31458
31233
|
// Aria label strings
|
|
31459
31234
|
const srCircleGraph = strings.srCircleGraph;
|
|
@@ -31461,7 +31236,10 @@ function describeCircleGraph(state, i18n) {
|
|
|
31461
31236
|
centerX: srFormatNumber(center[0], locale),
|
|
31462
31237
|
centerY: srFormatNumber(center[1], locale)
|
|
31463
31238
|
});
|
|
31464
|
-
const srCircleRadiusPoint = strings.
|
|
31239
|
+
const srCircleRadiusPoint = isRadiusOnRight ? strings.srCircleRadiusPointRight({
|
|
31240
|
+
radiusPointX: srFormatNumber(radiusPoint[0], locale),
|
|
31241
|
+
radiusPointY: srFormatNumber(radiusPoint[1], locale)
|
|
31242
|
+
}) : strings.srCircleRadiusPointLeft({
|
|
31465
31243
|
radiusPointX: srFormatNumber(radiusPoint[0], locale),
|
|
31466
31244
|
radiusPointY: srFormatNumber(radiusPoint[1], locale)
|
|
31467
31245
|
});
|
|
@@ -31627,6 +31405,12 @@ const LinearSystemGraph = props => {
|
|
|
31627
31405
|
locale
|
|
31628
31406
|
} = usePerseusI18n();
|
|
31629
31407
|
const id = React.useId();
|
|
31408
|
+
const intersectionId = `${id}-intersection`;
|
|
31409
|
+
const intersectionPoint = geometry.getLineIntersection(lines[0], lines[1]);
|
|
31410
|
+
const intersectionDescription = intersectionPoint ? strings.srLinearSystemIntersection({
|
|
31411
|
+
x: srFormatNumber(intersectionPoint[0], locale),
|
|
31412
|
+
y: srFormatNumber(intersectionPoint[1], locale)
|
|
31413
|
+
}) : strings.srLinearSystemParallel;
|
|
31630
31414
|
const linesAriaInfo = lines.map((line, i) => {
|
|
31631
31415
|
return {
|
|
31632
31416
|
pointsDescriptionId: `${id}-line${i + 1}-points`,
|
|
@@ -31643,13 +31427,14 @@ const LinearSystemGraph = props => {
|
|
|
31643
31427
|
slopeDescription: getSlopeStringForLine(line, strings)
|
|
31644
31428
|
};
|
|
31645
31429
|
});
|
|
31430
|
+
const individualLineDescriptions = linesAriaInfo.map(({
|
|
31431
|
+
pointsDescriptionId,
|
|
31432
|
+
interceptDescriptionId,
|
|
31433
|
+
slopeDescriptionId
|
|
31434
|
+
}) => `${pointsDescriptionId} ${interceptDescriptionId} ${slopeDescriptionId}`).join(" ");
|
|
31646
31435
|
return /*#__PURE__*/React.createElement("g", {
|
|
31647
31436
|
"aria-label": strings.srLinearSystemGraph,
|
|
31648
|
-
"aria-describedby":
|
|
31649
|
-
pointsDescriptionId,
|
|
31650
|
-
interceptDescriptionId,
|
|
31651
|
-
slopeDescriptionId
|
|
31652
|
-
}) => `${pointsDescriptionId} ${interceptDescriptionId} ${slopeDescriptionId}`).join(" ")
|
|
31437
|
+
"aria-describedby": `${individualLineDescriptions} ${intersectionId}`
|
|
31653
31438
|
}, lines == null ? void 0 : lines.map((line, i) => /*#__PURE__*/React.createElement(MovableLine, {
|
|
31654
31439
|
key: i,
|
|
31655
31440
|
points: line,
|
|
@@ -31666,9 +31451,15 @@ const LinearSystemGraph = props => {
|
|
|
31666
31451
|
x: srFormatNumber(line[1][0], locale),
|
|
31667
31452
|
y: srFormatNumber(line[1][1], locale)
|
|
31668
31453
|
}),
|
|
31669
|
-
grabHandleAriaLabel:
|
|
31454
|
+
grabHandleAriaLabel: strings.srLinearSystemGrabHandle({
|
|
31455
|
+
lineNumber: i + 1,
|
|
31456
|
+
point1X: srFormatNumber(line[0][0], locale),
|
|
31457
|
+
point1Y: srFormatNumber(line[0][1], locale),
|
|
31458
|
+
point2X: srFormatNumber(line[1][0], locale),
|
|
31459
|
+
point2Y: srFormatNumber(line[1][1], locale)
|
|
31460
|
+
})
|
|
31670
31461
|
},
|
|
31671
|
-
ariaDescribedBy: `${linesAriaInfo[i].interceptDescriptionId} ${linesAriaInfo[i].slopeDescriptionId}`,
|
|
31462
|
+
ariaDescribedBy: `${linesAriaInfo[i].interceptDescriptionId} ${linesAriaInfo[i].slopeDescriptionId} ${intersectionId}`,
|
|
31672
31463
|
onMoveLine: delta => {
|
|
31673
31464
|
dispatch(actions.linearSystem.moveLine(i, delta));
|
|
31674
31465
|
},
|
|
@@ -31697,7 +31488,10 @@ const LinearSystemGraph = props => {
|
|
|
31697
31488
|
key: slopeDescriptionId,
|
|
31698
31489
|
id: slopeDescriptionId,
|
|
31699
31490
|
style: a11y.srOnly
|
|
31700
|
-
}, slopeDescription)))
|
|
31491
|
+
}, slopeDescription))), /*#__PURE__*/React.createElement("g", {
|
|
31492
|
+
id: intersectionId,
|
|
31493
|
+
style: a11y.srOnly
|
|
31494
|
+
}, intersectionDescription));
|
|
31701
31495
|
};
|
|
31702
31496
|
|
|
31703
31497
|
// Exported for testing
|
|
@@ -32358,7 +32152,8 @@ function QuadraticGraph(props) {
|
|
|
32358
32152
|
graphState
|
|
32359
32153
|
} = props;
|
|
32360
32154
|
const {
|
|
32361
|
-
coords
|
|
32155
|
+
coords,
|
|
32156
|
+
snapStep
|
|
32362
32157
|
} = graphState;
|
|
32363
32158
|
const {
|
|
32364
32159
|
strings,
|
|
@@ -32411,6 +32206,7 @@ function QuadraticGraph(props) {
|
|
|
32411
32206
|
ariaLabel: `${srQuadraticPoint}${srVertex}`,
|
|
32412
32207
|
point: coord,
|
|
32413
32208
|
sequenceNumber: i + 1,
|
|
32209
|
+
constrain: getQuadraticKeyboardConstraint(coords, snapStep, i),
|
|
32414
32210
|
onMove: destination => dispatch(actions.quadratic.movePoint(i, destination))
|
|
32415
32211
|
});
|
|
32416
32212
|
}), srQuadraticDirection && /*#__PURE__*/React.createElement("g", {
|
|
@@ -32499,6 +32295,60 @@ function describeQuadraticGraph(state, i18n) {
|
|
|
32499
32295
|
srQuadraticInteractiveElements
|
|
32500
32296
|
};
|
|
32501
32297
|
}
|
|
32298
|
+
const getQuadraticKeyboardConstraint = (coords, snapStep, pointMoved) => {
|
|
32299
|
+
// Make newCoords mutable
|
|
32300
|
+
const newCoords = [coords[0], coords[1], coords[2]];
|
|
32301
|
+
|
|
32302
|
+
// Get the point that is being moved
|
|
32303
|
+
const coordToBeMoved = newCoords[pointMoved];
|
|
32304
|
+
|
|
32305
|
+
// Create a function to validate and adjust the movement of the point
|
|
32306
|
+
const movePointWithConstraint = moveFunc => {
|
|
32307
|
+
// Move the point the desired direction
|
|
32308
|
+
let movedCoord = moveFunc(coordToBeMoved);
|
|
32309
|
+
newCoords[pointMoved] = movedCoord;
|
|
32310
|
+
|
|
32311
|
+
// If these new coordinates are valid, return the moved coord
|
|
32312
|
+
if (areCoordsValid(newCoords)) {
|
|
32313
|
+
return movedCoord;
|
|
32314
|
+
}
|
|
32315
|
+
|
|
32316
|
+
// If the new coordinates are invalid, we need to move the point an additional
|
|
32317
|
+
// snapStep to avoid creating an invalid quadratic graph.
|
|
32318
|
+
movedCoord = moveFunc(movedCoord);
|
|
32319
|
+
newCoords[pointMoved] = movedCoord;
|
|
32320
|
+
|
|
32321
|
+
// If these updated coordinates are valid now, we can return the new coordinates.
|
|
32322
|
+
if (areCoordsValid(newCoords)) {
|
|
32323
|
+
return movedCoord;
|
|
32324
|
+
}
|
|
32325
|
+
|
|
32326
|
+
// Otherwise, if the new coordinates are still invalid, we need to move the point one final snapStep.
|
|
32327
|
+
// This is to support the edge case where all of the points are each exactly one snapStep away
|
|
32328
|
+
// from each other.
|
|
32329
|
+
// Eg. When coords = [[0, 0], [1, 0], [2, 0]], snapStep = [1, 1], and we're moving coords[0] to the right.
|
|
32330
|
+
return moveFunc(movedCoord);
|
|
32331
|
+
};
|
|
32332
|
+
return {
|
|
32333
|
+
up: vec.add(coordToBeMoved, [0, snapStep[1]]),
|
|
32334
|
+
down: vec.sub(coordToBeMoved, [0, snapStep[1]]),
|
|
32335
|
+
// For horizontal movement, we need to ensure that the points are not on the same vertical line.
|
|
32336
|
+
left: movePointWithConstraint(coord => vec.sub(coord, [snapStep[0], 0])),
|
|
32337
|
+
right: movePointWithConstraint(coord => vec.add(coord, [snapStep[0], 0]))
|
|
32338
|
+
};
|
|
32339
|
+
};
|
|
32340
|
+
const areCoordsValid = coords => {
|
|
32341
|
+
const p1 = coords[0];
|
|
32342
|
+
const p2 = coords[1];
|
|
32343
|
+
const p3 = coords[2];
|
|
32344
|
+
|
|
32345
|
+
// If any of the points share the same x-coordinate,
|
|
32346
|
+
// we are unable to calculate the coefficients, and the graph is invalid.
|
|
32347
|
+
if (p1[0] === p2[0] || p2[0] === p3[0] || p1[0] === p3[0]) {
|
|
32348
|
+
return false;
|
|
32349
|
+
}
|
|
32350
|
+
return true;
|
|
32351
|
+
};
|
|
32502
32352
|
|
|
32503
32353
|
function renderRayGraph(state, dispatch, i18n) {
|
|
32504
32354
|
return {
|
|
@@ -32749,7 +32599,8 @@ function SinusoidGraph(props) {
|
|
|
32749
32599
|
// Note: The order of the coordinates is important:
|
|
32750
32600
|
// The coords[0] is the root and the coords[1] is the first peak
|
|
32751
32601
|
const {
|
|
32752
|
-
coords
|
|
32602
|
+
coords,
|
|
32603
|
+
snapStep
|
|
32753
32604
|
} = graphState;
|
|
32754
32605
|
|
|
32755
32606
|
// The coefficients are used to calculate the sinusoid equation, plot the graph, and to indicate
|
|
@@ -32788,11 +32639,37 @@ function SinusoidGraph(props) {
|
|
|
32788
32639
|
key: "point-" + i,
|
|
32789
32640
|
point: coord,
|
|
32790
32641
|
sequenceNumber: i + 1,
|
|
32642
|
+
constrain: getSinusoidKeyboardConstraint(coords, snapStep, i),
|
|
32791
32643
|
onMove: destination => dispatch(actions.sinusoid.movePoint(i, destination))
|
|
32792
32644
|
})), /*#__PURE__*/React.createElement("g", {
|
|
32793
32645
|
id: descriptionId
|
|
32794
32646
|
}, srSinusoidDescription));
|
|
32795
32647
|
}
|
|
32648
|
+
const getSinusoidKeyboardConstraint = (coords, snapStep, pointIndex) => {
|
|
32649
|
+
// Separate the two points into their own variables, and determine which point is being moved
|
|
32650
|
+
const coordToBeMoved = coords[pointIndex];
|
|
32651
|
+
const otherPoint = coords[1 - pointIndex];
|
|
32652
|
+
|
|
32653
|
+
// Create a helper function that checks if the new point is on the same
|
|
32654
|
+
// vertical line as the other point. If it is, we need to move the point
|
|
32655
|
+
// an additional snapStep.
|
|
32656
|
+
const movePointWithConstraint = moveFunc => {
|
|
32657
|
+
// Move the point
|
|
32658
|
+
let movedCoord = moveFunc(coordToBeMoved);
|
|
32659
|
+
// If the moved point overlaps with the other point in the line,
|
|
32660
|
+
// move the point again.
|
|
32661
|
+
if (movedCoord[X] === otherPoint[X]) {
|
|
32662
|
+
movedCoord = moveFunc(movedCoord);
|
|
32663
|
+
}
|
|
32664
|
+
return movedCoord;
|
|
32665
|
+
};
|
|
32666
|
+
return {
|
|
32667
|
+
up: movePointWithConstraint(coord => vec.add(coord, [0, snapStep[1]])),
|
|
32668
|
+
down: movePointWithConstraint(coord => vec.sub(coord, [0, snapStep[1]])),
|
|
32669
|
+
left: movePointWithConstraint(coord => vec.sub(coord, [snapStep[0], 0])),
|
|
32670
|
+
right: movePointWithConstraint(coord => vec.add(coord, [snapStep[0], 0]))
|
|
32671
|
+
};
|
|
32672
|
+
};
|
|
32796
32673
|
|
|
32797
32674
|
// Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
|
|
32798
32675
|
const computeSine = function computeSine(x,
|
|
@@ -32844,6 +32721,14 @@ function describeSinusoidGraph(state, i18n) {
|
|
|
32844
32721
|
const [root, peak] = coords;
|
|
32845
32722
|
const diffX = Math.abs(peak[X] - root[X]);
|
|
32846
32723
|
const diffY = Math.abs(peak[Y] - root[Y]);
|
|
32724
|
+
const formattedRoot = {
|
|
32725
|
+
x: srFormatNumber(root[X], locale),
|
|
32726
|
+
y: srFormatNumber(root[Y], locale)
|
|
32727
|
+
};
|
|
32728
|
+
const formattedPeak = {
|
|
32729
|
+
x: srFormatNumber(peak[X], locale),
|
|
32730
|
+
y: srFormatNumber(peak[Y], locale)
|
|
32731
|
+
};
|
|
32847
32732
|
const srSinusoidGraph = strings.srSinusoidGraph;
|
|
32848
32733
|
const srSinusoidDescription = strings.srSinusoidDescription({
|
|
32849
32734
|
minValue: srFormatNumber(root[Y] - diffY, locale),
|
|
@@ -32851,14 +32736,8 @@ function describeSinusoidGraph(state, i18n) {
|
|
|
32851
32736
|
cycleStart: srFormatNumber(root[X] - 2 * diffX, locale),
|
|
32852
32737
|
cycleEnd: srFormatNumber(root[X] + 2 * diffX, locale)
|
|
32853
32738
|
});
|
|
32854
|
-
const srSinusoidRootPoint = strings.srSinusoidRootPoint(
|
|
32855
|
-
|
|
32856
|
-
y: srFormatNumber(root[Y], locale)
|
|
32857
|
-
});
|
|
32858
|
-
const srSinusoidPeakPoint = strings.srSinusoidPeakPoint({
|
|
32859
|
-
x: srFormatNumber(peak[X], locale),
|
|
32860
|
-
y: srFormatNumber(peak[Y], locale)
|
|
32861
|
-
});
|
|
32739
|
+
const srSinusoidRootPoint = strings.srSinusoidRootPoint(formattedRoot);
|
|
32740
|
+
const srSinusoidPeakPoint = peak[Y] === root[Y] ? strings.srSinusoidFlatPoint(formattedPeak) : peak[Y] > root[Y] ? strings.srSinusoidMaxPoint(formattedPeak) : strings.srSinusoidMinPoint(formattedPeak);
|
|
32862
32741
|
const srSinusoidInteractiveElements = strings.srInteractiveElements({
|
|
32863
32742
|
elements: strings.srSinusoidInteractiveElements({
|
|
32864
32743
|
point1X: srFormatNumber(root[X], locale),
|
|
@@ -32880,16 +32759,17 @@ const {
|
|
|
32880
32759
|
calculateAngleInDegrees,
|
|
32881
32760
|
convertDegreesToRadians
|
|
32882
32761
|
} = angles;
|
|
32883
|
-
const protractorImage = "https://
|
|
32762
|
+
const protractorImage = "https://cdn.kastatic.org/images/perseus/protractor.svg";
|
|
32884
32763
|
|
|
32885
32764
|
// The vector from the center of the protractor to the top left corner of the
|
|
32886
32765
|
// protractor image, in pixels. Used for positioning.
|
|
32887
|
-
const centerToTopLeft = [-
|
|
32766
|
+
const centerToTopLeft = [-195, -190];
|
|
32888
32767
|
|
|
32889
32768
|
// The vector from the center of the protractor to the center of the rotation
|
|
32890
32769
|
// handle.
|
|
32891
|
-
const centerToRotationHandle = [-
|
|
32770
|
+
const centerToRotationHandle = [-201, -15];
|
|
32892
32771
|
function Protractor() {
|
|
32772
|
+
const staticUrl = getDependencies().staticUrl;
|
|
32893
32773
|
const {
|
|
32894
32774
|
range,
|
|
32895
32775
|
snapStep
|
|
@@ -32901,7 +32781,9 @@ function Protractor() {
|
|
|
32901
32781
|
const [center, setCenter] = useState(initialCenter);
|
|
32902
32782
|
const [rotationHandleOffset, setRotationHandleOffset] = useState(centerToRotationHandle);
|
|
32903
32783
|
const draggableRef = useRef(null);
|
|
32904
|
-
|
|
32784
|
+
const {
|
|
32785
|
+
dragging
|
|
32786
|
+
} = useDraggable({
|
|
32905
32787
|
gestureTarget: draggableRef,
|
|
32906
32788
|
onMove: setCenter,
|
|
32907
32789
|
point: center,
|
|
@@ -32925,10 +32807,11 @@ function Protractor() {
|
|
|
32925
32807
|
ref: draggableRef,
|
|
32926
32808
|
transform: `translate(${topLeftPx[X]}, ${topLeftPx[Y]}), rotate(${angle})`,
|
|
32927
32809
|
style: {
|
|
32928
|
-
transformOrigin: `${-centerToTopLeft[X]}px ${-centerToTopLeft[Y]}px
|
|
32810
|
+
transformOrigin: `${-centerToTopLeft[X]}px ${-centerToTopLeft[Y]}px`,
|
|
32811
|
+
cursor: dragging ? "grabbing" : "grab"
|
|
32929
32812
|
}
|
|
32930
32813
|
}, /*#__PURE__*/React.createElement("image", {
|
|
32931
|
-
href: protractorImage
|
|
32814
|
+
href: staticUrl(protractorImage)
|
|
32932
32815
|
}), /*#__PURE__*/React.createElement("g", {
|
|
32933
32816
|
transform: `translate(5, ${-centerToTopLeft[1]})`,
|
|
32934
32817
|
ref: rotationHandleRef
|
|
@@ -34803,7 +34686,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
34803
34686
|
|
|
34804
34687
|
// If the graph is static, it always displays the correct answer. This is
|
|
34805
34688
|
// standard behavior for Perseus widgets (e.g. compare the Radio widget).
|
|
34806
|
-
if (props.static) {
|
|
34689
|
+
if (props.static && props.correct) {
|
|
34807
34690
|
return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, props, {
|
|
34808
34691
|
state: initializeGraphState(_extends({}, props, {
|
|
34809
34692
|
graph: props.correct
|
|
@@ -34822,7 +34705,7 @@ const {
|
|
|
34822
34705
|
} = angles;
|
|
34823
34706
|
const {
|
|
34824
34707
|
getLineEquation,
|
|
34825
|
-
|
|
34708
|
+
getLineIntersectionString,
|
|
34826
34709
|
magnitude,
|
|
34827
34710
|
vector
|
|
34828
34711
|
} = geometry;
|
|
@@ -35180,7 +35063,7 @@ class InteractiveGraph extends React.Component {
|
|
|
35180
35063
|
}
|
|
35181
35064
|
static getLinearSystemEquationString(props) {
|
|
35182
35065
|
const coords = InteractiveGraph.getLinearSystemCoords(props.graph, props);
|
|
35183
|
-
return "\n" + getLineEquation(coords[0][0], coords[0][1]) + "\n" + getLineEquation(coords[1][0], coords[1][1]) + "\n" +
|
|
35066
|
+
return "\n" + getLineEquation(coords[0][0], coords[0][1]) + "\n" + getLineEquation(coords[1][0], coords[1][1]) + "\n" + getLineIntersectionString(coords[0], coords[1]);
|
|
35184
35067
|
}
|
|
35185
35068
|
static getPointEquationString(props) {
|
|
35186
35069
|
if (props.graph.type !== "point") {
|
|
@@ -35628,7 +35511,6 @@ const styles$a = StyleSheet.create({
|
|
|
35628
35511
|
markerCorrect: {
|
|
35629
35512
|
background: "#00880b" // WB green darkened by 18%
|
|
35630
35513
|
},
|
|
35631
|
-
|
|
35632
35514
|
markerIncorrect: {
|
|
35633
35515
|
background: color.offBlack64
|
|
35634
35516
|
}
|
|
@@ -36021,7 +35903,6 @@ class LabelImage extends React.Component {
|
|
|
36021
35903
|
const adjustPillDistance = {
|
|
36022
35904
|
[`margin${markerPosition.charAt(0).toUpperCase() + markerPosition.slice(1)}`]: 10 // move pill further from marker
|
|
36023
35905
|
};
|
|
36024
|
-
|
|
36025
35906
|
const answerChoicesActive = index === activeMarkerIndex;
|
|
36026
35907
|
const showAnswerChoice = marker.selected && !answerChoicesActive && !this.state.hideAnswers;
|
|
36027
35908
|
return /*#__PURE__*/React.createElement(View, {
|
|
@@ -36466,7 +36347,6 @@ class Draggable extends React.Component {
|
|
|
36466
36347
|
if (this.props.width) {
|
|
36467
36348
|
style.width = this.props.width + 1; // Fix for non-integer widths
|
|
36468
36349
|
}
|
|
36469
|
-
|
|
36470
36350
|
if (this.props.height) {
|
|
36471
36351
|
style.height = this.props.height;
|
|
36472
36352
|
}
|
|
@@ -36808,8 +36688,7 @@ class Sortable extends React.Component {
|
|
|
36808
36688
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36809
36689
|
margin = "0 0 " + this.props.margin + "px 0"; // bottom
|
|
36810
36690
|
}
|
|
36811
|
-
|
|
36812
|
-
cards.push( /*#__PURE__*/React.createElement(Draggable, {
|
|
36691
|
+
cards.push(/*#__PURE__*/React.createElement(Draggable, {
|
|
36813
36692
|
content: item.option,
|
|
36814
36693
|
key: item.key,
|
|
36815
36694
|
state: item.state
|
|
@@ -36862,7 +36741,7 @@ class Sortable extends React.Component {
|
|
|
36862
36741
|
this, item.key)
|
|
36863
36742
|
}));
|
|
36864
36743
|
if (item.state === ItemState.DRAGGING || item.state === ItemState.ANIMATING) {
|
|
36865
|
-
cards.push(
|
|
36744
|
+
cards.push(/*#__PURE__*/React.createElement(Placeholder, {
|
|
36866
36745
|
key: "placeholder_" + item.key,
|
|
36867
36746
|
ref: "placeholder_" + item.key,
|
|
36868
36747
|
width: item.width,
|
|
@@ -38652,7 +38531,6 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
38652
38531
|
if (!_.isFinite(props.tickStep) || props.tickStep <= 0) {
|
|
38653
38532
|
return []; // this has screwed me for the last time!
|
|
38654
38533
|
}
|
|
38655
|
-
|
|
38656
38534
|
const results = [];
|
|
38657
38535
|
|
|
38658
38536
|
// For convenience, extract some props into separate variables
|
|
@@ -38723,6 +38601,7 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
38723
38601
|
|
|
38724
38602
|
// TODO: most widgets use some like Widget<Something, PerseusNumberLineWidgetOptions>
|
|
38725
38603
|
// should this one?
|
|
38604
|
+
|
|
38726
38605
|
class NumberLine extends React.Component {
|
|
38727
38606
|
constructor(..._args) {
|
|
38728
38607
|
super(..._args);
|
|
@@ -39088,7 +38967,6 @@ const numberLineTransform = editorProps => {
|
|
|
39088
38967
|
} else {
|
|
39089
38968
|
numDivisions = undefined; // send to getDefaultProps()
|
|
39090
38969
|
}
|
|
39091
|
-
|
|
39092
38970
|
_.extend(props, {
|
|
39093
38971
|
numLinePosition: numLinePosition,
|
|
39094
38972
|
numDivisions: numDivisions,
|
|
@@ -39113,7 +38991,6 @@ const staticTransform$1 = editorProps => {
|
|
|
39113
38991
|
} else {
|
|
39114
38992
|
numDivisions = undefined; // send to getDefaultProps()
|
|
39115
38993
|
}
|
|
39116
|
-
|
|
39117
38994
|
_.extend(props, {
|
|
39118
38995
|
numLinePosition: numLinePosition,
|
|
39119
38996
|
numDivisions: numDivisions,
|
|
@@ -40102,7 +39979,6 @@ class Plotter extends React.Component {
|
|
|
40102
39979
|
const initialY = isMobile ? c.scaleY : 0;
|
|
40103
39980
|
for (let y = initialY; y <= c.dimY; y += c.scaleY) {
|
|
40104
39981
|
graphie.label([0, y], KhanMath.roundToApprox(y, 2), "left", /* isTeX */true /* for the \approx symbol */);
|
|
40105
|
-
|
|
40106
39982
|
graphie.style({
|
|
40107
39983
|
stroke: isMobile ? "#e9ebec" : "#000",
|
|
40108
39984
|
strokeWidth: 1,
|
|
@@ -41312,9 +41188,51 @@ const init = function init() {
|
|
|
41312
41188
|
replaceDeprecatedWidgets();
|
|
41313
41189
|
};
|
|
41314
41190
|
|
|
41191
|
+
/**
|
|
41192
|
+
* Adds the given perseus library version information to the __perseus_debug__
|
|
41193
|
+
* object and ensures that the object is attached to `globalThis` (`window` in
|
|
41194
|
+
* browser environments).
|
|
41195
|
+
*
|
|
41196
|
+
* This allows each library to provide runtime version information to assist in
|
|
41197
|
+
* debugging in production environments.
|
|
41198
|
+
*/
|
|
41199
|
+
const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
|
|
41200
|
+
// If the library version is the default value, then we don't want to
|
|
41201
|
+
// prefix it with a "v" to indicate that it is a version number.
|
|
41202
|
+
let prefix = "v";
|
|
41203
|
+
if (libraryVersion === "__lib_version__") {
|
|
41204
|
+
prefix = "";
|
|
41205
|
+
}
|
|
41206
|
+
const formattedVersion = `${prefix}${libraryVersion}`;
|
|
41207
|
+
if (typeof globalThis !== "undefined") {
|
|
41208
|
+
var _globalThis$__perseus;
|
|
41209
|
+
globalThis.__perseus_debug__ = (_globalThis$__perseus = globalThis.__perseus_debug__) != null ? _globalThis$__perseus : {};
|
|
41210
|
+
const existingVersionEntry = globalThis.__perseus_debug__[libraryName];
|
|
41211
|
+
if (existingVersionEntry) {
|
|
41212
|
+
// If we already have an entry and it doesn't match the registered
|
|
41213
|
+
// version, we morph the entry into an array and log a warning.
|
|
41214
|
+
if (existingVersionEntry !== formattedVersion) {
|
|
41215
|
+
// Existing entry might be an array already (oops, at least 2
|
|
41216
|
+
// versions of the library already loaded!).
|
|
41217
|
+
const allVersions = Array.isArray(existingVersionEntry) ? existingVersionEntry : [existingVersionEntry];
|
|
41218
|
+
allVersions.push(formattedVersion);
|
|
41219
|
+
globalThis.__perseus_debug__[libraryName] = allVersions;
|
|
41220
|
+
|
|
41221
|
+
// eslint-disable-next-line no-console
|
|
41222
|
+
console.warn(`Multiple versions of ${libraryName} loaded on this page: ${allVersions.sort().join(", ")}`);
|
|
41223
|
+
}
|
|
41224
|
+
} else {
|
|
41225
|
+
globalThis.__perseus_debug__[libraryName] = formattedVersion;
|
|
41226
|
+
}
|
|
41227
|
+
} else {
|
|
41228
|
+
// eslint-disable-next-line no-console
|
|
41229
|
+
console.warn(`globalThis not found found (${formattedVersion})`);
|
|
41230
|
+
}
|
|
41231
|
+
};
|
|
41232
|
+
|
|
41315
41233
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
41316
41234
|
const libName = "@khanacademy/perseus";
|
|
41317
|
-
const libVersion = "
|
|
41235
|
+
const libVersion = "55.0.1";
|
|
41318
41236
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
41319
41237
|
|
|
41320
41238
|
const apiVersion = {
|
|
@@ -41708,7 +41626,6 @@ class HintsRenderer extends React.Component {
|
|
|
41708
41626
|
ReactDOM__default.findDOMNode(this.refs["hintRenderer" + pos]).focus(); // eslint-disable-line react/no-string-refs
|
|
41709
41627
|
}
|
|
41710
41628
|
}
|
|
41711
|
-
|
|
41712
41629
|
render() {
|
|
41713
41630
|
const apiOptions = this.getApiOptions();
|
|
41714
41631
|
const hintsVisible = this._hintsVisible();
|