@mathscan/math-exercise-engine 1.2.3 → 1.2.5

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.mjs CHANGED
@@ -265,6 +265,10 @@ const HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
265
265
  const isNumberLineHighlightColor = (value) => NUMBER_LINE_HIGHLIGHT_COLORS.includes(value) || HEX_COLOR_REGEX.test(value);
266
266
  const highlightColorMessage = (block) => `${block}: color must be one of ${NUMBER_LINE_HIGHLIGHT_COLORS.join(", ")} or a hex value (e.g. "#3B82F6")`;
267
267
  const NUMBER_LINE_DRAG_MODES = ["axis", "inputs"];
268
+ const DEFAULT_TABLE_BORDER_COLOR = "#7c6cf2";
269
+ const isTableBorderColor = (value) => value.toLowerCase() === "transparent" || HEX_COLOR_REGEX.test(value);
270
+ const TABLE_BORDER_COLOR_ERROR = 'TABLE: bordercolor must be "transparent" or a hex value (e.g. "#E67E22" or "#F00")';
271
+ const TableBorderColorSchema = z.string({ message: TABLE_BORDER_COLOR_ERROR }).refine(isTableBorderColor, { message: TABLE_BORDER_COLOR_ERROR }).default(DEFAULT_TABLE_BORDER_COLOR);
268
272
  function getFractionKey(widgetId, part) {
269
273
  return `${widgetId}__${part}`;
270
274
  }
