# @edgepdf/viewer-react-native

React Native wrapper for the EdgePDF viewer. The viewer itself runs as web
code inside a `WebView`; this package renders that WebView and speaks a
`postMessage` bridge to it.

## Marker icons

Markers render one of two ways:

|         | Field                                               | Appearance                                             |
| ------- | --------------------------------------------------- | ------------------------------------------------------ |
| Legacy  | `iconType: 'pin-gray' \| 'pin-yellow' \| 'pin-red'` | Fixed 30×40 PNG pin                                    |
| Dynamic | `icon: MarkerIcon`                                  | Any allowlisted icon, at a runtime name / color / size |

`icon` takes precedence when both are set.

```tsx
import { useMarkers } from '@edgepdf/viewer-react-native';
import type { MarkerIcon } from '@edgepdf/viewer-react-native';

const { addMarker } = useMarkers();

addMarker({
  id: 'm1',
  position: [-35, 49],
  x: 4966,
  y: 3508,
  zoom: 1,
  label: 'Blocked penetration',
  icon: { name: 'triangle-alert', color: '#ef4444', size: 32 },
});
```

`MarkerIcon` fields — only `name` is required:

- `name` — icon name from the viewer's allowlist, e.g. `'map-pin'`
- `color` — stroke color (default `#333333`)
- `size` — rendered px, square (default `24`)
- `anchor` — `'center'` (default) puts the icon's midpoint on the coordinate;
  `'bottom'` puts its bottom edge there, for pin-style icons
- `strokeWidth` — SVG stroke width, `0`–`4`. `0` means no stroke (default `2`)
- `fill` — fill color, `#RGB`/`#RRGGBB` (default unfilled, `fill="none"`)
- `fillOpacity` — fill alpha, `0`–`1` (default fully opaque; no-op without `fill`)
- `strokeOpacity` — stroke alpha, `0`–`1` (default fully opaque)
- `strokeLinecap` — `'butt' | 'round' | 'square'` (default `'round'`)
- `strokeLinejoin` — `'miter' | 'round' | 'bevel'` (default `'round'`)
- `strokeDasharray` — dash pattern, e.g. `'4 2'` (default solid)

`icon` reaches the WebView as plain JSON over the bridge — this package does
not render markers itself. `fill`, `fillOpacity`, `strokeOpacity`,
`strokeLinecap`, `strokeLinejoin` and `strokeDasharray` are applied as SVG
presentation attributes by `@edgepdf/viewer-js` inside the WebView; see its
README for the full field-to-attribute mapping.

### The icon set is frozen at build time

This is the constraint to plan around. The RN app loads the viewer as a
**single self-contained `viewer.html` string inlined into the Metro bundle** —
there is no server and no base URL, so the viewer cannot fetch an icon at
runtime. Every renderable icon is statically bundled from a curated allowlist
in `@edgepdf/viewer-js`.

Consequences:

- **Per-marker `color`, `size`, `anchor`, `strokeWidth`, `fill`, `fillOpacity`,
  `strokeOpacity`, `strokeLinecap`, `strokeLinejoin` and `strokeDasharray`
  are fully dynamic.** Change them at runtime, from the server, from user
  input — they are just values in the marker payload.
- **Adding a new icon `name` is a rebuild**, not a config change:
  1. add the icon to the allowlist in `packages/viewer-js/src/lib/icon-registry.ts`
  2. `nx build @edgepdf/web-runtime` — regenerates the baked `viewer.html`
  3. rebuild and re-release the native app
- An OTA JS update is **not** enough on its own, because the icon data lives
  inside the inlined HTML string.
- A `name` that is not in the allowlist does not throw — the viewer logs a
  warning and renders a fallback glyph. A version-skewed app (older
  `viewer.html`, newer server data) therefore degrades to fallback glyphs
  rather than breaking the map.

### Discovering what a build can render

The registry lives inside `viewer.html`, so native code cannot call into it
directly. The viewer reports the list on `PDF_MAP_READY` instead, and
`useMarkers()` surfaces it:

```tsx
const { availableMarkerIcons } = useMarkers();
```

It is empty until the map is ready, and also empty against a viewer build
that predates this feature — treat empty as "unknown", not as "nothing is
renderable".

### Tap-to-create markers

Markers created by tapping the map are legacy PNG pins unless you say
otherwise. Set a default icon at mount, or change it at runtime:

```tsx
<EdgePDFViewer
  enableAnnotation
  defaultMarkerIcon={{ name: 'circle-dot', color: '#2563eb' }}
  ...
/>
```

```tsx
const { setDefaultMarkerIcon } = useMarkers();

setDefaultMarkerIcon({ name: 'flame', color: '#f97316' });
setDefaultMarkerIcon(null); // back to the PNG pin
```

## Running unit tests

Run `nx test @edgepdf/viewer-react-native` to execute the unit tests via [Jest](https://jestjs.io).
