@mathscan/math-exercise-engine 1.1.2 → 1.1.4
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/dist/index.d.ts +7579 -3151
- package/dist/index.js +958 -83
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +958 -83
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +144 -8
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -21,6 +21,8 @@ const AST_TYPES = {
|
|
|
21
21
|
BOLD_TEXT: "BoldText",
|
|
22
22
|
/** Inline input widget (string or numeric) */
|
|
23
23
|
INLINE_INPUT: "InlineInput",
|
|
24
|
+
/** Single inline draggable option (appears in-flow with text) */
|
|
25
|
+
INLINE_DRAG_OPTION: "InlineDragOption",
|
|
24
26
|
/** Inline fraction widget (numerator/denominator with optional inputs) */
|
|
25
27
|
FRACTION: "Fraction",
|
|
26
28
|
/** Inline image element */
|
|
@@ -38,6 +40,16 @@ const AST_TYPES = {
|
|
|
38
40
|
TABLE_ROW: "TableRow",
|
|
39
41
|
/** Cell within a table row */
|
|
40
42
|
TABLE_CELL: "TableCell",
|
|
43
|
+
/** Vertical operation (column arithmetic) block */
|
|
44
|
+
VERTICAL_OPERATION_BLOCK: "VerticalOperationBlock",
|
|
45
|
+
/** Row within a vertical operation */
|
|
46
|
+
VOP_ROW: "VopRow",
|
|
47
|
+
/** Cell within a vertical-operation row */
|
|
48
|
+
VOP_CELL: "VopCell",
|
|
49
|
+
/** Separator line within a vertical operation */
|
|
50
|
+
VOP_SEPARATOR: "VopSeparator",
|
|
51
|
+
/** Carry row within a vertical operation */
|
|
52
|
+
VOP_CARRY_ROW: "VopCarryRow",
|
|
41
53
|
/** Number line layout block */
|
|
42
54
|
NUMBER_LINE_BLOCK: "NumberLineBlock",
|
|
43
55
|
/** A position marker on the number line */
|
|
@@ -112,6 +124,16 @@ const JSON_TYPES = {
|
|
|
112
124
|
TABLE_ROW: "table-row",
|
|
113
125
|
/** Cell within a table */
|
|
114
126
|
TABLE_CELL: "table-cell",
|
|
127
|
+
/** Vertical operation (column arithmetic) block */
|
|
128
|
+
VERTICAL_OPERATION: "vertical-operation",
|
|
129
|
+
/** Row within a vertical operation */
|
|
130
|
+
VOP_ROW: "vop-row",
|
|
131
|
+
/** Cell within a vertical-operation row */
|
|
132
|
+
VOP_CELL: "vop-cell",
|
|
133
|
+
/** Separator within a vertical operation */
|
|
134
|
+
VOP_SEPARATOR: "vop-separator",
|
|
135
|
+
/** Carry row within a vertical operation */
|
|
136
|
+
VOP_CARRY_ROW: "vop-carry-row",
|
|
115
137
|
/** Number line layout block */
|
|
116
138
|
NUMBER_LINE: "number-line",
|
|
117
139
|
/** Position marker within a number line */
|
|
@@ -135,6 +157,8 @@ const JSON_TYPES = {
|
|
|
135
157
|
INLINE_INPUT: "inline-input",
|
|
136
158
|
/** Drag-and-drop options widget */
|
|
137
159
|
DRAG_OPTIONS: "drag-options",
|
|
160
|
+
/** Single inline draggable option (appears in-flow with text) */
|
|
161
|
+
INLINE_DRAG_OPTION: "inline-drag-option",
|
|
138
162
|
/** Radio button options widget */
|
|
139
163
|
RADIO_OPTIONS: "radio-options",
|
|
140
164
|
/** Numeric keypad widget */
|
|
@@ -150,6 +174,13 @@ const STATEFUL_WIDGET_TYPES = /* @__PURE__ */ new Set([
|
|
|
150
174
|
JSON_TYPES.PAIR_MATCHER,
|
|
151
175
|
JSON_TYPES.VISUAL_FRACTION
|
|
152
176
|
]);
|
|
177
|
+
const INLINE_DRAG_OPT_PREFIX = "_inline_drag_opt_";
|
|
178
|
+
const RTL_INLINE_WIDGET_TYPES = /* @__PURE__ */ new Set([
|
|
179
|
+
JSON_TYPES.INLINE_INPUT,
|
|
180
|
+
JSON_TYPES.INLINE_DRAG_OPTION,
|
|
181
|
+
JSON_TYPES.FRACTION,
|
|
182
|
+
JSON_TYPES.VISUAL_FRACTION
|
|
183
|
+
]);
|
|
153
184
|
const VALIDATION_MODES = {
|
|
154
185
|
/** Simple answer matching validation */
|
|
155
186
|
SIMPLE: "simple",
|
|
@@ -232,6 +263,10 @@ const STRING_INPUT = createToken({
|
|
|
232
263
|
name: "STRING_INPUT",
|
|
233
264
|
pattern: /STRING_INPUT/
|
|
234
265
|
});
|
|
266
|
+
const INLINE_DRAG_OPTION = createToken({
|
|
267
|
+
name: "INLINE_DRAG_OPTION",
|
|
268
|
+
pattern: /INLINE_DRAG_OPTION/
|
|
269
|
+
});
|
|
235
270
|
const DRAGABLE_BUTTONS_OPTIONS = createToken({
|
|
236
271
|
name: "DRAGABLE_BUTTONS_OPTIONS",
|
|
237
272
|
pattern: /DRAGABLE_BUTTONS_OPTIONS/
|
|
@@ -320,6 +355,26 @@ const IMAGE = createToken({
|
|
|
320
355
|
name: "IMAGE",
|
|
321
356
|
pattern: /IMAGE/
|
|
322
357
|
});
|
|
358
|
+
const VERTICAL_OPERATION = createToken({
|
|
359
|
+
name: "VERTICAL_OPERATION",
|
|
360
|
+
pattern: /VERTICAL_OPERATION/
|
|
361
|
+
});
|
|
362
|
+
const SEP = createToken({
|
|
363
|
+
name: "SEP",
|
|
364
|
+
pattern: /SEP(?![a-zA-Z0-9_])/
|
|
365
|
+
});
|
|
366
|
+
const CARRY = createToken({
|
|
367
|
+
name: "CARRY",
|
|
368
|
+
pattern: /CARRY(?![a-zA-Z0-9_])/
|
|
369
|
+
});
|
|
370
|
+
const INPUT = createToken({
|
|
371
|
+
name: "INPUT",
|
|
372
|
+
pattern: /INPUT(?=\s*\[)/
|
|
373
|
+
});
|
|
374
|
+
const Underscore = createToken({
|
|
375
|
+
name: "Underscore",
|
|
376
|
+
pattern: /_(?![a-zA-Z0-9_])/
|
|
377
|
+
});
|
|
323
378
|
const NumberLiteral = createToken({
|
|
324
379
|
name: "NumberLiteral",
|
|
325
380
|
pattern: /-?\d+(\.\d+)?/
|
|
@@ -416,6 +471,7 @@ const allTokens = [
|
|
|
416
471
|
HIGHLIGHT_TEXT,
|
|
417
472
|
W_S_I,
|
|
418
473
|
STRING_INPUT,
|
|
474
|
+
INLINE_DRAG_OPTION,
|
|
419
475
|
DRAGABLE_BUTTONS_OPTIONS,
|
|
420
476
|
RADIO_BUTTONS_OPTIONS,
|
|
421
477
|
NUMERIC_KEYPAD,
|
|
@@ -438,6 +494,11 @@ const allTokens = [
|
|
|
438
494
|
ITEM,
|
|
439
495
|
ANSWERS,
|
|
440
496
|
IMAGE,
|
|
497
|
+
VERTICAL_OPERATION,
|
|
498
|
+
SEP,
|
|
499
|
+
CARRY,
|
|
500
|
+
INPUT,
|
|
501
|
+
Underscore,
|
|
441
502
|
// Literals
|
|
442
503
|
NumberLiteral,
|
|
443
504
|
StringLiteral,
|
|
@@ -595,6 +656,10 @@ class MathExerciseParser extends CstParser {
|
|
|
595
656
|
GATE: this.BACKTRACK(this.matcher),
|
|
596
657
|
ALT: () => this.SUBRULE(this.matcher)
|
|
597
658
|
},
|
|
659
|
+
{
|
|
660
|
+
GATE: this.BACKTRACK(this.verticalOperation),
|
|
661
|
+
ALT: () => this.SUBRULE(this.verticalOperation)
|
|
662
|
+
},
|
|
598
663
|
{ ALT: () => this.SUBRULE(this.line) }
|
|
599
664
|
]);
|
|
600
665
|
});
|
|
@@ -915,6 +980,130 @@ class MathExerciseParser extends CstParser {
|
|
|
915
980
|
this.OR4([{ ALT: () => this.CONSUME4(Space) }, { ALT: () => this.CONSUME4(Newline) }]);
|
|
916
981
|
});
|
|
917
982
|
});
|
|
983
|
+
this.verticalOperation = this.RULE("verticalOperation", () => {
|
|
984
|
+
this.MANY(() => {
|
|
985
|
+
this.OR([{ ALT: () => this.CONSUME(Space) }, { ALT: () => this.CONSUME(Newline) }]);
|
|
986
|
+
});
|
|
987
|
+
this.CONSUME(VERTICAL_OPERATION);
|
|
988
|
+
this.OPTION(() => {
|
|
989
|
+
this.CONSUME(LBracket);
|
|
990
|
+
this.SUBRULE(this.parameterList);
|
|
991
|
+
this.CONSUME(RBracket);
|
|
992
|
+
});
|
|
993
|
+
this.MANY2(() => {
|
|
994
|
+
this.OR2([{ ALT: () => this.CONSUME2(Space) }, { ALT: () => this.CONSUME2(Newline) }]);
|
|
995
|
+
});
|
|
996
|
+
this.CONSUME(LBrace);
|
|
997
|
+
this.MANY3(() => {
|
|
998
|
+
this.OR3([{ ALT: () => this.CONSUME3(Space) }, { ALT: () => this.CONSUME3(Newline) }]);
|
|
999
|
+
});
|
|
1000
|
+
this.AT_LEAST_ONE(() => {
|
|
1001
|
+
this.OR4([
|
|
1002
|
+
{ ALT: () => this.SUBRULE(this.vopRow) },
|
|
1003
|
+
{ ALT: () => this.SUBRULE(this.vopCarry) },
|
|
1004
|
+
{ ALT: () => this.SUBRULE(this.vopSep) }
|
|
1005
|
+
]);
|
|
1006
|
+
});
|
|
1007
|
+
this.CONSUME(RBrace);
|
|
1008
|
+
this.MANY4(() => {
|
|
1009
|
+
this.OR5([{ ALT: () => this.CONSUME4(Newline) }, { ALT: () => this.CONSUME4(Space) }]);
|
|
1010
|
+
});
|
|
1011
|
+
});
|
|
1012
|
+
this.vopRow = this.RULE("vopRow", () => {
|
|
1013
|
+
this.CONSUME(ROW);
|
|
1014
|
+
this.OPTION(() => {
|
|
1015
|
+
this.CONSUME(LBracket);
|
|
1016
|
+
this.SUBRULE(this.parameterList);
|
|
1017
|
+
this.CONSUME(RBracket);
|
|
1018
|
+
});
|
|
1019
|
+
this.MANY(() => {
|
|
1020
|
+
this.OR([{ ALT: () => this.CONSUME(Space) }, { ALT: () => this.CONSUME(Newline) }]);
|
|
1021
|
+
});
|
|
1022
|
+
this.CONSUME(LBrace);
|
|
1023
|
+
this.MANY2(() => {
|
|
1024
|
+
this.OR2([{ ALT: () => this.CONSUME2(Space) }, { ALT: () => this.CONSUME2(Newline) }]);
|
|
1025
|
+
});
|
|
1026
|
+
this.SUBRULE(this.vopCell);
|
|
1027
|
+
this.MANY3(() => {
|
|
1028
|
+
this.MANY4(() => {
|
|
1029
|
+
this.OR3([{ ALT: () => this.CONSUME3(Space) }, { ALT: () => this.CONSUME3(Newline) }]);
|
|
1030
|
+
});
|
|
1031
|
+
this.CONSUME(Comma);
|
|
1032
|
+
this.MANY5(() => {
|
|
1033
|
+
this.OR4([{ ALT: () => this.CONSUME4(Space) }, { ALT: () => this.CONSUME4(Newline) }]);
|
|
1034
|
+
});
|
|
1035
|
+
this.SUBRULE2(this.vopCell);
|
|
1036
|
+
});
|
|
1037
|
+
this.MANY6(() => {
|
|
1038
|
+
this.OR5([{ ALT: () => this.CONSUME5(Space) }, { ALT: () => this.CONSUME5(Newline) }]);
|
|
1039
|
+
});
|
|
1040
|
+
this.CONSUME(RBrace);
|
|
1041
|
+
this.MANY7(() => {
|
|
1042
|
+
this.OR6([{ ALT: () => this.CONSUME6(Space) }, { ALT: () => this.CONSUME6(Newline) }]);
|
|
1043
|
+
});
|
|
1044
|
+
});
|
|
1045
|
+
this.vopCell = this.RULE("vopCell", () => {
|
|
1046
|
+
this.OR([
|
|
1047
|
+
{ ALT: () => this.CONSUME(NumberLiteral) },
|
|
1048
|
+
{ ALT: () => this.CONSUME(StringLiteral) },
|
|
1049
|
+
{ ALT: () => this.CONSUME(Underscore) },
|
|
1050
|
+
{
|
|
1051
|
+
ALT: () => {
|
|
1052
|
+
this.CONSUME(INPUT);
|
|
1053
|
+
this.CONSUME(LBracket);
|
|
1054
|
+
this.SUBRULE(this.parameterList);
|
|
1055
|
+
this.CONSUME(RBracket);
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
{ ALT: () => this.SUBRULE(this.colorText) },
|
|
1059
|
+
{ ALT: () => this.SUBRULE(this.boldText) },
|
|
1060
|
+
{ ALT: () => this.SUBRULE(this.textHighlighter) }
|
|
1061
|
+
]);
|
|
1062
|
+
});
|
|
1063
|
+
this.vopCarry = this.RULE("vopCarry", () => {
|
|
1064
|
+
this.CONSUME(CARRY);
|
|
1065
|
+
this.MANY(() => {
|
|
1066
|
+
this.OR([{ ALT: () => this.CONSUME(Space) }, { ALT: () => this.CONSUME(Newline) }]);
|
|
1067
|
+
});
|
|
1068
|
+
this.CONSUME(LBrace);
|
|
1069
|
+
this.MANY2(() => {
|
|
1070
|
+
this.OR2([{ ALT: () => this.CONSUME2(Space) }, { ALT: () => this.CONSUME2(Newline) }]);
|
|
1071
|
+
});
|
|
1072
|
+
this.SUBRULE(this.vopCell);
|
|
1073
|
+
this.MANY3(() => {
|
|
1074
|
+
this.MANY4(() => {
|
|
1075
|
+
this.OR3([{ ALT: () => this.CONSUME3(Space) }, { ALT: () => this.CONSUME3(Newline) }]);
|
|
1076
|
+
});
|
|
1077
|
+
this.CONSUME(Comma);
|
|
1078
|
+
this.MANY5(() => {
|
|
1079
|
+
this.OR4([{ ALT: () => this.CONSUME4(Space) }, { ALT: () => this.CONSUME4(Newline) }]);
|
|
1080
|
+
});
|
|
1081
|
+
this.SUBRULE2(this.vopCell);
|
|
1082
|
+
});
|
|
1083
|
+
this.MANY6(() => {
|
|
1084
|
+
this.OR5([{ ALT: () => this.CONSUME5(Space) }, { ALT: () => this.CONSUME5(Newline) }]);
|
|
1085
|
+
});
|
|
1086
|
+
this.CONSUME(RBrace);
|
|
1087
|
+
this.MANY7(() => {
|
|
1088
|
+
this.OR6([{ ALT: () => this.CONSUME6(Newline) }, { ALT: () => this.CONSUME6(Space) }]);
|
|
1089
|
+
});
|
|
1090
|
+
});
|
|
1091
|
+
this.vopSep = this.RULE("vopSep", () => {
|
|
1092
|
+
this.CONSUME(SEP);
|
|
1093
|
+
this.OPTION(() => {
|
|
1094
|
+
this.MANY(() => {
|
|
1095
|
+
this.OR([{ ALT: () => this.CONSUME(Space) }, { ALT: () => this.CONSUME(Newline) }]);
|
|
1096
|
+
});
|
|
1097
|
+
this.CONSUME(LBrace);
|
|
1098
|
+
this.MANY2(() => {
|
|
1099
|
+
this.OR2([{ ALT: () => this.CONSUME2(Space) }, { ALT: () => this.CONSUME2(Newline) }]);
|
|
1100
|
+
});
|
|
1101
|
+
this.CONSUME(RBrace);
|
|
1102
|
+
});
|
|
1103
|
+
this.MANY3(() => {
|
|
1104
|
+
this.OR3([{ ALT: () => this.CONSUME3(Space) }, { ALT: () => this.CONSUME3(Newline) }]);
|
|
1105
|
+
});
|
|
1106
|
+
});
|
|
918
1107
|
this.line = this.RULE("line", () => {
|
|
919
1108
|
this.MANY(() => {
|
|
920
1109
|
this.CONSUME(Space);
|
|
@@ -968,7 +1157,9 @@ class MathExerciseParser extends CstParser {
|
|
|
968
1157
|
// VISUAL_FRACTION — always allowed; display-only when answer=[] or absence of answer parameter, interactive when answer=[...] with values.
|
|
969
1158
|
{ ALT: () => this.SUBRULE(this.visualFraction) },
|
|
970
1159
|
// W_S_I (inline input widget) — excluded in display-only contexts (e.g. MATCHER ITEM)
|
|
971
|
-
{ GATE: () => !displayOnly, ALT: () => this.SUBRULE(this.inlineInput) }
|
|
1160
|
+
{ GATE: () => !displayOnly, ALT: () => this.SUBRULE(this.inlineInput) },
|
|
1161
|
+
// INLINE_DRAG_OPTION (single draggable option inline with text) — excluded in display-only
|
|
1162
|
+
{ GATE: () => !displayOnly, ALT: () => this.SUBRULE(this.inlineDragOption) }
|
|
972
1163
|
]);
|
|
973
1164
|
});
|
|
974
1165
|
this.textHighlighter = this.RULE("textHighlighter", () => {
|
|
@@ -1050,6 +1241,12 @@ class MathExerciseParser extends CstParser {
|
|
|
1050
1241
|
this.SUBRULE(this.parameterList);
|
|
1051
1242
|
this.CONSUME(RBracket);
|
|
1052
1243
|
});
|
|
1244
|
+
this.inlineDragOption = this.RULE("inlineDragOption", () => {
|
|
1245
|
+
this.CONSUME(INLINE_DRAG_OPTION);
|
|
1246
|
+
this.CONSUME(LBracket);
|
|
1247
|
+
this.CONSUME(StringLiteral);
|
|
1248
|
+
this.CONSUME(RBracket);
|
|
1249
|
+
});
|
|
1053
1250
|
this.tool = this.RULE("tool", () => {
|
|
1054
1251
|
this.OR([
|
|
1055
1252
|
{ ALT: () => this.SUBRULE(this.dragableButtons) },
|
|
@@ -1318,6 +1515,10 @@ function isPerfectSquare(value) {
|
|
|
1318
1515
|
const root = Math.sqrt(value);
|
|
1319
1516
|
return Number.isInteger(root);
|
|
1320
1517
|
}
|
|
1518
|
+
const InlineDragOptionSchema = z.object({
|
|
1519
|
+
type: z.literal(AST_TYPES.INLINE_DRAG_OPTION),
|
|
1520
|
+
value: z.string().min(1)
|
|
1521
|
+
});
|
|
1321
1522
|
const InlineNodeSchema = z.union([
|
|
1322
1523
|
FreeTextInlineSchema,
|
|
1323
1524
|
SpaceInlineSchema,
|
|
@@ -1327,7 +1528,8 @@ const InlineNodeSchema = z.union([
|
|
|
1327
1528
|
InlineInputWidgetSchema,
|
|
1328
1529
|
FractionInlineSchema,
|
|
1329
1530
|
ImageInlineSchema,
|
|
1330
|
-
VisualFractionInlineSchema
|
|
1531
|
+
VisualFractionInlineSchema,
|
|
1532
|
+
InlineDragOptionSchema
|
|
1331
1533
|
]);
|
|
1332
1534
|
function createFreeText(value) {
|
|
1333
1535
|
return FreeTextInlineSchema.parse({
|
|
@@ -1363,24 +1565,32 @@ function createBoldText(value, highlight) {
|
|
|
1363
1565
|
});
|
|
1364
1566
|
}
|
|
1365
1567
|
function createStringInput(name, length, answer, placeholder) {
|
|
1366
|
-
return parseOrThrow(
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1568
|
+
return parseOrThrow(
|
|
1569
|
+
InlineInputWidgetSchema,
|
|
1570
|
+
{
|
|
1571
|
+
type: AST_TYPES.INLINE_INPUT,
|
|
1572
|
+
name,
|
|
1573
|
+
inputType: INPUT_TYPES.STRING,
|
|
1574
|
+
length,
|
|
1575
|
+
placeholder,
|
|
1576
|
+
answer
|
|
1577
|
+
},
|
|
1578
|
+
"STRING_INPUT"
|
|
1579
|
+
);
|
|
1374
1580
|
}
|
|
1375
1581
|
function createNumericInput(name, digits, answer, placeholder) {
|
|
1376
|
-
return parseOrThrow(
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1582
|
+
return parseOrThrow(
|
|
1583
|
+
InlineInputWidgetSchema,
|
|
1584
|
+
{
|
|
1585
|
+
type: AST_TYPES.INLINE_INPUT,
|
|
1586
|
+
name,
|
|
1587
|
+
inputType: INPUT_TYPES.NUMERIC,
|
|
1588
|
+
digits,
|
|
1589
|
+
placeholder,
|
|
1590
|
+
answer
|
|
1591
|
+
},
|
|
1592
|
+
"NUMERIC_INPUT"
|
|
1593
|
+
);
|
|
1384
1594
|
}
|
|
1385
1595
|
function createImage(name, src, alt, options = {}) {
|
|
1386
1596
|
return ImageInlineSchema.parse({
|
|
@@ -1406,6 +1616,12 @@ function createVisualFraction(name, divisions, options = {}) {
|
|
|
1406
1616
|
...options
|
|
1407
1617
|
});
|
|
1408
1618
|
}
|
|
1619
|
+
function createInlineDragOption(value) {
|
|
1620
|
+
return InlineDragOptionSchema.parse({
|
|
1621
|
+
type: AST_TYPES.INLINE_DRAG_OPTION,
|
|
1622
|
+
value
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1409
1625
|
function isFreeText(value) {
|
|
1410
1626
|
return FreeTextInlineSchema.safeParse(value).success;
|
|
1411
1627
|
}
|
|
@@ -1427,6 +1643,9 @@ function isInlineNode(value) {
|
|
|
1427
1643
|
function isVisualFractionInline(value) {
|
|
1428
1644
|
return VisualFractionInlineSchema.safeParse(value).success;
|
|
1429
1645
|
}
|
|
1646
|
+
function isInlineDragOption(value) {
|
|
1647
|
+
return InlineDragOptionSchema.safeParse(value).success;
|
|
1648
|
+
}
|
|
1430
1649
|
const TextBlockSchema = z.object({
|
|
1431
1650
|
type: z.literal(AST_TYPES.TEXT_BLOCK),
|
|
1432
1651
|
value: z.string()
|
|
@@ -1480,17 +1699,19 @@ const NumberLineCounterSchema = z.object({
|
|
|
1480
1699
|
min: z.number().int().min(1, { message: "COUNTER: min must be >= 1" }),
|
|
1481
1700
|
max: z.number().int().positive({ message: "COUNTER: max must be > 0" }),
|
|
1482
1701
|
default: z.number().int().nonnegative().optional(),
|
|
1483
|
-
name: z.string().optional()
|
|
1702
|
+
name: z.string().optional(),
|
|
1703
|
+
answer: z.number().int().positive({ message: "COUNTER: answer must be a positive integer" }).optional()
|
|
1484
1704
|
}).refine((d) => d.min <= d.max, {
|
|
1485
1705
|
message: "COUNTER: min must be <= max"
|
|
1486
1706
|
});
|
|
1487
|
-
function createNumberLineCounter(max, min, defaultValue2, name) {
|
|
1707
|
+
function createNumberLineCounter(max, min, defaultValue2, name, answer) {
|
|
1488
1708
|
return NumberLineCounterSchema.parse({
|
|
1489
1709
|
type: AST_TYPES.NUMBER_LINE_COUNTER,
|
|
1490
1710
|
min: min ?? 1,
|
|
1491
1711
|
max,
|
|
1492
1712
|
...defaultValue2 !== void 0 && { default: defaultValue2 },
|
|
1493
|
-
...name !== void 0 && { name }
|
|
1713
|
+
...name !== void 0 && { name },
|
|
1714
|
+
...answer !== void 0 && { answer }
|
|
1494
1715
|
});
|
|
1495
1716
|
}
|
|
1496
1717
|
const NumberLineHighlightSchema = z.object({
|
|
@@ -1624,6 +1845,146 @@ function createTableBlock(cols, rows) {
|
|
|
1624
1845
|
rows
|
|
1625
1846
|
});
|
|
1626
1847
|
}
|
|
1848
|
+
const VOP_OPERATORS = ["+", "-", "×", "÷", "="];
|
|
1849
|
+
function normalizeVopOperator(raw) {
|
|
1850
|
+
const map = {
|
|
1851
|
+
"+": "+",
|
|
1852
|
+
"-": "-",
|
|
1853
|
+
"−": "-",
|
|
1854
|
+
"*": "×",
|
|
1855
|
+
"x": "×",
|
|
1856
|
+
"X": "×",
|
|
1857
|
+
"×": "×",
|
|
1858
|
+
"/": "÷",
|
|
1859
|
+
"÷": "÷",
|
|
1860
|
+
"=": "="
|
|
1861
|
+
};
|
|
1862
|
+
const op = map[raw];
|
|
1863
|
+
if (!op) throw new Error(`VERTICAL_OPERATION: unsupported op "${raw}"`);
|
|
1864
|
+
return op;
|
|
1865
|
+
}
|
|
1866
|
+
const VopDigitCellSchema = z.object({
|
|
1867
|
+
type: z.literal(AST_TYPES.VOP_CELL),
|
|
1868
|
+
kind: z.literal("digit"),
|
|
1869
|
+
value: z.string()
|
|
1870
|
+
});
|
|
1871
|
+
const VopLabelCellSchema = z.object({
|
|
1872
|
+
type: z.literal(AST_TYPES.VOP_CELL),
|
|
1873
|
+
kind: z.literal("label"),
|
|
1874
|
+
value: z.string()
|
|
1875
|
+
});
|
|
1876
|
+
const VopInputCellSchema = z.object({
|
|
1877
|
+
type: z.literal(AST_TYPES.VOP_CELL),
|
|
1878
|
+
kind: z.literal("input"),
|
|
1879
|
+
name: z.string().min(1),
|
|
1880
|
+
digits: z.number().int().positive().default(1),
|
|
1881
|
+
answer: z.union([z.string(), z.number()]).optional()
|
|
1882
|
+
});
|
|
1883
|
+
const VopEmptyCellSchema = z.object({
|
|
1884
|
+
type: z.literal(AST_TYPES.VOP_CELL),
|
|
1885
|
+
kind: z.literal("empty")
|
|
1886
|
+
});
|
|
1887
|
+
const VopColorCellSchema = z.object({
|
|
1888
|
+
type: z.literal(AST_TYPES.VOP_CELL),
|
|
1889
|
+
kind: z.literal("color"),
|
|
1890
|
+
value: z.string(),
|
|
1891
|
+
color: z.string()
|
|
1892
|
+
});
|
|
1893
|
+
const VopBoldCellSchema = z.object({
|
|
1894
|
+
type: z.literal(AST_TYPES.VOP_CELL),
|
|
1895
|
+
kind: z.literal("bold"),
|
|
1896
|
+
value: z.string()
|
|
1897
|
+
});
|
|
1898
|
+
const VopHighlightCellSchema = z.object({
|
|
1899
|
+
type: z.literal(AST_TYPES.VOP_CELL),
|
|
1900
|
+
kind: z.literal("highlight"),
|
|
1901
|
+
value: z.string()
|
|
1902
|
+
});
|
|
1903
|
+
const VopCellSchema = z.discriminatedUnion("kind", [
|
|
1904
|
+
VopDigitCellSchema,
|
|
1905
|
+
VopLabelCellSchema,
|
|
1906
|
+
VopColorCellSchema,
|
|
1907
|
+
VopBoldCellSchema,
|
|
1908
|
+
VopHighlightCellSchema,
|
|
1909
|
+
VopInputCellSchema,
|
|
1910
|
+
VopEmptyCellSchema
|
|
1911
|
+
]);
|
|
1912
|
+
function createVopDigitCell(value) {
|
|
1913
|
+
return VopDigitCellSchema.parse({ type: AST_TYPES.VOP_CELL, kind: "digit", value });
|
|
1914
|
+
}
|
|
1915
|
+
function createVopInputCell(name, digits = 1, answer) {
|
|
1916
|
+
return VopInputCellSchema.parse({
|
|
1917
|
+
type: AST_TYPES.VOP_CELL,
|
|
1918
|
+
kind: "input",
|
|
1919
|
+
name,
|
|
1920
|
+
digits,
|
|
1921
|
+
answer
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
function createVopLabelCell(value) {
|
|
1925
|
+
return VopLabelCellSchema.parse({ type: AST_TYPES.VOP_CELL, kind: "label", value });
|
|
1926
|
+
}
|
|
1927
|
+
function createVopEmptyCell() {
|
|
1928
|
+
return VopEmptyCellSchema.parse({ type: AST_TYPES.VOP_CELL, kind: "empty" });
|
|
1929
|
+
}
|
|
1930
|
+
function createVopColorCell(value, color) {
|
|
1931
|
+
return VopColorCellSchema.parse({ type: AST_TYPES.VOP_CELL, kind: "color", value, color });
|
|
1932
|
+
}
|
|
1933
|
+
function createVopBoldCell(value) {
|
|
1934
|
+
return VopBoldCellSchema.parse({ type: AST_TYPES.VOP_CELL, kind: "bold", value });
|
|
1935
|
+
}
|
|
1936
|
+
function createVopHighlightCell(value) {
|
|
1937
|
+
return VopHighlightCellSchema.parse({ type: AST_TYPES.VOP_CELL, kind: "highlight", value });
|
|
1938
|
+
}
|
|
1939
|
+
const VopRowSchema = z.object({
|
|
1940
|
+
type: z.literal(AST_TYPES.VOP_ROW),
|
|
1941
|
+
cells: z.array(VopCellSchema).min(1),
|
|
1942
|
+
op: z.enum(VOP_OPERATORS).optional()
|
|
1943
|
+
});
|
|
1944
|
+
function createVopRow(cells, op) {
|
|
1945
|
+
return VopRowSchema.parse({ type: AST_TYPES.VOP_ROW, cells, op });
|
|
1946
|
+
}
|
|
1947
|
+
const VopCarryRowSchema = z.object({
|
|
1948
|
+
type: z.literal(AST_TYPES.VOP_CARRY_ROW),
|
|
1949
|
+
cells: z.array(VopCellSchema).min(1)
|
|
1950
|
+
});
|
|
1951
|
+
function createVopCarryRow(cells) {
|
|
1952
|
+
return VopCarryRowSchema.parse({ type: AST_TYPES.VOP_CARRY_ROW, cells });
|
|
1953
|
+
}
|
|
1954
|
+
const VopSeparatorSchema = z.object({
|
|
1955
|
+
type: z.literal(AST_TYPES.VOP_SEPARATOR)
|
|
1956
|
+
});
|
|
1957
|
+
function createVopSeparator() {
|
|
1958
|
+
return VopSeparatorSchema.parse({ type: AST_TYPES.VOP_SEPARATOR });
|
|
1959
|
+
}
|
|
1960
|
+
const VopBlockItemSchema = z.union([VopRowSchema, VopCarryRowSchema, VopSeparatorSchema]);
|
|
1961
|
+
const VerticalOperationBlockSchema = z.object({
|
|
1962
|
+
type: z.literal(AST_TYPES.VERTICAL_OPERATION_BLOCK),
|
|
1963
|
+
op: z.enum(VOP_OPERATORS).optional(),
|
|
1964
|
+
items: z.array(VopBlockItemSchema).min(1)
|
|
1965
|
+
});
|
|
1966
|
+
function createVerticalOperationBlock(op, items) {
|
|
1967
|
+
return VerticalOperationBlockSchema.parse({
|
|
1968
|
+
type: AST_TYPES.VERTICAL_OPERATION_BLOCK,
|
|
1969
|
+
op,
|
|
1970
|
+
items
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
function isVopCell(value) {
|
|
1974
|
+
return VopCellSchema.safeParse(value).success;
|
|
1975
|
+
}
|
|
1976
|
+
function isVopRow(value) {
|
|
1977
|
+
return VopRowSchema.safeParse(value).success;
|
|
1978
|
+
}
|
|
1979
|
+
function isVopCarryRow(value) {
|
|
1980
|
+
return VopCarryRowSchema.safeParse(value).success;
|
|
1981
|
+
}
|
|
1982
|
+
function isVopSeparator(value) {
|
|
1983
|
+
return VopSeparatorSchema.safeParse(value).success;
|
|
1984
|
+
}
|
|
1985
|
+
function isVerticalOperationBlock(value) {
|
|
1986
|
+
return VerticalOperationBlockSchema.safeParse(value).success;
|
|
1987
|
+
}
|
|
1627
1988
|
const DisplayFractionInlineSchema = FractionInlineSchema.refine(
|
|
1628
1989
|
(f) => f.numerator !== void 0 && f.denominator !== void 0,
|
|
1629
1990
|
{ message: "FRACTION inside MATCHER ITEM must be in display mode (both numerator and denominator required)" }
|
|
@@ -1910,7 +2271,7 @@ function isMixedValidation(value) {
|
|
|
1910
2271
|
function isValidationNode(value) {
|
|
1911
2272
|
return ValidationNodeSchema.safeParse(value).success;
|
|
1912
2273
|
}
|
|
1913
|
-
const ContentBlockSchema = z.union([LineBlockSchema, TableBlockSchema, RadioOptionsBlockSchema, NumberLineBlockSchema, MatcherBlockSchema, PairMatcherBlockSchema]);
|
|
2274
|
+
const ContentBlockSchema = z.union([LineBlockSchema, TableBlockSchema, RadioOptionsBlockSchema, NumberLineBlockSchema, MatcherBlockSchema, PairMatcherBlockSchema, VerticalOperationBlockSchema]);
|
|
1914
2275
|
const ExerciseDocumentSchema = z.object({
|
|
1915
2276
|
type: z.literal(AST_TYPES.EXERCISE_DOCUMENT),
|
|
1916
2277
|
question: TextBlockSchema.optional(),
|
|
@@ -2014,6 +2375,7 @@ class NaturalDSLVisitor extends BaseCstVisitor {
|
|
|
2014
2375
|
if (ctx.numberLine) return this.visit(ctx.numberLine);
|
|
2015
2376
|
if (ctx.pairMatcher) return this.visit(ctx.pairMatcher);
|
|
2016
2377
|
if (ctx.matcher) return this.visit(ctx.matcher);
|
|
2378
|
+
if (ctx.verticalOperation) return this.visit(ctx.verticalOperation);
|
|
2017
2379
|
if (ctx.line) return this.visit(ctx.line);
|
|
2018
2380
|
throw new Error("Unknown content block");
|
|
2019
2381
|
}
|
|
@@ -2085,7 +2447,8 @@ class NaturalDSLVisitor extends BaseCstVisitor {
|
|
|
2085
2447
|
const min = params.min !== void 0 ? Number(params.min) : void 0;
|
|
2086
2448
|
const defaultValue2 = params.default !== void 0 ? Number(params.default) : void 0;
|
|
2087
2449
|
const name = params.name;
|
|
2088
|
-
|
|
2450
|
+
const answer = params.answer !== void 0 ? Number(params.answer) : void 0;
|
|
2451
|
+
return createNumberLineCounter(max, min, defaultValue2, name, answer);
|
|
2089
2452
|
}
|
|
2090
2453
|
highlight(ctx) {
|
|
2091
2454
|
const params = this.visit(ctx.parameterList);
|
|
@@ -2152,6 +2515,76 @@ class NaturalDSLVisitor extends BaseCstVisitor {
|
|
|
2152
2515
|
return ctx.matcherItem ? ctx.matcherItem.map((i) => this.visit(i)) : [];
|
|
2153
2516
|
}
|
|
2154
2517
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2518
|
+
// Vertical Operation Block
|
|
2519
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2520
|
+
verticalOperation(ctx) {
|
|
2521
|
+
const params = ctx.parameterList ? this.visit(ctx.parameterList) : {};
|
|
2522
|
+
const op = params.op !== void 0 ? normalizeVopOperator(String(params.op)) : void 0;
|
|
2523
|
+
const tagged = [];
|
|
2524
|
+
ctx.vopRow?.forEach((cstNode, i) => {
|
|
2525
|
+
tagged.push({ offset: cstNode.location?.startOffset ?? i, node: this.visit(cstNode) });
|
|
2526
|
+
});
|
|
2527
|
+
ctx.vopCarry?.forEach((cstNode, i) => {
|
|
2528
|
+
tagged.push({ offset: cstNode.location?.startOffset ?? i, node: this.visit(cstNode) });
|
|
2529
|
+
});
|
|
2530
|
+
ctx.vopSep?.forEach((cstNode, i) => {
|
|
2531
|
+
tagged.push({ offset: cstNode.location?.startOffset ?? i, node: this.visit(cstNode) });
|
|
2532
|
+
});
|
|
2533
|
+
tagged.sort((a, b) => a.offset - b.offset);
|
|
2534
|
+
const items = tagged.map((t) => t.node);
|
|
2535
|
+
return createVerticalOperationBlock(op, items);
|
|
2536
|
+
}
|
|
2537
|
+
vopRow(ctx) {
|
|
2538
|
+
const cells = ctx.vopCell ? ctx.vopCell.map((c) => this.visit(c)) : [];
|
|
2539
|
+
const rowParams = ctx.parameterList ? this.visit(ctx.parameterList) : {};
|
|
2540
|
+
const op = rowParams.op !== void 0 ? normalizeVopOperator(String(rowParams.op)) : void 0;
|
|
2541
|
+
return createVopRow(cells, op);
|
|
2542
|
+
}
|
|
2543
|
+
vopCarry(ctx) {
|
|
2544
|
+
const cells = ctx.vopCell ? ctx.vopCell.map((c) => this.visit(c)) : [];
|
|
2545
|
+
return createVopCarryRow(cells);
|
|
2546
|
+
}
|
|
2547
|
+
vopSep(_ctx) {
|
|
2548
|
+
return createVopSeparator();
|
|
2549
|
+
}
|
|
2550
|
+
vopCell(ctx) {
|
|
2551
|
+
if (ctx.NumberLiteral) {
|
|
2552
|
+
const token = ctx.NumberLiteral[0];
|
|
2553
|
+
return createVopDigitCell(token.image);
|
|
2554
|
+
}
|
|
2555
|
+
if (ctx.StringLiteral) {
|
|
2556
|
+
const token = ctx.StringLiteral[0];
|
|
2557
|
+
const value = token.image.slice(1, -1);
|
|
2558
|
+
return createVopLabelCell(value);
|
|
2559
|
+
}
|
|
2560
|
+
if (ctx.Underscore) {
|
|
2561
|
+
return createVopEmptyCell();
|
|
2562
|
+
}
|
|
2563
|
+
if (ctx.INPUT) {
|
|
2564
|
+
const params = this.visit(ctx.parameterList);
|
|
2565
|
+
const name = params.name;
|
|
2566
|
+
if (!name) {
|
|
2567
|
+
throw new Error("INPUT inside VERTICAL_OPERATION requires parameter: name");
|
|
2568
|
+
}
|
|
2569
|
+
const digits = params.digits !== void 0 ? Number(params.digits) : 1;
|
|
2570
|
+
const answer = params.answer;
|
|
2571
|
+
return createVopInputCell(name, digits, answer);
|
|
2572
|
+
}
|
|
2573
|
+
if (ctx.colorText) {
|
|
2574
|
+
const inline = this.visit(ctx.colorText);
|
|
2575
|
+
return createVopColorCell(inline.value, inline.color);
|
|
2576
|
+
}
|
|
2577
|
+
if (ctx.boldText) {
|
|
2578
|
+
const inline = this.visit(ctx.boldText);
|
|
2579
|
+
return createVopBoldCell(inline.value);
|
|
2580
|
+
}
|
|
2581
|
+
if (ctx.textHighlighter) {
|
|
2582
|
+
const inline = this.visit(ctx.textHighlighter);
|
|
2583
|
+
return createVopHighlightCell(inline.value);
|
|
2584
|
+
}
|
|
2585
|
+
throw new Error("Unknown vopCell");
|
|
2586
|
+
}
|
|
2587
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2155
2588
|
// Line Block
|
|
2156
2589
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2157
2590
|
line(ctx) {
|
|
@@ -2247,6 +2680,9 @@ class NaturalDSLVisitor extends BaseCstVisitor {
|
|
|
2247
2680
|
if (ctx.inlineInput) {
|
|
2248
2681
|
return this.visit(ctx.inlineInput);
|
|
2249
2682
|
}
|
|
2683
|
+
if (ctx.inlineDragOption) {
|
|
2684
|
+
return this.visit(ctx.inlineDragOption);
|
|
2685
|
+
}
|
|
2250
2686
|
throw new Error("Unknown inline element");
|
|
2251
2687
|
}
|
|
2252
2688
|
textHighlighter(ctx) {
|
|
@@ -2323,6 +2759,11 @@ class NaturalDSLVisitor extends BaseCstVisitor {
|
|
|
2323
2759
|
const digits = params.digits;
|
|
2324
2760
|
return createNumericInput(name, digits, answer, placeholder);
|
|
2325
2761
|
}
|
|
2762
|
+
inlineDragOption(ctx) {
|
|
2763
|
+
const valueToken = ctx.StringLiteral[0];
|
|
2764
|
+
const value = this.extractString(valueToken.image);
|
|
2765
|
+
return createInlineDragOption(value);
|
|
2766
|
+
}
|
|
2326
2767
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2327
2768
|
// Tools
|
|
2328
2769
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -2517,7 +2958,8 @@ const LayoutItemSchema = z.lazy(
|
|
|
2517
2958
|
GroupItemSchema,
|
|
2518
2959
|
OperatorItemSchema,
|
|
2519
2960
|
TableItemSchema,
|
|
2520
|
-
NumberLineItemSchema
|
|
2961
|
+
NumberLineItemSchema,
|
|
2962
|
+
VerticalOperationItemSchema
|
|
2521
2963
|
])
|
|
2522
2964
|
);
|
|
2523
2965
|
const GroupItemSchema = z.object({
|
|
@@ -2554,7 +2996,8 @@ const NumberLineCounterItemSchema = z.object({
|
|
|
2554
2996
|
min: z.number().int().min(1),
|
|
2555
2997
|
max: z.number().int().positive(),
|
|
2556
2998
|
default: z.number().int().nonnegative().optional(),
|
|
2557
|
-
name: z.string().optional()
|
|
2999
|
+
name: z.string().optional(),
|
|
3000
|
+
answer: z.number().int().positive().optional()
|
|
2558
3001
|
});
|
|
2559
3002
|
const NumberLineHighlightItemSchema = z.object({
|
|
2560
3003
|
id: z.string(),
|
|
@@ -2591,6 +3034,80 @@ const MatcherRowItemSchema = z.object({
|
|
|
2591
3034
|
type: z.literal(JSON_TYPES.MATCHER_ROW),
|
|
2592
3035
|
items: z.array(MatcherItemContentSchema).min(1)
|
|
2593
3036
|
});
|
|
3037
|
+
const VopOperatorJSONSchema = z.enum(["+", "-", "×", "÷", "="]);
|
|
3038
|
+
const VopDigitCellItemSchema = z.object({
|
|
3039
|
+
id: z.string(),
|
|
3040
|
+
type: z.literal(JSON_TYPES.VOP_CELL),
|
|
3041
|
+
kind: z.literal("digit"),
|
|
3042
|
+
value: z.string()
|
|
3043
|
+
});
|
|
3044
|
+
const VopInputCellItemSchema = z.object({
|
|
3045
|
+
id: z.string(),
|
|
3046
|
+
type: z.literal(JSON_TYPES.VOP_CELL),
|
|
3047
|
+
kind: z.literal("input"),
|
|
3048
|
+
widgetId: z.string()
|
|
3049
|
+
});
|
|
3050
|
+
const VopLabelCellItemSchema = z.object({
|
|
3051
|
+
id: z.string(),
|
|
3052
|
+
type: z.literal(JSON_TYPES.VOP_CELL),
|
|
3053
|
+
kind: z.literal("label"),
|
|
3054
|
+
value: z.string()
|
|
3055
|
+
});
|
|
3056
|
+
const VopEmptyCellItemSchema = z.object({
|
|
3057
|
+
id: z.string(),
|
|
3058
|
+
type: z.literal(JSON_TYPES.VOP_CELL),
|
|
3059
|
+
kind: z.literal("empty")
|
|
3060
|
+
});
|
|
3061
|
+
const VopColorCellItemSchema = z.object({
|
|
3062
|
+
id: z.string(),
|
|
3063
|
+
type: z.literal(JSON_TYPES.VOP_CELL),
|
|
3064
|
+
kind: z.literal("color"),
|
|
3065
|
+
value: z.string(),
|
|
3066
|
+
color: z.string()
|
|
3067
|
+
});
|
|
3068
|
+
const VopBoldCellItemSchema = z.object({
|
|
3069
|
+
id: z.string(),
|
|
3070
|
+
type: z.literal(JSON_TYPES.VOP_CELL),
|
|
3071
|
+
kind: z.literal("bold"),
|
|
3072
|
+
value: z.string()
|
|
3073
|
+
});
|
|
3074
|
+
const VopHighlightCellItemSchema = z.object({
|
|
3075
|
+
id: z.string(),
|
|
3076
|
+
type: z.literal(JSON_TYPES.VOP_CELL),
|
|
3077
|
+
kind: z.literal("highlight"),
|
|
3078
|
+
value: z.string()
|
|
3079
|
+
});
|
|
3080
|
+
const VopCellItemSchema = z.discriminatedUnion("kind", [
|
|
3081
|
+
VopDigitCellItemSchema,
|
|
3082
|
+
VopLabelCellItemSchema,
|
|
3083
|
+
VopColorCellItemSchema,
|
|
3084
|
+
VopBoldCellItemSchema,
|
|
3085
|
+
VopHighlightCellItemSchema,
|
|
3086
|
+
VopInputCellItemSchema,
|
|
3087
|
+
VopEmptyCellItemSchema
|
|
3088
|
+
]);
|
|
3089
|
+
const VopRowItemSchema = z.object({
|
|
3090
|
+
id: z.string(),
|
|
3091
|
+
type: z.literal(JSON_TYPES.VOP_ROW),
|
|
3092
|
+
cells: z.array(VopCellItemSchema),
|
|
3093
|
+
op: VopOperatorJSONSchema.optional()
|
|
3094
|
+
});
|
|
3095
|
+
const VopCarryRowItemSchema = z.object({
|
|
3096
|
+
id: z.string(),
|
|
3097
|
+
type: z.literal(JSON_TYPES.VOP_CARRY_ROW),
|
|
3098
|
+
cells: z.array(VopCellItemSchema)
|
|
3099
|
+
});
|
|
3100
|
+
const VopSeparatorItemSchema = z.object({
|
|
3101
|
+
id: z.string(),
|
|
3102
|
+
type: z.literal(JSON_TYPES.VOP_SEPARATOR)
|
|
3103
|
+
});
|
|
3104
|
+
const VerticalOperationItemSchema = z.object({
|
|
3105
|
+
id: z.string(),
|
|
3106
|
+
type: z.literal(JSON_TYPES.VERTICAL_OPERATION),
|
|
3107
|
+
op: VopOperatorJSONSchema.optional(),
|
|
3108
|
+
cols: z.number().int().positive(),
|
|
3109
|
+
items: z.array(z.union([VopRowItemSchema, VopCarryRowItemSchema, VopSeparatorItemSchema]))
|
|
3110
|
+
});
|
|
2594
3111
|
const StringInputJSONSchema = z.object({
|
|
2595
3112
|
type: z.literal(JSON_TYPES.INLINE_INPUT),
|
|
2596
3113
|
inputType: z.literal(INPUT_TYPES.STRING),
|
|
@@ -2611,6 +3128,10 @@ const DragOptionsJSONSchema = z.object({
|
|
|
2611
3128
|
type: z.literal(JSON_TYPES.DRAG_OPTIONS),
|
|
2612
3129
|
options: z.array(z.string())
|
|
2613
3130
|
});
|
|
3131
|
+
const InlineDragOptionJSONSchema = z.object({
|
|
3132
|
+
type: z.literal(JSON_TYPES.INLINE_DRAG_OPTION),
|
|
3133
|
+
value: z.string().min(1)
|
|
3134
|
+
});
|
|
2614
3135
|
const RadioOptionsJSONSchema = z.object({
|
|
2615
3136
|
type: z.literal(JSON_TYPES.RADIO_OPTIONS),
|
|
2616
3137
|
options: z.array(z.string())
|
|
@@ -2661,6 +3182,7 @@ const VisualFractionJSONSchema = z.object({
|
|
|
2661
3182
|
const WidgetJSONSchema = z.union([
|
|
2662
3183
|
InlineInputJSONSchema,
|
|
2663
3184
|
DragOptionsJSONSchema,
|
|
3185
|
+
InlineDragOptionJSONSchema,
|
|
2664
3186
|
RadioOptionsJSONSchema,
|
|
2665
3187
|
NumericKeypadJSONSchema,
|
|
2666
3188
|
FractionJSONSchema,
|
|
@@ -2753,12 +3275,18 @@ function isTableRowItem(item) {
|
|
|
2753
3275
|
function isTableCellItem(item) {
|
|
2754
3276
|
return item.type === JSON_TYPES.TABLE_CELL;
|
|
2755
3277
|
}
|
|
3278
|
+
function isVerticalOperationItem(item) {
|
|
3279
|
+
return item.type === JSON_TYPES.VERTICAL_OPERATION;
|
|
3280
|
+
}
|
|
2756
3281
|
function isInlineInputJSON(widget) {
|
|
2757
3282
|
return widget.type === JSON_TYPES.INLINE_INPUT;
|
|
2758
3283
|
}
|
|
2759
3284
|
function isDragOptionsJSON(widget) {
|
|
2760
3285
|
return widget.type === JSON_TYPES.DRAG_OPTIONS;
|
|
2761
3286
|
}
|
|
3287
|
+
function isInlineDragOptionJSON(widget) {
|
|
3288
|
+
return widget.type === JSON_TYPES.INLINE_DRAG_OPTION;
|
|
3289
|
+
}
|
|
2762
3290
|
function isRadioOptionsJSON(widget) {
|
|
2763
3291
|
return widget.type === JSON_TYPES.RADIO_OPTIONS;
|
|
2764
3292
|
}
|
|
@@ -2781,6 +3309,18 @@ class LayoutIdGenerator {
|
|
|
2781
3309
|
}
|
|
2782
3310
|
function toJSON(ast) {
|
|
2783
3311
|
const idGenerator = new LayoutIdGenerator();
|
|
3312
|
+
const widgets = {};
|
|
3313
|
+
const answers = {};
|
|
3314
|
+
const allInlines = collectAllInlines(ast.lines);
|
|
3315
|
+
const inlineDragIds = /* @__PURE__ */ new Map();
|
|
3316
|
+
{
|
|
3317
|
+
let counter = 0;
|
|
3318
|
+
for (const inline of allInlines) {
|
|
3319
|
+
if (inline.type === AST_TYPES.INLINE_DRAG_OPTION) {
|
|
3320
|
+
inlineDragIds.set(inline, `${INLINE_DRAG_OPT_PREFIX}${counter++}`);
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
2784
3324
|
const layout = [];
|
|
2785
3325
|
if (ast.question) {
|
|
2786
3326
|
layout.push({
|
|
@@ -2791,9 +3331,13 @@ function toJSON(ast) {
|
|
|
2791
3331
|
}
|
|
2792
3332
|
for (const block of ast.lines) {
|
|
2793
3333
|
if (block.type === AST_TYPES.TABLE_BLOCK) {
|
|
2794
|
-
layout.push(transformTable(block, idGenerator));
|
|
3334
|
+
layout.push(transformTable(block, idGenerator, inlineDragIds));
|
|
2795
3335
|
} else if (block.type === AST_TYPES.NUMBER_LINE_BLOCK) {
|
|
2796
|
-
|
|
3336
|
+
const nlItem = transformNumberLine(block, idGenerator, inlineDragIds);
|
|
3337
|
+
layout.push(nlItem);
|
|
3338
|
+
if (nlItem.counter?.answer !== void 0) {
|
|
3339
|
+
answers[nlItem.counter.id] = nlItem.counter.answer;
|
|
3340
|
+
}
|
|
2797
3341
|
} else if (block.type === AST_TYPES.MATCHER_BLOCK) {
|
|
2798
3342
|
const matcherBlock = block;
|
|
2799
3343
|
layout.push({
|
|
@@ -2816,6 +3360,8 @@ function toJSON(ast) {
|
|
|
2816
3360
|
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
2817
3361
|
type: JSON_TYPES.LINEBREAK
|
|
2818
3362
|
});
|
|
3363
|
+
} else if (block.type === AST_TYPES.VERTICAL_OPERATION_BLOCK) {
|
|
3364
|
+
layout.push(transformVerticalOperation(block, idGenerator));
|
|
2819
3365
|
} else if (block.type === AST_TYPES.RADIO_OPTIONS) {
|
|
2820
3366
|
const radioBlock = block;
|
|
2821
3367
|
layout.push({
|
|
@@ -2828,13 +3374,10 @@ function toJSON(ast) {
|
|
|
2828
3374
|
type: JSON_TYPES.LINEBREAK
|
|
2829
3375
|
});
|
|
2830
3376
|
} else {
|
|
2831
|
-
const lineItems = transformLine(block, idGenerator);
|
|
3377
|
+
const lineItems = transformLine(block, idGenerator, inlineDragIds);
|
|
2832
3378
|
layout.push(...lineItems);
|
|
2833
3379
|
}
|
|
2834
3380
|
}
|
|
2835
|
-
const widgets = {};
|
|
2836
|
-
const answers = {};
|
|
2837
|
-
const allInlines = collectAllInlines(ast.lines);
|
|
2838
3381
|
for (const inline of allInlines) {
|
|
2839
3382
|
if (inline.type === AST_TYPES.INLINE_INPUT) {
|
|
2840
3383
|
if (inline.inputType === INPUT_TYPES.STRING) {
|
|
@@ -2868,6 +3411,13 @@ function toJSON(ast) {
|
|
|
2868
3411
|
answers[inline.name] = inline.answer;
|
|
2869
3412
|
}
|
|
2870
3413
|
}
|
|
3414
|
+
if (inline.type === AST_TYPES.INLINE_DRAG_OPTION) {
|
|
3415
|
+
const widgetId = inlineDragIds.get(inline);
|
|
3416
|
+
widgets[widgetId] = {
|
|
3417
|
+
type: JSON_TYPES.INLINE_DRAG_OPTION,
|
|
3418
|
+
value: inline.value
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
2871
3421
|
if (inline.type === AST_TYPES.VISUAL_FRACTION) {
|
|
2872
3422
|
const answerArr = inline.answer ?? [];
|
|
2873
3423
|
widgets[inline.name] = {
|
|
@@ -2885,6 +3435,27 @@ function toJSON(ast) {
|
|
|
2885
3435
|
}
|
|
2886
3436
|
}
|
|
2887
3437
|
}
|
|
3438
|
+
for (const block of ast.lines) {
|
|
3439
|
+
if (block.type !== AST_TYPES.VERTICAL_OPERATION_BLOCK) continue;
|
|
3440
|
+
const vop = block;
|
|
3441
|
+
for (const item of vop.items) {
|
|
3442
|
+
if (item.type !== AST_TYPES.VOP_ROW && item.type !== AST_TYPES.VOP_CARRY_ROW) continue;
|
|
3443
|
+
for (const cell of item.cells) {
|
|
3444
|
+
if (cell.kind !== "input") continue;
|
|
3445
|
+
if (widgets[cell.name]) {
|
|
3446
|
+
throw new Error(`VERTICAL_OPERATION: duplicate INPUT name "${cell.name}"`);
|
|
3447
|
+
}
|
|
3448
|
+
widgets[cell.name] = {
|
|
3449
|
+
type: JSON_TYPES.INLINE_INPUT,
|
|
3450
|
+
inputType: INPUT_TYPES.NUMERIC,
|
|
3451
|
+
digits: cell.digits
|
|
3452
|
+
};
|
|
3453
|
+
if (cell.answer !== void 0) {
|
|
3454
|
+
answers[cell.name] = cell.answer;
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
2888
3459
|
for (const block of ast.lines) {
|
|
2889
3460
|
if (block.type === AST_TYPES.RADIO_OPTIONS) {
|
|
2890
3461
|
const radioBlock = block;
|
|
@@ -2895,11 +3466,11 @@ function toJSON(ast) {
|
|
|
2895
3466
|
answers[radioBlock.name] = radioBlock.answer;
|
|
2896
3467
|
} else if (block.type === AST_TYPES.MATCHER_BLOCK) {
|
|
2897
3468
|
const matcherBlock = block;
|
|
2898
|
-
widgets[matcherBlock.name] = buildMatcherWidget(matcherBlock, idGenerator);
|
|
3469
|
+
widgets[matcherBlock.name] = buildMatcherWidget(matcherBlock, idGenerator, inlineDragIds);
|
|
2899
3470
|
answers[matcherBlock.name] = matcherBlock.answers;
|
|
2900
3471
|
} else if (block.type === AST_TYPES.PAIR_MATCHER_BLOCK) {
|
|
2901
3472
|
const pmBlock = block;
|
|
2902
|
-
widgets[pmBlock.name] = buildPairMatcherWidget(pmBlock, idGenerator);
|
|
3473
|
+
widgets[pmBlock.name] = buildPairMatcherWidget(pmBlock, idGenerator, inlineDragIds);
|
|
2903
3474
|
answers[pmBlock.name] = pmBlock.answers;
|
|
2904
3475
|
}
|
|
2905
3476
|
}
|
|
@@ -2958,9 +3529,9 @@ function collectAllInlines(blocks) {
|
|
|
2958
3529
|
}
|
|
2959
3530
|
return result;
|
|
2960
3531
|
}
|
|
2961
|
-
function transformNumberLine(numberLine, idGenerator) {
|
|
3532
|
+
function transformNumberLine(numberLine, idGenerator, inlineDragIds) {
|
|
2962
3533
|
const positions = numberLine.positions.map((pos) => {
|
|
2963
|
-
const content = pos.inlines.map((inline) => transformInline(inline, idGenerator));
|
|
3534
|
+
const content = pos.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds));
|
|
2964
3535
|
return {
|
|
2965
3536
|
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_POSITION),
|
|
2966
3537
|
type: JSON_TYPES.NUMBER_LINE_POSITION,
|
|
@@ -2977,7 +3548,8 @@ function transformNumberLine(numberLine, idGenerator) {
|
|
|
2977
3548
|
min: c.min,
|
|
2978
3549
|
max: c.max,
|
|
2979
3550
|
...c.default !== void 0 && { default: c.default },
|
|
2980
|
-
...c.name !== void 0 && { name: c.name }
|
|
3551
|
+
...c.name !== void 0 && { name: c.name },
|
|
3552
|
+
...c.answer !== void 0 && { answer: c.answer }
|
|
2981
3553
|
};
|
|
2982
3554
|
}
|
|
2983
3555
|
let highlights;
|
|
@@ -3007,19 +3579,19 @@ function transformNumberLine(numberLine, idGenerator) {
|
|
|
3007
3579
|
};
|
|
3008
3580
|
return item;
|
|
3009
3581
|
}
|
|
3010
|
-
function mapMatcherItemList(items, idGenerator) {
|
|
3582
|
+
function mapMatcherItemList(items, idGenerator, inlineDragIds) {
|
|
3011
3583
|
return items.map((item) => ({
|
|
3012
3584
|
id: idGenerator.generate(JSON_TYPES.MATCHER_ITEM),
|
|
3013
3585
|
type: JSON_TYPES.MATCHER_ITEM,
|
|
3014
3586
|
itemId: item.itemId,
|
|
3015
|
-
content: item.inlines.map((inline) => transformInline(inline, idGenerator))
|
|
3587
|
+
content: item.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds))
|
|
3016
3588
|
}));
|
|
3017
3589
|
}
|
|
3018
|
-
function buildMatcherWidget(matcher, idGenerator) {
|
|
3590
|
+
function buildMatcherWidget(matcher, idGenerator, inlineDragIds) {
|
|
3019
3591
|
const rows = matcher.rows.map((row) => ({
|
|
3020
3592
|
id: idGenerator.generate(JSON_TYPES.MATCHER_ROW),
|
|
3021
3593
|
type: JSON_TYPES.MATCHER_ROW,
|
|
3022
|
-
items: mapMatcherItemList(row.items, idGenerator)
|
|
3594
|
+
items: mapMatcherItemList(row.items, idGenerator, inlineDragIds)
|
|
3023
3595
|
}));
|
|
3024
3596
|
const result = {
|
|
3025
3597
|
type: JSON_TYPES.MATCHER,
|
|
@@ -3031,17 +3603,72 @@ function buildMatcherWidget(matcher, idGenerator) {
|
|
|
3031
3603
|
}
|
|
3032
3604
|
return result;
|
|
3033
3605
|
}
|
|
3034
|
-
function buildPairMatcherWidget(pm, idGenerator) {
|
|
3606
|
+
function buildPairMatcherWidget(pm, idGenerator, inlineDragIds) {
|
|
3035
3607
|
return {
|
|
3036
3608
|
type: JSON_TYPES.PAIR_MATCHER,
|
|
3037
|
-
topItems: mapMatcherItemList(pm.topItems, idGenerator),
|
|
3038
|
-
bottomItems: mapMatcherItemList(pm.bottomItems, idGenerator)
|
|
3609
|
+
topItems: mapMatcherItemList(pm.topItems, idGenerator, inlineDragIds),
|
|
3610
|
+
bottomItems: mapMatcherItemList(pm.bottomItems, idGenerator, inlineDragIds)
|
|
3039
3611
|
};
|
|
3040
3612
|
}
|
|
3041
|
-
function
|
|
3613
|
+
function transformVerticalOperation(vop, idGenerator) {
|
|
3614
|
+
const anyRowHasOp = vop.items.some(
|
|
3615
|
+
(item) => item.type === AST_TYPES.VOP_ROW && item.op !== void 0
|
|
3616
|
+
);
|
|
3617
|
+
if (!anyRowHasOp && vop.op === void 0) {
|
|
3618
|
+
throw new Error("VERTICAL_OPERATION: at least one op is required (set op on the block or on individual ROWs)");
|
|
3619
|
+
}
|
|
3620
|
+
let cols = 1;
|
|
3621
|
+
for (const item of vop.items) {
|
|
3622
|
+
if (item.type === AST_TYPES.VOP_ROW || item.type === AST_TYPES.VOP_CARRY_ROW) {
|
|
3623
|
+
cols = Math.max(cols, item.cells.length);
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
function buildCells(row) {
|
|
3627
|
+
return row.cells.map((cell) => {
|
|
3628
|
+
const id = idGenerator.generate(JSON_TYPES.VOP_CELL);
|
|
3629
|
+
if (cell.kind === "digit") return { id, type: JSON_TYPES.VOP_CELL, kind: "digit", value: cell.value };
|
|
3630
|
+
if (cell.kind === "label") return { id, type: JSON_TYPES.VOP_CELL, kind: "label", value: cell.value };
|
|
3631
|
+
if (cell.kind === "color") return { id, type: JSON_TYPES.VOP_CELL, kind: "color", value: cell.value, color: cell.color };
|
|
3632
|
+
if (cell.kind === "bold") return { id, type: JSON_TYPES.VOP_CELL, kind: "bold", value: cell.value };
|
|
3633
|
+
if (cell.kind === "highlight") return { id, type: JSON_TYPES.VOP_CELL, kind: "highlight", value: cell.value };
|
|
3634
|
+
if (cell.kind === "input") return { id, type: JSON_TYPES.VOP_CELL, kind: "input", widgetId: cell.name };
|
|
3635
|
+
return { id, type: JSON_TYPES.VOP_CELL, kind: "empty" };
|
|
3636
|
+
});
|
|
3637
|
+
}
|
|
3638
|
+
const items = vop.items.map((item) => {
|
|
3639
|
+
if (item.type === AST_TYPES.VOP_SEPARATOR) {
|
|
3640
|
+
return {
|
|
3641
|
+
id: idGenerator.generate(JSON_TYPES.VOP_SEPARATOR),
|
|
3642
|
+
type: JSON_TYPES.VOP_SEPARATOR
|
|
3643
|
+
};
|
|
3644
|
+
}
|
|
3645
|
+
if (item.type === AST_TYPES.VOP_CARRY_ROW) {
|
|
3646
|
+
return {
|
|
3647
|
+
id: idGenerator.generate(JSON_TYPES.VOP_CARRY_ROW),
|
|
3648
|
+
type: JSON_TYPES.VOP_CARRY_ROW,
|
|
3649
|
+
cells: buildCells(item)
|
|
3650
|
+
};
|
|
3651
|
+
}
|
|
3652
|
+
const row = item;
|
|
3653
|
+
return {
|
|
3654
|
+
id: idGenerator.generate(JSON_TYPES.VOP_ROW),
|
|
3655
|
+
type: JSON_TYPES.VOP_ROW,
|
|
3656
|
+
cells: buildCells(row),
|
|
3657
|
+
...row.op !== void 0 && { op: row.op }
|
|
3658
|
+
};
|
|
3659
|
+
});
|
|
3660
|
+
return {
|
|
3661
|
+
id: idGenerator.generate(JSON_TYPES.VERTICAL_OPERATION),
|
|
3662
|
+
type: JSON_TYPES.VERTICAL_OPERATION,
|
|
3663
|
+
op: vop.op,
|
|
3664
|
+
cols,
|
|
3665
|
+
items
|
|
3666
|
+
};
|
|
3667
|
+
}
|
|
3668
|
+
function transformTable(table, idGenerator, inlineDragIds) {
|
|
3042
3669
|
const rows = table.rows.map((row) => {
|
|
3043
3670
|
const cells = row.cells.map((cell) => {
|
|
3044
|
-
const items = cell.inlines.map((inline) => transformInline(inline, idGenerator));
|
|
3671
|
+
const items = cell.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds));
|
|
3045
3672
|
const cellItem = {
|
|
3046
3673
|
id: idGenerator.generate(JSON_TYPES.TABLE_CELL),
|
|
3047
3674
|
type: JSON_TYPES.TABLE_CELL,
|
|
@@ -3065,10 +3692,10 @@ function transformTable(table, idGenerator) {
|
|
|
3065
3692
|
rows
|
|
3066
3693
|
};
|
|
3067
3694
|
}
|
|
3068
|
-
function transformLine(line, idGenerator) {
|
|
3695
|
+
function transformLine(line, idGenerator, inlineDragIds) {
|
|
3069
3696
|
const items = [];
|
|
3070
3697
|
for (const inline of line.inlines) {
|
|
3071
|
-
items.push(transformInline(inline, idGenerator));
|
|
3698
|
+
items.push(transformInline(inline, idGenerator, inlineDragIds));
|
|
3072
3699
|
}
|
|
3073
3700
|
items.push({
|
|
3074
3701
|
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
@@ -3076,7 +3703,7 @@ function transformLine(line, idGenerator) {
|
|
|
3076
3703
|
});
|
|
3077
3704
|
return items;
|
|
3078
3705
|
}
|
|
3079
|
-
function transformInline(inline, idGenerator) {
|
|
3706
|
+
function transformInline(inline, idGenerator, inlineDragIds) {
|
|
3080
3707
|
switch (inline.type) {
|
|
3081
3708
|
case AST_TYPES.FREE_TEXT:
|
|
3082
3709
|
const trimmed = inline.value.trim();
|
|
@@ -3148,6 +3775,12 @@ function transformInline(inline, idGenerator) {
|
|
|
3148
3775
|
type: JSON_TYPES.WIDGET_REF,
|
|
3149
3776
|
widgetId: inline.name
|
|
3150
3777
|
};
|
|
3778
|
+
case AST_TYPES.INLINE_DRAG_OPTION:
|
|
3779
|
+
return {
|
|
3780
|
+
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
3781
|
+
type: JSON_TYPES.WIDGET_REF,
|
|
3782
|
+
widgetId: inlineDragIds.get(inline)
|
|
3783
|
+
};
|
|
3151
3784
|
default:
|
|
3152
3785
|
throw new Error(`Unknown inline type: ${inline.type}`);
|
|
3153
3786
|
}
|
|
@@ -13312,7 +13945,7 @@ const InlineInput = ({
|
|
|
13312
13945
|
id: widgetId,
|
|
13313
13946
|
accepts: ["drag-option"],
|
|
13314
13947
|
onDrop: handleDrop,
|
|
13315
|
-
className: "inline-input-droppable",
|
|
13948
|
+
className: "inline-widget inline-input-droppable",
|
|
13316
13949
|
children: isDropped && value !== "" ? (
|
|
13317
13950
|
// Show as chip only when value came from drag-drop
|
|
13318
13951
|
/* @__PURE__ */ jsx(
|
|
@@ -13344,6 +13977,32 @@ const InlineInput = ({
|
|
|
13344
13977
|
}
|
|
13345
13978
|
);
|
|
13346
13979
|
};
|
|
13980
|
+
const SingleDragOption = ({
|
|
13981
|
+
optionId,
|
|
13982
|
+
value,
|
|
13983
|
+
onOptionClick,
|
|
13984
|
+
className = ""
|
|
13985
|
+
}) => {
|
|
13986
|
+
const { usedOptionIds } = useExerciseDnd();
|
|
13987
|
+
const isUsed = usedOptionIds.has(optionId);
|
|
13988
|
+
return /* @__PURE__ */ jsx("div", { className: `drag-option-slot ${className}`.trim(), children: isUsed ? /* @__PURE__ */ jsx("div", { className: "drag-option-empty", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(
|
|
13989
|
+
Draggable,
|
|
13990
|
+
{
|
|
13991
|
+
id: optionId,
|
|
13992
|
+
type: "drag-option",
|
|
13993
|
+
data: { value, label: value },
|
|
13994
|
+
className: "drag-option-wrapper",
|
|
13995
|
+
children: /* @__PURE__ */ jsx(
|
|
13996
|
+
"button",
|
|
13997
|
+
{
|
|
13998
|
+
className: "drag-option",
|
|
13999
|
+
onClick: () => onOptionClick?.(value),
|
|
14000
|
+
children: value
|
|
14001
|
+
}
|
|
14002
|
+
)
|
|
14003
|
+
}
|
|
14004
|
+
) });
|
|
14005
|
+
};
|
|
13347
14006
|
const PillBackground = ({
|
|
13348
14007
|
width = "100%",
|
|
13349
14008
|
minHeight = "72px",
|
|
@@ -13416,36 +14075,32 @@ const DragOptions = ({
|
|
|
13416
14075
|
options,
|
|
13417
14076
|
onOptionClick
|
|
13418
14077
|
}) => {
|
|
13419
|
-
const { usedOptionIds } = useExerciseDnd();
|
|
13420
14078
|
return /* @__PURE__ */ jsx(PillBackground, { children: /* @__PURE__ */ jsx("div", { className: "drag-options", children: options.map((option, idx) => {
|
|
13421
14079
|
const optionId = `drag-option-${idx}`;
|
|
13422
14080
|
const optionValue = String(option);
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
|
|
13427
|
-
|
|
13428
|
-
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
id: optionId,
|
|
13433
|
-
type: "drag-option",
|
|
13434
|
-
data: { value: optionValue, label: optionValue },
|
|
13435
|
-
className: "drag-option-wrapper",
|
|
13436
|
-
children: /* @__PURE__ */ jsx(
|
|
13437
|
-
"button",
|
|
13438
|
-
{
|
|
13439
|
-
className: "drag-option",
|
|
13440
|
-
onClick: () => onOptionClick?.(optionValue),
|
|
13441
|
-
children: optionValue
|
|
13442
|
-
}
|
|
13443
|
-
)
|
|
13444
|
-
}
|
|
13445
|
-
)
|
|
13446
|
-
) }, optionId);
|
|
14081
|
+
return /* @__PURE__ */ jsx(
|
|
14082
|
+
SingleDragOption,
|
|
14083
|
+
{
|
|
14084
|
+
optionId,
|
|
14085
|
+
value: optionValue,
|
|
14086
|
+
onOptionClick
|
|
14087
|
+
},
|
|
14088
|
+
optionId
|
|
14089
|
+
);
|
|
13447
14090
|
}) }) });
|
|
13448
14091
|
};
|
|
14092
|
+
const InlineDragOption = ({
|
|
14093
|
+
widgetId,
|
|
14094
|
+
widget
|
|
14095
|
+
}) => {
|
|
14096
|
+
return /* @__PURE__ */ jsx("div", { className: "inline-widget", children: /* @__PURE__ */ jsx(
|
|
14097
|
+
SingleDragOption,
|
|
14098
|
+
{
|
|
14099
|
+
optionId: widgetId,
|
|
14100
|
+
value: widget.value
|
|
14101
|
+
}
|
|
14102
|
+
) });
|
|
14103
|
+
};
|
|
13449
14104
|
const NumericKeypad = ({
|
|
13450
14105
|
value,
|
|
13451
14106
|
includeOperators = false,
|
|
@@ -13540,7 +14195,7 @@ const FractionInput = ({
|
|
|
13540
14195
|
}
|
|
13541
14196
|
);
|
|
13542
14197
|
};
|
|
13543
|
-
return /* @__PURE__ */ jsxs("span", { className: "fraction", role: "math", "aria-label": `fraction ${widgetId}`, children: [
|
|
14198
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-widget fraction", role: "math", "aria-label": `fraction ${widgetId}`, children: [
|
|
13544
14199
|
/* @__PURE__ */ jsx("span", { className: "fraction-numerator", children: renderSlot(numKey, numerator, numValue, "numerator", numValidationStatus) }),
|
|
13545
14200
|
/* @__PURE__ */ jsx("span", { className: "fraction-bar", "aria-hidden": "true" }),
|
|
13546
14201
|
/* @__PURE__ */ jsx("span", { className: "fraction-denominator", children: renderSlot(denKey, denominator, denValue, "denominator", denValidationStatus) })
|
|
@@ -13972,7 +14627,7 @@ const VisualFraction = ({
|
|
|
13972
14627
|
return /* @__PURE__ */ jsxs(
|
|
13973
14628
|
"div",
|
|
13974
14629
|
{
|
|
13975
|
-
className: `visual-fraction-widget visual-fraction-widget--${shape}${statusClass}`,
|
|
14630
|
+
className: `inline-widget visual-fraction-widget visual-fraction-widget--${shape}${statusClass}`,
|
|
13976
14631
|
style: rootStyle,
|
|
13977
14632
|
role: "group",
|
|
13978
14633
|
"aria-label": `${divisions} part ${shape} fraction`,
|
|
@@ -14593,6 +15248,9 @@ function isInlineInputWidget(widget) {
|
|
|
14593
15248
|
function isDragOptionsWidget(widget) {
|
|
14594
15249
|
return widget.type === "drag-options";
|
|
14595
15250
|
}
|
|
15251
|
+
function isInlineDragOptionWidget(widget) {
|
|
15252
|
+
return widget.type === "inline-drag-option";
|
|
15253
|
+
}
|
|
14596
15254
|
function isRadioOptionsWidget(widget) {
|
|
14597
15255
|
return widget.type === "radio-options";
|
|
14598
15256
|
}
|
|
@@ -14655,6 +15313,15 @@ const WidgetRenderer = ({
|
|
|
14655
15313
|
}
|
|
14656
15314
|
);
|
|
14657
15315
|
}
|
|
15316
|
+
if (isInlineDragOptionWidget(widget)) {
|
|
15317
|
+
return /* @__PURE__ */ jsx(
|
|
15318
|
+
InlineDragOption,
|
|
15319
|
+
{
|
|
15320
|
+
widgetId,
|
|
15321
|
+
widget
|
|
15322
|
+
}
|
|
15323
|
+
);
|
|
15324
|
+
}
|
|
14658
15325
|
if (isRadioOptionsWidget(widget)) {
|
|
14659
15326
|
return /* @__PURE__ */ jsx(
|
|
14660
15327
|
RadioOptions,
|
|
@@ -15371,6 +16038,13 @@ const NumberLineRenderer = ({
|
|
|
15371
16038
|
useEffect(() => {
|
|
15372
16039
|
setCounterCount(defaultCounterCount);
|
|
15373
16040
|
}, [counter]);
|
|
16041
|
+
const onChangeRef = useRef(onChange);
|
|
16042
|
+
onChangeRef.current = onChange;
|
|
16043
|
+
useEffect(() => {
|
|
16044
|
+
if (counter !== void 0) {
|
|
16045
|
+
onChangeRef.current(counter.id, String(counterCount));
|
|
16046
|
+
}
|
|
16047
|
+
}, [counterCount, counter?.id]);
|
|
15374
16048
|
const effectiveStep = useMemo(() => {
|
|
15375
16049
|
if (counter !== void 0 && counterCount > 0) {
|
|
15376
16050
|
return cleanFloat((max - min) / counterCount);
|
|
@@ -15404,10 +16078,17 @@ const NumberLineRenderer = ({
|
|
|
15404
16078
|
[visiblePositions]
|
|
15405
16079
|
);
|
|
15406
16080
|
const { positionContentHeight, getPositionContentRef } = useMeasuredPositionContentHeight(visiblePositionIds);
|
|
16081
|
+
const nlValidationStatus = useMemo(() => {
|
|
16082
|
+
if (!validationDetails || counter === void 0) return null;
|
|
16083
|
+
if (counter.answer === void 0) return null;
|
|
16084
|
+
const counterValidation = validationDetails[counter.id];
|
|
16085
|
+
if (counterValidation === void 0) return null;
|
|
16086
|
+
return counterValidation.isCorrect ? "correct" : "incorrect";
|
|
16087
|
+
}, [validationDetails, counter]);
|
|
15407
16088
|
return /* @__PURE__ */ jsxs(
|
|
15408
16089
|
"div",
|
|
15409
16090
|
{
|
|
15410
|
-
className: `number-line${isAxisDragMode ? " number-line--drag-mode" : ""}${lineStyle === "line" ? " number-line--style-line" : ""}`,
|
|
16091
|
+
className: `number-line${isAxisDragMode ? " number-line--drag-mode" : ""}${lineStyle === "line" ? " number-line--style-line" : ""}${nlValidationStatus ? ` number-line--${nlValidationStatus}` : ""}`,
|
|
15411
16092
|
style: {
|
|
15412
16093
|
[CSS_VAR_SIDE_PAD]: `${SIDE_PAD}%`,
|
|
15413
16094
|
...positionContentHeight && { "--nl-position-content-height": positionContentHeight }
|
|
@@ -15674,6 +16355,126 @@ const LayoutRenderer = ({
|
|
|
15674
16355
|
},
|
|
15675
16356
|
resetKey
|
|
15676
16357
|
);
|
|
16358
|
+
case JSON_TYPES.VERTICAL_OPERATION: {
|
|
16359
|
+
const vop = item;
|
|
16360
|
+
const anyRowHasOp = vop.items.some(
|
|
16361
|
+
(x) => x.type === JSON_TYPES.VOP_ROW && x.op !== void 0
|
|
16362
|
+
);
|
|
16363
|
+
let operatorRowIdx = -1;
|
|
16364
|
+
if (!anyRowHasOp && vop.op !== void 0) {
|
|
16365
|
+
const firstSepIdx = vop.items.findIndex((x) => x.type === JSON_TYPES.VOP_SEPARATOR);
|
|
16366
|
+
for (let i = (firstSepIdx === -1 ? vop.items.length : firstSepIdx) - 1; i >= 0; i--) {
|
|
16367
|
+
const curr = vop.items[i];
|
|
16368
|
+
if (curr && curr.type === JSON_TYPES.VOP_ROW) {
|
|
16369
|
+
operatorRowIdx = i;
|
|
16370
|
+
break;
|
|
16371
|
+
}
|
|
16372
|
+
}
|
|
16373
|
+
}
|
|
16374
|
+
const totalCols = vop.cols + 1;
|
|
16375
|
+
return /* @__PURE__ */ jsx(
|
|
16376
|
+
"div",
|
|
16377
|
+
{
|
|
16378
|
+
className: "vop",
|
|
16379
|
+
style: { "--vop-cols": totalCols },
|
|
16380
|
+
dir: "ltr",
|
|
16381
|
+
children: vop.items.map((child, idx) => {
|
|
16382
|
+
if (child.type === JSON_TYPES.VOP_SEPARATOR) {
|
|
16383
|
+
const sep = child;
|
|
16384
|
+
return /* @__PURE__ */ jsx("div", { className: "vop-sep" }, sep.id);
|
|
16385
|
+
}
|
|
16386
|
+
if (child.type === JSON_TYPES.VOP_CARRY_ROW) {
|
|
16387
|
+
const carry = child;
|
|
16388
|
+
const carryLeadingEmpty = Math.max(0, vop.cols - carry.cells.length);
|
|
16389
|
+
return /* @__PURE__ */ jsxs("div", { className: "vop-row vop-carry-row", children: [
|
|
16390
|
+
/* @__PURE__ */ jsx("span", { className: "vop-op-cell" }),
|
|
16391
|
+
Array.from({ length: carryLeadingEmpty }).map((_, i) => /* @__PURE__ */ jsx("span", { className: "vop-cell vop-carry-cell-empty" }, `cpad-${i}`)),
|
|
16392
|
+
carry.cells.map((cell) => {
|
|
16393
|
+
if (cell.kind === "digit") {
|
|
16394
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-carry-cell-digit", children: cell.value }, cell.id);
|
|
16395
|
+
}
|
|
16396
|
+
if (cell.kind === "label") {
|
|
16397
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-carry-cell-label", children: cell.value }, cell.id);
|
|
16398
|
+
}
|
|
16399
|
+
if (cell.kind === "color") {
|
|
16400
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-carry-cell-color", style: { color: cell.color }, children: cell.value }, cell.id);
|
|
16401
|
+
}
|
|
16402
|
+
if (cell.kind === "bold") {
|
|
16403
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-carry-cell-bold", children: cell.value }, cell.id);
|
|
16404
|
+
}
|
|
16405
|
+
if (cell.kind === "highlight") {
|
|
16406
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-carry-cell-highlight", children: /* @__PURE__ */ jsx("mark", { children: cell.value }) }, cell.id);
|
|
16407
|
+
}
|
|
16408
|
+
if (cell.kind === "empty") {
|
|
16409
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-carry-cell-empty" }, cell.id);
|
|
16410
|
+
}
|
|
16411
|
+
const widget = widgets[cell.widgetId];
|
|
16412
|
+
if (!widget) return /* @__PURE__ */ jsx("span", { className: "vop-cell" }, cell.id);
|
|
16413
|
+
const isCorrect = validationDetails?.[cell.widgetId]?.isCorrect;
|
|
16414
|
+
const validationStatus = isCorrect === true ? "correct" : isCorrect === false ? "incorrect" : null;
|
|
16415
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-carry-cell-input", children: /* @__PURE__ */ jsx(
|
|
16416
|
+
WidgetRenderer,
|
|
16417
|
+
{
|
|
16418
|
+
widget,
|
|
16419
|
+
widgetId: cell.widgetId,
|
|
16420
|
+
value: values[cell.widgetId] || "",
|
|
16421
|
+
isDropped: droppedInputs.has(cell.widgetId),
|
|
16422
|
+
validationStatus,
|
|
16423
|
+
onChange,
|
|
16424
|
+
onInputFocus
|
|
16425
|
+
}
|
|
16426
|
+
) }, cell.id);
|
|
16427
|
+
})
|
|
16428
|
+
] }, carry.id);
|
|
16429
|
+
}
|
|
16430
|
+
const row = child;
|
|
16431
|
+
const showOperator = row.op !== void 0 || idx === operatorRowIdx;
|
|
16432
|
+
const displayOp = row.op ?? vop.op;
|
|
16433
|
+
const leadingEmpty = vop.cols - row.cells.length;
|
|
16434
|
+
return /* @__PURE__ */ jsxs("div", { className: "vop-row", children: [
|
|
16435
|
+
/* @__PURE__ */ jsx("span", { className: "vop-op-cell", children: showOperator ? /* @__PURE__ */ jsx("span", { className: "operator vop-op", children: displayOp }) : null }),
|
|
16436
|
+
Array.from({ length: leadingEmpty }).map((_, i) => /* @__PURE__ */ jsx("span", { className: "vop-cell vop-cell-empty" }, `pad-${i}`)),
|
|
16437
|
+
row.cells.map((cell) => {
|
|
16438
|
+
if (cell.kind === "digit") {
|
|
16439
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-cell-digit", children: cell.value }, cell.id);
|
|
16440
|
+
}
|
|
16441
|
+
if (cell.kind === "label") {
|
|
16442
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-cell-label", children: cell.value }, cell.id);
|
|
16443
|
+
}
|
|
16444
|
+
if (cell.kind === "color") {
|
|
16445
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-cell-color", style: { color: cell.color }, children: cell.value }, cell.id);
|
|
16446
|
+
}
|
|
16447
|
+
if (cell.kind === "bold") {
|
|
16448
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-cell-bold", children: cell.value }, cell.id);
|
|
16449
|
+
}
|
|
16450
|
+
if (cell.kind === "highlight") {
|
|
16451
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-cell-highlight", children: /* @__PURE__ */ jsx("mark", { children: cell.value }) }, cell.id);
|
|
16452
|
+
}
|
|
16453
|
+
if (cell.kind === "empty") {
|
|
16454
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-cell-empty" }, cell.id);
|
|
16455
|
+
}
|
|
16456
|
+
const widget = widgets[cell.widgetId];
|
|
16457
|
+
if (!widget) return /* @__PURE__ */ jsx("span", { className: "vop-cell" }, cell.id);
|
|
16458
|
+
const isCorrect = validationDetails?.[cell.widgetId]?.isCorrect;
|
|
16459
|
+
const validationStatus = isCorrect === true ? "correct" : isCorrect === false ? "incorrect" : null;
|
|
16460
|
+
return /* @__PURE__ */ jsx("span", { className: "vop-cell vop-cell-input", children: /* @__PURE__ */ jsx(
|
|
16461
|
+
WidgetRenderer,
|
|
16462
|
+
{
|
|
16463
|
+
widget,
|
|
16464
|
+
widgetId: cell.widgetId,
|
|
16465
|
+
value: values[cell.widgetId] || "",
|
|
16466
|
+
isDropped: droppedInputs.has(cell.widgetId),
|
|
16467
|
+
validationStatus,
|
|
16468
|
+
onChange,
|
|
16469
|
+
onInputFocus
|
|
16470
|
+
}
|
|
16471
|
+
) }, cell.id);
|
|
16472
|
+
})
|
|
16473
|
+
] }, row.id);
|
|
16474
|
+
})
|
|
16475
|
+
}
|
|
16476
|
+
);
|
|
16477
|
+
}
|
|
15677
16478
|
default:
|
|
15678
16479
|
return null;
|
|
15679
16480
|
}
|
|
@@ -15767,6 +16568,13 @@ const ExerciseRenderer = forwardRef(({
|
|
|
15767
16568
|
onSubmit?.(values);
|
|
15768
16569
|
};
|
|
15769
16570
|
const lineGroups = useMemo(() => {
|
|
16571
|
+
const isRTLInlineWidget = (item) => {
|
|
16572
|
+
if (item.type === JSON_TYPES.WIDGET_REF) {
|
|
16573
|
+
const widget = json.widgets[item.widgetId];
|
|
16574
|
+
return !!widget && RTL_INLINE_WIDGET_TYPES.has(widget.type);
|
|
16575
|
+
}
|
|
16576
|
+
return RTL_INLINE_WIDGET_TYPES.has(item.type);
|
|
16577
|
+
};
|
|
15770
16578
|
const groups = [];
|
|
15771
16579
|
let currentGroup = [];
|
|
15772
16580
|
for (const item of json.layout) {
|
|
@@ -15774,6 +16582,7 @@ const ExerciseRenderer = forwardRef(({
|
|
|
15774
16582
|
groups.push({
|
|
15775
16583
|
items: [item],
|
|
15776
16584
|
hasArabic: itemContainsArabic(item),
|
|
16585
|
+
hasRTLInlineWidget: false,
|
|
15777
16586
|
subGroups: []
|
|
15778
16587
|
// Question text doesn't need sub-grouping
|
|
15779
16588
|
});
|
|
@@ -15782,13 +16591,15 @@ const ExerciseRenderer = forwardRef(({
|
|
|
15782
16591
|
if (item.type === JSON_TYPES.TABLE) {
|
|
15783
16592
|
if (currentGroup.length > 0) {
|
|
15784
16593
|
const hasArabic = currentGroup.some((item2) => itemContainsArabic(item2));
|
|
16594
|
+
const hasRTLInlineWidget = currentGroup.some(isRTLInlineWidget);
|
|
15785
16595
|
const subGroups = groupLineItems(currentGroup);
|
|
15786
|
-
groups.push({ items: currentGroup, hasArabic, subGroups });
|
|
16596
|
+
groups.push({ items: currentGroup, hasArabic, hasRTLInlineWidget, subGroups });
|
|
15787
16597
|
currentGroup = [];
|
|
15788
16598
|
}
|
|
15789
16599
|
groups.push({
|
|
15790
16600
|
items: [item],
|
|
15791
16601
|
hasArabic: false,
|
|
16602
|
+
hasRTLInlineWidget: false,
|
|
15792
16603
|
subGroups: []
|
|
15793
16604
|
});
|
|
15794
16605
|
continue;
|
|
@@ -15796,13 +16607,31 @@ const ExerciseRenderer = forwardRef(({
|
|
|
15796
16607
|
if (item.type === JSON_TYPES.NUMBER_LINE) {
|
|
15797
16608
|
if (currentGroup.length > 0) {
|
|
15798
16609
|
const hasArabic = currentGroup.some((item2) => itemContainsArabic(item2));
|
|
16610
|
+
const hasRTLInlineWidget = currentGroup.some(isRTLInlineWidget);
|
|
16611
|
+
const subGroups = groupLineItems(currentGroup);
|
|
16612
|
+
groups.push({ items: currentGroup, hasArabic, hasRTLInlineWidget, subGroups });
|
|
16613
|
+
currentGroup = [];
|
|
16614
|
+
}
|
|
16615
|
+
groups.push({
|
|
16616
|
+
items: [item],
|
|
16617
|
+
hasArabic: false,
|
|
16618
|
+
hasRTLInlineWidget: false,
|
|
16619
|
+
subGroups: []
|
|
16620
|
+
});
|
|
16621
|
+
continue;
|
|
16622
|
+
}
|
|
16623
|
+
if (item.type === JSON_TYPES.VERTICAL_OPERATION) {
|
|
16624
|
+
if (currentGroup.length > 0) {
|
|
16625
|
+
const hasArabic = currentGroup.some((item2) => itemContainsArabic(item2));
|
|
16626
|
+
const hasRTLInlineWidget = currentGroup.some(isRTLInlineWidget);
|
|
15799
16627
|
const subGroups = groupLineItems(currentGroup);
|
|
15800
|
-
groups.push({ items: currentGroup, hasArabic, subGroups });
|
|
16628
|
+
groups.push({ items: currentGroup, hasArabic, hasRTLInlineWidget, subGroups });
|
|
15801
16629
|
currentGroup = [];
|
|
15802
16630
|
}
|
|
15803
16631
|
groups.push({
|
|
15804
16632
|
items: [item],
|
|
15805
16633
|
hasArabic: false,
|
|
16634
|
+
hasRTLInlineWidget: false,
|
|
15806
16635
|
subGroups: []
|
|
15807
16636
|
});
|
|
15808
16637
|
continue;
|
|
@@ -15810,8 +16639,9 @@ const ExerciseRenderer = forwardRef(({
|
|
|
15810
16639
|
if (item.type === JSON_TYPES.LINEBREAK) {
|
|
15811
16640
|
if (currentGroup.length > 0) {
|
|
15812
16641
|
const hasArabic = currentGroup.some((item2) => itemContainsArabic(item2));
|
|
16642
|
+
const hasRTLInlineWidget = currentGroup.some(isRTLInlineWidget);
|
|
15813
16643
|
const subGroups = groupLineItems(currentGroup);
|
|
15814
|
-
groups.push({ items: currentGroup, hasArabic, subGroups });
|
|
16644
|
+
groups.push({ items: currentGroup, hasArabic, hasRTLInlineWidget, subGroups });
|
|
15815
16645
|
currentGroup = [];
|
|
15816
16646
|
}
|
|
15817
16647
|
} else {
|
|
@@ -15820,11 +16650,12 @@ const ExerciseRenderer = forwardRef(({
|
|
|
15820
16650
|
}
|
|
15821
16651
|
if (currentGroup.length > 0) {
|
|
15822
16652
|
const hasArabic = currentGroup.some((item) => itemContainsArabic(item));
|
|
16653
|
+
const hasRTLInlineWidget = currentGroup.some(isRTLInlineWidget);
|
|
15823
16654
|
const subGroups = groupLineItems(currentGroup);
|
|
15824
|
-
groups.push({ items: currentGroup, hasArabic, subGroups });
|
|
16655
|
+
groups.push({ items: currentGroup, hasArabic, hasRTLInlineWidget, subGroups });
|
|
15825
16656
|
}
|
|
15826
16657
|
return groups;
|
|
15827
|
-
}, [json.layout]);
|
|
16658
|
+
}, [json.layout, json.widgets]);
|
|
15828
16659
|
const droppedInputs = useMemo(() => new Set(droppedSources.keys()), [droppedSources]);
|
|
15829
16660
|
const dragOptionsWidget = json.widgets["_drag_options"];
|
|
15830
16661
|
const rulerWidget = json.widgets["_ruler"];
|
|
@@ -15842,7 +16673,8 @@ const ExerciseRenderer = forwardRef(({
|
|
|
15842
16673
|
/* @__PURE__ */ jsx("div", { className: "exercise-layout", children: lineGroups.map((group, groupIdx) => /* @__PURE__ */ jsx(
|
|
15843
16674
|
"div",
|
|
15844
16675
|
{
|
|
15845
|
-
className:
|
|
16676
|
+
className: "exercise-line",
|
|
16677
|
+
dir: group.hasArabic && group.hasRTLInlineWidget ? "rtl" : void 0,
|
|
15846
16678
|
children: group.subGroups.length > 0 ? (
|
|
15847
16679
|
// Render sub-groups for regular lines
|
|
15848
16680
|
group.subGroups.map((subGroup, subIdx) => subGroup.isTextGroup ? (
|
|
@@ -15987,6 +16819,7 @@ export {
|
|
|
15987
16819
|
HighlightedTextSchema,
|
|
15988
16820
|
ImageInlineSchema,
|
|
15989
16821
|
ImageItemSchema,
|
|
16822
|
+
InlineDragOptionSchema,
|
|
15990
16823
|
InlineInputJSONSchema,
|
|
15991
16824
|
InlineInputWidgetSchema,
|
|
15992
16825
|
InlineNodeSchema,
|
|
@@ -16028,10 +16861,31 @@ export {
|
|
|
16028
16861
|
VISUAL_FRACTION_MAX_DIVISIONS,
|
|
16029
16862
|
VISUAL_FRACTION_SHAPE,
|
|
16030
16863
|
VISUAL_FRACTION_SHAPES,
|
|
16864
|
+
VOP_OPERATORS,
|
|
16031
16865
|
ValidationError,
|
|
16032
16866
|
ValidationJSONSchema,
|
|
16033
16867
|
ValidationNodeSchema,
|
|
16868
|
+
VerticalOperationBlockSchema,
|
|
16869
|
+
VerticalOperationItemSchema,
|
|
16034
16870
|
VisualFractionInlineSchema,
|
|
16871
|
+
VopBoldCellItemSchema,
|
|
16872
|
+
VopBoldCellSchema,
|
|
16873
|
+
VopCarryRowItemSchema,
|
|
16874
|
+
VopCarryRowSchema,
|
|
16875
|
+
VopCellItemSchema,
|
|
16876
|
+
VopCellSchema,
|
|
16877
|
+
VopColorCellItemSchema,
|
|
16878
|
+
VopColorCellSchema,
|
|
16879
|
+
VopDigitCellSchema,
|
|
16880
|
+
VopEmptyCellSchema,
|
|
16881
|
+
VopHighlightCellItemSchema,
|
|
16882
|
+
VopHighlightCellSchema,
|
|
16883
|
+
VopInputCellSchema,
|
|
16884
|
+
VopLabelCellSchema,
|
|
16885
|
+
VopRowItemSchema,
|
|
16886
|
+
VopRowSchema,
|
|
16887
|
+
VopSeparatorItemSchema,
|
|
16888
|
+
VopSeparatorSchema,
|
|
16035
16889
|
WidgetJSONSchema,
|
|
16036
16890
|
WidgetRefSchema,
|
|
16037
16891
|
WidgetRenderer,
|
|
@@ -16046,6 +16900,7 @@ export {
|
|
|
16046
16900
|
createFreeText,
|
|
16047
16901
|
createHighlightedText,
|
|
16048
16902
|
createImage,
|
|
16903
|
+
createInlineDragOption,
|
|
16049
16904
|
createLineBlock,
|
|
16050
16905
|
createMatcherBlock,
|
|
16051
16906
|
createMatcherItem,
|
|
@@ -16058,7 +16913,18 @@ export {
|
|
|
16058
16913
|
createSpace,
|
|
16059
16914
|
createStringInput,
|
|
16060
16915
|
createTextBlock,
|
|
16916
|
+
createVerticalOperationBlock,
|
|
16061
16917
|
createVisualFraction,
|
|
16918
|
+
createVopBoldCell,
|
|
16919
|
+
createVopCarryRow,
|
|
16920
|
+
createVopColorCell,
|
|
16921
|
+
createVopDigitCell,
|
|
16922
|
+
createVopEmptyCell,
|
|
16923
|
+
createVopHighlightCell,
|
|
16924
|
+
createVopInputCell,
|
|
16925
|
+
createVopLabelCell,
|
|
16926
|
+
createVopRow,
|
|
16927
|
+
createVopSeparator,
|
|
16062
16928
|
evaluate,
|
|
16063
16929
|
extractVariables as extractExpressionVariables,
|
|
16064
16930
|
findValidCombination,
|
|
@@ -16078,6 +16944,8 @@ export {
|
|
|
16078
16944
|
isHighlightedText,
|
|
16079
16945
|
isImage,
|
|
16080
16946
|
isImageItem,
|
|
16947
|
+
isInlineDragOption,
|
|
16948
|
+
isInlineDragOptionJSON,
|
|
16081
16949
|
isInlineInput,
|
|
16082
16950
|
isInlineInputJSON,
|
|
16083
16951
|
isInlineNode,
|
|
@@ -16106,8 +16974,15 @@ export {
|
|
|
16106
16974
|
isToolNode,
|
|
16107
16975
|
isValidExpression,
|
|
16108
16976
|
isValidationNode,
|
|
16977
|
+
isVerticalOperationBlock,
|
|
16978
|
+
isVerticalOperationItem,
|
|
16109
16979
|
isVisualFractionInline,
|
|
16980
|
+
isVopCarryRow,
|
|
16981
|
+
isVopCell,
|
|
16982
|
+
isVopRow,
|
|
16983
|
+
isVopSeparator,
|
|
16110
16984
|
isWidgetRef,
|
|
16985
|
+
normalizeVopOperator,
|
|
16111
16986
|
parse,
|
|
16112
16987
|
parseFractionKey,
|
|
16113
16988
|
parseSafe,
|