📚 Update AbacusReact examples and documentation

Auto-generated fresh examples and balanced documentation from latest component changes.
Includes comprehensive usage patterns, API documentation, and educational examples.

Files updated:
- packages/abacus-react/README.md
- packages/abacus-react/src/AbacusReact.examples.stories.tsx

🤖 Generated with GitHub Actions

Co-Authored-By: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
github-actions[bot] 2025-10-12 03:18:33 +00:00
parent 76163a0846
commit af209fe6ac
2 changed files with 361 additions and 632 deletions

View File

@ -1,26 +1,21 @@
# @soroban/abacus-react
A comprehensive React component for rendering interactive Soroban (Japanese abacus) visualizations with advanced tutorial capabilities, directional gestures, and complete visual customization.
A comprehensive React component for rendering interactive Soroban (Japanese abacus) visualizations with advanced customization and tutorial capabilities.
## Features
- 🎯 **Interactive beads** - Click to toggle or use directional drag gestures
- 🎨 **Complete visual customization** - Style every element individually with granular control
- 📱 **Responsive scaling** - Configurable scale factor for different display sizes
- 🎯 **Interactive beads** - Click to toggle or use directional gestures
- 🎨 **Complete visual customization** - Style every element individually
- 📱 **Responsive scaling** - Configurable scale factor for different sizes
- 🌈 **Multiple color schemes** - Monochrome, place-value, alternating, heaven-earth
- 🎭 **Flexible bead shapes** - Diamond, square, or circle beads
- ⚡ **React Spring animations** - Smooth bead movements and state transitions
- 🔧 **Developer-friendly** - Comprehensive hooks, callbacks, and ref system
- 🎓 **Tutorial system** - Built-in overlay system with tooltips and highlights
- 🧩 **Framework-free SVG** - Complete control over rendering and styling
- 🏗️ **Type-safe APIs** - Full TypeScript support with branded types
- 📐 **Precise positioning** - Place-value based bead targeting system
- 🎮 **Directional gestures** - Natural drag interactions for bead manipulation
- 🎭 **Flexible shapes** - Diamond, square, or circle beads
- ⚡ **React Spring animations** - Smooth bead movements and transitions
- 🔧 **Developer-friendly** - Comprehensive hooks and callback system
- 🎓 **Tutorial system** - Built-in overlay and guidance capabilities
- 🧩 **Framework-free SVG** - Complete control over rendering
## Installation
### From npm (recommended)
```bash
npm install @soroban/abacus-react
# or
@ -29,33 +24,29 @@ pnpm add @soroban/abacus-react
yarn add @soroban/abacus-react
```
### From GitHub Packages
```bash
# Configure npm to use GitHub Packages for @soroban scope
echo "@soroban:registry=https://npm.pkg.github.com" >> .npmrc
# Then install
npm install @soroban/abacus-react
```
The package is published to both npm and GitHub Packages simultaneously for redundancy and choice.
## Quick Start
### Basic Usage
Simple static abacus display:
Simple abacus showing a number
<img src="https://raw.githubusercontent.com/antialias/soroban-abacus-flashcards/main/packages/abacus-react/examples/basic-usage.svg" alt="Basic Usage">
```tsx
import { AbacusReact } from "@soroban/abacus-react";
<AbacusReact value={123} columns={3} showNumbers={true} scaleFactor={1.0} />;
<AbacusReact
value={123}
columns={3}
showNumbers={true}
scaleFactor={1.0}
/>
```
### Interactive Mode
Clickable abacus with animations and callbacks:
Clickable abacus with animations
<img src="https://raw.githubusercontent.com/antialias/soroban-abacus-flashcards/main/packages/abacus-react/examples/interactive.svg" alt="Interactive Mode">
```tsx
<AbacusReact
@ -63,18 +54,19 @@ Clickable abacus with animations and callbacks:
columns={3}
interactive={true}
animated={true}
gestures={true}
showNumbers={true}
callbacks={{
onValueChange: (newValue) => console.log("New value:", newValue),
onBeadClick: (event) => console.log("Bead clicked:", event),
onValueChange: (newValue) => console.log('New value:', newValue),
onBeadClick: (event) => console.log('Bead clicked:', event)
}}
/>
```
### Custom Styling
Personalized colors and visual themes:
Personalized colors and highlights
<img src="https://raw.githubusercontent.com/antialias/soroban-abacus-flashcards/main/packages/abacus-react/examples/custom-styling.svg" alt="Custom Styling">
```tsx
<AbacusReact
@ -82,116 +74,72 @@ Personalized colors and visual themes:
columns={3}
colorScheme="place-value"
beadShape="circle"
colorPalette="nature"
customStyles={{
heavenBeads: { fill: "#2ecc71", stroke: "#27ae60" },
earthBeads: { fill: "#3498db", stroke: "#2980b9" },
numerals: { color: "#2c3e50", fontWeight: "bold" },
reckoningBar: { stroke: "#34495e", strokeWidth: 3 },
heavenBeads: { fill: '#ff6b35' },
earthBeads: { fill: '#3498db' },
numerals: { color: '#2c3e50', fontWeight: 'bold' }
}}
highlightBeads={[
{ placeValue: 2, beadType: "heaven" }, // Hundreds place heaven bead
{ placeValue: 0, beadType: "earth", position: 1 }, // Ones place, second earth bead
{ columnIndex: 1, beadType: 'heaven' }
]}
/>
```
### Tutorial System
Educational guidance with interactive overlays:
Educational guidance with tooltips
<img src="https://raw.githubusercontent.com/antialias/soroban-abacus-flashcards/main/packages/abacus-react/examples/tutorial-mode.svg" alt="Tutorial System">
```tsx
<AbacusReact
value={42}
columns={2}
interactive={true}
overlays={[
{
id: "tutorial-tip",
type: "tooltip",
target: {
type: "bead",
columnIndex: 0,
beadType: "earth",
beadPosition: 1,
},
content: (
<div
style={{
background: "#333",
color: "white",
padding: "8px",
borderRadius: "4px",
fontSize: "14px",
}}
>
Click this bead to add 1!
</div>
),
offset: { x: 0, y: -30 },
},
]}
stepBeadHighlights={[
{
placeValue: 0,
beadType: "earth",
position: 1,
stepIndex: 0,
direction: "activate",
order: 1,
},
]}
showDirectionIndicators={true}
overlays={[{
id: 'tip',
type: 'tooltip',
target: { type: 'bead', columnIndex: 0, beadType: 'earth', beadPosition: 1 },
content: <div>Click this bead!</div>,
offset: { x: 0, y: -30 }
}]}
callbacks={{
onBeadClick: (event) => {
if (
event.placeValue === 0 &&
event.beadType === "earth" &&
event.position === 1
) {
console.log("Tutorial step completed!");
if (event.columnIndex === 0 && event.beadType === 'earth' && event.position === 1) {
console.log('Correct!');
}
},
}
}}
/>
```
## Core API
### AbacusConfig Interface
### Basic Props
```tsx
interface AbacusConfig {
// Display
value?: number; // 0-99999, number to display
columns?: number | "auto"; // Number of columns or auto-calculate
showNumbers?: boolean; // Show place value numbers below
scaleFactor?: number; // 0.5 - 3.0, size multiplier
showEmptyColumns?: boolean; // Display columns with value 0
value?: number; // 0-99999, number to display
columns?: number | 'auto'; // Number of columns or auto-calculate
showNumbers?: boolean; // Show place value numbers
scaleFactor?: number; // 0.5 - 3.0, size multiplier
// Appearance
beadShape?: "diamond" | "square" | "circle";
colorScheme?: "monochrome" | "place-value" | "alternating" | "heaven-earth";
colorPalette?: "default" | "colorblind" | "mnemonic" | "grayscale" | "nature";
hideInactiveBeads?: boolean; // Hide/show inactive beads
beadShape?: 'diamond' | 'square' | 'circle';
colorScheme?: 'monochrome' | 'place-value' | 'alternating' | 'heaven-earth';
colorPalette?: 'default' | 'colorblind' | 'mnemonic' | 'grayscale' | 'nature';
hideInactiveBeads?: boolean; // Hide/show inactive beads
// Interaction
interactive?: boolean; // Enable user interactions
animated?: boolean; // Enable React Spring animations
gestures?: boolean; // Enable directional drag gestures
// Advanced
customStyles?: AbacusCustomStyles; // Granular styling control
callbacks?: AbacusCallbacks; // Event handlers
overlays?: AbacusOverlay[]; // Tutorial overlay system
highlightBeads?: BeadHighlight[]; // Highlight specific beads
stepBeadHighlights?: StepBeadHighlight[]; // Progressive tutorial highlighting
showDirectionIndicators?: boolean; // Show movement direction indicators
disabledBeads?: BeadHighlight[]; // Disable specific bead interactions
interactive?: boolean; // Enable user interactions
animated?: boolean; // Enable animations
gestures?: boolean; // Enable drag gestures
}
```
### Event System
### Event Callbacks
```tsx
interface AbacusCallbacks {
@ -199,214 +147,109 @@ interface AbacusCallbacks {
onBeadClick?: (event: BeadClickEvent) => void;
onBeadHover?: (event: BeadClickEvent) => void;
onBeadLeave?: (event: BeadClickEvent) => void;
onColumnClick?: (columnIndex: number, event: React.MouseEvent) => void;
onNumeralClick?: (
columnIndex: number,
value: number,
event: React.MouseEvent,
) => void;
onColumnClick?: (columnIndex: number) => void;
onNumeralClick?: (columnIndex: number, value: number) => void;
onBeadRef?: (bead: BeadConfig, element: SVGElement | null) => void;
}
interface BeadClickEvent {
bead: BeadConfig; // Complete bead configuration
columnIndex: number; // 0, 1, 2... (array index)
placeValue: ValidPlaceValues; // 0=ones, 1=tens, 2=hundreds...
beadType: "heaven" | "earth"; // Type of bead
position: number; // Position within type (0-3 for earth)
active: boolean; // Current activation state
value: number; // Numeric value (1 or 5)
event: React.MouseEvent; // Original mouse event
columnIndex: number; // 0, 1, 2...
beadType: 'heaven' | 'earth'; // Type of bead
position: number; // Position within type (0-3 for earth)
active: boolean; // Current state
value: number; // Numeric value (1 or 5)
bead: BeadConfig; // Full bead configuration
}
```
## Advanced Features
## Advanced Customization
### Place-Value Based Targeting
Target beads by mathematical place value instead of visual column position:
```tsx
// Target beads by place value (recommended)
const placeValueHighlights = [
{ placeValue: 0, beadType: "earth", position: 2 }, // Ones place, 3rd earth bead
{ placeValue: 1, beadType: "heaven" }, // Tens place, heaven bead
{ placeValue: 2, beadType: "earth", position: 0 }, // Hundreds place, 1st earth bead
];
// Legacy column-index targeting (still supported)
const columnHighlights = [
{ columnIndex: 2, beadType: "earth", position: 2 }, // Rightmost column
{ columnIndex: 1, beadType: "heaven" }, // Middle column
{ columnIndex: 0, beadType: "earth", position: 0 }, // Leftmost column
];
<AbacusReact highlightBeads={placeValueHighlights} />;
```
### Progressive Tutorial Steps
Create multi-step interactive tutorials:
```tsx
const tutorialSteps = [
{
placeValue: 0,
beadType: "earth",
position: 0,
stepIndex: 0,
direction: "activate",
order: 1,
},
{
placeValue: 0,
beadType: "earth",
position: 1,
stepIndex: 1,
direction: "activate",
order: 1,
},
{
placeValue: 1,
beadType: "heaven",
stepIndex: 2,
direction: "activate",
order: 1,
},
];
<AbacusReact
stepBeadHighlights={tutorialSteps}
currentStep={currentStepIndex}
showDirectionIndicators={true}
interactive={true}
/>;
```
### Granular Style Customization
### Granular Styling
Target any visual element with precise control:
```tsx
const advancedStyles = {
const customStyles = {
// Global defaults
heavenBeads: { fill: "#e74c3c", stroke: "#c0392b" },
earthBeads: { fill: "#3498db", stroke: "#2980b9" },
heavenBeads: { fill: '#ff6b35' },
earthBeads: { fill: '#3498db' },
activeBeads: { opacity: 1.0 },
inactiveBeads: { opacity: 0.3 },
// Column-specific overrides (by array index)
// Column-specific overrides
columns: {
0: {
// Leftmost column (highest place value)
heavenBeads: { fill: "#f39c12" },
earthBeads: { fill: "#e67e22" },
backgroundGlow: { fill: "#fff3cd", opacity: 0.3 },
},
0: { // Hundreds column
heavenBeads: { fill: '#e74c3c' },
earthBeads: { fill: '#2ecc71' }
}
},
// Individual bead targeting (by array index)
// Individual bead targeting
beads: {
1: {
// Middle column
heaven: { fill: "#9b59b6" },
1: { // Middle column
heaven: { fill: '#f39c12' },
earth: {
0: { fill: "#1abc9c" }, // First earth bead
1: { fill: "#16a085" }, // Second earth bead
2: { fill: "#17a2b8" }, // Third earth bead
3: { fill: "#138496" }, // Fourth earth bead
},
},
0: { fill: '#1abc9c' }, // First earth bead
3: { fill: '#e67e22' } // Fourth earth bead
}
}
},
// UI elements
reckoningBar: { stroke: "#34495e", strokeWidth: 3 },
columnPosts: { stroke: "#7f8c8d", strokeWidth: 2 },
reckoningBar: { stroke: '#34495e', strokeWidth: 3 },
columnPosts: { stroke: '#7f8c8d' },
numerals: {
color: "#2c3e50",
fontSize: "16px",
fontFamily: "monospace",
fontWeight: "bold",
},
color: '#2c3e50',
fontSize: '14px',
fontFamily: 'monospace'
}
};
<AbacusReact customStyles={advancedStyles} />;
<AbacusReact customStyles={customStyles} />
```
### Overlay System
### Tutorial and Overlay System
Create rich interactive educational experiences:
Create interactive educational experiences:
```tsx
const educationalOverlays = [
const overlays = [
{
id: "value-explanation",
type: "tooltip",
target: { type: "bead", columnIndex: 0, beadType: "heaven" },
id: 'welcome-tooltip',
type: 'tooltip',
target: {
type: 'bead',
columnIndex: 0,
beadType: 'earth',
beadPosition: 0
},
content: (
<div className="tutorial-tooltip">
<h4>Heaven Bead</h4>
<p>Worth 5 in this place value</p>
<button onClick={() => nextStep()}>Got it!</button>
<div style={{
background: '#333',
color: 'white',
padding: '8px',
borderRadius: '4px'
}}>
Click me to start!
</div>
),
offset: { x: 0, y: -40 },
},
{
id: "direction-arrow",
type: "arrow",
target: {
type: "bead",
columnIndex: 1,
beadType: "earth",
beadPosition: 0,
},
content: <div className="arrow-down"></div>,
offset: { x: 0, y: -20 },
},
offset: { x: 0, y: -30 }
}
];
<AbacusReact
overlays={educationalOverlays}
interactive={true}
overlays={overlays}
highlightBeads={[
{ columnIndex: 0, beadType: 'earth', position: 0 }
]}
callbacks={{
onBeadClick: handleTutorialProgression,
onBeadClick: (event) => {
if (event.columnIndex === 0 && event.beadType === 'earth' && event.position === 0) {
console.log('Tutorial step completed!');
}
}
}}
/>;
```
### Dimension Calculation Hook
Get exact sizing information for layout planning:
```tsx
import { useAbacusDimensions } from "@soroban/abacus-react";
function ResponsiveAbacusContainer() {
const dimensions = useAbacusDimensions(
5, // columns
1.2, // scale factor
true, // show numbers
);
return (
<div
style={{
width: dimensions.width,
height: dimensions.height,
border: "1px solid #ccc",
padding: "10px",
}}
>
<AbacusReact
columns={5}
scaleFactor={1.2}
showNumbers={true}
value={12345}
/>
</div>
);
}
/>
```
### Bead Reference System
@ -414,7 +257,7 @@ function ResponsiveAbacusContainer() {
Access individual bead DOM elements for advanced positioning:
```tsx
function AdvancedPositioning() {
function AdvancedExample() {
const beadRefs = useRef(new Map<string, SVGElement>());
const handleBeadRef = (bead: BeadConfig, element: SVGElement | null) => {
@ -422,21 +265,100 @@ function AdvancedPositioning() {
if (element) {
beadRefs.current.set(key, element);
// Position custom elements relative to beads
// Now you can position tooltips, highlights, etc. precisely
const rect = element.getBoundingClientRect();
console.log(`Bead at column ${bead.columnIndex} positioned at:`, rect);
console.log(`Bead at column ${bead.columnIndex} is at:`, rect);
}
};
return (
<AbacusReact callbacks={{ onBeadRef: handleBeadRef }} interactive={true} />
<AbacusReact
callbacks={{ onBeadRef: handleBeadRef }}
// ... other props
/>
);
}
```
## Hooks
### useAbacusDimensions
Get exact sizing information for layout planning:
```tsx
import { useAbacusDimensions } from '@soroban/abacus-react';
function MyComponent() {
const dimensions = useAbacusDimensions(3, 1.2); // 3 columns, 1.2x scale
return (
<div style={{ width: dimensions.width, height: dimensions.height }}>
<AbacusReact columns={3} scaleFactor={1.2} />
</div>
);
}
```
## Educational Use Cases
### Interactive Math Lessons
```tsx
function MathLesson() {
const [problem, setProblem] = useState({ a: 23, b: 45 });
const [step, setStep] = useState('show-first');
return (
<div>
<h3>Add {problem.a} + {problem.b}</h3>
<AbacusReact
value={step === 'show-first' ? problem.a : 0}
interactive={step === 'add-second'}
callbacks={{
onValueChange: (value) => {
if (value === problem.a + problem.b) {
celebrate();
}
}
}}
/>
</div>
);
}
```
### Assessment Tools
```tsx
function AbacusQuiz() {
const [answers, setAnswers] = useState([]);
const checkAnswer = (event: BeadClickEvent) => {
const isCorrect = validateBeadClick(event, expectedAnswer);
recordAnswer(event, isCorrect);
if (isCorrect) {
showSuccessFeedback();
} else {
showHint(event);
}
};
return (
<AbacusReact
interactive={true}
callbacks={{ onBeadClick: checkAnswer }}
customStyles={getAnswerHighlighting(answers)}
/>
);
}
```
## TypeScript Support
Full TypeScript definitions with branded types for enhanced type safety:
Full TypeScript definitions included:
```tsx
import {
@ -447,191 +369,16 @@ import {
AbacusCustomStyles,
AbacusOverlay,
AbacusCallbacks,
useAbacusDimensions,
PlaceValueBead,
ColumnIndexBead,
StepBeadHighlight,
PlaceValue,
ColumnIndex,
ValidPlaceValues,
EarthBeadPosition,
} from "@soroban/abacus-react";
useAbacusDimensions
} from '@soroban/abacus-react';
// Branded types prevent mixing place values and column indices
const placeValue: ValidPlaceValues = 2; // hundreds place
const earthPosition: EarthBeadPosition = 3; // fourth earth bead
// Type-safe bead specification
const bead: PlaceValueBead = {
placeValue: 1, // tens place
beadType: "earth",
position: 2, // third earth bead
};
// All interfaces fully typed for excellent developer experience
```
## Educational Use Cases
### Interactive Math Lessons
```tsx
function AdditionLesson() {
const [problem] = useState({ a: 23, b: 45 });
const [step, setStep] = useState("show-first");
const [userValue, setUserValue] = useState(0);
const checkAnswer = (newValue: number) => {
setUserValue(newValue);
if (newValue === problem.a + problem.b) {
setStep("completed");
showCelebration();
}
};
return (
<div className="math-lesson">
<h3>
Add {problem.a} + {problem.b}
</h3>
<AbacusReact
value={step === "show-first" ? problem.a : userValue}
columns={3}
interactive={step === "user-input"}
animated={true}
showNumbers={true}
callbacks={{ onValueChange: checkAnswer }}
highlightBeads={step === "hint" ? getHintBeads() : []}
/>
{step === "completed" && (
<div className="success">
🎉 Correct! {problem.a} + {problem.b} = {problem.a + problem.b}
</div>
)}
</div>
);
}
```
### Assessment and Quizzing
```tsx
function AbacusQuiz() {
const [answers, setAnswers] = useState<BeadClickEvent[]>([]);
const [feedback, setFeedback] = useState<string>("");
const validateAnswer = (event: BeadClickEvent) => {
const isCorrect = checkBeadClick(event, expectedAnswer);
setAnswers((prev) => [...prev, event]);
if (isCorrect) {
setFeedback("Correct! Well done.");
advanceToNextQuestion();
} else {
setFeedback("Try again. Remember: this bead represents...");
showHint(event);
}
};
return (
<div className="abacus-quiz">
<AbacusReact
value={currentQuestionValue}
interactive={true}
callbacks={{ onBeadClick: validateAnswer }}
customStyles={getAnswerHighlighting(answers)}
overlays={currentHints}
/>
<div className="feedback">{feedback}</div>
</div>
);
}
```
## Color Schemes and Accessibility
### Built-in Color Schemes
- **`monochrome`** - Single color for all beads
- **`place-value`** - Different colors for each place value column
- **`alternating`** - Alternating colors between columns
- **`heaven-earth`** - Different colors for heaven vs earth beads
### Accessibility Palettes
- **`colorblind`** - High contrast, colorblind-friendly palette
- **`grayscale`** - Monochrome grayscale for maximum compatibility
- **`mnemonic`** - Colors that aid memory and learning
- **`nature`** - Earth-tone palette for reduced eye strain
```tsx
<AbacusReact
colorScheme="place-value"
colorPalette="colorblind"
value={12345}
columns={5}
/>
```
## Publishing and Versioning
This package uses [semantic-release](https://semantic-release.gitbook.io/) for automated publishing. Versions are determined by conventional commit messages:
### Commit Message Format
Use these prefixes for commits that affect the `packages/abacus-react` directory:
```bash
# New features (minor version bump)
feat(abacus-react): add gesture recognition system
# Bug fixes (patch version bump)
fix(abacus-react): resolve animation timing issues
# Performance improvements (patch version bump)
perf(abacus-react): optimize bead rendering performance
# Breaking changes (major version bump)
feat(abacus-react)!: redesign callback API
# or
feat(abacus-react): change component interface
BREAKING CHANGE: callback functions now receive different parameters
```
### Release Process
1. **Automatic**: Releases happen automatically when changes are pushed to `main` branch
2. **Dual publishing**: Package is published to both npm and GitHub Packages simultaneously
3. **Manual testing**: Run `pnpm release:dry-run` to test release without publishing
4. **Version tags**: Releases are tagged as `abacus-react-v1.2.3` (separate from monorepo versions)
### Development Commands
```bash
# Build the package
pnpm build
# Run tests
pnpm test:run
# Run Storybook locally
pnpm storybook
# Test release process (dry run)
pnpm release:dry-run
```
## Live Documentation
- **Storybook**: [Component examples and documentation](https://antialias.github.io/soroban-abacus-flashcards/abacus-react/)
- **Source Code**: [GitHub Repository](https://github.com/antialias/soroban-abacus-flashcards/tree/main/packages/abacus-react)
## Contributing
Contributions welcome! Please see our [contributing guidelines](../../CONTRIBUTING.md) and feel free to submit issues or pull requests.
Contributions welcome! Please see our contributing guidelines and feel free to submit issues or pull requests.
## License
MIT License - see [LICENSE](../../LICENSE) file for details.
MIT License - see LICENSE file for details.

View File

@ -1,134 +1,133 @@
import type { Meta, StoryObj } from "@storybook/react";
import { AbacusReact } from "./AbacusReact";
import React from "react";
import type { Meta, StoryObj } from '@storybook/react';
import { AbacusReact } from './AbacusReact';
import React from 'react';
const meta: Meta<typeof AbacusReact> = {
title: "Examples/AbacusReact",
title: 'Examples/AbacusReact',
component: AbacusReact,
parameters: {
layout: "centered",
layout: 'centered',
docs: {
description: {
component:
"Interactive Soroban (Japanese abacus) component with comprehensive customization options.",
},
},
component: 'Interactive Soroban (Japanese abacus) component with comprehensive customization options.'
}
}
},
tags: ["autodocs"],
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof meta>;
export const basicUsage: Story = {
name: "Basic Usage",
name: 'Basic Usage',
args: {
value: 123,
columns: 3,
showNumbers: true,
scaleFactor: 1,
animated: false,
},
"value": 123,
"columns": 3,
"showNumbers": true,
"scaleFactor": 1,
"animated": false
},
parameters: {
docs: {
description: {
story: "Simple abacus showing a number",
},
},
},
story: 'Simple abacus showing a number'
}
}
}
};
export const interactive: Story = {
name: "Interactive Mode",
name: 'Interactive Mode',
args: {
value: 456,
columns: 3,
interactive: true,
animated: false,
showNumbers: true,
},
"value": 456,
"columns": 3,
"interactive": true,
"animated": false,
"showNumbers": true
},
parameters: {
docs: {
description: {
story: "Clickable abacus with animations",
},
},
},
story: 'Clickable abacus with animations'
}
}
}
};
export const customStyling: Story = {
name: "Custom Styling",
name: 'Custom Styling',
args: {
value: 789,
columns: 3,
colorScheme: "place-value",
beadShape: "circle",
animated: false,
customStyles: {
heavenBeads: {
fill: "#ff6b35",
},
earthBeads: {
fill: "#3498db",
},
numerals: {
color: "#2c3e50",
fontWeight: "bold",
},
"value": 789,
"columns": 3,
"colorScheme": "place-value",
"beadShape": "circle",
"animated": false,
"customStyles": {
"heavenBeads": {
"fill": "#ff6b35"
},
highlightBeads: [
{
columnIndex: 1,
beadType: "heaven",
},
],
"earthBeads": {
"fill": "#3498db"
},
"numerals": {
"color": "#2c3e50",
"fontWeight": "bold"
}
},
"highlightBeads": [
{
"columnIndex": 1,
"beadType": "heaven"
}
]
},
parameters: {
docs: {
description: {
story: "Personalized colors and highlights",
},
},
},
story: 'Personalized colors and highlights'
}
}
}
};
export const tutorialMode: Story = {
name: "Tutorial System",
name: 'Tutorial System',
args: {
value: 42,
columns: 2,
interactive: true,
animated: false,
showNumbers: true,
},
"value": 42,
"columns": 2,
"interactive": true,
"animated": false,
"showNumbers": true
},
parameters: {
docs: {
description: {
story: "Educational guidance with tooltips",
},
},
},
story: 'Educational guidance with tooltips'
}
}
}
};
// Advanced tutorial example (from our previous implementation)
export const TutorialExample: Story = {
name: "Interactive Tutorial",
name: 'Interactive Tutorial',
render: () => {
const [step, setStep] = React.useState(0);
const [feedbackMessage, setFeedbackMessage] = React.useState("");
const [feedbackMessage, setFeedbackMessage] = React.useState('');
const tutorialSteps = [
{
instruction:
"Click the orange highlighted bead in the ones column (leftmost)",
highlightBeads: [{ columnIndex: 4, beadType: "earth", position: 2 }],
value: 7,
instruction: "Click the orange highlighted bead in the ones column (leftmost)",
highlightBeads: [{ columnIndex: 0, beadType: 'earth', position: 2 }],
value: 7
},
{
instruction: "Click anywhere in the ones column (leftmost column)",
highlightColumns: [0],
value: 7,
},
value: 7
}
];
const currentStep = tutorialSteps[step];
@ -138,8 +137,7 @@ export const TutorialExample: Story = {
if (step === 0) {
const target = currentStep.highlightBeads?.[0];
isCorrectTarget =
target &&
isCorrectTarget = target &&
event.columnIndex === target.columnIndex &&
event.beadType === target.beadType &&
event.position === target.position;
@ -149,25 +147,23 @@ export const TutorialExample: Story = {
if (isCorrectTarget && step < tutorialSteps.length - 1) {
setStep(step + 1);
setFeedbackMessage("✅ Correct! Moving to next step...");
setTimeout(() => setFeedbackMessage(""), 2000);
setFeedbackMessage('✅ Correct! Moving to next step...');
setTimeout(() => setFeedbackMessage(''), 2000);
} else if (!isCorrectTarget) {
setFeedbackMessage("⚠️ Try clicking the highlighted area.");
setTimeout(() => setFeedbackMessage(""), 3000);
setFeedbackMessage('⚠️ Try clicking the highlighted area.');
setTimeout(() => setFeedbackMessage(''), 3000);
}
};
const handleRestart = () => {
setStep(0);
setFeedbackMessage("");
setFeedbackMessage('');
};
return (
<div style={{ position: "relative", padding: "20px" }}>
<div style={{ marginBottom: "20px", textAlign: "center" }}>
<h3>
Interactive Tutorial - Step {step + 1} of {tutorialSteps.length}
</h3>
<div style={{ position: 'relative', padding: '20px' }}>
<div style={{ marginBottom: '20px', textAlign: 'center' }}>
<h3>Interactive Tutorial - Step {step + 1} of {tutorialSteps.length}</h3>
<p>{currentStep.instruction}</p>
</div>
@ -178,105 +174,91 @@ export const TutorialExample: Story = {
animated={true}
showNumbers={true}
scaleFactor={1.2}
highlightColumns={currentStep.highlightColumns}
highlightBeads={currentStep.highlightBeads}
customStyles={{
beads: {
0: {
earth: {
2:
step === 0
? { fill: "#ff6b35", stroke: "#d63031", strokeWidth: 2 }
: undefined,
},
},
},
2: step === 0 ? { fill: '#ff6b35', stroke: '#d63031', strokeWidth: 2 } : undefined
}
}
}
}}
overlays={
step === 0
? [
{
id: "tutorial-tip",
type: "tooltip",
target: {
type: "bead",
columnIndex: 4,
beadType: "earth",
beadPosition: 2,
},
content: (
<div
style={{
background: "#333",
color: "white",
padding: "8px",
borderRadius: "4px",
fontSize: "12px",
maxWidth: "120px",
textAlign: "center",
}}
>
Click this bead!
<div
style={{
position: "absolute",
bottom: "-6px",
left: "50%",
transform: "translateX(-50%)",
borderTop: "6px solid #333",
borderLeft: "6px solid transparent",
borderRight: "6px solid transparent",
}}
/>
</div>
),
offset: { x: 0, y: -50 },
},
]
: []
}
overlays={step === 0 ? [{
id: 'tutorial-tip',
type: 'tooltip',
target: {
type: 'bead',
columnIndex: 0,
beadType: 'earth',
beadPosition: 2
},
content: (
<div style={{
background: '#333',
color: 'white',
padding: '8px',
borderRadius: '4px',
fontSize: '12px',
maxWidth: '120px',
textAlign: 'center'
}}>
Click this bead!
<div style={{
position: 'absolute',
bottom: '-6px',
left: '50%',
transform: 'translateX(-50%)',
borderTop: '6px solid #333',
borderLeft: '6px solid transparent',
borderRight: '6px solid transparent'
}} />
</div>
),
offset: { x: 0, y: -50 }
}] : []}
callbacks={{
onBeadClick: handleBeadClick,
onBeadClick: handleBeadClick
}}
/>
<div style={{ marginTop: "20px", textAlign: "center" }}>
<button
onClick={handleRestart}
style={{
padding: "8px 16px",
backgroundColor: "#007bff",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer",
}}
>
<div style={{ marginTop: '20px', textAlign: 'center' }}>
<button onClick={handleRestart} style={{
padding: '8px 16px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}>
Restart Tutorial
</button>
</div>
{feedbackMessage && (
<div
style={{
position: "absolute",
right: "-300px",
top: "50%",
transform: "translateY(-50%)",
background: "#f8f9fa",
border: "1px solid #dee2e6",
borderRadius: "4px",
padding: "12px",
fontSize: "14px",
maxWidth: "250px",
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
transition: "all 0.3s ease",
}}
>
<div style={{
position: 'absolute',
right: '-300px',
top: '50%',
transform: 'translateY(-50%)',
background: '#f8f9fa',
border: '1px solid #dee2e6',
borderRadius: '4px',
padding: '12px',
fontSize: '14px',
maxWidth: '250px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
transition: 'all 0.3s ease'
}}>
{feedbackMessage}
</div>
)}
</div>
);
},
}
};