12 KiB
Abacus-React Feature Enhancement Plan
Executive Summary
The web application has developed numerous custom patterns and workarounds for styling, layout, and interactions with the abacus component. These patterns reveal gaps in the abacus-react API that, if addressed, would significantly improve developer experience and reduce code duplication across the application.
Priority 1: Critical Features (High Impact, High Frequency)
1. Inline "Mini Abacus" Component
Location: apps/web/src/app/arcade/complement-race/components/AbacusTarget.tsx
Current Implementation:
<AbacusReact
value={number}
columns={1}
interactive={false}
showNumbers={false}
hideInactiveBeads={true}
scaleFactor={0.72}
customStyles={{
columnPosts: { opacity: 0 },
}}
/>
Problem: Creating an inline mini-abacus for displaying single digits requires multiple props and style overrides. This pattern appears throughout game UIs.
Proposed Solution: Add a variant prop with preset configurations:
// Native API proposal
<AbacusReact
value={7}
variant="inline-digit"
// Automatically sets: columns=1, hideInactiveBeads, transparent frame, optimal scaleFactor
/>
// Or more granular:
<AbacusReact
value={7}
compact={true} // Removes frame, optimizes spacing
frameVisible={false} // Hide posts and bar
/>
Benefits:
- Single prop instead of 5+
- Consistent inline abacus appearance across the app
- Better semantic intent
2. Theme-Aware Styling Presets
Locations:
MyAbacus.tsx(lines 60-85) - structural & trophy stylesHeroAbacus.tsx(lines 20-32) - structural stylesLevelSliderDisplay.tsx(lines 263-275) - dark theme styles
Current Pattern: Every component defines custom style objects for structural elements:
const structuralStyles = {
columnPosts: {
fill: "rgb(255, 255, 255)",
stroke: "rgb(200, 200, 200)",
strokeWidth: 2,
},
reckoningBar: {
fill: "rgb(255, 255, 255)",
stroke: "rgb(200, 200, 200)",
strokeWidth: 3,
},
};
Problem: Manual style object creation for common themes is repetitive and error-prone.
Proposed Solution: Add theme presets to abacus-react:
// Native API proposal
<AbacusReact
value={123}
theme="dark" // or "light", "translucent", "solid", "trophy"
/>;
// Or expose theme constants
import { ABACUS_THEMES } from "@soroban/abacus-react";
<AbacusReact customStyles={ABACUS_THEMES.dark} />;
Benefits:
- Eliminates ~30 lines of style definitions per component
- Ensures visual consistency
- Makes theme switching trivial
3. Scaling Containers & Responsive Layouts
Locations:
HeroAbacus.tsx(lines 133-138) - manual scale transformsMyAbacus.tsx(lines 214-218) - responsive scale valuesLevelSliderDisplay.tsx(lines 370-379) - dynamic scale calculation
Current Pattern: Components manually wrap abacus in transform containers:
<div
style={{
transform: "scale(3.5)",
transformOrigin: "center center",
}}
>
<AbacusReact value={1234} columns={4} />
</div>
Problem: Manual transform handling requires extra DOM nesting, breaks click boundaries, and makes centering complex.
Proposed Solution: Enhanced scaleFactor with responsive breakpoints:
// Native API proposal
<AbacusReact
value={1234}
scaleFactor={{ base: 2.5, md: 3.5, lg: 4.5 }} // Responsive
scaleOrigin="center" // Handle transform origin
scaleContainer={true} // Apply correct boundaries for interaction
/>
Benefits:
- Eliminates wrapper divs
- Proper click/hover boundaries
- Built-in responsive scaling
Priority 2: Developer Experience Improvements
4. Bead Diff Calculation System
Location: apps/web/src/utils/beadDiff.ts (285 lines) + abacusInstructionGenerator.ts (400+ lines)
Current Implementation: Complex utilities to calculate which beads need to move between states:
// Current external pattern
import { calculateBeadDiffFromValues } from "@/utils/beadDiff";
const diff = calculateBeadDiffFromValues(fromValue, toValue);
const stepBeadHighlights = diff.changes.map((change) => ({
placeValue: change.placeValue,
beadType: change.beadType,
direction: change.direction,
// ...
}));
Problem: Tutorial/game developers need to calculate bead movements manually. This core logic belongs in abacus-react.
Proposed Solution: Add a diff calculation hook:
// Native API proposal
import { useAbacusDiff } from "@soroban/abacus-react";
function Tutorial() {
const diff = useAbacusDiff(startValue, targetValue);
return (
<AbacusReact
value={currentValue}
stepBeadHighlights={diff.highlights} // Generated by hook
// diff also includes: instructions, order, validation
/>
);
}
Benefits:
- Centralizes "diff" algorithm
- Eliminates ~500 lines of application code
- Better tested and maintained
5. Tutorial/Step Context Provider
Location: apps/web/src/components/tutorial/TutorialContext.tsx
Current Pattern: Apps need to implement complex state management for multi-step tutorial flows with reducer patterns, event tracking, and error handling.
Problem: Tutorial infrastructure is duplicated across components. The logic for tracking progress through abacus instruction steps is tightly coupled to application code.
Proposed Solution: Add optional tutorial/stepper context to abacus-react:
// Native API proposal
import { AbacusReact, AbacusTutorial } from "@soroban/abacus-react";
<AbacusTutorial
steps={[
{ from: 0, to: 5, instruction: "Add 5" },
{ from: 5, to: 15, instruction: "Add 10" },
]}
onStepComplete={(step) => {
/* analytics */
}}
onComplete={() => {
/* celebration */
}}
>
<AbacusReact />
</AbacusTutorial>;
Benefits:
- Reusable tutorial infrastructure
- Built-in progress tracking and validation
- Could power educational features across projects
Priority 3: Nice-to-Have Enhancements
6. Animation Speed Configuration
Location: LevelSliderDisplay.tsx (lines 306-345)
Current Pattern: Applications control animation speed by rapidly changing the value prop:
const intervalMs = 500 - danProgress * 490; // 500ms down to 10ms
setInterval(() => {
setAnimatedDigits((prev) => {
// Rapidly change digits to simulate calculation
});
}, intervalMs);
Problem: "Rapid calculation" animation requires external interval management.
Proposed Solution: Add animation speed prop:
// Native API proposal
<AbacusReact
value={calculatingValue}
animationSpeed="fast" // or "normal", "slow", or ms number
autoAnimate={true} // Animate value prop changes automatically
/>
Benefits:
- Smoother animations with internal management
- Consistent timing across the app
7. Draggable/Positionable Abacus Cards
Location: InteractiveFlashcards.tsx
Current Pattern: Complex drag-and-drop implementation wrapped around each AbacusReact instance with pointer capture, offset tracking, and rotation.
Problem: Making abacus instances draggable requires significant boilerplate.
Proposed Solution: This is probably too specific to remain external. However, a ref-based API to get bounding boxes would help:
// Possible improvement
const abacusRef = useAbacusRef()
<AbacusReact ref={abacusRef} />
// abacusRef.current.getBoundingBox() for drag calculations
8. Column Highlighting for Multi-Step Problems
Location: Tutorial system extensively
Current Pattern: Manual column highlighting based on place values with custom overlay positioning logic.
Problem: Highlighting specific columns (e.g., "the tens column") requires external overlay management.
Proposed Solution: Add native column highlighting:
// Native API proposal
<AbacusReact
value={123}
highlightColumns={[1]} // Highlight tens column
columnLabels={["ones", "tens", "hundreds"]} // Optional labels
/>
Priority 4: Documentation & Exports
9. Utility Functions & Types
Current State: Apps re-implement utilities for working with abacus states:
numberToAbacusState()- convert numbers to bead statescalculateBeadChanges()- diff algorithmValidPlaceValuestype - imported but limited
Proposed Solution: Export more utilities from abacus-react:
// Expanded exports
export {
// Utilities
numberToAbacusState,
abacusStateToNumber,
calculateBeadDiff,
validateAbacusValue,
// Types
AbacusState,
BeadState,
PlaceValue,
// Hooks
useAbacusDiff,
useAbacusValidation,
useAbacusState,
};
Implementation Roadmap
Phase 1 (Immediate) ✅ COMPLETED
- ✅ Add
frameVisible={false}prop - ✅ Add
compactprop/variant - ✅ Export theme presets (ABACUS_THEMES constant)
Phase 2 (Short-term) ✅ COMPLETED
- ⏸️ Enhanced
scaleFactorwith responsive object support (DEFERRED - too complex, low priority) - ✅ Export utility functions (numberToAbacusState, calculateBeadDiff, etc.)
Phase 3 (Medium-term) ✅ COMPLETED
- ✅ Add
useAbacusDiffhook - ✅ Add native column highlighting with
highlightColumnsandcolumnLabelsprops
Phase 4 (Long-term - Future)
- 📋 Consider tutorial context provider (needs more research)
- 📋 Animation speed controls
Completed Features Summary
New Props
frameVisible?: boolean- Show/hide column posts and reckoning barcompact?: boolean- Compact layout for inline display (implies frameVisible=false)highlightColumns?: number[]- Highlight specific columns by indexcolumnLabels?: string[]- Optional labels for columns
New Exports
ABACUS_THEMES- Pre-defined theme presets (light, dark, trophy, translucent, solid, traditional)AbacusThemeNametype - TypeScript type for theme names
New Utility Functions
numberToAbacusState(value, maxPlaces)- Convert number to bead positionsabacusStateToNumber(state)- Convert bead positions to numbercalculateBeadChanges(startState, targetState)- Calculate bead differencescalculateBeadDiff(fromState, toState)- Full diff with order and directionscalculateBeadDiffFromValues(from, to, maxPlaces)- Convenience wrappervalidateAbacusValue(value, maxPlaces)- Validate number rangesareStatesEqual(state1, state2)- Compare states
New Hooks
useAbacusDiff(fromValue, toValue, maxPlaces)- Calculate bead differences for tutorialsuseAbacusState(value, maxPlaces)- Convert number to abacus state (memoized)
New Types
BeadState- Bead state in a single columnAbacusState- Complete abacus stateBeadDiffResult- Single bead movement resultBeadDiffOutput- Complete diff outputPlaceValueBasedBead- Internal place-value based bead type
Metrics & Impact
Code Reduction Estimate:
- Eliminates ~800-1000 lines of repetitive application code
- Reduces component complexity by ~40% for tutorial/game components
Developer Experience:
- Faster onboarding for new features using abacus
- More consistent UX across application
- Better TypeScript support and autocomplete
Maintenance:
- Centralized logic easier to test and debug
- Single source of truth for abacus behavior
- Easier to add new features (e.g., sound effects for different themes)
Questions for Discussion
- Should we split these into separate packages (e.g.,
@soroban/abacus-tutorial)? - Which theme presets should be included by default?
- Should responsive scaling use CSS media queries or JS breakpoints?
- How much tutorial logic belongs in the core library vs. app code?