@mathscan/math-exercise-engine 1.0.8 → 1.0.9
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 +1269 -286
- package/dist/index.js +603 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +603 -7
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +238 -0
- package/package.json +1 -1
package/dist/style.css
CHANGED
|
@@ -135,6 +135,17 @@
|
|
|
135
135
|
/* When keypad is present, use flex layout for side-by-side */
|
|
136
136
|
display: flex;
|
|
137
137
|
justify-content: space-between;
|
|
138
|
+
align-items: flex-start;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* When inside a keypad layout, the exercise layout must grow to fill
|
|
142
|
+
all available space. Without this, containers whose only content is
|
|
143
|
+
a NUMBER_LINE (all children absolutely-positioned) collapse to 0 width,
|
|
144
|
+
causing the number line to render as a zero-width element. */
|
|
145
|
+
|
|
146
|
+
.exercise-layout-container.has-keypad .exercise-layout {
|
|
147
|
+
flex: 1;
|
|
148
|
+
min-width: 0;
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
/* ========================================
|
|
@@ -545,6 +556,233 @@ td.exercise-table-cell, th.exercise-table-cell {
|
|
|
545
556
|
border: 2px solid #7c6cf2;
|
|
546
557
|
}
|
|
547
558
|
|
|
559
|
+
/**
|
|
560
|
+
* Number Line Styles
|
|
561
|
+
*
|
|
562
|
+
* Layout and visual styles for the number line component.
|
|
563
|
+
* Follows the same token and convention patterns as widgets.css / inputs.css.
|
|
564
|
+
* All sizing, spacing and color values use --exercise-* design tokens.
|
|
565
|
+
*/
|
|
566
|
+
|
|
567
|
+
/* ========================================
|
|
568
|
+
NUMBER LINE — ROOT
|
|
569
|
+
======================================== */
|
|
570
|
+
|
|
571
|
+
.number-line {
|
|
572
|
+
position: relative;
|
|
573
|
+
width: 100%;
|
|
574
|
+
margin-bottom: var(--exercise-margin-bottom);
|
|
575
|
+
user-select: none;
|
|
576
|
+
--nl-chip-zone-height: 3rem;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/* Chip zone: push the axis row down so chips overflowing upward
|
|
580
|
+
stay within the root boundary — no geometry side-effects on the axis */
|
|
581
|
+
|
|
582
|
+
.number-line--drag-mode {
|
|
583
|
+
padding-top: var(--nl-chip-zone-height);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/* ========================================
|
|
587
|
+
ROW 1 — POSITION CONTENT (inputs / labels above axis)
|
|
588
|
+
======================================== */
|
|
589
|
+
|
|
590
|
+
.number-line-content-row {
|
|
591
|
+
position: relative;
|
|
592
|
+
width: 100%;
|
|
593
|
+
min-height: 2.8rem;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.number-line-position-content {
|
|
597
|
+
position: absolute;
|
|
598
|
+
bottom: 4px;
|
|
599
|
+
transform: translateX(-50%);
|
|
600
|
+
display: flex;
|
|
601
|
+
align-items: center;
|
|
602
|
+
justify-content: center;
|
|
603
|
+
z-index: 1;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/* ========================================
|
|
607
|
+
ROW 2 — AXIS (track + ticks + arrows + dots)
|
|
608
|
+
======================================== */
|
|
609
|
+
|
|
610
|
+
.number-line-axis-row {
|
|
611
|
+
position: relative;
|
|
612
|
+
width: 100%;
|
|
613
|
+
height: 2rem;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/* In drag mode, chip zone is handled by .number-line--drag-mode padding-top on root */
|
|
617
|
+
|
|
618
|
+
/* Horizontal track line — spans [--nl-side-pad, 100% - --nl-side-pad] */
|
|
619
|
+
|
|
620
|
+
.number-line-track {
|
|
621
|
+
position: absolute;
|
|
622
|
+
left: var(--nl-side-pad, 7%);
|
|
623
|
+
right: var(--nl-side-pad, 7%);
|
|
624
|
+
top: 50%;
|
|
625
|
+
height: 2px;
|
|
626
|
+
background-color: var(--exercise-text, #333);
|
|
627
|
+
transform: translateY(-50%);
|
|
628
|
+
/* transition on base state so both enter and exit are smooth */
|
|
629
|
+
transition: all 0.2s ease;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/* ── Arrowheads (CSS border-triangle technique) ── */
|
|
633
|
+
|
|
634
|
+
.number-line-arrow {
|
|
635
|
+
position: absolute;
|
|
636
|
+
top: 50%;
|
|
637
|
+
width: 0;
|
|
638
|
+
height: 0;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/* Right-pointing arrow — base sits at the right end of the track */
|
|
642
|
+
|
|
643
|
+
.number-line-arrow-right {
|
|
644
|
+
left: calc(100% - var(--nl-side-pad, 7%));
|
|
645
|
+
transform: translateY(-50%);
|
|
646
|
+
border-top: 5px solid transparent;
|
|
647
|
+
border-bottom: 5px solid transparent;
|
|
648
|
+
border-left: 8px solid var(--exercise-text, #333);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/* Left-pointing arrow — tip extends just past the left end of the track */
|
|
652
|
+
|
|
653
|
+
.number-line-arrow-left {
|
|
654
|
+
left: var(--nl-side-pad, 7%);
|
|
655
|
+
transform: translate(-100%, -50%);
|
|
656
|
+
border-top: 5px solid transparent;
|
|
657
|
+
border-bottom: 5px solid transparent;
|
|
658
|
+
border-right: 8px solid var(--exercise-text, #333);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/* ── Major ticks ── */
|
|
662
|
+
|
|
663
|
+
.number-line-major-tick {
|
|
664
|
+
position: absolute;
|
|
665
|
+
top: 50%;
|
|
666
|
+
transform: translate(-50%, -50%);
|
|
667
|
+
width: 2px;
|
|
668
|
+
height: 14px;
|
|
669
|
+
background-color: var(--exercise-text, #333);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/* ── Minor ticks ── */
|
|
673
|
+
|
|
674
|
+
.number-line-minor-tick {
|
|
675
|
+
position: absolute;
|
|
676
|
+
top: 50%;
|
|
677
|
+
transform: translate(-50%, -50%);
|
|
678
|
+
width: 1px;
|
|
679
|
+
height: 8px;
|
|
680
|
+
background-color: var(--exercise-text-light, #555);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/* ── Position marker dots ── */
|
|
684
|
+
|
|
685
|
+
.number-line-position-dot {
|
|
686
|
+
position: absolute;
|
|
687
|
+
top: 50%;
|
|
688
|
+
transform: translate(-50%, -50%);
|
|
689
|
+
width: 16px;
|
|
690
|
+
height: 16px;
|
|
691
|
+
border-radius: 50%;
|
|
692
|
+
background-color: var(--exercise-primary, #007acc);
|
|
693
|
+
/** Shadow: same 0.2 opacity convention as .drag-option.selected */
|
|
694
|
+
box-shadow: 0 2px 4px rgba(0, 122, 204, 0.2);
|
|
695
|
+
z-index: 2;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/* ========================================
|
|
699
|
+
PLACED CHIP (free-drag mode)
|
|
700
|
+
======================================== */
|
|
701
|
+
|
|
702
|
+
/*
|
|
703
|
+
* After the student drops a chip on the axis, a labelled button appears
|
|
704
|
+
* above the snapped position. Clicking it returns the chip to the bank.
|
|
705
|
+
*
|
|
706
|
+
* Structure:
|
|
707
|
+
* .number-line-placed-chip — absolute wrapper, positioned by left%
|
|
708
|
+
* .number-line-placed-chip-btn — the visible chip button
|
|
709
|
+
* ::after pseudo — thin connector line down to the axis
|
|
710
|
+
*/
|
|
711
|
+
|
|
712
|
+
.number-line-placed-chip {
|
|
713
|
+
position: absolute;
|
|
714
|
+
bottom: calc(50% + 2px);
|
|
715
|
+
left: 0;
|
|
716
|
+
transform: translateX(-50%);
|
|
717
|
+
z-index: 3;
|
|
718
|
+
display: flex;
|
|
719
|
+
flex-direction: column;
|
|
720
|
+
align-items: center;
|
|
721
|
+
pointer-events: none;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/* Thin connector between chip and axis */
|
|
725
|
+
|
|
726
|
+
.number-line-placed-chip::after {
|
|
727
|
+
content: '';
|
|
728
|
+
display: block;
|
|
729
|
+
width: 2px;
|
|
730
|
+
height: 12px;
|
|
731
|
+
background-color: var(--exercise-primary, #007acc);
|
|
732
|
+
margin-top: 2px;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
.number-line-placed-chip-btn {
|
|
736
|
+
pointer-events: all;
|
|
737
|
+
background-color: var(--exercise-primary, #007acc);
|
|
738
|
+
color: white;
|
|
739
|
+
/** White inner border uses border-width token for consistency */
|
|
740
|
+
border: var(--exercise-border-width) solid white;
|
|
741
|
+
border-radius: var(--exercise-border-radius, 0.25rem);
|
|
742
|
+
padding: var(--exercise-input-padding, 0.375rem 0.625rem);
|
|
743
|
+
font-size: var(--exercise-font-size, 0.875rem);
|
|
744
|
+
font-weight: var(--exercise-font-weight-bold, 600);
|
|
745
|
+
cursor: pointer;
|
|
746
|
+
/** Shadow: same convention as .drag-option.selected and .radio-option.selected */
|
|
747
|
+
box-shadow: 0 2px 4px rgba(0, 122, 204, 0.2);
|
|
748
|
+
transition: all 0.2s ease;
|
|
749
|
+
white-space: nowrap;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.number-line-placed-chip-btn:hover {
|
|
753
|
+
background-color: var(--exercise-danger, #ef4444);
|
|
754
|
+
/** Scale down like other interactive elements (consistent with .drag-option:active) */
|
|
755
|
+
transform: scale(0.98);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/* Axis highlight while a chip is hovering over it */
|
|
759
|
+
|
|
760
|
+
.nl-axis-drag-over .number-line-track {
|
|
761
|
+
background-color: var(--exercise-primary, #007acc);
|
|
762
|
+
/** Shadow: same 0.2 opacity convention */
|
|
763
|
+
box-shadow: 0 0 0 3px rgba(0, 122, 204, 0.2);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/* ========================================
|
|
767
|
+
ROW 3 — NUMERIC TICK LABELS
|
|
768
|
+
======================================== */
|
|
769
|
+
|
|
770
|
+
.number-line-labels-row {
|
|
771
|
+
position: relative;
|
|
772
|
+
width: 100%;
|
|
773
|
+
height: 1.2rem;
|
|
774
|
+
margin-top: calc(var(--exercise-line-gap) * 0.5);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.number-line-tick-label {
|
|
778
|
+
position: absolute;
|
|
779
|
+
transform: translateX(-50%);
|
|
780
|
+
/** Slightly smaller than base font — same pattern as secondary text in the app */
|
|
781
|
+
font-size: calc(var(--exercise-font-size) * 0.8);
|
|
782
|
+
color: var(--exercise-text-light, #555);
|
|
783
|
+
white-space: nowrap;
|
|
784
|
+
}
|
|
785
|
+
|
|
548
786
|
/**
|
|
549
787
|
* Button & Action Styles
|
|
550
788
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mathscan/math-exercise-engine",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "A DSL parser and validator for math exercises - transforms text-based exercise definitions into structured AST and JSON",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|