@@ -2534,12 +2538,14 @@ function createTableRow(cells, backgroundColor) {
2534
2538
  const TableBlockSchema = z.object({
2535
2539
  type: z.literal(AST_TYPES.TABLE_BLOCK),
2536
2540
  cols: z.number().int().positive(),
2541
+ borderColor: TableBorderColorSchema,
2537
2542
  rows: z.array(TableRowSchema).min(1)
2538
2543
  });
2539
- function createTableBlock(cols, rows) {
2544
+ function createTableBlock(cols, rows, borderColor) {
2540
2545
  return TableBlockSchema.parse({
2541
2546
  type: AST_TYPES.TABLE_BLOCK,
2542
2547
  cols,
2548
+ borderColor,
2543
2549
  rows
2544
2550
  });
2545
2551
  }
@@ -3484,8 +3490,10 @@ class NaturalDSLVisitor extends BaseCstVisitor {
3484
3490
  table(ctx) {
3485
3491
  const params = this.visit(ctx.parameterList);
3486
3492
  const cols = params.cols;
3493
+ const borderColorParam = params.bordercolor;
3494
+ const borderColor = borderColorParam === void 0 ? void 0 : String(borderColorParam);
3487
3495
  const rows = ctx.row.map((r) => this.visit(r));
3488
- return createTableBlock(cols, rows);
3496
+ return createTableBlock(cols, rows, borderColor);
3489
3497
  }
3490
3498
  row(ctx) {
3491
3499
  const params = ctx.parameterList ? this.visit(ctx.parameterList) : {};
@@ -4280,6 +4288,7 @@ const TableItemSchema = z.object({
4280
4288
  id: z.string(),
4281
4289
  type: z.literal(JSON_TYPES.TABLE),
4282
4290
  cols: z.number().int().positive(),
4291
+ borderColor: TableBorderColorSchema,
4283
4292
  rows: z.array(TableRowItemSchema)
4284
4293
  });
4285
4294
  const NumberLinePositionItemSchema = z.object({
@@ -10864,6 +10873,7 @@ function transformTable(table, idGenerator, inlineDragIds) {
10864
10873
  id: idGenerator.generate(JSON_TYPES.TABLE),
10865
10874
  type: JSON_TYPES.TABLE,
10866
10875
  cols: table.cols,
10876
+ borderColor: table.borderColor,
10867
10877
  rows
10868
10878
  };
10869
10879
  }
@@ -11906,7 +11916,7 @@ function getEventCoordinates(event) {
11906
11916
  }
11907
11917
  return null;
11908
11918
  }
11909
- const CSS = /* @__PURE__ */ Object.freeze({
11919
+ const CSS$1 = /* @__PURE__ */ Object.freeze({
11910
11920
  Translate: {
11911
11921
  toString(transform) {
11912
11922
  if (!transform) {
@@ -11936,7 +11946,7 @@ const CSS = /* @__PURE__ */ Object.freeze({
11936
11946
  if (!transform) {
11937
11947
  return;
11938
11948
  }
11939
- return [CSS.Translate.toString(transform), CSS.Scale.toString(transform)].join(" ");
11949
+ return [CSS$1.Translate.toString(transform), CSS$1.Scale.toString(transform)].join(" ");
11940
11950
  }
11941
11951
  },
11942
11952
  Transition: {
@@ -15055,7 +15065,7 @@ const PositionedOverlay = /* @__PURE__ */ forwardRef((_ref, ref) => {
15055
15065
  height: rect.height,
15056
15066
  top: rect.top,
15057
15067
  left: rect.left,
15058
- transform: CSS.Transform.toString(scaleAdjustedTransform),
15068
+ transform: CSS$1.Transform.toString(scaleAdjustedTransform),
15059
15069
  transformOrigin: adjustScale2 && activatorEvent ? getRelativeTransformOrigin(activatorEvent, rect) : void 0,
15060
15070
  transition: typeof transition === "function" ? transition(activatorEvent) : transition,
15061
15071
  ...style
@@ -15116,9 +15126,9 @@ const defaultKeyframeResolver = (_ref2) => {
15116
15126
  }
15117
15127
  } = _ref2;
15118
15128
  return [{
15119
- transform: CSS.Transform.toString(initial)
15129
+ transform: CSS$1.Transform.toString(initial)
15120
15130
  }, {
15121
- transform: CSS.Transform.toString(final)
15131
+ transform: CSS$1.Transform.toString(final)
15122
15132
  }];
15123
15133
  };
15124
15134
  const defaultDropAnimationConfiguration = {
@@ -15551,7 +15561,7 @@ const Draggable = ({
15551
15561
  disabled
15552
15562
  });
15553
15563
  const style = {
15554
- transform: CSS.Translate.toString(transform),
15564
+ transform: CSS$1.Translate.toString(transform),
15555
15565
  cursor: disabled ? "default" : "grab",
15556
15566
  touchAction: "none"
15557
15567
  };
@@ -17911,7 +17921,7 @@ const CHART_TYPE_COMPONENTS = {
17911
17921
  const Chart = (props) => {
17912
17922
  const ChartComponent = CHART_TYPE_COMPONENTS[props.item.chartType];
17913
17923
  if (!ChartComponent) {
17914
- return /* @__PURE__ */ jsx("div", { className: "chart chart--unsupported", children: /* @__PURE__ */ jsx("span", { style: { color: "var(--exercise-incorrect, #ef4444)", fontSize: "0.85rem" }, children: `Chart type "${props.item.chartType}" is not yet supported.` }) });
17924
+ return /* @__PURE__ */ jsx("div", { className: "chart chart--unsupported", children: /* @__PURE__ */ jsx("span", { style: { color: "var(--exercise-error, #ef4444)", fontSize: "0.85rem" }, children: `Chart type "${props.item.chartType}" is not yet supported.` }) });
17915
17925
  }
17916
17926
  return /* @__PURE__ */ jsx(ChartComponent, { ...props });
17917
17927
  };
@@ -20625,7 +20635,10 @@ const LayoutRenderer = ({
20625
20635
  )) });
20626
20636
  case JSON_TYPES.TABLE: {
20627
20637
  const totalRows = item.rows.length;
20628
- return /* @__PURE__ */ jsx("div", { className: "exercise-table-container", children: /* @__PURE__ */ jsx("table", { className: "exercise-table", children: /* @__PURE__ */ jsx("tbody", { children: item.rows.map((row, rowIndex) => /* @__PURE__ */ jsx("tr", { className: "exercise-table-row", style: { backgroundColor: row.backgroundColor }, children: row.cells.map((cell) => {
20638
+ const tableStyle = {
20639
+ "--exercise-table-border-color": item.borderColor ?? DEFAULT_TABLE_BORDER_COLOR
20640
+ };
20641
+ return /* @__PURE__ */ jsx("div", { className: "exercise-table-container", style: tableStyle, children: /* @__PURE__ */ jsx("table", { className: "exercise-table", children: /* @__PURE__ */ jsx("tbody", { children: item.rows.map((row, rowIndex) => /* @__PURE__ */ jsx("tr", { className: "exercise-table-row", style: { backgroundColor: row.backgroundColor }, children: row.cells.map((cell) => {
20629
20642
  const rowspan = cell.rowspan || 1;
20630
20643
  const isLastRow = rowIndex + rowspan - 1 >= totalRows - 1;
20631
20644
  return /* @__PURE__ */ jsx(
@@ -20828,6 +20841,15 @@ const TOOL_ASPECT_RATIO = {
20828
20841
  "set-square": SET_SQUARE_ASPECT_RATIO
20829
20842
  };
20830
20843
  const FLIP_BTN_EXTRA = 44;
20844
+ function warnOnInvalidThemeColors(theme) {
20845
+ if (process.env.NODE_ENV === "production" || !theme) return;
20846
+ if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return;
20847
+ for (const [key2, value] of Object.entries(theme)) {
20848
+ if (value && !CSS.supports("color", value)) {
20849
+ console.warn(`[ExerciseRenderer] theme.${key2} is not a valid CSS color: "${value}"`);
20850
+ }
20851
+ }
20852
+ }
20831
20853
  function buildInitialValues(json) {
20832
20854
  const initial = {};
20833
20855
  for (const [id, widget] of Object.entries(json.widgets)) {
@@ -20900,7 +20922,8 @@ const ExerciseRenderer = forwardRef(({
20900
20922
  showSubmitButton = false,
20901
20923
  className = "",
20902
20924
  soundEnabled = true,
20903
- sounds
20925
+ sounds,
20926
+ theme
20904
20927
  }, ref) => {
20905
20928
  const initialValuesSignature = useMemo(() => buildInitialValuesSignature(json), [json]);
20906
20929
  const [values, setValues] = useState(() => buildInitialValues(json));
@@ -20914,6 +20937,15 @@ const ExerciseRenderer = forwardRef(({
20914
20937
  const rootRef = useRef(null);
20915
20938
  const [resetKey, setResetKey] = useState(0);
20916
20939
  const [revealed, setRevealed] = useState(false);
20940
+ const themeStyle = useMemo(() => {
20941
+ const style = {};
20942
+ if (theme?.correct) style["--exercise-correct"] = theme.correct;
20943
+ if (theme?.incorrect) style["--exercise-incorrect"] = theme.incorrect;
20944
+ return Object.keys(style).length > 0 ? style : void 0;
20945
+ }, [theme?.correct, theme?.incorrect]);
20946
+ useEffect(() => {
20947
+ warnOnInvalidThemeColors({ correct: theme?.correct, incorrect: theme?.incorrect });
20948
+ }, [theme?.correct, theme?.incorrect]);
20917
20949
  const playSound = useSoundPlayer(soundEnabled, sounds);
20918
20950
  const externallyLocked = useExerciseLocked();
20919
20951
  useLayoutEffect(() => {
@@ -21170,7 +21202,7 @@ const ExerciseRenderer = forwardRef(({
21170
21202
  if (setSquareWidget) add2("set-square-1", "set-square", Math.round(getSize(setSquareWidget) * 160));
21171
21203
  return { tools: list, toolsMinHeight: maxH };
21172
21204
  }, [rulerWidget, protractorWidget, setSquareWidget]);
21173
- 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}`, children: [
21205
+ 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: [
21174
21206
  /* @__PURE__ */ jsxs(
21175
21207
  "div",
21176
21208
  {
@@ -21332,6 +21364,7 @@ export {
21332
21364
  ColorTextInlineSchema,
21333
21365
  ColorTextSchema,
21334
21366
  ContentBlockSchema,
21367
+ DEFAULT_TABLE_BORDER_COLOR,
21335
21368
  DisplayInlineNodeSchema,
21336
21369
  DragOptionsJSONSchema,
21337
21370
  DragOptionsToolSchema,