@edgepdf/viewer-react 0.0.32 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +571 -12
- package/dist/index.css +55 -44
- package/dist/index.js +115 -7
- package/dist/lib/use-markers.d.ts +6 -0
- package/dist/lib/use-markers.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ pnpm add @edgepdf/viewer-react @edgepdf/viewer-js
|
|
|
12
12
|
yarn add @edgepdf/viewer-react @edgepdf/viewer-js
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Quick Start
|
|
16
16
|
|
|
17
17
|
### Next.js
|
|
18
18
|
|
|
@@ -98,6 +98,7 @@ import {
|
|
|
98
98
|
EdgePDFViewer,
|
|
99
99
|
useMarkers,
|
|
100
100
|
useViewer,
|
|
101
|
+
useZoom,
|
|
101
102
|
} from '@edgepdf/viewer-react';
|
|
102
103
|
import type { ViewerConfig } from '@edgepdf/types';
|
|
103
104
|
|
|
@@ -113,16 +114,17 @@ const config: ViewerConfig = {
|
|
|
113
114
|
};
|
|
114
115
|
|
|
115
116
|
function MarkerPanel() {
|
|
116
|
-
const { markers } = useMarkers();
|
|
117
|
+
const { markers, addMarker, removeMarker } = useMarkers();
|
|
117
118
|
const { viewer, isInitialized } = useViewer();
|
|
118
|
-
|
|
119
|
-
// You can interact with viewer + markers here,
|
|
120
|
-
// independent of where <EdgePDFViewer /> is rendered.
|
|
119
|
+
const { zoomIn, zoomOut, currentZoom } = useZoom();
|
|
121
120
|
|
|
122
121
|
return (
|
|
123
122
|
<div>
|
|
124
123
|
<div>Initialized: {isInitialized ? 'yes' : 'no'}</div>
|
|
125
124
|
<div>Total markers: {markers.length}</div>
|
|
125
|
+
<div>Current zoom: {currentZoom}</div>
|
|
126
|
+
<button onClick={zoomIn}>Zoom In</button>
|
|
127
|
+
<button onClick={zoomOut}>Zoom Out</button>
|
|
126
128
|
</div>
|
|
127
129
|
);
|
|
128
130
|
}
|
|
@@ -130,20 +132,577 @@ function MarkerPanel() {
|
|
|
130
132
|
function App() {
|
|
131
133
|
return (
|
|
132
134
|
<ViewerProvider>
|
|
133
|
-
{/* This can be on one route/screen */}
|
|
134
135
|
<MarkerPanel />
|
|
135
|
-
|
|
136
|
-
{/* And EdgePDFViewer can be on another */}
|
|
137
136
|
<EdgePDFViewer config={config} />
|
|
138
137
|
</ViewerProvider>
|
|
139
138
|
);
|
|
140
139
|
}
|
|
141
140
|
```
|
|
142
141
|
|
|
143
|
-
##
|
|
142
|
+
## API Reference
|
|
143
|
+
|
|
144
|
+
### Components
|
|
145
|
+
|
|
146
|
+
#### `<ViewerProvider>`
|
|
147
|
+
|
|
148
|
+
Provides viewer context to child components. Must wrap all components that use viewer hooks.
|
|
149
|
+
|
|
150
|
+
**Props:**
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
interface ViewerProviderProps {
|
|
154
|
+
children: ReactNode;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Example:**
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
<ViewerProvider>
|
|
162
|
+
<App />
|
|
163
|
+
</ViewerProvider>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
#### `<EdgePDFViewer>`
|
|
169
|
+
|
|
170
|
+
Main React component for the EdgePDF viewer. Must be used within a `ViewerProvider`.
|
|
171
|
+
|
|
172
|
+
**Props:**
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
interface EdgePDFViewerProps {
|
|
176
|
+
/** Viewer configuration */
|
|
177
|
+
config: ViewerConfig;
|
|
178
|
+
/** Optional map options */
|
|
179
|
+
mapOptions?: MapOptions;
|
|
180
|
+
/** Optional className for the container */
|
|
181
|
+
className?: string;
|
|
182
|
+
/** Optional style for the container */
|
|
183
|
+
style?: React.CSSProperties;
|
|
184
|
+
/** Show zoom controls (default: true) */
|
|
185
|
+
showZoomControls?: boolean;
|
|
186
|
+
/** Zoom controls position (only used if showZoomControls is true) */
|
|
187
|
+
zoomControlsPosition?:
|
|
188
|
+
| 'top-left'
|
|
189
|
+
| 'top-right'
|
|
190
|
+
| 'bottom-left'
|
|
191
|
+
| 'bottom-right';
|
|
192
|
+
/** Show zoom level in zoom controls (only used if showZoomControls is true) */
|
|
193
|
+
showZoomLevel?: boolean;
|
|
194
|
+
/** Enable annotation functionality (default: true) */
|
|
195
|
+
enableAnnotation?: boolean;
|
|
196
|
+
/** Show edit button in marker action controls (default: true) */
|
|
197
|
+
showEditButton?: boolean;
|
|
198
|
+
/** Show delete button in marker action controls (default: true) */
|
|
199
|
+
showDeleteButton?: boolean;
|
|
200
|
+
/** Default zoom level for initial view */
|
|
201
|
+
defaultZoomLevel?: number;
|
|
202
|
+
/** Callback when markers/pins are updated */
|
|
203
|
+
onPinsUpdate?: (pins: Marker[]) => void;
|
|
204
|
+
/** Callback when a marker is clicked */
|
|
205
|
+
onMarkerClick?: (marker: Marker) => void;
|
|
206
|
+
/** Callback when a marker is updated */
|
|
207
|
+
onMarkerUpdate?: (marker: Marker) => void;
|
|
208
|
+
/** Callback when a marker is deleted */
|
|
209
|
+
onMarkerDelete?: (marker: Marker) => void;
|
|
210
|
+
/** Callback when a marker is added (created by user tap/click) */
|
|
211
|
+
onMarkerAdd?: (marker: Marker) => void;
|
|
212
|
+
/** Default pins to preload (uses internal import) */
|
|
213
|
+
defaultPins?: Marker[] | MarkerData;
|
|
214
|
+
/** Children components */
|
|
215
|
+
children?: ReactNode;
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Example:**
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
<EdgePDFViewer
|
|
223
|
+
config={config}
|
|
224
|
+
showZoomControls={true}
|
|
225
|
+
zoomControlsPosition="top-right"
|
|
226
|
+
enableAnnotation={true}
|
|
227
|
+
onMarkerClick={(marker) => console.log('Marker clicked:', marker)}
|
|
228
|
+
onPinsUpdate={(pins) => console.log('Pins updated:', pins)}
|
|
229
|
+
/>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
#### `<ZoomControls>`
|
|
235
|
+
|
|
236
|
+
React component for zoom controls. Uses the JS library's zoom controls internally.
|
|
237
|
+
|
|
238
|
+
**Props:**
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
interface ZoomControlsProps {
|
|
242
|
+
/** Optional className for the container */
|
|
243
|
+
className?: string;
|
|
244
|
+
/** Optional style for the container */
|
|
245
|
+
style?: React.CSSProperties;
|
|
246
|
+
/** Position of the controls */
|
|
247
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
248
|
+
/** Show zoom level display */
|
|
249
|
+
showZoomLevel?: boolean;
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Example:**
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
<EdgePDFViewer config={config}>
|
|
257
|
+
<ZoomControls position="top-right" showZoomLevel={true} />
|
|
258
|
+
</EdgePDFViewer>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
### Hooks
|
|
264
|
+
|
|
265
|
+
#### `useViewer()`
|
|
266
|
+
|
|
267
|
+
Hook to access and interact with the viewer instance.
|
|
268
|
+
|
|
269
|
+
**Returns:**
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
interface UseViewerReturn {
|
|
273
|
+
/** Viewer instance */
|
|
274
|
+
viewer: EdgePdfViewer | null;
|
|
275
|
+
/** Whether viewer is initialized */
|
|
276
|
+
isInitialized: boolean;
|
|
277
|
+
/** Get the Leaflet map instance */
|
|
278
|
+
getMap: () => L.Map | null;
|
|
279
|
+
/** Check if viewer is initialized */
|
|
280
|
+
checkInitialized: () => boolean;
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Example:**
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
function MyComponent() {
|
|
288
|
+
const { viewer, isInitialized, getMap } = useViewer();
|
|
289
|
+
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
if (isInitialized && viewer) {
|
|
292
|
+
const map = getMap();
|
|
293
|
+
// Use map...
|
|
294
|
+
}
|
|
295
|
+
}, [isInitialized, viewer, getMap]);
|
|
296
|
+
|
|
297
|
+
return <div>Viewer is {isInitialized ? 'ready' : 'loading'}</div>;
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
#### `useMarkers()`
|
|
304
|
+
|
|
305
|
+
Hook to manage markers in the viewer.
|
|
306
|
+
|
|
307
|
+
**Returns:**
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
interface UseMarkersReturn {
|
|
311
|
+
/** Current markers */
|
|
312
|
+
markers: Marker[];
|
|
313
|
+
/** Add a new marker */
|
|
314
|
+
addMarker: (options: CreateMarkerOptions) => Marker | null;
|
|
315
|
+
/** Remove a marker by ID */
|
|
316
|
+
removeMarker: (id: string) => boolean;
|
|
317
|
+
/** Update a marker */
|
|
318
|
+
updateMarker: (id: string, updates: Partial<Marker>) => boolean;
|
|
319
|
+
/** Get a marker by ID */
|
|
320
|
+
getMarker: (id: string) => Marker | null;
|
|
321
|
+
/** Get all markers */
|
|
322
|
+
getAllMarkers: () => Marker[];
|
|
323
|
+
/** Export markers as JSON */
|
|
324
|
+
exportMarkers: () => MarkerData;
|
|
325
|
+
/** Import markers from JSON */
|
|
326
|
+
importMarkers: (data: MarkerData) => boolean;
|
|
327
|
+
/** Clear all markers */
|
|
328
|
+
clearMarkers: () => void;
|
|
329
|
+
/** Focus on a marker by panning and zooming to its position */
|
|
330
|
+
focusMarker: (
|
|
331
|
+
markerOrId: string | Marker,
|
|
332
|
+
options?: {
|
|
333
|
+
zoom?: number;
|
|
334
|
+
animate?: boolean;
|
|
335
|
+
duration?: number;
|
|
336
|
+
}
|
|
337
|
+
) => boolean;
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Example:**
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
function MarkerControls() {
|
|
345
|
+
const {
|
|
346
|
+
markers,
|
|
347
|
+
addMarker,
|
|
348
|
+
removeMarker,
|
|
349
|
+
updateMarker,
|
|
350
|
+
exportMarkers,
|
|
351
|
+
importMarkers,
|
|
352
|
+
clearMarkers,
|
|
353
|
+
focusMarker,
|
|
354
|
+
} = useMarkers();
|
|
355
|
+
|
|
356
|
+
const handleAddMarker = () => {
|
|
357
|
+
addMarker({
|
|
358
|
+
position: [100, 200],
|
|
359
|
+
x: 100,
|
|
360
|
+
y: 200,
|
|
361
|
+
zoom: 1,
|
|
362
|
+
label: 'New Marker',
|
|
363
|
+
});
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const handleExport = () => {
|
|
367
|
+
const data = exportMarkers();
|
|
368
|
+
console.log(JSON.stringify(data, null, 2));
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const handleFocus = (markerId: string) => {
|
|
372
|
+
focusMarker(markerId, { zoom: 3, animate: true });
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
return (
|
|
376
|
+
<div>
|
|
377
|
+
<button onClick={handleAddMarker}>Add Marker</button>
|
|
378
|
+
<button onClick={handleExport}>Export Markers</button>
|
|
379
|
+
<button onClick={clearMarkers}>Clear All</button>
|
|
380
|
+
{markers.map((marker) => (
|
|
381
|
+
<div key={marker.id}>
|
|
382
|
+
{marker.label}
|
|
383
|
+
<button onClick={() => removeMarker(marker.id)}>Remove</button>
|
|
384
|
+
<button onClick={() => handleFocus(marker.id)}>Focus</button>
|
|
385
|
+
</div>
|
|
386
|
+
))}
|
|
387
|
+
</div>
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Methods:**
|
|
393
|
+
|
|
394
|
+
- **`addMarker(options: CreateMarkerOptions): Marker | null`** - Creates a new marker. Returns the created marker or `null` if viewer is not initialized.
|
|
395
|
+
|
|
396
|
+
- **`removeMarker(id: string): boolean`** - Removes a marker by ID. Returns `true` if successful, `false` otherwise.
|
|
397
|
+
|
|
398
|
+
- **`updateMarker(id: string, updates: Partial<Marker>): boolean`** - Updates a marker's properties. Returns `true` if successful, `false` otherwise.
|
|
399
|
+
|
|
400
|
+
- **`getMarker(id: string): Marker | null`** - Gets a marker by ID. Returns the marker or `null` if not found.
|
|
401
|
+
|
|
402
|
+
- **`getAllMarkers(): Marker[]`** - Gets all markers. Returns an array of all markers.
|
|
403
|
+
|
|
404
|
+
- **`exportMarkers(): MarkerData`** - Exports all markers as JSON data. Returns a `MarkerData` object.
|
|
405
|
+
|
|
406
|
+
- **`importMarkers(data: MarkerData): boolean`** - Imports markers from JSON data. Returns `true` if successful, `false` otherwise.
|
|
407
|
+
|
|
408
|
+
- **`clearMarkers(): void`** - Removes all markers.
|
|
409
|
+
|
|
410
|
+
- **`focusMarker(markerOrId: string | Marker, options?: FocusOptions): boolean`** - Focuses on a marker by panning and zooming. Returns `true` if successful, `false` otherwise.
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
#### `useZoom()`
|
|
415
|
+
|
|
416
|
+
Hook to manage zoom in the viewer.
|
|
417
|
+
|
|
418
|
+
**Returns:**
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
interface UseZoomReturn {
|
|
422
|
+
/** Current zoom state */
|
|
423
|
+
zoomState: ZoomState | null;
|
|
424
|
+
/** Current zoom level */
|
|
425
|
+
currentZoom: number;
|
|
426
|
+
/** Minimum zoom level */
|
|
427
|
+
minZoom: number;
|
|
428
|
+
/** Maximum zoom level */
|
|
429
|
+
maxZoom: number;
|
|
430
|
+
/** Zoom in */
|
|
431
|
+
zoomIn: () => void;
|
|
432
|
+
/** Zoom out */
|
|
433
|
+
zoomOut: () => void;
|
|
434
|
+
/** Set zoom level */
|
|
435
|
+
setZoom: (zoom: number) => void;
|
|
436
|
+
/** Check if can zoom in */
|
|
437
|
+
canZoomIn: () => boolean;
|
|
438
|
+
/** Check if can zoom out */
|
|
439
|
+
canZoomOut: () => boolean;
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Example:**
|
|
444
|
+
|
|
445
|
+
```tsx
|
|
446
|
+
function ZoomControls() {
|
|
447
|
+
const {
|
|
448
|
+
zoomIn,
|
|
449
|
+
zoomOut,
|
|
450
|
+
setZoom,
|
|
451
|
+
currentZoom,
|
|
452
|
+
minZoom,
|
|
453
|
+
maxZoom,
|
|
454
|
+
canZoomIn,
|
|
455
|
+
canZoomOut,
|
|
456
|
+
} = useZoom();
|
|
457
|
+
|
|
458
|
+
return (
|
|
459
|
+
<div>
|
|
460
|
+
<button onClick={zoomOut} disabled={!canZoomOut()}>
|
|
461
|
+
Zoom Out
|
|
462
|
+
</button>
|
|
463
|
+
<span>
|
|
464
|
+
Zoom: {currentZoom} ({minZoom}-{maxZoom})
|
|
465
|
+
</span>
|
|
466
|
+
<button onClick={zoomIn} disabled={!canZoomIn()}>
|
|
467
|
+
Zoom In
|
|
468
|
+
</button>
|
|
469
|
+
<button onClick={() => setZoom(2)}>Set Zoom to 2</button>
|
|
470
|
+
</div>
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Methods:**
|
|
476
|
+
|
|
477
|
+
- **`zoomIn(): void`** - Zooms in by one level.
|
|
478
|
+
|
|
479
|
+
- **`zoomOut(): void`** - Zooms out by one level.
|
|
480
|
+
|
|
481
|
+
- **`setZoom(zoom: number): void`** - Sets a specific zoom level.
|
|
482
|
+
|
|
483
|
+
- **`canZoomIn(): boolean`** - Checks if can zoom in. Returns `true` if current zoom is less than max zoom.
|
|
484
|
+
|
|
485
|
+
- **`canZoomOut(): boolean`** - Checks if can zoom out. Returns `true` if current zoom is greater than min zoom.
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
#### `useViewerContext()`
|
|
490
|
+
|
|
491
|
+
Hook to access viewer context directly. Usually you should use `useViewer()`, `useMarkers()`, or `useZoom()` instead.
|
|
492
|
+
|
|
493
|
+
**Returns:**
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
interface ViewerContextValue {
|
|
497
|
+
/** Viewer instance */
|
|
498
|
+
viewer: EdgePdfViewer | null;
|
|
499
|
+
/** Whether viewer is initialized */
|
|
500
|
+
isInitialized: boolean;
|
|
501
|
+
/** Current markers */
|
|
502
|
+
markers: Marker[];
|
|
503
|
+
/** Current zoom state */
|
|
504
|
+
zoomState: ZoomState | null;
|
|
505
|
+
/** Internal: Function to update context value */
|
|
506
|
+
setContextValue: (
|
|
507
|
+
updates: Partial<Omit<ViewerContextValue, 'setContextValue'>>
|
|
508
|
+
) => void;
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
**Throws:**
|
|
513
|
+
|
|
514
|
+
- `Error` if used outside `ViewerProvider`
|
|
515
|
+
|
|
516
|
+
**Example:**
|
|
517
|
+
|
|
518
|
+
```tsx
|
|
519
|
+
function MyComponent() {
|
|
520
|
+
const { viewer, isInitialized, markers, zoomState } = useViewerContext();
|
|
521
|
+
// Use context values...
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## Type Exports
|
|
528
|
+
|
|
529
|
+
The library also exports TypeScript types for convenience:
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
import type {
|
|
533
|
+
EdgePDFViewerProps,
|
|
534
|
+
ZoomControlsProps,
|
|
535
|
+
ViewerContextValue,
|
|
536
|
+
ViewerProviderProps,
|
|
537
|
+
UseViewerReturn,
|
|
538
|
+
UseMarkersReturn,
|
|
539
|
+
UseZoomReturn,
|
|
540
|
+
} from '@edgepdf/viewer-react';
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## Examples
|
|
544
|
+
|
|
545
|
+
### Basic Usage with Markers
|
|
546
|
+
|
|
547
|
+
```tsx
|
|
548
|
+
import {
|
|
549
|
+
ViewerProvider,
|
|
550
|
+
EdgePDFViewer,
|
|
551
|
+
useMarkers,
|
|
552
|
+
} from '@edgepdf/viewer-react';
|
|
553
|
+
|
|
554
|
+
function MarkerList() {
|
|
555
|
+
const { markers, addMarker, removeMarker } = useMarkers();
|
|
556
|
+
|
|
557
|
+
return (
|
|
558
|
+
<div>
|
|
559
|
+
<h3>Markers ({markers.length})</h3>
|
|
560
|
+
{markers.map((marker) => (
|
|
561
|
+
<div key={marker.id}>
|
|
562
|
+
<strong>{marker.label}</strong>
|
|
563
|
+
<button onClick={() => removeMarker(marker.id)}>Remove</button>
|
|
564
|
+
</div>
|
|
565
|
+
))}
|
|
566
|
+
</div>
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function App() {
|
|
571
|
+
const config = {
|
|
572
|
+
tileUrl: 'https://example.com/tiles/{z}/{x}/{y}.png',
|
|
573
|
+
imageInfo: {
|
|
574
|
+
width: 2000,
|
|
575
|
+
height: 3000,
|
|
576
|
+
tileSize: 256,
|
|
577
|
+
maxZoom: 5,
|
|
578
|
+
minZoom: 0,
|
|
579
|
+
},
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
return (
|
|
583
|
+
<ViewerProvider>
|
|
584
|
+
<div style={{ display: 'flex' }}>
|
|
585
|
+
<div style={{ width: '300px' }}>
|
|
586
|
+
<MarkerList />
|
|
587
|
+
</div>
|
|
588
|
+
<div style={{ flex: 1, height: '100vh' }}>
|
|
589
|
+
<EdgePDFViewer config={config} />
|
|
590
|
+
</div>
|
|
591
|
+
</div>
|
|
592
|
+
</ViewerProvider>
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### With Callbacks
|
|
598
|
+
|
|
599
|
+
```tsx
|
|
600
|
+
function App() {
|
|
601
|
+
const config = {
|
|
602
|
+
tileUrl: 'https://example.com/tiles/{z}/{x}/{y}.png',
|
|
603
|
+
imageInfo: {
|
|
604
|
+
width: 2000,
|
|
605
|
+
height: 3000,
|
|
606
|
+
tileSize: 256,
|
|
607
|
+
maxZoom: 5,
|
|
608
|
+
minZoom: 0,
|
|
609
|
+
},
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
const handleMarkerClick = (marker: Marker) => {
|
|
613
|
+
console.log('Marker clicked:', marker);
|
|
614
|
+
};
|
|
144
615
|
|
|
145
|
-
|
|
616
|
+
const handlePinsUpdate = (pins: Marker[]) => {
|
|
617
|
+
console.log('Pins updated:', pins);
|
|
618
|
+
// Save to backend, localStorage, etc.
|
|
619
|
+
};
|
|
146
620
|
|
|
147
|
-
|
|
621
|
+
return (
|
|
622
|
+
<ViewerProvider>
|
|
623
|
+
<EdgePDFViewer
|
|
624
|
+
config={config}
|
|
625
|
+
onMarkerClick={handleMarkerClick}
|
|
626
|
+
onPinsUpdate={handlePinsUpdate}
|
|
627
|
+
enableAnnotation={true}
|
|
628
|
+
/>
|
|
629
|
+
</ViewerProvider>
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
```
|
|
148
633
|
|
|
149
|
-
|
|
634
|
+
### Custom Zoom Controls
|
|
635
|
+
|
|
636
|
+
```tsx
|
|
637
|
+
function CustomZoomControls() {
|
|
638
|
+
const { zoomIn, zoomOut, currentZoom, canZoomIn, canZoomOut } = useZoom();
|
|
639
|
+
|
|
640
|
+
return (
|
|
641
|
+
<div style={{ position: 'absolute', top: 10, right: 10, zIndex: 1000 }}>
|
|
642
|
+
<button onClick={zoomOut} disabled={!canZoomOut()}>
|
|
643
|
+
−
|
|
644
|
+
</button>
|
|
645
|
+
<span>{currentZoom}</span>
|
|
646
|
+
<button onClick={zoomIn} disabled={!canZoomIn()}>
|
|
647
|
+
+
|
|
648
|
+
</button>
|
|
649
|
+
</div>
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function App() {
|
|
654
|
+
return (
|
|
655
|
+
<ViewerProvider>
|
|
656
|
+
<EdgePDFViewer config={config} showZoomControls={false}>
|
|
657
|
+
<CustomZoomControls />
|
|
658
|
+
</EdgePDFViewer>
|
|
659
|
+
</ViewerProvider>
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### Import/Export Markers
|
|
665
|
+
|
|
666
|
+
```tsx
|
|
667
|
+
function MarkerManager() {
|
|
668
|
+
const { markers, exportMarkers, importMarkers, clearMarkers } = useMarkers();
|
|
669
|
+
|
|
670
|
+
const handleExport = () => {
|
|
671
|
+
const data = exportMarkers();
|
|
672
|
+
const blob = new Blob([JSON.stringify(data, null, 2)], {
|
|
673
|
+
type: 'application/json',
|
|
674
|
+
});
|
|
675
|
+
const url = URL.createObjectURL(blob);
|
|
676
|
+
const a = document.createElement('a');
|
|
677
|
+
a.href = url;
|
|
678
|
+
a.download = 'markers.json';
|
|
679
|
+
a.click();
|
|
680
|
+
URL.revokeObjectURL(url);
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
const handleImport = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
684
|
+
const file = event.target.files?.[0];
|
|
685
|
+
if (!file) return;
|
|
686
|
+
|
|
687
|
+
const reader = new FileReader();
|
|
688
|
+
reader.onload = (e) => {
|
|
689
|
+
try {
|
|
690
|
+
const data = JSON.parse(e.target?.result as string);
|
|
691
|
+
importMarkers(data);
|
|
692
|
+
} catch (error) {
|
|
693
|
+
console.error('Failed to import markers:', error);
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
reader.readAsText(file);
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
return (
|
|
700
|
+
<div>
|
|
701
|
+
<button onClick={handleExport}>Export Markers</button>
|
|
702
|
+
<input type="file" accept=".json" onChange={handleImport} />
|
|
703
|
+
<button onClick={clearMarkers}>Clear All</button>
|
|
704
|
+
<div>Total markers: {markers.length}</div>
|
|
705
|
+
</div>
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
```
|
package/dist/index.css
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@import "https://fonts.googleapis.com/css2?family=Lato:wght@300;400;500;600;700&display=swap";
|
|
2
|
+
|
|
1
3
|
/* packages/viewer-js/dist/index.css */
|
|
2
4
|
.leaflet-pane,
|
|
3
5
|
.leaflet-tile,
|
|
@@ -671,6 +673,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
671
673
|
display: flex;
|
|
672
674
|
align-items: center;
|
|
673
675
|
justify-content: center;
|
|
676
|
+
font-family: "Lato", sans-serif;
|
|
674
677
|
}
|
|
675
678
|
.edgepdf-marker-edit-popup-overlay {
|
|
676
679
|
position: absolute;
|
|
@@ -678,14 +681,15 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
678
681
|
left: 0;
|
|
679
682
|
right: 0;
|
|
680
683
|
bottom: 0;
|
|
681
|
-
background-color: rgba(0, 0, 0, 0.
|
|
684
|
+
background-color: rgba(0, 0, 0, 0.7);
|
|
682
685
|
cursor: pointer;
|
|
683
686
|
}
|
|
684
687
|
.edgepdf-marker-edit-popup-content {
|
|
685
688
|
position: relative;
|
|
686
|
-
background-color: #
|
|
689
|
+
background-color: #1a1a1a;
|
|
687
690
|
border-radius: 8px;
|
|
688
|
-
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.
|
|
691
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
|
692
|
+
border: 1px solid #333;
|
|
689
693
|
width: 90%;
|
|
690
694
|
max-width: 500px;
|
|
691
695
|
max-height: 90vh;
|
|
@@ -697,20 +701,20 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
697
701
|
justify-content: space-between;
|
|
698
702
|
align-items: center;
|
|
699
703
|
padding: 16px 20px;
|
|
700
|
-
border-bottom: 1px solid #
|
|
704
|
+
border-bottom: 1px solid #333;
|
|
701
705
|
}
|
|
702
706
|
.edgepdf-marker-edit-popup-header h3 {
|
|
703
707
|
margin: 0;
|
|
704
708
|
font-size: 18px;
|
|
705
709
|
font-weight: 600;
|
|
706
|
-
color: #
|
|
710
|
+
color: #fff;
|
|
707
711
|
}
|
|
708
712
|
.edgepdf-marker-edit-popup-close {
|
|
709
713
|
background: none;
|
|
710
714
|
border: none;
|
|
711
715
|
font-size: 24px;
|
|
712
716
|
line-height: 1;
|
|
713
|
-
color: #
|
|
717
|
+
color: #fff;
|
|
714
718
|
cursor: pointer;
|
|
715
719
|
padding: 0;
|
|
716
720
|
width: 32px;
|
|
@@ -722,8 +726,8 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
722
726
|
transition: background-color 0.2s ease;
|
|
723
727
|
}
|
|
724
728
|
.edgepdf-marker-edit-popup-close:hover {
|
|
725
|
-
background-color: #
|
|
726
|
-
color: #
|
|
729
|
+
background-color: #333;
|
|
730
|
+
color: #ffa500;
|
|
727
731
|
}
|
|
728
732
|
.edgepdf-marker-edit-popup-body {
|
|
729
733
|
padding: 20px;
|
|
@@ -739,52 +743,57 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
739
743
|
margin-bottom: 8px;
|
|
740
744
|
font-size: 14px;
|
|
741
745
|
font-weight: 500;
|
|
742
|
-
color: #
|
|
746
|
+
color: #fff;
|
|
743
747
|
}
|
|
744
748
|
.edgepdf-marker-edit-input {
|
|
745
749
|
width: 100%;
|
|
746
750
|
padding: 10px 12px;
|
|
747
|
-
border: 1px solid #
|
|
751
|
+
border: 1px solid #444;
|
|
748
752
|
border-radius: 4px;
|
|
749
753
|
font-size: 14px;
|
|
754
|
+
background-color: #2a2a2a;
|
|
755
|
+
color: #fff;
|
|
750
756
|
box-sizing: border-box;
|
|
751
757
|
transition: border-color 0.2s ease;
|
|
752
758
|
}
|
|
753
759
|
.edgepdf-marker-edit-input:focus {
|
|
754
760
|
outline: none;
|
|
755
|
-
border-color: #
|
|
756
|
-
box-shadow: 0 0 0 2px rgba(
|
|
761
|
+
border-color: #ffa500;
|
|
762
|
+
box-shadow: 0 0 0 2px rgba(255, 165, 0, 0.2);
|
|
763
|
+
}
|
|
764
|
+
.edgepdf-marker-edit-input::placeholder {
|
|
765
|
+
color: #888;
|
|
757
766
|
}
|
|
758
767
|
.edgepdf-marker-edit-popup-footer {
|
|
759
768
|
display: flex;
|
|
760
769
|
justify-content: flex-end;
|
|
761
770
|
gap: 12px;
|
|
762
771
|
padding: 16px 20px;
|
|
763
|
-
border-top: 1px solid #
|
|
772
|
+
border-top: 1px solid #333;
|
|
764
773
|
}
|
|
765
774
|
.edgepdf-marker-edit-button {
|
|
766
775
|
padding: 10px 20px;
|
|
767
|
-
border: 1px solid #
|
|
776
|
+
border: 1px solid #444;
|
|
768
777
|
border-radius: 4px;
|
|
769
|
-
background-color: #
|
|
770
|
-
color: #
|
|
778
|
+
background-color: #2a2a2a;
|
|
779
|
+
color: #fff;
|
|
771
780
|
cursor: pointer;
|
|
772
781
|
font-size: 14px;
|
|
773
782
|
font-weight: 500;
|
|
774
783
|
transition: all 0.2s ease;
|
|
775
784
|
}
|
|
776
785
|
.edgepdf-marker-edit-button-cancel:hover {
|
|
777
|
-
background-color: #
|
|
778
|
-
border-color: #
|
|
786
|
+
background-color: #333;
|
|
787
|
+
border-color: #555;
|
|
779
788
|
}
|
|
780
789
|
.edgepdf-marker-edit-button-save {
|
|
781
|
-
background-color: #
|
|
782
|
-
color: #
|
|
783
|
-
border-color: #
|
|
790
|
+
background-color: #ffa500;
|
|
791
|
+
color: #000;
|
|
792
|
+
border-color: #ffa500;
|
|
784
793
|
}
|
|
785
794
|
.edgepdf-marker-edit-button-save:hover {
|
|
786
|
-
background-color: #
|
|
787
|
-
border-color: #
|
|
795
|
+
background-color: #ffd700;
|
|
796
|
+
border-color: #ffd700;
|
|
788
797
|
}
|
|
789
798
|
.edgepdf-marker-delete-popup {
|
|
790
799
|
position: fixed;
|
|
@@ -796,6 +805,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
796
805
|
display: flex;
|
|
797
806
|
align-items: center;
|
|
798
807
|
justify-content: center;
|
|
808
|
+
font-family: "Lato", sans-serif;
|
|
799
809
|
}
|
|
800
810
|
.edgepdf-marker-delete-popup-overlay {
|
|
801
811
|
position: absolute;
|
|
@@ -803,14 +813,15 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
803
813
|
left: 0;
|
|
804
814
|
right: 0;
|
|
805
815
|
bottom: 0;
|
|
806
|
-
background-color: rgba(0, 0, 0, 0.
|
|
816
|
+
background-color: rgba(0, 0, 0, 0.7);
|
|
807
817
|
cursor: pointer;
|
|
808
818
|
}
|
|
809
819
|
.edgepdf-marker-delete-popup-content {
|
|
810
820
|
position: relative;
|
|
811
|
-
background-color: #
|
|
821
|
+
background-color: #1a1a1a;
|
|
812
822
|
border-radius: 8px;
|
|
813
|
-
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.
|
|
823
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
|
824
|
+
border: 1px solid #333;
|
|
814
825
|
width: 90%;
|
|
815
826
|
max-width: 500px;
|
|
816
827
|
max-height: 90vh;
|
|
@@ -822,20 +833,20 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
822
833
|
justify-content: space-between;
|
|
823
834
|
align-items: center;
|
|
824
835
|
padding: 16px 20px;
|
|
825
|
-
border-bottom: 1px solid #
|
|
836
|
+
border-bottom: 1px solid #333;
|
|
826
837
|
}
|
|
827
838
|
.edgepdf-marker-delete-popup-header h3 {
|
|
828
839
|
margin: 0;
|
|
829
840
|
font-size: 18px;
|
|
830
841
|
font-weight: 600;
|
|
831
|
-
color: #
|
|
842
|
+
color: #fff;
|
|
832
843
|
}
|
|
833
844
|
.edgepdf-marker-delete-popup-close {
|
|
834
845
|
background: none;
|
|
835
846
|
border: none;
|
|
836
847
|
font-size: 24px;
|
|
837
848
|
line-height: 1;
|
|
838
|
-
color: #
|
|
849
|
+
color: #fff;
|
|
839
850
|
cursor: pointer;
|
|
840
851
|
padding: 0;
|
|
841
852
|
width: 32px;
|
|
@@ -847,8 +858,8 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
847
858
|
transition: background-color 0.2s ease;
|
|
848
859
|
}
|
|
849
860
|
.edgepdf-marker-delete-popup-close:hover {
|
|
850
|
-
background-color: #
|
|
851
|
-
color: #
|
|
861
|
+
background-color: #333;
|
|
862
|
+
color: #ffa500;
|
|
852
863
|
}
|
|
853
864
|
.edgepdf-marker-delete-popup-body {
|
|
854
865
|
padding: 20px;
|
|
@@ -856,43 +867,43 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
|
|
856
867
|
.edgepdf-marker-delete-popup-body p {
|
|
857
868
|
margin: 0;
|
|
858
869
|
font-size: 14px;
|
|
859
|
-
color: #
|
|
870
|
+
color: #fff;
|
|
860
871
|
line-height: 1.5;
|
|
861
872
|
}
|
|
862
873
|
.edgepdf-marker-delete-popup-body strong {
|
|
863
874
|
font-weight: 600;
|
|
864
|
-
color: #
|
|
875
|
+
color: #ffa500;
|
|
865
876
|
}
|
|
866
877
|
.edgepdf-marker-delete-popup-footer {
|
|
867
878
|
display: flex;
|
|
868
879
|
justify-content: flex-end;
|
|
869
880
|
gap: 12px;
|
|
870
881
|
padding: 16px 20px;
|
|
871
|
-
border-top: 1px solid #
|
|
882
|
+
border-top: 1px solid #333;
|
|
872
883
|
}
|
|
873
884
|
.edgepdf-marker-delete-button {
|
|
874
885
|
padding: 10px 20px;
|
|
875
|
-
border: 1px solid #
|
|
886
|
+
border: 1px solid #444;
|
|
876
887
|
border-radius: 4px;
|
|
877
|
-
background-color: #
|
|
878
|
-
color: #
|
|
888
|
+
background-color: #2a2a2a;
|
|
889
|
+
color: #fff;
|
|
879
890
|
cursor: pointer;
|
|
880
891
|
font-size: 14px;
|
|
881
892
|
font-weight: 500;
|
|
882
893
|
transition: all 0.2s ease;
|
|
883
894
|
}
|
|
884
895
|
.edgepdf-marker-delete-button-cancel:hover {
|
|
885
|
-
background-color: #
|
|
886
|
-
border-color: #
|
|
896
|
+
background-color: #333;
|
|
897
|
+
border-color: #555;
|
|
887
898
|
}
|
|
888
899
|
.edgepdf-marker-delete-button-confirm {
|
|
889
|
-
background-color: #
|
|
890
|
-
color: #
|
|
891
|
-
border-color: #
|
|
900
|
+
background-color: #ffa500;
|
|
901
|
+
color: #000;
|
|
902
|
+
border-color: #ffa500;
|
|
892
903
|
}
|
|
893
904
|
.edgepdf-marker-delete-button-confirm:hover {
|
|
894
|
-
background-color: #
|
|
895
|
-
border-color: #
|
|
905
|
+
background-color: #ffd700;
|
|
906
|
+
border-color: #ffd700;
|
|
896
907
|
}
|
|
897
908
|
.edgepdf-marker-icon-overlay-container {
|
|
898
909
|
background: transparent !important;
|
package/dist/index.js
CHANGED
|
@@ -9579,6 +9579,12 @@ var require_leaflet_src = __commonJS({
|
|
|
9579
9579
|
var import_leaflet4 = __toESM(require_leaflet_src(), 1);
|
|
9580
9580
|
var import_leaflet = __toESM(require_leaflet_src(), 1);
|
|
9581
9581
|
var MAX_ZOOM_LEVEL = 4;
|
|
9582
|
+
var MARKER_SPEC = {
|
|
9583
|
+
WIDTH: 30,
|
|
9584
|
+
HEIGHT: 40,
|
|
9585
|
+
ANCHOR_X: 16,
|
|
9586
|
+
ANCHOR_Y: 36
|
|
9587
|
+
};
|
|
9582
9588
|
function calculateTileCount(imageSize, tileSize, zoom) {
|
|
9583
9589
|
if (imageSize <= 0 || tileSize <= 0 || zoom < 0) {
|
|
9584
9590
|
return 0;
|
|
@@ -11254,6 +11260,65 @@ var MarkerManager = class {
|
|
|
11254
11260
|
this.updateMarkerIcon(id, iconType);
|
|
11255
11261
|
});
|
|
11256
11262
|
}
|
|
11263
|
+
/**
|
|
11264
|
+
* Focuses on a marker by panning and zooming to its position
|
|
11265
|
+
*
|
|
11266
|
+
* This method smoothly animates the map view to center on the specified marker
|
|
11267
|
+
* and optionally sets a specific zoom level. If no zoom level is provided,
|
|
11268
|
+
* it uses the marker's saved zoom level or a reasonable default.
|
|
11269
|
+
*
|
|
11270
|
+
* @param markerOrId - Marker ID string or Marker object
|
|
11271
|
+
* @param options - Optional focus options
|
|
11272
|
+
* @param options.zoom - Target zoom level (uses marker's zoom or default if not provided)
|
|
11273
|
+
* @param options.animate - Whether to animate the transition (default: true)
|
|
11274
|
+
* @param options.duration - Animation duration in seconds (default: 0.5)
|
|
11275
|
+
* @returns True if focus was successful, false if marker not found
|
|
11276
|
+
*
|
|
11277
|
+
* @example
|
|
11278
|
+
* ```typescript
|
|
11279
|
+
* // Focus on marker by ID
|
|
11280
|
+
* markerManager.focusMarker('marker-123');
|
|
11281
|
+
*
|
|
11282
|
+
* // Focus with specific zoom level
|
|
11283
|
+
* markerManager.focusMarker('marker-123', { zoom: 3 });
|
|
11284
|
+
*
|
|
11285
|
+
* // Focus without animation
|
|
11286
|
+
* markerManager.focusMarker('marker-123', { animate: false });
|
|
11287
|
+
*
|
|
11288
|
+
* // Focus with custom duration
|
|
11289
|
+
* markerManager.focusMarker('marker-123', { zoom: 2, duration: 1.0 });
|
|
11290
|
+
* ```
|
|
11291
|
+
*/
|
|
11292
|
+
focusMarker(markerOrId, options) {
|
|
11293
|
+
let marker = null;
|
|
11294
|
+
if (typeof markerOrId === "string") {
|
|
11295
|
+
marker = this.getMarker(markerOrId);
|
|
11296
|
+
} else {
|
|
11297
|
+
marker = markerOrId;
|
|
11298
|
+
}
|
|
11299
|
+
if (!marker) {
|
|
11300
|
+
console.warn(`Marker not found: ${markerOrId}`);
|
|
11301
|
+
return false;
|
|
11302
|
+
}
|
|
11303
|
+
const targetZoom = options?.zoom ?? marker.zoom ?? 2;
|
|
11304
|
+
const minZoom = this.imageInfo.minZoom ?? 0;
|
|
11305
|
+
const maxZoom = this.imageInfo.maxZoom ?? 5;
|
|
11306
|
+
const constrainedZoom = Math.max(minZoom, Math.min(maxZoom, targetZoom));
|
|
11307
|
+
const position = [
|
|
11308
|
+
marker.position[0],
|
|
11309
|
+
marker.position[1]
|
|
11310
|
+
];
|
|
11311
|
+
const animate = options?.animate !== false;
|
|
11312
|
+
const duration = options?.duration ?? 0.5;
|
|
11313
|
+
if (animate) {
|
|
11314
|
+
this.map.flyTo(position, constrainedZoom, {
|
|
11315
|
+
duration
|
|
11316
|
+
});
|
|
11317
|
+
} else {
|
|
11318
|
+
this.map.setView(position, constrainedZoom);
|
|
11319
|
+
}
|
|
11320
|
+
return true;
|
|
11321
|
+
}
|
|
11257
11322
|
/**
|
|
11258
11323
|
* Disposes of the marker manager and cleans up resources
|
|
11259
11324
|
*
|
|
@@ -11276,10 +11341,9 @@ var MarkerManager = class {
|
|
|
11276
11341
|
const iconUrl = `${this.iconBasePath}${iconType}.png`;
|
|
11277
11342
|
return import_leaflet3.default.icon({
|
|
11278
11343
|
iconUrl,
|
|
11279
|
-
iconSize: [
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
// Point of the icon which will correspond to marker's location
|
|
11344
|
+
iconSize: [MARKER_SPEC.WIDTH, MARKER_SPEC.HEIGHT],
|
|
11345
|
+
iconAnchor: [MARKER_SPEC.ANCHOR_X, MARKER_SPEC.ANCHOR_Y],
|
|
11346
|
+
// Anchor point aligns with marker location
|
|
11283
11347
|
popupAnchor: [0, 0],
|
|
11284
11348
|
// Point from which the popup should open relative to the iconAnchor
|
|
11285
11349
|
shadowUrl: void 0,
|
|
@@ -11409,8 +11473,8 @@ var MarkerManager = class {
|
|
|
11409
11473
|
}
|
|
11410
11474
|
const overlaySize = 40;
|
|
11411
11475
|
const borderWidth = 1;
|
|
11412
|
-
const overlayAnchorX =
|
|
11413
|
-
const overlayAnchorY =
|
|
11476
|
+
const overlayAnchorX = overlaySize / 2 + (MARKER_SPEC.WIDTH / 2 - MARKER_SPEC.ANCHOR_X);
|
|
11477
|
+
const overlayAnchorY = overlaySize / 2 + (MARKER_SPEC.HEIGHT / 2 - MARKER_SPEC.ANCHOR_Y);
|
|
11414
11478
|
const overlayHTML = `
|
|
11415
11479
|
<div style="
|
|
11416
11480
|
width: ${overlaySize}px;
|
|
@@ -12363,6 +12427,34 @@ var EdgePdfViewer = class {
|
|
|
12363
12427
|
getMarkerManager() {
|
|
12364
12428
|
return this.markerManager;
|
|
12365
12429
|
}
|
|
12430
|
+
/**
|
|
12431
|
+
* Focuses on a marker by panning and zooming to its position
|
|
12432
|
+
*
|
|
12433
|
+
* This is a convenience method that delegates to the marker manager's focusMarker method.
|
|
12434
|
+
*
|
|
12435
|
+
* @param markerOrId - Marker ID string or Marker object
|
|
12436
|
+
* @param options - Optional focus options
|
|
12437
|
+
* @param options.zoom - Target zoom level (uses marker's zoom or default if not provided)
|
|
12438
|
+
* @param options.animate - Whether to animate the transition (default: true)
|
|
12439
|
+
* @param options.duration - Animation duration in seconds (default: 0.5)
|
|
12440
|
+
* @returns True if focus was successful, false if marker not found or viewer not initialized
|
|
12441
|
+
*
|
|
12442
|
+
* @example
|
|
12443
|
+
* ```typescript
|
|
12444
|
+
* // Focus on marker by ID
|
|
12445
|
+
* viewer.focusMarker('marker-123');
|
|
12446
|
+
*
|
|
12447
|
+
* // Focus with specific zoom level
|
|
12448
|
+
* viewer.focusMarker('marker-123', { zoom: 3 });
|
|
12449
|
+
* ```
|
|
12450
|
+
*/
|
|
12451
|
+
focusMarker(markerOrId, options) {
|
|
12452
|
+
if (!this.markerManager) {
|
|
12453
|
+
console.warn("Marker manager not available");
|
|
12454
|
+
return false;
|
|
12455
|
+
}
|
|
12456
|
+
return this.markerManager.focusMarker(markerOrId, options);
|
|
12457
|
+
}
|
|
12366
12458
|
/**
|
|
12367
12459
|
* Disposes of the map instance and cleans up resources
|
|
12368
12460
|
*
|
|
@@ -12974,6 +13066,21 @@ function useMarkers() {
|
|
|
12974
13066
|
console.error("Failed to clear markers:", error);
|
|
12975
13067
|
}
|
|
12976
13068
|
}, [viewer, isInitialized]);
|
|
13069
|
+
const focusMarker = useCallback3(
|
|
13070
|
+
(markerOrId, options) => {
|
|
13071
|
+
if (!viewer || !isInitialized) {
|
|
13072
|
+
console.warn("Viewer not initialized");
|
|
13073
|
+
return false;
|
|
13074
|
+
}
|
|
13075
|
+
try {
|
|
13076
|
+
return viewer.focusMarker(markerOrId, options);
|
|
13077
|
+
} catch (error) {
|
|
13078
|
+
console.error("Failed to focus marker:", error);
|
|
13079
|
+
return false;
|
|
13080
|
+
}
|
|
13081
|
+
},
|
|
13082
|
+
[viewer, isInitialized]
|
|
13083
|
+
);
|
|
12977
13084
|
return {
|
|
12978
13085
|
markers,
|
|
12979
13086
|
addMarker,
|
|
@@ -12983,7 +13090,8 @@ function useMarkers() {
|
|
|
12983
13090
|
getAllMarkers,
|
|
12984
13091
|
exportMarkers,
|
|
12985
13092
|
importMarkers,
|
|
12986
|
-
clearMarkers
|
|
13093
|
+
clearMarkers,
|
|
13094
|
+
focusMarker
|
|
12987
13095
|
};
|
|
12988
13096
|
}
|
|
12989
13097
|
|
|
@@ -22,6 +22,12 @@ export interface UseMarkersReturn {
|
|
|
22
22
|
importMarkers: (data: MarkerData) => boolean;
|
|
23
23
|
/** Clear all markers */
|
|
24
24
|
clearMarkers: () => void;
|
|
25
|
+
/** Focus on a marker by panning and zooming to its position */
|
|
26
|
+
focusMarker: (markerOrId: string | Marker, options?: {
|
|
27
|
+
zoom?: number;
|
|
28
|
+
animate?: boolean;
|
|
29
|
+
duration?: number;
|
|
30
|
+
}) => boolean;
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* Hook to manage markers in the viewer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-markers.d.ts","sourceRoot":"","sources":["../../src/lib/use-markers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uBAAuB;IACvB,SAAS,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,MAAM,GAAG,IAAI,CAAC;IAC3D,4BAA4B;IAC5B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,sBAAsB;IACtB,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;IAChE,yBAAyB;IACzB,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACzC,sBAAsB;IACtB,aAAa,EAAE,MAAM,MAAM,EAAE,CAAC;IAC9B,6BAA6B;IAC7B,aAAa,EAAE,MAAM,UAAU,CAAC;IAChC,+BAA+B;IAC/B,aAAa,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC;IAC7C,wBAAwB;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"use-markers.d.ts","sourceRoot":"","sources":["../../src/lib/use-markers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uBAAuB;IACvB,SAAS,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,MAAM,GAAG,IAAI,CAAC;IAC3D,4BAA4B;IAC5B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,sBAAsB;IACtB,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;IAChE,yBAAyB;IACzB,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACzC,sBAAsB;IACtB,aAAa,EAAE,MAAM,MAAM,EAAE,CAAC;IAC9B,6BAA6B;IAC7B,aAAa,EAAE,MAAM,UAAU,CAAC;IAChC,+BAA+B;IAC/B,aAAa,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC;IAC7C,wBAAwB;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,+DAA+D;IAC/D,WAAW,EAAE,CACX,UAAU,EAAE,MAAM,GAAG,MAAM,EAC3B,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,KACE,OAAO,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,UAAU,IAAI,gBAAgB,CA2O7C"}
|
package/package.json
CHANGED