npm package: @mokcj0825/hole-mask
Description: A React component for creating mask overlays with customizable holes
Author: mokcj0825
Latest version: 1.0.1

# Hole Mask

A React component for creating mask overlays with customizable holes. Perfect for creating focus areas, tutorials, or highlighting specific content on your web pages. Supports multiple shapes (rectangles, squares, circles) with CSS-like behavior and proper click event handling.

## Installation

```bash
npm install @mokcj0825/hole-mask
```

## Usage

```tsx
import { MaskLayer, Position } from '@mokcj0825/hole-mask';

function App() {
  const holePosition: Position = {
    x: '50%',
    y: '50%',
    size: '200px 150px', // width height for rectangles
    anchor: 'ANCHOR_MIDDLE',
    shape: 'SHAPE_RECTANGLE'
  };

  return (
    <div 
      style={{ position: 'relative', width: '100vw', height: '100vh' }}
      onClick={() => console.log('Unmasked area clicked!')}
    >
      <div>Some content to mask</div>
      
      <MaskLayer
        holePosition={holePosition}
        backgroundColor="rgba(0, 0, 0, 0.5)"
        onOverlayClick={() => console.log('Overlay clicked!')}
        maskIndex={1000}
      />
    </div>
  );
}
```

## Features

### ✅ **Multiple Shape Support**
- **Rectangles**: `size: "200px 150px"` (width height format)
- **Squares**: `size: "200px"` (single dimension)
- **Circles**: `size: "200px"` (single dimension, creates perfect circular holes)

### ✅ **CSS-like Behavior**
- **Rectangles**: Single value `"200px"` becomes `"200px 200px"` (square rectangle)
- **Squares/Circles**: Multiple values `"200px 100px"` uses only `"200px"` (ignores second value)

### ✅ **Smart Click Handling**
- **Overlay clicks**: Triggered when clicking on masked areas
- **Unmasked clicks**: Triggered when clicking in the transparent hole area
- **Proper event propagation**: No interference between overlay and unmasked clicks

### ✅ **Flexible Positioning**
- **5 anchor points**: MIDDLE, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT
- **Mixed units**: Support for both `px` and `%` values
- **Responsive**: Works with any container size

## API Reference

### MaskLayer Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `holePosition` | `Position` | **required** | The position, size, and shape of the hole |
| `backgroundColor` | `string` | `'rgba(0, 0, 0, 0.1)'` | Background color of the mask overlay |
| `onOverlayClick` | `() => void` | `undefined` | Callback function when overlay is clicked |
| `maskIndex` | `number` | `1000` | CSS z-index for the mask layer |

### Position Interface

```tsx
interface Position {
  x: PositionValue;        // X coordinate (e.g., "50%", "100px")
  y: PositionValue;        // Y coordinate (e.g., "50%", "100px")
  size: PositionValue;     // Size of the hole (shape-specific format)
  anchor: AnchorType;      // Anchor point for positioning
  shape: ShapeType;        // Shape type (RECTANGLE, SQUARE, CIRCLE)
}
```

### PositionValue Type

A string in the format `"numberpx"` or `"number%"` (e.g., `"100px"`, `"50%"`).

### ShapeType

- `'SHAPE_RECTANGLE'` - Rectangular hole (supports width/height format)
- `'SHAPE_SQUARE'` - Square hole (single dimension)
- `'SHAPE_CIRCLE'` - Circular hole (single dimension, perfect circles)

### AnchorType

- `'ANCHOR_MIDDLE'` - Center the hole at the specified position
- `'ANCHOR_TOP_LEFT'` - Position the top-left corner at the specified position
- `'ANCHOR_TOP_RIGHT'` - Position the top-right corner at the specified position
- `'ANCHOR_BOTTOM_LEFT'` - Position the bottom-left corner at the specified position
- `'ANCHOR_BOTTOM_RIGHT'` - Position the bottom-right corner at the specified position

## Examples

### Rectangle Mask

```tsx
const rectanglePosition: Position = {
  x: '50%',
  y: '50%',
  size: '200px 150px', // width height format
  anchor: 'ANCHOR_MIDDLE',
  shape: 'SHAPE_RECTANGLE'
};

<MaskLayer holePosition={rectanglePosition} />
```

### Square Mask (CSS-like behavior)

```tsx
const squarePosition: Position = {
  x: '50%',
  y: '50%',
  size: '200px', // Single value creates square
  anchor: 'ANCHOR_MIDDLE',
  shape: 'SHAPE_SQUARE'
};

<MaskLayer holePosition={squarePosition} />
```

### Circle Mask (Perfect circular holes)

```tsx
const circlePosition: Position = {
  x: '50%',
  y: '50%',
  size: '200px', // Diameter of the circle
  anchor: 'ANCHOR_MIDDLE',
  shape: 'SHAPE_CIRCLE'
};

<MaskLayer holePosition={circlePosition} />
```

### With Click Handlers

```tsx
function App() {
  const [overlayClicks, setOverlayClicks] = useState(0);
  const [unmaskedClicks, setUnmaskedClicks] = useState(0);

  return (
    <div 
      style={{ position: 'relative', width: '100vw', height: '100vh' }}
      onClick={() => setUnmaskedClicks(prev => prev + 1)}
    >
      <MaskLayer
        holePosition={position}
        onOverlayClick={() => setOverlayClicks(prev => prev + 1)}
      />
      
      <div>Overlay clicks: {overlayClicks}</div>
      <div>Unmasked clicks: {unmaskedClicks}</div>
    </div>
  );
}
```

### Custom Styling

```tsx
<MaskLayer
  holePosition={position}
  backgroundColor="rgba(255, 0, 0, 0.3)"
  maskIndex={9999}
/>
```

## Shape-Specific Behavior

### Rectangle (`SHAPE_RECTANGLE`)
- **Format**: `"widthpx heightpx"` or `"width% height%"`
- **CSS-like**: `"200px"` becomes `"200px 200px"` (square rectangle)
- **Example**: `"200px 150px"` creates 200×150 rectangle

### Square (`SHAPE_SQUARE`)
- **Format**: `"numberpx"` or `"number%"`
- **CSS-like**: `"200px 100px"` uses only `"200px"` (ignores second value)
- **Example**: `"200px"` creates 200×200 square

### Circle (`SHAPE_CIRCLE`)
- **Format**: `"numberpx"` or `"number%"`
- **CSS-like**: `"200px 100px"` uses only `"200px"` (ignores second value)
- **Example**: `"200px"` creates 200px diameter circle
- **Technology**: Uses CSS masking for perfect circular holes

## Requirements

- React 16.8.0 or higher
- React DOM 16.8.0 or higher
- Modern browsers with CSS mask support

## Browser Support

- ✅ Chrome/Edge (with WebKit prefix support)
- ✅ Firefox
- ⚠️ Safari (limited mask support)

## License

MIT