@khanacademy/perseus 54.1.0 → 55.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +901 -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 +1035 -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,182 @@ 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
|
+
onFocus: onFocus,
|
|
2030
|
+
onBlur: onBlur,
|
|
2031
|
+
onKeyDown: onKeyDown
|
|
2032
|
+
// @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>'.
|
|
2033
|
+
,
|
|
2034
|
+
autoCorrect: "off",
|
|
2035
|
+
autoCapitalize: "off",
|
|
2036
|
+
autoComplete: "off"
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
TextInput.defaultProps = {
|
|
2041
|
+
value: "",
|
|
2042
|
+
disabled: false
|
|
2043
|
+
};
|
|
2044
|
+
|
|
1850
2045
|
/**
|
|
1851
2046
|
* A React context for keeping track of whether an component is fully
|
|
1852
2047
|
* loaded. This is used to know when SvgImage and (Zoomable)TeX are finished
|
|
@@ -2411,9 +2606,11 @@ const Util = {
|
|
|
2411
2606
|
// TODO(LP-11481): Change the primary API we use for logging in Perseus to use
|
|
2412
2607
|
// getDependencies() internally... simplify usage to just using a "singleton"
|
|
2413
2608
|
// which get's the injected logger.
|
|
2609
|
+
|
|
2414
2610
|
/**
|
|
2415
2611
|
* Provides logging infrastructure for Perseus
|
|
2416
2612
|
*/
|
|
2613
|
+
|
|
2417
2614
|
const Log = {
|
|
2418
2615
|
// Logs a message
|
|
2419
2616
|
log: (message, extra) => {
|
|
@@ -3087,40 +3284,40 @@ const articleMaxWidthInPx = 688;
|
|
|
3087
3284
|
const articleMaxWidthTableInPx = 512;
|
|
3088
3285
|
|
|
3089
3286
|
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
|
-
|
|
3287
|
+
__proto__: null,
|
|
3288
|
+
baseFontFamily: baseFontFamily,
|
|
3289
|
+
boldFontFamily: boldFontFamily,
|
|
3290
|
+
kaGreen: kaGreen,
|
|
3291
|
+
white: white,
|
|
3292
|
+
gray97: gray97,
|
|
3293
|
+
gray85: gray85,
|
|
3294
|
+
gray76: gray76,
|
|
3295
|
+
gray68: gray68,
|
|
3296
|
+
gray17: gray17,
|
|
3297
|
+
pureSmMin: pureSmMin$1,
|
|
3298
|
+
pureMdMin: pureMdMin$1,
|
|
3299
|
+
pureLgMin: pureLgMin$1,
|
|
3300
|
+
pureXlMin: pureXlMin$1,
|
|
3301
|
+
pureXsMax: pureXsMax$1,
|
|
3302
|
+
pureSmMax: pureSmMax$1,
|
|
3303
|
+
pureMdMax: pureMdMax$1,
|
|
3304
|
+
pureLgMax: pureLgMax$1,
|
|
3305
|
+
tableBackgroundAccent: tableBackgroundAccent,
|
|
3306
|
+
phoneMargin: phoneMargin,
|
|
3307
|
+
negativePhoneMargin: negativePhoneMargin,
|
|
3308
|
+
hintBorderWidth: hintBorderWidth,
|
|
3309
|
+
baseUnitPx: baseUnitPx,
|
|
3310
|
+
interactiveSizes: interactiveSizes$1,
|
|
3311
|
+
circleSize: circleSize$1,
|
|
3312
|
+
radioMarginWidth: radioMarginWidth$1,
|
|
3313
|
+
warningColor: warningColor,
|
|
3314
|
+
warningColorHover: warningColorHover,
|
|
3315
|
+
warningColorActive: warningColorActive,
|
|
3316
|
+
publishBlockingErrorColor: publishBlockingErrorColor,
|
|
3317
|
+
radioBorderColor: radioBorderColor$1,
|
|
3318
|
+
checkedColor: checkedColor$1,
|
|
3319
|
+
articleMaxWidthInPx: articleMaxWidthInPx,
|
|
3320
|
+
articleMaxWidthTableInPx: articleMaxWidthTableInPx
|
|
3124
3321
|
});
|
|
3125
3322
|
|
|
3126
3323
|
/**
|
|
@@ -3470,16 +3667,6 @@ const InteractiveUtil = {
|
|
|
3470
3667
|
}
|
|
3471
3668
|
};
|
|
3472
3669
|
|
|
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
3670
|
/*!
|
|
3484
3671
|
* Raphael 1.5.2 - JavaScript Vector Library
|
|
3485
3672
|
*
|
|
@@ -3536,7 +3723,7 @@ var Raphael$1 = (function () {
|
|
|
3536
3723
|
appendChild = "appendChild",
|
|
3537
3724
|
apply = "apply",
|
|
3538
3725
|
concat = "concat",
|
|
3539
|
-
supportsTouch =
|
|
3726
|
+
supportsTouch = "createTouch" in doc,
|
|
3540
3727
|
E = "",
|
|
3541
3728
|
S = " ",
|
|
3542
3729
|
Str = String,
|
|
@@ -5141,10 +5328,10 @@ var Raphael$1 = (function () {
|
|
|
5141
5328
|
});
|
|
5142
5329
|
break;
|
|
5143
5330
|
case "gradient":
|
|
5144
|
-
(
|
|
5331
|
+
({
|
|
5145
5332
|
circle: 1,
|
|
5146
5333
|
ellipse: 1
|
|
5147
|
-
}
|
|
5334
|
+
}[has](o.type) || Str(value).charAt() != "r") && _addGradientFill(node, value, o.paper);
|
|
5148
5335
|
break;
|
|
5149
5336
|
case "opacity":
|
|
5150
5337
|
if (attrs.gradient && !attrs[has]("stroke-opacity")) {
|
|
@@ -7971,8 +8158,8 @@ function polar$2(r, th) {
|
|
|
7971
8158
|
th = th * Math.PI / 180;
|
|
7972
8159
|
return [r[0] * Math.cos(th), r[1] * Math.sin(th)];
|
|
7973
8160
|
}
|
|
7974
|
-
var _bounds = /*#__PURE__*/
|
|
7975
|
-
var _drawingTransform = /*#__PURE__*/
|
|
8161
|
+
var _bounds = /*#__PURE__*/_classPrivateFieldLooseKey("bounds");
|
|
8162
|
+
var _drawingTransform = /*#__PURE__*/_classPrivateFieldLooseKey("drawingTransform");
|
|
7976
8163
|
class Graphie$1 {
|
|
7977
8164
|
constructor(el) {
|
|
7978
8165
|
this.el = void 0;
|
|
@@ -8177,8 +8364,8 @@ class Graphie$1 {
|
|
|
8177
8364
|
if (options.range == null) {
|
|
8178
8365
|
throw new PerseusError("range should be specified in graph init", Errors.Internal);
|
|
8179
8366
|
}
|
|
8180
|
-
|
|
8181
|
-
|
|
8367
|
+
_classPrivateFieldLooseBase(this, _bounds)[_bounds] = new GraphBounds(...options.range);
|
|
8368
|
+
_classPrivateFieldLooseBase(this, _drawingTransform)[_drawingTransform] = new DrawingTransform(this.raphael, scale, this.bounds());
|
|
8182
8369
|
const [w, h] = this.drawingTransform().canvasDimensions();
|
|
8183
8370
|
$(this.el).css({
|
|
8184
8371
|
width: w,
|
|
@@ -8413,16 +8600,16 @@ class Graphie$1 {
|
|
|
8413
8600
|
}
|
|
8414
8601
|
}
|
|
8415
8602
|
drawingTransform() {
|
|
8416
|
-
if (
|
|
8603
|
+
if (_classPrivateFieldLooseBase(this, _drawingTransform)[_drawingTransform] == null) {
|
|
8417
8604
|
throw new Error("Can't get drawingTransform of an uninitialized Graphie");
|
|
8418
8605
|
}
|
|
8419
|
-
return
|
|
8606
|
+
return _classPrivateFieldLooseBase(this, _drawingTransform)[_drawingTransform];
|
|
8420
8607
|
}
|
|
8421
8608
|
bounds() {
|
|
8422
|
-
if (
|
|
8609
|
+
if (_classPrivateFieldLooseBase(this, _bounds)[_bounds] == null) {
|
|
8423
8610
|
throw new Error("Can't get bounds of an uninitialized Graphie");
|
|
8424
8611
|
}
|
|
8425
|
-
return
|
|
8612
|
+
return _classPrivateFieldLooseBase(this, _bounds)[_bounds];
|
|
8426
8613
|
}
|
|
8427
8614
|
style(attrs, fn) {
|
|
8428
8615
|
const processed = this.processAttributes(attrs);
|
|
@@ -8973,8 +9160,10 @@ const labelDirections = {
|
|
|
8973
9160
|
// is equivalent to [[-5, 5], [-10, 10]].
|
|
8974
9161
|
// - A single magnitude to be used for both x and y. E.g. 10 is equivalent to
|
|
8975
9162
|
// [[-10, 10], [-10, 10]]
|
|
9163
|
+
|
|
8976
9164
|
// Overload normalizeRange to specify that it only returns undefined if its
|
|
8977
9165
|
// argument is undefined.
|
|
9166
|
+
|
|
8978
9167
|
// normalizeRange converts a RangeSpecifier into a pair of Intervals that give
|
|
8979
9168
|
// the x and y ranges, respectively
|
|
8980
9169
|
function normalizeRange(range) {
|
|
@@ -9167,7 +9356,7 @@ const GraphUtils = {
|
|
|
9167
9356
|
clickBlockList = [],
|
|
9168
9357
|
blockMouseTriggers = false,
|
|
9169
9358
|
blockTouchTriggers = false,
|
|
9170
|
-
eventCaptureSupported =
|
|
9359
|
+
eventCaptureSupported = "addEventListener" in document,
|
|
9171
9360
|
$document = $(document),
|
|
9172
9361
|
nextTouchID = 1,
|
|
9173
9362
|
lastTouchID = 0;
|
|
@@ -9862,7 +10051,6 @@ function scaledPolarRad(radius, radians) {
|
|
|
9862
10051
|
return [radius * Math.cos(radians), radius * Math.sin(radians) * -1 // SVG flips y axis
|
|
9863
10052
|
];
|
|
9864
10053
|
}
|
|
9865
|
-
|
|
9866
10054
|
function scaledPolarDeg(radius, degrees) {
|
|
9867
10055
|
const radians = degrees * Math.PI / 180;
|
|
9868
10056
|
return scaledPolarRad(radius, radians);
|
|
@@ -11680,7 +11868,6 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
11680
11868
|
if (hideArrow) {
|
|
11681
11869
|
return; // Don't draw anything!
|
|
11682
11870
|
}
|
|
11683
|
-
|
|
11684
11871
|
const handleVec = vector$3.subtract(handleCoord, rotatePoint.coord);
|
|
11685
11872
|
const handlePolar = vector$3.polarDegFromCart(handleVec);
|
|
11686
11873
|
const angle = handlePolar[1];
|
|
@@ -12644,7 +12831,6 @@ _.extend(MovableAngle.prototype, {
|
|
|
12644
12831
|
// clockwise to the first point
|
|
12645
12832
|
coords[1] // the vertex parameter is last
|
|
12646
12833
|
);
|
|
12647
|
-
|
|
12648
12834
|
const clockwiseAngle = rawAngle + 360 % 360;
|
|
12649
12835
|
return clockwiseAngle;
|
|
12650
12836
|
},
|
|
@@ -14018,7 +14204,7 @@ class MovablePoint$5 {
|
|
|
14018
14204
|
const showTrashTooltip = () => {
|
|
14019
14205
|
this._showTooltip(container => {
|
|
14020
14206
|
// eslint-disable-next-line import/no-deprecated
|
|
14021
|
-
render(
|
|
14207
|
+
render(/*#__PURE__*/React.createElement("span", {
|
|
14022
14208
|
style: {
|
|
14023
14209
|
fontSize: "2em"
|
|
14024
14210
|
}
|
|
@@ -14817,7 +15003,6 @@ const DEFAULT_PROPS = {
|
|
|
14817
15003
|
// likewise
|
|
14818
15004
|
labelStyle: null // likewise
|
|
14819
15005
|
};
|
|
14820
|
-
|
|
14821
15006
|
const DEFAULT_STATE = {
|
|
14822
15007
|
added: false,
|
|
14823
15008
|
hasMoved: false,
|
|
@@ -17468,7 +17653,7 @@ class TranslationLinter {
|
|
|
17468
17653
|
* Preprocess TeX code to convert things that MathJax doesn't know how to handle
|
|
17469
17654
|
* to things is does.
|
|
17470
17655
|
*/
|
|
17471
|
-
var preprocessTex =
|
|
17656
|
+
var preprocessTex = texCode => texCode
|
|
17472
17657
|
// Replace uses of \begin{align}...\end{align} which KaTeX doesn't
|
|
17473
17658
|
// support (yet) with \begin{aligned}...\end{aligned} which renders
|
|
17474
17659
|
// the same is supported by KaTeX. It does the same for align*.
|
|
@@ -17478,7 +17663,7 @@ var preprocessTex = (texCode => texCode
|
|
|
17478
17663
|
// "aligned").
|
|
17479
17664
|
.replace(/\{align[*]?\}/g, "{aligned}")
|
|
17480
17665
|
// Replace non-breaking spaces with regular spaces.
|
|
17481
|
-
.replace(/[\u00a0]/g, " ")
|
|
17666
|
+
.replace(/[\u00a0]/g, " ");
|
|
17482
17667
|
|
|
17483
17668
|
const {
|
|
17484
17669
|
interactiveSizes
|
|
@@ -17740,29 +17925,29 @@ const isLintable = type => {
|
|
|
17740
17925
|
};
|
|
17741
17926
|
|
|
17742
17927
|
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
|
-
|
|
17928
|
+
__proto__: null,
|
|
17929
|
+
registerWidget: registerWidget,
|
|
17930
|
+
registerWidgets: registerWidgets,
|
|
17931
|
+
replaceWidget: replaceWidget,
|
|
17932
|
+
replaceDeprecatedWidgets: replaceDeprecatedWidgets,
|
|
17933
|
+
registerEditors: registerEditors,
|
|
17934
|
+
replaceEditor: replaceEditor,
|
|
17935
|
+
replaceDeprecatedEditors: replaceDeprecatedEditors,
|
|
17936
|
+
getWidget: getWidget,
|
|
17937
|
+
getWidgetExport: getWidgetExport,
|
|
17938
|
+
getEditor: getEditor,
|
|
17939
|
+
getTransform: getTransform,
|
|
17940
|
+
getVersion: getVersion,
|
|
17941
|
+
getVersionVector: getVersionVector,
|
|
17942
|
+
getPublicWidgets: getPublicWidgets,
|
|
17943
|
+
isAccessible: isAccessible,
|
|
17944
|
+
getAllWidgetTypes: getAllWidgetTypes,
|
|
17945
|
+
getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
|
|
17946
|
+
traverseChildWidgets: traverseChildWidgets$3,
|
|
17947
|
+
supportsStaticMode: supportsStaticMode,
|
|
17948
|
+
getStaticTransform: getStaticTransform,
|
|
17949
|
+
getTracking: getTracking,
|
|
17950
|
+
isLintable: isLintable
|
|
17766
17951
|
});
|
|
17767
17952
|
|
|
17768
17953
|
class WidgetContainer extends React.Component {
|
|
@@ -17805,7 +17990,6 @@ class WidgetContainer extends React.Component {
|
|
|
17805
17990
|
/* eslint-enable react/no-did-mount-set-state */
|
|
17806
17991
|
}
|
|
17807
17992
|
}
|
|
17808
|
-
|
|
17809
17993
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
17810
17994
|
if (this.props.type !== nextProps.type) {
|
|
17811
17995
|
throw new Error("WidgetContainer can't change widget type; set a different " + "key instead to recreate the container.");
|
|
@@ -19259,620 +19443,123 @@ const styles$A = {
|
|
|
19259
19443
|
}
|
|
19260
19444
|
};
|
|
19261
19445
|
|
|
19262
|
-
|
|
19263
|
-
|
|
19264
|
-
|
|
19265
|
-
|
|
19266
|
-
|
|
19267
|
-
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
19275
|
-
|
|
19446
|
+
const {
|
|
19447
|
+
captureScratchpadTouchStart: captureScratchpadTouchStart$2
|
|
19448
|
+
} = Util;
|
|
19449
|
+
// [LEMS-2411](Jan 2025) Third: This component has been moved to the NumericInput
|
|
19450
|
+
// folder as we are actively working towards removing the InputNumber widget.
|
|
19451
|
+
// This comment can be removed as part of LEMS-2411.
|
|
19452
|
+
|
|
19453
|
+
/**
|
|
19454
|
+
* The InputWithExamples component is a child component of the NumericInput
|
|
19455
|
+
* and InputNumber components. It is responsible for rendering the UI elements
|
|
19456
|
+
* for the desktop versions of these widgets, and displays a tooltip with
|
|
19457
|
+
* examples of how to input the selected answer forms.
|
|
19458
|
+
*/
|
|
19459
|
+
const InputWithExamples = /*#__PURE__*/forwardRef((props, ref) => {
|
|
19460
|
+
// Desctructure the props to set default values
|
|
19461
|
+
const {
|
|
19462
|
+
shouldShowExamples = true,
|
|
19463
|
+
onFocus = () => {},
|
|
19464
|
+
onBlur = () => {},
|
|
19465
|
+
disabled = false,
|
|
19466
|
+
linterContext = PerseusLinter.linterContextDefault,
|
|
19467
|
+
className = ""
|
|
19468
|
+
} = props;
|
|
19469
|
+
const context = React.useContext(PerseusI18nContext);
|
|
19470
|
+
const inputRef = React.useRef(null);
|
|
19471
|
+
const [inputFocused, setInputFocused] = React.useState(false);
|
|
19472
|
+
const id = useId();
|
|
19473
|
+
const ariaId = `aria-for-${id}`;
|
|
19474
|
+
useImperativeHandle(ref, () => ({
|
|
19475
|
+
current: inputRef.current,
|
|
19476
|
+
focus: () => {
|
|
19477
|
+
if (inputRef.current) {
|
|
19478
|
+
inputRef.current.focus();
|
|
19479
|
+
}
|
|
19480
|
+
},
|
|
19481
|
+
blur: () => {
|
|
19482
|
+
if (inputRef.current) {
|
|
19483
|
+
inputRef.current.blur();
|
|
19276
19484
|
}
|
|
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
19485
|
}
|
|
19307
|
-
}
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
|
|
19322
|
-
|
|
19486
|
+
}));
|
|
19487
|
+
const getInputClassName = () => {
|
|
19488
|
+
let inputClassName = ClassNames.INPUT;
|
|
19489
|
+
if (inputFocused) {
|
|
19490
|
+
inputClassName += " " + ClassNames.FOCUSED;
|
|
19491
|
+
}
|
|
19492
|
+
if (className) {
|
|
19493
|
+
inputClassName += " " + className;
|
|
19494
|
+
}
|
|
19495
|
+
return inputClassName;
|
|
19496
|
+
};
|
|
19497
|
+
const handleFocus = () => {
|
|
19498
|
+
onFocus();
|
|
19499
|
+
setInputFocused(true);
|
|
19500
|
+
};
|
|
19501
|
+
const handleBlur = () => {
|
|
19502
|
+
onBlur();
|
|
19503
|
+
setInputFocused(false);
|
|
19504
|
+
};
|
|
19505
|
+
const renderInput = () => {
|
|
19506
|
+
// Generate the provided examples in simple language for screen readers.
|
|
19507
|
+
const examplesAria = props.shouldShowExamples ? `${props.examples[0]}
|
|
19508
|
+
${props.examples.slice(1).join(", or\n")}`
|
|
19509
|
+
// @ts-expect-error TS2550: Property replaceAll does not exist on type string.
|
|
19510
|
+
.replaceAll("*", "").replaceAll("$", "").replaceAll("\\ \\text{pi}", " pi").replaceAll("\\ ", " and ") : "";
|
|
19511
|
+
const inputProps = {
|
|
19512
|
+
id: id,
|
|
19513
|
+
// If we have examples, we want to provide the aria-describedby attribute
|
|
19514
|
+
"aria-describedby": props.shouldShowExamples ? ariaId : undefined,
|
|
19515
|
+
ref: inputRef,
|
|
19516
|
+
className: getInputClassName(),
|
|
19517
|
+
labelText: props.labelText,
|
|
19518
|
+
value: props.value,
|
|
19519
|
+
onFocus: handleFocus,
|
|
19520
|
+
onBlur: handleBlur,
|
|
19323
19521
|
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",
|
|
19522
|
+
style: props.style,
|
|
19523
|
+
onChange: props.onChange,
|
|
19524
|
+
onTouchStart: captureScratchpadTouchStart$2,
|
|
19337
19525
|
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
|
|
19526
|
+
autoComplete: "off",
|
|
19527
|
+
autoCorrect: "off",
|
|
19528
|
+
spellCheck: "false"
|
|
19521
19529
|
};
|
|
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", {
|
|
19530
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TextInput, inputProps), /*#__PURE__*/React.createElement("span", {
|
|
19531
|
+
id: ariaId,
|
|
19587
19532
|
style: {
|
|
19588
|
-
|
|
19589
|
-
height: 0,
|
|
19590
|
-
display: this.props.show ? "block" : "none"
|
|
19533
|
+
display: "none"
|
|
19591
19534
|
}
|
|
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()
|
|
19535
|
+
}, examplesAria));
|
|
19536
|
+
};
|
|
19537
|
+
const renderTooltipContent = () => {
|
|
19538
|
+
return /*#__PURE__*/React.createElement(TooltipContent, null, /*#__PURE__*/React.createElement("div", {
|
|
19539
|
+
id: id,
|
|
19540
|
+
className: "input-with-examples-tooltip"
|
|
19765
19541
|
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
19766
19542
|
content: examplesContent,
|
|
19767
|
-
linterContext: PerseusLinter.pushContextStack(
|
|
19768
|
-
strings:
|
|
19543
|
+
linterContext: PerseusLinter.pushContextStack(linterContext, "input-with-examples"),
|
|
19544
|
+
strings: context.strings
|
|
19769
19545
|
})));
|
|
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
19546
|
};
|
|
19873
|
-
|
|
19547
|
+
// Display the examples as a string when there are less than or equal to 2 examples.
|
|
19548
|
+
// Otherwise, display the examples as a list.
|
|
19549
|
+
const examplesContent = props.examples.length <= 2 ? props.examples.join(" ") : props.examples.map((example, index) => {
|
|
19550
|
+
return index === 0 && example.startsWith("**") ? `${example}\n` : `- ${example}`;
|
|
19551
|
+
}).join("\n");
|
|
19552
|
+
|
|
19553
|
+
// Display the examples when they are enabled (shouldShowExamples) and the input is focused.
|
|
19554
|
+
const showExamplesTooltip = shouldShowExamples && inputFocused;
|
|
19555
|
+
return /*#__PURE__*/React.createElement(Tooltip, {
|
|
19556
|
+
content: renderTooltipContent(),
|
|
19557
|
+
opened: showExamplesTooltip,
|
|
19558
|
+
placement: "bottom"
|
|
19559
|
+
}, renderInput());
|
|
19560
|
+
});
|
|
19874
19561
|
|
|
19875
|
-
const formExamples
|
|
19562
|
+
const formExamples = {
|
|
19876
19563
|
integer: function (options, strings) {
|
|
19877
19564
|
return strings.integerExample;
|
|
19878
19565
|
},
|
|
@@ -19975,7 +19662,7 @@ class InputNumber extends React.Component {
|
|
|
19975
19662
|
} = this.context;
|
|
19976
19663
|
const type = this.props.answerType;
|
|
19977
19664
|
const forms = inputNumberAnswerTypes[type].forms.split(/\s*,\s*/);
|
|
19978
|
-
const examples = _.map(forms, form => formExamples
|
|
19665
|
+
const examples = _.map(forms, form => formExamples[form](this.props, strings));
|
|
19979
19666
|
return [strings.yourAnswer].concat(examples);
|
|
19980
19667
|
}
|
|
19981
19668
|
render() {
|
|
@@ -20100,7 +19787,11 @@ const getPromptJSON$r = (renderProps, userInput) => {
|
|
|
20100
19787
|
};
|
|
20101
19788
|
};
|
|
20102
19789
|
|
|
20103
|
-
|
|
19790
|
+
/**
|
|
19791
|
+
* The full list of available strings for the numeric input widget,
|
|
19792
|
+
* based on whether the Content Creator has specified that the answer must be simplified.
|
|
19793
|
+
*/
|
|
19794
|
+
const NumericExampleStrings = {
|
|
20104
19795
|
integer: (form, strings) => strings.integerExample,
|
|
20105
19796
|
proper: (form, strings) => form.simplify === "optional" ? strings.properExample : strings.simplifiedProperExample,
|
|
20106
19797
|
improper: (form, strings) => form.simplify === "optional" ? strings.improperExample : strings.simplifiedImproperExample,
|
|
@@ -20108,38 +19799,200 @@ const formExamples = {
|
|
|
20108
19799
|
decimal: (form, strings) => strings.decimalExample,
|
|
20109
19800
|
pi: (form, strings) => strings.piExample
|
|
20110
19801
|
};
|
|
19802
|
+
|
|
19803
|
+
/**
|
|
19804
|
+
* Generates the specific set of examples for the current question.
|
|
19805
|
+
* This string is shown as examples to the user in a tooltip.
|
|
19806
|
+
*/
|
|
19807
|
+
const generateExamples = (answerForms, strings) => {
|
|
19808
|
+
// If the Content Creator has not specified any answer forms,
|
|
19809
|
+
// we do not need to show any examples.
|
|
19810
|
+
if (answerForms.length === 0) {
|
|
19811
|
+
return [];
|
|
19812
|
+
}
|
|
19813
|
+
|
|
19814
|
+
// Generate a list of the unique answer forms.
|
|
19815
|
+
const uniqueForms = getUniqueAnswerForms(answerForms);
|
|
19816
|
+
|
|
19817
|
+
// Generate the example strings for each unique form.
|
|
19818
|
+
const examples = uniqueForms.map(form => {
|
|
19819
|
+
return NumericExampleStrings[form.name](form, strings);
|
|
19820
|
+
});
|
|
19821
|
+
|
|
19822
|
+
// Add the "Your answer" string to the beginning of the examples list.
|
|
19823
|
+
return [strings.yourAnswer].concat(examples);
|
|
19824
|
+
};
|
|
19825
|
+
|
|
19826
|
+
/**
|
|
19827
|
+
* Determines whether to show examples of how to input the various supported answer forms.
|
|
19828
|
+
* We do not show examples if all forms are accepted or if no forms are accepted.
|
|
19829
|
+
*/
|
|
19830
|
+
const shouldShowExamples = answerForms => {
|
|
19831
|
+
// If the Content Creator has not specified any answer forms,
|
|
19832
|
+
// we do not need to show any examples.
|
|
19833
|
+
if (answerForms.length === 0) {
|
|
19834
|
+
return false;
|
|
19835
|
+
}
|
|
19836
|
+
|
|
19837
|
+
// Generate a list of the unique names of the selected answer forms.
|
|
19838
|
+
const answerFormNames = getUniqueAnswerForms(answerForms).map(form => form.name);
|
|
19839
|
+
|
|
19840
|
+
// If all forms are accepted, we do not need to show any examples.
|
|
19841
|
+
const allFormsAccepted = answerFormNames.length >= Object.keys(NumericExampleStrings).length;
|
|
19842
|
+
return !allFormsAccepted;
|
|
19843
|
+
};
|
|
19844
|
+
|
|
19845
|
+
/**
|
|
19846
|
+
* uniqueAnswerForms takes a list of answer forms and returns a list of unique
|
|
19847
|
+
* answer forms. This is useful for ensuring that we don't show duplicate examples
|
|
19848
|
+
* to the user.
|
|
19849
|
+
*/
|
|
19850
|
+
const getUniqueAnswerForms = function getUniqueAnswerForms(list) {
|
|
19851
|
+
// We use a Set to keep track of the forms we've already seen.
|
|
19852
|
+
const foundForms = new Set();
|
|
19853
|
+
return list.filter(form => {
|
|
19854
|
+
// If we've already seen this form, skip it.
|
|
19855
|
+
if (foundForms.has(form.name)) {
|
|
19856
|
+
return false;
|
|
19857
|
+
}
|
|
19858
|
+
// Otherwise, add it to the set and return true.
|
|
19859
|
+
foundForms.add(form.name);
|
|
19860
|
+
return true;
|
|
19861
|
+
});
|
|
19862
|
+
};
|
|
19863
|
+
|
|
19864
|
+
/**
|
|
19865
|
+
* Takes a list of lists of answer forms, and returns a list of the forms
|
|
19866
|
+
* in each of these lists in the same order that they're listed in the
|
|
19867
|
+
* `formExamples` forms from above.
|
|
19868
|
+
*/
|
|
19869
|
+
const unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
19870
|
+
// Pull out all of the forms from the different lists.
|
|
19871
|
+
const allForms = answerFormsList.flat();
|
|
19872
|
+
// Pull out the unique forms using getUniqueAnswerForms.
|
|
19873
|
+
const uniqueForms = getUniqueAnswerForms(allForms);
|
|
19874
|
+
// Sort them by the order they appear in the `formExamples` list.
|
|
19875
|
+
const formExampleKeys = Object.keys(NumericExampleStrings);
|
|
19876
|
+
return uniqueForms.sort((a, b) => {
|
|
19877
|
+
return formExampleKeys.indexOf(a.name) - formExampleKeys.indexOf(b.name);
|
|
19878
|
+
});
|
|
19879
|
+
};
|
|
19880
|
+
|
|
19881
|
+
/**
|
|
19882
|
+
* The NumericInputComponent is a child component of the NumericInput class
|
|
19883
|
+
* component. It is responsible for rendering the UI elements of the Numeric
|
|
19884
|
+
* Input widget.
|
|
19885
|
+
*/
|
|
19886
|
+
const NumericInputComponent = /*#__PURE__*/forwardRef((props, ref) => {
|
|
19887
|
+
const context = useContext(PerseusI18nContext);
|
|
19888
|
+
const inputRef = useRef(null);
|
|
19889
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
19890
|
+
|
|
19891
|
+
// Pass the focus and blur methods to the Numeric Input Class component
|
|
19892
|
+
useImperativeHandle(ref, () => ({
|
|
19893
|
+
current: inputRef.current,
|
|
19894
|
+
focus: () => {
|
|
19895
|
+
if (inputRef.current) {
|
|
19896
|
+
var _inputRef$current;
|
|
19897
|
+
(_inputRef$current = inputRef.current) == null || _inputRef$current.focus();
|
|
19898
|
+
setIsFocused(true);
|
|
19899
|
+
}
|
|
19900
|
+
},
|
|
19901
|
+
blur: () => {
|
|
19902
|
+
if (inputRef.current) {
|
|
19903
|
+
var _inputRef$current2;
|
|
19904
|
+
(_inputRef$current2 = inputRef.current) == null || _inputRef$current2.blur();
|
|
19905
|
+
setIsFocused(false);
|
|
19906
|
+
}
|
|
19907
|
+
}
|
|
19908
|
+
}));
|
|
19909
|
+
const handleChange = (newValue, cb) => {
|
|
19910
|
+
props.onChange({
|
|
19911
|
+
currentValue: newValue
|
|
19912
|
+
}, cb);
|
|
19913
|
+
props.trackInteraction();
|
|
19914
|
+
};
|
|
19915
|
+
const handleFocus = () => {
|
|
19916
|
+
props.onFocus([]);
|
|
19917
|
+
setIsFocused(true);
|
|
19918
|
+
};
|
|
19919
|
+
const handleBlur = () => {
|
|
19920
|
+
props.onBlur([]);
|
|
19921
|
+
setIsFocused(false);
|
|
19922
|
+
};
|
|
19923
|
+
|
|
19924
|
+
// Styles for the InputWithExamples
|
|
19925
|
+
const styles = StyleSheet.create({
|
|
19926
|
+
inputWithExamples: {
|
|
19927
|
+
borderRadius: "3px",
|
|
19928
|
+
borderWidth: isFocused ? "2px" : "1px",
|
|
19929
|
+
display: "inline-block",
|
|
19930
|
+
fontFamily: `Symbola, "Times New Roman", serif`,
|
|
19931
|
+
fontSize: "18px",
|
|
19932
|
+
height: "32px",
|
|
19933
|
+
lineHeight: "18px",
|
|
19934
|
+
padding: isFocused ? "4px" : "4px 5px",
|
|
19935
|
+
textAlign: props.rightAlign ? "right" : "left",
|
|
19936
|
+
width: props.size === "small" ? 40 : 80
|
|
19937
|
+
}
|
|
19938
|
+
});
|
|
19939
|
+
|
|
19940
|
+
// (mobile-only) If the custom keypad is enabled, use the SimpleKeypadInput component
|
|
19941
|
+
if (props.apiOptions.customKeypad) {
|
|
19942
|
+
const alignmentClass = props.rightAlign ? "perseus-input-right-align" : undefined;
|
|
19943
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
19944
|
+
className: alignmentClass
|
|
19945
|
+
}, /*#__PURE__*/React.createElement(SimpleKeypadInput, {
|
|
19946
|
+
ref: inputRef,
|
|
19947
|
+
value: props.currentValue,
|
|
19948
|
+
keypadElement: props.keypadElement,
|
|
19949
|
+
onChange: handleChange,
|
|
19950
|
+
onFocus: handleFocus,
|
|
19951
|
+
onBlur: handleBlur
|
|
19952
|
+
}));
|
|
19953
|
+
}
|
|
19954
|
+
// (desktop-only) Otherwise, use the InputWithExamples component
|
|
19955
|
+
return /*#__PURE__*/React.createElement(InputWithExamples, {
|
|
19956
|
+
ref: inputRef,
|
|
19957
|
+
value: props.currentValue,
|
|
19958
|
+
onChange: handleChange,
|
|
19959
|
+
labelText: props.labelText,
|
|
19960
|
+
examples: generateExamples(props.answerForms, context.strings),
|
|
19961
|
+
shouldShowExamples: shouldShowExamples(props.answerForms),
|
|
19962
|
+
onFocus: handleFocus,
|
|
19963
|
+
onBlur: handleBlur,
|
|
19964
|
+
id: props.widgetId,
|
|
19965
|
+
disabled: props.apiOptions.readOnly,
|
|
19966
|
+
style: styles.inputWithExamples
|
|
19967
|
+
});
|
|
19968
|
+
});
|
|
19969
|
+
|
|
19970
|
+
const _excluded$3 = ["answers"];
|
|
19971
|
+
|
|
19972
|
+
/**
|
|
19973
|
+
* The NumericInput widget is a numeric input field that supports a variety of
|
|
19974
|
+
* answer forms, including integers, decimals, fractions, and mixed numbers.
|
|
19975
|
+
*
|
|
19976
|
+
* [Jan 2025] We're currenly migrating from class-based components to functional
|
|
19977
|
+
* components. While we cannot fully migrate this component yet, we can start
|
|
19978
|
+
* by using the functional component for the rendering the UI of the widget.
|
|
19979
|
+
*/
|
|
20111
19980
|
class NumericInput extends React.Component {
|
|
20112
19981
|
constructor(...args) {
|
|
20113
19982
|
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
|
-
};
|
|
19983
|
+
this.inputRef = /*#__PURE__*/React.createRef();
|
|
20131
19984
|
this.focus = () => {
|
|
20132
|
-
var _this$inputRef;
|
|
20133
|
-
(_this$inputRef = this.inputRef) == null || _this$inputRef.focus();
|
|
19985
|
+
var _this$inputRef$curren;
|
|
19986
|
+
(_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.focus();
|
|
20134
19987
|
return true;
|
|
20135
19988
|
};
|
|
20136
19989
|
this.focusInputPath = () => {
|
|
20137
|
-
var _this$
|
|
20138
|
-
(_this$
|
|
19990
|
+
var _this$inputRef$curren2;
|
|
19991
|
+
(_this$inputRef$curren2 = this.inputRef.current) == null || _this$inputRef$curren2.focus();
|
|
20139
19992
|
};
|
|
20140
19993
|
this.blurInputPath = () => {
|
|
20141
|
-
var _this$
|
|
20142
|
-
(_this$
|
|
19994
|
+
var _this$inputRef$curren3;
|
|
19995
|
+
(_this$inputRef$curren3 = this.inputRef.current) == null || _this$inputRef$curren3.blur();
|
|
20143
19996
|
};
|
|
20144
19997
|
this.getInputPaths = () => {
|
|
20145
19998
|
// The widget itself is an input, so we return a single empty list to
|
|
@@ -20147,33 +20000,13 @@ class NumericInput extends React.Component {
|
|
|
20147
20000
|
/* c8 ignore next */
|
|
20148
20001
|
return [[]];
|
|
20149
20002
|
};
|
|
20003
|
+
/**
|
|
20004
|
+
* Sets the value of the input at the given path.
|
|
20005
|
+
*/
|
|
20150
20006
|
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
20007
|
this.props.onChange({
|
|
20158
20008
|
currentValue: newValue
|
|
20159
20009
|
}, 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
20010
|
};
|
|
20178
20011
|
}
|
|
20179
20012
|
static getUserInputFromProps(props) {
|
|
@@ -20182,97 +20015,26 @@ class NumericInput extends React.Component {
|
|
|
20182
20015
|
};
|
|
20183
20016
|
}
|
|
20184
20017
|
/**
|
|
20185
|
-
*
|
|
20186
|
-
* answer forms.
|
|
20018
|
+
* Returns the value the user has currently input for this widget.
|
|
20187
20019
|
*/
|
|
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
20020
|
getUserInput() {
|
|
20206
20021
|
return NumericInput.getUserInputFromProps(this.props);
|
|
20207
20022
|
}
|
|
20023
|
+
|
|
20024
|
+
/**
|
|
20025
|
+
* Returns the JSON representation of the prompt for this widget.
|
|
20026
|
+
* This is used by the AI to determine the prompt for the widget.
|
|
20027
|
+
*/
|
|
20208
20028
|
getPromptJSON() {
|
|
20209
20029
|
return getPromptJSON$r(this.props, this.getUserInput());
|
|
20210
20030
|
}
|
|
20211
20031
|
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
|
-
});
|
|
20032
|
+
return /*#__PURE__*/React.createElement(NumericInputComponent, _extends({}, this.props, {
|
|
20033
|
+
ref: this.inputRef
|
|
20034
|
+
}));
|
|
20266
20035
|
}
|
|
20267
20036
|
}
|
|
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;
|
|
20037
|
+
// Transforms the widget options to the props used to render the widget.
|
|
20276
20038
|
NumericInput.defaultProps = {
|
|
20277
20039
|
currentValue: "",
|
|
20278
20040
|
size: "normal",
|
|
@@ -20283,52 +20045,23 @@ NumericInput.defaultProps = {
|
|
|
20283
20045
|
labelText: "",
|
|
20284
20046
|
linterContext: linterContextDefault
|
|
20285
20047
|
};
|
|
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
20048
|
const propsTransform = function propsTransform(widgetOptions) {
|
|
20318
|
-
|
|
20319
|
-
|
|
20320
|
-
|
|
20321
|
-
|
|
20322
|
-
|
|
20323
|
-
//
|
|
20324
|
-
|
|
20325
|
-
return {
|
|
20326
|
-
|
|
20327
|
-
|
|
20328
|
-
|
|
20329
|
-
|
|
20330
|
-
|
|
20331
|
-
|
|
20049
|
+
// Omit the answers from the widget options since they are
|
|
20050
|
+
// not needed for rendering the widget.
|
|
20051
|
+
const _widgetOptions$answer = _extends({}, widgetOptions, {
|
|
20052
|
+
answerForms: unionAnswerForms(
|
|
20053
|
+
// Filter out the correct answers and map them to the answer forms
|
|
20054
|
+
// so that we can generate the examples for the widget.
|
|
20055
|
+
widgetOptions.answers.filter(answer => answer.status === "correct").map(answer => {
|
|
20056
|
+
return (answer.answerForms || []).map(form => {
|
|
20057
|
+
return {
|
|
20058
|
+
simplify: answer.simplify,
|
|
20059
|
+
name: form
|
|
20060
|
+
};
|
|
20061
|
+
});
|
|
20062
|
+
}))
|
|
20063
|
+
}),
|
|
20064
|
+
rendererProps = _objectWithoutPropertiesLoose(_widgetOptions$answer, _excluded$3);
|
|
20332
20065
|
return rendererProps;
|
|
20333
20066
|
};
|
|
20334
20067
|
var NumericInput$1 = {
|
|
@@ -20343,23 +20076,11 @@ var NumericInput$1 = {
|
|
|
20343
20076
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
20344
20077
|
const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
20345
20078
|
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'.
|
|
20079
|
+
// Either get the first answer form or default to decimal
|
|
20080
|
+
const format = answer.answerForms && answer.answerForms[0] ? answer.answerForms[0] : "decimal";
|
|
20358
20081
|
let answerString = KhanMath.toNumericString(answer.value, format);
|
|
20359
20082
|
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);
|
|
20083
|
+
answerString += " \u00B1 " + KhanMath.toNumericString(answer.maxError, format);
|
|
20363
20084
|
}
|
|
20364
20085
|
return answerString;
|
|
20365
20086
|
});
|
|
@@ -21163,7 +20884,7 @@ class HighlightTooltip extends React.PureComponent {
|
|
|
21163
20884
|
style: style,
|
|
21164
20885
|
onMouseEnter: this.props.onMouseEnter,
|
|
21165
20886
|
onMouseLeave: this.props.onMouseLeave
|
|
21166
|
-
}, /*#__PURE__*/React.createElement(Tooltip
|
|
20887
|
+
}, /*#__PURE__*/React.createElement(Tooltip, {
|
|
21167
20888
|
content: content,
|
|
21168
20889
|
opened: true
|
|
21169
20890
|
}, /*#__PURE__*/React.createElement("div", null)))
|
|
@@ -22886,6 +22607,7 @@ const BASE_ICON_SIZE = 10;
|
|
|
22886
22607
|
// If you pass in an object you need to specify an SVG path to
|
|
22887
22608
|
// render along with the width and height of the path (to ensure
|
|
22888
22609
|
// that it gets scaled correctly).
|
|
22610
|
+
|
|
22889
22611
|
/**
|
|
22890
22612
|
* An SVG Icon
|
|
22891
22613
|
*
|
|
@@ -23971,7 +23693,6 @@ const styles$o = StyleSheet.create({
|
|
|
23971
23693
|
paddingLeft: phoneMargin
|
|
23972
23694
|
// paddingRight is handled by responsiveFieldset
|
|
23973
23695
|
},
|
|
23974
|
-
|
|
23975
23696
|
responsiveFieldset: {
|
|
23976
23697
|
paddingRight: phoneMargin,
|
|
23977
23698
|
minWidth: "auto"
|
|
@@ -24377,7 +24098,7 @@ const _choiceTransform = (widgetOptions, strings, problemNum) => {
|
|
|
24377
24098
|
// 1) we randomize the order first
|
|
24378
24099
|
_maybeRandomize(choices)));
|
|
24379
24100
|
};
|
|
24380
|
-
const transform = (widgetOptions, strings, problemNum) => {
|
|
24101
|
+
const transform$1 = (widgetOptions, strings, problemNum) => {
|
|
24381
24102
|
const choices = _choiceTransform(widgetOptions, strings, problemNum);
|
|
24382
24103
|
const numCorrect = _.reduce(widgetOptions.choices, function (memo, choice) {
|
|
24383
24104
|
return choice.correct ? memo + 1 : memo;
|
|
@@ -24403,8 +24124,8 @@ var Radio = {
|
|
|
24403
24124
|
displayName: "Radio / Multiple choice",
|
|
24404
24125
|
accessible: true,
|
|
24405
24126
|
widget: Radio$1,
|
|
24406
|
-
transform: transform,
|
|
24407
|
-
staticTransform: transform,
|
|
24127
|
+
transform: transform$1,
|
|
24128
|
+
staticTransform: transform$1,
|
|
24408
24129
|
version: radioLogic.version,
|
|
24409
24130
|
propUpgrades: radioLogic.widgetOptionsUpgrades,
|
|
24410
24131
|
isLintable: true
|
|
@@ -24975,6 +24696,7 @@ var Definition$1 = {
|
|
|
24975
24696
|
|
|
24976
24697
|
// There are no required props, but this component can receive props from any
|
|
24977
24698
|
// deprecated widget
|
|
24699
|
+
|
|
24978
24700
|
class DeprecatedStandin extends React__default.Component {
|
|
24979
24701
|
constructor(...args) {
|
|
24980
24702
|
super(...args);
|
|
@@ -25101,20 +24823,20 @@ Dropdown.defaultProps = {
|
|
|
25101
24823
|
placeholder: "",
|
|
25102
24824
|
apiOptions: ApiOptions.defaults
|
|
25103
24825
|
};
|
|
25104
|
-
|
|
24826
|
+
function transform(widgetOptions) {
|
|
25105
24827
|
return {
|
|
25106
24828
|
placeholder: widgetOptions.placeholder,
|
|
25107
24829
|
visibleLabel: widgetOptions.visibleLabel,
|
|
25108
24830
|
ariaLabel: widgetOptions.ariaLabel,
|
|
25109
24831
|
choices: widgetOptions.choices.map(choice => choice.content)
|
|
25110
24832
|
};
|
|
25111
|
-
}
|
|
24833
|
+
}
|
|
25112
24834
|
var Dropdown$1 = {
|
|
25113
24835
|
name: "dropdown",
|
|
25114
24836
|
displayName: "Drop down",
|
|
25115
24837
|
accessible: true,
|
|
25116
24838
|
widget: Dropdown,
|
|
25117
|
-
transform
|
|
24839
|
+
transform
|
|
25118
24840
|
};
|
|
25119
24841
|
|
|
25120
24842
|
const getPromptJSON$j = renderProps => {
|
|
@@ -25178,7 +24900,6 @@ class Explanation extends React.Component {
|
|
|
25178
24900
|
// adds space between the right border and any text that follows
|
|
25179
24901
|
paddingLeft: "2px" // adds space between the left border and the button text
|
|
25180
24902
|
};
|
|
25181
|
-
|
|
25182
24903
|
const labelStyle = {
|
|
25183
24904
|
fontSize: "18px",
|
|
25184
24905
|
lineHeight: "inherit",
|
|
@@ -27502,7 +27223,6 @@ class Interaction extends React.Component {
|
|
|
27502
27223
|
}
|
|
27503
27224
|
});
|
|
27504
27225
|
}
|
|
27505
|
-
|
|
27506
27226
|
if (element.type === "parametric") {
|
|
27507
27227
|
const fn = t => {
|
|
27508
27228
|
return [
|
|
@@ -27555,7 +27275,6 @@ class Interaction extends React.Component {
|
|
|
27555
27275
|
}
|
|
27556
27276
|
});
|
|
27557
27277
|
}
|
|
27558
|
-
|
|
27559
27278
|
if (element.type === "label") {
|
|
27560
27279
|
const coord = [
|
|
27561
27280
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
@@ -27900,7 +27619,6 @@ function scaleCommandBy(scaleFactor) {
|
|
|
27900
27619
|
command.args[6] * scaleFactor // end y
|
|
27901
27620
|
]
|
|
27902
27621
|
});
|
|
27903
|
-
|
|
27904
27622
|
default:
|
|
27905
27623
|
return _extends({}, command, {
|
|
27906
27624
|
args: command.args.map(c => c * scaleFactor)
|
|
@@ -28642,7 +28360,6 @@ const styles$f = StyleSheet.create({
|
|
|
28642
28360
|
userSelect: "none",
|
|
28643
28361
|
borderWidth: 0 // <button> gives us a border by default
|
|
28644
28362
|
},
|
|
28645
|
-
|
|
28646
28363
|
// We want to render the element in-place in our fixtures, so allow us to
|
|
28647
28364
|
// render in the bottom corner in production and in-place in testing.
|
|
28648
28365
|
hudFixedPosition: {
|
|
@@ -28672,7 +28389,6 @@ const styles$f = StyleSheet.create({
|
|
|
28672
28389
|
":hover": {
|
|
28673
28390
|
backgroundColor: "#a1a5a9" // in between those two grays
|
|
28674
28391
|
},
|
|
28675
|
-
|
|
28676
28392
|
":active": {
|
|
28677
28393
|
backgroundColor: gray68
|
|
28678
28394
|
}
|
|
@@ -28680,7 +28396,7 @@ const styles$f = StyleSheet.create({
|
|
|
28680
28396
|
});
|
|
28681
28397
|
|
|
28682
28398
|
const InfoTipBase = props => {
|
|
28683
|
-
return /*#__PURE__*/React.createElement(Tooltip
|
|
28399
|
+
return /*#__PURE__*/React.createElement(Tooltip, {
|
|
28684
28400
|
content: /*#__PURE__*/React.createElement(Body, {
|
|
28685
28401
|
style: styles$e.body
|
|
28686
28402
|
}, props.children),
|
|
@@ -28720,7 +28436,6 @@ class InfoTip extends React.Component {
|
|
|
28720
28436
|
});
|
|
28721
28437
|
/* eslint-enable react/no-did-mount-set-state */
|
|
28722
28438
|
}
|
|
28723
|
-
|
|
28724
28439
|
render() {
|
|
28725
28440
|
if (this.state.didMount) {
|
|
28726
28441
|
return /*#__PURE__*/React.createElement(InfoTipBase, this.props);
|
|
@@ -28952,7 +28667,6 @@ class NumberInput extends React.Component {
|
|
|
28952
28667
|
if (val !== Math.floor(val)) {
|
|
28953
28668
|
return; // bail if not an integer
|
|
28954
28669
|
}
|
|
28955
|
-
|
|
28956
28670
|
if (e.key === "ArrowUp") {
|
|
28957
28671
|
val = val + 1;
|
|
28958
28672
|
} else if (e.key === "ArrowDown") {
|
|
@@ -29086,20 +28800,20 @@ RangeInput.defaultProps = {
|
|
|
29086
28800
|
};
|
|
29087
28801
|
|
|
29088
28802
|
var components = /*#__PURE__*/Object.freeze({
|
|
29089
|
-
|
|
29090
|
-
|
|
29091
|
-
|
|
29092
|
-
|
|
29093
|
-
|
|
29094
|
-
|
|
29095
|
-
|
|
29096
|
-
|
|
29097
|
-
|
|
29098
|
-
|
|
29099
|
-
|
|
29100
|
-
|
|
29101
|
-
|
|
29102
|
-
|
|
28803
|
+
__proto__: null,
|
|
28804
|
+
ButtonGroup: ButtonGroup,
|
|
28805
|
+
Graphie: Graphie,
|
|
28806
|
+
HUD: HUD,
|
|
28807
|
+
Icon: Icon,
|
|
28808
|
+
InfoTip: InfoTip,
|
|
28809
|
+
InlineIcon: InlineIcon,
|
|
28810
|
+
MathInput: MathInput,
|
|
28811
|
+
MultiButtonGroup: MultiButtonGroup,
|
|
28812
|
+
NumberInput: NumberInput,
|
|
28813
|
+
RangeInput: RangeInput,
|
|
28814
|
+
SvgImage: SvgImage,
|
|
28815
|
+
TextInput: TextInput,
|
|
28816
|
+
TextListEditor: TextListEditor
|
|
29103
28817
|
});
|
|
29104
28818
|
|
|
29105
28819
|
/**
|
|
@@ -29228,9 +28942,9 @@ const LockedFunction = props => {
|
|
|
29228
28942
|
const {
|
|
29229
28943
|
color,
|
|
29230
28944
|
strokeStyle,
|
|
29231
|
-
directionalAxis
|
|
28945
|
+
directionalAxis,
|
|
28946
|
+
domain
|
|
29232
28947
|
} = 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
28948
|
const plotProps = {
|
|
29235
28949
|
color: lockedFigureColors[color],
|
|
29236
28950
|
style: strokeStyle,
|
|
@@ -30642,7 +30356,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
30642
30356
|
}));
|
|
30643
30357
|
return /*#__PURE__*/React.createElement(React.Fragment, null, showHairlines && /*#__PURE__*/React.createElement(Hairlines, {
|
|
30644
30358
|
point: point
|
|
30645
|
-
}), showTooltips ? /*#__PURE__*/React.createElement(Tooltip
|
|
30359
|
+
}), showTooltips ? /*#__PURE__*/React.createElement(Tooltip, {
|
|
30646
30360
|
autoUpdate: true,
|
|
30647
30361
|
backgroundColor: wbColorName,
|
|
30648
30362
|
content: `(${point[X]}, ${point[Y]})`,
|
|
@@ -30775,6 +30489,9 @@ const MovableLine = props => {
|
|
|
30775
30489
|
onMoveLine = () => {},
|
|
30776
30490
|
onMovePoint = () => {}
|
|
30777
30491
|
} = props;
|
|
30492
|
+
const {
|
|
30493
|
+
snapStep
|
|
30494
|
+
} = useGraphConfig();
|
|
30778
30495
|
|
|
30779
30496
|
// Aria live states for (0) point 1, (1) point 2, and (2) grab handle.
|
|
30780
30497
|
// When moving an element, set its aria live to "polite" and the others
|
|
@@ -30806,7 +30523,8 @@ const MovableLine = props => {
|
|
|
30806
30523
|
onMove: p => {
|
|
30807
30524
|
setAriaLives(["polite", "off", "off"]);
|
|
30808
30525
|
onMovePoint(0, p);
|
|
30809
|
-
}
|
|
30526
|
+
},
|
|
30527
|
+
constrain: getMovableLineKeyboardConstraint([start, end], snapStep, 0)
|
|
30810
30528
|
});
|
|
30811
30529
|
const {
|
|
30812
30530
|
visiblePoint: visiblePoint2,
|
|
@@ -30821,7 +30539,8 @@ const MovableLine = props => {
|
|
|
30821
30539
|
onMove: p => {
|
|
30822
30540
|
setAriaLives(["off", "polite", "off"]);
|
|
30823
30541
|
onMovePoint(1, p);
|
|
30824
|
-
}
|
|
30542
|
+
},
|
|
30543
|
+
constrain: getMovableLineKeyboardConstraint([start, end], snapStep, 1)
|
|
30825
30544
|
});
|
|
30826
30545
|
const line = /*#__PURE__*/React.createElement(Line$1, {
|
|
30827
30546
|
ariaLabel: ariaLabels == null ? void 0 : ariaLabels.grabHandleAriaLabel,
|
|
@@ -30927,6 +30646,34 @@ const Line$1 = props => {
|
|
|
30927
30646
|
color: stroke
|
|
30928
30647
|
}));
|
|
30929
30648
|
};
|
|
30649
|
+
const getMovableLineKeyboardConstraint = (line, snapStep, pointIndex) => {
|
|
30650
|
+
// Separate the two points into their own variables, and determine which point is being moved
|
|
30651
|
+
const coordToBeMoved = line[pointIndex];
|
|
30652
|
+
const otherPoint = line[1 - pointIndex];
|
|
30653
|
+
|
|
30654
|
+
// Create a helper function that moves the point and then checks
|
|
30655
|
+
// if it overlaps with the other point in the line after the move.
|
|
30656
|
+
const movePointWithConstraint = moveFunc => {
|
|
30657
|
+
// Move the point
|
|
30658
|
+
let movedCoord = moveFunc(coordToBeMoved);
|
|
30659
|
+
// If the moved point overlaps with the other point in the line,
|
|
30660
|
+
// move the point again.
|
|
30661
|
+
if (vec.dist(movedCoord, otherPoint) === 0) {
|
|
30662
|
+
movedCoord = moveFunc(movedCoord);
|
|
30663
|
+
}
|
|
30664
|
+
return movedCoord;
|
|
30665
|
+
};
|
|
30666
|
+
|
|
30667
|
+
// Check if the new point will overlap the other point.
|
|
30668
|
+
// If it will, we need to snap the point to the left or right an additional
|
|
30669
|
+
// snapStep to avoid overlap.
|
|
30670
|
+
return {
|
|
30671
|
+
up: movePointWithConstraint(coord => vec.add(coord, [0, snapStep[1]])),
|
|
30672
|
+
down: movePointWithConstraint(coord => vec.sub(coord, [0, snapStep[1]])),
|
|
30673
|
+
left: movePointWithConstraint(coord => vec.sub(coord, [snapStep[0], 0])),
|
|
30674
|
+
right: movePointWithConstraint(coord => vec.add(coord, [snapStep[0], 0]))
|
|
30675
|
+
};
|
|
30676
|
+
};
|
|
30930
30677
|
function trimRange(range, graphDimensionsInPixels) {
|
|
30931
30678
|
const pixelsToTrim = 4;
|
|
30932
30679
|
const [xRange, yRange] = range;
|
|
@@ -31268,7 +31015,8 @@ function CircleGraph(props) {
|
|
|
31268
31015
|
} = props;
|
|
31269
31016
|
const {
|
|
31270
31017
|
center,
|
|
31271
|
-
radiusPoint
|
|
31018
|
+
radiusPoint,
|
|
31019
|
+
snapStep
|
|
31272
31020
|
} = graphState;
|
|
31273
31021
|
const {
|
|
31274
31022
|
strings,
|
|
@@ -31331,7 +31079,8 @@ function CircleGraph(props) {
|
|
|
31331
31079
|
onMove: newRadiusPoint => {
|
|
31332
31080
|
setRadiusPointAriaLive("polite");
|
|
31333
31081
|
dispatch(actions.circle.moveRadiusPoint(newRadiusPoint));
|
|
31334
|
-
}
|
|
31082
|
+
},
|
|
31083
|
+
constrain: getCircleKeyboardConstraint(center, radiusPoint, snapStep)
|
|
31335
31084
|
}), /*#__PURE__*/React.createElement("g", {
|
|
31336
31085
|
id: radiusId,
|
|
31337
31086
|
style: a11y.srOnly
|
|
@@ -31442,6 +31191,30 @@ function getCircleGraphDescription(state, i18n) {
|
|
|
31442
31191
|
const strings = describeCircleGraph(state, i18n);
|
|
31443
31192
|
return strings.srCircleInteractiveElement;
|
|
31444
31193
|
}
|
|
31194
|
+
const getCircleKeyboardConstraint = (center, radiusPoint, snapStep) => {
|
|
31195
|
+
// Create a helper function that moves the point and then checks
|
|
31196
|
+
// if it overlaps with the center point after the move.
|
|
31197
|
+
const movePointWithConstraint = moveFunc => {
|
|
31198
|
+
// Move the point
|
|
31199
|
+
let movedCoord = moveFunc(radiusPoint);
|
|
31200
|
+
// If the moved point overlaps with the center point,
|
|
31201
|
+
// move the point again.
|
|
31202
|
+
if (vec.dist(movedCoord, center) === 0) {
|
|
31203
|
+
movedCoord = moveFunc(movedCoord);
|
|
31204
|
+
}
|
|
31205
|
+
return movedCoord;
|
|
31206
|
+
};
|
|
31207
|
+
|
|
31208
|
+
// Check if the new point overlaps the center point.
|
|
31209
|
+
// If it does, we need to snap the point to the left
|
|
31210
|
+
// or right an additional snapStep to avoid the overlap.
|
|
31211
|
+
return {
|
|
31212
|
+
up: movePointWithConstraint(coord => vec.add(coord, [0, snapStep[1]])),
|
|
31213
|
+
down: movePointWithConstraint(coord => vec.sub(coord, [0, snapStep[1]])),
|
|
31214
|
+
left: movePointWithConstraint(coord => vec.sub(coord, [snapStep[0], 0])),
|
|
31215
|
+
right: movePointWithConstraint(coord => vec.add(coord, [snapStep[0], 0]))
|
|
31216
|
+
};
|
|
31217
|
+
};
|
|
31445
31218
|
|
|
31446
31219
|
// Exported for testing
|
|
31447
31220
|
function describeCircleGraph(state, i18n) {
|
|
@@ -31454,6 +31227,7 @@ function describeCircleGraph(state, i18n) {
|
|
|
31454
31227
|
radiusPoint
|
|
31455
31228
|
} = state;
|
|
31456
31229
|
const radius = getRadius(state);
|
|
31230
|
+
const isRadiusOnRight = radiusPoint[X] >= center[X];
|
|
31457
31231
|
|
|
31458
31232
|
// Aria label strings
|
|
31459
31233
|
const srCircleGraph = strings.srCircleGraph;
|
|
@@ -31461,7 +31235,10 @@ function describeCircleGraph(state, i18n) {
|
|
|
31461
31235
|
centerX: srFormatNumber(center[0], locale),
|
|
31462
31236
|
centerY: srFormatNumber(center[1], locale)
|
|
31463
31237
|
});
|
|
31464
|
-
const srCircleRadiusPoint = strings.
|
|
31238
|
+
const srCircleRadiusPoint = isRadiusOnRight ? strings.srCircleRadiusPointRight({
|
|
31239
|
+
radiusPointX: srFormatNumber(radiusPoint[0], locale),
|
|
31240
|
+
radiusPointY: srFormatNumber(radiusPoint[1], locale)
|
|
31241
|
+
}) : strings.srCircleRadiusPointLeft({
|
|
31465
31242
|
radiusPointX: srFormatNumber(radiusPoint[0], locale),
|
|
31466
31243
|
radiusPointY: srFormatNumber(radiusPoint[1], locale)
|
|
31467
31244
|
});
|
|
@@ -31627,6 +31404,12 @@ const LinearSystemGraph = props => {
|
|
|
31627
31404
|
locale
|
|
31628
31405
|
} = usePerseusI18n();
|
|
31629
31406
|
const id = React.useId();
|
|
31407
|
+
const intersectionId = `${id}-intersection`;
|
|
31408
|
+
const intersectionPoint = geometry.getLineIntersection(lines[0], lines[1]);
|
|
31409
|
+
const intersectionDescription = intersectionPoint ? strings.srLinearSystemIntersection({
|
|
31410
|
+
x: srFormatNumber(intersectionPoint[0], locale),
|
|
31411
|
+
y: srFormatNumber(intersectionPoint[1], locale)
|
|
31412
|
+
}) : strings.srLinearSystemParallel;
|
|
31630
31413
|
const linesAriaInfo = lines.map((line, i) => {
|
|
31631
31414
|
return {
|
|
31632
31415
|
pointsDescriptionId: `${id}-line${i + 1}-points`,
|
|
@@ -31643,13 +31426,14 @@ const LinearSystemGraph = props => {
|
|
|
31643
31426
|
slopeDescription: getSlopeStringForLine(line, strings)
|
|
31644
31427
|
};
|
|
31645
31428
|
});
|
|
31429
|
+
const individualLineDescriptions = linesAriaInfo.map(({
|
|
31430
|
+
pointsDescriptionId,
|
|
31431
|
+
interceptDescriptionId,
|
|
31432
|
+
slopeDescriptionId
|
|
31433
|
+
}) => `${pointsDescriptionId} ${interceptDescriptionId} ${slopeDescriptionId}`).join(" ");
|
|
31646
31434
|
return /*#__PURE__*/React.createElement("g", {
|
|
31647
31435
|
"aria-label": strings.srLinearSystemGraph,
|
|
31648
|
-
"aria-describedby":
|
|
31649
|
-
pointsDescriptionId,
|
|
31650
|
-
interceptDescriptionId,
|
|
31651
|
-
slopeDescriptionId
|
|
31652
|
-
}) => `${pointsDescriptionId} ${interceptDescriptionId} ${slopeDescriptionId}`).join(" ")
|
|
31436
|
+
"aria-describedby": `${individualLineDescriptions} ${intersectionId}`
|
|
31653
31437
|
}, lines == null ? void 0 : lines.map((line, i) => /*#__PURE__*/React.createElement(MovableLine, {
|
|
31654
31438
|
key: i,
|
|
31655
31439
|
points: line,
|
|
@@ -31666,9 +31450,15 @@ const LinearSystemGraph = props => {
|
|
|
31666
31450
|
x: srFormatNumber(line[1][0], locale),
|
|
31667
31451
|
y: srFormatNumber(line[1][1], locale)
|
|
31668
31452
|
}),
|
|
31669
|
-
grabHandleAriaLabel:
|
|
31453
|
+
grabHandleAriaLabel: strings.srLinearSystemGrabHandle({
|
|
31454
|
+
lineNumber: i + 1,
|
|
31455
|
+
point1X: srFormatNumber(line[0][0], locale),
|
|
31456
|
+
point1Y: srFormatNumber(line[0][1], locale),
|
|
31457
|
+
point2X: srFormatNumber(line[1][0], locale),
|
|
31458
|
+
point2Y: srFormatNumber(line[1][1], locale)
|
|
31459
|
+
})
|
|
31670
31460
|
},
|
|
31671
|
-
ariaDescribedBy: `${linesAriaInfo[i].interceptDescriptionId} ${linesAriaInfo[i].slopeDescriptionId}`,
|
|
31461
|
+
ariaDescribedBy: `${linesAriaInfo[i].interceptDescriptionId} ${linesAriaInfo[i].slopeDescriptionId} ${intersectionId}`,
|
|
31672
31462
|
onMoveLine: delta => {
|
|
31673
31463
|
dispatch(actions.linearSystem.moveLine(i, delta));
|
|
31674
31464
|
},
|
|
@@ -31697,7 +31487,10 @@ const LinearSystemGraph = props => {
|
|
|
31697
31487
|
key: slopeDescriptionId,
|
|
31698
31488
|
id: slopeDescriptionId,
|
|
31699
31489
|
style: a11y.srOnly
|
|
31700
|
-
}, slopeDescription)))
|
|
31490
|
+
}, slopeDescription))), /*#__PURE__*/React.createElement("g", {
|
|
31491
|
+
id: intersectionId,
|
|
31492
|
+
style: a11y.srOnly
|
|
31493
|
+
}, intersectionDescription));
|
|
31701
31494
|
};
|
|
31702
31495
|
|
|
31703
31496
|
// Exported for testing
|
|
@@ -32358,7 +32151,8 @@ function QuadraticGraph(props) {
|
|
|
32358
32151
|
graphState
|
|
32359
32152
|
} = props;
|
|
32360
32153
|
const {
|
|
32361
|
-
coords
|
|
32154
|
+
coords,
|
|
32155
|
+
snapStep
|
|
32362
32156
|
} = graphState;
|
|
32363
32157
|
const {
|
|
32364
32158
|
strings,
|
|
@@ -32411,6 +32205,7 @@ function QuadraticGraph(props) {
|
|
|
32411
32205
|
ariaLabel: `${srQuadraticPoint}${srVertex}`,
|
|
32412
32206
|
point: coord,
|
|
32413
32207
|
sequenceNumber: i + 1,
|
|
32208
|
+
constrain: getQuadraticKeyboardConstraint(coords, snapStep, i),
|
|
32414
32209
|
onMove: destination => dispatch(actions.quadratic.movePoint(i, destination))
|
|
32415
32210
|
});
|
|
32416
32211
|
}), srQuadraticDirection && /*#__PURE__*/React.createElement("g", {
|
|
@@ -32499,6 +32294,60 @@ function describeQuadraticGraph(state, i18n) {
|
|
|
32499
32294
|
srQuadraticInteractiveElements
|
|
32500
32295
|
};
|
|
32501
32296
|
}
|
|
32297
|
+
const getQuadraticKeyboardConstraint = (coords, snapStep, pointMoved) => {
|
|
32298
|
+
// Make newCoords mutable
|
|
32299
|
+
const newCoords = [coords[0], coords[1], coords[2]];
|
|
32300
|
+
|
|
32301
|
+
// Get the point that is being moved
|
|
32302
|
+
const coordToBeMoved = newCoords[pointMoved];
|
|
32303
|
+
|
|
32304
|
+
// Create a function to validate and adjust the movement of the point
|
|
32305
|
+
const movePointWithConstraint = moveFunc => {
|
|
32306
|
+
// Move the point the desired direction
|
|
32307
|
+
let movedCoord = moveFunc(coordToBeMoved);
|
|
32308
|
+
newCoords[pointMoved] = movedCoord;
|
|
32309
|
+
|
|
32310
|
+
// If these new coordinates are valid, return the moved coord
|
|
32311
|
+
if (areCoordsValid(newCoords)) {
|
|
32312
|
+
return movedCoord;
|
|
32313
|
+
}
|
|
32314
|
+
|
|
32315
|
+
// If the new coordinates are invalid, we need to move the point an additional
|
|
32316
|
+
// snapStep to avoid creating an invalid quadratic graph.
|
|
32317
|
+
movedCoord = moveFunc(movedCoord);
|
|
32318
|
+
newCoords[pointMoved] = movedCoord;
|
|
32319
|
+
|
|
32320
|
+
// If these updated coordinates are valid now, we can return the new coordinates.
|
|
32321
|
+
if (areCoordsValid(newCoords)) {
|
|
32322
|
+
return movedCoord;
|
|
32323
|
+
}
|
|
32324
|
+
|
|
32325
|
+
// Otherwise, if the new coordinates are still invalid, we need to move the point one final snapStep.
|
|
32326
|
+
// This is to support the edge case where all of the points are each exactly one snapStep away
|
|
32327
|
+
// from each other.
|
|
32328
|
+
// Eg. When coords = [[0, 0], [1, 0], [2, 0]], snapStep = [1, 1], and we're moving coords[0] to the right.
|
|
32329
|
+
return moveFunc(movedCoord);
|
|
32330
|
+
};
|
|
32331
|
+
return {
|
|
32332
|
+
up: vec.add(coordToBeMoved, [0, snapStep[1]]),
|
|
32333
|
+
down: vec.sub(coordToBeMoved, [0, snapStep[1]]),
|
|
32334
|
+
// For horizontal movement, we need to ensure that the points are not on the same vertical line.
|
|
32335
|
+
left: movePointWithConstraint(coord => vec.sub(coord, [snapStep[0], 0])),
|
|
32336
|
+
right: movePointWithConstraint(coord => vec.add(coord, [snapStep[0], 0]))
|
|
32337
|
+
};
|
|
32338
|
+
};
|
|
32339
|
+
const areCoordsValid = coords => {
|
|
32340
|
+
const p1 = coords[0];
|
|
32341
|
+
const p2 = coords[1];
|
|
32342
|
+
const p3 = coords[2];
|
|
32343
|
+
|
|
32344
|
+
// If any of the points share the same x-coordinate,
|
|
32345
|
+
// we are unable to calculate the coefficients, and the graph is invalid.
|
|
32346
|
+
if (p1[0] === p2[0] || p2[0] === p3[0] || p1[0] === p3[0]) {
|
|
32347
|
+
return false;
|
|
32348
|
+
}
|
|
32349
|
+
return true;
|
|
32350
|
+
};
|
|
32502
32351
|
|
|
32503
32352
|
function renderRayGraph(state, dispatch, i18n) {
|
|
32504
32353
|
return {
|
|
@@ -32749,7 +32598,8 @@ function SinusoidGraph(props) {
|
|
|
32749
32598
|
// Note: The order of the coordinates is important:
|
|
32750
32599
|
// The coords[0] is the root and the coords[1] is the first peak
|
|
32751
32600
|
const {
|
|
32752
|
-
coords
|
|
32601
|
+
coords,
|
|
32602
|
+
snapStep
|
|
32753
32603
|
} = graphState;
|
|
32754
32604
|
|
|
32755
32605
|
// The coefficients are used to calculate the sinusoid equation, plot the graph, and to indicate
|
|
@@ -32788,11 +32638,37 @@ function SinusoidGraph(props) {
|
|
|
32788
32638
|
key: "point-" + i,
|
|
32789
32639
|
point: coord,
|
|
32790
32640
|
sequenceNumber: i + 1,
|
|
32641
|
+
constrain: getSinusoidKeyboardConstraint(coords, snapStep, i),
|
|
32791
32642
|
onMove: destination => dispatch(actions.sinusoid.movePoint(i, destination))
|
|
32792
32643
|
})), /*#__PURE__*/React.createElement("g", {
|
|
32793
32644
|
id: descriptionId
|
|
32794
32645
|
}, srSinusoidDescription));
|
|
32795
32646
|
}
|
|
32647
|
+
const getSinusoidKeyboardConstraint = (coords, snapStep, pointIndex) => {
|
|
32648
|
+
// Separate the two points into their own variables, and determine which point is being moved
|
|
32649
|
+
const coordToBeMoved = coords[pointIndex];
|
|
32650
|
+
const otherPoint = coords[1 - pointIndex];
|
|
32651
|
+
|
|
32652
|
+
// Create a helper function that checks if the new point is on the same
|
|
32653
|
+
// vertical line as the other point. If it is, we need to move the point
|
|
32654
|
+
// an additional snapStep.
|
|
32655
|
+
const movePointWithConstraint = moveFunc => {
|
|
32656
|
+
// Move the point
|
|
32657
|
+
let movedCoord = moveFunc(coordToBeMoved);
|
|
32658
|
+
// If the moved point overlaps with the other point in the line,
|
|
32659
|
+
// move the point again.
|
|
32660
|
+
if (movedCoord[X] === otherPoint[X]) {
|
|
32661
|
+
movedCoord = moveFunc(movedCoord);
|
|
32662
|
+
}
|
|
32663
|
+
return movedCoord;
|
|
32664
|
+
};
|
|
32665
|
+
return {
|
|
32666
|
+
up: movePointWithConstraint(coord => vec.add(coord, [0, snapStep[1]])),
|
|
32667
|
+
down: movePointWithConstraint(coord => vec.sub(coord, [0, snapStep[1]])),
|
|
32668
|
+
left: movePointWithConstraint(coord => vec.sub(coord, [snapStep[0], 0])),
|
|
32669
|
+
right: movePointWithConstraint(coord => vec.add(coord, [snapStep[0], 0]))
|
|
32670
|
+
};
|
|
32671
|
+
};
|
|
32796
32672
|
|
|
32797
32673
|
// Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
|
|
32798
32674
|
const computeSine = function computeSine(x,
|
|
@@ -32844,6 +32720,14 @@ function describeSinusoidGraph(state, i18n) {
|
|
|
32844
32720
|
const [root, peak] = coords;
|
|
32845
32721
|
const diffX = Math.abs(peak[X] - root[X]);
|
|
32846
32722
|
const diffY = Math.abs(peak[Y] - root[Y]);
|
|
32723
|
+
const formattedRoot = {
|
|
32724
|
+
x: srFormatNumber(root[X], locale),
|
|
32725
|
+
y: srFormatNumber(root[Y], locale)
|
|
32726
|
+
};
|
|
32727
|
+
const formattedPeak = {
|
|
32728
|
+
x: srFormatNumber(peak[X], locale),
|
|
32729
|
+
y: srFormatNumber(peak[Y], locale)
|
|
32730
|
+
};
|
|
32847
32731
|
const srSinusoidGraph = strings.srSinusoidGraph;
|
|
32848
32732
|
const srSinusoidDescription = strings.srSinusoidDescription({
|
|
32849
32733
|
minValue: srFormatNumber(root[Y] - diffY, locale),
|
|
@@ -32851,14 +32735,8 @@ function describeSinusoidGraph(state, i18n) {
|
|
|
32851
32735
|
cycleStart: srFormatNumber(root[X] - 2 * diffX, locale),
|
|
32852
32736
|
cycleEnd: srFormatNumber(root[X] + 2 * diffX, locale)
|
|
32853
32737
|
});
|
|
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
|
-
});
|
|
32738
|
+
const srSinusoidRootPoint = strings.srSinusoidRootPoint(formattedRoot);
|
|
32739
|
+
const srSinusoidPeakPoint = peak[Y] === root[Y] ? strings.srSinusoidFlatPoint(formattedPeak) : peak[Y] > root[Y] ? strings.srSinusoidMaxPoint(formattedPeak) : strings.srSinusoidMinPoint(formattedPeak);
|
|
32862
32740
|
const srSinusoidInteractiveElements = strings.srInteractiveElements({
|
|
32863
32741
|
elements: strings.srSinusoidInteractiveElements({
|
|
32864
32742
|
point1X: srFormatNumber(root[X], locale),
|
|
@@ -32880,16 +32758,17 @@ const {
|
|
|
32880
32758
|
calculateAngleInDegrees,
|
|
32881
32759
|
convertDegreesToRadians
|
|
32882
32760
|
} = angles;
|
|
32883
|
-
const protractorImage = "https://
|
|
32761
|
+
const protractorImage = "https://cdn.kastatic.org/images/perseus/protractor.svg";
|
|
32884
32762
|
|
|
32885
32763
|
// The vector from the center of the protractor to the top left corner of the
|
|
32886
32764
|
// protractor image, in pixels. Used for positioning.
|
|
32887
|
-
const centerToTopLeft = [-
|
|
32765
|
+
const centerToTopLeft = [-195, -190];
|
|
32888
32766
|
|
|
32889
32767
|
// The vector from the center of the protractor to the center of the rotation
|
|
32890
32768
|
// handle.
|
|
32891
|
-
const centerToRotationHandle = [-
|
|
32769
|
+
const centerToRotationHandle = [-201, -15];
|
|
32892
32770
|
function Protractor() {
|
|
32771
|
+
const staticUrl = getDependencies().staticUrl;
|
|
32893
32772
|
const {
|
|
32894
32773
|
range,
|
|
32895
32774
|
snapStep
|
|
@@ -32901,7 +32780,9 @@ function Protractor() {
|
|
|
32901
32780
|
const [center, setCenter] = useState(initialCenter);
|
|
32902
32781
|
const [rotationHandleOffset, setRotationHandleOffset] = useState(centerToRotationHandle);
|
|
32903
32782
|
const draggableRef = useRef(null);
|
|
32904
|
-
|
|
32783
|
+
const {
|
|
32784
|
+
dragging
|
|
32785
|
+
} = useDraggable({
|
|
32905
32786
|
gestureTarget: draggableRef,
|
|
32906
32787
|
onMove: setCenter,
|
|
32907
32788
|
point: center,
|
|
@@ -32925,10 +32806,11 @@ function Protractor() {
|
|
|
32925
32806
|
ref: draggableRef,
|
|
32926
32807
|
transform: `translate(${topLeftPx[X]}, ${topLeftPx[Y]}), rotate(${angle})`,
|
|
32927
32808
|
style: {
|
|
32928
|
-
transformOrigin: `${-centerToTopLeft[X]}px ${-centerToTopLeft[Y]}px
|
|
32809
|
+
transformOrigin: `${-centerToTopLeft[X]}px ${-centerToTopLeft[Y]}px`,
|
|
32810
|
+
cursor: dragging ? "grabbing" : "grab"
|
|
32929
32811
|
}
|
|
32930
32812
|
}, /*#__PURE__*/React.createElement("image", {
|
|
32931
|
-
href: protractorImage
|
|
32813
|
+
href: staticUrl(protractorImage)
|
|
32932
32814
|
}), /*#__PURE__*/React.createElement("g", {
|
|
32933
32815
|
transform: `translate(5, ${-centerToTopLeft[1]})`,
|
|
32934
32816
|
ref: rotationHandleRef
|
|
@@ -34803,7 +34685,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
34803
34685
|
|
|
34804
34686
|
// If the graph is static, it always displays the correct answer. This is
|
|
34805
34687
|
// standard behavior for Perseus widgets (e.g. compare the Radio widget).
|
|
34806
|
-
if (props.static) {
|
|
34688
|
+
if (props.static && props.correct) {
|
|
34807
34689
|
return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, props, {
|
|
34808
34690
|
state: initializeGraphState(_extends({}, props, {
|
|
34809
34691
|
graph: props.correct
|
|
@@ -34822,7 +34704,7 @@ const {
|
|
|
34822
34704
|
} = angles;
|
|
34823
34705
|
const {
|
|
34824
34706
|
getLineEquation,
|
|
34825
|
-
|
|
34707
|
+
getLineIntersectionString,
|
|
34826
34708
|
magnitude,
|
|
34827
34709
|
vector
|
|
34828
34710
|
} = geometry;
|
|
@@ -35180,7 +35062,7 @@ class InteractiveGraph extends React.Component {
|
|
|
35180
35062
|
}
|
|
35181
35063
|
static getLinearSystemEquationString(props) {
|
|
35182
35064
|
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" +
|
|
35065
|
+
return "\n" + getLineEquation(coords[0][0], coords[0][1]) + "\n" + getLineEquation(coords[1][0], coords[1][1]) + "\n" + getLineIntersectionString(coords[0], coords[1]);
|
|
35184
35066
|
}
|
|
35185
35067
|
static getPointEquationString(props) {
|
|
35186
35068
|
if (props.graph.type !== "point") {
|
|
@@ -35628,7 +35510,6 @@ const styles$a = StyleSheet.create({
|
|
|
35628
35510
|
markerCorrect: {
|
|
35629
35511
|
background: "#00880b" // WB green darkened by 18%
|
|
35630
35512
|
},
|
|
35631
|
-
|
|
35632
35513
|
markerIncorrect: {
|
|
35633
35514
|
background: color.offBlack64
|
|
35634
35515
|
}
|
|
@@ -36021,7 +35902,6 @@ class LabelImage extends React.Component {
|
|
|
36021
35902
|
const adjustPillDistance = {
|
|
36022
35903
|
[`margin${markerPosition.charAt(0).toUpperCase() + markerPosition.slice(1)}`]: 10 // move pill further from marker
|
|
36023
35904
|
};
|
|
36024
|
-
|
|
36025
35905
|
const answerChoicesActive = index === activeMarkerIndex;
|
|
36026
35906
|
const showAnswerChoice = marker.selected && !answerChoicesActive && !this.state.hideAnswers;
|
|
36027
35907
|
return /*#__PURE__*/React.createElement(View, {
|
|
@@ -36466,7 +36346,6 @@ class Draggable extends React.Component {
|
|
|
36466
36346
|
if (this.props.width) {
|
|
36467
36347
|
style.width = this.props.width + 1; // Fix for non-integer widths
|
|
36468
36348
|
}
|
|
36469
|
-
|
|
36470
36349
|
if (this.props.height) {
|
|
36471
36350
|
style.height = this.props.height;
|
|
36472
36351
|
}
|
|
@@ -36808,8 +36687,7 @@ class Sortable extends React.Component {
|
|
|
36808
36687
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36809
36688
|
margin = "0 0 " + this.props.margin + "px 0"; // bottom
|
|
36810
36689
|
}
|
|
36811
|
-
|
|
36812
|
-
cards.push( /*#__PURE__*/React.createElement(Draggable, {
|
|
36690
|
+
cards.push(/*#__PURE__*/React.createElement(Draggable, {
|
|
36813
36691
|
content: item.option,
|
|
36814
36692
|
key: item.key,
|
|
36815
36693
|
state: item.state
|
|
@@ -36862,7 +36740,7 @@ class Sortable extends React.Component {
|
|
|
36862
36740
|
this, item.key)
|
|
36863
36741
|
}));
|
|
36864
36742
|
if (item.state === ItemState.DRAGGING || item.state === ItemState.ANIMATING) {
|
|
36865
|
-
cards.push(
|
|
36743
|
+
cards.push(/*#__PURE__*/React.createElement(Placeholder, {
|
|
36866
36744
|
key: "placeholder_" + item.key,
|
|
36867
36745
|
ref: "placeholder_" + item.key,
|
|
36868
36746
|
width: item.width,
|
|
@@ -38652,7 +38530,6 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
38652
38530
|
if (!_.isFinite(props.tickStep) || props.tickStep <= 0) {
|
|
38653
38531
|
return []; // this has screwed me for the last time!
|
|
38654
38532
|
}
|
|
38655
|
-
|
|
38656
38533
|
const results = [];
|
|
38657
38534
|
|
|
38658
38535
|
// For convenience, extract some props into separate variables
|
|
@@ -38723,6 +38600,7 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
38723
38600
|
|
|
38724
38601
|
// TODO: most widgets use some like Widget<Something, PerseusNumberLineWidgetOptions>
|
|
38725
38602
|
// should this one?
|
|
38603
|
+
|
|
38726
38604
|
class NumberLine extends React.Component {
|
|
38727
38605
|
constructor(..._args) {
|
|
38728
38606
|
super(..._args);
|
|
@@ -39088,7 +38966,6 @@ const numberLineTransform = editorProps => {
|
|
|
39088
38966
|
} else {
|
|
39089
38967
|
numDivisions = undefined; // send to getDefaultProps()
|
|
39090
38968
|
}
|
|
39091
|
-
|
|
39092
38969
|
_.extend(props, {
|
|
39093
38970
|
numLinePosition: numLinePosition,
|
|
39094
38971
|
numDivisions: numDivisions,
|
|
@@ -39113,7 +38990,6 @@ const staticTransform$1 = editorProps => {
|
|
|
39113
38990
|
} else {
|
|
39114
38991
|
numDivisions = undefined; // send to getDefaultProps()
|
|
39115
38992
|
}
|
|
39116
|
-
|
|
39117
38993
|
_.extend(props, {
|
|
39118
38994
|
numLinePosition: numLinePosition,
|
|
39119
38995
|
numDivisions: numDivisions,
|
|
@@ -40102,7 +39978,6 @@ class Plotter extends React.Component {
|
|
|
40102
39978
|
const initialY = isMobile ? c.scaleY : 0;
|
|
40103
39979
|
for (let y = initialY; y <= c.dimY; y += c.scaleY) {
|
|
40104
39980
|
graphie.label([0, y], KhanMath.roundToApprox(y, 2), "left", /* isTeX */true /* for the \approx symbol */);
|
|
40105
|
-
|
|
40106
39981
|
graphie.style({
|
|
40107
39982
|
stroke: isMobile ? "#e9ebec" : "#000",
|
|
40108
39983
|
strokeWidth: 1,
|
|
@@ -41312,9 +41187,51 @@ const init = function init() {
|
|
|
41312
41187
|
replaceDeprecatedWidgets();
|
|
41313
41188
|
};
|
|
41314
41189
|
|
|
41190
|
+
/**
|
|
41191
|
+
* Adds the given perseus library version information to the __perseus_debug__
|
|
41192
|
+
* object and ensures that the object is attached to `globalThis` (`window` in
|
|
41193
|
+
* browser environments).
|
|
41194
|
+
*
|
|
41195
|
+
* This allows each library to provide runtime version information to assist in
|
|
41196
|
+
* debugging in production environments.
|
|
41197
|
+
*/
|
|
41198
|
+
const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
|
|
41199
|
+
// If the library version is the default value, then we don't want to
|
|
41200
|
+
// prefix it with a "v" to indicate that it is a version number.
|
|
41201
|
+
let prefix = "v";
|
|
41202
|
+
if (libraryVersion === "__lib_version__") {
|
|
41203
|
+
prefix = "";
|
|
41204
|
+
}
|
|
41205
|
+
const formattedVersion = `${prefix}${libraryVersion}`;
|
|
41206
|
+
if (typeof globalThis !== "undefined") {
|
|
41207
|
+
var _globalThis$__perseus;
|
|
41208
|
+
globalThis.__perseus_debug__ = (_globalThis$__perseus = globalThis.__perseus_debug__) != null ? _globalThis$__perseus : {};
|
|
41209
|
+
const existingVersionEntry = globalThis.__perseus_debug__[libraryName];
|
|
41210
|
+
if (existingVersionEntry) {
|
|
41211
|
+
// If we already have an entry and it doesn't match the registered
|
|
41212
|
+
// version, we morph the entry into an array and log a warning.
|
|
41213
|
+
if (existingVersionEntry !== formattedVersion) {
|
|
41214
|
+
// Existing entry might be an array already (oops, at least 2
|
|
41215
|
+
// versions of the library already loaded!).
|
|
41216
|
+
const allVersions = Array.isArray(existingVersionEntry) ? existingVersionEntry : [existingVersionEntry];
|
|
41217
|
+
allVersions.push(formattedVersion);
|
|
41218
|
+
globalThis.__perseus_debug__[libraryName] = allVersions;
|
|
41219
|
+
|
|
41220
|
+
// eslint-disable-next-line no-console
|
|
41221
|
+
console.warn(`Multiple versions of ${libraryName} loaded on this page: ${allVersions.sort().join(", ")}`);
|
|
41222
|
+
}
|
|
41223
|
+
} else {
|
|
41224
|
+
globalThis.__perseus_debug__[libraryName] = formattedVersion;
|
|
41225
|
+
}
|
|
41226
|
+
} else {
|
|
41227
|
+
// eslint-disable-next-line no-console
|
|
41228
|
+
console.warn(`globalThis not found found (${formattedVersion})`);
|
|
41229
|
+
}
|
|
41230
|
+
};
|
|
41231
|
+
|
|
41315
41232
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
41316
41233
|
const libName = "@khanacademy/perseus";
|
|
41317
|
-
const libVersion = "
|
|
41234
|
+
const libVersion = "55.0.0";
|
|
41318
41235
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
41319
41236
|
|
|
41320
41237
|
const apiVersion = {
|
|
@@ -41708,7 +41625,6 @@ class HintsRenderer extends React.Component {
|
|
|
41708
41625
|
ReactDOM__default.findDOMNode(this.refs["hintRenderer" + pos]).focus(); // eslint-disable-line react/no-string-refs
|
|
41709
41626
|
}
|
|
41710
41627
|
}
|
|
41711
|
-
|
|
41712
41628
|
render() {
|
|
41713
41629
|
const apiOptions = this.getApiOptions();
|
|
41714
41630
|
const hintsVisible = this._hintsVisible();
|