@mathscan/math-exercise-engine 1.2.10 → 1.2.12
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 +2 -0
- package/dist/{GeometryWidget-DgPfqyQv.js → GeometryWidget-BMOiQMvj.js} +2 -2
- package/dist/{GeometryWidget-DgPfqyQv.js.map → GeometryWidget-BMOiQMvj.js.map} +1 -1
- package/dist/{GeometryWidget-gtAP_-_S.mjs → GeometryWidget-Qi5s7Lql.mjs} +2 -2
- package/dist/{GeometryWidget-gtAP_-_S.mjs.map → GeometryWidget-Qi5s7Lql.mjs.map} +1 -1
- package/dist/{index-DBteHZkS.mjs → index-BtJRsuwL.mjs} +883 -264
- package/dist/index-BtJRsuwL.mjs.map +1 -0
- package/dist/{index-tEgJE6m2.js → index-Di53h19p.js} +713 -94
- package/dist/index-Di53h19p.js.map +1 -0
- package/dist/index.d.ts +894 -32
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +178 -171
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +431 -38
- package/package.json +1 -1
- package/dist/index-DBteHZkS.mjs.map +0 -1
- package/dist/index-tEgJE6m2.js.map +0 -1
|
@@ -240,6 +240,7 @@ const JSON_TYPES = {
|
|
|
240
240
|
GEOMETRY: "geometry"
|
|
241
241
|
};
|
|
242
242
|
const DRAG_OPTIONS_WIDGET_ID = "_drag_options";
|
|
243
|
+
const NUMERIC_KEYPAD_WIDGET_ID = "_numeric_keypad";
|
|
243
244
|
const RULER_WIDGET_ID = "_ruler";
|
|
244
245
|
const PROTRACTOR_WIDGET_ID = "_protractor";
|
|
245
246
|
const SET_SQUARE_WIDGET_ID = "_set_square";
|
|
@@ -272,7 +273,8 @@ const INPUT_TYPES = {
|
|
|
272
273
|
/** Numeric input (digits only) */
|
|
273
274
|
NUMERIC: "numeric"
|
|
274
275
|
};
|
|
275
|
-
const NUMBER_LINE_STYLES = ["arrow-right", "arrow-left", "arrow-both", "line"];
|
|
276
|
+
const NUMBER_LINE_STYLES = ["arrow-right", "arrow-left", "arrow-both", "line", "line-left"];
|
|
277
|
+
const DEFAULT_NUMBER_LINE_STYLE = "line-left";
|
|
276
278
|
const INTERNAL_PRECISION = 9;
|
|
277
279
|
const DISPLAY_PRECISION = 2;
|
|
278
280
|
const SIDE_PAD = 7;
|
|
@@ -2055,6 +2057,8 @@ const VISUAL_FRACTION_SHAPE = {
|
|
|
2055
2057
|
};
|
|
2056
2058
|
const VISUAL_FRACTION_DEFAULT_SIZE = 1.35;
|
|
2057
2059
|
const VISUAL_FRACTION_MAX_DIVISIONS = 100;
|
|
2060
|
+
const INLINE_INPUT_MAX_WIDTH = 20;
|
|
2061
|
+
const InlineInputWidthSchema = z.number().int().positive().max(INLINE_INPUT_MAX_WIDTH, { message: `width cannot exceed ${INLINE_INPUT_MAX_WIDTH}` }).optional();
|
|
2058
2062
|
const FreeTextInlineSchema = z.object({
|
|
2059
2063
|
type: z.literal(AST_TYPES.FREE_TEXT),
|
|
2060
2064
|
value: z.string()
|
|
@@ -2084,6 +2088,7 @@ const InlineInputWidgetSchema = z.discriminatedUnion("inputType", [
|
|
|
2084
2088
|
name: z.string(),
|
|
2085
2089
|
inputType: z.literal(INPUT_TYPES.STRING),
|
|
2086
2090
|
length: z.number().int().positive(),
|
|
2091
|
+
width: InlineInputWidthSchema,
|
|
2087
2092
|
placeholder: z.string().max(32).optional(),
|
|
2088
2093
|
disabled: z.boolean().optional(),
|
|
2089
2094
|
answer: z.union([z.string(), z.number()]).optional()
|
|
@@ -2093,6 +2098,7 @@ const InlineInputWidgetSchema = z.discriminatedUnion("inputType", [
|
|
|
2093
2098
|
name: z.string(),
|
|
2094
2099
|
inputType: z.literal(INPUT_TYPES.NUMERIC),
|
|
2095
2100
|
digits: z.number().int().positive(),
|
|
2101
|
+
width: InlineInputWidthSchema,
|
|
2096
2102
|
placeholder: z.string().max(32).optional(),
|
|
2097
2103
|
disabled: z.boolean().optional(),
|
|
2098
2104
|
answer: z.union([z.string(), z.number()]).optional()
|
|
@@ -2250,7 +2256,7 @@ function createBoldText(value, highlight) {
|
|
|
2250
2256
|
highlight
|
|
2251
2257
|
});
|
|
2252
2258
|
}
|
|
2253
|
-
function createStringInput(name, length, answer, placeholder, disabled) {
|
|
2259
|
+
function createStringInput(name, length, answer, placeholder, disabled, width) {
|
|
2254
2260
|
return parseOrThrow(
|
|
2255
2261
|
InlineInputWidgetSchema,
|
|
2256
2262
|
{
|
|
@@ -2258,6 +2264,7 @@ function createStringInput(name, length, answer, placeholder, disabled) {
|
|
|
2258
2264
|
name,
|
|
2259
2265
|
inputType: INPUT_TYPES.STRING,
|
|
2260
2266
|
length,
|
|
2267
|
+
width,
|
|
2261
2268
|
placeholder,
|
|
2262
2269
|
disabled,
|
|
2263
2270
|
// A disabled input is never graded, so its answer is dropped.
|
|
@@ -2266,7 +2273,7 @@ function createStringInput(name, length, answer, placeholder, disabled) {
|
|
|
2266
2273
|
"STRING_INPUT"
|
|
2267
2274
|
);
|
|
2268
2275
|
}
|
|
2269
|
-
function createNumericInput(name, digits, answer, placeholder, disabled) {
|
|
2276
|
+
function createNumericInput(name, digits, answer, placeholder, disabled, width) {
|
|
2270
2277
|
return parseOrThrow(
|
|
2271
2278
|
InlineInputWidgetSchema,
|
|
2272
2279
|
{
|
|
@@ -2274,6 +2281,7 @@ function createNumericInput(name, digits, answer, placeholder, disabled) {
|
|
|
2274
2281
|
name,
|
|
2275
2282
|
inputType: INPUT_TYPES.NUMERIC,
|
|
2276
2283
|
digits,
|
|
2284
|
+
width,
|
|
2277
2285
|
placeholder,
|
|
2278
2286
|
disabled,
|
|
2279
2287
|
// A disabled input is never graded, so its answer is dropped.
|
|
@@ -2490,7 +2498,7 @@ const NumberLineBlockSchema = z.object({
|
|
|
2490
2498
|
errorMap: () => ({
|
|
2491
2499
|
message: `NUMBER_LINE: style must be one of ${NUMBER_LINE_STYLES.join(", ")}`
|
|
2492
2500
|
})
|
|
2493
|
-
}).
|
|
2501
|
+
}).default(DEFAULT_NUMBER_LINE_STYLE),
|
|
2494
2502
|
labelVisibility: z.enum(NUMBER_LINE_LABELS, {
|
|
2495
2503
|
errorMap: () => ({
|
|
2496
2504
|
message: `NUMBER_LINE: labelVisibility must be one of ${NUMBER_LINE_LABELS.join(", ")}`
|
|
@@ -4323,12 +4331,19 @@ class NaturalDSLVisitor extends BaseCstVisitor {
|
|
|
4323
4331
|
`${ctx.stringInput ? "STRING_INPUT" : "NUMERIC_INPUT"} "${name}": disabled must be true or false, got ${JSON.stringify(params.disabled)}`
|
|
4324
4332
|
);
|
|
4325
4333
|
}
|
|
4334
|
+
const width = params.width;
|
|
4335
|
+
const maxChars = ctx.stringInput ? params.length : params.digits;
|
|
4336
|
+
if (width !== void 0 && width < maxChars) {
|
|
4337
|
+
throw new Error(
|
|
4338
|
+
`${ctx.stringInput ? "STRING_INPUT" : "NUMERIC_INPUT"} "${name}": width (${width}) cannot be smaller than ${ctx.stringInput ? "length" : "digits"} (${maxChars})`
|
|
4339
|
+
);
|
|
4340
|
+
}
|
|
4326
4341
|
if (ctx.stringInput) {
|
|
4327
4342
|
const length = params.length;
|
|
4328
|
-
return createStringInput(name, length, answer, placeholder, disabled);
|
|
4343
|
+
return createStringInput(name, length, answer, placeholder, disabled, width);
|
|
4329
4344
|
}
|
|
4330
4345
|
const digits = params.digits;
|
|
4331
|
-
return createNumericInput(name, digits, answer, placeholder, disabled);
|
|
4346
|
+
return createNumericInput(name, digits, answer, placeholder, disabled, width);
|
|
4332
4347
|
}
|
|
4333
4348
|
inlineDragOption(ctx) {
|
|
4334
4349
|
const valueToken = ctx.StringLiteral[0];
|
|
@@ -5068,6 +5083,8 @@ const StringInputJSONSchema = z.object({
|
|
|
5068
5083
|
type: z.literal(JSON_TYPES.INLINE_INPUT),
|
|
5069
5084
|
inputType: z.literal(INPUT_TYPES.STRING),
|
|
5070
5085
|
length: z.number().int().positive(),
|
|
5086
|
+
/** Box width in characters; defaults to `length`. */
|
|
5087
|
+
width: InlineInputWidthSchema,
|
|
5071
5088
|
placeholder: z.string().max(32).optional(),
|
|
5072
5089
|
/** Gray, non-interactive, never graded (any answer for it is ignored). */
|
|
5073
5090
|
disabled: z.boolean().optional()
|
|
@@ -5076,6 +5093,8 @@ const NumericInputJSONSchema = z.object({
|
|
|
5076
5093
|
type: z.literal(JSON_TYPES.INLINE_INPUT),
|
|
5077
5094
|
inputType: z.literal(INPUT_TYPES.NUMERIC),
|
|
5078
5095
|
digits: z.number().int().positive(),
|
|
5096
|
+
/** Box width in characters; defaults to `digits`. */
|
|
5097
|
+
width: InlineInputWidthSchema,
|
|
5079
5098
|
placeholder: z.string().max(32).optional(),
|
|
5080
5099
|
/** Gray, non-interactive, never graded (any answer for it is ignored). */
|
|
5081
5100
|
disabled: z.boolean().optional()
|
|
@@ -6066,7 +6085,7 @@ lodash.exports;
|
|
|
6066
6085
|
var reIsNative = RegExp2(
|
|
6067
6086
|
"^" + funcToString.call(hasOwnProperty).replace(reRegExpChar, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$"
|
|
6068
6087
|
);
|
|
6069
|
-
var Buffer = moduleExports ? context.Buffer : undefined$1,
|
|
6088
|
+
var Buffer = moduleExports ? context.Buffer : undefined$1, Symbol2 = context.Symbol, Uint8Array = context.Uint8Array, allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined$1, getPrototype = overArg(Object2.getPrototypeOf, Object2), objectCreate = Object2.create, propertyIsEnumerable = objectProto.propertyIsEnumerable, splice = arrayProto.splice, spreadableSymbol = Symbol2 ? Symbol2.isConcatSpreadable : undefined$1, symIterator = Symbol2 ? Symbol2.iterator : undefined$1, symToStringTag = Symbol2 ? Symbol2.toStringTag : undefined$1;
|
|
6070
6089
|
var defineProperty = function() {
|
|
6071
6090
|
try {
|
|
6072
6091
|
var func = getNative(Object2, "defineProperty");
|
|
@@ -6081,7 +6100,7 @@ lodash.exports;
|
|
|
6081
6100
|
var metaMap = WeakMap && new WeakMap();
|
|
6082
6101
|
var realNames = {};
|
|
6083
6102
|
var dataViewCtorString = toSource(DataView), mapCtorString = toSource(Map2), promiseCtorString = toSource(Promise2), setCtorString = toSource(Set2), weakMapCtorString = toSource(WeakMap);
|
|
6084
|
-
var symbolProto =
|
|
6103
|
+
var symbolProto = Symbol2 ? Symbol2.prototype : undefined$1, symbolValueOf = symbolProto ? symbolProto.valueOf : undefined$1, symbolToString = symbolProto ? symbolProto.toString : undefined$1;
|
|
6085
6104
|
function lodash2(value) {
|
|
6086
6105
|
if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
|
|
6087
6106
|
if (value instanceof LodashWrapper) {
|
|
@@ -10980,6 +10999,7 @@ function toJSON(ast) {
|
|
|
10980
10999
|
type: JSON_TYPES.INLINE_INPUT,
|
|
10981
11000
|
inputType: INPUT_TYPES.STRING,
|
|
10982
11001
|
length: inline.length,
|
|
11002
|
+
...inline.width !== void 0 && { width: inline.width },
|
|
10983
11003
|
placeholder: inline.placeholder,
|
|
10984
11004
|
...inline.disabled && { disabled: true }
|
|
10985
11005
|
};
|
|
@@ -10988,6 +11008,7 @@ function toJSON(ast) {
|
|
|
10988
11008
|
type: JSON_TYPES.INLINE_INPUT,
|
|
10989
11009
|
inputType: INPUT_TYPES.NUMERIC,
|
|
10990
11010
|
digits: inline.digits,
|
|
11011
|
+
...inline.width !== void 0 && { width: inline.width },
|
|
10991
11012
|
placeholder: inline.placeholder,
|
|
10992
11013
|
...inline.disabled && { disabled: true }
|
|
10993
11014
|
};
|
|
@@ -11083,7 +11104,7 @@ function toJSON(ast) {
|
|
|
11083
11104
|
options: lodashExports.shuffle(tool.options)
|
|
11084
11105
|
};
|
|
11085
11106
|
} else if (tool.type === AST_TYPES.NUMERIC_KEYPAD) {
|
|
11086
|
-
widgets[
|
|
11107
|
+
widgets[NUMERIC_KEYPAD_WIDGET_ID] = {
|
|
11087
11108
|
type: JSON_TYPES.NUMERIC_KEYPAD,
|
|
11088
11109
|
includeOperators: tool.includeOperators,
|
|
11089
11110
|
includeDecimal: tool.includeDecimal,
|
|
@@ -11346,7 +11367,7 @@ function transformNumberLine(numberLine, idGenerator, inlineDragIds) {
|
|
|
11346
11367
|
max: numberLine.max,
|
|
11347
11368
|
step: numberLine.step,
|
|
11348
11369
|
...numberLine.minorTicks !== void 0 && { minorTicks: numberLine.minorTicks },
|
|
11349
|
-
|
|
11370
|
+
style: numberLine.style,
|
|
11350
11371
|
...numberLine.labelVisibility !== void 0 && { labelVisibility: numberLine.labelVisibility },
|
|
11351
11372
|
...counterItem !== void 0 && { counter: counterItem },
|
|
11352
11373
|
...highlights !== void 0 && { highlights },
|
|
@@ -16003,22 +16024,22 @@ function createDefaultDropAnimation(options) {
|
|
|
16003
16024
|
easing,
|
|
16004
16025
|
fill: "forwards"
|
|
16005
16026
|
});
|
|
16006
|
-
return new Promise((
|
|
16027
|
+
return new Promise((resolve2) => {
|
|
16007
16028
|
animation.onfinish = () => {
|
|
16008
16029
|
cleanup == null ? void 0 : cleanup();
|
|
16009
|
-
|
|
16030
|
+
resolve2();
|
|
16010
16031
|
};
|
|
16011
16032
|
});
|
|
16012
16033
|
};
|
|
16013
16034
|
}
|
|
16014
|
-
let key = 0;
|
|
16035
|
+
let key$1 = 0;
|
|
16015
16036
|
function useKey(id) {
|
|
16016
16037
|
return useMemo(() => {
|
|
16017
16038
|
if (id == null) {
|
|
16018
16039
|
return;
|
|
16019
16040
|
}
|
|
16020
|
-
key++;
|
|
16021
|
-
return key;
|
|
16041
|
+
key$1++;
|
|
16042
|
+
return key$1;
|
|
16022
16043
|
}, [id]);
|
|
16023
16044
|
}
|
|
16024
16045
|
const DragOverlay = /* @__PURE__ */ React.memo((_ref) => {
|
|
@@ -16100,6 +16121,7 @@ const DEFAULT_SOUNDS = {
|
|
|
16100
16121
|
"drag-start": dragStart,
|
|
16101
16122
|
"drag-drop": dragDrop,
|
|
16102
16123
|
"drag-cancel": dragDrop,
|
|
16124
|
+
"drag-hint": buttonClick,
|
|
16103
16125
|
"radio-select": buttonClick,
|
|
16104
16126
|
"pair-select": buttonClick,
|
|
16105
16127
|
"pair-link": buttonClick,
|
|
@@ -16203,14 +16225,24 @@ function useExerciseDnd() {
|
|
|
16203
16225
|
}
|
|
16204
16226
|
return ctx;
|
|
16205
16227
|
}
|
|
16228
|
+
const NO_EXERCISE = Symbol("no-exercise");
|
|
16206
16229
|
const ExerciseDndProvider = ({
|
|
16207
16230
|
children,
|
|
16208
16231
|
droppedSources,
|
|
16209
|
-
onDragEnd
|
|
16232
|
+
onDragEnd,
|
|
16233
|
+
exerciseKey
|
|
16210
16234
|
}) => {
|
|
16211
16235
|
const [activeItem, setActiveItem] = useState(null);
|
|
16236
|
+
const [draggedExerciseKey, setDraggedExerciseKey] = useState(NO_EXERCISE);
|
|
16237
|
+
const hasDraggedOption = draggedExerciseKey === exerciseKey;
|
|
16212
16238
|
const [dropHandlers] = useState(() => /* @__PURE__ */ new Map());
|
|
16213
16239
|
const playSound = useSound();
|
|
16240
|
+
const hintSoundExerciseRef = useRef(NO_EXERCISE);
|
|
16241
|
+
const playHintSound = useCallback(() => {
|
|
16242
|
+
if (hintSoundExerciseRef.current === exerciseKey) return;
|
|
16243
|
+
hintSoundExerciseRef.current = exerciseKey;
|
|
16244
|
+
playSound("drag-hint");
|
|
16245
|
+
}, [playSound, exerciseKey]);
|
|
16214
16246
|
const usedOptionIds = useMemo(() => {
|
|
16215
16247
|
return new Set(droppedSources.values());
|
|
16216
16248
|
}, [droppedSources]);
|
|
@@ -16234,6 +16266,7 @@ const ExerciseDndProvider = ({
|
|
|
16234
16266
|
const raw = active.data.current;
|
|
16235
16267
|
const item = raw?.type === "placed-chip" ? { type: "placed-chip", widgetId: raw.widgetId, label: raw.label, sourceOptionId: raw.sourceOptionId } : raw;
|
|
16236
16268
|
setActiveItem(item);
|
|
16269
|
+
if (item.type === "drag-option") setDraggedExerciseKey(exerciseKey);
|
|
16237
16270
|
playSound("drag-start");
|
|
16238
16271
|
};
|
|
16239
16272
|
const handleDragEnd = (event) => {
|
|
@@ -16259,6 +16292,9 @@ const ExerciseDndProvider = ({
|
|
|
16259
16292
|
value: {
|
|
16260
16293
|
activeItem,
|
|
16261
16294
|
isDragging: activeItem !== null,
|
|
16295
|
+
exerciseKey,
|
|
16296
|
+
hasDraggedOption,
|
|
16297
|
+
playHintSound,
|
|
16262
16298
|
usedOptionIds,
|
|
16263
16299
|
droppedSources,
|
|
16264
16300
|
registerDropHandler,
|
|
@@ -17039,7 +17075,38 @@ function isUndoDrop(event, dragId) {
|
|
|
17039
17075
|
const LockContext = createContext(false);
|
|
17040
17076
|
const ExerciseLockProvider = ({ locked, children }) => /* @__PURE__ */ jsx(LockContext.Provider, { value: locked, children });
|
|
17041
17077
|
const useExerciseLocked = () => useContext(LockContext);
|
|
17042
|
-
const
|
|
17078
|
+
const OVERLAY_STYLE = {
|
|
17079
|
+
position: "absolute",
|
|
17080
|
+
top: 0,
|
|
17081
|
+
left: 0,
|
|
17082
|
+
width: "100%",
|
|
17083
|
+
height: "100%",
|
|
17084
|
+
minWidth: 0,
|
|
17085
|
+
maxWidth: "none",
|
|
17086
|
+
margin: 0,
|
|
17087
|
+
boxSizing: "border-box"
|
|
17088
|
+
};
|
|
17089
|
+
const SlotSizer = ({
|
|
17090
|
+
style,
|
|
17091
|
+
...props
|
|
17092
|
+
}) => /* @__PURE__ */ jsx(
|
|
17093
|
+
"input",
|
|
17094
|
+
{
|
|
17095
|
+
...props,
|
|
17096
|
+
type: "text",
|
|
17097
|
+
value: "",
|
|
17098
|
+
readOnly: true,
|
|
17099
|
+
tabIndex: -1,
|
|
17100
|
+
"aria-hidden": "true",
|
|
17101
|
+
style: { ...style, visibility: "hidden" }
|
|
17102
|
+
}
|
|
17103
|
+
);
|
|
17104
|
+
const PlacedInputChip = ({
|
|
17105
|
+
widgetId,
|
|
17106
|
+
value,
|
|
17107
|
+
className = "",
|
|
17108
|
+
onRemove
|
|
17109
|
+
}) => {
|
|
17043
17110
|
const { droppedSources } = useExerciseDnd();
|
|
17044
17111
|
const locked = useExerciseLocked();
|
|
17045
17112
|
const sourceOptionId = droppedSources.get(widgetId);
|
|
@@ -17063,13 +17130,41 @@ const PlacedInputChip = ({ widgetId, value, className = "", onRemove }) => {
|
|
|
17063
17130
|
if (!locked && e.detail !== 0) onRemove();
|
|
17064
17131
|
},
|
|
17065
17132
|
title: locked ? void 0 : "Drag to move, or click to remove",
|
|
17066
|
-
style: {
|
|
17133
|
+
style: {
|
|
17134
|
+
...OVERLAY_STYLE,
|
|
17135
|
+
touchAction: "none",
|
|
17136
|
+
cursor: locked ? "default" : "grab"
|
|
17137
|
+
},
|
|
17067
17138
|
...listeners,
|
|
17068
17139
|
...attributes,
|
|
17069
17140
|
children: value
|
|
17070
17141
|
}
|
|
17071
17142
|
);
|
|
17072
17143
|
};
|
|
17144
|
+
function getMaxInputLength(exercise, inputKey) {
|
|
17145
|
+
const widget = exercise.widgets[inputKey];
|
|
17146
|
+
if (widget?.type !== JSON_TYPES.INLINE_INPUT) return void 0;
|
|
17147
|
+
return widget.inputType === INPUT_TYPES.NUMERIC ? widget.digits : widget.length;
|
|
17148
|
+
}
|
|
17149
|
+
function getInputElementId(key2) {
|
|
17150
|
+
return `${key2}-input`;
|
|
17151
|
+
}
|
|
17152
|
+
function getTextInputs(container) {
|
|
17153
|
+
return Array.from(
|
|
17154
|
+
container.querySelectorAll(
|
|
17155
|
+
'input[type="text"], input[type="number"], textarea, select'
|
|
17156
|
+
)
|
|
17157
|
+
);
|
|
17158
|
+
}
|
|
17159
|
+
function canTakeInput(el) {
|
|
17160
|
+
return !el.readOnly && !el.disabled;
|
|
17161
|
+
}
|
|
17162
|
+
function findNextInput(container, currentKey) {
|
|
17163
|
+
const inputs = getTextInputs(container);
|
|
17164
|
+
const currentIndex = inputs.findIndex((el) => el.id === getInputElementId(currentKey));
|
|
17165
|
+
if (currentIndex === -1) return null;
|
|
17166
|
+
return inputs.slice(currentIndex + 1).find(canTakeInput) ?? null;
|
|
17167
|
+
}
|
|
17073
17168
|
const InlineInput = ({
|
|
17074
17169
|
widgetId,
|
|
17075
17170
|
value,
|
|
@@ -17078,6 +17173,7 @@ const InlineInput = ({
|
|
|
17078
17173
|
inputType,
|
|
17079
17174
|
length,
|
|
17080
17175
|
digits,
|
|
17176
|
+
width,
|
|
17081
17177
|
placeholder,
|
|
17082
17178
|
disabled = false,
|
|
17083
17179
|
onChange,
|
|
@@ -17091,8 +17187,8 @@ const InlineInput = ({
|
|
|
17091
17187
|
const handleDrop = useCallback((item) => {
|
|
17092
17188
|
return applyDropToInput(widgetId, item, onChange);
|
|
17093
17189
|
}, [onChange, widgetId]);
|
|
17094
|
-
const expectedLength = inputType ===
|
|
17095
|
-
const dynamicWidth = `${(expectedLength
|
|
17190
|
+
const expectedLength = inputType === INPUT_TYPES.NUMERIC ? digits : length;
|
|
17191
|
+
const dynamicWidth = `${(width ?? expectedLength ?? 5) * 0.8 + 1}em`;
|
|
17096
17192
|
const statusClass = validationStatus ? ` widget--${validationStatus}` : "";
|
|
17097
17193
|
const disabledClass = disabled ? " inline-input--disabled" : "";
|
|
17098
17194
|
return /* @__PURE__ */ jsx(
|
|
@@ -17105,21 +17201,31 @@ const InlineInput = ({
|
|
|
17105
17201
|
className: "inline-widget inline-input-droppable",
|
|
17106
17202
|
children: isDropped && value !== "" ? (
|
|
17107
17203
|
// Show as chip only when value came from drag-drop
|
|
17108
|
-
/* @__PURE__ */
|
|
17109
|
-
|
|
17110
|
-
|
|
17111
|
-
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17204
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
17205
|
+
/* @__PURE__ */ jsx(
|
|
17206
|
+
SlotSizer,
|
|
17207
|
+
{
|
|
17208
|
+
className: `inline-input${disabledClass}`,
|
|
17209
|
+
style: { width: dynamicWidth },
|
|
17210
|
+
placeholder
|
|
17211
|
+
}
|
|
17212
|
+
),
|
|
17213
|
+
/* @__PURE__ */ jsx(
|
|
17214
|
+
PlacedInputChip,
|
|
17215
|
+
{
|
|
17216
|
+
widgetId,
|
|
17217
|
+
value,
|
|
17218
|
+
className: statusClass.trim(),
|
|
17219
|
+
onRemove: () => handleChange("", false)
|
|
17220
|
+
}
|
|
17221
|
+
)
|
|
17222
|
+
] })
|
|
17117
17223
|
) : (
|
|
17118
17224
|
// Regular input for typing
|
|
17119
17225
|
/* @__PURE__ */ jsx(
|
|
17120
17226
|
"input",
|
|
17121
17227
|
{
|
|
17122
|
-
id: widgetId
|
|
17228
|
+
id: getInputElementId(widgetId),
|
|
17123
17229
|
type: "text",
|
|
17124
17230
|
className: `inline-input${statusClass}${disabledClass}`,
|
|
17125
17231
|
value,
|
|
@@ -17139,15 +17245,20 @@ const InlineInput = ({
|
|
|
17139
17245
|
}
|
|
17140
17246
|
);
|
|
17141
17247
|
};
|
|
17142
|
-
const SingleDragOption = ({
|
|
17248
|
+
const SingleDragOption = (props) => {
|
|
17249
|
+
const { exerciseKey } = useExerciseDnd();
|
|
17250
|
+
return /* @__PURE__ */ jsx(DragOptionChip, { ...props }, exerciseKey ?? "");
|
|
17251
|
+
};
|
|
17252
|
+
const DragOptionChip = ({
|
|
17143
17253
|
optionId,
|
|
17144
17254
|
value,
|
|
17145
17255
|
onOptionClick,
|
|
17146
17256
|
className = ""
|
|
17147
17257
|
}) => {
|
|
17148
|
-
const { usedOptionIds } = useExerciseDnd();
|
|
17258
|
+
const { usedOptionIds, hasDraggedOption, playHintSound } = useExerciseDnd();
|
|
17149
17259
|
const locked = useExerciseLocked();
|
|
17150
17260
|
const isUsed = usedOptionIds.has(optionId);
|
|
17261
|
+
const { jiggling: hinting, onAnimationEnd: onHintEnd } = useJiggle(hasDraggedOption);
|
|
17151
17262
|
return /* @__PURE__ */ jsx("div", { className: `drag-option-slot ${className}`.trim(), children: isUsed ? /* @__PURE__ */ jsx("div", { className: "drag-option-empty", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(
|
|
17152
17263
|
Draggable,
|
|
17153
17264
|
{
|
|
@@ -17159,7 +17270,11 @@ const SingleDragOption = ({
|
|
|
17159
17270
|
children: /* @__PURE__ */ jsx(
|
|
17160
17271
|
"button",
|
|
17161
17272
|
{
|
|
17162
|
-
className: "drag-option",
|
|
17273
|
+
className: hinting && !locked ? "drag-option drag-option--hint" : "drag-option",
|
|
17274
|
+
onAnimationStart: () => {
|
|
17275
|
+
if (hinting) playHintSound();
|
|
17276
|
+
},
|
|
17277
|
+
onAnimationEnd: onHintEnd,
|
|
17163
17278
|
onClick: () => {
|
|
17164
17279
|
if (!locked) onOptionClick?.(value);
|
|
17165
17280
|
},
|
|
@@ -17288,23 +17403,23 @@ const NumericKeypad = ({
|
|
|
17288
17403
|
playSound("keypad-press");
|
|
17289
17404
|
onChange(value.slice(0, -1));
|
|
17290
17405
|
};
|
|
17291
|
-
return /* @__PURE__ */ jsxs("div", { className: `numeric-keypad ${includeOperators ? "numeric-keypad-with-operators" : ""}`, children: [
|
|
17292
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("1"), children: "1" }),
|
|
17293
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("2"), children: "2" }),
|
|
17294
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("3"), children: "3" }),
|
|
17295
|
-
includeOperators && /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-operator", onClick: () => handleDigit("+"), children: "+" }),
|
|
17296
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("4"), children: "4" }),
|
|
17297
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("5"), children: "5" }),
|
|
17298
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("6"), children: "6" }),
|
|
17299
|
-
includeOperators && /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-operator", onClick: () => handleDigit("-"), children: "-" }),
|
|
17300
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("7"), children: "7" }),
|
|
17301
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("8"), children: "8" }),
|
|
17302
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("9"), children: "9" }),
|
|
17303
|
-
includeOperators && /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-operator", onClick: () => handleDigit("×"), children: "×" }),
|
|
17304
|
-
includeDecimal ? /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-decimal", onClick: () => handleDigit(","), children: "," }) : /* @__PURE__ */ jsx("div", {}),
|
|
17305
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", onClick: () => handleDigit("0"), children: "0" }),
|
|
17306
|
-
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-backspace", onClick: handleBackspace, children: "⌫" }),
|
|
17307
|
-
includeOperators && /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-operator", onClick: () => handleDigit("÷"), children: "÷" })
|
|
17406
|
+
return /* @__PURE__ */ jsxs("div", { className: `numeric-keypad ${includeOperators ? "numeric-keypad-with-operators" : ""}`, "data-tutorial": "keypad", children: [
|
|
17407
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "1", onClick: () => handleDigit("1"), children: "1" }),
|
|
17408
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "2", onClick: () => handleDigit("2"), children: "2" }),
|
|
17409
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "3", onClick: () => handleDigit("3"), children: "3" }),
|
|
17410
|
+
includeOperators && /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-operator", "data-key": "+", onClick: () => handleDigit("+"), children: "+" }),
|
|
17411
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "4", onClick: () => handleDigit("4"), children: "4" }),
|
|
17412
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "5", onClick: () => handleDigit("5"), children: "5" }),
|
|
17413
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "6", onClick: () => handleDigit("6"), children: "6" }),
|
|
17414
|
+
includeOperators && /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-operator", "data-key": "-", onClick: () => handleDigit("-"), children: "-" }),
|
|
17415
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "7", onClick: () => handleDigit("7"), children: "7" }),
|
|
17416
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "8", onClick: () => handleDigit("8"), children: "8" }),
|
|
17417
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "9", onClick: () => handleDigit("9"), children: "9" }),
|
|
17418
|
+
includeOperators && /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-operator", "data-key": "×", onClick: () => handleDigit("×"), children: "×" }),
|
|
17419
|
+
includeDecimal ? /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-decimal", "data-key": ",", onClick: () => handleDigit(","), children: "," }) : /* @__PURE__ */ jsx("div", {}),
|
|
17420
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-digit", "data-key": "0", onClick: () => handleDigit("0"), children: "0" }),
|
|
17421
|
+
/* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-backspace", "data-key": "backspace", onClick: handleBackspace, children: "⌫" }),
|
|
17422
|
+
includeOperators && /* @__PURE__ */ jsx("button", { disabled: locked, className: "keypad-btn keypad-btn-operator", "data-key": "÷", onClick: () => handleDigit("÷"), children: "÷" })
|
|
17308
17423
|
] });
|
|
17309
17424
|
};
|
|
17310
17425
|
const FractionInput = ({
|
|
@@ -17344,18 +17459,28 @@ const FractionInput = ({
|
|
|
17344
17459
|
onDrop: handleDrop(subKey),
|
|
17345
17460
|
disabled: locked,
|
|
17346
17461
|
className: "fraction-slot-droppable",
|
|
17347
|
-
children: isDroppedHere && currentValue !== "" ? /* @__PURE__ */
|
|
17348
|
-
|
|
17349
|
-
|
|
17350
|
-
|
|
17351
|
-
|
|
17352
|
-
|
|
17353
|
-
|
|
17354
|
-
|
|
17355
|
-
|
|
17462
|
+
children: isDroppedHere && currentValue !== "" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
17463
|
+
/* @__PURE__ */ jsx(
|
|
17464
|
+
SlotSizer,
|
|
17465
|
+
{
|
|
17466
|
+
className: "inline-input fraction-input",
|
|
17467
|
+
size: placeholder ? placeholder.length : void 0,
|
|
17468
|
+
placeholder
|
|
17469
|
+
}
|
|
17470
|
+
),
|
|
17471
|
+
/* @__PURE__ */ jsx(
|
|
17472
|
+
PlacedInputChip,
|
|
17473
|
+
{
|
|
17474
|
+
widgetId: subKey,
|
|
17475
|
+
value: currentValue,
|
|
17476
|
+
className: `fraction-chip${slotClass}`,
|
|
17477
|
+
onRemove: () => onChange(subKey, "", false)
|
|
17478
|
+
}
|
|
17479
|
+
)
|
|
17480
|
+
] }) : /* @__PURE__ */ jsx(
|
|
17356
17481
|
"input",
|
|
17357
17482
|
{
|
|
17358
|
-
id:
|
|
17483
|
+
id: getInputElementId(subKey),
|
|
17359
17484
|
type: "text",
|
|
17360
17485
|
className: `inline-input fraction-input${slotClass}`,
|
|
17361
17486
|
value: currentValue,
|
|
@@ -18933,11 +19058,12 @@ const RadioOptions = ({
|
|
|
18933
19058
|
};
|
|
18934
19059
|
const Btn = ({
|
|
18935
19060
|
label,
|
|
19061
|
+
dataKey = label,
|
|
18936
19062
|
extraClass = "keypad-btn-digit",
|
|
18937
19063
|
onClick
|
|
18938
19064
|
}) => {
|
|
18939
19065
|
const locked = useExerciseLocked();
|
|
18940
|
-
return /* @__PURE__ */ jsx("button", { className: `keypad-btn ${extraClass}`, disabled: locked, onClick, children: /* @__PURE__ */ jsx("span", { children: label }) });
|
|
19066
|
+
return /* @__PURE__ */ jsx("button", { className: `keypad-btn ${extraClass}`, "data-key": dataKey, disabled: locked, onClick, children: /* @__PURE__ */ jsx("span", { children: label }) });
|
|
18941
19067
|
};
|
|
18942
19068
|
const DiamondKeypad = ({ value, onChange }) => {
|
|
18943
19069
|
const playSound = useSound();
|
|
@@ -18949,7 +19075,7 @@ const DiamondKeypad = ({ value, onChange }) => {
|
|
|
18949
19075
|
playSound("keypad-press");
|
|
18950
19076
|
onChange(value.slice(0, -1));
|
|
18951
19077
|
};
|
|
18952
|
-
return /* @__PURE__ */ jsxs("div", { className: "diamond-keypad", children: [
|
|
19078
|
+
return /* @__PURE__ */ jsxs("div", { className: "diamond-keypad", "data-tutorial": "keypad", children: [
|
|
18953
19079
|
/* @__PURE__ */ jsxs("div", { className: "diamond-keypad-row", children: [
|
|
18954
19080
|
/* @__PURE__ */ jsx(Btn, { label: "1", onClick: () => append("1") }),
|
|
18955
19081
|
/* @__PURE__ */ jsx(Btn, { label: "2", onClick: () => append("2") }),
|
|
@@ -18968,7 +19094,7 @@ const DiamondKeypad = ({ value, onChange }) => {
|
|
|
18968
19094
|
] }),
|
|
18969
19095
|
/* @__PURE__ */ jsxs("div", { className: "diamond-keypad-row", children: [
|
|
18970
19096
|
/* @__PURE__ */ jsx(Btn, { label: ".", extraClass: "keypad-btn-decimal", onClick: () => append(".") }),
|
|
18971
|
-
/* @__PURE__ */ jsx(Btn, { label: "⌫", extraClass: "keypad-btn-backspace", onClick: backspace })
|
|
19097
|
+
/* @__PURE__ */ jsx(Btn, { label: "⌫", dataKey: "backspace", extraClass: "keypad-btn-backspace", onClick: backspace })
|
|
18972
19098
|
] })
|
|
18973
19099
|
] });
|
|
18974
19100
|
};
|
|
@@ -19574,7 +19700,7 @@ function isGeometryWidget(widget) {
|
|
|
19574
19700
|
return widget.type === JSON_TYPES.GEOMETRY;
|
|
19575
19701
|
}
|
|
19576
19702
|
const GeometryWidget = React.lazy(async () => ({
|
|
19577
|
-
default: (await import("./GeometryWidget-
|
|
19703
|
+
default: (await import("./GeometryWidget-Qi5s7Lql.mjs")).GeometryWidget
|
|
19578
19704
|
}));
|
|
19579
19705
|
const WidgetRenderer = ({
|
|
19580
19706
|
widget,
|
|
@@ -19602,8 +19728,9 @@ const WidgetRenderer = ({
|
|
|
19602
19728
|
isDropped,
|
|
19603
19729
|
validationStatus,
|
|
19604
19730
|
inputType: widget.inputType,
|
|
19605
|
-
length: widget.inputType ===
|
|
19606
|
-
digits: widget.inputType ===
|
|
19731
|
+
length: widget.inputType === INPUT_TYPES.STRING ? widget.length : void 0,
|
|
19732
|
+
digits: widget.inputType === INPUT_TYPES.NUMERIC ? widget.digits : void 0,
|
|
19733
|
+
width: widget.width,
|
|
19607
19734
|
placeholder: widget.placeholder,
|
|
19608
19735
|
disabled: widget.disabled,
|
|
19609
19736
|
onChange,
|
|
@@ -19974,6 +20101,13 @@ const EPSILON = 10 ** -INTERNAL_PRECISION;
|
|
|
19974
20101
|
const cleanFloat = (n) => parseFloat(n.toFixed(INTERNAL_PRECISION));
|
|
19975
20102
|
const roundTick = (n) => parseFloat(n.toFixed(DISPLAY_PRECISION));
|
|
19976
20103
|
const almostEqual = (a, b) => Math.abs(a - b) <= EPSILON;
|
|
20104
|
+
const NUMBER_LINE_AXIS_ENDS = {
|
|
20105
|
+
"arrow-right": { left: "tick", right: "arrow" },
|
|
20106
|
+
"arrow-left": { left: "arrow", right: "tick" },
|
|
20107
|
+
"arrow-both": { left: "arrow", right: "arrow" },
|
|
20108
|
+
"line": { left: "line", right: "line" },
|
|
20109
|
+
"line-left": { left: "line", right: "arrow" }
|
|
20110
|
+
};
|
|
19977
20111
|
const toPercent = (value, min, max) => {
|
|
19978
20112
|
const raw = SIDE_PAD + (value - min) / (max - min) * (100 - 2 * SIDE_PAD);
|
|
19979
20113
|
return Math.max(SIDE_PAD, Math.min(100 - SIDE_PAD, raw));
|
|
@@ -21021,7 +21155,7 @@ const NumberLineRenderer = ({
|
|
|
21021
21155
|
max,
|
|
21022
21156
|
step,
|
|
21023
21157
|
minorTicks = 0,
|
|
21024
|
-
style: lineStyle =
|
|
21158
|
+
style: lineStyle = DEFAULT_NUMBER_LINE_STYLE,
|
|
21025
21159
|
labelVisibility,
|
|
21026
21160
|
highlights,
|
|
21027
21161
|
arrows,
|
|
@@ -21085,8 +21219,9 @@ const NumberLineRenderer = ({
|
|
|
21085
21219
|
() => getMinorTickValues(majorTicks, minorTicks),
|
|
21086
21220
|
[majorTicks, minorTicks]
|
|
21087
21221
|
);
|
|
21088
|
-
const
|
|
21089
|
-
const showArrowLeft =
|
|
21222
|
+
const axisEnds = NUMBER_LINE_AXIS_ENDS[lineStyle] ?? NUMBER_LINE_AXIS_ENDS[DEFAULT_NUMBER_LINE_STYLE];
|
|
21223
|
+
const showArrowLeft = axisEnds.left === "arrow";
|
|
21224
|
+
const showArrowRight = axisEnds.right === "arrow";
|
|
21090
21225
|
const hasDragOptions = Boolean(widgets[DRAG_OPTIONS_WIDGET_ID]);
|
|
21091
21226
|
const isAxisDragMode = hasDragOptions && item.dragMode === "axis";
|
|
21092
21227
|
const inputTickSet = useMemo(() => {
|
|
@@ -21134,7 +21269,7 @@ const NumberLineRenderer = ({
|
|
|
21134
21269
|
return /* @__PURE__ */ jsxs(
|
|
21135
21270
|
"div",
|
|
21136
21271
|
{
|
|
21137
|
-
className: `number-line${isAxisDragMode ? " number-line--drag-mode" : ""}${
|
|
21272
|
+
className: `number-line${isAxisDragMode ? " number-line--drag-mode" : ""}${axisEnds.left === "line" ? " number-line--extend-left" : ""}${axisEnds.right === "line" ? " number-line--extend-right" : ""}${nlValidationStatus ? ` number-line--${nlValidationStatus}` : ""}`,
|
|
21138
21273
|
style: {
|
|
21139
21274
|
[CSS_VAR_SIDE_PAD]: `${SIDE_PAD}%`,
|
|
21140
21275
|
...positionContentHeight && { "--nl-position-content-height": positionContentHeight }
|
|
@@ -21507,6 +21642,35 @@ function groupLineItems(items) {
|
|
|
21507
21642
|
}
|
|
21508
21643
|
return groups;
|
|
21509
21644
|
}
|
|
21645
|
+
function placeTableCells(rows, cols) {
|
|
21646
|
+
const placements = /* @__PURE__ */ new Map();
|
|
21647
|
+
const occupied = rows.map(() => []);
|
|
21648
|
+
const totalRows = rows.length;
|
|
21649
|
+
rows.forEach((row, rowIndex) => {
|
|
21650
|
+
const taken = occupied[rowIndex] ?? [];
|
|
21651
|
+
let col = 0;
|
|
21652
|
+
for (const cell of row.cells) {
|
|
21653
|
+
while (taken[col]) col++;
|
|
21654
|
+
const colspan = cell.colspan || 1;
|
|
21655
|
+
const rowspan = Math.min(cell.rowspan || 1, totalRows - rowIndex);
|
|
21656
|
+
for (let r = rowIndex; r < rowIndex + rowspan; r++) {
|
|
21657
|
+
const line = occupied[r];
|
|
21658
|
+
if (!line) continue;
|
|
21659
|
+
for (let c = col; c < col + colspan; c++) line[c] = true;
|
|
21660
|
+
}
|
|
21661
|
+
placements.set(cell.id, {
|
|
21662
|
+
column: col + 1,
|
|
21663
|
+
row: rowIndex + 1,
|
|
21664
|
+
colspan,
|
|
21665
|
+
rowspan,
|
|
21666
|
+
isLastColumn: col + colspan >= cols,
|
|
21667
|
+
isLastRow: rowIndex + rowspan >= totalRows
|
|
21668
|
+
});
|
|
21669
|
+
col += colspan;
|
|
21670
|
+
}
|
|
21671
|
+
});
|
|
21672
|
+
return placements;
|
|
21673
|
+
}
|
|
21510
21674
|
const toValidationStatus = (v) => v === true ? "correct" : v === false ? "incorrect" : null;
|
|
21511
21675
|
const isNumericText = (value) => /^[\d.,\s]+$/.test(value);
|
|
21512
21676
|
const numberClass = (value) => isNumericText(value) ? "text-item-number" : "";
|
|
@@ -21630,21 +21794,28 @@ const LayoutRenderer = ({
|
|
|
21630
21794
|
subItem.id
|
|
21631
21795
|
)) });
|
|
21632
21796
|
case JSON_TYPES.TABLE: {
|
|
21633
|
-
const
|
|
21797
|
+
const placements = placeTableCells(item.rows, item.cols);
|
|
21634
21798
|
const tableStyle = {
|
|
21635
|
-
"--exercise-table-border-color": item.borderColor ?? DEFAULT_TABLE_BORDER_COLOR
|
|
21799
|
+
"--exercise-table-border-color": item.borderColor ?? DEFAULT_TABLE_BORDER_COLOR,
|
|
21800
|
+
"--exercise-table-cols": item.cols
|
|
21636
21801
|
};
|
|
21637
|
-
return /* @__PURE__ */ jsx("div", { className: "exercise-table-container", style: tableStyle, children: /* @__PURE__ */ jsx("
|
|
21638
|
-
const
|
|
21639
|
-
const
|
|
21802
|
+
return /* @__PURE__ */ jsx("div", { className: "exercise-table-container", style: tableStyle, children: /* @__PURE__ */ jsx("div", { className: "exercise-table", role: "table", children: item.rows.map((row) => /* @__PURE__ */ jsx("div", { className: "exercise-table-row", role: "row", children: row.cells.map((cell) => {
|
|
21803
|
+
const placement = placements.get(cell.id);
|
|
21804
|
+
const modifiers = [
|
|
21805
|
+
placement?.isLastRow ? " exercise-table-cell--last-row" : "",
|
|
21806
|
+
placement?.isLastColumn ? " exercise-table-cell--last-col" : ""
|
|
21807
|
+
].join("");
|
|
21640
21808
|
return /* @__PURE__ */ jsx(
|
|
21641
|
-
"
|
|
21809
|
+
"div",
|
|
21642
21810
|
{
|
|
21643
|
-
className: `exercise-table-cell${
|
|
21644
|
-
|
|
21645
|
-
|
|
21646
|
-
|
|
21647
|
-
|
|
21811
|
+
className: `exercise-table-cell${modifiers}`,
|
|
21812
|
+
role: "cell",
|
|
21813
|
+
style: {
|
|
21814
|
+
backgroundColor: cell.backgroundColor ?? row.backgroundColor,
|
|
21815
|
+
gridColumn: placement && `${placement.column} / span ${placement.colspan}`,
|
|
21816
|
+
gridRow: placement && `${placement.row} / span ${placement.rowspan}`
|
|
21817
|
+
},
|
|
21818
|
+
children: /* @__PURE__ */ jsx("div", { className: "exercise-table-cell-content", children: cell.items.map((subItem) => /* @__PURE__ */ jsx(
|
|
21648
21819
|
LayoutRenderer,
|
|
21649
21820
|
{
|
|
21650
21821
|
item: subItem,
|
|
@@ -21657,11 +21828,11 @@ const LayoutRenderer = ({
|
|
|
21657
21828
|
resetKey
|
|
21658
21829
|
},
|
|
21659
21830
|
subItem.id
|
|
21660
|
-
))
|
|
21831
|
+
)) })
|
|
21661
21832
|
},
|
|
21662
21833
|
cell.id
|
|
21663
21834
|
);
|
|
21664
|
-
}) }, row.id)) }) })
|
|
21835
|
+
}) }, row.id)) }) });
|
|
21665
21836
|
}
|
|
21666
21837
|
case JSON_TYPES.NUMBER_LINE:
|
|
21667
21838
|
return /* @__PURE__ */ jsx(
|
|
@@ -21831,6 +22002,394 @@ const LayoutRenderer = ({
|
|
|
21831
22002
|
return null;
|
|
21832
22003
|
}
|
|
21833
22004
|
};
|
|
22005
|
+
function LionHand({ className }) {
|
|
22006
|
+
const id = useId();
|
|
22007
|
+
const shadowId = `${id}-shadow`;
|
|
22008
|
+
const maskId = `${id}-mask`;
|
|
22009
|
+
return /* @__PURE__ */ jsxs(
|
|
22010
|
+
"svg",
|
|
22011
|
+
{
|
|
22012
|
+
"aria-hidden": "true",
|
|
22013
|
+
focusable: "false",
|
|
22014
|
+
width: "75",
|
|
22015
|
+
height: "78",
|
|
22016
|
+
viewBox: "0 0 75 78",
|
|
22017
|
+
fill: "none",
|
|
22018
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
22019
|
+
className,
|
|
22020
|
+
children: [
|
|
22021
|
+
/* @__PURE__ */ jsxs("g", { filter: `url(#${shadowId})`, children: [
|
|
22022
|
+
/* @__PURE__ */ jsx(
|
|
22023
|
+
"mask",
|
|
22024
|
+
{
|
|
22025
|
+
id: maskId,
|
|
22026
|
+
style: { maskType: "alpha" },
|
|
22027
|
+
maskUnits: "userSpaceOnUse",
|
|
22028
|
+
x: "9",
|
|
22029
|
+
y: "19",
|
|
22030
|
+
width: "49",
|
|
22031
|
+
height: "42",
|
|
22032
|
+
children: /* @__PURE__ */ jsx("path", { d: "M49.7484 33.9668C58.158 45.6281 37.9572 61.4399 28.1745 60.1051C17.1397 58.5995 5.03802 42.2474 10.4349 38.3224C15.8318 34.3974 18.9715 32.4031 23.1181 30.1278C24.1486 29.5624 25.1742 29.0263 26.1947 28.5358C33.7314 24.764 52.8138 16.0308 57.0969 21.97C59.9201 25.8848 54.8651 28.9522 48.1097 31.8806C48.658 32.518 49.2042 33.2123 49.7484 33.9668Z", fill: "#FCD592" })
|
|
22033
|
+
}
|
|
22034
|
+
),
|
|
22035
|
+
/* @__PURE__ */ jsxs("g", { mask: `url(#${maskId})`, children: [
|
|
22036
|
+
/* @__PURE__ */ jsx("path", { d: "M49.7484 33.9672C58.158 45.6285 38.3118 60.3151 32.1037 64.7332C22.6672 71.4486 2.68204 43.9969 12.2424 37.1926C15.1884 35.0959 18.9716 32.4035 23.1182 30.1282C24.7308 29.2433 26.3314 28.4303 27.9195 27.7514C36.611 23.6398 54.4269 16.1501 58.4471 21.7248C61.34 25.7362 55.5686 28.9294 48.2024 31.9893C48.7196 32.5968 49.2349 33.2552 49.7484 33.9672Z", fill: "#FCD592" }),
|
|
22037
|
+
/* @__PURE__ */ jsx("path", { d: "M12.2426 37.1924C2.68223 43.9967 22.6674 71.4485 32.1039 64.733C31.3434 63.6785 26.4317 61.1668 25.3134 59.6161C24.1951 58.0654 22.794 50.5211 22.123 49.5907C21.4521 48.6603 16.3219 45.7409 15.5637 44.4032C14.8055 43.0655 13.8979 39.4877 14.7258 35.4253L12.2426 37.1924Z", fill: "#6B291E" }),
|
|
22038
|
+
/* @__PURE__ */ jsx("path", { d: "M50.4102 36.1737C50.6823 37.524 52.0035 38.4016 53.3614 38.1339C54.7192 37.8662 55.5994 36.5546 55.3274 35.2043L52.8688 35.689L50.4102 36.1737ZM38.8014 26.9523C37.4241 27.0934 36.425 28.3178 36.5697 29.6872C36.7144 31.0565 37.9482 32.0522 39.3255 31.9111L39.0634 29.4317L38.8014 26.9523ZM52.8688 35.689C55.3274 35.2043 55.327 35.2023 55.3266 35.2004C55.3265 35.1997 55.326 35.1977 55.3258 35.1963C55.3252 35.1935 55.3246 35.1906 55.324 35.1875C55.3227 35.1815 55.3213 35.1749 55.3198 35.1679C55.3168 35.1539 55.3133 35.1379 55.3092 35.1201C55.3011 35.0845 55.2907 35.0415 55.2779 34.9916C55.2522 34.8918 55.2163 34.764 55.1676 34.6127C55.0703 34.3106 54.9205 33.911 54.6961 33.4507C54.247 32.5296 53.4925 31.3535 52.2552 30.241C49.7078 27.9508 45.5021 26.2658 38.8014 26.9523L39.0634 29.4317L39.3255 31.9111C44.9159 31.3384 47.6128 32.782 48.8954 33.9352C49.5731 34.5445 49.9653 35.1687 50.1842 35.6177C50.2938 35.8424 50.3584 36.0201 50.3919 36.1242C50.4086 36.1759 50.4172 36.2084 50.4197 36.2181C50.4209 36.223 50.4206 36.222 50.419 36.2148C50.4181 36.2111 50.417 36.2059 50.4155 36.1991C50.4148 36.1957 50.414 36.1919 50.4131 36.1876C50.4127 36.1855 50.4122 36.1833 50.4117 36.181C50.4115 36.1798 50.4111 36.178 50.411 36.1774C50.4106 36.1755 50.4102 36.1737 52.8688 35.689Z", fill: "#A2634A" }),
|
|
22039
|
+
/* @__PURE__ */ jsx("path", { d: "M54.7526 47.3947C55.5602 48.5146 57.1269 48.7734 58.2518 47.9729C59.3767 47.1723 59.634 45.6155 58.8264 44.4956L56.7895 45.9452L54.7526 47.3947ZM38.1539 36.5617C36.7805 36.7357 35.8113 37.9837 35.9891 39.3493C36.1669 40.7148 37.4244 41.6808 38.7978 41.5068L38.4758 39.0342L38.1539 36.5617ZM56.7895 45.9452L58.8264 44.4956C57.4277 42.5562 55.3182 40.1317 51.9378 38.4204C48.5385 36.6995 44.0772 35.8112 38.1539 36.5617L38.4758 39.0342L38.7978 41.5068C43.8796 40.863 47.3003 41.6624 49.6691 42.8616C52.0569 44.0704 53.6015 45.7985 54.7526 47.3947L56.7895 45.9452Z", fill: "#A2634A" })
|
|
22040
|
+
] })
|
|
22041
|
+
] }),
|
|
22042
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("filter", { id: shadowId, x: "-1.99995", y: "0", width: "78.3658", height: "81.8518", filterUnits: "userSpaceOnUse", colorInterpolationFilters: "sRGB", children: [
|
|
22043
|
+
/* @__PURE__ */ jsx("feFlood", { floodOpacity: "0", result: "BackgroundImageFix" }),
|
|
22044
|
+
/* @__PURE__ */ jsx("feColorMatrix", { in: "SourceAlpha", type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", result: "hardAlpha" }),
|
|
22045
|
+
/* @__PURE__ */ jsx("feOffset", { dy: "2" }),
|
|
22046
|
+
/* @__PURE__ */ jsx("feGaussianBlur", { stdDeviation: "1" }),
|
|
22047
|
+
/* @__PURE__ */ jsx("feComposite", { in2: "hardAlpha", operator: "out" }),
|
|
22048
|
+
/* @__PURE__ */ jsx("feColorMatrix", { type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" }),
|
|
22049
|
+
/* @__PURE__ */ jsx("feBlend", { mode: "normal", in2: "BackgroundImageFix", result: "effect1_dropShadow" }),
|
|
22050
|
+
/* @__PURE__ */ jsx("feBlend", { mode: "normal", in: "SourceGraphic", in2: "effect1_dropShadow", result: "shape" })
|
|
22051
|
+
] }) })
|
|
22052
|
+
]
|
|
22053
|
+
}
|
|
22054
|
+
);
|
|
22055
|
+
}
|
|
22056
|
+
const TUTORIAL_HAND_TIP = { x: 56 / 75, y: 22 / 78 };
|
|
22057
|
+
const CYCLE_MS = 1400;
|
|
22058
|
+
const PRESS_AT_MS = 650;
|
|
22059
|
+
const PRESS_HOLD_MS = 220;
|
|
22060
|
+
const HOLE_PAD = 10;
|
|
22061
|
+
const HOLE_RADIUS = 18;
|
|
22062
|
+
const HAND_W = 72;
|
|
22063
|
+
const HAND_H = 75;
|
|
22064
|
+
const BUBBLE_W = 240;
|
|
22065
|
+
const BUBBLE_GAP = 14;
|
|
22066
|
+
const EDGE = 8;
|
|
22067
|
+
const DEFAULT_NEXT_LABEL = "فهمت";
|
|
22068
|
+
function resolve(t) {
|
|
22069
|
+
return (typeof t === "function" ? t() : t) ?? null;
|
|
22070
|
+
}
|
|
22071
|
+
function toRect(t) {
|
|
22072
|
+
if (!t) return null;
|
|
22073
|
+
const r = t instanceof Element ? t.getBoundingClientRect() : t;
|
|
22074
|
+
if (r.width <= 0 || r.height <= 0) return null;
|
|
22075
|
+
return { x: Math.round(r.x), y: Math.round(r.y), width: Math.round(r.width), height: Math.round(r.height) };
|
|
22076
|
+
}
|
|
22077
|
+
const centerOf = (r) => ({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
|
|
22078
|
+
const TutorialOverlay = ({ tutorial, root, json, options, onClose }) => {
|
|
22079
|
+
const { steps } = tutorial;
|
|
22080
|
+
const [stepIndex, setStepIndex] = useState(
|
|
22081
|
+
() => Math.min(Math.max(0, Math.floor(options.startStep ?? 0)), steps.length - 1)
|
|
22082
|
+
);
|
|
22083
|
+
const step = steps[stepIndex];
|
|
22084
|
+
const optionsRef = useRef(options);
|
|
22085
|
+
optionsRef.current = options;
|
|
22086
|
+
const ctx = { root, json, submitTarget: resolve(options.submitTarget) };
|
|
22087
|
+
const ctxRef = useRef(ctx);
|
|
22088
|
+
ctxRef.current = ctx;
|
|
22089
|
+
const [layout, setLayout] = useState(null);
|
|
22090
|
+
useEffect(() => {
|
|
22091
|
+
let raf = 0;
|
|
22092
|
+
let last = "";
|
|
22093
|
+
const tick = () => {
|
|
22094
|
+
const c = ctxRef.current;
|
|
22095
|
+
const next2 = {
|
|
22096
|
+
spotlight: step.spotlight(c).map(toRect),
|
|
22097
|
+
taps: step.taps(c).map(toRect),
|
|
22098
|
+
anchor: toRect(resolve(optionsRef.current.bubbleAnchor)),
|
|
22099
|
+
root: toRect(c.root),
|
|
22100
|
+
vw: window.innerWidth,
|
|
22101
|
+
vh: window.innerHeight
|
|
22102
|
+
};
|
|
22103
|
+
const key2 = JSON.stringify(next2);
|
|
22104
|
+
if (key2 !== last) {
|
|
22105
|
+
last = key2;
|
|
22106
|
+
setLayout(next2);
|
|
22107
|
+
}
|
|
22108
|
+
raf = requestAnimationFrame(tick);
|
|
22109
|
+
};
|
|
22110
|
+
tick();
|
|
22111
|
+
return () => cancelAnimationFrame(raf);
|
|
22112
|
+
}, [step]);
|
|
22113
|
+
const [tapIndex, setTapIndex] = useState(0);
|
|
22114
|
+
const [tapCount, setTapCount] = useState(0);
|
|
22115
|
+
const [shownStep, setShownStep] = useState(stepIndex);
|
|
22116
|
+
if (shownStep !== stepIndex) {
|
|
22117
|
+
setShownStep(stepIndex);
|
|
22118
|
+
setTapIndex(0);
|
|
22119
|
+
setTapCount(0);
|
|
22120
|
+
}
|
|
22121
|
+
useEffect(() => {
|
|
22122
|
+
let release;
|
|
22123
|
+
const press = setTimeout(() => {
|
|
22124
|
+
setTapCount((n) => n + 1);
|
|
22125
|
+
const targets = step.taps(ctxRef.current);
|
|
22126
|
+
const el = targets[tapIndex % Math.max(1, targets.length)];
|
|
22127
|
+
if (el instanceof Element) {
|
|
22128
|
+
el.classList.add("met-tutorial-pressed");
|
|
22129
|
+
release = setTimeout(() => el.classList.remove("met-tutorial-pressed"), PRESS_HOLD_MS);
|
|
22130
|
+
}
|
|
22131
|
+
}, PRESS_AT_MS);
|
|
22132
|
+
const advance = setTimeout(() => setTapIndex((i) => i + 1), CYCLE_MS);
|
|
22133
|
+
return () => {
|
|
22134
|
+
clearTimeout(press);
|
|
22135
|
+
clearTimeout(advance);
|
|
22136
|
+
if (release) clearTimeout(release);
|
|
22137
|
+
root.querySelectorAll(".met-tutorial-pressed").forEach((el) => el.classList.remove("met-tutorial-pressed"));
|
|
22138
|
+
};
|
|
22139
|
+
}, [step, tapIndex, root]);
|
|
22140
|
+
const text = options.texts?.[step.id] ?? step.text;
|
|
22141
|
+
const textRef = useRef(text);
|
|
22142
|
+
textRef.current = text;
|
|
22143
|
+
useEffect(() => {
|
|
22144
|
+
optionsRef.current.onStep?.(stepIndex, step.id);
|
|
22145
|
+
optionsRef.current.onSpeak?.(step.id, textRef.current);
|
|
22146
|
+
}, [stepIndex, step]);
|
|
22147
|
+
const isLast = stepIndex === steps.length - 1;
|
|
22148
|
+
const next = () => {
|
|
22149
|
+
if (isLast) onClose(true);
|
|
22150
|
+
else setStepIndex((i) => i + 1);
|
|
22151
|
+
};
|
|
22152
|
+
const overlayRef = useRef(null);
|
|
22153
|
+
const nextRef = useRef(null);
|
|
22154
|
+
const onCloseRef = useRef(onClose);
|
|
22155
|
+
onCloseRef.current = onClose;
|
|
22156
|
+
useEffect(() => {
|
|
22157
|
+
const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
22158
|
+
previous?.blur();
|
|
22159
|
+
const onKeyDown = (e) => {
|
|
22160
|
+
const overlay = overlayRef.current;
|
|
22161
|
+
if (e.key === "Escape") {
|
|
22162
|
+
e.preventDefault();
|
|
22163
|
+
e.stopPropagation();
|
|
22164
|
+
onCloseRef.current(false);
|
|
22165
|
+
return;
|
|
22166
|
+
}
|
|
22167
|
+
if (e.key === "Tab" && overlay) {
|
|
22168
|
+
e.preventDefault();
|
|
22169
|
+
const buttons = Array.from(overlay.querySelectorAll("button"));
|
|
22170
|
+
if (buttons.length === 0) return;
|
|
22171
|
+
const at = buttons.indexOf(document.activeElement);
|
|
22172
|
+
const to = (at + (e.shiftKey ? -1 : 1) + buttons.length) % buttons.length;
|
|
22173
|
+
buttons[to]?.focus();
|
|
22174
|
+
return;
|
|
22175
|
+
}
|
|
22176
|
+
if (overlay && e.target instanceof Node && overlay.contains(e.target)) return;
|
|
22177
|
+
e.preventDefault();
|
|
22178
|
+
e.stopPropagation();
|
|
22179
|
+
};
|
|
22180
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
22181
|
+
return () => {
|
|
22182
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
22183
|
+
if (previous?.isConnected) previous.focus();
|
|
22184
|
+
};
|
|
22185
|
+
}, []);
|
|
22186
|
+
const bubbleShown = layout !== null;
|
|
22187
|
+
useEffect(() => {
|
|
22188
|
+
nextRef.current?.focus({ focusVisible: false });
|
|
22189
|
+
}, [stepIndex, bubbleShown]);
|
|
22190
|
+
const maskId = `met-tutorial-mask-${useId()}`;
|
|
22191
|
+
if (typeof document === "undefined") return null;
|
|
22192
|
+
const holes = (layout?.spotlight ?? []).filter((r) => r !== null);
|
|
22193
|
+
const tapTarget = layout?.taps.length ? layout.taps[tapIndex % layout.taps.length] ?? null : null;
|
|
22194
|
+
const Scene = step.Scene;
|
|
22195
|
+
return createPortal(
|
|
22196
|
+
/* @__PURE__ */ jsxs("div", { ref: overlayRef, className: "met-tutorial", role: "dialog", "aria-modal": "true", "aria-label": text, children: [
|
|
22197
|
+
/* @__PURE__ */ jsxs("svg", { className: "met-tutorial-dim", "aria-hidden": "true", children: [
|
|
22198
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("mask", { id: maskId, children: [
|
|
22199
|
+
/* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: "white" }),
|
|
22200
|
+
holes.map((r, i) => /* @__PURE__ */ jsx(
|
|
22201
|
+
"rect",
|
|
22202
|
+
{
|
|
22203
|
+
x: r.x - HOLE_PAD,
|
|
22204
|
+
y: r.y - HOLE_PAD,
|
|
22205
|
+
width: r.width + HOLE_PAD * 2,
|
|
22206
|
+
height: r.height + HOLE_PAD * 2,
|
|
22207
|
+
rx: HOLE_RADIUS,
|
|
22208
|
+
fill: "black"
|
|
22209
|
+
},
|
|
22210
|
+
i
|
|
22211
|
+
))
|
|
22212
|
+
] }) }),
|
|
22213
|
+
/* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", mask: `url(#${maskId})` })
|
|
22214
|
+
] }),
|
|
22215
|
+
holes.map((r, i) => /* @__PURE__ */ jsx(
|
|
22216
|
+
"div",
|
|
22217
|
+
{
|
|
22218
|
+
className: "met-tutorial-glow",
|
|
22219
|
+
style: {
|
|
22220
|
+
left: r.x - HOLE_PAD,
|
|
22221
|
+
top: r.y - HOLE_PAD,
|
|
22222
|
+
width: r.width + HOLE_PAD * 2,
|
|
22223
|
+
height: r.height + HOLE_PAD * 2,
|
|
22224
|
+
borderRadius: HOLE_RADIUS
|
|
22225
|
+
}
|
|
22226
|
+
},
|
|
22227
|
+
i
|
|
22228
|
+
)),
|
|
22229
|
+
Scene && layout && /* @__PURE__ */ jsx(Scene, { ctx, spotlight: layout.spotlight, taps: layout.taps, tapCount }),
|
|
22230
|
+
tapTarget && /* @__PURE__ */ jsx(Hand, { target: tapTarget, tapKey: `${stepIndex}-${tapIndex}` }),
|
|
22231
|
+
layout && /* @__PURE__ */ jsx(
|
|
22232
|
+
Bubble,
|
|
22233
|
+
{
|
|
22234
|
+
layout,
|
|
22235
|
+
text,
|
|
22236
|
+
stepIndex,
|
|
22237
|
+
stepCount: steps.length,
|
|
22238
|
+
nextLabel: options.nextLabel ?? DEFAULT_NEXT_LABEL,
|
|
22239
|
+
onSpeak: options.onSpeak && (options.canSpeak?.(step.id) ?? true) ? () => optionsRef.current.onSpeak?.(step.id, textRef.current) : void 0,
|
|
22240
|
+
onNext: next,
|
|
22241
|
+
nextRef
|
|
22242
|
+
}
|
|
22243
|
+
)
|
|
22244
|
+
] }),
|
|
22245
|
+
document.body
|
|
22246
|
+
);
|
|
22247
|
+
};
|
|
22248
|
+
const Hand = ({ target, tapKey }) => {
|
|
22249
|
+
const c = centerOf(target);
|
|
22250
|
+
return /* @__PURE__ */ jsx(
|
|
22251
|
+
"div",
|
|
22252
|
+
{
|
|
22253
|
+
className: "met-tutorial-hand",
|
|
22254
|
+
style: {
|
|
22255
|
+
width: HAND_W,
|
|
22256
|
+
height: HAND_H,
|
|
22257
|
+
transform: `translate(${c.x - TUTORIAL_HAND_TIP.x * HAND_W}px, ${c.y - TUTORIAL_HAND_TIP.y * HAND_H}px)`
|
|
22258
|
+
},
|
|
22259
|
+
children: /* @__PURE__ */ jsxs("div", { className: "met-tutorial-hand-press", children: [
|
|
22260
|
+
/* @__PURE__ */ jsx(
|
|
22261
|
+
"span",
|
|
22262
|
+
{
|
|
22263
|
+
className: "met-tutorial-ripple",
|
|
22264
|
+
style: { left: TUTORIAL_HAND_TIP.x * HAND_W, top: TUTORIAL_HAND_TIP.y * HAND_H }
|
|
22265
|
+
}
|
|
22266
|
+
),
|
|
22267
|
+
/* @__PURE__ */ jsx(LionHand, {})
|
|
22268
|
+
] }, tapKey)
|
|
22269
|
+
}
|
|
22270
|
+
);
|
|
22271
|
+
};
|
|
22272
|
+
const Bubble = ({ layout, text, stepIndex, stepCount, nextLabel, onSpeak, onNext, nextRef }) => {
|
|
22273
|
+
const { anchor, root, vw, vh } = layout;
|
|
22274
|
+
const clampLeft = (x) => Math.min(Math.max(EDGE, x), Math.max(EDGE, vw - BUBBLE_W - EDGE));
|
|
22275
|
+
const style = anchor ? { left: clampLeft(anchor.x), bottom: Math.max(EDGE, vh - anchor.y + BUBBLE_GAP), width: BUBBLE_W } : { left: clampLeft((root?.x ?? 0) + EDGE * 2), top: (root?.y ?? 0) + EDGE * 2, width: BUBBLE_W };
|
|
22276
|
+
return /* @__PURE__ */ jsxs("div", { className: `met-tutorial-bubble${anchor ? " met-tutorial-bubble--anchored" : ""}`, style, children: [
|
|
22277
|
+
/* @__PURE__ */ jsx("p", { className: "met-tutorial-text", dir: "auto", children: text }),
|
|
22278
|
+
/* @__PURE__ */ jsxs("div", { className: "met-tutorial-actions", children: [
|
|
22279
|
+
onSpeak && /* @__PURE__ */ jsx("button", { type: "button", className: "met-tutorial-speak", "aria-label": "🔊", onClick: onSpeak, children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "24", height: "24", "aria-hidden": "true", children: [
|
|
22280
|
+
/* @__PURE__ */ jsx("path", { d: "M4 9v6h4l5 4V5L8 9H4z", fill: "currentColor" }),
|
|
22281
|
+
/* @__PURE__ */ jsx("path", { d: "M16 8.5a5 5 0 0 1 0 7M18.5 6a8.5 8.5 0 0 1 0 12", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
|
|
22282
|
+
] }) }),
|
|
22283
|
+
/* @__PURE__ */ jsx("button", { ref: nextRef, type: "button", className: "met-tutorial-next", onClick: onNext, children: nextLabel })
|
|
22284
|
+
] }),
|
|
22285
|
+
stepCount > 1 && /* @__PURE__ */ jsx("div", { className: "met-tutorial-dots", "aria-hidden": "true", children: Array.from({ length: stepCount }, (_, i) => /* @__PURE__ */ jsx("span", { className: i === stepIndex ? "is-current" : i < stepIndex ? "is-done" : "" }, i)) })
|
|
22286
|
+
] });
|
|
22287
|
+
};
|
|
22288
|
+
const DEMO_KEYS = ["2", "5", "8"];
|
|
22289
|
+
const DEFAULT_SLOTS = 3;
|
|
22290
|
+
const MAX_SLOTS = 6;
|
|
22291
|
+
const STAND_IN_CHECK_SIZE = 52;
|
|
22292
|
+
const STAND_IN_CHECK_GAP = 20;
|
|
22293
|
+
function keypad(ctx) {
|
|
22294
|
+
return ctx.root.querySelector('[data-tutorial="keypad"]');
|
|
22295
|
+
}
|
|
22296
|
+
function key(ctx, k) {
|
|
22297
|
+
return ctx.root.querySelector(`[data-tutorial="keypad"] [data-key="${k}"]`);
|
|
22298
|
+
}
|
|
22299
|
+
function answerBox(ctx) {
|
|
22300
|
+
const inputs = getTextInputs(ctx.root);
|
|
22301
|
+
return inputs.find(canTakeInput) ?? inputs[0] ?? null;
|
|
22302
|
+
}
|
|
22303
|
+
function answerSlots(ctx) {
|
|
22304
|
+
const id = answerBox(ctx)?.id.replace(/-input$/, "");
|
|
22305
|
+
const max = id ? getMaxInputLength(ctx.json, id) : void 0;
|
|
22306
|
+
return Math.min(MAX_SLOTS, Math.max(1, max ?? DEFAULT_SLOTS));
|
|
22307
|
+
}
|
|
22308
|
+
function checkTarget(ctx) {
|
|
22309
|
+
if (ctx.submitTarget) return ctx.submitTarget;
|
|
22310
|
+
const box = answerBox(ctx)?.getBoundingClientRect();
|
|
22311
|
+
if (!box) return null;
|
|
22312
|
+
return new DOMRect(
|
|
22313
|
+
box.right + STAND_IN_CHECK_GAP,
|
|
22314
|
+
box.top + box.height / 2 - STAND_IN_CHECK_SIZE / 2,
|
|
22315
|
+
STAND_IN_CHECK_SIZE,
|
|
22316
|
+
STAND_IN_CHECK_SIZE
|
|
22317
|
+
);
|
|
22318
|
+
}
|
|
22319
|
+
const demoKeys = (ctx) => DEMO_KEYS.map((k) => key(ctx, k));
|
|
22320
|
+
const StandInFill = ({ box, slots, filled }) => /* @__PURE__ */ jsx(
|
|
22321
|
+
"div",
|
|
22322
|
+
{
|
|
22323
|
+
className: "met-tutorial-fill",
|
|
22324
|
+
style: { left: box.x, top: box.y, width: box.width, height: box.height },
|
|
22325
|
+
children: Array.from({ length: slots }, (_, i) => /* @__PURE__ */ jsx("span", { className: i < filled ? "met-tutorial-fill-dot" : "met-tutorial-fill-slot", children: i < filled ? "●" : "_" }, i))
|
|
22326
|
+
}
|
|
22327
|
+
);
|
|
22328
|
+
const center = (r) => ({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
|
|
22329
|
+
const HopScene = ({ ctx, spotlight, taps, tapCount }) => {
|
|
22330
|
+
const box = spotlight[1];
|
|
22331
|
+
if (!box) return null;
|
|
22332
|
+
const slots = answerSlots(ctx);
|
|
22333
|
+
const filled = tapCount === 0 ? 0 : (tapCount - 1) % slots + 1;
|
|
22334
|
+
const from = tapCount > 0 ? taps[(tapCount - 1) % taps.length] : null;
|
|
22335
|
+
let arc = null;
|
|
22336
|
+
if (from) {
|
|
22337
|
+
const a = center(from);
|
|
22338
|
+
const b = center(box);
|
|
22339
|
+
const lift = Math.max(40, Math.hypot(b.x - a.x, b.y - a.y) * 0.35);
|
|
22340
|
+
arc = `M ${a.x} ${a.y} Q ${(a.x + b.x) / 2} ${Math.min(a.y, b.y) - lift} ${b.x} ${b.y}`;
|
|
22341
|
+
}
|
|
22342
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22343
|
+
arc && /* @__PURE__ */ jsx("svg", { className: "met-tutorial-arc", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: arc }) }),
|
|
22344
|
+
arc && /* @__PURE__ */ jsx("div", { className: "met-tutorial-hop", style: { offsetPath: `path('${arc}')` } }, tapCount),
|
|
22345
|
+
/* @__PURE__ */ jsx(StandInFill, { box, slots, filled })
|
|
22346
|
+
] });
|
|
22347
|
+
};
|
|
22348
|
+
const CheckScene = ({ ctx, spotlight }) => {
|
|
22349
|
+
const box = spotlight[0];
|
|
22350
|
+
const check = spotlight[1];
|
|
22351
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22352
|
+
box && /* @__PURE__ */ jsx(StandInFill, { box, slots: answerSlots(ctx), filled: answerSlots(ctx) }),
|
|
22353
|
+
check && !ctx.submitTarget && /* @__PURE__ */ jsx(
|
|
22354
|
+
"div",
|
|
22355
|
+
{
|
|
22356
|
+
className: "met-tutorial-stand-in-check",
|
|
22357
|
+
style: { left: check.x, top: check.y, width: check.width, height: check.height },
|
|
22358
|
+
children: /* @__PURE__ */ jsx("span", { children: "✓" })
|
|
22359
|
+
}
|
|
22360
|
+
)
|
|
22361
|
+
] });
|
|
22362
|
+
};
|
|
22363
|
+
const keypadTutorial = {
|
|
22364
|
+
id: "keypad",
|
|
22365
|
+
appliesTo: (json) => json.widgets[NUMERIC_KEYPAD_WIDGET_ID]?.type === JSON_TYPES.NUMERIC_KEYPAD,
|
|
22366
|
+
steps: [
|
|
22367
|
+
{
|
|
22368
|
+
id: "keypad.tap-number",
|
|
22369
|
+
text: "اضغط على رقم",
|
|
22370
|
+
spotlight: (ctx) => [keypad(ctx), answerBox(ctx)],
|
|
22371
|
+
taps: demoKeys
|
|
22372
|
+
},
|
|
22373
|
+
{
|
|
22374
|
+
id: "keypad.hop",
|
|
22375
|
+
text: "يظهر الرقم في الخانة",
|
|
22376
|
+
spotlight: (ctx) => [keypad(ctx), answerBox(ctx)],
|
|
22377
|
+
taps: demoKeys,
|
|
22378
|
+
Scene: HopScene
|
|
22379
|
+
},
|
|
22380
|
+
{
|
|
22381
|
+
id: "keypad.tap-check",
|
|
22382
|
+
text: "اضغط على زر التحقق",
|
|
22383
|
+
spotlight: (ctx) => [answerBox(ctx), checkTarget(ctx)],
|
|
22384
|
+
taps: (ctx) => [checkTarget(ctx)],
|
|
22385
|
+
Scene: CheckScene
|
|
22386
|
+
}
|
|
22387
|
+
]
|
|
22388
|
+
};
|
|
22389
|
+
const WIDGET_TUTORIALS = [keypadTutorial];
|
|
22390
|
+
function findWidgetTutorial(json) {
|
|
22391
|
+
return WIDGET_TUTORIALS.find((t) => t.appliesTo(json)) ?? null;
|
|
22392
|
+
}
|
|
21834
22393
|
const TOOL_ASPECT_RATIO = {
|
|
21835
22394
|
ruler: RULER_ASPECT_RATIO,
|
|
21836
22395
|
protractor: PROTRACTOR_ASPECT_RATIO,
|
|
@@ -21869,6 +22428,11 @@ function buildInitialValues(json) {
|
|
|
21869
22428
|
function toBaseId(widgetId) {
|
|
21870
22429
|
return parseFractionKey(widgetId)?.widgetId ?? widgetId;
|
|
21871
22430
|
}
|
|
22431
|
+
function hasJustFilledBox(json, key2, previous, value) {
|
|
22432
|
+
const maxLength = getMaxInputLength(json, key2);
|
|
22433
|
+
if (maxLength === void 0) return false;
|
|
22434
|
+
return String(previous ?? "").length < maxLength && String(value).length >= maxLength;
|
|
22435
|
+
}
|
|
21872
22436
|
function buildInitialValuesSignature(json) {
|
|
21873
22437
|
return JSON.stringify({
|
|
21874
22438
|
widgets: json.widgets,
|
|
@@ -21918,6 +22482,27 @@ function buildRevealedValues(json) {
|
|
|
21918
22482
|
}
|
|
21919
22483
|
return revealed;
|
|
21920
22484
|
}
|
|
22485
|
+
function buildRevealedDropSources(json, revealedValues) {
|
|
22486
|
+
const pool = [];
|
|
22487
|
+
const bank = json.widgets[DRAG_OPTIONS_WIDGET_ID];
|
|
22488
|
+
if (bank?.type === JSON_TYPES.DRAG_OPTIONS) {
|
|
22489
|
+
bank.options.forEach((value, idx) => pool.push({ optionId: `drag-option-${idx}`, value: String(value) }));
|
|
22490
|
+
}
|
|
22491
|
+
for (const [widgetId, widget] of Object.entries(json.widgets)) {
|
|
22492
|
+
if (widget.type === JSON_TYPES.INLINE_DRAG_OPTION) pool.push({ optionId: widgetId, value: widget.value });
|
|
22493
|
+
}
|
|
22494
|
+
const sources = /* @__PURE__ */ new Map();
|
|
22495
|
+
if (pool.length === 0) return sources;
|
|
22496
|
+
const used = /* @__PURE__ */ new Set();
|
|
22497
|
+
for (const [inputId, value] of Object.entries(revealedValues)) {
|
|
22498
|
+
if (typeof value !== "string" || value === "") continue;
|
|
22499
|
+
const match = pool.find((o) => !used.has(o.optionId) && normalize(o.value) === normalize(value));
|
|
22500
|
+
if (!match) continue;
|
|
22501
|
+
used.add(match.optionId);
|
|
22502
|
+
sources.set(inputId, match.optionId);
|
|
22503
|
+
}
|
|
22504
|
+
return sources;
|
|
22505
|
+
}
|
|
21921
22506
|
const ExerciseRenderer = forwardRef(({
|
|
21922
22507
|
json,
|
|
21923
22508
|
onSubmit,
|
|
@@ -21940,6 +22525,8 @@ const ExerciseRenderer = forwardRef(({
|
|
|
21940
22525
|
const rootRef = useRef(null);
|
|
21941
22526
|
const [resetKey, setResetKey] = useState(0);
|
|
21942
22527
|
const [revealed, setRevealed] = useState(false);
|
|
22528
|
+
const tutorial = useMemo(() => findWidgetTutorial(json), [json]);
|
|
22529
|
+
const [tutorialRun, setTutorialRun] = useState(null);
|
|
21943
22530
|
const themeStyle = useMemo(() => {
|
|
21944
22531
|
const style = {};
|
|
21945
22532
|
if (theme?.correct) style["--exercise-correct"] = theme.correct;
|
|
@@ -21960,6 +22547,7 @@ const ExerciseRenderer = forwardRef(({
|
|
|
21960
22547
|
setResetKey((k) => k + 1);
|
|
21961
22548
|
setLoadedValuesSignature(initialValuesSignature);
|
|
21962
22549
|
touchedRef.current = /* @__PURE__ */ new Set();
|
|
22550
|
+
setTutorialRun(null);
|
|
21963
22551
|
}, [json, initialValuesSignature, loadedValuesSignature]);
|
|
21964
22552
|
const locked = validationDetails !== null || externallyLocked;
|
|
21965
22553
|
const lastResultRef = useRef(null);
|
|
@@ -22043,7 +22631,7 @@ const ExerciseRenderer = forwardRef(({
|
|
|
22043
22631
|
const next = { ...valuesRef.current, ...revealedValues };
|
|
22044
22632
|
valuesRef.current = next;
|
|
22045
22633
|
setValues(next);
|
|
22046
|
-
setDroppedSources(
|
|
22634
|
+
setDroppedSources(buildRevealedDropSources(json, revealedValues));
|
|
22047
22635
|
setValidationDetails(details);
|
|
22048
22636
|
setRevealed(true);
|
|
22049
22637
|
setResetKey((k) => k + 1);
|
|
@@ -22051,20 +22639,30 @@ const ExerciseRenderer = forwardRef(({
|
|
|
22051
22639
|
focusFirstInput: () => {
|
|
22052
22640
|
const container = rootRef.current ?? document.querySelector(".math-exercise-renderer");
|
|
22053
22641
|
if (!container) return;
|
|
22054
|
-
const candidates =
|
|
22055
|
-
container.querySelectorAll('input[type="text"], input[type="number"], textarea, select')
|
|
22056
|
-
);
|
|
22057
|
-
const canTakeInput = (el) => !el.readOnly && !el.disabled;
|
|
22642
|
+
const candidates = getTextInputs(container);
|
|
22058
22643
|
const target = candidates.find(canTakeInput) ?? (locked ? void 0 : candidates[0]);
|
|
22059
22644
|
target?.focus();
|
|
22060
|
-
}
|
|
22061
|
-
|
|
22645
|
+
},
|
|
22646
|
+
hasTutorial: () => tutorial !== null,
|
|
22647
|
+
tutorialId: () => tutorial?.id ?? null,
|
|
22648
|
+
startTutorial: (options = {}) => {
|
|
22649
|
+
if (!tutorial || !rootRef.current) return false;
|
|
22650
|
+
setTutorialRun((prev) => ({ options, run: (prev?.run ?? 0) + 1 }));
|
|
22651
|
+
return true;
|
|
22652
|
+
},
|
|
22653
|
+
stopTutorial: () => setTutorialRun(null)
|
|
22654
|
+
}), [json, values, onSubmit, playSound, locked, initialValuesSignature, tutorial]);
|
|
22655
|
+
const focusNextInput = (currentKey) => {
|
|
22656
|
+
if (!rootRef.current) return;
|
|
22657
|
+
findNextInput(rootRef.current, currentKey)?.focus();
|
|
22658
|
+
};
|
|
22062
22659
|
const handleChange = (widgetId, value, fromDrop = false, sourceOptionId, silent = false) => {
|
|
22063
22660
|
if (locked) return;
|
|
22064
22661
|
if (!silent) {
|
|
22065
22662
|
setRevealed(false);
|
|
22066
22663
|
touchedRef.current.add(toBaseId(widgetId));
|
|
22067
22664
|
}
|
|
22665
|
+
const previous = valuesRef.current[widgetId];
|
|
22068
22666
|
const next = { ...valuesRef.current, [widgetId]: value };
|
|
22069
22667
|
valuesRef.current = next;
|
|
22070
22668
|
setValues(next);
|
|
@@ -22078,6 +22676,9 @@ const ExerciseRenderer = forwardRef(({
|
|
|
22078
22676
|
return next2;
|
|
22079
22677
|
});
|
|
22080
22678
|
onChange?.(next);
|
|
22679
|
+
if (!fromDrop && !silent && hasJustFilledBox(json, widgetId, previous, value)) {
|
|
22680
|
+
focusNextInput(widgetId);
|
|
22681
|
+
}
|
|
22081
22682
|
};
|
|
22082
22683
|
const handleSubmit = () => {
|
|
22083
22684
|
runSubmit();
|
|
@@ -22192,7 +22793,7 @@ const ExerciseRenderer = forwardRef(({
|
|
|
22192
22793
|
const rulerWidget = json.widgets[RULER_WIDGET_ID];
|
|
22193
22794
|
const protractorWidget = json.widgets[PROTRACTOR_WIDGET_ID];
|
|
22194
22795
|
const setSquareWidget = json.widgets[SET_SQUARE_WIDGET_ID];
|
|
22195
|
-
const numericKeypadWidget = json.widgets[
|
|
22796
|
+
const numericKeypadWidget = json.widgets[NUMERIC_KEYPAD_WIDGET_ID];
|
|
22196
22797
|
const { tools, toolsMinHeight } = useMemo(() => {
|
|
22197
22798
|
const list = [];
|
|
22198
22799
|
const PAD = 12;
|
|
@@ -22211,7 +22812,7 @@ const ExerciseRenderer = forwardRef(({
|
|
|
22211
22812
|
if (setSquareWidget) add2("set-square-1", "set-square", Math.round(getSize(setSquareWidget) * 160));
|
|
22212
22813
|
return { tools: list, toolsMinHeight: maxH };
|
|
22213
22814
|
}, [rulerWidget, protractorWidget, setSquareWidget]);
|
|
22214
|
-
return /* @__PURE__ */ jsx(SoundPlayerProvider, { play: playSound, children: /* @__PURE__ */ jsx(ExerciseLockProvider, { locked, children: /* @__PURE__ */ jsx(ExerciseDndProvider, { droppedSources, children: /* @__PURE__ */ jsxs("div", { ref: rootRef, className: `math-exercise-renderer ${revealed ? "revealed" : ""} ${locked ? "locked" : ""} ${className}`, style: themeStyle, children: [
|
|
22815
|
+
return /* @__PURE__ */ jsx(SoundPlayerProvider, { play: playSound, children: /* @__PURE__ */ jsx(ExerciseLockProvider, { locked, children: /* @__PURE__ */ jsx(ExerciseDndProvider, { droppedSources, exerciseKey: loadedValuesSignature, children: /* @__PURE__ */ jsxs("div", { ref: rootRef, className: `math-exercise-renderer ${revealed ? "revealed" : ""} ${locked ? "locked" : ""} ${className}`, style: themeStyle, children: [
|
|
22215
22816
|
/* @__PURE__ */ jsxs(
|
|
22216
22817
|
"div",
|
|
22217
22818
|
{
|
|
@@ -22307,7 +22908,21 @@ const ExerciseRenderer = forwardRef(({
|
|
|
22307
22908
|
}
|
|
22308
22909
|
}
|
|
22309
22910
|
) }),
|
|
22310
|
-
showSubmitButton && /* @__PURE__ */ jsx("button", { className: "exercise-submit-btn", disabled: locked, onClick: handleSubmit, children: "Check Answer" })
|
|
22911
|
+
showSubmitButton && /* @__PURE__ */ jsx("button", { className: "exercise-submit-btn", disabled: locked, onClick: handleSubmit, children: "Check Answer" }),
|
|
22912
|
+
tutorial && tutorialRun && rootRef.current && /* @__PURE__ */ jsx(
|
|
22913
|
+
TutorialOverlay,
|
|
22914
|
+
{
|
|
22915
|
+
tutorial,
|
|
22916
|
+
root: rootRef.current,
|
|
22917
|
+
json,
|
|
22918
|
+
options: tutorialRun.options,
|
|
22919
|
+
onClose: (finished) => {
|
|
22920
|
+
setTutorialRun(null);
|
|
22921
|
+
if (finished) tutorialRun.options.onDone?.();
|
|
22922
|
+
}
|
|
22923
|
+
},
|
|
22924
|
+
tutorialRun.run
|
|
22925
|
+
)
|
|
22311
22926
|
] }) }) }) });
|
|
22312
22927
|
});
|
|
22313
22928
|
ExerciseRenderer.displayName = "ExerciseRenderer";
|
|
@@ -22441,7 +23056,7 @@ export {
|
|
|
22441
23056
|
VopSeparatorItemSchema as ay,
|
|
22442
23057
|
VopCellItemSchema as az,
|
|
22443
23058
|
tokenizeSafe as b,
|
|
22444
|
-
|
|
23059
|
+
TextBlockSchema as b$,
|
|
22445
23060
|
validateWidgetJSON as b0,
|
|
22446
23061
|
validateWidgetJSONSafe as b1,
|
|
22447
23062
|
EngineError as b2,
|
|
@@ -22452,181 +23067,185 @@ export {
|
|
|
22452
23067
|
ExerciseRenderer as b7,
|
|
22453
23068
|
LayoutRenderer as b8,
|
|
22454
23069
|
WidgetRenderer as b9,
|
|
22455
|
-
|
|
22456
|
-
|
|
22457
|
-
|
|
22458
|
-
|
|
22459
|
-
|
|
22460
|
-
|
|
22461
|
-
|
|
22462
|
-
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
|
|
22469
|
-
|
|
22470
|
-
|
|
22471
|
-
|
|
22472
|
-
|
|
22473
|
-
|
|
22474
|
-
|
|
22475
|
-
|
|
22476
|
-
|
|
22477
|
-
|
|
22478
|
-
|
|
22479
|
-
|
|
22480
|
-
|
|
22481
|
-
|
|
23070
|
+
CLOCK_MODES as bA,
|
|
23071
|
+
CLOCK_FORMATS as bB,
|
|
23072
|
+
CLOCK_TIME_REGEX as bC,
|
|
23073
|
+
ClockInlineSchema as bD,
|
|
23074
|
+
InlineNodeSchema as bE,
|
|
23075
|
+
createFreeText as bF,
|
|
23076
|
+
createSpace as bG,
|
|
23077
|
+
createHighlightedText as bH,
|
|
23078
|
+
createColorText as bI,
|
|
23079
|
+
createBoldText as bJ,
|
|
23080
|
+
createStringInput as bK,
|
|
23081
|
+
createNumericInput as bL,
|
|
23082
|
+
createImage as bM,
|
|
23083
|
+
createFraction as bN,
|
|
23084
|
+
createVisualFraction as bO,
|
|
23085
|
+
createInlineDragOption as bP,
|
|
23086
|
+
createDisplayClock as bQ,
|
|
23087
|
+
createInteractiveClock as bR,
|
|
23088
|
+
isFreeText as bS,
|
|
23089
|
+
isSpace as bT,
|
|
23090
|
+
isInlineInput as bU,
|
|
23091
|
+
isFraction as bV,
|
|
23092
|
+
isImage as bW,
|
|
23093
|
+
isClock as bX,
|
|
23094
|
+
isInlineNode as bY,
|
|
23095
|
+
isVisualFractionInline as bZ,
|
|
23096
|
+
isInlineDragOption as b_,
|
|
22482
23097
|
renderExercise as ba,
|
|
22483
23098
|
SoundProvider as bb,
|
|
22484
23099
|
useSound as bc,
|
|
22485
23100
|
ExerciseLockProvider as bd,
|
|
22486
|
-
|
|
22487
|
-
|
|
22488
|
-
|
|
22489
|
-
|
|
22490
|
-
|
|
22491
|
-
|
|
22492
|
-
|
|
22493
|
-
|
|
22494
|
-
|
|
22495
|
-
|
|
22496
|
-
|
|
22497
|
-
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
|
|
22502
|
-
|
|
22503
|
-
|
|
22504
|
-
|
|
22505
|
-
|
|
22506
|
-
|
|
22507
|
-
|
|
23101
|
+
WIDGET_TUTORIALS as be,
|
|
23102
|
+
findWidgetTutorial as bf,
|
|
23103
|
+
isPairMatcherWidget as bg,
|
|
23104
|
+
isMatcherWidget as bh,
|
|
23105
|
+
VISUAL_FRACTION_SHAPES as bi,
|
|
23106
|
+
VISUAL_FRACTION_SHAPE as bj,
|
|
23107
|
+
VISUAL_FRACTION_DEFAULT_SIZE as bk,
|
|
23108
|
+
VISUAL_FRACTION_MAX_DIVISIONS as bl,
|
|
23109
|
+
INLINE_INPUT_MAX_WIDTH as bm,
|
|
23110
|
+
InlineInputWidthSchema as bn,
|
|
23111
|
+
FreeTextInlineSchema as bo,
|
|
23112
|
+
SpaceInlineSchema as bp,
|
|
23113
|
+
HighlightedTextInlineSchema as bq,
|
|
23114
|
+
ColorTextInlineSchema as br,
|
|
23115
|
+
BoldTextInlineSchema as bs,
|
|
23116
|
+
InlineInputWidgetSchema as bt,
|
|
23117
|
+
FractionInlineSchema as bu,
|
|
23118
|
+
ImageInlineSchema as bv,
|
|
23119
|
+
VisualFractionInlineSchema as bw,
|
|
23120
|
+
validateVisualFractionCapacity as bx,
|
|
23121
|
+
InlineDragOptionSchema as by,
|
|
23122
|
+
CLOCK_TYPES as bz,
|
|
22508
23123
|
compile as c,
|
|
22509
|
-
|
|
22510
|
-
|
|
22511
|
-
|
|
22512
|
-
|
|
22513
|
-
|
|
22514
|
-
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
|
|
22518
|
-
|
|
22519
|
-
|
|
22520
|
-
|
|
22521
|
-
|
|
22522
|
-
|
|
22523
|
-
|
|
22524
|
-
|
|
22525
|
-
|
|
22526
|
-
|
|
22527
|
-
|
|
22528
|
-
|
|
22529
|
-
|
|
22530
|
-
|
|
22531
|
-
|
|
22532
|
-
|
|
22533
|
-
|
|
22534
|
-
|
|
22535
|
-
|
|
22536
|
-
|
|
22537
|
-
|
|
22538
|
-
|
|
22539
|
-
|
|
22540
|
-
|
|
22541
|
-
|
|
22542
|
-
|
|
22543
|
-
|
|
22544
|
-
|
|
22545
|
-
|
|
22546
|
-
|
|
22547
|
-
|
|
22548
|
-
|
|
22549
|
-
|
|
22550
|
-
|
|
22551
|
-
|
|
22552
|
-
|
|
22553
|
-
|
|
22554
|
-
|
|
22555
|
-
|
|
22556
|
-
|
|
22557
|
-
|
|
22558
|
-
|
|
22559
|
-
|
|
22560
|
-
|
|
22561
|
-
|
|
22562
|
-
|
|
22563
|
-
|
|
22564
|
-
|
|
22565
|
-
|
|
22566
|
-
|
|
22567
|
-
|
|
22568
|
-
|
|
22569
|
-
|
|
22570
|
-
|
|
22571
|
-
|
|
22572
|
-
|
|
23124
|
+
createVopEmptyCell as c$,
|
|
23125
|
+
LineBlockSchema as c0,
|
|
23126
|
+
createTextBlock as c1,
|
|
23127
|
+
createLineBlock as c2,
|
|
23128
|
+
isTextBlock as c3,
|
|
23129
|
+
isLineBlock as c4,
|
|
23130
|
+
DisplayInlineNodeSchema as c5,
|
|
23131
|
+
MatcherItemSchema as c6,
|
|
23132
|
+
createMatcherItem as c7,
|
|
23133
|
+
isMatcherItem as c8,
|
|
23134
|
+
MatcherRowSchema as c9,
|
|
23135
|
+
ExpressionValidationSchema as cA,
|
|
23136
|
+
MixedValidationSchema as cB,
|
|
23137
|
+
ValidationNodeSchema as cC,
|
|
23138
|
+
createSimpleValidation as cD,
|
|
23139
|
+
createExpressionValidation as cE,
|
|
23140
|
+
createMixedValidation as cF,
|
|
23141
|
+
isSimpleValidation as cG,
|
|
23142
|
+
isExpressionValidation as cH,
|
|
23143
|
+
isMixedValidation as cI,
|
|
23144
|
+
isValidationNode as cJ,
|
|
23145
|
+
ContentBlockSchema as cK,
|
|
23146
|
+
ExerciseDocumentSchema as cL,
|
|
23147
|
+
createExerciseDocument as cM,
|
|
23148
|
+
isExerciseDocument as cN,
|
|
23149
|
+
VOP_OPERATORS as cO,
|
|
23150
|
+
normalizeVopOperator as cP,
|
|
23151
|
+
VopDigitCellSchema as cQ,
|
|
23152
|
+
VopLabelCellSchema as cR,
|
|
23153
|
+
VopInputCellSchema as cS,
|
|
23154
|
+
VopEmptyCellSchema as cT,
|
|
23155
|
+
VopColorCellSchema as cU,
|
|
23156
|
+
VopBoldCellSchema as cV,
|
|
23157
|
+
VopHighlightCellSchema as cW,
|
|
23158
|
+
VopCellSchema as cX,
|
|
23159
|
+
createVopDigitCell as cY,
|
|
23160
|
+
createVopInputCell as cZ,
|
|
23161
|
+
createVopLabelCell as c_,
|
|
23162
|
+
createMatcherRow as ca,
|
|
23163
|
+
isMatcherRow as cb,
|
|
23164
|
+
MatcherBlockSchema as cc,
|
|
23165
|
+
createMatcherBlock as cd,
|
|
23166
|
+
isMatcherBlock as ce,
|
|
23167
|
+
PairMatcherBlockSchema as cf,
|
|
23168
|
+
createPairMatcherBlock as cg,
|
|
23169
|
+
isPairMatcherBlock as ch,
|
|
23170
|
+
DragOptionsToolSchema as ci,
|
|
23171
|
+
NumericKeypadToolSchema as cj,
|
|
23172
|
+
RulerToolSchema as ck,
|
|
23173
|
+
ProtractorToolSchema as cl,
|
|
23174
|
+
SetSquareToolSchema as cm,
|
|
23175
|
+
ToolNodeSchema as cn,
|
|
23176
|
+
createDragOptions as co,
|
|
23177
|
+
createNumericKeypad as cp,
|
|
23178
|
+
createRulerTool as cq,
|
|
23179
|
+
createProtractorTool as cr,
|
|
23180
|
+
createSetSquareTool as cs,
|
|
23181
|
+
isDragOptions as ct,
|
|
23182
|
+
isNumericKeypad as cu,
|
|
23183
|
+
isRulerTool as cv,
|
|
23184
|
+
isProtractorTool as cw,
|
|
23185
|
+
isSetSquareTool as cx,
|
|
23186
|
+
isToolNode as cy,
|
|
23187
|
+
SimpleValidationSchema as cz,
|
|
22573
23188
|
parse as d,
|
|
22574
|
-
|
|
22575
|
-
|
|
22576
|
-
|
|
22577
|
-
|
|
22578
|
-
|
|
22579
|
-
|
|
22580
|
-
|
|
22581
|
-
|
|
22582
|
-
|
|
22583
|
-
|
|
22584
|
-
|
|
22585
|
-
|
|
22586
|
-
|
|
22587
|
-
|
|
22588
|
-
|
|
22589
|
-
|
|
22590
|
-
|
|
22591
|
-
|
|
22592
|
-
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
|
|
22599
|
-
|
|
22600
|
-
|
|
22601
|
-
|
|
22602
|
-
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
|
|
22606
|
-
|
|
22607
|
-
|
|
22608
|
-
|
|
22609
|
-
|
|
22610
|
-
|
|
22611
|
-
|
|
22612
|
-
|
|
22613
|
-
|
|
22614
|
-
|
|
22615
|
-
|
|
22616
|
-
|
|
22617
|
-
|
|
22618
|
-
|
|
22619
|
-
|
|
22620
|
-
|
|
22621
|
-
|
|
22622
|
-
|
|
22623
|
-
|
|
22624
|
-
|
|
22625
|
-
|
|
22626
|
-
|
|
22627
|
-
|
|
22628
|
-
|
|
22629
|
-
|
|
23189
|
+
createVopColorCell as d0,
|
|
23190
|
+
createVopBoldCell as d1,
|
|
23191
|
+
createVopHighlightCell as d2,
|
|
23192
|
+
VopRowSchema as d3,
|
|
23193
|
+
createVopRow as d4,
|
|
23194
|
+
VopCarryRowSchema as d5,
|
|
23195
|
+
createVopCarryRow as d6,
|
|
23196
|
+
VopSeparatorSchema as d7,
|
|
23197
|
+
createVopSeparator as d8,
|
|
23198
|
+
VerticalOperationBlockSchema as d9,
|
|
23199
|
+
isChartAxis as dA,
|
|
23200
|
+
ChartPictoSchema as dB,
|
|
23201
|
+
createChartPicto as dC,
|
|
23202
|
+
isChartPicto as dD,
|
|
23203
|
+
ChartBarSchema as dE,
|
|
23204
|
+
createChartBar as dF,
|
|
23205
|
+
isChartBar as dG,
|
|
23206
|
+
ChartSeriesSchema as dH,
|
|
23207
|
+
createChartSeries as dI,
|
|
23208
|
+
isChartSeries as dJ,
|
|
23209
|
+
ChartSeriesBarSchema as dK,
|
|
23210
|
+
createChartSeriesBar as dL,
|
|
23211
|
+
isChartSeriesBar as dM,
|
|
23212
|
+
ChartGroupSchema as dN,
|
|
23213
|
+
createChartGroup as dO,
|
|
23214
|
+
isChartGroup as dP,
|
|
23215
|
+
alignGroupBarsToSeries as dQ,
|
|
23216
|
+
ChartBlockSchema as dR,
|
|
23217
|
+
resolveChartAxes as dS,
|
|
23218
|
+
validateUniqueIds as dT,
|
|
23219
|
+
createChartBlock as dU,
|
|
23220
|
+
isChartBlock as dV,
|
|
23221
|
+
GeometryBlockSchema as dW,
|
|
23222
|
+
createGeometryBlock as dX,
|
|
23223
|
+
isGeometryBlock as dY,
|
|
23224
|
+
createVerticalOperationBlock as da,
|
|
23225
|
+
isVopCell as db,
|
|
23226
|
+
isVopRow as dc,
|
|
23227
|
+
isVopCarryRow as dd,
|
|
23228
|
+
isVopSeparator as de,
|
|
23229
|
+
isVerticalOperationBlock as df,
|
|
23230
|
+
GRID_SYMMETRY_AXES as dg,
|
|
23231
|
+
GRID_SYMMETRY_INTERACTIVE_AREAS as dh,
|
|
23232
|
+
GRID_SYMMETRY_MAX_ROWS as di,
|
|
23233
|
+
GRID_SYMMETRY_MAX_COLS as dj,
|
|
23234
|
+
GRID_SYMMETRY_MIN_CELL_SIZE as dk,
|
|
23235
|
+
GRID_SYMMETRY_MAX_CELL_SIZE as dl,
|
|
23236
|
+
isCellInInteractiveArea as dm,
|
|
23237
|
+
reflectGridSymmetryCell as dn,
|
|
23238
|
+
computeGridSymmetryExpectedCells as dp,
|
|
23239
|
+
computeGridSymmetryCellsToFill as dq,
|
|
23240
|
+
GridSymmetryBlockSchema as dr,
|
|
23241
|
+
createGridSymmetryBlock as ds,
|
|
23242
|
+
isGridSymmetryBlock as dt,
|
|
23243
|
+
CHART_TYPES as du,
|
|
23244
|
+
CHART_AXIS_DEFAULTS as dv,
|
|
23245
|
+
CHART_MAX_AXIS_TICKS as dw,
|
|
23246
|
+
CHART_MAX_PICTO_SLOTS as dx,
|
|
23247
|
+
ChartAxisSchema as dy,
|
|
23248
|
+
createChartAxis as dz,
|
|
22630
23249
|
parseSafe as e,
|
|
22631
23250
|
toAST as f,
|
|
22632
23251
|
getFractionKey as g,
|
|
@@ -22650,4 +23269,4 @@ export {
|
|
|
22650
23269
|
extractVariables as y,
|
|
22651
23270
|
validateBooleanExpression as z
|
|
22652
23271
|
};
|
|
22653
|
-
//# sourceMappingURL=index-
|
|
23272
|
+
//# sourceMappingURL=index-BtJRsuwL.mjs.map
|