diff --git a/packages/abacus-react/ENHANCEMENT_PLAN.md b/packages/abacus-react/ENHANCEMENT_PLAN.md new file mode 100644 index 00000000..ea3a1a47 --- /dev/null +++ b/packages/abacus-react/ENHANCEMENT_PLAN.md @@ -0,0 +1,402 @@ +# 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**: +```tsx + +``` + +**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: + +```tsx +// Native API proposal + + +// Or more granular: + +``` + +**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: + +```tsx +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: + +```tsx +// Native API proposal + + +// Or expose theme constants +import { ABACUS_THEMES } from '@soroban/abacus-react' + +``` + +**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: + +```tsx +
+ +
+``` + +**Problem**: Manual transform handling requires extra DOM nesting, breaks click boundaries, and makes centering complex. + +**Proposed Solution**: Enhanced `scaleFactor` with responsive breakpoints: + +```tsx +// Native API proposal + +``` + +**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: + +```tsx +// 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: + +```tsx +// Native API proposal +import { useAbacusDiff } from '@soroban/abacus-react' + +function Tutorial() { + const diff = useAbacusDiff(startValue, targetValue) + + return ( + + ) +} +``` + +**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: + +```tsx +// Native API proposal +import { AbacusReact, AbacusTutorial } from '@soroban/abacus-react' + + { /* analytics */ }} + onComplete={() => { /* celebration */ }} +> + + +``` + +**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: + +```tsx +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: + +```tsx +// Native API proposal + +``` + +**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: + +```tsx +// Possible improvement +const abacusRef = useAbacusRef() + + + +// 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: + +```tsx +// Native API proposal + +``` + +--- + +## 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: + +```tsx +// 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 +4. ⏸️ Enhanced `scaleFactor` with responsive object support (DEFERRED - too complex, low priority) +5. ✅ Export utility functions (numberToAbacusState, calculateBeadDiff, etc.) + +### Phase 3 (Medium-term) ✅ COMPLETED +6. ✅ Add `useAbacusDiff` hook +7. ✅ Add native column highlighting with `highlightColumns` and `columnLabels` props + +### Phase 4 (Long-term - Future) +8. 📋 Consider tutorial context provider (needs more research) +9. 📋 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? diff --git a/packages/abacus-react/INTEGRATION_SUMMARY.md b/packages/abacus-react/INTEGRATION_SUMMARY.md new file mode 100644 index 00000000..c31cd1f4 --- /dev/null +++ b/packages/abacus-react/INTEGRATION_SUMMARY.md @@ -0,0 +1,162 @@ +# Integration Summary + +## ✅ Completed: Apps/Web Integration with Abacus-React Enhancements + +### Features Implemented & Integrated + +#### 1. **Theme Presets (ABACUS_THEMES)** +**Status:** ✅ Fully integrated + +**Files Updated:** +- `apps/web/src/components/MyAbacus.tsx` - Now uses `ABACUS_THEMES.light` and `ABACUS_THEMES.trophy` +- `apps/web/src/components/HeroAbacus.tsx` - Now uses `ABACUS_THEMES.light` +- `apps/web/src/components/LevelSliderDisplay.tsx` - Now uses `ABACUS_THEMES.dark` + +**Code Eliminated:** ~60 lines of duplicate theme style definitions + +--- + +#### 2. **Compact Prop** +**Status:** ✅ Fully integrated + +**Files Updated:** +- `apps/web/src/app/arcade/complement-race/components/AbacusTarget.tsx` - Now uses `compact={true}` + +**Before:** +```tsx + +``` + +**After:** +```tsx + +``` + +--- + +#### 3. **Utility Functions** +**Status:** ✅ Fully integrated + +**Files Updated:** +- `apps/web/src/utils/beadDiff.ts` - Now re-exports from abacus-react +- `apps/web/src/utils/abacusInstructionGenerator.ts` - Now re-exports from abacus-react +- `apps/web/src/components/tutorial/TutorialPlayer.tsx` - Imports `calculateBeadDiffFromValues` from abacus-react +- `apps/web/src/components/tutorial/TutorialEditor.tsx` - Imports `calculateBeadDiffFromValues` from abacus-react + +**Exports from abacus-react:** +- `numberToAbacusState()` +- `abacusStateToNumber()` +- `calculateBeadChanges()` +- `calculateBeadDiff()` +- `calculateBeadDiffFromValues()` +- `validateAbacusValue()` +- `areStatesEqual()` + +**Code Eliminated:** ~200+ lines of duplicate utility implementations + +--- + +#### 4. **React Hooks** +**Status:** ✅ Exported and ready to use + +**Available Hooks:** +- `useAbacusDiff(fromValue, toValue, maxPlaces)` - Memoized bead diff calculation +- `useAbacusState(value, maxPlaces)` - Memoized state conversion + +**Not yet used in app** (available for future tutorials) + +--- + +#### 5. **Column Highlighting** +**Status:** ✅ Implemented, not yet used + +**New Props:** +- `highlightColumns?: number[]` - Highlight specific columns +- `columnLabels?: string[]` - Add educational labels above columns + +**Usage Example:** +```tsx + +``` + +--- + +### Code Deduplication Summary + +**Total Lines Eliminated:** ~260-300 lines + +**Breakdown:** +- Theme style definitions: ~60 lines +- Utility function implementations: ~200 lines +- Custom styles for inline abacus: ~5-10 lines per usage + +--- + +### Remaining Work (Optional Future Enhancements) + +1. Use `highlightColumns` and `columnLabels` in tutorial components +2. Replace manual bead diff calculations with `useAbacusDiff` hook in interactive tutorials +3. Use `useAbacusState` for state inspection in debugging/development tools +4. Consider implementing `frameVisible` toggles in settings pages + +--- + +### Files Modified + +**packages/abacus-react:** +- `src/AbacusReact.tsx` - Added new props (frameVisible, compact, highlightColumns, columnLabels) +- `src/AbacusThemes.ts` - **NEW FILE** - 6 theme presets +- `src/AbacusUtils.ts` - **NEW FILE** - Core utility functions +- `src/AbacusHooks.ts` - **NEW FILE** - React hooks +- `src/index.ts` - Updated exports +- `src/AbacusReact.themes-and-utilities.stories.tsx` - **NEW FILE** - Storybook demos +- `README.md` - Updated with new features documentation +- `ENHANCEMENT_PLAN.md` - Updated with completion status + +**apps/web:** +- `src/components/MyAbacus.tsx` - Using ABACUS_THEMES +- `src/components/HeroAbacus.tsx` - Using ABACUS_THEMES +- `src/components/LevelSliderDisplay.tsx` - Using ABACUS_THEMES +- `src/app/arcade/complement-race/components/AbacusTarget.tsx` - Using compact prop +- `src/components/tutorial/TutorialPlayer.tsx` - Importing from abacus-react +- `src/components/tutorial/TutorialEditor.tsx` - Importing from abacus-react +- `src/utils/beadDiff.ts` - Re-exports from abacus-react +- `src/utils/abacusInstructionGenerator.ts` - Re-exports from abacus-react + +--- + +### Testing + +✅ Build successful for packages/abacus-react +✅ TypeScript compilation passes for integrated files +✅ Runtime tests confirm functions work correctly +✅ Storybook stories demonstrate all new features + +--- + +### Next Steps + +1. Monitor app for any runtime issues with the new integrations +2. Consider using hooks in future tutorial implementations +3. Explore using column highlighting in educational content +4. Document best practices for theme usage in the app diff --git a/packages/abacus-react/README.md b/packages/abacus-react/README.md index 57c28c51..50bda30c 100644 --- a/packages/abacus-react/README.md +++ b/packages/abacus-react/README.md @@ -86,34 +86,98 @@ Personalized colors and highlights /> ``` +### Theme Presets + +Use pre-defined themes for quick styling: + +```tsx +import { AbacusReact, ABACUS_THEMES } from '@soroban/abacus-react'; + +// Available themes: 'light', 'dark', 'trophy', 'translucent', 'solid', 'traditional' + + + + + +``` + +**Available Themes:** +- `light` - Solid white frame with subtle gray accents (best for light backgrounds) +- `dark` - Translucent white with subtle glow (best for dark backgrounds) +- `trophy` - Golden frame with warm tones (best for achievements/rewards) +- `translucent` - Nearly invisible frame (best for inline/minimal UI) +- `solid` - Black frame (best for high contrast/educational contexts) +- `traditional` - Brown wooden appearance (best for traditional soroban aesthetic) + +### Compact/Inline Display + +Create mini abacus displays for inline use: + +```tsx +// Compact mode - automatically hides frame and optimizes spacing + + +// Or manually control frame visibility + +``` + ### Tutorial System -Educational guidance with tooltips +Educational guidance with tooltips and column highlighting Tutorial System ```tsx Click this bead!, + target: { type: 'bead', columnIndex: 1, beadType: 'earth', beadPosition: 1 }, + content:
Click this bead in the tens column!
, offset: { x: 0, y: -30 } }]} callbacks={{ onBeadClick: (event) => { - if (event.columnIndex === 0 && event.beadType === 'earth' && event.position === 1) { - console.log('Correct!'); + if (event.columnIndex === 1 && event.beadType === 'earth' && event.position === 1) { + console.log('Correct! You clicked the tens column.'); } } }} /> ``` +**Column Highlighting:** +- `highlightColumns` - Array of column indices to highlight (e.g., `[0, 2]` highlights first and third columns) +- `columnLabels` - Optional labels displayed above each column (indexed left to right) + ## 3D Enhancement Make the abacus feel tangible and satisfying with three progressive levels of 3D effects. @@ -209,10 +273,18 @@ interface AbacusConfig { colorPalette?: 'default' | 'colorblind' | 'mnemonic' | 'grayscale' | 'nature'; hideInactiveBeads?: boolean; // Hide/show inactive beads + // Layout & Frame + frameVisible?: boolean; // Show/hide column posts and reckoning bar + compact?: boolean; // Compact layout (implies frameVisible=false) + // Interaction interactive?: boolean; // Enable user interactions animated?: boolean; // Enable animations gestures?: boolean; // Enable drag gestures + + // Tutorial Features + highlightColumns?: number[]; // Highlight specific columns by index + columnLabels?: string[]; // Optional labels for columns } ``` @@ -359,6 +431,60 @@ function AdvancedExample() { ## Hooks +### useAbacusDiff + +Calculate bead differences between values for tutorials and animations: + +```tsx +import { useAbacusDiff } from '@soroban/abacus-react'; + +function Tutorial() { + const [currentValue, setCurrentValue] = useState(5); + const targetValue = 15; + + // Get diff information: which beads need to move + const diff = useAbacusDiff(currentValue, targetValue); + + return ( +
+

{diff.summary}

{/* "add heaven bead in tens column, then..." */} + +

Changes needed: {diff.changes.length}

+
+ ); +} +``` + +**Returns:** +- `changes` - Array of bead movements with direction and order +- `highlights` - Bead highlight data for stepBeadHighlights prop +- `hasChanges` - Boolean indicating if any changes needed +- `summary` - Human-readable description of changes (e.g., "add heaven bead in ones column") + +### useAbacusState + +Convert numbers to abacus bead states: + +```tsx +import { useAbacusState } from '@soroban/abacus-react'; + +function BeadAnalyzer() { + const value = 123; + const state = useAbacusState(value); + + // Check bead positions + const onesHasHeaven = state[0].heavenActive; // false (3 < 5) + const tensEarthCount = state[1].earthActive; // 2 (20 = 2 tens) + + return
Ones column heaven bead: {onesHasHeaven ? 'active' : 'inactive'}
; +} +``` + ### useAbacusDimensions Get exact sizing information for layout planning: @@ -377,6 +503,92 @@ function MyComponent() { } ``` +## Utility Functions + +Low-level functions for working with abacus states and calculations: + +### numberToAbacusState + +Convert a number to bead positions: + +```tsx +import { numberToAbacusState } from '@soroban/abacus-react'; + +const state = numberToAbacusState(123, 5); // 5 columns +// Returns: { +// 0: { heavenActive: false, earthActive: 3 }, // ones = 3 +// 1: { heavenActive: false, earthActive: 2 }, // tens = 2 +// 2: { heavenActive: true, earthActive: 0 }, // hundreds = 1 +// ... +// } +``` + +### abacusStateToNumber + +Convert bead positions back to a number: + +```tsx +import { abacusStateToNumber } from '@soroban/abacus-react'; + +const state = { + 0: { heavenActive: false, earthActive: 3 }, + 1: { heavenActive: false, earthActive: 2 }, + 2: { heavenActive: true, earthActive: 0 } +}; + +const value = abacusStateToNumber(state); // 123 +``` + +### calculateBeadDiff + +Calculate the exact bead movements needed between two states: + +```tsx +import { calculateBeadDiff, numberToAbacusState } from '@soroban/abacus-react'; + +const fromState = numberToAbacusState(5); +const toState = numberToAbacusState(15); +const diff = calculateBeadDiff(fromState, toState); + +console.log(diff.summary); // "add heaven bead in tens column" +console.log(diff.changes); // Detailed array of movements with order +``` + +### calculateBeadDiffFromValues + +Convenience wrapper for calculating diff from numbers: + +```tsx +import { calculateBeadDiffFromValues } from '@soroban/abacus-react'; + +const diff = calculateBeadDiffFromValues(42, 57); +// Equivalent to: calculateBeadDiff(numberToAbacusState(42), numberToAbacusState(57)) +``` + +### validateAbacusValue + +Check if a value is within the supported range: + +```tsx +import { validateAbacusValue } from '@soroban/abacus-react'; + +const result = validateAbacusValue(123456, 5); // 5 columns max +console.log(result.isValid); // false +console.log(result.error); // "Value exceeds maximum for 5 columns (max: 99999)" +``` + +### areStatesEqual + +Compare two abacus states: + +```tsx +import { areStatesEqual, numberToAbacusState } from '@soroban/abacus-react'; + +const state1 = numberToAbacusState(123); +const state2 = numberToAbacusState(123); +const isEqual = areStatesEqual(state1, state2); // true +``` + ## Educational Use Cases ### Interactive Math Lessons @@ -439,14 +651,37 @@ Full TypeScript definitions included: ```tsx import { + // Components AbacusReact, + + // Hooks + useAbacusDiff, + useAbacusState, + useAbacusDimensions, + + // Utility Functions + numberToAbacusState, + abacusStateToNumber, + calculateBeadDiff, + calculateBeadDiffFromValues, + validateAbacusValue, + areStatesEqual, + + // Theme Presets + ABACUS_THEMES, + + // Types AbacusConfig, BeadConfig, BeadClickEvent, AbacusCustomStyles, AbacusOverlay, AbacusCallbacks, - useAbacusDimensions + AbacusState, + BeadState, + BeadDiffResult, + BeadDiffOutput, + AbacusThemeName } from '@soroban/abacus-react'; // All interfaces fully typed for excellent developer experience