440 lines
12 KiB
Markdown
440 lines
12 KiB
Markdown
# 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
|
|
<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:
|
|
|
|
```tsx
|
|
// 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:
|
|
|
|
```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
|
|
<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:
|
|
|
|
```tsx
|
|
<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:
|
|
|
|
```tsx
|
|
// 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:
|
|
|
|
```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 (
|
|
<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:
|
|
|
|
```tsx
|
|
// 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:
|
|
|
|
```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
|
|
<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:
|
|
|
|
```tsx
|
|
// 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:
|
|
|
|
```tsx
|
|
// 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:
|
|
|
|
```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?
|