soroban-abacus-flashcards/packages/abacus-react/ENHANCEMENT_PLAN.md

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 styles
  • HeroAbacus.tsx (lines 20-32) - structural styles
  • LevelSliderDisplay.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 transforms
  • MyAbacus.tsx (lines 214-218) - responsive scale values
  • LevelSliderDisplay.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 states
  • calculateBeadChanges() - diff algorithm
  • ValidPlaceValues type - 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

  1. Add frameVisible={false} prop
  2. Add compact prop/variant
  3. Export theme presets (ABACUS_THEMES constant)

Phase 2 (Short-term) COMPLETED

  1. ⏸️ Enhanced scaleFactor with responsive object support (DEFERRED - too complex, low priority)
  2. Export utility functions (numberToAbacusState, calculateBeadDiff, etc.)

Phase 3 (Medium-term) COMPLETED

  1. Add useAbacusDiff hook
  2. Add native column highlighting with highlightColumns and columnLabels props

Phase 4 (Long-term - Future)

  1. 📋 Consider tutorial context provider (needs more research)
  2. 📋 Animation speed controls

Completed Features Summary

New Props

  • frameVisible?: boolean - Show/hide column posts and reckoning bar
  • compact?: boolean - Compact layout for inline display (implies frameVisible=false)
  • highlightColumns?: number[] - Highlight specific columns by index
  • columnLabels?: string[] - Optional labels for columns

New Exports

  • ABACUS_THEMES - Pre-defined theme presets (light, dark, trophy, translucent, solid, traditional)
  • AbacusThemeName type - TypeScript type for theme names

New Utility Functions

  • numberToAbacusState(value, maxPlaces) - Convert number to bead positions
  • abacusStateToNumber(state) - Convert bead positions to number
  • calculateBeadChanges(startState, targetState) - Calculate bead differences
  • calculateBeadDiff(fromState, toState) - Full diff with order and directions
  • calculateBeadDiffFromValues(from, to, maxPlaces) - Convenience wrapper
  • validateAbacusValue(value, maxPlaces) - Validate number ranges
  • areStatesEqual(state1, state2) - Compare states

New Hooks

  • useAbacusDiff(fromValue, toValue, maxPlaces) - Calculate bead differences for tutorials
  • useAbacusState(value, maxPlaces) - Convert number to abacus state (memoized)

New Types

  • BeadState - Bead state in a single column
  • AbacusState - Complete abacus state
  • BeadDiffResult - Single bead movement result
  • BeadDiffOutput - Complete diff output
  • PlaceValueBasedBead - 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

  1. Should we split these into separate packages (e.g., @soroban/abacus-tutorial)?
  2. Which theme presets should be included by default?
  3. Should responsive scaling use CSS media queries or JS breakpoints?
  4. How much tutorial logic belongs in the core library vs. app code?