@pie-lib/mask-markup 1.14.1-beta.0 → 1.16.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/CHANGELOG.json +1 -871
- package/CHANGELOG.md +145 -1
- package/lib/choices/choice.js +221 -0
- package/lib/choices/choice.js.map +1 -0
- package/lib/choices/index.js +135 -0
- package/lib/choices/index.js.map +1 -0
- package/lib/componentize.js +25 -0
- package/lib/componentize.js.map +1 -0
- package/lib/components/blank.js +415 -0
- package/lib/components/blank.js.map +1 -0
- package/lib/components/correct-input.js +118 -0
- package/lib/components/correct-input.js.map +1 -0
- package/lib/components/dropdown.js +484 -0
- package/lib/components/dropdown.js.map +1 -0
- package/lib/components/input.js +57 -0
- package/lib/components/input.js.map +1 -0
- package/lib/constructed-response.js +116 -0
- package/lib/constructed-response.js.map +1 -0
- package/lib/customizable.js +48 -0
- package/lib/customizable.js.map +1 -0
- package/lib/drag-in-the-blank.js +217 -0
- package/lib/drag-in-the-blank.js.map +1 -0
- package/lib/index.js +62 -0
- package/lib/index.js.map +1 -0
- package/lib/inline-dropdown.js +48 -0
- package/lib/inline-dropdown.js.map +1 -0
- package/lib/mask.js +254 -0
- package/lib/mask.js.map +1 -0
- package/lib/serialization.js +207 -0
- package/lib/serialization.js.map +1 -0
- package/lib/shared/index.js +136 -0
- package/lib/with-mask.js +125 -0
- package/lib/with-mask.js.map +1 -0
- package/package.json +4 -5
- package/src/choices/choice.jsx +1 -1
- package/src/components/__tests__/__snapshots__/dropdown.test.js.snap +15 -12
- package/src/components/blank.jsx +1 -1
- package/src/components/correct-input.jsx +1 -1
- package/src/components/dropdown.jsx +118 -23
- package/src/constructed-response.jsx +1 -1
package/lib/with-mask.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.withMask = exports.buildLayoutFromMarkup = void 0;
|
|
9
|
+
|
|
10
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
11
|
+
|
|
12
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
13
|
+
|
|
14
|
+
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
|
|
15
|
+
|
|
16
|
+
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
|
|
17
|
+
|
|
18
|
+
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
|
|
19
|
+
|
|
20
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
21
|
+
|
|
22
|
+
var _react = _interopRequireDefault(require("react"));
|
|
23
|
+
|
|
24
|
+
var _reactDom = _interopRequireDefault(require("react-dom"));
|
|
25
|
+
|
|
26
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
27
|
+
|
|
28
|
+
var _mask = _interopRequireDefault(require("./mask"));
|
|
29
|
+
|
|
30
|
+
var _componentize2 = _interopRequireDefault(require("./componentize"));
|
|
31
|
+
|
|
32
|
+
var _serialization = require("./serialization");
|
|
33
|
+
|
|
34
|
+
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }
|
|
35
|
+
|
|
36
|
+
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
|
|
37
|
+
|
|
38
|
+
var buildLayoutFromMarkup = function buildLayoutFromMarkup(markup, type) {
|
|
39
|
+
var _componentize = (0, _componentize2["default"])(markup, type),
|
|
40
|
+
processed = _componentize.markup;
|
|
41
|
+
|
|
42
|
+
var value = (0, _serialization.deserialize)(processed);
|
|
43
|
+
return value.document;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
exports.buildLayoutFromMarkup = buildLayoutFromMarkup;
|
|
47
|
+
|
|
48
|
+
var withMask = function withMask(type, renderChildren) {
|
|
49
|
+
var _class;
|
|
50
|
+
|
|
51
|
+
return _class = /*#__PURE__*/function (_React$Component) {
|
|
52
|
+
(0, _inherits2["default"])(WithMask, _React$Component);
|
|
53
|
+
|
|
54
|
+
var _super = _createSuper(WithMask);
|
|
55
|
+
|
|
56
|
+
function WithMask() {
|
|
57
|
+
(0, _classCallCheck2["default"])(this, WithMask);
|
|
58
|
+
return _super.apply(this, arguments);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
(0, _createClass2["default"])(WithMask, [{
|
|
62
|
+
key: "componentDidUpdate",
|
|
63
|
+
value: function componentDidUpdate(prevProps) {
|
|
64
|
+
if (this.props.markup !== prevProps.markup) {
|
|
65
|
+
// eslint-disable-next-line
|
|
66
|
+
var domNode = _reactDom["default"].findDOMNode(this); // Query all elements that may contain outdated MathJax renderings
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
var mathElements = domNode && domNode.querySelectorAll('[data-latex][data-math-handled="true"]'); // Clean up for fresh MathJax processing
|
|
70
|
+
|
|
71
|
+
(mathElements || []).forEach(function (el) {
|
|
72
|
+
// Remove the MathJax container to allow for clean updates
|
|
73
|
+
var mjxContainer = el.querySelector('mjx-container');
|
|
74
|
+
|
|
75
|
+
if (mjxContainer) {
|
|
76
|
+
el.removeChild(mjxContainer);
|
|
77
|
+
} // Update the innerHTML to match the raw LaTeX data, ensuring it is reprocessed correctly
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
var latexCode = el.getAttribute('data-raw');
|
|
81
|
+
el.innerHTML = latexCode; // Remove the attribute to signal that MathJax should reprocess this element
|
|
82
|
+
|
|
83
|
+
el.removeAttribute('data-math-handled');
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}, {
|
|
88
|
+
key: "render",
|
|
89
|
+
value: function render() {
|
|
90
|
+
var _this$props = this.props,
|
|
91
|
+
markup = _this$props.markup,
|
|
92
|
+
layout = _this$props.layout,
|
|
93
|
+
value = _this$props.value,
|
|
94
|
+
onChange = _this$props.onChange,
|
|
95
|
+
elementType = _this$props.elementType;
|
|
96
|
+
var maskLayout = layout ? layout : buildLayoutFromMarkup(markup, type);
|
|
97
|
+
return /*#__PURE__*/_react["default"].createElement(_mask["default"], {
|
|
98
|
+
elementType: elementType,
|
|
99
|
+
layout: maskLayout,
|
|
100
|
+
value: value,
|
|
101
|
+
onChange: onChange,
|
|
102
|
+
renderChildren: renderChildren(this.props)
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}]);
|
|
106
|
+
return WithMask;
|
|
107
|
+
}(_react["default"].Component), (0, _defineProperty2["default"])(_class, "propTypes", {
|
|
108
|
+
/**
|
|
109
|
+
* At the start we'll probably work with markup
|
|
110
|
+
*/
|
|
111
|
+
markup: _propTypes["default"].string,
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Once we start authoring, it may make sense for use to us layout, which will be a simple js object that maps to `slate.Value`.
|
|
115
|
+
*/
|
|
116
|
+
layout: _propTypes["default"].object,
|
|
117
|
+
value: _propTypes["default"].object,
|
|
118
|
+
onChange: _propTypes["default"].func,
|
|
119
|
+
customMarkMarkupComponent: _propTypes["default"].func,
|
|
120
|
+
elementType: _propTypes["default"].string
|
|
121
|
+
}), _class;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
exports.withMask = withMask;
|
|
125
|
+
//# sourceMappingURL=with-mask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-mask.jsx"],"names":["buildLayoutFromMarkup","markup","type","processed","value","document","withMask","renderChildren","prevProps","props","domNode","ReactDOM","findDOMNode","mathElements","querySelectorAll","forEach","el","mjxContainer","querySelector","removeChild","latexCode","getAttribute","innerHTML","removeAttribute","layout","onChange","elementType","maskLayout","React","Component","PropTypes","string","object","func","customMarkMarkupComponent"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;AACA;;AACA;;AACA;;AACA;;AACA;;;;;;AAEO,IAAMA,qBAAqB,GAAG,SAAxBA,qBAAwB,CAACC,MAAD,EAASC,IAAT,EAAkB;AACrD,sBAA8B,+BAAaD,MAAb,EAAqBC,IAArB,CAA9B;AAAA,MAAgBC,SAAhB,iBAAQF,MAAR;;AACA,MAAMG,KAAK,GAAG,gCAAYD,SAAZ,CAAd;AACA,SAAOC,KAAK,CAACC,QAAb;AACD,CAJM;;;;AAMA,IAAMC,QAAQ,GAAG,SAAXA,QAAW,CAACJ,IAAD,EAAOK,cAAP,EAA0B;AAAA;;AAChD;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,aAgBE,4BAAmBC,SAAnB,EAA8B;AAC5B,YAAI,KAAKC,KAAL,CAAWR,MAAX,KAAsBO,SAAS,CAACP,MAApC,EAA4C;AAC1C;AACA,cAAMS,OAAO,GAAGC,qBAASC,WAAT,CAAqB,IAArB,CAAhB,CAF0C,CAG1C;;;AACA,cAAMC,YAAY,GAAGH,OAAO,IAAIA,OAAO,CAACI,gBAAR,CAAyB,wCAAzB,CAAhC,CAJ0C,CAM1C;;AACA,WAACD,YAAY,IAAI,EAAjB,EAAqBE,OAArB,CAA6B,UAACC,EAAD,EAAQ;AACnC;AACA,gBAAMC,YAAY,GAAGD,EAAE,CAACE,aAAH,CAAiB,eAAjB,CAArB;;AAEA,gBAAID,YAAJ,EAAkB;AAChBD,cAAAA,EAAE,CAACG,WAAH,CAAeF,YAAf;AACD,aANkC,CAQnC;;;AACA,gBAAMG,SAAS,GAAGJ,EAAE,CAACK,YAAH,CAAgB,UAAhB,CAAlB;AACAL,YAAAA,EAAE,CAACM,SAAH,GAAeF,SAAf,CAVmC,CAYnC;;AACAJ,YAAAA,EAAE,CAACO,eAAH,CAAmB,mBAAnB;AACD,WAdD;AAeD;AACF;AAxCH;AAAA;AAAA,aA0CE,kBAAS;AACP,0BAAyD,KAAKd,KAA9D;AAAA,YAAQR,MAAR,eAAQA,MAAR;AAAA,YAAgBuB,MAAhB,eAAgBA,MAAhB;AAAA,YAAwBpB,KAAxB,eAAwBA,KAAxB;AAAA,YAA+BqB,QAA/B,eAA+BA,QAA/B;AAAA,YAAyCC,WAAzC,eAAyCA,WAAzC;AAEA,YAAMC,UAAU,GAAGH,MAAM,GAAGA,MAAH,GAAYxB,qBAAqB,CAACC,MAAD,EAASC,IAAT,CAA1D;AACA,4BACE,gCAAC,gBAAD;AACE,UAAA,WAAW,EAAEwB,WADf;AAEE,UAAA,MAAM,EAAEC,UAFV;AAGE,UAAA,KAAK,EAAEvB,KAHT;AAIE,UAAA,QAAQ,EAAEqB,QAJZ;AAKE,UAAA,cAAc,EAAElB,cAAc,CAAC,KAAKE,KAAN;AALhC,UADF;AASD;AAvDH;AAAA;AAAA,IAA8BmB,kBAAMC,SAApC,yDACqB;AACjB;AACN;AACA;AACM5B,IAAAA,MAAM,EAAE6B,sBAAUC,MAJD;;AAKjB;AACN;AACA;AACMP,IAAAA,MAAM,EAAEM,sBAAUE,MARD;AASjB5B,IAAAA,KAAK,EAAE0B,sBAAUE,MATA;AAUjBP,IAAAA,QAAQ,EAAEK,sBAAUG,IAVH;AAWjBC,IAAAA,yBAAyB,EAAEJ,sBAAUG,IAXpB;AAYjBP,IAAAA,WAAW,EAAEI,sBAAUC;AAZN,GADrB;AAyDD,CA1DM","sourcesContent":["import React from 'react';\nimport ReactDOM from 'react-dom';\nimport PropTypes from 'prop-types';\nimport Mask from './mask';\nimport componentize from './componentize';\nimport { deserialize } from './serialization';\n\nexport const buildLayoutFromMarkup = (markup, type) => {\n const { markup: processed } = componentize(markup, type);\n const value = deserialize(processed);\n return value.document;\n};\n\nexport const withMask = (type, renderChildren) => {\n return class WithMask extends React.Component {\n static propTypes = {\n /**\n * At the start we'll probably work with markup\n */\n markup: PropTypes.string,\n /**\n * Once we start authoring, it may make sense for use to us layout, which will be a simple js object that maps to `slate.Value`.\n */\n layout: PropTypes.object,\n value: PropTypes.object,\n onChange: PropTypes.func,\n customMarkMarkupComponent: PropTypes.func,\n elementType: PropTypes.string,\n };\n\n componentDidUpdate(prevProps) {\n if (this.props.markup !== prevProps.markup) {\n // eslint-disable-next-line\n const domNode = ReactDOM.findDOMNode(this);\n // Query all elements that may contain outdated MathJax renderings\n const mathElements = domNode && domNode.querySelectorAll('[data-latex][data-math-handled=\"true\"]');\n\n // Clean up for fresh MathJax processing\n (mathElements || []).forEach((el) => {\n // Remove the MathJax container to allow for clean updates\n const mjxContainer = el.querySelector('mjx-container');\n\n if (mjxContainer) {\n el.removeChild(mjxContainer);\n }\n\n // Update the innerHTML to match the raw LaTeX data, ensuring it is reprocessed correctly\n const latexCode = el.getAttribute('data-raw');\n el.innerHTML = latexCode;\n\n // Remove the attribute to signal that MathJax should reprocess this element\n el.removeAttribute('data-math-handled');\n });\n }\n }\n\n render() {\n const { markup, layout, value, onChange, elementType } = this.props;\n\n const maskLayout = layout ? layout : buildLayoutFromMarkup(markup, type);\n return (\n <Mask\n elementType={elementType}\n layout={maskLayout}\n value={value}\n onChange={onChange}\n renderChildren={renderChildren(this.props)}\n />\n );\n }\n };\n};\n"],"file":"with-mask.js"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-lib/mask-markup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -10,10 +10,9 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@material-ui/core": "^3.9.3",
|
|
12
12
|
"@material-ui/icons": "^3.0.2",
|
|
13
|
-
"@pie-lib/drag": "
|
|
13
|
+
"@pie-lib/drag": "beta",
|
|
14
14
|
"@pie-lib/editable-html": "beta",
|
|
15
|
-
"@pie-lib/math-rendering": "
|
|
16
|
-
"@pie-lib/render-ui": "^4.16.1-beta.0",
|
|
15
|
+
"@pie-lib/math-rendering": "beta",
|
|
17
16
|
"classnames": "^2.2.6",
|
|
18
17
|
"debug": "^4.1.1",
|
|
19
18
|
"immutable": ">=3.8.1",
|
|
@@ -31,5 +30,5 @@
|
|
|
31
30
|
"keywords": [],
|
|
32
31
|
"author": "",
|
|
33
32
|
"license": "ISC",
|
|
34
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "ecb1ca31aa1c24d23af00d5ad04ce73ed8871057"
|
|
35
34
|
}
|
package/src/choices/choice.jsx
CHANGED
|
@@ -6,7 +6,7 @@ import Chip from '@material-ui/core/Chip';
|
|
|
6
6
|
import classnames from 'classnames';
|
|
7
7
|
|
|
8
8
|
import { renderMath } from '@pie-lib/math-rendering';
|
|
9
|
-
import { color } from '
|
|
9
|
+
import { color } from '../../../render-ui/src/index';
|
|
10
10
|
import { DragSource } from '@pie-lib/drag';
|
|
11
11
|
|
|
12
12
|
export const DRAG_TYPE = 'MaskBlank';
|
|
@@ -23,17 +23,18 @@ exports[`Dropdown render renders correctly with correct as true 1`] = `
|
|
|
23
23
|
}
|
|
24
24
|
classes={
|
|
25
25
|
Object {
|
|
26
|
-
"correctIcon": "Dropdown-correctIcon-
|
|
27
|
-
"correctnessIndicatorIcon": "Dropdown-correctnessIndicatorIcon-
|
|
26
|
+
"correctIcon": "Dropdown-correctIcon-11",
|
|
27
|
+
"correctnessIndicatorIcon": "Dropdown-correctnessIndicatorIcon-10",
|
|
28
28
|
"disabledCorrect": "Dropdown-disabledCorrect-2",
|
|
29
29
|
"disabledIncorrect": "Dropdown-disabledIncorrect-3",
|
|
30
|
-
"incorrectIcon": "Dropdown-incorrectIcon-
|
|
30
|
+
"incorrectIcon": "Dropdown-incorrectIcon-12",
|
|
31
31
|
"label": "Dropdown-label-7",
|
|
32
32
|
"menuRoot": "Dropdown-menuRoot-6",
|
|
33
33
|
"root": "Dropdown-root-1",
|
|
34
34
|
"selectMenu": "Dropdown-selectMenu-4",
|
|
35
35
|
"selected": "Dropdown-selected-5",
|
|
36
|
-
"
|
|
36
|
+
"selectedIndicator": "Dropdown-selectedIndicator-8",
|
|
37
|
+
"srOnly": "Dropdown-srOnly-9",
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
correct={true}
|
|
@@ -67,17 +68,18 @@ exports[`Dropdown render renders correctly with default props 1`] = `
|
|
|
67
68
|
}
|
|
68
69
|
classes={
|
|
69
70
|
Object {
|
|
70
|
-
"correctIcon": "Dropdown-correctIcon-
|
|
71
|
-
"correctnessIndicatorIcon": "Dropdown-correctnessIndicatorIcon-
|
|
71
|
+
"correctIcon": "Dropdown-correctIcon-11",
|
|
72
|
+
"correctnessIndicatorIcon": "Dropdown-correctnessIndicatorIcon-10",
|
|
72
73
|
"disabledCorrect": "Dropdown-disabledCorrect-2",
|
|
73
74
|
"disabledIncorrect": "Dropdown-disabledIncorrect-3",
|
|
74
|
-
"incorrectIcon": "Dropdown-incorrectIcon-
|
|
75
|
+
"incorrectIcon": "Dropdown-incorrectIcon-12",
|
|
75
76
|
"label": "Dropdown-label-7",
|
|
76
77
|
"menuRoot": "Dropdown-menuRoot-6",
|
|
77
78
|
"root": "Dropdown-root-1",
|
|
78
79
|
"selectMenu": "Dropdown-selectMenu-4",
|
|
79
80
|
"selected": "Dropdown-selected-5",
|
|
80
|
-
"
|
|
81
|
+
"selectedIndicator": "Dropdown-selectedIndicator-8",
|
|
82
|
+
"srOnly": "Dropdown-srOnly-9",
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
correct={false}
|
|
@@ -111,17 +113,18 @@ exports[`Dropdown render renders correctly with disabled prop as true 1`] = `
|
|
|
111
113
|
}
|
|
112
114
|
classes={
|
|
113
115
|
Object {
|
|
114
|
-
"correctIcon": "Dropdown-correctIcon-
|
|
115
|
-
"correctnessIndicatorIcon": "Dropdown-correctnessIndicatorIcon-
|
|
116
|
+
"correctIcon": "Dropdown-correctIcon-11",
|
|
117
|
+
"correctnessIndicatorIcon": "Dropdown-correctnessIndicatorIcon-10",
|
|
116
118
|
"disabledCorrect": "Dropdown-disabledCorrect-2",
|
|
117
119
|
"disabledIncorrect": "Dropdown-disabledIncorrect-3",
|
|
118
|
-
"incorrectIcon": "Dropdown-incorrectIcon-
|
|
120
|
+
"incorrectIcon": "Dropdown-incorrectIcon-12",
|
|
119
121
|
"label": "Dropdown-label-7",
|
|
120
122
|
"menuRoot": "Dropdown-menuRoot-6",
|
|
121
123
|
"root": "Dropdown-root-1",
|
|
122
124
|
"selectMenu": "Dropdown-selectMenu-4",
|
|
123
125
|
"selected": "Dropdown-selected-5",
|
|
124
|
-
"
|
|
126
|
+
"selectedIndicator": "Dropdown-selectedIndicator-8",
|
|
127
|
+
"srOnly": "Dropdown-srOnly-9",
|
|
125
128
|
}
|
|
126
129
|
}
|
|
127
130
|
correct={false}
|
package/src/components/blank.jsx
CHANGED
|
@@ -8,7 +8,7 @@ import { DragSource, DropTarget } from '@pie-lib/drag';
|
|
|
8
8
|
import { withStyles } from '@material-ui/core/styles';
|
|
9
9
|
import Chip from '@material-ui/core/Chip';
|
|
10
10
|
import classnames from 'classnames';
|
|
11
|
-
import { color } from '
|
|
11
|
+
import { color } from '../../../render-ui/src/index';
|
|
12
12
|
|
|
13
13
|
const log = debug('pie-lib:mask-markup:blank');
|
|
14
14
|
export const DRAG_TYPE = 'MaskBlank';
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import OutlinedInput from '@material-ui/core/OutlinedInput';
|
|
3
3
|
import classnames from 'classnames';
|
|
4
4
|
import { withStyles } from '@material-ui/core/styles';
|
|
5
|
-
import { color } from '
|
|
5
|
+
import { color } from '../../../render-ui/src/index';
|
|
6
6
|
|
|
7
7
|
const correctStyle = (color) => ({
|
|
8
8
|
borderColor: `${color} !important`,
|
|
@@ -5,12 +5,14 @@ import InputLabel from '@material-ui/core/InputLabel';
|
|
|
5
5
|
import Menu from '@material-ui/core/Menu';
|
|
6
6
|
import MenuItem from '@material-ui/core/MenuItem';
|
|
7
7
|
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
|
|
8
|
+
import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp';
|
|
8
9
|
import Close from '@material-ui/icons/Close';
|
|
9
10
|
import Check from '@material-ui/icons/Check';
|
|
10
11
|
import { withStyles } from '@material-ui/core/styles';
|
|
11
12
|
import classNames from 'classnames';
|
|
13
|
+
import isEqual from 'lodash/isEqual';
|
|
12
14
|
|
|
13
|
-
import { color } from '
|
|
15
|
+
import { color } from '../../../render-ui/src/index';
|
|
14
16
|
import { renderMath } from '@pie-lib/math-rendering';
|
|
15
17
|
|
|
16
18
|
class Dropdown extends React.Component {
|
|
@@ -23,6 +25,8 @@ class Dropdown extends React.Component {
|
|
|
23
25
|
correct: PropTypes.bool,
|
|
24
26
|
choices: PropTypes.arrayOf(PropTypes.shape({ value: PropTypes.string, label: PropTypes.string })),
|
|
25
27
|
showCorrectAnswer: PropTypes.bool,
|
|
28
|
+
singleQuery: PropTypes.bool,
|
|
29
|
+
correctValue: PropTypes.string,
|
|
26
30
|
};
|
|
27
31
|
|
|
28
32
|
constructor(props) {
|
|
@@ -31,27 +35,73 @@ class Dropdown extends React.Component {
|
|
|
31
35
|
this.state = {
|
|
32
36
|
anchorEl: null,
|
|
33
37
|
highlightedOptionId: null,
|
|
38
|
+
menuWidth: null,
|
|
39
|
+
previewValue: null,
|
|
34
40
|
};
|
|
35
|
-
|
|
41
|
+
this.hiddenRef = React.createRef();
|
|
42
|
+
this.buttonRef = React.createRef();
|
|
43
|
+
this.previewRef = React.createRef();
|
|
36
44
|
this.elementRefs = [];
|
|
37
45
|
}
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
componentDidMount() {
|
|
48
|
+
// measure hidden menu width once
|
|
49
|
+
if (this.hiddenRef.current && this.state.menuWidth === null) {
|
|
50
|
+
this.setState({ menuWidth: this.hiddenRef.current.clientWidth });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
componentDidUpdate(prevProps, prevState) {
|
|
55
|
+
const hiddenEl = this.hiddenRef.current;
|
|
56
|
+
|
|
57
|
+
const dropdownJustOpened = !prevState.anchorEl && this.state.anchorEl;
|
|
58
|
+
if (dropdownJustOpened) {
|
|
59
|
+
this.elementRefs.forEach((ref) => {
|
|
60
|
+
if (!ref) return;
|
|
61
|
+
|
|
62
|
+
const containsLatex = ref.querySelector('[data-latex], [data-raw]');
|
|
63
|
+
const hasMathJax = ref.querySelector('mjx-container');
|
|
64
|
+
const mathHandled = ref.querySelector('[data-math-handled="true"]');
|
|
65
|
+
|
|
66
|
+
if (containsLatex && (!mathHandled || !hasMathJax)) {
|
|
67
|
+
renderMath(ref);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (hiddenEl) {
|
|
73
|
+
const newWidth = hiddenEl.clientWidth;
|
|
74
|
+
if (newWidth !== this.state.menuWidth) {
|
|
75
|
+
this.elementRefs.forEach((ref) => {
|
|
76
|
+
if (ref) renderMath(ref);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
renderMath(hiddenEl);
|
|
80
|
+
this.setState({ menuWidth: newWidth });
|
|
43
81
|
}
|
|
44
|
-
}
|
|
82
|
+
}
|
|
45
83
|
}
|
|
46
84
|
|
|
47
85
|
handleClick = (event) => this.setState({ anchorEl: event.currentTarget });
|
|
48
86
|
|
|
49
|
-
handleClose = () =>
|
|
87
|
+
handleClose = () => {
|
|
88
|
+
const { value } = this.props;
|
|
89
|
+
this.setState({ anchorEl: null, previewValue: null, highlightedOptionId: null });
|
|
90
|
+
// clear displayed preview if no selection
|
|
91
|
+
if (!value && this.previewRef.current) {
|
|
92
|
+
this.previewRef.current.innerHTML = '';
|
|
93
|
+
}
|
|
94
|
+
};
|
|
50
95
|
|
|
51
96
|
handleHighlight = (index) => {
|
|
52
97
|
const highlightedOptionId = `dropdown-option-${this.props.id}-${index}`;
|
|
53
98
|
|
|
54
|
-
|
|
99
|
+
// preview on hover if nothing selected
|
|
100
|
+
const stateUpdate = { highlightedOptionId };
|
|
101
|
+
if (!this.props.value) {
|
|
102
|
+
stateUpdate.previewValue = this.props.choices[index].value;
|
|
103
|
+
}
|
|
104
|
+
this.setState(stateUpdate);
|
|
55
105
|
};
|
|
56
106
|
|
|
57
107
|
handleSelect = (value, index) => {
|
|
@@ -60,6 +110,25 @@ class Dropdown extends React.Component {
|
|
|
60
110
|
this.handleClose();
|
|
61
111
|
};
|
|
62
112
|
|
|
113
|
+
handleHover = (index) => {
|
|
114
|
+
const selectedValue = this.props.value;
|
|
115
|
+
|
|
116
|
+
if (selectedValue) return;
|
|
117
|
+
|
|
118
|
+
const highlightedOptionId = `dropdown-option-${this.props.id}-${index}`;
|
|
119
|
+
const previewValue = this.state.previewValue;
|
|
120
|
+
|
|
121
|
+
this.setState({ highlightedOptionId, previewValue }, () => {
|
|
122
|
+
// On hover, preview the math-rendered content inside the button if no value is selected.
|
|
123
|
+
const ref = this.elementRefs[index];
|
|
124
|
+
const preview = this.previewRef.current;
|
|
125
|
+
|
|
126
|
+
if (ref && preview) {
|
|
127
|
+
preview.innerHTML = ref.innerHTML;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
|
|
63
132
|
getLabel(choices, value) {
|
|
64
133
|
const found = (choices || []).find((choice) => choice.value === value);
|
|
65
134
|
|
|
@@ -68,7 +137,6 @@ class Dropdown extends React.Component {
|
|
|
68
137
|
|
|
69
138
|
render() {
|
|
70
139
|
const { classes, id, correct, disabled, value, choices, showCorrectAnswer, singleQuery, correctValue } = this.props;
|
|
71
|
-
|
|
72
140
|
const { anchorEl } = this.state;
|
|
73
141
|
const open = Boolean(anchorEl);
|
|
74
142
|
const buttonId = `dropdown-button-${id}`;
|
|
@@ -90,7 +158,6 @@ class Dropdown extends React.Component {
|
|
|
90
158
|
const labelText = singleQuery ? 'Query' : `Query ${incrementedId}`;
|
|
91
159
|
|
|
92
160
|
// Changed from Select to Button for dropdown to enhance accessibility. This modification offers explicit control over aria attributes and focuses management, ensuring the dropdown is compliant with accessibility standards. The use of Button and Menu components allows for better handling of keyboard interactions and provides accessible labels and menus, aligning with WCAG guidelines and improving usability for assistive technology users.
|
|
93
|
-
|
|
94
161
|
let correctnessIcon = null;
|
|
95
162
|
if (disabled && correct !== undefined) {
|
|
96
163
|
correctnessIcon =
|
|
@@ -103,10 +170,23 @@ class Dropdown extends React.Component {
|
|
|
103
170
|
|
|
104
171
|
return (
|
|
105
172
|
<>
|
|
173
|
+
<div ref={this.hiddenRef} style={{ position: 'absolute', visibility: 'hidden', top: 0, left: 0 }}>
|
|
174
|
+
{(choices || []).map((c, index) => (
|
|
175
|
+
<MenuItem key={index} classes={{ root: classes.menuRoot, selected: classes.selected }}>
|
|
176
|
+
<span className={classes.label} dangerouslySetInnerHTML={{ __html: c.label }} />
|
|
177
|
+
</MenuItem>
|
|
178
|
+
))}
|
|
179
|
+
</div>
|
|
106
180
|
<InputLabel className={classes.srOnly} id={labelId}>
|
|
107
181
|
{labelText}
|
|
108
182
|
</InputLabel>
|
|
109
183
|
<Button
|
|
184
|
+
ref={this.buttonRef}
|
|
185
|
+
style={{
|
|
186
|
+
...(this.state.menuWidth && { minWidth: `calc(${this.state.menuWidth}px + 8px)` }),
|
|
187
|
+
borderWidth: open ? '2px' : '1px',
|
|
188
|
+
transition: 'border-width 0.2s ease-in-out',
|
|
189
|
+
}}
|
|
110
190
|
aria-controls={open ? menuId : undefined}
|
|
111
191
|
aria-haspopup="listbox"
|
|
112
192
|
aria-expanded={open ? 'true' : undefined}
|
|
@@ -125,12 +205,17 @@ class Dropdown extends React.Component {
|
|
|
125
205
|
{correctnessIcon}
|
|
126
206
|
<span
|
|
127
207
|
id={valueDisplayId}
|
|
208
|
+
ref={this.previewRef}
|
|
128
209
|
className={classes.label}
|
|
129
210
|
dangerouslySetInnerHTML={{
|
|
130
|
-
__html: correctValue
|
|
211
|
+
__html: correctValue
|
|
212
|
+
? correctValue
|
|
213
|
+
: open && this.state.previewValue
|
|
214
|
+
? this.getLabel(choices, this.state.previewValue)
|
|
215
|
+
: this.getLabel(choices, value) || '',
|
|
131
216
|
}}
|
|
132
217
|
/>
|
|
133
|
-
<ArrowDropDownIcon />
|
|
218
|
+
{open ? <ArrowDropUpIcon /> : <ArrowDropDownIcon />}
|
|
134
219
|
</Button>
|
|
135
220
|
<Menu
|
|
136
221
|
id={menuId}
|
|
@@ -139,9 +224,14 @@ class Dropdown extends React.Component {
|
|
|
139
224
|
keepMounted
|
|
140
225
|
open={open}
|
|
141
226
|
onClose={this.handleClose}
|
|
227
|
+
getContentAnchorEl={null}
|
|
228
|
+
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
|
229
|
+
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
|
230
|
+
PaperProps={this.state.menuWidth ? { style: { minWidth: this.state.menuWidth, padding: '4px' } } : undefined}
|
|
142
231
|
MenuListProps={{
|
|
143
232
|
'aria-labelledby': buttonId,
|
|
144
233
|
role: 'listbox',
|
|
234
|
+
disablePadding: true,
|
|
145
235
|
}}
|
|
146
236
|
>
|
|
147
237
|
{(choices || []).map((c, index) => {
|
|
@@ -156,6 +246,7 @@ class Dropdown extends React.Component {
|
|
|
156
246
|
onClick={() => this.handleSelect(c.value, index)}
|
|
157
247
|
role="option"
|
|
158
248
|
aria-selected={this.state.highlightedOptionId === optionId ? 'true' : undefined}
|
|
249
|
+
onMouseOver={() => this.handleHover(index)}
|
|
159
250
|
>
|
|
160
251
|
<span
|
|
161
252
|
ref={(ref) => (this.elementRefs[index] = ref)}
|
|
@@ -163,7 +254,7 @@ class Dropdown extends React.Component {
|
|
|
163
254
|
dangerouslySetInnerHTML={{ __html: c.label }}
|
|
164
255
|
/>
|
|
165
256
|
<span
|
|
166
|
-
className={classes.
|
|
257
|
+
className={classes.selectedIndicator}
|
|
167
258
|
dangerouslySetInnerHTML={{ __html: c.value === value ? ' ✓' : '' }}
|
|
168
259
|
/>
|
|
169
260
|
</MenuItem>
|
|
@@ -178,7 +269,7 @@ class Dropdown extends React.Component {
|
|
|
178
269
|
const styles = () => ({
|
|
179
270
|
root: {
|
|
180
271
|
color: color.text(),
|
|
181
|
-
border: `1px solid ${color.
|
|
272
|
+
border: `1px solid ${color.borderGray()}`,
|
|
182
273
|
borderRadius: '4px',
|
|
183
274
|
justifyContent: 'space-between',
|
|
184
275
|
backgroundColor: color.background(),
|
|
@@ -220,13 +311,17 @@ const styles = () => ({
|
|
|
220
311
|
border: `1px solid ${color.text()}`,
|
|
221
312
|
borderColor: 'initial',
|
|
222
313
|
},
|
|
314
|
+
// remove default padding on the inner list
|
|
315
|
+
'& .MuiList-root': {
|
|
316
|
+
padding: 0,
|
|
317
|
+
},
|
|
223
318
|
},
|
|
224
319
|
selected: {
|
|
225
320
|
color: `${color.text()} !important`,
|
|
226
321
|
backgroundColor: `${color.background()} !important`,
|
|
227
322
|
'&:hover': {
|
|
228
323
|
color: color.text(),
|
|
229
|
-
backgroundColor: `${color.
|
|
324
|
+
backgroundColor: `${color.dropdownBackground()} !important`,
|
|
230
325
|
},
|
|
231
326
|
},
|
|
232
327
|
menuRoot: {
|
|
@@ -238,20 +333,20 @@ const styles = () => ({
|
|
|
238
333
|
},
|
|
239
334
|
'&:hover': {
|
|
240
335
|
color: color.text(),
|
|
241
|
-
backgroundColor: color.
|
|
336
|
+
backgroundColor: color.dropdownBackground(),
|
|
242
337
|
},
|
|
243
338
|
boxSizing: 'border-box',
|
|
244
339
|
padding: '25px',
|
|
245
|
-
'
|
|
246
|
-
borderRadius: '3px 3px 0 0',
|
|
247
|
-
},
|
|
248
|
-
'&:last-of-type': {
|
|
249
|
-
borderRadius: '0 0 3px 3px',
|
|
250
|
-
},
|
|
340
|
+
borderRadius: '4px',
|
|
251
341
|
},
|
|
252
342
|
label: {
|
|
253
343
|
fontSize: 'max(1rem, 14px)',
|
|
254
344
|
},
|
|
345
|
+
selectedIndicator: {
|
|
346
|
+
fontSize: 'max(1rem, 14px)',
|
|
347
|
+
position: 'absolute',
|
|
348
|
+
right: '10px',
|
|
349
|
+
},
|
|
255
350
|
srOnly: {
|
|
256
351
|
position: 'absolute',
|
|
257
352
|
left: '-10000px',
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { withStyles } from '@material-ui/core/styles';
|
|
3
3
|
import classnames from 'classnames';
|
|
4
4
|
|
|
5
|
-
import { color } from '
|
|
5
|
+
import { color } from '../../render-ui/src/index';
|
|
6
6
|
import EditableHtml from '@pie-lib/editable-html';
|
|
7
7
|
import { withMask } from './with-mask';
|
|
8
8
|
|