@mathscan/math-exercise-engine 1.2.12 → 1.2.13
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/README.md +3 -1
- package/dist/{GeometryWidget-BMOiQMvj.js → GeometryWidget-CMJTNSAG.js} +2 -2
- package/dist/{GeometryWidget-BMOiQMvj.js.map → GeometryWidget-CMJTNSAG.js.map} +1 -1
- package/dist/{GeometryWidget-Qi5s7Lql.mjs → GeometryWidget-h2yNZsas.mjs} +2 -2
- package/dist/{GeometryWidget-Qi5s7Lql.mjs.map → GeometryWidget-h2yNZsas.mjs.map} +1 -1
- package/dist/{index-BtJRsuwL.mjs → index-CAvdtiDQ.mjs} +70 -12
- package/dist/index-CAvdtiDQ.mjs.map +1 -0
- package/dist/{index-Di53h19p.js → index-CbKXPRsS.js} +70 -12
- package/dist/index-CbKXPRsS.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/index-BtJRsuwL.mjs.map +0 -1
- package/dist/index-Di53h19p.js.map +0 -1
|
@@ -17160,12 +17160,17 @@ function getTextInputs(container) {
|
|
|
17160
17160
|
function canTakeInput(el) {
|
|
17161
17161
|
return !el.readOnly && !el.disabled;
|
|
17162
17162
|
}
|
|
17163
|
+
function canAdvanceTo(el) {
|
|
17164
|
+
return !el.disabled && el.tabIndex >= 0;
|
|
17165
|
+
}
|
|
17163
17166
|
function findNextInput(container, currentKey) {
|
|
17164
17167
|
const inputs = getTextInputs(container);
|
|
17165
17168
|
const currentIndex = inputs.findIndex((el) => el.id === getInputElementId(currentKey));
|
|
17166
17169
|
if (currentIndex === -1) return null;
|
|
17167
|
-
return inputs.slice(currentIndex + 1).find(
|
|
17170
|
+
return inputs.slice(currentIndex + 1).find(canAdvanceTo) ?? null;
|
|
17168
17171
|
}
|
|
17172
|
+
const KeypadContext = React.createContext(false);
|
|
17173
|
+
const useHasKeypad = () => React.useContext(KeypadContext);
|
|
17169
17174
|
const InlineInput = ({
|
|
17170
17175
|
widgetId,
|
|
17171
17176
|
value,
|
|
@@ -17182,6 +17187,7 @@ const InlineInput = ({
|
|
|
17182
17187
|
}) => {
|
|
17183
17188
|
const playSound = useSound();
|
|
17184
17189
|
const locked = useExerciseLocked();
|
|
17190
|
+
const hasKeypad = useHasKeypad();
|
|
17185
17191
|
const handleChange = React.useCallback((newValue, fromDrop = false, sourceOptionId) => {
|
|
17186
17192
|
onChange(widgetId, newValue, fromDrop, sourceOptionId);
|
|
17187
17193
|
}, [onChange, widgetId]);
|
|
@@ -17236,6 +17242,7 @@ const InlineInput = ({
|
|
|
17236
17242
|
},
|
|
17237
17243
|
onFocus: () => onFocus?.(widgetId),
|
|
17238
17244
|
readOnly: locked,
|
|
17245
|
+
inputMode: hasKeypad ? "none" : void 0,
|
|
17239
17246
|
disabled,
|
|
17240
17247
|
maxLength: expectedLength,
|
|
17241
17248
|
style: { width: dynamicWidth },
|
|
@@ -17438,6 +17445,7 @@ const FractionInput = ({
|
|
|
17438
17445
|
}) => {
|
|
17439
17446
|
const playSound = useSound();
|
|
17440
17447
|
const locked = useExerciseLocked();
|
|
17448
|
+
const hasKeypad = useHasKeypad();
|
|
17441
17449
|
const numKey = getFractionKey(widgetId, "num");
|
|
17442
17450
|
const denKey = getFractionKey(widgetId, "den");
|
|
17443
17451
|
const numValue = values[numKey] ?? "";
|
|
@@ -17491,6 +17499,7 @@ const FractionInput = ({
|
|
|
17491
17499
|
},
|
|
17492
17500
|
onFocus: () => onFocus?.(subKey),
|
|
17493
17501
|
readOnly: locked,
|
|
17502
|
+
inputMode: hasKeypad ? "none" : void 0,
|
|
17494
17503
|
maxLength: placeholder ? Math.max(20, placeholder.length) : 4,
|
|
17495
17504
|
size: placeholder ? placeholder.length : void 0,
|
|
17496
17505
|
"aria-label": label,
|
|
@@ -19701,7 +19710,7 @@ function isGeometryWidget(widget) {
|
|
|
19701
19710
|
return widget.type === JSON_TYPES.GEOMETRY;
|
|
19702
19711
|
}
|
|
19703
19712
|
const GeometryWidget = React.lazy(async () => ({
|
|
19704
|
-
default: (await Promise.resolve().then(() => require("./GeometryWidget-
|
|
19713
|
+
default: (await Promise.resolve().then(() => require("./GeometryWidget-CMJTNSAG.js"))).GeometryWidget
|
|
19705
19714
|
}));
|
|
19706
19715
|
const WidgetRenderer = ({
|
|
19707
19716
|
widget,
|
|
@@ -22397,6 +22406,7 @@ const TOOL_ASPECT_RATIO = {
|
|
|
22397
22406
|
"set-square": SET_SQUARE_ASPECT_RATIO
|
|
22398
22407
|
};
|
|
22399
22408
|
const FLIP_BTN_EXTRA = 44;
|
|
22409
|
+
const INPUT_FINISH_IDLE_MS = 1e3;
|
|
22400
22410
|
function warnOnInvalidThemeColors(theme) {
|
|
22401
22411
|
if (process.env.NODE_ENV === "production" || !theme) return;
|
|
22402
22412
|
if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return;
|
|
@@ -22434,6 +22444,9 @@ function hasJustFilledBox(json, key2, previous, value) {
|
|
|
22434
22444
|
if (maxLength === void 0) return false;
|
|
22435
22445
|
return String(previous ?? "").length < maxLength && String(value).length >= maxLength;
|
|
22436
22446
|
}
|
|
22447
|
+
function isTypedInputKey(json, key2) {
|
|
22448
|
+
return json.widgets[key2]?.type === JSON_TYPES.INLINE_INPUT || Boolean(parseFractionKey(key2));
|
|
22449
|
+
}
|
|
22437
22450
|
function buildInitialValuesSignature(json) {
|
|
22438
22451
|
return JSON.stringify({
|
|
22439
22452
|
widgets: json.widgets,
|
|
@@ -22519,6 +22532,24 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22519
22532
|
const valuesRef = React.useRef(values);
|
|
22520
22533
|
valuesRef.current = values;
|
|
22521
22534
|
const touchedRef = React.useRef(/* @__PURE__ */ new Set());
|
|
22535
|
+
const pendingRef = React.useRef(/* @__PURE__ */ new Map());
|
|
22536
|
+
const onChangeRef = React.useRef(onChange);
|
|
22537
|
+
onChangeRef.current = onChange;
|
|
22538
|
+
const clearPending = (key2) => {
|
|
22539
|
+
const timer = pendingRef.current.get(key2);
|
|
22540
|
+
if (timer === void 0) return false;
|
|
22541
|
+
clearTimeout(timer);
|
|
22542
|
+
pendingRef.current.delete(key2);
|
|
22543
|
+
return true;
|
|
22544
|
+
};
|
|
22545
|
+
const clearAllPending = () => {
|
|
22546
|
+
pendingRef.current.forEach((timer) => clearTimeout(timer));
|
|
22547
|
+
pendingRef.current = /* @__PURE__ */ new Map();
|
|
22548
|
+
};
|
|
22549
|
+
const finishPending = (key2) => {
|
|
22550
|
+
if (clearPending(key2)) onChangeRef.current?.(valuesRef.current);
|
|
22551
|
+
};
|
|
22552
|
+
React.useEffect(() => clearAllPending, []);
|
|
22522
22553
|
const [loadedValuesSignature, setLoadedValuesSignature] = React.useState(initialValuesSignature);
|
|
22523
22554
|
const [droppedSources, setDroppedSources] = React.useState(/* @__PURE__ */ new Map());
|
|
22524
22555
|
const [focusedInputId, setFocusedInputId] = React.useState(null);
|
|
@@ -22549,11 +22580,13 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22549
22580
|
setLoadedValuesSignature(initialValuesSignature);
|
|
22550
22581
|
touchedRef.current = /* @__PURE__ */ new Set();
|
|
22551
22582
|
setTutorialRun(null);
|
|
22583
|
+
clearAllPending();
|
|
22552
22584
|
}, [json, initialValuesSignature, loadedValuesSignature]);
|
|
22553
22585
|
const locked = validationDetails !== null || externallyLocked;
|
|
22554
22586
|
const lastResultRef = React.useRef(null);
|
|
22555
22587
|
const runSubmit = () => {
|
|
22556
22588
|
if (locked) return lastResultRef.current ?? validate(json, values);
|
|
22589
|
+
clearAllPending();
|
|
22557
22590
|
const result = validate(json, values);
|
|
22558
22591
|
playSound(result.isCorrect ? "answer-correct" : "answer-wrong");
|
|
22559
22592
|
const details = { ...result.details };
|
|
@@ -22591,6 +22624,11 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22591
22624
|
for (const id of answerable) {
|
|
22592
22625
|
if (!touchedRef.current.has(id)) missingSet.add(id);
|
|
22593
22626
|
}
|
|
22627
|
+
const isAnyFilled = answerable.some((id) => !missingSet.has(id));
|
|
22628
|
+
for (const key2 of pendingRef.current.keys()) {
|
|
22629
|
+
const id = toBaseId(key2);
|
|
22630
|
+
if (answerable.includes(id)) missingSet.add(id);
|
|
22631
|
+
}
|
|
22594
22632
|
const missing = [...missingSet];
|
|
22595
22633
|
const filledIds = answerable.filter((id) => !missingSet.has(id));
|
|
22596
22634
|
return {
|
|
@@ -22599,7 +22637,7 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22599
22637
|
missing,
|
|
22600
22638
|
filledIds,
|
|
22601
22639
|
isComplete: answerable.length > 0 && missing.length === 0,
|
|
22602
|
-
isAnyFilled
|
|
22640
|
+
isAnyFilled
|
|
22603
22641
|
};
|
|
22604
22642
|
},
|
|
22605
22643
|
reset: () => {
|
|
@@ -22613,10 +22651,12 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22613
22651
|
setRevealed(false);
|
|
22614
22652
|
setLoadedValuesSignature(initialValuesSignature);
|
|
22615
22653
|
touchedRef.current = /* @__PURE__ */ new Set();
|
|
22654
|
+
clearAllPending();
|
|
22616
22655
|
},
|
|
22617
22656
|
revealAnswers: () => {
|
|
22618
22657
|
const revealedValues = buildRevealedValues(json);
|
|
22619
22658
|
if (Object.keys(revealedValues).length === 0) return;
|
|
22659
|
+
clearAllPending();
|
|
22620
22660
|
playSound("answer-reveal");
|
|
22621
22661
|
const details = {};
|
|
22622
22662
|
for (const [widgetId, userValue] of Object.entries(revealedValues)) {
|
|
@@ -22676,11 +22716,28 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22676
22716
|
}
|
|
22677
22717
|
return next2;
|
|
22678
22718
|
});
|
|
22719
|
+
if (!silent && isTypedInputKey(json, widgetId)) {
|
|
22720
|
+
clearPending(widgetId);
|
|
22721
|
+
const maxLength = getMaxInputLength(json, widgetId);
|
|
22722
|
+
const isFull = maxLength !== void 0 && String(value).length >= maxLength;
|
|
22723
|
+
if (!fromDrop && value !== "" && !isFull) {
|
|
22724
|
+
pendingRef.current.set(
|
|
22725
|
+
widgetId,
|
|
22726
|
+
setTimeout(() => finishPending(widgetId), INPUT_FINISH_IDLE_MS)
|
|
22727
|
+
);
|
|
22728
|
+
}
|
|
22729
|
+
}
|
|
22679
22730
|
onChange?.(next);
|
|
22680
22731
|
if (!fromDrop && !silent && hasJustFilledBox(json, widgetId, previous, value)) {
|
|
22681
22732
|
focusNextInput(widgetId);
|
|
22682
22733
|
}
|
|
22683
22734
|
};
|
|
22735
|
+
const handleInputFocus = (key2) => {
|
|
22736
|
+
for (const pendingKey of [...pendingRef.current.keys()]) {
|
|
22737
|
+
if (pendingKey !== key2) finishPending(pendingKey);
|
|
22738
|
+
}
|
|
22739
|
+
setFocusedInputId(key2);
|
|
22740
|
+
};
|
|
22684
22741
|
const handleSubmit = () => {
|
|
22685
22742
|
runSubmit();
|
|
22686
22743
|
};
|
|
@@ -22813,7 +22870,7 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22813
22870
|
if (setSquareWidget) add2("set-square-1", "set-square", Math.round(getSize(setSquareWidget) * 160));
|
|
22814
22871
|
return { tools: list, toolsMinHeight: maxH };
|
|
22815
22872
|
}, [rulerWidget, protractorWidget, setSquareWidget]);
|
|
22816
|
-
return /* @__PURE__ */ jsxRuntime.jsx(SoundPlayerProvider, { play: playSound, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseLockProvider, { locked, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseDndProvider, { droppedSources, exerciseKey: loadedValuesSignature, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: `math-exercise-renderer ${revealed ? "revealed" : ""} ${locked ? "locked" : ""} ${className}`, style: themeStyle, children: [
|
|
22873
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SoundPlayerProvider, { play: playSound, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseLockProvider, { locked, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseDndProvider, { droppedSources, exerciseKey: loadedValuesSignature, children: /* @__PURE__ */ jsxRuntime.jsx(KeypadContext.Provider, { value: Boolean(numericKeypadWidget), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: `math-exercise-renderer ${revealed ? "revealed" : ""} ${locked ? "locked" : ""} ${className}`, style: themeStyle, children: [
|
|
22817
22874
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
22818
22875
|
"div",
|
|
22819
22876
|
{
|
|
@@ -22838,7 +22895,7 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22838
22895
|
droppedInputs,
|
|
22839
22896
|
validationDetails,
|
|
22840
22897
|
onChange: handleChange,
|
|
22841
|
-
onInputFocus:
|
|
22898
|
+
onInputFocus: handleInputFocus,
|
|
22842
22899
|
resetKey
|
|
22843
22900
|
},
|
|
22844
22901
|
item.id
|
|
@@ -22854,7 +22911,7 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22854
22911
|
droppedInputs,
|
|
22855
22912
|
validationDetails,
|
|
22856
22913
|
onChange: handleChange,
|
|
22857
|
-
onInputFocus:
|
|
22914
|
+
onInputFocus: handleInputFocus,
|
|
22858
22915
|
resetKey
|
|
22859
22916
|
},
|
|
22860
22917
|
item.id
|
|
@@ -22871,7 +22928,7 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22871
22928
|
droppedInputs,
|
|
22872
22929
|
validationDetails,
|
|
22873
22930
|
onChange: handleChange,
|
|
22874
|
-
onInputFocus:
|
|
22931
|
+
onInputFocus: handleInputFocus,
|
|
22875
22932
|
resetKey
|
|
22876
22933
|
},
|
|
22877
22934
|
item.id
|
|
@@ -22889,9 +22946,10 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22889
22946
|
onChange: () => {
|
|
22890
22947
|
},
|
|
22891
22948
|
onKeypadChange: (newValue) => {
|
|
22892
|
-
if (focusedInputId)
|
|
22893
|
-
|
|
22894
|
-
|
|
22949
|
+
if (!focusedInputId) return;
|
|
22950
|
+
const maxLength = getMaxInputLength(json, focusedInputId);
|
|
22951
|
+
if (maxLength !== void 0 && newValue.length > maxLength) return;
|
|
22952
|
+
handleChange(focusedInputId, newValue, false);
|
|
22895
22953
|
}
|
|
22896
22954
|
}
|
|
22897
22955
|
) }),
|
|
@@ -22924,7 +22982,7 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
22924
22982
|
},
|
|
22925
22983
|
tutorialRun.run
|
|
22926
22984
|
)
|
|
22927
|
-
] }) }) }) });
|
|
22985
|
+
] }) }) }) }) });
|
|
22928
22986
|
});
|
|
22929
22987
|
ExerciseRenderer.displayName = "ExerciseRenderer";
|
|
22930
22988
|
function renderExercise(options) {
|
|
@@ -23268,4 +23326,4 @@ exports.validateUniqueIds = validateUniqueIds;
|
|
|
23268
23326
|
exports.validateVisualFractionCapacity = validateVisualFractionCapacity;
|
|
23269
23327
|
exports.validateWidgetJSON = validateWidgetJSON;
|
|
23270
23328
|
exports.validateWidgetJSONSafe = validateWidgetJSONSafe;
|
|
23271
|
-
//# sourceMappingURL=index-
|
|
23329
|
+
//# sourceMappingURL=index-CbKXPRsS.js.map
|