# DeckFX Demos

<div align="center">

![npm version](https://img.shields.io/npm/v/deckfx?style=flat-square)
![license](https://img.shields.io/npm/l/deckfx?style=flat-square)
![Node.js](https://img.shields.io/node/v/deckfx?style=flat-square)

**Interactive demo showcase for the DeckFX library**

[🎮 Live Demo](https://emerson.github.io/DeckFX/) • [📦 npm Package](https://www.npmjs.com/package/deckfx) • [💻 Examples](#examples)

</div>

This repository contains **interactive demonstrations** of the [**DeckFX**](https://www.npmjs.com/package/deckfx) library - a high-performance, interaction-rich React library for building card and board games.

**DeckFX** provides production-ready components for 3D card rendering, layout management, and dice rolling. Built with React, Three.js, and Framer Motion for smooth animations and interactions.

> **Note:** This repository uses the published `deckfx` npm package. For the library source code, see the [npm package](https://www.npmjs.com/package/deckfx).

## Features

- 🎴 **3D Card Rendering** - Realistic card physics with tilt, glare, and flip animations
- 🎯 **Layout Engine** - Multiple layout modes (fan, grid, stack, cascade, messy)
- 🎲 **3D Dice System** - Support for D4, D6, D8, D10, D12, and D20 with physics
- 🖱️ **Drag & Drop** - Built-in drag interactions with snap-to-origin
- ✅ **Async Validation** - Server-side validation for all card interactions (drag, drop, reorder, draw)
- 🎨 **Customizable** - Full control over card appearance, animations, and interactions
- 📱 **TypeScript** - Fully typed with comprehensive type definitions
- ⚡ **Performance** - Optimized for smooth 60fps animations

## Installation

```bash
npm install deckfx framer-motion three @react-three/fiber @react-three/drei lucide-react
```

## Quick Start

```tsx
import { Card, CardZone, DiceRoller } from 'deckfx';

function App() {
  return (
    <CardZone layout="fan">
      <Card faceUp={true} width={120} height={170}>
        <div>Ace of Spades</div>
      </Card>
    </CardZone>
  );
}
```

## Examples

Check out the [live demo](https://emerson.github.io/DeckFX/) to see DeckFX in action:

- **TCG Battle** - Trading card game battlefield with drag & drop
- **Hand Physics** - Interactive card hand with hover effects
- **Inventory Grid** - Grid-based card inventory system
- **Solitaire** - Classic solitaire game implementation
- **RPG Adventure** - Role-playing game card system
- **War** - Card game with deck management
- **Blackjack** - Casino card game
- **Dice Roller** - 3D dice with physics simulation

## Core Components

### Card

The fundamental building block. Handles 3D tilt, glare effects, flipping animations, and drag interactions.

```tsx
<Card 
  faceUp={true}
  imageUrl="/card-art.jpg"
  width={200}
  height={300}
  showGlare={true}
  maxTilt={15}
  drag
  dragSnapToOrigin
  onClick={() => console.log('clicked')}
/>
```

**Props:**

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `faceUp` | `boolean` | `true` | Whether the card face is visible |
| `width` | `number` | `200` | Card width in pixels |
| `height` | `number` | `300` | Card height in pixels |
| `imageUrl` | `string` | - | Background image for the card |
| `showGlare` | `boolean` | `true` | Enable holographic glare effect |
| `maxTilt` | `number` | `15` | Maximum 3D tilt angle in degrees |
| `rotation` | `number` | `0` | Z-axis rotation in degrees |
| `drag` | `boolean` | `false` | Enable drag interactions |
| `onValidateDragStart` | `(cardId: string, cardData?: any) => Promise<ValidationResult>` | - | Async validator for drag start |
| `onValidateDragEnd` | `(cardId: string, fromZone: string, toZone: string, cardData?: any) => Promise<ValidationResult>` | - | Async validator for drag end |
| `onValidationError` | `(error: string) => void` | - | Callback for validation errors |
| `cardId` | `string` | - | Card identifier for validation |
| `cardData` | `any` | - | Additional data to pass to validators |
| `onFlip` | `(newFaceUp: boolean) => void` | - | Callback when flip animation starts |
| `onFlipComplete` | `(faceUp: boolean) => void` | - | Callback when flip animation completes |
| `flipDuration` | `number` | - | Flip animation duration in ms |
| `flipDirection` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direction of flip animation |
| `selectable` | `boolean` | `false` | Whether card can be selected |
| `selected` | `boolean` | `false` | Controlled selected state |
| `onSelect` | `(cardId: string, event?: MouseEvent) => void` | - | Callback when card is selected |
| `counters` | `Counter[]` | - | Array of counters to display on card |
| `renderCounter` | `(counter: Counter) => ReactNode` | - | Custom counter renderer |
| `stackId` | `string` | - | ID of the stack this card belongs to |
| `stackOrder` | `number` | - | Order within stack (0 = bottom) |
| `isStackTop` | `boolean` | - | Whether card is on top of stack |

### CardZone

A layout engine that automatically positions child cards.

```tsx
<CardZone 
  layout="fan" 
  fanAngle={15} 
  fanArcHeight={20}
>
  {cards.map(card => <Card key={card.id} {...card} />)}
</CardZone>
```

**Layouts:**
- `stack` - Cards piled on top (deck)
- `grid` - Standard grid (inventory)
- `linear-horizontal` - Row of cards
- `linear-vertical` - Column of cards
- `messy` - Random rotation (discard pile)
- `fan` - Arc layout (player hand)
- `cascade-vertical` - Solitaire-style cascade
- `cascade-horizontal` - Horizontal cascade

**Capacity Props:**
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `maxCards` | `number` | - | Maximum number of cards allowed |
| `currentCount` | `number` | - | Manual count override (auto-calculated if not provided) |
| `showCapacity` | `boolean` | `false` | Show capacity badge |
| `capacityWarningThreshold` | `number` | `0.8` | Threshold (0-1) for warning color |
| `onCapacityReached` | `() => void` | - | Callback when capacity is reached |
| `dropZoneValid` | `boolean` | - | External control for drop zone validity |
| `onDragOver` | `(isOver: boolean) => void` | - | Callback when dragging over zone |

### DiceRoller

A 3D dice engine supporting D4, D6, D8, D10, D12, and D20.

```tsx
import { DiceRoller, DieState, DieConfig } from 'deckfx';

const [dice, setDice] = useState<DieState[]>([
  { id: 1, type: 'd20', value: 1, target: 20, isRolling: false }
]);

const config: DieConfig = {
  color: '#ffffff',
  duration: 2000,
  size: 1,
  spinForce: 5
};

<DiceRoller 
  dice={dice}
  config={config}
  zoom={15}
  onRollComplete={(id) => {
    setDice(prev => prev.map(d => 
      d.id === id ? { ...d, isRolling: false } : d
    ));
  }}
/>
```

### DeckSearchModal

A pre-built modal for searching through card collections.

```tsx
<DeckSearchModal
  isOpen={isOpen}
  onClose={() => setIsOpen(false)}
  title="Search Deck"
  cards={deckCards}
  searchProperty="title"
  onCardSelect={(card) => handleSelect(card)}
  renderCard={(card) => <Card {...card} />}
/>
```

### Deck

A visual representation of a card deck with draw functionality and validation support.

```tsx
<Deck 
  count={52}
  onDraw={() => {
    // Handle card draw
  }}
  onValidateDraw={async () => {
    // Validate if draw is allowed
    return { valid: true };
  }}
  onValidationError={(error) => {
    // Handle validation errors
    console.error(error);
  }}
/>
```

**Props:**

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `count` | `number` | - | Number of cards in the deck |
| `onDraw` | `() => void` | - | Callback when deck is clicked |
| `thickness` | `number` | `1` | Visual thickness per card |
| `onValidateDraw` | `() => Promise<ValidationResult>` | - | Async validator for draw operation |
| `onValidationError` | `(error: string) => void` | - | Callback for validation errors |

### Hand

A specialized component for player hands with hover effects, reordering, and validation.

```tsx
<Hand 
  spacing={-40} 
  arcHeight={30} 
  maxFanAngle={15}
  sortable={true}
  onReorder={(fromIndex, toIndex) => {
    // Handle reorder
  }}
  onValidateReorder={async (cardId, fromIndex, toIndex) => {
    // Validate if reorder is allowed
    return { valid: true };
  }}
  onValidationError={(error) => {
    // Handle validation errors
    console.error(error);
  }}
>
  {handCards.map(card => <Card key={card.id} {...card} />)}
</Hand>
```

**Props:**

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `spacing` | `number` | `-50` | Card spacing (negative for overlap) |
| `arcHeight` | `number` | `30` | Height of the arc |
| `maxFanAngle` | `number` | `15` | Maximum rotation angle |
| `hoverScale` | `number` | `1.2` | Scale on hover |
| `hoverYOffset` | `number` | `-100` | Vertical offset on hover |
| `straightenOnHover` | `boolean` | `true` | Straighten card on hover |
| `sortable` | `boolean` | `false` | Enable card reordering |
| `onReorder` | `(fromIndex: number, toIndex: number) => void` | - | Callback when cards are reordered |
| `onValidateReorder` | `(cardId: string, fromIndex: number, toIndex: number) => Promise<ValidationResult>` | - | Async validator for reorder operation |
| `onValidationError` | `(error: string) => void` | - | Callback for validation errors |

### TargetingArrow

An SVG arrow for targeting interactions (like Hearthstone).

```tsx
<TargetingArrow 
  start={{ x: 100, y: 100 }}
  end={{ x: 300, y: 200 }}
  color="#ff0000"
/>
```

### Card Flip API

Programmatic control over card flip animations.

```tsx
import { Card, CardRef } from 'deckfx';

const cardRef = useRef<CardRef>(null);

<Card
  ref={cardRef}
  faceUp={isFaceUp}
  onFlip={(newFaceUp) => console.log('Flipping to', newFaceUp)}
  onFlipComplete={(faceUp) => console.log('Flip complete')}
  flipDuration={500}
  flipDirection="horizontal"
/>

// Programmatic flip
cardRef.current?.flipTo(true);
```

### Card Selection

Context-based selection management with multi-select support.

```tsx
import { SelectionProvider, useCardSelection } from 'deckfx';

<SelectionProvider
  selectionMode="multi"
  onSelectionChange={(selectedIds) => console.log(selectedIds)}
>
  <Card cardId="card-1" selectable>Card 1</Card>
  <Card cardId="card-2" selectable>Card 2</Card>
</SelectionProvider>

// Hook usage
const { selectedIds, selectCard, clearSelection } = useCardSelection();
```

### Card Counters

Display counters and tokens on cards.

```tsx
<Card
  counters={[
    { id: 'damage', type: 'damage', value: 3, color: 'red', position: 'top-right' },
    { id: 'mana', type: 'mana', value: 2, color: 'blue', position: 'top-left' }
  ]}
/>
```

### Zone Capacity

Add capacity limits and visual feedback to zones.

```tsx
<CardZone
  zoneId="hand"
  maxCards={7}
  showCapacity={true}
  capacityWarningThreshold={0.8}
  onCapacityReached={() => console.log('Zone full!')}
>
  {cards.map(card => <Card key={card.id} {...card} />)}
</CardZone>
```

### Card Stacking

Group cards into stacks with drag and peek functionality.

```tsx
import { CardStack } from 'deckfx';

<CardStack stackId="stack-1" peekOffset={20}>
  <Card cardId="card-1" stackId="stack-1" stackOrder={0} isStackTop />
  <Card cardId="card-2" stackId="stack-1" stackOrder={1} />
  <Card cardId="card-3" stackId="stack-1" stackOrder={2} />
</CardStack>
```

## Validation

DeckFX provides async validation for all card interactions, allowing you to validate actions against server-side rules, game state, or API endpoints.

### Validation Context (Recommended)

For applications with multiple components, use the `ValidationProvider` to centralize validation configuration:

```tsx
import { ValidationProvider, Card, CardZone } from 'deckfx';

function GameBoard() {
  return (
    <ValidationProvider
      onValidateDragEnd={async (cardId, fromZone, toZone) => {
        // Centralized validation logic
        const response = await fetch('/api/validate-move', {
          method: 'POST',
          body: JSON.stringify({ cardId, fromZone, toZone })
        });
        const data = await response.json();
        return { valid: data.allowed, error: data.error };
      }}
      onValidationError={(error) => {
        showToast(error);
      }}
    >
      <CardZone zoneId="hand" layout="fan">
        <Card cardId="card-1" drag>Card 1</Card>
      </CardZone>
      <CardZone zoneId="board" layout="grid">
        {/* Drop zone */}
      </CardZone>
    </ValidationProvider>
  );
}
```

### Prop-Based Validation (Legacy)

You can also pass validators directly to components:

```tsx
import { Card, ValidationError, useValidationError } from 'deckfx';

function GameBoard() {
  const { error, showError, ErrorComponent } = useValidationError();
  
  return (
    <>
      <Card
        drag
        cardId={card.id}
        onValidateDragEnd={async (cardId, fromZone, toZone) => {
          // Server-side validation
          const response = await fetch('/api/validate-move', {
            method: 'POST',
            body: JSON.stringify({ cardId, fromZone, toZone })
          });
          const data = await response.json();
          return { valid: data.allowed, error: data.error };
        }}
        onValidationError={showError}
      />
      {ErrorComponent}
    </>
  );
}
```

### Zone Identification

Use the `zoneId` prop on `CardZone` to identify zones for drag and drop validation:

```tsx
<CardZone zoneId="hand" layout="fan">
  <Card drag cardId="card-1">Card 1</Card>
</CardZone>

<CardZone zoneId="board" layout="grid">
  {/* Drop target */}
</CardZone>
```

The `zoneId` is automatically set as a `data-zone-id` attribute, which is used by the drag validation system to identify source and target zones.

### Validation Types

- **Drag Start** - Validate before a card can be picked up
- **Drag End** - Validate before a card can be dropped (snaps back on failure)
- **Reorder** - Validate before cards can be reordered in a hand
- **Draw** - Validate before a card can be drawn from a deck

### Validation Result

All validators return a `Promise<ValidationResult>`:

```tsx
type ValidationResult = {
  valid: boolean;
  error?: string;
};
```

For detailed validation documentation, see the [full documentation](#documentation).

## Peer Dependencies

DeckFX requires the following peer dependencies:

```json
{
  "react": "^18.0.0",
  "react-dom": "^18.0.0",
  "framer-motion": "^11.0.0",
  "three": "^0.160.0",
  "@react-three/fiber": "^8.0.0",
  "@react-three/drei": "^9.0.0"
}
```

## TypeScript

DeckFX is written in TypeScript and exports all types:

```tsx
import type { 
  DieType,            // 'd4' | 'd6' | 'd8' | 'd10' | 'd12' | 'd20'
  DieState,           // { id, type, value, target, isRolling }
  DieConfig,          // { color, duration, size, spinForce }
  ValidationResult,   // { valid: boolean; error?: string }
  DragStartValidator, // (cardId: string, cardData?: any) => Promise<ValidationResult>
  DragEndValidator,   // (cardId: string, fromZone: string, toZone: string, cardData?: any) => Promise<ValidationResult>
  ReorderValidator,   // (cardId: string, fromIndex: number, toIndex: number) => Promise<ValidationResult>
  DrawValidator,      // () => Promise<ValidationResult>
  ValidationConfig,   // Configuration for ValidationProvider
  ZoneLayout,         // Layout modes for CardZone
  CardRef,            // Ref type for Card component
  FlipDirection,      // 'horizontal' | 'vertical'
  Counter,            // Counter type for card counters
  CounterPosition,    // Counter position type
  SelectionMode,      // 'single' | 'multi' | 'none'
  SelectionContextValue // Selection context value type
} from 'deckfx';
```

## Running the Demos Locally

```bash
# Install dependencies
npm install

# Run development server
npm run dev

# Build for production (GitHub Pages)
npm run build:demo

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Run linting
npm run lint
```

## Contributing

Contributions to the demos are welcome! If you'd like to add a new demo or improve existing ones:

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/new-demo`)
3. Commit your changes (`git commit -m 'Add new poker demo'`)
4. Push to the branch (`git push origin feature/new-demo`)
5. Open a Pull Request

For contributions to the DeckFX library itself, please visit the [npm package repository](https://www.npmjs.com/package/deckfx).

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Links

- [Live Demo](https://emerson.github.io/DeckFX/)
- [GitHub Repository](https://github.com/emerson/deckfx)
- [npm Package](https://www.npmjs.com/package/deckfx)
