npm package: @siali/canvas-viewer
Description: Canvas viewer for Siali
Latest version: 0.3.0

# @siali/canvas-viewer

Canvas viewer component with support for rectangles and polygons annotations.

## Installation

```bash
pnpm add @siali/canvas-viewer
```

## Usage

### Basic Example (Rectangles)

```typescript
import { CanvasViewer } from '@siali/canvas-viewer'

<CanvasViewer
  width={600}
  height={400}
  shapeList={[
    [100, 100, 50, 30], // [centerX, centerY, width, height]
    [200, 150, 80, 40],
  ]}
  shapeType="rectangle"
  colorList={[
    { fill: "#ff0000", stroke: "#ffffff", textColor: "#ffffff" },
    { fill: "#00ff00", stroke: "#ffffff", textColor: "#ffffff" },
  ]}
  textList={["Label 1", "Label 2"]}
  imageState={imageState}
  image={image}
/>
```

### Polygons Example

```typescript
<CanvasViewer
  width={600}
  height={400}
  shapeList={[
    [100, 100, 150, 120, 200, 200, 50, 180], // [x1, y1, x2, y2, x3, y3, x4, y4, ...]
  ]}
  shapeType="polygon"
  colorList={[
    { fill: "#ff0000", stroke: "#ffffff", textColor: "#ffffff" },
  ]}
  textList={["Polygon Label"]}
  imageState={imageState}
  image={image}
/>
```

### Mixed Shape Types

To render a mix of rectangles and polygons, set `shapeType="multiShape"`. This changes the expected format of the `shapeList` prop to an array of objects, where each object explicitly defines the shape's type and its data.

This is the recommended approach for handling mixed shapes as it is explicit and robust.

```typescript
<CanvasViewer
  width={600}
  height={400}
  shapeType="multiShape"
  shapeList={[
    { type: "rectangle", value: [100, 100, 50, 30] },
    { type: "polygon", value: [100, 100, 150, 120, 200, 200] },
    { type: "rectangle", value: [200, 150, 80, 40] },
  ]}
  colorList={[
    { fill: "#ff0000", stroke: "#ffffff", textColor: "#ffffff" },
    { fill: "#0000ff", stroke: "#ffffff", textColor: "#ffffff" },
    { fill: "#00ff00", stroke: "#ffffff", textColor: "#ffffff" },
  ]}
  textList={["Rectangle 1", "Polygon 1", "Rectangle 2"]}
  imageState={imageState}
  image={image}
/>
```

### Advanced Styling Examples

#### Custom Fill and Stroke

```typescript
<CanvasViewer
  shapeList={rectangles}
  showFill={false} // No fill, only stroke
  strokeWidth={3} // Thicker border
  cornerRadius={8} // More rounded corners
/>
```

#### Global Shape Styling

```typescript
<CanvasViewer
  shapeList={rectangles}
  strokeWidth={3} // All shapes have thick border
  cornerRadius={10} // All rectangles have rounded corners
  showFill={false} // No fill, only borders
  labelOpacity={0.8} // More opaque labels
/>
```

#### Mixed Visibility

```typescript
<CanvasViewer
  shapeList={shapes}
  visibilityList={[true, false, true]} // Hide middle shape
  showFill={true}
  dashedList={[false, false, true]} // Last shape dashed
/>
```

#### Secondary Labels

```typescript
// Secondary labels positioned above primary labels (always on TOP). Secondary background is gray.
<CanvasViewer
  shapeList={shapes}
  textList={["Label 1", "Label 2", "Label 3"]}
  secondaryTextList={["Shape ok", "Shape ko", ""]} // Empty strings are ignored
/>

```

## Breaking Changes

### 🚨 v0.2.0:

**✅ After (v0.2.0):**

```typescript
// For mixed shapes, be explicit
<CanvasViewer shapeType="multiShape" shapeList={shapeObjects} />

// For single shape types, be explicit
<CanvasViewer shapeType="polygon" shapeList={polygons} />
<CanvasViewer shapeType="rectangle" shapeList={rectangles} />
```

### 🚨 v0.1.3 → v0.1.4: API Changes

**❌ Before (v0.1.3):**

```typescript
<CanvasViewer
  bboxList={rectangles}
  shapeList={polygons}
/>
```

**✅ After (v0.1.4):**

```typescript
<CanvasViewer
  shapeList={rectangles} // or polygons, or mixed
  shapeType="rectangle" // optional, auto-detected
/>
```

### Migration Guide

1. **Rename prop:**
   - `bboxList` → `shapeList`

2. **Simplify conditional logic:**

   ```typescript
   // Before:
   bboxList={usePolygons ? undefined : rectangles}
   shapeList={usePolygons ? polygons : undefined}

   // After:
   shapeList={usePolygons ? polygons : rectangles}
   ```

3. **No data format changes needed:**
   - Rectangle arrays remain: `[centerX, centerY, width, height]`
   - Polygon arrays remain: `[x1, y1, x2, y2, x3, y3, ...]`

## Props

### Core Props

| Prop         | Type                                       | Default       | Description                                                               |
| ------------ | ------------------------------------------ | ------------- | ------------------------------------------------------------------------- |
| `width`      | `number`                                   | required      | Canvas width                                                              |
| `height`     | `number`                                   | required      | Canvas height                                                             |
| `shapeList`  | `ShapeData[]`                              | `[]`          | Array of shapes (see `shapeType` prop)                                    |
| `shapeType`  | `"rectangle" \| "polygon" \| "multiShape"` | `"rectangle"` | Defines how to interpret `shapeList`. Use `"multiShape"` for mixed types. |
| `colorList`  | `ColorLabel[]`                             | `[]`          | Colors for each shape                                                     |
| `textList`   | `string[]`                                 | `[]`          | Labels for each shape                                                     |
| `enableZoom` | `boolean`                                  | `false`       | Enable zoom and pan                                                       |
| `imageState` | `object`                                   | undefined     | Image dimensions and position                                             |
| `image`      | `HTMLImageElement`                         | undefined     | Image to display behind shapes                                            |

### Label Props

| Prop                | Type        | Default   | Description                                                      |
| ------------------- | ----------- | --------- | ---------------------------------------------------------------- | -------------------------------- |
| `showLabels`        | `boolean`   | `true`    | Show/hide labels                                                 |
| `labelPosition`     | `"TOP"      | "BOTTOM"` | `"TOP"`                                                          | Label position relative to shape |
| `labelOpacity`      | `number`    | `0.2`     | Opacity of label background (0-1)                                |
| `hideLabelsList`    | `boolean[]` | `[]`      | Hide specific labels by index                                    |
| `iconMap`           | `object`    | `{}`      | Icon mapping for labels                                          |
| `secondaryTextList` | `string[]`  | `[]`      | Secondary labels for each shape (always on TOP, gray background) |

### Shape Styling Props

| Prop           | Type        | Default | Description                      |
| -------------- | ----------- | ------- | -------------------------------- |
| `showFill`     | `boolean`   | `true`  | Show/hide shape fill             |
| `strokeWidth`  | `number`    | `2`     | Stroke width for all shapes      |
| `cornerRadius` | `number`    | `4`     | Corner radius for all rectangles |
| `dashedList`   | `boolean[]` | `[]`    | Dashed border for each shape     |

### Visibility & Interaction Props

| Prop             | Type        | Default | Description                             |
| ---------------- | ----------- | ------- | --------------------------------------- |
| `visibilityList` | `boolean[]` | `[]`    | Show/hide specific shapes by index      |
| `ignoreList`     | `boolean[]` | `[]`    | Ignore specific shapes (no interaction) |

## Types

```typescript
type bbox = [number, number, number, number] // [centerX, centerY, width, height]
type polygon = number[] // [x1, y1, x2, y2, x3, y3, ...]
type ShapeType = "rectangle" | "polygon" | "multiShape"

// The object format used when shapeType="multiShape"
type ShapeObject = {
  type: "rectangle" | "polygon"
  value: bbox | polygon
}

// The shapeList prop accepts an array of one of these types
type ShapeData = bbox | polygon | ShapeObject

type ColorLabel = {
  fill: string
  stroke: string
  textColor: string
}
```

## Version History

- **v0.1.7**: Added `secondaryTextList` (secondary labels always on TOP with gray background)
- **v0.1.4**: Added polygon support, breaking changes in API
- **v0.1.3**: Rectangle support only